Hi, 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. My idea is to use this loop and avoid the queueing and rescheduling. The packet could be processed in a single flow.
- The ip_input() and ip6_input() functions behave like regular protocol input functions. - Use one ip_local() loop that can process IPv4 and IPv6 headers. When we have IP in IP the address family may change. - The ipip_input() function calls ip_input() and ip6_input(). - Add a check to prevent recursion, only a single loop may run. If we agree that this is a useful direction, I will split die diff in smaller parts for review. bluhm Index: netinet/ip_input.c =================================================================== RCS file: /data/mirror/openbsd/cvs/src/sys/netinet/ip_input.c,v retrieving revision 1.304 diff -u -p -r1.304 ip_input.c --- netinet/ip_input.c 22 May 2017 22:23:11 -0000 1.304 +++ netinet/ip_input.c 23 May 2017 11:28:32 -0000 @@ -61,6 +61,11 @@ #include <netinet/ip_var.h> #include <netinet/ip_icmp.h> +#ifdef INET6 +#include <netinet6/ip6protosw.h> +#include <netinet6/ip6_var.h> +#endif + #if NPF > 0 #include <net/pfvar.h> #endif @@ -126,7 +131,7 @@ int ip_sysctl_ipstat(void *, size_t *, v static struct mbuf_queue ipsend_mq; -void ip_ours(struct mbuf *); +void ip_ours(struct mbuf *, int *, int *, int); int ip_dooptions(struct mbuf *, struct ifnet *); int in_ouraddr(struct mbuf *, struct ifnet *, struct rtentry **); @@ -211,6 +216,7 @@ void ipintr(void) { struct mbuf *m; + int off; /* * Get next datagram off input queue and get IP header @@ -221,7 +227,8 @@ ipintr(void) if ((m->m_flags & M_PKTHDR) == 0) panic("ipintr no HDR"); #endif - ipv4_input(m); + off = 0; + ip_input(&m, &off, IPPROTO_IPV4, AF_UNSPEC); } } @@ -230,24 +237,27 @@ ipintr(void) * * Checksum and byte swap header. Process options. Forward or deliver. */ -void -ipv4_input(struct mbuf *m) +int +ip_input(struct mbuf **mp, int *offp, int nxt, int af) { + struct mbuf *m = *mp; struct ifnet *ifp; struct rtentry *rt = NULL; struct ip *ip; int hlen, len; in_addr_t pfrdr = 0; + KASSERT(*offp == 0); + ifp = if_get(m->m_pkthdr.ph_ifidx); if (ifp == NULL) goto bad; ipstat_inc(ips_total); if (m->m_len < sizeof (struct ip) && - (m = m_pullup(m, sizeof (struct ip))) == NULL) { + (m = *mp = m_pullup(m, sizeof (struct ip))) == NULL) { ipstat_inc(ips_toosmall); - goto out; + goto bad; } ip = mtod(m, struct ip *); if (ip->ip_v != IPVERSION) { @@ -260,9 +270,9 @@ ipv4_input(struct mbuf *m) goto bad; } if (hlen > m->m_len) { - if ((m = m_pullup(m, hlen)) == NULL) { + if ((m = *mp = m_pullup(m, hlen)) == NULL) { ipstat_inc(ips_badhlen); - goto out; + goto bad; } ip = mtod(m, struct ip *); } @@ -329,10 +339,11 @@ ipv4_input(struct mbuf *m) * Packet filter */ pfrdr = ip->ip_dst.s_addr; - if (pf_test(AF_INET, PF_IN, ifp, &m) != PF_PASS) + if (pf_test(AF_INET, PF_IN, ifp, mp) != PF_PASS) goto bad; + m = *mp; if (m == NULL) - goto out; + goto bad; ip = mtod(m, struct ip *); hlen = ip->ip_hl << 2; @@ -346,17 +357,18 @@ ipv4_input(struct mbuf *m) * to be sent and the original packet to be freed). */ if (hlen > sizeof (struct ip) && ip_dooptions(m, ifp)) { - goto out; + m = *mp = NULL; + goto bad; } if (ip->ip_dst.s_addr == INADDR_BROADCAST || ip->ip_dst.s_addr == INADDR_ANY) { - ip_ours(m); + ip_ours(m, offp, &nxt, af); goto out; } if (in_ouraddr(m, ifp, &rt)) { - ip_ours(m); + ip_ours(m, offp, &nxt, af); goto out; } @@ -373,9 +385,9 @@ ipv4_input(struct mbuf *m) int rv; if (m->m_flags & M_EXT) { - if ((m = m_pullup(m, hlen)) == NULL) { + if ((m = *mp = m_pullup(m, hlen)) == NULL) { ipstat_inc(ips_toosmall); - goto out; + goto bad; } ip = mtod(m, struct ip *); } @@ -405,7 +417,7 @@ ipv4_input(struct mbuf *m) * host belongs to their destination groups. */ if (ip->ip_p == IPPROTO_IGMP) { - ip_ours(m); + ip_ours(m, offp, &nxt, af); goto out; } ipstat_inc(ips_forward); @@ -421,7 +433,7 @@ ipv4_input(struct mbuf *m) ipstat_inc(ips_cantforward); goto bad; } - ip_ours(m); + ip_ours(m, offp, &nxt, af); goto out; } @@ -457,12 +469,14 @@ ipv4_input(struct mbuf *m) ip_forward(m, ifp, rt, pfrdr); if_put(ifp); - return; -bad: - m_freem(m); -out: + return IPPROTO_DONE; + bad: + m_freem(*mp); + nxt = IPPROTO_DONE; + out: rtfree(rt); if_put(ifp); + return nxt; } /* @@ -471,7 +485,7 @@ out: * If fragmented try to reassemble. Pass to next level. */ void -ip_ours(struct mbuf *m) +ip_ours(struct mbuf *m, int *offp, int *nxtp, int af) { struct ip *ip = mtod(m, struct ip *); struct ipq *fp; @@ -491,7 +505,7 @@ ip_ours(struct mbuf *m) if (m->m_flags & M_EXT) { /* XXX */ if ((m = m_pullup(m, hlen)) == NULL) { ipstat_inc(ips_toosmall); - return; + goto bad; } ip = mtod(m, struct ip *); } @@ -552,9 +566,8 @@ found: ipqe->ipqe_m = m; ipqe->ipqe_ip = ip; m = ip_reass(ipqe, fp); - if (m == NULL) { - return; - } + if (m == NULL) + goto bad; ipstat_inc(ips_reassembled); ip = mtod(m, struct ip *); hlen = ip->ip_hl << 2; @@ -564,37 +577,125 @@ found: ip_freef(fp); } - ip_local(m, hlen, ip->ip_p); + *offp = hlen; + *nxtp = ip->ip_p; + /* Check wheter we are already in a IPv4/IPv6 local processing loop. */ + if (af == AF_UNSPEC) + ip_local(m, offp, nxtp, AF_INET); return; -bad: + bad: m_freem(m); + *nxtp = 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 + void -ip_local(struct mbuf *m, int off, int nxt) +ip_local(struct mbuf *m, int *offp, int *nxtp, int af) { + int nxt = *nxtp; + struct protosw *psw; +#ifdef INET6 + int nest = 0; +#endif /* INET6 */ + KERNEL_ASSERT_LOCKED(); /* pf might have modified stuff, might have to chksum */ - in_proto_cksum_out(m, NULL); + switch (af) { + case AF_INET: + in_proto_cksum_out(m, NULL); + break; +#ifdef INET6 + case AF_INET6: + in6_proto_cksum_out(m, NULL); + break; +#endif /* INET6 */ + } + + /* + * 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; + } +#endif /* INET6 */ + + /* + * protection against faulty packet - there should be + * more sanity checks in header chain processing. + */ + if (m->m_pkthdr.len < *offp) { + IPSTAT_INC(tooshort); + goto bad; + } + +#ifdef INET6 + /* draft-itojun-ipv6-tcp-to-anycast */ + if (af == AF_INET6 && + ISSET(m->m_flags, M_ACAST) && (nxt == IPPROTO_TCP)) { + if (m->m_len >= sizeof(struct ip6_hdr)) { + icmp6_error(m, ICMP6_DST_UNREACH, + ICMP6_DST_UNREACH_ADDR, + offsetof(struct ip6_hdr, ip6_dst)); + m = NULL; + } + goto bad; + } +#endif /* INET6 */ #ifdef IPSEC - if (ipsec_in_use) { - if (ipsec_local_check(m, off, nxt, AF_INET) != 0) { - ipstat_inc(ips_cantforward); - m_freem(m); - return; + if (ipsec_in_use) { + if (ipsec_local_check(m, *offp, nxt, af) != 0) { + IPSTAT_INC(cantforward); + goto bad; + } } - } - /* Otherwise, just fall through and deliver the packet */ + /* Otherwise, just fall through and deliver the packet */ #endif /* IPSEC */ - /* - * Switch out to protocol's input routine. - */ - ipstat_inc(ips_delivered); - (*inetsw[ip_protox[nxt]].pr_input)(&m, &off, nxt, AF_INET); + switch (af) { + case AF_INET: + psw = &inetsw[ip_protox[nxt]]; + break; +#ifdef INET6 + case AF_INET6: + psw = &inet6sw[ip6_protox[nxt]]; + break; +#endif /* INET6 */ + } + *nxtp = (*psw->pr_input)(&m, offp, nxt, af); + switch (nxt) { + case IPPROTO_IPV4: + af = AF_INET; + ipstat_inc(ips_delivered); + break; +#ifdef INET6 + case IPPROTO_IPV6: + af = AF_INET6; + ip6stat_inc(ip6s_delivered); + break; +#endif /* INET6 */ + } + nxt = *nxtp; + } + return; + bad: + m_freem(m); + *nxtp = IPPROTO_DONE; } +#undef IPSTAT_INC int in_ouraddr(struct mbuf *m, struct ifnet *ifp, struct rtentry **prt) Index: netinet/ip_ipip.c =================================================================== RCS file: /data/mirror/openbsd/cvs/src/sys/netinet/ip_ipip.c,v retrieving revision 1.78 diff -u -p -r1.78 ip_ipip.c --- netinet/ip_ipip.c 18 May 2017 10:56:45 -0000 1.78 +++ netinet/ip_ipip.c 23 May 2017 11:00:09 -0000 @@ -120,37 +120,30 @@ ipip_input_gif(struct mbuf **mp, int *of struct ifnet *gifp) { struct mbuf *m = *mp; - int iphlen = *offp; struct sockaddr_in *sin; struct ifnet *ifp; - struct niqueue *ifq = NULL; - struct ip *ipo; + struct ip *ip; #ifdef INET6 struct sockaddr_in6 *sin6; struct ip6_hdr *ip6; #endif int mode, hlen; u_int8_t itos, otos; - u_int8_t v; sa_family_t iaf; ipipstat_inc(ipips_ipackets); - m_copydata(m, 0, 1, &v); - - switch (v >> 4) { - case 4: + switch (oaf) { + case AF_INET: hlen = sizeof(struct ip); break; #ifdef INET6 - case 6: + case AF_INET6: hlen = sizeof(struct ip6_hdr); break; #endif default: - ipipstat_inc(ipips_family); - m_freem(m); - return IPPROTO_DONE; + unhandled_af(oaf); } /* Bring the IP header in the first mbuf, if not there already */ @@ -158,36 +151,32 @@ ipip_input_gif(struct mbuf **mp, int *of if ((m = *mp = m_pullup(m, hlen)) == NULL) { DPRINTF(("%s: m_pullup() failed\n", __func__)); ipipstat_inc(ipips_hdrops); - return IPPROTO_DONE; + goto bad; } } - /* Keep outer ecn field. */ - switch (v >> 4) { - case 4: - ipo = mtod(m, struct ip *); - otos = ipo->ip_tos; + switch (oaf) { + case AF_INET: + ip = mtod(m, struct ip *); + otos = ip->ip_tos; break; #ifdef INET6 - case 6: + case AF_INET6: ip6 = mtod(m, struct ip6_hdr *); otos = (ntohl(ip6->ip6_flow) >> 20) & 0xff; break; #endif - default: - panic("%s: should never reach here", __func__); } /* Remove outer IP header */ - m_adj(m, iphlen); - - /* Sanity check */ - if (m->m_pkthdr.len < sizeof(struct ip)) { - ipipstat_inc(ipips_hdrops); - m_freem(m); - return IPPROTO_DONE; - } + KASSERT(*offp > 0); + m_adj(m, *offp); + *offp = 0; + ip = NULL; +#ifdef INET6 + ip6 = NULL; +#endif switch (proto) { case IPPROTO_IPV4: @@ -201,8 +190,13 @@ ipip_input_gif(struct mbuf **mp, int *of #endif default: ipipstat_inc(ipips_family); - m_freem(m); - return IPPROTO_DONE; + goto bad; + } + + /* Sanity check */ + if (m->m_pkthdr.len < hlen) { + ipipstat_inc(ipips_hdrops); + goto bad; } /* @@ -212,7 +206,7 @@ ipip_input_gif(struct mbuf **mp, int *of if ((m = *mp = m_pullup(m, hlen)) == NULL) { DPRINTF(("%s: m_pullup() failed\n", __func__)); ipipstat_inc(ipips_hdrops); - return IPPROTO_DONE; + goto bad; } } @@ -225,48 +219,40 @@ ipip_input_gif(struct mbuf **mp, int *of /* Some sanity checks in the inner IP header */ switch (proto) { case IPPROTO_IPV4: - ipo = mtod(m, struct ip *); -#ifdef INET6 - ip6 = NULL; -#endif - itos = ipo->ip_tos; + ip = mtod(m, struct ip *); + hlen = ip->ip_hl << 2; + if (m->m_pkthdr.len < hlen) { + ipipstat_inc(ipips_hdrops); + m_freem(m); + return IPPROTO_DONE; + } + itos = ip->ip_tos; mode = m->m_flags & (M_AUTH|M_CONF) ? ECN_ALLOWED_IPSEC : ECN_ALLOWED; - if (!ip_ecn_egress(mode, &otos, &ipo->ip_tos)) { + if (!ip_ecn_egress(mode, &otos, &ip->ip_tos)) { DPRINTF(("%s: ip_ecn_egress() failed\n", __func__)); ipipstat_inc(ipips_pdrops); - m_freem(m); - return IPPROTO_DONE; + goto bad; } /* re-calculate the checksum if ip_tos was changed */ - if (itos != ipo->ip_tos) { - hlen = ipo->ip_hl << 2; - if (m->m_pkthdr.len >= hlen) { - ipo->ip_sum = 0; - ipo->ip_sum = in_cksum(m, hlen); - } + if (itos != ip->ip_tos) { + ip->ip_sum = 0; + ip->ip_sum = in_cksum(m, hlen); } break; #ifdef INET6 case IPPROTO_IPV6: - ipo = NULL; ip6 = mtod(m, struct ip6_hdr *); itos = (ntohl(ip6->ip6_flow) >> 20) & 0xff; if (!ip_ecn_egress(ECN_ALLOWED, &otos, &itos)) { DPRINTF(("%s: ip_ecn_egress() failed\n", __func__)); ipipstat_inc(ipips_pdrops); - m_freem(m); - return IPPROTO_DONE; + goto bad; } ip6->ip6_flow &= ~htonl(0xff << 20); ip6->ip6_flow |= htonl((u_int32_t) itos << 20); break; #endif - default: - ipo = NULL; -#ifdef INET6 - ip6 = NULL; -#endif } /* Check for local address spoofing. */ @@ -280,11 +266,11 @@ ipip_input_gif(struct mbuf **mp, int *of memset(&ss, 0, sizeof(ss)); - if (ipo) { + if (ip) { sin = (struct sockaddr_in *)&ss; sin->sin_family = AF_INET; sin->sin_len = sizeof(*sin); - sin->sin_addr = ipo->ip_src; + sin->sin_addr = ip->ip_src; #ifdef INET6 } else if (ip6) { sin6 = (struct sockaddr_in6 *)&ss; @@ -296,9 +282,8 @@ ipip_input_gif(struct mbuf **mp, int *of rt = rtalloc(sstosa(&ss), 0, m->m_pkthdr.ph_rtableid); if ((rt != NULL) && (rt->rt_flags & RTF_LOCAL)) { ipipstat_inc(ipips_spoof); - m_freem(m); rtfree(rt); - return IPPROTO_DONE; + goto bad; } rtfree(rt); } else { @@ -306,7 +291,7 @@ ipip_input_gif(struct mbuf **mp, int *of } /* Statistics */ - ipipstat_add(ipips_ibytes, m->m_pkthdr.len - iphlen); + ipipstat_add(ipips_ibytes, m->m_pkthdr.len - hlen); /* * Interface pointer stays the same; if no IPsec processing has @@ -318,17 +303,13 @@ ipip_input_gif(struct mbuf **mp, int *of switch (proto) { case IPPROTO_IPV4: - ifq = &ipintrq; iaf = AF_INET; break; #ifdef INET6 case IPPROTO_IPV6: - ifq = &ip6intrq; iaf = AF_INET6; break; #endif - default: - panic("%s: should never reach here", __func__); } #if NBPFILTER > 0 @@ -339,11 +320,16 @@ ipip_input_gif(struct mbuf **mp, int *of pf_pkt_addr_changed(m); #endif - if (niq_enqueue(ifq, m) != 0) { - ipipstat_inc(ipips_qfull); - DPRINTF(("%s: packet dropped because of full queue\n", - __func__)); + switch (proto) { + case IPPROTO_IPV4: + return ip_input(mp, offp, proto, oaf); +#ifdef INET6 + case IPPROTO_IPV6: + return ip6_input(mp, offp, proto, oaf); +#endif } + bad: + m_freem(m); return IPPROTO_DONE; } Index: netinet/ip_var.h =================================================================== RCS file: /data/mirror/openbsd/cvs/src/sys/netinet/ip_var.h,v retrieving revision 1.75 diff -u -p -r1.75 ip_var.h --- netinet/ip_var.h 22 May 2017 22:23:11 -0000 1.75 +++ netinet/ip_var.h 23 May 2017 10:51:30 -0000 @@ -248,8 +248,8 @@ int ip_sysctl(int *, u_int, void *, siz void ip_savecontrol(struct inpcb *, struct mbuf **, struct ip *, struct mbuf *); void ipintr(void); -void ipv4_input(struct mbuf *); -void ip_local(struct mbuf *, int, int); +int ip_input(struct mbuf **, int *, int, int); +void ip_local(struct mbuf *, int *, int *, int); void ip_forward(struct mbuf *, struct ifnet *, struct rtentry *, int); int rip_ctloutput(int, struct socket *, int, int, struct mbuf *); void rip_init(void); Index: netinet/ipsec_input.c =================================================================== RCS file: /data/mirror/openbsd/cvs/src/sys/netinet/ipsec_input.c,v retrieving revision 1.153 diff -u -p -r1.153 ipsec_input.c --- netinet/ipsec_input.c 22 May 2017 22:23:11 -0000 1.153 +++ netinet/ipsec_input.c 23 May 2017 10:54:02 -0000 @@ -324,6 +324,7 @@ ipsec_common_input_cb(struct mbuf *m, st { int af, sproto; u_int8_t prot; + int nxt; #if NBPFILTER > 0 struct ifnet *encif; @@ -607,21 +608,8 @@ ipsec_common_input_cb(struct mbuf *m, st } #endif /* Call the appropriate IPsec transform callback. */ - switch (af) { - case AF_INET: - ip_local(m, skip, prot); - return; -#ifdef INET6 - case AF_INET6: - ip6_local(m, skip, prot); - return; -#endif /* INET6 */ - default: - DPRINTF(("ipsec_common_input_cb(): unknown/unsupported " - "protocol family %d\n", af)); - m_freem(m); - return; - } + nxt = prot; + ip_local(m, &skip, &nxt, af); #undef IPSEC_ISTAT } Index: netinet6/ip6_input.c =================================================================== RCS file: /data/mirror/openbsd/cvs/src/sys/netinet6/ip6_input.c,v retrieving revision 1.189 diff -u -p -r1.189 ip6_input.c --- netinet6/ip6_input.c 23 May 2017 08:13:10 -0000 1.189 +++ netinet6/ip6_input.c 23 May 2017 11:28:58 -0000 @@ -118,7 +118,7 @@ struct niqueue ip6intrq = NIQUEUE_INITIA struct cpumem *ip6counters; -void ip6_ours(struct mbuf *); +void ip6_ours(struct mbuf *, int *, int *, int); int ip6_check_rh0hdr(struct mbuf *, int *); int ip6_hbhchcheck(struct mbuf *, int *, int *, int *); int ip6_hopopts_input(u_int32_t *, u_int32_t *, struct mbuf **, int *); @@ -167,25 +167,31 @@ void ip6intr(void) { struct mbuf *m; + int off; - while ((m = niq_dequeue(&ip6intrq)) != NULL) - ip6_input(m); + while ((m = niq_dequeue(&ip6intrq)) != NULL) { + off = 0; + ip6_input(&m, &off, IPPROTO_IPV6, AF_UNSPEC); + } } -void -ip6_input(struct mbuf *m) +int +ip6_input(struct mbuf **mp, int *offp, int nxt, int af) { + struct mbuf *m = *mp; struct ifnet *ifp; struct ip6_hdr *ip6; struct sockaddr_in6 sin6; struct rtentry *rt = NULL; - int off, nxt, ours = 0; + int ours = 0; u_int16_t src_scope, dst_scope; #if NPF > 0 struct in6_addr odst; #endif int srcrt = 0; + KASSERT(*offp == 0); + ifp = if_get(m->m_pkthdr.ph_ifidx); if (ifp == NULL) goto bad; @@ -193,9 +199,9 @@ ip6_input(struct mbuf *m) ip6stat_inc(ip6s_total); if (m->m_len < sizeof(struct ip6_hdr)) { - if ((m = m_pullup(m, sizeof(struct ip6_hdr))) == NULL) { + if ((m = *mp = m_pullup(m, sizeof(struct ip6_hdr))) == NULL) { ip6stat_inc(ip6s_toosmall); - goto out; + goto bad; } } @@ -300,8 +306,9 @@ ip6_input(struct mbuf *m) * Packet filter */ odst = ip6->ip6_dst; - if (pf_test(AF_INET6, PF_IN, ifp, &m) != PF_PASS) + if (pf_test(AF_INET6, PF_IN, ifp, mp) != PF_PASS) goto bad; + m = *mp; if (m == NULL) goto bad; @@ -330,21 +337,22 @@ ip6_input(struct mbuf *m) * 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)) { + ip6_check_rh0hdr(m, offp)) { ip6stat_inc(ip6s_badoptions); - icmp6_error(m, ICMP6_PARAM_PROB, ICMP6_PARAMPROB_HEADER, off); - goto out; + icmp6_error(m, ICMP6_PARAM_PROB, ICMP6_PARAMPROB_HEADER, *offp); + m = *mp = NULL; + goto bad; } if (IN6_IS_ADDR_LOOPBACK(&ip6->ip6_src) || IN6_IS_ADDR_LOOPBACK(&ip6->ip6_dst)) { - ip6_ours(m); + ip6_ours(m, offp, &nxt, af); goto out; } #if NPF > 0 if (pf_ouraddr(m) == 1) { - ip6_ours(m); + ip6_ours(m, offp, &nxt, af); goto out; } #endif @@ -369,7 +377,7 @@ ip6_input(struct mbuf *m) #ifdef MROUTING if (ip6_mforwarding && ip6_mrouter[ifp->if_rdomain]) { - if (ip6_hbhchcheck(m, &off, &nxt, &ours)) + if (ip6_hbhchcheck(m, offp, &nxt, &ours)) goto out; ip6 = mtod(m, struct ip6_hdr *); @@ -386,10 +394,13 @@ ip6_input(struct mbuf *m) if (ip6_mforward(ip6, ifp, m)) { ip6stat_inc(ip6s_cantforward); m_freem(m); + nxt = IPPROTO_DONE; } else if (ours) { - ip6_local(m, off, nxt); + if (af == AF_UNSPEC) + ip_local(m, offp, &nxt, af); } else { m_freem(m); + nxt = IPPROTO_DONE; } KERNEL_UNLOCK(); goto out; @@ -401,7 +412,7 @@ ip6_input(struct mbuf *m) ip6stat_inc(ip6s_cantforward); goto bad; } - ip6_ours(m); + ip6_ours(m, offp, &nxt, af); goto out; } @@ -440,7 +451,7 @@ ip6_input(struct mbuf *m) goto bad; } else { - ip6_ours(m); + ip6_ours(m, offp, &nxt, af); goto out; } } @@ -460,12 +471,13 @@ ip6_input(struct mbuf *m) goto bad; } - if (ip6_hbhchcheck(m, &off, &nxt, &ours)) + if (ip6_hbhchcheck(m, offp, &nxt, &ours)) goto out; if (ours) { KERNEL_LOCK(); - ip6_local(m, off, nxt); + if (af == AF_UNSPEC) + ip_local(m, offp, &nxt, af); KERNEL_UNLOCK(); goto out; } @@ -475,7 +487,7 @@ ip6_input(struct mbuf *m) int rv; KERNEL_LOCK(); - rv = ipsec_forward_check(m, off, AF_INET6); + rv = ipsec_forward_check(m, *offp, AF_INET6); KERNEL_UNLOCK(); if (rv != 0) { ip6stat_inc(ip6s_cantforward); @@ -490,83 +502,25 @@ ip6_input(struct mbuf *m) ip6_forward(m, rt, srcrt); if_put(ifp); - return; + return IPPROTO_DONE; bad: - m_freem(m); + m_freem(*mp); + nxt = IPPROTO_DONE; out: rtfree(rt); if_put(ifp); + return nxt; } void -ip6_ours(struct mbuf *m) +ip6_ours(struct mbuf *m, int *offp, int *nxtp, int af) { - int off, nxt; - - if (ip6_hbhchcheck(m, &off, &nxt, NULL)) + if (ip6_hbhchcheck(m, offp, nxtp, NULL)) return; - ip6_local(m, off, nxt); -} - -void -ip6_local(struct mbuf *m, int off, int nxt) -{ - int nest = 0; - - KERNEL_ASSERT_LOCKED(); - - /* pf might have changed things */ - in6_proto_cksum_out(m, 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 (m->m_pkthdr.len < off) { - ip6stat_inc(ip6s_tooshort); - goto bad; - } - - /* draft-itojun-ipv6-tcp-to-anycast */ - if (ISSET(m->m_flags, M_ACAST) && (nxt == IPPROTO_TCP)) { - if (m->m_len >= sizeof(struct ip6_hdr)) { - icmp6_error(m, ICMP6_DST_UNREACH, - ICMP6_DST_UNREACH_ADDR, - offsetof(struct ip6_hdr, ip6_dst)); - break; - } else - goto bad; - } - -#ifdef IPSEC - if (ipsec_in_use) { - if (ipsec_local_check(m, off, nxt, AF_INET6) != 0) { - ip6stat_inc(ip6s_cantforward); - m_freem(m); - return; - } - } - /* Otherwise, just fall through and deliver the packet */ -#endif /* IPSEC */ - - nxt = (*inet6sw[ip6_protox[nxt]].pr_input)(&m, &off, nxt, - AF_INET6); - } - return; - bad: - m_freem(m); + /* Check wheter we are already in a IPv4/IPv6 local processing loop. */ + if (af == AF_UNSPEC) + ip_local(m, offp, nxtp, AF_INET6); } int @@ -588,7 +542,7 @@ ip6_hbhchcheck(struct mbuf *m, int *offp struct ip6_hbh *hbh; if (ip6_hopopts_input(&plen, &rtalert, &m, offp)) { - return (-1); /* m have already been freed */ + goto bad; /* m have already been freed */ } /* adjust pointer */ @@ -609,13 +563,13 @@ ip6_hbhchcheck(struct mbuf *m, int *offp icmp6_error(m, ICMP6_PARAM_PROB, ICMP6_PARAMPROB_HEADER, (caddr_t)&ip6->ip6_plen - (caddr_t)ip6); - return (-1); + goto bad; } IP6_EXTHDR_GET(hbh, struct ip6_hbh *, m, sizeof(struct ip6_hdr), sizeof(struct ip6_hbh)); if (hbh == NULL) { ip6stat_inc(ip6s_tooshort); - return (-1); + goto bad; } *nxtp = hbh->ip6h_nxt; @@ -637,7 +591,7 @@ ip6_hbhchcheck(struct mbuf *m, int *offp if (m->m_pkthdr.len - sizeof(struct ip6_hdr) < plen) { ip6stat_inc(ip6s_tooshort); m_freem(m); - return (-1); + goto bad; } if (m->m_pkthdr.len > sizeof(struct ip6_hdr) + plen) { if (m->m_len == m->m_pkthdr.len) { @@ -650,6 +604,10 @@ ip6_hbhchcheck(struct mbuf *m, int *offp } return (0); + + bad: + *nxtp = IPPROTO_DONE; + return (-1); } /* scan packet for RH0 routing header. Mostly stolen from pf.c:pf_test() */ Index: netinet6/ip6_var.h =================================================================== RCS file: /data/mirror/openbsd/cvs/src/sys/netinet6/ip6_var.h,v retrieving revision 1.73 diff -u -p -r1.73 ip6_var.h --- netinet6/ip6_var.h 8 May 2017 08:46:39 -0000 1.73 +++ netinet6/ip6_var.h 23 May 2017 10:54:02 -0000 @@ -303,8 +303,7 @@ int icmp6_ctloutput(int, struct socket * void ip6_init(void); void ip6intr(void); -void ip6_input(struct mbuf *); -void ip6_local(struct mbuf *, int, int); +int ip6_input(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);