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 - 1.133
+++ net/route.h 29 Mar 2016 09:52:29 -
@@ -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 0x1 /* this is a cloned route */
#define RTF_MPATH 0x4 /* 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.c26 Mar 2016 21:56:04 - 1.187
+++ net/rtsock.c29 Mar 2016 10:07:53 -
@@ -98,6 +98,7 @@ struct walkarg {
introute_ctloutput(int, struct socket *, int, int, struct mbuf **);
void route_input(struct mbuf *m0, ...);
+introute_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(, tableid)) {
+ error = EEXIST;
+ goto flush;
+ }
error = rtrequest(RTM_ADD, , prio, _nrt, tableid);
if (error == 0) {
rt_setmetrics(rtm->rtm_inits, >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 - 1.203
+++ netinet/if_ether.c 29 Mar 2016 10:07:12 -
@@ -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