Allow identical subnets from different owners.
[tinc] / src / subnet.c
1 /*
2     subnet.c -- handle subnet lookups and lists
3     Copyright (C) 2000-2002 Guus Sliepen <guus@sliepen.eu.org>,
4                   2000-2002 Ivo Timmermans <ivo@o2w.nl>
5
6     This program is free software; you can redistribute it and/or modify
7     it under the terms of the GNU General Public License as published by
8     the Free Software Foundation; either version 2 of the License, or
9     (at your option) any later version.
10
11     This program is distributed in the hope that it will be useful,
12     but WITHOUT ANY WARRANTY; without even the implied warranty of
13     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14     GNU General Public License for more details.
15
16     You should have received a copy of the GNU General Public License
17     along with this program; if not, write to the Free Software
18     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19
20     $Id: subnet.c,v 1.1.2.38 2002/07/10 11:32:33 guus Exp $
21 */
22
23 #include "config.h"
24
25 #include <stdio.h>
26 #include <syslog.h>
27 #include <string.h>
28 #include <errno.h>
29 #include <fcntl.h>
30 #include <netdb.h>
31 #include <netinet/in.h>
32
33 #include <utils.h>
34 #include <xalloc.h>
35 #include <avl_tree.h>
36
37 #include "conf.h"
38 #include "net.h"
39 #include "node.h"
40 #include "subnet.h"
41 #include "netutl.h"
42
43 #include "system.h"
44
45 /* lists type of subnet */
46
47 avl_tree_t *subnet_tree;
48
49 /* Subnet comparison */
50
51 int subnet_compare_mac(subnet_t *a, subnet_t *b)
52 {
53   int result;
54 cp
55   result = memcmp(&a->net.mac.address, &b->net.mac.address, sizeof(mac_t));
56   
57   if(result || !a->owner || !b->owner)
58     return result;
59
60   return strcmp(a->owner->name, b->owner->name);
61 }
62
63 int subnet_compare_ipv4(subnet_t *a, subnet_t *b)
64 {
65   int result;
66 cp
67   result = memcmp(&a->net.ipv4.address, &b->net.ipv4.address, sizeof(ipv4_t));
68   
69   if(result)
70     return result;
71
72   result = a->net.ipv4.prefixlength - b->net.ipv4.prefixlength;
73   
74   if(result || !a->owner || !b->owner)
75     return result;
76
77   return strcmp(a->owner->name, b->owner->name);
78 }
79
80 int subnet_compare_ipv6(subnet_t *a, subnet_t *b)
81 {
82   int result;
83 cp
84   result = memcmp(&a->net.ipv6.address, &b->net.ipv6.address, sizeof(ipv6_t));
85   
86   if(result)
87     return result;
88
89   result = a->net.ipv6.prefixlength - b->net.ipv6.prefixlength;
90   
91   if(result || !a->owner || !b->owner)
92     return result;
93
94   return strcmp(a->owner->name, b->owner->name);
95 }
96
97 int subnet_compare(subnet_t *a, subnet_t *b)
98 {
99   int result;
100 cp  
101   result = a->type - b->type;
102  
103   if(result)
104     return result;
105     
106   switch(a->type)
107     {
108       case SUBNET_MAC:
109         return subnet_compare_mac(a, b);
110       case SUBNET_IPV4:
111         return subnet_compare_ipv4(a, b);
112       case SUBNET_IPV6:
113         return subnet_compare_ipv6(a, b);
114       default:
115         syslog(LOG_ERR, _("subnet_compare() was called with unknown subnet type %d, exitting!"), a->type);
116         cp_trace();
117         exit(0);
118     }
119
120   return 0;
121 }
122
123 /* Initialising trees */
124
125 void init_subnets(void)
126 {
127 cp
128   subnet_tree = avl_alloc_tree((avl_compare_t)subnet_compare, (avl_action_t)free_subnet);
129 cp
130 }
131
132 void exit_subnets(void)
133 {
134 cp
135   avl_delete_tree(subnet_tree);
136 cp
137 }
138
139 avl_tree_t *new_subnet_tree(void)
140 {
141 cp
142   return avl_alloc_tree((avl_compare_t)subnet_compare, NULL);
143 cp
144 }
145
146 void free_subnet_tree(avl_tree_t *subnet_tree)
147 {
148 cp
149   avl_delete_tree(subnet_tree);
150 cp
151 }
152
153 /* Allocating and freeing space for subnets */
154
155 subnet_t *new_subnet(void)
156 {
157 cp
158   return (subnet_t *)xmalloc(sizeof(subnet_t));
159 }
160
161 void free_subnet(subnet_t *subnet)
162 {
163 cp
164   free(subnet);
165 }
166
167 /* Adding and removing subnets */
168
169 void subnet_add(node_t *n, subnet_t *subnet)
170 {
171 cp
172   subnet->owner = n;
173
174   avl_insert(subnet_tree, subnet);
175 cp
176   avl_insert(n->subnet_tree, subnet);
177 cp
178 }
179
180 void subnet_del(node_t *n, subnet_t *subnet)
181 {
182 cp
183   avl_delete(n->subnet_tree, subnet);
184 cp
185   avl_delete(subnet_tree, subnet);
186 cp
187 }
188
189 /* Ascii representation of subnets */
190
191 subnet_t *str2net(char *subnetstr)
192 {
193   int i, l;
194   subnet_t *subnet;
195   uint16_t x[8];
196 cp
197   subnet = new_subnet();
198 cp
199   if(sscanf(subnetstr, "%hu.%hu.%hu.%hu/%d",
200               &x[0], &x[1], &x[2], &x[3],
201               &l) == 5)
202     {
203       subnet->type = SUBNET_IPV4;
204       subnet->net.ipv4.prefixlength = l;
205       for(i = 0; i < 4; i++)
206         subnet->net.ipv4.address.x[i] = x[i];
207       return subnet;
208     }
209               
210   if(sscanf(subnetstr, "%hx:%hx:%hx:%hx:%hx:%hx:%hx:%hx/%d",
211              &x[0], &x[1], &x[2], &x[3], &x[4], &x[5], &x[6], &x[7],
212              &l) == 9)
213     {
214       subnet->type = SUBNET_IPV6;
215       subnet->net.ipv6.prefixlength = l;
216       for(i = 0; i < 8; i++)
217         subnet->net.ipv6.address.x[i] = htons(x[i]);
218       return subnet;
219     }
220
221   if(sscanf(subnetstr, "%hu.%hu.%hu.%hu",
222               &x[0], &x[1], &x[2], &x[3]) == 4)
223     {
224       subnet->type = SUBNET_IPV4;
225       subnet->net.ipv4.prefixlength = 32;
226       for(i = 0; i < 4; i++)
227         subnet->net.ipv4.address.x[i] = x[i];
228       return subnet;
229     }
230               
231   if(sscanf(subnetstr, "%hx:%hx:%hx:%hx:%hx:%hx:%hx:%hx",
232              &x[0], &x[1], &x[2], &x[3], &x[4], &x[5], &x[6], &x[7]) == 8)
233     {
234       subnet->type = SUBNET_IPV6;
235       subnet->net.ipv6.prefixlength = 128;
236       for(i = 0; i < 8; i++)
237         subnet->net.ipv6.address.x[i] = htons(x[i]);
238       return subnet;
239     }
240
241   if(sscanf(subnetstr, "%hx:%hx:%hx:%hx:%hx:%hx",
242               &x[0], &x[1], &x[2], &x[3], &x[4], &x[5]) == 6)
243     {
244       subnet->type = SUBNET_MAC;
245       for(i = 0; i < 6; i++)
246         subnet->net.mac.address.x[i] = x[i];
247       return subnet;
248     }
249
250   free(subnet);
251   return NULL;
252 }
253
254 char *net2str(subnet_t *subnet)
255 {
256   char *netstr;
257 cp
258   switch(subnet->type)
259     {
260       case SUBNET_MAC:
261         asprintf(&netstr, "%hx:%hx:%hx:%hx:%hx:%hx",
262                    subnet->net.mac.address.x[0],
263                    subnet->net.mac.address.x[1],
264                    subnet->net.mac.address.x[2],
265                    subnet->net.mac.address.x[3],
266                    subnet->net.mac.address.x[4],
267                    subnet->net.mac.address.x[5]);
268         break;
269       case SUBNET_IPV4:
270         asprintf(&netstr, "%hu.%hu.%hu.%hu/%d",
271                    subnet->net.ipv4.address.x[0],
272                    subnet->net.ipv4.address.x[1],
273                    subnet->net.ipv4.address.x[2],
274                    subnet->net.ipv4.address.x[3],
275                    subnet->net.ipv4.prefixlength);
276         break;
277       case SUBNET_IPV6:
278         asprintf(&netstr, "%hx:%hx:%hx:%hx:%hx:%hx:%hx:%hx/%d",
279                    ntohs(subnet->net.ipv6.address.x[0]),
280                    ntohs(subnet->net.ipv6.address.x[1]),
281                    ntohs(subnet->net.ipv6.address.x[2]),
282                    ntohs(subnet->net.ipv6.address.x[3]),
283                    ntohs(subnet->net.ipv6.address.x[4]),
284                    ntohs(subnet->net.ipv6.address.x[5]),
285                    ntohs(subnet->net.ipv6.address.x[6]),
286                    ntohs(subnet->net.ipv6.address.x[7]),
287                    subnet->net.ipv6.prefixlength);
288         break;
289       default:
290         syslog(LOG_ERR, _("net2str() was called with unknown subnet type %d, exiting!"), subnet->type);
291         cp_trace();
292         exit(0);
293     }
294 cp
295   return netstr;
296 }
297
298 /* Subnet lookup routines */
299
300 subnet_t *lookup_subnet(node_t *owner, subnet_t *subnet)
301 {
302 cp  
303   return avl_search(owner->subnet_tree, subnet);
304 }
305
306 subnet_t *lookup_subnet_mac(mac_t *address)
307 {
308   subnet_t subnet, *p;
309 cp
310   subnet.type = SUBNET_MAC;
311   memcpy(&subnet.net.mac.address, address, sizeof(mac_t));
312   subnet.owner = NULL;
313
314   p = (subnet_t *)avl_search(subnet_tree, &subnet);
315 cp
316   return p;
317 }
318
319 subnet_t *lookup_subnet_ipv4(ipv4_t *address)
320 {
321   subnet_t subnet, *p;
322 cp
323   subnet.type = SUBNET_IPV4;
324   memcpy(&subnet.net.ipv4.address, address, sizeof(ipv4_t));
325   subnet.net.ipv4.prefixlength = 32;
326   subnet.owner = NULL;
327
328   do
329   {
330     /* Go find subnet */
331   
332     p = (subnet_t *)avl_search_closest_smaller(subnet_tree, &subnet);
333
334   /* Check if the found subnet REALLY matches */
335 cp
336     if(p)
337       {
338         if(p->type != SUBNET_IPV4)
339           {
340             p = NULL;
341             break;
342           }
343
344         if (!maskcmp(address, &p->net.ipv4.address, p->net.ipv4.prefixlength, sizeof(ipv4_t)))
345           break;
346         else
347           {
348             /* Otherwise, see if there is a bigger enclosing subnet */
349
350             subnet.net.ipv4.prefixlength = p->net.ipv4.prefixlength - 1;
351             maskcpy(&subnet.net.ipv4.address, &p->net.ipv4.address, subnet.net.ipv4.prefixlength, sizeof(ipv4_t));
352           }
353       }
354   } while (p);
355 cp
356   return p;
357 }
358
359 subnet_t *lookup_subnet_ipv6(ipv6_t *address)
360 {
361   subnet_t subnet, *p;
362 cp
363   subnet.type = SUBNET_IPV6;
364   memcpy(&subnet.net.ipv6.address, address, sizeof(ipv6_t));
365   subnet.net.ipv6.prefixlength = 128;
366   subnet.owner = NULL;
367   
368   do
369   {
370     /* Go find subnet */
371   
372     p = (subnet_t *)avl_search_closest_smaller(subnet_tree, &subnet);
373
374     /* Check if the found subnet REALLY matches */
375
376 cp
377     if(p)
378       {
379         if(p->type != SUBNET_IPV6)
380           return NULL;
381
382         if (!maskcmp(address, &p->net.ipv6.address, p->net.ipv6.prefixlength, sizeof(ipv6_t)))
383           break;
384         else
385           {
386             /* Otherwise, see if there is a bigger enclosing subnet */
387
388             subnet.net.ipv6.prefixlength = p->net.ipv6.prefixlength - 1;
389             maskcpy(&subnet.net.ipv6.address, &p->net.ipv6.address, subnet.net.ipv6.prefixlength, sizeof(ipv6_t));
390           }
391       }
392    } while (p);
393 cp   
394   return p;
395 }
396
397 void dump_subnets(void)
398 {
399   char *netstr;
400   subnet_t *subnet;
401   avl_node_t *node;
402 cp
403   syslog(LOG_DEBUG, _("Subnet list:"));
404   for(node = subnet_tree->head; node; node = node->next)
405     {
406       subnet = (subnet_t *)node->data;
407       netstr = net2str(subnet);
408       syslog(LOG_DEBUG, _(" %s owner %s"), netstr, subnet->owner->name);
409       free(netstr);
410     }
411   syslog(LOG_DEBUG, _("End of subnet list."));
412 cp
413 }