On 23/05/17(Tue) 13:36, 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.
>
> - 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.
I think it's a useful direction however your diff conflict with the
one to unlock the IPv4 forwarding path:
http://marc.info/?l=openbsd-tech&m=148778467826115&w=2
I'm using ip{6,}_ours() to enqueue mbufs. I worked hard to make sure
these functions only take a single mbuf, your diff is going in the
opposite direction.
I also think that you should introduce ip6_local() instead of mixing the
two protocols. The reason is simple, right now my diff enqueues packets
for local delivery in ip_ours(), but we might want to move that to ip_local()
if somebody would do the work to turn the reassembly code MP-safe. IPv6
is different in this regard since multiples protocol input routines can
be called per packet, so merging the two functions won't help.
I'm also don't think we should add more parameters to ip_input() because
it will be called directly.
> 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);
>