diff options
author | itojun <itojun@FreeBSD.org> | 2000-07-04 16:35:15 +0000 |
---|---|---|
committer | itojun <itojun@FreeBSD.org> | 2000-07-04 16:35:15 +0000 |
commit | 5f4e854de19331a53788d6100bbcd42845056bc1 (patch) | |
tree | 3ff8c876a5868b103fb8713055d83e29a3fa38d5 /sys/netinet6/ip6_forward.c | |
parent | bdc16885232d771a99d7dfc247cd27a44cd061f9 (diff) | |
download | FreeBSD-src-5f4e854de19331a53788d6100bbcd42845056bc1.zip FreeBSD-src-5f4e854de19331a53788d6100bbcd42845056bc1.tar.gz |
sync with kame tree as of july00. tons of bug fixes/improvements.
API changes:
- additional IPv6 ioctls
- IPsec PF_KEY API was changed, it is mandatory to upgrade setkey(8).
(also syntax change)
Diffstat (limited to 'sys/netinet6/ip6_forward.c')
-rw-r--r-- | sys/netinet6/ip6_forward.c | 237 |
1 files changed, 200 insertions, 37 deletions
diff --git a/sys/netinet6/ip6_forward.c b/sys/netinet6/ip6_forward.c index 249d38c..ba9bdad 100644 --- a/sys/netinet6/ip6_forward.c +++ b/sys/netinet6/ip6_forward.c @@ -1,3 +1,6 @@ +/* $FreeBSD$ */ +/* $KAME: ip6_forward.c,v 1.39 2000/07/03 13:23:28 itojun Exp $ */ + /* * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. * All rights reserved. @@ -25,10 +28,11 @@ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. - * - * $FreeBSD$ */ +#include "opt_ip6fw.h" +#include "opt_inet.h" +#include "opt_inet6.h" #include "opt_ipsec.h" #include <sys/param.h> @@ -46,20 +50,15 @@ #include <netinet/in.h> #include <netinet/in_var.h> -#include <netinet6/ip6.h> +#include <netinet/ip_var.h> +#include <netinet/ip6.h> #include <netinet6/ip6_var.h> -#include <netinet6/icmp6.h> +#include <netinet/icmp6.h> #include <netinet6/nd6.h> #ifdef IPSEC_IPV6FWD #include <netinet6/ipsec.h> -#include <netinet6/ipsec6.h> #include <netkey/key.h> -#ifdef IPSEC_DEBUG -#include <netkey/key_debug.h> -#else -#define KEYDEBUG(lev,arg) -#endif #endif /* IPSEC_IPV6FWD */ #ifdef IPV6FIREWALL @@ -92,7 +91,8 @@ ip6_forward(m, srcrt) register struct sockaddr_in6 *dst; register struct rtentry *rt; int error, type = 0, code = 0; - struct mbuf *mcopy; + struct mbuf *mcopy = NULL; + struct ifnet *origifp; /* maybe unnecessary */ #ifdef IPSEC_IPV6FWD struct secpolicy *sp = NULL; #endif @@ -112,19 +112,17 @@ ip6_forward(m, srcrt) } #endif /*IPSEC_IPV6FWD*/ - if (m->m_flags & (M_BCAST|M_MCAST) || - in6_canforward(&ip6->ip6_src, &ip6->ip6_dst) == 0) { + if ((m->m_flags & (M_BCAST|M_MCAST)) != 0 || + IN6_IS_ADDR_MULTICAST(&ip6->ip6_dst)) { ip6stat.ip6s_cantforward++; - ip6stat.ip6s_badscope++; /* XXX in6_ifstat_inc(rt->rt_ifp, ifs6_in_discard) */ if (ip6_log_time + ip6_log_interval < time_second) { - char addr[INET6_ADDRSTRLEN]; ip6_log_time = time_second; - strncpy(addr, ip6_sprintf(&ip6->ip6_src), sizeof(addr)); log(LOG_DEBUG, "cannot forward " "from %s to %s nxt %d received on %s\n", - addr, ip6_sprintf(&ip6->ip6_dst), + ip6_sprintf(&ip6->ip6_src), + ip6_sprintf(&ip6->ip6_dst), ip6->ip6_nxt, if_name(m->m_pkthdr.rcvif)); } @@ -140,13 +138,30 @@ ip6_forward(m, srcrt) } 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_IPV6FWD /* get a security policy for this packet */ sp = ipsec6_getpolicybyaddr(m, IPSEC_DIR_OUTBOUND, 0, &error); if (sp == NULL) { ipsec6stat.out_inval++; ip6stat.ip6s_cantforward++; - /* XXX: any icmp ? */ + if (mcopy) { +#if 0 + /* XXX: what icmp ? */ +#else + m_freem(mcopy); +#endif + } m_freem(m); return; } @@ -162,7 +177,13 @@ ip6_forward(m, srcrt) ipsec6stat.out_polvio++; ip6stat.ip6s_cantforward++; key_freesp(sp); - /* XXX: any icmp ? */ + if (mcopy) { +#if 0 + /* XXX: what icmp ? */ +#else + m_freem(mcopy); +#endif + } m_freem(m); return; @@ -171,14 +192,20 @@ ip6_forward(m, srcrt) /* no need to do IPsec. */ key_freesp(sp); goto skip_ipsec; - + case IPSEC_POLICY_IPSEC: if (sp->req == NULL) { /* XXX should be panic ? */ printf("ip6_forward: No IPsec request specified.\n"); ip6stat.ip6s_cantforward++; key_freesp(sp); - /* XXX: any icmp ? */ + if (mcopy) { +#if 0 + /* XXX: what icmp ? */ +#else + m_freem(mcopy); +#endif + } m_freem(m); return; } @@ -212,7 +239,10 @@ ip6_forward(m, srcrt) error = ipsec6_output_tunnel(&state, sp, 0); m = state.m; - /* XXX allocate a route (ro, dst) again later */ +#if 0 /* XXX allocate a route (ro, dst) again later */ + ro = (struct route_in6 *)state.ro; + dst = (struct sockaddr_in6 *)state.dst; +#endif key_freesp(sp); if (error) { @@ -232,7 +262,13 @@ ip6_forward(m, srcrt) break; } ip6stat.ip6s_cantforward++; - /* XXX: any icmp ? */ + if (mcopy) { +#if 0 + /* XXX: what icmp ? */ +#else + m_freem(mcopy); +#endif + } m_freem(m); return; } @@ -255,12 +291,15 @@ ip6_forward(m, srcrt) rtalloc_ign((struct route *)&ip6_forward_rt, RTF_PRCLONING); } - + if (ip6_forward_rt.ro_rt == 0) { ip6stat.ip6s_noroute++; /* XXX in6_ifstat_inc(rt->rt_ifp, ifs6_in_noroute) */ - icmp6_error(m, ICMP6_DST_UNREACH, - ICMP6_DST_UNREACH_NOROUTE, 0); + if (mcopy) { + icmp6_error(mcopy, ICMP6_DST_UNREACH, + ICMP6_DST_UNREACH_NOROUTE, 0); + } + m_freem(m); return; } } else if ((rt = ip6_forward_rt.ro_rt) == 0 || @@ -278,26 +317,89 @@ ip6_forward(m, srcrt) if (ip6_forward_rt.ro_rt == 0) { ip6stat.ip6s_noroute++; /* XXX in6_ifstat_inc(rt->rt_ifp, ifs6_in_noroute) */ - icmp6_error(m, ICMP6_DST_UNREACH, - ICMP6_DST_UNREACH_NOROUTE, 0); + if (mcopy) { + icmp6_error(mcopy, ICMP6_DST_UNREACH, + ICMP6_DST_UNREACH_NOROUTE, 0); + } + m_freem(m); return; } } rt = ip6_forward_rt.ro_rt; - if (m->m_pkthdr.len > rt->rt_ifp->if_mtu){ + + /* + * 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). + * [draft-ietf-ipngwg-icmp-v3-00.txt, Section 3.1] + */ + if (in6_addr2scopeid(m->m_pkthdr.rcvif, &ip6->ip6_src) != + in6_addr2scopeid(rt->rt_ifp, &ip6->ip6_src)) { + ip6stat.ip6s_cantforward++; + ip6stat.ip6s_badscope++; + in6_ifstat_inc(rt->rt_ifp, ifs6_in_discard); + + if (ip6_log_time + ip6_log_interval < time_second) { + ip6_log_time = time_second; + log(LOG_DEBUG, + "cannot forward " + "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)); + } + if (mcopy) + icmp6_error(mcopy, ICMP6_DST_UNREACH, + ICMP6_DST_UNREACH_BEYONDSCOPE, 0); + m_freem(m); + return; + } + + if (m->m_pkthdr.len > rt->rt_ifp->if_mtu) { in6_ifstat_inc(rt->rt_ifp, ifs6_in_toobig); - icmp6_error(m, ICMP6_PACKET_TOO_BIG, 0, rt->rt_ifp->if_mtu); + if (mcopy) { + u_long mtu; +#ifdef IPSEC_IPV6FWD + struct secpolicy *sp; + int ipsecerror; + size_t ipsechdrsiz; +#endif + + mtu = rt->rt_ifp->if_mtu; +#ifdef IPSEC_IPV6FWD + /* + * When we do IPsec tunnel ingress, we need to play + * with if_mtu value (decrement IPsec header size + * from mtu value). The code is much simpler than v4 + * case, as we have the outgoing interface for + * encapsulated packet as "rt->rt_ifp". + */ + sp = ipsec6_getpolicybyaddr(mcopy, IPSEC_DIR_OUTBOUND, + IP_FORWARDING, &ipsecerror); + if (sp) { + ipsechdrsiz = ipsec6_hdrsiz(mcopy, + IPSEC_DIR_OUTBOUND, NULL); + if (ipsechdrsiz < mtu) + mtu -= ipsechdrsiz; + } + + /* + * if mtu becomes less than minimum MTU, + * tell minimum MTU (and I'll need to fragment it). + */ + if (mtu < IPV6_MMTU) + mtu = IPV6_MMTU; +#endif + 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; - /* - * Save at most 528 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. - */ - mcopy = m_copy(m, 0, imin(m->m_pkthdr.len, ICMPV6_PLD_MAXLEN)); /* * If we are to forward the packet using the same interface @@ -328,7 +430,66 @@ ip6_forward(m, srcrt) } #endif - error = nd6_output(rt->rt_ifp, m, dst, rt); + /* + * 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 make a copy for + * returning 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. (jinmei@kame.net) + * 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)); + } + + if (IN6_IS_SCOPE_LINKLOCAL(&ip6->ip6_src)) + origifp = ifindex2ifnet[ntohs(ip6->ip6_src.s6_addr16[1])]; + else if (IN6_IS_SCOPE_LINKLOCAL(&ip6->ip6_dst)) + origifp = ifindex2ifnet[ntohs(ip6->ip6_dst.s6_addr16[1])]; + else + origifp = rt->rt_ifp; + } + else + origifp = rt->rt_ifp; +#ifndef FAKE_LOOPBACK_IF + if ((rt->rt_ifp->if_flags & IFF_LOOPBACK) == 0) +#else + if (1) +#endif + { + if (IN6_IS_SCOPE_LINKLOCAL(&ip6->ip6_src)) + ip6->ip6_src.s6_addr16[1] = 0; + if (IN6_IS_SCOPE_LINKLOCAL(&ip6->ip6_dst)) + ip6->ip6_dst.s6_addr16[1] = 0; + } + +#ifdef OLDIP6OUTPUT + error = (*rt->rt_ifp->if_output)(rt->rt_ifp, m, + (struct sockaddr *)dst, + ip6_forward_rt.ro_rt); +#else + error = nd6_output(rt->rt_ifp, origifp, m, dst, rt); +#endif if (error) { in6_ifstat_inc(rt->rt_ifp, ifs6_out_discard); ip6stat.ip6s_cantforward++; @@ -347,10 +508,12 @@ ip6_forward(m, srcrt) switch (error) { case 0: +#if 1 if (type == ND_REDIRECT) { icmp6_redirect_output(mcopy, rt); return; } +#endif goto freecopy; case EMSGSIZE: |