src/openvpn/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.
  *
49979459
  *  Copyright (C) 2002-2018 OpenVPN 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.
  *
caa54ac3
  *  You should have received a copy of the GNU General Public License along
  *  with this program; if not, write to the Free Software Foundation, Inc.,
  *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
6fbf66fa
  */
 
c110b289
 #ifdef HAVE_CONFIG_H
 #include "config.h"
 #elif defined(_MSC_VER)
 #include "config-msvc.h"
 #endif
 
6fbf66fa
 #include "syshead.h"
 
 #if P2MP_SERVER
 
 #include "mroute.h"
 #include "proto.h"
 #include "error.h"
 #include "socket.h"
 
 #include "memdbg.h"
 
 void
81d882d5
 mroute_addr_init(struct mroute_addr *addr)
6fbf66fa
 {
81d882d5
     CLEAR(*addr);
6fbf66fa
 }
 
 /*
  * Ethernet multicast addresses.
  */
 
 static inline bool
81d882d5
 is_mac_mcast_addr(const uint8_t *mac)
6fbf66fa
 {
81d882d5
     return (bool) (mac[0] & 1);
6fbf66fa
 }
 
 static inline bool
81d882d5
 is_mac_mcast_maddr(const struct mroute_addr *addr)
6fbf66fa
 {
81d882d5
     return (addr->type & MR_ADDR_MASK) == MR_ADDR_ETHER
            && is_mac_mcast_addr(addr->eth_addr);
6fbf66fa
 }
 
 /*
  * Don't learn certain addresses.
  */
 bool
a19c56db
 mroute_learnable_address(const struct mroute_addr *addr, struct gc_arena *gc)
6fbf66fa
 {
81d882d5
     int i;
a19c56db
     bool all_zeros = true;
     bool all_ones = true;
6fbf66fa
 
81d882d5
     for (i = 0; i < addr->len; ++i)
6fbf66fa
     {
81d882d5
         int b = addr->raw_addr[i];
         if (b != 0x00)
         {
a19c56db
             all_zeros = false;
81d882d5
         }
         if (b != 0xFF)
         {
a19c56db
             all_ones = false;
81d882d5
         }
6fbf66fa
     }
a19c56db
 
     /* only networkss shorter than 8 bits are allowed to be all 0s. */
     if (all_zeros
         && !((addr->type & MR_WITH_NETBITS) && (addr->netbits < 8)))
     {
         msg(D_MULTI_LOW, "Can't learn %s: network is all 0s, but netbits >= 8",
             mroute_addr_print(addr, gc));
         return false;
     }
 
     if (all_ones)
     {
         msg(D_MULTI_LOW, "Can't learn %s: network is all 1s",
             mroute_addr_print(addr, gc));
         return false;
     }
 
     if (is_mac_mcast_maddr(addr))
     {
         msg(D_MULTI_LOW, "Can't learn %s: network is a multicast address",
             mroute_addr_print(addr, gc));
         return false;
     }
 
     return true;
6fbf66fa
 }
 
90efcacb
 static inline void
81d882d5
 mroute_get_in_addr_t(struct mroute_addr *ma, const in_addr_t src, unsigned int mask)
90efcacb
 {
81d882d5
     if (ma)
90efcacb
     {
81d882d5
         ma->type = MR_ADDR_IPV4 | mask;
         ma->netbits = 0;
         ma->len = 4;
         ma->v4.addr = src;
90efcacb
     }
 }
 
512cda46
 static inline void
81d882d5
 mroute_get_in6_addr(struct mroute_addr *ma, const struct in6_addr src, unsigned int mask)
512cda46
 {
81d882d5
     if (ma)
512cda46
     {
81d882d5
         ma->type = MR_ADDR_IPV6 | mask;
         ma->netbits = 0;
         ma->len = 16;
         ma->v6.addr = src;
512cda46
     }
 }
 
90efcacb
 static inline bool
81d882d5
 mroute_is_mcast(const in_addr_t addr)
90efcacb
 {
81d882d5
     return ((addr & htonl(IP_MCAST_SUBNET_MASK)) == htonl(IP_MCAST_NETWORK));
90efcacb
 }
 
81d882d5
 /* RFC 4291, 2.7, "binary 11111111 at the start of an address identifies
512cda46
  *                 the address as being a multicast address"
  */
 static inline bool
81d882d5
 mroute_is_mcast_ipv6(const struct in6_addr addr)
512cda46
 {
81d882d5
     return (addr.s6_addr[0] == 0xff);
512cda46
 }
 
90efcacb
 #ifdef ENABLE_PF
 
 static unsigned int
