/* * Fragment input */ int frag6_input(struct mbuf **mp, int *offp, int proto) { struct mbuf *m = *mp, *t; struct ip6_hdr *ip6; struct ip6_frag *ip6f; struct ip6q *q6; struct ip6asfrag *af6, *ip6af, *af6dwn; #ifdef IN6_IFSTAT_STRICT struct in6_ifaddr *ia; #endif int offset = *offp, nxt, i, next; int first_frag = 0; int fragoff, frgpartlen; /* must be larger than u_int16_t */ struct ifnet *dstifp; u_int8_t ecn, ecn0; #if 0 char ip6buf[INET6_ADDRSTRLEN]; #endif ip6 = mtod(m, struct ip6_hdr *); #ifndef PULLDOWN_TEST IP6_EXTHDR_CHECK(m, offset, sizeof(struct ip6_frag), IPPROTO_DONE); ip6f = (struct ip6_frag *)((caddr_t)ip6 + offset); #else IP6_EXTHDR_GET(ip6f, struct ip6_frag *, m, offset, sizeof(*ip6f)); if (ip6f == NULL) return (IPPROTO_DONE); #endif dstifp = NULL; #ifdef IN6_IFSTAT_STRICT /* find the destination interface of the packet. */ if ((ia = ip6_getdstifaddr(m)) != NULL) { dstifp = ia->ia_ifp; ifa_free(&ia->ia_ifa); } #else /* we are violating the spec, this is not the destination interface */ if ((m->m_flags & M_PKTHDR) != 0) dstifp = m->m_pkthdr.rcvif; #endif /* jumbo payload can't contain a fragment header */ if (ip6->ip6_plen == 0) { icmp6_error(m, ICMP6_PARAM_PROB, ICMP6_PARAMPROB_HEADER, offset); in6_ifstat_inc(dstifp, ifs6_reass_fail); return IPPROTO_DONE; } /* * check whether fragment packet's fragment length is * multiple of 8 octets. * sizeof(struct ip6_frag) == 8 * sizeof(struct ip6_hdr) = 40 */ if ((ip6f->ip6f_offlg & IP6F_MORE_FRAG) && (((ntohs(ip6->ip6_plen) - offset) & 0x7) != 0)) { icmp6_error(m, ICMP6_PARAM_PROB, ICMP6_PARAMPROB_HEADER, offsetof(struct ip6_hdr, ip6_plen)); in6_ifstat_inc(dstifp, ifs6_reass_fail); return IPPROTO_DONE; } IP6STAT_INC(ip6s_fragments); in6_ifstat_inc(dstifp, ifs6_reass_reqd); /* offset now points to data portion */ offset += sizeof(struct ip6_frag); /* * RFC 6946: Handle "atomic" fragments (offset and m bit set to 0) * upfront, unrelated to any reassembly. Just skip the fragment header. */ if ((ip6f->ip6f_offlg & ~IP6F_RESERVED_MASK) == 0) { /* XXX-BZ we want dedicated counters for this. */ IP6STAT_INC(ip6s_reassembled); in6_ifstat_inc(dstifp, ifs6_reass_ok); *offp = offset; return (ip6f->ip6f_nxt); } IP6Q_LOCK(); /* * Enforce upper bound on number of fragments. * If maxfrag is 0, never accept fragments. * If maxfrag is -1, accept all fragments without limitation. */ if (V_ip6_maxfrags < 0) ; else if (V_frag6_nfrags >= (u_int)V_ip6_maxfrags) goto dropfrag; for (q6 = V_ip6q.ip6q_next; q6 != &V_ip6q; q6 = q6->ip6q_next) if (ip6f->ip6f_ident == q6->ip6q_ident && IN6_ARE_ADDR_EQUAL(&ip6->ip6_src, &q6->ip6q_src) && IN6_ARE_ADDR_EQUAL(&ip6->ip6_dst, &q6->ip6q_dst) #ifdef MAC && mac_ip6q_match(m, q6) #endif ) break; if (q6 == &V_ip6q) { /* * the first fragment to arrive, create a reassembly queue. */ first_frag = 1; /* * Enforce upper bound on number of fragmented packets * for which we attempt reassembly; * If maxfragpackets is 0, never accept fragments. * If maxfragpackets is -1, accept all fragments without * limitation. */ if (V_ip6_maxfragpackets < 0) ; else if (V_frag6_nfragpackets >= (u_int)V_ip6_maxfragpackets) goto dropfrag; V_frag6_nfragpackets++; q6 = (struct ip6q *)malloc(sizeof(struct ip6q), M_FTABLE, M_NOWAIT); if (q6 == NULL) goto dropfrag; bzero(q6, sizeof(*q6)); #ifdef MAC if (mac_ip6q_init(q6, M_NOWAIT) != 0) { free(q6, M_FTABLE); goto dropfrag; } mac_ip6q_create(m, q6); #endif frag6_insque(q6, &V_ip6q); /* ip6q_nxt will be filled afterwards, from 1st fragment */ q6->ip6q_down = q6->ip6q_up = (struct ip6asfrag *)q6; #ifdef notyet q6->ip6q_nxtp = (u_char *)nxtp; #endif q6->ip6q_ident = ip6f->ip6f_ident; q6->ip6q_ttl = IPV6_FRAGTTL; q6->ip6q_src = ip6->ip6_src; q6->ip6q_dst = ip6->ip6_dst; q6->ip6q_ecn = (ntohl(ip6->ip6_flow) >> 20) & IPTOS_ECN_MASK; q6->ip6q_unfrglen = -1; /* The 1st fragment has not arrived. */ q6->ip6q_nfrag = 0; }
/* * Type0 routing header processing * * RFC2292 backward compatibility warning: no support for strict/loose bitmap, * as it was dropped between RFC1883 and RFC2460. */ static int ip6_rthdr0(struct mbuf *m, struct ip6_hdr *ip6, struct ip6_rthdr0 *rh0) { int addrs, index; struct in6_addr *nextaddr, tmpaddr; const struct ip6aux *ip6a; if (rh0->ip6r0_segleft == 0) return (0); if (rh0->ip6r0_len % 2 #ifdef COMPAT_RFC1883 || rh0->ip6r0_len > 46 #endif ) { /* * Type 0 routing header can't contain more than 23 addresses. * RFC 2462: this limitation was removed since strict/loose * bitmap field was deleted. */ IP6_STATINC(IP6_STAT_BADOPTIONS); icmp6_error(m, ICMP6_PARAM_PROB, ICMP6_PARAMPROB_HEADER, (char *)&rh0->ip6r0_len - (char *)ip6); return (-1); } if ((addrs = rh0->ip6r0_len / 2) < rh0->ip6r0_segleft) { IP6_STATINC(IP6_STAT_BADOPTIONS); icmp6_error(m, ICMP6_PARAM_PROB, ICMP6_PARAMPROB_HEADER, (char *)&rh0->ip6r0_segleft - (char *)ip6); return (-1); } index = addrs - rh0->ip6r0_segleft; rh0->ip6r0_segleft--; nextaddr = ((struct in6_addr *)(rh0 + 1)) + index; /* * reject invalid addresses. be proactive about malicious use of * IPv4 mapped/compat address. * XXX need more checks? */ if (IN6_IS_ADDR_MULTICAST(nextaddr) || IN6_IS_ADDR_UNSPECIFIED(nextaddr) || IN6_IS_ADDR_V4MAPPED(nextaddr) || IN6_IS_ADDR_V4COMPAT(nextaddr)) { p6stat[IP6_STAT_BADOPTIONS]++; goto bad; } if (IN6_IS_ADDR_MULTICAST(&ip6->ip6_dst) || IN6_IS_ADDR_UNSPECIFIED(&ip6->ip6_dst) || IN6_IS_ADDR_V4MAPPED(&ip6->ip6_dst) || IN6_IS_ADDR_V4COMPAT(&ip6->ip6_dst)) { IP6_STATINC(IP6_STAT_BADOPTIONS); goto bad; } /* * Determine the scope zone of the next hop, based on the interface * of the current hop. [RFC4007, Section 9] * Then disambiguate the scope zone for the next hop (if necessary). */ if ((ip6a = ip6_getdstifaddr(m)) == NULL) goto bad; if (in6_setzoneid(nextaddr, ip6a->ip6a_scope_id) != 0) { IP6_STATINC(IP6_STAT_BADSCOPE); goto bad; } /* * Swap the IPv6 destination address and nextaddr. Forward the packet. */ tmpaddr = *nextaddr; *nextaddr = ip6->ip6_dst; in6_clearscope(nextaddr); /* XXX */ ip6->ip6_dst = tmpaddr; #ifdef COMPAT_RFC1883 if (rh0->ip6r0_slmap[index / 8] & (1 << (7 - (index % 8)))) ip6_forward(m, IPV6_SRCRT_NEIGHBOR); else ip6_forward(m, IPV6_SRCRT_NOTNEIGHBOR); #else ip6_forward(m, 1); #endif return (-1); /* m would be freed in ip6_forward() */ bad: m_freem(m); return (-1); }