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