1*11570Ssam /* if_ether.c 4.1 83/03/15 */ 2*11570Ssam 3*11570Ssam /* 4*11570Ssam * Ethernet address resolution protocol. 5*11570Ssam */ 6*11570Ssam 7*11570Ssam #include "../h/param.h" 8*11570Ssam #include "../h/systm.h" 9*11570Ssam #include "../h/mbuf.h" 10*11570Ssam #include "../h/socket.h" 11*11570Ssam #include "../h/time.h" 12*11570Ssam #include "../h/kernel.h" 13*11570Ssam 14*11570Ssam #include "../net/if.h" 15*11570Ssam #include "../netinet/in.h" 16*11570Ssam #include "../netinet/if_ether.h" 17*11570Ssam 18*11570Ssam 19*11570Ssam /* 20*11570Ssam * Internet to ethernet address resolution table. 21*11570Ssam */ 22*11570Ssam struct arptab { 23*11570Ssam struct in_addr at_iaddr; /* internet address */ 24*11570Ssam u_char at_enaddr[6]; /* ethernet address */ 25*11570Ssam struct mbuf *at_hold; /* last packet until resolved/timeout */ 26*11570Ssam u_char at_timer; /* minutes since last reference */ 27*11570Ssam u_char at_flags; /* flags */ 28*11570Ssam }; 29*11570Ssam /* at_flags field values */ 30*11570Ssam #define ATF_INUSE 1 /* entry in use */ 31*11570Ssam #define ATF_COM 2 /* completed entry (enaddr valid) */ 32*11570Ssam 33*11570Ssam #define ARPTAB_BSIZ 5 /* bucket size */ 34*11570Ssam #define ARPTAB_NB 19 /* number of buckets */ 35*11570Ssam #define ARPTAB_SIZE (ARPTAB_BSIZ * ARPTAB_NB) 36*11570Ssam struct arptab arptab[ARPTAB_SIZE]; 37*11570Ssam 38*11570Ssam #define ARPTAB_HASH(a) \ 39*11570Ssam ((short)((((a) >> 16) ^ (a)) & 0x7fff) % ARPTAB_NB) 40*11570Ssam 41*11570Ssam #define ARPTAB_LOOK(at,addr) { \ 42*11570Ssam register n; \ 43*11570Ssam at = &arptab[ARPTAB_HASH(addr) * ARPTAB_BSIZ]; \ 44*11570Ssam for (n = 0 ; n < ARPTAB_BSIZ ; n++,at++) \ 45*11570Ssam if (at->at_iaddr.s_addr == addr) \ 46*11570Ssam break; \ 47*11570Ssam if (n >= ARPTAB_BSIZ) \ 48*11570Ssam at = 0; } 49*11570Ssam 50*11570Ssam struct arpcom *arpcom; /* chain of active ether interfaces */ 51*11570Ssam int arpt_age; /* aging timer */ 52*11570Ssam 53*11570Ssam /* timer values */ 54*11570Ssam #define ARPT_AGE (60*1) /* aging timer, 1 min. */ 55*11570Ssam #define ARPT_KILLC 20 /* kill completed entry in 20 mins. */ 56*11570Ssam #define ARPT_KILLI 3 /* kill incomplete entry in 3 minutes */ 57*11570Ssam 58*11570Ssam u_char etherbroadcastaddr[6] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; 59*11570Ssam extern struct ifnet loif; 60*11570Ssam 61*11570Ssam #define OLDMAP 1 /* if LNA > 1023 use old 3COM mapping */ 62*11570Ssam 63*11570Ssam /* 64*11570Ssam * Attach an ethernet interface to the list "arpcom" where 65*11570Ssam * arptimer() can find it. If first time 66*11570Ssam * initialization, start arptimer(). 67*11570Ssam */ 68*11570Ssam arpattach(ac) 69*11570Ssam register struct arpcom *ac; 70*11570Ssam { 71*11570Ssam register struct arpcom *acp; 72*11570Ssam 73*11570Ssam for (acp = arpcom; acp != (struct arpcom *)0; acp = acp->ac_ac) 74*11570Ssam if (acp == ac) /* if already on list */ 75*11570Ssam return; 76*11570Ssam ac->ac_ac = arpcom; 77*11570Ssam arpcom = ac; 78*11570Ssam if (arpcom->ac_ac == 0) /* very first time */ 79*11570Ssam arptimer(); 80*11570Ssam } 81*11570Ssam 82*11570Ssam /* 83*11570Ssam * Timeout routine. Age arp_tab entries once a minute. 84*11570Ssam */ 85*11570Ssam arptimer() 86*11570Ssam { 87*11570Ssam register struct arpcom *ac; 88*11570Ssam register struct arptab *at; 89*11570Ssam register i; 90*11570Ssam 91*11570Ssam timeout(arptimer, 0, hz); 92*11570Ssam #ifdef notdef 93*11570Ssam if (++arpt_sanity > ARPT_SANITY) { 94*11570Ssam /* 95*11570Ssam * Randomize sanity timer based on my host address. 96*11570Ssam * Ask who has my own address; if someone else replies, 97*11570Ssam * then they are impersonating me. 98*11570Ssam */ 99*11570Ssam arpt_sanity = arpcom->ac_enaddr[5] & 0x3f; 100*11570Ssam for (ac = arpcom; ac != (struct arpcom *)-1; ac = ac->ac_ac) 101*11570Ssam arpwhohas(ac, &((struct sockaddr_in *) 102*11570Ssam &ac->ac_if.if_addr)->sin_addr); 103*11570Ssam } 104*11570Ssam #endif 105*11570Ssam if (++arpt_age > ARPT_AGE) { 106*11570Ssam arpt_age = 0; 107*11570Ssam at = &arptab[0]; 108*11570Ssam for (i = 0; i < ARPTAB_SIZE; i++, at++) { 109*11570Ssam if (at->at_flags == 0) 110*11570Ssam continue; 111*11570Ssam if (++at->at_timer < ((at->at_flags&ATF_COM) ? 112*11570Ssam ARPT_KILLC : ARPT_KILLI)) 113*11570Ssam continue; 114*11570Ssam /* timer has expired, clear entry */ 115*11570Ssam arptfree(at); 116*11570Ssam } 117*11570Ssam } 118*11570Ssam } 119*11570Ssam 120*11570Ssam /* 121*11570Ssam * Broadcast an ARP packet, asking who has addr on interface ac. 122*11570Ssam */ 123*11570Ssam arpwhohas(ac, addr) 124*11570Ssam register struct arpcom *ac; 125*11570Ssam struct in_addr *addr; 126*11570Ssam { 127*11570Ssam register struct mbuf *m; 128*11570Ssam register struct ether_header *eh; 129*11570Ssam register struct ether_arp *ea; 130*11570Ssam struct sockaddr sa; 131*11570Ssam 132*11570Ssam if ((m = m_get(M_DONTWAIT, MT_DATA)) == NULL) 133*11570Ssam return (1); 134*11570Ssam m->m_len = sizeof *ea + sizeof *eh; 135*11570Ssam m->m_off = MMAXOFF - m->m_len; 136*11570Ssam ea = mtod(m, struct ether_arp *); 137*11570Ssam eh = (struct ether_header *)sa.sa_data; 138*11570Ssam bzero((caddr_t)ea, sizeof *ea); 139*11570Ssam bcopy(etherbroadcastaddr, eh->ether_dhost, sizeof etherbroadcastaddr); 140*11570Ssam eh->ether_type = ETHERPUP_ARPTYPE; /* if_output will swap */ 141*11570Ssam ea->arp_hrd = htons(ARPHRD_ETHER); 142*11570Ssam ea->arp_pro = htons(ETHERPUP_IPTYPE); 143*11570Ssam ea->arp_hln = sizeof ea->arp_sha; /* hardware address length */ 144*11570Ssam ea->arp_pln = sizeof ea->arp_spa; /* protocol address length */ 145*11570Ssam ea->arp_op = htons(ARPOP_REQUEST); 146*11570Ssam bcopy(ac->ac_enaddr, ea->arp_sha, sizeof ea->arp_sha); 147*11570Ssam bcopy((caddr_t)&((struct sockaddr_in *)&ac->ac_if.if_addr)->sin_addr, 148*11570Ssam ea->arp_spa, sizeof ea->arp_spa); 149*11570Ssam bcopy((caddr_t)addr, ea->arp_tpa, sizeof ea->arp_tpa); 150*11570Ssam sa.sa_family = AF_UNSPEC; 151*11570Ssam return ((*ac->ac_if.if_output)(&ac->ac_if, m, &sa)); 152*11570Ssam } 153*11570Ssam 154*11570Ssam /* 155*11570Ssam * Resolve an IP address into an ethernet address. If success, 156*11570Ssam * desten is filled in and 1 is returned. If there is no entry 157*11570Ssam * in arptab, set one up and broadcast a request 158*11570Ssam * for the IP address; return 0. Hold onto this mbuf and 159*11570Ssam * resend it once the address is finally resolved. 160*11570Ssam * 161*11570Ssam * We do some (conservative) locking here at splimp, since 162*11570Ssam * arptab is also altered from input interrupt service (ecintr/ilintr 163*11570Ssam * calls arpinput when ETHERPUP_ARPTYPE packets come in). 164*11570Ssam */ 165*11570Ssam arpresolve(ac, m, destip, desten) 166*11570Ssam register struct arpcom *ac; 167*11570Ssam struct mbuf *m; 168*11570Ssam register struct in_addr *destip; 169*11570Ssam register u_char *desten; 170*11570Ssam { 171*11570Ssam register struct arptab *at; 172*11570Ssam register i; 173*11570Ssam struct sockaddr_in sin; 174*11570Ssam int s, lna; 175*11570Ssam 176*11570Ssam lna = in_lnaof(*destip); 177*11570Ssam if (lna == INADDR_ANY) { /* broadcast address */ 178*11570Ssam bcopy(etherbroadcastaddr, desten, sizeof etherbroadcastaddr); 179*11570Ssam return (1); 180*11570Ssam } 181*11570Ssam if (destip->s_addr == ((struct sockaddr_in *)&ac->ac_if.if_addr)-> 182*11570Ssam sin_addr.s_addr) { /* if for us, use lo driver */ 183*11570Ssam sin.sin_family = AF_INET; 184*11570Ssam sin.sin_addr = *destip; 185*11570Ssam looutput(&loif, m, &sin); 186*11570Ssam return (0); 187*11570Ssam } 188*11570Ssam #ifdef OLDMAP 189*11570Ssam if (lna >= 1024) { 190*11570Ssam bcopy(ac->ac_enaddr, desten, 3); 191*11570Ssam desten[3] = (lna >> 16) & 0x7f; 192*11570Ssam desten[4] = (lna >> 8) & 0xff; 193*11570Ssam desten[5] = lna & 0xff; 194*11570Ssam return (1); 195*11570Ssam } 196*11570Ssam #endif OLDMAP 197*11570Ssam s = splimp(); 198*11570Ssam ARPTAB_LOOK(at, destip->s_addr); 199*11570Ssam if (at == 0) { /* not found */ 200*11570Ssam at = arptnew(destip); 201*11570Ssam at->at_hold = m; 202*11570Ssam arpwhohas(ac, destip); 203*11570Ssam splx(s); 204*11570Ssam return (0); 205*11570Ssam } 206*11570Ssam at->at_timer = 0; /* restart the timer */ 207*11570Ssam if (at->at_flags & ATF_COM) { /* entry IS complete */ 208*11570Ssam bcopy(at->at_enaddr, desten, 6); 209*11570Ssam splx(s); 210*11570Ssam return (1); 211*11570Ssam } 212*11570Ssam /* 213*11570Ssam * There is an arptab entry, but no ethernet address 214*11570Ssam * response yet. Replace the held mbuf with this 215*11570Ssam * latest one. 216*11570Ssam */ 217*11570Ssam if (at->at_hold) 218*11570Ssam m_freem(at->at_hold); 219*11570Ssam at->at_hold = m; 220*11570Ssam arpwhohas(ac, destip); /* ask again */ 221*11570Ssam splx(s); 222*11570Ssam return (0); 223*11570Ssam } 224*11570Ssam 225*11570Ssam /* 226*11570Ssam * Find my own IP address. It will either be waiting for us in 227*11570Ssam * monitor RAM, or can be obtained via broadcast to the file/boot 228*11570Ssam * server (not necessarily using the ARP packet format). 229*11570Ssam * 230*11570Ssam * Unimplemented at present, return 0 and assume that the host 231*11570Ssam * will set his own IP address via the SIOCSIFADDR ioctl. 232*11570Ssam */ 233*11570Ssam struct in_addr 234*11570Ssam arpmyaddr(ac) 235*11570Ssam register struct arpcom *ac; 236*11570Ssam { 237*11570Ssam static struct in_addr addr; 238*11570Ssam 239*11570Ssam addr.s_addr = 0; 240*11570Ssam return (addr); 241*11570Ssam } 242*11570Ssam 243*11570Ssam /* 244*11570Ssam * Called from ecintr/ilintr when ether packet type ETHERPUP_ARP 245*11570Ssam * is received. Algorithm is exactly that given in RFC 826. 246*11570Ssam * In addition, a sanity check is performed on the sender 247*11570Ssam * protocol address, to catch impersonators. 248*11570Ssam */ 249*11570Ssam arpinput(ac, m) 250*11570Ssam register struct arpcom *ac; 251*11570Ssam struct mbuf *m; 252*11570Ssam { 253*11570Ssam register struct ether_arp *ea; 254*11570Ssam struct ether_header *eh; 255*11570Ssam register struct arptab *at = 0; /* same as "merge" flag */ 256*11570Ssam struct sockaddr_in sin; 257*11570Ssam struct sockaddr sa; 258*11570Ssam struct mbuf *mhold; 259*11570Ssam struct in_addr isaddr,itaddr,myaddr; 260*11570Ssam 261*11570Ssam if (m->m_len < sizeof *ea) 262*11570Ssam goto out; 263*11570Ssam myaddr = ((struct sockaddr_in *)&ac->ac_if.if_addr)->sin_addr; 264*11570Ssam ea = mtod(m, struct ether_arp *); 265*11570Ssam if (ntohs(ea->arp_pro) != ETHERPUP_IPTYPE) 266*11570Ssam goto out; 267*11570Ssam isaddr.s_addr = ((struct in_addr *)ea->arp_spa)->s_addr; 268*11570Ssam itaddr.s_addr = ((struct in_addr *)ea->arp_tpa)->s_addr; 269*11570Ssam if (!bcmp(ea->arp_sha, ac->ac_enaddr, sizeof ac->ac_enaddr)) 270*11570Ssam goto out; /* it's from me, ignore it. */ 271*11570Ssam if (isaddr.s_addr == myaddr.s_addr) { 272*11570Ssam printf("duplicate IP address!! sent from ethernet address: "); 273*11570Ssam printf("%x %x %x %x %x %x\n", ea->arp_sha[0], ea->arp_sha[1], 274*11570Ssam ea->arp_sha[2], ea->arp_sha[3], 275*11570Ssam ea->arp_sha[4], ea->arp_sha[5]); 276*11570Ssam if (ntohs(ea->arp_op) == ARPOP_REQUEST) 277*11570Ssam goto reply; 278*11570Ssam goto out; 279*11570Ssam } 280*11570Ssam ARPTAB_LOOK(at, isaddr.s_addr); 281*11570Ssam if (at) { 282*11570Ssam bcopy(ea->arp_sha, at->at_enaddr, sizeof ea->arp_sha); 283*11570Ssam at->at_flags |= ATF_COM; 284*11570Ssam if (at->at_hold) { 285*11570Ssam mhold = at->at_hold; 286*11570Ssam at->at_hold = 0; 287*11570Ssam sin.sin_family = AF_INET; 288*11570Ssam sin.sin_addr = isaddr; 289*11570Ssam (*ac->ac_if.if_output)(&ac->ac_if, 290*11570Ssam mhold, (struct sockaddr *)&sin); 291*11570Ssam } 292*11570Ssam } 293*11570Ssam if (itaddr.s_addr != myaddr.s_addr) 294*11570Ssam goto out; /* if I am not the target */ 295*11570Ssam if (at == 0) { /* ensure we have a table entry */ 296*11570Ssam at = arptnew(&isaddr); 297*11570Ssam bcopy(ea->arp_sha, at->at_enaddr, sizeof ea->arp_sha); 298*11570Ssam at->at_flags |= ATF_COM; 299*11570Ssam } 300*11570Ssam if (ntohs(ea->arp_op) != ARPOP_REQUEST) 301*11570Ssam goto out; 302*11570Ssam reply: 303*11570Ssam bcopy(ea->arp_sha, ea->arp_tha, sizeof ea->arp_sha); 304*11570Ssam bcopy(ea->arp_spa, ea->arp_tpa, sizeof ea->arp_spa); 305*11570Ssam bcopy(ac->ac_enaddr, ea->arp_sha, sizeof ea->arp_sha); 306*11570Ssam bcopy((caddr_t)&myaddr, ea->arp_spa, sizeof ea->arp_spa); 307*11570Ssam ea->arp_op = htons(ARPOP_REPLY); 308*11570Ssam eh = (struct ether_header *)sa.sa_data; 309*11570Ssam bcopy(ea->arp_tha, eh->ether_dhost, sizeof eh->ether_dhost); 310*11570Ssam eh->ether_type = ETHERPUP_ARPTYPE; 311*11570Ssam sa.sa_family = AF_UNSPEC; 312*11570Ssam (*ac->ac_if.if_output)(&ac->ac_if, m, &sa); 313*11570Ssam return; 314*11570Ssam out: 315*11570Ssam m_freem(m); 316*11570Ssam return; 317*11570Ssam } 318*11570Ssam 319*11570Ssam /* 320*11570Ssam * Free an arptab entry. 321*11570Ssam */ 322*11570Ssam arptfree(at) 323*11570Ssam register struct arptab *at; 324*11570Ssam { 325*11570Ssam int s = splimp(); 326*11570Ssam 327*11570Ssam if (at->at_hold) 328*11570Ssam m_freem(at->at_hold); 329*11570Ssam at->at_hold = 0; 330*11570Ssam at->at_timer = at->at_flags = 0; 331*11570Ssam at->at_iaddr.s_addr = 0; 332*11570Ssam splx(s); 333*11570Ssam } 334*11570Ssam 335*11570Ssam /* 336*11570Ssam * Enter a new address in arptab, pushing out the oldest entry 337*11570Ssam * from the bucket if there is no room. 338*11570Ssam */ 339*11570Ssam struct arptab * 340*11570Ssam arptnew(addr) 341*11570Ssam struct in_addr *addr; 342*11570Ssam { 343*11570Ssam register n; 344*11570Ssam int oldest = 0; 345*11570Ssam register struct arptab *at, *ato; 346*11570Ssam 347*11570Ssam ato = at = &arptab[ARPTAB_HASH(addr->s_addr) * ARPTAB_BSIZ]; 348*11570Ssam for (n = 0 ; n < ARPTAB_BSIZ ; n++,at++) { 349*11570Ssam if (at->at_flags == 0) 350*11570Ssam goto out; /* found an empty entry */ 351*11570Ssam if (at->at_timer > oldest) { 352*11570Ssam oldest = at->at_timer; 353*11570Ssam ato = at; 354*11570Ssam } 355*11570Ssam } 356*11570Ssam at = ato; 357*11570Ssam arptfree(at); 358*11570Ssam out: 359*11570Ssam at->at_iaddr = *addr; 360*11570Ssam at->at_flags = ATF_INUSE; 361*11570Ssam return (at); 362*11570Ssam } 363