f4b147411dd287deb32af5a35e5cb30ea23c5800
[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.37 2002/06/21 10:11: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 cp
54   return memcmp(&a->net.mac.address, &b->net.mac.address, sizeof(mac_t));
55 }
56
57 int subnet_compare_ipv4(subnet_t *a, subnet_t *b)
58 {
59   int result;
60 cp
61   result = memcmp(&a->net.ipv4.address, &b->net.ipv4.address, sizeof(ipv4_t));
62   
63   if(result)
64     return result;
65
66   return a->net.ipv4.prefixlength - b->net.ipv4.prefixlength;
67 }
68
69 int subnet_compare_ipv6(subnet_t *a, subnet_t *b)
70 {
71   int result;
72 cp
73   result = memcmp(&a->net.ipv6.address, &b->net.ipv6.address, sizeof(ipv6_t));
74   
75   if(result)
76     return result;
77
78   return a->net.ipv6.prefixlength - b->net.ipv6.prefixlength;
79 }
80
81 int subnet_compare(subnet_t *a, subnet_t *b)
82 {
83   int result;
84 cp  
85   result = a->type - b->type;
86  
87   if(result)
88     return result;
89     
90   switch(a->type)
91     {
92       case SUBNET_MAC:
93         return subnet_compare_mac(a, b);
94       case SUBNET_IPV4:
95         return subnet_compare_ipv4(a, b);
96       case SUBNET_IPV6:
97         return subnet_compare_ipv6(a, b);
98       default:
99         syslog(LOG_ERR, _("subnet_compare() was called with unknown subnet type %d, exitting!"), a->type);
100         cp_trace();
101         exit(0);
102     }
103
104   return 0;
105 }
106
107 /* Initialising trees */
108
109 void init_subnets(void)
110 {
111 cp
112   subnet_tree = avl_alloc_tree((avl_compare_t)subnet_compare, (avl_action_t)free_subnet);
113 cp
114 }
115
116 void exit_subnets(void)
117 {
118 cp
119   avl_delete_tree(subnet_tree);
120 cp
121 }
122
123 avl_tree_t *new_subnet_tree(void)
124 {
125 cp
126   return avl_alloc_tree((avl_compare_t)subnet_compare, NULL);
127 cp
128 }
129
130 void free_subnet_tree(avl_tree_t *subnet_tree)
131 {
132 cp
133   avl_delete_tree(subnet_tree);
134 cp
135 }
136
137 /* Allocating and freeing space for subnets */
138
139 subnet_t *new_subnet(void)
140 {
141 cp
142   return (subnet_t *)xmalloc(sizeof(subnet_t));
143 }
144
145 void free_subnet(subnet_t *subnet)
146 {
147 cp
148   free(subnet);
149 }
150
151 /* Adding and removing subnets */
152
153 void subnet_add(node_t *n, subnet_t *subnet)
154 {
155 cp
156   subnet->owner = n;
157
158   avl_insert(subnet_tree, subnet);
159 cp
160   avl_insert(n->subnet_tree, subnet);
161 cp
162 }
163
164 void subnet_del(node_t *n, subnet_t *subnet)
165 {
166 cp
167   avl_delete(n->subnet_tree, subnet);
168 cp
169   avl_delete(subnet_tree, subnet);
170 cp
171 }
172
173 /* Ascii representation of subnets */
174
175 subnet_t *str2net(char *subnetstr)
176 {
177   int i, l;
178   subnet_t *subnet;
179   uint16_t x[8];
180 cp
181   subnet = new_subnet();
182 cp
183   if(sscanf(subnetstr, "%hu.%hu.%hu.%hu/%d",
184               &x[0], &x[1], &x[2], &x[3],
185               &l) == 5)
186     {
187       subnet->type = SUBNET_IPV4;
188       subnet->net.ipv4.prefixlength = l;
189       for(i = 0; i < 4; i++)
190         subnet->net.ipv4.address.x[i] = x[i];
191       return subnet;
192     }
193               
194   if(sscanf(subnetstr, "%hx:%hx:%hx:%hx:%hx:%hx:%hx:%hx/%d",
195              &x[0], &x[1], &x[2], &x[3], &x[4], &x[5], &x[6], &x[7],
196              &l) == 9)
197     {
198       subnet->type = SUBNET_IPV6;
199       subnet->net.ipv6.prefixlength = l;
200       for(i = 0; i < 8; i++)
201         subnet->net.ipv6.address.x[i] = htons(x[i]);
202       return subnet;
203     }
204
205   if(sscanf(subnetstr, "%hu.%hu.%hu.%hu",
206               &x[0], &x[1], &x[2], &x[3]) == 4)
207     {
208       subnet->type = SUBNET_IPV4;
209       subnet->net.ipv4.prefixlength = 32;
210       for(i = 0; i < 4; i++)
211         subnet->net.ipv4.address.x[i] = x[i];
212       return subnet;
213     }
214               
215   if(sscanf(subnetstr, "%hx:%hx:%hx:%hx:%hx:%hx:%hx:%hx",
216              &x[0], &x[1], &x[2], &x[3], &x[4], &x[5], &x[6], &x[7]) == 8)
217     {
218       subnet->type = SUBNET_IPV6;
219       subnet->net.ipv6.prefixlength = 128;
220       for(i = 0; i < 8; i++)
221         subnet->net.ipv6.address.x[i] = htons(x[i]);
222       return subnet;
223     }
224
225   if(sscanf(subnetstr, "%hx:%hx:%hx:%hx:%hx:%hx",
226               &x[0], &x[1], &x[2], &x[3], &x[4], &x[5]) == 6)
227     {
228       subnet->type = SUBNET_MAC;
229       for(i = 0; i < 6; i++)
230         subnet->net.mac.address.x[i] = x[i];
231       return subnet;
232     }
233
234   free(subnet);
235   return NULL;
236 }
237
238 char *net2str(subnet_t *subnet)
239 {
240   char *netstr;
241 cp
242   switch(subnet->type)
243     {
244       case SUBNET_MAC:
245         asprintf(&netstr, "%hx:%hx:%hx:%hx:%hx:%hx",
246                    subnet->net.mac.address.x[0],
247                    subnet->net.mac.address.x[1],
248                    subnet->net.mac.address.x[2],
249                    subnet->net.mac.address.x[3],
250                    subnet->net.mac.address.x[4],
251                    subnet->net.mac.address.x[5]);
252         break;
253       case SUBNET_IPV4:
254         asprintf(&netstr, "%hu.%hu.%hu.%hu/%d",
255                    subnet->net.ipv4.address.x[0],
256                    subnet->net.ipv4.address.x[1],
257                    subnet->net.ipv4.address.x[2],
258                    subnet->net.ipv4.address.x[3],
259                    subnet->net.ipv4.prefixlength);
260         break;
261       case SUBNET_IPV6:
262         asprintf(&netstr, "%hx:%hx:%hx:%hx:%hx:%hx:%hx:%hx/%d",
263                    ntohs(subnet->net.ipv6.address.x[0]),
264                    ntohs(subnet->net.ipv6.address.x[1]),
265                    ntohs(subnet->net.ipv6.address.x[2]),
266                    ntohs(subnet->net.ipv6.address.x[3]),
267                    ntohs(subnet->net.ipv6.address.x[4]),
268                    ntohs(subnet->net.ipv6.address.x[5]),
269                    ntohs(subnet->net.ipv6.address.x[6]),
270                    ntohs(subnet->net.ipv6.address.x[7]),
271                    subnet->net.ipv6.prefixlength);
272         break;
273       default:
274         syslog(LOG_ERR, _("net2str() was called with unknown subnet type %d, exiting!"), subnet->type);
275         cp_trace();
276         exit(0);
277     }
278 cp
279   return netstr;
280 }
281
282 /* Subnet lookup routines */
283
284 subnet_t *lookup_subnet(node_t *owner, subnet_t *subnet)
285 {
286 cp  
287   return avl_search(owner->subnet_tree, subnet);
288 }
289
290 subnet_t *lookup_subnet_mac(mac_t *address)
291 {
292   subnet_t subnet, *p;
293 cp
294   subnet.type = SUBNET_MAC;
295   memcpy(&subnet.net.mac.address, address, sizeof(mac_t));
296
297   p = (subnet_t *)avl_search(subnet_tree, &subnet);
298 cp
299   return p;
300 }
301
302 subnet_t *lookup_subnet_ipv4(ipv4_t *address)
303 {
304   subnet_t subnet, *p;
305 cp
306   subnet.type = SUBNET_IPV4;
307   memcpy(&subnet.net.ipv4.address, address, sizeof(ipv4_t));
308   subnet.net.ipv4.prefixlength = 32;
309
310   do
311   {
312     /* Go find subnet */
313   
314     p = (subnet_t *)avl_search_closest_smaller(subnet_tree, &subnet);
315
316   /* Check if the found subnet REALLY matches */
317 cp
318     if(p)
319       {
320         if(p->type != SUBNET_IPV4)
321           {
322             p = NULL;
323             break;
324           }
325
326         if (!maskcmp(address, &p->net.ipv4.address, p->net.ipv4.prefixlength, sizeof(ipv4_t)))
327           break;
328         else
329           {
330             /* Otherwise, see if there is a bigger enclosing subnet */
331
332             subnet.net.ipv4.prefixlength = p->net.ipv4.prefixlength - 1;
333             maskcpy(&subnet.net.ipv4.address, &p->net.ipv4.address, subnet.net.ipv4.prefixlength, sizeof(ipv4_t));
334           }
335       }
336   } while (p);
337 cp
338   return p;
339 }
340
341 subnet_t *lookup_subnet_ipv6(ipv6_t *address)
342 {
343   subnet_t subnet, *p;
344 cp
345   subnet.type = SUBNET_IPV6;
346   memcpy(&subnet.net.ipv6.address, address, sizeof(ipv6_t));
347   subnet.net.ipv6.prefixlength = 128;
348   
349   do
350   {
351     /* Go find subnet */
352   
353     p = (subnet_t *)avl_search_closest_smaller(subnet_tree, &subnet);
354
355     /* Check if the found subnet REALLY matches */
356
357 cp
358     if(p)
359       {
360         if(p->type != SUBNET_IPV6)
361           return NULL;
362
363         if (!maskcmp(address, &p->net.ipv6.address, p->net.ipv6.prefixlength, sizeof(ipv6_t)))
364           break;
365         else
366           {
367             /* Otherwise, see if there is a bigger enclosing subnet */
368
369             subnet.net.ipv6.prefixlength = p->net.ipv6.prefixlength - 1;
370             maskcpy(&subnet.net.ipv6.address, &p->net.ipv6.address, subnet.net.ipv6.prefixlength, sizeof(ipv6_t));
371           }
372       }
373    } while (p);
374 cp   
375   return p;
376 }
377
378 void dump_subnets(void)
379 {
380   char *netstr;
381   subnet_t *subnet;
382   avl_node_t *node;
383 cp
384   syslog(LOG_DEBUG, _("Subnet list:"));
385   for(node = subnet_tree->head; node; node = node->next)
386     {
387       subnet = (subnet_t *)node->data;
388       netstr = net2str(subnet);
389       syslog(LOG_DEBUG, _(" %s owner %s"), netstr, subnet->owner->name);
390       free(netstr);
391     }
392   syslog(LOG_DEBUG, _("End of subnet list."));
393 cp
394 }