/* Address Resolution Protocol (ARP) functions. Sits between IP and * Level 2, mapping IP to Level 2 addresses for all outgoing datagrams. */ #include "global.h" #include "mbuf.h" #include "timer.h" #include "iface.h" #ifndef ATARI_ST /* DG2KK */ #include "enet.h" #endif #include "ax25.h" #include "arp.h" 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 */ arp_init(hwtype,hwalen,iptype,arptype,bdcst,format,scan) unsigned int hwtype; /* ARP Hardware type */ int hwalen; /* Hardware address length */ int iptype; /* Subnet's protocol ID for IP */ int arptype; /* Subnet's protocol ID for ARP */ char *bdcst; /* Subnet's broadcast address (if any) */ int (*format)(); /* Function to format hardware addresses */ int (*scan)(); /* Function to scan addresses in ascii */ { 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(interface,hardware,target,bp) struct interface *interface; /* Pointer to interface block */ int16 hardware; /* Hardware type */ int32 target; /* Target IP address */ struct mbuf *bp; /* IP datagram to be queued if unresolved */ { void arp_output(); register struct arp_tab *arp; if((arp = arp_lookup(hardware,target)) != NULLARP && arp->state == ARP_VALID) return arp->hw_addr; /* Create an entry and put the datagram on the * queue pending an answer */ arp = arp_add(target,hardware,NULLCHAR,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. */ void arp_input(interface,bp) struct interface *interface; struct mbuf *bp; { struct arp arp; struct arp_tab *ap; struct arp_type *at; struct mbuf *htonarp(); 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(arp.hwalen > MAXHWALEN || 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,arp.hwalen & 0xff); } /* 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,arp.hwalen & 0xff); 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,arp.hwalen); /* Mark the end of the sender's AX.25 address * in case he didn't */ if(arp.hardware == ARP_AX25) arp.thwaddr[arp.hwalen-1] |= E; memcpy(arp.shwaddr,inte2face->hwaddr,at->hwalen); arp.tprotaddr = arp.sprotaddr; arp.sprotaddr = ip_addr; arp.opcode = ARP_REPLY; bp = htonarp(&arp); (*interface->output)(interface,arp.thwaddr, interface->hwaddr,at->arptype,bp); arp_stat.inreq++; } else { arp_stat.replies++; } } } /* Add an IP-addr / hardware-addr pair to the ARP table */ struct arp_tab * arp_add(ipaddr,hardware,hw_addr,hw_alen) int32 ipaddr; /* IP address, host order */ int16 hardware; /* HardwareHardwareHardwareHardwareHardwarehtonarp(); struct arp_type *at; at = &arp_type[hardware]; if(interface->output == NULLFP) return; arp.hardware = hardware; arp.protocol = at->iptype; arp.hwalen = at->hwalen; arp.pralen = sizeof(int32); arp.opcode = ARP_REQUEST; memcpy(arp.shwaddr,interface->hwaddr,at->hwalen); arp.sprotaddr = ip_addr; memset(arp.thwaddr,0,at->hwalen); arp.tprotaddr = target; bp = htonarp(&arp); (*interface->output)(interface,at->bdcst, interface->hwaddr,at->arptype,bp); arp_stat.outreq++; } /* Hash a {hardware type, IP address} pair */ static unsigned arp_hash(hardware,ipaddr) int16 hardware; int32 ipaddr; { register unsigned hashval; hashval = hardware; hashval ^= hiword(ipaddr); hashval ^= loword(ipaddr); hashval %= ARPSIZE; return hashval; } /* Copy a host format arp structure into mbuf for transmission */ struct mbuf * htonarp(arp) register struct arp *arp; { struct mbuf *bp; register char *buf; if(arp == (struct arp *)0) return NULLBUF; if((bp = alloc_mbuf(sizeof(struct arp))) == NULLBUF) return NULLBUF; buf = bp->data; buf = put16(buf,arp->hardware); buf = put16(buf,arp->protocol); *buf++ = arp->hwalen; *buf++ = arp->pralen; buf = put16(buf,arp->opcode); memcpy(buf,arp->shwaddr,arp->hwalen); buf += arp->hwalen; buf = put32(buf,arp->sprotaddr); memcpy(buf,arp->thwaddr,arp->hwalen); buf += arp->hwalen; buf = put32(buf,arp->tprotaddr); bp->cnt = buf - bp->data; return bp; } /* Convert an incoming ARP packet into a host-format structure */ int ntoharp(arp,bpp) register struct arp *arp; struct mbuf **bpp; { if(arp == (struct arp *)0 || bpp == NULLBUFP) return -1; arp->hardware = pull16(bpp); arp->protocol = pull16(bpp); arp->hwalen = pullchar(bpp); arp->pralen = pullchar(bpp); arp->opcode = pull16(bpp); pullup(bpp,arp->shwaddr,arp->hwalen); arp->sprotaddr = pull32(bpp); pullup(bpp,arp->thwaddr,arp->hwalen); arp->tprotaddr = pull32(bpp); /* Get rid of anything left over */ free_p(*bpp); *bpp = NULLBUF; return 0; }