/* Address Resolution Protocol (ARP) functions. Sits between IP and * Level 2, mapping IP to Level 2 addresses for all outgoing datagrams. */ #include "machdep.h" #include "mbuf.h" #include "timer.h" #include "iface.h" #include "ether.h" #include "ax25.h" #include "arp.h" #include "cmdparse.h" extern int32 ip_addr; /* Our IP address */ int ec_output(); int pether(),gether(); int setcall(),psax25(); /* Table of ARP hardware types */ struct arp_type arp_type[] = { 0, 0, 0, NULLCHAR, NULLFP, NULLFP, /* 10 megabit Ethernet */ 6, /* Ethernet address length */ 0x800, /* Ethernet type field for IP */ 0x806, /* Ethernet type field for ARP */ ether_bdcst, /* Ethernet broadcast address */ pether, gether, /* 3 megabit Ethernet */ 0, 0, 0, NULLCHAR, NULLFP, NULLFP, /* AX.25 */ 7, /* AX.25 address length */ 0xCC, /* AX.25 pid field for IP */ 0xCD, /* AX.25 pid field for ARP */ (char *)&ax25_bdcst, /* AX.25 broadcast address */ psax25, setcall, }; #define NTYPES 4 /* Hash table headers */ struct arp_tab *arp_tab[ARPSIZE]; struct arp_stat arp_stat; /* 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 */ { struct arp_tab *arp_lookup(),*arp_add(); 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 *arp_lookup(),*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 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 */ bcopy(arp.shwaddr,arp.thwaddr,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; bcopy(interface->hwaddr,arp.shwaddr,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 */ static struct arp_tab * arp_add(ip_addr,hardware,hw_addr,hw_alen) int32 ip_addr; /* IP address, host order */ int16 hardware; /* Hardware type */ char *hw_addr; /* Hardware address, if known; NULLCHAR otherwise */ int16 hw_alen; /* Length of hardware address */ { char *calloc(),*malloc(); struct arp_tab *arp_lookup(); void arp_drop(); struct mbuf *bp,*dequeue(); register struct arp_tab *ap; register struct arp_type *at; unsigned hashval,arp_hash(); at = &arp_type[hardware]; if((ap = arp_lookup(hardware,ip_addr)) == 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 = (int *)ap; ap->hardware = hardware; ap->ip_addr = ip_addr; /* Put on head of hash chain */ hashval = arp_hash(hardware,ip_addr); ap->prev = NULLARP; ap->next = arp_tab[hashval]; arp_tab[hashval] = ap; if(ap->next != NULLARP){ ap->next->prev = ap; } } if(hw_addr == NULLCHAR){ /* Await response */ ap->state = ARP_PENDING; ap->timer.start = PENDTIME; } else { /* Response has come in, update entry and run through queue */ ap->state = ARP_VALID; ap->timer.start = ARPLIFE; if(ap->hw_addr != NULLCHAR) free(ap->hw_addr); if((ap->hw_addr = malloc(hw_alen)) == NULLCHAR){ free((char *)ap); return NULLARP; } bcopy(hw_addr,ap->hw_addr,hw_alen); /* This kludge marks the end of an AX.25 address to allow * for optional digipeaters (insert Joan Rivers salute here) */ if(hardware == ARP_AX25) ap->hw_addr[hw_alen-1] |= E; while((bp = dequeue(&ap->pending)) != NULLBUF) ip_route(bp,0); } start_timer(&ap->timer); return ap; } /* Remove an entry from the ARP table */ static void arp_drop(ap) register struct arp_tab *ap; { unsigned arp_hash(); if(ap == NULLARP) return; stop_timer(&ap->timer); /* Shouldn't be necessary */ if(ap->next != NULLARP) ap->next->prev = ap->prev; if(ap->prev != NULLARP) ap->prev->next = ap->next; else arp_tab[arp_hash(ap->hardware,ap->ip_addr)] = ap->next; if(ap->hw_addr != NULLCHAR) free(ap->hw_addr); free_q(&ap->pending); free((char *)ap); } /* Look up the given IP address in the ARP table */ static struct arp_tab * arp_lookup(hardware,ip_addr) int16 hardware; int32 ip_addr; { unsigned arp_hash(); register struct arp_tab *ap; for(ap = arp_tab[arp_hash(hardware,ip_addr)]; ap != NULLARP; ap = ap->next){ if(ap->ip_addr == ip_addr && ap->hardware == hardware) break; } return ap; } /* Send an ARP request to resolve IP address target_ip */ static void arp_output(interface,hardware,target) struct interface *interface; int16 hardware; int32 target; { struct arp arp; struct mbuf *bp,*htonarp(); 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; bcopy(interface->hwaddr,arp.shwaddr,at->hwalen); arp.sprotaddr = ip_addr; bzero(arp.thwaddr,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,ip_addr) int16 hardware; int32 ip_addr; { register unsigned hashval; hashval = hardware; hashval ^= hiword(ip_addr); hashval ^= loword(ip_addr); hashval %= ARPSIZE; return hashval; } /* Copy a host format arp structure into mbuf for transmission */ #ifdef AMIGA /* * We play some dirty tricks here. Since the AMIGA is a 68000 based * machine, it doesn't take kindly to doing word and long word stores * on odd address boundaries. We'll use bcopy() instead. We can do * this simply because the 68000 is a big-endian machine, and we don't * need to convert to network byte order. This is ugly. */ #endif static struct mbuf * htonarp(arp) register struct arp *arp; { struct mbuf *bp; register char *buf; if(arp == (struct arp *)NULL) return NULLBUF; if((bp = alloc_mbuf(sizeof(struct arp))) == NULLBUF) return NULLBUF; buf = bp->data; *(int16 *)buf = htons(arp->hardware); buf += sizeof(int16); *(int16 *)buf = htons(arp->protocol); buf += sizeof(int16); *buf++ = arp->hwalen; *buf++ = arp->pralen; *(int16 *)buf = htons(arp->opcode); buf += sizeof(int16); bcopy(arp->shwaddr,buf,arp->hwalen); buf += arp->hwalen; #ifndef AMIGA *(int32 *)buf = htonl(arp->sprotaddr); #else /* we've been alright up to now, but arp->hwalen may have been odd, so we don't know if buf is word aligned any more! */ bcopy(&arp->sprotaddr, buf, sizeof(int32)); #endif buf += sizeof(int32); bcopy(arp->thwaddr,buf,arp->hwalen); buf += arp->hwalen; #ifndef AMIGA *(int32 *)buf = htonl(arp->tprotaddr); #else bcopy(&arp->tprotaddr, buf, sizeof(int32)); #endif buf += sizeof(int32); bp->cnt = buf - bp->data; return bp; } /* Convert an incoming ARP packet into a host-format structure */ static int ntoharp(arp,bp) register struct arp *arp; struct mbuf *bp; { if(arp == (struct arp *)NULL || bp == NULLBUF) return -1; pullup(&bp,(char *)&arp->hardware,sizeof(int16)); arp->hardware = ntohs(arp->hardware); pullup(&bp,(char *)&arp->protocol,sizeof(int16)); arp->protocol = ntohs(arp->protocol); pullup(&bp,(char *)&arp->hwalen,sizeof(char)); pullup(&bp,(char *)&arp->pralen,sizeof(char)); pullup(&bp,(char *)&arp->opcode,sizeof(int16)); arp->opcode = ntohs(arp->opcode); pullup(&bp,arp->shwaddr,arp->hwalen); pullup(&bp,(char *)&arp->sprotaddr,sizeof(int32)); arp->sprotaddr = ntohl(arp->sprotaddr); pullup(&bp,arp->thwaddr,arp->hwalen); pullup(&bp,(char *)&arp->tprotaddr,sizeof(int32)); arp->tprotaddr = ntohl(arp->tprotaddr); free_p(bp); return 0; } #ifdef TRACE char *arptypes[] = { NULLCHAR, "Ethernet", "Exp Ethernet", "AX.25", "Pronet", "Chaos" }; int doarpadd(),doarpdrop(); struct cmds arpcmds[] = { "add", doarpadd, 4, "usage: arp add ether|ax25 ", "arp add failed", "drop", doarpdrop, 3, "usage: arp drop ether|ax25", "not in table", NULLCHAR, NULLFP, 0, "arp subcommands: add, drop", NULLCHAR, }; int doarp(argc,argv) int argc; char *argv[]; { if(argc < 2){ dumparp(); return 0; } return subcmd(arpcmds,argc,argv); } static doarpadd(argc,argv) int argc; char *argv[]; { int hardware,hwalen,i; int32 addr,aton(); char *malloc(),*hwaddr; int naddr; struct arp_tab *ap; struct arp_type *at; struct ax25_addr *axp; addr = aton(argv[1]); /* This is a kludge. It really ought to be table driven */ switch(tolower(argv[2][0])){ case 'e': /* "ether" */ hardware = ARP_ETHER; naddr = 1; break; case 'a': /* "ax25" */ hardware = ARP_AX25; naddr = argc - 3; break; default: printf("unknown hardware type \"%s\"\r\n",argv[2]); return -1; } /* If an entry already exists, clear it */ if((ap = arp_lookup(hardware,addr)) != NULLARP) arp_drop(ap); at = &arp_type[hardware]; /* Allocate buffer for hardware address and fill with remaining args */ hwalen = at->hwalen * naddr; if((hwaddr = malloc(hwalen)) == NULLCHAR){ printf("No space\r\n"); return 0; } (*at->scan)(hwaddr,argv[3]); /* Destination address */ /* Special hackery to handle a series of AX.25 digipeaters */ if(hardware == ARP_AX25){ axp = (struct ax25_addr *)hwaddr; for(i=1;issid &= ~E; /* axp++; */ axp = (struct ax25_addr *) ((char *)axp + AXALEN); (*at->scan)((char *)axp,argv[3+i]); } } ap = arp_add(addr,hardware,hwaddr,hwalen); /* Put in table */ free(hwaddr); /* Clean up */ stop_timer(&ap->timer); /* Make entry permanent */ ap->timer.count = ap->timer.start = 0; } /* Remove an ARP entry */ static doarpdrop(argc,argv) int argc; char *argv[]; { int hardware; int32 addr,aton(); struct arp_tab *ap; addr = aton(argv[1]); /* This is a kludge. It really ought to be table driven */ switch(tolower(argv[2][0])){ case 'e': /* "ether" */ hardware = ARP_ETHER; break; case 'a': /* "ax25" */ hardware = ARP_AX25; break; default: hardware = 0; break; } if((ap = arp_lookup(hardware,addr)) == NULLARP) return -1; arp_drop(ap); return 0; } /* Dump ARP table */ static dumparp() { register int i; extern struct arp_stat arp_stat; register struct arp_tab *ap; char e[128]; char *inet_ntoa(); printf("received %u badtype %u reqst in %u replies %u reqst out %u\r\n", arp_stat.recv,arp_stat.badtype,arp_stat.inreq, arp_stat.replies,arp_stat.outreq); printf("IP addr Type Time Q Addr\r\n"); for(i=0;inext){ printf("%-16s",inet_ntoa(ap->ip_addr)); printf("%-9s",arptypes[ap->hardware]); printf("%-5ld",ap->timer.count*(long)MSPTICK/1000); if(ap->state == ARP_PENDING) printf("%-2u",len_q(ap->pending)); else printf(" "); if(ap->state == ARP_VALID){ if(arp_type[ap->hardware].format != NULLFP){ (*arp_type[ap->hardware].format)(e,ap->hw_addr); } else { e[0] = '\0'; } printf("%s",e); } else { printf("[unknown]"); } printf("\r\n"); } } return 0; } /* Dump ARP packets (incomplete) */ static char *hwtypes[] = { "", "10 Mb Ethernet", "3 Mb Ethernet", "AX.25", NULLCHAR, }; #define NHWTYPES 4 arp_dump(bp) struct mbuf *bp; { struct arp arp; struct mbuf *tbp; char *inet_ntoa(); if(bp == NULLBUF) return; /* Make temporary copy */ dup_p(&tbp,bp,0,len_mbuf(bp)); ntoharp(&arp,tbp); if(arp.hardware < NHWTYPES) printf("ARP: hwtype %s",hwtypes[arp.hardware]); else printf("ARP: hwtype %u",arp.hardware); printf(" prot 0x%x hwlen %u prlen %u", arp.protocol,arp.hwalen,arp.pralen); switch(arp.opcode){ case ARP_REQUEST: printf(" op REQUEST"); break; case ARP_REPLY: printf(" op REPLY"); break; default: printf(" op %u",arp.opcode); break; } printf(" target %s\r\n",inet_ntoa(arp.tprotaddr)); } #endif