mroute.c
6fbf66fa
 /*
  *  OpenVPN -- An application to securely tunnel IP networks
  *             over a single TCP/UDP port, with support for SSL/TLS-based
  *             session authentication and key exchange,
  *             packet encryption, packet authentication, and
  *             packet compression.
  *
d7fa38f2
  *  Copyright (C) 2002-2009 OpenVPN Technologies, Inc. <sales@openvpn.net>
6fbf66fa
  *
  *  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.
  *
  *  This program is distributed in the hope that it will be useful,
  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  *  GNU General Public License for more details.
  *
  *  You should have received a copy of the GNU General Public License
  *  along with this program (see the file COPYING included with this
  *  distribution); if not, write to the Free Software Foundation, Inc.,
  *  59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  */
 
 #include "syshead.h"
 
 #if P2MP_SERVER
 
 #include "mroute.h"
 #include "proto.h"
 #include "error.h"
 #include "socket.h"
 
 #include "memdbg.h"
 
 void
 mroute_addr_init (struct mroute_addr *addr)
 {
   CLEAR (*addr);
 }
 
 /*
  * Ethernet multicast addresses.
  */
 
 static inline bool
 is_mac_mcast_addr (const uint8_t *mac)
 {
   return (bool) mac[0] & 1;
 }
 
 static inline bool
 is_mac_mcast_maddr (const struct mroute_addr *addr)
 {
   return (addr->type & MR_ADDR_MASK) == MR_ADDR_ETHER && is_mac_mcast_addr (addr->addr); 
 }
 
 /*
  * Don't learn certain addresses.
  */
 bool
 mroute_learnable_address (const struct mroute_addr *addr)
 {
   int i;
   bool not_all_zeros = false;
   bool not_all_ones = false;
 
   for (i = 0; i < addr->len; ++i)
     {
       int b = addr->addr[i];
       if (b != 0x00)
 	not_all_zeros = true;
       if (b != 0xFF)
 	not_all_ones = true;
     }
   return not_all_zeros && not_all_ones && !is_mac_mcast_maddr (addr);
 }
 
90efcacb
 static inline void
 mroute_get_in_addr_t (struct mroute_addr *ma, const in_addr_t src, unsigned int mask)
 {
   if (ma)
     {
       ma->type = MR_ADDR_IPV4 | mask;
       ma->netbits = 0;
       ma->len = 4;
       *(in_addr_t*)ma->addr = src;
     }
 }
 
 static inline bool
 mroute_is_mcast (const in_addr_t addr)
 {
   return ((addr & htonl(IP_MCAST_SUBNET_MASK)) == htonl(IP_MCAST_NETWORK));
 }
 
 #ifdef ENABLE_PF
 
 static unsigned int
 mroute_extract_addr_arp (struct mroute_addr *src,
 			 struct mroute_addr *dest,
 			 const struct buffer *buf)
 {
   unsigned int ret = 0;
   if (BLEN (buf) >= (int) sizeof (struct openvpn_arp))
     {
       const struct openvpn_arp *arp = (const struct openvpn_arp *) BPTR (buf);
       if (arp->mac_addr_type == htons(0x0001)
 	  && arp->proto_addr_type == htons(0x0800)
 	  && arp->mac_addr_size == 0x06
 	  && arp->proto_addr_size == 0x04)
 	{
 	  mroute_get_in_addr_t (src, arp->ip_src, MR_ARP);
 	  mroute_get_in_addr_t (dest, arp->ip_dest, MR_ARP);
 
 	  /* multicast packet? */
 	  if (mroute_is_mcast (arp->ip_dest))
 	    ret |= MROUTE_EXTRACT_MCAST;
 
 	  ret |= MROUTE_EXTRACT_SUCCEEDED;
 	}
     }
   return ret;
 }
 
 #endif
 
6fbf66fa
 unsigned int
90efcacb
 mroute_extract_addr_ipv4 (struct mroute_addr *src,
 			  struct mroute_addr *dest,
 			  const struct buffer *buf)
