Please note that diffs are not public domain; they are subject to the copyright notices on the relevant files. =================================================================== RCS file: /ftp/cvs/cvsroot/src/sys/netinet6/ip6_input.c,v rcsdiff: /ftp/cvs/cvsroot/src/sys/netinet6/ip6_input.c,v: warning: Unknown phrases like `commitid ...;' are present. retrieving revision 1.20 retrieving revision 1.22.2.5 diff -u -p -r1.20 -r1.22.2.5 --- src/sys/netinet6/ip6_input.c 2000/04/12 10:36:45 1.20 +++ src/sys/netinet6/ip6_input.c 2002/02/26 20:14:36 1.22.2.5 @@ -1,10 +1,10 @@ -/* $NetBSD: ip6_input.c,v 1.20 2000/04/12 10:36:45 itojun Exp $ */ -/* $KAME: ip6_input.c,v 1.72 2000/03/21 09:23:19 itojun Exp $ */ +/* $NetBSD: ip6_input.c,v 1.22.2.5 2002/02/26 20:14:36 he Exp $ */ +/* $KAME: ip6_input.c,v 1.119 2000/08/26 10:00:45 itojun Exp $ */ /* * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. * All rights reserved. - * + * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: @@ -16,7 +16,7 @@ * 3. Neither the name of the project nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. - * + * * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE @@ -111,6 +111,10 @@ #include #endif +#ifdef IPSEC +#include +#endif + #include /* we need it for NLOOP. */ @@ -146,13 +150,6 @@ static void ip6_init2 __P((void *)); static int ip6_hopopts_input __P((u_int32_t *, u_int32_t *, struct mbuf **, int *)); -#ifdef PTR -extern int ip6_protocol_tr; - -int ptr_in6 __P((struct mbuf *, struct mbuf **)); -extern void ip_forward __P((struct mbuf *, int)); -#endif - /* * IP6 initialization: fill in IP6 protocol switch table. * All protocols not implemented in kernel go to raw IP6 protocol handler. @@ -196,7 +193,7 @@ ip6_init2(dummy) { /* * to route local address of p2p link to loopback, - * assign loopback address first. + * assign loopback address first. */ in6_ifattach(&loif[0], NULL); @@ -268,8 +265,7 @@ ip6_input(m) if (m->m_next) { if (m->m_flags & M_LOOP) { ip6stat.ip6s_m2m[loif[0].if_index]++; /*XXX*/ - } - else if (m->m_pkthdr.rcvif->if_index <= 31) + } else if (m->m_pkthdr.rcvif->if_index <= 31) ip6stat.ip6s_m2m[m->m_pkthdr.rcvif->if_index]++; else ip6stat.ip6s_m2m[0]++; @@ -312,7 +308,15 @@ ip6_input(m) * in the list may have previously cleared it. */ m0 = m; - pfh = pfil_hook_get(PFIL_IN, &inetsw[ip_protox[IPPROTO_IPV6]].pr_pfh); +#ifdef IPSEC + if (ipsec_gethist(m, NULL)) + pfh = NULL; + else + pfh = pfil_hook_get(PFIL_IN, + &inet6sw[ip6_protox[IPPROTO_IPV6]].pr_pfh); +#else + pfh = pfil_hook_get(PFIL_IN, &inet6sw[ip6_protox[IPPROTO_IPV6]].pr_pfh); +#endif for (; pfh; pfh = pfh->pfil_link.tqe_next) if (pfh->pfil_func) { rv = pfh->pfil_func(ip6, sizeof(*ip6), @@ -395,13 +399,12 @@ ip6_input(m) } } - if (m->m_pkthdr.rcvif->if_flags & IFF_LOOPBACK) { - if (IN6_IS_ADDR_LINKLOCAL(&ip6->ip6_dst)) { - ours = 1; - deliverifp = m->m_pkthdr.rcvif; - goto hbhcheck; - } - } else { +#ifndef FAKE_LOOPBACK_IF + if ((m->m_pkthdr.rcvif->if_flags & IFF_LOOPBACK) == 0) +#else + if (1) +#endif + { if (IN6_IS_SCOPE_LINKLOCAL(&ip6->ip6_src)) ip6->ip6_src.s6_addr16[1] = htons(m->m_pkthdr.rcvif->if_index); @@ -410,32 +413,20 @@ ip6_input(m) = htons(m->m_pkthdr.rcvif->if_index); } -#ifdef PTR /* - * + * XXX we need this since we do not have "goto ours" hack route + * for some of our ifaddrs on loopback interface. + * we should correct it by changing in6_ifattach to install + * "goto ours" hack route. */ - if (ip6_protocol_tr) - { - struct mbuf *m1 = NULL; - - switch (ptr_in6(m, &m1)) - { - case IPPROTO_IP: goto mcastcheck; - case IPPROTO_IPV4: ip_forward(m1, 0); break; - case IPPROTO_IPV6: ip6_forward(m1, 0); break; - case IPPROTO_MAX: /* discard this packet */ - default: - } - - if (m != m1) - m_freem(m); - - return; + if ((m->m_pkthdr.rcvif->if_flags & IFF_LOOPBACK) != 0) { + if (IN6_IS_ADDR_LINKLOCAL(&ip6->ip6_dst)) { + ours = 1; + deliverifp = m->m_pkthdr.rcvif; + goto hbhcheck; + } } - mcastcheck: -#endif - /* * Multicast check */ @@ -463,13 +454,18 @@ ip6_input(m) /* * Unicast check */ - if (ip6_forward_rt.ro_rt == 0 || - !IN6_ARE_ADDR_EQUAL(&ip6->ip6_dst, - &ip6_forward_rt.ro_dst.sin6_addr)) { + if (ip6_forward_rt.ro_rt != NULL && + (ip6_forward_rt.ro_rt->rt_flags & RTF_UP) != 0 && + IN6_ARE_ADDR_EQUAL(&ip6->ip6_dst, + &ip6_forward_rt.ro_dst.sin6_addr)) + ; /* cache hit */ + else { if (ip6_forward_rt.ro_rt) { + /* route is down or destination is different */ RTFREE(ip6_forward_rt.ro_rt); ip6_forward_rt.ro_rt = 0; } + bzero(&ip6_forward_rt.ro_dst, sizeof(struct sockaddr_in6)); ip6_forward_rt.ro_dst.sin6_len = sizeof(struct sockaddr_in6); ip6_forward_rt.ro_dst.sin6_family = AF_INET6; @@ -504,16 +500,25 @@ ip6_input(m) ip6_forward_rt.ro_rt->rt_ifp->if_type == IFT_LOOP) { struct in6_ifaddr *ia6 = (struct in6_ifaddr *)ip6_forward_rt.ro_rt->rt_ifa; - /* packet to tentative address must not be received */ if (ia6->ia6_flags & IN6_IFF_ANYCAST) m->m_flags |= M_ANYCAST6; + /* + * packets to a tentative, duplicated, or somehow invalid + * address must not be accepted. + */ if (!(ia6->ia6_flags & IN6_IFF_NOTREADY)) { - /* this interface is ready */ + /* this address is ready */ ours = 1; deliverifp = ia6->ia_ifp; /* correct? */ goto hbhcheck; } else { - /* this interface is not ready, fall through */ + /* address is not ready, so discard the packet. */ + log(LOG_INFO, + "ip6_input: packet to an unready address %s->%s", + ip6_sprintf(&ip6->ip6_src), + ip6_sprintf(&ip6->ip6_dst)); + + goto bad; } } @@ -582,8 +587,29 @@ ip6_input(m) #endif return; /* m have already been freed */ } + /* adjust pointer */ ip6 = mtod(m, struct ip6_hdr *); + + /* + * if the payload length field is 0 and the next header field + * indicates Hop-by-Hop Options header, then a Jumbo Payload + * option MUST be included. + */ + if (ip6->ip6_plen == 0 && plen == 0) { + /* + * Note that if a valid jumbo payload option is + * contained, ip6_hoptops_input() must set a valid + * (non-zero) payload length to the variable plen. + */ + ip6stat.ip6s_badoptions++; + in6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_discard); + in6_ifstat_inc(m->m_pkthdr.rcvif, ifs6_in_hdrerr); + icmp6_error(m, ICMP6_PARAM_PROB, + ICMP6_PARAMPROB_HEADER, + (caddr_t)&ip6->ip6_plen - (caddr_t)ip6); + return; + } #ifndef PULLDOWN_TEST /* ip6_hopopts_input() ensures that mbuf is contiguous */ hbh = (struct ip6_hbh *)(ip6 + 1); @@ -646,8 +672,7 @@ ip6_input(m) m_freem(m); return; } - } - else if (!ours) { + } else if (!ours) { ip6_forward(m, 0); return; } @@ -658,7 +683,6 @@ ip6_input(m) #ifdef IFA_STATS if (IFA_STATS && deliverifp != NULL) { struct in6_ifaddr *ia6; - ip6 = mtod(m, struct ip6_hdr *); ia6 = in6_ifawithifp(deliverifp, &ip6->ip6_dst); if (ia6) ia6->ia_ifa.ifa_data.ifad_inbytes += m->m_pkthdr.len; @@ -683,6 +707,19 @@ ip6_input(m) goto bad; } +#ifdef IPSEC + /* + * enforce IPsec policy checking if we are seeing last header. + * note that we do not visit this with protocols with pcb layer + * code - like udp/tcp/raw ip. + */ + if ((inet6sw[ip6_protox[nxt]].pr_flags & PR_LASTHDR) != 0 && + ipsec6_in_reject(m, NULL)) { + ipsec6stat.in_polvio++; + goto bad; + } +#endif + nxt = (*inet6sw[ip6_protox[nxt]].pr_input)(&m, &off, nxt); } return; @@ -760,6 +797,7 @@ ip6_process_hopopts(m, opthead, hbhlen, int optlen = 0; u_int8_t *opt = opthead; u_int16_t rtalert_val; + u_int32_t jumboplen; for (; hbhlen > 0; hbhlen -= optlen, opt += optlen) { switch(*opt) { @@ -788,57 +826,75 @@ ip6_process_hopopts(m, opthead, hbhlen, *rtalertp = ntohs(rtalert_val); break; case IP6OPT_JUMBO: - /* XXX may need check for alignment */ - if (hbhlen < IP6OPT_JUMBO_LEN) { - ip6stat.ip6s_toosmall++; - goto bad; - } - if (*(opt + 1) != IP6OPT_JUMBO_LEN - 2) - /* XXX: should we discard the packet? */ - log(LOG_ERR, "length of jumbopayload opt " - "is inconsistent(%d)", - *(opt + 1)); - optlen = IP6OPT_JUMBO_LEN; + /* XXX may need check for alignment */ + if (hbhlen < IP6OPT_JUMBO_LEN) { + ip6stat.ip6s_toosmall++; + goto bad; + } + if (*(opt + 1) != IP6OPT_JUMBO_LEN - 2) + /* XXX: should we discard the packet? */ + log(LOG_ERR, "length of jumbopayload opt " + "is inconsistent(%d)", + *(opt + 1)); + optlen = IP6OPT_JUMBO_LEN; - /* - * We can simply cast because of the alignment - * requirement of the jumbo payload option. - */ -#if 0 - *plenp = ntohl(*(u_int32_t *)(opt + 2)); -#else - bcopy(opt + 2, plenp, sizeof(*plenp)); - *plenp = htonl(*plenp); + /* + * IPv6 packets that have non 0 payload length + * must not contain a jumbo paylod option. + */ + ip6 = mtod(m, struct ip6_hdr *); + if (ip6->ip6_plen) { + ip6stat.ip6s_badoptions++; + icmp6_error(m, ICMP6_PARAM_PROB, + ICMP6_PARAMPROB_HEADER, + sizeof(struct ip6_hdr) + + sizeof(struct ip6_hbh) + + opt - opthead); + return(-1); + } + + /* + * We may see jumbolen in unaligned location, so + * we'd need to perform bcopy(). + */ + bcopy(opt + 2, &jumboplen, sizeof(jumboplen)); + jumboplen = (u_int32_t)htonl(jumboplen); + +#if 1 + /* + * if there are multiple jumbo payload options, + * *plenp will be non-zero and the packet will be + * rejected. + * the behavior may need some debate in ipngwg - + * multiple options does not make sense, however, + * there's no explicit mention in specification. + */ + if (*plenp != 0) { + ip6stat.ip6s_badoptions++; + icmp6_error(m, ICMP6_PARAM_PROB, + ICMP6_PARAMPROB_HEADER, + sizeof(struct ip6_hdr) + + sizeof(struct ip6_hbh) + + opt + 2 - opthead); + return(-1); + } #endif - if (*plenp <= IPV6_MAXPACKET) { - /* - * jumbo payload length must be larger - * than 65535 - */ - ip6stat.ip6s_badoptions++; - icmp6_error(m, ICMP6_PARAM_PROB, - ICMP6_PARAMPROB_HEADER, - sizeof(struct ip6_hdr) + - sizeof(struct ip6_hbh) + - opt + 2 - opthead); - return(-1); - } - ip6 = mtod(m, struct ip6_hdr *); - if (ip6->ip6_plen) { - /* - * IPv6 packets that have non 0 payload length - * must not contain a jumbo paylod option. - */ - ip6stat.ip6s_badoptions++; - icmp6_error(m, ICMP6_PARAM_PROB, - ICMP6_PARAMPROB_HEADER, - sizeof(struct ip6_hdr) + - sizeof(struct ip6_hbh) + - opt - opthead); - return(-1); - } - break; + /* + * jumbo payload length must be larger than 65535. + */ + if (jumboplen <= IPV6_MAXPACKET) { + ip6stat.ip6s_badoptions++; + icmp6_error(m, ICMP6_PARAM_PROB, + ICMP6_PARAMPROB_HEADER, + sizeof(struct ip6_hdr) + + sizeof(struct ip6_hbh) + + opt + 2 - opthead); + return(-1); + } + *plenp = jumboplen; + + break; default: /* unknown option */ if (hbhlen < IP6OPT_MINLEN) { ip6stat.ip6s_toosmall++; @@ -1315,6 +1371,8 @@ ip6_sysctl(name, namelen, oldp, oldlenp, void *newp; size_t newlen; { + int old, error; + /* All sysctl names at this level are terminal. */ if (namelen != 1) return ENOTDIR; @@ -1366,6 +1424,58 @@ ip6_sysctl(name, namelen, oldp, oldlenp, return sysctl_int(oldp, oldlenp, newp, newlen, &ip6_bindv6only); #endif + case IPV6CTL_ANONPORTMIN: + old = ip6_anonportmin; + error = sysctl_int(oldp, oldlenp, newp, newlen, + &ip6_anonportmin); + if (ip6_anonportmin >= ip6_anonportmax || ip6_anonportmin < 0 || + ip6_anonportmin > 65535 +#ifndef IPNOPRIVPORTS + || ip6_anonportmin < IPV6PORT_RESERVED +#endif + ) { + ip6_anonportmin = old; + return (EINVAL); + } + return (error); + case IPV6CTL_ANONPORTMAX: + old = ip6_anonportmax; + error = sysctl_int(oldp, oldlenp, newp, newlen, + &ip6_anonportmax); + if (ip6_anonportmin >= ip6_anonportmax || ip6_anonportmax < 0 || + ip6_anonportmax > 65535 +#ifndef IPNOPRIVPORTS + || ip6_anonportmax < IPV6PORT_RESERVED +#endif + ) { + ip6_anonportmax = old; + return (EINVAL); + } + return (error); +#ifndef IPNOPRIVPORTS + case IPV6CTL_LOWPORTMIN: + old = ip6_lowportmin; + error = sysctl_int(oldp, oldlenp, newp, newlen, + &ip6_lowportmin); + if (ip6_lowportmin >= ip6_lowportmax || + ip6_lowportmin > IPV6PORT_RESERVEDMAX || + ip6_lowportmin < IPV6PORT_RESERVEDMIN) { + ip6_lowportmin = old; + return (EINVAL); + } + return (error); + case IPV6CTL_LOWPORTMAX: + old = ip6_lowportmax; + error = sysctl_int(oldp, oldlenp, newp, newlen, + &ip6_lowportmax); + if (ip6_lowportmin >= ip6_lowportmax || + ip6_lowportmax > IPV6PORT_RESERVEDMAX || + ip6_lowportmax < IPV6PORT_RESERVEDMIN) { + ip6_lowportmax = old; + return (EINVAL); + } + return (error); +#endif default: return EOPNOTSUPP; }