Use splay trees inside node_t directly.
[tinc] / src / subnet.c
1 /*
2     subnet.c -- handle subnet lookups and lists
3     Copyright (C) 2000-2017 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 "splay_tree.h"
24 #include "control_common.h"
25 #include "hash.h"
26 #include "logger.h"
27 #include "net.h"
28 #include "netutl.h"
29 #include "node.h"
30 #include "script.h"
31 #include "subnet.h"
32 #include "xalloc.h"
33
34 /* lists type of subnet */
35
36 splay_tree_t subnet_tree = {
37         .compare = (splay_compare_t) subnet_compare,
38         .delete = (splay_action_t) free_subnet,
39 };
40
41 /* Subnet lookup cache */
42
43 static hash_t *ipv4_cache;
44 static hash_t *ipv6_cache;
45 static hash_t *mac_cache;
46
47 void subnet_cache_flush(void) {
48         hash_clear(ipv4_cache);
49         hash_clear(ipv6_cache);
50         hash_clear(mac_cache);
51 }
52
53 /* Initialising trees */
54
55 void init_subnets(void) {
56         ipv4_cache = hash_alloc(0x100, sizeof(ipv4_t));
57         ipv6_cache = hash_alloc(0x100, sizeof(ipv6_t));
58         mac_cache = hash_alloc(0x100, sizeof(mac_t));
59 }
60
61 void exit_subnets(void) {
62         splay_empty_tree(&subnet_tree);
63
64         hash_free(ipv4_cache);
65         hash_free(ipv6_cache);
66         hash_free(mac_cache);
67 }
68
69 void init_subnet_tree(splay_tree_t *tree) {
70         memset(tree, 0, sizeof(*tree));
71         tree->compare = (splay_compare_t) subnet_compare;
72 }
73
74 /* Allocating and freeing space for subnets */
75
76 subnet_t *new_subnet(void) {
77         return xzalloc(sizeof(subnet_t));
78 }
79
80 void free_subnet(subnet_t *subnet) {
81         free(subnet);
82 }
83
84 /* Adding and removing subnets */
85
86 void subnet_add(node_t *n, subnet_t *subnet) {
87         subnet->owner = n;
88
89         splay_insert(&subnet_tree, subnet);
90
91         if(n) {
92                 splay_insert(&n->subnet_tree, subnet);
93         }
94
95         subnet_cache_flush();
96 }
97
98 void subnet_del(node_t *n, subnet_t *subnet) {
99         if(n) {
100                 splay_delete(&n->subnet_tree, subnet);
101         }
102
103         splay_delete(&subnet_tree, subnet);
104
105         subnet_cache_flush();
106 }
107
108 /* Subnet lookup routines */
109
110 subnet_t *lookup_subnet(node_t *owner, const subnet_t *subnet) {
111         return splay_search(&owner->subnet_tree, subnet);
112 }
113
114 subnet_t *lookup_subnet_mac(const node_t *owner, const mac_t *address) {
115         subnet_t *r = NULL;
116
117         // Check if this address is cached
118
119         if((r = hash_search(mac_cache, address))) {
120                 return r;
121         }
122
123         // Search all subnets for a matching one
124
125         for splay_each(subnet_t, p, owner ? &owner->subnet_tree : &subnet_tree) {
126                 if(!p || p->type != SUBNET_MAC) {
127                         continue;
128                 }
129
130                 if(!memcmp(address, &p->net.mac.address, sizeof(*address))) {
131                         r = p;
132
133                         if(!p->owner || p->owner->status.reachable) {
134                                 break;
135                         }
136                 }
137         }
138
139         // Cache the result
140
141         if(r) {
142                 hash_insert(mac_cache, address, r);
143         }
144
145         return r;
146 }
147
148 subnet_t *lookup_subnet_ipv4(const ipv4_t *address) {
149         subnet_t *r = NULL;
150
151         // Check if this address is cached
152
153         if((r = hash_search(ipv4_cache, address))) {
154                 return r;
155         }
156
157         // Search all subnets for a matching one
158
159         for splay_each(subnet_t, p, &subnet_tree) {
160                 if(!p || p->type != SUBNET_IPV4) {
161                         continue;
162                 }
163
164                 if(!maskcmp(address, &p->net.ipv4.address, p->net.ipv4.prefixlength)) {
165                         r = p;
166
167                         if(!p->owner || p->owner->status.reachable) {
168                                 break;
169                         }
170                 }
171         }
172
173         // Cache the result
174
175         if(r) {
176                 hash_insert(ipv4_cache, address, r);
177         }
178
179         return r;
180 }
181
182 subnet_t *lookup_subnet_ipv6(const ipv6_t *address) {
183         subnet_t *r = NULL;
184
185         // Check if this address is cached
186
187         if((r = hash_search(ipv6_cache, address))) {
188                 return r;
189         }
190
191         // Search all subnets for a matching one
192
193         for splay_each(subnet_t, p, &subnet_tree) {
194                 if(!p || p->type != SUBNET_IPV6) {
195                         continue;
196                 }
197
198                 if(!maskcmp(address, &p->net.ipv6.address, p->net.ipv6.prefixlength)) {
199                         r = p;
200
201                         if(!p->owner || p->owner->status.reachable) {
202                                 break;
203                         }
204                 }
205         }
206
207         // Cache the result
208
209         if(r) {
210                 hash_insert(ipv6_cache, address, r);
211         }
212
213         return r;
214 }
215
216 void subnet_update(node_t *owner, subnet_t *subnet, bool up) {
217         char netstr[MAXNETSTR];
218         char *name, *address, *port;
219         char empty[] = "";
220
221         // Prepare environment variables to be passed to the script
222
223         environment_t env;
224         environment_init(&env);
225         environment_add(&env, "NODE=%s", owner->name);
226
227         if(owner != myself) {
228                 sockaddr2str(&owner->address, &address, &port);
229                 environment_add(&env, "REMOTEADDRESS=%s", address);
230                 environment_add(&env, "REMOTEPORT=%s", port);
231                 free(port);
232                 free(address);
233         }
234
235         int env_subnet = environment_add(&env, NULL);
236         int env_weight = environment_add(&env, NULL);
237
238         name = up ? "subnet-up" : "subnet-down";
239
240         if(!subnet) {
241                 for splay_each(subnet_t, subnet, &owner->subnet_tree) {
242                         if(!net2str(netstr, sizeof(netstr), subnet)) {
243                                 continue;
244                         }
245
246                         // Strip the weight from the subnet, and put it in its own environment variable
247                         char *weight = strchr(netstr, '#');
248
249                         if(weight) {
250                                 *weight++ = 0;
251                         } else {
252                                 weight = empty;
253                         }
254
255                         // Prepare the SUBNET and WEIGHT variables
256                         environment_update(&env, env_subnet, "SUBNET=%s", netstr);
257                         environment_update(&env, env_weight, "WEIGHT=%s", weight);
258
259                         execute_script(name, &env);
260                 }
261         } else {
262                 if(net2str(netstr, sizeof(netstr), subnet)) {
263                         // Strip the weight from the subnet, and put it in its own environment variable
264                         char *weight = strchr(netstr, '#');
265
266                         if(weight) {
267                                 *weight++ = 0;
268                         } else {
269                                 weight = empty;
270                         }
271
272                         // Prepare the SUBNET and WEIGHT variables
273                         environment_update(&env, env_subnet, "SUBNET=%s", netstr);
274                         environment_update(&env, env_weight, "WEIGHT=%s", weight);
275
276                         execute_script(name, &env);
277                 }
278         }
279
280         environment_exit(&env);
281 }
282
283 bool dump_subnets(connection_t *c) {
284         for splay_each(subnet_t, subnet, &subnet_tree) {
285                 char netstr[MAXNETSTR];
286
287                 if(!net2str(netstr, sizeof(netstr), subnet)) {
288                         continue;
289                 }
290
291                 send_request(c, "%d %d %s %s",
292                              CONTROL, REQ_DUMP_SUBNETS,
293                              netstr, subnet->owner ? subnet->owner->name : "(broadcast)");
294         }
295
296         return send_request(c, "%d %d", CONTROL, REQ_DUMP_SUBNETS);
297 }