Skip site navigation (1)Skip section navigation (2)
Date:      Sun, 25 Apr 2004 09:49:40 -0700
From:      Luigi Rizzo <rizzo@icir.org>
To:        current@freebsd.org
Cc:        net@freebsd.org
Subject:   new arp code snapshot for review...
Message-ID:  <20040425094940.A50968@xorpc.icir.org>

next in thread | raw e-mail | index | archive | help
Here is a snapshot of the new arp code that i have been working on
lately, based a on Andre's ideas. (I say 'ARP' for brevity, what i
mean is the layer3-to-layer2 address translation code -- arp, aarp, nd6
all fit in the category).

The basic idea is to have per-ifp, per-af tables linked to the
ifnet itself. Each table is address-family specific, and as such
is managed by the protocol itself. It can be structured as a
list, an array with direct access, or a hash table depending on
the requirements. The search key is always the layer3 address.

The advantage is a reduction in size of the routing table, because
it does not have to store ARP entries anymore, and
a likely speedup of the arp lookups because now the table lends
itself nicely to quick lookup and easy management.

Also, when the approach is used for INET6 as well (which is the
only other AF using the routing table to store arp entries) rtentry's
will not need to support cloning anymore, nor store 'rt_gwroute',
'rt_llinfo', 'rt_genmask' and 'rt_parent' fields, which means another
large chunk of code simply goes away.

Entries in the table are tagged with some flags so the code knows
which ones refer to dynamic entries, local interface addresses,
or statically configured entries.

Compatibility with userland tools is preserved usign some stub
routines which trap requests on the routing sockets and manipulate
the arp tables accordingly.

I have tried to keep the changes to a minimum (see below)
Basically all the existing functionality should be preserved, with
a few minor differences:

+ routing entries associated to interfaces are now non-clonable

+ the 'useloopback' flag is not yet implemented, because i have some
  doubts on its semantic. At the moment, and despite what you might
  think, 'useloopback' means "when you create (by cloning) a routing
  entry to the local host, use the loopback interface if useloopback
  is set at the time of cloning".
  Because there is no cloning anymore, the above semantics
  (which is not a design decision, just an accident) has to change
  slightly, to one of these two forms:
  - use the loopback interface for any local traffic if useloopback=1
  - create a routing entry that uses the loopback interface if
    useloopback is set when you assign an address to an interface
  The former is a lot simpler, so i would vote for that.

I also have patches for nd6, but these are a bit more extensive
and i am trying to see if i can write them in a way to minimize
differences with the existing code. In any case, ipv6 should work
unmodified.

--- Code changes: ---

src/usr.sbin/arp/arp.c
    one small change to make 'arp' requests clearly identifiable;

src/sys/net/route.c
    rtinit calls the new arp code, arp_ifscrub(), to remove an
    interface address when the address goes away.
    It also creates a route-to-interface, non clonable, entry
    when a new interface address is configured.

src/sys/net/rtsock.c
    route_output() calls the new arp code, arp_rt_output(), to
    implement routing socket requests that relate to the arp table.
    Another method, sysctl_dumparp(), is used for the sysctl interface
    to the arp table. In both cases, the input and output data format
    is the same as before

src/sys/netinet/if_ether.c
    this is the core of the new arp code for ipv4.
    At the moment this file also contains a number of generic routines
    which are not specific for ipv4 and so could be well moved to a
    different file.

    Note that arpresolve now completely ignores the 'rtentry' parameter
    passed by the upper layer.

src/sys/netinet/if_ether.h
    contains the definition of the 'struct lltable' and various
    flags that control the behaviour of the each entry.
    All this should probably go elsewhere as it is not INET specific.


----------------------

comments welcome. The questions i have is mainly:

    Have i forgotten anything ?

(the routing API is quite hard to follow...)
Please keep in mind that some things such as malloc vs uma,
field and variable names, location of code are going to change, so
if you have preferences please state them.
Also, as you see, there is no locking in place yet, i am leaving that task
to the locking gurus

cheers
luigi

=========================================

Index: src/usr.sbin/arp/arp.c
===================================================================
RCS file: /home/ncvs/src/usr.sbin/arp/arp.c,v
retrieving revision 1.50
diff -u -p -r1.50 arp.c
--- src/usr.sbin/arp/arp.c	13 Apr 2004 14:16:37 -0000	1.50
+++ src/usr.sbin/arp/arp.c	25 Apr 2004 15:42:21 -0000
@@ -439,6 +439,17 @@ delete(char *host, int do_proxy)
 		    !(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);
