Starting to work towards 1.0.11.
[tinc] / src / subnet.c
1 /*
2     subnet.c -- handle subnet lookups and lists
3     Copyright (C) 2000-2009 Guus Sliepen <guus@tinc-vpn.org>,
4                   2000-2005 Ivo Timmermans
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 along
17     with this program; if not, write to the Free Software Foundation, Inc.,
18     51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19 */
20
21 #include "system.h"
22
23 #include "avl_tree.h"
24 #include "device.h"
25 #include "logger.h"
26 #include "net.h"
27 #include "netutl.h"
28 #include "node.h"
29 #include "process.h"
30 #include "subnet.h"
31 #include "utils.h"
32 #include "xalloc.h"
33
34 /* lists type of subnet */
35
36 avl_tree_t *subnet_tree;
37
38 /* Subnet lookup cache */
39
40 static ipv4_t cache_ipv4_address[2];
41 static subnet_t *cache_ipv4_subnet[2];
42 static bool cache_ipv4_valid[2];
43 static int cache_ipv4_slot;
44
45 static ipv6_t cache_ipv6_address[2];
46 static subnet_t *cache_ipv6_subnet[2];
47 static bool cache_ipv6_valid[2];
48 static int cache_ipv6_slot;
49
50 void subnet_cache_flush() {
51         cache_ipv4_valid[0] = cache_ipv4_valid[1] = false;
52         cache_ipv6_valid[0] = cache_ipv6_valid[1] = false;
53 }
54
55 /* Subnet comparison */
56
57 static int subnet_compare_mac(const subnet_t *a, const subnet_t *b) {
58         int result;
59
60         result = memcmp(&a->net.mac.address, &b->net.mac.address, sizeof(mac_t));
61
62         if(result)
63                 return result;
64         
65         result = a->weight - b->weight;
66
67         if(result || !a->owner || !b->owner)
68                 return result;
69
70         return strcmp(a->owner->name, b->owner->name);
71 }
72
73 static int subnet_compare_ipv4(const subnet_t *a, const subnet_t *b) {
74         int result;
75
76         result = b->net.ipv4.prefixlength - a->net.ipv4.prefixlength;
77
78         if(result)
79                 return result;
80
81         result = memcmp(&a->net.ipv4.address, &b->net.ipv4.address, sizeof(ipv4_t));
82
83         if(result)
84                 return result;
85         
86         result = a->weight - b->weight;
87
88         if(result || !a->owner || !b->owner)
89                 return result;
90
91         return strcmp(a->owner->name, b->owner->name);
92 }
93
94 static int subnet_compare_ipv6(const subnet_t *a, const subnet_t *b) {
95         int result;
96
97         result = b->net.ipv6.prefixlength - a->net.ipv6.prefixlength;
98
99         if(result)
100                 return result;
101         
102         result = memcmp(&a->net.ipv6.address, &b->net.ipv6.address, sizeof(ipv6_t));
103
104         if(result)
105                 return result;
106         
107         result = a->weight - b->weight;
108
109         if(result || !a->owner || !b->owner)
110                 return result;
111
112         return strcmp(a->owner->name, b->owner->name);
113 }
114
115 int subnet_compare(const subnet_t *a, const subnet_t *b) {
116         int result;
117
118         result = a->type - b->type;
119
120         if(result)
121                 return result;
122
123         switch (a->type) {
124         case SUBNET_MAC:
125                 return subnet_compare_mac(a, b);
126         case SUBNET_IPV4:
127                 return subnet_compare_ipv4(a, b);
128         case SUBNET_IPV6:
129                 return subnet_compare_ipv6(a, b);
130         default:
131                 logger(LOG_ERR, "subnet_compare() was called with unknown subnet type %d, exitting!",
132                            a->type);
133                 exit(0);
134         }
135
136         return 0;
137 }
138
139 /* Initialising trees */
140
141 void init_subnets(void) {
142         subnet_tree = avl_alloc_tree((avl_compare_t) subnet_compare, (avl_action_t) free_subnet);
143
144         subnet_cache_flush();
145 }
146
147 void exit_subnets(void) {
148         avl_delete_tree(subnet_tree);
149 }
150
151 avl_tree_t *new_subnet_tree(void) {
152         return avl_alloc_tree((avl_compare_t) subnet_compare, NULL);
153 }
154
155 void free_subnet_tree(avl_tree_t *subnet_tree) {
156         avl_delete_tree(subnet_tree);
157 }
158
159 /* Allocating and freeing space for subnets */
160
161 subnet_t *new_subnet(void) {
162         return xmalloc_and_zero(sizeof(subnet_t));
163 }
164
165 void free_subnet(subnet_t *subnet) {
166         free(subnet);
167 }
168
169 /* Adding and removing subnets */
170
171 void subnet_add(node_t *n, subnet_t *subnet) {
172         subnet->owner = n;
173
174         avl_insert(subnet_tree, subnet);
175         avl_insert(n->subnet_tree, subnet);
176
177         subnet_cache_flush();
178 }
179
180 void subnet_del(node_t *n, subnet_t *subnet) {
181         avl_delete(n->subnet_tree, subnet);
182         avl_delete(subnet_tree, subnet);
183
184         subnet_cache_flush();
185 }
186
187 /* Ascii representation of subnets */
188
189 bool str2net(subnet_t *subnet, const char *subnetstr) {
190         int i, l;
191         uint16_t x[8];
192         int weight = 10;
193
194         if(sscanf(subnetstr, "%hu.%hu.%hu.%hu/%d#%d",
195                           &x[0], &x[1], &x[2], &x[3], &l, &weight) >= 5) {
196                 if(l < 0 || l > 32)
197                         return false;
198
199                 subnet->type = SUBNET_IPV4;
200                 subnet->net.ipv4.prefixlength = l;
201                 subnet->weight = weight;
202
203                 for(i = 0; i < 4; i++) {
204                         if(x[i] > 255)
205                                 return false;
206                         subnet->net.ipv4.address.x[i] = x[i];
207                 }
208
209                 return true;
210         }
211
212         if(sscanf(subnetstr, "%hx:%hx:%hx:%hx:%hx:%hx:%hx:%hx/%d#%d",
213                           &x[0], &x[1], &x[2], &x[3], &x[4], &x[5], &x[6], &x[7],
214                           &l, &weight) >= 9) {
215                 if(l < 0 || l > 128)
216                         return false;
217
218                 subnet->type = SUBNET_IPV6;
219                 subnet->net.ipv6.prefixlength = l;
220                 subnet->weight = weight;
221
222                 for(i = 0; i < 8; i++)
223                         subnet->net.ipv6.address.x[i] = htons(x[i]);
224
225                 return true;
226         }
227
228         if(sscanf(subnetstr, "%hu.%hu.%hu.%hu#%d", &x[0], &x[1], &x[2], &x[3], &weight) >= 4) {
229                 subnet->type = SUBNET_IPV4;
230                 subnet->net.ipv4.prefixlength = 32;
231                 subnet->weight = weight;
232
233                 for(i = 0; i < 4; i++) {
234                         if(x[i] > 255)
235                                 return false;
236                         subnet->net.ipv4.address.x[i] = x[i];
237                 }
238
239                 return true;
240         }
241
242         if(sscanf(subnetstr, "%hx:%hx:%hx:%hx:%hx:%hx:%hx:%hx#%d",
243                           &x[0], &x[1], &x[2], &x[3], &x[4], &x[5], &x[6], &x[7], &weight) >= 8) {
244                 subnet->type = SUBNET_IPV6;
245                 subnet->net.ipv6.prefixlength = 128;
246                 subnet->weight = weight;
247
248                 for(i = 0; i < 8; i++)
249                         subnet->net.ipv6.address.x[i] = htons(x[i]);
250
251                 return true;
252         }
253
254         if(sscanf(subnetstr, "%hx:%hx:%hx:%hx:%hx:%hx#%d",
255                           &x[0], &x[1], &x[2], &x[3], &x[4], &x[5], &weight) >= 6) {
256                 subnet->type = SUBNET_MAC;
257                 subnet->weight = weight;
258
259                 for(i = 0; i < 6; i++)
260                         subnet->net.mac.address.x[i] = x[i];
261
262                 return true;
263         }
264
265         return false;
266 }
267
268 bool net2str(char *netstr, int len, const subnet_t *subnet) {
269         if(!netstr || !subnet) {
270                 logger(LOG_ERR, "net2str() was called with netstr=%p, subnet=%p!\n", netstr, subnet);
271                 return false;
272         }
273
274         switch (subnet->type) {
275                 case SUBNET_MAC:
276                         snprintf(netstr, len, "%hx:%hx:%hx:%hx:%hx:%hx#%d",
277                                          subnet->net.mac.address.x[0],
278                                          subnet->net.mac.address.x[1],
279                                          subnet->net.mac.address.x[2],
280                                          subnet->net.mac.address.x[3],
281                                          subnet->net.mac.address.x[4],
282                                          subnet->net.mac.address.x[5],
283                                          subnet->weight);
284                         break;
285
286                 case SUBNET_IPV4:
287                         snprintf(netstr, len, "%hu.%hu.%hu.%hu/%d#%d",
288                                          subnet->net.ipv4.address.x[0],
289                                          subnet->net.ipv4.address.x[1],
290                                          subnet->net.ipv4.address.x[2],
291                                          subnet->net.ipv4.address.x[3],
292                                          subnet->net.ipv4.prefixlength,
293                                          subnet->weight);
294                         break;
295
296                 case SUBNET_IPV6:
297                         snprintf(netstr, len, "%hx:%hx:%hx:%hx:%hx:%hx:%hx:%hx/%d#%d",
298                                          ntohs(subnet->net.ipv6.address.x[0]),
299                                          ntohs(subnet->net.ipv6.address.x[1]),
300                                          ntohs(subnet->net.ipv6.address.x[2]),
301                                          ntohs(subnet->net.ipv6.address.x[3]),
302                                          ntohs(subnet->net.ipv6.address.x[4]),
303                                          ntohs(subnet->net.ipv6.address.x[5]),
304                                          ntohs(subnet->net.ipv6.address.x[6]),
305                                          ntohs(subnet->net.ipv6.address.x[7]),
306                                          subnet->net.ipv6.prefixlength,
307                                          subnet->weight);
308                         break;
309
310                 default:
311                         logger(LOG_ERR,
312                                    "net2str() was called with unknown subnet type %d, exiting!",
313                                    subnet->type);
314                         exit(0);
315         }
316
317         return true;
318 }
319
320 /* Subnet lookup routines */
321
322 subnet_t *lookup_subnet(const node_t *owner, const subnet_t *subnet) {
323         return avl_search(owner->subnet_tree, subnet);
324 }
325
326 subnet_t *lookup_subnet_mac(const mac_t *address) {
327         subnet_t *p, subnet = {0};
328
329         subnet.type = SUBNET_MAC;
330         subnet.net.mac.address = *address;
331         subnet.owner = NULL;
332
333         p = avl_search(subnet_tree, &subnet);
334
335         return p;
336 }
337
338 subnet_t *lookup_subnet_ipv4(const ipv4_t *address) {
339         subnet_t *p, *r = NULL, subnet = {0};
340         avl_node_t *n;
341         int i;
342
343         // Check if this address is cached
344
345         for(i = 0; i < 2; i++) {
346                 if(!cache_ipv4_valid[i])
347                         continue;
348                 if(!memcmp(address, &cache_ipv4_address[i], sizeof *address))
349                         return cache_ipv4_subnet[i];
350         }
351
352         // Search all subnets for a matching one
353
354         subnet.type = SUBNET_IPV4;
355         subnet.net.ipv4.address = *address;
356         subnet.net.ipv4.prefixlength = 32;
357         subnet.owner = NULL;
358
359         for(n = subnet_tree->head; n; n = n->next) {
360                 p = n->data;
361                 
362                 if(!p || p->type != subnet.type)
363                         continue;
364
365                 if(!maskcmp(address, &p->net.ipv4.address, p->net.ipv4.prefixlength)) {
366                         r = p;
367                         if(p->owner->status.reachable)
368                                 break;
369                 }
370         }
371
372         // Cache the result
373
374         cache_ipv4_slot = !cache_ipv4_slot;
375         memcpy(&cache_ipv4_address[cache_ipv4_slot], address, sizeof *address);
376         cache_ipv4_subnet[cache_ipv4_slot] = r;
377         cache_ipv4_valid[cache_ipv4_slot] = true;
378
379         return r;
380 }
381
382 subnet_t *lookup_subnet_ipv6(const ipv6_t *address) {
383         subnet_t *p, *r = NULL, subnet = {0};
384         avl_node_t *n;
385         int i;
386
387         // Check if this address is cached
388
389         for(i = 0; i < 2; i++) {
390                 if(!cache_ipv6_valid[i])
391                         continue;
392                 if(!memcmp(address, &cache_ipv6_address[i], sizeof *address))
393                         return cache_ipv6_subnet[i];
394         }
395
396         // Search all subnets for a matching one
397
398         subnet.type = SUBNET_IPV6;
399         subnet.net.ipv6.address = *address;
400         subnet.net.ipv6.prefixlength = 128;
401         subnet.owner = NULL;
402
403         for(n = subnet_tree->head; n; n = n->next) {
404                 p = n->data;
405                 
406                 if(!p || p->type != subnet.type)
407                         continue;
408
409                 if(!maskcmp(address, &p->net.ipv6.address, p->net.ipv6.prefixlength)) {
410                         r = p;
411                         if(p->owner->status.reachable)
412                                 break;
413                 }
414         }
415
416         // Cache the result
417
418         cache_ipv6_slot = !cache_ipv6_slot;
419         memcpy(&cache_ipv6_address[cache_ipv6_slot], address, sizeof *address);
420         cache_ipv6_subnet[cache_ipv6_slot] = r;
421         cache_ipv6_valid[cache_ipv6_slot] = true;
422
423         return r;
424 }
425
426 void subnet_update(node_t *owner, subnet_t *subnet, bool up) {
427         avl_node_t *node;
428         int i;
429         char *envp[9] = {0};
430         char netstr[MAXNETSTR];
431         char *name, *address, *port;
432         char empty[] = "";
433
434         // Prepare environment variables to be passed to the script
435
436         xasprintf(&envp[0], "NETNAME=%s", netname ? : "");
437         xasprintf(&envp[1], "DEVICE=%s", device ? : "");
438         xasprintf(&envp[2], "INTERFACE=%s", iface ? : "");
439         xasprintf(&envp[3], "NODE=%s", owner->name);
440
441         if(owner != myself) {
442                 sockaddr2str(&owner->address, &address, &port);
443                 // 4 and 5 are reserved for SUBNET and WEIGHT
444                 xasprintf(&envp[6], "REMOTEADDRESS=%s", address);
445                 xasprintf(&envp[7], "REMOTEPORT=%s", port);
446         }
447
448         name = up ? "subnet-up" : "subnet-down";
449
450         if(!subnet) {
451                 for(node = owner->subnet_tree->head; node; node = node->next) {
452                         subnet = node->data;
453                         if(!net2str(netstr, sizeof netstr, subnet))
454                                 continue;
455                         // Strip the weight from the subnet, and put it in its own environment variable
456                         char *weight = strchr(netstr + 7, '#');
457                         if(weight)
458                                 *weight++ = 0;
459                         else
460                                 weight = empty;
461
462                         // Prepare the SUBNET and WEIGHT variables
463                         if(envp[4])
464                                 free(envp[4]);
465                         if(envp[5])
466                                 free(envp[5]);
467                         xasprintf(&envp[4], "SUBNET=%s", netstr);
468                         xasprintf(&envp[5], "WEIGHT=%s", weight);
469
470                         execute_script(name, envp);
471                 }
472         } else {
473                 if(net2str(netstr + 7, sizeof netstr - 7, subnet)) {
474                         // Strip the weight from the subnet, and put it in its own environment variable
475                         char *weight = strchr(netstr + 7, '#');
476                         if(weight)
477                                 *weight++ = 0;
478                         else
479                                 weight = empty;
480
481                         // Prepare the SUBNET and WEIGHT variables
482                         xasprintf(&envp[4], "SUBNET=%s", netstr);
483                         xasprintf(&envp[5], "WEIGHT=%s", weight);
484
485                         execute_script(name, envp);
486                 }
487         }
488
489         for(i = 0; envp[i] && i < 9; i++)
490                 free(envp[i]);
491 }
492
493 void dump_subnets(void) {
494         char netstr[MAXNETSTR];
495         subnet_t *subnet;
496         avl_node_t *node;
497
498         logger(LOG_DEBUG, "Subnet list:");
499
500         for(node = subnet_tree->head; node; node = node->next) {
501                 subnet = node->data;
502                 if(!net2str(netstr, sizeof netstr, subnet))
503                         continue;
504                 logger(LOG_DEBUG, " %s owner %s", netstr, subnet->owner->name);
505         }
506
507         logger(LOG_DEBUG, "End of subnet list.");
508 }