aintroduce ip6_{next,last}hdr which lets us parse IPv6 header chain correctly.
authoritojun <itojun@openbsd.org>
Wed, 22 Mar 2000 03:50:35 +0000 (03:50 +0000)
committeritojun <itojun@openbsd.org>
Wed, 22 Mar 2000 03:50:35 +0000 (03:50 +0000)
use it from icmp6 code.

sys/netinet6/icmp6.c
sys/netinet6/ip6_input.c
sys/netinet6/ip6_var.h

index c056e23..6a5be74 100644 (file)
@@ -1,5 +1,5 @@
-/*     $OpenBSD: icmp6.c,v 1.9 2000/02/28 14:30:40 itojun Exp $        */
-/*     $KAME: icmp6.c,v 1.71 2000/02/28 09:25:42 jinmei Exp $  */
+/*     $OpenBSD: icmp6.c,v 1.10 2000/03/22 03:50:35 itojun Exp $       */
+/*     $KAME: icmp6.c,v 1.75 2000/03/11 09:32:17 itojun Exp $  */
 
 /*
  * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
@@ -150,7 +150,7 @@ icmp6_error(m, type, code, param)
        struct icmp6_hdr *icmp6;
        u_int preplen;
        int off;
-       u_char nxt;
+       int nxt;
 
        icmp6stat.icp6s_error++;
 
@@ -183,86 +183,41 @@ icmp6_error(m, type, code, param)
                goto freeit;
 
        /*
-        * If the erroneous packet is also an ICMP error, discard it.
+        * If we are about to send ICMPv6 against ICMPv6 error/redirect,
+        * don't do it.
         */
