493 lines
10 KiB
C
493 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 */
|
|
|
|
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 $
|
|
*/
|