On Thu, Nov 14, 2013 at 05:38:14PM -0700, Theo de Raadt wrote: > Beautiful.
I seems there was enough discussion. The Security argument is more important than the others. The new diff has no performance impact when pf is turned on. So I need OKs. bluhm Index: net/pf.c =================================================================== RCS file: /data/mirror/openbsd/cvs/src/sys/net/pf.c,v retrieving revision 1.857 diff -u -p -u -p -r1.857 pf.c --- net/pf.c 30 Oct 2013 11:35:10 -0000 1.857 +++ net/pf.c 13 Nov 2013 23:14:32 -0000 @@ -6490,6 +6490,7 @@ pf_test(sa_family_t af, int fwdir, struc } } pd.eh = eh; + pd.m->m_pkthdr.pf.flags |= PF_TAG_PROCESSED; switch (pd.virtual_proto) { Index: netinet6/ip6_input.c =================================================================== RCS file: /data/mirror/openbsd/cvs/src/sys/netinet6/ip6_input.c,v retrieving revision 1.120 diff -u -p -u -p -r1.120 ip6_input.c --- netinet6/ip6_input.c 11 Nov 2013 09:15:35 -0000 1.120 +++ netinet6/ip6_input.c 13 Nov 2013 23:38:22 -0000 @@ -122,6 +122,7 @@ struct ifqueue ip6intrq; struct ip6stat ip6stat; void ip6_init2(void *); +int ip6_check_rh0hdr(struct mbuf *, int *); int ip6_hopopts_input(u_int32_t *, u_int32_t *, struct mbuf **, int *); struct mbuf *ip6_pullexthdr(struct mbuf *, size_t, int); @@ -331,6 +332,20 @@ ip6_input(struct mbuf *m) srcrt = !IN6_ARE_ADDR_EQUAL(&odst, &ip6->ip6_dst); #endif + /* + * Be more secure than RFC5095 and scan for type 0 routing headers. + * If pf has already scanned the header chain, do not do it twice. + */ + if (!(m->m_pkthdr.pf.flags & PF_TAG_PROCESSED) && + ip6_check_rh0hdr(m, &off)) { + ip6stat.ip6s_badoptions++; + in6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_discard); + in6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_hdrerr); + icmp6_error(m, ICMP6_PARAM_PROB, ICMP6_PARAMPROB_HEADER, off); + /* m is already freed */ + return; + } + if (IN6_IS_ADDR_LOOPBACK(&ip6->ip6_src) || IN6_IS_ADDR_LOOPBACK(&ip6->ip6_dst)) { ours = 1; @@ -698,6 +713,74 @@ ip6_input(struct mbuf *m) return; bad: m_freem(m); +} + +/* scan packet for RH0 routing header. Mostly stolen from pf.c:pf_test() */ +int +ip6_check_rh0hdr(struct mbuf *m, int *offp) +{ + struct ip6_hdr *ip6 = mtod(m, struct ip6_hdr *); + struct ip6_rthdr rthdr; + struct ip6_ext opt6; + u_int8_t proto = ip6->ip6_nxt; + int done = 0, lim, off, rh_cnt = 0; + + off = ((caddr_t)ip6 - m->m_data) + sizeof(struct ip6_hdr); + lim = min(m->m_pkthdr.len, ntohs(ip6->ip6_plen) + sizeof(*ip6)); + do { + switch (proto) { + case IPPROTO_ROUTING: + *offp = off; + if (rh_cnt++) { + /* more then one rh header present */ + return (1); + } + + if (off + sizeof(rthdr) > lim) { + /* packet to short to make sense */ + return (1); + } + + m_copydata(m, off, sizeof(rthdr), (caddr_t)&rthdr); + + if (rthdr.ip6r_type == IPV6_RTHDR_TYPE_0) { + *offp += offsetof(struct ip6_rthdr, ip6r_type); + return (1); + } + + off += (rthdr.ip6r_len + 1) * 8; + proto = rthdr.ip6r_nxt; + break; + case IPPROTO_AH: + case IPPROTO_HOPOPTS: + case IPPROTO_DSTOPTS: + /* get next header and header length */ + if (off + sizeof(opt6) > lim) { + /* + * Packet to short to make sense, we could + * reject the packet but as a router we + * should not do that so forward it. + */ + return (0); + } + + m_copydata(m, off, sizeof(opt6), (caddr_t)&opt6); + + if (proto == IPPROTO_AH) + off += (opt6.ip6e_len + 2) * 4; + else + off += (opt6.ip6e_len + 1) * 8; + proto = opt6.ip6e_nxt; + break; + case IPPROTO_FRAGMENT: + default: + /* end of header stack */ + done = 1; + break; + } + } while (!done); + + return (0); } /* Index: sys/mbuf.h =================================================================== RCS file: /data/mirror/openbsd/cvs/src/sys/sys/mbuf.h,v retrieving revision 1.168 diff -u -p -u -p -r1.168 mbuf.h --- sys/mbuf.h 13 Oct 2013 10:10:04 -0000 1.168 +++ sys/mbuf.h 13 Nov 2013 23:14:32 -0000 @@ -100,6 +100,7 @@ struct pkthdr_pf { #define PF_TAG_DIVERTED_PACKET 0x10 #define PF_TAG_REROUTE 0x20 #define PF_TAG_REFRAGMENTED 0x40 /* refragmented ipv6 packet */ +#define PF_TAG_PROCESSED 0x80 /* packet was checked by pf */ /* record/packet header in first mbuf of chain; valid if M_PKTHDR set */ struct pkthdr {