81d882d5
 mroute_extract_addr_arp(struct mroute_addr *src,
                         struct mroute_addr *dest,
                         const struct buffer *buf)
90efcacb
 {
81d882d5
     unsigned int ret = 0;
     if (BLEN(buf) >= (int) sizeof(struct openvpn_arp))
90efcacb
     {
81d882d5
         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;
         }
90efcacb
     }
81d882d5
     return ret;
90efcacb
 }
 
81d882d5
 #endif /* ifdef ENABLE_PF */
90efcacb
 
6fbf66fa
 unsigned int
3b38c43b
 mroute_extract_addr_ip(struct mroute_addr *src, struct mroute_addr *dest,
                        const struct buffer *buf)
6fbf66fa
 {
81d882d5
     unsigned int ret = 0;
     if (BLEN(buf) >= 1)
6fbf66fa
     {
81d882d5
         switch (OPENVPN_IPH_GET_VER(*BPTR(buf)))
         {
             case 4:
                 if (BLEN(buf) >= (int) sizeof(struct openvpn_iphdr))
                 {
                     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;
                 }
                 break;
 
             case 6:
                 if (BLEN(buf) >= (int) sizeof(struct openvpn_ipv6hdr))
                 {
                     const struct openvpn_ipv6hdr *ipv6 = (const struct openvpn_ipv6hdr *) BPTR(buf);
 #if 0                           /* very basic debug */
                     struct gc_arena gc = gc_new();
                     msg( M_INFO, "IPv6 packet! src=%s, dst=%s",
                          print_in6_addr( ipv6->saddr, 0, &gc ),
                          print_in6_addr( ipv6->daddr, 0, &gc ));
                     gc_free(&gc);
512cda46
 #endif
 
81d882d5
                     mroute_get_in6_addr(src, ipv6->saddr, 0);
                     mroute_get_in6_addr(dest, ipv6->daddr, 0);
 
                     if (mroute_is_mcast_ipv6(ipv6->daddr))
                     {
                         ret |= MROUTE_EXTRACT_MCAST;
                     }
512cda46
 
81d882d5
                     ret |= MROUTE_EXTRACT_SUCCEEDED;
                 }
                 break;
512cda46
 
81d882d5
             default:
                 msg(M_WARN, "IP packet with unknown IP version=%d seen",
                     OPENVPN_IPH_GET_VER(*BPTR(buf)));
         }
6fbf66fa
     }
81d882d5
     return ret;
90efcacb
 }
 
 unsigned int
81d882d5
 mroute_extract_addr_ether(struct mroute_addr *src,
                           struct mroute_addr *dest,
                           struct mroute_addr *esrc,
                           struct mroute_addr *edest,
                           const struct buffer *buf)
90efcacb
 {
81d882d5
     unsigned int ret = 0;
     if (BLEN(buf) >= (int) sizeof(struct openvpn_ethhdr))
6fbf66fa
     {
81d882d5
         const struct openvpn_ethhdr *eth = (const struct openvpn_ethhdr *) BPTR(buf);
         if (src)
         {
             src->type = MR_ADDR_ETHER;
             src->netbits = 0;
             src->len = 6;
             memcpy(src->eth_addr, eth->source, sizeof(dest->eth_addr));
         }
         if (dest)
         {
             dest->type = MR_ADDR_ETHER;
             dest->netbits = 0;
             dest->len = 6;
             memcpy(dest->eth_addr, eth->dest, sizeof(dest->eth_addr));
 
             /* ethernet broadcast/multicast packet? */
             if (is_mac_mcast_addr(eth->dest))
             {
                 ret |= MROUTE_EXTRACT_BCAST;
             }
         }
 
         ret |= MROUTE_EXTRACT_SUCCEEDED;
90efcacb
 
 #ifdef ENABLE_PF
81d882d5
         if (esrc || edest)
         {
             struct buffer b = *buf;
             if (buf_advance(&b, sizeof(struct openvpn_ethhdr)))
             {
                 switch (ntohs(eth->proto))
                 {
                     case OPENVPN_ETH_P_IPV4:
3b38c43b
                         ret |= (mroute_extract_addr_ip(esrc, edest, &b) << MROUTE_SEC_SHIFT);
81d882d5
                         break;
 
                     case OPENVPN_ETH_P_ARP:
                         ret |= (mroute_extract_addr_arp(esrc, edest, &b) << MROUTE_SEC_SHIFT);
                         break;
                 }
             }
         }
90efcacb
 #endif
6fbf66fa
     }
81d882d5
     return ret;
6fbf66fa
 }
 
 /*
8bc93d7f
  * Translate a struct openvpn_sockaddr (osaddr)
6fbf66fa
  * to a struct mroute_addr (addr).
  */
