cf5b0b44df33d622d3a6c3d4400b380f66bf6ada
[mmondor.git] / mmsoftware / tap-bridge / tap-bridge.c
1 /* $Id: tap-bridge.c,v 1.2 2007/12/20 01:32:19 mmondor Exp $ */
2
3 /*
4 * Copyright (C) 2007, Matthew Mondor
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 * 3. All advertising materials mentioning features or use of this software
16 * must display the following acknowledgement:
17 * This product includes software developed by Matthew Mondor.
18 * 4. The name of Matthew Mondor may not be used to endorse or promote
19 * products derived from this software without specific prior written
20 * permission.
21 * 5. Redistribution of source code may not be released under the terms of
22 * any GNU Public License derivate.
23 *
24 * THIS SOFTWARE IS PROVIDED BY MATTHEW MONDOR ``AS IS'' AND ANY EXPRESS OR
25 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
26 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
27 * IN NO EVENT SHALL MATTHEW MONDOR BE LIABLE FOR ANY DIRECT, INDIRECT,
28 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
29 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
30 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
31 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
32 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
33 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
34 */
35
36 /*
37 * TODO:
38 * - Fix syslog related problem when building with -DDEBUG.
39 * - Write a TCP fingerprinting prevention system.
40 */
41
42 #include <assert.h>
43 #include <dlfcn.h>
44 #include <errno.h>
45 #include <fcntl.h>
46 #include <grp.h>
47 #include <pwd.h>
48 #include <poll.h>
49 #include <signal.h>
50 #include <stdarg.h>
51 #include <stdbool.h>
52 #include <stdint.h>
53 #include <stdio.h>
54 #include <stdlib.h>
55 #include <string.h>
56 #include <syslog.h>
57 #include <unistd.h>
58
59 #include <arpa/inet.h>
60 #include <net/if.h>
61 #include <net/if_ether.h>
62 #include <net/if_bridgevar.h>
63 #include <net/if_dl.h>
64 #include <net/if_tap.h>
65 #include <net/if_types.h>
66 #include <netinet/in.h>
67 #include <ifaddrs.h>
68 #include <sys/ioctl.h>
69 #include <sys/param.h>
70 #include <sys/resource.h>
71 #include <sys/socket.h>
72 #include <sys/sysctl.h>
73 #include <sys/time.h>
74 #include <sys/wait.h>
75
76 #include <mmlist.h>
77 #include <mmpool.h>
78 #include <mmhash.h>
79
80 #include <tap-bridge.h>
81 #include <tap-endian.h>
82
83
84 #define OUTMAX 1048576
85 #define INMAX 262144
86
87 /*
88 * These no longer necessary once my diff commited, although it might still be
89 * useful to allow configuring these.
90 */
91 #define INTMAC "f2:0b:a4:e9:70:0d"
92 #define EXTMAC "f2:0b:a4:e9:70:0e"
93
94 #define MODULE_DIR "/usr/local/lib/tap-bridge"
95 #define IFBR "bridge0"
96 #define PIDFILE "/var/run/tap-bridge.pid"
97 #define TAPFILE "/var/run/tap-bridge.if"
98 #define USER "daemon" /* XXX */
99 #define GROUP "daemon"
100
101
102 typedef struct storage {
103 hashnode_t node;
104 char key[64];
105 void *udata;
106 } storage_t;
107
108
109 int main(int, char **);
110 static void usage(void);
111 static int detach(const char *, const char *, const char *);
112 static int unprivileged(const char *, const char *);
113 static bool parent_init(void);
114 static void parent_main(void);
115 static void parent_cleanup(void);
116 static void parent_sighandler(int);
117 static bool child_init(void);
118 static void child_main(void);
119 static void child_cleanup(void);
120 static void child_sighandler(int);
121 static void macaddr_align8(macaddr_t *, const uint8_t *);
122 static void macaddr_align16(macaddr_t *, const uint16_t *);
123 static tap_t *tap_open(void);
124 static void tap_close(tap_t *);
125 static int if_create(const char *);
126 static int if_destroy(const char *);
127 static int if_up(const char *);
128 static int if_down(const char *);
129 static int if_get_macaddr(uint8_t *, const char *);
130 static int if_set_macaddr(const char *, const char *);
131 /*
132 static int if_get_ipaddr(in_addr_t *, in_addr_t *, in_addr_t *,
133 const char *);
134 */
135 static int if_set_ipaddr(const char *, const char *,
136 const char *);
137 static int bridge_add(const char *, const char *);
138 static bool frame_ctor(pnode_t *);
139 static void frame_dtor(pnode_t *);
140 static void queue_schedule(tap_t *, struct timeval *);
141 static bool queue_send(tap_t *);
142 static bool frame_receive(tap_t *);
143 static bool modules_reload(const char *, bool);
144
145
146 /* Parameters */
147 static char *extmac = EXTMAC, *intmac = INTMAC, *ifeth = NULL,
148 *ifbr = IFBR, *pidfile = PIDFILE, *tapfile = TAPFILE,
149 *ipaddr = NULL, *netmask = NULL, *user = USER,
150 *group = GROUP, *module_dir = MODULE_DIR;
151
152 /* Flow control and book keeping */
153 static int ifsockinet = -1;
154 static bool bridgecreated = false;
155 static bool parent_exit = false;
156 static pid_t child_pid = -1;
157 static bool child_exit = false;
158 static bool schedule_now = false;
159 static struct timeval time_current, time_event;
160 static pool_t frame_pool, fnode_pool, key_pool, modnode_pool;
161 static hashtable_t storage_table;
162 static list_t module_list;
163 static tap_t *tap_ext = NULL, *tap_int = NULL;
164
165
166 /* ARGSUSED */
167 int
168 main(int argc, char **argv)
169 {
170 int ret;
171 char c;
172
173 openlog("tap-bridge", LOG_PERROR | LOG_PID, LOG_DAEMON);
174
175 /* Parse arguments */
176 while ((c = getopt(argc, argv, "E:I:i:b:p:t:a:n:m:")) != -1) {
177 switch (c) {
178 case 'm': /* Modules list file */
179 module_dir = optarg;
180 break;
181 case 'E': /* External tap(4) MAC address */
182 extmac = optarg;
183 break;
184 case 'I': /* Internal tap(4) MAC address */
185 intmac = optarg;
186 break;
187 case 'i': /* Ethernet interface */
188 ifeth = optarg;
189 break;
190 case 'b': /* bridge(4) interface */
191 ifbr = optarg;
192 break;
193 case 'p': /* PID file */
194 pidfile = optarg;
195 break;
196 case 't': /* tap(4) interface file */
197 tapfile = optarg;
198 break;
199 case 'a': /* IP address to assign to external tap(4) */
200 ipaddr = optarg;
201 break;
202 case 'n': /* Netmask to assign to exnternal tap(4) */
203 netmask = optarg;
204 break;
205 case 'u': /* User to drop privileges to */
206 user = optarg;
207 break;
208 case 'g': /* Group to drop privileges to */
209 group = optarg;
210 break;
211 case '?':
212 /* FALLTHROUGH */
213 default:
214 usage();
215 return EXIT_FAILURE;
216 }
217 }
218 argc -= optind;
219 argv += optind;
220
221 if (ifeth == NULL) {
222 usage();
223 return EXIT_FAILURE;
224 }
225 if (ipaddr != NULL || netmask != NULL) {
226 if (ipaddr == NULL || netmask == NULL) {
227 usage();
228 return EXIT_FAILURE;
229 }
230 }
231
232 /* Initialize privileged resources */
233 if (!parent_init())
234 return EXIT_FAILURE;
235
236 /* Launch child unprivileged process */
237 ret = EXIT_SUCCESS;
238 if (!child_init()) {
239 ret = EXIT_FAILURE;
240 goto out;
241 }
242
243 /*
244 * Wait until child exits before cleaning up privileged resources and
245 * exiting.
246 */
247 parent_main();
248
249 out:
250 parent_cleanup();
251 return ret;
252 }
253
254 static void
255 usage(void)
256 {
257
258 warning(0,
259 "Usage: %s -i <ethernet-iface> [-E <ext-tap-mac>]\n"
260 " [-I <int-tap-mac>] [-b <bridge-iface>] [-p <pidfile>]\n"
261 " [-t <tapfile>] [-a <ipaddr> -n <netmask>]\n"
262 " [-m <modulesdir>]\n", getprogname());
263 }
264
265 /*
266 * Become a background daemon and write our PID file.
267 * Returns 0 on success or -1 on error.
268 */
269 static pid_t
270 detach(const char *pidfile, const char *tapfile, const char *tapiface)
271 {
272 pid_t pid;
273 int fd;
274
275 if ((pid = fork()) == -1)
276 return -1;
277 if (pid != 0)
278 exit(EXIT_SUCCESS);
279
280 /* Create PID file */
281 if ((fd = open(pidfile, O_CREAT | O_TRUNC | O_WRONLY, 0600)) != -1) {
282 char str[16];
283
284 (void) snprintf(str, 15, "%d\n", getpid());
285 if (write(fd, str, strlen(str)) == -1 || close(fd) == -1) {
286 warning(errno, "Error writing PID file [%s]", pidfile);
287 return -1;
288 }
289 } else {
290 warning(errno, "Error creating PID file [%s]", pidfile);
291 return -1;
292 }
293
294 /* Create tap(4) interface file */
295 if ((fd = open(tapfile, O_CREAT | O_TRUNC | O_WRONLY, 0600)) != -1) {
296 char str[16];
297
298 (void) snprintf(str, 15, "%s\n", tapiface);
299 if (write(fd, str, strlen(str)) == -1 || close(fd) == -1) {
300 warning(errno, "Error writing tap file [%s]", tapfile);
301 return -1;
302 }
303 } else {
304 warning(errno, "Error creating tap file [%s]", tapfile);
305 return -1;
306 }
307
308 /* Be paranoid, redirect our stdio file descriptors to null(4) */
309 (void) setsid();
310 (void) chdir("/");
311 if ((fd = open("/dev/null", O_RDWR)) != -1) {
312 (void) dup2(fd, STDIN_FILENO);
313 (void) dup2(fd, STDOUT_FILENO);
314 /* Keep stderr */
315 if (fd > STDERR_FILENO)
316 (void) close(fd);
317 } else {
318 warning(errno, "Error opening null(4) device");
319 return -1;
320 }
321
322 return 0;
323 }
324
325 static int
326 unprivileged(const char *user, const char *group)
327 {
328 struct passwd *pwd;
329 uid_t uid;
330 struct group *grp;
331 gid_t gid;
332
333 if ((pwd = getpwnam(user)) == NULL) {
334 warning(errno, "getpwnam");
335 return -1;
336 }
337 uid = pwd->pw_uid;
338
339 if ((grp = getgrnam(group)) == NULL) {
340 warning(errno, "getgrnam");
341 return -1;
342 }
343 gid = grp->gr_gid;
344
345 if (setgid(gid) == -1) {
346 warning(errno, "setgid");
347 return -1;
348 }
349 if (setegid(gid) == -1) {
350 warning(errno, "setegid");
351 return -1;
352 }
353 if (setgroups(0, &gid) == -1) {
354 warning(errno, "setgroups");
355 return -1;
356 }
357
358 if (setuid(uid) == -1) {
359 warning(errno, "setuid");
360 return -1;
361 }
362 if (seteuid(uid) == -1) {
363 warning(errno, "seteuid");
364 return -1;
365 }
366
367 return 0;
368 }
369
370 static bool
371 parent_init(void)
372 {
373
374 /* We need this socket for interface ioctls */
375 if ((ifsockinet = socket(AF_INET, SOCK_DGRAM, 0)) == -1) {
376 warning(errno, "socket(ifsockinet)");
377 goto err;
378 }
379
380 /* Remove address from ethernet interface and down it */
381 if (if_set_ipaddr(ifeth, "0.0.0.0", "0.255.255.255") == -1 ||
382 if_down(ifeth) == -1)
383 goto err;
384
385 /* Create a pair of tap(4) devices/interfaces */
386 if ((tap_int = tap_open()) == NULL ||
387 (tap_ext = tap_open()) == NULL)
388 goto err;
389
390 /* Assign MAC addresses to them */
391 /*
392 * XXX Only do if specified, use default addresses otherwise.
393 * We use these because of a bug in tap(4) which can cause two tap
394 * interfaces to have the same MAC address if they're created during
395 * the same second,
396 */
397 if (if_set_macaddr(tap_ext->name, extmac) == -1 ||
398 if_set_macaddr(tap_int->name, intmac) == -1)
399 goto err;
400
401 /* Obtain/remember MAC addresses */
402 {
403 uint8_t mac[ETHER_ADDR_LEN];
404
405 if (if_get_macaddr(mac, tap_ext->name) == -1)
406 goto err;
407 macaddr_align8(&tap_ext->mac, mac);
408
409 if (if_get_macaddr(mac, tap_int->name) == -1)
410 goto err;
411 macaddr_align8(&tap_int->mac, mac);
412 }
413 /* Assign IP address/netmask to external interface if wanted */
414 if (ipaddr != NULL) {
415 if (if_set_ipaddr(tap_ext->name, netmask, ipaddr) == -1)
416 goto err;
417 }
418
419 /* Set non-blocking mode */
420 if (fcntl(tap_ext->fd, F_SETFL, O_NONBLOCK) == -1 ||
421 fcntl(tap_int->fd, F_SETFL, O_NONBLOCK) == -1) {
422 warning(errno, "fcntl(O_NONBLOCK)");
423 goto err;
424 }
425
426 /* Create bridge interface */
427 if (if_create(ifbr) == -1)
428 goto err;
429 bridgecreated = true;
430 if (bridge_add(ifbr, ifeth) == -1 ||
431 bridge_add(ifbr, tap_int->name) == -1 ||
432 if_up(tap_int->name) == -1 ||
433 if_up(ifeth) == -1 ||
434 if_up(ifbr) == -1)
435 goto err;
436 bridgecreated = true;
437
438 if (detach(pidfile, tapfile, tap_ext->name) == -1)
439 goto err;
440
441 /* Setup parent sighandler */
442 {
443 struct sigaction act;
444
445 act.sa_handler = parent_sighandler;
446 (void) sigemptyset(&act.sa_mask);
447 act.sa_flags = 0;
448 if (sigaction(SIGTERM, &act, NULL) == -1 ||
449 sigaction(SIGINT, &act, NULL) == -1 ||
450 sigaction(SIGCHLD, &act, NULL) == -1 ||
451 sigaction(SIGHUP, &act, NULL) == -1) {
452 warning(errno, "sigaction(parent)");
453 goto err;
454 }
455 act.sa_handler = SIG_IGN;
456 (void) sigaction(SIGTTOU, &act, NULL);
457 (void) sigaction(SIGTTIN, &act, NULL);
458 (void) sigaction(SIGTSTP, &act, NULL);
459 }
460
461 /* This application is latency-sensitive */
462 if (setpriority(PRIO_PGRP, 0, -10) == -1) {
463 warning(errno, "setpriority");
464 goto err;
465 }
466
467 return true;
468
469 err:
470 parent_cleanup();
471 return false;
472 }
473
474 /*
475 * Simply loop until our child process dies.
476 */
477 static void
478 parent_main(void)
479 {
480 sigset_t set;
481
482 warning(0, "Parent process started");
483
484 (void) sigemptyset(&set);
485
486 while (!parent_exit)
487 (void) sigsuspend(&set);
488
489 warning(0, "Parent process exiting");
490 }
491
492 static void
493 parent_sighandler(int sig)
494 {
495
496 switch (sig) {
497 case SIGTERM:
498 /* FALLTHROUGH */
499 case SIGINT:
500 /* FALLTHROUGH */
501 case SIGHUP:
502 if (child_pid != -1) {
503 warning(0, "Parent forwarding signal %d to child",
504 sig);
505 (void) kill(child_pid, sig);
506 }
507 break;
508 case SIGCHLD:
509 for (;;) {
510 pid_t pid;
511 int status = 0;
512
513 while ((pid = wait3(&status, WNOHANG, NULL)) == -1 &&
514 errno == EINTR) ;
515 if (pid < 1)
516 break;
517 if (!WIFEXITED(status))
518 continue;
519 warning(0, "Parent detected child exit");
520 parent_exit = true;
521 break;
522 }
523 break;
524 }
525 }
526
527 static void
528 parent_cleanup(void)
529 {
530
531 (void) if_down(ifeth);
532 if (bridgecreated) {
533 (void) if_down(ifbr);
534 (void) if_destroy(ifbr);
535 }
536 if (tap_int != NULL) {
537 (void) if_down(tap_int->name);
538 tap_close(tap_int);
539 }
540 if (tap_ext != NULL) {
541 (void) if_down(tap_ext->name);
542 tap_close(tap_ext);
543 }
544 if (ifsockinet != -1)
545 (void) close(ifsockinet);
546 }
547
548 static bool
549 child_init(void)
550 {
551
552 /*
553 * Create a child process to which we delegate the following code.
554 * We use this for privilege separation. The parent simply waits
555 * until the child exits to then clean up resources.
556 */
557 {
558 pid_t pid;
559
560 if ((pid = fork()) == -1) {
561 warning(errno, "fork(child)");
562 return false;
563 }
564 if (pid != 0) {
565 child_pid = pid;
566 return true;
567 }
568 }
569
570 /*
571 * We're the child, drop privileges and continue.
572 */
573 if (unprivileged(user, group) == -1)
574 goto err;
575
576 /* Setup child sighandler */
577 {
578 struct sigaction act;
579
580 act.sa_handler = child_sighandler;
581 (void) sigemptyset(&act.sa_mask);
582 act.sa_flags = 0;
583 if (sigaction(SIGTERM, &act, NULL) == -1 ||
584 sigaction(SIGINT, &act, NULL) == -1 ||
585 sigaction(SIGALRM, &act, NULL) == -1 ||
586 sigaction(SIGHUP, &act, NULL) == -1) {
587 warning(errno, "sigaction(child)");
588 goto err;
589 }
590 act.sa_handler = SIG_IGN;
591 (void) sigaction(SIGCHLD, &act, NULL);
592 }
593
594 /* Setup our pools */
595 {
596 size_t pagesize;
597
598 pagesize = sysconf(_SC_PAGESIZE);
599 if (!pool_init(&frame_pool, "frame_pool", malloc, free,
600 frame_ctor, frame_dtor, sizeof(frame_t),
601 pagesize / sizeof(frame_t), 1, 0)) {
602 warning(errno, "pool_init(frame_pool)");
603 goto err;
604 }
605 if (!pool_init(&fnode_pool, "fnode_pool", malloc, free,
606 NULL, NULL, sizeof(fnode_t), pagesize / sizeof(fnode_t),
607 1, 0)) {
608 warning(errno, "pool_init(fnode_pool)");
609 goto err;
610 }
611 if (!pool_init(&key_pool, "key_pool", malloc, free, NULL,
612 NULL, sizeof(storage_t), pagesize / sizeof(storage_t),
613 1, 0)) {
614 warning(errno, "pool_init(key_pool)");
615 goto err;
616 }
617 if (!pool_init(&modnode_pool, "modnode_pool", malloc, free,
618 NULL, NULL, sizeof(modnode_t),
619 pagesize / sizeof(modnode_t), 1, 0)) {
620 warning(errno, "pool_init(modnode_pool)");
621 goto err;
622 }
623 }
624 /* And our keys hash table */
625 if (!hashtable_init(&storage_table, "storage_table",
626 HT_DEFAULT_CAPACITY, HT_DEFAULT_FACTOR, malloc, free, memcmp,
627 hashtable_hash, true)) {
628 warning(errno, "hashtable_init(storage_table)");
629 goto err;
630 }
631 /* List of loaded modules */
632 DLIST_INIT(&module_list);
633
634 /* Start timer */
635 {
636 struct itimerval itv;
637
638 timerclear(&time_event);
639 itv.it_interval.tv_sec = 0;
640 itv.it_interval.tv_usec = 1000000 / TIMER_HZ;
641 itv.it_value = itv.it_interval;
642 if (setitimer(ITIMER_REAL, &itv, NULL) == -1) {
643 warning(errno, "setitimer");
644 goto err;
645 }
646 }
647
648 /* Load modules */
649 if (!modules_reload(module_dir, true)) {
650 warning(errno, "modules_reload");
651 goto err;
652 }
653
654 child_main();
655 child_cleanup();
656 exit(EXIT_SUCCESS);
657
658 err:
659 exit(EXIT_SUCCESS);
660 }
661
662 static void
663 child_main(void)
664 {
665 struct pollfd fds[2];
666
667 warning(0, "Child unprivileged process started");
668
669 fds[0].fd = tap_ext->fd;
670 fds[1].fd = tap_int->fd;
671 fds[0].events = fds[1].events = POLLIN;
672 fds[0].revents = fds[1].revents = 0;
673 while (!child_exit) {
674
675 /*
676 * If a scheduling round was triggered, transfer all expired
677 * nodes to the send list and update timeout to soonest to be
678 * expired node.
679 */
680 if (schedule_now) {
681 struct timeval ttv;
682
683 schedule_now = false;
684
685 /* Start with initial timeout of one minute */
686 ttv = time_current;
687 ttv.tv_sec += 60;
688
689 queue_schedule(tap_ext, &ttv);
690 queue_schedule(tap_int, &ttv);
691
692 /* Set new timeout to soonest expireing node */
693 time_event = ttv;
694 }
695
696 /*
697 * For performance we process as much data as possible
698 * before polling. Prioritize output, and use a limit to
699 * prevent starvation of one direction.
700 */
701
702 /*
703 * Send out some data from our send queue if any.
704 * What resides here should already have been sanity checked
705 * by any user code, as it was queued by user code for
706 * sending.
707 */
708 if (!queue_send(tap_ext) && errno != EAGAIN &&
709 errno != EHOSTDOWN) {
710 warning(errno, "queue_send(tap_ext)");
711 break;
712 }
713 if (!queue_send(tap_int) && errno != EAGAIN &&
714 errno != EHOSTDOWN) {
715 warning(errno, "queue_send(tap_int)");
716 break;
717 }
718
719 /*
720 * Read in data if any, allocating new frames for it and
721 * feeding them to user code, which may filter, modify, drop
722 * or respond by sending response packets. We also fill in a
723 * few fields of the frame_t structure prior to passing them
724 * to user code.
725 */
726 if (!frame_receive(tap_int) && errno != EAGAIN &&
727 errno != EHOSTDOWN) {
728 warning(errno, "frame_receive(tap_int)");
729 break;
730 }
731 if (!frame_receive(tap_ext) && errno != EAGAIN &&
732 errno != EHOSTDOWN) {
733 warning(errno, "frame_receive(tap_ext)");
734 break;
735 }
736
737 /* Are we supposed to quit? */
738 if (child_exit)
739 break;
740
741 /* Don't poll yet if we have more events to process. */
742 if (schedule_now && errno != EHOSTDOWN)
743 continue;
744
745 /*
746 * Poll and continue loop if any interesting data.
747 */
748 for (;;) {
749 int ret;
750
751 ret = poll(fds, 2, -1);
752 if (ret == 0) {
753 if (schedule_now)
754 break;
755 continue;
756 }
757 if (ret == -1) {
758 if (errno == EINTR)
759 break;
760 warning(errno, "poll");
761 goto out;
762 }
763 if ((fds[0].revents & (POLLHUP | POLLERR)) != 0 ||
764 (fds[1].revents & (POLLHUP | POLLERR)) != 0) {
765 warning(errno, "poll");
766 goto out;
767 }
768 if ((fds[0].revents & POLLIN) != 0 ||
769 (fds[1].revents & POLLIN) != 0)
770 break;
771 }
772 }
773
774 out:
775 warning(0, "Child unprivileged process exiting");
776 }
777
778 static void
779 child_sighandler(int sig)
780 {
781 modnode_t *n;
782
783 switch (sig) {
784 case SIGTERM:
785 /* FALLTHROUGH */
786 case SIGINT:
787 warning(0, "Child received SIGTERM; exiting");
788 child_exit = true;
789 break;
790 case SIGALRM:
791 /*
792 * Update current time value to prevent having to invoke
793 * gettimeofday(2) at a too high frequency.
794 * Call modules callback functions if any, and verify if any
795 * scheduled to be sent packets should now be sent, setting
796 * the schedule_now flag if so.
797 */
798 (void) gettimeofday(&time_current, NULL);
799 DLIST_FOREACH(&module_list, n) {
800 if (n->module->tick != NULL)
801 n->module->tick(&time_current);
802 }
803 if (timerisset(&time_event) &&
804 timercmp(&time_event, &time_current, <))
805 schedule_now = true;
806 break;
807 case SIGHUP:
808 warning(0, "Child received SIGHUP; reloading modules");
809 /* Reload modules specified in configuration file */
810 if (!modules_reload(module_dir, true))
811 warning(errno, "modules_reload");
812 break;
813 }
814 }
815
816 static void
817 child_cleanup(void)
818 {
819
820 (void) modules_reload(module_dir, false);
821 }
822
823 /*
824 * Converts the ETHER_ADDR_LEN sized byte string to a 64-bit aligned macaddr_t
825 * type for better performance. Since this consists of an internal format,
826 * endian byte order is unimportant. This will be used for comparisions
827 * against other macaddr_t. To simplify things even more, accessing a
828 * macaddr_t as an uint8_t array should return back the size bytes in the same
829 * order they previously were, which may be useful for operations such as
830 * logging.
831 */
832 static void
833 macaddr_align8(macaddr_t *a, const uint8_t *b)
834 {
835 register const uint8_t *rb = b;
836 register uint8_t *rab = (uint8_t *)a;
837
838 /* Unroll loop */
839 rab[0] = rb[0];
840 rab[1] = rb[1];
841 rab[2] = rb[2];
842 rab[3] = rb[3];
843 rab[4] = rb[4];
844 rab[5] = rb[5];
845 rab[6] = 0x00;
846 rab[7] = 0x00;
847 }
848
849 /*
850 * This optimized version deals with known to be 16-bit aligned MAC addresses,
851 * as is the case with origin/destination addresses received in an ethernet
852 * frame.
853 */
854 static void
855 macaddr_align16(macaddr_t *a, const uint16_t *b)
856 {
857 register const uint16_t *rb = b;
858 register uint16_t *rab = (uint16_t *)a;
859
860 /* Unroll loop */
861 rab[0] = rb[0];
862 rab[1] = rb[1];
863 rab[2] = rb[2];
864 rab[3] = 0x0000;
865 }
866
867 static tap_t *
868 tap_open(void)
869 {
870 int fd = -1;
871 tap_t *tap = NULL;
872 struct ifreq iface;
873
874 if ((fd = open("/dev/tap", O_RDWR)) == -1) {
875 warning(errno, "open(/dev/tap)");
876 goto err;
877 }
878 if ((tap = malloc(sizeof(tap_t))) == NULL) {
879 warning(errno, "malloc(tap_t)");
880 goto err;
881 }
882 if (ioctl(fd, TAPGIFNAME, &iface) == -1) {
883 warning(errno, "ioctl(TAPGIFNAME)");
884 goto err;
885 }
886
887 tap->fd = fd;
888 if ((tap->name = strdup(iface.ifr_name)) == NULL) {
889 warning(errno, "stddup(iface.ifr_name)");
890 goto err;
891 }
892
893 DLIST_INIT(&tap->schedq);
894 DLIST_INIT(&tap->sendq);
895
896 return tap;
897
898 err:
899 if (fd != -1)
900 (void) close(fd);
901 if (tap != NULL) {
902 if (tap->name != NULL)
903 free(tap->name);
904 free(tap);
905 }
906 return NULL;
907 }
908
909 static void
910 tap_close(tap_t *tap)
911 {
912
913 assert(tap != NULL);
914 assert(tap->name != NULL);
915 assert(tap->fd != -1);
916 free(tap->name);
917 (void) close(tap->fd);
918 free(tap);
919 }
920
921 static int
922 if_create(const char *iface)
923 {
924 struct ifreq ifr;
925 int error;
926
927 (void) strncpy(ifr.ifr_name, iface, sizeof(ifr.ifr_name) - 1);
928 if ((error = ioctl(ifsockinet, SIOCIFCREATE, &ifr)) == -1)
929 warning(errno, "if_create(%s)", iface);
930 return error;
931 }
932
933 static int
934 if_destroy(const char *iface)
935 {
936 struct ifreq ifr;
937 int error;
938
939 (void) strncpy(ifr.ifr_name, iface, sizeof(ifr.ifr_name) - 1);
940 if ((error = ioctl(ifsockinet, SIOCIFDESTROY, &ifr)) == -1)
941 warning(errno, "if_destroy(%s)", iface);
942 return error;
943 }
944
945 static int
946 if_up(const char *iface)
947 {
948 struct ifreq ifr;
949 int error;
950
951 (void) strncpy(ifr.ifr_name, iface, sizeof(ifr.ifr_name) - 1);
952 if (ioctl(ifsockinet, SIOCGIFFLAGS, &ifr) != 0)
953 return -1;
954 ifr.ifr_flags |= IFF_UP;
955 if ((error = ioctl(ifsockinet, SIOCSIFFLAGS, &ifr)) == -1)
956 warning(errno, "if_up(%s)", iface);
957 return error;
958 }
959
960 static int
961 if_down(const char *iface)
962 {
963 struct ifreq ifr;
964 int error;
965
966 (void) strncpy(ifr.ifr_name, iface, sizeof(ifr.ifr_name) - 1);
967 if (ioctl(ifsockinet, SIOCGIFFLAGS, &ifr) != 0)
968 return -1;
969 ifr.ifr_flags &= ~IFF_UP;
970 if ((error = ioctl(ifsockinet, SIOCSIFFLAGS, &ifr)) == -1)
971 warning(errno, "if_down(%s)", iface);
972 return error;
973 }
974
975 static int
976 if_get_macaddr(uint8_t *buf, const char *iface)
977 {
978 struct ifaddrs *ifa = NULL, *p;
979 int error = 0;
980
981 if ((error = getifaddrs(&ifa)) == -1)
982 goto out;
983 for (p = ifa; p != NULL; p = p->ifa_next) {
984 if (strcmp(iface, p->ifa_name) == 0)
985 break;
986 }
987 if (p == NULL) {
988 error = -1;
989 errno = ENOENT;
990 goto out;
991 }
992 if (p->ifa_addr->sa_family != AF_LINK) {
993 error = -1;
994 errno = EAFNOSUPPORT;
995 goto out;
996 }
997
998 (void) memcpy(buf, p->ifa_addr->sa_data, ETHER_ADDR_LEN);
999
1000 out:
1001 if (ifa != NULL)
1002 freeifaddrs(ifa);
1003
1004 if (error != 0)
1005 warning(errno, "if_get_macaddr(%s)", iface);
1006 return error;
1007 }
1008
1009 /*
1010 * This is tap(4) specific for now. Hopefully a generic ethernet ioctl(2)
1011 * will eventually be provided for this.
1012 */
1013 static int
1014 if_set_macaddr(const char *iface, const char *addr)
1015 {
1016 struct ifaliasreq ifra;
1017 struct ether_addr *eaddr;
1018
1019 (void) memset(&ifra, 0x00, sizeof(struct ifaliasreq));
1020
1021 if ((eaddr = ether_aton(addr)) == NULL)
1022 goto err;
1023 (void) memcpy(ifra.ifra_addr.sa_data, eaddr->ether_addr_octet,
1024 ETHER_ADDR_LEN);
1025 (void) strncpy(ifra.ifra_name, iface, sizeof(ifra.ifra_name) - 1);
1026 ifra.ifra_addr.sa_family = AF_LINK;
1027 ifra.ifra_addr.sa_len = sizeof(struct sockaddr_dl);
1028
1029 if (ioctl(ifsockinet, SIOCSIFPHYADDR, &ifra) == -1)
1030 goto err;
1031 return 0;
1032
1033 err:
1034 warning(errno, "if_set_macaddr(%s, %s)", iface, addr);
1035 return -1;
1036 }
1037
1038 /*
1039 static int
1040 if_get_ipaddr(in_addr_t *addr, in_addr_t *mask, in_addr_t *braddr,
1041 const char *iface)
1042 {
1043 struct ifreq ifr;
1044 struct sockaddr_in *sin = (struct sockaddr_in *)&ifr.ifr_addr;
1045
1046 (void) memset(&ifr, 0x00, sizeof(struct ifreq));
1047 (void) strncpy(ifr.ifr_name, iface, sizeof(ifr.ifr_name) - 1);
1048
1049 ifr.ifr_addr.sa_family = AF_INET;
1050 ifr.ifr_addr.sa_len = sizeof(struct sockaddr_in);
1051 if (ioctl(ifsockinet, SIOCGIFADDR, &ifr) == -1)
1052 goto err;
1053 *addr = sin->sin_addr.s_addr;
1054 sin->sin_addr.s_addr = 0x00000000;
1055
1056 ifr.ifr_addr.sa_family = AF_INET;
1057 ifr.ifr_addr.sa_len = sizeof(struct sockaddr_in);
1058 if (ioctl(ifsockinet, SIOCGIFNETMASK, &ifr) == -1)
1059 goto err;
1060 *mask = sin->sin_addr.s_addr;
1061 sin->sin_addr.s_addr = 0x00000000;
1062
1063 ifr.ifr_addr.sa_family = AF_INET;
1064 ifr.ifr_addr.sa_len = sizeof(struct sockaddr_in);
1065 if (ioctl(ifsockinet, SIOCGIFBRDADDR, &ifr) == -1)
1066 goto err;
1067 *braddr = sin->sin_addr.s_addr;
1068
1069 return 0;
1070
1071 err:
1072 warning(errno, "if_get_ipaddr(%s)", iface);
1073 return -1;
1074 }
1075 */
1076
1077 static int
1078 if_set_ipaddr(const char *iface, const char *netmask, const char *address)
1079 {
1080 struct ifaliasreq ifra;
1081 struct sockaddr_in *sin;
1082
1083 (void) memset(&ifra, 0x00, sizeof(struct ifaliasreq));
1084 (void) strncpy(ifra.ifra_name, iface, sizeof(ifra.ifra_name) - 1);
1085
1086 sin = (struct sockaddr_in *)&ifra.ifra_addr;
1087 if (inet_pton(AF_INET, address, &sin->sin_addr) != 1)
1088 goto err;
1089 ifra.ifra_addr.sa_family = AF_INET;
1090 ifra.ifra_addr.sa_len = sizeof(struct sockaddr_in);
1091
1092 sin = (struct sockaddr_in *)&ifra.ifra_mask;
1093 if (inet_pton(AF_INET, netmask, &sin->sin_addr) != 1)
1094 goto err;
1095 ifra.ifra_mask.sa_family = AF_INET;
1096 ifra.ifra_mask.sa_len = sizeof(struct sockaddr_in);
1097
1098 if (ioctl(ifsockinet, SIOCAIFADDR, &ifra) == -1)
1099 goto err;
1100
1101 return 0;
1102
1103 err:
1104 warning(errno, "if_set_ipaddr(%s)", iface);
1105 return -1;
1106 }
1107
1108 static int
1109 bridge_add(const char *br, const char *iface)
1110 {
1111 struct ifbreq req;
1112 struct ifdrv ifd;
1113 int error;
1114
1115 (void) memset(&req, 0x00, sizeof(struct ifbreq));
1116 (void) strncpy(req.ifbr_ifsname, iface, sizeof(req.ifbr_ifsname) - 1);
1117
1118 (void) memset(&ifd, 0x00, sizeof(struct ifdrv));
1119 (void) strncpy(ifd.ifd_name, br, sizeof(ifd.ifd_name) - 1);
1120 ifd.ifd_cmd = BRDGADD;
1121 ifd.ifd_len = sizeof(struct ifbreq);
1122 ifd.ifd_data = &req;
1123
1124 if ((error = ioctl(ifsockinet, SIOCSDRVSPEC, &ifd)) == -1)
1125 warning(errno, "bridge_add(%s, %s)", br, iface);
1126 return error;
1127 }
1128
1129 static bool
1130 frame_ctor(pnode_t *pnod)
1131 {
1132 frame_t *f = (frame_t *)pnod;
1133
1134 if ((f->data = malloc(FRAMESIZE)) == NULL)
1135 return false;
1136
1137 return true;
1138 }
1139
1140 static void
1141 frame_dtor(pnode_t *pnod)
1142 {
1143 frame_t *f = (frame_t *)pnod;
1144
1145 if (f->data != NULL)
1146 free(f->data);
1147 }
1148
1149 static void
1150 queue_schedule(tap_t *tap, struct timeval *ttv)
1151 {
1152 fnode_t *n, *t;
1153 struct timeval *tv;
1154
1155 tv = ttv;
1156 for (n = (fnode_t *)DLIST_TOP(&tap->schedq); n != NULL; n = t) {
1157 t = (fnode_t *)DLIST_NEXT((node_t *)n);
1158 if (timercmp(&n->scheduled, &time_current, <))
1159 DLIST_SWAP(&tap->sendq, &tap->schedq, (node_t *)n,
1160 false);
1161 else if (timercmp(&n->scheduled, tv, <))
1162 tv = &n->scheduled;
1163 }
1164
1165 if (tv != ttv)
1166 *ttv = *tv;
1167 }
1168
1169 static bool
1170 queue_send(tap_t *tap)
1171 {
1172 node_t *n, *t;
1173 size_t count;
1174
1175 count = 0;
1176 for (n = DLIST_TOP(&tap->sendq);
1177 !child_exit && count < OUTMAX && n != NULL;
1178 n = t) {
1179 fnode_t *fn = (fnode_t *)n;
1180 ssize_t ret;
1181 struct iovec iov[2];
1182
1183 t = DLIST_NEXT(n);
1184
1185 iov[0].iov_base = fn->frame->data;
1186 iov[0].iov_len = 14;
1187 iov[1].iov_base = &fn->frame->data[16];
1188 iov[1].iov_len = fn->frame->size - 16;
1189 while ((ret = writev(tap->fd, iov, 2)) == -1 &&
1190 errno == EINTR) ;
1191 /* On error leave pending frame on queue */
1192 if (ret != fn->frame->size - 2)
1193 return false;
1194
1195 count += fn->frame->size;
1196 DLIST_UNLINK(&tap->sendq, n);
1197 fnode_destroy(fn);
1198 }
1199
1200 errno = EAGAIN;
1201 return false;
1202 }
1203
1204 static bool
1205 frame_receive(tap_t *tap)
1206 {
1207 size_t count;
1208 ssize_t len;
1209 frame_t *f;
1210
1211 count = 0;
1212 for (f = NULL; !child_exit && count < INMAX; count += len) {
1213 struct iovec iov[2];
1214 modnode_t *n;
1215
1216 if ((f = (frame_t *)pool_alloc(&frame_pool, false)) == NULL) {
1217 errno = ENOMEM;
1218 warning(errno, "pool_alloc");
1219 return false;
1220 }
1221
1222 ((uint16_t *)f->data)[7] = 0x0000;
1223 iov[0].iov_base = f->data;
1224 iov[0].iov_len = 14;
1225 iov[1].iov_base = &f->data[16];
1226 iov[1].iov_len = FRAMESIZE - 14;
1227 while ((len = readv(tap->fd, iov, 2)) == -1
1228 && errno == EINTR) ;
1229
1230 if (len == -1 || len == 0) {
1231 (void) pool_free((pnode_t *)f);
1232 return false;
1233 }
1234
1235 macaddr_align16(&f->destination, (uint16_t *)f->data);
1236 macaddr_align16(&f->origin, &((uint16_t *)f->data)[3]);
1237 f->received = time_current;
1238 f->size = len + 2;
1239 f->type = BYTEORDER_HOST16(((uint16_t *)f->data)[6]);
1240
1241 /*
1242 * To every module receive function we pass a new fnode_t
1243 * which it becomes responsible for.
1244 */
1245 DLIST_FOREACH(&module_list, n) {
1246 bool (*recv)(tap_t *, fnode_t *);
1247 fnode_t *fn;
1248
1249 if ((recv = (tap == tap_int ? n->module->recv_int :
1250 n->module->recv_ext)) == NULL)
1251 continue;
1252
1253 if ((fn = fnode_new(f)) == NULL) {
1254 warning(errno, "fnode_new");
1255 continue;
1256 }
1257 if (!recv(tap, fn))
1258 break;
1259 }
1260 }
1261
1262 return true;
1263 }
1264
1265 static bool
1266 modules_reload(const char *fp, bool load)
1267 {
1268 bool ret = false;
1269 FILE *fh = NULL;
1270 modnode_t *n, *t;
1271 char buf[256], path[1024];
1272 void *handle = NULL;
1273 sigset_t set, oset;
1274
1275 (void) sigemptyset(&set);
1276 (void) sigaddset(&set, SIGALRM);
1277 (void) sigaddset(&set, SIGTERM);
1278 (void) sigaddset(&set, SIGHUP);
1279 oset = set;
1280 (void) sigprocmask(SIG_BLOCK, &set, NULL);
1281
1282 /* Unload all modules in reverse order */
1283 for (n = (modnode_t *)DLIST_BOTTOM(&module_list); n != NULL; n = t) {
1284 t = (modnode_t *)DLIST_PREV((pnode_t *)n);
1285
1286 n->module->exit();
1287 (void) dlclose(n->handle);
1288 DLIST_UNLINK(&module_list, (node_t *)n);
1289 (void) pool_free((pnode_t *)n);
1290 }
1291 if (!load) {
1292 ret = true;
1293 goto out;
1294 }
1295
1296 (void) snprintf(path, sizeof(path) - 1, "%s/%s", fp, "modules.conf");
1297 if ((fh = fopen(path, "r")) == NULL) {
1298 warning(errno, "fopen");
1299 goto out;
1300 }
1301
1302 /* Attempt to reload every specified module */
1303 while (fgets(buf, sizeof(buf) - 1, fh) != NULL) {
1304 void *sym;
1305 char *cptr;
1306
1307 if ((cptr = strchr(buf, '\n')) != NULL)
1308 *cptr = '\0';
1309 (void) snprintf(path, sizeof(path) - 1, "%s/%s", fp, buf);
1310 if ((handle = dlopen(path, O_RDONLY)) == NULL) {
1311 warning(errno, "dlopen(%s) - %s", buf, dlerror());
1312 goto out;
1313 }
1314 if ((sym = dlsym(handle, "module")) == NULL) {
1315 warning(errno, "dlsym(%s, module) - %s",
1316 buf, dlerror());
1317 goto out;
1318 }
1319 if ((n = (modnode_t *)pool_alloc(&modnode_pool, false))
1320 == NULL)
1321 goto out;
1322
1323 n->handle = handle;
1324 handle = NULL;
1325 n->module = sym;
1326
1327 if (n->module->init == NULL || n->module->exit == NULL) {
1328 warning(errno, "module(%s) - No init()/exit()!", buf);
1329 goto out;
1330 }
1331 if (!n->module->init(tap_int, tap_ext)) {
1332 warning(errno, "module(%s) - init() failed!", buf);
1333 goto out;
1334 }
1335
1336 DLIST_APPEND(&module_list, (node_t *)n);
1337 n = NULL;
1338 }
1339
1340 ret = true;
1341
1342 out:
1343 if (n != NULL)
1344 (void) pool_free((pnode_t *)n);
1345 if (handle != NULL)
1346 (void) dlclose(handle);
1347 if (fh != NULL)
1348 (void) fclose(fh);
1349
1350 (void) sigprocmask(SIG_UNBLOCK, &oset, NULL);
1351 return ret;
1352 }
1353
1354
1355 /*
1356 * EXPORTED FUNCTIONS
1357 */
1358
1359 /*
1360 * Creates space for a new frame. The caller should fill the frame itself.
1361 * Returns NULL on error.
1362 */
1363 frame_t *
1364 frame_new(void)
1365 {
1366 frame_t *f;
1367
1368 if ((f = (frame_t *)pool_alloc(&frame_pool, false)) != NULL)
1369 f->refcount = 0;
1370
1371 return f;
1372 }
1373
1374 /*
1375 * Creates a new frame node. These may be manipulated as wanted. A frame is
1376 * never freed unless all created fnode_t objects tried to a frame_t are
1377 * destroyed. This is a useful block to use custom queueing with frames.
1378 */
1379 fnode_t *
1380 fnode_new(frame_t *f)
1381 {
1382 fnode_t *n;
1383
1384 assert(f != NULL);
1385
1386 if ((n = (fnode_t *)pool_alloc(&fnode_pool, false)) != NULL) {
1387 f->refcount++;
1388 n->frame = f;
1389 }
1390
1391 return n;
1392 }
1393
1394 /*
1395 * Destroys the specified frame node. If all frame nodes tied to the
1396 * underlaying frame_t the frame is also destroyed.
1397 */
1398 void
1399 fnode_destroy(fnode_t *n)
1400 {
1401
1402 assert(n != NULL && n->frame != NULL);
1403
1404 if (--(n->frame->refcount) == 0)
1405 (void) pool_free((pnode_t *)n->frame);
1406 (void) pool_free((pnode_t *)n);
1407 }
1408
1409 size_t
1410 macaddr_string(char *str, size_t len, macaddr_t *a)
1411 {
1412 register uint8_t *rb = (uint8_t *)a;
1413
1414 return snprintf(str, len, "%02x:%02x:%02x:%02x:%02x:%02x",
1415 rb[0], rb[1], rb[2], rb[3], rb[4], rb[5]);
1416 }
1417
1418 /*
1419 * Send the specified frame as soon as possible.
1420 * A new fnode_t object is internally created to queue this node for sending.
1421 * The sender thus should still destroy its fnode_t unless it intends to send
1422 * the same frame again.
1423 */
1424 void
1425 frame_send(tap_t *tap, fnode_t *n)
1426 {
1427 fnode_t *fn;
1428
1429 assert(tap != NULL && n != NULL);
1430
1431 if ((fn = fnode_new(n->frame)) != NULL)
1432 DLIST_APPEND(&tap->sendq, (node_t *)fn);
1433 /* Flush queue immediately to minimize latency */
1434 (void) queue_send(tap);
1435 }
1436
1437 /*
1438 * Sets up this frame_t to be scheduled for sending after the specified time
1439 * has elapsed. The frame_t will be queued and recycled automatically once it
1440 * has been sent.
1441 */
1442 void
1443 frame_schedule(tap_t *tap, fnode_t *n, const struct timeval *tv)
1444 {
1445 fnode_t *fn;
1446
1447 assert(tap != NULL && n != NULL && tv != NULL);
1448
1449 if (!timerisset(&time_event) || timercmp(tv, &time_event, <))
1450 time_event = *tv;
1451 if ((fn = fnode_new(n->frame)) != NULL) {
1452 fn->scheduled = *tv;
1453 DLIST_APPEND(&tap->schedq, (node_t *)fn);
1454 }
1455 }
1456
1457 /*
1458 * Attempts to create a new storage key. Returns a pointer to a void * on
1459 * success, or NULL on error. This void * may then be initialized and used as
1460 * wanted.
1461 */
1462 void **
1463 key_create(const char *key)
1464 {
1465 storage_t *node;
1466
1467 assert(key != NULL);
1468
1469 if (hashtable_lookup(&storage_table, key, strlen(key)) != NULL)
1470 return NULL;
1471
1472 if ((node = (storage_t *)pool_alloc(&key_pool, false)) != NULL) {
1473
1474 (void) strncpy(node->key, key, sizeof(node->key) - 1);
1475 node->udata = NULL;
1476
1477 if (hashtable_link(&storage_table, (hashnode_t *)node,
1478 node->key, strlen(key), false))
1479 return &node->udata;
1480
1481 (void) pool_free((pnode_t *)node);
1482 }
1483
1484 return NULL;
1485 }
1486
1487 /*
1488 * Attempts to locate an existing storage key. Returns a pointer to a void *
1489 * on success, or NULL if it doesn't exist. This void * may previously have
1490 * been initialized for custom storage.
1491 */
1492 void **
1493 key_lookup(const char *key)
1494 {
1495 storage_t *node;
1496
1497 assert(key != NULL);
1498
1499 if ((node = (storage_t *)hashtable_lookup(&storage_table, key,
1500 strlen(key))) != NULL)
1501 return &node->udata;
1502
1503 return NULL;
1504 }
1505
1506 /*
1507 * Attempts to destroy an existing storage key. Does nothing about the
1508 * storage void *, it thus should be freed as necessary by the module.
1509 * It is possible to not call this function and expect the storage key to
1510 * still exist after modules were reloaded.
1511 */
1512 void
1513 key_destroy(const char *key)
1514 {
1515 hashnode_t *node;
1516
1517 assert(key != NULL);
1518
1519 if ((node = hashtable_lookup(&storage_table, key, strlen(key)))
1520 != NULL) {
1521 hashtable_unlink(&storage_table, (node));
1522 (void) pool_free((pnode_t *)node);
1523 }
1524 }
1525
1526 void
1527 warning(int err, const char *fmt, ...)
1528 {
1529 va_list lst;
1530 char msgbuf[1024];
1531 sigset_t set, oset;
1532
1533 (void) sigemptyset(&set);
1534 (void) sigaddset(&set, SIGALRM);
1535 (void) sigaddset(&set, SIGHUP);
1536 oset = set;
1537 (void) sigprocmask(SIG_BLOCK, &set, NULL);
1538
1539 /*
1540 * Since we want to append the errno error message after the custom
1541 * message, we need to store it in a buffer rather than simply use
1542 * vsyslog(3).
1543 */
1544 va_start(lst, fmt);
1545 (void) vsnprintf(msgbuf, sizeof(msgbuf) - 1, fmt, lst);
1546 va_end(lst);
1547
1548 if (err != 0)
1549 syslog(LOG_NOTICE, "warning: %s: %s", msgbuf, strerror(err));
1550 else
1551 syslog(LOG_NOTICE, "notice: %s", msgbuf);
1552
1553 (void) sigprocmask(SIG_UNBLOCK, &oset, NULL);
1554 }