6fbf66fa
 {
   unsigned int ret = 0;
90efcacb
   if (BLEN (buf) >= 1)
6fbf66fa
     {
90efcacb
       switch (OPENVPN_IPH_GET_VER (*BPTR(buf)))
6fbf66fa
 	{
90efcacb
 	case 4:
 	  if (BLEN (buf) >= (int) sizeof (struct openvpn_iphdr))
6fbf66fa
 	    {
90efcacb
 	      const struct openvpn_iphdr *ip = (const struct openvpn_iphdr *) BPTR (buf);
 
 	      mroute_get_in_addr_t (src, ip->saddr, 0);
 	      mroute_get_in_addr_t (dest, ip->daddr, 0);
 
 	      /* multicast packet? */
 	      if (mroute_is_mcast (ip->daddr))
 		ret |= MROUTE_EXTRACT_MCAST;
 
 	      /* IGMP message? */
 	      if (ip->protocol == OPENVPN_IPPROTO_IGMP)
 		ret |= MROUTE_EXTRACT_IGMP;
 
 	      ret |= MROUTE_EXTRACT_SUCCEEDED;
6fbf66fa
 	    }
90efcacb
 	  break;
 	case 6:
 	  {
 	    msg (M_WARN, "Need IPv6 code in mroute_extract_addr_from_packet"); 
 	    break;
 	  }
6fbf66fa
 	}
     }
90efcacb
   return ret;
 }
 
 unsigned int
 mroute_extract_addr_ether (struct mroute_addr *src,
 			   struct mroute_addr *dest,
 			   struct mroute_addr *esrc,
 			   struct mroute_addr *edest,
 			   const struct buffer *buf)
 {
   unsigned int ret = 0;
   if (BLEN (buf) >= (int) sizeof (struct openvpn_ethhdr))
6fbf66fa
     {
90efcacb
       const struct openvpn_ethhdr *eth = (const struct openvpn_ethhdr *) BPTR (buf);
       if (src)
6fbf66fa
 	{
90efcacb
 	  src->type = MR_ADDR_ETHER;
 	  src->netbits = 0;
 	  src->len = 6;
 	  memcpy (src->addr, eth->source, 6);
 	}
       if (dest)
 	{
 	  dest->type = MR_ADDR_ETHER;
 	  dest->netbits = 0;
 	  dest->len = 6;
 	  memcpy (dest->addr, eth->dest, 6);
 
 	  /* ethernet broadcast/multicast packet? */
 	  if (is_mac_mcast_addr (eth->dest))
 	    ret |= MROUTE_EXTRACT_BCAST;
 	}
 	  
       ret |= MROUTE_EXTRACT_SUCCEEDED;
 
 #ifdef ENABLE_PF
       if (esrc || edest)
 	{
 	  struct buffer b = *buf;
 	  if (buf_advance (&b, sizeof (struct openvpn_ethhdr)))
6fbf66fa
 	    {
90efcacb
 	      switch (ntohs (eth->proto))
 		{
 		case OPENVPN_ETH_P_IPV4:
 		  ret |= (mroute_extract_addr_ipv4 (esrc, edest, &b) << MROUTE_SEC_SHIFT);
 		  break;
 		case OPENVPN_ETH_P_ARP:
 		  ret |= (mroute_extract_addr_arp (esrc, edest, &b) << MROUTE_SEC_SHIFT);
 		  break;
 		}
6fbf66fa
 	    }
 	}
90efcacb
 #endif
6fbf66fa
     }
   return ret;
 }
 
 /*
8bc93d7f
  * Translate a struct openvpn_sockaddr (osaddr)
6fbf66fa
  * to a struct mroute_addr (addr).
  */
8bc93d7f
 bool mroute_extract_openvpn_sockaddr (struct mroute_addr *addr,
 				      const struct openvpn_sockaddr *osaddr,
 				      bool use_port)
