summaryrefslogtreecommitdiff
path: root/sys/net/rtsock.c
diff options
context:
space:
mode:
authorClaudio Jeker <claudio@cvs.openbsd.org>2009-01-28 12:34:10 +0000
committerClaudio Jeker <claudio@cvs.openbsd.org>2009-01-28 12:34:10 +0000
commitc6f251c468201e9a9d210697044f12e5e64e4d6f (patch)
tree2c7111720c6fc449b2f384f5c113da67484f2f43 /sys/net/rtsock.c
parentf799ce5ad12dc57f158c92360fd1fcb6e973017a (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/rtsock.c')
-rw-r--r--sys/net/rtsock.c155
1 files changed, 147 insertions, 8 deletions
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,