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