/* * Destination options header processing. */ int dest6_input(struct mbuf **mp, int *offp, int proto) { struct mbuf *m = *mp; int off = *offp, dstoptlen, optlen; struct ip6_dest *dstopts; u_int8_t *opt; /* validation of the length of the header */ IP6_EXTHDR_GET(dstopts, struct ip6_dest *, m, off, sizeof(*dstopts)); if (dstopts == NULL) return IPPROTO_DONE; dstoptlen = (dstopts->ip6d_len + 1) << 3; IP6_EXTHDR_GET(dstopts, struct ip6_dest *, m, off, dstoptlen); if (dstopts == NULL) return IPPROTO_DONE; off += dstoptlen; dstoptlen -= sizeof(struct ip6_dest); opt = (u_int8_t *)dstopts + sizeof(struct ip6_dest); /* search header for all options. */ for (optlen = 0; dstoptlen > 0; dstoptlen -= optlen, opt += optlen) { if (*opt != IP6OPT_PAD1 && (dstoptlen < IP6OPT_MINLEN || *(opt + 1) + 2 > dstoptlen)) { IP6_STATINC(IP6_STAT_TOOSMALL); goto bad; } switch (*opt) { case IP6OPT_PAD1: optlen = 1; break; case IP6OPT_PADN: optlen = *(opt + 1) + 2; break; default: /* unknown option */ optlen = ip6_unknown_opt(opt, m, opt - mtod(m, u_int8_t *)); if (optlen == -1) return (IPPROTO_DONE); optlen += 2; break; } } *offp = off; return (dstopts->ip6d_nxt); bad: m_freem(m); return (IPPROTO_DONE); }
int route6_input(struct mbuf **mp, int *offp, int proto) { struct ip6_hdr *ip6; struct mbuf *m = *mp; struct ip6_rthdr *rh; int off = *offp, rhlen; ip6 = mtod(m, struct ip6_hdr *); IP6_EXTHDR_GET(rh, struct ip6_rthdr *, m, off, sizeof(*rh)); if (rh == NULL) { IP6_STATINC(IP6_STAT_TOOSHORT); return IPPROTO_DONE; } switch (rh->ip6r_type) { #if 0 /* * See http://www.secdev.org/conf/IPv6_RH_security-csw07.pdf * for why IPV6_RTHDR_TYPE_0 is banned here. * * We return ICMPv6 parameter problem so that innocent people * (not an attacker) would notice about the use of IPV6_RTHDR_TYPE_0. * Since there's no amplification, and ICMPv6 error will be rate- * controlled, it shouldn't cause any problem. * If you are concerned about this, you may want to use the following * code fragment: * * case IPV6_RTHDR_TYPE_0: * m_freem(m); * return (IPPROTO_DONE); */ case IPV6_RTHDR_TYPE_0: rhlen = (rh->ip6r_len + 1) << 3; /* * note on option length: * maximum rhlen: 2048 * max mbuf m_pulldown can handle: MCLBYTES == usually 2048 * so, here we are assuming that m_pulldown can handle * rhlen == 2048 case. this may not be a good thing to * assume - we may want to avoid pulling it up altogether. */ IP6_EXTHDR_GET(rh, struct ip6_rthdr *, m, off, rhlen); if (rh == NULL) { IP6_STATINC(IP6_STAT_TOOSHORT); return IPPROTO_DONE; } if (ip6_rthdr0(m, ip6, (struct ip6_rthdr0 *)rh)) return (IPPROTO_DONE); break; #endif default: /* unknown routing type */ if (rh->ip6r_segleft == 0) { rhlen = (rh->ip6r_len + 1) << 3; break; /* Final dst. Just ignore the header. */ } IP6_STATINC(IP6_STAT_BADOPTIONS); icmp6_error(m, ICMP6_PARAM_PROB, ICMP6_PARAMPROB_HEADER, (char *)&rh->ip6r_type - (char *)ip6); return (IPPROTO_DONE); } *offp += rhlen; return (rh->ip6r_nxt); }
void ip6_forward(struct mbuf *m, int srcrt) { struct ip6_hdr *ip6 = mtod(m, struct ip6_hdr *); const struct sockaddr_in6 *dst; struct rtentry *rt; int error = 0, type = 0, code = 0; struct mbuf *mcopy = NULL; struct ifnet *origifp; /* maybe unnecessary */ uint32_t inzone, outzone; struct in6_addr src_in6, dst_in6; #ifdef IPSEC int needipsec = 0; struct secpolicy *sp = NULL; #endif /* * Clear any in-bound checksum flags for this packet. */ m->m_pkthdr.csum_flags = 0; /* * Do not forward packets to multicast destination (should be handled * by ip6_mforward(). * Do not forward packets with unspecified source. It was discussed * in July 2000, on ipngwg mailing list. */ if ((m->m_flags & (M_BCAST|M_MCAST)) != 0 || IN6_IS_ADDR_MULTICAST(&ip6->ip6_dst) || IN6_IS_ADDR_UNSPECIFIED(&ip6->ip6_src)) { ip6_cantforward(ip6, m->m_pkthdr.rcvif, NULL, ((m->m_flags & (M_BCAST|M_MCAST)) != 0) ? "bcast/mcast" : IN6_IS_ADDR_MULTICAST(&ip6->ip6_dst) ? "mcast/dst" : "unspec/src"); m_freem(m); return; } if (ip6->ip6_hlim <= IPV6_HLIMDEC) { /* XXX in6_ifstat_inc(rt->rt_ifp, ifs6_in_discard) */ icmp6_error(m, ICMP6_TIME_EXCEEDED, ICMP6_TIME_EXCEED_TRANSIT, 0); return; } ip6->ip6_hlim -= IPV6_HLIMDEC; /* * Save at most ICMPV6_PLD_MAXLEN (= the min IPv6 MTU - * size of IPv6 + ICMPv6 headers) bytes of the packet in case * we need to generate an ICMP6 message to the src. * Thanks to M_EXT, in most cases copy will not occur. * * It is important to save it before IPsec processing as IPsec * processing may modify the mbuf. */ mcopy = m_copy(m, 0, imin(m->m_pkthdr.len, ICMPV6_PLD_MAXLEN)); #ifdef IPSEC if (ipsec_used) { /* Check the security policy (SP) for the packet */ sp = ipsec6_check_policy(m, NULL, 0, &needipsec, &error); if (error != 0) { /* * Hack: -EINVAL is used to signal that a packet * should be silently discarded. This is typically * because we asked key management for an SA and * it was delayed (e.g. kicked up to IKE). */ if (error == -EINVAL) error = 0; goto freecopy; } } #endif /* IPSEC */ if (srcrt) { union { struct sockaddr dst; struct sockaddr_in6 dst6; } u; sockaddr_in6_init(&u.dst6, &ip6->ip6_dst, 0, 0, 0); if ((rt = rtcache_lookup(&ip6_forward_rt, &u.dst)) == NULL) { IP6_STATINC(IP6_STAT_NOROUTE); /* XXX in6_ifstat_inc(rt->rt_ifp, ifs6_in_noroute) */ if (mcopy) { icmp6_error(mcopy, ICMP6_DST_UNREACH, ICMP6_DST_UNREACH_NOROUTE, 0); } m_freem(m); return; } } else if ((rt = rtcache_validate(&ip6_forward_rt)) == NULL && (rt = rtcache_update(&ip6_forward_rt, 1)) == NULL) { /* * rtcache_getdst(ip6_forward_rt)->sin6_addr was equal to * ip6->ip6_dst */ IP6_STATINC(IP6_STAT_NOROUTE); /* XXX in6_ifstat_inc(rt->rt_ifp, ifs6_in_noroute) */ if (mcopy) { icmp6_error(mcopy, ICMP6_DST_UNREACH, ICMP6_DST_UNREACH_NOROUTE, 0); } m_freem(m); return; } dst = satocsin6(rtcache_getdst(&ip6_forward_rt)); /* * Source scope check: if a packet can't be delivered to its * destination for the reason that the destination is beyond the scope * of the source address, discard the packet and return an icmp6 * destination unreachable error with Code 2 (beyond scope of source * address). We use a local copy of ip6_src, since in6_setscope() * will possibly modify its first argument. * [draft-ietf-ipngwg-icmp-v3-07, Section 3.1] */ src_in6 = ip6->ip6_src; inzone = outzone = ~0; if (in6_setscope(&src_in6, rt->rt_ifp, &outzone) != 0 || in6_setscope(&src_in6, m->m_pkthdr.rcvif, &inzone) != 0 || inzone != outzone) { ip6_cantforward(ip6, m->m_pkthdr.rcvif, rt->rt_ifp, "src[%s] inzone %d outzone %d", in6_getscopename(&ip6->ip6_src), inzone, outzone); if (mcopy) icmp6_error(mcopy, ICMP6_DST_UNREACH, ICMP6_DST_UNREACH_BEYONDSCOPE, 0); m_freem(m); return; } #ifdef IPSEC /* * If we need to encapsulate the packet, do it here * ipsec6_proces_packet will send the packet using ip6_output */ if (needipsec) { int s = splsoftnet(); error = ipsec6_process_packet(m, sp->req); splx(s); if (mcopy) goto freecopy; } #endif /* * Destination scope check: if a packet is going to break the scope * zone of packet's destination address, discard it. This case should * usually be prevented by appropriately-configured routing table, but * we need an explicit check because we may mistakenly forward the * packet to a different zone by (e.g.) a default route. */ dst_in6 = ip6->ip6_dst; inzone = outzone = ~0; if (in6_setscope(&dst_in6, m->m_pkthdr.rcvif, &inzone) != 0 || in6_setscope(&dst_in6, rt->rt_ifp, &outzone) != 0 || inzone != outzone) { ip6_cantforward(ip6, m->m_pkthdr.rcvif, rt->rt_ifp, "dst[%s] inzone %d outzone %d", in6_getscopename(&ip6->ip6_dst), inzone, outzone); if (mcopy) icmp6_error(mcopy, ICMP6_DST_UNREACH, ICMP6_DST_UNREACH_BEYONDSCOPE, 0); m_freem(m); return; } if (m->m_pkthdr.len > IN6_LINKMTU(rt->rt_ifp)) { in6_ifstat_inc(rt->rt_ifp, ifs6_in_toobig); if (mcopy) { u_long mtu; mtu = IN6_LINKMTU(rt->rt_ifp); icmp6_error(mcopy, ICMP6_PACKET_TOO_BIG, 0, mtu); } m_freem(m); return; } if (rt->rt_flags & RTF_GATEWAY) dst = (struct sockaddr_in6 *)rt->rt_gateway; /* * If we are to forward the packet using the same interface * as one we got the packet from, perhaps we should send a redirect * to sender to shortcut a hop. * Only send redirect if source is sending directly to us, * and if packet was not source routed (or has any options). * Also, don't send redirect if forwarding using a route * modified by a redirect. */ if (rt->rt_ifp == m->m_pkthdr.rcvif && !srcrt && ip6_sendredirects && (rt->rt_flags & (RTF_DYNAMIC|RTF_MODIFIED)) == 0) { if ((rt->rt_ifp->if_flags & IFF_POINTOPOINT) && nd6_is_addr_neighbor( satocsin6(rtcache_getdst(&ip6_forward_rt)), rt->rt_ifp)) { /* * If the incoming interface is equal to the outgoing * one, the link attached to the interface is * point-to-point, and the IPv6 destination is * regarded as on-link on the link, then it will be * highly probable that the destination address does * not exist on the link and that the packet is going * to loop. Thus, we immediately drop the packet and * send an ICMPv6 error message. * For other routing loops, we dare to let the packet * go to the loop, so that a remote diagnosing host * can detect the loop by traceroute. * type/code is based on suggestion by Rich Draves. * not sure if it is the best pick. */ icmp6_error(mcopy, ICMP6_DST_UNREACH, ICMP6_DST_UNREACH_ADDR, 0); m_freem(m); return; } type = ND_REDIRECT; } /* * Fake scoped addresses. Note that even link-local source or * destinaion can appear, if the originating node just sends the * packet to us (without address resolution for the destination). * Since both icmp6_error and icmp6_redirect_output fill the embedded * link identifiers, we can do this stuff after making a copy for * returning an error. */ if ((rt->rt_ifp->if_flags & IFF_LOOPBACK) != 0) { /* * See corresponding comments in ip6_output. * XXX: but is it possible that ip6_forward() sends a packet * to a loopback interface? I don't think so, and thus * I bark here. ([email protected]) * XXX: it is common to route invalid packets to loopback. * also, the codepath will be visited on use of ::1 in * rthdr. (itojun) */ #if 1 if (0) #else if ((rt->rt_flags & (RTF_BLACKHOLE|RTF_REJECT)) == 0) #endif { printf("ip6_forward: outgoing interface is loopback. " "src %s, dst %s, nxt %d, rcvif %s, outif %s\n", ip6_sprintf(&ip6->ip6_src), ip6_sprintf(&ip6->ip6_dst), ip6->ip6_nxt, if_name(m->m_pkthdr.rcvif), if_name(rt->rt_ifp)); } /* we can just use rcvif in forwarding. */ origifp = m->m_pkthdr.rcvif; } else origifp = rt->rt_ifp; /* * clear embedded scope identifiers if necessary. * in6_clearscope will touch the addresses only when necessary. */ in6_clearscope(&ip6->ip6_src); in6_clearscope(&ip6->ip6_dst); /* * Run through list of hooks for output packets. */ if ((error = pfil_run_hooks(inet6_pfil_hook, &m, rt->rt_ifp, PFIL_OUT)) != 0) goto senderr; if (m == NULL) goto freecopy; ip6 = mtod(m, struct ip6_hdr *); error = nd6_output(rt->rt_ifp, origifp, m, dst, rt); if (error) { in6_ifstat_inc(rt->rt_ifp, ifs6_out_discard); IP6_STATINC(IP6_STAT_CANTFORWARD); } else { IP6_STATINC(IP6_STAT_FORWARD); in6_ifstat_inc(rt->rt_ifp, ifs6_out_forward); if (type) IP6_STATINC(IP6_STAT_REDIRECTSENT); else { #ifdef GATEWAY if (m->m_flags & M_CANFASTFWD) ip6flow_create(&ip6_forward_rt, m); #endif if (mcopy) goto freecopy; } } senderr: if (mcopy == NULL) return; switch (error) { case 0: if (type == ND_REDIRECT) { icmp6_redirect_output(mcopy, rt); return; } goto freecopy; case EMSGSIZE: /* xxx MTU is constant in PPP? */ goto freecopy; case ENOBUFS: /* Tell source to slow down like source quench in IP? */ goto freecopy; case ENETUNREACH: /* shouldn't happen, checked above */ case EHOSTUNREACH: case ENETDOWN: case EHOSTDOWN: default: type = ICMP6_DST_UNREACH; code = ICMP6_DST_UNREACH_ADDR; break; } icmp6_error(mcopy, type, code, 0); return; freecopy: m_freem(mcopy); return; }
/* * 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); }