Diff below implements proxy ARP using the mpath property of our routing table. This solution is not limited to ART and could be used for different purposes, like putting multicast addresses in the routing table. However I'm keeping it under "#ifdef ART" as long as we are not totally committed to this new routing table.
The new function in net/rtsock.c enforces that at most one private and one public ARP entry are inserted in a routing table. I didn't put it in netinet/if_ether.c because I don't want to spread more "rt_addrinfo" than we already have. I'll work on removing the KERNEL_LOCK() around rtable_mpath_next() soon. With this all ARP and arp(8) regression tests pass. ok? Index: net/route.h =================================================================== RCS file: /cvs/src/sys/net/route.h,v retrieving revision 1.133 diff -u -p -r1.133 route.h --- net/route.h 26 Mar 2016 21:56:04 -0000 1.133 +++ net/route.h 29 Mar 2016 09:52:29 -0000 @@ -136,6 +136,7 @@ struct rtentry { #define RTF_BLACKHOLE 0x1000 /* just discard pkts (during updates) */ #define RTF_PROTO3 0x2000 /* protocol specific routing flag */ #define RTF_PROTO2 0x4000 /* protocol specific routing flag */ +#define RTF_ANNOUNCE RTF_PROTO2 /* announce L2 entry */ #define RTF_PROTO1 0x8000 /* protocol specific routing flag */ #define RTF_CLONED 0x10000 /* this is a cloned route */ #define RTF_MPATH 0x40000 /* multipath route or operation */ Index: net/rtsock.c =================================================================== RCS file: /cvs/src/sys/net/rtsock.c,v retrieving revision 1.187 diff -u -p -r1.187 rtsock.c --- net/rtsock.c 26 Mar 2016 21:56:04 -0000 1.187 +++ net/rtsock.c 29 Mar 2016 10:07:53 -0000 @@ -98,6 +98,7 @@ struct walkarg { int route_ctloutput(int, struct socket *, int, int, struct mbuf **); void route_input(struct mbuf *m0, ...); +int route_arp_conflict(struct rt_addrinfo *, unsigned int); struct mbuf *rt_msg1(int, struct rt_addrinfo *); int rt_msg2(int, int, struct rt_addrinfo *, caddr_t, @@ -600,6 +601,10 @@ route_output(struct mbuf *m, ...) error = EINVAL; goto flush; } + if (route_arp_conflict(&info, tableid)) { + error = EEXIST; + goto flush; + } error = rtrequest(RTM_ADD, &info, prio, &saved_nrt, tableid); if (error == 0) { rt_setmetrics(rtm->rtm_inits, &rtm->rtm_rmx, @@ -884,6 +889,47 @@ fail: rp->rcb_proto.sp_family = PF_ROUTE; return (error); +} + +/* + * Check if the user request to insert an ARP entry does not conflict + * with existing ones. + * + * Only two entries are allowed for a given IP address: a private one + * (priv) and a public one (pub). + */ +int +route_arp_conflict(struct rt_addrinfo *info, unsigned int tableid) +{ +#ifdef ART + struct rtentry *rt; + int proxy = (info->rti_flags & RTF_ANNOUNCE); + + if ((info->rti_flags & RTF_LLINFO) == 0 || + (info->rti_info[RTAX_DST]->sa_family != AF_INET)) + return (0); + + rt = rtalloc(info->rti_info[RTAX_DST], 0, tableid); + if (rt == NULL || !ISSET(rt->rt_flags, RTF_LLINFO)) { + rtfree(rt); + return (0); + } + + /* + * Same destination and both "priv" or "pub" conflict. + * If a second entry exists, it always conflict. + */ + if ((ISSET(rt->rt_flags, RTF_ANNOUNCE) == proxy) || + (rtable_mpath_next(rt) != NULL)) { + rtfree(rt); + return (1); + } + + /* No conflict but an entry exist so we need to force mpath. */ + info->rti_flags |= RTF_MPATH; + rtfree(rt); +#endif /* ART */ + return (0); } void Index: netinet/if_ether.c =================================================================== RCS file: /cvs/src/sys/netinet/if_ether.c,v retrieving revision 1.203 diff -u -p -r1.203 if_ether.c --- netinet/if_ether.c 24 Mar 2016 07:15:10 -0000 1.203 +++ netinet/if_ether.c 29 Mar 2016 10:07:12 -0000 @@ -698,8 +698,20 @@ arplookup(u_int32_t addr, int create, in } if (proxy && !ISSET(rt->rt_flags, RTF_ANNOUNCE)) { + struct rtentry *mrt = NULL; +#ifdef ART + mrt = rt; + KERNEL_LOCK(); + while ((mrt = rtable_mpath_next(mrt)) != NULL) { + if (ISSET(mrt->rt_flags, RTF_ANNOUNCE)) { + rtref(mrt); + break; + } + } + KERNEL_UNLOCK(); +#endif /* ART */ rtfree(rt); - return (NULL); + return (mrt); } return (rt); Index: netinet/if_ether.h =================================================================== RCS file: /cvs/src/sys/netinet/if_ether.h,v retrieving revision 1.69 diff -u -p -r1.69 if_ether.h --- netinet/if_ether.h 16 Mar 2016 11:48:27 -0000 1.69 +++ netinet/if_ether.h 29 Mar 2016 09:54:24 -0000 @@ -148,7 +148,6 @@ struct sockaddr_inarp { * IP and ethernet specific routing flags */ #define RTF_USETRAILERS RTF_PROTO1 /* use trailers */ -#define RTF_ANNOUNCE RTF_PROTO2 /* announce new arp entry */ #define RTF_PERMANENT_ARP RTF_PROTO3 /* only manual overwrite of entry */ #ifdef _KERNEL Index: netinet6/nd6.h =================================================================== RCS file: /cvs/src/sys/netinet6/nd6.h,v retrieving revision 1.57 diff -u -p -r1.57 nd6.h --- netinet6/nd6.h 3 Mar 2016 12:57:15 -0000 1.57 +++ netinet6/nd6.h 29 Mar 2016 09:52:29 -0000 @@ -35,11 +35,6 @@ #include <sys/task.h> -/* see net/route.h, or net/if_inarp.h */ -#ifndef RTF_ANNOUNCE -#define RTF_ANNOUNCE RTF_PROTO2 -#endif - #define ND6_LLINFO_PURGE -3 #define ND6_LLINFO_NOSTATE -2 #define ND6_LLINFO_INCOMPLETE 0