tech-kern archive

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]

route(4): Adding ROUTE_MSGFILTER socket option



Hi List!

Applications such as dhcpcd(8), ntpd(8) and wpa_supplicant(8) all listen
to route(4) for important events.
But they don't actually need to listen to ALL the possible events and
none of them need to listen to the very spamy RTM_MISS message.

Attached is a patch based on OpenBSD's ROUTE_MSGFILTER socket option
which allows an application to filter for messages it's interested in
and thus won't be woken up to burn needless CPU time.

Comments welcome!

Roy
Index: share/man/man4/route.4
===================================================================
RCS file: /cvsroot/src/share/man/man4/route.4,v
retrieving revision 1.28
diff -u -p -r1.28 route.4
--- share/man/man4/route.4	21 Sep 2016 10:50:23 -0000	1.28
+++ share/man/man4/route.4	5 Apr 2017 09:43:06 -0000
@@ -29,7 +29,7 @@
 .\"
 .\"     @(#)route.4	8.6 (Berkeley) 4/19/94
 .\"
-.Dd September 15, 2016
+.Dd March 5, 2017
 .Dt ROUTE 4
 .Os
 .Sh NAME
@@ -174,6 +174,27 @@ by doing a
 .Xr shutdown 2
 system call for further input.
 .Pp
+A process can specify which route message types it's interested in by using
+.Dv ROUTE_FILTER(unsigned int type)
+and issuing a
+.Xr setsockopt 2
+call with the
+.Dv ROUTE_MSGFILTER
+option at the
+.Dv PF_ROUTE
+level.
+For example, to only get specific messages:
+.Bd -literal -offset indent
+unsigned int rtfilter;
+
+rtfilter = ROUTE_FILTER(RTM_IFINFO) |
+    ROUTE_FILTER(RTM_IFANNOUNCE);
+
+if (setsockopt(routefd, PF_ROUTE, ROUTE_MSGFILTER,
+    &rtfilter, sizeof(rtfilter)) == -1)
+	err(1, "setsockopt(ROUTE_MSGFILTER)");
+.Ed
+.Pp
 If a route is in use when it is deleted,
 the routing entry will be marked down and removed from the routing table,
 but the resources associated with it will not
Index: sys/net/raw_cb.h
===================================================================
RCS file: /cvsroot/src/sys/net/raw_cb.h,v
retrieving revision 1.26
diff -u -p -r1.26 raw_cb.h
--- sys/net/raw_cb.h	20 Jan 2016 21:43:59 -0000	1.26
+++ sys/net/raw_cb.h	5 Apr 2017 09:43:06 -0000
@@ -46,6 +46,8 @@ struct rawcb {
 	struct	sockaddr *rcb_faddr;	/* destination address */
 	struct	sockaddr *rcb_laddr;	/* socket's address */
 	struct	sockproto rcb_proto;	/* protocol family, protocol */
+	int	(*rcb_filter)(struct mbuf *, struct sockproto *,
+		              struct rawcb *);
 	size_t	rcb_len;
 };
 
Index: sys/net/raw_usrreq.c
===================================================================
RCS file: /cvsroot/src/sys/net/raw_usrreq.c,v
retrieving revision 1.55
diff -u -p -r1.55 raw_usrreq.c
--- sys/net/raw_usrreq.c	20 Jan 2016 21:43:59 -0000	1.55
+++ sys/net/raw_usrreq.c	5 Apr 2017 09:43:06 -0000
@@ -107,6 +107,9 @@ raw_input(struct mbuf *m0, ...)
 			continue;
 		if (rp->rcb_faddr && !equal(rp->rcb_faddr, src))
 			continue;
