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);

Reply via email to