kern/68692: Move ARP out of routing table
Sergey Matveychuk
sem at ciam.ru
Mon Jul 5 06:50:21 PDT 2004
>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 at 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 at FreeBSD.org and andre at 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:
More information about the freebsd-bugs
mailing list