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