3 DHCP/BOOTP Relay Agent. */
6 * Copyright(c) 2004-2022 by Internet Systems Consortium, Inc.("ISC")
7 * Copyright(c) 1997-2003 by Internet Software Consortium
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/.
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.
21 * Internet Systems Consortium, Inc.
23 * Newmarket, NH 03857 USA
25 * https://www.isc.org/
35 TIME default_lease_time
= 43200; /* 12 hours... */
36 TIME max_lease_time
= 86400; /* 24 hours... */
37 struct tree_cache
*global_options
[256];
39 struct option
*requested_opts
[2];
41 /* Needed to prevent linking against conflex.c. */
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
;
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
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. */
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. */
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
78 int max_hop_count
= 10; /* Maximum hop count */
81 int dfd
[2] = { -1, -1 };
84 /* Force use of DHCPv6 interface-id option. */
85 isc_boolean_t use_if_id
= ISC_FALSE
;
88 /* Maximum size of a packet with agent options added. */
89 int dhcp_max_agent_option_packet_length
= DHCP_MTU_MIN
;
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
;
98 extern u_int16_t local_port
;
99 extern u_int16_t remote_port
;
101 /* Relay agent server list. */
103 struct server_list
*next
;
104 struct sockaddr_in to
;
107 struct interface_info
*uplink
= NULL
;
108 isc_boolean_t use_fake_gw
= ISC_FALSE
;
109 struct in_addr gw
= {0};
113 struct stream_list
*next
;
114 struct interface_info
*ifp
;
115 struct sockaddr_in6 link
;
117 } *downstreams
, *upstreams
;
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 */
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.
131 char *dhcrelay_sub_id
= NULL
;
135 static void do_relay4(struct interface_info
*, struct dhcp_packet
*,
136 unsigned int, unsigned int, struct iaddr
,
138 #endif /* UNIT_TEST */
140 extern int add_relay_agent_options(struct interface_info
*,
141 struct dhcp_packet
*, unsigned,
143 extern int find_interface_by_agent_option(struct dhcp_packet
*,
144 struct interface_info
**, u_int8_t
*, int);
146 extern int strip_relay_agent_options(struct interface_info
*,
147 struct interface_info
**,
148 struct dhcp_packet
*, unsigned);
151 static void request_v4_interface(const char* name
, int flags
);
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/";
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}"
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}"
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}"
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}"
234 * \brief Print the generic usage message
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.
242 * \param sfmt - The basic string and format for the specific error
243 * \param sarg - Generally the offending argument from the command line.
247 static const char use_noarg
[] = "No argument for command: %s";
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";
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";
261 usage(const char *sfmt
, const char *sarg
) {
262 log_info("%s %s", message
, PACKAGE_VERSION
);
267 /* If desired print out the specific error message */
268 #ifdef PRINT_SPECIFIC_CL_ERRORS
270 log_error(sfmt
, sarg
);
273 log_fatal(DHCRELAY_USAGE
,
275 isc_file_basename(progname
),
277 isc_file_basename(progname
),
278 isc_file_basename(progname
));
282 main(int argc
, char **argv
) {
285 struct server_list
*sp
= NULL
;
286 char *service_local
= NULL
, *service_remote
= NULL
;
287 u_int16_t port_local
= 0, port_remote
= 0;
292 int port_defined
= 0;
295 struct stream_list
*sl
= NULL
;
296 int local_family_set
= 0;
300 progname
= "dhcrelay";
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
);
310 fd
= open("/dev/null", O_RDWR
);
312 fd
= open("/dev/null", O_RDWR
);
314 log_perror
= 0; /* No sense logging to /dev/null. */
318 openlog(isc_file_basename(progname
), DHCP_LOG_OPTIONS
, LOG_DAEMON
);
321 setlogmask(LOG_UPTO(LOG_INFO
));
324 /* Parse arguments changing no_daemon */
325 for (i
= 1; i
< argc
; i
++) {
326 if (!strcmp(argv
[i
], "-d")) {
328 } else if (!strcmp(argv
[i
], "--version")) {
329 log_info("isc-dhcrelay-%s", PACKAGE_VERSION
);
331 } else if (!strcmp(argv
[i
], "--help") ||
332 !strcmp(argv
[i
], "-h")) {
333 log_info(DHCRELAY_USAGE
,
335 isc_file_basename(progname
),
337 isc_file_basename(progname
),
338 isc_file_basename(progname
));
342 /* When not forbidden prepare to become a daemon */
347 log_fatal("Can't get pipe: %m");
348 if ((pid
= fork ()) < 0)
349 log_fatal("Can't fork daemon: %m");
351 /* Parent: wait for the child to start */
354 (void) close(dfd
[1]);
358 n
= read(dfd
[0], &buf
, 1);
361 } while (n
== -1 && errno
== EINTR
);
365 (void) close(dfd
[0]);
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
));
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
));
381 /* Set up the OMAPI wrappers for the interface object. */
384 for (i
= 1; i
< argc
; i
++) {
385 if (!strcmp(argv
[i
], "-4")) {
387 if (local_family_set
&& (local_family
== AF_INET6
)) {
388 usage(use_badproto
, "-4");
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");
396 local_family_set
= 1;
397 local_family
= AF_INET6
;
399 } else if (!strcmp(argv
[i
], "-d")) {
401 } else if (!strcmp(argv
[i
], "-q")) {
403 quiet_interface_discovery
= 1;
404 } else if (!strcmp(argv
[i
], "-p")) {
406 usage(use_noarg
, argv
[i
-1]);
409 usage(use_port_defined
, argv
[i
-1]);
412 local_port
= validate_port(argv
[i
]);
413 log_debug("binding to user-specified port %d",
416 } else if (!strcmp(argv
[i
], "-rp")) {
418 usage(use_noarg
, argv
[i
-1]);
420 usage(use_port_defined
, argv
[i
-1]);
422 relay_port
= validate_port(argv
[i
]);
423 log_debug("binding to user-specified relay port %d",
425 add_agent_options
= 1;
427 } else if (!strcmp(argv
[i
], "-c")) {
430 usage(use_noarg
, argv
[i
-1]);
431 hcount
= atoi(argv
[i
]);
433 max_hop_count
= hcount
;
435 usage("Bad hop count to -c: %s", argv
[i
]);
436 } else if (!strcmp(argv
[i
], "-i")) {
438 if (local_family_set
&& (local_family
== AF_INET6
)) {
439 usage(use_v4command
, argv
[i
]);
441 local_family_set
= 1;
442 local_family
= AF_INET
;
445 usage(use_noarg
, argv
[i
-1]);
448 request_v4_interface(argv
[i
], INTERFACE_STREAMS
);
449 } else if (!strcmp(argv
[i
], "-iu")) {
451 if (local_family_set
&& (local_family
== AF_INET6
)) {
452 usage(use_v4command
, argv
[i
]);
454 local_family_set
= 1;
455 local_family
= AF_INET
;
458 usage(use_noarg
, argv
[i
-1]);
461 request_v4_interface(argv
[i
], INTERFACE_UPSTREAM
);
462 } else if (!strcmp(argv
[i
], "-id")) {
464 if (local_family_set
&& (local_family
== AF_INET6
)) {
465 usage(use_v4command
, argv
[i
]);
467 local_family_set
= 1;
468 local_family
= AF_INET
;
471 usage(use_noarg
, argv
[i
-1]);
474 request_v4_interface(argv
[i
], INTERFACE_DOWNSTREAM
);
475 } else if (!strcmp(argv
[i
], "-a")) {
477 if (local_family_set
&& (local_family
== AF_INET6
)) {
478 usage(use_v4command
, argv
[i
]);
480 local_family_set
= 1;
481 local_family
= AF_INET
;
483 add_agent_options
= 1;
484 } else if (!strcmp(argv
[i
], "-A")) {
486 if (local_family_set
&& (local_family
== AF_INET6
)) {
487 usage(use_v4command
, argv
[i
]);
489 local_family_set
= 1;
490 local_family
= AF_INET
;
493 usage(use_noarg
, argv
[i
-1]);
495 dhcp_max_agent_option_packet_length
= atoi(argv
[i
]);
497 if (dhcp_max_agent_option_packet_length
> DHCP_MTU_MAX
)
498 log_fatal("%s: packet length exceeds "
499 "longest possible MTU\n",
501 } else if (!strcmp(argv
[i
], "-m")) {
503 if (local_family_set
&& (local_family
== AF_INET6
)) {
504 usage(use_v4command
, argv
[i
]);
506 local_family_set
= 1;
507 local_family
= AF_INET
;
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
;
520 usage("Unknown argument to -m: %s", argv
[i
]);
521 } else if (!strcmp(argv
[i
], "-U")) {
523 usage(use_noarg
, argv
[i
-1]);
526 usage("more than one uplink (-U) specified: %s"
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
));
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));
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
|
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")) {
554 usage(use_noarg
, argv
[i
-1]);
556 if (local_family_set
&& (local_family
== AF_INET6
)) {
557 usage(use_v4command
, argv
[i
]);
559 local_family_set
= 1;
560 local_family
= AF_INET
;
562 if (inet_pton(AF_INET
, argv
[i
], &gw
) <= 0) {
563 usage("Invalid gateway address '%s'", argv
[i
]);
565 use_fake_gw
= ISC_TRUE
;
567 } else if (!strcmp(argv
[i
], "-D")) {
569 if (local_family_set
&& (local_family
== AF_INET6
)) {
570 usage(use_v4command
, argv
[i
]);
572 local_family_set
= 1;
573 local_family
= AF_INET
;
575 drop_agent_mismatches
= 1;
577 } else if (!strcmp(argv
[i
], "-I")) {
578 if (local_family_set
&& (local_family
== AF_INET
)) {
579 usage(use_v6command
, argv
[i
]);
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
]);
588 local_family_set
= 1;
589 local_family
= AF_INET6
;
590 if (downstreams
!= NULL
)
591 use_if_id
= ISC_TRUE
;
593 usage(use_noarg
, argv
[i
-1]);
594 sl
= parse_downstream(argv
[i
]);
595 sl
->next
= downstreams
;
597 } else if (!strcmp(argv
[i
], "-u")) {
598 if (local_family_set
&& (local_family
== AF_INET
)) {
599 usage(use_v6command
, argv
[i
]);
601 local_family_set
= 1;
602 local_family
= AF_INET6
;
604 usage(use_noarg
, argv
[i
-1]);
605 sl
= parse_upstream(argv
[i
]);
606 sl
->next
= upstreams
;
608 } else if (!strcmp(argv
[i
], "-s")) {
609 if (local_family_set
&& (local_family
== AF_INET
)) {
610 usage(use_v6command
, argv
[i
]);
612 local_family_set
= 1;
613 local_family
= AF_INET6
;
615 usage(use_noarg
, argv
[i
-1]);
616 dhcrelay_sub_id
= argv
[i
];
618 } else if (!strcmp(argv
[i
], "-pf")) {
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
]);
629 struct in_addr ia
, *iap
= NULL
;
632 if (local_family_set
&& (local_family
== AF_INET6
)) {
633 usage(use_v4command
, argv
[i
]);
635 local_family_set
= 1;
636 local_family
= AF_INET
;
638 if (inet_aton(argv
[i
], &ia
)) {
641 he
= gethostbyname(argv
[i
]);
643 log_error("%s: host unknown", argv
[i
]);
645 iap
= ((struct in_addr
*)
651 sp
= ((struct server_list
*)
652 dmalloc(sizeof *sp
, MDL
));
654 log_fatal("no memory for server.\n");
657 memcpy(&sp
->to
.sin_addr
, iap
, sizeof *iap
);
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");
669 * If the user didn't specify a pid file directly
670 * find one from environment variables or defaults
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
;
680 path_dhcrelay_pid
= getenv("PATH_DHCRELAY6_PID");
681 if (path_dhcrelay_pid
== NULL
)
682 path_dhcrelay_pid
= _PATH_DHCRELAY6_PID
;
688 log_info("%s %s", message
, PACKAGE_VERSION
);
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);
704 service_local
= "dhcpv6-server";
705 service_remote
= "dhcpv6-client";
706 port_local
= htons(547);
707 port_remote
= htons(546);
712 ent
= getservbyname(service_local
, "udp");
714 local_port
= ent
->s_port
;
716 local_port
= port_local
;
718 ent
= getservbyname(service_remote
, "udp");
720 remote_port
= ent
->s_port
;
722 remote_port
= port_remote
;
727 if (local_family
== AF_INET
) {
728 /* We need at least one server */
729 if (servers
== NULL
) {
730 log_fatal("No servers specified.");
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
;
739 sp
->to
.sin_len
= sizeof sp
->to
;
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");
754 /* Set up the initial dhcp option universe. */
755 initialize_common_option_spaces();
757 /* Check requested options. */
758 code
= D6O_RELAY_MSG
;
759 if (!option_code_hash_lookup(&requested_opts
[0],
760 dhcpv6_universe
.code_hash
,
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
,
768 log_fatal("Unable to find the INTERFACE_ID "
769 "option definition.");
773 /* Get the current time... */
774 gettimeofday(&cur_tv
, NULL
);
776 /* Discover all the network interfaces. */
777 discover_interfaces(DISCOVER_RELAY
);
780 if (local_family
== AF_INET6
)
784 /* Become a daemon... */
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;
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);
806 log_error("Can't create %s: %m",
809 pf
= fdopen(pfdesc
, "w");
811 log_error("Can't fdopen %s: %m",
814 fprintf(pf
, "%ld\n",(long)getpid());
825 IGNORE_RET (chdir("/"));
828 /* Set up the packet handler... */
829 if (local_family
== AF_INET
)
830 bootp_packet_handler
= do_relay4
;
833 dhcpv6_packet_handler
= do_packet6
;
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 */
843 /* Start dispatching packets and timeouts... */
846 /* In fact dispatch() never returns. */
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
;
859 if (packet
->hlen
> sizeof packet
->chaddr
) {
860 log_info("Discarding packet with invalid hlen, received on "
861 "%s interface.", ip
->name
);
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
);
870 /* Find the interface that corresponds to the giaddr
872 if (packet
->giaddr
.s_addr
) {
873 for (out
= interfaces
; out
; out
= out
->next
) {
876 for (i
= 0 ; i
< out
->address_count
; i
++ ) {
877 if (out
->addresses
[i
].s_addr
==
878 packet
->giaddr
.s_addr
) {
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
);
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
;
904 /* and hardware address is not broadcast */
907 to
.sin_addr
.s_addr
= htonl(INADDR_BROADCAST
);
908 to
.sin_port
= remote_port
;
910 /* hardware address is broadcast */
913 to
.sin_family
= AF_INET
;
915 to
.sin_len
= sizeof to
;
918 memcpy(&hto
.hbuf
[1], packet
->chaddr
, packet
->hlen
);
919 hto
.hbuf
[0] = packet
->htype
;
920 hto
.hlen
= packet
->hlen
+ 1;
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
927 strip_relay_agent_options(ip
, &out
, packet
, length
)))
931 log_error("Packet to bogus giaddr %s.\n",
932 inet_ntoa(packet
->giaddr
));
933 ++bogus_giaddr_drops
;
941 if (send_packet(out
, NULL
, packet
, length
, out
->addresses
[0],
943 ++server_packet_errors
;
945 log_debug("Forwarded BOOTREPLY for %s to %s",
946 print_hw_addr(packet
->htype
, packet
->hlen
,
948 inet_ntoa(to
.sin_addr
));
950 ++server_packets_relayed
;
955 /* If giaddr matches one of our addresses, ignore the packet -
960 if (!(ip
->flags
& INTERFACE_DOWNSTREAM
)) {
961 log_debug("Dropping request received on %s", ip
->name
);
965 /* Add relay agent options if indicated. If something goes wrong,
966 * drop the packet. Note this may set packet->giaddr if RFC3527
968 if (!(length
= add_relay_agent_options(ip
, packet
, length
,
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;
984 /* Otherwise, it's a BOOTREQUEST, so forward it to all the
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
;
993 log_debug("Forwarded BOOTREQUEST for %s to %s",
994 print_hw_addr(packet
->htype
, packet
->hlen
,
996 inet_ntoa(sp
->to
.sin_addr
));
997 ++client_packets_relayed
;
1003 #endif /* UNIT_TEST */
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. */
1010 strip_relay_agent_options(struct interface_info
*in
,
1011 struct interface_info
**out
,
1012 struct dhcp_packet
*packet
,
1015 u_int8_t
*op
, *nextop
, *sp
, *max
;
1016 int good_agent_option
= 0;
1019 /* If we're not adding agent options to packets, we're not taking
1021 if (!add_agent_options
)
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))
1029 max
= ((u_int8_t
*)packet
) + length
;
1030 sp
= op
= &packet
->options
[4];
1034 /* Skip padding... */
1042 /* If we see a message type, it's a DHCP packet. */
1043 case DHO_DHCP_MESSAGE_TYPE
:
1048 /* Quit immediately if we hit an End option. */
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. */
1061 /* Do not process an agent option if it exceeds the
1062 * buffer. Fail this packet.
1064 nextop
= op
+ op
[1] + 2;
1068 status
= find_interface_by_agent_option(packet
,
1071 if (status
== -1 && drop_agent_mismatches
)
1074 good_agent_option
= 1;
1079 /* Skip over other options. */
1081 /* Fail if processing this option will exceed the
1082 * buffer(op[1] is malformed).
1084 nextop
= op
+ op
[1] + 2;
1089 size_t mlen
= op
[1] + 2;
1090 memmove(sp
, op
, mlen
);
1105 /* If it's not a DHCP packet, we're not supposed to touch it. */
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. */
1114 if (!good_agent_option
) {
1115 ++missing_agent_option
;
1116 if (drop_agent_mismatches
)
1120 /* Adjust the length... */
1122 length
= sp
-((u_int8_t
*)packet
);
1124 /* Make sure the packet isn't short(this is unlikely,
1126 if (length
< BOOTP_MIN_LEN
) {
1127 memset(sp
, DHO_PAD
, BOOTP_MIN_LEN
- length
);
1128 length
= BOOTP_MIN_LEN
;
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.
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. */
1147 find_interface_by_agent_option(struct dhcp_packet
*packet
,
1148 struct interface_info
**out
,
1149 u_int8_t
*buf
, int len
) {
1151 u_int8_t
*circuit_id
= 0;
1152 unsigned circuit_id_len
= 0;
1153 struct interface_info
*ip
;
1156 /* If the next agent option overflows the end of the
1157 packet, the agent option buffer is corrupt. */
1159 i
+ buf
[i
+ 1] + 2 > len
) {
1160 ++corrupt_agent_options
;
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;
1172 i
+= buf
[i
+ 1] + 2;
1177 /* If there's no circuit ID, it's not really ours, tell the caller
1180 ++missing_circuit_id
;
1184 /* Scan the interface list looking for an interface whose
1185 name matches the one specified in circuit_id. */
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
))
1194 /* If we got a match, use it. */
1200 /* If we didn't get a match, the circuit ID was bogus. */
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
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
;
1215 u_int8_t
*op
, *nextop
, *sp
, *max
, *end_pad
= NULL
;
1216 int adding_link_select
;
1218 /* If we're not adding agent options to packets, we can skip
1220 if (!add_agent_options
)
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))
1228 max
= ((u_int8_t
*)packet
) + dhcp_max_agent_option_packet_length
;
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));
1234 /* Commence processing after the cookie. */
1235 sp
= op
= &packet
->options
[4];
1239 /* Skip padding... */
1241 /* Remember the first pad byte so we can commandeer
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.
1250 if (end_pad
== NULL
)
1260 /* If we see a message type, it's a DHCP packet. */
1261 case DHO_DHCP_MESSAGE_TYPE
:
1266 * If there's a maximum message size option, we
1267 * should pay attention to it
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
;
1276 /* Quit immediately if we hit an End option. */
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. */
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. */
1293 switch(agent_relay_mode
) {
1294 case forward_and_append
:
1296 case forward_untouched
:
1300 case forward_and_replace
:
1305 /* Skip over the agent option and start copying
1306 if we aren't copying already. */
1311 /* Skip over other options. */
1313 /* Fail if processing this option will exceed the
1314 * buffer(op[1] is malformed).
1316 nextop
= op
+ op
[1] + 2;
1323 size_t mlen
= op
[1] + 2;
1324 memmove(sp
, op
, mlen
);
1339 /* If it's not a DHCP packet, we're not supposed to touch it. */
1343 /* If the packet was padded out, we can store the agent option
1344 at the beginning of the padding. */
1346 if (end_pad
!= NULL
)
1350 /* Remember where the end of the packet was after parsing
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 */
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 */
1368 if (adding_link_select
) {
1378 /* We do not support relay option fragmenting(multiple options to
1379 * support an option data exceeding 255 bytes).
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
);
1386 * Is there room for the option, its code+len, and DHO_END?
1387 * If not, forward without adding the option.
1389 if (max
- sp
>= optlen
+ 3) {
1390 log_debug("Adding %d-byte relay agent option", optlen
+ 3);
1392 /* Okay, cons up *our* Relay Agent Information option. */
1393 *sp
++ = DHO_DHCP_AGENT_OPTIONS
;
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
;
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
;
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
;
1416 memcpy(sp
, &giaddr
.s_addr
, 4);
1418 packet
->giaddr
= uplink
->addresses
[0];
1419 log_debug ("Adding link selection suboption"
1420 " with addr: %s", inet_ntoa(giaddr
));
1424 /* draft-ietf-dhc-relay-port-10.txt section 5.1 */
1426 *sp
++ = RAI_RELAY_PORT
;
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
)),
1440 * Deposit an END option unless the packet is full (shouldn't
1446 /* Recalculate total packet length. */
1447 length
= sp
-((u_int8_t
*)packet
);
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
);
1461 * Parse a downstream argument: [address%]interface[#index].
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
;
1470 if (!supports_multiple_interfaces(ifp
) &&
1471 (downstreams
!= NULL
))
1472 log_fatal("No support for multiple interfaces.");
1474 /* Decode the argument. */
1475 ifname
= strchr(arg
, '%');
1476 if (ifname
== NULL
) {
1483 iid
= strchr(ifname
, '#');
1487 if (strlen(ifname
) >= sizeof(ifp
->name
)) {
1488 usage("Interface name '%s' too long", ifname
);
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.",
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
);
1508 /* New interface. */
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
);
1516 interface_reference(&ifp
->next
, interfaces
, MDL
);
1517 interface_dereference(&interfaces
, MDL
);
1519 interface_reference(&interfaces
, ifp
, MDL
);
1521 ifp
->flags
|= INTERFACE_REQUESTED
| INTERFACE_DOWNSTREAM
;
1523 /* New downstream. */
1524 dp
= (struct stream_list
*) dmalloc(sizeof(*dp
), MDL
);
1526 log_fatal("No memory for downstream.");
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
);
1541 * Parse an upstream argument: [address]%interface.
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
;
1550 /* Decode the argument. */
1551 ifname
= strchr(arg
, '%');
1552 if (ifname
== NULL
) {
1554 addr
= All_DHCP_Servers
;
1559 if (strlen(ifname
) >= sizeof(ifp
->name
)) {
1560 log_fatal("Interface name '%s' too long", ifname
);
1563 /* Shared up interface? */
1564 for (up
= upstreams
; up
; up
= up
->next
) {
1565 if (strcmp(ifname
, up
->ifp
->name
) == 0) {
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
);
1579 /* New interface. */
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
);
1587 interface_reference(&ifp
->next
, interfaces
, MDL
);
1588 interface_dereference(&interfaces
, MDL
);
1590 interface_reference(&interfaces
, ifp
, MDL
);
1592 ifp
->flags
|= INTERFACE_REQUESTED
| INTERFACE_UPSTREAM
;
1595 up
= (struct stream_list
*) dmalloc(sizeof(*up
), MDL
);
1597 log_fatal("No memory for upstream.");
1601 if (inet_pton(AF_INET6
, addr
, &up
->link
.sin6_addr
) <= 0)
1602 log_fatal("Bad address %s", addr
);
1608 * Setup downstream interfaces.
1611 setup_streams(void) {
1612 struct stream_list
*dp
, *up
;
1614 isc_boolean_t link_is_set
;
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.",
1622 /* Check/set link. */
1623 if (IN6_IS_ADDR_UNSPECIFIED(&dp
->link
.sin6_addr
))
1624 link_is_set
= ISC_FALSE
;
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
]))
1632 if (!memcmp(&dp
->ifp
->v6addresses
[i
],
1633 &dp
->link
.sin6_addr
,
1634 sizeof(dp
->link
.sin6_addr
)))
1637 if (i
== dp
->ifp
->v6address_count
)
1638 log_fatal("Interface %s does not have global IPv6 "
1639 "address assigned.", dp
->ifp
->name
);
1641 memcpy(&dp
->link
.sin6_addr
,
1642 &dp
->ifp
->v6addresses
[i
],
1643 sizeof(dp
->link
.sin6_addr
));
1645 /* Set interface-id. */
1647 dp
->id
= dp
->ifp
->index
;
1650 for (up
= upstreams
; up
; up
= up
->next
) {
1651 up
->link
.sin6_port
= local_port
;
1652 up
->link
.sin6_family
= AF_INET6
;
1654 up
->link
.sin6_len
= sizeof(up
->link
);
1657 if (up
->ifp
->v6address_count
== 0)
1658 log_fatal("Interface '%s' has no IPv6 addresses.",
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
);
1671 * Add DHCPv6 agent options here.
1673 static const int required_forw_opts
[] = {
1676 #if defined(RELAY_PORT)
1677 D6O_RELAY_SOURCE_PORT
,
1684 * Process a packet upwards, i.e., from client to server.
1687 process_up6(struct packet
*packet
, struct stream_list
*dp
) {
1688 char forw_data
[65535];
1690 struct dhcpv6_relay_packet
*relay
;
1691 struct option_state
*opts
;
1692 struct stream_list
*up
;
1693 u_int16_t relay_client_port
= 0;
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
:
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
));
1714 case DHCPV6_ADVERTISE
:
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
));
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
));
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,");
1743 relay
->hop_count
= packet
->dhcpv6_hop_count
+ 1;
1745 memcpy(&relay
->link_address
, &dp
->link
.sin6_addr
, 16);
1747 /* On smart relay add: && !global. */
1748 if (!use_if_id
&& downstreams
->next
) {
1749 log_info("Shan't get back the interface.");
1752 memset(&relay
->link_address
, 0, 16);
1755 if (packet
->client_port
!= htons(547)) {
1756 relay_client_port
= packet
->client_port
;
1759 relay
->hop_count
= 0;
1762 memcpy(&relay
->link_address
, &dp
->link
.sin6_addr
, 16);
1764 memcpy(&relay
->peer_address
, packet
->client_addr
.iabuf
, 16);
1766 /* Get an option state. */
1768 if (!option_state_allocate(&opts
, MDL
)) {
1769 log_fatal("No memory for upwards options.");
1772 /* Add an interface-id (if used). */
1778 } else if (!downstreams
->next
) {
1779 if_id
= downstreams
->id
;
1781 log_info("Don't know the interface.");
1782 option_state_dereference(&opts
, MDL
);
1786 if (!save_option_buffer(&dhcpv6_universe
, opts
,
1787 NULL
, (unsigned char *) &if_id
,
1789 D6O_INTERFACE_ID
, 0)) {
1790 log_error("Can't save interface-id.");
1791 option_state_dereference(&opts
, MDL
);
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
);
1810 #if defined(RELAY_PORT)
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.
1819 if (relay_port
|| relay_client_port
) {
1820 if (!save_option_buffer(&dhcpv6_universe
, opts
, NULL
,
1821 (unsigned char *) &relay_client_port
,
1823 D6O_RELAY_SOURCE_PORT
, 0)) {
1824 log_error("Can't save relay-source-port.");
1825 option_state_dereference(&opts
, MDL
);
1830 /* Avoid unused but set warning, */
1831 (void)(relay_client_port
);
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
);
1844 /* Finish the relay-forward message. */
1845 cursor
+= store_options6(forw_data
+ cursor
,
1846 sizeof(forw_data
) - cursor
,
1848 required_forw_opts
, NULL
);
1849 option_state_dereference(&opts
, MDL
);
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
);
1859 * Process a packet downwards, i.e., from server to client.
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
;
1871 struct sockaddr_in6 to
;
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
));
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
));
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
));
1895 memset(&to
, 0, sizeof(to
));
1896 to
.sin6_family
= AF_INET6
;
1898 to
.sin6_len
= sizeof(to
);
1900 to
.sin6_port
= remote_port
;
1903 /* Get the relay-msg option (carrying the message to relay). */
1904 oc
= lookup_option(&dhcpv6_universe
, packet
->options
, D6O_RELAY_MSG
);
1906 log_info("No relay-msg.");
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.");
1916 msg
= (const struct dhcpv6_packet
*) relay_msg
.data
;
1918 /* Get the interface-id (if exists) and the downstream. */
1919 oc
= lookup_option(&dhcpv6_universe
, packet
->options
,
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.");
1931 memcpy(&if_index
, if_id
.data
, sizeof(int));
1932 for (dp
= downstreams
; dp
; dp
= dp
->next
) {
1933 if (dp
->id
== if_index
)
1938 /* Require an interface-id. */
1939 log_info("No interface-id.");
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
)))
1950 /* Why bother when there is no choice. */
1951 if (!dp
&& downstreams
&& !downstreams
->next
)
1954 log_info("Can't find the down interface.");
1957 memcpy(peer
.iabuf
, &packet
->dhcpv6_peer_address
, peer
.len
);
1958 to
.sin6_addr
= packet
->dhcpv6_peer_address
;
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
;
1966 #if defined(RELAY_PORT)
1967 oc
= lookup_option(&dhcpv6_universe
, packet
->options
,
1968 D6O_RELAY_SOURCE_PORT
);
1970 u_int16_t down_relay_port
;
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.");
1981 memcpy(&down_relay_port
, down_port
.data
,
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.
1990 if (down_relay_port
) {
1991 to
.sin6_port
= down_relay_port
;
1998 case DHCPV6_ADVERTISE
:
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
],
2007 ntohs(to
.sin6_port
));
2010 case DHCPV6_SOLICIT
:
2011 case DHCPV6_REQUEST
:
2012 case DHCPV6_CONFIRM
:
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
],
2023 ntohs(to
.sin6_port
));
2027 log_info("Unknown %d type to %s port %d down.",
2030 ntohs(to
.sin6_port
));
2034 /* Send the message to the downstream. */
2035 send_packet6(dp
->ifp
, (unsigned char *) relay_msg
.data
,
2036 (size_t) relay_msg
.len
, &to
);
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
);
2044 #endif /* UNIT_TEST */
2047 * Called by the dispatch packet handler with a decoded packet.
2050 dhcpv6(struct packet
*packet
) {
2052 struct stream_list
*dp
;
2054 /* Try all relay-replies downwards. */
2055 if (packet
->dhcpv6_msg_type
== DHCPV6_RELAY_REPL
) {
2056 process_down6(packet
);
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
)
2063 process_up6(packet
, dp
);
2066 /* Relay-forward could work from an unknown interface. */
2067 if (packet
->dhcpv6_msg_type
== DHCPV6_RELAY_FORW
) {
2068 process_up6(packet
, NULL
);
2072 log_info("Can't process packet from interface '%s'.",
2073 packet
->interface
->name
);
2074 #endif /* UNIT_TEST */
2078 /* Stub routines needed for linking with DHCP libraries. */
2080 bootp(struct packet
*packet
) {
2085 dhcp(struct packet
*packet
) {
2089 #if defined(DHCPv6) && defined(DHCP4o6)
2090 isc_result_t
dhcpv4o6_handler(omapi_object_t
*h
)
2092 return ISC_R_NOTIMPLEMENTED
;
2097 classify(struct packet
*p
, struct class *c
) {
2102 check_collection(struct packet
*p
, struct lease
*l
, struct collection
*c
) {
2107 find_class(struct class **class, const char *c1
, const char *c2
, int i
) {
2108 return ISC_R_NOTFOUND
;
2112 parse_allow_deny(struct option_cache
**oc
, struct parse
*p
, int i
) {
2117 dhcp_set_control_state(control_object_state_t oldstate
,
2118 control_object_state_t newstate
) {
2121 if (newstate
!= server_shutdown
)
2122 return ISC_R_SUCCESS
;
2124 /* Log shutdown on signal. */
2125 log_info("Received signal %d, initiating shutdown.", shutdown_signal
);
2127 if (no_pid_file
== ISC_FALSE
)
2128 (void) unlink(path_dhcrelay_pid
);
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;
2140 * \brief Allocate an interface as requested with a given set of flags
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.
2146 * \param name - name of the requested interface
2147 * \param flags - additional flags for the interface
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
;
2156 if (len
>= sizeof(tmp
->name
)) {
2157 log_fatal("%s: interface name too long (is %d)", name
, len
);
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
));
2166 log_debug("Requesting: %s as upstream: %c downstream: %c", name
,
2167 (flags
& INTERFACE_UPSTREAM
? 'Y' : 'N'),
2168 (flags
& INTERFACE_DOWNSTREAM
? 'Y' : 'N'));
2170 memcpy(tmp
->name
, name
, len
);
2171 interface_snorf(tmp
, (INTERFACE_REQUESTED
| flags
));
2172 interface_dereference(&tmp
, MDL
);