home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
The Datafile PD-CD 3
/
PDCD_3.iso
/
internet
/
tcpipsrc
/
ARP
/
c
/
ARP
next >
Wrap
Text File
|
1994-06-24
|
13KB
|
364 lines
/* Address Resolution Protocol (ARP) functions. Sits between IP and
* Level 2, mapping IP to Level 2 addresses for all outgoing datagrams.
*/
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include "global.h"
#include "mbuf.h"
#include "timer.h"
#include "ip.h"
#include "misc.h"
#include "icmp.h"
#include "iface.h"
#include "ax25.h"
#include "arp.h"
static void arp_output(struct interface *, int16, int32);
static unsigned arp_hash(int16, int32);
extern int32 ip_addr; /* Our IP address */
/* ARP entries for particular subnetwork types. The table values
* are filled in by calls to arp_init() at device attach time
*/
#define NTYPES 9
struct arp_type arp_type[NTYPES];
/* Hash table headers */
struct arp_tab *arp_tab[ARPSIZE];
struct arp_stat arp_stat;
/* Initialize an entry in the ARP table
* Called by the device driver at attach time
*/
int arp_init(unsigned int hwtype, int hwalen, int iptype, int arptype,
char *bdcst, int (*format)(), int (*scan)())
{
register struct arp_type *at;
if(hwtype >= NTYPES)
return -1; /* Table too small */
at = &arp_type[hwtype];
at->hwalen = (int16)hwalen;
at->iptype = (int16)iptype;
at->arptype = (int16)arptype;
at->bdcst = bdcst;
at->format = format;
at->scan = scan;
return 0;
}
/* Resolve an IP address to a hardware address; if not found,
* initiate query and return NULLCHAR. If an address is returned, the
* interface driver may send the packet; if NULLCHAR is returned,
* res_arp() will have saved the packet on its pending queue,
* so no further action (like freeing the packet) is necessary.
*/
char *res_arp(struct interface *interface, int16 hardware, int32 target, struct mbuf *bp)
{
register struct arp_tab *arp;
struct ip ip;
if((arp = arp_lookup(hardware,target)) != NULLARP && arp->state == ARP_VALID)
return arp->hw_addr;
if(arp != NULLARP){
/* Earlier packets are already pending, kick this one back
* as a source quench
*/
ntohip(&ip,&bp);
icmp_output(&ip,bp,QUENCH,0,NULLICMP);
free_p(bp);
} else {
/* Create an entry and put the datagram on the
* queue pending an answer
*/
arp = arp_add(target,hardware,NULLCHAR,0,0);
enqueue(&arp->pending,bp);
arp_output(interface,hardware,target);
}
return NULLCHAR;
}
/* Handle incoming ARP packets. This is almost a direct implementation of
* the algorithm on page 5 of RFC 826, except for:
* 1. Outgoing datagrams to unresolved addresses are kept on a queue
* pending a reply to our ARP request.
* 2. The names of the fields in the ARP packet were made more mnemonic.
* 3. Requests for IP addresses listed in our table as "published" are
* responded to, even if the address is not our own.
*/
void arp_input(struct interface *interface, struct mbuf *bp)
{
struct arp arp;
struct arp_tab *ap;
struct arp_type *at;
arp_stat.recv++;
if(ntoharp(&arp,&bp) == -1) /* Convert into host format */
return;
if(arp.hardware >= NTYPES){
/* Unknown hardware type, ignore */
arp_stat.badtype++;
return;
}
at = &arp_type[arp.hardware];
if(arp.protocol != at->iptype){
/* Unsupported protocol type, ignore */
arp_stat.badtype++;
return;
}
if(uchar(arp.hwalen) > MAXHWALEN || uchar(arp.pralen) != sizeof(int32)){
/* Incorrect protocol addr length (different hw addr lengths
* are OK since AX.25 addresses can be of variable length)
*/
arp_stat.badlen++;
return;
}
if(memcmp(arp.shwaddr,at->bdcst,at->hwalen) == 0){
/* This guy is trying to say he's got the broadcast address! */
arp_stat.badaddr++;
return;
}
/* If this guy is already in the table, update its entry
* unless it's a manual entry (noted by the lack of a timer)
*/
ap = NULLARP; /* ap plays the role of merge_flag in the spec */
if((ap = arp_lookup(arp.hardware,arp.sprotaddr)) != NULLARP
&& ap->timer.start != 0){
ap = arp_add(arp.sprotaddr,arp.hardware,arp.shwaddr,uchar(arp.hwalen),0);
}
/* See if we're the address they're looking for */
if(arp.tprotaddr == ip_addr){
if(ap == NULLARP) /* Only if not already in the table */
arp_add(arp.sprotaddr,arp.hardware,arp.shwaddr,uchar(arp.hwalen),0);
if(arp.opcode == ARP_REQUEST){
/* Swap sender's and target's (us) hardware and protocol
* fields, and send the packet back as a reply
*/
memcpy(arp.thwaddr,arp.shwaddr,(int16)uchar(arp.hwalen));
/* Mark the end of the sender's AX.25 address
* in case he didn't
*/
if(arp.hardware == ARP_AX25)
arp.thwaddr[uchar(arp.hwalen)-1] |= E;
memcpy(arp.shwaddr,interface->hwaddr,at->hwalen);
arp.tprotaddr = arp.sprotaddr;
arp.sprotaddr = ip_addr;
arp.opcode = ARP_REPLY;
if((bp = htonarp(&arp)) == NULLBUF)
return;
if(interface->forw != NULLIF)
(*interface->forw->output)(interface->forw,
arp.thwaddr,interface->forw->hwaddr,at->arptype,bp);
else
(*interface->output)(interface,arp.thwaddr,
interface->hwaddr,at->arptype,bp);
arp_stat.inreq++;
} else {
arp_stat.replies++;
}
} else if(arp.opcode == ARP_REQUEST
&& (ap = arp_lookup(arp.hardware,arp.tprotaddr)) != NULLARP
&& ap->pub){
/* Otherwise, respond if the guy he's looking for is
* published in our table.
*/
memcpy(arp.thwaddr,arp.shwaddr,(int16)uchar(arp.hwalen));
/* Mark the end of the sender's AX.25 address
* in case he didn't
*/
if(arp.hardware == ARP_AX25)
arp.thwaddr[uchar(arp.hwalen)-1] |= E;
memcpy(arp.shwaddr,ap->hw_addr,at->hwalen);
arp.tprotaddr = arp.sprotaddr;
arp.sprotaddr = ap->ip_addr;
arp.opcode = ARP_REPLY;
if((bp = htonarp(&arp)) == NULLBUF)
return;
if(interface->forw != NULLIF)
(*interface->forw->output)(interface->forw,
arp.thwaddr,interface->forw->hwaddr,at->arptype,bp);
else
(*interface->output)(interface,arp.thwaddr,
interface->hwaddr,at->arptype,bp);
arp_stat.inreq++;
}
}
/* Add an IP-addr / hardware-addr pair to the ARP table */
struct arp_tab *
arp_add(int32 ipaddr, int16 hardware, char *hw_addr, int16 hw_alen, int pub)
{
struct mbuf *bp;
register struct arp_tab *ap;
unsigned hashval;
if((ap = arp_lookup(hardware,ipaddr)) == NULLARP){
/* New entry */
if((ap = (struct arp_tab *)calloc(1,sizeof(struct arp_tab))) == NULLARP)
return NULLARP;
ap->timer.func = arp_drop;
ap->timer.arg = (char *)ap;
ap->hardware = hardware;
ap->ip_