-       off = sizeof(struct ip6_hdr);
-       nxt = oip6->ip6_nxt;
-       while (1) {             /* XXX: should avoid inf. loop explicitly? */
-               struct ip6_ext *ip6e;
+       nxt = -1;
+       off = ip6_lasthdr(m, sizeof(struct ip6_hdr), oip6->ip6_nxt, &nxt);
+       if (off >= 0 && nxt == IPPROTO_ICMPV6) {
                struct icmp6_hdr *icp;
 
-               switch(nxt) {
-               case IPPROTO_IPV6:
-               case IPPROTO_IPV4:
-               case IPPROTO_UDP:
-               case IPPROTO_TCP:
-               case IPPROTO_ESP:
-               case IPPROTO_IPCOMP:
-               case IPPROTO_FRAGMENT:
-                       /*
-                        * ICMPv6 error must not be fragmented.
-                        * XXX: but can we trust the sender?
-                        */
-               default:
-                       /* What if unknown header followed by ICMP error? */
-                       goto generate;
-               case IPPROTO_ICMPV6:
 #ifndef PULLDOWN_TEST
-                       IP6_EXTHDR_CHECK(m, 0, off + sizeof(struct icmp6_hdr), );
-                       icp = (struct icmp6_hdr *)(mtod(m, caddr_t) + off);
+               IP6_EXTHDR_CHECK(m, 0, off + sizeof(struct icmp6_hdr), );
+               icp = (struct icmp6_hdr *)(mtod(m, caddr_t) + off);
 #else
-                       IP6_EXTHDR_GET(icp, struct icmp6_hdr *, m, off,
-                               sizeof(*icp));
-                       if (icp == NULL) {
-                               icmp6stat.icp6s_tooshort++;
-                               return;
-                       }
-#endif
-                       if (icp->icmp6_type < ICMP6_ECHO_REQUEST
-                        || icp->icmp6_type == ND_REDIRECT) {
-                               /*
-                                * ICMPv6 error
-                                * Special case: for redirect (which is
-                                * informational) we must not send icmp6 error.
-                                */
-                               icmp6stat.icp6s_canterror++;
-                               goto freeit;
-                       } else {
-                               /* ICMPv6 informational */
-                               goto generate;
-                       }
-               case IPPROTO_HOPOPTS:
-               case IPPROTO_DSTOPTS:
-               case IPPROTO_ROUTING:
-               case IPPROTO_AH:
-#ifndef PULLDOWN_TEST
-                       IP6_EXTHDR_CHECK(m, 0, off + sizeof(struct ip6_ext), );
-                       ip6e = (struct ip6_ext *)(mtod(m, caddr_t) + off);
-#else
-                       IP6_EXTHDR_GET(ip6e, struct ip6_ext *, m, off,
-                               sizeof(*ip6e));
-                       if (ip6e == NULL) {
-                               /*XXX stat */
-                               return;
-                       }
+               IP6_EXTHDR_GET(icp, struct icmp6_hdr *, m, off,
+                       sizeof(*icp));
+               if (icp == NULL) {
+                       icmp6stat.icp6s_tooshort++;
+                       return;
+               }
 #endif
-                       if (nxt == IPPROTO_AH)
-                               off += (ip6e->ip6e_len + 2) << 2;
-                       else
-                               off += (ip6e->ip6e_len + 1) << 3;
-                       nxt = ip6e->ip6e_nxt;
-                       break;
+               if (icp->icmp6_type < ICMP6_ECHO_REQUEST ||
+                   icp->icmp6_type == ND_REDIRECT) {
+                       /*
+                        * ICMPv6 error
+                        * Special case: for redirect (which is
+                        * informational) we must not send icmp6 error.
+                        */
+                       icmp6stat.icp6s_canterror++;
+                       goto freeit;
+               } else {
+                       /* ICMPv6 informational - send the error */
                }
+       } else {
+               /* non-ICMPv6 - send the error */
        }
 
-  freeit:
-       /*
-        * If we can't tell wheter or not we can generate ICMP6, free it.
-        */
-       m_freem(m);
-       return;
-
-  generate:
        oip6 = mtod(m, struct ip6_hdr *); /* adjust pointer */
 
        /* Finally, do rate limitation check. */
@@ -303,6 +258,14 @@ icmp6_error(m, type, code, param)
 
        icmp6stat.icp6s_outhist[type]++;
        icmp6_reflect(m, sizeof(struct ip6_hdr)); /*header order: IPv6 - ICMPv6*/
+
+       return;
+
+  freeit:
+       /*
+        * If we can't tell wheter or not we can generate ICMP6, free it.
+        */
+       m_freem(m);
 }
 
 /*
index 6180f47..e39d551 100644 (file)
@@ -1,4 +1,5 @@
-/*     $OpenBSD: ip6_input.c,v 1.8 2000/02/07 06:09:10 itojun Exp $    */
+/*     $OpenBSD: ip6_input.c,v 1.9 2000/03/22 03:50:35 itojun Exp $    */
+/*     $KAME: ip6_input.c,v 1.72 2000/03/21 09:23:19 itojun Exp $      */
 
 /*
  * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
@@ -320,6 +321,35 @@ ip6_input(m)
                in6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_addrerr);
                goto bad;
        }
+#if 1
+       /*
+        * The following check is not documented in the spec.  Malicious party
+        * may be able to use IPv4 mapped addr to confuse tcp/udp stack and
+        * bypass security checks (act as if it was from 127.0.0.1 by using
+        * IPv6 src ::ffff:127.0.0.1).  Be cautious.
+        */
+       if (IN6_IS_ADDR_V4MAPPED(&ip6->ip6_src) ||
+           IN6_IS_ADDR_V4MAPPED(&ip6->ip6_dst)) {
+               ip6stat.ip6s_badscope++;
+               in6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_addrerr);
+               goto bad;
+       }
+#endif
+#if 0
+       /*
+        * Reject packets with IPv4 compatible addresses (auto tunnel).
+        *
+        * The code forbids auto tunnel relay case in RFC1933 (the check is
+        * stronger than RFC1933).  We may want to re-enable it if mech-xx
+        * is revised to forbid relaying case.
+        */
+       if (IN6_IS_ADDR_V4COMPAT(&ip6->ip6_src) ||
+           IN6_IS_ADDR_V4COMPAT(&ip6->ip6_dst)) {
+               ip6stat.ip6s_badscope++;
+               in6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_addrerr);
+               goto bad;
+       }
+#endif
        if (IN6_IS_ADDR_LOOPBACK(&ip6->ip6_src) ||
            IN6_IS_ADDR_LOOPBACK(&ip6->ip6_dst)) {
                if (m->m_pkthdr.rcvif->if_flags & IFF_LOOPBACK) {
@@ -1109,6 +1139,115 @@ ip6_get_prevhdr(m, off)
        }
 }
 
