diff options
author | Claudio Jeker <claudio@cvs.openbsd.org> | 2009-01-28 12:34:10 +0000 |
---|---|---|
committer | Claudio Jeker <claudio@cvs.openbsd.org> | 2009-01-28 12:34:10 +0000 |
commit | c6f251c468201e9a9d210697044f12e5e64e4d6f (patch) | |
tree | 2c7111720c6fc449b2f384f5c113da67484f2f43 /sys/net | |
parent | f799ce5ad12dc57f158c92360fd1fcb6e973017a (diff) |
Implement basic routing socket filtering. It is possible to give a list --
actually a bitfield -- of routing messages a listener is interested in.
This list can be changed with a setsockopt(s, AF_ROUTE, ROUTE_MSGFILTER, ...)
call. OK henning@, dlg@
Diffstat (limited to 'sys/net')
-rw-r--r-- | sys/net/route.h | 10 | ||||
-rw-r--r-- | sys/net/rtsock.c | 155 |
2 files changed, 156 insertions, 9 deletions
diff --git a/sys/net/route.h b/sys/net/route.h index 3ce2f803fba..45df3f207d2 100644 --- a/sys/net/route.h +++ b/sys/net/route.h @@ -1,4 +1,4 @@ -/* $OpenBSD: route.h,v 1.56 2009/01/08 12:47:45 michele Exp $ */ +/* $OpenBSD: route.h,v 1.57 2009/01/28 12:34:09 claudio Exp $ */ /* $NetBSD: route.h,v 1.9 1996/02/13 22:00:49 christos Exp $ */ /* @@ -297,6 +297,14 @@ struct rt_omsghdr { #define RTAX_LABEL 10 /* route label present */ #define RTAX_MAX 11 /* size of array to allocate */ +/* + * setsockopt defines used for the filtering. + */ +#define ROUTE_MSGFILTER 1 /* bitmask to specifiy which types should be + sent to the client. */ + +#define ROUTE_SETFILTER(x, m) (x) |= (1 << (m)) + struct rt_addrinfo { int rti_addrs; struct sockaddr *rti_info[RTAX_MAX]; diff --git a/sys/net/rtsock.c b/sys/net/rtsock.c index 7d830e5e40c..e63e31063ea 100644 --- a/sys/net/rtsock.c +++ b/sys/net/rtsock.c @@ -1,4 +1,4 @@ -/* $OpenBSD: rtsock.c,v 1.81 2009/01/08 12:47:45 michele Exp $ */ +/* $OpenBSD: rtsock.c,v 1.82 2009/01/28 12:34:09 claudio Exp $ */ /* $NetBSD: rtsock.c,v 1.18 1996/03/29 00:32:10 cgd Exp $ */ /* @@ -92,6 +92,9 @@ struct walkarg { caddr_t w_where, w_tmem; }; +int route_ctloutput(int, struct socket *, int, int, struct mbuf **); +void route_input(struct mbuf *m0, ...); + struct mbuf *rt_msg1(int, struct rt_addrinfo *); int rt_msg2(int, int, struct rt_addrinfo *, caddr_t, struct walkarg *); @@ -109,6 +112,13 @@ struct rt_msghdr *rtmsg_3to4(struct mbuf *, int *); #define ifaaddr info.rti_info[RTAX_IFA] #define brdaddr info.rti_info[RTAX_BRD] +struct routecb { + struct rawcb rcb; + unsigned int msgfilter; +}; +#define sotoroutecb(so) ((struct routecb *)(so)->so_pcb) + + int route_usrreq(struct socket *so, int req, struct mbuf *m, struct mbuf *nam, struct mbuf *control, struct proc *p) @@ -117,8 +127,12 @@ route_usrreq(struct socket *so, int req, struct mbuf *m, struct mbuf *nam, struct rawcb *rp = sotorawcb(so); int s; + /* + * use the rawcb but allocate a rooutecb, this code does not care + * about the additional fields and works directly on the raw socket. + */ if (req == PRU_ATTACH) { - rp = malloc(sizeof(*rp), M_PCB, M_WAITOK|M_ZERO); + rp = malloc(sizeof(struct routecb), M_PCB, M_WAITOK|M_ZERO); so->so_pcb = rp; } if (req == PRU_DETACH && rp) { @@ -169,6 +183,131 @@ route_usrreq(struct socket *so, int req, struct mbuf *m, struct mbuf *nam, } int +route_ctloutput(int op, struct socket *so, int level, int optname, + struct mbuf **mp) +{ + struct routecb *rop = sotoroutecb(so); + struct mbuf *m = *mp; + int error = 0; + + if (level != AF_ROUTE) { + error = EINVAL; + if (op == PRCO_SETOPT && *mp) + m_free(*mp); + return (error); + } + + switch (op) { + case PRCO_SETOPT: + switch (optname) { + case ROUTE_MSGFILTER: + if (m == NULL || m->m_len != sizeof(unsigned int)) + error = EINVAL; + else + rop->msgfilter = *mtod(m, unsigned int *); + break; + default: + error = ENOPROTOOPT; + break; + } + if (m) + m_free(m); + break; + case PRCO_GETOPT: + switch (optname) { + case ROUTE_MSGFILTER: + *mp = m = m_get(M_WAIT, MT_SOOPTS); + m->m_len = sizeof(int); + *mtod(m, unsigned int *) = rop->msgfilter; + break; + default: + error = ENOPROTOOPT; + break; + } + } + return (error); +} + +void +route_input(struct mbuf *m0, ...) +{ + struct rawcb *rp; + struct routecb *rop; + struct mbuf *m = m0; + int sockets = 0; + struct socket *last; + va_list ap; + struct sockproto *proto; + struct sockaddr *sosrc, *sodst; + + va_start(ap, m0); + proto = va_arg(ap, struct sockproto *); + sosrc = va_arg(ap, struct sockaddr *); + sodst = va_arg(ap, struct sockaddr *); + va_end(ap); + + /* ensure that we can access the rtm_type via mtod() */ + if (m->m_len < offsetof(struct rt_msghdr, rtm_type) + 1) { + m_freem(m); + return; + } + + last = 0; + LIST_FOREACH(rp, &rawcb, rcb_list) { + if (rp->rcb_proto.sp_family != proto->sp_family) + continue; + if (rp->rcb_proto.sp_protocol && + rp->rcb_proto.sp_protocol != proto->sp_protocol) + continue; + /* + * We assume the lower level routines have + * placed the address in a canonical format + * suitable for a structure comparison. + * + * Note that if the lengths are not the same + * the comparison will fail at the first byte. + */ +#define equal(a1, a2) \ + (bcmp((caddr_t)(a1), (caddr_t)(a2), a1->sa_len) == 0) + if (rp->rcb_laddr && !equal(rp->rcb_laddr, sodst)) + continue; + if (rp->rcb_faddr && !equal(rp->rcb_faddr, sosrc)) + continue; + + /* filter messages that the process does not want */ + rop = (struct routecb *)rp; + if (rop->msgfilter != 0 && !(rop->msgfilter & (1 << + mtod(m, struct rt_msghdr *)->rtm_type))) + continue; + + if (last) { + struct mbuf *n; + if ((n = m_copy(m, 0, (int)M_COPYALL)) != NULL) { + if (sbappendaddr(&last->so_rcv, sosrc, + n, (struct mbuf *)0) == 0) + /* should notify about lost packet */ + m_freem(n); + else { + sorwakeup(last); + sockets++; + } + } + } + last = rp->rcb_socket; + } + if (last) { + if (sbappendaddr(&last->so_rcv, sosrc, + m, (struct mbuf *)0) == 0) + m_freem(m); + else { + sorwakeup(last); + sockets++; + } + } else + m_freem(m); +} + +int route_output(struct mbuf *m, ...) { struct rt_msghdr *rtm = NULL; @@ -581,7 +720,7 @@ flush: Free(rtm); } if (m) - raw_input(m, &route_proto, &route_src, &route_dst); + route_input(m, &route_proto, &route_src, &route_dst); if (rp) rp->rcb_proto.sp_family = PF_ROUTE; @@ -814,7 +953,7 @@ rt_missmsg(int type, struct rt_addrinfo *rtinfo, int flags, route_proto.sp_protocol = 0; else route_proto.sp_protocol = sa->sa_family; - raw_input(m, &route_proto, &route_src, &route_dst); + route_input(m, &route_proto, &route_src, &route_dst); } /* @@ -838,7 +977,7 @@ rt_ifmsg(struct ifnet *ifp) ifm->ifm_data = ifp->if_data; ifm->ifm_addrs = 0; route_proto.sp_protocol = 0; - raw_input(m, &route_proto, &route_src, &route_dst); + route_input(m, &route_proto, &route_src, &route_dst); } /* @@ -905,7 +1044,7 @@ rt_newaddrmsg(int cmd, struct ifaddr *ifa, int error, struct rtentry *rt) route_proto.sp_protocol = 0; else route_proto.sp_protocol = sa->sa_family; - raw_input(m, &route_proto, &route_src, &route_dst); + route_input(m, &route_proto, &route_src, &route_dst); } } @@ -929,7 +1068,7 @@ rt_ifannouncemsg(struct ifnet *ifp, int what) strlcpy(ifan->ifan_name, ifp->if_xname, sizeof(ifan->ifan_name)); ifan->ifan_what = what; route_proto.sp_protocol = 0; - raw_input(m, &route_proto, &route_src, &route_dst); + route_input(m, &route_proto, &route_src, &route_dst); } /* @@ -1229,7 +1368,7 @@ extern struct domain routedomain; /* or at least forward */ struct protosw routesw[] = { { SOCK_RAW, &routedomain, 0, PR_ATOMIC|PR_ADDR, - raw_input, route_output, raw_ctlinput, 0, + route_input, route_output, raw_ctlinput, route_ctloutput, route_usrreq, raw_init, 0, 0, 0, sysctl_rtable, |