]> git.ipfire.org Git - thirdparty/dhcp.git/blob - relay/dhcrelay.c
980dacaeb1745ea83945259b6d04c1ed30bd10f8
[thirdparty/dhcp.git] / relay / dhcrelay.c
1 /* dhcrelay.c
2
3 DHCP/BOOTP Relay Agent. */
4
5 /*
6 * Copyright(c) 2004-2019 by Internet Systems Consortium, Inc.("ISC")
7 * Copyright(c) 1997-2003 by Internet Software Consortium
8 *
9 * This Source Code Form is subject to the terms of the Mozilla Public
10 * License, v. 2.0. If a copy of the MPL was not distributed with this
11 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
12 *
13 * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
14 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
15 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
16 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
17 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
18 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
19 * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
20 *
21 * Internet Systems Consortium, Inc.
22 * 950 Charter Street
23 * Redwood City, CA 94063
24 * <info@isc.org>
25 * https://www.isc.org/
26 *
27 */
28
29 #include "dhcpd.h"
30 #include <syslog.h>
31 #include <signal.h>
32 #include <sys/time.h>
33 #include <isc/file.h>
34
35 TIME default_lease_time = 43200; /* 12 hours... */
36 TIME max_lease_time = 86400; /* 24 hours... */
37 struct tree_cache *global_options[256];
38
39 struct option *requested_opts[2];
40
41 /* Needed to prevent linking against conflex.c. */
42 int lexline;
43 int lexchar;
44 char *token_line;
45 char *tlname;
46
47 const char *path_dhcrelay_pid = _PATH_DHCRELAY_PID;
48 isc_boolean_t no_dhcrelay_pid = ISC_FALSE;
49 /* False (default) => we write and use a pid file */
50 isc_boolean_t no_pid_file = ISC_FALSE;
51
52 int bogus_agent_drops = 0; /* Packets dropped because agent option
53 field was specified and we're not relaying
54 packets that already have an agent option
55 specified. */
56 int bogus_giaddr_drops = 0; /* Packets sent to us to relay back to a
57 client, but with a bogus giaddr. */
58 int client_packets_relayed = 0; /* Packets relayed from client to server. */
59 int server_packet_errors = 0; /* Errors sending packets to servers. */
60 int server_packets_relayed = 0; /* Packets relayed from server to client. */
61 int client_packet_errors = 0; /* Errors sending packets to clients. */
62
63 int add_agent_options = 0; /* If nonzero, add relay agent options. */
64 int add_rfc3527_suboption = 0; /* If nonzero, add RFC3527 link selection sub-option. */
65
66 int agent_option_errors = 0; /* Number of packets forwarded without
67 agent options because there was no room. */
68 int drop_agent_mismatches = 0; /* If nonzero, drop server replies that
69 don't have matching circuit-id's. */
70 int corrupt_agent_options = 0; /* Number of packets dropped because
71 relay agent information option was bad. */
72 int missing_agent_option = 0; /* Number of packets dropped because no
73 RAI option matching our ID was found. */
74 int bad_circuit_id = 0; /* Circuit ID option in matching RAI option
75 did not match any known circuit ID. */
76 int missing_circuit_id = 0; /* Circuit ID option in matching RAI option
77 was missing. */
78 int max_hop_count = 10; /* Maximum hop count */
79
80 int no_daemon = 0;
81 int dfd[2] = { -1, -1 };
82
83 #ifdef DHCPv6
84 /* Force use of DHCPv6 interface-id option. */
85 isc_boolean_t use_if_id = ISC_FALSE;
86 #endif
87
88 /* Maximum size of a packet with agent options added. */
89 int dhcp_max_agent_option_packet_length = DHCP_MTU_MIN;
90
91 /* What to do about packets we're asked to relay that
92 already have a relay option: */
93 enum { forward_and_append, /* Forward and append our own relay option. */
94 forward_and_replace, /* Forward, but replace theirs with ours. */
95 forward_untouched, /* Forward without changes. */
96 discard } agent_relay_mode = forward_and_replace;
97
98 u_int16_t local_port;
99 u_int16_t remote_port;
100
101 /* Relay agent server list. */
102 struct server_list {
103 struct server_list *next;
104 struct sockaddr_in to;
105 } *servers;
106
107 struct interface_info *uplink = NULL;
108
109 #ifdef DHCPv6
110 struct stream_list {
111 struct stream_list *next;
112 struct interface_info *ifp;
113 struct sockaddr_in6 link;
114 int id;
115 } *downstreams, *upstreams;
116
117 #ifndef UNIT_TEST
118 static struct stream_list *parse_downstream(char *);
119 static struct stream_list *parse_upstream(char *);
120 static void setup_streams(void);
121 #endif /* UNIT_TEST */
122
123 /*
124 * A pointer to a subscriber id to add to the message we forward.
125 * This is primarily for testing purposes as we only have one id
126 * for the entire relay and don't determine one per client which
127 * would be more useful.
128 */
129 char *dhcrelay_sub_id = NULL;
130 #endif
131
132 #ifndef UNIT_TEST
133 static void do_relay4(struct interface_info *, struct dhcp_packet *,
134 unsigned int, unsigned int, struct iaddr,
135 struct hardware *);
136 #endif /* UNIT_TEST */
137
138 extern int add_relay_agent_options(struct interface_info *,
139 struct dhcp_packet *, unsigned,
140 struct in_addr);
141 extern int find_interface_by_agent_option(struct dhcp_packet *,
142 struct interface_info **, u_int8_t *, int);
143
144 extern int strip_relay_agent_options(struct interface_info *,
145 struct interface_info **,
146 struct dhcp_packet *, unsigned);
147
148 #ifndef UNIT_TEST
149 static void request_v4_interface(const char* name, int flags);
150
151 static const char copyright[] =
152 "Copyright 2004-2019 Internet Systems Consortium.";
153 static const char arr[] = "All rights reserved.";
154 static const char message[] =
155 "Internet Systems Consortium DHCP Relay Agent";
156 static const char url[] =
157 "For info, please visit https://www.isc.org/software/dhcp/";
158
159 char *progname;
160
161 #ifdef DHCPv6
162 #ifdef RELAY_PORT
163 #define DHCRELAY_USAGE \
164 "Usage: %s [-4] [-d] [-q] [-a] [-D]\n" \
165 " [-A <length>] [-c <hops>]\n" \
166 " [-p <port> | -rp <relay-port>]\n" \
167 " [-pf <pid-file>] [--no-pid]\n"\
168 " [-m append|replace|forward|discard]\n" \
169 " [-i interface0 [ ... -i interfaceN]\n" \
170 " [-iu interface0 [ ... -iu interfaceN]\n" \
171 " [-id interface0 [ ... -id interfaceN]\n" \
172 " [-U interface]\n" \
173 " server0 [ ... serverN]\n\n" \
174 " %s -6 [-d] [-q] [-I] [-c <hops>]\n" \
175 " [-p <port> | -rp <relay-port>]\n" \
176 " [-pf <pid-file>] [--no-pid]\n" \
177 " [-s <subscriber-id>]\n" \
178 " -l lower0 [ ... -l lowerN]\n" \
179 " -u upper0 [ ... -u upperN]\n" \
180 " lower (client link): [address%%]interface[#index]\n" \
181 " upper (server link): [address%%]interface\n\n" \
182 " %s {--version|--help|-h}"
183 #else
184 #define DHCRELAY_USAGE \
185 "Usage: %s [-4] [-d] [-q] [-a] [-D]\n" \
186 " [-A <length>] [-c <hops>] [-p <port>]\n" \
187 " [-pf <pid-file>] [--no-pid]\n"\
188 " [-m append|replace|forward|discard]\n" \
189 " [-i interface0 [ ... -i interfaceN]\n" \
190 " [-iu interface0 [ ... -iu interfaceN]\n" \
191 " [-id interface0 [ ... -id interfaceN]\n" \
192 " [-U interface]\n" \
193 " server0 [ ... serverN]\n\n" \
194 " %s -6 [-d] [-q] [-I] [-c <hops>] [-p <port>]\n" \
195 " [-pf <pid-file>] [--no-pid]\n" \
196 " [-s <subscriber-id>]\n" \
197 " -l lower0 [ ... -l lowerN]\n" \
198 " -u upper0 [ ... -u upperN]\n" \
199 " lower (client link): [address%%]interface[#index]\n" \
200 " upper (server link): [address%%]interface\n\n" \
201 " %s {--version|--help|-h}"
202 #endif
203 #else /* !DHCPv6 */
204 #ifdef RELAY_PORT
205 #define DHCRELAY_USAGE \
206 "Usage: %s [-d] [-q] [-a] [-D] [-A <length>] [-c <hops>]\n" \
207 " [-p <port> | -rp <relay-port>]\n" \
208 " [-pf <pid-file>] [--no-pid]\n" \
209 " [-m append|replace|forward|discard]\n" \
210 " [-i interface0 [ ... -i interfaceN]\n" \
211 " [-iu interface0 [ ... -iu interfaceN]\n" \
212 " [-id interface0 [ ... -id interfaceN]\n" \
213 " [-U interface]\n" \
214 " server0 [ ... serverN]\n\n" \
215 " %s {--version|--help|-h}"
216 #else
217 #define DHCRELAY_USAGE \
218 "Usage: %s [-d] [-q] [-a] [-D] [-A <length>] [-c <hops>] [-p <port>]\n" \
219 " [-pf <pid-file>] [--no-pid]\n" \
220 " [-m append|replace|forward|discard]\n" \
221 " [-i interface0 [ ... -i interfaceN]\n" \
222 " [-iu interface0 [ ... -iu interfaceN]\n" \
223 " [-id interface0 [ ... -id interfaceN]\n" \
224 " [-U interface]\n" \
225 " server0 [ ... serverN]\n\n" \
226 " %s {--version|--help|-h}"
227 #endif
228 #endif
229
230 /*!
231 *
232 * \brief Print the generic usage message
233 *
234 * If the user has provided an incorrect command line print out
235 * the description of the command line. The arguments provide
236 * a way for the caller to request more specific information about
237 * the error be printed as well. Mostly this will be that some
238 * comamnd doesn't include its argument.
239 *
240 * \param sfmt - The basic string and format for the specific error
241 * \param sarg - Generally the offending argument from the comamnd line.
242 *
243 * \return Nothing
244 */
245 static const char use_noarg[] = "No argument for command: %s";
246 #ifdef RELAY_PORT
247 static const char use_port_defined[] = "Port already set, %s inappropriate";
248 #if !defined (USE_BPF_RECEIVE) && !defined (USE_LPF_RECEIVE)
249 static const char bpf_sock_support[] = "Only LPF and BPF are supported: %s";
250 #endif
251 #endif
252 #ifdef DHCPv6
253 static const char use_badproto[] = "Protocol already set, %s inappropriate";
254 static const char use_v4command[] = "Command not used for DHCPv6: %s";
255 static const char use_v6command[] = "Command not used for DHCPv4: %s";
256 #endif
257
258 static void
259 usage(const char *sfmt, const char *sarg) {
260 log_info("%s %s", message, PACKAGE_VERSION);
261 log_info(copyright);
262 log_info(arr);
263 log_info(url);
264
265 /* If desired print out the specific error message */
266 #ifdef PRINT_SPECIFIC_CL_ERRORS
267 if (sfmt != NULL)
268 log_error(sfmt, sarg);
269 #endif
270
271 log_fatal(DHCRELAY_USAGE,
272 #ifdef DHCPv6
273 isc_file_basename(progname),
274 #endif
275 isc_file_basename(progname),
276 isc_file_basename(progname));
277 }
278
279 int
280 main(int argc, char **argv) {
281 isc_result_t status;
282 struct servent *ent;
283 struct server_list *sp = NULL;
284 char *service_local = NULL, *service_remote = NULL;
285 u_int16_t port_local = 0, port_remote = 0;
286 int quiet = 0;
287 int fd;
288 int i;
289 #ifdef RELAY_PORT
290 int port_defined = 0;
291 #endif
292 #ifdef DHCPv6
293 struct stream_list *sl = NULL;
294 int local_family_set = 0;
295 #endif
296
297 #ifdef OLD_LOG_NAME
298 progname = "dhcrelay";
299 #else
300 progname = argv[0];
301 #endif
302
303 /* Make sure that file descriptors 0(stdin), 1,(stdout), and
304 2(stderr) are open. To do this, we assume that when we
305 open a file the lowest available file descriptor is used. */
306 fd = open("/dev/null", O_RDWR);
307 if (fd == 0)
308 fd = open("/dev/null", O_RDWR);
309 if (fd == 1)
310 fd = open("/dev/null", O_RDWR);
311 if (fd == 2)
312 log_perror = 0; /* No sense logging to /dev/null. */
313 else if (fd != -1)
314 close(fd);
315
316 openlog(isc_file_basename(progname), DHCP_LOG_OPTIONS, LOG_DAEMON);
317
318 #if !defined(DEBUG)
319 setlogmask(LOG_UPTO(LOG_INFO));
320 #endif
321
322 /* Parse arguments changing no_daemon */
323 for (i = 1; i < argc; i++) {
324 if (!strcmp(argv[i], "-d")) {
325 no_daemon = 1;
326 } else if (!strcmp(argv[i], "--version")) {
327 log_info("isc-dhcrelay-%s", PACKAGE_VERSION);
328 exit(0);
329 } else if (!strcmp(argv[i], "--help") ||
330 !strcmp(argv[i], "-h")) {
331 log_info(DHCRELAY_USAGE,
332 #ifdef DHCPv6
333 isc_file_basename(progname),
334 #endif
335 isc_file_basename(progname),
336 isc_file_basename(progname));
337 exit(0);
338 }
339 }
340 /* When not forbidden prepare to become a daemon */
341 if (!no_daemon) {
342 int pid;
343
344 if (pipe(dfd) == -1)
345 log_fatal("Can't get pipe: %m");
346 if ((pid = fork ()) < 0)
347 log_fatal("Can't fork daemon: %m");
348 if (pid != 0) {
349 /* Parent: wait for the child to start */
350 int n;
351
352 (void) close(dfd[1]);
353 do {
354 char buf;
355
356 n = read(dfd[0], &buf, 1);
357 if (n == 1)
358 _exit(0);
359 } while (n == -1 && errno == EINTR);
360 _exit(1);
361 }
362 /* Child */
363 (void) close(dfd[0]);
364 }
365
366
367 /* Set up the isc and dns library managers */
368 status = dhcp_context_create(DHCP_CONTEXT_PRE_DB, NULL, NULL);
369 if (status != ISC_R_SUCCESS)
370 log_fatal("Can't initialize context: %s",
371 isc_result_totext(status));
372
373 /* Set up the OMAPI. */
374 status = omapi_init();
375 if (status != ISC_R_SUCCESS)
376 log_fatal("Can't initialize OMAPI: %s",
377 isc_result_totext(status));
378
379 /* Set up the OMAPI wrappers for the interface object. */
380 interface_setup();
381
382 for (i = 1; i < argc; i++) {
383 if (!strcmp(argv[i], "-4")) {
384 #ifdef DHCPv6
385 if (local_family_set && (local_family == AF_INET6)) {
386 usage(use_badproto, "-4");
387 }
388 local_family_set = 1;
389 local_family = AF_INET;
390 } else if (!strcmp(argv[i], "-6")) {
391 if (local_family_set && (local_family == AF_INET)) {
392 usage(use_badproto, "-6");
393 }
394 local_family_set = 1;
395 local_family = AF_INET6;
396 #endif
397 } else if (!strcmp(argv[i], "-d")) {
398 /* no_daemon = 1; */
399 } else if (!strcmp(argv[i], "-q")) {
400 quiet = 1;
401 quiet_interface_discovery = 1;
402 } else if (!strcmp(argv[i], "-p")) {
403 if (++i == argc)
404 usage(use_noarg, argv[i-1]);
405 #ifdef RELAY_PORT
406 if (port_defined)
407 usage(use_port_defined, argv[i-1]);
408 port_defined = 1;
409 #endif
410 local_port = validate_port(argv[i]);
411 log_debug("binding to user-specified port %d",
412 ntohs(local_port));
413 #ifdef RELAY_PORT
414 } else if (!strcmp(argv[i], "-rp")) {
415 if (++i == argc)
416 usage(use_noarg, argv[i-1]);
417 if (port_defined)
418 usage(use_port_defined, argv[i-1]);
419 port_defined = 1;
420 relay_port = validate_port(argv[i]);
421 log_debug("binding to user-specified relay port %d",
422 ntohs(relay_port));
423 add_agent_options = 1;
424 #endif
425 } else if (!strcmp(argv[i], "-c")) {
426 int hcount;
427 if (++i == argc)
428 usage(use_noarg, argv[i-1]);
429 hcount = atoi(argv[i]);
430 if (hcount <= 255)
431 max_hop_count= hcount;
432 else
433 usage("Bad hop count to -c: %s", argv[i]);
434 } else if (!strcmp(argv[i], "-i")) {
435 #ifdef DHCPv6
436 if (local_family_set && (local_family == AF_INET6)) {
437 usage(use_v4command, argv[i]);
438 }
439 local_family_set = 1;
440 local_family = AF_INET;
441 #endif
442 if (++i == argc) {
443 usage(use_noarg, argv[i-1]);
444 }
445
446 request_v4_interface(argv[i], INTERFACE_STREAMS);
447 } else if (!strcmp(argv[i], "-iu")) {
448 #ifdef DHCPv6
449 if (local_family_set && (local_family == AF_INET6)) {
450 usage(use_v4command, argv[i]);
451 }
452 local_family_set = 1;
453 local_family = AF_INET;
454 #endif
455 if (++i == argc) {
456 usage(use_noarg, argv[i-1]);
457 }
458
459 request_v4_interface(argv[i], INTERFACE_UPSTREAM);
460 } else if (!strcmp(argv[i], "-id")) {
461 #ifdef DHCPv6
462 if (local_family_set && (local_family == AF_INET6)) {
463 usage(use_v4command, argv[i]);
464 }
465 local_family_set = 1;
466 local_family = AF_INET;
467 #endif
468 if (++i == argc) {
469 usage(use_noarg, argv[i-1]);
470 }
471
472 request_v4_interface(argv[i], INTERFACE_DOWNSTREAM);
473 } else if (!strcmp(argv[i], "-a")) {
474 #ifdef DHCPv6
475 if (local_family_set && (local_family == AF_INET6)) {
476 usage(use_v4command, argv[i]);
477 }
478 local_family_set = 1;
479 local_family = AF_INET;
480 #endif
481 add_agent_options = 1;
482 } else if (!strcmp(argv[i], "-A")) {
483 #ifdef DHCPv6
484 if (local_family_set && (local_family == AF_INET6)) {
485 usage(use_v4command, argv[i]);
486 }
487 local_family_set = 1;
488 local_family = AF_INET;
489 #endif
490 if (++i == argc)
491 usage(use_noarg, argv[i-1]);
492
493 dhcp_max_agent_option_packet_length = atoi(argv[i]);
494
495 if (dhcp_max_agent_option_packet_length > DHCP_MTU_MAX)
496 log_fatal("%s: packet length exceeds "
497 "longest possible MTU\n",
498 argv[i]);
499 } else if (!strcmp(argv[i], "-m")) {
500 #ifdef DHCPv6
501 if (local_family_set && (local_family == AF_INET6)) {
502 usage(use_v4command, argv[i]);
503 }
504 local_family_set = 1;
505 local_family = AF_INET;
506 #endif
507 if (++i == argc)
508 usage(use_noarg, argv[i-1]);
509 if (!strcasecmp(argv[i], "append")) {
510 agent_relay_mode = forward_and_append;
511 } else if (!strcasecmp(argv[i], "replace")) {
512 agent_relay_mode = forward_and_replace;
513 } else if (!strcasecmp(argv[i], "forward")) {
514 agent_relay_mode = forward_untouched;
515 } else if (!strcasecmp(argv[i], "discard")) {
516 agent_relay_mode = discard;
517 } else
518 usage("Unknown argument to -m: %s", argv[i]);
519 } else if (!strcmp(argv [i], "-U")) {
520 if (++i == argc)
521 usage(use_noarg, argv[i-1]);
522
523 if (uplink) {
524 usage("more than one uplink (-U) specified: %s"
525 ,argv[i]);
526 }
527
528 /* Allocate the uplink interface */
529 status = interface_allocate(&uplink, MDL);
530 if (status != ISC_R_SUCCESS) {
531 log_fatal("%s: uplink interface_allocate: %s",
532 argv[i], isc_result_totext(status));
533 }
534
535 if (strlen(argv[i]) >= sizeof(uplink->name)) {
536 log_fatal("%s: uplink name too long,"
537 " it cannot exceed: %ld characters",
538 argv[i], (long)(sizeof(uplink->name) - 1));
539 }
540
541 uplink->name[sizeof(uplink->name) - 1] = 0x00;
542 strncpy(uplink->name, argv[i],
543 sizeof(uplink->name) - 1);
544 interface_snorf(uplink, (INTERFACE_REQUESTED |
545 INTERFACE_STREAMS));
546
547 /* Turn on -a, in case they don't do so explicitly */
548 add_agent_options = 1;
549 add_rfc3527_suboption = 1;
550 } else if (!strcmp(argv[i], "-D")) {
551 #ifdef DHCPv6
552 if (local_family_set && (local_family == AF_INET6)) {
553 usage(use_v4command, argv[i]);
554 }
555 local_family_set = 1;
556 local_family = AF_INET;
557 #endif
558 drop_agent_mismatches = 1;
559 #ifdef DHCPv6
560 } else if (!strcmp(argv[i], "-I")) {
561 if (local_family_set && (local_family == AF_INET)) {
562 usage(use_v6command, argv[i]);
563 }
564 local_family_set = 1;
565 local_family = AF_INET6;
566 use_if_id = ISC_TRUE;
567 } else if (!strcmp(argv[i], "-l")) {
568 if (local_family_set && (local_family == AF_INET)) {
569 usage(use_v6command, argv[i]);
570 }
571 local_family_set = 1;
572 local_family = AF_INET6;
573 if (downstreams != NULL)
574 use_if_id = ISC_TRUE;
575 if (++i == argc)
576 usage(use_noarg, argv[i-1]);
577 sl = parse_downstream(argv[i]);
578 sl->next = downstreams;
579 downstreams = sl;
580 } else if (!strcmp(argv[i], "-u")) {
581 if (local_family_set && (local_family == AF_INET)) {
582 usage(use_v6command, argv[i]);
583 }
584 local_family_set = 1;
585 local_family = AF_INET6;
586 if (++i == argc)
587 usage(use_noarg, argv[i-1]);
588 sl = parse_upstream(argv[i]);
589 sl->next = upstreams;
590 upstreams = sl;
591 } else if (!strcmp(argv[i], "-s")) {
592 if (local_family_set && (local_family == AF_INET)) {
593 usage(use_v6command, argv[i]);
594 }
595 local_family_set = 1;
596 local_family = AF_INET6;
597 if (++i == argc)
598 usage(use_noarg, argv[i-1]);
599 dhcrelay_sub_id = argv[i];
600 #endif
601 } else if (!strcmp(argv[i], "-pf")) {
602 if (++i == argc)
603 usage(use_noarg, argv[i-1]);
604 path_dhcrelay_pid = argv[i];
605 no_dhcrelay_pid = ISC_TRUE;
606 } else if (!strcmp(argv[i], "--no-pid")) {
607 no_pid_file = ISC_TRUE;
608 } else if (argv[i][0] == '-') {
609 usage("Unknown command: %s", argv[i]);
610 } else {
611 struct hostent *he;
612 struct in_addr ia, *iap = NULL;
613
614 #ifdef DHCPv6
615 if (local_family_set && (local_family == AF_INET6)) {
616 usage(use_v4command, argv[i]);
617 }
618 local_family_set = 1;
619 local_family = AF_INET;
620 #endif
621 if (inet_aton(argv[i], &ia)) {
622 iap = &ia;
623 } else {
624 he = gethostbyname(argv[i]);
625 if (!he) {
626 log_error("%s: host unknown", argv[i]);
627 } else {
628 iap = ((struct in_addr *)
629 he->h_addr_list[0]);
630 }
631 }
632
633 if (iap) {
634 sp = ((struct server_list *)
635 dmalloc(sizeof *sp, MDL));
636 if (!sp)
637 log_fatal("no memory for server.\n");
638 sp->next = servers;
639 servers = sp;
640 memcpy(&sp->to.sin_addr, iap, sizeof *iap);
641 }
642 }
643 }
644
645 #if defined(RELAY_PORT) && \
646 !defined (USE_BPF_RECEIVE) && !defined (USE_LPF_RECEIVE)
647 if (relay_port && (local_family == AF_INET))
648 usage(bpf_sock_support, "-rp");
649 #endif
650
651 /*
652 * If the user didn't specify a pid file directly
653 * find one from environment variables or defaults
654 */
655 if (no_dhcrelay_pid == ISC_FALSE) {
656 if (local_family == AF_INET) {
657 path_dhcrelay_pid = getenv("PATH_DHCRELAY_PID");
658 if (path_dhcrelay_pid == NULL)
659 path_dhcrelay_pid = _PATH_DHCRELAY_PID;
660 }
661 #ifdef DHCPv6
662 else {
663 path_dhcrelay_pid = getenv("PATH_DHCRELAY6_PID");
664 if (path_dhcrelay_pid == NULL)
665 path_dhcrelay_pid = _PATH_DHCRELAY6_PID;
666 }
667 #endif
668 }
669
670 if (!quiet) {
671 log_info("%s %s", message, PACKAGE_VERSION);
672 log_info(copyright);
673 log_info(arr);
674 log_info(url);
675 } else
676 log_perror = 0;
677
678 /* Set default port */
679 if (local_family == AF_INET) {
680 service_local = "bootps";
681 service_remote = "bootpc";
682 port_local = htons(67);
683 port_remote = htons(68);
684 }
685 #ifdef DHCPv6
686 else {
687 service_local = "dhcpv6-server";
688 service_remote = "dhcpv6-client";
689 port_local = htons(547);
690 port_remote = htons(546);
691 }
692 #endif
693
694 if (!local_port) {
695 ent = getservbyname(service_local, "udp");
696 if (ent)
697 local_port = ent->s_port;
698 else
699 local_port = port_local;
700
701 ent = getservbyname(service_remote, "udp");
702 if (ent)
703 remote_port = ent->s_port;
704 else
705 remote_port = port_remote;
706
707 endservent();
708 }
709
710 if (local_family == AF_INET) {
711 /* We need at least one server */
712 if (servers == NULL) {
713 log_fatal("No servers specified.");
714 }
715
716
717 /* Set up the server sockaddrs. */
718 for (sp = servers; sp; sp = sp->next) {
719 sp->to.sin_port = local_port;
720 sp->to.sin_family = AF_INET;
721 #ifdef HAVE_SA_LEN
722 sp->to.sin_len = sizeof sp->to;
723 #endif
724 }
725 }
726 #ifdef DHCPv6
727 else {
728 unsigned code;
729
730 /* We need at least one upstream and one downstream interface */
731 if (upstreams == NULL || downstreams == NULL) {
732 log_info("Must specify at least one lower "
733 "and one upper interface.\n");
734 usage(NULL, NULL);
735 }
736
737 /* Set up the initial dhcp option universe. */
738 initialize_common_option_spaces();
739
740 /* Check requested options. */
741 code = D6O_RELAY_MSG;
742 if (!option_code_hash_lookup(&requested_opts[0],
743 dhcpv6_universe.code_hash,
744 &code, 0, MDL))
745 log_fatal("Unable to find the RELAY_MSG "
746 "option definition.");
747 code = D6O_INTERFACE_ID;
748 if (!option_code_hash_lookup(&requested_opts[1],
749 dhcpv6_universe.code_hash,
750 &code, 0, MDL))
751 log_fatal("Unable to find the INTERFACE_ID "
752 "option definition.");
753 }
754 #endif
755
756 /* Get the current time... */
757 gettimeofday(&cur_tv, NULL);
758
759 /* Discover all the network interfaces. */
760 discover_interfaces(DISCOVER_RELAY);
761
762 #ifdef DHCPv6
763 if (local_family == AF_INET6)
764 setup_streams();
765 #endif
766
767 /* Become a daemon... */
768 if (!no_daemon) {
769 char buf = 0;
770 FILE *pf;
771 int pfdesc;
772
773 log_perror = 0;
774
775 /* Signal parent we started successfully. */
776 if (dfd[0] != -1 && dfd[1] != -1) {
777 if (write(dfd[1], &buf, 1) != 1)
778 log_fatal("write to parent: %m");
779 (void) close(dfd[1]);
780 dfd[0] = dfd[1] = -1;
781 }
782
783 /* Create the pid file. */
784 if (no_pid_file == ISC_FALSE) {
785 pfdesc = open(path_dhcrelay_pid,
786 O_CREAT | O_TRUNC | O_WRONLY, 0644);
787
788 if (pfdesc < 0) {
789 log_error("Can't create %s: %m",
790 path_dhcrelay_pid);
791 } else {
792 pf = fdopen(pfdesc, "w");
793 if (!pf)
794 log_error("Can't fdopen %s: %m",
795 path_dhcrelay_pid);
796 else {
797 fprintf(pf, "%ld\n",(long)getpid());
798 fclose(pf);
799 }
800 }
801 }
802
803 (void) close(0);
804 (void) close(1);
805 (void) close(2);
806 (void) setsid();
807
808 IGNORE_RET (chdir("/"));
809 }
810
811 /* Set up the packet handler... */
812 if (local_family == AF_INET)
813 bootp_packet_handler = do_relay4;
814 #ifdef DHCPv6
815 else
816 dhcpv6_packet_handler = do_packet6;
817 #endif
818
819 #if defined(ENABLE_GENTLE_SHUTDOWN)
820 /* no signal handlers until we deal with the side effects */
821 /* install signal handlers */
822 signal(SIGINT, dhcp_signal_handler); /* control-c */
823 signal(SIGTERM, dhcp_signal_handler); /* kill */
824 #endif
825
826 /* Start dispatching packets and timeouts... */
827 dispatch();
828
829 /* In fact dispatch() never returns. */
830 return (0);
831 }
832
833 static void
834 do_relay4(struct interface_info *ip, struct dhcp_packet *packet,
835 unsigned int length, unsigned int from_port, struct iaddr from,
836 struct hardware *hfrom) {
837 struct server_list *sp;
838 struct sockaddr_in to;
839 struct interface_info *out;
840 struct hardware hto, *htop;
841
842 if (packet->hlen > sizeof packet->chaddr) {
843 log_info("Discarding packet with invalid hlen, received on "
844 "%s interface.", ip->name);
845 return;
846 }
847 if (ip->address_count < 1 || ip->addresses == NULL) {
848 log_info("Discarding packet received on %s interface that "
849 "has no IPv4 address assigned.", ip->name);
850 return;
851 }
852
853 /* Find the interface that corresponds to the giaddr
854 in the packet. */
855 if (packet->giaddr.s_addr) {
856 for (out = interfaces; out; out = out->next) {
857 int i;
858
859 for (i = 0 ; i < out->address_count ; i++ ) {
860 if (out->addresses[i].s_addr ==
861 packet->giaddr.s_addr) {
862 i = -1;
863 break;
864 }
865 }
866
867 if (i == -1)
868 break;
869 }
870 } else {
871 out = NULL;
872 }
873
874 /* If it's a bootreply, forward it to the client. */
875 if (packet->op == BOOTREPLY) {
876 if (!(ip->flags & INTERFACE_UPSTREAM)) {
877 log_debug("Dropping reply received on %s", ip->name);
878 return;
879 }
880
881 if (!(packet->flags & htons(BOOTP_BROADCAST)) &&
882 can_unicast_without_arp(out)) {
883 to.sin_addr = packet->yiaddr;
884 to.sin_port = remote_port;
885
886 /* and hardware address is not broadcast */
887 htop = &hto;
888 } else {
889 to.sin_addr.s_addr = htonl(INADDR_BROADCAST);
890 to.sin_port = remote_port;
891
892 /* hardware address is broadcast */
893 htop = NULL;
894 }
895 to.sin_family = AF_INET;
896 #ifdef HAVE_SA_LEN
897 to.sin_len = sizeof to;
898 #endif
899
900 memcpy(&hto.hbuf[1], packet->chaddr, packet->hlen);
901 hto.hbuf[0] = packet->htype;
902 hto.hlen = packet->hlen + 1;
903
904 /* Wipe out the agent relay options and, if possible, figure
905 out which interface to use based on the contents of the
906 option that we put on the request to which the server is
907 replying. */
908 if (!(length =
909 strip_relay_agent_options(ip, &out, packet, length)))
910 return;
911
912 if (!out) {
913 log_error("Packet to bogus giaddr %s.\n",
914 inet_ntoa(packet->giaddr));
915 ++bogus_giaddr_drops;
916 return;
917 }
918
919 if (send_packet(out, NULL, packet, length, out->addresses[0],
920 &to, htop) < 0) {
921 ++server_packet_errors;
922 } else {
923 log_debug("Forwarded BOOTREPLY for %s to %s",
924 print_hw_addr(packet->htype, packet->hlen,
925 packet->chaddr),
926 inet_ntoa(to.sin_addr));
927
928 ++server_packets_relayed;
929 }
930 return;
931 }
932
933 /* If giaddr matches one of our addresses, ignore the packet -
934 we just sent it. */
935 if (out)
936 return;
937
938 if (!(ip->flags & INTERFACE_DOWNSTREAM)) {
939 log_debug("Dropping request received on %s", ip->name);
940 return;
941 }
942
943 /* Add relay agent options if indicated. If something goes wrong,
944 * drop the packet. Note this may set packet->giaddr if RFC3527
945 * is enabled. */
946 if (!(length = add_relay_agent_options(ip, packet, length,
947 ip->addresses[0])))
948 return;
949
950 /* If giaddr is not already set, Set it so the server can
951 figure out what net it's from and so that we can later
952 forward the response to the correct net. If it's already
953 set, the response will be sent directly to the relay agent
954 that set giaddr, so we won't see it. */
955 if (!packet->giaddr.s_addr)
956 packet->giaddr = ip->addresses[0];
957 if (packet->hops < max_hop_count)
958 packet->hops = packet->hops + 1;
959 else
960 return;
961
962 /* Otherwise, it's a BOOTREQUEST, so forward it to all the
963 servers. */
964 for (sp = servers; sp; sp = sp->next) {
965 if (send_packet((fallback_interface
966 ? fallback_interface : interfaces),
967 NULL, packet, length, ip->addresses[0],
968 &sp->to, NULL) < 0) {
969 ++client_packet_errors;
970 } else {
971 log_debug("Forwarded BOOTREQUEST for %s to %s",
972 print_hw_addr(packet->htype, packet->hlen,
973 packet->chaddr),
974 inet_ntoa(sp->to.sin_addr));
975 ++client_packets_relayed;
976 }
977 }
978
979 }
980
981 #endif /* UNIT_TEST */
982
983 /* Strip any Relay Agent Information options from the DHCP packet
984 option buffer. If there is a circuit ID suboption, look up the
985 outgoing interface based upon it. */
986
987 int
988 strip_relay_agent_options(struct interface_info *in,
989 struct interface_info **out,
990 struct dhcp_packet *packet,
991 unsigned length) {
992 int is_dhcp = 0;
993 u_int8_t *op, *nextop, *sp, *max;
994 int good_agent_option = 0;
995 int status;
996
997 /* If we're not adding agent options to packets, we're not taking
998 them out either. */
999 if (!add_agent_options)
1000 return (length);
1001
1002 /* If there's no cookie, it's a bootp packet, so we should just
1003 forward it unchanged. */
1004 if (memcmp(packet->options, DHCP_OPTIONS_COOKIE, 4))
1005 return (length);
1006
1007 max = ((u_int8_t *)packet) + length;
1008 sp = op = &packet->options[4];
1009
1010 while (op < max) {
1011 switch(*op) {
1012 /* Skip padding... */
1013 case DHO_PAD:
1014 if (sp != op)
1015 *sp = *op;
1016 ++op;
1017 ++sp;
1018 continue;
1019
1020 /* If we see a message type, it's a DHCP packet. */
1021 case DHO_DHCP_MESSAGE_TYPE:
1022 is_dhcp = 1;
1023 goto skip;
1024 break;
1025
1026 /* Quit immediately if we hit an End option. */
1027 case DHO_END:
1028 if (sp != op)
1029 *sp++ = *op++;
1030 goto out;
1031
1032 case DHO_DHCP_AGENT_OPTIONS:
1033 /* We shouldn't see a relay agent option in a
1034 packet before we've seen the DHCP packet type,
1035 but if we do, we have to leave it alone. */
1036 if (!is_dhcp)
1037 goto skip;
1038
1039 /* Do not process an agent option if it exceeds the
1040 * buffer. Fail this packet.
1041 */
1042 nextop = op + op[1] + 2;
1043 if (nextop > max)
1044 return (0);
1045
1046 status = find_interface_by_agent_option(packet,
1047 out, op + 2,
1048 op[1]);
1049 if (status == -1 && drop_agent_mismatches)
1050 return (0);
1051 if (status)
1052 good_agent_option = 1;
1053 op = nextop;
1054 break;
1055
1056 skip:
1057 /* Skip over other options. */
1058 default:
1059 /* Fail if processing this option will exceed the
1060 * buffer(op[1] is malformed).
1061 */
1062 nextop = op + op[1] + 2;
1063 if (nextop > max)
1064 return (0);
1065
1066 if (sp != op) {
1067 size_t mlen = op[1] + 2;
1068 memmove(sp, op, mlen);
1069 sp += mlen;
1070 if (sp > max) {
1071 return (0);
1072 }
1073
1074 op = nextop;
1075 } else
1076 op = sp = nextop;
1077
1078 break;
1079 }
1080 }
1081 out:
1082
1083 /* If it's not a DHCP packet, we're not supposed to touch it. */
1084 if (!is_dhcp)
1085 return (length);
1086
1087 /* If none of the agent options we found matched, or if we didn't
1088 find any agent options, count this packet as not having any
1089 matching agent options, and if we're relying on agent options
1090 to determine the outgoing interface, drop the packet. */
1091
1092 if (!good_agent_option) {
1093 ++missing_agent_option;
1094 if (drop_agent_mismatches)
1095 return (0);
1096 }
1097
1098 /* Adjust the length... */
1099 if (sp != op) {
1100 length = sp -((u_int8_t *)packet);
1101
1102 /* Make sure the packet isn't short(this is unlikely,
1103 but WTH) */
1104 if (length < BOOTP_MIN_LEN) {
1105 memset(sp, DHO_PAD, BOOTP_MIN_LEN - length);
1106 length = BOOTP_MIN_LEN;
1107 }
1108 }
1109 return (length);
1110 }
1111
1112
1113 /* Find an interface that matches the circuit ID specified in the
1114 Relay Agent Information option. If one is found, store it through
1115 the pointer given; otherwise, leave the existing pointer alone.
1116
1117 We actually deviate somewhat from the current specification here:
1118 if the option buffer is corrupt, we suggest that the caller not
1119 respond to this packet. If the circuit ID doesn't match any known
1120 interface, we suggest that the caller to drop the packet. Only if
1121 we find a circuit ID that matches an existing interface do we tell
1122 the caller to go ahead and process the packet. */
1123
1124 int
1125 find_interface_by_agent_option(struct dhcp_packet *packet,
1126 struct interface_info **out,
1127 u_int8_t *buf, int len) {
1128 int i = 0;
1129 u_int8_t *circuit_id = 0;
1130 unsigned circuit_id_len = 0;
1131 struct interface_info *ip;
1132
1133 while (i < len) {
1134 /* If the next agent option overflows the end of the
1135 packet, the agent option buffer is corrupt. */
1136 if (i + 1 == len ||
1137 i + buf[i + 1] + 2 > len) {
1138 ++corrupt_agent_options;
1139 return (-1);
1140 }
1141 switch(buf[i]) {
1142 /* Remember where the circuit ID is... */
1143 case RAI_CIRCUIT_ID:
1144 circuit_id = &buf[i + 2];
1145 circuit_id_len = buf[i + 1];
1146 i += circuit_id_len + 2;
1147 continue;
1148
1149 default:
1150 i += buf[i + 1] + 2;
1151 break;
1152 }
1153 }
1154
1155 /* If there's no circuit ID, it's not really ours, tell the caller
1156 it's no good. */
1157 if (!circuit_id) {
1158 ++missing_circuit_id;
1159 return (-1);
1160 }
1161
1162 /* Scan the interface list looking for an interface whose
1163 name matches the one specified in circuit_id. */
1164
1165 for (ip = interfaces; ip; ip = ip->next) {
1166 if (ip->circuit_id &&
1167 ip->circuit_id_len == circuit_id_len &&
1168 !memcmp(ip->circuit_id, circuit_id, circuit_id_len))
1169 break;
1170 }
1171
1172 /* If we got a match, use it. */
1173 if (ip) {
1174 *out = ip;
1175 return (1);
1176 }
1177
1178 /* If we didn't get a match, the circuit ID was bogus. */
1179 ++bad_circuit_id;
1180 return (-1);
1181 }
1182
1183 /*
1184 * Examine a packet to see if it's a candidate to have a Relay
1185 * Agent Information option tacked onto its tail. If it is, tack
1186 * the option on.
1187 */
1188 int
1189 add_relay_agent_options(struct interface_info *ip, struct dhcp_packet *packet,
1190 unsigned length, struct in_addr giaddr) {
1191 int is_dhcp = 0, mms;
1192 unsigned optlen;
1193 u_int8_t *op, *nextop, *sp, *max, *end_pad = NULL;
1194 int adding_link_select;
1195
1196 /* If we're not adding agent options to packets, we can skip
1197 this. */
1198 if (!add_agent_options)
1199 return (length);
1200
1201 /* If there's no cookie, it's a bootp packet, so we should just
1202 forward it unchanged. */
1203 if (memcmp(packet->options, DHCP_OPTIONS_COOKIE, 4))
1204 return (length);
1205
1206 max = ((u_int8_t *)packet) + dhcp_max_agent_option_packet_length;
1207
1208 /* Add link selection suboption if enabled and we're the first relay */
1209 adding_link_select = (add_rfc3527_suboption
1210 && (packet->giaddr.s_addr == 0));
1211
1212 /* Commence processing after the cookie. */
1213 sp = op = &packet->options[4];
1214
1215 while (op < max) {
1216 switch(*op) {
1217 /* Skip padding... */
1218 case DHO_PAD:
1219 /* Remember the first pad byte so we can commandeer
1220 * padded space.
1221 *
1222 * XXX: Is this really a good idea? Sure, we can
1223 * seemingly reduce the packet while we're looking,
1224 * but if the packet was signed by the client then
1225 * this padding is part of the checksum(RFC3118),
1226 * and its nonpresence would break authentication.
1227 */
1228 if (end_pad == NULL)
1229 end_pad = sp;
1230
1231 if (sp != op)
1232 *sp++ = *op++;
1233 else
1234 sp = ++op;
1235
1236 continue;
1237
1238 /* If we see a message type, it's a DHCP packet. */
1239 case DHO_DHCP_MESSAGE_TYPE:
1240 is_dhcp = 1;
1241 goto skip;
1242
1243 /*
1244 * If there's a maximum message size option, we
1245 * should pay attention to it
1246 */
1247 case DHO_DHCP_MAX_MESSAGE_SIZE:
1248 mms = ntohs(*(op + 2));
1249 if (mms < dhcp_max_agent_option_packet_length &&
1250 mms >= DHCP_MTU_MIN)
1251 max = ((u_int8_t *)packet) + mms;
1252 goto skip;
1253
1254 /* Quit immediately if we hit an End option. */
1255 case DHO_END:
1256 goto out;
1257
1258 case DHO_DHCP_AGENT_OPTIONS:
1259 /* We shouldn't see a relay agent option in a
1260 packet before we've seen the DHCP packet type,
1261 but if we do, we have to leave it alone. */
1262 if (!is_dhcp)
1263 goto skip;
1264
1265 end_pad = NULL;
1266
1267 /* There's already a Relay Agent Information option
1268 in this packet. How embarrassing. Decide what
1269 to do based on the mode the user specified. */
1270
1271 switch(agent_relay_mode) {
1272 case forward_and_append:
1273 goto skip;
1274 case forward_untouched:
1275 return (length);
1276 case discard:
1277 return (0);
1278 case forward_and_replace:
1279 default:
1280 break;
1281 }
1282
1283 /* Skip over the agent option and start copying
1284 if we aren't copying already. */
1285 op += op[1] + 2;
1286 break;
1287
1288 skip:
1289 /* Skip over other options. */
1290 default:
1291 /* Fail if processing this option will exceed the
1292 * buffer(op[1] is malformed).
1293 */
1294 nextop = op + op[1] + 2;
1295 if (nextop > max)
1296 return (0);
1297
1298 end_pad = NULL;
1299
1300 if (sp != op) {
1301 size_t mlen = op[1] + 2;
1302 memmove(sp, op, mlen);
1303 sp += mlen;
1304 if (sp > max) {
1305 return (0);
1306 }
1307
1308 op = nextop;
1309 } else
1310 op = sp = nextop;
1311
1312 break;
1313 }
1314 }
1315 out:
1316
1317 /* If it's not a DHCP packet, we're not supposed to touch it. */
1318 if (!is_dhcp)
1319 return (length);
1320
1321 /* If the packet was padded out, we can store the agent option
1322 at the beginning of the padding. */
1323
1324 if (end_pad != NULL)
1325 sp = end_pad;
1326
1327 #if 0
1328 /* Remember where the end of the packet was after parsing
1329 it. */
1330 op = sp;
1331 #endif
1332
1333 /* Sanity check. Had better not ever happen. */
1334 if ((ip->circuit_id_len > 255) ||(ip->circuit_id_len < 1))
1335 log_fatal("Circuit ID length %d out of range [1-255] on "
1336 "%s\n", ip->circuit_id_len, ip->name);
1337 optlen = ip->circuit_id_len + 2; /* RAI_CIRCUIT_ID + len */
1338
1339 if (ip->remote_id) {
1340 if (ip->remote_id_len > 255 || ip->remote_id_len < 1)
1341 log_fatal("Remote ID length %d out of range [1-255] "
1342 "on %s\n", ip->remote_id_len, ip->name);
1343 optlen += ip->remote_id_len + 2; /* RAI_REMOTE_ID + len */
1344 }
1345
1346 if (adding_link_select) {
1347 optlen += 6;
1348 }
1349
1350 #ifdef RELAY_PORT
1351 if (relay_port) {
1352 optlen += 2;
1353 }
1354 #endif
1355
1356 /* We do not support relay option fragmenting(multiple options to
1357 * support an option data exceeding 255 bytes).
1358 */
1359 if ((optlen < 3) ||(optlen > 255))
1360 log_fatal("Total agent option length(%u) out of range "
1361 "[3 - 255] on %s\n", optlen, ip->name);
1362
1363 /*
1364 * Is there room for the option, its code+len, and DHO_END?
1365 * If not, forward without adding the option.
1366 */
1367 if (max - sp >= optlen + 3) {
1368 log_debug("Adding %d-byte relay agent option", optlen + 3);
1369
1370 /* Okay, cons up *our* Relay Agent Information option. */
1371 *sp++ = DHO_DHCP_AGENT_OPTIONS;
1372 *sp++ = optlen;
1373
1374 /* Copy in the circuit id... */
1375 *sp++ = RAI_CIRCUIT_ID;
1376 *sp++ = ip->circuit_id_len;
1377 memcpy(sp, ip->circuit_id, ip->circuit_id_len);
1378 sp += ip->circuit_id_len;
1379
1380 /* Copy in remote ID... */
1381 if (ip->remote_id) {
1382 *sp++ = RAI_REMOTE_ID;
1383 *sp++ = ip->remote_id_len;
1384 memcpy(sp, ip->remote_id, ip->remote_id_len);
1385 sp += ip->remote_id_len;
1386 }
1387
1388 /* RFC3527: Use the inbound packet's interface address in
1389 * the link selection suboption and set the outbound giaddr
1390 * to the uplink address. */
1391 if (adding_link_select) {
1392 *sp++ = RAI_LINK_SELECT;
1393 *sp++ = 4u;
1394 memcpy(sp, &giaddr.s_addr, 4);
1395 sp += 4;
1396 packet->giaddr = uplink->addresses[0];
1397 log_debug ("Adding link selection suboption"
1398 " with addr: %s", inet_ntoa(giaddr));
1399 }
1400
1401 #ifdef RELAY_PORT
1402 /* draft-ietf-dhc-relay-port-10.txt section 5.1 */
1403 if (relay_port) {
1404 *sp++ = RAI_RELAY_PORT;
1405 *sp++ = 0u;
1406 }
1407 #endif
1408 } else {
1409 ++agent_option_errors;
1410 log_error("No room in packet (used %d of %d) "
1411 "for %d-byte relay agent option: omitted",
1412 (int) (sp - ((u_int8_t *) packet)),
1413 (int) (max - ((u_int8_t *) packet)),
1414 optlen + 3);
1415 }
1416
1417 /*
1418 * Deposit an END option unless the packet is full (shouldn't
1419 * be possible).
1420 */
1421 if (sp < max)
1422 *sp++ = DHO_END;
1423
1424 /* Recalculate total packet length. */
1425 length = sp -((u_int8_t *)packet);
1426
1427 /* Make sure the packet isn't short(this is unlikely, but WTH) */
1428 if (length < BOOTP_MIN_LEN) {
1429 memset(sp, DHO_PAD, BOOTP_MIN_LEN - length);
1430 return (BOOTP_MIN_LEN);
1431 }
1432
1433 return (length);
1434 }
1435
1436 #ifndef UNIT_TEST
1437
1438 #ifdef DHCPv6
1439 /*
1440 * Parse a downstream argument: [address%]interface[#index].
1441 */
1442 static struct stream_list *
1443 parse_downstream(char *arg) {
1444 struct stream_list *dp, *up;
1445 struct interface_info *ifp = NULL;
1446 char *ifname, *addr, *iid;
1447 isc_result_t status;
1448
1449 if (!supports_multiple_interfaces(ifp) &&
1450 (downstreams != NULL))
1451 log_fatal("No support for multiple interfaces.");
1452
1453 /* Decode the argument. */
1454 ifname = strchr(arg, '%');
1455 if (ifname == NULL) {
1456 ifname = arg;
1457 addr = NULL;
1458 } else {
1459 *ifname++ = '\0';
1460 addr = arg;
1461 }
1462 iid = strchr(ifname, '#');
1463 if (iid != NULL) {
1464 *iid++ = '\0';
1465 }
1466 if (strlen(ifname) >= sizeof(ifp->name)) {
1467 usage("Interface name '%s' too long", ifname);
1468 }
1469
1470 /* Don't declare twice. */
1471 for (dp = downstreams; dp; dp = dp->next) {
1472 if (strcmp(ifname, dp->ifp->name) == 0)
1473 log_fatal("Down interface '%s' declared twice.",
1474 ifname);
1475 }
1476
1477 /* Share with up side? */
1478 for (up = upstreams; up; up = up->next) {
1479 if (strcmp(ifname, up->ifp->name) == 0) {
1480 log_info("parse_downstream: Interface '%s' is "
1481 "both down and up.", ifname);
1482 ifp = up->ifp;
1483 break;
1484 }
1485 }
1486
1487 /* New interface. */
1488 if (ifp == NULL) {
1489 status = interface_allocate(&ifp, MDL);
1490 if (status != ISC_R_SUCCESS)
1491 log_fatal("%s: interface_allocate: %s",
1492 arg, isc_result_totext(status));
1493 strcpy(ifp->name, ifname);
1494 if (interfaces) {
1495 interface_reference(&ifp->next, interfaces, MDL);
1496 interface_dereference(&interfaces, MDL);
1497 }
1498 interface_reference(&interfaces, ifp, MDL);
1499 }
1500 ifp->flags |= INTERFACE_REQUESTED | INTERFACE_DOWNSTREAM;
1501
1502 /* New downstream. */
1503 dp = (struct stream_list *) dmalloc(sizeof(*dp), MDL);
1504 if (!dp)
1505 log_fatal("No memory for downstream.");
1506 dp->ifp = ifp;
1507 if (iid != NULL) {
1508 dp->id = atoi(iid);
1509 } else {
1510 dp->id = -1;
1511 }
1512 /* !addr case handled by setup. */
1513 if (addr && (inet_pton(AF_INET6, addr, &dp->link.sin6_addr) <= 0))
1514 log_fatal("Bad link address '%s'", addr);
1515
1516 return dp;
1517 }
1518
1519 /*
1520 * Parse an upstream argument: [address]%interface.
1521 */
1522 static struct stream_list *
1523 parse_upstream(char *arg) {
1524 struct stream_list *up, *dp;
1525 struct interface_info *ifp = NULL;
1526 char *ifname, *addr;
1527 isc_result_t status;
1528
1529 /* Decode the argument. */
1530 ifname = strchr(arg, '%');
1531 if (ifname == NULL) {
1532 ifname = arg;
1533 addr = All_DHCP_Servers;
1534 } else {
1535 *ifname++ = '\0';
1536 addr = arg;
1537 }
1538 if (strlen(ifname) >= sizeof(ifp->name)) {
1539 log_fatal("Interface name '%s' too long", ifname);
1540 }
1541
1542 /* Shared up interface? */
1543 for (up = upstreams; up; up = up->next) {
1544 if (strcmp(ifname, up->ifp->name) == 0) {
1545 ifp = up->ifp;
1546 break;
1547 }
1548 }
1549 for (dp = downstreams; dp; dp = dp->next) {
1550 if (strcmp(ifname, dp->ifp->name) == 0) {
1551 log_info("parse_upstream: Interface '%s' is "
1552 "both down and up.", ifname);
1553 ifp = dp->ifp;
1554 break;
1555 }
1556 }
1557
1558 /* New interface. */
1559 if (ifp == NULL) {
1560 status = interface_allocate(&ifp, MDL);
1561 if (status != ISC_R_SUCCESS)
1562 log_fatal("%s: interface_allocate: %s",
1563 arg, isc_result_totext(status));
1564 strcpy(ifp->name, ifname);
1565 if (interfaces) {
1566 interface_reference(&ifp->next, interfaces, MDL);
1567 interface_dereference(&interfaces, MDL);
1568 }
1569 interface_reference(&interfaces, ifp, MDL);
1570 }
1571 ifp->flags |= INTERFACE_REQUESTED | INTERFACE_UPSTREAM;
1572
1573 /* New upstream. */
1574 up = (struct stream_list *) dmalloc(sizeof(*up), MDL);
1575 if (up == NULL)
1576 log_fatal("No memory for upstream.");
1577
1578 up->ifp = ifp;
1579
1580 if (inet_pton(AF_INET6, addr, &up->link.sin6_addr) <= 0)
1581 log_fatal("Bad address %s", addr);
1582
1583 return up;
1584 }
1585
1586 /*
1587 * Setup downstream interfaces.
1588 */
1589 static void
1590 setup_streams(void) {
1591 struct stream_list *dp, *up;
1592 int i;
1593 isc_boolean_t link_is_set;
1594
1595 for (dp = downstreams; dp; dp = dp->next) {
1596 /* Check interface */
1597 if (dp->ifp->v6address_count == 0)
1598 log_fatal("Interface '%s' has no IPv6 addresses.",
1599 dp->ifp->name);
1600
1601 /* Check/set link. */
1602 if (IN6_IS_ADDR_UNSPECIFIED(&dp->link.sin6_addr))
1603 link_is_set = ISC_FALSE;
1604 else
1605 link_is_set = ISC_TRUE;
1606 for (i = 0; i < dp->ifp->v6address_count; i++) {
1607 if (IN6_IS_ADDR_LINKLOCAL(&dp->ifp->v6addresses[i]))
1608 continue;
1609 if (!link_is_set)
1610 break;
1611 if (!memcmp(&dp->ifp->v6addresses[i],
1612 &dp->link.sin6_addr,
1613 sizeof(dp->link.sin6_addr)))
1614 break;
1615 }
1616 if (i == dp->ifp->v6address_count)
1617 log_fatal("Interface %s does not have global IPv6 "
1618 "address assigned.", dp->ifp->name);
1619 if (!link_is_set)
1620 memcpy(&dp->link.sin6_addr,
1621 &dp->ifp->v6addresses[i],
1622 sizeof(dp->link.sin6_addr));
1623
1624 /* Set interface-id. */
1625 if (dp->id == -1)
1626 dp->id = dp->ifp->index;
1627 }
1628
1629 for (up = upstreams; up; up = up->next) {
1630 up->link.sin6_port = local_port;
1631 up->link.sin6_family = AF_INET6;
1632 #ifdef HAVE_SA_LEN
1633 up->link.sin6_len = sizeof(up->link);
1634 #endif
1635
1636 if (up->ifp->v6address_count == 0)
1637 log_fatal("Interface '%s' has no IPv6 addresses.",
1638 up->ifp->name);
1639
1640 /* RFC 3315 Sec 20 - "If the relay agent relays messages to
1641 * the All_DHCP_Servers address or other multicast addresses,
1642 * it sets the Hop Limit field to 32." */
1643 if (IN6_IS_ADDR_MULTICAST(&up->link.sin6_addr)) {
1644 set_multicast_hop_limit(up->ifp, HOP_COUNT_LIMIT);
1645 }
1646 }
1647 }
1648
1649 /*
1650 * Add DHCPv6 agent options here.
1651 */
1652 static const int required_forw_opts[] = {
1653 D6O_INTERFACE_ID,
1654 D6O_SUBSCRIBER_ID,
1655 #if defined(RELAY_PORT)
1656 D6O_RELAY_SOURCE_PORT,
1657 #endif
1658 D6O_RELAY_MSG,
1659 0
1660 };
1661
1662 /*
1663 * Process a packet upwards, i.e., from client to server.
1664 */
1665 static void
1666 process_up6(struct packet *packet, struct stream_list *dp) {
1667 char forw_data[65535];
1668 unsigned cursor;
1669 struct dhcpv6_relay_packet *relay;
1670 struct option_state *opts;
1671 struct stream_list *up;
1672 u_int16_t relay_client_port = 0;
1673
1674 /* Check if the message should be relayed to the server. */
1675 switch (packet->dhcpv6_msg_type) {
1676 case DHCPV6_SOLICIT:
1677 case DHCPV6_REQUEST:
1678 case DHCPV6_CONFIRM:
1679 case DHCPV6_RENEW:
1680 case DHCPV6_REBIND:
1681 case DHCPV6_RELEASE:
1682 case DHCPV6_DECLINE:
1683 case DHCPV6_INFORMATION_REQUEST:
1684 case DHCPV6_RELAY_FORW:
1685 case DHCPV6_LEASEQUERY:
1686 case DHCPV6_DHCPV4_QUERY:
1687 log_info("Relaying %s from %s port %d going up.",
1688 dhcpv6_type_names[packet->dhcpv6_msg_type],
1689 piaddr(packet->client_addr),
1690 ntohs(packet->client_port));
1691 break;
1692
1693 case DHCPV6_ADVERTISE:
1694 case DHCPV6_REPLY:
1695 case DHCPV6_RECONFIGURE:
1696 case DHCPV6_RELAY_REPL:
1697 case DHCPV6_LEASEQUERY_REPLY:
1698 case DHCPV6_DHCPV4_RESPONSE:
1699 log_info("Discarding %s from %s port %d going up.",
1700 dhcpv6_type_names[packet->dhcpv6_msg_type],
1701 piaddr(packet->client_addr),
1702 ntohs(packet->client_port));
1703 return;
1704
1705 default:
1706 log_info("Unknown %d type from %s port %d going up.",
1707 packet->dhcpv6_msg_type,
1708 piaddr(packet->client_addr),
1709 ntohs(packet->client_port));
1710 return;
1711 }
1712
1713 /* Build the relay-forward header. */
1714 relay = (struct dhcpv6_relay_packet *) forw_data;
1715 cursor = offsetof(struct dhcpv6_relay_packet, options);
1716 relay->msg_type = DHCPV6_RELAY_FORW;
1717 if (packet->dhcpv6_msg_type == DHCPV6_RELAY_FORW) {
1718 if (packet->dhcpv6_hop_count >= max_hop_count) {
1719 log_info("Hop count exceeded,");
1720 return;
1721 }
1722 relay->hop_count = packet->dhcpv6_hop_count + 1;
1723 if (dp) {
1724 memcpy(&relay->link_address, &dp->link.sin6_addr, 16);
1725 } else {
1726 /* On smart relay add: && !global. */
1727 if (!use_if_id && downstreams->next) {
1728 log_info("Shan't get back the interface.");
1729 return;
1730 }
1731 memset(&relay->link_address, 0, 16);
1732 }
1733
1734 if (packet->client_port != htons(547)) {
1735 relay_client_port = packet->client_port;
1736 }
1737 } else {
1738 relay->hop_count = 0;
1739 if (!dp)
1740 return;
1741 memcpy(&relay->link_address, &dp->link.sin6_addr, 16);
1742 }
1743 memcpy(&relay->peer_address, packet->client_addr.iabuf, 16);
1744
1745 /* Get an option state. */
1746 opts = NULL;
1747 if (!option_state_allocate(&opts, MDL)) {
1748 log_fatal("No memory for upwards options.");
1749 }
1750
1751 /* Add an interface-id (if used). */
1752 if (use_if_id) {
1753 int if_id;
1754
1755 if (dp) {
1756 if_id = dp->id;
1757 } else if (!downstreams->next) {
1758 if_id = downstreams->id;
1759 } else {
1760 log_info("Don't know the interface.");
1761 option_state_dereference(&opts, MDL);
1762 return;
1763 }
1764
1765 if (!save_option_buffer(&dhcpv6_universe, opts,
1766 NULL, (unsigned char *) &if_id,
1767 sizeof(int),
1768 D6O_INTERFACE_ID, 0)) {
1769 log_error("Can't save interface-id.");
1770 option_state_dereference(&opts, MDL);
1771 return;
1772 }
1773 }
1774
1775 /* Add a subscriber-id if desired. */
1776 /* This is for testing rather than general use */
1777 if (dhcrelay_sub_id != NULL) {
1778 if (!save_option_buffer(&dhcpv6_universe, opts, NULL,
1779 (unsigned char *) dhcrelay_sub_id,
1780 strlen(dhcrelay_sub_id),
1781 D6O_SUBSCRIBER_ID, 0)) {
1782 log_error("Can't save subsriber-id.");
1783 option_state_dereference(&opts, MDL);
1784 return;
1785 }
1786 }
1787
1788
1789 #if defined(RELAY_PORT)
1790 /*
1791 * If we use a non-547 UDP source port or if we have received
1792 * from a downstream relay agent uses a non-547 port, we need
1793 * to include the RELAY-SOURCE-PORT option. The "Downstream
1794 * UDP Port" field value in the option allow us to send
1795 * relay-reply message back to the downstream relay agent
1796 * with the correct UDP source port.
1797 */
1798 if (relay_port || relay_client_port) {
1799 if (!save_option_buffer(&dhcpv6_universe, opts, NULL,
1800 (unsigned char *) &relay_client_port,
1801 sizeof(u_int16_t),
1802 D6O_RELAY_SOURCE_PORT, 0)) {
1803 log_error("Can't save relay-source-port.");
1804 option_state_dereference(&opts, MDL);
1805 return;
1806 }
1807 }
1808 #else
1809 /* Avoid unused but set warning, */
1810 (void)(relay_client_port);
1811 #endif
1812
1813 /* Add the relay-msg carrying the packet. */
1814 if (!save_option_buffer(&dhcpv6_universe, opts,
1815 NULL, (unsigned char *) packet->raw,
1816 packet->packet_length,
1817 D6O_RELAY_MSG, 0)) {
1818 log_error("Can't save relay-msg.");
1819 option_state_dereference(&opts, MDL);
1820 return;
1821 }
1822
1823 /* Finish the relay-forward message. */
1824 cursor += store_options6(forw_data + cursor,
1825 sizeof(forw_data) - cursor,
1826 opts, packet,
1827 required_forw_opts, NULL);
1828 option_state_dereference(&opts, MDL);
1829
1830 /* Send it to all upstreams. */
1831 for (up = upstreams; up; up = up->next) {
1832 send_packet6(up->ifp, (unsigned char *) forw_data,
1833 (size_t) cursor, &up->link);
1834 }
1835 }
1836
1837 /*
1838 * Process a packet downwards, i.e., from server to client.
1839 */
1840 static void
1841 process_down6(struct packet *packet) {
1842 struct stream_list *dp;
1843 struct option_cache *oc;
1844 struct data_string relay_msg;
1845 const struct dhcpv6_packet *msg;
1846 struct data_string if_id;
1847 #if defined(RELAY_PORT)
1848 struct data_string down_port;
1849 #endif
1850 struct sockaddr_in6 to;
1851 struct iaddr peer;
1852
1853 /* The packet must be a relay-reply message. */
1854 if (packet->dhcpv6_msg_type != DHCPV6_RELAY_REPL) {
1855 if (packet->dhcpv6_msg_type < dhcpv6_type_name_max)
1856 log_info("Discarding %s from %s port %d going down.",
1857 dhcpv6_type_names[packet->dhcpv6_msg_type],
1858 piaddr(packet->client_addr),
1859 ntohs(packet->client_port));
1860 else
1861 log_info("Unknown %d type from %s port %d going down.",
1862 packet->dhcpv6_msg_type,
1863 piaddr(packet->client_addr),
1864 ntohs(packet->client_port));
1865 return;
1866 }
1867
1868 /* Inits. */
1869 memset(&relay_msg, 0, sizeof(relay_msg));
1870 memset(&if_id, 0, sizeof(if_id));
1871 #if defined(RELAY_PORT)
1872 memset(&down_port, 0, sizeof(down_port));
1873 #endif
1874 memset(&to, 0, sizeof(to));
1875 to.sin6_family = AF_INET6;
1876 #ifdef HAVE_SA_LEN
1877 to.sin6_len = sizeof(to);
1878 #endif
1879 to.sin6_port = remote_port;
1880 peer.len = 16;
1881
1882 /* Get the relay-msg option (carrying the message to relay). */
1883 oc = lookup_option(&dhcpv6_universe, packet->options, D6O_RELAY_MSG);
1884 if (oc == NULL) {
1885 log_info("No relay-msg.");
1886 return;
1887 }
1888 if (!evaluate_option_cache(&relay_msg, packet, NULL, NULL,
1889 packet->options, NULL,
1890 &global_scope, oc, MDL) ||
1891 (relay_msg.len < offsetof(struct dhcpv6_packet, options))) {
1892 log_error("Can't evaluate relay-msg.");
1893 goto cleanup;
1894 }
1895 msg = (const struct dhcpv6_packet *) relay_msg.data;
1896
1897 /* Get the interface-id (if exists) and the downstream. */
1898 oc = lookup_option(&dhcpv6_universe, packet->options,
1899 D6O_INTERFACE_ID);
1900 if (oc != NULL) {
1901 int if_index;
1902
1903 if (!evaluate_option_cache(&if_id, packet, NULL, NULL,
1904 packet->options, NULL,
1905 &global_scope, oc, MDL) ||
1906 (if_id.len != sizeof(int))) {
1907 log_info("Can't evaluate interface-id.");
1908 goto cleanup;
1909 }
1910 memcpy(&if_index, if_id.data, sizeof(int));
1911 for (dp = downstreams; dp; dp = dp->next) {
1912 if (dp->id == if_index)
1913 break;
1914 }
1915 } else {
1916 if (use_if_id) {
1917 /* Require an interface-id. */
1918 log_info("No interface-id.");
1919 goto cleanup;
1920 }
1921 for (dp = downstreams; dp; dp = dp->next) {
1922 /* Get the first matching one. */
1923 if (!memcmp(&dp->link.sin6_addr,
1924 &packet->dhcpv6_link_address,
1925 sizeof(struct in6_addr)))
1926 break;
1927 }
1928 }
1929 /* Why bother when there is no choice. */
1930 if (!dp && downstreams && !downstreams->next)
1931 dp = downstreams;
1932 if (!dp) {
1933 log_info("Can't find the down interface.");
1934 goto cleanup;
1935 }
1936 memcpy(peer.iabuf, &packet->dhcpv6_peer_address, peer.len);
1937 to.sin6_addr = packet->dhcpv6_peer_address;
1938
1939 /* Check if we should relay the carried message. */
1940 switch (msg->msg_type) {
1941 /* Relay-Reply of for another relay, not a client. */
1942 case DHCPV6_RELAY_REPL:
1943 to.sin6_port = local_port;
1944
1945 #if defined(RELAY_PORT)
1946 oc = lookup_option(&dhcpv6_universe, packet->options,
1947 D6O_RELAY_SOURCE_PORT);
1948 if (oc != NULL) {
1949 u_int16_t down_relay_port;
1950
1951 memset(&down_port, 0, sizeof(down_port));
1952 if (!evaluate_option_cache(&down_port, packet, NULL,
1953 NULL, packet->options, NULL,
1954 &global_scope, oc, MDL) ||
1955 (down_port.len != sizeof(u_int16_t))) {
1956 log_info("Can't evaluate down "
1957 "relay-source-port.");
1958 goto cleanup;
1959 }
1960 memcpy(&down_relay_port, down_port.data,
1961 sizeof(u_int16_t));
1962 /*
1963 * If the down_relay_port value is non-zero,
1964 * that means our downstream relay agent uses
1965 * a non-547 UDP source port sending
1966 * relay-forw message to us. We need to use
1967 * the same UDP port sending reply back.
1968 */
1969 if (down_relay_port) {
1970 to.sin6_port = down_relay_port;
1971 }
1972 }
1973 #endif
1974
1975 /* Fall into: */
1976
1977 case DHCPV6_ADVERTISE:
1978 case DHCPV6_REPLY:
1979 case DHCPV6_RECONFIGURE:
1980 case DHCPV6_RELAY_FORW:
1981 case DHCPV6_LEASEQUERY_REPLY:
1982 case DHCPV6_DHCPV4_RESPONSE:
1983 log_info("Relaying %s to %s port %d down.",
1984 dhcpv6_type_names[msg->msg_type],
1985 piaddr(peer),
1986 ntohs(to.sin6_port));
1987 break;
1988
1989 case DHCPV6_SOLICIT:
1990 case DHCPV6_REQUEST:
1991 case DHCPV6_CONFIRM:
1992 case DHCPV6_RENEW:
1993 case DHCPV6_REBIND:
1994 case DHCPV6_RELEASE:
1995 case DHCPV6_DECLINE:
1996 case DHCPV6_INFORMATION_REQUEST:
1997 case DHCPV6_LEASEQUERY:
1998 case DHCPV6_DHCPV4_QUERY:
1999 log_info("Discarding %s to %s port %d down.",
2000 dhcpv6_type_names[msg->msg_type],
2001 piaddr(peer),
2002 ntohs(to.sin6_port));
2003 goto cleanup;
2004
2005 default:
2006 log_info("Unknown %d type to %s port %d down.",
2007 msg->msg_type,
2008 piaddr(peer),
2009 ntohs(to.sin6_port));
2010 goto cleanup;
2011 }
2012
2013 /* Send the message to the downstream. */
2014 send_packet6(dp->ifp, (unsigned char *) relay_msg.data,
2015 (size_t) relay_msg.len, &to);
2016
2017 cleanup:
2018 if (relay_msg.data != NULL)
2019 data_string_forget(&relay_msg, MDL);
2020 if (if_id.data != NULL)
2021 data_string_forget(&if_id, MDL);
2022 }
2023
2024 /*
2025 * Called by the dispatch packet handler with a decoded packet.
2026 */
2027 void
2028 dhcpv6(struct packet *packet) {
2029 struct stream_list *dp;
2030
2031 /* Try all relay-replies downwards. */
2032 if (packet->dhcpv6_msg_type == DHCPV6_RELAY_REPL) {
2033 process_down6(packet);
2034 return;
2035 }
2036 /* Others are candidates to go up if they come from down. */
2037 for (dp = downstreams; dp; dp = dp->next) {
2038 if (packet->interface != dp->ifp)
2039 continue;
2040 process_up6(packet, dp);
2041 return;
2042 }
2043 /* Relay-forward could work from an unknown interface. */
2044 if (packet->dhcpv6_msg_type == DHCPV6_RELAY_FORW) {
2045 process_up6(packet, NULL);
2046 return;
2047 }
2048
2049 log_info("Can't process packet from interface '%s'.",
2050 packet->interface->name);
2051 }
2052 #endif
2053
2054 /* Stub routines needed for linking with DHCP libraries. */
2055 void
2056 bootp(struct packet *packet) {
2057 return;
2058 }
2059
2060 void
2061 dhcp(struct packet *packet) {
2062 return;
2063 }
2064
2065 #if defined(DHCPv6) && defined(DHCP4o6)
2066 isc_result_t dhcpv4o6_handler(omapi_object_t *h)
2067 {
2068 return ISC_R_NOTIMPLEMENTED;
2069 }
2070 #endif
2071
2072 void
2073 classify(struct packet *p, struct class *c) {
2074 return;
2075 }
2076
2077 int
2078 check_collection(struct packet *p, struct lease *l, struct collection *c) {
2079 return 0;
2080 }
2081
2082 isc_result_t
2083 find_class(struct class **class, const char *c1, const char *c2, int i) {
2084 return ISC_R_NOTFOUND;
2085 }
2086
2087 int
2088 parse_allow_deny(struct option_cache **oc, struct parse *p, int i) {
2089 return 0;
2090 }
2091
2092 isc_result_t
2093 dhcp_set_control_state(control_object_state_t oldstate,
2094 control_object_state_t newstate) {
2095 char buf = 0;
2096
2097 if (newstate != server_shutdown)
2098 return ISC_R_SUCCESS;
2099
2100 /* Log shutdown on signal. */
2101 log_info("Received signal %d, initiating shutdown.", shutdown_signal);
2102
2103 if (no_pid_file == ISC_FALSE)
2104 (void) unlink(path_dhcrelay_pid);
2105
2106 if (!no_daemon && dfd[0] != -1 && dfd[1] != -1) {
2107 IGNORE_RET(write(dfd[1], &buf, 1));
2108 (void) close(dfd[1]);
2109 dfd[0] = dfd[1] = -1;
2110 }
2111 exit(0);
2112 }
2113
2114 /*!
2115 *
2116 * \brief Allocate an interface as requested with a given set of flags
2117 *
2118 * The requested interface is allocated, its flags field is set to
2119 * INTERFACE_REQUESTED OR'd with the given flags, and then added to
2120 * the list of interfaces.
2121 *
2122 * \param name - name of the requested interface
2123 * \param flags - additional flags for the interface
2124 *
2125 * \return Nothing
2126 */
2127 void request_v4_interface(const char* name, int flags) {
2128 struct interface_info *tmp = NULL;
2129 int len = strlen(name);
2130 isc_result_t status;
2131
2132 if (len >= sizeof(tmp->name)) {
2133 log_fatal("%s: interface name too long (is %d)", name, len);
2134 }
2135
2136 status = interface_allocate(&tmp, MDL);
2137 if (status != ISC_R_SUCCESS) {
2138 log_fatal("%s: interface_allocate: %s", name,
2139 isc_result_totext(status));
2140 }
2141
2142 log_debug("Requesting: %s as upstream: %c downstream: %c", name,
2143 (flags & INTERFACE_UPSTREAM ? 'Y' : 'N'),
2144 (flags & INTERFACE_DOWNSTREAM ? 'Y' : 'N'));
2145
2146 memcpy(tmp->name, name, len);
2147 interface_snorf(tmp, (INTERFACE_REQUESTED | flags));
2148 interface_dereference(&tmp, MDL);
2149 }
2150 #endif /* UNIT_TEST */