+		/* Run any filtering that may have been installed. */
+		if (rp->rcb_filter != NULL && rp->rcb_filter(m, proto, rp) != 0)
+			continue;
 		if (last != NULL) {
 			struct mbuf *n;
 			if ((n = m_copy(m, 0, M_COPYALL)) == NULL)
Index: sys/net/route.h
===================================================================
RCS file: /cvsroot/src/sys/net/route.h,v
retrieving revision 1.111
diff -u -p -r1.111 route.h
--- sys/net/route.h	19 Dec 2016 11:17:00 -0000	1.111
+++ sys/net/route.h	5 Apr 2017 09:43:06 -0000
@@ -250,6 +250,13 @@ struct rt_msghdr {
 #define RTM_DELADDR	0x17	/* address being removed from iface */
 #define RTM_CHGADDR	0x18	/* address properties changed */
 
+/*
+ * setsockopt defines used for the filtering.
+ */
+#define	ROUTE_MSGFILTER	1	/* bitmask to specifiy which types should be
+				   sent to the client. */
+#define	ROUTE_FILTER(m)	(1U << (m))
+
 #define RTV_MTU		0x1	/* init or lock _mtu */
 #define RTV_HOPCOUNT	0x2	/* init or lock _hopcount */
 #define RTV_EXPIRE	0x4	/* init or lock _expire */
Index: sys/net/rtsock.c
===================================================================
RCS file: /cvsroot/src/sys/net/rtsock.c,v
retrieving revision 1.211
diff -u -p -r1.211 rtsock.c
--- sys/net/rtsock.c	24 Mar 2017 03:45:02 -0000	1.211
+++ sys/net/rtsock.c	5 Apr 2017 09:43:07 -0000
@@ -177,6 +177,12 @@ static void rt_adjustcount(int, int);
 
 static const struct protosw COMPATNAME(route_protosw)[];
 
+struct routecb {
+	struct rawcb	rocb_rcb;
+	unsigned int	rocb_msgfilter;
+};
+#define sotoroutecb(so)	((struct routecb *)(so)->so_pcb)
+
 static void
 rt_adjustcount(int af, int cnt)
 {
@@ -200,14 +206,48 @@ rt_adjustcount(int af, int cnt)
 }
 
 static int
+COMPATNAME(route_filter)(struct mbuf *m, struct sockproto *proto,
+    struct rawcb *rp)
+{
+	struct routecb *rop = (struct routecb *)rp;
+	struct rt_xmsghdr *rtm;
+
+	KASSERT(m != NULL);
+	KASSERT(proto != NULL);
+	KASSERT(rp != NULL);
+
+	/* Wrong family for this socket. */
+	if (proto->sp_family != PF_ROUTE)
+		return ENOPROTOOPT;
+
+	/* If no filter set, just return. */
+	if (rop->rocb_msgfilter == 0)
+		return 0;
+
+	/* Ensure we can access rtm_type */
+	if (m->m_len < offsetof(struct rt_xmsghdr, rtm_type) + 1)
+		return EINVAL;
+
+	rtm = mtod(m, struct rt_xmsghdr *);
+	/* If the rtm type is filtered out, return a positive. */
+	if (!(rop->rocb_msgfilter & (1 << rtm->rtm_type)))
+		return EEXIST;
+
+	/* Passed the filter. */
+	return 0;
+}
+
+static int
 COMPATNAME(route_attach)(struct socket *so, int proto)
 {
 	struct rawcb *rp;
+	struct routecb *rop;
 	int s, error;
 
 	KASSERT(sotorawcb(so) == NULL);
-	rp = kmem_zalloc(sizeof(*rp), KM_SLEEP);
-	rp->rcb_len = sizeof(*rp);
+	rop = kmem_zalloc(sizeof(*rop), KM_SLEEP);
+	rp = &rop->rocb_rcb;
+	rp->rcb_len = sizeof(*rop);
 	so->so_pcb = rp;
 
 	s = splsoftnet();
@@ -215,11 +255,12 @@ COMPATNAME(route_attach)(struct socket *
 		rt_adjustcount(rp->rcb_proto.sp_protocol, 1);
 		rp->rcb_laddr = &COMPATNAME(route_info).ri_src;
 		rp->rcb_faddr = &COMPATNAME(route_info).ri_dst;
+		rp->rcb_filter = COMPATNAME(route_filter);
 	}
 	splx(s);
 
 	if (error) {
-		kmem_free(rp, sizeof(*rp));
+		kmem_free(rop, sizeof(*rop));
 		so->so_pcb = NULL;
 		return error;
 	}
@@ -981,6 +1022,45 @@ out:
 	return error;
 }
 
+static int
+route_ctloutput(int op, struct socket *so, struct sockopt *sopt)
+{
+	struct routecb *rop = sotoroutecb(so);
+	int error;
+	unsigned int msgfilter;
+
+	KASSERT(solocked(so));
+
+	if (sopt->sopt_level != AF_ROUTE) {
+		error = ENOPROTOOPT;
+	} else switch (op) {
+	case PRCO_SETOPT:
+		switch (sopt->sopt_name) {
+		case ROUTE_MSGFILTER:
+			error = sockopt_get(sopt, &msgfilter,
+			    sizeof(msgfilter));
+			if (error == 0)
+				rop->rocb_msgfilter = msgfilter;
+			break;
+		default:
+			error = ENOPROTOOPT;
+			break;
+		}
+		break;
+	case PRCO_GETOPT:
+		switch (sopt->sopt_name) {
+		case ROUTE_MSGFILTER:
+			error = sockopt_set(sopt, &rop->rocb_msgfilter,
+			    sizeof(rop->rocb_msgfilter));
+			break;
+		default:
+			error = ENOPROTOOPT;
+			break;
+		}
+	}
+	return error;
+}
+
 static void
 rt_setmetrics(int which, const struct rt_xmsghdr *in, struct rtentry *out)
 {
@@ -1946,6 +2026,7 @@ static const struct protosw COMPATNAME(r
 		.pr_flags = PR_ATOMIC|PR_ADDR,
 		.pr_input = raw_input,
 		.pr_ctlinput = raw_ctlinput,
+		.pr_ctloutput = route_ctloutput,
 		.pr_usrreqs = &route_usrreqs,
 		.pr_init = raw_init,
 	},


Home | Main Index | Thread Index | Old Index