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;