Ejemplo n.º 1
0
/* Internal mapping from ifnum to net_device. 
   This function will assert that 'ifnum' is a valid 
   SshInterceptorIfnum and that the corresponding net_device
   exists in the interface hashtable. This function will dev_hold()
   the net_device. The caller of this function must release it by 
   calling ssh_interceptor_release_netdev(). If `context_return' is
   not NULL then this sets it to point to the interface context. */
inline struct net_device *
ssh_interceptor_ifnum_to_netdev_ctx(SshInterceptor interceptor,
				    SshUInt32 ifnum,
				    void **context_return)
{
  SshInterceptorInternalInterface iface;
  struct net_device *dev = NULL;

  SSH_LINUX_ASSERT_VALID_IFNUM(ifnum);

  read_lock(&interceptor->nf->if_table_lock);
  for (iface = interceptor->nf->if_hash[ifnum % SSH_LINUX_IFACE_HASH_SIZE];
       iface && iface->ifindex != ifnum;
       iface = iface->next)
    ;
  if (iface)
    {
      SSH_ASSERT(iface->generation == interceptor->nf->if_generation);
      dev = iface->dev;
      SSH_ASSERT(dev != NULL);
      dev_hold(dev);
      if (context_return != NULL)
	*context_return = iface->context;
    }
  read_unlock(&interceptor->nf->if_table_lock);
  
  return dev;
}
Ejemplo n.º 2
0
/* Set the interface context. */
inline Boolean
ssh_interceptor_iface_set_context(SshInterceptor interceptor,
				  SshUInt32 ifnum,
				  void *context)
{
  SshInterceptorInternalInterface iface;
  Boolean ret = FALSE;

  SSH_LINUX_ASSERT_VALID_IFNUM(ifnum);

  read_lock(&interceptor->nf->if_table_lock);
  for (iface = interceptor->nf->if_hash[ifnum % SSH_LINUX_IFACE_HASH_SIZE];
       iface && iface->ifindex != ifnum;
       iface = iface->next)
    ;
  if (iface)
    {
      SSH_ASSERT(iface->generation == interceptor->nf->if_generation);
      iface->context = context;
      ret = TRUE;
    }
  read_unlock(&interceptor->nf->if_table_lock);

  return ret;
}
/* Netdevice low level transmit callback function. */
static int
ssh_virtual_adapter_xmit(struct sk_buff *skbp, 
			 struct net_device *dev)
{
  struct net_device_stats *stats;
  SshInterceptorInternalPacket ipp;
  SshInterceptor interceptor;
  SshVirtualAdapter adapter;
  SshInterceptorIfnum ifnum_in;
  SshVirtualAdapterPacketCB packet_cb;
  void *adapter_context;

  SSH_ASSERT(skbp != NULL && dev != NULL);

  interceptor = ssh_interceptor_context;
  
  ssh_kernel_mutex_lock(interceptor->interceptor_lock);
  adapter = (SshVirtualAdapter) SSH_LINUX_NET_DEVICE_PRIV(dev);
  if (adapter == NULL)
    {
      /* Virtual adapter is not attached. */
      ssh_kernel_mutex_unlock(interceptor->interceptor_lock);
      SSH_DEBUG(SSH_D_NICETOKNOW, 
		("Device %d [%s] is not attached to a SshVirtualAdapter",
		 dev->ifindex, dev->name));
    discard:
      /* Silently discard the packet. */
      dev_kfree_skb_any(skbp);
      return NET_XMIT_SUCCESS;
    }

  /* Update statistics */
  stats = ssh_virtual_adapter_get_stats(dev);
  SSH_ASSERT(stats != NULL);
  stats->tx_packets++;

  if (!adapter->initialized || !adapter->packet_cb)
    {
      /* This is not very uncommon. Packets end up here if the virtual
	 adapter is set up when policymanager is not running. We discard
	 the packets silently, as otherwise the stack will attempt to 
	 transmit IPv6 IGMP messages indefinitely. */
      ssh_kernel_mutex_unlock(interceptor->interceptor_lock);
      SSH_DEBUG(SSH_D_LOWOK,
		("Virtual adapter %d [%s] not initialized / no cb.",
		 adapter->dev->ifindex, adapter->dev->name));
      goto discard;
    }

  ifnum_in = (SshInterceptorIfnum) dev->ifindex;
  SSH_LINUX_ASSERT_VALID_IFNUM(ifnum_in);

