Currently all routing messages are sent to the clients no matter if they
need to see the message or not. One such example are messages from other
routing tables that are sent to userland even though the process has no
interest in these messages. This diff implememnts a filter that limits
the messages to the rtable/rdomain a process belongs to. For special
cases (route(8) and bgpd(8)) a socket option can be used to change the
filter to a different table (or all in case of RTABLE_ANY).

This makes stuff like "route -T 1 exec route -n show -inet" show rtable 1.
netstat(8) will need a similar change and RTM_IFINFO should be rdomain
aware but those are independent changes.
-- 
:wq Claudio

Index: sbin/route/route.c
===================================================================
RCS file: /cvs/src/sbin/route/route.c,v
retrieving revision 1.150
diff -u -p -r1.150 route.c
--- sbin/route/route.c  21 Sep 2010 10:58:23 -0000      1.150
+++ sbin/route/route.c  30 Sep 2010 09:53:53 -0000
@@ -68,7 +68,7 @@ union sockunion so_dst, so_gate, so_mask
 typedef union sockunion *sup;
 pid_t  pid;
 int    rtm_addrs, s;
-int    forcehost, forcenet, Fflag, nflag, af, qflag, tflag;
+int    forcehost, forcenet, Fflag, nflag, af, qflag, tflag, Tflag;
 int    iflag, verbose, aflen = sizeof(struct sockaddr_in);
 int    locking, lockrest, debugonly;
 u_long mpls_flags = MPLS_OP_LOCAL;
@@ -134,6 +134,7 @@ main(int argc, char **argv)
        if (argc < 2)
                usage(NULL);
 
+       tableid = getrtable();
        while ((ch = getopt(argc, argv, "dnqtT:v")) != -1)
                switch (ch) {
                case 'n':
@@ -150,6 +151,7 @@ main(int argc, char **argv)
                        break;
                case 'T':
                        gettable(optarg);
+                       Tflag = 1;
                        break;
                case 'd':
                        debugonly = 1;
@@ -180,6 +182,10 @@ main(int argc, char **argv)
                        s = socket(PF_ROUTE, SOCK_RAW, 0);
                if (s == -1)
                        err(1, "socket");
+               /* force socket onto table user requested */
+               if (Tflag && setsockopt(s, AF_ROUTE, ROUTE_TABLEFILTER,
+                   &tableid, sizeof(tableid)) == -1)
+                       err(1, "setsockopt(ROUTE_TABLEFILTER)");
                break;
        }
        switch (kw) {
@@ -688,7 +694,7 @@ show(int argc, char *argv[])
                        usage(*argv);
        }
 
-       p_rttables(af, tableid);
+       p_rttables(af, tableid, Tflag);
 }
 
 void
@@ -1046,7 +1052,10 @@ monitor(int argc, char *argv[])
 
        if (setsockopt(s, AF_ROUTE, ROUTE_MSGFILTER, &filter,
            sizeof(filter)) == -1)
