/********************************************************************** * Copyright (c) Digital Equipment Corporation 1984, 1985, 1986. * * All Rights Reserved. * * Reference "/usr/src/COPYRIGHT" for applicable restrictions. * **********************************************************************/ /* * Copyright (c) 1982 Regents of the University of California. * All rights reserved. The Berkeley software License Agreement * specifies the terms and conditions for redistribution. * * SCCSID: @(#)if_ether.c 3.0 4/21/86 * Based on: @(#)if_ether.c 6.10 (Berkeley) 6/8/85 */ /* * Ethernet address resolution protocol. */ #include <sys/param.h> #include <sys/systm.h> #include <sys/mbuf.h> #include <sys/socket.h> #include <errno.h> #include <sys/ioctl.h> #ifdef pdp11 extern int hz; #endif #include <net/if.h> #include <netinet/in.h> #include <netinet/in_systm.h> #include <netinet/ip.h> #include <netinet/if_ether.h> #define ARPTAB_BSIZ 5 /* bucket size */ #ifdef SEP_ID #define ARPTAB_NB 7 /* number of buckets */ #else #define ARPTAB_NB 3 /* number of buckets */ #endif SEP_ID #define ARPTAB_SIZE (ARPTAB_BSIZ * ARPTAB_NB) struct arptab arptab[ARPTAB_SIZE]; int arptab_size = ARPTAB_SIZE; /* for arp command */ #define ARPTAB_HASH(a) \ ((short)((((a) >> 16) ^ (a)) & 0x7fff) % ARPTAB_NB) #define ARPTAB_LOOK(at,addr) { \ register n; \ at = &arptab[ARPTAB_HASH(addr) * ARPTAB_BSIZ]; \ for (n = 0 ; n < ARPTAB_BSIZ ; n++,at++) \ if (at->at_iaddr.s_addr == addr) \ break; \ if (n >= ARPTAB_BSIZ) \ at = 0; } int arpt_age; /* aging timer */ /* timer values */ #define ARPT_AGE (60*1) /* aging timer, 1 min. */ #define ARPT_KILLC 20 /* kill completed entry in 20 mins. */ #define ARPT_KILLI 3 /* kill incomplete entry in 3 minutes */ u_char etherbroadcastaddr[6] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; extern struct ifnet loif; /* * Local addresses in the range oldmap to infinity are * mapped according to the old mapping scheme. That is, * mapping of Internet to Ethernet addresses is performed * by taking the high three bytes of the network interface's * address and the low three bytes of the local address part. * This only allows boards from the same manufacturer to * communicate unless the on-board address is overridden * (not possible in many manufacture's hardware). * * NB: setting oldmap to zero completely disables ARP * (i.e. identical to setting IFF_NOARP with an ioctl). */ int oldmap = 1024; /* * Timeout routine. Age arp_tab entries once a minute. */ arptimer() { register struct arptab *at; register i; timeout(arptimer, (caddr_t)0, hz); if (++arpt_age > ARPT_AGE) { arpt_age = 0; at = &arptab[0]; for (i = 0; i < ARPTAB_SIZE; i++, at++) { if (at->at_flags == 0 || (at->at_flags & ATF_PERM)) continue; if (++at->at_timer < ((at->at_flags&ATF_COM) ? ARPT_KILLC : ARPT_KILLI)) continue; /* timer has expired, clear entry */ arptfree(at); } } } /* * Broadcast an ARP packet, asking who has addr on interface ac. */ arpwhohas(ac, addr) register struct arpcom *ac; struct in_addr *addr; { register struct mbuf *m; register struct ether_header *eh; register struct ether_arp *ea; struct sockaddr sa; #ifdef pdp11 segm map; #endif pdp11 if ((m = m_get(M_DONTWAIT, MT_DATA)) == NULL) return (1); m->m_len = sizeof *ea; m->m_off = MMAXOFF - m->m_len; #ifdef pdp11 saveseg5(map); #endif pdp11 ea = mtod(m, struct ether_arp *); eh = (struct ether_header *)sa.sa_data; bzero((caddr_t)ea, sizeof (*ea)); bcopy((caddr_t)etherbroadcastaddr, (caddr_t)eh->ether_dhost, sizeof(eh->ether_dhost)); eh->ether_type = ETHERTYPE_ARP; /* if_output will swap */ ea->arp_hrd = HTONS(ARPHRD_ETHER); ea->arp_pro = HTONS(ETHERTYPE_IP); ea->arp_hln = sizeof(ea->arp_sha); /* hardware address length */ ea->arp_pln = sizeof(ea->arp_spa); /* protocol address length */ ea->arp_op = HTONS(ARPOP_REQUEST); bcopy((caddr_t)ac->ac_enaddr, (caddr_t)ea->arp_sha, sizeof(ea->arp_sha)); bcopy((caddr_t)&ac->ac_ipaddr, (caddr_t)ea->arp_spa, sizeof(ea->arp_spa)); bcopy((caddr_t)addr, (caddr_t)ea->arp_tpa, sizeof(ea->arp_tpa)); sa.sa_family = AF_UNSPEC; #ifdef pdp11 restorseg5(map); #endif pdp11 return ((*ac->ac_if.if_output)(&ac->ac_if, m, &sa)); } /* * Resolve an IP address into an ethernet address. If success, * desten is filled in and 1 is returned. If there is no entry * in arptab, set one up and broadcast a request * for the IP address; return 0. Hold onto this mbuf and * resend it once the address is finally resolved. * * We do some (conservative) locking here at splimp, since * arptab is also altered from input interrupt service (ecintr/ilintr * calls arpinput when ETHERTYPE_ARP packets come in). */ arpresolve(ac, m, destip, desten) register struct arpcom *ac; struct mbuf *m; register struct in_addr *destip; register u_char *desten; { register struct arptab *at; register struct ifnet *ifp; register int i; struct sockaddr_in sin; #ifdef pdp11 int s; long lna; #else vax int s, lna; #endif if (in_broadcast(*destip)) { /* broadcast address */ bcopy((caddr_t)etherbroadcastaddr, (caddr_t)desten, sizeof(etherbroadcastaddr)); return (1); } lna = in_lnaof(*destip); ifp = &ac->ac_if; /* if for us, then use software loopback driver */ if (destip->s_addr == ac->ac_ipaddr.s_addr && (loif.if_flags & IFF_UP)) { sin.sin_family = AF_INET; sin.sin_addr = *destip; (void) looutput(&loif, m, (struct sockaddr *)&sin); /* * We really don't want to indicate failure, * but the packet has already been sent and freed. */ return (0); } s = splimp(); ARPTAB_LOOK(at, destip->s_addr); if (at == 0) { /* not found */ if ((ifp->if_flags & IFF_NOARP) || lna >= oldmap) { bcopy((caddr_t)ac->ac_enaddr, (caddr_t)desten, 3); desten[3] = (lna >> 16) & 0x7f; desten[4] = (lna >> 8) & 0xff; desten[5] = lna & 0xff; splx(s); return (1); } else { at = arptnew(destip); at->at_hold = m; arpwhohas(ac, destip); splx(s); return (0); } } at->at_timer = 0; /* restart the timer */ if (at->at_flags & ATF_COM) { /* entry IS complete */ bcopy((caddr_t)at->at_enaddr, (caddr_t)desten, sizeof(at->at_enaddr)); splx(s); return (1); } /* * There is an arptab entry, but no ethernet address * response yet. Replace the held mbuf with this * latest one. */ if (at->at_hold) m_freem(at->at_hold); at->at_hold = m; arpwhohas(ac, destip); /* ask again */ splx(s); return (0); } /* * Called from 10 Mb/s Ethernet interrupt handlers * when ether packet type ETHERTYPE_ARP * is received. Algorithm is that given in RFC 826. * In addition, a sanity check is performed on the sender * protocol address, to catch impersonators. */ arpinput(ac, m) register struct arpcom *ac; struct mbuf *m; { register struct ether_arp *ea; struct ether_header *eh; register struct arptab *at = 0; /* same as "merge" flag */ struct sockaddr_in sin; struct sockaddr sa; struct in_addr isaddr,itaddr,myaddr; #ifdef pdp11 segm map; #endif pdp11 #ifdef pdp11 saveseg5(map); #endif pdp11 if (m->m_len < sizeof *ea) goto out; if (ac->ac_if.if_flags & IFF_NOARP) goto out; myaddr = ac->ac_ipaddr; ea = mtod(m, struct ether_arp *); if (ea->arp_pro != NTOHS(ETHERTYPE_IP)) goto out; isaddr.s_addr = ((struct in_addr *)ea->arp_spa)->s_addr; itaddr.s_addr = ((struct in_addr *)ea->arp_tpa)->s_addr; if (!bcmp((caddr_t)ea->arp_sha, (caddr_t)ac->ac_enaddr, sizeof (ea->arp_sha))) goto out; /* it's from me, ignore it. */ if (isaddr.s_addr == myaddr.s_addr) { printf("duplicate IP address!! sent from ethernet address: "); printf("%x %x %x %x %x %x\n", ea->arp_sha[0], ea->arp_sha[1], ea->arp_sha[2], ea->arp_sha[3], ea->arp_sha[4], ea->arp_sha[5]); itaddr = myaddr; if (ea->arp_op == NTOHS(ARPOP_REQUEST)) { bcopy((caddr_t)ac->ac_enaddr, (caddr_t)ea->arp_sha, sizeof(ea->arp_sha)); goto reply; } goto out; } ARPTAB_LOOK(at, isaddr.s_addr); if (at && (at->at_flags & ATF_COM) == 0) { bcopy((caddr_t)ea->arp_sha, (caddr_t)at->at_enaddr, sizeof(ea->arp_sha)); at->at_flags |= ATF_COM; if (at->at_hold) { sin.sin_family = AF_INET; sin.sin_addr = isaddr; (*ac->ac_if.if_output)(&ac->ac_if, at->at_hold, (struct sockaddr *)&sin); at->at_hold = 0; } } if (at == 0 && itaddr.s_addr == myaddr.s_addr) { /* ensure we have a table entry */ at = arptnew(&isaddr); bcopy((caddr_t)ea->arp_sha, (caddr_t)at->at_enaddr, sizeof(ea->arp_sha)); at->at_flags |= ATF_COM; } if (ea->arp_op != NTOHS(ARPOP_REQUEST)) goto out; ARPTAB_LOOK(at, itaddr.s_addr); if (at == NULL) { if (itaddr.s_addr != myaddr.s_addr) goto out; /* if I am not the target */ at = arptnew(&myaddr); bcopy((caddr_t)ac->ac_enaddr, (caddr_t)at->at_enaddr, sizeof(at->at_enaddr)); at->at_flags |= ATF_COM; } if (itaddr.s_addr != myaddr.s_addr && (at->at_flags & ATF_PUBL) == 0) goto out; reply: bcopy((caddr_t)ea->arp_sha, (caddr_t)ea->arp_tha, sizeof(ea->arp_sha)); bcopy((caddr_t)ea->arp_spa, (caddr_t)ea->arp_tpa, sizeof(ea->arp_spa)); if (at) /* done above if at == 0 */ bcopy((caddr_t)at->at_enaddr, (caddr_t)ea->arp_sha, sizeof(ea->arp_sha)); bcopy((caddr_t)&itaddr, (caddr_t)ea->arp_spa, sizeof(ea->arp_spa)); ea->arp_op = HTONS(ARPOP_REPLY); eh = (struct ether_header *)sa.sa_data; bcopy((caddr_t)ea->arp_tha, (caddr_t)eh->ether_dhost, sizeof(eh->ether_dhost)); eh->ether_type = ETHERTYPE_ARP; sa.sa_family = AF_UNSPEC; #ifdef pdp11 restorseg5(map); #endif pdp11 (*ac->ac_if.if_output)(&ac->ac_if, m, &sa); return; out: m_freem(m); #ifdef pdp11 restorseg5(map); #endif pdp11 return; } /* * Free an arptab entry. */ static arptfree(at) register struct arptab *at; { int s = splimp(); if (at->at_hold) m_freem(at->at_hold); at->at_hold = 0; at->at_timer = at->at_flags = 0; at->at_iaddr.s_addr = 0; splx(s); } /* * Enter a new address in arptab, pushing out the oldest entry * from the bucket if there is no room. * This always succeeds since no bucket can be completely filled * with permanent entries (except from arpioctl when testing whether * another permanent entry will fit). * * The "oldest" variable used to be initialized to 0, but then it * would think a bucket was full if it got in a whole slew of entries * at one time, which can happen on a large network with a small * arp table and a broadcast request... 8/8/85 -Dave Borman */ static struct arptab * arptnew(addr) struct in_addr *addr; { register n; int oldest = -1; register struct arptab *at, *ato = NULL; static int first = 1; if (first) { first = 0; timeout(arptimer, (caddr_t)0, hz); } at = &arptab[ARPTAB_HASH(addr->s_addr) * ARPTAB_BSIZ]; for (n = 0; n < ARPTAB_BSIZ; n++,at++) { if (at->at_flags == 0) goto out; /* found an empty entry */ if (at->at_flags & ATF_PERM) continue; if ((int)at->at_timer > oldest) { oldest = at->at_timer; ato = at; } } if (ato == NULL) return (NULL); at = ato; arptfree(at); out: at->at_iaddr = *addr; at->at_flags = ATF_INUSE; return (at); } arpioctl(cmd, data) int cmd; caddr_t data; { register struct arpreq *ar = (struct arpreq *)data; register struct arptab *at; register struct sockaddr_in *sin; int s; if (ar->arp_pa.sa_family != AF_INET || ar->arp_ha.sa_family != AF_UNSPEC) return (EAFNOSUPPORT); sin = (struct sockaddr_in *)&ar->arp_pa; s = splimp(); ARPTAB_LOOK(at, sin->sin_addr.s_addr); if (at == NULL) { /* not found */ if (cmd != SIOCSARP) { splx(s); return (ENXIO); } if (ifa_ifwithnet(&ar->arp_pa) == NULL) { splx(s); return (ENETUNREACH); } } switch (cmd) { case SIOCSARP: /* set entry */ if (at == NULL) { at = arptnew(&sin->sin_addr); if (ar->arp_flags & ATF_PERM) { /* never make all entries in a bucket permanent */ register struct arptab *tat; /* try to re-allocate */ tat = arptnew(&sin->sin_addr); if (tat == NULL) { arptfree(at); splx(s); return (EADDRNOTAVAIL); } arptfree(tat); } } bcopy((caddr_t)ar->arp_ha.sa_data, (caddr_t)at->at_enaddr, sizeof(at->at_enaddr)); at->at_flags = ATF_COM | ATF_INUSE | (ar->arp_flags & (ATF_PERM|ATF_PUBL)); at->at_timer = 0; break; case SIOCDARP: /* delete entry */ arptfree(at); break; case SIOCGARP: /* get entry */ bcopy((caddr_t)at->at_enaddr, (caddr_t)ar->arp_ha.sa_data, sizeof(at->at_enaddr)); ar->arp_flags = at->at_flags; break; } splx(s); return (0); }