81d882d5
 bool
 mroute_extract_openvpn_sockaddr(struct mroute_addr *addr,
                                 const struct openvpn_sockaddr *osaddr,
                                 bool use_port)
6fbf66fa
 {
81d882d5
     switch (osaddr->addr.sa.sa_family)
6fbf66fa
     {
81d882d5
         case AF_INET:
         {
             if (use_port)
             {
                 addr->type = MR_ADDR_IPV4 | MR_WITH_PORT;
                 addr->netbits = 0;
                 addr->len = 6;
                 addr->v4.addr = osaddr->addr.in4.sin_addr.s_addr;
                 addr->v4.port = osaddr->addr.in4.sin_port;
             }
             else
             {
                 addr->type = MR_ADDR_IPV4;
                 addr->netbits = 0;
                 addr->len = 4;
                 addr->v4.addr = osaddr->addr.in4.sin_addr.s_addr;
             }
             return true;
         }
 
         case AF_INET6:
             if (use_port)
             {
                 addr->type = MR_ADDR_IPV6 | MR_WITH_PORT;
                 addr->netbits = 0;
                 addr->len = 18;
                 addr->v6.addr = osaddr->addr.in6.sin6_addr;
                 addr->v6.port = osaddr->addr.in6.sin6_port;
             }
             else
             {
                 addr->type = MR_ADDR_IPV6;
                 addr->netbits = 0;
                 addr->len = 16;
                 addr->v6.addr = osaddr->addr.in6.sin6_addr;
             }
             return true;
6fbf66fa
     }
81d882d5
     return false;
6fbf66fa
 }
 
 /*
  * 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.
512cda46
  *
  * TODO: this is called for route-lookup for every yet-unhashed
  * destination address, so for lots of active net-iroutes, this
  * might benefit from some "zeroize 32 bit at a time" improvements
6fbf66fa
  */
 void
81d882d5
 mroute_addr_mask_host_bits(struct mroute_addr *ma)
6fbf66fa
 {
81d882d5
     if ((ma->type & MR_ADDR_MASK) == MR_ADDR_IPV4)
512cda46
     {
81d882d5
         in_addr_t addr = ntohl(ma->v4.addr);
         addr &= netbits_to_netmask(ma->netbits);
         ma->v4.addr = htonl(addr);
512cda46
     }
81d882d5
     else if ((ma->type & MR_ADDR_MASK) == MR_ADDR_IPV6)
512cda46
     {
81d882d5
         int byte = sizeof(ma->v6.addr) - 1;     /* rightmost byte in address */
         int bits_to_clear = 128 - ma->netbits;
512cda46
 
81d882d5
         while (byte >= 0 && bits_to_clear > 0)
512cda46
         {
81d882d5
             if (bits_to_clear >= 8)
             {
                 ma->v6.addr.s6_addr[byte--] = 0;
                 bits_to_clear -= 8;
             }
             else
             {
                 ma->v6.addr.s6_addr[byte--] &= (IPV4_NETMASK_HOST << bits_to_clear);
                 bits_to_clear = 0;
             }
512cda46
         }
81d882d5
         ASSERT( bits_to_clear == 0 );
     }
     else
     {
         ASSERT(0);
512cda46
     }
6fbf66fa
 }
 
 /*
  * The mroute_addr hash function takes into account the
  * address type, number of bits in the network address,
  * and the actual address.
  */
 uint32_t
81d882d5
 mroute_addr_hash_function(const void *key, uint32_t iv)
6fbf66fa
 {
81d882d5
     return hash_func(mroute_addr_hash_ptr((const struct mroute_addr *) key),
                      mroute_addr_hash_len((const struct mroute_addr *) key),
                      iv);
6fbf66fa
 }
 
 bool
81d882d5
 mroute_addr_compare_function(const void *key1, const void *key2)
6fbf66fa
 {
81d882d5
     return mroute_addr_equal((const struct mroute_addr *) key1,
                              (const struct mroute_addr *) key2);
6fbf66fa
 }
 
 const char *
81d882d5
 mroute_addr_print(const struct mroute_addr *ma,
                   struct gc_arena *gc)
6fbf66fa
 {
81d882d5
     return mroute_addr_print_ex(ma, MAPF_IA_EMPTY_IF_UNDEF, gc);
90efcacb
 }
 
 const char *
81d882d5
 mroute_addr_print_ex(const struct mroute_addr *ma,
                      const unsigned int flags,
                      struct gc_arena *gc)
