OpenVPN
mroute.c
Go to the documentation of this file.
1/*
2 * OpenVPN -- An application to securely tunnel IP networks
3 * over a single TCP/UDP port, with support for SSL/TLS-based
4 * session authentication and key exchange,
5 * packet encryption, packet authentication, and
6 * packet compression.
7 *
8 * Copyright (C) 2002-2026 OpenVPN Inc <sales@openvpn.net>
9 *
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License version 2
12 * as published by the Free Software Foundation.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License along
20 * with this program; if not, see <https://www.gnu.org/licenses/>.
21 */
22
23#ifdef HAVE_CONFIG_H
24#include "config.h"
25#endif
26
27#include "syshead.h"
28
29
30#include "mroute.h"
31#include "proto.h"
32#include "error.h"
33#include "socket_util.h"
34
35#include "memdbg.h"
36#include "siphash.h"
37
38void
40{
41 CLEAR(*addr);
42}
43
44/*
45 * Ethernet multicast addresses.
46 */
47
48static inline bool
49is_mac_mcast_addr(const uint8_t *mac)
50{
51 return (bool)(mac[0] & 1);
52}
53
54static inline bool
56{
57 return (addr->type & MR_ADDR_MASK) == MR_ADDR_ETHER && is_mac_mcast_addr(addr->ether.addr);
58}
59
60/*
61 * Don't learn certain addresses.
62 */
63bool
64mroute_learnable_address(const struct mroute_addr *addr, struct gc_arena *gc)
65{
66 int i;
67 bool all_zeros = true;
68 bool all_ones = true;
69
70 for (i = 0; i < addr->len; ++i)
71 {
72 int b = addr->raw_addr[i];
73 if (b != 0x00)
74 {
75 all_zeros = false;
76 }
77 if (b != 0xFF)
78 {
79 all_ones = false;
80 }
81 }
82
83 /* only networkss shorter than 8 bits are allowed to be all 0s. */
84 if (all_zeros && !((addr->type & MR_WITH_NETBITS) && (addr->netbits < 8)))
85 {
86 msg(D_MULTI_LOW, "Can't learn %s: network is all 0s, but netbits >= 8",
87 mroute_addr_print(addr, gc));
88 return false;
89 }
90
91 if (all_ones)
92 {
93 msg(D_MULTI_LOW, "Can't learn %s: network is all 1s", mroute_addr_print(addr, gc));
94 return false;
95 }
96
97 if (is_mac_mcast_maddr(addr))
98 {
99 msg(D_MULTI_LOW, "Can't learn %s: network is a multicast address",
100 mroute_addr_print(addr, gc));
101 return false;
102 }
103
104 return true;
105}
106
107static inline void
109{
110 if (ma)
111 {
112 ma->type = MR_ADDR_IPV4;
113 ma->netbits = 0;
114 ma->len = 4;
115 ma->v4.addr = src;
116 }
117}
118
119static inline void
120mroute_get_in6_addr(struct mroute_addr *ma, const struct in6_addr src)
121{
122 if (ma)
123 {
124 ma->type = MR_ADDR_IPV6;
125 ma->netbits = 0;
126 ma->len = 16;
127 ma->v6.addr = src;
128 }
129}
130
131static inline bool
133{
134 return ((addr & htonl(IP_MCAST_SUBNET_MASK)) == htonl(IP_MCAST_NETWORK));
135}
136
137/* RFC 4291, 2.7, "binary 11111111 at the start of an address identifies
138 * the address as being a multicast address"
139 */
140static inline bool
141mroute_is_mcast_ipv6(const struct in6_addr addr)
142{
143 return (addr.s6_addr[0] == 0xff);
144}
145
146
147unsigned int
148mroute_extract_addr_ip(struct mroute_addr *src, struct mroute_addr *dest, const struct buffer *buf)
149{
150 unsigned int ret = 0;
151 if (BLEN(buf) >= 1)
152 {
153 switch (OPENVPN_IPH_GET_VER(*BPTR(buf)))
154 {
155 case 4:
156 if (BLENZ(buf) >= sizeof(struct openvpn_iphdr))
157 {
158 const struct openvpn_iphdr *ip = (const struct openvpn_iphdr *)BPTR(buf);
159
160 mroute_get_in_addr_t(src, ip->saddr);
161 mroute_get_in_addr_t(dest, ip->daddr);
162
163 /* multicast packet? */
164 if (mroute_is_mcast(ip->daddr))
165 {
167 }
168
169 /* IGMP message? */
171 {
172 ret |= MROUTE_EXTRACT_IGMP;
173 }
174
176 }
177 break;
178
179 case 6:
180 if (BLENZ(buf) >= sizeof(struct openvpn_ipv6hdr))
181 {
182 const struct openvpn_ipv6hdr *ipv6 = (const struct openvpn_ipv6hdr *)BPTR(buf);
183#if 0 /* very basic debug */
184 struct gc_arena gc = gc_new();
185 msg( M_INFO, "IPv6 packet! src=%s, dst=%s",
186 print_in6_addr( ipv6->saddr, 0, &gc ),
187 print_in6_addr( ipv6->daddr, 0, &gc ));
188 gc_free(&gc);
189#endif
190
191 mroute_get_in6_addr(src, ipv6->saddr);
192 mroute_get_in6_addr(dest, ipv6->daddr);
193
194 if (mroute_is_mcast_ipv6(ipv6->daddr))
195 {
197 }
198
200 }
201 break;
202
203 default:
204 msg(M_WARN, "IP packet with unknown IP version=%d seen",
206 }
207 }
208 return ret;
209}
210
211static void
212mroute_copy_ether_to_addr(struct mroute_addr *maddr, const uint8_t *ether_addr, uint16_t vid)
213{
214 maddr->type = MR_ADDR_ETHER;
215 maddr->netbits = 0;
216 maddr->len = OPENVPN_ETH_ALEN;
217 memcpy(maddr->ether.addr, ether_addr, OPENVPN_ETH_ALEN);
218 maddr->len += sizeof(vid);
219 maddr->ether.vid = vid;
220}
221
222unsigned int
223mroute_extract_addr_ether(struct mroute_addr *src, struct mroute_addr *dest, uint16_t vid,
224 const struct buffer *buf)
225{
226 unsigned int ret = 0;
227 if (BLEN(buf) >= (int)sizeof(struct openvpn_ethhdr))
228 {
229 const struct openvpn_ethhdr *eth = (const struct openvpn_ethhdr *)BPTR(buf);
230 if (src)
231 {
232 mroute_copy_ether_to_addr(src, eth->source, vid);
233 }
234 if (dest)
235 {
237
238 /* ethernet broadcast/multicast packet? */
239 if (is_mac_mcast_addr(eth->dest))
240 {
242 }
243 }
244
246 }
247 return ret;
248}
249
250/*
251 * Translate a struct openvpn_sockaddr (osaddr)
252 * to a struct mroute_addr (addr).
253 */
254bool
256 bool use_port)
257{
258 switch (osaddr->addr.sa.sa_family)
259 {
260 case AF_INET:
261 {
262 if (use_port)
263 {
265 addr->netbits = 0;
266 addr->len = 6;
267 addr->v4.addr = osaddr->addr.in4.sin_addr.s_addr;
268 addr->v4.port = osaddr->addr.in4.sin_port;
269 if (addr->proto != PROTO_NONE)
270 {
271 addr->type |= MR_WITH_PROTO;
272 }
273 }
274 else
275 {
276 addr->type = MR_ADDR_IPV4;
277 addr->netbits = 0;
278 addr->len = 4;
279 addr->v4.addr = osaddr->addr.in4.sin_addr.s_addr;
280 }
281 return true;
282 }
283
284 case AF_INET6:
285 if (use_port)
286 {
288 addr->netbits = 0;
289 addr->len = 18;
290 addr->v6.addr = osaddr->addr.in6.sin6_addr;
291 addr->v6.port = osaddr->addr.in6.sin6_port;
292 if (addr->proto != PROTO_NONE)
293 {
294 addr->type |= MR_WITH_PROTO;
295 }
296 }
297 else
298 {
299 addr->type = MR_ADDR_IPV6;
300 addr->netbits = 0;
301 addr->len = 16;
302 addr->v6.addr = osaddr->addr.in6.sin6_addr;
303 }
304 return true;
305 }
306 return false;
307}
308
309/*
310 * Zero off the host bits in an address, leaving
311 * only the network bits, using the netbits member of
312 * struct mroute_addr as the controlling parameter.
313 *
314 * TODO: this is called for route-lookup for every yet-unhashed
315 * destination address, so for lots of active net-iroutes, this
316 * might benefit from some "zeroize 32 bit at a time" improvements
317 */
318void
320{
321 if ((ma->type & MR_ADDR_MASK) == MR_ADDR_IPV4)
322 {
323 in_addr_t addr = ntohl(ma->v4.addr);
324 addr &= netbits_to_netmask(ma->netbits);
325 ma->v4.addr = htonl(addr);
326 }
327 else if ((ma->type & MR_ADDR_MASK) == MR_ADDR_IPV6)
328 {
329 int byte = sizeof(ma->v6.addr) - 1; /* rightmost byte in address */
330 int bits_to_clear = 128 - ma->netbits;
331
332 while (byte >= 0 && bits_to_clear > 0)
333 {
334 if (bits_to_clear >= 8)
335 {
336 ma->v6.addr.s6_addr[byte--] = 0;
337 bits_to_clear -= 8;
338 }
339 else
340 {
341 ma->v6.addr.s6_addr[byte--] &= (uint8_t)(0xFF << bits_to_clear);
342 bits_to_clear = 0;
343 }
344 }
345 ASSERT(bits_to_clear == 0);
346 }
347 else
348 {
349 ASSERT(0);
350 }
351}
352
353/*
354 * The mroute_addr hash function takes into account the
355 * address type, number of bits in the network address,
356 * and the actual address.
357 */
358uint64_t
359mroute_addr_hash_function(const void *key, const uint8_t hash_key[HASH_KEY_LEN])
360{
362 mroute_addr_hash_len((const struct mroute_addr *)key), hash_key);
363}
364
365bool
366mroute_addr_compare_function(const void *key1, const void *key2)
367{
368 return mroute_addr_equal((const struct mroute_addr *)key1, (const struct mroute_addr *)key2);
369}
370
371const char *
372mroute_addr_print(const struct mroute_addr *ma, struct gc_arena *gc)
373{
375}
376
377const char *
378mroute_addr_print_ex(const struct mroute_addr *ma, const unsigned int flags, struct gc_arena *gc)
379{
380 struct buffer out = alloc_buf_gc(64, gc);
381 if (ma)
382 {
383 struct mroute_addr maddr = *ma;
384
385 switch (maddr.type & MR_ADDR_MASK)
386 {
387 case MR_ADDR_ETHER:
388 buf_printf(&out, "%s",
389 format_hex_ex(ma->ether.addr, sizeof(ma->ether.addr), 0, 1, ":", gc));
390 buf_printf(&out, "@%hu", ma->ether.vid);
391 break;
392
393 case MR_ADDR_IPV4:
394 {
395 if ((flags & MAPF_SHOW_ARP) && (maddr.type & MR_ARP))
396 {
397 buf_printf(&out, "ARP/");
398 }
399 if (maddr.type & MR_WITH_PROTO)
400 {
401 buf_printf(&out, "%s:", proto2ascii(maddr.proto, AF_INET, false));
402 }
403 if (flags & MAPF_SHOW_FAMILY)
404 {
405 buf_printf(&out, "[AF_INET]");
406 }
407 buf_printf(&out, "%s",
408 print_in_addr_t(ntohl(maddr.v4.addr),
410 gc));
411 if (maddr.type & MR_WITH_NETBITS)
412 {
413 if (flags & MAPF_SUBNET)
414 {
415 const in_addr_t netmask = netbits_to_netmask(maddr.netbits);
416 buf_printf(&out, "/%s", print_in_addr_t(netmask, 0, gc));
417 }
418 else
419 {
420 buf_printf(&out, "/%d", maddr.netbits);
421 }
422 }
423 if (maddr.type & MR_WITH_PORT)
424 {
425 buf_printf(&out, ":%d", ntohs(maddr.v4.port));
426 }
427 }
428 break;
429
430 case MR_ADDR_IPV6:
431 {
432 if (maddr.type & MR_WITH_PROTO)
433 {
434 buf_printf(&out, "%s:", proto2ascii(maddr.proto, AF_INET6, false));
435 }
436 if (flags & MAPF_SHOW_FAMILY)
437 {
438 buf_printf(&out, "[AF_INET6]");
439 }
440 if (IN6_IS_ADDR_V4MAPPED(&maddr.v6.addr))
441 {
442 buf_printf(&out, "%s",
443 print_in_addr_t(maddr.v4mappedv6.addr, IA_NET_ORDER, gc));
444 }
445 else if (maddr.type & MR_WITH_PORT)
446 {
447 buf_printf(&out, "[%s]", print_in6_addr(maddr.v6.addr, 0, gc));
448 }
449 else
450 {
451 buf_printf(&out, "%s", print_in6_addr(maddr.v6.addr, 0, gc));
452 }
453 if (maddr.type & MR_WITH_PORT)
454 {
455 buf_printf(&out, ":%d", ntohs(maddr.v6.port));
456 }
457 if (maddr.type & MR_WITH_NETBITS)
458 {
459 buf_printf(&out, "/%d", maddr.netbits);
460 }
461 }
462 break;
463
464 default:
465 buf_printf(&out, "UNKNOWN");
466 break;
467 }
468 return BSTR(&out);
469 }
470 else
471 {
472 return "[NULL]";
473 }
474}
475
476/*
477 * mroute_helper's main job is keeping track of
478 * currently used CIDR netlengths, so we don't
479 * have to cycle through all 33.
480 */
481
482struct mroute_helper *
484{
485 struct mroute_helper *mh;
486 ALLOC_OBJ_CLEAR(mh, struct mroute_helper);
488 return mh;
489}
490
491static void
493{
494 int i, j = 0;
495 for (i = MR_HELPER_NET_LEN - 1; i >= 0; --i)
496 {
497 if (mh->net_len_refcount[i] > 0)
498 {
499 mh->net_len[j++] = (uint8_t)i;
500 }
501 }
502 mh->n_net_len = j;
503
504#ifdef ENABLE_DEBUG
506 {
507 struct gc_arena gc = gc_new();
508 struct buffer out = alloc_buf_gc(256, &gc);
509 buf_printf(&out, "MROUTE CIDR netlen:");
510 for (i = 0; i < mh->n_net_len; ++i)
511 {
512 buf_printf(&out, " /%d", mh->net_len[i]);
513 }
514 dmsg(D_MULTI_DEBUG, "%s", BSTR(&out));
515 gc_free(&gc);
516 }
517#endif
518}
519
520void
522{
523 if (netbits >= 0)
524 {
525 ASSERT(netbits < MR_HELPER_NET_LEN);
526 ++mh->cache_generation;
527 ++mh->net_len_refcount[netbits];
528 if (mh->net_len_refcount[netbits] == 1)
529 {
531 }
532 }
533}
534
535void
537{
538 if (netbits >= 0)
539 {
540 ASSERT(netbits < MR_HELPER_NET_LEN);
541 ++mh->cache_generation;
542 --mh->net_len_refcount[netbits];
543 ASSERT(mh->net_len_refcount[netbits] >= 0);
544 if (!mh->net_len_refcount[netbits])
545 {
547 }
548 }
549}
550
551void
553{
554 free(mh);
555}
bool buf_printf(struct buffer *buf, const char *format,...)
printf-style append to a buffer with overflow check.
Definition buffer.c:226
struct buffer alloc_buf_gc(size_t size, struct gc_arena *gc)
Allocate a buffer of the given size under garbage collection.
Definition buffer.c:77
char * format_hex_ex(const uint8_t *data, size_t size, size_t maxoutput, unsigned int space_break_flags, const char *separator, struct gc_arena *gc)
Format a binary buffer as a hex string.
Definition buffer.c:452
#define BSTR(buf)
Return the buffer content pointer cast to char *.
Definition buffer.h:151
#define BPTR(buf)
Return a pointer to the start of the buffer content.
Definition buffer.h:139
#define BLEN(buf)
Return the length of the buffer content in bytes.
Definition buffer.h:145
#define BLENZ(buf)
Return the length of the buffer content as a size_t.
Definition buffer.h:147
static void gc_free(struct gc_arena *a)
Free all allocations in a garbage collection arena.
Definition buffer.h:1912
#define ALLOC_OBJ_CLEAR(dptr, type)
Allocate and zero-initialise memory for a single object of the given type.
Definition buffer.h:1974
static struct gc_arena gc_new(void)
Allocate and return a new, empty garbage collection arena.
Definition buffer.h:1896
#define D_MULTI_DEBUG
Definition errlevel.h:126
#define D_MULTI_LOW
Definition errlevel.h:85
#define M_INFO
Definition errlevel.h:54
#define HASH_KEY_LEN
Definition list.h:53
void mroute_addr_mask_host_bits(struct mroute_addr *ma)
Definition mroute.c:319
void mroute_helper_add_iroute46(struct mroute_helper *mh, int netbits)
Definition mroute.c:521
unsigned int mroute_extract_addr_ip(struct mroute_addr *src, struct mroute_addr *dest, const struct buffer *buf)
Definition mroute.c:148
static bool is_mac_mcast_maddr(const struct mroute_addr *addr)
Definition mroute.c:55
static void mroute_copy_ether_to_addr(struct mroute_addr *maddr, const uint8_t *ether_addr, uint16_t vid)
Definition mroute.c:212
const char * mroute_addr_print_ex(const struct mroute_addr *ma, const unsigned int flags, struct gc_arena *gc)
Definition mroute.c:378
bool mroute_extract_openvpn_sockaddr(struct mroute_addr *addr, const struct openvpn_sockaddr *osaddr, bool use_port)
Definition mroute.c:255
const char * mroute_addr_print(const struct mroute_addr *ma, struct gc_arena *gc)
Definition mroute.c:372
static bool is_mac_mcast_addr(const uint8_t *mac)
Definition mroute.c:49
static void mroute_get_in_addr_t(struct mroute_addr *ma, const in_addr_t src)
Definition mroute.c:108
void mroute_helper_del_iroute46(struct mroute_helper *mh, int netbits)
Definition mroute.c:536
bool mroute_learnable_address(const struct mroute_addr *addr, struct gc_arena *gc)
Definition mroute.c:64
static bool mroute_is_mcast(const in_addr_t addr)
Definition mroute.c:132
bool mroute_addr_compare_function(const void *key1, const void *key2)
Definition mroute.c:366
static bool mroute_is_mcast_ipv6(const struct in6_addr addr)
Definition mroute.c:141
unsigned int mroute_extract_addr_ether(struct mroute_addr *src, struct mroute_addr *dest, uint16_t vid, const struct buffer *buf)
Definition mroute.c:223
struct mroute_helper * mroute_helper_init(int ageable_ttl_secs)
Definition mroute.c:483
static void mroute_get_in6_addr(struct mroute_addr *ma, const struct in6_addr src)
Definition mroute.c:120
void mroute_addr_init(struct mroute_addr *addr)
Definition mroute.c:39
static void mroute_helper_regenerate(struct mroute_helper *mh)
Definition mroute.c:492
uint64_t mroute_addr_hash_function(const void *key, const uint8_t hash_key[HASH_KEY_LEN])
Definition mroute.c:359
void mroute_helper_free(struct mroute_helper *mh)
Definition mroute.c:552
static uint32_t mroute_addr_hash_len(const struct mroute_addr *a)
Definition mroute.h:238
#define MROUTE_EXTRACT_SUCCEEDED
Definition mroute.h:38
#define MROUTE_EXTRACT_MCAST
Definition mroute.h:40
#define MR_ADDR_ETHER
Definition mroute.h:61
#define IP_MCAST_NETWORK
Definition mroute.h:34
#define MR_WITH_NETBITS
Definition mroute.h:70
#define MR_WITH_PROTO
Definition mroute.h:76
#define IP_MCAST_SUBNET_MASK
Definition mroute.h:33
#define MR_ADDR_IPV4
Definition mroute.h:62
#define MAPF_SHOW_ARP
Definition mroute.h:157
#define MAPF_SUBNET
Definition mroute.h:155
static const uint8_t * mroute_addr_hash_ptr(const struct mroute_addr *a)
Definition mroute.h:231
#define MR_WITH_PORT
Definition mroute.h:67
#define MR_ARP
Definition mroute.h:73
#define MR_HELPER_NET_LEN
Definition mroute.h:126
#define MAPF_IA_EMPTY_IF_UNDEF
Definition mroute.h:156
static bool mroute_addr_equal(const struct mroute_addr *a1, const struct mroute_addr *a2)
Definition mroute.h:209
#define MAPF_SHOW_FAMILY
Definition mroute.h:158
#define MROUTE_EXTRACT_BCAST
Definition mroute.h:39
#define MROUTE_EXTRACT_IGMP
Definition mroute.h:41
#define MR_ADDR_IPV6
Definition mroute.h:63
#define MR_ADDR_MASK
Definition mroute.h:64
#define CLEAR(x)
Definition basic.h:32
static bool check_debug_level(msglvl_t level)
Definition error.h:251
#define dmsg(flags,...)
Definition error.h:172
#define msg(flags,...)
Definition error.h:152
#define ASSERT(x)
Definition error.h:219
#define M_WARN
Definition error.h:92
#define OPENVPN_ETH_ALEN
Definition proto.h:52
#define OPENVPN_IPH_GET_VER(v)
Definition proto.h:91
#define OPENVPN_IPPROTO_IGMP
Definition proto.h:104
static in_addr_t netbits_to_netmask(const int netbits)
Definition route.h:399
uint64_t siphash_hash_func(const uint8_t *k, uint32_t length, const uint8_t hash_key[SIPHASH_KEY_SIZE])
Wrapper of the siphash function to be able to use it in the hash map.
Definition siphash.c:37
const char * proto2ascii(int proto, sa_family_t af, bool display_form)
const char * print_in6_addr(struct in6_addr a6, unsigned int flags, struct gc_arena *gc)
const char * print_in_addr_t(in_addr_t addr, unsigned int flags, struct gc_arena *gc)
#define IA_EMPTY_IF_UNDEF
Definition socket_util.h:89
@ PROTO_NONE
#define IA_NET_ORDER
Definition socket_util.h:90
Wrapper structure for dynamically allocated memory.
Definition buffer.h:71
int len
Length in bytes of the actual content within the allocated memory.
Definition buffer.h:76
Garbage collection arena used to keep track of dynamically allocated memory.
Definition buffer.h:127
Container for bidirectional cipher and HMAC key material.
Definition crypto.h:240
Container for unidirectional cipher and HMAC key material.
Definition crypto.h:152
uint8_t raw_addr[MR_MAX_ADDR_LEN]
Definition mroute.h:90
uint16_t vid
Definition mroute.h:94
struct mroute_addr::@2::@4 ether
struct mroute_addr::@2::@6 v6
uint8_t addr[OPENVPN_ETH_ALEN]
Definition mroute.h:93
struct mroute_addr::@2::@5 v4
uint8_t proto
Definition mroute.h:84
uint8_t type
Definition mroute.h:85
in_port_t port
Definition mroute.h:99
uint8_t len
Definition mroute.h:83
uint8_t netbits
Definition mroute.h:86
int ageable_ttl_secs
Definition mroute.h:134
int net_len_refcount[MR_HELPER_NET_LEN]
Definition mroute.h:137
uint8_t net_len[MR_HELPER_NET_LEN]
Definition mroute.h:136
int n_net_len
Definition mroute.h:135
uint8_t dest[OPENVPN_ETH_ALEN]
Definition proto.h:55
uint8_t source[OPENVPN_ETH_ALEN]
Definition proto.h:56
uint32_t saddr
Definition proto.h:111
uint32_t daddr
Definition proto.h:112
uint8_t protocol
Definition proto.h:108
struct in6_addr saddr
Definition proto.h:127
struct in6_addr daddr
Definition proto.h:128
union openvpn_sockaddr::@27 addr
struct sockaddr sa
Definition socket_util.h:42
struct sockaddr_in in4
Definition socket_util.h:43
struct sockaddr_in6 in6
Definition socket_util.h:44
uint32_t in_addr_t
Definition syshead.h:52
struct gc_arena gc
Definition test_ssl.c:122