6fbf66fa
 {
8bc93d7f
   if (osaddr->sa.sin_family == AF_INET)
6fbf66fa
     {
       if (use_port)
 	{
 	  addr->type = MR_ADDR_IPV4 | MR_WITH_PORT;
 	  addr->netbits = 0;
 	  addr->len = 6;
8bc93d7f
 	  memcpy (addr->addr, &osaddr->sa.sin_addr.s_addr, 4);
 	  memcpy (addr->addr + 4, &osaddr->sa.sin_port, 2);
6fbf66fa
 	}
       else
 	{
 	  addr->type = MR_ADDR_IPV4;
 	  addr->netbits = 0;
 	  addr->len = 4;
8bc93d7f
 	  memcpy (addr->addr, &osaddr->sa.sin_addr.s_addr, 4);
6fbf66fa
 	}
       return true;
     }
   return false;
 }
 
 /*
  * Zero off the host bits in an address, leaving
  * only the network bits, using the netbits member of
  * struct mroute_addr as the controlling parameter.
  */
 void
 mroute_addr_mask_host_bits (struct mroute_addr *ma)
 {
   in_addr_t addr = ntohl(*(in_addr_t*)ma->addr);
   ASSERT ((ma->type & MR_ADDR_MASK) == MR_ADDR_IPV4);
   addr &= netbits_to_netmask (ma->netbits);
   *(in_addr_t*)ma->addr = htonl (addr);
 }
 
 /*
  * The mroute_addr hash function takes into account the
  * address type, number of bits in the network address,
  * and the actual address.
  */
 uint32_t
 mroute_addr_hash_function (const void *key, uint32_t iv)
 {
   return hash_func (mroute_addr_hash_ptr ((const struct mroute_addr *) key),
 		    mroute_addr_hash_len ((const struct mroute_addr *) key),
 		    iv);
 }
 
 bool
 mroute_addr_compare_function (const void *key1, const void *key2)
 {
   return mroute_addr_equal ((const struct mroute_addr *) key1,
 			    (const struct mroute_addr *) key2);
 }
 
 const char *
 mroute_addr_print (const struct mroute_addr *ma,
 		   struct gc_arena *gc)
 {
90efcacb
   return mroute_addr_print_ex (ma, MAPF_IA_EMPTY_IF_UNDEF, gc);
 }
 
 const char *
 mroute_addr_print_ex (const struct mroute_addr *ma,
 		      const unsigned int flags,
 		      struct gc_arena *gc)
 {
6fbf66fa
   struct buffer out = alloc_buf_gc (64, gc);
   if (ma)
     {
       struct mroute_addr maddr = *ma;
 
       switch (maddr.type & MR_ADDR_MASK)
 	{
 	case MR_ADDR_ETHER:
 	  buf_printf (&out, "%s", format_hex_ex (ma->addr, 6, 0, 1, ":", gc)); 
 	  break;
 	case MR_ADDR_IPV4:
 	  {
 	    struct buffer buf;
 	    in_addr_t addr;
 	    int port;
 	    bool status;
 	    buf_set_read (&buf, maddr.addr, maddr.len);
 	    addr = buf_read_u32 (&buf, &status);
 	    if (status)
 	      {
90efcacb
 		if ((flags & MAPF_SHOW_ARP) && (maddr.type & MR_ARP))
 		  buf_printf (&out, "ARP/");
 		buf_printf (&out, "%s", print_in_addr_t (addr, (flags & MAPF_IA_EMPTY_IF_UNDEF) ? IA_EMPTY_IF_UNDEF : 0, gc));
6fbf66fa
 		if (maddr.type & MR_WITH_NETBITS)
90efcacb
 		  {
 		    if (flags & MAPF_SUBNET)
 		      {
 			const in_addr_t netmask = netbits_to_netmask (maddr.netbits);
 			buf_printf (&out, "/%s", print_in_addr_t (netmask, 0, gc));
 		      }
 		    else
 		      buf_printf (&out, "/%d", maddr.netbits);
 		  }
6fbf66fa
 	      }
 	    if (maddr.type & MR_WITH_PORT)
 	      {
 		port = buf_read_u16 (&buf);
 		if (port >= 0)
 		  buf_printf (&out, ":%d", port);
 	      }
 	  }
 	  break;
 	case MR_ADDR_IPV6:
 	  buf_printf (&out, "IPV6"); 
 	  break;
 	default:
 	  buf_printf (&out, "UNKNOWN"); 
 	  break;
 	}
       return BSTR (&out);
     }
   else
     return "[NULL]";
 }
 
 /*
  * mroute_helper's main job is keeping track of
  * currently used CIDR netlengths, so we don't
  * have to cycle through all 33.
  */
 
 struct mroute_helper *
 mroute_helper_init (int ageable_ttl_secs)
 {
   struct mroute_helper *mh;
   ALLOC_OBJ_CLEAR (mh, struct mroute_helper);
   /*mutex_init (&mh->mutex);*/
   mh->ageable_ttl_secs = ageable_ttl_secs;
   return mh;
 }
 
 static void
 mroute_helper_regenerate (struct mroute_helper *mh)
 {
   int i, j = 0;
   for (i = MR_HELPER_NET_LEN - 1; i >= 0; --i)
     {
       if (mh->net_len_refcount[i] > 0)
 	mh->net_len[j++] = (uint8_t) i;
     }
   mh->n_net_len = j;
 
 #ifdef ENABLE_DEBUG
   if (check_debug_level (D_MULTI_DEBUG))
     {
       struct gc_arena gc = gc_new ();
       struct buffer out = alloc_buf_gc (256, &gc);
       buf_printf (&out, "MROUTE CIDR netlen:");
       for (i = 0; i < mh->n_net_len; ++i)
 	{
 	  buf_printf (&out, " /%d", mh->net_len[i]);
 	}
       dmsg (D_MULTI_DEBUG, "%s", BSTR (&out));
       gc_free (&gc);
     }
 #endif
 }
 
 void
 mroute_helper_add_iroute (struct mroute_helper *mh, const struct iroute *ir)
 {
   if (ir->netbits >= 0)
     {
       ASSERT (ir->netbits < MR_HELPER_NET_LEN);
       mroute_helper_lock (mh);
       ++mh->cache_generation;
       ++mh->net_len_refcount[ir->netbits];
       if (mh->net_len_refcount[ir->netbits] == 1)
 	mroute_helper_regenerate (mh);
       mroute_helper_unlock (mh);
     }
 }
 
 void
 mroute_helper_del_iroute (struct mroute_helper *mh, const struct iroute *ir)
 {
   if (ir->netbits >= 0)
     {
       ASSERT (ir->netbits < MR_HELPER_NET_LEN);
       mroute_helper_lock (mh);
       ++mh->cache_generation;
       --mh->net_len_refcount[ir->netbits];
       ASSERT (mh->net_len_refcount[ir->netbits] >= 0);
       if (!mh->net_len_refcount[ir->netbits])
 	mroute_helper_regenerate (mh);
       mroute_helper_unlock (mh);
     }
 }
 
 void
 mroute_helper_free (struct mroute_helper *mh)
 {
   /*mutex_destroy (&mh->mutex);*/
   free (mh);
 }
 
 #else
 static void dummy(void) {}
 #endif /* P2MP_SERVER */