-               err(1, "setsockopt");
+               err(1, "setsockopt(ROUTE_MSGFILTER)");
+       if (Tflag && setsockopt(s, AF_ROUTE, ROUTE_TABLEFILTER, &tableid,
+           sizeof(tableid)) == -1)
+               err(1, "setsockopt(ROUTE_TABLEFILTER)");
 
        verbose = 1;
        if (debugonly) {
@@ -1617,11 +1626,25 @@ getlabel(char *name)
 void
 gettable(const char *s)
 {
-       const char      *errstr;
+       const char              *errstr;
+       struct rt_tableinfo      info;
+       int                      mib[6];
+       size_t                   len;
 
        tableid = strtonum(s, 0, RT_TABLEID_MAX, &errstr);
        if (errstr)
                errx(1, "invalid table id: %s", errstr);
+
+       mib[0] = CTL_NET;
+       mib[1] = AF_ROUTE;
+       mib[2] = 0;
+       mib[3] = 0;
+       mib[4] = NET_RT_TABLE;
+       mib[5] = tableid;
+
+       len = sizeof(info);
+       if (sysctl(mib, 6, &info, &len, NULL, 0) == -1)
+               err(1, "routing table %i", tableid);
 }
 
 int
Index: sbin/route/show.c
===================================================================
RCS file: /cvs/src/sbin/route/show.c,v
retrieving revision 1.88
diff -u -p -r1.88 show.c
--- sbin/route/show.c   21 Sep 2010 10:58:23 -0000      1.88
+++ sbin/route/show.c   30 Sep 2010 08:55:02 -0000
@@ -123,13 +123,13 @@ void       index_pfk(struct sadb_msg *, void 
  * Print routing tables.
  */
 void
-p_rttables(int af, u_int tableid)
+p_rttables(int af, u_int tableid, int hastable)
 {
        struct rt_msghdr *rtm;
        struct sadb_msg *msg;
        char *buf = NULL, *next, *lim = NULL;
        size_t needed;
-       int mib[7];
+       int mib[7], mcnt;
        struct sockaddr *sa;
 
        mib[0] = CTL_NET;
@@ -138,14 +138,18 @@ p_rttables(int af, u_int tableid)
        mib[3] = af;
        mib[4] = NET_RT_DUMP;
        mib[5] = 0;
-       mib[6] = tableid;
+       if (hastable) {
+               mib[6] = tableid;
+               mcnt = 7;
+       } else
+               mcnt = 6;
 
-       if (sysctl(mib, 7, NULL, &needed, NULL, 0) < 0)
+       if (sysctl(mib, mcnt, NULL, &needed, NULL, 0) < 0)
                err(1, "route-sysctl-estimate");
        if (needed > 0) {
                if ((buf = malloc(needed)) == 0)
                        err(1, NULL);
-               if (sysctl(mib, 7, buf, &needed, NULL, 0) < 0)
+               if (sysctl(mib, mcnt, buf, &needed, NULL, 0) < 0)
                        err(1, "sysctl of routing table");
                lim = buf + needed;
        }
Index: sbin/route/show.h
===================================================================
RCS file: /cvs/src/sbin/route/show.h,v
retrieving revision 1.8
diff -u -p -r1.8 show.h
--- sbin/route/show.h   21 Sep 2010 10:58:23 -0000      1.8
+++ sbin/route/show.h   30 Sep 2010 08:53:19 -0000
@@ -28,7 +28,7 @@ union sockunion {
        struct sockaddr_mpls    smpls;
 };
 
-void    p_rttables(int, u_int);
+void    p_rttables(int, u_int, int);
 char   *routename(struct sockaddr *);
 char   *netname(struct sockaddr *, struct sockaddr *);
 char   *mpls_op(u_int32_t);
Index: sys/net/route.h
===================================================================
RCS file: /cvs/src/sys/net/route.h,v
retrieving revision 1.73
diff -u -p -r1.73 route.h
--- sys/net/route.h     25 Aug 2010 14:07:24 -0000      1.73
+++ sys/net/route.h     30 Sep 2010 09:25:57 -0000
@@ -277,8 +277,11 @@ struct rt_msghdr {
  */
 #define ROUTE_MSGFILTER        1       /* bitmask to specifiy which types 
should be
                                   sent to the client. */
+#define ROUTE_TABLEFILTER 2    /* change routing table the socket is listening
+                                  on, RTABLE_ANY listens on all tables. */
 
 #define ROUTE_FILTER(m)        (1 << (m))
+#define RTABLE_ANY     0xffffffff
 
 struct rt_addrinfo {
        int     rti_addrs;
Index: sys/net/rtsock.c
===================================================================
RCS file: /cvs/src/sys/net/rtsock.c,v
retrieving revision 1.109
diff -u -p -r1.109 rtsock.c
--- sys/net/rtsock.c    8 Sep 2010 08:20:45 -0000       1.109
+++ sys/net/rtsock.c    30 Sep 2010 09:28:42 -0000
@@ -113,9 +113,10 @@ void                rt_xaddrs(caddr_t, caddr_t, struc
 
 struct routecb {
        struct rawcb    rcb;
+       struct timeout  timeout;
        unsigned int    msgfilter;
        unsigned int    flags;
-       struct timeout  timeout;
+       u_int           rtableid;
 };
 #define        sotoroutecb(so) ((struct routecb *)(so)->so_pcb)
 
@@ -151,7 +152,8 @@ route_usrreq(struct socket *so, int req,
                 * code does not care about the additional fields
                 * and works directly on the raw socket.
                 */
-               rp = malloc(sizeof(struct routecb), M_PCB, M_WAITOK|M_ZERO);
+               rop = malloc(sizeof(struct routecb), M_PCB, M_WAITOK|M_ZERO);
+               rp = &rop->rcb;
                so->so_pcb = rp;
                /* Init the timeout structure */
                timeout_set(&((struct routecb *)rp)->timeout, rt_senddesync, 
rp);
@@ -169,6 +171,7 @@ route_usrreq(struct socket *so, int req,
                        splx(s);
                        return (error);
                }
+               rop->rtableid = curproc->p_p->ps_rtableid;
                af = rp->rcb_proto.sp_protocol;
                if (af == AF_INET)
                        route_cb.ip_count++;
@@ -227,6 +230,7 @@ route_ctloutput(int op, struct socket *s
        struct routecb *rop = sotoroutecb(so);
        struct mbuf *m = *mp;
        int error = 0;
+       unsigned int tid;
 
        if (level != AF_ROUTE) {
                error = EINVAL;
@@ -244,6 +248,17 @@ route_ctloutput(int op, struct socket *s
                        else
                                rop->msgfilter = *mtod(m, unsigned int *);
                        break;
+               case ROUTE_TABLEFILTER:
+                       if (m == NULL || m->m_len != sizeof(unsigned int)) {
+                               error = EINVAL;
+                               break;
+                       }
+                       tid = *mtod(m, unsigned int *);
+                       if (tid != RTABLE_ANY && !rtable_exists(tid))
+                               error = ENOENT;
+                       else
+                               rop->rtableid = tid;
+                       break;
                default:
                        error = ENOPROTOOPT;
                        break;
@@ -255,9 +270,14 @@ route_ctloutput(int op, struct socket *s
                switch (optname) {
                case ROUTE_MSGFILTER:
                        *mp = m = m_get(M_WAIT, MT_SOOPTS);   
-                       m->m_len = sizeof(int);
+                       m->m_len = sizeof(unsigned int);
                        *mtod(m, unsigned int *) = rop->msgfilter;
                        break;
+               case ROUTE_TABLEFILTER:
+                       *mp = m = m_get(M_WAIT, MT_SOOPTS);   
+                       m->m_len = sizeof(unsigned int);
+                       *mtod(m, unsigned int *) = rop->rtableid;
+                       break;
                default:
                        error = ENOPROTOOPT;
                        break;
@@ -300,6 +320,7 @@ route_input(struct mbuf *m0, ...)
 {
        struct rawcb *rp;
        struct routecb *rop;
+       struct rt_msghdr *rtm;
        struct mbuf *m = m0;
        int sockets = 0;
        struct socket *last = NULL;
@@ -345,9 +366,31 @@ route_input(struct mbuf *m0, ...)
 
                /* filter messages that the process does not want */
                rop = (struct routecb *)rp;
+               rtm = mtod(m, struct rt_msghdr *);
                if (rop->msgfilter != 0 && !(rop->msgfilter & (1 <<
-                   mtod(m, struct rt_msghdr *)->rtm_type)))
+                   rtm->rtm_type)))
                        continue;
+               switch (rtm->rtm_type) {
+               case RTM_IFANNOUNCE:
+               case RTM_IFINFO:
+               case RTM_DESYNC:
+                       /* no tableid */
+                       break;
+               case RTM_RESOLVE:
+               case RTM_NEWADDR:
+               case RTM_DELADDR:
+                       /* check against rdomain id */
+                       if (rop->rtableid != RTABLE_ANY &&
+                           rtable_l2(rop->rtableid) != rtm->rtm_tableid)
+                               continue;
+                       break;
+               default:
+                       /* check against rtable id */
+                       if (rop->rtableid != RTABLE_ANY &&
+                           rop->rtableid != rtm->rtm_tableid)
+                               continue;
+                       break;
+               }
 
                /*
                 * Check to see if the flush flag is set. If so, don't queue 
@@ -1340,7 +1383,8 @@ sysctl_rtable(int *name, u_int namelen, 
                tableid = name[3];
                if (!rtable_exists(tableid))
                        return (ENOENT);
-       }
+       } else
+               tableid = curproc->p_p->ps_rtableid;
 
        s = splsoftnet();
        switch (w.w_op) {
Index: usr.sbin/bgpd/kroute.c
===================================================================
RCS file: /cvs/src/usr.sbin/bgpd/kroute.c,v
retrieving revision 1.184
diff -u -p -r1.184 kroute.c
--- usr.sbin/bgpd/kroute.c      2 Sep 2010 14:03:21 -0000       1.184
+++ usr.sbin/bgpd/kroute.c      30 Sep 2010 10:08:49 -0000
@@ -193,6 +193,7 @@ int
 kr_init(void)
 {
        int             opt = 0, rcvbuf, default_rcvbuf;
+       unsigned int    tid = RTABLE_ANY;
        socklen_t       optlen;
 
        if ((kr_state.fd = socket(AF_ROUTE, SOCK_RAW, 0)) == -1) {
@@ -217,6 +218,12 @@ kr_init(void)
                    &rcvbuf, sizeof(rcvbuf)) == -1 && errno == ENOBUFS;
                    rcvbuf /= 2)
                        ;       /* nothing */
+
+       if (setsockopt(kr_state.fd, AF_ROUTE, ROUTE_TABLEFILTER, &tid,
+           sizeof(tid)) == -1) {
+               log_warn("kr_init: setsockopt AF_ROUTE ROUTE_TABLEFILTER");
+               return (-1);
+       }
 
        kr_state.pid = getpid();
        kr_state.rtseq = 1;

Reply via email to