Index: src/sys/net/route.c
===================================================================
RCS file: /home/ncvs/src/sys/net/route.c,v
retrieving revision 1.104
diff -u -p -r1.104 route.c
--- src/sys/net/route.c	25 Apr 2004 01:39:00 -0000	1.104
+++ src/sys/net/route.c	25 Apr 2004 16:13:39 -0000
@@ -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 @@ rt_maskedcopy(struct sockaddr *src, stru
 		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 @@ rtinit(struct ifaddr *ifa, int cmd, int 
 	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 @@ rtinit(struct ifaddr *ifa, int cmd, int 
 		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 @@ rtinit(struct ifaddr *ifa, int cmd, int 
 		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 @@ bad:
 	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
 		 */
Index: src/sys/net/rtsock.c
===================================================================
RCS file: /home/ncvs/src/sys/net/rtsock.c,v
retrieving revision 1.107
diff -u -p -r1.107 rtsock.c
--- src/sys/net/rtsock.c	19 Apr 2004 07:20:32 -0000	1.107
+++ src/sys/net/rtsock.c	25 Apr 2004 15:39:49 -0000
@@ -91,6 +91,10 @@ static void	rt_getmetrics(const struct r
 			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
@@ -275,6 +279,8 @@ static struct pr_usrreqs route_usrreqs =
 	sosend, soreceive, sopoll, pru_sosetlabel_null
 };
 
+
+
 /*ARGSUSED*/
 static int
 route_output(struct mbuf *m, struct socket *so)
@@ -350,6 +356,11 @@ route_output(struct mbuf *m, struct sock
 		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);
@@ -363,6 +374,11 @@ route_output(struct mbuf *m, struct sock
 
 	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);
@@ -1069,6 +1085,7 @@ sysctl_rtsock(SYSCTL_HANDLER_ARGS)
 	int	i, lim, s, error = EINVAL;
 	u_char	af;
 	struct	walkarg w;
+	int found = 0;
 
 	name ++;
 	namelen--;
@@ -1100,8 +1117,17 @@ sysctl_rtsock(SYSCTL_HANDLER_ARGS)
 			    	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:
Index: src/sys/netinet/if_ether.c
===================================================================
RCS file: /home/ncvs/src/sys/netinet/if_ether.c,v
retrieving revision 1.127
diff -u -p -r1.127 if_ether.c
--- src/sys/netinet/if_ether.c	25 Apr 2004 15:00:17 -0000	1.127
+++ src/sys/netinet/if_ether.c	25 Apr 2004 16:14:03 -0000
@@ -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 @@ struct llinfo_arp {
 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 @@ SYSCTL_INT(_net_link_ether_inet, OID_AUT
 	   &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 @@ arptimer(ignored_arg)
 	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 @@ arp_rtrequest(req, rt, info)
 		Free((caddr_t)la);
 	}
 }
+#endif /* arp_rtrequest unused */
 
 /*
  * Broadcast an ARP request. Caller specifies:
@@ -301,6 +589,28 @@ arprequest(ifp, sip, tip, enaddr)
 	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 @@ int
 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 @@ arpresolve(struct ifnet *ifp, struct rte
 		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 @@ arpresolve(struct ifnet *ifp, struct rte
 	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 @@ in_arpinput(m)
 {
 	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 @@ in_arpinput(m)
 	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 @@ match:
 	}
 	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 @@ match:
 			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 @@ reply:
 		(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 @@ reply:
 			       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 @@ arptfree(la)
 /*
  * 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 @@ arp_init(void)
 
 	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);
Index: src/sys/netinet/if_ether.h
===================================================================
RCS file: /home/ncvs/src/sys/netinet/if_ether.h,v
retrieving revision 1.30
diff -u -p -r1.30 if_ether.h
--- src/sys/netinet/if_ether.h	7 Apr 2004 20:46:13 -0000	1.30
+++ src/sys/netinet/if_ether.h	25 Apr 2004 15:09:46 -0000
@@ -112,6 +112,33 @@ extern u_char	ether_ipmulticast_max[ETHE
 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



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?20040425094940.A50968>