1*12763Ssam /* if_ether.c 4.2 83/05/27 */ 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" 13*12763Ssam #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 6211570Ssam #define OLDMAP 1 /* if LNA > 1023 use old 3COM mapping */ 6311570Ssam 6411570Ssam /* 6511570Ssam * Attach an ethernet interface to the list "arpcom" where 6611570Ssam * arptimer() can find it. If first time 6711570Ssam * initialization, start arptimer(). 6811570Ssam */ 6911570Ssam arpattach(ac) 7011570Ssam register struct arpcom *ac; 7111570Ssam { 7211570Ssam register struct arpcom *acp; 7311570Ssam 7411570Ssam for (acp = arpcom; acp != (struct arpcom *)0; acp = acp->ac_ac) 7511570Ssam if (acp == ac) /* if already on list */ 7611570Ssam return; 7711570Ssam ac->ac_ac = arpcom; 7811570Ssam arpcom = ac; 7911570Ssam if (arpcom->ac_ac == 0) /* very first time */ 8011570Ssam arptimer(); 8111570Ssam } 8211570Ssam 8311570Ssam /* 8411570Ssam * Timeout routine. Age arp_tab entries once a minute. 8511570Ssam */ 8611570Ssam arptimer() 8711570Ssam { 8811570Ssam register struct arptab *at; 8911570Ssam register i; 9011570Ssam 91*12763Ssam timeout(arptimer, (caddr_t)0, hz); 9211570Ssam #ifdef notdef 9311570Ssam if (++arpt_sanity > ARPT_SANITY) { 94*12763Ssam register struct arpcom *ac; 95*12763Ssam 9611570Ssam /* 9711570Ssam * Randomize sanity timer based on my host address. 9811570Ssam * Ask who has my own address; if someone else replies, 9911570Ssam * then they are impersonating me. 10011570Ssam */ 10111570Ssam arpt_sanity = arpcom->ac_enaddr[5] & 0x3f; 10211570Ssam for (ac = arpcom; ac != (struct arpcom *)-1; ac = ac->ac_ac) 10311570Ssam arpwhohas(ac, &((struct sockaddr_in *) 10411570Ssam &ac->ac_if.if_addr)->sin_addr); 10511570Ssam } 10611570Ssam #endif 10711570Ssam if (++arpt_age > ARPT_AGE) { 10811570Ssam arpt_age = 0; 10911570Ssam at = &arptab[0]; 11011570Ssam for (i = 0; i < ARPTAB_SIZE; i++, at++) { 11111570Ssam if (at->at_flags == 0) 11211570Ssam continue; 11311570Ssam if (++at->at_timer < ((at->at_flags&ATF_COM) ? 11411570Ssam ARPT_KILLC : ARPT_KILLI)) 11511570Ssam continue; 11611570Ssam /* timer has expired, clear entry */ 11711570Ssam arptfree(at); 11811570Ssam } 11911570Ssam } 12011570Ssam } 12111570Ssam 12211570Ssam /* 12311570Ssam * Broadcast an ARP packet, asking who has addr on interface ac. 12411570Ssam */ 12511570Ssam arpwhohas(ac, addr) 12611570Ssam register struct arpcom *ac; 12711570Ssam struct in_addr *addr; 12811570Ssam { 12911570Ssam register struct mbuf *m; 13011570Ssam register struct ether_header *eh; 13111570Ssam register struct ether_arp *ea; 13211570Ssam struct sockaddr sa; 13311570Ssam 13411570Ssam if ((m = m_get(M_DONTWAIT, MT_DATA)) == NULL) 135*12763Ssam return (ENOBUFS); 13611570Ssam m->m_len = sizeof *ea + sizeof *eh; 13711570Ssam m->m_off = MMAXOFF - m->m_len; 13811570Ssam ea = mtod(m, struct ether_arp *); 13911570Ssam eh = (struct ether_header *)sa.sa_data; 140*12763Ssam bzero((caddr_t)ea, sizeof (*ea)); 141*12763Ssam bcopy((caddr_t)etherbroadcastaddr, (caddr_t)eh->ether_dhost, 142*12763Ssam sizeof (etherbroadcastaddr)); 14311570Ssam eh->ether_type = ETHERPUP_ARPTYPE; /* if_output will swap */ 14411570Ssam ea->arp_hrd = htons(ARPHRD_ETHER); 14511570Ssam ea->arp_pro = htons(ETHERPUP_IPTYPE); 14611570Ssam ea->arp_hln = sizeof ea->arp_sha; /* hardware address length */ 14711570Ssam ea->arp_pln = sizeof ea->arp_spa; /* protocol address length */ 14811570Ssam ea->arp_op = htons(ARPOP_REQUEST); 149*12763Ssam bcopy((caddr_t)ac->ac_enaddr, (caddr_t)ea->arp_sha, 150*12763Ssam sizeof (ea->arp_sha)); 15111570Ssam bcopy((caddr_t)&((struct sockaddr_in *)&ac->ac_if.if_addr)->sin_addr, 152*12763Ssam (caddr_t)ea->arp_spa, sizeof (ea->arp_spa)); 153*12763Ssam bcopy((caddr_t)addr, (caddr_t)ea->arp_tpa, sizeof (ea->arp_tpa)); 15411570Ssam sa.sa_family = AF_UNSPEC; 15511570Ssam return ((*ac->ac_if.if_output)(&ac->ac_if, m, &sa)); 15611570Ssam } 15711570Ssam 15811570Ssam /* 15911570Ssam * Resolve an IP address into an ethernet address. If success, 16011570Ssam * desten is filled in and 1 is returned. If there is no entry 16111570Ssam * in arptab, set one up and broadcast a request 16211570Ssam * for the IP address; return 0. Hold onto this mbuf and 16311570Ssam * resend it once the address is finally resolved. 16411570Ssam * 16511570Ssam * We do some (conservative) locking here at splimp, since 16611570Ssam * arptab is also altered from input interrupt service (ecintr/ilintr 16711570Ssam * calls arpinput when ETHERPUP_ARPTYPE packets come in). 16811570Ssam */ 16911570Ssam arpresolve(ac, m, destip, desten) 17011570Ssam register struct arpcom *ac; 17111570Ssam struct mbuf *m; 17211570Ssam register struct in_addr *destip; 17311570Ssam register u_char *desten; 17411570Ssam { 17511570Ssam register struct arptab *at; 17611570Ssam struct sockaddr_in sin; 17711570Ssam int s, lna; 17811570Ssam 17911570Ssam lna = in_lnaof(*destip); 18011570Ssam if (lna == INADDR_ANY) { /* broadcast address */ 181*12763Ssam bcopy((caddr_t)etherbroadcastaddr, (caddr_t)desten, 182*12763Ssam sizeof (etherbroadcastaddr)); 18311570Ssam return (1); 18411570Ssam } 18511570Ssam if (destip->s_addr == ((struct sockaddr_in *)&ac->ac_if.if_addr)-> 18611570Ssam sin_addr.s_addr) { /* if for us, use lo driver */ 18711570Ssam sin.sin_family = AF_INET; 18811570Ssam sin.sin_addr = *destip; 189*12763Ssam return (looutput(&loif, m, (struct sockaddr *)&sin)); 19011570Ssam } 19111570Ssam #ifdef OLDMAP 19211570Ssam if (lna >= 1024) { 193*12763Ssam bcopy((caddr_t)ac->ac_enaddr, (caddr_t)desten, 3); 19411570Ssam desten[3] = (lna >> 16) & 0x7f; 19511570Ssam desten[4] = (lna >> 8) & 0xff; 19611570Ssam desten[5] = lna & 0xff; 19711570Ssam return (1); 19811570Ssam } 19911570Ssam #endif OLDMAP 20011570Ssam s = splimp(); 20111570Ssam ARPTAB_LOOK(at, destip->s_addr); 20211570Ssam if (at == 0) { /* not found */ 20311570Ssam at = arptnew(destip); 20411570Ssam at->at_hold = m; 20511570Ssam arpwhohas(ac, destip); 20611570Ssam splx(s); 20711570Ssam return (0); 20811570Ssam } 20911570Ssam at->at_timer = 0; /* restart the timer */ 21011570Ssam if (at->at_flags & ATF_COM) { /* entry IS complete */ 211*12763Ssam bcopy((caddr_t)at->at_enaddr, (caddr_t)desten, 6); 21211570Ssam splx(s); 21311570Ssam return (1); 21411570Ssam } 21511570Ssam /* 21611570Ssam * There is an arptab entry, but no ethernet address 21711570Ssam * response yet. Replace the held mbuf with this 21811570Ssam * latest one. 21911570Ssam */ 22011570Ssam if (at->at_hold) 22111570Ssam m_freem(at->at_hold); 22211570Ssam at->at_hold = m; 22311570Ssam arpwhohas(ac, destip); /* ask again */ 22411570Ssam splx(s); 22511570Ssam return (0); 22611570Ssam } 22711570Ssam 22811570Ssam /* 22911570Ssam * Find my own IP address. It will either be waiting for us in 23011570Ssam * monitor RAM, or can be obtained via broadcast to the file/boot 23111570Ssam * server (not necessarily using the ARP packet format). 23211570Ssam * 23311570Ssam * Unimplemented at present, return 0 and assume that the host 23411570Ssam * will set his own IP address via the SIOCSIFADDR ioctl. 23511570Ssam */ 236*12763Ssam /*ARGSUSED*/ 23711570Ssam struct in_addr 23811570Ssam arpmyaddr(ac) 23911570Ssam register struct arpcom *ac; 24011570Ssam { 24111570Ssam static struct in_addr addr; 24211570Ssam 24311570Ssam addr.s_addr = 0; 24411570Ssam return (addr); 24511570Ssam } 24611570Ssam 24711570Ssam /* 24811570Ssam * Called from ecintr/ilintr when ether packet type ETHERPUP_ARP 24911570Ssam * is received. Algorithm is exactly that given in RFC 826. 25011570Ssam * In addition, a sanity check is performed on the sender 25111570Ssam * protocol address, to catch impersonators. 25211570Ssam */ 25311570Ssam arpinput(ac, m) 25411570Ssam register struct arpcom *ac; 25511570Ssam struct mbuf *m; 25611570Ssam { 25711570Ssam register struct ether_arp *ea; 25811570Ssam struct ether_header *eh; 25911570Ssam register struct arptab *at = 0; /* same as "merge" flag */ 26011570Ssam struct sockaddr_in sin; 26111570Ssam struct sockaddr sa; 26211570Ssam struct mbuf *mhold; 26311570Ssam struct in_addr isaddr,itaddr,myaddr; 26411570Ssam 26511570Ssam if (m->m_len < sizeof *ea) 26611570Ssam goto out; 26711570Ssam myaddr = ((struct sockaddr_in *)&ac->ac_if.if_addr)->sin_addr; 26811570Ssam ea = mtod(m, struct ether_arp *); 26911570Ssam if (ntohs(ea->arp_pro) != ETHERPUP_IPTYPE) 27011570Ssam goto out; 27111570Ssam isaddr.s_addr = ((struct in_addr *)ea->arp_spa)->s_addr; 27211570Ssam itaddr.s_addr = ((struct in_addr *)ea->arp_tpa)->s_addr; 273*12763Ssam if (!bcmp((caddr_t)ea->arp_sha, (caddr_t)ac->ac_enaddr, 274*12763Ssam sizeof (ac->ac_enaddr))) 27511570Ssam goto out; /* it's from me, ignore it. */ 27611570Ssam if (isaddr.s_addr == myaddr.s_addr) { 27711570Ssam printf("duplicate IP address!! sent from ethernet address: "); 27811570Ssam printf("%x %x %x %x %x %x\n", ea->arp_sha[0], ea->arp_sha[1], 27911570Ssam ea->arp_sha[2], ea->arp_sha[3], 28011570Ssam ea->arp_sha[4], ea->arp_sha[5]); 28111570Ssam if (ntohs(ea->arp_op) == ARPOP_REQUEST) 28211570Ssam goto reply; 28311570Ssam goto out; 28411570Ssam } 28511570Ssam ARPTAB_LOOK(at, isaddr.s_addr); 28611570Ssam if (at) { 287*12763Ssam bcopy((caddr_t)ea->arp_sha, (caddr_t)at->at_enaddr, 288*12763Ssam sizeof (ea->arp_sha)); 28911570Ssam at->at_flags |= ATF_COM; 29011570Ssam if (at->at_hold) { 29111570Ssam mhold = at->at_hold; 29211570Ssam at->at_hold = 0; 29311570Ssam sin.sin_family = AF_INET; 29411570Ssam sin.sin_addr = isaddr; 29511570Ssam (*ac->ac_if.if_output)(&ac->ac_if, 29611570Ssam mhold, (struct sockaddr *)&sin); 29711570Ssam } 29811570Ssam } 29911570Ssam if (itaddr.s_addr != myaddr.s_addr) 30011570Ssam goto out; /* if I am not the target */ 30111570Ssam if (at == 0) { /* ensure we have a table entry */ 30211570Ssam at = arptnew(&isaddr); 303*12763Ssam bcopy((caddr_t)ea->arp_sha, (caddr_t)at->at_enaddr, 304*12763Ssam sizeof (ea->arp_sha)); 30511570Ssam at->at_flags |= ATF_COM; 30611570Ssam } 30711570Ssam if (ntohs(ea->arp_op) != ARPOP_REQUEST) 30811570Ssam goto out; 30911570Ssam reply: 310*12763Ssam bcopy((caddr_t)ea->arp_sha, (caddr_t)ea->arp_tha, 311*12763Ssam sizeof (ea->arp_sha)); 312*12763Ssam bcopy((caddr_t)ea->arp_spa, (caddr_t)ea->arp_tpa, 313*12763Ssam sizeof (ea->arp_spa)); 314*12763Ssam bcopy((caddr_t)ac->ac_enaddr, (caddr_t)ea->arp_sha, 315*12763Ssam sizeof (ea->arp_sha)); 316*12763Ssam bcopy((caddr_t)&myaddr, (caddr_t)ea->arp_spa, 317*12763Ssam sizeof (ea->arp_spa)); 31811570Ssam ea->arp_op = htons(ARPOP_REPLY); 31911570Ssam eh = (struct ether_header *)sa.sa_data; 320*12763Ssam bcopy((caddr_t)ea->arp_tha, (caddr_t)eh->ether_dhost, 321*12763Ssam sizeof (eh->ether_dhost)); 32211570Ssam eh->ether_type = ETHERPUP_ARPTYPE; 32311570Ssam sa.sa_family = AF_UNSPEC; 32411570Ssam (*ac->ac_if.if_output)(&ac->ac_if, m, &sa); 32511570Ssam return; 32611570Ssam out: 32711570Ssam m_freem(m); 32811570Ssam return; 32911570Ssam } 33011570Ssam 33111570Ssam /* 33211570Ssam * Free an arptab entry. 33311570Ssam */ 33411570Ssam arptfree(at) 33511570Ssam register struct arptab *at; 33611570Ssam { 33711570Ssam int s = splimp(); 33811570Ssam 33911570Ssam if (at->at_hold) 34011570Ssam m_freem(at->at_hold); 34111570Ssam at->at_hold = 0; 34211570Ssam at->at_timer = at->at_flags = 0; 34311570Ssam at->at_iaddr.s_addr = 0; 34411570Ssam splx(s); 34511570Ssam } 34611570Ssam 34711570Ssam /* 34811570Ssam * Enter a new address in arptab, pushing out the oldest entry 34911570Ssam * from the bucket if there is no room. 35011570Ssam */ 35111570Ssam struct arptab * 35211570Ssam arptnew(addr) 35311570Ssam struct in_addr *addr; 35411570Ssam { 35511570Ssam register n; 35611570Ssam int oldest = 0; 35711570Ssam register struct arptab *at, *ato; 35811570Ssam 35911570Ssam ato = at = &arptab[ARPTAB_HASH(addr->s_addr) * ARPTAB_BSIZ]; 36011570Ssam for (n = 0 ; n < ARPTAB_BSIZ ; n++,at++) { 36111570Ssam if (at->at_flags == 0) 36211570Ssam goto out; /* found an empty entry */ 36311570Ssam if (at->at_timer > oldest) { 36411570Ssam oldest = at->at_timer; 36511570Ssam ato = at; 36611570Ssam } 36711570Ssam } 36811570Ssam at = ato; 36911570Ssam arptfree(at); 37011570Ssam out: 37111570Ssam at->at_iaddr = *addr; 37211570Ssam at->at_flags = ATF_INUSE; 37311570Ssam return (at); 37411570Ssam } 375