Date: Mon, 05 Jul 2004 17:46:27 +0400 From: Sergey Matveychuk <sem@ciam.ru> To: FreeBSD-gnats-submit@FreeBSD.org Cc: andre@FreeBSD.org Subject: kern/68692: Move ARP out of routing table Message-ID: <E1BhTnf-000OCF-MT@mail.ciam.ru> Resent-Message-ID: <200407051350.i65DoKAV082851@freefall.freebsd.org>
next in thread | raw e-mail | index | archive | help
>Number: 68692 >Category: kern >Synopsis: Move ARP out of routing table >Confidential: no >Severity: non-critical >Priority: low >Responsible: freebsd-bugs >State: open >Quarter: >Keywords: >Date-Required: >Class: change-request >Submitter-Id: current-users >Arrival-Date: Mon Jul 05 13:50:20 GMT 2004 >Closed-Date: >Last-Modified: >Originator: Sergey Matveychuk >Release: FreeBSD 5.2-CURRENT i386 >Organization: >Environment: System: FreeBSD proxy.ciam.ru 5.2-CURRENT FreeBSD 5.2-CURRENT #3: Wed Jun 22 23:30:55 MSD 2004 root@orion.ciam.ru:/usr/src/sys/compile/PROXY i386 >Description: Patch from http://lists.freebsd.org/pipermail/freebsd-current/2004-April/026380.html adapted for and tested on -CURRENT (20040608). I've tried to connect with both luigi@FreeBSD.org and andre@FreeBSD.org but got no answer for a month. So I'm send-pr'ing this patch. >How-To-Repeat: >Fix: --- new_arp.patch begins here --- diff -ruN src.orig/sys/net/route.c src/sys/net/route.c --- src.orig/sys/net/route.c Tue Jun 8 11:48:13 2004 +++ src/sys/net/route.c Tue Jun 8 12:11:09 2004 @@ -42,6 +42,7 @@ #include <sys/kernel.h> #include <net/if.h> +#include <net/if_dl.h> /* for sockaddr_dl */ #include <net/route.h> #include <netinet/in.h> @@ -1105,9 +1106,13 @@ bzero((caddr_t)cp2, (unsigned)(cplim2 - cp2)); } +void arp_ifscrub(struct ifnet *ifp, uint32_t addr); + /* * Set up a routing table entry, normally * for an interface. + * Instead of the destination address, use a sockaddr_dl for the + * gateway, using the index and type of the interface. */ int rtinit(struct ifaddr *ifa, int cmd, int flags) @@ -1118,6 +1123,7 @@ struct rtentry *rt = NULL; struct rt_addrinfo info; int error; + static struct sockaddr_dl null_sdl = {sizeof(null_sdl), AF_LINK}; if (flags & RTF_HOST) { dst = ifa->ifa_dstaddr; @@ -1126,6 +1132,13 @@ dst = ifa->ifa_addr; netmask = ifa->ifa_netmask; } + printf("rtinit cmd %d flags 0x%x, ifa_ifp %p dst %d:0x%x gw %d:0x%x\n", + cmd, flags, ifa->ifa_ifp, + dst->sa_family, + ntohl(((struct sockaddr_in *)dst)->sin_addr.s_addr), + ifa->ifa_addr->sa_family, + ntohl(((struct sockaddr_in *)ifa->ifa_addr)->sin_addr.s_addr)); + /* * If it's a delete, check that if it exists, it's on the correct * interface or we might scrub a route to another ifa which would @@ -1136,6 +1149,9 @@ struct radix_node_head *rnh; struct radix_node *rn; + if (dst->sa_family == AF_INET) + arp_ifscrub(ifa->ifa_ifp, + ((struct sockaddr_in *)dst)->sin_addr.s_addr); /* * It's a delete, so it should already exist.. * If it's a net, mask off the host bits @@ -1175,10 +1191,14 @@ info.rti_ifa = ifa; info.rti_flags = flags | ifa->ifa_flags; info.rti_info[RTAX_DST] = dst; - info.rti_info[RTAX_GATEWAY] = ifa->ifa_addr; + info.rti_info[RTAX_GATEWAY] = (struct sockaddr *)&null_sdl; info.rti_info[RTAX_NETMASK] = netmask; error = rtrequest1(cmd, &info, &rt); if (error == 0 && rt != NULL) { + ((struct sockaddr_dl *)rt->rt_gateway)->sdl_type = + rt->rt_ifp->if_type; + ((struct sockaddr_dl *)rt->rt_gateway)->sdl_index = + rt->rt_ifp->if_index; /* * notify any listening routing agents of the change */ diff -ruN src.orig/sys/net/rtsock.c src/sys/net/rtsock.c --- src.orig/sys/net/rtsock.c Tue Jun 8 11:48:15 2004 +++ src/sys/net/rtsock.c Tue Jun 8 12:11:09 2004 @@ -93,6 +93,10 @@ struct rt_metrics *out); static void rt_dispatch(struct mbuf *, const struct sockaddr *); +/* support new arp code */ +int arp_rt_output(struct rt_msghdr *rtm, struct rt_addrinfo *info); +int sysctl_dumparp(int af, struct sysctl_req *wr); + /* * It really doesn't make any sense at all for this code to share much * with raw_usrreq.c, since its functionality is so restricted. XXX @@ -277,6 +281,8 @@ sosend, soreceive, sopoll, pru_sosetlabel_null }; + + /*ARGSUSED*/ static int route_output(struct mbuf *m, struct socket *so) @@ -353,6 +359,11 @@ if (info.rti_info[RTAX_GATEWAY] == NULL) senderr(EINVAL); saved_nrt = NULL; + if (info.rti_info[RTAX_GATEWAY]->sa_family == AF_LINK) { + /* support for new ARP code */ + arp_rt_output(rtm, &info); + break; + } error = rtrequest1(RTM_ADD, &info, &saved_nrt); if (error == 0 && saved_nrt) { RT_LOCK(saved_nrt); @@ -366,6 +377,11 @@ case RTM_DELETE: saved_nrt = NULL; + if (info.rti_info[RTAX_GATEWAY]->sa_family == AF_LINK) { + /* support for new ARP code */ + arp_rt_output(rtm, &info); + break; + } error = rtrequest1(RTM_DELETE, &info, &saved_nrt); if (error == 0) { RT_LOCK(saved_nrt); @@ -1081,6 +1097,7 @@ int i, lim, s, error = EINVAL; u_char af; struct walkarg w; + int found = 0; name ++; namelen--; @@ -1112,8 +1129,17 @@ error = rnh->rnh_walktree(rnh, sysctl_dumpentry, &w);/* could sleep XXX */ /* RADIX_NODE_HEAD_UNLOCK(rnh); */ - } else if (af != 0) - error = EAFNOSUPPORT; + if (error) + break; + found = 1; + } + /* + * take care of llinfo entries. XXX check AF_INET ? + */ + if (w.w_op == NET_RT_FLAGS && (RTF_LLINFO & w.w_arg)) + error = sysctl_dumparp(af, w.w_req); + else if (af != 0 && found == 0) + error = EAFNOSUPPORT; break; case NET_RT_IFLIST: diff -ruN src.orig/sys/netinet/if_ether.c src/sys/netinet/if_ether.c --- src.orig/sys/netinet/if_ether.c Tue Jun 8 11:48:18 2004 +++ src/sys/netinet/if_ether.c Tue Jun 8 12:20:44 2004 @@ -27,7 +27,7 @@ * SUCH DAMAGE. * * @(#)if_ether.c 8.1 (Berkeley) 6/10/93 - * $FreeBSD: src/sys/netinet/if_ether.c,v 1.127 2004/04/25 15:00:17 luigi Exp $ + * $FreeBSD$ */ /* @@ -101,7 +101,6 @@ static LIST_HEAD(, llinfo_arp) llinfo_arp; static struct ifqueue arpintrq; -static int arp_allocated; static int arp_maxtries = 5; static int useloopback = 1; /* use loopback interface for local traffic */ @@ -116,18 +115,303 @@ &arp_proxyall, 0, ""); static void arp_init(void); -static void arp_rtrequest(int, struct rtentry *, struct rt_addrinfo *); static void arprequest(struct ifnet *, struct in_addr *, struct in_addr *, u_char *); static void arpintr(struct mbuf *); static void arptfree(struct llinfo_arp *); static void arptimer(void *); -static struct llinfo_arp - *arplookup(u_long, int, int); +struct llentry *arplookup(struct ifnet *ifp, uint32_t addr, uint32_t flags); #ifdef INET static void in_arpinput(struct mbuf *); #endif +/*** + *** + *** Start of new arp support routines which should go to a separate file. + *** + ***/ +#define DEB(x) +#define DDB(x) x + +struct llentry { + struct llentry *lle_next; + struct mbuf *la_hold; + uint16_t flags; /* see values in if_ether.h */ + uint8_t la_preempt; + uint8_t la_asked; + time_t expire; + struct in_addr l3_addr; + union { + uint64_t mac_aligned; + uint16_t mac16[3]; + } ll_addr; +}; + +MALLOC_DEFINE(M_ARP, "arp", "arp entries"); /* XXX will move to UMA */ + +int arp_rt_output(struct rt_msghdr *rtm, struct rt_addrinfo *info); +int sysctl_dumparp(int af, struct sysctl_req *wr); +void arp_ifscrub(struct ifnet *ifp, uint32_t addr); + +/* + * called by in_ifscrub to remove entry from the table when + * the interface goes away + */ +void +arp_ifscrub(struct ifnet *ifp, uint32_t addr) +{ + arplookup(ifp, addr, LLE_DELETE | LLE_IFADDR); +} + +/* + * Find an interface address matching the ifp-addr pair. + * This may replicate some of the functions of ifa_ifwithnet() + */ +static struct ifaddr * +find_ifa(struct ifnet *ifp, uint32_t addr) +{ + struct ifaddr *ifa; + + if (ifp == NULL) + return NULL; + TAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) { + if (ifa->ifa_addr->sa_family != AF_INET) + continue; + if (ifp->if_flags & IFF_POINTOPOINT) + break; + if (((addr ^ SIN(ifa->ifa_addr)->sin_addr.s_addr) & + SIN(ifa->ifa_netmask)->sin_addr.s_addr ) == 0) + break; /* found! */ + } + return ifa; +} + +static void +llentry_free(struct llentry **e) +{ + struct llentry *x; + + if (e == 0) + panic("llentry_free: null ptr"); + x = *e; + *e = x->lle_next; + if (x->la_hold) + m_freem(x->la_hold); + free(x, M_ARP); +} + +/* + * Add a new table at the head of the list for interface ifp + */ +struct lltable * +lltable_new(struct ifnet *ifp, int af) +{ + struct lltable *t; + + t = malloc(sizeof (struct lltable), M_ARP, M_DONTWAIT | M_ZERO); + if (t != NULL) { + t->llt_next = ifp->lltables; + t->llt_af = af; + ifp->lltables = t; + } + return t; +} + +struct lltable ** +lltable_free(struct lltable **t) +{ + struct lltable *x; + + if (t == NULL) + panic("lltable_free: null ptr"); + x = *t; + *t = x->llt_next; + free(x, M_ARP); + return t; +} + +static void +newarptimer(__unused void *ignored_arg) +{ + struct lltable *t; + struct llentry **e; + struct ifnet *ifp; + + IFNET_RLOCK(); + printf("arptimer!\n"); + TAILQ_FOREACH(ifp, &ifnet, if_link) { + for (t = ifp->lltables; t ; t = t->llt_next) { + if (t->llt_af != AF_INET) + continue; + for (e = (struct llentry **)&t->lle_head; *e; ) { + int kill; + + if ((*e)->flags & LLE_DELETED) + kill = 1; + else if ((*e)->flags & LLE_STATIC) + kill = 0; + else + kill = time_second >= (*e)->expire; + if (kill) + llentry_free(e); + else + e = &((*e)->lle_next); + } + } + } + IFNET_RUNLOCK(); + callout_reset(&arp_callout, arpt_prune * hz, newarptimer, NULL); +} + +static int +inet_dumparp(struct ifnet *ifp, void *head, struct sysctl_req *wr) +{ + struct llentry *e; + int error = 0; + + for (e = head; e; e = e->lle_next) { + struct { + struct rt_msghdr rtm; + struct sockaddr_inarp sin2; + struct sockaddr_dl sdl; + //struct sockaddr_inarp addr2; + } d; + + DEB(printf("ifp %p index %d flags 0x%x ip %x %s\n", + ifp, ifp->if_index, + e->flags, + ntohl(e->l3_addr.s_addr), + (e->flags & LLA_VALID) ? "valid" : "incomplete");) + if (e->flags & LLE_DELETED) /* skip deleted entries */ + continue; + /* + * produce a msg made of: + * struct rt_msghdr; + * struct sockaddr_inarp; + * struct sockaddr_dl; + */ + bzero(&d, sizeof (d)); + d.rtm.rtm_msglen = sizeof(d); + d.sin2.sin_family = AF_INET; + d.sin2.sin_len = sizeof(d.sin2); + d.sin2.sin_addr.s_addr = e->l3_addr.s_addr; + + if (e->flags & LLA_VALID) { /* valid MAC */ + d.sdl.sdl_family = AF_LINK; + d.sdl.sdl_len = sizeof(d.sdl); + d.sdl.sdl_alen = ifp->if_addrlen; + d.sdl.sdl_index = ifp->if_index; + d.sdl.sdl_type = ifp->if_type; + bcopy(&e->ll_addr, LLADDR(&d.sdl), ifp->if_addrlen); + } + d.rtm.rtm_rmx.rmx_expire = + e->flags & LLE_STATIC ? 0 : e->expire; + d.rtm.rtm_flags = RTF_LLINFO; + if (e->flags & LLE_STATIC) + d.rtm.rtm_flags |= RTF_STATIC; + d.rtm.rtm_index = ifp->if_index; + error = SYSCTL_OUT(wr, &d, sizeof(d)); + if (error) + break; + } + return error; +} + +/* + * glue to dump arp tables + */ +int +sysctl_dumparp(int af, struct sysctl_req *wr) +{ + struct lltable *t; + struct ifnet *ifp; + int error = 0; + + IFNET_RLOCK(); + TAILQ_FOREACH(ifp, &ifnet, if_link) { + for (t = ifp->lltables; t ; t = t->llt_next) { + if (af != 0 && t->llt_af != af) + continue; + switch (af) { + case AF_INET: + error = inet_dumparp(ifp, t->lle_head, wr); + break; + /* other handlers, if any */ + } + if (error) + goto done; + } + } +done: + IFNET_RUNLOCK(); + return (error); +} + +/* + * Called in route_output when adding/deleting a route to an interface. + */ +int +arp_rt_output(struct rt_msghdr *rtm, struct rt_addrinfo *info) +{ + struct sockaddr_dl *dl = + (struct sockaddr_dl *)info->rti_info[RTAX_GATEWAY]; + struct sockaddr_in *dst = + (struct sockaddr_in *)info->rti_info[RTAX_DST]; + struct ifnet *ifp; + struct llentry *la; + u_int flags; + + printf("arp_rt_output type %d af: gw %d dst %d:%x if_index %d\n", + rtm->rtm_type, + dl ? dl->sdl_family : 0, + dst ? dst->sin_family : 0, + dst && dst->sin_family == AF_INET ? + ntohl(dst->sin_addr.s_addr) : 0, + dl ? dl->sdl_index : 0); + if (dl == NULL || dl->sdl_family != AF_LINK) { + /* XXX should also check (dl->sdl_index < if_indexlim) */ + printf("invalid gateway/index\n"); + return EINVAL; + } + ifp = ifnet_byindex(dl->sdl_index); + if (ifp == NULL) { + printf("invalid ifp\n"); + return EINVAL; + } + + switch (rtm->rtm_type) { + case RTM_ADD: + flags = LLE_CREATE; + break; + + case RTM_CHANGE: + default: + return EINVAL; /* XXX not implemented yet */ + + case RTM_DELETE: + flags = LLE_DELETE; + break; + } + la = arplookup(ifp, dst->sin_addr.s_addr, flags); + if (la == NULL) { + bcopy(LLADDR(dl), &la->ll_addr, ifp->if_addrlen); + la->flags |= LLA_VALID; + if (rtm->rtm_flags & RTF_STATIC) + la->flags |= LLE_STATIC; + else + la->expire = time_second + arpt_keep; + } + return 0; +} + + + +/*** + *** + *** End of new arp support routines which should go to a separate file. + *** + ***/ + /* * Timeout routine. Age arp_tab entries periodically. */ @@ -152,6 +436,9 @@ callout_reset(&arp_callout, arpt_prune * hz, arptimer, NULL); } +#if 0 /* this is unused */ +static int arp_allocated; + /* * Parallel to llc_rtrequest. */ @@ -284,6 +571,7 @@ Free((caddr_t)la); } } +#endif /* arp_rtrequest unused */ /* * Broadcast an ARP request. Caller specifies: @@ -301,6 +589,28 @@ struct arphdr *ah; struct sockaddr sa; + if (sip == NULL) { + /* + * The caller did not supply a source address, try to find + * a compatible one among those assigned to this interface. + */ + struct ifaddr *ifa; + + TAILQ_FOREACH(ifa, &ifp->if_addrhead, ifa_link) { + if (!ifa->ifa_addr || + ifa->ifa_addr->sa_family != AF_INET) + continue; + sip = &SIN(ifa->ifa_addr)->sin_addr; + if (0 == ((sip->s_addr ^ tip->s_addr) & + SIN(ifa->ifa_netmask)->sin_addr.s_addr) ) + break; /* found it. */ + } + } + if (sip == NULL) { + printf(" cannot find matching address, no arprequest\n"); + return; + } + if ((m = m_gethdr(M_DONTWAIT, MT_DATA)) == NULL) return; m->m_len = sizeof(*ah) + 2*sizeof(struct in_addr) + @@ -344,16 +654,11 @@ arpresolve(struct ifnet *ifp, struct rtentry *rt0, struct mbuf *m, struct sockaddr *dst, u_char *desten) { - struct llinfo_arp *la = 0; + struct llentry *la = 0; struct sockaddr_dl *sdl; - int error; struct rtentry *rt; - - error = rt_check(&rt, &rt0, dst); - if (error) { - m_freem(m); - return error; - } + u_int flags = (ifp->if_flags & (IFF_NOARP | IFF_STATICARP)) ? + 0 : LLE_CREATE; if (m->m_flags & M_BCAST) { /* broadcast */ (void)memcpy(desten, ifp->if_broadcastaddr, ifp->if_addrlen); @@ -363,51 +668,39 @@ ETHER_MAP_IP_MULTICAST(&SIN(dst)->sin_addr, desten); return (0); } - if (rt) - la = (struct llinfo_arp *)rt->rt_llinfo; - if (la == 0) { - la = arplookup(SIN(dst)->sin_addr.s_addr, 1, 0); - if (la) - rt = la->la_rt; - } - if (la == 0 || rt == 0) { - log(LOG_DEBUG, "arpresolve: can't allocate llinfo for %s%s%s\n", - inet_ntoa(SIN(dst)->sin_addr), la ? "la" : "", - rt ? "rt" : ""); + la = arplookup(ifp, SIN(dst)->sin_addr.s_addr, flags); + if (la == NULL) { + if (flags & LLE_CREATE) + log(LOG_DEBUG, + "arpresolve: can't allocate llinfo for %s\n", + inet_ntoa(SIN(dst)->sin_addr)); m_freem(m); return (EINVAL); /* XXX */ } sdl = SDL(rt->rt_gateway); /* - * Check the address family and length is valid, the address - * is resolved; otherwise, try to resolve. + * If the entry is valid and not expired, use it. */ - if ((rt->rt_expire == 0 || rt->rt_expire > time_second) && - sdl->sdl_family == AF_LINK && sdl->sdl_alen != 0) { + if (la->flags & LLA_VALID && + (la->flags & LLE_STATIC || la->expire > time_second)) { + bcopy(&la->ll_addr, desten, ifp->if_addrlen); /* * If entry has an expiry time and it is approaching, * see if we need to send an ARP request within this * arpt_down interval. */ - if ((rt->rt_expire != 0) && - (time_second + la->la_preempt > rt->rt_expire)) { - arprequest(ifp, - &SIN(rt->rt_ifa->ifa_addr)->sin_addr, - &SIN(dst)->sin_addr, - IF_LLADDR(ifp)); + if (!(la->flags & LLE_STATIC) && + time_second + la->la_preempt > la->expire) { + arprequest(ifp, NULL, + &SIN(dst)->sin_addr, IF_LLADDR(ifp)); la->la_preempt--; } - bcopy(LLADDR(sdl), desten, sdl->sdl_alen); return (0); } - /* - * If ARP is disabled or static on this interface, stop. - * XXX - * Probably should not allocate empty llinfo struct if we are - * not going to be sending out an arp request. - */ - if (ifp->if_flags & (IFF_NOARP | IFF_STATICARP)) { + if (la->flags & LLE_STATIC) { /* should not happen! */ + log(LOG_DEBUG, "arpresolve: ouch, empty static llinfo for %s\n", + inet_ntoa(SIN(dst)->sin_addr)); m_freem(m); return (EINVAL); } @@ -419,26 +712,26 @@ if (la->la_hold) m_freem(la->la_hold); la->la_hold = m; - if (rt->rt_expire) { - RT_LOCK(rt); - rt->rt_flags &= ~RTF_REJECT; - if (la->la_asked == 0 || rt->rt_expire != time_second) { - rt->rt_expire = time_second; - if (la->la_asked++ < arp_maxtries) { - arprequest(ifp, - &SIN(rt->rt_ifa->ifa_addr)->sin_addr, - &SIN(dst)->sin_addr, - IF_LLADDR(ifp)); - } else { - rt->rt_flags |= RTF_REJECT; - rt->rt_expire += arpt_down; - la->la_asked = 0; - la->la_preempt = arp_maxtries; - } - + /* + * Now implement the logic to issue requests -- we can send up + * to arp_maxtries with a 1-sec spacing, followed by a pause + * of arpt_down seconds if no replies are coming back. + * Take the chance to enforce limits on arp_maxtries and arpt_down + */ + if (la->expire <= time_second) { /* ok, expired */ + if (arp_maxtries > 100) /* enforce a sane limit */ + arp_maxtries = 100; + else if (arp_maxtries < 3) + arp_maxtries = 3; + if (la->la_asked++ < arp_maxtries) + la->expire = time_second + 1; + else { + la->la_asked = 0; + la->expire = time_second + arpt_down; + la->la_preempt = arp_maxtries; } - RT_UNLOCK(rt); - } + arprequest(ifp, NULL, &SIN(dst)->sin_addr, IF_LLADDR(ifp)); + } return (EWOULDBLOCK); } @@ -518,16 +811,12 @@ { struct arphdr *ah; struct ifnet *ifp = m->m_pkthdr.rcvif; - struct iso88025_header *th = (struct iso88025_header *)0; - struct iso88025_sockaddr_dl_data *trld; - struct llinfo_arp *la = 0; - struct rtentry *rt; + struct llentry *la = 0; struct ifaddr *ifa; struct in_ifaddr *ia; - struct sockaddr_dl *sdl; struct sockaddr sa; struct in_addr isaddr, itaddr, myaddr; - int op, rif_len; + int op; int req_len; req_len = arphdr_len2(ifp->if_addrlen, sizeof(struct in_addr)); @@ -540,6 +829,19 @@ op = ntohs(ah->ar_op); (void)memcpy(&isaddr, ar_spa(ah), sizeof (isaddr)); (void)memcpy(&itaddr, ar_tpa(ah), sizeof (itaddr)); + /* + * sanity check for the address length. + * XXX this does not work for protocols with variable address + * length. -is + */ + if (ifp->if_addrlen != ah->ar_hln) { + log(LOG_WARNING, + "arp from %*D: addr len: new %d, i/f %d (ignored)", + ifp->if_addrlen, (u_char *) ar_sha(ah), ":", + ah->ar_hln, ifp->if_addrlen); + goto drop; + } + #ifdef BRIDGE #define BRIDGE_TEST (do_bridge) #else @@ -592,62 +894,41 @@ } if (ifp->if_flags & IFF_STATICARP) goto reply; - la = arplookup(isaddr.s_addr, itaddr.s_addr == myaddr.s_addr, 0); - if (la && (rt = la->la_rt) && (sdl = SDL(rt->rt_gateway))) { - /* the following is not an error when doing bridging */ - if (!BRIDGE_TEST && rt->rt_ifp != ifp) { - if (log_arp_wrong_iface) - log(LOG_ERR, "arp: %s is on %s but got reply from %*D on %s\n", - inet_ntoa(isaddr), - rt->rt_ifp->if_xname, - ifp->if_addrlen, (u_char *)ar_sha(ah), ":", - ifp->if_xname); - goto reply; - } - if (sdl->sdl_alen && - bcmp(ar_sha(ah), LLADDR(sdl), sdl->sdl_alen)) { - if (rt->rt_expire) { - if (log_arp_movements) - log(LOG_INFO, "arp: %s moved from %*D to %*D on %s\n", - inet_ntoa(isaddr), - ifp->if_addrlen, (u_char *)LLADDR(sdl), ":", - ifp->if_addrlen, (u_char *)ar_sha(ah), ":", - ifp->if_xname); - } else { + /* Look up the source. If I am the target, create an entry for it. */ + la = arplookup(ifp, isaddr.s_addr, + (itaddr.s_addr == myaddr.s_addr) ? LLE_CREATE : 0); + if (la != NULL) { + /* We have a valid entry. Check and store the MAC. */ + if (la->flags & LLA_VALID && + bcmp(ar_sha(ah), &la->ll_addr, ifp->if_addrlen)) { + if (la->flags & LLE_STATIC) { log(LOG_ERR, "arp: %*D attempts to modify permanent entry for %s on %s\n", ifp->if_addrlen, (u_char *)ar_sha(ah), ":", inet_ntoa(isaddr), ifp->if_xname); goto reply; } + if (log_arp_movements) + log(LOG_INFO, "arp: %s moved from %*D to %*D on %s\n", + inet_ntoa(isaddr), + ifp->if_addrlen, (u_char *)&la->ll_addr, ":", + ifp->if_addrlen, (u_char *)ar_sha(ah), ":", + ifp->if_xname); } - /* - * sanity check for the address length. - * XXX this does not work for protocols with variable address - * length. -is - */ - if (sdl->sdl_alen && - sdl->sdl_alen != ah->ar_hln) { - log(LOG_WARNING, - "arp from %*D: new addr len %d, was %d", - ifp->if_addrlen, (u_char *) ar_sha(ah), ":", - ah->ar_hln, sdl->sdl_alen); - } - if (ifp->if_addrlen != ah->ar_hln) { - log(LOG_WARNING, - "arp from %*D: addr len: new %d, i/f %d (ignored)", - ifp->if_addrlen, (u_char *) ar_sha(ah), ":", - ah->ar_hln, ifp->if_addrlen); - goto reply; - } - (void)memcpy(LLADDR(sdl), ar_sha(ah), - sdl->sdl_alen = ah->ar_hln); + bcopy(ar_sha(ah), &la->ll_addr, ifp->if_addrlen); + la->flags |= LLA_VALID; +#if 0 /* XXX this needs to be fixed */ /* * If we receive an arp from a token-ring station over * a token-ring nic then try to save the source * routing info. */ if (ifp->if_type == IFT_ISO88025) { + struct iso88025_header *th; + struct iso88025_sockaddr_dl_data *trld; + struct sockaddr_dl *sdl; + int rif_len; + th = (struct iso88025_header *)m->m_pkthdr.header; trld = SDL_ISO88025(sdl); rif_len = TR_RCF_RIFLEN(th->rcf); @@ -673,15 +954,20 @@ m->m_pkthdr.len += 8; th->rcf = trld->trld_rcf; } - RT_LOCK(rt); - if (rt->rt_expire) - rt->rt_expire = time_second + arpt_keep; - rt->rt_flags &= ~RTF_REJECT; - RT_UNLOCK(rt); +#endif + if (!(la->flags & LLE_STATIC)) + la->expire = time_second + arpt_keep; la->la_asked = 0; la->la_preempt = arp_maxtries; if (la->la_hold) { - (*ifp->if_output)(ifp, la->la_hold, rt_key(rt), rt); + struct sockaddr_in sin; + + bzero(&sin, sizeof(sin)); + sin.sin_len = sizeof(struct sockaddr_in); + sin.sin_family = AF_INET; + sin.sin_addr.s_addr = la->l3_addr.s_addr; + ifp->if_output(ifp, la->la_hold, + (struct sockaddr *)&sin, NULL); la->la_hold = 0; } } @@ -693,9 +979,10 @@ (void)memcpy(ar_tha(ah), ar_sha(ah), ah->ar_hln); (void)memcpy(ar_sha(ah), IF_LLADDR(ifp), ah->ar_hln); } else { - la = arplookup(itaddr.s_addr, 0, SIN_PROXY); + la = arplookup(ifp, itaddr.s_addr, LLE_PROXY); if (la == NULL) { struct sockaddr_in sin; + struct rtentry *rt; if (!arp_proxyall) goto drop; @@ -747,10 +1034,8 @@ inet_ntoa(itaddr)); #endif } else { - rt = la->la_rt; (void)memcpy(ar_tha(ah), ar_sha(ah), ah->ar_hln); - sdl = SDL(rt->rt_gateway); - (void)memcpy(ar_sha(ah), LLADDR(sdl), ah->ar_hln); + (void)memcpy(ar_sha(ah), &la->ll_addr, ah->ar_hln); } } @@ -798,66 +1083,77 @@ /* * Lookup or enter a new address in arptab. */ -static struct llinfo_arp * -arplookup(addr, create, proxy) - u_long addr; - int create, proxy; +struct llentry * +arplookup(struct ifnet *ifp, uint32_t l3addr, u_int flags) { - struct rtentry *rt; - struct sockaddr_inarp sin; - const char *why = 0; - - bzero(&sin, sizeof(sin)); - sin.sin_len = sizeof(sin); - sin.sin_family = AF_INET; - sin.sin_addr.s_addr = addr; - if (proxy) - sin.sin_other = SIN_PROXY; - rt = rtalloc1((struct sockaddr *)&sin, create, 0UL); - if (rt == 0) - return (0); - - if (rt->rt_flags & RTF_GATEWAY) - why = "host is not on local network"; - else if ((rt->rt_flags & RTF_LLINFO) == 0) - why = "could not allocate llinfo"; - else if (rt->rt_gateway->sa_family != AF_LINK) - why = "gateway route is not ours"; - - if (why) { -#define ISDYNCLONE(_rt) \ - (((_rt)->rt_flags & (RTF_STATIC | RTF_WASCLONED)) == RTF_WASCLONED) - if (create) - log(LOG_DEBUG, "arplookup %s failed: %s\n", - inet_ntoa(sin.sin_addr), why); - /* - * If there are no references to this Layer 2 route, - * and it is a cloned route, and not static, and - * arplookup() is creating the route, then purge - * it from the routing table as it is probably bogus. - */ - if (rt->rt_refcnt == 1 && ISDYNCLONE(rt)) - rtexpunge(rt); - RTFREE_LOCKED(rt); - return (0); -#undef ISDYNCLONE - } else { - RT_REMREF(rt); - RT_UNLOCK(rt); - return ((struct llinfo_arp *)rt->rt_llinfo); - } + struct llentry *e; + struct lltable *t; + // uint proxy = flags & LLE_PROXY; + + if (ifp == NULL) + return NULL; + /* LOCK_IFNET */ + for (t = ifp->lltables; t && t->llt_af != AF_INET; t = t->llt_next) + ; + if (t == NULL && flags & LLE_CREATE) + t = lltable_new(ifp, AF_INET); + if (t == NULL) { + /* UNLOCK_ALL_TABLES */ + return NULL; /* failed! */ + } + /* LOCK_TABLE(t) */ + /* UNLOCK_ALL_TABLES */ + for (e = (struct llentry *)t->lle_head; e ; e = e->lle_next) { + if (e->flags & LLE_DELETED) + continue; + if (l3addr == e->l3_addr.s_addr) + break; + } + if (e == NULL) { /* entry not found */ + if (!(flags & LLE_CREATE)) + goto done; + if (find_ifa(ifp, l3addr) == NULL) { + printf("host is not on local network\n"); + goto done; + } + e = malloc(sizeof (struct llentry), M_ARP, M_DONTWAIT | M_ZERO); + if (e == NULL) { + printf("arp malloc failed\n"); + goto done; + } + e->expire = time_second; /* mark expired */ + e->l3_addr.s_addr = l3addr; + e->lle_next = t->lle_head; + t->lle_head = e; + } + if (flags & LLE_DELETE && + (e->flags & LLE_IFADDR) == (flags & LLE_IFADDR)) + e->flags = LLE_DELETED; +done: + /* UNLOCK(t) */ + return e; } + void arp_ifinit(ifp, ifa) struct ifnet *ifp; struct ifaddr *ifa; { + struct llentry *la; + + printf("arp_ifinit ifp %p addr 0x%x\n", + ifp, ntohl(IA_SIN(ifa)->sin_addr.s_addr)); + if (ntohl(IA_SIN(ifa)->sin_addr.s_addr) != INADDR_ANY) arprequest(ifp, &IA_SIN(ifa)->sin_addr, &IA_SIN(ifa)->sin_addr, IF_LLADDR(ifp)); - ifa->ifa_rtrequest = arp_rtrequest; - ifa->ifa_flags |= RTF_CLONING; + la = arplookup(ifp, IA_SIN(ifa)->sin_addr.s_addr, LLE_CREATE); + if (la) { /* store our address */ + bcopy(IF_LLADDR(ifp), &la->ll_addr, ifp->if_addrlen); + la->flags |= LLA_VALID | LLE_STATIC | LLE_IFADDR; + } + ifa->ifa_rtrequest = NULL; } static void @@ -866,9 +1162,8 @@ arpintrq.ifq_maxlen = 50; mtx_init(&arpintrq.ifq_mtx, "arp_inq", NULL, MTX_DEF); - LIST_INIT(&llinfo_arp); callout_init(&arp_callout, CALLOUT_MPSAFE); netisr_register(NETISR_ARP, arpintr, &arpintrq, NETISR_MPSAFE); - callout_reset(&arp_callout, hz, arptimer, NULL); + callout_reset(&arp_callout, hz, newarptimer, NULL); } SYSINIT(arp, SI_SUB_PROTO_DOMAIN, SI_ORDER_ANY, arp_init, 0); diff -ruN src.orig/sys/netinet/if_ether.h src/sys/netinet/if_ether.h --- src.orig/sys/netinet/if_ether.h Tue Jun 8 11:48:18 2004 +++ src/sys/netinet/if_ether.h Tue Jun 8 12:11:09 2004 @@ -112,6 +112,33 @@ int arpresolve(struct ifnet *ifp, struct rtentry *rt, struct mbuf *m, struct sockaddr *dst, u_char *desten); void arp_ifinit(struct ifnet *, struct ifaddr *); + +/* + * Support routines for the new arp table + */ +struct lltable *lltable_new(struct ifnet *ifp, int af); +struct lltable **lltable_free(struct lltable **t); #endif +struct lltable { + struct lltable *llt_next; + void *lle_head; /* pointer to the list of address entries */ + int llt_af; /* address family */ +}; + +/* + * flags to be passed to arplookup. + */ +#define LLE_DELETED 0x0001 /* entry must be deleted */ +#define LLE_STATIC 0x0002 /* entry is static */ +#define LLE_IFADDR 0x0004 /* entry is interface addr */ +#define LLA_VALID 0x0008 /* ll_addr is valid */ +#define LLE_PROXY 0x0010 /* proxy entry ??? */ +#define LLE_PUB 0x0020 /* publish entry ??? */ +#define LLE_CREATE 0x8000 /* create on a lookup miss */ +#define LLE_DELETE 0x4000 /* delete on a lookup - match LLE_IFADDR */ + +/* + * End of support code for the new arp table + */ #endif diff -ruN src.orig/usr.sbin/arp/arp.c src/usr.sbin/arp/arp.c --- src.orig/usr.sbin/arp/arp.c Tue Jun 8 11:48:36 2004 +++ src/usr.sbin/arp/arp.c Tue Jun 8 12:11:09 2004 @@ -439,6 +439,17 @@ !(rtm->rtm_flags & RTF_GATEWAY) && valid_type(sdl->sdl_type) ) break; /* found it */ + /* check the new arp interface */ + if (sdl->sdl_family == AF_LINK && + !(rtm->rtm_flags & RTF_GATEWAY) && + valid_type(sdl->sdl_type) ) { + /* + * found it. But overwrite the address to make + * sure that we really get it. + */ + addr->sin_addr.s_addr = dst->sin_addr.s_addr; + break; + } if (dst->sin_other & SIN_PROXY) { fprintf(stderr, "delete: cannot locate %s\n",host); return (1); --- new_arp.patch ends here --- >Release-Note: >Audit-Trail: >Unformatted:
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?E1BhTnf-000OCF-MT>