minix3/net/inet/generic/ip.c

495 lines
10 KiB
C

/*
ip.c
Copyright 1995 Philip Homburg
*/
#include "inet.h"
#include "buf.h"
#include "event.h"
#include "type.h"
#include "arp.h"
#include "assert.h"
#include "clock.h"
#include "eth.h"
#include "icmp.h"
#include "icmp_lib.h"
#include "io.h"
#include "ip.h"
#include "ip_int.h"
#include "ipr.h"
#include "sr.h"
THIS_FILE
static void ip_close ARGS(( int fd ));
static int ip_cancel ARGS(( int fd, int which_operation ));
static int ip_select ARGS(( int fd, unsigned operations ));
static void ip_buffree ARGS(( int priority ));
#ifdef BUF_CONSISTENCY_CHECK
static void ip_bufcheck ARGS(( void ));
#endif
static void ip_bad_callback ARGS(( struct ip_port *ip_port ));
ip_port_t *ip_port_table;
ip_fd_t ip_fd_table[IP_FD_NR];
ip_ass_t ip_ass_table[IP_ASS_NR];
void ip_prep()
{
ip_port_table= alloc(ip_conf_nr * sizeof(ip_port_table[0]));
icmp_prep();
}
void ip_init()
{
int i, j, result;
ip_ass_t *ip_ass;
ip_fd_t *ip_fd;
ip_port_t *ip_port;
struct ip_conf *icp;
assert (BUF_S >= sizeof(struct nwio_ethopt));
assert (BUF_S >= IP_MAX_HDR_SIZE + ETH_HDR_SIZE);
assert (BUF_S >= sizeof(nwio_ipopt_t));
assert (BUF_S >= sizeof(nwio_route_t));
for (i=0, ip_ass= ip_ass_table; i<IP_ASS_NR; i++, ip_ass++)
{
ip_ass->ia_frags= 0;
ip_ass->ia_first_time= 0;
ip_ass->ia_port= 0;
}
for (i=0, ip_fd= ip_fd_table; i<IP_FD_NR; i++, ip_fd++)
{
ip_fd->if_flags= IFF_EMPTY;
ip_fd->if_rdbuf_head= 0;
}
for (i=0, ip_port= ip_port_table, icp= ip_conf;
i<ip_conf_nr; i++, ip_port++, icp++)
{
ip_port->ip_port= i;
ip_port->ip_flags= IPF_EMPTY;
ip_port->ip_dev_main= (ip_dev_t)ip_bad_callback;
ip_port->ip_dev_set_ipaddr= (ip_dev_t)ip_bad_callback;
ip_port->ip_dev_send= (ip_dev_send_t)ip_bad_callback;
ip_port->ip_dl_type= icp->ic_devtype;
ip_port->ip_mtu= IP_DEF_MTU;
ip_port->ip_mtu_max= IP_MAX_PACKSIZE;
switch(ip_port->ip_dl_type)
{
case IPDL_ETH:
ip_port->ip_dl.dl_eth.de_port= icp->ic_port;
result= ipeth_init(ip_port);
if (result == -1)
continue;
assert(result == NW_OK);
break;
case IPDL_PSIP:
ip_port->ip_dl.dl_ps.ps_port= icp->ic_port;
result= ipps_init(ip_port);
if (result == -1)
continue;
assert(result == NW_OK);
break;
default:
ip_panic(( "unknown ip_dl_type %d",
ip_port->ip_dl_type ));
break;
}
ip_port->ip_loopb_head= NULL;
ip_port->ip_loopb_tail= NULL;
ev_init(&ip_port->ip_loopb_event);
ip_port->ip_routeq_head= NULL;
ip_port->ip_routeq_tail= NULL;
ev_init(&ip_port->ip_routeq_event);
ip_port->ip_flags |= IPF_CONFIGURED;
ip_port->ip_proto_any= NULL;
for (j= 0; j<IP_PROTO_HASH_NR; j++)
ip_port->ip_proto[j]= NULL;
}
#ifndef BUF_CONSISTENCY_CHECK
bf_logon(ip_buffree);
#else
bf_logon(ip_buffree, ip_bufcheck);
#endif
icmp_init();
ipr_init();
for (i=0, ip_port= ip_port_table; i<ip_conf_nr; i++, ip_port++)
{
if (!(ip_port->ip_flags & IPF_CONFIGURED))
continue;
ip_port->ip_frame_id= (u16_t)get_time();
sr_add_minor(if2minor(ip_conf[i].ic_ifno, IP_DEV_OFF),
i, ip_open, ip_close, ip_read,
ip_write, ip_ioctl, ip_cancel, ip_select);
(*ip_port->ip_dev_main)(ip_port);
}
}
static int ip_cancel (fd, which_operation)
int fd;
int which_operation;
{
ip_fd_t *ip_fd;
acc_t *repl_res;
int result;
ip_fd= &ip_fd_table[fd];
switch (which_operation)
{
case SR_CANCEL_IOCTL:
assert (ip_fd->if_flags & IFF_IOCTL_IP);
ip_fd->if_flags &= ~IFF_IOCTL_IP;
repl_res= (*ip_fd->if_get_userdata)(ip_fd->if_srfd,
(size_t)EINTR, (size_t)0, TRUE);
assert (!repl_res);
break;
case SR_CANCEL_READ:
assert (ip_fd->if_flags & IFF_READ_IP);
ip_fd->if_flags &= ~IFF_READ_IP;
result= (*ip_fd->if_put_userdata)(ip_fd->if_srfd,
(size_t)EINTR, (acc_t *)0, FALSE);
assert (!result);
break;
#if 0
case SR_CANCEL_WRITE:
assert(0);
assert (ip_fd->if_flags & IFF_WRITE_MASK);
ip_fd->if_flags &= ~IFF_WRITE_MASK;
repl_res= (*ip_fd->if_get_userdata)(ip_fd->if_srfd,
(size_t)EINTR, (size_t)0, FALSE);
assert (!repl_res);
break;
#endif
default:
ip_panic(( "unknown cancel request" ));
break;
}
return NW_OK;
}
static int ip_select(fd, operations)
int fd;
unsigned operations;
{
unsigned resops;
ip_fd_t *ip_fd;
ip_fd= &ip_fd_table[fd];
assert (ip_fd->if_flags & IFF_INUSE);
resops= 0;
if (operations & SR_SELECT_READ)
{
if (ip_sel_read(ip_fd))
resops |= SR_SELECT_READ;
else if (!(operations & SR_SELECT_POLL))
ip_fd->if_flags |= IFF_SEL_READ;
}
if (operations & SR_SELECT_WRITE)
{
/* Should handle special case when the interface is down */
resops |= SR_SELECT_WRITE;
}
if (operations & SR_SELECT_EXCEPTION)
{
printf("ip_select: not implemented for exceptions\n");
}
return resops;
}
int ip_open (port, srfd, get_userdata, put_userdata, put_pkt,
select_res)
int port;
int srfd;
get_userdata_t get_userdata;
put_userdata_t put_userdata;
put_pkt_t put_pkt;
select_res_t select_res;
{
int i;
ip_fd_t *ip_fd;
ip_port_t *ip_port;
ip_port= &ip_port_table[port];
if (!(ip_port->ip_flags & IPF_CONFIGURED))
return ENXIO;
for (i=0; i<IP_FD_NR && (ip_fd_table[i].if_flags & IFF_INUSE);
i++);
if (i>=IP_FD_NR)
{
DBLOCK(1, printf("out of fds\n"));
return EAGAIN;
}
ip_fd= &ip_fd_table[i];
ip_fd->if_flags= IFF_INUSE;
ip_fd->if_ipopt.nwio_flags= NWIO_DEFAULT;
ip_fd->if_ipopt.nwio_tos= 0;
ip_fd->if_ipopt.nwio_df= FALSE;
ip_fd->if_ipopt.nwio_ttl= 255;
ip_fd->if_ipopt.nwio_hdropt.iho_opt_siz= 0;
ip_fd->if_port= ip_port;
ip_fd->if_srfd= srfd;
assert(ip_fd->if_rdbuf_head == NULL);
ip_fd->if_get_userdata= get_userdata;
ip_fd->if_put_userdata= put_userdata;
ip_fd->if_put_pkt= put_pkt;
ip_fd->if_select_res= select_res;
return i;
}
static void ip_close (fd)
int fd;
{
ip_fd_t *ip_fd;
acc_t *pack;
ip_fd= &ip_fd_table[fd];
assert ((ip_fd->if_flags & IFF_INUSE) &&
!(ip_fd->if_flags & IFF_BUSY));
if (ip_fd->if_flags & IFF_OPTSET)
ip_unhash_proto(ip_fd);
while (ip_fd->if_rdbuf_head)
{
pack= ip_fd->if_rdbuf_head;
ip_fd->if_rdbuf_head= pack->acc_ext_link;
bf_afree(pack);
}
ip_fd->if_flags= IFF_EMPTY;
}
static void ip_buffree(priority)
int priority;
{
int i;
ip_port_t *ip_port;
ip_fd_t *ip_fd;
ip_ass_t *ip_ass;
acc_t *pack, *next_pack;
for (i= 0, ip_port= ip_port_table; i<ip_conf_nr; i++, ip_port++)
{
if (ip_port->ip_dl_type == IPDL_ETH)
{
/* Can't free de_frame.
* bf_check_acc(ip_port->ip_dl.dl_eth.de_frame);
*/
if (priority == IP_PRI_PORTBUFS)
{
next_pack= ip_port->ip_dl.dl_eth.de_arp_head;
while(next_pack != NULL)
{
pack= next_pack;
next_pack= pack->acc_ext_link;
bf_afree(pack);
}
ip_port->ip_dl.dl_eth.de_arp_head= next_pack;
next_pack= ip_port->ip_dl.dl_eth.de_q_head;
while(next_pack != NULL)
{
pack= next_pack;
next_pack= pack->acc_ext_link;
bf_afree(pack);
}
ip_port->ip_dl.dl_eth.de_q_head= next_pack;
}
}
else if (ip_port->ip_dl_type == IPDL_PSIP)
{
if (priority == IP_PRI_PORTBUFS)
{
next_pack= ip_port->ip_dl.dl_ps.ps_send_head;
while (next_pack != NULL)
{
pack= next_pack;
next_pack= pack->acc_ext_link;
bf_afree(pack);
}
ip_port->ip_dl.dl_ps.ps_send_head= next_pack;
}
}
if (priority == IP_PRI_PORTBUFS)
{
next_pack= ip_port->ip_loopb_head;
while(next_pack && next_pack->acc_ext_link)
{
pack= next_pack;
next_pack= pack->acc_ext_link;
bf_afree(pack);
}
if (next_pack)
{
if (ev_in_queue(&ip_port->ip_loopb_event))
{
#if DEBUG
printf(
"not freeing ip_loopb_head, ip_loopb_event enqueued\n");
#endif
}
else
{
bf_afree(next_pack);
next_pack= NULL;
}
}
ip_port->ip_loopb_head= next_pack;
next_pack= ip_port->ip_routeq_head;
while(next_pack && next_pack->acc_ext_link)
{
pack= next_pack;
next_pack= pack->acc_ext_link;
bf_afree(pack);
}
if (next_pack)
{
if (ev_in_queue(&ip_port->ip_routeq_event))
{
#if DEBUG
printf(
"not freeing ip_loopb_head, ip_routeq_event enqueued\n");
#endif
}
else
{
bf_afree(next_pack);
next_pack= NULL;
}
}
ip_port->ip_routeq_head= next_pack;
}
}
if (priority == IP_PRI_FDBUFS_EXTRA)
{
for (i= 0, ip_fd= ip_fd_table; i<IP_FD_NR; i++, ip_fd++)
{
while (ip_fd->if_rdbuf_head &&
ip_fd->if_rdbuf_head->acc_ext_link)
{
pack= ip_fd->if_rdbuf_head;
ip_fd->if_rdbuf_head= pack->acc_ext_link;
bf_afree(pack);
}
}
}
if (priority == IP_PRI_FDBUFS)
{
for (i= 0, ip_fd= ip_fd_table; i<IP_FD_NR; i++, ip_fd++)
{
while (ip_fd->if_rdbuf_head)
{
pack= ip_fd->if_rdbuf_head;
ip_fd->if_rdbuf_head= pack->acc_ext_link;
bf_afree(pack);
}
}
}
if (priority == IP_PRI_ASSBUFS)
{
for (i= 0, ip_ass= ip_ass_table; i<IP_ASS_NR; i++, ip_ass++)
{
while(ip_ass->ia_frags != NULL)
{
pack= ip_ass->ia_frags;
ip_ass->ia_frags= pack->acc_ext_link;
bf_afree(pack);
}
ip_ass->ia_first_time= 0;
}
}
}
#ifdef BUF_CONSISTENCY_CHECK
static void ip_bufcheck()
{
int i;
ip_port_t *ip_port;
ip_fd_t *ip_fd;
ip_ass_t *ip_ass;
acc_t *pack;
for (i= 0, ip_port= ip_port_table; i<ip_conf_nr; i++, ip_port++)
{
if (ip_port->ip_dl_type == IPDL_ETH)
{
bf_check_acc(ip_port->ip_dl.dl_eth.de_frame);
for (pack= ip_port->ip_dl.dl_eth.de_q_head; pack;
pack= pack->acc_ext_link)
{
bf_check_acc(pack);
}
for (pack= ip_port->ip_dl.dl_eth.de_arp_head; pack;
pack= pack->acc_ext_link)
{
bf_check_acc(pack);
}
}
else if (ip_port->ip_dl_type == IPDL_PSIP)
{
for (pack= ip_port->ip_dl.dl_ps.ps_send_head; pack;
pack= pack->acc_ext_link)
{
bf_check_acc(pack);
}
}
for (pack= ip_port->ip_loopb_head; pack;
pack= pack->acc_ext_link)
{
bf_check_acc(pack);
}
for (pack= ip_port->ip_routeq_head; pack;
pack= pack->acc_ext_link)
{
bf_check_acc(pack);
}
}
for (i= 0, ip_fd= ip_fd_table; i<IP_FD_NR; i++, ip_fd++)
{
for (pack= ip_fd->if_rdbuf_head; pack;
pack= pack->acc_ext_link)
{
bf_check_acc(pack);
}
}
for (i= 0, ip_ass= ip_ass_table; i<IP_ASS_NR; i++, ip_ass++)
{
for (pack= ip_ass->ia_frags; pack; pack= pack->acc_ext_link)
bf_check_acc(pack);
}
}
#endif /* BUF_CONSISTENCY_CHECK */
__dead
static void ip_bad_callback(ip_port)
struct ip_port *ip_port;
{
ip_panic(( "no callback filled in for port %d", ip_port->ip_port ));
}
/*
* $PchId: ip.c,v 1.19 2005/06/28 14:17:40 philip Exp $
*/