On Tue, May 23, 2017 at 01:36:00PM +0200, Alexander Bluhm wrote:
> 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.

After commiting parts of it and merging with mpi@'s unlocking of
the forwarding path, the current diff looks like this.

The main idea is to have a single ip_deliver() loop 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?

bluhm

Index: netinet/ip_input.c
===================================================================
RCS file: /data/mirror/openbsd/cvs/src/sys/netinet/ip_input.c,v
retrieving revision 1.313
diff -u -p -r1.313 ip_input.c
--- netinet/ip_input.c  26 Jun 2017 19:06:12 -0000      1.313
+++ netinet/ip_input.c  27 Jun 2017 13:29:20 -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
@@ -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, 
        m_freemp(mp);
        return 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.85
diff -u -p -r1.85 ip_ipip.c
--- netinet/ip_ipip.c   20 Jun 2017 11:12:13 -0000      1.85
+++ netinet/ip_ipip.c   27 Jun 2017 13:29:20 -0000
@@ -316,14 +316,10 @@ ipip_input_if(struct mbuf **mp, int *off
 
        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:
Index: netinet/ipsec_input.c
===================================================================
RCS file: /data/mirror/openbsd/cvs/src/sys/netinet/ipsec_input.c,v
retrieving revision 1.155
diff -u -p -r1.155 ipsec_input.c
--- netinet/ipsec_input.c       19 Jun 2017 17:58:49 -0000      1.155
+++ netinet/ipsec_input.c       27 Jun 2017 13:29:20 -0000
@@ -607,21 +607,7 @@ ipsec_common_input_cb(struct mbuf *m, st
        }
 #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
 }
 
Index: netinet6/ip6_input.c
===================================================================
RCS file: /data/mirror/openbsd/cvs/src/sys/netinet6/ip6_input.c,v
retrieving revision 1.197
diff -u -p -r1.197 ip6_input.c
--- netinet6/ip6_input.c        27 Jun 2017 13:28:02 -0000      1.197
+++ netinet6/ip6_input.c        27 Jun 2017 13:31:58 -0000
@@ -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
                        }
 
                        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
                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, i
        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
Index: netinet6/ip6_var.h
===================================================================
RCS file: /data/mirror/openbsd/cvs/src/sys/netinet6/ip6_var.h,v
retrieving revision 1.76
diff -u -p -r1.76 ip6_var.h
--- netinet6/ip6_var.h  27 Jun 2017 13:28:02 -0000      1.76
+++ netinet6/ip6_var.h  27 Jun 2017 13:31:58 -0000
@@ -304,7 +304,6 @@ int icmp6_ctloutput(int, struct socket *
 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);

Reply via email to