route: Add support for route(4) message filtering.
authorRoy Marples <roy@marples.name>
Fri, 16 Aug 2019 15:39:35 +0000 (16:39 +0100)
committerRoy Marples <roy@marples.name>
Fri, 16 Aug 2019 15:39:35 +0000 (16:39 +0100)
This saves waking up listeners for messages they have no interest in.
It also helps to reduce the chance of a receive buffer overflow.

Taken-from: OpenBSD
Reviewd-by: sephe
share/man/man4/route.4
sys/net/raw_cb.h
sys/net/raw_usrreq.c
sys/net/route.h
sys/net/rtsock.c

index 9a3968f..caa8b8f 100644 (file)
@@ -29,7 +29,7 @@
 .\" $FreeBSD: src/share/man/man4/route.4,v 1.9.2.6 2002/03/17 09:12:44 schweikh Exp $
 .\" $DragonFly: src/share/man/man4/route.4,v 1.3 2004/03/11 12:28:55 hmp Exp $
 .\"
-.Dd January 18, 2002
+.Dd August 7, 2019
 .Dt ROUTE 4
 .Os
 .Sh NAME
@@ -169,6 +169,25 @@ 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 passing
+an array of route message types to the
+.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 c575a38..d8f162e 100644 (file)
@@ -57,6 +57,8 @@ struct rawcb {
        const 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 *, const struct sockproto *,
+                             const struct rawcb *);
 };
 
 #define        sotorawcb(so)           ((struct rawcb *)(so)->so_pcb)
index b837ef2..3945822 100644 (file)
@@ -104,6 +104,9 @@ raw_input(struct mbuf *m0, const struct sockproto *proto,
                        continue;
                if (rp->rcb_faddr && !sa_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) {
                        struct mbuf *n;
 
index ade7258..363ee5a 100644 (file)
@@ -249,6 +249,12 @@ struct rt_msghdr {
 #define        RTM_IFANNOUNCE  0x11    /* iface arrival/departure */
 #define        RTM_IEEE80211   0x12    /* IEEE80211 wireless event */
 
+/*
+ * setsockopt defines used for the filtering.
+ */
+#define        ROUTE_MSGFILTER 1       /* bitmask of which rtm_type to send to client */
+#define        ROUTE_FILTER(m) (1U << (m))
+
 /*
  * Bitmask values for rtm_inits and rmx_locks.
  */
index 74a5575..232cd74 100644 (file)
@@ -130,6 +130,12 @@ struct netmsg_rttable_walk {
        struct rttable_walkarg  *w;
 };
 
+struct routecb {
+       struct rawcb    rocb_rcb;
+       unsigned int    rocb_msgfilter;
+};
+#define        sotoroutecb(so) ((struct routecb *)(so)->so_pcb)
+
 static struct mbuf *
                rt_msg_mbuf (int, struct rt_addrinfo *);
 static void    rt_msg_buffer (int, struct rt_addrinfo *, void *buf, int len);
@@ -154,6 +160,40 @@ rts_abort(netmsg_t msg)
        crit_exit();
 }
 
+static int
+rts_filter(struct mbuf *m, const struct sockproto *proto,
+       const struct rawcb *rp)
+{
+       const struct routecb *rop = (const struct routecb *)rp;
+       const struct rt_msghdr *rtm;
+
+       KKASSERT(m != NULL);
+       KKASSERT(proto != NULL);
+       KKASSERT(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_msghdr, rtm_type) + sizeof(rtm->rtm_type))
+               return EINVAL;
+
+       rtm = mtod(m, const struct rt_msghdr *);
+       /* If the rtm type is filtered out, return a positive. */
+       if (!(rop->rocb_msgfilter & ROUTE_FILTER(rtm->rtm_type)))
+               return EEXIST;
+
+       /* Passed the filter. */
+       return 0;
+}
+
+
 /* pru_accept is EOPNOTSUPP */
 
 static void
@@ -162,6 +202,7 @@ rts_attach(netmsg_t msg)
        struct socket *so = msg->base.nm_so;
        struct pru_attach_info *ai = msg->attach.nm_ai;
        struct rawcb *rp;
+       struct routecb *rop;
        int proto = msg->attach.nm_proto;
        int error;
 
@@ -171,7 +212,8 @@ rts_attach(netmsg_t msg)
                goto done;
        }
 
-       rp = kmalloc(sizeof *rp, M_PCB, M_WAITOK | M_ZERO);
+       rop = kmalloc(sizeof *rop, M_PCB, M_WAITOK | M_ZERO);
+       rp = &rop->rocb_rcb;
 
        /*
         * The critical section is necessary to block protocols from sending
@@ -185,7 +227,7 @@ rts_attach(netmsg_t msg)
        error = raw_attach(so, proto, ai->sb_rlimit);
        rp = sotorawcb(so);
        if (error) {
-               kfree(rp, M_PCB);
+               kfree(rop, M_PCB);
                goto done;
        }
        switch(rp->rcb_proto.sp_protocol) {
@@ -197,6 +239,7 @@ rts_attach(netmsg_t msg)
                break;
        }
        rp->rcb_faddr = &route_src;
+       rp->rcb_filter = rts_filter;
        route_cb.any_count++;
        soisconnected(so);
        so->so_options |= SO_USELOOPBACK;
@@ -384,6 +427,53 @@ rts_input(struct mbuf *m, sa_family_t family)
        rts_input_skip(m, family, NULL);
 }
 
+static void
+route_ctloutput(netmsg_t msg)
+{
+       struct socket *so = msg->ctloutput.base.nm_so;
+       struct sockopt *sopt = msg->ctloutput.nm_sopt;
+       struct routecb *rop = sotoroutecb(so);
+       int error;
+       unsigned int msgfilter;
+
+       if (sopt->sopt_level != AF_ROUTE) {
+               error = EINVAL;
+               goto out;
+       }
+
+       error = 0;
+
+       switch (sopt->sopt_dir) {
+       case SOPT_SET:
+               switch (sopt->sopt_name) {
+               case ROUTE_MSGFILTER:
+                       error = soopt_to_kbuf(sopt, &msgfilter,
+                           sizeof(msgfilter), sizeof(msgfilter));
+                       if (error == 0)
+                               rop->rocb_msgfilter = msgfilter;
+                       break;
+               default:
+                       error = ENOPROTOOPT;
+                       break;
+               }
+               break;
+       case SOPT_GET:
+               switch (sopt->sopt_name) {
+               case ROUTE_MSGFILTER:
+                       msgfilter = rop->rocb_msgfilter;
+                       soopt_from_kbuf(sopt, &msgfilter, sizeof(msgfilter));
+                       break;
+               default:
+                       error = ENOPROTOOPT;
+                       break;
+               }
+       }
+out:
+       lwkt_replymsg(&msg->ctloutput.base.lmsg, error);
+}
+
+
+
 static void *
 reallocbuf_nofree(void *ptr, size_t len, size_t olen)
 {
@@ -1682,7 +1772,7 @@ static struct protosw routesw[] = {
        .pr_input = NULL,
        .pr_output = route_output,
        .pr_ctlinput = raw_ctlinput,
-       .pr_ctloutput = NULL,
+       .pr_ctloutput = route_ctloutput,
        .pr_ctlport = cpu0_ctlport,
 
        .pr_init = raw_init,