2 subnet.c -- subnet handling
4 Copyright (C) 2003-2004 Guus Sliepen <guus@tinc-vpn.org>,
5 2003-2004 Ivo Timmermans <ivo@tinc-vpn.org>
7 This program is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 2 of the License, or
10 (at your option) any later version.
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
17 You should have received a copy of the GNU General Public License
18 along with this program; if not, write to the Free Software
19 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
27 #include "logger/logger.h"
29 #include "rt/subnet.h"
30 #include "support/avl.h"
31 #include "support/xalloc.h"
34 /* Subnet mask handling */
36 static int maskcmp(const void *va, const void *vb, int masklen, int len) {
41 for(m = masklen, i = 0; m >= 8; m -= 8, i++) {
47 return m ? (a[i] & (0x100 - (1 << (8 - m)))) - (b[i] & (0x100 - (1 << (8 - m)))) : 0;
50 static void mask(void *va, int masklen, int len) {
58 a[i++] &= (0x100 - (1 << masklen));
64 static void maskcpy(void *va, const void *vb, int masklen, int len) {
69 for(m = masklen, i = 0; m >= 8; m -= 8, i++)
73 a[i] = b[i] & (0x100 - (1 << m));
81 static bool maskcheck(const void *va, int masklen, int len) {
88 if(masklen && a[i++] & (0xff >> masklen))
109 static void cache_flush(void) {
110 memset(cache, 0, sizeof *cache * cache_size);
113 static void cache_init(void) {
116 cache_mask = cache_size - 1;
118 dim(cache, cache_size);
123 static void cache_exit(void) {
127 static uint32_t subnet_hash(const subnet_t *subnet) {
133 for(i = 0; i < sizeof subnet->net / sizeof(uint32_t); i++)
134 hash ^= ((uint32_t *)&subnet->net)[i];
139 return hash & cache_mask;
142 static subnet_t *cache_get(subnet_t *subnet) {
143 uint32_t hash = subnet_hash(subnet);
145 if(cache[hash].subnet && memcmp(&cache[hash].key, subnet, sizeof *subnet))
146 return cache[hash].subnet;
151 static void cache_add(subnet_t *key, subnet_t *subnet) {
152 uint32_t hash = subnet_hash(subnet);
154 cache[hash].key = *key;
155 cache[hash].subnet = subnet;
158 /* Subnet tree handling */
160 static int subnet_compare_mac(const subnet_t *a, const subnet_t *b) {
161 return memcmp(&a->net.mac.address, &b->net.mac.address, sizeof(mac_t))
162 ?: (a->owner && b->owner) ? strcmp(a->owner->name, b->owner->name) : 0;
165 static int subnet_compare_ipv4(const subnet_t *a, const subnet_t *b) {
166 return memcmp(&a->net.ipv4.address, &b->net.ipv4.address, sizeof(ipv4_t))
167 ?: (a->net.ipv4.prefixlength - b->net.ipv4.prefixlength)
168 ?: (a->owner && b->owner) ? strcmp(a->owner->name, b->owner->name) : 0;
171 static int subnet_compare_ipv6(const subnet_t *a, const subnet_t *b) {
172 return memcmp(&a->net.ipv6.address, &b->net.ipv6.address, sizeof(ipv6_t))
173 ?: (a->net.ipv6.prefixlength - b->net.ipv6.prefixlength)
174 ?: (a->owner && b->owner) ? strcmp(a->owner->name, b->owner->name) : 0;
177 static int subnet_compare(const subnet_t *a, const subnet_t *b) {
180 result = a->type - b->type;
186 case SUBNET_TYPE_MAC:
187 return subnet_compare_mac(a, b);
188 case SUBNET_TYPE_IPV4:
189 return subnet_compare_ipv4(a, b);
190 case SUBNET_TYPE_IPV6:
191 return subnet_compare_ipv6(a, b);
193 logger(LOG_ERR, _("rt: subnet_compare() was called with unknown subnet type %d, exitting!"), a->type);
198 avl_tree_t *subnet_tree_new(void) {
199 return avl_tree_new((avl_compare_t)subnet_compare, NULL);
202 void subnet_tree_free(avl_tree_t *subnets) {
203 avl_tree_free(subnets);
206 subnet_t *subnet_new(void) {
209 return clear(new(subnet));
212 void subnet_free(subnet_t *subnet) {
216 void subnet_add(subnet_t *subnet) {
217 avl_add(subnets, subnet);
218 avl_add(subnet->owner->subnets, subnet);
222 void subnet_del(subnet_t *subnet) {
223 avl_del(subnet->owner->subnets, subnet);
224 avl_del(subnets, subnet);
228 bool subnet_init(void) {
230 subnets = avl_tree_new((avl_compare_t)subnet_compare, (avl_action_t)subnet_free);
235 bool subnet_exit(void) {
236 avl_tree_del(subnets);
242 subnet_t *str2net(const char *subnetstr) {
244 subnet_t subnet = {0};
247 if(sscanf(subnetstr, "%hu.%hu.%hu.%hu/%d",
248 &x[0], &x[1], &x[2], &x[3], &l) == 5) {
249 subnet.type = SUBNET_TYPE_IPV4;
250 subnet.net.ipv4.prefixlength = l;
252 for(i = 0; i < 4; i++)
253 subnet.net.ipv4.address.x[i] = x[i];
255 return copy(&subnet);
258 if(sscanf(subnetstr, "%hx:%hx:%hx:%hx:%hx:%hx:%hx:%hx/%d",
259 &x[0], &x[1], &x[2], &x[3], &x[4], &x[5], &x[6], &x[7],
261 subnet.type = SUBNET_TYPE_IPV6;
262 subnet.net.ipv6.prefixlength = l;
264 for(i = 0; i < 8; i++)
265 subnet.net.ipv6.address.x[i] = htons(x[i]);
267 return copy(&subnet);
270 if(sscanf(subnetstr, "%hu.%hu.%hu.%hu", &x[0], &x[1], &x[2], &x[3]) == 4) {
271 subnet.type = SUBNET_TYPE_IPV4;
272 subnet.net.ipv4.prefixlength = 32;
274 for(i = 0; i < 4; i++)
275 subnet.net.ipv4.address.x[i] = x[i];
277 return copy(&subnet);
280 if(sscanf(subnetstr, "%hx:%hx:%hx:%hx:%hx:%hx:%hx:%hx",
281 &x[0], &x[1], &x[2], &x[3], &x[4], &x[5], &x[6], &x[7]) == 8) {
282 subnet.type = SUBNET_TYPE_IPV6;
283 subnet.net.ipv6.prefixlength = 128;
285 for(i = 0; i < 8; i++)
286 subnet.net.ipv6.address.x[i] = htons(x[i]);
288 return copy(&subnet);
291 if(sscanf(subnetstr, "%hx:%hx:%hx:%hx:%hx:%hx",
292 &x[0], &x[1], &x[2], &x[3], &x[4], &x[5]) == 6) {
293 subnet.type = SUBNET_TYPE_MAC;
295 for(i = 0; i < 6; i++)
296 subnet.net.mac.address.x[i] = x[i];
298 return copy(&subnet);
304 char *net2str(const subnet_t *subnet) {
307 switch (subnet->type) {
308 case SUBNET_TYPE_MAC:
309 asprintf(&netstr, "%hx:%hx:%hx:%hx:%hx:%hx",
310 subnet->net.mac.address.x[0],
311 subnet->net.mac.address.x[1],
312 subnet->net.mac.address.x[2],
313 subnet->net.mac.address.x[3],
314 subnet->net.mac.address.x[4],
315 subnet->net.mac.address.x[5]);
318 case SUBNET_TYPE_IPV4:
319 asprintf(&netstr, "%hu.%hu.%hu.%hu/%d",
320 subnet->net.ipv4.address.x[0],
321 subnet->net.ipv4.address.x[1],
322 subnet->net.ipv4.address.x[2],
323 subnet->net.ipv4.address.x[3],
324 subnet->net.ipv4.prefixlength);
327 case SUBNET_TYPE_IPV6:
328 asprintf(&netstr, "%hx:%hx:%hx:%hx:%hx:%hx:%hx:%hx/%d",
329 ntohs(subnet->net.ipv6.address.x[0]),
330 ntohs(subnet->net.ipv6.address.x[1]),
331 ntohs(subnet->net.ipv6.address.x[2]),
332 ntohs(subnet->net.ipv6.address.x[3]),
333 ntohs(subnet->net.ipv6.address.x[4]),
334 ntohs(subnet->net.ipv6.address.x[5]),
335 ntohs(subnet->net.ipv6.address.x[6]),
336 ntohs(subnet->net.ipv6.address.x[7]),
337 subnet->net.ipv6.prefixlength);
341 logger(LOG_ERR, _("net2str() was called with unknown subnet type %d, exiting!"), subnet->type);
348 bool cfg_subnet(cfg_t *cfg, subnet_t **result) {
351 subnet = str2net(cfg->value);
354 logger(LOG_ERR, _("rt: invalid subnet for configuration variable %s in %s line %d"),
355 cfg->variable, cfg->file, cfg->line);
364 subnet_t *subnet_get(const subnet_t *subnet) {
365 return subnet->owner ? avl_get(subnet->owner->subnets, subnet) : avl_get(subnets, subnet);
368 subnet_t *subnet_get_mac(const mac_t *address) {
369 subnet_t *subnet, search = {0};
371 search.type = SUBNET_TYPE_MAC;
372 search.net.mac.address = *address;
374 subnet = cache_get(&search);
379 subnet = avl_get(subnets, &search);
382 cache_add(&search, subnet);
387 subnet_t *subnet_get_ipv4(const ipv4_t *address) {
388 subnet_t *subnet, search = {0};
390 search.type = SUBNET_TYPE_IPV4;
391 search.net.ipv4.address = *address;
392 search.net.ipv4.prefixlength = 32;
394 subnet = cache_get(&search);
399 while(subnet = avl_get_closest_smaller(subnets, &search)) {
400 if(subnet->type != SUBNET_TYPE_IPV4)
403 if(!maskcmp(address, &subnet->net.ipv4.address, subnet->net.ipv4.prefixlength, sizeof(ipv4_t))) {
404 cache_add(&search, subnet);
408 search.net.ipv4.prefixlength = subnet->net.ipv4.prefixlength - 1;
409 maskcpy(&search.net.ipv4.address, &subnet->net.ipv4.address, search.net.ipv4.prefixlength, sizeof(ipv4_t));
415 subnet_t *subnet_get_ipv6(const ipv6_t *address) {
416 subnet_t *subnet, search = {0};
418 search.type = SUBNET_TYPE_IPV6;
419 search.net.ipv6.address = *address;
420 search.net.ipv6.prefixlength = 128;
422 subnet = cache_get(&search);
427 while(subnet = avl_get_closest_smaller(subnets, &search)) {
428 if(subnet->type != SUBNET_TYPE_IPV6)
431 if(!maskcmp(address, &subnet->net.ipv6.address, subnet->net.ipv6.prefixlength, sizeof(ipv6_t))) {
432 cache_add(&search, subnet);
436 search.net.ipv6.prefixlength = subnet->net.ipv6.prefixlength - 1;
437 maskcpy(&search.net.ipv6.address, &subnet->net.ipv6.address, search.net.ipv6.prefixlength, sizeof(ipv6_t));