  /* Pass the packet to the packet callback. */
  ipp = ssh_interceptor_packet_alloc_header(interceptor,
				 SSH_PACKET_FROMPROTOCOL,
				 SSH_PROTOCOL_ETHERNET, 
				 ifnum_in,
				 SSH_INTERCEPTOR_INVALID_IFNUM,
				 skbp,
                                 FALSE, FALSE, TRUE);
  if (ipp == NULL)
    {
      SSH_DEBUG(SSH_D_NICETOKNOW, 
		("Could not allocate packet header, virtual adapter %d [%s]",
		 adapter->dev->ifindex, adapter->dev->name));
      stats->tx_errors++;
      ssh_kernel_mutex_unlock(interceptor->interceptor_lock);
      return NET_XMIT_DROP;
    }

  stats->tx_bytes += ipp->skb->len;

  packet_cb = adapter->packet_cb;
  adapter_context = adapter->adapter_context;

  ssh_kernel_mutex_unlock(interceptor->interceptor_lock);

  SSH_DEBUG(SSH_D_NICETOKNOW, 
	    ("Passing skb %p from virtual adapter %d [%s] to engine", 
	     ipp->skb, (int)ifnum_in, dev->name));
  
  /* Call the callback.  This will eventually free `pp'. */
  (*packet_cb)(interceptor, (SshInterceptorPacket) ipp, adapter_context);
  
  return NET_XMIT_SUCCESS;
}
Ejemplo n.º 4
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 (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;
}
Ejemplo n.º 5
0
/* 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;

  SSH_INTERCEPTOR_STACK_MARK();
  
  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 %@ src %@ ifnum %d",
	     ssh_ipaddr_render, &key->dst,
	     ssh_ipaddr_render, 
	     ((selector & SSH_INTERCEPTOR_ROUTE_KEY_SRC) ? 
	      &key->src : NULL),
	     ((selector & SSH_INTERCEPTOR_ROUTE_KEY_OUT_IFNUM) ? 
	      key->ifnum : -1)));

  /* Perform route lookup */
  /* we do not need a socket, only fake flow */
#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)
    {
      goto fail;
    }
  else if (dst->error != 0)
    {
      error = dst->error;
      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 */

  if (rt->rt6i_nexthop)
    SSH_IP6_DECODE(result->gw, &rt->rt6i_nexthop->primary_key);
  else
      SSH_IP6_DECODE(result->gw, &rt_key.fl6_dst.s6_addr);
  
  result->mtu = SSH_LINUX_DST_MTU(&rt->u.dst); 
 
  /* 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: %@ via %@ ifnum %d[%s] mtu %d flags 0x%08x[%s%s]",
	     ssh_ipaddr_render, &key->dst,
	     ssh_ipaddr_render, result->gw, result->ifnum,
	     (dst->dev ? dst->dev->name : "none"),
	     result->mtu, rt6i_flags,
	     ((rt6i_flags & RTF_UP) ? "up " : ""),
	     ((rt6i_flags & RTF_REJECT) ? "reject" : "")
	     ));

  /* 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 %@ failed with code %d",
	     ssh_ipaddr_render, &key->dst, 
	     error));
  return FALSE;
}
Ejemplo n.º 6
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;
#endif /* DEBUG_LIGHT */

  SSH_INTERCEPTOR_STACK_MARK();

  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); 

  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 %@ src %@ ifnum %d[%s] ipproto %d tos 0x%02x fwmark 0x%x",
	     ssh_ipaddr_render, &key->dst,
	     ssh_ipaddr_render, 
	     ((selector & SSH_INTERCEPTOR_ROUTE_KEY_SRC) ? 
	      &key->src : 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 || skb_dst(skbp) == NULL)
    {      
      dev_kfree_skb(skbp);
      goto fail;
    }

  /* Get the gateway, mtu and ifnum */
  rt = (struct rtable *) skb_dst(skbp);
  SSH_IP4_DECODE(result->gw, &rt->rt_gateway);
  result->mtu = SSH_LINUX_DST_MTU(skb_dst(skbp));
  result->ifnum = 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 %@ via %@ ifnum %d[%s] mtu %d type %s [%d]",
	     ssh_ipaddr_render, &key->dst,
	     ssh_ipaddr_render, result->gw, 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)
      && skb_dst(skbp)->child == NULL)
    {
      if (interceptor_route_create_child_dst(skb_dst(skbp)) == NULL)
	SSH_DEBUG(SSH_D_FAIL, ("Could not create child dst_entry for dst %p",
			       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(skb_dst(skbp));
  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 %@ failed with code %d",
	     ssh_ipaddr_render, &key->dst, rval));
  return FALSE;
}
Ejemplo n.º 7
0
/* 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;
#endif /* DEBUG_LIGHT */

  SSH_INTERCEPTOR_STACK_MARK();

  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 %@ src %@ ifnum %d ipproto %d tos 0x%02x fwmark 0x%x",
	     ssh_ipaddr_render, &key->dst,
	     ssh_ipaddr_render, 
	     ((selector & SSH_INTERCEPTOR_ROUTE_KEY_SRC) ? 
	      &key->src : NULL),
	     ((selector & SSH_INTERCEPTOR_ROUTE_KEY_OUT_IFNUM) ? 
	      key->ifnum : -1),
	     ((selector & SSH_INTERCEPTOR_ROUTE_KEY_IPPROTO) ? 
	      key->ipproto : -1),
	     ((selector & SSH_INTERCEPTOR_ROUTE_KEY_IP4_TOS) ? 
	      key->nh.ip4.tos : 0),
	     ((selector & SSH_INTERCEPTOR_ROUTE_KEY_EXTENSION) ? 
	      fwmark : 0)));

  /* Perform route lookup */

#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)
    {
      goto fail;
    }

  /* Get the gateway, mtu and ifnum */

  SSH_IP4_DECODE(result->gw, &rt->rt_gateway);
  result->mtu = SSH_LINUX_DST_MTU(&rt->u.dst);
  result->ifnum = rt->u.dst.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 %@ via %@ ifnum %d[%s] mtu %d type %s [%d]",
	     ssh_ipaddr_render, &key->dst,
	     ssh_ipaddr_render, result->gw, 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)
      && rt->u.dst.child == NULL)
    {
      if (interceptor_route_create_child_dst(&rt->u.dst) == NULL)
	SSH_DEBUG(SSH_D_FAIL, ("Could not create child dst_entry for dst %p",
			       &rt->u.dst));
    }
#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 %@ failed with code %d",
	     ssh_ipaddr_render, &key->dst, rval));

  return FALSE;
}
Ejemplo n.º 8
0
/* The interceptor_update_interfaces() function traverses the kernels
   list of interfaces, grabs a refcnt for each one, and updates the
   interceptors 'ifnum->net_device' cache (optimizing away the need to
   grab a lock, traverse dev_base linked list, unlock, for each
   packet). 
   
   This function grabs 'if_table_lock' (for writing) and dev_base lock. */
static void
ssh_interceptor_update_interfaces(SshInterceptor interceptor)
{
  SshInterceptorInternalInterface iface, iface_prev, iface_next;
  struct net_device *dev;
  SshUInt32 i, hashvalue, ifindex;

  /* WARNING: TWO LOCKS HELD AT THE SAME TIME. BE CAREFUL!
     dev_base_lock MUST be held to ensure integrity during traversal
     of list of interfaces in kernel. */
  SSH_LOCK_LINUX_DEV_LIST();

  /* Grab 'if_table_lock' for modifying the interface table. */
  write_lock(&interceptor->nf->if_table_lock);

  /* Increment 'if_generation' */
  interceptor->nf->if_generation++;
  if (interceptor->nf->if_generation == 0)
    interceptor->nf->if_generation++; /* Handle wrapping */

  /* Traverse net_device list, add new interfaces to hashtable,
     and mark existing entries up-to-date. */
  for (dev = SSH_FIRST_NET_DEVICE(); 
       dev != NULL; 
       dev = SSH_NEXT_NET_DEVICE(dev))
    {
      ifindex = (SshUInt32) dev->ifindex;

      /* Ignore the loopback device. */
      if (dev->flags & IFF_LOOPBACK)
	continue;

      /* Ignore interfaces that collide with SSH_INTERCEPTOR_INVALID_IFNUM */
      if (ifindex == SSH_INTERCEPTOR_INVALID_IFNUM)
	{



	  ssh_warning("Interface index collides with "
		      "SSH_INTERCEPTOR_INVALID_IFNUM, "
		      "ignoring interface %d[%s]",
		      ifindex,
		      (dev->name ? dev->name : "<none>"));
	  continue;
	}



















      /* Assert that 'dev->ifindex' is otherwise valid. */ 
      SSH_LINUX_ASSERT_VALID_IFNUM(ifindex);

      /* Lookup interface from the hashtable. */
      for (iface = 
	     interceptor->nf->if_hash[ifindex % SSH_LINUX_IFACE_HASH_SIZE];
	   iface != NULL && iface->ifindex != ifindex;
	   iface = iface->next)
	;

      /* Interface found */
      if (iface)
	{
	  if (iface->dev == dev)
	    {
	      SSH_DEBUG(SSH_D_NICETOKNOW, 
			("Old interface %d[%s]", 
			 ifindex, (dev->name ? dev->name : "<none>")));
	      
	      /* Mark up-to-date. */
	      iface->generation = interceptor->nf->if_generation;
	    }

	  /* Interface index matches, but net_device has changed. */
	  else 
	    {
	      SSH_DEBUG(SSH_D_NICETOKNOW, 
			("Changed interface %d[%s] (from %d[%s])", 
			 ifindex,
			 (dev->name ? dev->name : "<none>"),
			 iface->ifindex,
			 (iface->dev->name ? iface->dev->name : "<none>")));

	      /* Release old net_device. */
	      SSH_ASSERT(iface->dev != NULL);
	      dev_put(iface->dev);
	      /* Hold new net_device. */
	      dev_hold(dev);
	      wmb(); /* Make sure assignments are not reordered. */
	      iface->dev = dev;
	      iface->ifindex = ifindex;
	      iface->context = NULL;
	      /* Mark up-to-date. */
	      iface->generation = interceptor->nf->if_generation;
	    }
	}

      /* Interface not found */
      else
	{
	  SSH_DEBUG(SSH_D_NICETOKNOW, 
		    ("New interface %d[%s]", 
		     ifindex, (dev->name ? dev->name : "<none>")));

	  /* Allocate new interface entry */
	  iface = ssh_interceptor_alloc_iface(interceptor);
	  if (iface)
	    {
	      /* Hold new net_device. */
	      dev_hold(dev);
	      /* Fill interface entry. */
	      iface->ifindex = ifindex;
	      iface->dev = dev;
	      iface->context = NULL;
	      /* Mark up-to-date */
	      iface->generation = interceptor->nf->if_generation;
	      /* Add entry to hashtable. */
	      hashvalue = iface->ifindex % SSH_LINUX_IFACE_HASH_SIZE;
	      iface->next = interceptor->nf->if_hash[hashvalue];
	      wmb();
	      interceptor->nf->if_hash[hashvalue] = iface;
	    }
	  else
	    {
	      SSH_DEBUG(SSH_D_FAIL, 
			("Could not allocate memory for new interface %d[%s]",
			 ifindex, (dev->name ? dev->name : "<none>")));
	    }
	}
    }

  /* Remove old interfaces from the table */
  for (i = 0; i < SSH_LINUX_IFACE_HASH_SIZE; i++)
    {
      iface_prev = NULL;
      for (iface = interceptor->nf->if_hash[i];
	   iface != NULL;
	   iface = iface_next)
	{
	  if (iface->generation != 0 &&
	      iface->generation != interceptor->nf->if_generation)
	    {
	      SSH_DEBUG(SSH_D_NICETOKNOW, 
			("Disappeared interface %d[%s]", 
			 iface->ifindex,
			 (iface->dev->name ? iface->dev->name : "<none>")));

	      /* Release old netdevice */
	      SSH_ASSERT(iface->dev != NULL);
	      dev_put(iface->dev);

#ifdef DEBUG_LIGHT
	      wmb();
	      iface->dev = NULL;
	      iface->ifindex = SSH_INTERCEPTOR_INVALID_IFNUM;	      
#endif /* DEBUG_LIGHT */

	      /* Mark entry freed. */
	      iface->generation = 0;
	      
	      /* Remove entry from hashtable. */
	      if (iface_prev)
		iface_prev->next = iface->next;
	      else
		interceptor->nf->if_hash[i] = iface->next;
	      iface_next = iface->next;
	      iface->next = NULL;
	    }
	  else
	    {
	      iface_prev = iface;
	      iface_next = iface->next;
	    }
	}
    }
  /* Unlock if_table_lock */
  write_unlock(&interceptor->nf->if_table_lock);

  /* Release dev_base_lock. */
  SSH_UNLOCK_LINUX_DEV_LIST();

  /* Notify changes to engine */
  if (interceptor->engine != NULL && interceptor->engine_open == TRUE)
    {
      local_bh_disable();
      ssh_interceptor_send_interfaces(interceptor);
      local_bh_enable();
    }

  return;
}