*** empty log message ***
[mmondor.git] / mmsoftware / tap-bridge / tap-bridge.c
1 /* $Id: tap-bridge.c,v 1.3 2007/12/24 03:16:21 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 <signal.h>
49 #include <stdarg.h>
50 #include <stdbool.h>
51 #include <stdint.h>
52 #include <stdio.h>
53 #include <stdlib.h>
54 #include <string.h>
55 #include <syslog.h>
56 #include <unistd.h>
57
58 #include <arpa/inet.h>
59 #include <net/if.h>
60 #include <net/if_ether.h>
61 #include <net/if_bridgevar.h>
62 #include <net/if_dl.h>
63 #include <net/if_tap.h>
64 #include <net/if_types.h>
65 #include <netinet/in.h>
66 #include <ifaddrs.h>
67 #include <sys/ioctl.h>
68 #include <sys/param.h>
69 #include <sys/resource.h>
70 #include <sys/socket.h>
71 #include <sys/sysctl.h>
72 #include <sys/time.h>
73 #include <sys/wait.h>
74
75 #include <mmlist.h>
76 #include <mmpool.h>
77 #include <mmhash.h>
78
79 #include <tap-bridge.h>
80 #include <tap-endian.h>
81
82
83 #define OUTMAX 1048576
84 #define INMAX 262144
85
86 /*
87 * These no longer necessary once my diff commited, although it might still be
88 * useful to allow configuring these.
89 */
90 #define INTMAC "f2:0b:a4:e9:70:0d"
91 #define EXTMAC "f2:0b:a4:e9:70:0e"
92
93 #define MODULE_DIR "/usr/local/lib/tap-bridge"
94 #define IFBR "bridge0"
95 #define PIDFILE "/var/run/tap-bridge.pid"
96 #define TAPFILE "/var/run/tap-bridge.if"
97 #define USER "daemon" /* XXX */
98 #define GROUP "daemon"
99
100
101 typedef struct storage {
102 hashnode_t node;
103 char key[64];
104 void *udata;
105 } storage_t;
106
107
108 int main(int, char **);
109 static void usage(void);
110 static int detach(const char *, const char *, const char *);
111 static int unprivileged(const char *, const char *);
112 static bool parent_init(void);
113 static void parent_main(void);
114 static void parent_cleanup(void);
115 static void parent_sighandler(int);
116 static bool child_init(void);
117 static void child_main(void);
118 static void child_cleanup(void);
119 static void child_sighandler(int);
120 static void macaddr_align8(macaddr_t *, const uint8_t *);
121 static void macaddr_align16(macaddr_t *, const uint16_t *);
122 static tap_t *tap_open(void);
123 static void tap_close(tap_t *);
124 static int if_create(const char *);
125 static int if_destroy(const char *);
126 static int if_up(const char *);
127 static int if_down(const char *);
128 static int if_get_macaddr(uint8_t *, const char *);
129 static int if_set_macaddr(const char *, const char *);
130 /*
131 static int if_get_ipaddr(in_addr_t *, in_addr_t *, in_addr_t *,
132 const char *);
133 */
134 static int if_set_ipaddr(const char *, const char *,
135 const char *);
136 static int bridge_add(const char *, const char *);
137 static bool frame_ctor(pnode_t *);
138 static void frame_dtor(pnode_t *);
139 static void queue_schedule(tap_t *, struct timeval *);
140 static bool queue_send(tap_t *);
141 static bool frame_receive(tap_t *);
142 static bool modules_reload(const char *, bool);
143
144
145 /* Parameters */
146 static char *extmac = EXTMAC, *intmac = INTMAC, *ifeth = NULL,
147 *ifbr = IFBR, *pidfile = PIDFILE, *tapfile = TAPFILE,
148 *ipaddr = NULL, *netmask = NULL, *user = USER,
149 *group = GROUP, *module_dir = MODULE_DIR;
150
151 /* Flow control and book keeping */
152 static int ifsockinet = -1;
153 static bool bridgecreated = false;
154 static bool parent_exit = false;
155 static pid_t child_pid = -1;
156 static bool child_exit = false;
157 static bool schedule_now = false;
158 static bool revents = 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 /* Create bridge interface */
420 if (if_create(ifbr) == -1)
421 goto err;
422 bridgecreated = true;
423 if (bridge_add(ifbr, ifeth) == -1 ||
424 bridge_add(ifbr, tap_int->name) == -1 ||
425 if_up(tap_int->name) == -1 ||
426 if_up(ifeth) == -1 ||
427 if_up(ifbr) == -1)
428 goto err;
429 bridgecreated = true;
430
431 if (detach(pidfile, tapfile, tap_ext->name) == -1)
432 goto err;
433
434 /* Setup parent sighandler */
435 {
436 struct sigaction act;
437
438 act.sa_handler = parent_sighandler;
439 (void) sigemptyset(&act.sa_mask);
440 act.sa_flags = SA_RESTART;
441 if (sigaction(SIGTERM, &act, NULL) == -1 ||
442 sigaction(SIGINT, &act, NULL) == -1 ||
443 sigaction(SIGCHLD, &act, NULL) == -1 ||
444 sigaction(SIGHUP, &act, NULL) == -1) {
445 warning(errno, "sigaction(parent)");
446 goto err;
447 }
448 act.sa_handler = SIG_IGN;
449 (void) sigaction(SIGTTOU, &act, NULL);
450 (void) sigaction(SIGTTIN, &act, NULL);
451 (void) sigaction(SIGTSTP, &act, NULL);
452 }
453
454 /* This application is latency-sensitive */
455 if (setpriority(PRIO_PGRP, 0, -10) == -1) {
456 warning(errno, "setpriority");
457 goto err;
458 }
459
460 return true;
461
462 err:
463 parent_cleanup();
464 return false;
465 }
466
467 /*
468 * Simply loop until our child process dies.
469 */
470 static void
471 parent_main(void)
472 {
473 sigset_t set;
474
475 warning(0, "Parent process started");
476
477 (void) sigemptyset(&set);
478
479 while (!parent_exit)
480 (void) sigsuspend(&set);
481
482 warning(0, "Parent process exiting");
483 }
484
485 static void
486 parent_sighandler(int sig)
487 {
488
489 switch (sig) {
490 case SIGTERM:
491 /* FALLTHROUGH */
492 case SIGINT:
493 /* FALLTHROUGH */
494 case SIGHUP:
495 if (child_pid != -1) {
496 warning(0, "Parent forwarding signal %d to child",
497 sig);
498 (void) kill(child_pid, sig);
499 }
500 break;
501 case SIGCHLD:
502 for (;;) {
503 pid_t pid;
504 int status = 0;
505
506 if ((pid = wait3(&status, WNOHANG, NULL)) < 1)
507 break;
508 if (!WIFEXITED(status))
509 continue;
510 warning(0, "Parent detected child exit");
511 parent_exit = true;
512 break;
513 }
514 break;
515 }
516 }
517
518 static void
519 parent_cleanup(void)
520 {
521
522 (void) if_down(ifeth);
523 if (bridgecreated) {
524 (void) if_down(ifbr);
525 (void) if_destroy(ifbr);
526 }
527 if (tap_int != NULL) {
528 (void) if_down(tap_int->name);
529 tap_close(tap_int);
530 }
531 if (tap_ext != NULL) {
532 (void) if_down(tap_ext->name);
533 tap_close(tap_ext);
534 }
535 if (ifsockinet != -1)
536 (void) close(ifsockinet);
537 }
538
539 static bool
540 child_init(void)
541 {
542
543 /*
544 * Create a child process to which we delegate the following code.
545 * We use this for privilege separation. The parent simply waits
546 * until the child exits to then clean up resources.
547 */
548 {
549 pid_t pid;
550
551 if ((pid = fork()) == -1) {
552 warning(errno, "fork(child)");
553 return false;
554 }
555 if (pid != 0) {
556 child_pid = pid;
557 return true;
558 }
559 }
560
561 /*
562 * We're the child, drop privileges and continue.
563 */
564 if (unprivileged(user, group) == -1)
565 goto err;
566
567 /* Setup child sighandler */
568 {
569 struct sigaction act;
570
571 act.sa_handler = child_sighandler;
572 (void) sigemptyset(&act.sa_mask);
573 act.sa_flags = SA_RESTART;
574 if (sigaction(SIGTERM, &act, NULL) == -1 ||
575 sigaction(SIGINT, &act, NULL) == -1 ||
576 sigaction(SIGALRM, &act, NULL) == -1 ||
577 sigaction(SIGHUP, &act, NULL) == -1 ||
578 sigaction(SIGIO, &act, NULL) == -1) {
579 warning(errno, "sigaction(child)");
580 goto err;
581 }
582 act.sa_handler = SIG_IGN;
583 (void) sigaction(SIGCHLD, &act, NULL);
584 }
585
586 /*
587 * Get SIGIO signal when data is available and use non-blocking mode
588 */
589 {
590 pid_t pid = getpid();
591
592 if (fcntl(tap_ext->fd, F_SETOWN, pid) == -1 ||
593 fcntl(tap_int->fd, F_SETOWN, pid) == -1) {
594 warning(errno, "fcntl(F_SETOWN)");
595 goto err;
596 }
597 if (fcntl(tap_ext->fd, F_SETFL, O_NONBLOCK | O_ASYNC) == -1 ||
598 fcntl(tap_int->fd, F_SETFL, O_NONBLOCK | O_ASYNC) == -1) {
599 warning(errno, "fcntl(O_NONBLOCK | O_ASYNC)");
600 goto err;
601 }
602 }
603
604 /* Setup our pools */
605 {
606 size_t pagesize;
607
608 pagesize = sysconf(_SC_PAGESIZE);
609 if (!pool_init(&frame_pool, "frame_pool", malloc, free,
610 frame_ctor, frame_dtor, sizeof(frame_t),
611 pagesize / sizeof(frame_t), 1, 0)) {
612 warning(errno, "pool_init(frame_pool)");
613 goto err;
614 }
615 if (!pool_init(&fnode_pool, "fnode_pool", malloc, free,
616 NULL, NULL, sizeof(fnode_t), pagesize / sizeof(fnode_t),
617 1, 0)) {
618 warning(errno, "pool_init(fnode_pool)");
619 goto err;
620 }
621 if (!pool_init(&key_pool, "key_pool", malloc, free, NULL,
622 NULL, sizeof(storage_t), pagesize / sizeof(storage_t),
623 1, 0)) {
624 warning(errno, "pool_init(key_pool)");
625 goto err;
626 }
627 if (!pool_init(&modnode_pool, "modnode_pool", malloc, free,
628 NULL, NULL, sizeof(modnode_t),
629 pagesize / sizeof(modnode_t), 1, 0)) {
630 warning(errno, "pool_init(modnode_pool)");
631 goto err;
632 }
633 }
634 /* And our keys hash table */
635 if (!hashtable_init(&storage_table, "storage_table",
636 HT_DEFAULT_CAPACITY, HT_DEFAULT_FACTOR, malloc, free, memcmp,
637 hashtable_hash, true)) {
638 warning(errno, "hashtable_init(storage_table)");
639 goto err;
640 }
641 /* List of loaded modules */
642 DLIST_INIT(&module_list);
643
644 /* Start timer */
645 {
646 struct itimerval itv;
647
648 timerclear(&time_event);
649 itv.it_interval.tv_sec = 0;
650 itv.it_interval.tv_usec = 1000000 / TIMER_HZ;
651 itv.it_value = itv.it_interval;
652 if (setitimer(ITIMER_REAL, &itv, NULL) == -1) {
653 warning(errno, "setitimer");
654 goto err;
655 }
656 }
657
658 /* Load modules */
659 if (!modules_reload(module_dir, true)) {
660 warning(errno, "modules_reload");
661 goto err;
662 }
663
664 child_main();
665 child_cleanup();
666 exit(EXIT_SUCCESS);
667
668 err:
669 exit(EXIT_SUCCESS);
670 }
671
672 static void
673 child_main(void)
674 {
675 sigset_t set;
676
677 warning(0, "Child unprivileged process started");
678
679 (void) sigemptyset(&set);
680
681 while (!child_exit) {
682
683 /*
684 * If a scheduling round was triggered, transfer all expired
685 * nodes to the send list and update timeout to soonest to be
686 * expired node.
687 */
688 if (schedule_now) {
689 struct timeval ttv;
690
691 schedule_now = false;
692
693 /* Start with initial timeout of one minute */
694 ttv = time_current;
695 ttv.tv_sec += 60;
696
697 queue_schedule(tap_ext, &ttv);
698 queue_schedule(tap_int, &ttv);
699
700 /* Set new timeout to soonest expireing node */
701 time_event = ttv;
702 }
703
704 /*
705 * Send out some data from our send queue if any.
706 * What resides here should already have been sanity checked
707 * by any user code, as it was queued by user code for
708 * sending.
709 */
710 if (!queue_send(tap_ext) && errno != EAGAIN &&
711 errno != EHOSTDOWN) {
712 warning(errno, "queue_send(tap_ext)");
713 break;
714 }
715 if (!queue_send(tap_int) && errno != EAGAIN &&
716 errno != EHOSTDOWN) {
717 warning(errno, "queue_send(tap_int)");
718 break;
719 }
720
721 /*
722 * Read in data if any, allocating new frames for it and
723 * feeding them to user code, which may filter, modify, drop
724 * or respond by sending response packets. We also fill in a
725 * few fields of the frame_t structure prior to passing them
726 * to user code.
727 */
728 revents = false;
729 if (!frame_receive(tap_int) && errno != EAGAIN &&
730 errno != EHOSTDOWN) {
731 warning(errno, "frame_receive(tap_int)");
732 break;
733 }
734 if (!frame_receive(tap_ext) && errno != EAGAIN &&
735 errno != EHOSTDOWN) {
736 warning(errno, "frame_receive(tap_ext)");
737 break;
738 }
739
740 /* Are we supposed to quit? */
741 if (child_exit)
742 break;
743
744 /* Don't sleep yet if we have more events to process. */
745 if ((schedule_now || revents) && errno != EHOSTDOWN)
746 continue;
747
748 while (!revents)
749 (void) sigsuspend(&set);
750 }
751
752 warning(0, "Child unprivileged process exiting");
753 }
754
755 /* ARGSUSED */
756 static void
757 child_sighandler(int sig)
758 {
759 modnode_t *n;
760
761 switch (sig) {
762 case SIGIO:
763 revents = true;
764 break;
765 case SIGTERM:
766 /* FALLTHROUGH */
767 case SIGINT:
768 warning(0, "Child received SIGTERM; exiting");
769 child_exit = true;
770 break;
771 case SIGALRM:
772 /*
773 * Update current time value to prevent having to invoke
774 * gettimeofday(2) at a too high frequency.
775 * Call modules callback functions if any, and verify if any
776 * scheduled to be sent packets should now be sent, setting
777 * the schedule_now flag if so.
778 */
779 (void) gettimeofday(&time_current, NULL);
780 DLIST_FOREACH(&module_list, n) {
781 if (n->module->tick != NULL)
782 n->module->tick(&time_current);
783 }
784 if (timerisset(&time_event) &&
785 timercmp(&time_event, &time_current, <))
786 schedule_now = true;
787 break;
788 case SIGHUP:
789 warning(0, "Child received SIGHUP; reloading modules");
790 /* Reload modules specified in configuration file */
791 if (!modules_reload(module_dir, true))
792 warning(errno, "modules_reload");
793 break;
794 }
795 }
796
797 static void
798 child_cleanup(void)
799 {
800
801 (void) modules_reload(module_dir, false);
802 }
803
804 /*
805 * Converts the ETHER_ADDR_LEN sized byte string to a 64-bit aligned macaddr_t
806 * type for better performance. Since this consists of an internal format,
807 * endian byte order is unimportant. This will be used for comparisions
808 * against other macaddr_t. To simplify things even more, accessing a
809 * macaddr_t as an uint8_t array should return back the size bytes in the same
810 * order they previously were, which may be useful for operations such as
811 * logging.
812 */
813 static void
814 macaddr_align8(macaddr_t *a, const uint8_t *b)
815 {
816 register const uint8_t *rb = b;
817 register uint8_t *rab = (uint8_t *)a;
818
819 /* Unroll loop */
820 rab[0] = rb[0];
821 rab[1] = rb[1];
822 rab[2] = rb[2];
823 rab[3] = rb[3];
824 rab[4] = rb[4];
825 rab[5] = rb[5];
826 rab[6] = 0x00;
827 rab[7] = 0x00;
828 }
829
830 /*
831 * This optimized version deals with known to be 16-bit aligned MAC addresses,
832 * as is the case with origin/destination addresses received in an ethernet
833 * frame.
834 */
835 static void
836 macaddr_align16(macaddr_t *a, const uint16_t *b)
837 {
838 register const uint16_t *rb = b;
839 register uint16_t *rab = (uint16_t *)a;
840
841 /* Unroll loop */
842 rab[0] = rb[0];
843 rab[1] = rb[1];
844 rab[2] = rb[2];
845 rab[3] = 0x0000;
846 }
847
848 static tap_t *
849 tap_open(void)
850 {
851 int fd = -1;
852 tap_t *tap = NULL;
853 struct ifreq iface;
854
855 if ((fd = open("/dev/tap", O_RDWR)) == -1) {
856 warning(errno, "open(/dev/tap)");
857 goto err;
858 }
859 if ((tap = malloc(sizeof(tap_t))) == NULL) {
860 warning(errno, "malloc(tap_t)");
861 goto err;
862 }
863 if (ioctl(fd, TAPGIFNAME, &iface) == -1) {
864 warning(errno, "ioctl(TAPGIFNAME)");
865 goto err;
866 }
867
868 tap->fd = fd;
869 if ((tap->name = strdup(iface.ifr_name)) == NULL) {
870 warning(errno, "stddup(iface.ifr_name)");
871 goto err;
872 }
873
874 DLIST_INIT(&tap->schedq);
875 DLIST_INIT(&tap->sendq);
876
877 return tap;
878
879 err:
880 if (fd != -1)
881 (void) close(fd);
882 if (tap != NULL) {
883 if (tap->name != NULL)
884 free(tap->name);
885 free(tap);
886 }
887 return NULL;
888 }
889
890 static void
891 tap_close(tap_t *tap)
892 {
893
894 assert(tap != NULL);
895 assert(tap->name != NULL);
896 assert(tap->fd != -1);
897 free(tap->name);
898 (void) close(tap->fd);
899 free(tap);
900 }
901
902 static int
903 if_create(const char *iface)
904 {
905 struct ifreq ifr;
906 int error;
907
908 (void) strncpy(ifr.ifr_name, iface, sizeof(ifr.ifr_name) - 1);
909 if ((error = ioctl(ifsockinet, SIOCIFCREATE, &ifr)) == -1)
910 warning(errno, "if_create(%s)", iface);
911 return error;
912 }
913
914 static int
915 if_destroy(const char *iface)
916 {
917 struct ifreq ifr;
918 int error;
919
920 (void) strncpy(ifr.ifr_name, iface, sizeof(ifr.ifr_name) - 1);
921 if ((error = ioctl(ifsockinet, SIOCIFDESTROY, &ifr)) == -1)
922 warning(errno, "if_destroy(%s)", iface);
923 return error;
924 }
925
926 static int
927 if_up(const char *iface)
928 {
929 struct ifreq ifr;
930 int error;
931
932 (void) strncpy(ifr.ifr_name, iface, sizeof(ifr.ifr_name) - 1);
933 if (ioctl(ifsockinet, SIOCGIFFLAGS, &ifr) != 0)
934 return -1;
935 ifr.ifr_flags |= IFF_UP;
936 if ((error = ioctl(ifsockinet, SIOCSIFFLAGS, &ifr)) == -1)
937 warning(errno, "if_up(%s)", iface);
938 return error;
939 }
940
941 static int
942 if_down(const char *iface)
943 {
944 struct ifreq ifr;
945 int error;
946
947 (void) strncpy(ifr.ifr_name, iface, sizeof(ifr.ifr_name) - 1);
948 if (ioctl(ifsockinet, SIOCGIFFLAGS, &ifr) != 0)
949 return -1;
950 ifr.ifr_flags &= ~IFF_UP;
951 if ((error = ioctl(ifsockinet, SIOCSIFFLAGS, &ifr)) == -1)
952 warning(errno, "if_down(%s)", iface);
953 return error;
954 }
955
956 static int
957 if_get_macaddr(uint8_t *buf, const char *iface)
958 {
959 struct ifaddrs *ifa = NULL, *p;
960 int error = 0;
961
962 if ((error = getifaddrs(&ifa)) == -1)
963 goto out;
964 for (p = ifa; p != NULL; p = p->ifa_next) {
965 if (strcmp(iface, p->ifa_name) == 0)
966 break;
967 }
968 if (p == NULL) {
969 error = -1;
970 errno = ENOENT;
971 goto out;
972 }
973 if (p->ifa_addr->sa_family != AF_LINK) {
974 error = -1;
975 errno = EAFNOSUPPORT;
976 goto out;
977 }
978
979 (void) memcpy(buf, p->ifa_addr->sa_data, ETHER_ADDR_LEN);
980
981 out:
982 if (ifa != NULL)
983 freeifaddrs(ifa);
984
985 if (error != 0)
986 warning(errno, "if_get_macaddr(%s)", iface);
987 return error;
988 }
989
990 /*
991 * This is tap(4) specific for now. Hopefully a generic ethernet ioctl(2)
992 * will eventually be provided for this.
993 */
994 static int
995 if_set_macaddr(const char *iface, const char *addr)
996 {
997 struct ifaliasreq ifra;
998 struct ether_addr *eaddr;
999
1000 (void) memset(&ifra, 0x00, sizeof(struct ifaliasreq));
1001
1002 if ((eaddr = ether_aton(addr)) == NULL)
1003 goto err;
1004 (void) memcpy(ifra.ifra_addr.sa_data, eaddr->ether_addr_octet,
1005 ETHER_ADDR_LEN);
1006 (void) strncpy(ifra.ifra_name, iface, sizeof(ifra.ifra_name) - 1);
1007 ifra.ifra_addr.sa_family = AF_LINK;
1008 ifra.ifra_addr.sa_len = sizeof(struct sockaddr_dl);
1009
1010 if (ioctl(ifsockinet, SIOCSIFPHYADDR, &ifra) == -1)
1011 goto err;
1012 return 0;
1013
1014 err:
1015 warning(errno, "if_set_macaddr(%s, %s)", iface, addr);
1016 return -1;
1017 }
1018
1019 /*
1020 static int
1021 if_get_ipaddr(in_addr_t *addr, in_addr_t *mask, in_addr_t *braddr,
1022 const char *iface)
1023 {
1024 struct ifreq ifr;
1025 struct sockaddr_in *sin = (struct sockaddr_in *)&ifr.ifr_addr;
1026
1027 (void) memset(&ifr, 0x00, sizeof(struct ifreq));
1028 (void) strncpy(ifr.ifr_name, iface, sizeof(ifr.ifr_name) - 1);
1029
1030 ifr.ifr_addr.sa_family = AF_INET;
1031 ifr.ifr_addr.sa_len = sizeof(struct sockaddr_in);
1032 if (ioctl(ifsockinet, SIOCGIFADDR, &ifr) == -1)
1033 goto err;
1034 *addr = sin->sin_addr.s_addr;
1035 sin->sin_addr.s_addr = 0x00000000;
1036
1037 ifr.ifr_addr.sa_family = AF_INET;
1038 ifr.ifr_addr.sa_len = sizeof(struct sockaddr_in);
1039 if (ioctl(ifsockinet, SIOCGIFNETMASK, &ifr) == -1)
1040 goto err;
1041 *mask = sin->sin_addr.s_addr;
1042 sin->sin_addr.s_addr = 0x00000000;
1043
1044 ifr.ifr_addr.sa_family = AF_INET;
1045 ifr.ifr_addr.sa_len = sizeof(struct sockaddr_in);
1046 if (ioctl(ifsockinet, SIOCGIFBRDADDR, &ifr) == -1)
1047 goto err;
1048 *braddr = sin->sin_addr.s_addr;
1049
1050 return 0;
1051
1052 err:
1053 warning(errno, "if_get_ipaddr(%s)", iface);
1054 return -1;
1055 }
1056 */
1057
1058 static int
1059 if_set_ipaddr(const char *iface, const char *netmask, const char *address)
1060 {
1061 struct ifaliasreq ifra;
1062 struct sockaddr_in *sin;
1063
1064 (void) memset(&ifra, 0x00, sizeof(struct ifaliasreq));
1065 (void) strncpy(ifra.ifra_name, iface, sizeof(ifra.ifra_name) - 1);
1066
1067 sin = (struct sockaddr_in *)&ifra.ifra_addr;
1068 if (inet_pton(AF_INET, address, &sin->sin_addr) != 1)
1069 goto err;
1070 ifra.ifra_addr.sa_family = AF_INET;
1071 ifra.ifra_addr.sa_len = sizeof(struct sockaddr_in);
1072
1073 sin = (struct sockaddr_in *)&ifra.ifra_mask;
1074 if (inet_pton(AF_INET, netmask, &sin->sin_addr) != 1)
1075 goto err;
1076 ifra.ifra_mask.sa_family = AF_INET;
1077 ifra.ifra_mask.sa_len = sizeof(struct sockaddr_in);
1078
1079 if (ioctl(ifsockinet, SIOCAIFADDR, &ifra) == -1)
1080 goto err;
1081
1082 return 0;
1083
1084 err:
1085 warning(errno, "if_set_ipaddr(%s)", iface);
1086 return -1;
1087 }
1088
1089 static int
1090 bridge_add(const char *br, const char *iface)
1091 {
1092 struct ifbreq req;
1093 struct ifdrv ifd;
1094 int error;
1095
1096 (void) memset(&req, 0x00, sizeof(struct ifbreq));
1097 (void) strncpy(req.ifbr_ifsname, iface, sizeof(req.ifbr_ifsname) - 1);
1098
1099 (void) memset(&ifd, 0x00, sizeof(struct ifdrv));
1100 (void) strncpy(ifd.ifd_name, br, sizeof(ifd.ifd_name) - 1);
1101 ifd.ifd_cmd = BRDGADD;
1102 ifd.ifd_len = sizeof(struct ifbreq);
1103 ifd.ifd_data = &req;
1104
1105 if ((error = ioctl(ifsockinet, SIOCSDRVSPEC, &ifd)) == -1)
1106 warning(errno, "bridge_add(%s, %s)", br, iface);
1107 return error;
1108 }
1109
1110 static bool
1111 frame_ctor(pnode_t *pnod)
1112 {
1113 frame_t *f = (frame_t *)pnod;
1114
1115 if ((f->data = malloc(FRAMESIZE)) == NULL)
1116 return false;
1117
1118 return true;
1119 }
1120
1121 static void
1122 frame_dtor(pnode_t *pnod)
1123 {
1124 frame_t *f = (frame_t *)pnod;
1125
1126 if (f->data != NULL)
1127 free(f->data);
1128 }
1129
1130 static void
1131 queue_schedule(tap_t *tap, struct timeval *ttv)
1132 {
1133 fnode_t *n, *t;
1134 struct timeval *tv;
1135
1136 tv = ttv;
1137 for (n = (fnode_t *)DLIST_TOP(&tap->schedq); n != NULL; n = t) {
1138 t = (fnode_t *)DLIST_NEXT((node_t *)n);
1139 if (timercmp(&n->scheduled, &time_current, <))
1140 DLIST_SWAP(&tap->sendq, &tap->schedq, (node_t *)n,
1141 false);
1142 else if (timercmp(&n->scheduled, tv, <))
1143 tv = &n->scheduled;
1144 }
1145
1146 if (tv != ttv)
1147 *ttv = *tv;
1148 }
1149
1150 static bool
1151 queue_send(tap_t *tap)
1152 {
1153 node_t *n, *t;
1154 size_t count;
1155
1156 count = 0;
1157 for (n = DLIST_TOP(&tap->sendq);
1158 !child_exit && count < OUTMAX && n != NULL;
1159 n = t) {
1160 fnode_t *fn = (fnode_t *)n;
1161 ssize_t ret;
1162 struct iovec iov[2];
1163
1164 t = DLIST_NEXT(n);
1165
1166 iov[0].iov_base = fn->frame->data;
1167 iov[0].iov_len = 14;
1168 iov[1].iov_base = &fn->frame->data[16];
1169 iov[1].iov_len = fn->frame->size - 16;
1170 /* On error leave pending frame on queue */
1171 if ((ret = writev(tap->fd, iov, 2)) != fn->frame->size - 2)
1172 return false;
1173
1174 count += fn->frame->size;
1175 DLIST_UNLINK(&tap->sendq, n);
1176 fnode_destroy(fn);
1177 }
1178
1179 errno = EAGAIN;
1180 return false;
1181 }
1182
1183 static bool
1184 frame_receive(tap_t *tap)
1185 {
1186 size_t count;
1187 ssize_t len;
1188 frame_t *f;
1189
1190 count = 0;
1191 for (f = NULL; !child_exit && count < INMAX; count += len) {
1192 struct iovec iov[2];
1193 modnode_t *n;
1194
1195 if ((f = (frame_t *)pool_alloc(&frame_pool, false)) == NULL) {
1196 errno = ENOMEM;
1197 warning(errno, "pool_alloc");
1198 return false;
1199 }
1200
1201 ((uint16_t *)f->data)[7] = 0x0000;
1202 iov[0].iov_base = f->data;
1203 iov[0].iov_len = 14;
1204 iov[1].iov_base = &f->data[16];
1205 iov[1].iov_len = FRAMESIZE - 14;
1206 len = readv(tap->fd, iov, 2);
1207 if (len == -1 || len == 0) {
1208 (void) pool_free((pnode_t *)f);
1209 return false;
1210 }
1211
1212 macaddr_align16(&f->destination, (uint16_t *)f->data);
1213 macaddr_align16(&f->origin, &((uint16_t *)f->data)[3]);
1214 f->received = time_current;
1215 f->size = len + 2;
1216 f->type = BYTEORDER_HOST16(((uint16_t *)f->data)[6]);
1217
1218 /*
1219 * To every module receive function we pass a new fnode_t
1220 * which it becomes responsible for.
1221 */
1222 DLIST_FOREACH(&module_list, n) {
1223 bool (*recv)(tap_t *, fnode_t *);
1224 fnode_t *fn;
1225
1226 if ((recv = (tap == tap_int ? n->module->recv_int :
1227 n->module->recv_ext)) == NULL)
1228 continue;
1229
1230 if ((fn = fnode_new(f)) == NULL) {
1231 warning(errno, "fnode_new");
1232 continue;
1233 }
1234 if (!recv(tap, fn))
1235 break;
1236 }
1237 }
1238
1239 return true;
1240 }
1241
1242 static bool
1243 modules_reload(const char *fp, bool load)
1244 {
1245 bool ret = false;
1246 FILE *fh = NULL;
1247 modnode_t *n, *t;
1248 char buf[256], path[1024];
1249 void *handle = NULL;
1250 sigset_t set, oset;
1251
1252 (void) sigemptyset(&set);
1253 (void) sigaddset(&set, SIGALRM);
1254 (void) sigaddset(&set, SIGTERM);
1255 (void) sigaddset(&set, SIGHUP);
1256 oset = set;
1257 (void) sigprocmask(SIG_BLOCK, &set, NULL);
1258
1259 /* Unload all modules in reverse order */
1260 for (n = (modnode_t *)DLIST_BOTTOM(&module_list); n != NULL; n = t) {
1261 t = (modnode_t *)DLIST_PREV((pnode_t *)n);
1262
1263 n->module->exit();
1264 (void) dlclose(n->handle);
1265 DLIST_UNLINK(&module_list, (node_t *)n);
1266 (void) pool_free((pnode_t *)n);
1267 }
1268 if (!load) {
1269 ret = true;
1270 goto out;
1271 }
1272
1273 (void) snprintf(path, sizeof(path) - 1, "%s/%s", fp, "modules.conf");
1274 if ((fh = fopen(path, "r")) == NULL) {
1275 warning(errno, "fopen");
1276 goto out;
1277 }
1278
1279 /* Attempt to reload every specified module */
1280 while (fgets(buf, sizeof(buf) - 1, fh) != NULL) {
1281 void *sym;
1282 char *cptr;
1283
1284 if ((cptr = strchr(buf, '\n')) != NULL)
1285 *cptr = '\0';
1286 (void) snprintf(path, sizeof(path) - 1, "%s/%s", fp, buf);
1287 if ((handle = dlopen(path, O_RDONLY)) == NULL) {
1288 warning(errno, "dlopen(%s) - %s", buf, dlerror());
1289 goto out;
1290 }
1291 if ((sym = dlsym(handle, "module")) == NULL) {
1292 warning(errno, "dlsym(%s, module) - %s",
1293 buf, dlerror());
1294 goto out;
1295 }
1296 if ((n = (modnode_t *)pool_alloc(&modnode_pool, false))
1297 == NULL)
1298 goto out;
1299
1300 n->handle = handle;
1301 handle = NULL;
1302 n->module = sym;
1303
1304 if (n->module->init == NULL || n->module->exit == NULL) {
1305 warning(errno, "module(%s) - No init()/exit()!", buf);
1306 goto out;
1307 }
1308 if (!n->module->init(tap_int, tap_ext)) {
1309 warning(errno, "module(%s) - init() failed!", buf);
1310 goto out;
1311 }
1312
1313 DLIST_APPEND(&module_list, (node_t *)n);
1314 n = NULL;
1315 }
1316
1317 ret = true;
1318
1319 out:
1320 if (n != NULL)
1321 (void) pool_free((pnode_t *)n);
1322 if (handle != NULL)
1323 (void) dlclose(handle);
1324 if (fh != NULL)
1325 (void) fclose(fh);
1326
1327 (void) sigprocmask(SIG_UNBLOCK, &oset, NULL);
1328 return ret;
1329 }
1330
1331
1332 /*
1333 * EXPORTED FUNCTIONS
1334 */
1335
1336 /*
1337 * Creates space for a new frame. The caller should fill the frame itself.
1338 * Returns NULL on error.
1339 */
1340 frame_t *
1341 frame_new(void)
1342 {
1343 frame_t *f;
1344
1345 if ((f = (frame_t *)pool_alloc(&frame_pool, false)) != NULL)
1346 f->refcount = 0;
1347
1348 return f;
1349 }
1350
1351 /*
1352 * Creates a new frame node. These may be manipulated as wanted. A frame is
1353 * never freed unless all created fnode_t objects tried to a frame_t are
1354 * destroyed. This is a useful block to use custom queueing with frames.
1355 */
1356 fnode_t *
1357 fnode_new(frame_t *f)
1358 {
1359 fnode_t *n;
1360
1361 assert(f != NULL);
1362
1363 if ((n = (fnode_t *)pool_alloc(&fnode_pool, false)) != NULL) {
1364 f->refcount++;
1365 n->frame = f;
1366 }
1367
1368 return n;
1369 }
1370
1371 /*
1372 * Destroys the specified frame node. If all frame nodes tied to the
1373 * underlaying frame_t the frame is also destroyed.
1374 */
1375 void
1376 fnode_destroy(fnode_t *n)
1377 {
1378
1379 assert(n != NULL && n->frame != NULL);
1380
1381 if (--(n->frame->refcount) == 0)
1382 (void) pool_free((pnode_t *)n->frame);
1383 (void) pool_free((pnode_t *)n);
1384 }
1385
1386 size_t
1387 macaddr_string(char *str, size_t len, macaddr_t *a)
1388 {
1389 register uint8_t *rb = (uint8_t *)a;
1390
1391 return snprintf(str, len, "%02x:%02x:%02x:%02x:%02x:%02x",
1392 rb[0], rb[1], rb[2], rb[3], rb[4], rb[5]);
1393 }
1394
1395 /*
1396 * Send the specified frame as soon as possible.
1397 * A new fnode_t object is internally created to queue this node for sending.
1398 * The sender thus should still destroy its fnode_t unless it intends to send
1399 * the same frame again.
1400 */
1401 void
1402 frame_send(tap_t *tap, fnode_t *n)
1403 {
1404 fnode_t *fn;
1405
1406 assert(tap != NULL && n != NULL);
1407
1408 if ((fn = fnode_new(n->frame)) != NULL)
1409 DLIST_APPEND(&tap->sendq, (node_t *)fn);
1410 /* Flush queue immediately to minimize latency */
1411 (void) queue_send(tap);
1412 }
1413
1414 /*
1415 * Sets up this frame_t to be scheduled for sending after the specified time
1416 * has elapsed. The frame_t will be queued and recycled automatically once it
1417 * has been sent.
1418 */
1419 void
1420 frame_schedule(tap_t *tap, fnode_t *n, const struct timeval *tv)
1421 {
1422 fnode_t *fn;
1423
1424 assert(tap != NULL && n != NULL && tv != NULL);
1425
1426 if (!timerisset(&time_event) || timercmp(tv, &time_event, <))
1427 time_event = *tv;
1428 if ((fn = fnode_new(n->frame)) != NULL) {
1429 fn->scheduled = *tv;
1430 DLIST_APPEND(&tap->schedq, (node_t *)fn);
1431 }
1432 }
1433
1434 /*
1435 * Attempts to create a new storage key. Returns a pointer to a void * on
1436 * success, or NULL on error. This void * may then be initialized and used as
1437 * wanted.
1438 */
1439 void **
1440 key_create(const char *key)
1441 {
1442 storage_t *node;
1443
1444 assert(key != NULL);
1445
1446 if (hashtable_lookup(&storage_table, key, strlen(key)) != NULL)
1447 return NULL;
1448
1449 if ((node = (storage_t *)pool_alloc(&key_pool, false)) != NULL) {
1450
1451 (void) strncpy(node->key, key, sizeof(node->key) - 1);
1452 node->udata = NULL;
1453
1454 if (hashtable_link(&storage_table, (hashnode_t *)node,
1455 node->key, strlen(key), false))
1456 return &node->udata;
1457
1458 (void) pool_free((pnode_t *)node);
1459 }
1460
1461 return NULL;
1462 }
1463
1464 /*
1465 * Attempts to locate an existing storage key. Returns a pointer to a void *
1466 * on success, or NULL if it doesn't exist. This void * may previously have
1467 * been initialized for custom storage.
1468 */
1469 void **
1470 key_lookup(const char *key)
1471 {
1472 storage_t *node;
1473
1474 assert(key != NULL);
1475
1476 if ((node = (storage_t *)hashtable_lookup(&storage_table, key,
1477 strlen(key))) != NULL)
1478 return &node->udata;
1479
1480 return NULL;
1481 }
1482
1483 /*
1484 * Attempts to destroy an existing storage key. Does nothing about the
1485 * storage void *, it thus should be freed as necessary by the module.
1486 * It is possible to not call this function and expect the storage key to
1487 * still exist after modules were reloaded.
1488 */
1489 void
1490 key_destroy(const char *key)
1491 {
1492 hashnode_t *node;
1493
1494 assert(key != NULL);
1495
1496 if ((node = hashtable_lookup(&storage_table, key, strlen(key)))
1497 != NULL) {
1498 hashtable_unlink(&storage_table, (node));
1499 (void) pool_free((pnode_t *)node);
1500 }
1501 }
1502
1503 void
1504 warning(int err, const char *fmt, ...)
1505 {
1506 va_list lst;
1507 char msgbuf[1024];
1508 sigset_t set, oset;
1509
1510 (void) sigemptyset(&set);
1511 (void) sigaddset(&set, SIGALRM);
1512 (void) sigaddset(&set, SIGHUP);
1513 oset = set;
1514 (void) sigprocmask(SIG_BLOCK, &set, NULL);
1515
1516 /*
1517 * Since we want to append the errno error message after the custom
1518 * message, we need to store it in a buffer rather than simply use
1519 * vsyslog(3).
1520 */
1521 va_start(lst, fmt);
1522 (void) vsnprintf(msgbuf, sizeof(msgbuf) - 1, fmt, lst);
1523 va_end(lst);
1524
1525 if (err != 0)
1526 syslog(LOG_NOTICE, "warning: %s: %s", msgbuf, strerror(err));
1527 else
1528 syslog(LOG_NOTICE, "notice: %s", msgbuf);
1529
1530 (void) sigprocmask(SIG_UNBLOCK, &oset, NULL);
1531 }