Example #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;
}
/* Cache a dst entry for later purposes. This is required by the
   pass unmodified to work. If we lose the dst entry, we basically
   cannot return the packet as unmodified to the linux. Return 0
   if the caching fails. If it succeeds, return a valid cache ID. */
SshUInt32
ssh_interceptor_packet_cache_dst_entry(SshInterceptor interceptor,
				       SshInterceptorPacket pp)
{
  SshInterceptorInternalPacket ipp = (SshInterceptorInternalPacket)pp;
  SshDstEntry cache_dst;
  SshDstEntry tmp;
  SshUInt32 slot;

  SSH_DEBUG(SSH_D_MIDOK,
	    ("Dst entry cache, caching dst for pp 0x%p, %lu items in cache",
	     pp, (unsigned long)interceptor->dst_entry_cached_items));

  if (ipp->skb == NULL || SSH_SKB_DST(ipp->skb) == NULL)
    return 0;

  cache_dst = ssh_calloc(1, sizeof(SshDstEntryStruct));
  if (cache_dst == NULL)
    return 0;

  cache_dst->allocation_time = jiffies;
  cache_dst->next = NULL;

  cache_dst->dst_entry = SSH_SKB_DST(ipp->skb);
  dst_hold(cache_dst->dst_entry);

  ssh_kernel_mutex_lock(interceptor->dst_entry_cache_lock);

  cache_dst->dst_entry_id = interceptor->dst_entry_id++;
  slot = cache_dst->dst_entry_id % SSH_DST_ENTRY_TBL_SIZE;

  interceptor->dst_entry_cached_items++;

  SSH_ASSERT(slot < SSH_DST_ENTRY_TBL_SIZE);

  /* Head of list. */
  if (interceptor->dst_entry_table[slot] == NULL)
    {
      interceptor->dst_entry_table[slot] = cache_dst;
    }
  else
    {
      /* We do not care about potential collisions. These are highly unlikely
	 to happen and in the end */
      for (tmp = interceptor->dst_entry_table[slot];
	   tmp->next != NULL;
	   tmp = tmp->next)
	SSH_ASSERT(cache_dst->dst_entry_id != tmp->dst_entry_id);

      tmp->next = cache_dst;
    }

  /* Handle special case, the id is overflowing. 0 is used for special
     purposes, i.e. for 'real' engine created packets. */
  if (interceptor->dst_entry_id == 0)
    interceptor->dst_entry_id = 1;

  if (interceptor->dst_entry_cache_timeout_registered == FALSE)
    {
      struct timeval tv;

      SSH_ASSERT(interceptor->dst_entry_cached_items > 0);

      tv.tv_sec = DST_ENTRY_MAX_CACHE_TIME;
      tv.tv_usec = 0;

      init_timer(&interceptor->dst_cache_timer);
      interceptor->dst_cache_timer.expires = jiffies + timeval_to_jiffies(&tv);
      interceptor->dst_cache_timer.data = (unsigned long)interceptor;
      interceptor->dst_cache_timer.function =
	ssh_interceptor_dst_entry_cache_timeout;
      add_timer(&interceptor->dst_cache_timer);

      interceptor->dst_entry_cache_timeout_registered = TRUE;
    }

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

  ssh_kernel_mutex_unlock(interceptor->dst_entry_cache_lock);

  return cache_dst->dst_entry_id;
}
Example #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;
}
Example #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;
}