1*14867Ssam /* if_ether.c 6.2 83/08/28 */ 211570Ssam 311570Ssam /* 411570Ssam * Ethernet address resolution protocol. 511570Ssam */ 611570Ssam 711570Ssam #include "../h/param.h" 811570Ssam #include "../h/systm.h" 911570Ssam #include "../h/mbuf.h" 1011570Ssam #include "../h/socket.h" 1111570Ssam #include "../h/time.h" 1211570Ssam #include "../h/kernel.h" 1312763Ssam #include "../h/errno.h" 1411570Ssam 1511570Ssam #include "../net/if.h" 1611570Ssam #include "../netinet/in.h" 1711570Ssam #include "../netinet/if_ether.h" 1811570Ssam 1911570Ssam 2011570Ssam /* 2111570Ssam * Internet to ethernet address resolution table. 2211570Ssam */ 2311570Ssam struct arptab { 2411570Ssam struct in_addr at_iaddr; /* internet address */ 2511570Ssam u_char at_enaddr[6]; /* ethernet address */ 2611570Ssam struct mbuf *at_hold; /* last packet until resolved/timeout */ 2711570Ssam u_char at_timer; /* minutes since last reference */ 2811570Ssam u_char at_flags; /* flags */ 2911570Ssam }; 3011570Ssam /* at_flags field values */ 3111570Ssam #define ATF_INUSE 1 /* entry in use */ 3211570Ssam #define ATF_COM 2 /* completed entry (enaddr valid) */ 3311570Ssam 3411570Ssam #define ARPTAB_BSIZ 5 /* bucket size */ 3511570Ssam #define ARPTAB_NB 19 /* number of buckets */ 3611570Ssam #define ARPTAB_SIZE (ARPTAB_BSIZ * ARPTAB_NB) 3711570Ssam struct arptab arptab[ARPTAB_SIZE]; 3811570Ssam 3911570Ssam #define ARPTAB_HASH(a) \ 4011570Ssam ((short)((((a) >> 16) ^ (a)) & 0x7fff) % ARPTAB_NB) 4111570Ssam 4211570Ssam #define ARPTAB_LOOK(at,addr) { \ 4311570Ssam register n; \ 4411570Ssam at = &arptab[ARPTAB_HASH(addr) * ARPTAB_BSIZ]; \ 4511570Ssam for (n = 0 ; n < ARPTAB_BSIZ ; n++,at++) \ 4611570Ssam if (at->at_iaddr.s_addr == addr) \ 4711570Ssam break; \ 4811570Ssam if (n >= ARPTAB_BSIZ) \ 4911570Ssam at = 0; } 5011570Ssam 5111570Ssam struct arpcom *arpcom; /* chain of active ether interfaces */ 5211570Ssam int arpt_age; /* aging timer */ 5311570Ssam 5411570Ssam /* timer values */ 5511570Ssam #define ARPT_AGE (60*1) /* aging timer, 1 min. */ 5611570Ssam #define ARPT_KILLC 20 /* kill completed entry in 20 mins. */ 5711570Ssam #define ARPT_KILLI 3 /* kill incomplete entry in 3 minutes */ 5811570Ssam 5911570Ssam u_char etherbroadcastaddr[6] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; 6011570Ssam extern struct ifnet loif; 6111570Ssam 62*14867Ssam /* 63*14867Ssam * Local addresses in the range oldmap to infinity are 64*14867Ssam * mapped according to the old mapping scheme. That is, 65*14867Ssam * mapping of Internet to Ethernet addresses is performed 66*14867Ssam * by taking the high three bytes of the network interface's 67*14867Ssam * address and the low three bytes of the local address part. 68*14867Ssam * This only allows boards from the same manufacturer to 69*14867Ssam * communicate unless the on-board address is overridden 70*14867Ssam * (not possible in many manufacture's hardware). 71*14867Ssam * 72*14867Ssam * NB: setting oldmap to zero completely disables ARP 73*14867Ssam * (i.e. identical to setting IFF_NOARP with an ioctl). 74*14867Ssam */ 75*14867Ssam int oldmap = 1024; 7611570Ssam 7711570Ssam /* 7811570Ssam * Attach an ethernet interface to the list "arpcom" where 7911570Ssam * arptimer() can find it. If first time 8011570Ssam * initialization, start arptimer(). 8111570Ssam */ 8211570Ssam arpattach(ac) 8311570Ssam register struct arpcom *ac; 8411570Ssam { 8511570Ssam register struct arpcom *acp; 8611570Ssam 8711570Ssam for (acp = arpcom; acp != (struct arpcom *)0; acp = acp->ac_ac) 8811570Ssam if (acp == ac) /* if already on list */ 8911570Ssam return; 9011570Ssam ac->ac_ac = arpcom; 9111570Ssam arpcom = ac; 9211570Ssam if (arpcom->ac_ac == 0) /* very first time */ 9311570Ssam arptimer(); 9411570Ssam } 9511570Ssam 9611570Ssam /* 9711570Ssam * Timeout routine. Age arp_tab entries once a minute. 9811570Ssam */ 9911570Ssam arptimer() 10011570Ssam { 10111570Ssam register struct arptab *at; 10211570Ssam register i; 10311570Ssam 10412763Ssam timeout(arptimer, (caddr_t)0, hz); 10511570Ssam #ifdef notdef 10611570Ssam if (++arpt_sanity > ARPT_SANITY) { 10712763Ssam register struct arpcom *ac; 10812763Ssam 10911570Ssam /* 11011570Ssam * Randomize sanity timer based on my host address. 11111570Ssam * Ask who has my own address; if someone else replies, 11211570Ssam * then they are impersonating me. 11311570Ssam */ 11411570Ssam arpt_sanity = arpcom->ac_enaddr[5] & 0x3f; 11511570Ssam for (ac = arpcom; ac != (struct arpcom *)-1; ac = ac->ac_ac) 11611570Ssam arpwhohas(ac, &((struct sockaddr_in *) 11711570Ssam &ac->ac_if.if_addr)->sin_addr); 11811570Ssam } 11911570Ssam #endif 12011570Ssam if (++arpt_age > ARPT_AGE) { 12111570Ssam arpt_age = 0; 12211570Ssam at = &arptab[0]; 12311570Ssam for (i = 0; i < ARPTAB_SIZE; i++, at++) { 12411570Ssam if (at->at_flags == 0) 12511570Ssam continue; 12611570Ssam if (++at->at_timer < ((at->at_flags&ATF_COM) ? 12711570Ssam ARPT_KILLC : ARPT_KILLI)) 12811570Ssam continue; 12911570Ssam /* timer has expired, clear entry */ 13011570Ssam arptfree(at); 13111570Ssam } 13211570Ssam } 13311570Ssam } 13411570Ssam 13511570Ssam /* 13611570Ssam * Broadcast an ARP packet, asking who has addr on interface ac. 13711570Ssam */ 13811570Ssam arpwhohas(ac, addr) 13911570Ssam register struct arpcom *ac; 14011570Ssam struct in_addr *addr; 14111570Ssam { 14211570Ssam register struct mbuf *m; 14311570Ssam register struct ether_header *eh; 14411570Ssam register struct ether_arp *ea; 14511570Ssam struct sockaddr sa; 14611570Ssam 14711570Ssam if ((m = m_get(M_DONTWAIT, MT_DATA)) == NULL) 14813095Ssam return; 14911570Ssam m->m_len = sizeof *ea + sizeof *eh; 15011570Ssam m->m_off = MMAXOFF - m->m_len; 15111570Ssam ea = mtod(m, struct ether_arp *); 15211570Ssam eh = (struct ether_header *)sa.sa_data; 15312763Ssam bzero((caddr_t)ea, sizeof (*ea)); 15412763Ssam bcopy((caddr_t)etherbroadcastaddr, (caddr_t)eh->ether_dhost, 15512763Ssam sizeof (etherbroadcastaddr)); 15611570Ssam eh->ether_type = ETHERPUP_ARPTYPE; /* if_output will swap */ 15711570Ssam ea->arp_hrd = htons(ARPHRD_ETHER); 15811570Ssam ea->arp_pro = htons(ETHERPUP_IPTYPE); 15911570Ssam ea->arp_hln = sizeof ea->arp_sha; /* hardware address length */ 16011570Ssam ea->arp_pln = sizeof ea->arp_spa; /* protocol address length */ 16111570Ssam ea->arp_op = htons(ARPOP_REQUEST); 16212763Ssam bcopy((caddr_t)ac->ac_enaddr, (caddr_t)ea->arp_sha, 16312763Ssam sizeof (ea->arp_sha)); 16411570Ssam bcopy((caddr_t)&((struct sockaddr_in *)&ac->ac_if.if_addr)->sin_addr, 16512763Ssam (caddr_t)ea->arp_spa, sizeof (ea->arp_spa)); 16612763Ssam bcopy((caddr_t)addr, (caddr_t)ea->arp_tpa, sizeof (ea->arp_tpa)); 16711570Ssam sa.sa_family = AF_UNSPEC; 16813095Ssam (void) (*ac->ac_if.if_output)(&ac->ac_if, m, &sa); 16911570Ssam } 17011570Ssam 17111570Ssam /* 17211570Ssam * Resolve an IP address into an ethernet address. If success, 17311570Ssam * desten is filled in and 1 is returned. If there is no entry 17411570Ssam * in arptab, set one up and broadcast a request 17511570Ssam * for the IP address; return 0. Hold onto this mbuf and 17611570Ssam * resend it once the address is finally resolved. 17711570Ssam * 17811570Ssam * We do some (conservative) locking here at splimp, since 17911570Ssam * arptab is also altered from input interrupt service (ecintr/ilintr 18011570Ssam * calls arpinput when ETHERPUP_ARPTYPE packets come in). 18111570Ssam */ 18211570Ssam arpresolve(ac, m, destip, desten) 18311570Ssam register struct arpcom *ac; 18411570Ssam struct mbuf *m; 18511570Ssam register struct in_addr *destip; 18611570Ssam register u_char *desten; 18711570Ssam { 18811570Ssam register struct arptab *at; 189*14867Ssam register struct ifnet *ifp; 19011570Ssam struct sockaddr_in sin; 19111570Ssam int s, lna; 19211570Ssam 19311570Ssam lna = in_lnaof(*destip); 19411570Ssam if (lna == INADDR_ANY) { /* broadcast address */ 19512763Ssam bcopy((caddr_t)etherbroadcastaddr, (caddr_t)desten, 19612763Ssam sizeof (etherbroadcastaddr)); 19711570Ssam return (1); 19811570Ssam } 199*14867Ssam ifp = &ac->ac_if; 200*14867Ssam /* if for us, then use software loopback driver */ 201*14867Ssam if (destip->s_addr == 202*14867Ssam ((struct sockaddr_in *)&ifp->if_addr)-> sin_addr.s_addr) { 20311570Ssam sin.sin_family = AF_INET; 20411570Ssam sin.sin_addr = *destip; 20512763Ssam return (looutput(&loif, m, (struct sockaddr *)&sin)); 20611570Ssam } 207*14867Ssam if ((ifp->if_flags & IFF_NOARP) || lna >= oldmap) { 20812763Ssam bcopy((caddr_t)ac->ac_enaddr, (caddr_t)desten, 3); 20911570Ssam desten[3] = (lna >> 16) & 0x7f; 21011570Ssam desten[4] = (lna >> 8) & 0xff; 21111570Ssam desten[5] = lna & 0xff; 21211570Ssam return (1); 21311570Ssam } 21411570Ssam s = splimp(); 21511570Ssam ARPTAB_LOOK(at, destip->s_addr); 21611570Ssam if (at == 0) { /* not found */ 21711570Ssam at = arptnew(destip); 21811570Ssam at->at_hold = m; 21911570Ssam arpwhohas(ac, destip); 22011570Ssam splx(s); 22111570Ssam return (0); 22211570Ssam } 22311570Ssam at->at_timer = 0; /* restart the timer */ 22411570Ssam if (at->at_flags & ATF_COM) { /* entry IS complete */ 22512763Ssam bcopy((caddr_t)at->at_enaddr, (caddr_t)desten, 6); 22611570Ssam splx(s); 22711570Ssam return (1); 22811570Ssam } 22911570Ssam /* 23011570Ssam * There is an arptab entry, but no ethernet address 23111570Ssam * response yet. Replace the held mbuf with this 23211570Ssam * latest one. 23311570Ssam */ 23411570Ssam if (at->at_hold) 23511570Ssam m_freem(at->at_hold); 23611570Ssam at->at_hold = m; 23711570Ssam arpwhohas(ac, destip); /* ask again */ 23811570Ssam splx(s); 23911570Ssam return (0); 24011570Ssam } 24111570Ssam 24211570Ssam /* 24311570Ssam * Find my own IP address. It will either be waiting for us in 24411570Ssam * monitor RAM, or can be obtained via broadcast to the file/boot 24511570Ssam * server (not necessarily using the ARP packet format). 24611570Ssam * 24711570Ssam * Unimplemented at present, return 0 and assume that the host 24811570Ssam * will set his own IP address via the SIOCSIFADDR ioctl. 24911570Ssam */ 25012763Ssam /*ARGSUSED*/ 25111570Ssam struct in_addr 25211570Ssam arpmyaddr(ac) 25311570Ssam register struct arpcom *ac; 25411570Ssam { 25511570Ssam static struct in_addr addr; 25611570Ssam 25713095Ssam #ifdef lint 25813095Ssam ac = ac; 25913095Ssam #endif 26011570Ssam addr.s_addr = 0; 26111570Ssam return (addr); 26211570Ssam } 26311570Ssam 26411570Ssam /* 26511570Ssam * Called from ecintr/ilintr when ether packet type ETHERPUP_ARP 26611570Ssam * is received. Algorithm is exactly that given in RFC 826. 26711570Ssam * In addition, a sanity check is performed on the sender 26811570Ssam * protocol address, to catch impersonators. 26911570Ssam */ 27011570Ssam arpinput(ac, m) 27111570Ssam register struct arpcom *ac; 27211570Ssam struct mbuf *m; 27311570Ssam { 27411570Ssam register struct ether_arp *ea; 27511570Ssam struct ether_header *eh; 27611570Ssam register struct arptab *at = 0; /* same as "merge" flag */ 27711570Ssam struct sockaddr_in sin; 27811570Ssam struct sockaddr sa; 27911570Ssam struct mbuf *mhold; 28011570Ssam struct in_addr isaddr,itaddr,myaddr; 28111570Ssam 28211570Ssam if (m->m_len < sizeof *ea) 28311570Ssam goto out; 28411570Ssam myaddr = ((struct sockaddr_in *)&ac->ac_if.if_addr)->sin_addr; 28511570Ssam ea = mtod(m, struct ether_arp *); 28611570Ssam if (ntohs(ea->arp_pro) != ETHERPUP_IPTYPE) 28711570Ssam goto out; 28811570Ssam isaddr.s_addr = ((struct in_addr *)ea->arp_spa)->s_addr; 28911570Ssam itaddr.s_addr = ((struct in_addr *)ea->arp_tpa)->s_addr; 29012763Ssam if (!bcmp((caddr_t)ea->arp_sha, (caddr_t)ac->ac_enaddr, 29112763Ssam sizeof (ac->ac_enaddr))) 29211570Ssam goto out; /* it's from me, ignore it. */ 29311570Ssam if (isaddr.s_addr == myaddr.s_addr) { 29411570Ssam printf("duplicate IP address!! sent from ethernet address: "); 29511570Ssam printf("%x %x %x %x %x %x\n", ea->arp_sha[0], ea->arp_sha[1], 29611570Ssam ea->arp_sha[2], ea->arp_sha[3], 29711570Ssam ea->arp_sha[4], ea->arp_sha[5]); 29811570Ssam if (ntohs(ea->arp_op) == ARPOP_REQUEST) 29911570Ssam goto reply; 30011570Ssam goto out; 30111570Ssam } 30211570Ssam ARPTAB_LOOK(at, isaddr.s_addr); 30311570Ssam if (at) { 30412763Ssam bcopy((caddr_t)ea->arp_sha, (caddr_t)at->at_enaddr, 30512763Ssam sizeof (ea->arp_sha)); 30611570Ssam at->at_flags |= ATF_COM; 30711570Ssam if (at->at_hold) { 30811570Ssam mhold = at->at_hold; 30911570Ssam at->at_hold = 0; 31011570Ssam sin.sin_family = AF_INET; 31111570Ssam sin.sin_addr = isaddr; 31211570Ssam (*ac->ac_if.if_output)(&ac->ac_if, 31311570Ssam mhold, (struct sockaddr *)&sin); 31411570Ssam } 31511570Ssam } 31611570Ssam if (itaddr.s_addr != myaddr.s_addr) 31711570Ssam goto out; /* if I am not the target */ 31811570Ssam if (at == 0) { /* ensure we have a table entry */ 31911570Ssam at = arptnew(&isaddr); 32012763Ssam bcopy((caddr_t)ea->arp_sha, (caddr_t)at->at_enaddr, 32112763Ssam sizeof (ea->arp_sha)); 32211570Ssam at->at_flags |= ATF_COM; 32311570Ssam } 32411570Ssam if (ntohs(ea->arp_op) != ARPOP_REQUEST) 32511570Ssam goto out; 32611570Ssam reply: 32712763Ssam bcopy((caddr_t)ea->arp_sha, (caddr_t)ea->arp_tha, 32812763Ssam sizeof (ea->arp_sha)); 32912763Ssam bcopy((caddr_t)ea->arp_spa, (caddr_t)ea->arp_tpa, 33012763Ssam sizeof (ea->arp_spa)); 33112763Ssam bcopy((caddr_t)ac->ac_enaddr, (caddr_t)ea->arp_sha, 33212763Ssam sizeof (ea->arp_sha)); 33312763Ssam bcopy((caddr_t)&myaddr, (caddr_t)ea->arp_spa, 33412763Ssam sizeof (ea->arp_spa)); 33511570Ssam ea->arp_op = htons(ARPOP_REPLY); 33611570Ssam eh = (struct ether_header *)sa.sa_data; 33712763Ssam bcopy((caddr_t)ea->arp_tha, (caddr_t)eh->ether_dhost, 33812763Ssam sizeof (eh->ether_dhost)); 33911570Ssam eh->ether_type = ETHERPUP_ARPTYPE; 34011570Ssam sa.sa_family = AF_UNSPEC; 34111570Ssam (*ac->ac_if.if_output)(&ac->ac_if, m, &sa); 34211570Ssam return; 34311570Ssam out: 34411570Ssam m_freem(m); 34511570Ssam return; 34611570Ssam } 34711570Ssam 34811570Ssam /* 34911570Ssam * Free an arptab entry. 35011570Ssam */ 35111570Ssam arptfree(at) 35211570Ssam register struct arptab *at; 35311570Ssam { 35411570Ssam int s = splimp(); 35511570Ssam 35611570Ssam if (at->at_hold) 35711570Ssam m_freem(at->at_hold); 35811570Ssam at->at_hold = 0; 35911570Ssam at->at_timer = at->at_flags = 0; 36011570Ssam at->at_iaddr.s_addr = 0; 36111570Ssam splx(s); 36211570Ssam } 36311570Ssam 36411570Ssam /* 36511570Ssam * Enter a new address in arptab, pushing out the oldest entry 36611570Ssam * from the bucket if there is no room. 36711570Ssam */ 36811570Ssam struct arptab * 36911570Ssam arptnew(addr) 37011570Ssam struct in_addr *addr; 37111570Ssam { 37211570Ssam register n; 37311570Ssam int oldest = 0; 37411570Ssam register struct arptab *at, *ato; 37511570Ssam 37611570Ssam ato = at = &arptab[ARPTAB_HASH(addr->s_addr) * ARPTAB_BSIZ]; 37711570Ssam for (n = 0 ; n < ARPTAB_BSIZ ; n++,at++) { 37811570Ssam if (at->at_flags == 0) 37911570Ssam goto out; /* found an empty entry */ 38011570Ssam if (at->at_timer > oldest) { 38111570Ssam oldest = at->at_timer; 38211570Ssam ato = at; 38311570Ssam } 38411570Ssam } 38511570Ssam at = ato; 38611570Ssam arptfree(at); 38711570Ssam out: 38811570Ssam at->at_iaddr = *addr; 38911570Ssam at->at_flags = ATF_INUSE; 39011570Ssam return (at); 39111570Ssam } 392