From: bluhm Date: Wed, 5 Jul 2017 11:34:10 +0000 (+0000) Subject: The IP in IP input function strips the outer header and reinserts X-Git-Url: http://artulab.com/gitweb/?a=commitdiff_plain;h=bb24c5264ce5c173de4416327edfcfa6883b62ff;p=openbsd The IP in IP input function strips the outer header and reinserts the inner IP packet into the internet queue. The IPv6 local delivery code has a loop to deal with header chains. The idea is to use this loop and avoid the queueing and rescheduling. The IPsec packet will be processed in a single flow. Merge the IP deliver loop from both IP versions into a single ip_deliver() function that can handle both addresss families. This allows to process an IP in IP header like a normal extension header. If af != AF_UNSPEC, we are already in a deliver loop and have the kernel look. Then we can just return the next protocol. Otherwise we enqueue. The dequeue thread has the kernel lock and starts an IP delivery loop. OK mpi@ --- diff --git a/sys/netinet/ip_input.c b/sys/netinet/ip_input.c index 65b27842075..34373376978 100644 --- a/sys/netinet/ip_input.c +++ b/sys/netinet/ip_input.c @@ -1,4 +1,4 @@ -/* $OpenBSD: ip_input.c,v 1.313 2017/06/26 19:06:12 bluhm Exp $ */ +/* $OpenBSD: ip_input.c,v 1.314 2017/07/05 11:34:10 bluhm Exp $ */ /* $NetBSD: ip_input.c,v 1.30 1996/03/16 23:53:58 christos Exp $ */ /* @@ -61,6 +61,11 @@ #include #include +#ifdef INET6 +#include +#include +#endif + #if NPF > 0 #include #endif @@ -216,6 +221,10 @@ ip_init(void) int ip_ours(struct mbuf **mp, int *offp, int nxt, int af) { + /* We are already in a IPv4/IPv6 local deliver loop. */ + if (af != AF_UNSPEC) + return ip_local(mp, offp, nxt, af); + niq_enqueue(&ipintrq, *mp); *mp = NULL; return IPPROTO_DONE; @@ -595,36 +604,118 @@ found: } *offp = hlen; - return ip_deliver(mp, offp, ip->ip_p, AF_INET); + nxt = ip->ip_p; + /* Check wheter we are already in a IPv4/IPv6 local deliver loop. */ + if (af == AF_UNSPEC) + nxt = ip_deliver(mp, offp, nxt, AF_INET); + return nxt; bad: m_freemp(mp); return IPPROTO_DONE; } +#ifndef INET6 +#define IPSTAT_INC(name) ipstat_inc(ips_##name) +#else +#define IPSTAT_INC(name) (af == AF_INET ? \ + ipstat_inc(ips_##name) : ip6stat_inc(ip6s_##name)) +#endif + int ip_deliver(struct mbuf **mp, int *offp, int nxt, int af) { + struct protosw *psw; + int naf = af; +#ifdef INET6 + int nest = 0; +#endif /* INET6 */ + KERNEL_ASSERT_LOCKED(); /* pf might have modified stuff, might have to chksum */ - in_proto_cksum_out(*mp, NULL); + switch (af) { + case AF_INET: + in_proto_cksum_out(*mp, NULL); + break; +#ifdef INET6 + case AF_INET6: + in6_proto_cksum_out(*mp, NULL); + break; +#endif /* INET6 */ + } -#ifdef IPSEC - if (ipsec_in_use) { - if (ipsec_local_check(*mp, *offp, nxt, af) != 0) { - ipstat_inc(ips_cantforward); + /* + * Tell launch routine the next header + */ + IPSTAT_INC(delivered); + + while (nxt != IPPROTO_DONE) { +#ifdef INET6 + if (af == AF_INET6 && + ip6_hdrnestlimit && (++nest > ip6_hdrnestlimit)) { + ip6stat_inc(ip6s_toomanyhdr); goto bad; } - } - /* Otherwise, just fall through and deliver the packet */ +#endif /* INET6 */ + + /* + * protection against faulty packet - there should be + * more sanity checks in header chain processing. + */ + if ((*mp)->m_pkthdr.len < *offp) { + IPSTAT_INC(tooshort); + goto bad; + } + +#ifdef INET6 + /* draft-itojun-ipv6-tcp-to-anycast */ + if (af == AF_INET6 && + ISSET((*mp)->m_flags, M_ACAST) && (nxt == IPPROTO_TCP)) { + if ((*mp)->m_len >= sizeof(struct ip6_hdr)) { + icmp6_error(*mp, ICMP6_DST_UNREACH, + ICMP6_DST_UNREACH_ADDR, + offsetof(struct ip6_hdr, ip6_dst)); + *mp = NULL; + } + goto bad; + } +#endif /* INET6 */ + +#ifdef IPSEC + if (ipsec_in_use) { + if (ipsec_local_check(*mp, *offp, nxt, af) != 0) { + IPSTAT_INC(cantforward); + goto bad; + } + } + /* Otherwise, just fall through and deliver the packet */ #endif /* IPSEC */ - /* - * Switch out to protocol's input routine. - */ - ipstat_inc(ips_delivered); - nxt = (*inetsw[ip_protox[nxt]].pr_input)(mp, offp, nxt, af); - KASSERT(nxt == IPPROTO_DONE); + switch (nxt) { + case IPPROTO_IPV4: + naf = AF_INET; + ipstat_inc(ips_delivered); + break; +#ifdef INET6 + case IPPROTO_IPV6: + naf = AF_INET6; + ip6stat_inc(ip6s_delivered); + break; +#endif /* INET6 */ + } + switch (af) { + case AF_INET: + psw = &inetsw[ip_protox[nxt]]; + break; +#ifdef INET6 + case AF_INET6: + psw = &inet6sw[ip6_protox[nxt]]; + break; +#endif /* INET6 */ + } + nxt = (*psw->pr_input)(mp, offp, nxt, af); + af = naf; + } return nxt; #ifdef IPSEC bad: @@ -632,6 +723,7 @@ ip_deliver(struct mbuf **mp, int *offp, int nxt, int af) m_freemp(mp); return IPPROTO_DONE; } +#undef IPSTAT_INC int in_ouraddr(struct mbuf *m, struct ifnet *ifp, struct rtentry **prt) diff --git a/sys/netinet/ip_ipip.c b/sys/netinet/ip_ipip.c index a7de3254c7a..1eb94e06d70 100644 --- a/sys/netinet/ip_ipip.c +++ b/sys/netinet/ip_ipip.c @@ -1,4 +1,4 @@ -/* $OpenBSD: ip_ipip.c,v 1.85 2017/06/20 11:12:13 bluhm Exp $ */ +/* $OpenBSD: ip_ipip.c,v 1.86 2017/07/05 11:34:10 bluhm Exp $ */ /* * The authors of this code are John Ioannidis (ji@tla.org), * Angelos D. Keromytis (kermit@csd.uch.gr) and @@ -316,14 +316,10 @@ ipip_input_if(struct mbuf **mp, int *offp, int proto, int oaf, switch (proto) { case IPPROTO_IPV4: - ipv4_input(ifp, m); - *mp = NULL; - return IPPROTO_DONE; + return ip_input_if(mp, offp, proto, oaf, ifp); #ifdef INET6 case IPPROTO_IPV6: - ipv6_input(ifp, m); - *mp = NULL; - return IPPROTO_DONE; + return ip6_input_if(mp, offp, proto, oaf, ifp); #endif } bad: diff --git a/sys/netinet/ipsec_input.c b/sys/netinet/ipsec_input.c index 05c0ae4ba13..a3fad485026 100644 --- a/sys/netinet/ipsec_input.c +++ b/sys/netinet/ipsec_input.c @@ -1,4 +1,4 @@ -/* $OpenBSD: ipsec_input.c,v 1.155 2017/06/19 17:58:49 bluhm Exp $ */ +/* $OpenBSD: ipsec_input.c,v 1.156 2017/07/05 11:34:10 bluhm Exp $ */ /* * The authors of this code are John Ioannidis (ji@tla.org), * Angelos D. Keromytis (kermit@csd.uch.gr) and @@ -607,21 +607,7 @@ ipsec_common_input_cb(struct mbuf *m, struct tdb *tdbp, int skip, int protoff) } #endif /* Call the appropriate IPsec transform callback. */ - switch (af) { - case AF_INET: - ip_deliver(&m, &skip, prot, af); - return; -#ifdef INET6 - case AF_INET6: - ip6_deliver(&m, &skip, prot, af); - return; -#endif /* INET6 */ - default: - DPRINTF(("ipsec_common_input_cb(): unknown/unsupported " - "protocol family %d\n", af)); - m_freem(m); - return; - } + ip_deliver(&m, &skip, prot, af); #undef IPSEC_ISTAT } diff --git a/sys/netinet6/ip6_input.c b/sys/netinet6/ip6_input.c index ce153075e73..ed8702fa71a 100644 --- a/sys/netinet6/ip6_input.c +++ b/sys/netinet6/ip6_input.c @@ -1,4 +1,4 @@ -/* $OpenBSD: ip6_input.c,v 1.197 2017/06/27 13:28:02 bluhm Exp $ */ +/* $OpenBSD: ip6_input.c,v 1.198 2017/07/05 11:34:10 bluhm Exp $ */ /* $KAME: ip6_input.c,v 1.188 2001/03/29 05:34:31 itojun Exp $ */ /* @@ -169,6 +169,10 @@ ip6_init(void) int ip6_ours(struct mbuf **mp, int *offp, int nxt, int af) { + /* We are already in a IPv4/IPv6 local deliver loop. */ + if (af != AF_UNSPEC) + return ip6_local(mp, offp, nxt, af); + niq_enqueue(&ip6intrq, *mp); *mp = NULL; return IPPROTO_DONE; @@ -425,9 +429,12 @@ ip6_input_if(struct mbuf **mp, int *offp, int nxt, int af, struct ifnet *ifp) } if (ours) { - KERNEL_LOCK(); - nxt = ip6_deliver(mp, offp, nxt, AF_INET6); - KERNEL_UNLOCK(); + if (af == AF_UNSPEC) { + KERNEL_LOCK(); + nxt = ip_deliver(mp, offp, nxt, + AF_INET6); + KERNEL_UNLOCK(); + } goto out; } goto bad; @@ -502,9 +509,11 @@ ip6_input_if(struct mbuf **mp, int *offp, int nxt, int af, struct ifnet *ifp) goto out; if (ours) { - KERNEL_LOCK(); - nxt = ip6_deliver(mp, offp, nxt, AF_INET6); - KERNEL_UNLOCK(); + if (af == AF_UNSPEC) { + KERNEL_LOCK(); + nxt = ip_deliver(mp, offp, nxt, AF_INET6); + KERNEL_UNLOCK(); + } goto out; } @@ -542,66 +551,10 @@ ip6_local(struct mbuf **mp, int *offp, int nxt, int af) if (ip6_hbhchcheck(*mp, offp, &nxt, NULL)) return IPPROTO_DONE; - return ip6_deliver(mp, offp, nxt, AF_INET6); -} - -int -ip6_deliver(struct mbuf **mp, int *offp, int nxt, int af) -{ - int nest = 0; - - KERNEL_ASSERT_LOCKED(); - - /* pf might have changed things */ - in6_proto_cksum_out(*mp, NULL); - - /* - * Tell launch routine the next header - */ - ip6stat_inc(ip6s_delivered); - - while (nxt != IPPROTO_DONE) { - if (ip6_hdrnestlimit && (++nest > ip6_hdrnestlimit)) { - ip6stat_inc(ip6s_toomanyhdr); - goto bad; - } - - /* - * protection against faulty packet - there should be - * more sanity checks in header chain processing. - */ - if ((*mp)->m_pkthdr.len < *offp) { - ip6stat_inc(ip6s_tooshort); - goto bad; - } - - /* draft-itojun-ipv6-tcp-to-anycast */ - if (ISSET((*mp)->m_flags, M_ACAST) && (nxt == IPPROTO_TCP)) { - if ((*mp)->m_len >= sizeof(struct ip6_hdr)) { - icmp6_error(*mp, ICMP6_DST_UNREACH, - ICMP6_DST_UNREACH_ADDR, - offsetof(struct ip6_hdr, ip6_dst)); - *mp = NULL; - } - goto bad; - } - -#ifdef IPSEC - if (ipsec_in_use) { - if (ipsec_local_check(*mp, *offp, nxt, af) != 0) { - ip6stat_inc(ip6s_cantforward); - goto bad; - } - } - /* Otherwise, just fall through and deliver the packet */ -#endif /* IPSEC */ - - nxt = (*inet6sw[ip6_protox[nxt]].pr_input)(mp, offp, nxt, af); - } + /* Check wheter we are already in a IPv4/IPv6 local deliver loop. */ + if (af == AF_UNSPEC) + nxt = ip_deliver(mp, offp, nxt, AF_INET6); return nxt; - bad: - m_freemp(mp); - return IPPROTO_DONE; } int diff --git a/sys/netinet6/ip6_var.h b/sys/netinet6/ip6_var.h index d031bdab95a..9b58d9ee0af 100644 --- a/sys/netinet6/ip6_var.h +++ b/sys/netinet6/ip6_var.h @@ -1,4 +1,4 @@ -/* $OpenBSD: ip6_var.h,v 1.76 2017/06/27 13:28:02 bluhm Exp $ */ +/* $OpenBSD: ip6_var.h,v 1.77 2017/07/05 11:34:10 bluhm Exp $ */ /* $KAME: ip6_var.h,v 1.33 2000/06/11 14:59:20 jinmei Exp $ */ /* @@ -304,7 +304,6 @@ int icmp6_ctloutput(int, struct socket *, int, int, struct mbuf *); void ip6_init(void); void ip6intr(void); int ip6_input_if(struct mbuf **, int *, int, int, struct ifnet *); -int ip6_deliver(struct mbuf **, int *, int, int); void ip6_freepcbopts(struct ip6_pktopts *); void ip6_freemoptions(struct ip6_moptions *); int ip6_unknown_opt(u_int8_t *, struct mbuf *, int);