On 05/04/2017 15:43, Robert Elz wrote:
>     Date:        Wed, 5 Apr 2017 13:25:44 +0100
>     From:        Roy Marples <[email protected]>
>     Message-ID:  <[email protected]>
> 
> 
>   | Right now I chose the same API, just so I can test and validate it works
>   | the same on both OS's.
> 
> That's reaosnable for testing, but preferably not for comitting.
> 
>   | Also, when NetBSD started it has 11 types. Now it has 21, so that's an
>   | extra 10 in 22 years, so at a rate of 1 every 2 years.
> 
> Most likely they come in bursts.
> 
>   | Strictly speaking, the types are beholden to the version so we could
>   | just bump the version and remove the O (ie old) compat vars and bury
>   | them to keep inside 32-bits. Thus would mean re-working how we handle
>   | compat in rtsock though.
> 
> We could, but let's not - it is a 256 value field (or 255 since 0 appears
> to not be used) and we should be able to cope with all values, and without
> needing hackery any time in the future.
> 
>   | Well yes, but the point of the API is that we don't want them by
>   | accident,
> 
> The scheme I proposed was intended to allow the kernel coder (when/if
> it is required) to arrange to map the message types so that accidents
> would be rare (that is combining in the bitmask values that tend to
> be enabled together, and excluding the types that are not.)
> 
> But...
> 
>   | If avoiding a variable length bitmask is needed then we just need
>   | something similar to poll
> 
> Yes, that's another option - anything to avoid 1 << 80 and similar...
> 
> The mapping scheme (I suggested) is likely cheaper in the kernel (just
> one extra mem reference compared to your/OpenBSD's original during
> packet processing) but a poll style interface is cleaner, and could
> avoid all unwanted packets being delivered.
> 
> With the API you proposed in this message, either internel implementation
> could be used, which is good - what matters most is that the application
> sends the actual rtm_type values to the kernel, rather than calculating a
> bitmask and sending that (as unless we do a select type interface, that
> bitmask is of fixed length, and I don't think we want to make it a 32 byte
> array to fit all possible bits in).
> 
> For now the kernel (packet processing) code could be just as you showed it
> initially (just a 32 bit mask operation) just as long as the API does not
> expose that.

These are all valid points.
v2 of the patch attached to address.
I changed the socket option name so apps can tell the two
implementations apart.

More comments are 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 16:44:08 -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,23 @@ 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 messsage types to the
+.Xr setsockopt 2
+call with the
+.Dv RO_MSGFILTER
+option at the
+.Dv PF_ROUTE
+level.
+For example, to only get specific messages:
+.Bd -literal -offset indent
+unsigned char rtfilter[] = { RTM_IFINFO, RTM_IFANNOUNCE };
+
+if (setsockopt(routefd, PF_ROUTE, RO_MSGFILTER,
+    &rtfilter, sizeof(rtfilter)) == -1)
+       err(1, "setsockopt(RO_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 16:44:08 -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 16:44:08 -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 16:44:08 -0000
@@ -250,6 +250,11 @@ 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        RO_MSGFILTER    1       /* array of which rtm_type to send to 
client */
+
 #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 16:44:09 -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 & (1U << 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,56 @@ out:
        return error;
 }
 
+static int
+route_ctloutput(int op, struct socket *so, struct sockopt *sopt)
+{
+       struct routecb *rop = sotoroutecb(so);
+       int error = 0;
+       unsigned char *rtm_type;
+       size_t len;
+       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 RO_MSGFILTER:
+                       msgfilter = 0;
+                       for (rtm_type = sopt->sopt_data, len = sopt->sopt_size;
+                            len != 0;
+                            rtm_type++, len -= sizeof(*rtm_type))
+                       {
+                               /* Guard against overflowing our storage. */
+                               if (*rtm_type >= sizeof(msgfilter) * CHAR_BIT) {
+                                       error = EOVERFLOW;
+                                       break;
+                               }
+                               msgfilter |= (1U << *rtm_type);
+                       }
+                       if (error == 0)
+                               rop->rocb_msgfilter = msgfilter;
+                       break;
+               default:
+                       error = ENOPROTOOPT;
+                       break;
+               }
+               break;
+       case PRCO_GETOPT:
+               switch (sopt->sopt_name) {
+               case RO_MSGFILTER:
+                       error = ENOTSUP;
+                       break;
+               default:
+                       error = ENOPROTOOPT;
+                       break;
+               }
+       }
+       return error;
+}
+
 static void
 rt_setmetrics(int which, const struct rt_xmsghdr *in, struct rtentry *out)
 {
@@ -1946,6 +2037,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