Index: rtsock.c =================================================================== RCS file: /cvs/src/sys/net/rtsock.c,v retrieving revision 1.188 diff -u -p -r1.188 rtsock.c --- rtsock.c 30 Mar 2016 10:13:14 -0000 1.188 +++ rtsock.c 6 Apr 2016 03:32:20 -0000 @@ -76,6 +76,7 @@ #include #include #include +#include #include @@ -113,6 +114,7 @@ struct routecb { struct timeout timeout; unsigned int msgfilter; unsigned int flags; + struct bpf_program *bpfilter; u_int rtableid; }; #define sotoroutecb(so) ((struct routecb *)(so)->so_pcb) @@ -124,6 +126,10 @@ struct route_cb { int any_count; }; +int route_bpf_set(struct routecb *, struct mbuf *); +int route_bpf_get(struct routecb *, struct mbuf **); +void route_bpf_free(struct bpf_program *); + struct route_cb route_cb; /* @@ -161,8 +167,7 @@ route_usrreq(struct socket *so, int req, 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); + /* * Don't call raw_usrreq() in the attach case, because * we want to allow non-privileged processes to listen @@ -177,6 +182,10 @@ route_usrreq(struct socket *so, int req, splx(s); return (error); } + + timeout_set(&rop->timeout, rt_senddesync, rp); + rop->msgfilter = ~0; + rop->rtableid = curproc->p_p->ps_rtableid; af = rp->rcb_proto.sp_protocol; if (af == AF_INET) @@ -208,7 +217,11 @@ route_usrreq(struct socket *so, int req, case PRU_DETACH: if (rp) { - timeout_del(&((struct routecb *)rp)->timeout); + struct routecb *rop = (struct routecb *)rp; + + route_bpf_free(rop->bpfilter); + timeout_del(&rop->timeout); + af = rp->rcb_proto.sp_protocol; if (af == AF_INET) route_cb.ip_count--; @@ -230,6 +243,103 @@ route_usrreq(struct socket *so, int req, } int +route_bpf_set(struct routecb *rop, struct mbuf *m) +{ + struct bpf_program *bf, *nbf; + struct bpf_insn *fcode; + struct mbuf *mn; + u_int mlen, len, size; + + if (m == NULL || m->m_len < sizeof(*bf)) + return (EINVAL); + + mlen = 0; + mn = m; + do { + mlen += mn->m_len; + + mn = mn->m_next; + } while (mn != NULL); + + bf = mtod(m, struct bpf_program *); + len = bf->bf_len; + size = len * sizeof(*bf->bf_insns); + + if (mlen != size + sizeof(*bf)) + return (EINVAL); + + if (len == 0) { + if (bf->bf_insns != NULL) + return (EINVAL); + + nbf = NULL; + } else { + fcode = mallocarray(len, sizeof(*fcode), M_RTABLE, + M_WAITOK | M_CANFAIL); + if (fcode == NULL) + return (ENOMEM); + + m_copydata(m, sizeof(*bf), size, (caddr_t)fcode); + + if (bpf_validate(fcode, len) != 0) { + free(fcode, M_RTABLE, size); + return (EINVAL); + } + + nbf = malloc(sizeof(*nbf), M_RTABLE, M_WAITOK); + nbf->bf_len = len; + nbf->bf_insns = fcode; + } + + bf = rop->bpfilter; + rop->bpfilter = nbf; + + route_bpf_free(bf); + + return (0); +} + +int +route_bpf_get(struct routecb *rop, struct mbuf **mp) +{ + struct mbuf *m; + struct bpf_program *bf, *nbf; + u_int len = sizeof(*bf), flen; + + bf = rop->bpfilter; + if (bf == NULL) + return (ENXIO); + + flen = sizeof(*bf->bf_insns) * bf->bf_len; + len += flen; + + m = m_get(M_WAIT, MT_SOOPTS); + if (len > MHLEN) + MCLGETI(m, M_WAIT, NULL, len); + + m->m_len = len; + nbf = mtod(m, struct bpf_program *); + nbf->bf_len = bf->bf_len; + nbf->bf_insns = NULL; + + memcpy(nbf + 1, bf->bf_insns, flen); + + *mp = m; + + return (0); +} + +void +route_bpf_free(struct bpf_program *bf) +{ + if (bf == NULL) + return; + + free(bf->bf_insns, M_RTABLE, bf->bf_len * sizeof(*bf->bf_insns)); + free(bf, M_DEVBUF, sizeof(*bf)); +} + +int route_ctloutput(int op, struct socket *so, int level, int optname, struct mbuf **mp) { @@ -265,6 +375,9 @@ route_ctloutput(int op, struct socket *s else rop->rtableid = tid; break; + case ROUTE_BPFILTER: + error = route_bpf_set(rop, m); + break; default: error = ENOPROTOOPT; break; @@ -284,6 +397,9 @@ route_ctloutput(int op, struct socket *s m->m_len = sizeof(unsigned int); *mtod(m, unsigned int *) = rop->rtableid; break; + case ROUTE_BPFILTER: + error = route_bpf_get(rop, mp); + break; default: error = ENOPROTOOPT; break; @@ -322,6 +438,22 @@ rt_senddesync(void *data) } } +static inline int +route_msg_filter(struct routecb *rop, struct rt_msghdr *rtm, struct mbuf *m) +{ + /* RTM_DESYNC can't be filtered */ + if (rtm->rtm_type == RTM_DESYNC) + return (0); + + if (!ISSET(rop->msgfilter, 1 << rtm->rtm_type)) + return (1); + + if (rop->bpfilter && bpf_mfilter(rop->bpfilter->bf_insns, m, 0) == 0) + return (1); + + return (0); +} + void route_input(struct mbuf *m0, ...) { @@ -373,10 +505,10 @@ route_input(struct mbuf *m0, ...) /* filter messages that the process does not want */ rop = (struct routecb *)rp; rtm = mtod(m, struct rt_msghdr *); - /* but RTM_DESYNC can't be filtered */ - if (rtm->rtm_type != RTM_DESYNC && rop->msgfilter != 0 && - !(rop->msgfilter & (1 << rtm->rtm_type))) + + if (route_msg_filter(rop, rtm, m)) continue; + switch (rtm->rtm_type) { case RTM_IFANNOUNCE: case RTM_DESYNC: