/* 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); } #ifdef LINUX_IP6_ROUTE_OUTPUT_KEY_HAS_NET_ARGUMENT dst = ip6_route_output(&init_net, NULL, &rt_key); #else /* LINUX_IP6_ROUTE_OUTPUT_KEY_HAS_NET_ARGUMENT */ dst = ip6_route_output(NULL, &rt_key); #endif /* LINUX_IP6_ROUTE_OUTPUT_KEY_HAS_NET_ARGUMENT */ if (dst == NULL || dst->error != 0) { 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)); SSH_SKB_DST_SET(skbp, dst_clone(dst)); return TRUE; }
/* 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 (skb_dst(skbp)) dst_release(skb_dst(skbp)); 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); /* Clear the IP protocol, if selector does not define it. Ugly, but necessary to make sure the skb gets rerouted like engine expects. */ 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. Ugly, but necessary to make sure the skb gets rerouted like engine expects. */ 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 */ if (dev) ssh_interceptor_release_netdev(dev); /* Note, skb modifications are not un-done as the caller frees the skb. If this is changed then the modifications should be un-done here before returning. */ 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 */ if (dev) 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) */ /* Call ip_route_output */ #ifdef LINUX_IP_ROUTE_OUTPUT_KEY_HAS_NET_ARGUMENT rval = ip_route_output_key(&init_net, &rt, &rt_key); #else /* LINUX_IP_ROUTE_OUTPUT_KEY_HAS_NET_ARGUMENT */ rval = ip_route_output_key(&rt, &rt_key); #endif /* LINUX_IP_ROUTE_OUTPUT_KEY_HAS_NET_ARGUMENT */ 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))); /* Note, skb modifications are not un-done as the caller frees the skb. If this is changed then the modifications should be un-done here before returning. */ return FALSE; } /* Make a new dst because we just rechecked the route. */ skb_dst_set(skbp, dst_clone(&rt->u.dst)); /* Release the routing table entry ; otherwise a memory leak occurs in the route entry table. */ ip_rt_put(rt); } SSH_ASSERT(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 (skb_dst(skbp)->child == NULL) { if (interceptor_route_create_child_dst(skb_dst(skbp)) == NULL) { SSH_DEBUG(SSH_D_ERROR, ("Could not create child dst_entry for dst %p", skb_dst(skbp))); return FALSE; } } /* Pop dst stack and use the child entry with interface MTU for sending the packet. */ skb_dst_set(skbp, dst_pop(skb_dst(skbp))); } #endif /* LINUX_FRAGMENTATION_AFTER_NF_POST_ROUTING */ #endif /* SSH_IPSEC_IP_ONLY_INTERCEPTOR */ return TRUE; }