90efcacb
 {
81d882d5
     struct buffer out = alloc_buf_gc(64, gc);
     if (ma)
6fbf66fa
     {
81d882d5
         struct mroute_addr maddr = *ma;
 
         switch (maddr.type & MR_ADDR_MASK)
         {
             case MR_ADDR_ETHER:
                 buf_printf(&out, "%s", format_hex_ex(ma->eth_addr,
                                                      sizeof(ma->eth_addr), 0, 1, ":", gc));
                 break;
 
             case MR_ADDR_IPV4:
             {
                 if ((flags & MAPF_SHOW_ARP) && (maddr.type & MR_ARP))
                 {
                     buf_printf(&out, "ARP/");
                 }
                 buf_printf(&out, "%s", print_in_addr_t(ntohl(maddr.v4.addr),
                                                        (flags & MAPF_IA_EMPTY_IF_UNDEF) ? IA_EMPTY_IF_UNDEF : 0, gc));
                 if (maddr.type & MR_WITH_NETBITS)
                 {
                     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);
                     }
                 }
                 if (maddr.type & MR_WITH_PORT)
                 {
                     buf_printf(&out, ":%d", ntohs(maddr.v4.port));
                 }
             }
             break;
 
             case MR_ADDR_IPV6:
             {
                 if (IN6_IS_ADDR_V4MAPPED( &maddr.v6.addr ) )
                 {
                     buf_printf(&out, "%s", print_in_addr_t(maddr.v4mappedv6.addr,
                                                            IA_NET_ORDER, gc));
                 }
                 else
                 {
                     buf_printf(&out, "%s", print_in6_addr(maddr.v6.addr, 0, gc));
                 }
                 if (maddr.type & MR_WITH_NETBITS)
                 {
                     buf_printf(&out, "/%d", maddr.netbits);
                 }
             }
             break;
 
             default:
                 buf_printf(&out, "UNKNOWN");
                 break;
         }
         return BSTR(&out);
     }
512cda46
     else
81d882d5
     {
         return "[NULL]";
     }
 }
6fbf66fa
 
 /*
  * 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 *
81d882d5
 mroute_helper_init(int ageable_ttl_secs)
6fbf66fa
 {
81d882d5
     struct mroute_helper *mh;
     ALLOC_OBJ_CLEAR(mh, struct mroute_helper);
     mh->ageable_ttl_secs = ageable_ttl_secs;
     return mh;
6fbf66fa
 }
 
 static void
81d882d5
 mroute_helper_regenerate(struct mroute_helper *mh)
6fbf66fa
 {
81d882d5
     int i, j = 0;
     for (i = MR_HELPER_NET_LEN - 1; i >= 0; --i)
6fbf66fa
     {
81d882d5
         if (mh->net_len_refcount[i] > 0)
         {
             mh->net_len[j++] = (uint8_t) i;
         }
6fbf66fa
     }
81d882d5
     mh->n_net_len = j;
6fbf66fa
 
 #ifdef ENABLE_DEBUG
81d882d5
     if (check_debug_level(D_MULTI_DEBUG))
6fbf66fa
     {
81d882d5
         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);
6fbf66fa
     }
 #endif
 }
 
 void
81d882d5
 mroute_helper_add_iroute46(struct mroute_helper *mh, int netbits)
6fbf66fa
 {
81d882d5
     if (netbits >= 0)
6fbf66fa
     {
81d882d5
         ASSERT(netbits < MR_HELPER_NET_LEN);
         ++mh->cache_generation;
         ++mh->net_len_refcount[netbits];
         if (mh->net_len_refcount[netbits] == 1)
         {
             mroute_helper_regenerate(mh);
         }
6fbf66fa
     }
 }
 
 void
81d882d5
 mroute_helper_del_iroute46(struct mroute_helper *mh, int netbits)
6fbf66fa
 {
81d882d5
     if (netbits >= 0)
6fbf66fa
     {
81d882d5
         ASSERT(netbits < MR_HELPER_NET_LEN);
         ++mh->cache_generation;
         --mh->net_len_refcount[netbits];
         ASSERT(mh->net_len_refcount[netbits] >= 0);
         if (!mh->net_len_refcount[netbits])
         {
             mroute_helper_regenerate(mh);
         }
512cda46
     }
 }
 
6fbf66fa
 void
81d882d5
 mroute_helper_free(struct mroute_helper *mh)
6fbf66fa
 {
81d882d5
     free(mh);
6fbf66fa
 }
 
81d882d5
 #else  /* if P2MP_SERVER */
 static void
4cd4899e
 dummy(void)
 {
81d882d5
 }
6fbf66fa
 #endif /* P2MP_SERVER */