+/*
+ * get next header offset.  m will be retained.
+ */
+int
+ip6_nexthdr(m, off, proto, nxtp)
+       struct mbuf *m;
+       int off;
+       int proto;
+       int *nxtp;
+{
+       struct ip6_hdr ip6;
+       struct ip6_ext ip6e;
+       struct ip6_frag fh;
+
+       /* just in case */
+       if (m == NULL)
+               panic("ip6_nexthdr: m == NULL");
+       if ((m->m_flags & M_PKTHDR) == 0 || m->m_pkthdr.len < off)
+               return -1;
+
+       switch (proto) {
+       case IPPROTO_IPV6:
+               if (m->m_pkthdr.len < off + sizeof(ip6))
+                       return -1;
+               m_copydata(m, off, sizeof(ip6), (caddr_t)&ip6);
+               if (nxtp)
+                       *nxtp = ip6.ip6_nxt;
+               off += sizeof(ip6);
+               return off;
+
+       case IPPROTO_FRAGMENT:
+               /*
+                * terminate parsing if it is not the first fragment,
+                * it does not make sense to parse through it.
+                */
+               if (m->m_pkthdr.len < off + sizeof(fh))
+                       return -1;
+               m_copydata(m, off, sizeof(fh), (caddr_t)&fh);
+               if ((ntohs(fh.ip6f_offlg) & IP6F_OFF_MASK) != 0)
+                       return -1;
+               if (nxtp)
+                       *nxtp = fh.ip6f_nxt;
+               off += sizeof(struct ip6_frag);
+               return off;
+
+       case IPPROTO_AH:
+               if (m->m_pkthdr.len < off + sizeof(ip6e))
+                       return -1;
+               m_copydata(m, off, sizeof(ip6e), (caddr_t)&ip6e);
+               if (nxtp)
+                       *nxtp = ip6e.ip6e_nxt;
+               off += (ip6e.ip6e_len + 2) << 2;
+               return off;
+
+       case IPPROTO_HOPOPTS:
+       case IPPROTO_ROUTING:
+       case IPPROTO_DSTOPTS:
+               if (m->m_pkthdr.len < off + sizeof(ip6e))
+                       return -1;
+               m_copydata(m, off, sizeof(ip6e), (caddr_t)&ip6e);
+               if (nxtp)
+                       *nxtp = ip6e.ip6e_nxt;
+               off += (ip6e.ip6e_len + 1) << 3;
+               return off;
+
+       case IPPROTO_NONE:
+       case IPPROTO_ESP:
+       case IPPROTO_IPCOMP:
+               /* give up */
+               return -1;
+
+       default:
+               return -1;
+       }
+
+       return -1;
+}
+
+/*
+ * get offset for the last header in the chain.  m will be kept untainted.
+ */
+int
+ip6_lasthdr(m, off, proto, nxtp)
+       struct mbuf *m;
+       int off;
+       int proto;
+       int *nxtp;
+{
+       int newoff;
+       int nxt;
+
+       if (!nxtp) {
+               nxt = -1;
+               nxtp = &nxt;
+       }
+       while (1) {
+               newoff = ip6_nexthdr(m, off, proto, nxtp);
+               if (newoff < 0)
+                       return off;
+               else if (newoff < off)
+                       return -1;      /* invalid */
+               else if (newoff == off)
+                       return newoff;
+
+               off = newoff;
+               proto = *nxtp;
+       }
+}
+
 /*
  * System control for IP6
  */
index 1a77cd6..4e31b1f 100644 (file)
@@ -1,5 +1,5 @@
-/*     $OpenBSD: ip6_var.h,v 1.6 2000/02/28 16:40:39 itojun Exp $      */
-/*     $KAME: ip6_var.h,v 1.27 2000/02/22 14:04:22 itojun Exp $        */
+/*     $OpenBSD: ip6_var.h,v 1.7 2000/03/22 03:50:35 itojun Exp $      */
+/*     $KAME: ip6_var.h,v 1.28 2000/03/09 00:46:12 itojun Exp $        */
 
 /*
  * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
@@ -253,6 +253,8 @@ void        ip6_input __P((struct mbuf *));
 void   ip6_freemoptions __P((struct ip6_moptions *));
 int    ip6_unknown_opt __P((u_int8_t *, struct mbuf *, int));
 char * ip6_get_prevhdr __P((struct mbuf *, int));
+int    ip6_nexthdr __P((struct mbuf *, int, int, int *));
+int    ip6_lasthdr __P((struct mbuf *, int, int, int *));
 int    ip6_mforward __P((struct ip6_hdr *, struct ifnet *, struct mbuf *));
 int    ip6_process_hopopts __P((struct mbuf *, u_int8_t *, int, u_int32_t *,
                                 u_int32_t *));