Esempio n. 1
0
/* 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;
}
void
ssh_interceptor_packet_return_dst_entry(SshInterceptor interceptor,
					SshUInt32 dst_entry_id,
					SshInterceptorPacket pp,
					Boolean remove_only)
{
  SshInterceptorInternalPacket ipp = (SshInterceptorInternalPacket)pp;
  SshUInt32 slot = dst_entry_id % SSH_DST_ENTRY_TBL_SIZE;
  SshDstEntry tmp, prev = NULL;

  SSH_DEBUG(SSH_D_MIDOK,
	    ("Returning dst entry ID %lu, pp 0x%p, %lu items in cache, "
	     "update %s",
	     (unsigned long)dst_entry_id, pp,
	     (unsigned long)interceptor->dst_entry_cached_items,
	     remove_only == TRUE ? "no" : "yes"));

  /* Special case, 'real' engine created packets. */
  if (dst_entry_id == 0)
    return;

  SSH_ASSERT(slot < SSH_DST_ENTRY_TBL_SIZE);

  ssh_kernel_mutex_lock(interceptor->dst_entry_cache_lock);
  for (tmp = interceptor->dst_entry_table[slot]; tmp != NULL; tmp = tmp->next)
    {
      /* Do we have a match? */
      if (tmp->dst_entry_id == dst_entry_id)
	{
	  /* Head of list. */
	  if (tmp == interceptor->dst_entry_table[slot])
	    {
	      interceptor->dst_entry_table[slot] = tmp->next;

	      interceptor->dst_entry_cached_items--;
	      ssh_kernel_mutex_unlock(interceptor->dst_entry_cache_lock);

	      if (remove_only == FALSE && pp != NULL)
		SSH_SKB_DST_SET(ipp->skb, tmp->dst_entry);
	      else
		dst_release(tmp->dst_entry);

	      ssh_free(tmp);

	      SSH_DEBUG(SSH_D_NICETOKNOW,
			("Removed cache ID %lu, left %lu items in dst cache",
			 (unsigned long)dst_entry_id,
			 (unsigned long)interceptor->dst_entry_cached_items));

	      return;
	    }

	  /* Any other place in the list. */
	  else
	    {
	      prev->next = tmp->next;

	      interceptor->dst_entry_cached_items--;
	      ssh_kernel_mutex_unlock(interceptor->dst_entry_cache_lock);

	      if (remove_only == FALSE)
		SSH_SKB_DST_SET(ipp->skb, tmp->dst_entry);
	      else
		dst_release(tmp->dst_entry);

	      ssh_free(tmp);

	      SSH_DEBUG(SSH_D_NICETOKNOW,
			("Removed cache ID %lu, left %lu items in dst cache",
			 (unsigned long)dst_entry_id,
			 (unsigned long)interceptor->dst_entry_cached_items));

	      return;
	    }
	}

      prev = tmp;
    }

  SSH_DEBUG(SSH_D_NICETOKNOW,
	    ("Cache ID %lu was not found, left %lu items in dst cache",
	     (unsigned long)dst_entry_id,
	     (unsigned long)interceptor->dst_entry_cached_items));

  ssh_kernel_mutex_unlock(interceptor->dst_entry_cache_lock);
}
Esempio n. 3
0
/* 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);

        /* 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 */
            if (dev)
                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 */
        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)));

            return FALSE;
        }

        /* Make a new dst because we just rechecked the route. */
        SSH_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(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)) == 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. */
        SSH_SKB_DST_SET(skbp, dst_pop(SSH_SKB_DST(skbp)));
    }
#endif /* LINUX_FRAGMENTATION_AFTER_NF_POST_ROUTING */
#endif /* SSH_IPSEC_IP_ONLY_INTERCEPTOR */

    return TRUE;
}
Esempio n. 4
0
/* 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 (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 ? dev->ifindex : -1), (dev ? dev->name : "none"),
               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,
               (rt->u.dst.dev->name ? rt->u.dst.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)) == 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:
    if (dev)
        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;
}