2005-04-21 16:53:53 +02:00
|
|
|
/*
|
|
|
|
arp.c
|
|
|
|
|
|
|
|
Copyright 1995 Philip Homburg
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "inet.h"
|
|
|
|
#include "type.h"
|
|
|
|
|
|
|
|
#include "arp.h"
|
|
|
|
#include "assert.h"
|
|
|
|
#include "buf.h"
|
|
|
|
#include "clock.h"
|
2005-06-28 17:19:58 +02:00
|
|
|
#include "event.h"
|
2005-04-21 16:53:53 +02:00
|
|
|
#include "eth.h"
|
|
|
|
#include "io.h"
|
|
|
|
|
|
|
|
THIS_FILE
|
|
|
|
|
2005-06-28 17:19:58 +02:00
|
|
|
#define ARP_CACHE_NR 256
|
|
|
|
#define AP_REQ_NR 32
|
|
|
|
|
|
|
|
#define ARP_HASH_NR 256
|
|
|
|
#define ARP_HASH_MASK 0xff
|
|
|
|
#define ARP_HASH_WIDTH 4
|
2005-04-21 16:53:53 +02:00
|
|
|
|
|
|
|
#define MAX_ARP_RETRIES 5
|
|
|
|
#define ARP_TIMEOUT (HZ/2+1) /* .5 seconds */
|
|
|
|
#ifndef ARP_EXP_TIME
|
|
|
|
#define ARP_EXP_TIME (20L*60L*HZ) /* 20 minutes */
|
|
|
|
#endif
|
2005-06-28 17:19:58 +02:00
|
|
|
#define ARP_NOTRCH_EXP_TIME (30*HZ) /* 30 seconds */
|
2005-04-21 16:53:53 +02:00
|
|
|
#define ARP_INUSE_OFFSET (60*HZ) /* an entry in the cache can be deleted
|
|
|
|
if its not used for 1 minute */
|
|
|
|
|
|
|
|
typedef struct arp46
|
|
|
|
{
|
|
|
|
ether_addr_t a46_dstaddr;
|
|
|
|
ether_addr_t a46_srcaddr;
|
|
|
|
ether_type_t a46_ethtype;
|
|
|
|
union
|
|
|
|
{
|
|
|
|
struct
|
|
|
|
{
|
|
|
|
u16_t a_hdr, a_pro;
|
|
|
|
u8_t a_hln, a_pln;
|
|
|
|
u16_t a_op;
|
|
|
|
ether_addr_t a_sha;
|
|
|
|
u8_t a_spa[4];
|
|
|
|
ether_addr_t a_tha;
|
|
|
|
u8_t a_tpa[4];
|
|
|
|
} a46_data;
|
|
|
|
char a46_dummy[ETH_MIN_PACK_SIZE-ETH_HDR_SIZE];
|
|
|
|
} a46_data;
|
|
|
|
} arp46_t;
|
|
|
|
|
|
|
|
#define a46_hdr a46_data.a46_data.a_hdr
|
|
|
|
#define a46_pro a46_data.a46_data.a_pro
|
|
|
|
#define a46_hln a46_data.a46_data.a_hln
|
|
|
|
#define a46_pln a46_data.a46_data.a_pln
|
|
|
|
#define a46_op a46_data.a46_data.a_op
|
|
|
|
#define a46_sha a46_data.a46_data.a_sha
|
|
|
|
#define a46_spa a46_data.a46_data.a_spa
|
|
|
|
#define a46_tha a46_data.a46_data.a_tha
|
|
|
|
#define a46_tpa a46_data.a46_data.a_tpa
|
|
|
|
|
|
|
|
typedef struct arp_port
|
|
|
|
{
|
|
|
|
int ap_flags;
|
|
|
|
int ap_state;
|
|
|
|
int ap_eth_port;
|
|
|
|
int ap_ip_port;
|
|
|
|
int ap_eth_fd;
|
|
|
|
|
2005-06-28 17:19:58 +02:00
|
|
|
ether_addr_t ap_ethaddr; /* Ethernet address of this port */
|
|
|
|
ipaddr_t ap_ipaddr; /* IP address of this port */
|
2005-04-21 16:53:53 +02:00
|
|
|
|
2005-06-28 17:19:58 +02:00
|
|
|
struct arp_req
|
|
|
|
{
|
|
|
|
timer_t ar_timer;
|
|
|
|
int ar_entry;
|
|
|
|
int ar_req_count;
|
|
|
|
} ap_req[AP_REQ_NR];
|
2005-04-21 16:53:53 +02:00
|
|
|
|
|
|
|
arp_func_t ap_arp_func;
|
2005-06-28 17:19:58 +02:00
|
|
|
|
|
|
|
acc_t *ap_sendpkt;
|
|
|
|
acc_t *ap_sendlist;
|
|
|
|
acc_t *ap_reclist;
|
|
|
|
event_t ap_event;
|
2005-04-21 16:53:53 +02:00
|
|
|
} arp_port_t;
|
|
|
|
|
2005-06-28 17:19:58 +02:00
|
|
|
#define APF_EMPTY 0x00
|
|
|
|
#define APF_ARP_RD_IP 0x01
|
|
|
|
#define APF_ARP_RD_SP 0x02
|
|
|
|
#define APF_ARP_WR_IP 0x04
|
|
|
|
#define APF_ARP_WR_SP 0x08
|
|
|
|
#define APF_INADDR_SET 0x10
|
|
|
|
#define APF_SUSPEND 0x20
|
|
|
|
|
|
|
|
#define APS_INITIAL 1
|
|
|
|
#define APS_GETADDR 2
|
|
|
|
#define APS_ARPSTART 3
|
|
|
|
#define APS_ARPPROTO 4
|
|
|
|
#define APS_ARPMAIN 5
|
|
|
|
#define APS_ERROR 6
|
2005-04-21 16:53:53 +02:00
|
|
|
|
|
|
|
typedef struct arp_cache
|
|
|
|
{
|
|
|
|
int ac_flags;
|
|
|
|
int ac_state;
|
|
|
|
ether_addr_t ac_ethaddr;
|
|
|
|
ipaddr_t ac_ipaddr;
|
|
|
|
arp_port_t *ac_port;
|
|
|
|
time_t ac_expire;
|
|
|
|
time_t ac_lastuse;
|
|
|
|
} arp_cache_t;
|
|
|
|
|
|
|
|
#define ACF_EMPTY 0
|
2005-06-28 17:19:58 +02:00
|
|
|
#define ACF_PERM 1
|
|
|
|
#define ACF_PUB 2
|
2005-04-21 16:53:53 +02:00
|
|
|
|
|
|
|
#define ACS_UNUSED 0
|
|
|
|
#define ACS_INCOMPLETE 1
|
|
|
|
#define ACS_VALID 2
|
|
|
|
#define ACS_UNREACHABLE 3
|
|
|
|
|
2005-06-28 17:19:58 +02:00
|
|
|
PRIVATE struct arp_hash_ent
|
|
|
|
{
|
|
|
|
arp_cache_t *ahe_row[ARP_HASH_WIDTH];
|
|
|
|
} arp_hash[ARP_HASH_NR];
|
|
|
|
|
|
|
|
PRIVATE arp_port_t *arp_port_table;
|
|
|
|
PRIVATE arp_cache_t *arp_cache;
|
|
|
|
PRIVATE int arp_cache_nr;
|
|
|
|
|
2005-04-21 16:53:53 +02:00
|
|
|
FORWARD acc_t *arp_getdata ARGS(( int fd, size_t offset,
|
|
|
|
size_t count, int for_ioctl ));
|
|
|
|
FORWARD int arp_putdata ARGS(( int fd, size_t offset,
|
|
|
|
acc_t *data, int for_ioctl ));
|
|
|
|
FORWARD void arp_main ARGS(( arp_port_t *arp_port ));
|
2005-06-28 17:19:58 +02:00
|
|
|
FORWARD void arp_timeout ARGS(( int ref, timer_t *timer ));
|
2005-04-21 16:53:53 +02:00
|
|
|
FORWARD void setup_write ARGS(( arp_port_t *arp_port ));
|
|
|
|
FORWARD void setup_read ARGS(( arp_port_t *arp_port ));
|
2005-06-28 17:19:58 +02:00
|
|
|
FORWARD void do_reclist ARGS(( event_t *ev, ev_arg_t ev_arg ));
|
|
|
|
FORWARD void process_arp_pkt ARGS(( arp_port_t *arp_port, acc_t *data ));
|
2005-04-21 16:53:53 +02:00
|
|
|
FORWARD void client_reply ARGS(( arp_port_t *arp_port,
|
|
|
|
ipaddr_t ipaddr, ether_addr_t *ethaddr ));
|
|
|
|
FORWARD arp_cache_t *find_cache_ent ARGS(( arp_port_t *arp_port,
|
|
|
|
ipaddr_t ipaddr ));
|
2005-06-28 17:19:58 +02:00
|
|
|
FORWARD arp_cache_t *alloc_cache_ent ARGS(( int flags ));
|
|
|
|
FORWARD void arp_buffree ARGS(( int priority ));
|
|
|
|
#ifdef BUF_CONSISTENCY_CHECK
|
|
|
|
FORWARD void arp_bufcheck ARGS(( void ));
|
|
|
|
#endif
|
2005-04-21 16:53:53 +02:00
|
|
|
|
|
|
|
PUBLIC void arp_prep()
|
|
|
|
{
|
|
|
|
arp_port_table= alloc(eth_conf_nr * sizeof(arp_port_table[0]));
|
2005-06-28 17:19:58 +02:00
|
|
|
|
|
|
|
arp_cache_nr= ARP_CACHE_NR;
|
|
|
|
if (arp_cache_nr < (eth_conf_nr+1)*AP_REQ_NR)
|
|
|
|
{
|
|
|
|
arp_cache_nr= (eth_conf_nr+1)*AP_REQ_NR;
|
|
|
|
printf("arp: using %d cache entries instead of %d\n",
|
|
|
|
arp_cache_nr, ARP_CACHE_NR);
|
|
|
|
}
|
|
|
|
arp_cache= alloc(arp_cache_nr * sizeof(arp_cache[0]));
|
2005-04-21 16:53:53 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
PUBLIC void arp_init()
|
|
|
|
{
|
|
|
|
arp_port_t *arp_port;
|
2005-06-28 17:19:58 +02:00
|
|
|
arp_cache_t *cache;
|
2005-04-21 16:53:53 +02:00
|
|
|
int i;
|
|
|
|
|
|
|
|
assert (BUF_S >= sizeof(struct nwio_ethstat));
|
|
|
|
assert (BUF_S >= sizeof(struct nwio_ethopt));
|
|
|
|
assert (BUF_S >= sizeof(arp46_t));
|
|
|
|
|
|
|
|
for (i=0, arp_port= arp_port_table; i<eth_conf_nr; i++, arp_port++)
|
|
|
|
{
|
|
|
|
arp_port->ap_state= APS_ERROR; /* Mark all ports as
|
|
|
|
* unavailable */
|
|
|
|
}
|
|
|
|
|
2005-06-28 17:19:58 +02:00
|
|
|
cache= arp_cache;
|
|
|
|
for (i=0; i<arp_cache_nr; i++, cache++)
|
|
|
|
{
|
|
|
|
cache->ac_state= ACS_UNUSED;
|
|
|
|
cache->ac_flags= ACF_EMPTY;
|
|
|
|
cache->ac_expire= 0;
|
|
|
|
cache->ac_lastuse= 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifndef BUF_CONSISTENCY_CHECK
|
|
|
|
bf_logon(arp_buffree);
|
|
|
|
#else
|
|
|
|
bf_logon(arp_buffree, arp_bufcheck);
|
|
|
|
#endif
|
2005-04-21 16:53:53 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
PRIVATE void arp_main(arp_port)
|
|
|
|
arp_port_t *arp_port;
|
|
|
|
{
|
|
|
|
int result;
|
|
|
|
|
|
|
|
switch (arp_port->ap_state)
|
|
|
|
{
|
|
|
|
case APS_INITIAL:
|
|
|
|
arp_port->ap_eth_fd= eth_open(arp_port->ap_eth_port,
|
2005-06-28 17:19:58 +02:00
|
|
|
arp_port->ap_eth_port, arp_getdata, arp_putdata,
|
|
|
|
0 /* no put_pkt */, 0 /* no select_res */);
|
2005-04-21 16:53:53 +02:00
|
|
|
|
|
|
|
if (arp_port->ap_eth_fd<0)
|
|
|
|
{
|
2005-06-28 17:19:58 +02:00
|
|
|
DBLOCK(1, printf("arp[%d]: unable to open eth[%d]\n",
|
|
|
|
arp_port-arp_port_table,
|
|
|
|
arp_port->ap_eth_port));
|
2005-04-21 16:53:53 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
arp_port->ap_state= APS_GETADDR;
|
|
|
|
|
|
|
|
result= eth_ioctl (arp_port->ap_eth_fd, NWIOGETHSTAT);
|
|
|
|
|
|
|
|
if ( result == NW_SUSPEND)
|
|
|
|
{
|
|
|
|
arp_port->ap_flags |= APF_SUSPEND;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
assert(result == NW_OK);
|
|
|
|
|
|
|
|
/* fall through */
|
|
|
|
case APS_GETADDR:
|
|
|
|
/* Wait for IP address */
|
|
|
|
if (!(arp_port->ap_flags & APF_INADDR_SET))
|
|
|
|
return;
|
|
|
|
|
|
|
|
/* fall through */
|
|
|
|
case APS_ARPSTART:
|
|
|
|
arp_port->ap_state= APS_ARPPROTO;
|
|
|
|
|
|
|
|
result= eth_ioctl (arp_port->ap_eth_fd, NWIOSETHOPT);
|
|
|
|
|
|
|
|
if (result==NW_SUSPEND)
|
|
|
|
{
|
|
|
|
arp_port->ap_flags |= APF_SUSPEND;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
assert(result == NW_OK);
|
|
|
|
|
|
|
|
/* fall through */
|
|
|
|
case APS_ARPPROTO:
|
|
|
|
arp_port->ap_state= APS_ARPMAIN;
|
2005-06-28 17:19:58 +02:00
|
|
|
setup_write(arp_port);
|
2005-04-21 16:53:53 +02:00
|
|
|
setup_read(arp_port);
|
|
|
|
return;
|
|
|
|
|
|
|
|
default:
|
|
|
|
ip_panic((
|
|
|
|
"arp_main(&arp_port_table[%d]) called but ap_state=0x%x\n",
|
|
|
|
arp_port->ap_eth_port, arp_port->ap_state ));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
PRIVATE acc_t *arp_getdata (fd, offset, count, for_ioctl)
|
|
|
|
int fd;
|
|
|
|
size_t offset;
|
|
|
|
size_t count;
|
|
|
|
int for_ioctl;
|
|
|
|
{
|
|
|
|
arp_port_t *arp_port;
|
|
|
|
acc_t *data;
|
|
|
|
int result;
|
|
|
|
|
|
|
|
arp_port= &arp_port_table[fd];
|
|
|
|
|
|
|
|
switch (arp_port->ap_state)
|
|
|
|
{
|
|
|
|
case APS_ARPPROTO:
|
|
|
|
if (!count)
|
|
|
|
{
|
|
|
|
result= (int)offset;
|
|
|
|
if (result<0)
|
|
|
|
{
|
|
|
|
arp_port->ap_state= APS_ERROR;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (arp_port->ap_flags & APF_SUSPEND)
|
|
|
|
{
|
|
|
|
arp_port->ap_flags &= ~APF_SUSPEND;
|
|
|
|
arp_main(arp_port);
|
|
|
|
}
|
|
|
|
return NW_OK;
|
|
|
|
}
|
|
|
|
assert ((!offset) && (count == sizeof(struct nwio_ethopt)));
|
|
|
|
{
|
|
|
|
struct nwio_ethopt *ethopt;
|
|
|
|
acc_t *acc;
|
|
|
|
|
|
|
|
acc= bf_memreq(sizeof(*ethopt));
|
|
|
|
ethopt= (struct nwio_ethopt *)ptr2acc_data(acc);
|
|
|
|
ethopt->nweo_flags= NWEO_COPY|NWEO_EN_BROAD|
|
|
|
|
NWEO_TYPESPEC;
|
|
|
|
ethopt->nweo_type= HTONS(ETH_ARP_PROTO);
|
|
|
|
return acc;
|
|
|
|
}
|
|
|
|
case APS_ARPMAIN:
|
|
|
|
assert (arp_port->ap_flags & APF_ARP_WR_IP);
|
|
|
|
if (!count)
|
|
|
|
{
|
2005-06-28 17:19:58 +02:00
|
|
|
data= arp_port->ap_sendpkt;
|
|
|
|
arp_port->ap_sendpkt= NULL;
|
|
|
|
assert(data);
|
|
|
|
bf_afree(data); data= NULL;
|
|
|
|
|
2005-04-21 16:53:53 +02:00
|
|
|
result= (int)offset;
|
|
|
|
if (result<0)
|
|
|
|
{
|
|
|
|
DIFBLOCK(1, (result != NW_SUSPEND),
|
|
|
|
printf(
|
2005-06-28 17:19:58 +02:00
|
|
|
"arp[%d]: write error on port %d: error %d\n",
|
|
|
|
fd, arp_port->ap_eth_fd, result));
|
2005-04-21 16:53:53 +02:00
|
|
|
|
|
|
|
arp_port->ap_state= APS_ERROR;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
arp_port->ap_flags &= ~APF_ARP_WR_IP;
|
|
|
|
if (arp_port->ap_flags & APF_ARP_WR_SP)
|
|
|
|
setup_write(arp_port);
|
|
|
|
return NW_OK;
|
|
|
|
}
|
|
|
|
assert (offset+count <= sizeof(arp46_t));
|
2005-06-28 17:19:58 +02:00
|
|
|
data= arp_port->ap_sendpkt;
|
|
|
|
assert(data);
|
|
|
|
data= bf_cut(data, offset, count);
|
|
|
|
|
2005-04-21 16:53:53 +02:00
|
|
|
return data;
|
|
|
|
default:
|
|
|
|
printf("arp_getdata(%d, 0x%d, 0x%d) called but ap_state=0x%x\n",
|
|
|
|
fd, offset, count, arp_port->ap_state);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
PRIVATE int arp_putdata (fd, offset, data, for_ioctl)
|
|
|
|
int fd;
|
|
|
|
size_t offset;
|
|
|
|
acc_t *data;
|
|
|
|
int for_ioctl;
|
|
|
|
{
|
|
|
|
arp_port_t *arp_port;
|
|
|
|
int result;
|
|
|
|
struct nwio_ethstat *ethstat;
|
2005-06-28 17:19:58 +02:00
|
|
|
ev_arg_t ev_arg;
|
|
|
|
acc_t *tmpacc;
|
2005-04-21 16:53:53 +02:00
|
|
|
|
|
|
|
arp_port= &arp_port_table[fd];
|
|
|
|
|
|
|
|
if (arp_port->ap_flags & APF_ARP_RD_IP)
|
|
|
|
{
|
|
|
|
if (!data)
|
|
|
|
{
|
|
|
|
result= (int)offset;
|
|
|
|
if (result<0)
|
|
|
|
{
|
|
|
|
DIFBLOCK(1, (result != NW_SUSPEND), printf(
|
2005-06-28 17:19:58 +02:00
|
|
|
"arp[%d]: read error on port %d: error %d\n",
|
|
|
|
fd, arp_port->ap_eth_fd, result));
|
2005-04-21 16:53:53 +02:00
|
|
|
|
|
|
|
return NW_OK;
|
|
|
|
}
|
|
|
|
if (arp_port->ap_flags & APF_ARP_RD_SP)
|
|
|
|
{
|
|
|
|
arp_port->ap_flags &= ~(APF_ARP_RD_IP|
|
|
|
|
APF_ARP_RD_SP);
|
|
|
|
setup_read(arp_port);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
arp_port->ap_flags &= ~(APF_ARP_RD_IP|
|
|
|
|
APF_ARP_RD_SP);
|
|
|
|
return NW_OK;
|
|
|
|
}
|
|
|
|
assert (!offset);
|
|
|
|
/* Warning: the above assertion is illegal; puts and gets of
|
|
|
|
data can be brokenup in any piece the server likes. However
|
|
|
|
we assume that the server is eth.c and it transfers only
|
2005-06-28 17:19:58 +02:00
|
|
|
whole packets.
|
|
|
|
*/
|
2005-04-21 16:53:53 +02:00
|
|
|
data= bf_packIffLess(data, sizeof(arp46_t));
|
|
|
|
if (data->acc_length >= sizeof(arp46_t))
|
2005-06-28 17:19:58 +02:00
|
|
|
{
|
|
|
|
if (!arp_port->ap_reclist)
|
|
|
|
{
|
|
|
|
ev_arg.ev_ptr= arp_port;
|
|
|
|
ev_enqueue(&arp_port->ap_event, do_reclist,
|
|
|
|
ev_arg);
|
|
|
|
}
|
|
|
|
if (data->acc_linkC != 1)
|
|
|
|
{
|
|
|
|
tmpacc= bf_dupacc(data);
|
|
|
|
bf_afree(data);
|
|
|
|
data= tmpacc;
|
|
|
|
tmpacc= NULL;
|
|
|
|
}
|
|
|
|
data->acc_ext_link= arp_port->ap_reclist;
|
|
|
|
arp_port->ap_reclist= data;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
bf_afree(data);
|
2005-04-21 16:53:53 +02:00
|
|
|
return NW_OK;
|
|
|
|
}
|
|
|
|
switch (arp_port->ap_state)
|
|
|
|
{
|
|
|
|
case APS_GETADDR:
|
|
|
|
if (!data)
|
|
|
|
{
|
|
|
|
result= (int)offset;
|
|
|
|
if (result<0)
|
|
|
|
{
|
|
|
|
arp_port->ap_state= APS_ERROR;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (arp_port->ap_flags & APF_SUSPEND)
|
|
|
|
{
|
|
|
|
arp_port->ap_flags &= ~APF_SUSPEND;
|
|
|
|
arp_main(arp_port);
|
|
|
|
}
|
|
|
|
return NW_OK;
|
|
|
|
}
|
|
|
|
compare (bf_bufsize(data), ==, sizeof(*ethstat));
|
|
|
|
data= bf_packIffLess(data, sizeof(*ethstat));
|
|
|
|
compare (data->acc_length, ==, sizeof(*ethstat));
|
|
|
|
ethstat= (struct nwio_ethstat *)ptr2acc_data(data);
|
|
|
|
arp_port->ap_ethaddr= ethstat->nwes_addr;
|
|
|
|
bf_afree(data);
|
|
|
|
return NW_OK;
|
|
|
|
default:
|
|
|
|
printf("arp_putdata(%d, 0x%d, 0x%lx) called but ap_state=0x%x\n",
|
|
|
|
fd, offset, (unsigned long)data, arp_port->ap_state);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
return EGENERIC;
|
|
|
|
}
|
|
|
|
|
|
|
|
PRIVATE void setup_read(arp_port)
|
|
|
|
arp_port_t *arp_port;
|
|
|
|
{
|
|
|
|
int result;
|
|
|
|
|
|
|
|
while (!(arp_port->ap_flags & APF_ARP_RD_IP))
|
|
|
|
{
|
|
|
|
arp_port->ap_flags |= APF_ARP_RD_IP;
|
|
|
|
result= eth_read (arp_port->ap_eth_fd, ETH_MAX_PACK_SIZE);
|
|
|
|
if (result == NW_SUSPEND)
|
|
|
|
{
|
|
|
|
arp_port->ap_flags |= APF_ARP_RD_SP;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
DIFBLOCK(1, (result != NW_OK),
|
2005-06-28 17:19:58 +02:00
|
|
|
printf("arp[%d]: eth_read(..,%d)=%d\n",
|
|
|
|
arp_port-arp_port_table, ETH_MAX_PACK_SIZE, result));
|
2005-04-21 16:53:53 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
PRIVATE void setup_write(arp_port)
|
|
|
|
arp_port_t *arp_port;
|
|
|
|
{
|
2005-06-28 17:19:58 +02:00
|
|
|
int result;
|
|
|
|
acc_t *data;
|
2005-04-21 16:53:53 +02:00
|
|
|
|
2005-06-28 17:19:58 +02:00
|
|
|
for(;;)
|
2005-04-21 16:53:53 +02:00
|
|
|
{
|
2005-06-28 17:19:58 +02:00
|
|
|
data= arp_port->ap_sendlist;
|
|
|
|
if (!data)
|
|
|
|
break;
|
|
|
|
arp_port->ap_sendlist= data->acc_ext_link;
|
|
|
|
|
|
|
|
if (arp_port->ap_ipaddr == HTONL(0x00000000))
|
2005-04-21 16:53:53 +02:00
|
|
|
{
|
2005-06-28 17:19:58 +02:00
|
|
|
/* Interface is down */
|
|
|
|
printf(
|
|
|
|
"arp[%d]: not sending ARP packet, interface is down\n",
|
|
|
|
arp_port-arp_port_table);
|
|
|
|
bf_afree(data); data= NULL;
|
|
|
|
continue;
|
2005-04-21 16:53:53 +02:00
|
|
|
}
|
|
|
|
|
2005-06-28 17:19:58 +02:00
|
|
|
assert(!arp_port->ap_sendpkt);
|
|
|
|
arp_port->ap_sendpkt= data; data= NULL;
|
|
|
|
|
2005-04-21 16:53:53 +02:00
|
|
|
arp_port->ap_flags= (arp_port->ap_flags & ~APF_ARP_WR_SP) |
|
|
|
|
APF_ARP_WR_IP;
|
|
|
|
result= eth_write(arp_port->ap_eth_fd, sizeof(arp46_t));
|
|
|
|
if (result == NW_SUSPEND)
|
2005-06-28 17:19:58 +02:00
|
|
|
{
|
2005-04-21 16:53:53 +02:00
|
|
|
arp_port->ap_flags |= APF_ARP_WR_SP;
|
2005-06-28 17:19:58 +02:00
|
|
|
break;
|
|
|
|
}
|
2005-04-21 16:53:53 +02:00
|
|
|
if (result<0)
|
|
|
|
{
|
|
|
|
DIFBLOCK(1, (result != NW_SUSPEND),
|
2005-06-28 17:19:58 +02:00
|
|
|
printf("arp[%d]: eth_write(..,%d)=%d\n",
|
|
|
|
arp_port-arp_port_table, sizeof(arp46_t),
|
|
|
|
result));
|
2005-04-21 16:53:53 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2005-06-28 17:19:58 +02:00
|
|
|
PRIVATE void do_reclist(ev, ev_arg)
|
|
|
|
event_t *ev;
|
|
|
|
ev_arg_t ev_arg;
|
|
|
|
{
|
|
|
|
arp_port_t *arp_port;
|
|
|
|
acc_t *data;
|
|
|
|
|
|
|
|
arp_port= ev_arg.ev_ptr;
|
|
|
|
assert(ev == &arp_port->ap_event);
|
|
|
|
|
|
|
|
while (data= arp_port->ap_reclist, data != NULL)
|
|
|
|
{
|
|
|
|
arp_port->ap_reclist= data->acc_ext_link;
|
|
|
|
process_arp_pkt(arp_port, data);
|
|
|
|
bf_afree(data);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
PRIVATE void process_arp_pkt (arp_port, data)
|
2005-04-21 16:53:53 +02:00
|
|
|
arp_port_t *arp_port;
|
|
|
|
acc_t *data;
|
|
|
|
{
|
2005-06-28 17:19:58 +02:00
|
|
|
int i, entry, do_reply;
|
2005-04-21 16:53:53 +02:00
|
|
|
arp46_t *arp;
|
2005-06-28 17:19:58 +02:00
|
|
|
u16_t *p;
|
|
|
|
arp_cache_t *ce, *cache;
|
|
|
|
struct arp_req *reqp;
|
2005-04-21 16:53:53 +02:00
|
|
|
time_t curr_time;
|
|
|
|
ipaddr_t spa, tpa;
|
|
|
|
|
|
|
|
curr_time= get_time();
|
|
|
|
|
|
|
|
arp= (arp46_t *)ptr2acc_data(data);
|
|
|
|
memcpy(&spa, arp->a46_spa, sizeof(ipaddr_t));
|
|
|
|
memcpy(&tpa, arp->a46_tpa, sizeof(ipaddr_t));
|
|
|
|
|
|
|
|
if (arp->a46_hdr != HTONS(ARP_ETHERNET) ||
|
|
|
|
arp->a46_hln != 6 ||
|
|
|
|
arp->a46_pro != HTONS(ETH_IP_PROTO) ||
|
|
|
|
arp->a46_pln != 4)
|
|
|
|
return;
|
2005-06-28 17:19:58 +02:00
|
|
|
if (arp_port->ap_ipaddr == HTONL(0x00000000))
|
|
|
|
{
|
|
|
|
/* Interface is down */
|
|
|
|
#if DEBUG
|
|
|
|
printf("arp[%d]: dropping ARP packet, interface is down\n",
|
|
|
|
arp_port-arp_port_table);
|
|
|
|
#endif
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2005-04-21 16:53:53 +02:00
|
|
|
ce= find_cache_ent(arp_port, spa);
|
2005-06-28 17:19:58 +02:00
|
|
|
cache= NULL; /* lint */
|
|
|
|
|
|
|
|
do_reply= 0;
|
|
|
|
if (arp->a46_op != HTONS(ARP_REQUEST))
|
|
|
|
; /* No need to reply */
|
|
|
|
else if (tpa == arp_port->ap_ipaddr)
|
|
|
|
do_reply= 1;
|
|
|
|
else
|
2005-04-21 16:53:53 +02:00
|
|
|
{
|
2005-06-28 17:19:58 +02:00
|
|
|
/* Look for a published entry */
|
|
|
|
cache= find_cache_ent(arp_port, tpa);
|
|
|
|
if (cache)
|
|
|
|
{
|
|
|
|
if (cache->ac_flags & ACF_PUB)
|
|
|
|
{
|
|
|
|
/* Published entry */
|
|
|
|
do_reply= 1;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/* Nothing to do */
|
|
|
|
cache= NULL;
|
|
|
|
}
|
|
|
|
}
|
2005-04-21 16:53:53 +02:00
|
|
|
}
|
2005-06-28 17:19:58 +02:00
|
|
|
|
2005-04-21 16:53:53 +02:00
|
|
|
if (ce == NULL)
|
|
|
|
{
|
2005-06-28 17:19:58 +02:00
|
|
|
if (!do_reply)
|
2005-04-21 16:53:53 +02:00
|
|
|
return;
|
|
|
|
|
2005-06-28 17:19:58 +02:00
|
|
|
DBLOCK(0x10, printf("arp[%d]: allocating entry for ",
|
|
|
|
arp_port-arp_port_table);
|
2005-04-21 16:53:53 +02:00
|
|
|
writeIpAddr(spa); printf("\n"));
|
|
|
|
|
2005-06-28 17:19:58 +02:00
|
|
|
ce= alloc_cache_ent(ACF_EMPTY);
|
2005-04-21 16:53:53 +02:00
|
|
|
ce->ac_flags= ACF_EMPTY;
|
|
|
|
ce->ac_state= ACS_VALID;
|
|
|
|
ce->ac_ethaddr= arp->a46_sha;
|
|
|
|
ce->ac_ipaddr= spa;
|
|
|
|
ce->ac_port= arp_port;
|
|
|
|
ce->ac_expire= curr_time+ARP_EXP_TIME;
|
|
|
|
ce->ac_lastuse= curr_time-ARP_INUSE_OFFSET; /* never used */
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ce->ac_state == ACS_INCOMPLETE || ce->ac_state == ACS_UNREACHABLE)
|
|
|
|
{
|
|
|
|
ce->ac_ethaddr= arp->a46_sha;
|
|
|
|
if (ce->ac_state == ACS_INCOMPLETE)
|
|
|
|
{
|
2005-06-28 17:19:58 +02:00
|
|
|
/* Find request entry */
|
|
|
|
entry= ce-arp_cache;
|
|
|
|
for (i= 0, reqp= arp_port->ap_req; i<AP_REQ_NR;
|
|
|
|
i++, reqp++)
|
|
|
|
{
|
|
|
|
if (reqp->ar_entry == entry)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
assert(i < AP_REQ_NR);
|
|
|
|
clck_untimer(&reqp->ar_timer);
|
|
|
|
reqp->ar_entry= -1;
|
|
|
|
|
2005-04-21 16:53:53 +02:00
|
|
|
ce->ac_state= ACS_VALID;
|
|
|
|
client_reply(arp_port, spa, &arp->a46_sha);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
ce->ac_state= ACS_VALID;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Update fields in the arp cache. */
|
|
|
|
if (memcmp(&ce->ac_ethaddr, &arp->a46_sha,
|
|
|
|
sizeof(ce->ac_ethaddr)) != 0)
|
|
|
|
{
|
2005-06-28 17:19:58 +02:00
|
|
|
printf("arp[%d]: ethernet address for IP address ",
|
|
|
|
arp_port-arp_port_table);
|
2005-04-21 16:53:53 +02:00
|
|
|
writeIpAddr(spa);
|
|
|
|
printf(" changed from ");
|
|
|
|
writeEtherAddr(&ce->ac_ethaddr);
|
|
|
|
printf(" to ");
|
|
|
|
writeEtherAddr(&arp->a46_sha);
|
|
|
|
printf("\n");
|
|
|
|
ce->ac_ethaddr= arp->a46_sha;
|
|
|
|
}
|
|
|
|
ce->ac_expire= curr_time+ARP_EXP_TIME;
|
|
|
|
|
2005-06-28 17:19:58 +02:00
|
|
|
if (do_reply)
|
2005-04-21 16:53:53 +02:00
|
|
|
{
|
2005-06-28 17:19:58 +02:00
|
|
|
data= bf_memreq(sizeof(arp46_t));
|
|
|
|
arp= (arp46_t *)ptr2acc_data(data);
|
|
|
|
|
|
|
|
/* Clear padding */
|
|
|
|
assert(sizeof(arp->a46_data.a46_dummy) % sizeof(*p) == 0);
|
|
|
|
for (i= 0, p= (u16_t *)arp->a46_data.a46_dummy;
|
|
|
|
i < sizeof(arp->a46_data.a46_dummy)/sizeof(*p);
|
|
|
|
i++, p++)
|
|
|
|
{
|
|
|
|
*p= 0xdead;
|
|
|
|
}
|
|
|
|
|
|
|
|
arp->a46_dstaddr= ce->ac_ethaddr;
|
|
|
|
arp->a46_hdr= HTONS(ARP_ETHERNET);
|
|
|
|
arp->a46_pro= HTONS(ETH_IP_PROTO);
|
|
|
|
arp->a46_hln= 6;
|
|
|
|
arp->a46_pln= 4;
|
|
|
|
|
|
|
|
arp->a46_op= htons(ARP_REPLY);
|
|
|
|
if (tpa == arp_port->ap_ipaddr)
|
|
|
|
{
|
|
|
|
arp->a46_sha= arp_port->ap_ethaddr;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
assert(cache);
|
|
|
|
arp->a46_sha= cache->ac_ethaddr;
|
|
|
|
}
|
|
|
|
memcpy (arp->a46_spa, &tpa, sizeof(ipaddr_t));
|
|
|
|
arp->a46_tha= ce->ac_ethaddr;
|
|
|
|
memcpy (arp->a46_tpa, &ce->ac_ipaddr, sizeof(ipaddr_t));
|
|
|
|
|
|
|
|
assert(data->acc_linkC == 1);
|
|
|
|
data->acc_ext_link= arp_port->ap_sendlist;
|
|
|
|
arp_port->ap_sendlist= data; data= NULL;
|
|
|
|
|
2005-04-21 16:53:53 +02:00
|
|
|
if (!(arp_port->ap_flags & APF_ARP_WR_IP))
|
|
|
|
setup_write(arp_port);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
PRIVATE void client_reply (arp_port, ipaddr, ethaddr)
|
|
|
|
arp_port_t *arp_port;
|
|
|
|
ipaddr_t ipaddr;
|
|
|
|
ether_addr_t *ethaddr;
|
|
|
|
{
|
|
|
|
(*arp_port->ap_arp_func)(arp_port->ap_ip_port, ipaddr, ethaddr);
|
|
|
|
}
|
|
|
|
|
|
|
|
PRIVATE arp_cache_t *find_cache_ent (arp_port, ipaddr)
|
|
|
|
arp_port_t *arp_port;
|
|
|
|
ipaddr_t ipaddr;
|
|
|
|
{
|
2005-06-28 17:19:58 +02:00
|
|
|
arp_cache_t *ce;
|
2005-04-21 16:53:53 +02:00
|
|
|
int i;
|
2005-06-28 17:19:58 +02:00
|
|
|
unsigned hash;
|
2005-04-21 16:53:53 +02:00
|
|
|
|
2005-06-28 17:19:58 +02:00
|
|
|
hash= (ipaddr >> 24) ^ (ipaddr >> 16) ^ (ipaddr >> 8) ^ ipaddr;
|
|
|
|
hash &= ARP_HASH_MASK;
|
|
|
|
|
|
|
|
ce= arp_hash[hash].ahe_row[0];
|
|
|
|
if (ce && ce->ac_ipaddr == ipaddr && ce->ac_port == arp_port &&
|
|
|
|
ce->ac_state != ACS_UNUSED)
|
|
|
|
{
|
|
|
|
return ce;
|
|
|
|
}
|
|
|
|
for (i= 1; i<ARP_HASH_WIDTH; i++)
|
2005-04-21 16:53:53 +02:00
|
|
|
{
|
2005-06-28 17:19:58 +02:00
|
|
|
ce= arp_hash[hash].ahe_row[i];
|
|
|
|
if (!ce || ce->ac_ipaddr != ipaddr || ce->ac_port != arp_port
|
|
|
|
|| ce->ac_state == ACS_UNUSED)
|
2005-04-21 16:53:53 +02:00
|
|
|
{
|
2005-06-28 17:19:58 +02:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
arp_hash[hash].ahe_row[i]= arp_hash[hash].ahe_row[0];
|
|
|
|
arp_hash[hash].ahe_row[0]= ce;
|
|
|
|
return ce;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (i=0, ce= arp_cache; i<arp_cache_nr; i++, ce++)
|
|
|
|
{
|
|
|
|
if (ce->ac_state != ACS_UNUSED &&
|
|
|
|
ce->ac_port == arp_port &&
|
|
|
|
ce->ac_ipaddr == ipaddr)
|
|
|
|
{
|
|
|
|
for (i= ARP_HASH_WIDTH-1; i>0; i--)
|
|
|
|
{
|
|
|
|
arp_hash[hash].ahe_row[i]=
|
|
|
|
arp_hash[hash].ahe_row[i-1];
|
|
|
|
}
|
|
|
|
assert(i == 0);
|
|
|
|
arp_hash[hash].ahe_row[0]= ce;
|
|
|
|
return ce;
|
2005-04-21 16:53:53 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2005-06-28 17:19:58 +02:00
|
|
|
PRIVATE arp_cache_t *alloc_cache_ent(flags)
|
|
|
|
int flags;
|
2005-04-21 16:53:53 +02:00
|
|
|
{
|
|
|
|
arp_cache_t *cache, *old;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
old= NULL;
|
2005-06-28 17:19:58 +02:00
|
|
|
for (i=0, cache= arp_cache; i<arp_cache_nr; i++, cache++)
|
2005-04-21 16:53:53 +02:00
|
|
|
{
|
|
|
|
if (cache->ac_state == ACS_UNUSED)
|
2005-06-28 17:19:58 +02:00
|
|
|
{
|
|
|
|
old= cache;
|
|
|
|
break;
|
|
|
|
}
|
2005-04-21 16:53:53 +02:00
|
|
|
if (cache->ac_state == ACS_INCOMPLETE)
|
|
|
|
continue;
|
2005-06-28 17:19:58 +02:00
|
|
|
if (cache->ac_flags & ACF_PERM)
|
|
|
|
continue;
|
2005-04-21 16:53:53 +02:00
|
|
|
if (!old || cache->ac_lastuse < old->ac_lastuse)
|
|
|
|
old= cache;
|
|
|
|
}
|
|
|
|
assert(old);
|
2005-06-28 17:19:58 +02:00
|
|
|
|
|
|
|
if (!flags)
|
|
|
|
return old;
|
|
|
|
|
|
|
|
/* Get next permanent entry */
|
|
|
|
for (i=0, cache= arp_cache; i<arp_cache_nr; i++, cache++)
|
|
|
|
{
|
|
|
|
if (cache->ac_state == ACS_UNUSED)
|
|
|
|
break;
|
|
|
|
if (cache->ac_flags & ACF_PERM)
|
|
|
|
continue;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (i >= arp_cache_nr/2)
|
|
|
|
return NULL; /* Too many entries */
|
|
|
|
if (cache != old)
|
|
|
|
{
|
|
|
|
assert(old > cache);
|
|
|
|
*old= *cache;
|
|
|
|
old= cache;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!(flags & ACF_PUB))
|
|
|
|
return old;
|
|
|
|
|
|
|
|
/* Get first nonpublished entry */
|
|
|
|
for (i=0, cache= arp_cache; i<arp_cache_nr; i++, cache++)
|
|
|
|
{
|
|
|
|
if (cache->ac_state == ACS_UNUSED)
|
|
|
|
break;
|
|
|
|
if (cache->ac_flags & ACF_PUB)
|
|
|
|
continue;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (cache != old)
|
|
|
|
{
|
|
|
|
assert(old > cache);
|
|
|
|
*old= *cache;
|
|
|
|
old= cache;
|
|
|
|
}
|
2005-04-21 16:53:53 +02:00
|
|
|
return old;
|
|
|
|
}
|
|
|
|
|
|
|
|
PUBLIC void arp_set_ipaddr (eth_port, ipaddr)
|
|
|
|
int eth_port;
|
|
|
|
ipaddr_t ipaddr;
|
|
|
|
{
|
|
|
|
arp_port_t *arp_port;
|
|
|
|
|
|
|
|
if (eth_port < 0 || eth_port >= eth_conf_nr)
|
|
|
|
return;
|
|
|
|
arp_port= &arp_port_table[eth_port];
|
|
|
|
|
|
|
|
arp_port->ap_ipaddr= ipaddr;
|
|
|
|
arp_port->ap_flags |= APF_INADDR_SET;
|
|
|
|
arp_port->ap_flags &= ~APF_SUSPEND;
|
|
|
|
if (arp_port->ap_state == APS_GETADDR)
|
|
|
|
arp_main(arp_port);
|
|
|
|
}
|
|
|
|
|
|
|
|
PUBLIC int arp_set_cb(eth_port, ip_port, arp_func)
|
|
|
|
int eth_port;
|
|
|
|
int ip_port;
|
|
|
|
arp_func_t arp_func;
|
|
|
|
{
|
|
|
|
int i;
|
2005-06-28 17:19:58 +02:00
|
|
|
arp_port_t *arp_port;
|
2005-04-21 16:53:53 +02:00
|
|
|
|
|
|
|
assert(eth_port >= 0);
|
|
|
|
if (eth_port >= eth_conf_nr)
|
|
|
|
return ENXIO;
|
|
|
|
|
|
|
|
arp_port= &arp_port_table[eth_port];
|
|
|
|
arp_port->ap_eth_port= eth_port;
|
|
|
|
arp_port->ap_ip_port= ip_port;
|
|
|
|
arp_port->ap_state= APS_INITIAL;
|
|
|
|
arp_port->ap_flags= APF_EMPTY;
|
|
|
|
arp_port->ap_arp_func= arp_func;
|
2005-06-28 17:19:58 +02:00
|
|
|
arp_port->ap_sendpkt= NULL;
|
|
|
|
arp_port->ap_sendlist= NULL;
|
|
|
|
arp_port->ap_reclist= NULL;
|
|
|
|
for (i= 0; i<AP_REQ_NR; i++)
|
|
|
|
arp_port->ap_req[i].ar_entry= -1;
|
|
|
|
ev_init(&arp_port->ap_event);
|
2005-04-21 16:53:53 +02:00
|
|
|
|
|
|
|
arp_main(arp_port);
|
|
|
|
|
|
|
|
return NW_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
PUBLIC int arp_ip_eth (eth_port, ipaddr, ethaddr)
|
|
|
|
int eth_port;
|
|
|
|
ipaddr_t ipaddr;
|
|
|
|
ether_addr_t *ethaddr;
|
|
|
|
{
|
2005-06-28 17:19:58 +02:00
|
|
|
int i, ref;
|
2005-04-21 16:53:53 +02:00
|
|
|
arp_port_t *arp_port;
|
2005-06-28 17:19:58 +02:00
|
|
|
struct arp_req *reqp;
|
2005-04-21 16:53:53 +02:00
|
|
|
arp_cache_t *ce;
|
|
|
|
time_t curr_time;
|
|
|
|
|
|
|
|
assert(eth_port >= 0 && eth_port < eth_conf_nr);
|
|
|
|
arp_port= &arp_port_table[eth_port];
|
|
|
|
assert(arp_port->ap_state == APS_ARPMAIN ||
|
2005-06-28 17:19:58 +02:00
|
|
|
(printf("arp[%d]: ap_state= %d\n", arp_port-arp_port_table,
|
|
|
|
arp_port->ap_state), 0));
|
2005-04-21 16:53:53 +02:00
|
|
|
|
|
|
|
curr_time= get_time();
|
|
|
|
|
|
|
|
ce= find_cache_ent (arp_port, ipaddr);
|
|
|
|
if (ce && ce->ac_expire < curr_time)
|
|
|
|
{
|
2005-06-28 17:19:58 +02:00
|
|
|
assert(ce->ac_state != ACS_INCOMPLETE);
|
|
|
|
|
|
|
|
/* Check whether there is enough space for an ARP
|
|
|
|
* request or not.
|
|
|
|
*/
|
|
|
|
for (i= 0, reqp= arp_port->ap_req; i<AP_REQ_NR; i++, reqp++)
|
|
|
|
{
|
|
|
|
if (reqp->ar_entry < 0)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (i < AP_REQ_NR)
|
|
|
|
{
|
|
|
|
/* Okay, expire this entry. */
|
|
|
|
ce->ac_state= ACS_UNUSED;
|
|
|
|
ce= NULL;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/* Continue using this entry for a while */
|
|
|
|
printf("arp[%d]: Overloaded! Keeping entry for ",
|
|
|
|
arp_port-arp_port_table);
|
|
|
|
writeIpAddr(ipaddr);
|
|
|
|
printf("\n");
|
|
|
|
ce->ac_expire= curr_time+ARP_NOTRCH_EXP_TIME;
|
|
|
|
}
|
2005-04-21 16:53:53 +02:00
|
|
|
}
|
|
|
|
if (ce)
|
|
|
|
{
|
|
|
|
/* Found an entry. This entry should be valid, unreachable
|
|
|
|
* or incomplete.
|
|
|
|
*/
|
|
|
|
ce->ac_lastuse= curr_time;
|
|
|
|
if (ce->ac_state == ACS_VALID)
|
|
|
|
{
|
|
|
|
*ethaddr= ce->ac_ethaddr;
|
|
|
|
return NW_OK;
|
|
|
|
}
|
|
|
|
if (ce->ac_state == ACS_UNREACHABLE)
|
2009-11-28 14:18:33 +01:00
|
|
|
return EHOSTUNREACH;
|
2005-04-21 16:53:53 +02:00
|
|
|
assert(ce->ac_state == ACS_INCOMPLETE);
|
2005-06-28 17:19:58 +02:00
|
|
|
|
2005-04-21 16:53:53 +02:00
|
|
|
return NW_SUSPEND;
|
|
|
|
}
|
|
|
|
|
2005-06-28 17:19:58 +02:00
|
|
|
/* Find an empty slot for an ARP request */
|
|
|
|
for (i= 0, reqp= arp_port->ap_req; i<AP_REQ_NR; i++, reqp++)
|
|
|
|
{
|
|
|
|
if (reqp->ar_entry < 0)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (i >= AP_REQ_NR)
|
2005-04-21 16:53:53 +02:00
|
|
|
{
|
2005-06-28 17:19:58 +02:00
|
|
|
/* We should be able to report that this ARP request
|
|
|
|
* cannot be accepted. At the moment we just return SUSPEND.
|
2005-04-21 16:53:53 +02:00
|
|
|
*/
|
|
|
|
return NW_SUSPEND;
|
|
|
|
}
|
2005-06-28 17:19:58 +02:00
|
|
|
ref= (eth_port*AP_REQ_NR + i);
|
|
|
|
|
|
|
|
ce= alloc_cache_ent(ACF_EMPTY);
|
2005-04-21 16:53:53 +02:00
|
|
|
ce->ac_flags= 0;
|
|
|
|
ce->ac_state= ACS_INCOMPLETE;
|
|
|
|
ce->ac_ipaddr= ipaddr;
|
|
|
|
ce->ac_port= arp_port;
|
|
|
|
ce->ac_expire= curr_time+ARP_EXP_TIME;
|
|
|
|
ce->ac_lastuse= curr_time;
|
2005-06-28 17:19:58 +02:00
|
|
|
|
|
|
|
reqp->ar_entry= ce-arp_cache;
|
|
|
|
reqp->ar_req_count= -1;
|
|
|
|
|
|
|
|
/* Send the first packet by expiring the timer */
|
|
|
|
clck_timer(&reqp->ar_timer, 1, arp_timeout, ref);
|
|
|
|
|
2005-04-21 16:53:53 +02:00
|
|
|
return NW_SUSPEND;
|
|
|
|
}
|
|
|
|
|
2005-06-28 17:19:58 +02:00
|
|
|
PUBLIC int arp_ioctl (eth_port, fd, req, get_userdata, put_userdata)
|
|
|
|
int eth_port;
|
2005-04-21 16:53:53 +02:00
|
|
|
int fd;
|
2005-06-28 17:19:58 +02:00
|
|
|
ioreq_t req;
|
|
|
|
get_userdata_t get_userdata;
|
|
|
|
put_userdata_t put_userdata;
|
|
|
|
{
|
|
|
|
arp_port_t *arp_port;
|
|
|
|
arp_cache_t *ce, *cache;
|
|
|
|
acc_t *data;
|
|
|
|
nwio_arp_t *arp_iop;
|
|
|
|
int entno, result, ac_flags;
|
|
|
|
u32_t flags;
|
|
|
|
ipaddr_t ipaddr;
|
|
|
|
time_t curr_time;
|
|
|
|
|
|
|
|
assert(eth_port >= 0 && eth_port < eth_conf_nr);
|
|
|
|
arp_port= &arp_port_table[eth_port];
|
|
|
|
assert(arp_port->ap_state == APS_ARPMAIN ||
|
|
|
|
(printf("arp[%d]: ap_state= %d\n", arp_port-arp_port_table,
|
|
|
|
arp_port->ap_state), 0));
|
|
|
|
|
|
|
|
switch(req)
|
|
|
|
{
|
|
|
|
case NWIOARPGIP:
|
|
|
|
data= (*get_userdata)(fd, 0, sizeof(*arp_iop), TRUE);
|
|
|
|
if (data == NULL)
|
|
|
|
return EFAULT;
|
|
|
|
data= bf_packIffLess(data, sizeof(*arp_iop));
|
|
|
|
arp_iop= (nwio_arp_t *)ptr2acc_data(data);
|
|
|
|
ipaddr= arp_iop->nwa_ipaddr;
|
|
|
|
ce= NULL; /* lint */
|
|
|
|
for (entno= 0; entno < arp_cache_nr; entno++)
|
|
|
|
{
|
|
|
|
ce= &arp_cache[entno];
|
|
|
|
if (ce->ac_state == ACS_UNUSED ||
|
|
|
|
ce->ac_port != arp_port)
|
|
|
|
{
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (ce->ac_ipaddr == ipaddr)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (entno == arp_cache_nr)
|
|
|
|
{
|
|
|
|
/* Also report the address of this interface */
|
|
|
|
if (ipaddr != arp_port->ap_ipaddr)
|
|
|
|
{
|
|
|
|
bf_afree(data);
|
|
|
|
return ENOENT;
|
|
|
|
}
|
|
|
|
arp_iop->nwa_entno= arp_cache_nr;
|
|
|
|
arp_iop->nwa_ipaddr= ipaddr;
|
|
|
|
arp_iop->nwa_ethaddr= arp_port->ap_ethaddr;
|
|
|
|
arp_iop->nwa_flags= NWAF_PERM | NWAF_PUB;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
arp_iop->nwa_entno= entno+1;
|
|
|
|
arp_iop->nwa_ipaddr= ce->ac_ipaddr;
|
|
|
|
arp_iop->nwa_ethaddr= ce->ac_ethaddr;
|
|
|
|
arp_iop->nwa_flags= 0;
|
|
|
|
if (ce->ac_state == ACS_INCOMPLETE)
|
|
|
|
arp_iop->nwa_flags |= NWAF_INCOMPLETE;
|
|
|
|
if (ce->ac_state == ACS_UNREACHABLE)
|
|
|
|
arp_iop->nwa_flags |= NWAF_DEAD;
|
|
|
|
if (ce->ac_flags & ACF_PERM)
|
|
|
|
arp_iop->nwa_flags |= NWAF_PERM;
|
|
|
|
if (ce->ac_flags & ACF_PUB)
|
|
|
|
arp_iop->nwa_flags |= NWAF_PUB;
|
|
|
|
}
|
|
|
|
|
|
|
|
result= (*put_userdata)(fd, 0, data, TRUE);
|
|
|
|
return result;
|
|
|
|
|
|
|
|
case NWIOARPGNEXT:
|
|
|
|
data= (*get_userdata)(fd, 0, sizeof(*arp_iop), TRUE);
|
|
|
|
if (data == NULL)
|
|
|
|
return EFAULT;
|
|
|
|
data= bf_packIffLess(data, sizeof(*arp_iop));
|
|
|
|
arp_iop= (nwio_arp_t *)ptr2acc_data(data);
|
|
|
|
entno= arp_iop->nwa_entno;
|
|
|
|
if (entno < 0)
|
|
|
|
entno= 0;
|
|
|
|
ce= NULL; /* lint */
|
|
|
|
for (; entno < arp_cache_nr; entno++)
|
|
|
|
{
|
|
|
|
ce= &arp_cache[entno];
|
|
|
|
if (ce->ac_state == ACS_UNUSED ||
|
|
|
|
ce->ac_port != arp_port)
|
|
|
|
{
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (entno == arp_cache_nr)
|
|
|
|
{
|
|
|
|
bf_afree(data);
|
|
|
|
return ENOENT;
|
|
|
|
}
|
|
|
|
arp_iop->nwa_entno= entno+1;
|
|
|
|
arp_iop->nwa_ipaddr= ce->ac_ipaddr;
|
|
|
|
arp_iop->nwa_ethaddr= ce->ac_ethaddr;
|
|
|
|
arp_iop->nwa_flags= 0;
|
|
|
|
if (ce->ac_state == ACS_INCOMPLETE)
|
|
|
|
arp_iop->nwa_flags |= NWAF_INCOMPLETE;
|
|
|
|
if (ce->ac_state == ACS_UNREACHABLE)
|
|
|
|
arp_iop->nwa_flags |= NWAF_DEAD;
|
|
|
|
if (ce->ac_flags & ACF_PERM)
|
|
|
|
arp_iop->nwa_flags |= NWAF_PERM;
|
|
|
|
if (ce->ac_flags & ACF_PUB)
|
|
|
|
arp_iop->nwa_flags |= NWAF_PUB;
|
|
|
|
|
|
|
|
result= (*put_userdata)(fd, 0, data, TRUE);
|
|
|
|
return result;
|
|
|
|
|
|
|
|
case NWIOARPSIP:
|
|
|
|
data= (*get_userdata)(fd, 0, sizeof(*arp_iop), TRUE);
|
|
|
|
if (data == NULL)
|
|
|
|
return EFAULT;
|
|
|
|
data= bf_packIffLess(data, sizeof(*arp_iop));
|
|
|
|
arp_iop= (nwio_arp_t *)ptr2acc_data(data);
|
|
|
|
ipaddr= arp_iop->nwa_ipaddr;
|
|
|
|
if (find_cache_ent(arp_port, ipaddr))
|
|
|
|
{
|
|
|
|
bf_afree(data);
|
|
|
|
return EEXIST;
|
|
|
|
}
|
|
|
|
|
|
|
|
flags= arp_iop->nwa_flags;
|
|
|
|
ac_flags= ACF_EMPTY;
|
|
|
|
if (flags & NWAF_PERM)
|
|
|
|
ac_flags |= ACF_PERM;
|
|
|
|
if (flags & NWAF_PUB)
|
|
|
|
ac_flags |= ACF_PUB|ACF_PERM;
|
|
|
|
|
|
|
|
/* Allocate a cache entry */
|
|
|
|
ce= alloc_cache_ent(ac_flags);
|
|
|
|
if (ce == NULL)
|
|
|
|
{
|
|
|
|
bf_afree(data);
|
|
|
|
return ENOMEM;
|
|
|
|
}
|
|
|
|
|
|
|
|
ce->ac_flags= ac_flags;
|
|
|
|
ce->ac_state= ACS_VALID;
|
|
|
|
ce->ac_ethaddr= arp_iop->nwa_ethaddr;
|
|
|
|
ce->ac_ipaddr= arp_iop->nwa_ipaddr;
|
|
|
|
ce->ac_port= arp_port;
|
|
|
|
|
|
|
|
curr_time= get_time();
|
|
|
|
ce->ac_expire= curr_time+ARP_EXP_TIME;
|
|
|
|
ce->ac_lastuse= curr_time;
|
|
|
|
|
|
|
|
bf_afree(data);
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
case NWIOARPDIP:
|
|
|
|
data= (*get_userdata)(fd, 0, sizeof(*arp_iop), TRUE);
|
|
|
|
if (data == NULL)
|
|
|
|
return EFAULT;
|
|
|
|
data= bf_packIffLess(data, sizeof(*arp_iop));
|
|
|
|
arp_iop= (nwio_arp_t *)ptr2acc_data(data);
|
|
|
|
ipaddr= arp_iop->nwa_ipaddr;
|
|
|
|
bf_afree(data); data= NULL;
|
|
|
|
ce= find_cache_ent(arp_port, ipaddr);
|
|
|
|
if (!ce)
|
|
|
|
return ENOENT;
|
|
|
|
if (ce->ac_state == ACS_INCOMPLETE)
|
|
|
|
return EINVAL;
|
|
|
|
|
|
|
|
ac_flags= ce->ac_flags;
|
|
|
|
if (ac_flags & ACF_PUB)
|
|
|
|
{
|
|
|
|
/* Make sure entry is at the end of published
|
|
|
|
* entries.
|
|
|
|
*/
|
|
|
|
for (entno= 0, cache= arp_cache;
|
|
|
|
entno<arp_cache_nr; entno++, cache++)
|
|
|
|
{
|
|
|
|
if (cache->ac_state == ACS_UNUSED)
|
|
|
|
break;
|
|
|
|
if (cache->ac_flags & ACF_PUB)
|
|
|
|
continue;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
assert(cache > arp_cache);
|
|
|
|
cache--;
|
|
|
|
if (cache != ce)
|
|
|
|
{
|
|
|
|
assert(cache > ce);
|
|
|
|
*ce= *cache;
|
|
|
|
ce= cache;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (ac_flags & ACF_PERM)
|
|
|
|
{
|
|
|
|
/* Make sure entry is at the end of permanent
|
|
|
|
* entries.
|
|
|
|
*/
|
|
|
|
for (entno= 0, cache= arp_cache;
|
|
|
|
entno<arp_cache_nr; entno++, cache++)
|
|
|
|
{
|
|
|
|
if (cache->ac_state == ACS_UNUSED)
|
|
|
|
break;
|
|
|
|
if (cache->ac_flags & ACF_PERM)
|
|
|
|
continue;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
assert(cache > arp_cache);
|
|
|
|
cache--;
|
|
|
|
if (cache != ce)
|
|
|
|
{
|
|
|
|
assert(cache > ce);
|
|
|
|
*ce= *cache;
|
|
|
|
ce= cache;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Clear entry */
|
|
|
|
ce->ac_state= ACS_UNUSED;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
default:
|
|
|
|
ip_panic(("arp_ioctl: unknown request 0x%lx",
|
|
|
|
(unsigned long)req));
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
PRIVATE void arp_timeout (ref, timer)
|
|
|
|
int ref;
|
2005-04-21 16:53:53 +02:00
|
|
|
timer_t *timer;
|
|
|
|
{
|
2005-06-28 17:19:58 +02:00
|
|
|
int i, port, reqind, acind;
|
2005-04-21 16:53:53 +02:00
|
|
|
arp_port_t *arp_port;
|
|
|
|
arp_cache_t *ce;
|
2005-06-28 17:19:58 +02:00
|
|
|
struct arp_req *reqp;
|
2005-04-21 16:53:53 +02:00
|
|
|
time_t curr_time;
|
2005-06-28 17:19:58 +02:00
|
|
|
acc_t *data;
|
|
|
|
arp46_t *arp;
|
|
|
|
u16_t *p;
|
2005-04-21 16:53:53 +02:00
|
|
|
|
2005-06-28 17:19:58 +02:00
|
|
|
port= ref / AP_REQ_NR;
|
|
|
|
reqind= ref % AP_REQ_NR;
|
|
|
|
|
|
|
|
assert(port >= 0 && port <eth_conf_nr);
|
|
|
|
arp_port= &arp_port_table[port];
|
|
|
|
|
|
|
|
reqp= &arp_port->ap_req[reqind];
|
|
|
|
assert (timer == &reqp->ar_timer);
|
2005-04-21 16:53:53 +02:00
|
|
|
|
2005-06-28 17:19:58 +02:00
|
|
|
acind= reqp->ar_entry;
|
2005-04-21 16:53:53 +02:00
|
|
|
|
2005-06-28 17:19:58 +02:00
|
|
|
assert(acind >= 0 && acind < arp_cache_nr);
|
|
|
|
ce= &arp_cache[acind];
|
|
|
|
|
|
|
|
assert(ce->ac_port == arp_port);
|
|
|
|
assert(ce->ac_state == ACS_INCOMPLETE);
|
|
|
|
|
|
|
|
if (++reqp->ar_req_count >= MAX_ARP_RETRIES)
|
2005-04-21 16:53:53 +02:00
|
|
|
{
|
2005-06-28 17:19:58 +02:00
|
|
|
curr_time= get_time();
|
|
|
|
ce->ac_state= ACS_UNREACHABLE;
|
|
|
|
ce->ac_expire= curr_time+ ARP_NOTRCH_EXP_TIME;
|
|
|
|
ce->ac_lastuse= curr_time;
|
|
|
|
|
|
|
|
clck_untimer(&reqp->ar_timer);
|
|
|
|
reqp->ar_entry= -1;
|
|
|
|
client_reply(arp_port, ce->ac_ipaddr, NULL);
|
|
|
|
return;
|
2005-04-21 16:53:53 +02:00
|
|
|
}
|
2005-06-28 17:19:58 +02:00
|
|
|
|
|
|
|
data= bf_memreq(sizeof(arp46_t));
|
|
|
|
arp= (arp46_t *)ptr2acc_data(data);
|
|
|
|
|
|
|
|
/* Clear padding */
|
|
|
|
assert(sizeof(arp->a46_data.a46_dummy) % sizeof(*p) == 0);
|
|
|
|
for (i= 0, p= (u16_t *)arp->a46_data.a46_dummy;
|
|
|
|
i < sizeof(arp->a46_data.a46_dummy)/sizeof(*p);
|
|
|
|
i++, p++)
|
2005-04-21 16:53:53 +02:00
|
|
|
{
|
2005-06-28 17:19:58 +02:00
|
|
|
*p= 0xdead;
|
|
|
|
}
|
2005-04-21 16:53:53 +02:00
|
|
|
|
2005-06-28 17:19:58 +02:00
|
|
|
arp->a46_dstaddr.ea_addr[0]= 0xff;
|
|
|
|
arp->a46_dstaddr.ea_addr[1]= 0xff;
|
|
|
|
arp->a46_dstaddr.ea_addr[2]= 0xff;
|
|
|
|
arp->a46_dstaddr.ea_addr[3]= 0xff;
|
|
|
|
arp->a46_dstaddr.ea_addr[4]= 0xff;
|
|
|
|
arp->a46_dstaddr.ea_addr[5]= 0xff;
|
|
|
|
arp->a46_hdr= HTONS(ARP_ETHERNET);
|
|
|
|
arp->a46_pro= HTONS(ETH_IP_PROTO);
|
|
|
|
arp->a46_hln= 6;
|
|
|
|
arp->a46_pln= 4;
|
|
|
|
arp->a46_op= HTONS(ARP_REQUEST);
|
|
|
|
arp->a46_sha= arp_port->ap_ethaddr;
|
|
|
|
memcpy (arp->a46_spa, &arp_port->ap_ipaddr, sizeof(ipaddr_t));
|
|
|
|
memset(&arp->a46_tha, '\0', sizeof(ether_addr_t));
|
|
|
|
memcpy (arp->a46_tpa, &ce->ac_ipaddr, sizeof(ipaddr_t));
|
|
|
|
|
|
|
|
assert(data->acc_linkC == 1);
|
|
|
|
data->acc_ext_link= arp_port->ap_sendlist;
|
|
|
|
arp_port->ap_sendlist= data; data= NULL;
|
|
|
|
|
|
|
|
if (!(arp_port->ap_flags & APF_ARP_WR_IP))
|
|
|
|
setup_write(arp_port);
|
|
|
|
|
|
|
|
clck_timer(&reqp->ar_timer, get_time() + ARP_TIMEOUT,
|
|
|
|
arp_timeout, ref);
|
|
|
|
}
|
|
|
|
|
|
|
|
PRIVATE void arp_buffree(priority)
|
|
|
|
int priority;
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
acc_t *pack, *next_pack;
|
|
|
|
arp_port_t *arp_port;
|
|
|
|
|
|
|
|
for (i= 0, arp_port= arp_port_table; i<eth_conf_nr; i++, arp_port++)
|
|
|
|
{
|
|
|
|
if (priority == ARP_PRI_REC)
|
|
|
|
{
|
|
|
|
next_pack= arp_port->ap_reclist;
|
|
|
|
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(&arp_port->ap_event))
|
|
|
|
{
|
|
|
|
DBLOCK(1, printf(
|
|
|
|
"not freeing ap_reclist, ap_event enqueued\n"));
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
bf_afree(next_pack);
|
|
|
|
next_pack= NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
arp_port->ap_reclist= next_pack;
|
|
|
|
}
|
|
|
|
if (priority == ARP_PRI_SEND)
|
|
|
|
{
|
|
|
|
next_pack= arp_port->ap_sendlist;
|
|
|
|
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(&arp_port->ap_event))
|
|
|
|
{
|
|
|
|
DBLOCK(1, printf(
|
|
|
|
"not freeing ap_sendlist, ap_event enqueued\n"));
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
bf_afree(next_pack);
|
|
|
|
next_pack= NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
arp_port->ap_sendlist= next_pack;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef BUF_CONSISTENCY_CHECK
|
|
|
|
PRIVATE void arp_bufcheck()
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
arp_port_t *arp_port;
|
|
|
|
acc_t *pack;
|
|
|
|
|
|
|
|
for (i= 0, arp_port= arp_port_table; i<eth_conf_nr; i++, arp_port++)
|
|
|
|
{
|
2006-02-16 15:13:36 +01:00
|
|
|
for (pack= arp_port->ap_reclist; pack;
|
|
|
|
pack= pack->acc_ext_link)
|
|
|
|
{
|
|
|
|
bf_check_acc(pack);
|
|
|
|
}
|
|
|
|
for (pack= arp_port->ap_sendlist; pack;
|
2005-06-28 17:19:58 +02:00
|
|
|
pack= pack->acc_ext_link)
|
|
|
|
{
|
|
|
|
bf_check_acc(pack);
|
2005-04-21 16:53:53 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2005-06-28 17:19:58 +02:00
|
|
|
#endif /* BUF_CONSISTENCY_CHECK */
|
2005-04-21 16:53:53 +02:00
|
|
|
|
|
|
|
/*
|
2005-06-28 17:19:58 +02:00
|
|
|
* $PchId: arp.c,v 1.22 2005/06/28 14:15:06 philip Exp $
|
2005-04-21 16:53:53 +02:00
|
|
|
*/
|