BadVPN – Blame information for rev 1

Subversion Repositories:
Rev:
Rev Author Line No. Line
1 office 1 /**
2 * @file net_watch_interfaces.c
3 * @author Ambroz Bizjak <ambrop7@gmail.com>
4 *
5 * @section LICENSE
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions are met:
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * 3. Neither the name of the author nor the
15 * names of its contributors may be used to endorse or promote products
16 * derived from this software without specific prior written permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
19 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
20 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21 * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
22 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
23 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
24 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
25 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
27 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 *
29 * @section DESCRIPTION
30 *
31 * Network interface watcher.
32 *
33 * Synopsis: net.watch_interfaces()
34 * Description: reports network interface events. Transitions up when an event is detected, and
35 * goes down waiting for the next event when net.watch_interfaces::nextevent() is called.
36 * On startup, "added" events are reported for existing interfaces.
37 * Variables:
38 * string event_type - what happened with the interface: "added" or "removed". This may not be
39 * consistent across events.
40 * string devname - interface name
41 * string bus - bus location, for example "pci:0000:06:00.0", "usb:2-1.3:1.0", or "unknown"
42 *
43 * Synopsis: net.watch_interfaces::nextevent()
44 * Description: makes the watch_interfaces module transition down in order to report the next event.
45 */
46  
47 #include <stdlib.h>
48 #include <string.h>
49 #include <regex.h>
50  
51 #include <misc/debug.h>
52 #include <misc/offset.h>
53 #include <misc/parse_number.h>
54 #include <misc/bsize.h>
55 #include <structure/LinkedList1.h>
56 #include <udevmonitor/NCDUdevManager.h>
57 #include <ncd/modules/event_template.h>
58  
59 #include <ncd/module_common.h>
60  
61 #include <generated/blog_channel_ncd_net_watch_interfaces.h>
62  
63 #define DEVPATH_REGEX "/net/[^/]+$"
64 #define DEVPATH_USB_REGEX "/usb[^/]*(/[^/]+)+/([^/]+)/net/[^/]+$"
65 #define DEVPATH_PCI_REGEX "/pci[^/]*/[^/]+/([^/]+)/net/[^/]+$"
66  
67 struct device {
68 char *ifname;
69 char *devpath;
70 uintmax_t ifindex;
71 BStringMap removed_map;
72 LinkedList1Node devices_list_node;
73 };
74  
75 struct instance {
76 NCDModuleInst *i;
77 NCDUdevClient client;
78 LinkedList1 devices_list;
79 regex_t reg;
80 regex_t usb_reg;
81 regex_t pci_reg;
82 event_template templ;
83 };
84  
85 static void templ_func_free (struct instance *o, int is_error);
86  
87 static struct device * find_device_by_ifname (struct instance *o, const char *ifname)
88 {
89 LinkedList1Node *list_node = LinkedList1_GetFirst(&o->devices_list);
90 while (list_node) {
91 struct device *device = UPPER_OBJECT(list_node, struct device, devices_list_node);
92 if (!strcmp(device->ifname, ifname)) {
93 return device;
94 }
95 list_node = LinkedList1Node_Next(list_node);
96 }
97  
98 return NULL;
99 }
100  
101 static struct device * find_device_by_devpath (struct instance *o, const char *devpath)
102 {
103 LinkedList1Node *list_node = LinkedList1_GetFirst(&o->devices_list);
104 while (list_node) {
105 struct device *device = UPPER_OBJECT(list_node, struct device, devices_list_node);
106 if (!strcmp(device->devpath, devpath)) {
107 return device;
108 }
109 list_node = LinkedList1Node_Next(list_node);
110 }
111  
112 return NULL;
113 }
114  
115 static void free_device (struct instance *o, struct device *device, int have_removed_map)
116 {
117 // remove from devices list
118 LinkedList1_Remove(&o->devices_list, &device->devices_list_node);
119  
120 // free removed map
121 if (have_removed_map) {
122 BStringMap_Free(&device->removed_map);
123 }
124  
125 // free devpath
126 free(device->devpath);
127  
128 // free ifname
129 free(device->ifname);
130  
131 // free structure
132 free(device);
133 }
134  
135 static int make_event_map (struct instance *o, int added, const char *ifname, const char *bus, BStringMap *out_map)
136 {
137 // init map
138 BStringMap map;
139 BStringMap_Init(&map);
140  
141 // set type
142 if (!BStringMap_Set(&map, "event_type", (added ? "added" : "removed"))) {
143 ModuleLog(o->i, BLOG_ERROR, "BStringMap_Set failed");
144 goto fail1;
145 }
146  
147 // set ifname
148 if (!BStringMap_Set(&map, "devname", ifname)) {
149 ModuleLog(o->i, BLOG_ERROR, "BStringMap_Set failed");
150 goto fail1;
151 }
152  
153 // set bus
154 if (!BStringMap_Set(&map, "bus", bus)) {
155 ModuleLog(o->i, BLOG_ERROR, "BStringMap_Set failed");
156 goto fail1;
157 }
158  
159 *out_map = map;
160 return 1;
161  
162 fail1:
163 BStringMap_Free(&map);
164 return 0;
165 }
166  
167 static void queue_event (struct instance *o, BStringMap map)
168 {
169 // pass event to template
170 int was_empty;
171 event_template_queue(&o->templ, map, &was_empty);
172  
173 // if event queue was empty, stop receiving udev events
174 if (was_empty) {
175 NCDUdevClient_Pause(&o->client);
176 }
177 }
178  
179 static void add_device (struct instance *o, const char *ifname, const char *devpath, uintmax_t ifindex, const char *bus)
180 {
181 ASSERT(!find_device_by_ifname(o, ifname))
182 ASSERT(!find_device_by_devpath(o, devpath))
183  
184 // allocate structure
185 struct device *device = malloc(sizeof(*device));
186 if (!device) {
187 ModuleLog(o->i, BLOG_ERROR, "malloc failed");
188 goto fail0;
189 }
190  
191 // init ifname
192 if (!(device->ifname = strdup(ifname))) {
193 ModuleLog(o->i, BLOG_ERROR, "strdup failed");
194 goto fail1;
195 }
196  
197 // init devpath
198 if (!(device->devpath = strdup(devpath))) {
199 ModuleLog(o->i, BLOG_ERROR, "strdup failed");
200 goto fail2;
201 }
202  
203 // set ifindex
204 device->ifindex = ifindex;
205  
206 // init removed map
207 if (!make_event_map(o, 0, ifname, bus, &device->removed_map)) {
208 ModuleLog(o->i, BLOG_ERROR, "make_event_map failed");
209 goto fail3;
210 }
211  
212 // init added map
213 BStringMap added_map;
214 if (!make_event_map(o, 1, ifname, bus, &added_map)) {
215 ModuleLog(o->i, BLOG_ERROR, "make_event_map failed");
216 goto fail4;
217 }
218  
219 // insert to devices list
220 LinkedList1_Append(&o->devices_list, &device->devices_list_node);
221  
222 // queue event
223 queue_event(o, added_map);
224  
225 return;
226  
227 fail4:
228 BStringMap_Free(&device->removed_map);
229 fail3:
230 free(device->devpath);
231 fail2:
232 free(device->ifname);
233 fail1:
234 free(device);
235 fail0:
236 ModuleLog(o->i, BLOG_ERROR, "failed to add device %s", ifname);
237 }
238  
239 static void remove_device (struct instance *o, struct device *device)
240 {
241 queue_event(o, device->removed_map);
242 free_device(o, device, 0);
243 }
244  
245 static void next_event (struct instance *o)
246 {
247 ASSERT(event_template_is_enabled(&o->templ))
248  
249 // order template to finish the current event
250 int is_empty;
251 event_template_dequeue(&o->templ, &is_empty);
252  
253 // if template has no events, continue udev events
254 if (is_empty) {
255 NCDUdevClient_Continue(&o->client);
256 }
257 }
258  
259 static void make_bus (struct instance *o, const char *devpath, const BStringMap *map, char *out_bus, size_t bus_avail)
260 {
261 regmatch_t pmatch[3];
262  
263 const char *type;
264 const char *id;
265 size_t id_len;
266  
267 if (!regexec(&o->usb_reg, devpath, 3, pmatch, 0)) {
268 type = "usb";
269 id = devpath + pmatch[2].rm_so;
270 id_len = pmatch[2].rm_eo - pmatch[2].rm_so;
271 }
272 else if (!regexec(&o->pci_reg, devpath, 3, pmatch, 0)) {
273 type = "pci";
274 id = devpath + pmatch[1].rm_so;
275 id_len = pmatch[1].rm_eo - pmatch[1].rm_so;
276 } else {
277 goto fail;
278 }
279  
280 size_t type_len = strlen(type);
281 bsize_t bus_len = bsize_add(bsize_fromsize(type_len), bsize_add(bsize_fromint(1), bsize_add(bsize_fromsize(id_len), bsize_fromint(1))));
282 if (bus_len.is_overflow || bus_len.value > bus_avail) {
283 goto fail;
284 }
285  
286 memcpy(out_bus, type, type_len);
287 out_bus[type_len] = ':';
288 memcpy(out_bus + type_len + 1, id, id_len);
289 out_bus[type_len + 1 + id_len] = '\0';
290 return;
291  
292 fail:
293 snprintf(out_bus, bus_avail, "%s", "unknown");
294 }
295  
296 static void client_handler (struct instance *o, char *devpath, int have_map, BStringMap map)
297 {
298 // lookup existing device with this devpath
299 struct device *ex_device = find_device_by_devpath(o, devpath);
300 // lookup cache entry
301 const BStringMap *cache_map = NCDUdevManager_Query(o->i->params->iparams->umanager, devpath);
302  
303 if (!cache_map) {
304 if (ex_device) {
305 remove_device(o, ex_device);
306 }
307 goto out;
308 }
309  
310 int match_res = regexec(&o->reg, devpath, 0, NULL, 0);
311 const char *interface = BStringMap_Get(cache_map, "INTERFACE");
312 const char *ifindex_str = BStringMap_Get(cache_map, "IFINDEX");
313  
314 uintmax_t ifindex;
315 if (!(!match_res && interface && ifindex_str && parse_unsigned_integer(MemRef_MakeCstr(ifindex_str), &ifindex))) {
316 if (ex_device) {
317 remove_device(o, ex_device);
318 }
319 goto out;
320 }
321  
322 if (ex_device && (strcmp(ex_device->ifname, interface) || ex_device->ifindex != ifindex)) {
323 remove_device(o, ex_device);
324 ex_device = NULL;
325 }
326  
327 if (!ex_device) {
328 struct device *ex_ifname_device = find_device_by_ifname(o, interface);
329 if (ex_ifname_device) {
330 remove_device(o, ex_ifname_device);
331 }
332  
333 char bus[128];
334 make_bus(o, devpath, cache_map, bus, sizeof(bus));
335  
336 add_device(o, interface, devpath, ifindex, bus);
337 }
338  
339 out:
340 free(devpath);
341 if (have_map) {
342 BStringMap_Free(&map);
343 }
344 }
345  
346 static void func_new (void *vo, NCDModuleInst *i, const struct NCDModuleInst_new_params *params)
347 {
348 struct instance *o = vo;
349 o->i = i;
350  
351 // check arguments
352 if (!NCDVal_ListRead(params->args, 0)) {
353 ModuleLog(o->i, BLOG_ERROR, "wrong arity");
354 goto fail0;
355 }
356  
357 // init client
358 NCDUdevClient_Init(&o->client, o->i->params->iparams->umanager, o, (NCDUdevClient_handler)client_handler);
359  
360 // init devices list
361 LinkedList1_Init(&o->devices_list);
362  
363 // compile regex's
364 if (regcomp(&o->reg, DEVPATH_REGEX, REG_EXTENDED)) {
365 ModuleLog(o->i, BLOG_ERROR, "regcomp failed");
366 goto fail1;
367 }
368 if (regcomp(&o->usb_reg, DEVPATH_USB_REGEX, REG_EXTENDED)) {
369 ModuleLog(o->i, BLOG_ERROR, "regcomp failed");
370 goto fail2;
371 }
372 if (regcomp(&o->pci_reg, DEVPATH_PCI_REGEX, REG_EXTENDED)) {
373 ModuleLog(o->i, BLOG_ERROR, "regcomp failed");
374 goto fail3;
375 }
376  
377 event_template_new(&o->templ, o->i, BLOG_CURRENT_CHANNEL, 3, o, (event_template_func_free)templ_func_free);
378 return;
379  
380 fail3:
381 regfree(&o->usb_reg);
382 fail2:
383 regfree(&o->reg);
384 fail1:
385 NCDUdevClient_Free(&o->client);
386 fail0:
387 NCDModuleInst_Backend_DeadError(i);
388 }
389  
390 static void templ_func_free (struct instance *o, int is_error)
391 {
392 // free devices
393 LinkedList1Node *list_node;
394 while (list_node = LinkedList1_GetFirst(&o->devices_list)) {
395 struct device *device = UPPER_OBJECT(list_node, struct device, devices_list_node);
396 free_device(o, device, 1);
397 }
398  
399 // free regex's
400 regfree(&o->pci_reg);
401 regfree(&o->usb_reg);
402 regfree(&o->reg);
403  
404 // free client
405 NCDUdevClient_Free(&o->client);
406  
407 if (is_error) {
408 NCDModuleInst_Backend_DeadError(o->i);
409 } else {
410 NCDModuleInst_Backend_Dead(o->i);
411 }
412 }
413  
414 static void func_die (void *vo)
415 {
416 struct instance *o = vo;
417 event_template_die(&o->templ);
418 }
419  
420 static int func_getvar (void *vo, const char *name, NCDValMem *mem, NCDValRef *out)
421 {
422 struct instance *o = vo;
423 return event_template_getvar(&o->templ, name, mem, out);
424 }
425  
426 static void nextevent_func_new (void *unused, NCDModuleInst *i, const struct NCDModuleInst_new_params *params)
427 {
428 // check arguments
429 if (!NCDVal_ListRead(params->args, 0)) {
430 ModuleLog(i, BLOG_ERROR, "wrong arity");
431 goto fail0;
432 }
433  
434 // get method object
435 struct instance *mo = NCDModuleInst_Backend_GetUser((NCDModuleInst *)params->method_user);
436  
437 // make sure we are currently reporting an event
438 if (!event_template_is_enabled(&mo->templ)) {
439 ModuleLog(i, BLOG_ERROR, "not reporting an event");
440 goto fail0;
441 }
442  
443 // signal up.
444 // Do it before finishing the event so our process does not advance any further if
445 // we would be killed the event provider going down.
446 NCDModuleInst_Backend_Up(i);
447  
448 // wait for next event
449 next_event(mo);
450 return;
451  
452 fail0:
453 NCDModuleInst_Backend_DeadError(i);
454 }
455  
456 static struct NCDModule modules[] = {
457 {
458 .type = "net.watch_interfaces",
459 .func_new2 = func_new,
460 .func_die = func_die,
461 .func_getvar = func_getvar,
462 .alloc_size = sizeof(struct instance)
463 }, {
464 .type = "net.watch_interfaces::nextevent",
465 .func_new2 = nextevent_func_new
466 }, {
467 .type = NULL
468 }
469 };
470  
471 const struct NCDModuleGroup ncdmodule_net_watch_interfaces = {
472 .modules = modules
473 };