diff options
author | Cedric Berger <cedric@cvs.openbsd.org> | 2004-06-06 16:49:10 +0000 |
---|---|---|
committer | Cedric Berger <cedric@cvs.openbsd.org> | 2004-06-06 16:49:10 +0000 |
commit | 9e25ab1fcd3c8a3b75b6ae3c7ca26a172a86d2e8 (patch) | |
tree | 021044703db486221cd747d714184ece1513b162 /sys/net | |
parent | ac4791c970556454694a0c9ab754ea95de5b9bc1 (diff) |
extend routing table to be able to match and route packets based on
their *source* IP address in addition to their destination address.
routing table "destination" now contains a "struct sockaddr_rtin"
for IPv4 instead of a "struct sockaddr_in".
the routing socket has been extended in a backward-compatible way.
todo: PMTU enhancements, IPv6. ok deraadt@ mcbride@
Diffstat (limited to 'sys/net')
-rw-r--r-- | sys/net/pf.c | 28 | ||||
-rw-r--r-- | sys/net/route.c | 24 | ||||
-rw-r--r-- | sys/net/route.h | 26 | ||||
-rw-r--r-- | sys/net/route_src.c | 203 | ||||
-rw-r--r-- | sys/net/rtsock.c | 7 |
5 files changed, 269 insertions, 19 deletions
diff --git a/sys/net/pf.c b/sys/net/pf.c index 12cff3c1ef8..0eead2874af 100644 --- a/sys/net/pf.c +++ b/sys/net/pf.c @@ -1,4 +1,4 @@ -/* $OpenBSD: pf.c,v 1.449 2004/05/19 17:50:51 dhartmei Exp $ */ +/* $OpenBSD: pf.c,v 1.450 2004/06/06 16:49:08 cedric Exp $ */ /* * Copyright (c) 2001 Daniel Hartmeier @@ -199,8 +199,8 @@ u_int8_t pf_get_wscale(struct mbuf *, int, u_int16_t, sa_family_t); u_int16_t pf_get_mss(struct mbuf *, int, u_int16_t, sa_family_t); -u_int16_t pf_calc_mss(struct pf_addr *, sa_family_t, - u_int16_t); +u_int16_t pf_calc_mss(struct pf_addr *, struct pf_addr *, + sa_family_t, u_int16_t); void pf_set_rt_ifp(struct pf_state *, struct pf_addr *); int pf_check_proto_cksum(struct mbuf *, int, int, @@ -2415,10 +2415,11 @@ pf_get_mss(struct mbuf *m, int off, u_int16_t th_off, sa_family_t af) } u_int16_t -pf_calc_mss(struct pf_addr *addr, sa_family_t af, u_int16_t offer) +pf_calc_mss(struct pf_addr *saddr, struct pf_addr *daddr, sa_family_t af, + u_int16_t offer) { #ifdef INET - struct sockaddr_in *dst; + struct sockaddr_rtin *dst; struct route ro; #endif /* INET */ #ifdef INET6 @@ -2434,10 +2435,11 @@ pf_calc_mss(struct pf_addr *addr, sa_family_t af, u_int16_t offer) case AF_INET: hlen = sizeof(struct ip); bzero(&ro, sizeof(ro)); - dst = (struct sockaddr_in *)&ro.ro_dst; - dst->sin_family = AF_INET; - dst->sin_len = sizeof(*dst); - dst->sin_addr = addr->v4; + dst = satortin(&ro.ro_dst); + dst->rtin_family = AF_INET; + dst->rtin_len = sizeof(*dst); + dst->rtin_dst = daddr->v4; + dst->rtin_src = saddr->v4; rtalloc_noclone(&ro, NO_CLONING); rt = ro.ro_rt; break; @@ -2449,7 +2451,7 @@ pf_calc_mss(struct pf_addr *addr, sa_family_t af, u_int16_t offer) dst6 = (struct sockaddr_in6 *)&ro6.ro_dst; dst6->sin6_family = AF_INET6; dst6->sin6_len = sizeof(*dst6); - dst6->sin6_addr = addr->v6; + dst6->sin6_addr = daddr->v6; rtalloc_noclone((struct route *)&ro6, NO_CLONING); rt = ro6.ro_rt; break; @@ -2838,8 +2840,8 @@ cleanup: s->src.seqhi = htonl(arc4random()); /* Find mss option */ mss = pf_get_mss(m, off, th->th_off, af); - mss = pf_calc_mss(saddr, af, mss); - mss = pf_calc_mss(daddr, af, mss); + mss = pf_calc_mss(saddr, daddr, af, mss); + mss = pf_calc_mss(daddr, saddr, af, mss); s->src.mss = mss; pf_send_tcp(r, af, daddr, saddr, th->th_dport, th->th_sport, s->src.seqhi, ntohl(th->th_seq) + 1, @@ -4999,7 +5001,9 @@ pf_route(struct mbuf **m, struct pf_rule *r, int dir, struct ifnet *oifp, dst->sin_addr = ip->ip_dst; if (r->rt == PF_FASTROUTE) { + satortin(&ro->ro_dst)->rtin_src = ip->ip_src; rtalloc(ro); + satortin(&ro->ro_dst)->rtin_src.s_addr = 0; if (ro->ro_rt == 0) { ipstat.ips_noroute++; goto bad; diff --git a/sys/net/route.c b/sys/net/route.c index 06fa17d9dcc..62c5b865121 100644 --- a/sys/net/route.c +++ b/sys/net/route.c @@ -1,4 +1,4 @@ -/* $OpenBSD: route.c,v 1.40 2004/04/25 02:48:04 itojun Exp $ */ +/* $OpenBSD: route.c,v 1.41 2004/06/06 16:49:09 cedric Exp $ */ /* $NetBSD: route.c,v 1.14 1996/02/13 22:00:46 christos Exp $ */ /* @@ -138,6 +138,9 @@ struct radix_node_head *rt_tables[AF_MAX+1]; int rttrash; /* routes not in table but not freed */ struct sockaddr wildcard; /* zero valued cookie for wildcard searches */ +const struct sockaddr_rtin rt_defmask4 = { /* default IPv4 route mask */ + offsetof(struct sockaddr_rtin, rtin_src), 0, 0, { -1 }}; + static int okaytoclone(u_int, int); static int rtdeletemsg(struct rtentry *); static int rtflushclone1(struct radix_node *, void *); @@ -668,8 +671,14 @@ rtrequest1(req, info, ret_nrt) if ((rnh = rt_tables[dst->sa_family]) == 0) senderr(EAFNOSUPPORT); - if (flags & RTF_HOST) - netmask = 0; + if (flags & RTF_HOST) { +#ifdef SMALL_KERNEL + netmask = (dst->sa_family == AF_INET) ? + (struct sockaddr *)&rt_defmask4 : NULL; +#else + sroute_verify_host(info); +#endif + } switch (req) { case RTM_DELETE: if ((rn = rnh->rnh_lookup(dst, netmask, rnh)) == NULL) @@ -726,8 +735,15 @@ rtrequest1(req, info, ret_nrt) flags = rt->rt_flags & ~(RTF_CLONING | RTF_STATIC); flags |= RTF_CLONED; gateway = rt->rt_gateway; - if ((netmask = rt->rt_genmask) == NULL) +#ifdef SMALL_KERNEL + if ((netmask = rt->rt_genmask) == NULL) { flags |= RTF_HOST; + if (dst->sa_family == AF_INET) + netmask = (struct sockaddr *)&rt_defmask4; + } +#else + sroute_clone_route(info, rt_mask(rt), rt->rt_genmask); +#endif goto makeroute; case RTM_ADD: diff --git a/sys/net/route.h b/sys/net/route.h index 2b79cd10dbb..fefbda574d2 100644 --- a/sys/net/route.h +++ b/sys/net/route.h @@ -1,4 +1,4 @@ -/* $OpenBSD: route.h,v 1.21 2004/05/04 22:50:18 claudio Exp $ */ +/* $OpenBSD: route.h,v 1.22 2004/06/06 16:49:09 cedric Exp $ */ /* $NetBSD: route.h,v 1.9 1996/02/13 22:00:49 christos Exp $ */ /* @@ -150,6 +150,7 @@ struct ortentry { #define RTF_PROTO2 0x4000 /* protocol specific routing flag */ #define RTF_PROTO1 0x8000 /* protocol specific routing flag */ #define RTF_CLONED 0x10000 /* this is a cloned route */ +#define RTF_SOURCE 0x20000 /* this route has a source selector */ #ifndef _KERNEL /* obsoleted */ @@ -223,6 +224,8 @@ struct rt_msghdr { #define RTA_IFA 0x20 /* interface addr sockaddr present */ #define RTA_AUTHOR 0x40 /* sockaddr for author of redirect */ #define RTA_BRD 0x80 /* for NEWADDR, broadcast or p-p dest addr */ +#define RTA_SRC 0x100 /* source sockaddr present */ +#define RTA_SRCMASK 0x200 /* source netmask present */ /* * Index offsets for sockaddr array for alternate internal encoding. @@ -235,7 +238,9 @@ struct rt_msghdr { #define RTAX_IFA 5 /* interface addr sockaddr present */ #define RTAX_AUTHOR 6 /* sockaddr for author of redirect */ #define RTAX_BRD 7 /* for NEWADDR, broadcast or p-p dest addr */ -#define RTAX_MAX 8 /* size of array to allocate */ +#define RTAX_SRC 8 /* source sockaddr present */ +#define RTAX_SRCMASK 9 /* source netmask present */ +#define RTAX_MAX 10 /* size of array to allocate */ struct rt_addrinfo { int rti_addrs; @@ -295,6 +300,7 @@ struct rttimer_queue { extern struct route_cb route_cb; extern struct rtstat rtstat; extern struct radix_node_head *rt_tables[]; +extern const struct sockaddr_rtin rt_defmask4; struct socket; void route_init(void); @@ -340,5 +346,21 @@ int rtrequest(int, struct sockaddr *, struct sockaddr *, struct sockaddr *, int, struct rtentry **); int rtrequest1(int, struct rt_addrinfo *, struct rtentry **); + +#ifndef SMALL_KERNEL +void sroute_verify_host(struct rt_addrinfo *); +void sroute_clone_route(struct rt_addrinfo *, struct sockaddr *, + struct sockaddr *); +void sroute_compact(struct rt_addrinfo *, int); +void sroute_expand(struct rt_addrinfo *); +struct sockaddr * + sroute_clone_mask4(struct sockaddr *, struct sockaddr *); +#else +#define sroute_compact(ai, int) +#define sroute_expand(ai) +#define sroute_clone_mask4(old, gen) ((gen) != NULL ? (gen) : \ + (struct sockaddr *)&rt_defmask4) +#endif + #endif /* _KERNEL */ #endif /* _NET_ROUTE_H_ */ diff --git a/sys/net/route_src.c b/sys/net/route_src.c new file mode 100644 index 00000000000..8287543f249 --- /dev/null +++ b/sys/net/route_src.c @@ -0,0 +1,203 @@ +/* $OpenBSD: route_src.c,v 1.1 2004/06/06 16:49:09 cedric Exp $ */ + +/* + * Copyright (c) 2004 Cedric Berger <cedric@berger.to> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <sys/param.h> +#include <sys/mbuf.h> +#include <sys/systm.h> +#include <sys/socket.h> + +#include <net/route.h> +#include <netinet/in.h> + +void mask_set(void *, struct sockaddr *, int, int); +struct sockaddr *mask_trim(struct sockaddr *, int, int); + +/* + * verify that the netmask is NULL or has all 1s for the destination part. + * setup a corrected mask otherwise. + */ +void +sroute_verify_host(struct rt_addrinfo *ai) +{ + static struct sockaddr_rtin netmask4; + + if (ai->rti_info[RTAX_NETMASK] == NULL) + return; /* no mask is a host mask */ + if (ai->rti_info[RTAX_DST]->sa_family == AF_INET) + goto _inet; + /* just clear the netmask */ + ai->rti_info[RTAX_NETMASK] = NULL; + return; + +_inet: + if (satortin(ai->rti_info[RTAX_NETMASK])->rtin_dst.s_addr == + (in_addr_t)-1) + return; /* all 1s is a host mask */ + netmask4.rtin_dst.s_addr = (in_addr_t)-1; + netmask4.rtin_src = satortin(ai->rti_info[RTAX_NETMASK])->rtin_src; + ai->rti_info[RTAX_NETMASK] = mask_trim(sintosa(&netmask4), + offsetof(struct sockaddr_rtin, rtin_dst), 8); +} + +/* + * set the netmask of a cloned route. take the source part of the + * old mask and use the genmask (or NULL) for the destination part. + */ +void +sroute_clone_route(struct rt_addrinfo *ai, struct sockaddr *oldmask, + struct sockaddr *genmask) +{ + if (ai->rti_info[RTAX_DST]->sa_family == AF_INET) + goto _inet; + if ((ai->rti_info[RTAX_NETMASK] = genmask) == NULL) + ai->rti_flags |= RTF_HOST; + return; + +_inet: + ai->rti_info[RTAX_NETMASK] = sroute_clone_mask4(oldmask, genmask); + if (ai->rti_info[RTAX_NETMASK] == NULL || satortin( + ai->rti_info[RTAX_NETMASK])->rtin_dst.s_addr == (in_addr_t)-1) + ai->rti_flags |= RTF_HOST; +} + +/* + * get the netmask of an IPv4 cloned route. take the source part of the + * old mask and use the genmask (or NULL) for the destination part. + */ +struct sockaddr * +sroute_clone_mask4(struct sockaddr *oldmask, struct sockaddr *genmask) +{ + static struct sockaddr_rtin netmask4; + + mask_set(&netmask4.rtin_dst, genmask, + offsetof(struct sockaddr_in, sin_addr), 4); + mask_set(&netmask4.rtin_src, oldmask, + offsetof(struct sockaddr_rtin, rtin_src), 4); + return mask_trim(sintosa(&netmask4), + offsetof(struct sockaddr_rtin, rtin_dst), 8); +} + +/* + * userland provides RTAX_SRC and RTAX_SRCMASK entries, but the kernel wants + * that info packed inside RTAX_DST and RTAX_NETMASK themselves. + */ +void +sroute_compact(struct rt_addrinfo *ai, int type) +{ + static struct sockaddr_rtin dst4 = { sizeof(dst4), AF_INET }; + static struct sockaddr_rtin netmask4; + + if (ai->rti_info[RTAX_DST]->sa_family == AF_INET) + goto _inet; + return; + +_inet: + dst4.rtin_dst = satosin(ai->rti_info[RTAX_DST])->sin_addr; + mask_set(&netmask4.rtin_dst, ai->rti_info[RTAX_NETMASK], + offsetof(struct sockaddr_in, sin_addr), 4); + if (ai->rti_info[RTAX_SRC] != NULL) { + dst4.rtin_src = satosin(ai->rti_info[RTAX_SRC])->sin_addr; + mask_set(&netmask4.rtin_src, ai->rti_info[RTAX_SRCMASK], + offsetof(struct sockaddr_in, sin_addr), 4); + } else + dst4.rtin_src.s_addr = netmask4.rtin_src.s_addr = 0; + ai->rti_info[RTAX_DST] = (struct sockaddr *)&dst4; + /* + * do not generate a netmask artificially for RTM_GET or it + * will break the loose-matching behaviour that is expected. + */ + if (type != RTM_GET || ai->rti_info[RTAX_NETMASK] != NULL || + ai->rti_info[RTAX_SRCMASK] != NULL) + ai->rti_info[RTAX_NETMASK] = mask_trim(sintosa(&netmask4), + offsetof(struct sockaddr_rtin, rtin_dst), 8); +} + +/* + * opposite of sroute_compact, when sending a routing message to userland. + */ +void +sroute_expand(struct rt_addrinfo *ai) +{ + static struct sockaddr_in dst4 = { sizeof(dst4), AF_INET }; + static struct sockaddr_in src4 = { sizeof(src4), AF_INET }; + static struct sockaddr_in netmask4, srcmask4; + + if (ai->rti_info[RTAX_DST]->sa_family == AF_INET) + goto _inet; + return; + +_inet: + dst4.sin_addr = satortin(ai->rti_info[RTAX_DST])->rtin_dst; + src4.sin_addr = satortin(ai->rti_info[RTAX_DST])->rtin_src; + ai->rti_info[RTAX_DST] = sintosa(&dst4); + ai->rti_info[RTAX_SRC] = sintosa(&src4); + mask_set(&netmask4.sin_addr, ai->rti_info[RTAX_NETMASK], + offsetof(struct sockaddr_rtin, rtin_dst), 4); + mask_set(&srcmask4.sin_addr, ai->rti_info[RTAX_NETMASK], + offsetof(struct sockaddr_rtin, rtin_src), 4); + ai->rti_info[RTAX_NETMASK] = mask_trim(sintosa(&netmask4), + offsetof(struct sockaddr_in, sin_addr), 4); + ai->rti_info[RTAX_SRCMASK] = mask_trim(sintosa(&srcmask4), + offsetof(struct sockaddr_in, sin_addr), 4); + if (!src4.sin_addr.s_addr && !srcmask4.sin_addr.s_addr) { + ai->rti_info[RTAX_SRC] = NULL; + ai->rti_info[RTAX_SRCMASK] = NULL; + } +} + +/* + * set a netmask from a potentially NULL or truncated mask. + */ +void +mask_set(void *mask, struct sockaddr *sa, int off, int sz) +{ + int i, len = off + sz; + + if (sa == NULL) { + memset(mask, -1, sz); + return; + } + bzero(mask, sz); + for (i = off; i < len; i++) { + if (i >= sa->sa_len) + break; + *((char *)mask)++ = ((char *)sa)[i]; + } +} + +/* + * set length and trim unused bytes from a netmask. + * return NULL if the mask is all 1s (host route) + */ +struct sockaddr * +mask_trim(struct sockaddr *sa, int off, int sz) +{ + int i, len = off + sz; + + if (sa == NULL) + return (NULL); + for (i = len; i > 1 && ((char *)sa)[i-1] == 0; i--) + ; + sa->sa_len = i; + if (i < len) + return (sa); + for (i = off; i < len; i++) + if (((char *)sa)[i] != (char)-1) + break; + return ((i < len) ? sa : NULL); +} diff --git a/sys/net/rtsock.c b/sys/net/rtsock.c index 7b64447eebe..3b55a425ed3 100644 --- a/sys/net/rtsock.c +++ b/sys/net/rtsock.c @@ -1,4 +1,4 @@ -/* $OpenBSD: rtsock.c,v 1.38 2004/05/12 20:46:00 claudio Exp $ */ +/* $OpenBSD: rtsock.c,v 1.39 2004/06/06 16:49:09 cedric Exp $ */ /* $NetBSD: rtsock.c,v 1.18 1996/03/29 00:32:10 cgd Exp $ */ /* @@ -221,6 +221,7 @@ route_output(struct mbuf *m, ...) senderr(EINVAL); if (gate != 0 && (gate->sa_family >= AF_MAX)) senderr(EINVAL); + sroute_compact(&info, rtm->rtm_type); if (genmask) { struct radix_node *t; t = rn_addmask((caddr_t)genmask, 0, 1); @@ -324,6 +325,7 @@ route_output(struct mbuf *m, ...) ifaaddr = 0; } } + sroute_expand(&info); len = rt_msg2(rtm->rtm_type, &info, (caddr_t)0, (struct walkarg *)0); if (len > rtm->rtm_msglen) { @@ -637,6 +639,7 @@ rt_missmsg(type, rtinfo, flags, error) if (route_cb.any_count == 0) return; + sroute_expand(rtinfo); m = rt_msg1(type, rtinfo); if (m == 0) return; @@ -725,6 +728,7 @@ rt_newaddrmsg(cmd, ifa, error, rt) netmask = rt_mask(rt); dst = sa = rt_key(rt); gate = rt->rt_gateway; + sroute_expand(&info); if ((m = rt_msg1(cmd, &info)) == NULL) continue; rtm = mtod(m, struct rt_msghdr *); @@ -791,6 +795,7 @@ sysctl_dumpentry(rn, v) if (rt->rt_ifp->if_flags & IFF_POINTOPOINT) brdaddr = rt->rt_ifa->ifa_dstaddr; } + sroute_expand(&info); size = rt_msg2(RTM_GET, &info, 0, w); if (w->w_where && w->w_tmem && w->w_needed <= 0) { struct rt_msghdr *rtm = (struct rt_msghdr *)w->w_tmem; |