summaryrefslogtreecommitdiff
path: root/sys/netinet6/ip6_forward.c
diff options
context:
space:
mode:
authorJun-ichiro itojun Hagino <itojun@cvs.openbsd.org>1999-12-08 06:50:25 +0000
committerJun-ichiro itojun Hagino <itojun@cvs.openbsd.org>1999-12-08 06:50:25 +0000
commitd9c080e0265d3557ac6fe2d2723cf4823eaa909f (patch)
tree422a747ed77f091d33ae234312450cc5cb2aa789 /sys/netinet6/ip6_forward.c
parentbfa115c3a1f89cedeb913741469312347a52f8ee (diff)
bring in KAME IPv6 code, dated 19991208.
replaces NRL IPv6 layer. reuses NRL pcb layer. no IPsec-on-v6 support. see sys/netinet6/{TODO,IMPLEMENTATION} for more details. GENERIC configuration should work fine as before. GENERIC.v6 works fine as well, but you'll need KAME userland tools to play with IPv6 (will be bringed into soon).
Diffstat (limited to 'sys/netinet6/ip6_forward.c')
-rw-r--r--sys/netinet6/ip6_forward.c450
1 files changed, 450 insertions, 0 deletions
diff --git a/sys/netinet6/ip6_forward.c b/sys/netinet6/ip6_forward.c
new file mode 100644
index 00000000000..df8b3879648
--- /dev/null
+++ b/sys/netinet6/ip6_forward.c
@@ -0,0 +1,450 @@
+/* $OpenBSD: ip6_forward.c,v 1.1 1999/12/08 06:50:21 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:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 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
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * 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.
+ */
+
+#if (defined(__FreeBSD__) && __FreeBSD__ >= 3)
+#include "opt_ip6fw.h"
+#include "opt_inet.h"
+#endif
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/malloc.h>
+#include <sys/mbuf.h>
+#include <sys/domain.h>
+#include <sys/protosw.h>
+#include <sys/socket.h>
+#include <sys/errno.h>
+#include <sys/time.h>
+#include <sys/kernel.h>
+#include <sys/syslog.h>
+
+#include <net/if.h>
+#include <net/route.h>
+
+#include <netinet/in.h>
+#include <netinet/in_var.h>
+#include <netinet6/ip6.h>
+#include <netinet6/ip6_var.h>
+#include <netinet6/icmp6.h>
+#include <netinet6/nd6.h>
+
+#ifdef __OpenBSD__ /*KAME IPSEC*/
+#undef IPSEC
+#endif
+
+#ifdef IPSEC_IPV6FWD
+#include <netinet6/ipsec.h>
+#include <netkey/key.h>
+#include <netkey/key_debug.h>
+#endif /* IPSEC_IPV6FWD */
+
+#ifdef IPV6FIREWALL
+#include <netinet6/ip6_fw.h>
+#endif
+
+#include <net/net_osdep.h>
+
+struct route_in6 ip6_forward_rt;
+
+/*
+ * Forward a packet. If some error occurs return the sender
+ * an icmp packet. Note we can't always generate a meaningful
+ * icmp message because icmp doesn't have a large enough repertoire
+ * of codes and types.
+ *
+ * If not forwarding, just drop the packet. This could be confusing
+ * if ipforwarding was zero but some routing protocol was advancing
+ * us as a gateway to somewhere. However, we must let the routing
+ * protocol deal with that.
+ *
+ */
+
+void
+ip6_forward(m, srcrt)
+ struct mbuf *m;
+ int srcrt;
+{
+ struct ip6_hdr *ip6 = mtod(m, struct ip6_hdr *);
+ register struct sockaddr_in6 *dst;
+ register struct rtentry *rt;
+ int error, type = 0, code = 0;
+ struct mbuf *mcopy = NULL;
+#ifdef IPSEC_IPV6FWD
+ struct secpolicy *sp = NULL;
+#endif
+#if !(defined(__FreeBSD__) && __FreeBSD__ >= 3)
+ long time_second = time.tv_sec;
+#endif
+
+#ifdef IPSEC_IPV6FWD
+ /*
+ * Check AH/ESP integrity.
+ */
+ /*
+ * Don't increment ip6s_cantforward because this is the check
+ * before forwarding packet actually.
+ */
+ if (ipsec6_in_reject(m, NULL)) {
+ ipsec6stat.in_polvio++;
+ m_freem(m);
+ return;
+ }
+#endif /*IPSEC_IPV6FWD*/
+
+ if (m->m_flags & (M_BCAST|M_MCAST) ||
+ in6_canforward(&ip6->ip6_src, &ip6->ip6_dst) == 0) {
+ 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->ip6_nxt,
+ if_name(m->m_pkthdr.rcvif));
+ }
+ 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_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++;
+ if (mcopy) {
+#if 0
+ /* XXX: what icmp ? */
+#else
+ m_freem(mcopy);
+#endif
+ }
+ m_freem(m);
+ return;
+ }
+
+ error = 0;
+
+ /* check policy */
+ switch (sp->policy) {
+ case IPSEC_POLICY_DISCARD:
+ /*
+ * This packet is just discarded.
+ */
+ ipsec6stat.out_polvio++;
+ ip6stat.ip6s_cantforward++;
+ key_freesp(sp);
+ if (mcopy) {
+#if 0
+ /* XXX: what icmp ? */
+#else
+ m_freem(mcopy);
+#endif
+ }
+ m_freem(m);
+ return;
+
+ case IPSEC_POLICY_BYPASS:
+ case IPSEC_POLICY_NONE:
+ /* 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);
+ if (mcopy) {
+#if 0
+ /* XXX: what icmp ? */
+#else
+ m_freem(mcopy);
+#endif
+ }
+ m_freem(m);
+ return;
+ }
+ /* do IPsec */
+ break;
+
+ case IPSEC_POLICY_ENTRUST:
+ default:
+ /* should be panic ?? */
+ printf("ip6_forward: Invalid policy found. %d\n", sp->policy);
+ key_freesp(sp);
+ goto skip_ipsec;
+ }
+
+ {
+ struct ipsec_output_state state;
+
+ /*
+ * All the extension headers will become inaccessible
+ * (since they can be encrypted).
+ * Don't panic, we need no more updates to extension headers
+ * on inner IPv6 packet (since they are now encapsulated).
+ *
+ * IPv6 [ESP|AH] IPv6 [extension headers] payload
+ */
+ bzero(&state, sizeof(state));
+ state.m = m;
+ state.ro = NULL; /* update at ipsec6_output_tunnel() */
+ state.dst = NULL; /* update at ipsec6_output_tunnel() */
+
+ error = ipsec6_output_tunnel(&state, sp, 0);
+
+ m = state.m;
+#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) {
+ /* mbuf is already reclaimed in ipsec6_output_tunnel. */
+ switch (error) {
+ case EHOSTUNREACH:
+ case ENETUNREACH:
+ case EMSGSIZE:
+ case ENOBUFS:
+ case ENOMEM:
+ break;
+ default:
+ printf("ip6_output (ipsec): error code %d\n", error);
+ /*fall through*/
+ case ENOENT:
+ /* don't show these error codes to the user */
+ break;
+ }
+ ip6stat.ip6s_cantforward++;
+ if (mcopy) {
+#if 0
+ /* XXX: what icmp ? */
+#else
+ m_freem(mcopy);
+#endif
+ }
+ m_freem(m);
+ return;
+ }
+ }
+ skip_ipsec:
+#endif /* IPSEC_IPV6FWD */
+
+ dst = &ip6_forward_rt.ro_dst;
+ if (!srcrt) {
+ /*
+ * ip6_forward_rt.ro_dst.sin6_addr is equal to ip6->ip6_dst
+ */
+ if (ip6_forward_rt.ro_rt == 0 ||
+ (ip6_forward_rt.ro_rt->rt_flags & RTF_UP) == 0) {
+ if (ip6_forward_rt.ro_rt) {
+ RTFREE(ip6_forward_rt.ro_rt);
+ ip6_forward_rt.ro_rt = 0;
+ }
+ /* this probably fails but give it a try again */
+#ifdef __FreeBSD__
+ rtalloc_ign((struct route *)&ip6_forward_rt,
+ RTF_PRCLONING);
+#else
+ rtalloc((struct route *)&ip6_forward_rt);
+#endif
+ }
+
+ if (ip6_forward_rt.ro_rt == 0) {
+ ip6stat.ip6s_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 = ip6_forward_rt.ro_rt) == 0 ||
+ !IN6_ARE_ADDR_EQUAL(&ip6->ip6_dst, &dst->sin6_addr)) {
+ if (ip6_forward_rt.ro_rt) {
+ RTFREE(ip6_forward_rt.ro_rt);
+ ip6_forward_rt.ro_rt = 0;
+ }
+ bzero(dst, sizeof(*dst));
+ dst->sin6_len = sizeof(struct sockaddr_in6);
+ dst->sin6_family = AF_INET6;
+ dst->sin6_addr = ip6->ip6_dst;
+
+#ifdef __FreeBSD__
+ rtalloc_ign((struct route *)&ip6_forward_rt, RTF_PRCLONING);
+#else
+ rtalloc((struct route *)&ip6_forward_rt);
+#endif
+ if (ip6_forward_rt.ro_rt == 0) {
+ ip6stat.ip6s_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;
+ }
+ }
+ rt = ip6_forward_rt.ro_rt;
+ if (m->m_pkthdr.len > rt->rt_ifp->if_mtu){
+ in6_ifstat_inc(rt->rt_ifp, ifs6_in_toobig);
+ if (mcopy) {
+ /*
+ * XXX
+ * When we do IPsec tunnel ingress, we need to play
+ * with if_mtu value (decrement IPsec header size
+ * from mtu value). see ip_input().
+ */
+ icmp6_error(mcopy, ICMP6_PACKET_TOO_BIG, 0,
+ rt->rt_ifp->if_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 &&
+ (rt->rt_flags & (RTF_DYNAMIC|RTF_MODIFIED)) == 0)
+ type = ND_REDIRECT;
+
+#ifdef IPV6FIREWALL
+ /*
+ * Check with the firewall...
+ */
+ if (ip6_fw_chk_ptr) {
+ u_short port = 0;
+ /* If ipfw says divert, we have to just drop packet */
+ if ((*ip6_fw_chk_ptr)(&ip6, rt->rt_ifp, &port, &m)) {
+ m_freem(m);
+ goto freecopy;
+ }
+ if (!m)
+ goto freecopy;
+ }
+#endif
+
+#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, m, dst, rt);
+#endif
+ if (error) {
+ in6_ifstat_inc(rt->rt_ifp, ifs6_out_discard);
+ ip6stat.ip6s_cantforward++;
+ } else {
+ ip6stat.ip6s_forward++;
+ in6_ifstat_inc(rt->rt_ifp, ifs6_out_forward);
+ if (type)
+ ip6stat.ip6s_redirectsent++;
+ else {
+ if (mcopy)
+ goto freecopy;
+ }
+ }
+ if (mcopy == NULL)
+ return;
+
+ switch (error) {
+ case 0:
+#if 1
+ if (type == ND_REDIRECT) {
+ icmp6_redirect_output(mcopy, rt);
+ return;
+ }
+#endif
+ 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;
+}