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 */ |