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,
        },

Reply via email to