aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/interceptor/linux_route.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/interceptor/linux_route.c')
-rw-r--r--drivers/interceptor/linux_route.c1136
1 files changed, 1136 insertions, 0 deletions
diff --git a/drivers/interceptor/linux_route.c b/drivers/interceptor/linux_route.c
new file mode 100644
index 0000000..ab93c6a
--- /dev/null
+++ b/drivers/interceptor/linux_route.c
@@ -0,0 +1,1136 @@
+/* Netfilter Driver for IPSec VPN Client
+ *
+ * Copyright(c) 2012 Samsung Electronics
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+/*
+ * linux_route.c
+ *
+ * Linux interceptor implementation of interceptor API routing functions.
+ *
+ */
+
+#include "linux_internal.h"
+
+/****************** Internal data types and declarations ********************/
+
+typedef struct SshInterceptorRouteResultRec
+{
+ SshIpAddrStruct gw[1];
+ SshInterceptorIfnum ifnum;
+ SshUInt16 mtu;
+} SshInterceptorRouteResultStruct, *SshInterceptorRouteResult;
+
+/****************** Internal Utility Functions ******************************/
+
+#ifdef SSH_IPSEC_IP_ONLY_INTERCEPTOR
+#if (defined LINUX_FRAGMENTATION_AFTER_NF_POST_ROUTING) \
+ || (defined LINUX_FRAGMENTATION_AFTER_NF6_POST_ROUTING)
+
+/* Create a child dst_entry with locked interface MTU, and attach it to `dst'.
+ This is needed on newer linux kernels and IP_ONLY_INTERCEPTOR builds,
+ where the IP stack fragments packets to path MTU after ssh_interceptor_send.
+*/
+static struct dst_entry *
+interceptor_route_create_child_dst(struct dst_entry *dst, Boolean ipv6)
+{
+ struct dst_entry *child;
+#ifdef LINUX_HAS_DST_COPY_METRICS
+ SshUInt32 set;
+ struct rt6_info *rt6;
+ struct rtable *rt;
+#endif /* LINUX_HAS_DST_COPY_METRICS */
+
+ /* Allocate a dst_entry and copy relevant fields from dst. */
+ child = SSH_DST_ALLOC(dst);
+ if (child == NULL)
+ return NULL;
+
+ child->input = dst->input;
+ child->output = dst->output;
+
+ /* Child is not added to dst hash, and linux native IPsec is disabled. */
+ child->flags |= (DST_NOHASH | DST_NOPOLICY | DST_NOXFRM);
+
+ /* Copy route metrics and lock MTU to interface MTU. */
+#ifdef LINUX_HAS_DST_COPY_METRICS
+ if (ipv6 == TRUE)
+ {
+ rt6 = (struct rt6_info *)child;
+ memset(&rt6->rt6i_table, 0, sizeof(*rt6) - sizeof(struct dst_entry));
+ }
+ else
+ {
+ rt = (struct rtable *)child;
+ memset(&SSH_RTABLE_FIRST_MEMBER(rt), 0,
+ sizeof(*rt) - sizeof(struct dst_entry));
+ }
+
+ dst_copy_metrics(child, dst);
+ set = dst_metric(child, RTAX_LOCK);
+ set |= 1 << RTAX_MTU;
+ dst_metric_set(child, RTAX_LOCK, set);
+#else /* LINUX_HAS_DST_COPY_METRICS */
+ memcpy(child->metrics, dst->metrics, sizeof(child->metrics));
+ child->metrics[RTAX_LOCK-1] |= 1 << RTAX_MTU;
+#endif /* LINUX_HAS_DST_COPY_METRICS */
+
+#ifdef CONFIG_NET_CLS_ROUTE
+ child->tclassid = dst->tclassid;
+#endif /* CONFIG_NET_CLS_ROUTE */
+
+#ifdef CONFIG_XFRM
+ child->xfrm = NULL;
+#endif /* CONFIG_XFRM */
+
+#ifdef LINUX_HAS_HH_CACHE
+ if (dst->hh)
+ {
+ atomic_inc(&dst->hh->hh_refcnt);
+ child->hh = dst->hh;
+ }
+#endif /* LINUX_HAS_HH_CACHE */
+
+#ifdef LINUX_HAS_DST_NEIGHBOUR_FUNCTIONS
+ if (dst_get_neighbour(dst) != NULL)
+ dst_set_neighbour(child, neigh_clone(dst_get_neighbour(dst)));
+#else /* LINUX_HAS_DST_NEIGHBOUR_FUNCTIONS */
+ if (dst->neighbour != NULL)
+ child->neighbour = neigh_clone(dst->neighbour);
+#endif /* LINUX_HAS_DST_NEIGHBOUR_FUNCTIONS */
+
+ if (dst->dev)
+ {
+ dev_hold(dst->dev);
+ child->dev = dst->dev;
+ }
+
+ SSH_ASSERT(dst->child == NULL);
+ dst->child = dst_clone(child);
+
+ SSH_DEBUG(SSH_D_MIDOK, ("Allocated child %p dst_entry for dst %p mtu %d",
+ child, dst, dst_mtu(dst)));
+
+ return child;
+}
+
+#endif /* LINUX_FRAGMENTATION_AFTER_NF_POST_ROUTING
+ || LINUX_FRAGMENTATION_AFTER_NF6_POST_ROUTING */
+#endif /* SSH_IPSEC_IP_ONLY_INTERCEPTOR */
+
+static inline int interceptor_ip4_route_output(struct rtable **rt,
+ struct flowi *fli)
+{
+ int rval = 0;
+#ifdef LINUX_USE_NF_FOR_ROUTE_OUTPUT
+ struct dst_entry *dst = NULL;
+ const struct nf_afinfo *afinfo = NULL;
+ unsigned short family = AF_INET;
+
+ rcu_read_lock();
+ afinfo = nf_get_afinfo(family);
+ if (!afinfo)
+ {
+ rcu_read_unlock();
+ SSH_DEBUG(SSH_D_FAIL, ("Failed to get nf afinfo"));
+ return -1;
+ }
+
+ rval = afinfo->route(&init_net, &dst, fli, TRUE);
+ rcu_read_unlock();
+
+ *rt = (struct rtable *)dst;
+
+#else /* LINUX_USE_NF_FOR_ROUTE_OUTPUT */
+
+#ifdef LINUX_IP_ROUTE_OUTPUT_KEY_HAS_NET_ARGUMENT
+ rval = ip_route_output_key(&init_net, rt, fli);
+#else /* LINUX_IP_ROUTE_OUTPUT_KEY_HAS_NET_ARGUMENT */
+ rval = ip_route_output_key(rt, fli);
+#endif /* LINUX_IP_ROUTE_OUTPUT_KEY_HAS_NET_ARGUMENT */
+#endif /* LINUX_USE_NF_FOR_ROUTE_OUTPUT */
+
+ if (rval > 0)
+ rval = -rval;
+
+ return rval;
+}
+
+static inline struct dst_entry *interceptor_ip6_route_output(struct flowi *fli)
+{
+ struct dst_entry *dst = NULL;
+
+ /* Perform route lookup */
+ /* we do not need a socket, only fake flow */
+#ifdef LINUX_USE_NF_FOR_ROUTE_OUTPUT
+ const struct nf_afinfo *afinfo = NULL;
+ unsigned short family = AF_INET6;
+ int rval = 0;
+
+ rcu_read_lock();
+ afinfo = nf_get_afinfo(family);
+ if (!afinfo)
+ {
+ rcu_read_unlock();
+ SSH_DEBUG(SSH_D_FAIL, ("Failed to get nf afinfo"));
+ return NULL;
+ }
+
+ rval = afinfo->route(&init_net, &dst, fli, FALSE);
+ rcu_read_unlock();
+ if (rval != 0)
+ {
+ SSH_DEBUG(SSH_D_FAIL, ("Failed to get route from IPv6 NF"));
+ return NULL;
+ }
+#else /* LINUX_USE_NF_FOR_ROUTE_OUTPUT */
+
+#ifdef LINUX_IP6_ROUTE_OUTPUT_KEY_HAS_NET_ARGUMENT
+ dst = ip6_route_output(&init_net, NULL, fli);
+#else /* LINUX_IP6_ROUTE_OUTPUT_KEY_HAS_NET_ARGUMENT */
+ dst = ip6_route_output(NULL, fli);
+#endif /* LINUX_IP6_ROUTE_OUTPUT_KEY_HAS_NET_ARGUMENT */
+#endif /* LINUX_USE_NF_FOR_ROUTE_OUTPUT */
+
+ return dst;
+}
+
+/****************** Implementation of ssh_interceptor_route *****************/
+
+/* Perform route lookup using linux ip_route_output_key.
+
+ The route lookup will use the following selectors:
+ dst, src, outbound ifnum, ip protocol, tos, and fwmark.
+ The source address is expected to be local or undefined.
+
+ The following selectors are ignored:
+ dst port, src port, icmp type, icmp code, ipsec spi. */
+Boolean
+ssh_interceptor_route_output_ipv4(SshInterceptor interceptor,
+ SshInterceptorRouteKey key,
+ SshUInt16 selector,
+ SshInterceptorRouteResult result)
+{
+ u32 daddr;
+ struct rtable *rt;
+ int rval;
+ struct flowi rt_key;
+ u16 rt_type;
+#ifdef DEBUG_LIGHT
+ unsigned char *rt_type_str;
+ u32 fwmark = 0;
+ unsigned char dst_buf[SSH_IP_ADDR_STRING_SIZE];
+ unsigned char src_buf[SSH_IP_ADDR_STRING_SIZE];
+#endif /* DEBUG_LIGHT */
+
+ SSH_IP4_ENCODE(&key->dst, (unsigned char *) &daddr);
+
+ /* Initialize rt_key with zero values */
+ memset(&rt_key, 0, sizeof(rt_key));
+
+ rt_key.fl4_dst = daddr;
+ if (selector & SSH_INTERCEPTOR_ROUTE_KEY_SRC)
+ SSH_IP4_ENCODE(&key->src, (unsigned char *) &rt_key.fl4_src);
+ if (selector & SSH_INTERCEPTOR_ROUTE_KEY_OUT_IFNUM)
+ {
+ SSH_LINUX_ASSERT_VALID_IFNUM(key->ifnum);
+ rt_key.oif = key->ifnum;
+ }
+ if (selector & SSH_INTERCEPTOR_ROUTE_KEY_IPPROTO)
+ rt_key.proto = key->ipproto;
+ if (selector & SSH_INTERCEPTOR_ROUTE_KEY_IP4_TOS)
+ rt_key.fl4_tos = key->nh.ip4.tos;
+ rt_key.fl4_scope = RT_SCOPE_UNIVERSE;
+
+#if (SSH_INTERCEPTOR_NUM_EXTENSION_SELECTORS > 0)
+#ifdef SSH_LINUX_FWMARK_EXTENSION_SELECTOR
+
+ /* Use linux fw_mark in routing */
+ if (selector & SSH_INTERCEPTOR_ROUTE_KEY_EXTENSION)
+ {
+#ifdef LINUX_HAS_SKB_MARK
+ rt_key.mark = key->extension[SSH_LINUX_FWMARK_EXTENSION_SELECTOR];
+#else /* LINUX_HAS_SKB_MARK */
+#ifdef CONFIG_IP_ROUTE_FWMARK
+ rt_key.fl4_fwmark =
+ key->extension[SSH_LINUX_FWMARK_EXTENSION_SELECTOR];
+#endif /* CONFIG_IP_ROUTE_FWMARK */
+#endif /* LINUX_HAS_SKB_MARK */
+#ifdef DEBUG_LIGHT
+ fwmark = key->extension[SSH_LINUX_FWMARK_EXTENSION_SELECTOR];
+#endif /* DEBUG_LIGHT */
+ }
+
+#endif /* SSH_LINUX_FWMARK_EXTENSION_SELECTOR */
+#endif /* (SSH_INTERCEPTOR_NUM_EXTENSION_SELECTORS > 0) */
+
+ SSH_DEBUG(SSH_D_LOWOK,
+ ("Route lookup: "
+ "dst %s src %s ifnum %d ipproto %d tos 0x%02x fwmark 0x%lx",
+ ssh_ipaddr_print(&key->dst, dst_buf, sizeof(dst_buf)),
+ ((selector & SSH_INTERCEPTOR_ROUTE_KEY_SRC) ?
+ ssh_ipaddr_print(&key->src, src_buf, sizeof(src_buf)) : NULL),
+ (int) ((selector & SSH_INTERCEPTOR_ROUTE_KEY_OUT_IFNUM)?
+ key->ifnum : -1),
+ (int) ((selector & SSH_INTERCEPTOR_ROUTE_KEY_IPPROTO) ?
+ key->ipproto : -1),
+ ((selector & SSH_INTERCEPTOR_ROUTE_KEY_IP4_TOS) ?
+ key->nh.ip4.tos : 0),
+ (unsigned long) ((selector & SSH_INTERCEPTOR_ROUTE_KEY_EXTENSION)?
+ fwmark : 0)));
+
+ /* Perform route lookup */
+
+ rval = interceptor_ip4_route_output(&rt, &rt_key);
+ if (rval < 0)
+ {
+ goto fail;
+ }
+
+ /* Get the gateway, mtu and ifnum */
+
+ SSH_IP4_DECODE(result->gw, &rt->rt_gateway);
+ result->mtu = SSH_DST_MTU(&SSH_RT_DST(rt));
+ result->ifnum = SSH_RT_DST(rt).dev->ifindex;
+ rt_type = rt->rt_type;
+
+#ifdef DEBUG_LIGHT
+ switch (rt_type)
+ {
+ case RTN_UNSPEC:
+ rt_type_str = "unspec";
+ break;
+ case RTN_UNICAST:
+ rt_type_str = "unicast";
+ break;
+ case RTN_LOCAL:
+ rt_type_str = "local";
+ break;
+ case RTN_BROADCAST:
+ rt_type_str = "broadcast";
+ break;
+ case RTN_ANYCAST:
+ rt_type_str = "anycast";
+ break;
+ case RTN_MULTICAST:
+ rt_type_str = "multicast";
+ break;
+ case RTN_BLACKHOLE:
+ rt_type_str = "blackhole";
+ break;
+ case RTN_UNREACHABLE:
+ rt_type_str = "unreachable";
+ break;
+ case RTN_PROHIBIT:
+ rt_type_str = "prohibit";
+ break;
+ case RTN_THROW:
+ rt_type_str = "throw";
+ break;
+ case RTN_NAT:
+ rt_type_str = "nat";
+ break;
+ case RTN_XRESOLVE:
+ rt_type_str = "xresolve";
+ break;
+ default:
+ rt_type_str = "unknown";
+ }
+#endif /* DEBUG_LIGHT */
+
+ SSH_DEBUG(SSH_D_LOWOK,
+ ("Route result: dst %s via %s ifnum %d[%s] mtu %d type %s [%d]",
+ dst_buf, ssh_ipaddr_print(result->gw, src_buf, sizeof(src_buf)),
+ (int) result->ifnum,
+ (SSH_RT_DST(rt).dev->name ? SSH_RT_DST(rt).dev->name : "none"),
+ result->mtu, rt_type_str, rt_type));
+
+#ifdef SSH_IPSEC_IP_ONLY_INTERCEPTOR
+#ifdef LINUX_FRAGMENTATION_AFTER_NF_POST_ROUTING
+ /* Check if need to create a child dst_entry with interface MTU. */
+ if ((selector & SSH_INTERCEPTOR_ROUTE_KEY_FLAG_TRANSFORM_APPLIED)
+ && SSH_RT_DST(rt).child == NULL)
+ {
+ if (interceptor_route_create_child_dst(&SSH_RT_DST(rt), FALSE) == NULL)
+ SSH_DEBUG(SSH_D_FAIL, ("Could not create child dst_entry for dst %p",
+ &SSH_RT_DST(rt)));
+ }
+#endif /* LINUX_FRAGMENTATION_AFTER_NF_POST_ROUTING */
+#endif /* SSH_IPSEC_IP_ONLY_INTERCEPTOR */
+
+ /* Release the routing table entry ; otherwise a memory leak occurs
+ in the route entry table. */
+ ip_rt_put(rt);
+
+ /* Assert that ifnum fits into the SshInterceptorIfnum data type. */
+ SSH_LINUX_ASSERT_IFNUM(result->ifnum);
+
+ /* Check that ifnum does not collide with SSH_INTERCEPTOR_INVALID_IFNUM. */
+ if (result->ifnum == SSH_INTERCEPTOR_INVALID_IFNUM)
+ goto fail;
+
+ /* Accept only unicast, broadcast, anycast, multicast and local routes */
+ if (rt_type == RTN_UNICAST
+ || rt_type == RTN_BROADCAST
+ || rt_type == RTN_ANYCAST
+ || rt_type == RTN_MULTICAST
+ || rt_type == RTN_LOCAL)
+ {
+ SSH_LINUX_ASSERT_VALID_IFNUM(result->ifnum);
+ return TRUE;
+ }
+
+ fail:
+ /* Fail route lookup for other route types */
+ SSH_DEBUG(SSH_D_FAIL, ("Route lookup for %s failed with code %d",
+ dst_buf, rval));
+
+ return FALSE;
+}
+
+
+/* Perform route lookup using linux ip_route_input.
+
+ The route lookup will use the following selectors:
+ dst, src, inbound ifnum, ip protocol, tos, and fwmark.
+ The source address is expected to be non-local and it must be defined.
+
+ The following selectors are ignored:
+ dst port, src port, icmp type, icmp code, ipsec spi. */
+Boolean
+ssh_interceptor_route_input_ipv4(SshInterceptor interceptor,
+ SshInterceptorRouteKey key,
+ SshUInt16 selector,
+ SshInterceptorRouteResult result)
+{
+ u32 daddr, saddr;
+ u8 ipproto;
+ u8 tos;
+ u32 fwmark;
+ struct sk_buff *skbp;
+ struct net_device *dev;
+ struct rtable *rt;
+ int rval = 0;
+ u16 rt_type;
+ struct iphdr *iph = NULL;
+#ifdef DEBUG_LIGHT
+ unsigned char *rt_type_str;
+ unsigned char dst_buf[SSH_IP_ADDR_STRING_SIZE];
+ unsigned char src_buf[SSH_IP_ADDR_STRING_SIZE];
+#endif /* DEBUG_LIGHT */
+
+ SSH_IP4_ENCODE(&key->dst, (unsigned char *) &daddr);
+
+ /* Initialize */
+ saddr = 0;
+ ipproto = 0;
+ tos = 0;
+ fwmark = 0;
+ dev = NULL;
+
+ if (selector & SSH_INTERCEPTOR_ROUTE_KEY_SRC)
+ SSH_IP4_ENCODE(&key->src, (unsigned char *) &saddr);
+ if (selector & SSH_INTERCEPTOR_ROUTE_KEY_IN_IFNUM)
+ {
+ SSH_LINUX_ASSERT_VALID_IFNUM(key->ifnum);
+ dev = ssh_interceptor_ifnum_to_netdev(interceptor, key->ifnum);
+ }
+
+ if (dev == NULL)
+ return FALSE;
+
+ if (selector & SSH_INTERCEPTOR_ROUTE_KEY_IPPROTO)
+ ipproto = key->ipproto;
+ if (selector & SSH_INTERCEPTOR_ROUTE_KEY_IP4_TOS)
+ tos = key->nh.ip4.tos;
+
+#if (SSH_INTERCEPTOR_NUM_EXTENSION_SELECTORS > 0)
+#ifdef SSH_LINUX_FWMARK_EXTENSION_SELECTOR
+ /* Use linux fw_mark in routing */
+ if (selector & SSH_INTERCEPTOR_ROUTE_KEY_EXTENSION)
+ fwmark = key->extension[SSH_LINUX_FWMARK_EXTENSION_SELECTOR];
+#endif /* SSH_LINUX_FWMARK_EXTENSION_SELECTOR */
+#endif /* (SSH_INTERCEPTOR_NUM_EXTENSION_SELECTORS > 0) */
+
+ /* Build dummy skb */
+ skbp = alloc_skb(SSH_IPH4_HDRLEN, GFP_ATOMIC);
+ if (skbp == NULL)
+ goto fail;
+
+ SSH_SKB_RESET_MACHDR(skbp);
+ iph = (struct iphdr *) skb_put(skbp, SSH_IPH4_HDRLEN);
+ if (iph == NULL)
+ {
+ dev_kfree_skb(skbp);
+ goto fail;
+ }
+ SSH_SKB_SET_NETHDR(skbp, (unsigned char *) iph);
+
+ SSH_SKB_DST_SET(skbp, NULL);
+ skbp->protocol = __constant_htons(ETH_P_IP);
+ SSH_SKB_MARK(skbp) = fwmark;
+ iph->protocol = ipproto;
+
+ SSH_DEBUG(SSH_D_LOWOK,
+ ("Route lookup: "
+ "dst %s src %s ifnum %d[%s] ipproto %d tos 0x%02x fwmark 0x%x",
+ ssh_ipaddr_print(&key->dst, dst_buf, sizeof(dst_buf)),
+ ((selector & SSH_INTERCEPTOR_ROUTE_KEY_SRC) ?
+ ssh_ipaddr_print(&key->src, src_buf, sizeof(src_buf)) : NULL),
+ dev->ifindex, dev->name,
+ ipproto, tos, fwmark));
+
+ /* Perform route lookup */
+
+ rval = ip_route_input(skbp, daddr, saddr, tos, dev);
+ if (rval < 0 || SSH_SKB_DST(skbp) == NULL)
+ {
+ dev_kfree_skb(skbp);
+ goto fail;
+ }
+
+ /* Get the gateway, mtu and ifnum */
+ rt = (struct rtable *) SSH_SKB_DST(skbp);
+ SSH_IP4_DECODE(result->gw, &rt->rt_gateway);
+ result->mtu = SSH_DST_MTU(SSH_SKB_DST(skbp));
+ result->ifnum = SSH_SKB_DST(skbp)->dev->ifindex;
+ rt_type = rt->rt_type;
+
+#ifdef DEBUG_LIGHT
+ switch (rt_type)
+ {
+ case RTN_UNSPEC:
+ rt_type_str = "unspec";
+ break;
+ case RTN_UNICAST:
+ rt_type_str = "unicast";
+ break;
+ case RTN_LOCAL:
+ rt_type_str = "local";
+ break;
+ case RTN_BROADCAST:
+ rt_type_str = "broadcast";
+ break;
+ case RTN_ANYCAST:
+ rt_type_str = "anycast";
+ break;
+ case RTN_MULTICAST:
+ rt_type_str = "multicast";
+ break;
+ case RTN_BLACKHOLE:
+ rt_type_str = "blackhole";
+ break;
+ case RTN_UNREACHABLE:
+ rt_type_str = "unreachable";
+ break;
+ case RTN_PROHIBIT:
+ rt_type_str = "prohibit";
+ break;
+ case RTN_THROW:
+ rt_type_str = "throw";
+ break;
+ case RTN_NAT:
+ rt_type_str = "nat";
+ break;
+ case RTN_XRESOLVE:
+ rt_type_str = "xresolve";
+ break;
+ default:
+ rt_type_str = "unknown";
+ }
+#endif /* DEBUG_LIGHT */
+
+ SSH_DEBUG(SSH_D_LOWOK,
+ ("Route result: dst %s via %s ifnum %d[%s] mtu %d type %s [%d]",
+ dst_buf, ssh_ipaddr_print(result->gw, src_buf, sizeof(src_buf)),
+ (int) result->ifnum,
+ (SSH_RT_DST(rt).dev->name ? SSH_RT_DST(rt).dev->name : "none"),
+ result->mtu, rt_type_str, rt_type));
+
+#ifdef SSH_IPSEC_IP_ONLY_INTERCEPTOR
+#ifdef LINUX_FRAGMENTATION_AFTER_NF_POST_ROUTING
+ /* Check if need to create a child dst_entry with interface MTU. */
+ if ((selector & SSH_INTERCEPTOR_ROUTE_KEY_FLAG_TRANSFORM_APPLIED)
+ && SSH_SKB_DST(skbp)->child == NULL)
+ {
+ if (interceptor_route_create_child_dst(SSH_SKB_DST(skbp), FALSE) == NULL)
+ SSH_DEBUG(SSH_D_FAIL, ("Could not create child dst_entry for dst %p",
+ SSH_SKB_DST(skbp)));
+ }
+#endif /* LINUX_FRAGMENTATION_AFTER_NF_POST_ROUTING */
+#endif /* SSH_IPSEC_IP_ONLY_INTERCEPTOR */
+
+ /* Release the routing table entry ; otherwise a memory leak occurs
+ in the route entry table. */
+ dst_release(SSH_SKB_DST(skbp));
+ SSH_SKB_DST_SET(skbp, NULL);
+ dev_kfree_skb(skbp);
+
+ /* Assert that ifnum fits into the SshInterceptorIfnum data type. */
+ SSH_LINUX_ASSERT_IFNUM(result->ifnum);
+
+ /* Check that ifnum does not collide with SSH_INTERCEPTOR_INVALID_IFNUM. */
+ if (result->ifnum == SSH_INTERCEPTOR_INVALID_IFNUM)
+ goto fail;
+
+ /* Accept only unicast, broadcast, anycast, multicast and local routes. */
+ if (rt_type == RTN_UNICAST
+ || rt_type == RTN_BROADCAST
+ || rt_type == RTN_ANYCAST
+ || rt_type == RTN_MULTICAST
+ || rt_type == RTN_LOCAL)
+ {
+ ssh_interceptor_release_netdev(dev);
+ SSH_LINUX_ASSERT_VALID_IFNUM(result->ifnum);
+ return TRUE;
+ }
+
+ /* Fail route lookup for other route types. */
+ fail:
+ ssh_interceptor_release_netdev(dev);
+
+ SSH_DEBUG(SSH_D_FAIL,
+ ("Route lookup for %s failed with code %d",
+ ssh_ipaddr_print(&key->dst, dst_buf, sizeof(dst_buf)), rval));
+
+ return FALSE;
+}
+
+#ifdef SSH_LINUX_INTERCEPTOR_IPV6
+
+/* Perform route lookup using linux ip6_route_output.
+
+ The route lookup will use the following selectors:
+ dst, src, outbound ifnum.
+
+ The following selectors are ignored:
+ ipv6 priority, flowlabel, ip protocol, dst port, src port,
+ icmp type, icmp code, ipsec spi, and fwmark. */
+Boolean
+ssh_interceptor_route_output_ipv6(SshInterceptor interceptor,
+ SshInterceptorRouteKey key,
+ SshUInt16 selector,
+ SshInterceptorRouteResult result)
+{
+ struct flowi rt_key;
+ struct dst_entry *dst;
+ struct rt6_info *rt;
+ u32 rt6i_flags;
+ int error = 0;
+ struct neighbour *neigh;
+#ifdef DEBUG_LIGHT
+ unsigned char dst_buf[SSH_IP_ADDR_STRING_SIZE];
+ unsigned char src_buf[SSH_IP_ADDR_STRING_SIZE];
+#endif /* DEBUG_LIGHT */
+
+ memset(&rt_key, 0, sizeof(rt_key));
+
+ SSH_IP6_ENCODE(&key->dst, rt_key.fl6_dst.s6_addr);
+
+ if (selector & SSH_INTERCEPTOR_ROUTE_KEY_SRC)
+ SSH_IP6_ENCODE(&key->src, rt_key.fl6_src.s6_addr);
+
+ if (selector & SSH_INTERCEPTOR_ROUTE_KEY_OUT_IFNUM)
+ {
+ SSH_LINUX_ASSERT_VALID_IFNUM(key->ifnum);
+ rt_key.oif = key->ifnum;
+ }
+
+ SSH_DEBUG(SSH_D_LOWOK,
+ ("Route lookup: dst %s src %s ifnum %d",
+ ssh_ipaddr_print(&key->dst, dst_buf, sizeof(dst_buf)),
+ ((selector & SSH_INTERCEPTOR_ROUTE_KEY_SRC) ?
+ ssh_ipaddr_print(&key->src, src_buf, sizeof(src_buf)) : NULL),
+ (int) ((selector & SSH_INTERCEPTOR_ROUTE_KEY_OUT_IFNUM) ?
+ key->ifnum : -1)));
+
+ dst = interceptor_ip6_route_output(&rt_key);
+ if (dst == NULL)
+ {
+ goto fail;
+ }
+ else if (dst->error != 0)
+ {
+ error = dst->error;
+ /* Release dst_entry */
+ dst_release(dst);
+ goto fail;
+ }
+
+ rt = (struct rt6_info *) dst;
+
+ /* Get the gateway, mtu and ifnum */
+
+ /* For an example of retrieving routing information for IPv6
+ within Linux kernel (2.4.19) see inet6_rtm_getroute()
+ in /usr/src/linux/net/ipv6/route.c */
+#ifdef LINUX_HAS_DST_NEIGHBOUR_FUNCTIONS
+ neigh = dst_get_neighbour(&rt->dst);
+#else /* LINUX_HAS_DST_NEIGHBOUR_FUNCTIONS */
+ neigh = rt->rt6i_nexthop;
+#endif /* LINUX_HAS_DST_NEIGHBOUR_FUNCTIONS */
+
+ if (neigh != NULL)
+ SSH_IP6_DECODE(result->gw, &neigh->primary_key);
+ else
+ SSH_IP6_DECODE(result->gw, &rt_key.fl6_dst.s6_addr);
+
+ result->mtu = SSH_DST_MTU(&SSH_RT_DST(rt));
+
+ /* The interface number might not be ok, but that is a problem
+ for the recipient of the routing information. */
+ result->ifnum = dst->dev->ifindex;
+
+ rt6i_flags = rt->rt6i_flags;
+
+ SSH_DEBUG(SSH_D_LOWOK,
+ ("Route result: %s via %s ifnum %d[%s] mtu %d flags 0x%08x[%s%s]",
+ dst_buf, ssh_ipaddr_print(result->gw, src_buf, sizeof(src_buf)),
+ (int) result->ifnum, (dst->dev ? dst->dev->name : "none"),
+ result->mtu, rt6i_flags, ((rt6i_flags & RTF_UP) ? "up " : ""),
+ ((rt6i_flags & RTF_REJECT) ? "reject" : "")));
+
+
+#ifdef SSH_IPSEC_IP_ONLY_INTERCEPTOR
+#ifdef LINUX_FRAGMENTATION_AFTER_NF6_POST_ROUTING
+ /* Check if need to create a child dst_entry with interface MTU. */
+ if (selector & SSH_INTERCEPTOR_ROUTE_KEY_FLAG_TRANSFORM_APPLIED)
+ {
+ if (dst->child == NULL)
+ if (interceptor_route_create_child_dst(dst, TRUE) == NULL)
+ SSH_DEBUG(SSH_D_FAIL, ("Could not create child dst_entry for dst %p",
+ dst));
+ }
+#endif /* LINUX_FRAGMENTATION_AFTER_NF6_POST_ROUTING */
+#endif /* SSH_IPSEC_IP_ONLY_INTERCEPTOR */
+
+ /* Release dst_entry */
+ dst_release(dst);
+
+ /* Assert that ifnum fits into the SshInterceptorIfnum data type. */
+ SSH_LINUX_ASSERT_IFNUM(result->ifnum);
+
+ /* Check that ifnum does not collide with SSH_INTERCEPTOR_INVALID_IFNUM. */
+ if (result->ifnum == SSH_INTERCEPTOR_INVALID_IFNUM)
+ goto fail;
+
+ /* Accept only valid routes */
+ if ((rt6i_flags & RTF_UP)
+ && (rt6i_flags & RTF_REJECT) == 0)
+ {
+ SSH_LINUX_ASSERT_VALID_IFNUM(result->ifnum);
+ return TRUE;
+ }
+
+ /* Fail route lookup for reject and unknown routes */
+ fail:
+ SSH_DEBUG(SSH_D_FAIL, ("Route lookup for %s failed with code %d",
+ dst_buf, error));
+
+ return FALSE;
+}
+#endif /* SSH_LINUX_INTERCEPTOR_IPV6 */
+
+/* Performs a route lookup for the given destination address.
+ This also always calls a callback function. */
+
+void
+ssh_interceptor_route(SshInterceptor interceptor,
+ SshInterceptorRouteKey key,
+ SshInterceptorRouteCompletion callback,
+ void *cb_context)
+{
+ SshInterceptorRouteResultStruct result;
+
+ /* It is a fatal error to call ssh_interceptor_route with
+ a routing key that does not specify the destination address. */
+ SSH_ASSERT(SSH_IP_DEFINED(&key->dst));
+
+ if (SSH_IP_IS4(&key->dst))
+ {
+ /* Key specifies non-local src address and inbound ifnum
+ -> use ssh_interceptor_route_input_ipv4 */
+ if ((key->selector & SSH_INTERCEPTOR_ROUTE_KEY_SRC)
+ && (key->selector & SSH_INTERCEPTOR_ROUTE_KEY_FLAG_LOCAL_SRC) == 0
+ && (key->selector & SSH_INTERCEPTOR_ROUTE_KEY_IN_IFNUM))
+ {
+ /* Assert that all mandatory selectors are present. */
+ SSH_ASSERT(key->selector & SSH_INTERCEPTOR_ROUTE_KEY_SRC);
+ SSH_ASSERT(SSH_IP_IS4(&key->src));
+ SSH_ASSERT(key->selector & SSH_INTERCEPTOR_ROUTE_KEY_IN_IFNUM);
+
+ if (!ssh_interceptor_route_input_ipv4(interceptor, key,
+ key->selector, &result))
+ goto fail;
+ }
+
+ /* Otherwise use ssh_interceptor_route_output_ipv4 */
+ else
+ {
+ SshUInt16 selector = key->selector;
+
+ /* Assert that all mandatory selectors are present. */
+ SSH_ASSERT((key->selector & SSH_INTERCEPTOR_ROUTE_KEY_SRC) == 0
+ || SSH_IP_IS4(&key->src));
+
+ /* Key specifies non-local src address.
+ Linux ip_route_output_key will fail such route lookups,
+ so we must clear the src address selector and do the
+ route lookup as if src was one of local addresses.
+ For example, locally generated TCP RST packets are
+ such packets. */
+ if ((key->selector & SSH_INTERCEPTOR_ROUTE_KEY_FLAG_LOCAL_SRC) == 0)
+ selector &= ~SSH_INTERCEPTOR_ROUTE_KEY_SRC;
+
+ if (!ssh_interceptor_route_output_ipv4(interceptor, key, selector,
+ &result))
+ goto fail;
+ }
+
+ (*callback)(TRUE, result.gw, result.ifnum, result.mtu, cb_context);
+ return;
+ }
+
+#ifdef SSH_LINUX_INTERCEPTOR_IPV6
+ if (SSH_IP_IS6(&key->dst))
+ {
+ /* Assert that all mandatory selectors are present. */
+ SSH_ASSERT((key->selector & SSH_INTERCEPTOR_ROUTE_KEY_SRC) == 0
+ || SSH_IP_IS6(&key->src));
+
+ /* Always use ip6_route_output for IPv6 */
+ if (!ssh_interceptor_route_output_ipv6(interceptor, key, key->selector,
+ &result))
+ goto fail;
+
+ (*callback)(TRUE, result.gw, result.ifnum, result.mtu, cb_context);
+ return;
+ }
+#endif /* SSH_LINUX_INTERCEPTOR_IPV6 */
+
+ /* Fallback to error */
+ fail:
+ SSH_DEBUG(SSH_D_FAIL, ("Route lookup failed, unknown dst address type"));
+ (*callback) (FALSE, NULL, 0, 0, cb_context);
+}
+
+
+/**************************** Rerouting of Packets **************************/
+
+
+/* Route IPv4 packet 'skbp', using the route key selectors in
+ 'route_selector' and the interface number 'ifnum_in'. */
+Boolean
+ssh_interceptor_reroute_skb_ipv4(SshInterceptor interceptor,
+ struct sk_buff *skbp,
+ SshUInt16 route_selector,
+ SshUInt32 ifnum_in)
+{
+ struct iphdr *iph;
+ int rval = 0;
+
+ /* Recalculate the route info as the engine might have touched the
+ destination address. This can happen for example if we are in
+ tunnel mode. */
+
+ iph = (struct iphdr *) SSH_SKB_GET_NETHDR(skbp);
+ if (iph == NULL)
+ {
+ SSH_DEBUG(SSH_D_ERROR, ("Could not access IP header"));
+ return FALSE;
+ }
+
+ /* Release old dst_entry */
+ if (SSH_SKB_DST(skbp))
+ dst_release(SSH_SKB_DST(skbp));
+
+ SSH_SKB_DST_SET(skbp, NULL);
+
+ if ((route_selector & SSH_INTERCEPTOR_ROUTE_KEY_SRC)
+ && (route_selector & SSH_INTERCEPTOR_ROUTE_KEY_FLAG_LOCAL_SRC) == 0
+ && (route_selector & SSH_INTERCEPTOR_ROUTE_KEY_IN_IFNUM))
+ {
+ u32 saddr = 0;
+ u8 ipproto = 0;
+ u8 tos = 0;
+#if (SSH_INTERCEPTOR_NUM_EXTENSION_SELECTORS > 0)
+#ifdef SSH_LINUX_FWMARK_EXTENSION_SELECTOR
+ u32 fwmark = 0;
+#endif /* SSH_LINUX_FWMARK_EXTENSION_SELECTOR */
+#endif /* (SSH_INTERCEPTOR_NUM_EXTENSION_SELECTORS > 0) */
+ struct net_device *dev;
+
+ SSH_ASSERT(skbp->protocol == __constant_htons(ETH_P_IP));
+
+ if (route_selector & SSH_INTERCEPTOR_ROUTE_KEY_SRC)
+ saddr = iph->saddr;
+
+ /* Map 'ifnum_in' to a net_device. */
+ SSH_LINUX_ASSERT_VALID_IFNUM(ifnum_in);
+ dev = ssh_interceptor_ifnum_to_netdev(interceptor, ifnum_in);
+ if (dev == NULL)
+ return FALSE;
+
+ /* Clear the IP protocol, if selector does not define it. */
+ if ((route_selector & SSH_INTERCEPTOR_ROUTE_KEY_IPPROTO) == 0)
+ {
+ ipproto = iph->protocol;
+ iph->protocol = 0;
+ }
+
+ if (route_selector & SSH_INTERCEPTOR_ROUTE_KEY_IP4_TOS)
+ tos = RT_TOS(iph->tos);
+
+#if (SSH_INTERCEPTOR_NUM_EXTENSION_SELECTORS > 0)
+#ifdef SSH_LINUX_FWMARK_EXTENSION_SELECTOR
+ /* Clear the nfmark, if selector does not define it. */
+ if ((route_selector & SSH_INTERCEPTOR_ROUTE_KEY_EXTENSION) == 0)
+ {
+ fwmark = SSH_SKB_MARK(skbp);
+ SSH_SKB_MARK(skbp) = 0;
+ }
+#endif /* SSH_LINUX_FWMARK_EXTENSION_SELECTOR */
+#endif /* (SSH_INTERCEPTOR_NUM_EXTENSION_SELECTORS > 0) */
+
+ /* Call ip_route_input */
+ if (ip_route_input(skbp, iph->daddr, saddr, tos, dev) < 0)
+ {
+ SSH_DEBUG(SSH_D_FAIL,
+ ("ip_route_input failed. (0x%08x -> 0x%08x)",
+ iph->saddr, iph->daddr));
+
+ SSH_DEBUG(SSH_D_NICETOKNOW,
+ ("dst 0x%08x src 0x%08x iif %d[%s] proto %d tos 0x%02x "
+ "fwmark 0x%x",
+ iph->daddr, saddr, (dev ? dev->ifindex : -1),
+ (dev ? dev->name : "none"), iph->protocol, tos,
+ SSH_SKB_MARK(skbp)));
+
+ /* Release netdev reference */
+ ssh_interceptor_release_netdev(dev);
+
+ return FALSE;
+ }
+
+ /* Write original IP protocol back to skb */
+ if (ipproto)
+ iph->protocol = ipproto;
+
+#if (SSH_INTERCEPTOR_NUM_EXTENSION_SELECTORS > 0)
+#ifdef SSH_LINUX_FWMARK_EXTENSION_SELECTOR
+ /* Write original fwmark back to skb */
+ if (fwmark)
+ SSH_SKB_MARK(skbp) = fwmark;
+#endif /* SSH_LINUX_FWMARK_EXTENSION_SELECTOR */
+#endif /* (SSH_INTERCEPTOR_NUM_EXTENSION_SELECTORS > 0) */
+
+ /* Release netdev reference */
+ ssh_interceptor_release_netdev(dev);
+ }
+
+ else
+ {
+ struct rtable *rt;
+ struct flowi rt_key;
+
+ if ((route_selector & SSH_INTERCEPTOR_ROUTE_KEY_FLAG_LOCAL_SRC) == 0)
+ route_selector &= ~SSH_INTERCEPTOR_ROUTE_KEY_SRC;
+
+ memset(&rt_key, 0, sizeof(rt_key));
+
+ rt_key.fl4_dst = iph->daddr;
+ if (route_selector & SSH_INTERCEPTOR_ROUTE_KEY_SRC)
+ rt_key.fl4_src = iph->saddr;
+ if (route_selector & SSH_INTERCEPTOR_ROUTE_KEY_OUT_IFNUM)
+ rt_key.oif = (skbp->dev ? skbp->dev->ifindex : 0);
+ if (route_selector & SSH_INTERCEPTOR_ROUTE_KEY_IPPROTO)
+ rt_key.proto = iph->protocol;
+ if (route_selector & SSH_INTERCEPTOR_ROUTE_KEY_IP4_TOS)
+ rt_key.fl4_tos = RT_TOS(iph->tos);
+ rt_key.fl4_scope = RT_SCOPE_UNIVERSE;
+
+#if (SSH_INTERCEPTOR_NUM_EXTENSION_SELECTORS > 0)
+#ifdef SSH_LINUX_FWMARK_EXTENSION_SELECTOR
+ if (route_selector & SSH_INTERCEPTOR_ROUTE_KEY_EXTENSION)
+ {
+#ifdef LINUX_HAS_SKB_MARK
+ rt_key.mark = SSH_SKB_MARK(skbp);
+#else /* LINUX_HAS_SKB_MARK */
+#ifdef CONFIG_IP_ROUTE_FWMARK
+ rt_key.fl4_fwmark = SSH_SKB_MARK(skbp);
+#endif /* CONFIG_IP_ROUTE_FWMARK */
+#endif /* LINUX_HAS_SKB_MARK */
+ }
+#endif /* SSH_LINUX_FWMARK_EXTENSION_SELECTOR */
+#endif /* (SSH_INTERCEPTOR_NUM_EXTENSION_SELECTORS > 0) */
+
+ rval = interceptor_ip4_route_output(&rt, &rt_key);
+ if (rval < 0)
+ {
+ SSH_DEBUG(SSH_D_FAIL,
+ ("ip_route_output_key failed (0x%08x -> 0x%08x): %d",
+ iph->saddr, iph->daddr, rval));
+
+ SSH_DEBUG(SSH_D_NICETOKNOW,
+ ("dst 0x%08x src 0x%08x oif %d[%s] proto %d tos 0x%02x"
+ "fwmark 0x%x",
+ iph->daddr,
+ ((route_selector & SSH_INTERCEPTOR_ROUTE_KEY_SRC) ?
+ iph->saddr : 0),
+ ((route_selector & SSH_INTERCEPTOR_ROUTE_KEY_OUT_IFNUM) ?
+ rt_key.oif : -1),
+ ((route_selector & SSH_INTERCEPTOR_ROUTE_KEY_OUT_IFNUM) ?
+ (skbp->dev ? skbp->dev->name : "none") : "none"),
+ ((route_selector & SSH_INTERCEPTOR_ROUTE_KEY_IPPROTO) ?
+ iph->protocol : -1),
+ ((route_selector & SSH_INTERCEPTOR_ROUTE_KEY_IP4_TOS) ?
+ iph->tos : 0),
+ ((route_selector & SSH_INTERCEPTOR_ROUTE_KEY_EXTENSION) ?
+ SSH_SKB_MARK(skbp) : 0)));
+
+ return FALSE;
+ }
+
+ /* Make a new dst because we just rechecked the route. */
+ SSH_SKB_DST_SET(skbp, dst_clone(&SSH_RT_DST(rt)));
+
+ /* Release the routing table entry ; otherwise a memory leak occurs
+ in the route entry table. */
+ ip_rt_put(rt);
+ }
+
+ SSH_ASSERT(SSH_SKB_DST(skbp) != NULL);
+
+#ifdef SSH_IPSEC_IP_ONLY_INTERCEPTOR
+#ifdef LINUX_FRAGMENTATION_AFTER_NF_POST_ROUTING
+ if (route_selector & SSH_INTERCEPTOR_ROUTE_KEY_FLAG_TRANSFORM_APPLIED)
+ {
+ /* Check if need to create a child dst_entry with interface MTU. */
+ if (SSH_SKB_DST(skbp)->child == NULL)
+ {
+ if (interceptor_route_create_child_dst(SSH_SKB_DST(skbp), FALSE)
+ == NULL)
+ {
+ SSH_DEBUG(SSH_D_ERROR,
+ ("Could not create child dst_entry for dst %p",
+ SSH_SKB_DST(skbp)));
+ return FALSE;
+ }
+ }
+
+ /* Pop dst stack and use the child entry with interface MTU
+ for sending the packet. */
+#ifdef LINUX_DST_POP_IS_SKB_DST_POP
+ SSH_SKB_DST_SET(skbp, dst_clone(skb_dst_pop(skbp)));
+#else /* LINUX_DST_POP_IS_SKB_DST_POP */
+ SSH_SKB_DST_SET(skbp, dst_pop(SSH_SKB_DST(skbp)));
+#endif /*LINUX_DST_POP_IS_SKB_DST_POP */
+ }
+#endif /* LINUX_FRAGMENTATION_AFTER_NF_POST_ROUTING */
+#endif /* SSH_IPSEC_IP_ONLY_INTERCEPTOR */
+
+ return TRUE;
+}
+
+
+#ifdef SSH_LINUX_INTERCEPTOR_IPV6
+
+/* Route IPv6 packet 'skbp', using the route key selectors in
+ 'route_selector' and the interface number 'ifnum_in'. */
+Boolean
+ssh_interceptor_reroute_skb_ipv6(SshInterceptor interceptor,
+ struct sk_buff *skbp,
+ SshUInt16 route_selector,
+ SshUInt32 ifnum_in)
+{
+ /* we do not need a socket, only fake flow */
+ struct flowi rt_key;
+ struct dst_entry *dst;
+ struct ipv6hdr *iph6;
+
+ iph6 = (struct ipv6hdr *) SSH_SKB_GET_NETHDR(skbp);
+ if (iph6 == NULL)
+ {
+ SSH_DEBUG(SSH_D_ERROR, ("Could not access IPv6 header"));
+ return FALSE;
+ }
+
+ memset(&rt_key, 0, sizeof(rt_key));
+
+ rt_key.fl6_dst = iph6->daddr;
+
+ if (route_selector & SSH_INTERCEPTOR_ROUTE_KEY_SRC)
+ rt_key.fl6_src = iph6->saddr;
+
+ if (route_selector & SSH_INTERCEPTOR_ROUTE_KEY_OUT_IFNUM)
+ {
+ rt_key.oif = (skbp->dev ? skbp->dev->ifindex : 0);
+ SSH_LINUX_ASSERT_IFNUM(rt_key.oif);
+ }
+
+ dst = interceptor_ip6_route_output(&rt_key);
+ if (dst == NULL || dst->error != 0)
+ {
+ if (dst != NULL)
+ dst_release(dst);
+
+ SSH_DEBUG(SSH_D_FAIL,
+ ("ip6_route_output failed."));
+
+ SSH_DEBUG_HEXDUMP(SSH_D_NICETOKNOW,
+ ("dst "),
+ (unsigned char *) &iph6->daddr, sizeof(iph6->daddr));
+ SSH_DEBUG_HEXDUMP(SSH_D_NICETOKNOW,
+ ("src "),
+ (unsigned char *) &iph6->saddr, sizeof(iph6->saddr));
+ SSH_DEBUG(SSH_D_NICETOKNOW,
+ ("oif %d[%s]",
+ (skbp->dev ? skbp->dev->ifindex : -1),
+ (skbp->dev ? skbp->dev->name : "none")));
+ return FALSE;
+ }
+ if (SSH_SKB_DST(skbp))
+ dst_release(SSH_SKB_DST(skbp));
+
+#ifdef SSH_IPSEC_IP_ONLY_INTERCEPTOR
+#ifdef LINUX_FRAGMENTATION_AFTER_NF6_POST_ROUTING
+ if (route_selector & SSH_INTERCEPTOR_ROUTE_KEY_FLAG_TRANSFORM_APPLIED)
+ {
+ SSH_DEBUG(SSH_D_LOWOK,
+ ("Creating a new child entry for dst %p, child %p %lu",
+ dst, dst->child, skbp->_skb_refdst));
+
+ /* Check if need to create a child dst_entry with interface MTU. */
+ if (dst->child == NULL)
+ {
+ if (interceptor_route_create_child_dst(dst, TRUE) == NULL)
+ {
+ SSH_DEBUG(SSH_D_ERROR,
+ ("Could not create child dst_entry for dst %p",
+ dst));
+
+ dst_release(dst);
+ return FALSE;
+ }
+ }
+
+ SSH_SKB_DST_SET(skbp, dst_clone(dst->child));
+ dst_release(dst);
+ }
+ else
+#endif /* LINUX_FRAGMENTATION_AFTER_NF6_POST_ROUTING */
+#endif /* SSH_IPSEC_IP_ONLY_INTERCEPTOR */
+ {
+ /* No need to clone the dst as ip6_route_output has already taken
+ one for us. */
+ SSH_SKB_DST_SET(skbp, dst);
+ }
+
+ return TRUE;
+}
+#endif /* SSH_LINUX_INTERCEPTOR_IPV6 */