3 DHCP/BOOTP Relay Agent. */
6 * Copyright(c) 2004-2019 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 * Redwood City, CA 94063
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
;
99 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
;
111 struct stream_list
*next
;
112 struct interface_info
*ifp
;
113 struct sockaddr_in6 link
;
115 } *downstreams
, *upstreams
;
118 static struct stream_list
*parse_downstream(char *);
119 static struct stream_list
*parse_upstream(char *);
120 static void setup_streams(void);
121 #endif /* UNIT_TEST */
124 * A pointer to a subscriber id to add to the message we forward.
125 * This is primarily for testing purposes as we only have one id
126 * for the entire relay and don't determine one per client which
127 * would be more useful.
129 char *dhcrelay_sub_id
= NULL
;
133 static void do_relay4(struct interface_info
*, struct dhcp_packet
*,
134 unsigned int, unsigned int, struct iaddr
,
136 #endif /* UNIT_TEST */
138 extern int add_relay_agent_options(struct interface_info
*,
139 struct dhcp_packet
*, unsigned,
141 extern int find_interface_by_agent_option(struct dhcp_packet
*,
142 struct interface_info
**, u_int8_t
*, int);
144 extern int strip_relay_agent_options(struct interface_info
*,
145 struct interface_info
**,
146 struct dhcp_packet
*, unsigned);
149 static void request_v4_interface(const char* name
, int flags
);
151 static const char copyright
[] =
152 "Copyright 2004-2019 Internet Systems Consortium.";
153 static const char arr
[] = "All rights reserved.";
154 static const char message
[] =
155 "Internet Systems Consortium DHCP Relay Agent";
156 static const char url
[] =
157 "For info, please visit https://www.isc.org/software/dhcp/";
163 #define DHCRELAY_USAGE \
164 "Usage: %s [-4] [-d] [-q] [-a] [-D]\n" \
165 " [-A <length>] [-c <hops>]\n" \
166 " [-p <port> | -rp <relay-port>]\n" \
167 " [-pf <pid-file>] [--no-pid]\n"\
168 " [-m append|replace|forward|discard]\n" \
169 " [-i interface0 [ ... -i interfaceN]\n" \
170 " [-iu interface0 [ ... -iu interfaceN]\n" \
171 " [-id interface0 [ ... -id interfaceN]\n" \
172 " [-U interface]\n" \
173 " server0 [ ... serverN]\n\n" \
174 " %s -6 [-d] [-q] [-I] [-c <hops>]\n" \
175 " [-p <port> | -rp <relay-port>]\n" \
176 " [-pf <pid-file>] [--no-pid]\n" \
177 " [-s <subscriber-id>]\n" \
178 " -l lower0 [ ... -l lowerN]\n" \
179 " -u upper0 [ ... -u upperN]\n" \
180 " lower (client link): [address%%]interface[#index]\n" \
181 " upper (server link): [address%%]interface\n\n" \
182 " %s {--version|--help|-h}"
184 #define DHCRELAY_USAGE \
185 "Usage: %s [-4] [-d] [-q] [-a] [-D]\n" \
186 " [-A <length>] [-c <hops>] [-p <port>]\n" \
187 " [-pf <pid-file>] [--no-pid]\n"\
188 " [-m append|replace|forward|discard]\n" \
189 " [-i interface0 [ ... -i interfaceN]\n" \
190 " [-iu interface0 [ ... -iu interfaceN]\n" \
191 " [-id interface0 [ ... -id interfaceN]\n" \
192 " [-U interface]\n" \
193 " server0 [ ... serverN]\n\n" \
194 " %s -6 [-d] [-q] [-I] [-c <hops>] [-p <port>]\n" \
195 " [-pf <pid-file>] [--no-pid]\n" \
196 " [-s <subscriber-id>]\n" \
197 " -l lower0 [ ... -l lowerN]\n" \
198 " -u upper0 [ ... -u upperN]\n" \
199 " lower (client link): [address%%]interface[#index]\n" \
200 " upper (server link): [address%%]interface\n\n" \
201 " %s {--version|--help|-h}"
205 #define DHCRELAY_USAGE \
206 "Usage: %s [-d] [-q] [-a] [-D] [-A <length>] [-c <hops>]\n" \
207 " [-p <port> | -rp <relay-port>]\n" \
208 " [-pf <pid-file>] [--no-pid]\n" \
209 " [-m append|replace|forward|discard]\n" \
210 " [-i interface0 [ ... -i interfaceN]\n" \
211 " [-iu interface0 [ ... -iu interfaceN]\n" \
212 " [-id interface0 [ ... -id interfaceN]\n" \
213 " [-U interface]\n" \
214 " server0 [ ... serverN]\n\n" \
215 " %s {--version|--help|-h}"
217 #define DHCRELAY_USAGE \
218 "Usage: %s [-d] [-q] [-a] [-D] [-A <length>] [-c <hops>] [-p <port>]\n" \
219 " [-pf <pid-file>] [--no-pid]\n" \
220 " [-m append|replace|forward|discard]\n" \
221 " [-i interface0 [ ... -i interfaceN]\n" \
222 " [-iu interface0 [ ... -iu interfaceN]\n" \
223 " [-id interface0 [ ... -id interfaceN]\n" \
224 " [-U interface]\n" \
225 " server0 [ ... serverN]\n\n" \
226 " %s {--version|--help|-h}"
232 * \brief Print the generic usage message
234 * If the user has provided an incorrect command line print out
235 * the description of the command line. The arguments provide
236 * a way for the caller to request more specific information about
237 * the error be printed as well. Mostly this will be that some
238 * comamnd doesn't include its argument.
240 * \param sfmt - The basic string and format for the specific error
241 * \param sarg - Generally the offending argument from the comamnd line.
245 static const char use_noarg
[] = "No argument for command: %s";
247 static const char use_port_defined
[] = "Port already set, %s inappropriate";
248 #if !defined (USE_BPF_RECEIVE) && !defined (USE_LPF_RECEIVE)
249 static const char bpf_sock_support
[] = "Only LPF and BPF are supported: %s";
253 static const char use_badproto
[] = "Protocol already set, %s inappropriate";
254 static const char use_v4command
[] = "Command not used for DHCPv6: %s";
255 static const char use_v6command
[] = "Command not used for DHCPv4: %s";
259 usage(const char *sfmt
, const char *sarg
) {
260 log_info("%s %s", message
, PACKAGE_VERSION
);
265 /* If desired print out the specific error message */
266 #ifdef PRINT_SPECIFIC_CL_ERRORS
268 log_error(sfmt
, sarg
);
271 log_fatal(DHCRELAY_USAGE
,
273 isc_file_basename(progname
),
275 isc_file_basename(progname
),
276 isc_file_basename(progname
));
280 main(int argc
, char **argv
) {
283 struct server_list
*sp
= NULL
;
284 char *service_local
= NULL
, *service_remote
= NULL
;
285 u_int16_t port_local
= 0, port_remote
= 0;
290 int port_defined
= 0;
293 struct stream_list
*sl
= NULL
;
294 int local_family_set
= 0;
298 progname
= "dhcrelay";
303 /* Make sure that file descriptors 0(stdin), 1,(stdout), and
304 2(stderr) are open. To do this, we assume that when we
305 open a file the lowest available file descriptor is used. */
306 fd
= open("/dev/null", O_RDWR
);
308 fd
= open("/dev/null", O_RDWR
);
310 fd
= open("/dev/null", O_RDWR
);
312 log_perror
= 0; /* No sense logging to /dev/null. */
316 openlog(isc_file_basename(progname
), DHCP_LOG_OPTIONS
, LOG_DAEMON
);
319 setlogmask(LOG_UPTO(LOG_INFO
));
322 /* Parse arguments changing no_daemon */
323 for (i
= 1; i
< argc
; i
++) {
324 if (!strcmp(argv
[i
], "-d")) {
326 } else if (!strcmp(argv
[i
], "--version")) {
327 log_info("isc-dhcrelay-%s", PACKAGE_VERSION
);
329 } else if (!strcmp(argv
[i
], "--help") ||
330 !strcmp(argv
[i
], "-h")) {
331 log_info(DHCRELAY_USAGE
,
333 isc_file_basename(progname
),
335 isc_file_basename(progname
),
336 isc_file_basename(progname
));
340 /* When not forbidden prepare to become a daemon */
345 log_fatal("Can't get pipe: %m");
346 if ((pid
= fork ()) < 0)
347 log_fatal("Can't fork daemon: %m");
349 /* Parent: wait for the child to start */
352 (void) close(dfd
[1]);
356 n
= read(dfd
[0], &buf
, 1);
359 } while (n
== -1 && errno
== EINTR
);
363 (void) close(dfd
[0]);
367 /* Set up the isc and dns library managers */
368 status
= dhcp_context_create(DHCP_CONTEXT_PRE_DB
, NULL
, NULL
);
369 if (status
!= ISC_R_SUCCESS
)
370 log_fatal("Can't initialize context: %s",
371 isc_result_totext(status
));
373 /* Set up the OMAPI. */
374 status
= omapi_init();
375 if (status
!= ISC_R_SUCCESS
)
376 log_fatal("Can't initialize OMAPI: %s",
377 isc_result_totext(status
));
379 /* Set up the OMAPI wrappers for the interface object. */
382 for (i
= 1; i
< argc
; i
++) {
383 if (!strcmp(argv
[i
], "-4")) {
385 if (local_family_set
&& (local_family
== AF_INET6
)) {
386 usage(use_badproto
, "-4");
388 local_family_set
= 1;
389 local_family
= AF_INET
;
390 } else if (!strcmp(argv
[i
], "-6")) {
391 if (local_family_set
&& (local_family
== AF_INET
)) {
392 usage(use_badproto
, "-6");
394 local_family_set
= 1;
395 local_family
= AF_INET6
;
397 } else if (!strcmp(argv
[i
], "-d")) {
399 } else if (!strcmp(argv
[i
], "-q")) {
401 quiet_interface_discovery
= 1;
402 } else if (!strcmp(argv
[i
], "-p")) {
404 usage(use_noarg
, argv
[i
-1]);
407 usage(use_port_defined
, argv
[i
-1]);
410 local_port
= validate_port(argv
[i
]);
411 log_debug("binding to user-specified port %d",
414 } else if (!strcmp(argv
[i
], "-rp")) {
416 usage(use_noarg
, argv
[i
-1]);
418 usage(use_port_defined
, argv
[i
-1]);
420 relay_port
= validate_port(argv
[i
]);
421 log_debug("binding to user-specified relay port %d",
423 add_agent_options
= 1;
425 } else if (!strcmp(argv
[i
], "-c")) {
428 usage(use_noarg
, argv
[i
-1]);
429 hcount
= atoi(argv
[i
]);
431 max_hop_count
= hcount
;
433 usage("Bad hop count to -c: %s", argv
[i
]);
434 } else if (!strcmp(argv
[i
], "-i")) {
436 if (local_family_set
&& (local_family
== AF_INET6
)) {
437 usage(use_v4command
, argv
[i
]);
439 local_family_set
= 1;
440 local_family
= AF_INET
;
443 usage(use_noarg
, argv
[i
-1]);
446 request_v4_interface(argv
[i
], INTERFACE_STREAMS
);
447 } else if (!strcmp(argv
[i
], "-iu")) {
449 if (local_family_set
&& (local_family
== AF_INET6
)) {
450 usage(use_v4command
, argv
[i
]);
452 local_family_set
= 1;
453 local_family
= AF_INET
;
456 usage(use_noarg
, argv
[i
-1]);
459 request_v4_interface(argv
[i
], INTERFACE_UPSTREAM
);
460 } else if (!strcmp(argv
[i
], "-id")) {
462 if (local_family_set
&& (local_family
== AF_INET6
)) {
463 usage(use_v4command
, argv
[i
]);
465 local_family_set
= 1;
466 local_family
= AF_INET
;
469 usage(use_noarg
, argv
[i
-1]);
472 request_v4_interface(argv
[i
], INTERFACE_DOWNSTREAM
);
473 } else if (!strcmp(argv
[i
], "-a")) {
475 if (local_family_set
&& (local_family
== AF_INET6
)) {
476 usage(use_v4command
, argv
[i
]);
478 local_family_set
= 1;
479 local_family
= AF_INET
;
481 add_agent_options
= 1;
482 } else if (!strcmp(argv
[i
], "-A")) {
484 if (local_family_set
&& (local_family
== AF_INET6
)) {
485 usage(use_v4command
, argv
[i
]);
487 local_family_set
= 1;
488 local_family
= AF_INET
;
491 usage(use_noarg
, argv
[i
-1]);
493 dhcp_max_agent_option_packet_length
= atoi(argv
[i
]);
495 if (dhcp_max_agent_option_packet_length
> DHCP_MTU_MAX
)
496 log_fatal("%s: packet length exceeds "
497 "longest possible MTU\n",
499 } else if (!strcmp(argv
[i
], "-m")) {
501 if (local_family_set
&& (local_family
== AF_INET6
)) {
502 usage(use_v4command
, argv
[i
]);
504 local_family_set
= 1;
505 local_family
= AF_INET
;
508 usage(use_noarg
, argv
[i
-1]);
509 if (!strcasecmp(argv
[i
], "append")) {
510 agent_relay_mode
= forward_and_append
;
511 } else if (!strcasecmp(argv
[i
], "replace")) {
512 agent_relay_mode
= forward_and_replace
;
513 } else if (!strcasecmp(argv
[i
], "forward")) {
514 agent_relay_mode
= forward_untouched
;
515 } else if (!strcasecmp(argv
[i
], "discard")) {
516 agent_relay_mode
= discard
;
518 usage("Unknown argument to -m: %s", argv
[i
]);
519 } else if (!strcmp(argv
[i
], "-U")) {
521 usage(use_noarg
, argv
[i
-1]);
524 usage("more than one uplink (-U) specified: %s"
528 /* Allocate the uplink interface */
529 status
= interface_allocate(&uplink
, MDL
);
530 if (status
!= ISC_R_SUCCESS
) {
531 log_fatal("%s: uplink interface_allocate: %s",
532 argv
[i
], isc_result_totext(status
));
535 if (strlen(argv
[i
]) >= sizeof(uplink
->name
)) {
536 log_fatal("%s: uplink name too long,"
537 " it cannot exceed: %ld characters",
538 argv
[i
], (long)(sizeof(uplink
->name
) - 1));
541 uplink
->name
[sizeof(uplink
->name
) - 1] = 0x00;
542 strncpy(uplink
->name
, argv
[i
],
543 sizeof(uplink
->name
) - 1);
544 interface_snorf(uplink
, (INTERFACE_REQUESTED
|
547 /* Turn on -a, in case they don't do so explicitly */
548 add_agent_options
= 1;
549 add_rfc3527_suboption
= 1;
550 } else if (!strcmp(argv
[i
], "-D")) {
552 if (local_family_set
&& (local_family
== AF_INET6
)) {
553 usage(use_v4command
, argv
[i
]);
555 local_family_set
= 1;
556 local_family
= AF_INET
;
558 drop_agent_mismatches
= 1;
560 } else if (!strcmp(argv
[i
], "-I")) {
561 if (local_family_set
&& (local_family
== AF_INET
)) {
562 usage(use_v6command
, argv
[i
]);
564 local_family_set
= 1;
565 local_family
= AF_INET6
;
566 use_if_id
= ISC_TRUE
;
567 } else if (!strcmp(argv
[i
], "-l")) {
568 if (local_family_set
&& (local_family
== AF_INET
)) {
569 usage(use_v6command
, argv
[i
]);
571 local_family_set
= 1;
572 local_family
= AF_INET6
;
573 if (downstreams
!= NULL
)
574 use_if_id
= ISC_TRUE
;
576 usage(use_noarg
, argv
[i
-1]);
577 sl
= parse_downstream(argv
[i
]);
578 sl
->next
= downstreams
;
580 } else if (!strcmp(argv
[i
], "-u")) {
581 if (local_family_set
&& (local_family
== AF_INET
)) {
582 usage(use_v6command
, argv
[i
]);
584 local_family_set
= 1;
585 local_family
= AF_INET6
;
587 usage(use_noarg
, argv
[i
-1]);
588 sl
= parse_upstream(argv
[i
]);
589 sl
->next
= upstreams
;
591 } else if (!strcmp(argv
[i
], "-s")) {
592 if (local_family_set
&& (local_family
== AF_INET
)) {
593 usage(use_v6command
, argv
[i
]);
595 local_family_set
= 1;
596 local_family
= AF_INET6
;
598 usage(use_noarg
, argv
[i
-1]);
599 dhcrelay_sub_id
= argv
[i
];
601 } else if (!strcmp(argv
[i
], "-pf")) {
603 usage(use_noarg
, argv
[i
-1]);
604 path_dhcrelay_pid
= argv
[i
];
605 no_dhcrelay_pid
= ISC_TRUE
;
606 } else if (!strcmp(argv
[i
], "--no-pid")) {
607 no_pid_file
= ISC_TRUE
;
608 } else if (argv
[i
][0] == '-') {
609 usage("Unknown command: %s", argv
[i
]);
612 struct in_addr ia
, *iap
= NULL
;
615 if (local_family_set
&& (local_family
== AF_INET6
)) {
616 usage(use_v4command
, argv
[i
]);
618 local_family_set
= 1;
619 local_family
= AF_INET
;
621 if (inet_aton(argv
[i
], &ia
)) {
624 he
= gethostbyname(argv
[i
]);
626 log_error("%s: host unknown", argv
[i
]);
628 iap
= ((struct in_addr
*)
634 sp
= ((struct server_list
*)
635 dmalloc(sizeof *sp
, MDL
));
637 log_fatal("no memory for server.\n");
640 memcpy(&sp
->to
.sin_addr
, iap
, sizeof *iap
);
645 #if defined(RELAY_PORT) && \
646 !defined (USE_BPF_RECEIVE) && !defined (USE_LPF_RECEIVE)
647 if (relay_port
&& (local_family
== AF_INET
))
648 usage(bpf_sock_support
, "-rp");
652 * If the user didn't specify a pid file directly
653 * find one from environment variables or defaults
655 if (no_dhcrelay_pid
== ISC_FALSE
) {
656 if (local_family
== AF_INET
) {
657 path_dhcrelay_pid
= getenv("PATH_DHCRELAY_PID");
658 if (path_dhcrelay_pid
== NULL
)
659 path_dhcrelay_pid
= _PATH_DHCRELAY_PID
;
663 path_dhcrelay_pid
= getenv("PATH_DHCRELAY6_PID");
664 if (path_dhcrelay_pid
== NULL
)
665 path_dhcrelay_pid
= _PATH_DHCRELAY6_PID
;
671 log_info("%s %s", message
, PACKAGE_VERSION
);
678 /* Set default port */
679 if (local_family
== AF_INET
) {
680 service_local
= "bootps";
681 service_remote
= "bootpc";
682 port_local
= htons(67);
683 port_remote
= htons(68);
687 service_local
= "dhcpv6-server";
688 service_remote
= "dhcpv6-client";
689 port_local
= htons(547);
690 port_remote
= htons(546);
695 ent
= getservbyname(service_local
, "udp");
697 local_port
= ent
->s_port
;
699 local_port
= port_local
;
701 ent
= getservbyname(service_remote
, "udp");
703 remote_port
= ent
->s_port
;
705 remote_port
= port_remote
;
710 if (local_family
== AF_INET
) {
711 /* We need at least one server */
712 if (servers
== NULL
) {
713 log_fatal("No servers specified.");
717 /* Set up the server sockaddrs. */
718 for (sp
= servers
; sp
; sp
= sp
->next
) {
719 sp
->to
.sin_port
= local_port
;
720 sp
->to
.sin_family
= AF_INET
;
722 sp
->to
.sin_len
= sizeof sp
->to
;
730 /* We need at least one upstream and one downstream interface */
731 if (upstreams
== NULL
|| downstreams
== NULL
) {
732 log_info("Must specify at least one lower "
733 "and one upper interface.\n");
737 /* Set up the initial dhcp option universe. */
738 initialize_common_option_spaces();
740 /* Check requested options. */
741 code
= D6O_RELAY_MSG
;
742 if (!option_code_hash_lookup(&requested_opts
[0],
743 dhcpv6_universe
.code_hash
,
745 log_fatal("Unable to find the RELAY_MSG "
746 "option definition.");
747 code
= D6O_INTERFACE_ID
;
748 if (!option_code_hash_lookup(&requested_opts
[1],
749 dhcpv6_universe
.code_hash
,
751 log_fatal("Unable to find the INTERFACE_ID "
752 "option definition.");
756 /* Get the current time... */
757 gettimeofday(&cur_tv
, NULL
);
759 /* Discover all the network interfaces. */
760 discover_interfaces(DISCOVER_RELAY
);
763 if (local_family
== AF_INET6
)
767 /* Become a daemon... */
775 /* Signal parent we started successfully. */
776 if (dfd
[0] != -1 && dfd
[1] != -1) {
777 if (write(dfd
[1], &buf
, 1) != 1)
778 log_fatal("write to parent: %m");
779 (void) close(dfd
[1]);
780 dfd
[0] = dfd
[1] = -1;
783 /* Create the pid file. */
784 if (no_pid_file
== ISC_FALSE
) {
785 pfdesc
= open(path_dhcrelay_pid
,
786 O_CREAT
| O_TRUNC
| O_WRONLY
, 0644);
789 log_error("Can't create %s: %m",
792 pf
= fdopen(pfdesc
, "w");
794 log_error("Can't fdopen %s: %m",
797 fprintf(pf
, "%ld\n",(long)getpid());
808 IGNORE_RET (chdir("/"));
811 /* Set up the packet handler... */
812 if (local_family
== AF_INET
)
813 bootp_packet_handler
= do_relay4
;
816 dhcpv6_packet_handler
= do_packet6
;
819 #if defined(ENABLE_GENTLE_SHUTDOWN)
820 /* no signal handlers until we deal with the side effects */
821 /* install signal handlers */
822 signal(SIGINT
, dhcp_signal_handler
); /* control-c */
823 signal(SIGTERM
, dhcp_signal_handler
); /* kill */
826 /* Start dispatching packets and timeouts... */
829 /* In fact dispatch() never returns. */
834 do_relay4(struct interface_info
*ip
, struct dhcp_packet
*packet
,
835 unsigned int length
, unsigned int from_port
, struct iaddr from
,
836 struct hardware
*hfrom
) {
837 struct server_list
*sp
;
838 struct sockaddr_in to
;
839 struct interface_info
*out
;
840 struct hardware hto
, *htop
;
842 if (packet
->hlen
> sizeof packet
->chaddr
) {
843 log_info("Discarding packet with invalid hlen, received on "
844 "%s interface.", ip
->name
);
847 if (ip
->address_count
< 1 || ip
->addresses
== NULL
) {
848 log_info("Discarding packet received on %s interface that "
849 "has no IPv4 address assigned.", ip
->name
);
853 /* Find the interface that corresponds to the giaddr
855 if (packet
->giaddr
.s_addr
) {
856 for (out
= interfaces
; out
; out
= out
->next
) {
859 for (i
= 0 ; i
< out
->address_count
; i
++ ) {
860 if (out
->addresses
[i
].s_addr
==
861 packet
->giaddr
.s_addr
) {
874 /* If it's a bootreply, forward it to the client. */
875 if (packet
->op
== BOOTREPLY
) {
876 if (!(ip
->flags
& INTERFACE_UPSTREAM
)) {
877 log_debug("Dropping reply received on %s", ip
->name
);
881 if (!(packet
->flags
& htons(BOOTP_BROADCAST
)) &&
882 can_unicast_without_arp(out
)) {
883 to
.sin_addr
= packet
->yiaddr
;
884 to
.sin_port
= remote_port
;
886 /* and hardware address is not broadcast */
889 to
.sin_addr
.s_addr
= htonl(INADDR_BROADCAST
);
890 to
.sin_port
= remote_port
;
892 /* hardware address is broadcast */
895 to
.sin_family
= AF_INET
;
897 to
.sin_len
= sizeof to
;
900 memcpy(&hto
.hbuf
[1], packet
->chaddr
, packet
->hlen
);
901 hto
.hbuf
[0] = packet
->htype
;
902 hto
.hlen
= packet
->hlen
+ 1;
904 /* Wipe out the agent relay options and, if possible, figure
905 out which interface to use based on the contents of the
906 option that we put on the request to which the server is
909 strip_relay_agent_options(ip
, &out
, packet
, length
)))
913 log_error("Packet to bogus giaddr %s.\n",
914 inet_ntoa(packet
->giaddr
));
915 ++bogus_giaddr_drops
;
919 if (send_packet(out
, NULL
, packet
, length
, out
->addresses
[0],
921 ++server_packet_errors
;
923 log_debug("Forwarded BOOTREPLY for %s to %s",
924 print_hw_addr(packet
->htype
, packet
->hlen
,
926 inet_ntoa(to
.sin_addr
));
928 ++server_packets_relayed
;
933 /* If giaddr matches one of our addresses, ignore the packet -
938 if (!(ip
->flags
& INTERFACE_DOWNSTREAM
)) {
939 log_debug("Dropping request received on %s", ip
->name
);
943 /* Add relay agent options if indicated. If something goes wrong,
944 * drop the packet. Note this may set packet->giaddr if RFC3527
946 if (!(length
= add_relay_agent_options(ip
, packet
, length
,
950 /* If giaddr is not already set, Set it so the server can
951 figure out what net it's from and so that we can later
952 forward the response to the correct net. If it's already
953 set, the response will be sent directly to the relay agent
954 that set giaddr, so we won't see it. */
955 if (!packet
->giaddr
.s_addr
)
956 packet
->giaddr
= ip
->addresses
[0];
957 if (packet
->hops
< max_hop_count
)
958 packet
->hops
= packet
->hops
+ 1;
962 /* Otherwise, it's a BOOTREQUEST, so forward it to all the
964 for (sp
= servers
; sp
; sp
= sp
->next
) {
965 if (send_packet((fallback_interface
966 ? fallback_interface
: interfaces
),
967 NULL
, packet
, length
, ip
->addresses
[0],
968 &sp
->to
, NULL
) < 0) {
969 ++client_packet_errors
;
971 log_debug("Forwarded BOOTREQUEST for %s to %s",
972 print_hw_addr(packet
->htype
, packet
->hlen
,
974 inet_ntoa(sp
->to
.sin_addr
));
975 ++client_packets_relayed
;
981 #endif /* UNIT_TEST */
983 /* Strip any Relay Agent Information options from the DHCP packet
984 option buffer. If there is a circuit ID suboption, look up the
985 outgoing interface based upon it. */
988 strip_relay_agent_options(struct interface_info
*in
,
989 struct interface_info
**out
,
990 struct dhcp_packet
*packet
,
993 u_int8_t
*op
, *nextop
, *sp
, *max
;
994 int good_agent_option
= 0;
997 /* If we're not adding agent options to packets, we're not taking
999 if (!add_agent_options
)
1002 /* If there's no cookie, it's a bootp packet, so we should just
1003 forward it unchanged. */
1004 if (memcmp(packet
->options
, DHCP_OPTIONS_COOKIE
, 4))
1007 max
= ((u_int8_t
*)packet
) + length
;
1008 sp
= op
= &packet
->options
[4];
1012 /* Skip padding... */
1020 /* If we see a message type, it's a DHCP packet. */
1021 case DHO_DHCP_MESSAGE_TYPE
:
1026 /* Quit immediately if we hit an End option. */
1032 case DHO_DHCP_AGENT_OPTIONS
:
1033 /* We shouldn't see a relay agent option in a
1034 packet before we've seen the DHCP packet type,
1035 but if we do, we have to leave it alone. */
1039 /* Do not process an agent option if it exceeds the
1040 * buffer. Fail this packet.
1042 nextop
= op
+ op
[1] + 2;
1046 status
= find_interface_by_agent_option(packet
,
1049 if (status
== -1 && drop_agent_mismatches
)
1052 good_agent_option
= 1;
1057 /* Skip over other options. */
1059 /* Fail if processing this option will exceed the
1060 * buffer(op[1] is malformed).
1062 nextop
= op
+ op
[1] + 2;
1067 size_t mlen
= op
[1] + 2;
1068 memmove(sp
, op
, mlen
);
1083 /* If it's not a DHCP packet, we're not supposed to touch it. */
1087 /* If none of the agent options we found matched, or if we didn't
1088 find any agent options, count this packet as not having any
1089 matching agent options, and if we're relying on agent options
1090 to determine the outgoing interface, drop the packet. */
1092 if (!good_agent_option
) {
1093 ++missing_agent_option
;
1094 if (drop_agent_mismatches
)
1098 /* Adjust the length... */
1100 length
= sp
-((u_int8_t
*)packet
);
1102 /* Make sure the packet isn't short(this is unlikely,
1104 if (length
< BOOTP_MIN_LEN
) {
1105 memset(sp
, DHO_PAD
, BOOTP_MIN_LEN
- length
);
1106 length
= BOOTP_MIN_LEN
;
1113 /* Find an interface that matches the circuit ID specified in the
1114 Relay Agent Information option. If one is found, store it through
1115 the pointer given; otherwise, leave the existing pointer alone.
1117 We actually deviate somewhat from the current specification here:
1118 if the option buffer is corrupt, we suggest that the caller not
1119 respond to this packet. If the circuit ID doesn't match any known
1120 interface, we suggest that the caller to drop the packet. Only if
1121 we find a circuit ID that matches an existing interface do we tell
1122 the caller to go ahead and process the packet. */
1125 find_interface_by_agent_option(struct dhcp_packet
*packet
,
1126 struct interface_info
**out
,
1127 u_int8_t
*buf
, int len
) {
1129 u_int8_t
*circuit_id
= 0;
1130 unsigned circuit_id_len
= 0;
1131 struct interface_info
*ip
;
1134 /* If the next agent option overflows the end of the
1135 packet, the agent option buffer is corrupt. */
1137 i
+ buf
[i
+ 1] + 2 > len
) {
1138 ++corrupt_agent_options
;
1142 /* Remember where the circuit ID is... */
1143 case RAI_CIRCUIT_ID
:
1144 circuit_id
= &buf
[i
+ 2];
1145 circuit_id_len
= buf
[i
+ 1];
1146 i
+= circuit_id_len
+ 2;
1150 i
+= buf
[i
+ 1] + 2;
1155 /* If there's no circuit ID, it's not really ours, tell the caller
1158 ++missing_circuit_id
;
1162 /* Scan the interface list looking for an interface whose
1163 name matches the one specified in circuit_id. */
1165 for (ip
= interfaces
; ip
; ip
= ip
->next
) {
1166 if (ip
->circuit_id
&&
1167 ip
->circuit_id_len
== circuit_id_len
&&
1168 !memcmp(ip
->circuit_id
, circuit_id
, circuit_id_len
))
1172 /* If we got a match, use it. */
1178 /* If we didn't get a match, the circuit ID was bogus. */
1184 * Examine a packet to see if it's a candidate to have a Relay
1185 * Agent Information option tacked onto its tail. If it is, tack
1189 add_relay_agent_options(struct interface_info
*ip
, struct dhcp_packet
*packet
,
1190 unsigned length
, struct in_addr giaddr
) {
1191 int is_dhcp
= 0, mms
;
1193 u_int8_t
*op
, *nextop
, *sp
, *max
, *end_pad
= NULL
;
1194 int adding_link_select
;
1196 /* If we're not adding agent options to packets, we can skip
1198 if (!add_agent_options
)
1201 /* If there's no cookie, it's a bootp packet, so we should just
1202 forward it unchanged. */
1203 if (memcmp(packet
->options
, DHCP_OPTIONS_COOKIE
, 4))
1206 max
= ((u_int8_t
*)packet
) + dhcp_max_agent_option_packet_length
;
1208 /* Add link selection suboption if enabled and we're the first relay */
1209 adding_link_select
= (add_rfc3527_suboption
1210 && (packet
->giaddr
.s_addr
== 0));
1212 /* Commence processing after the cookie. */
1213 sp
= op
= &packet
->options
[4];
1217 /* Skip padding... */
1219 /* Remember the first pad byte so we can commandeer
1222 * XXX: Is this really a good idea? Sure, we can
1223 * seemingly reduce the packet while we're looking,
1224 * but if the packet was signed by the client then
1225 * this padding is part of the checksum(RFC3118),
1226 * and its nonpresence would break authentication.
1228 if (end_pad
== NULL
)
1238 /* If we see a message type, it's a DHCP packet. */
1239 case DHO_DHCP_MESSAGE_TYPE
:
1244 * If there's a maximum message size option, we
1245 * should pay attention to it
1247 case DHO_DHCP_MAX_MESSAGE_SIZE
:
1248 mms
= ntohs(*(op
+ 2));
1249 if (mms
< dhcp_max_agent_option_packet_length
&&
1250 mms
>= DHCP_MTU_MIN
)
1251 max
= ((u_int8_t
*)packet
) + mms
;
1254 /* Quit immediately if we hit an End option. */
1258 case DHO_DHCP_AGENT_OPTIONS
:
1259 /* We shouldn't see a relay agent option in a
1260 packet before we've seen the DHCP packet type,
1261 but if we do, we have to leave it alone. */
1267 /* There's already a Relay Agent Information option
1268 in this packet. How embarrassing. Decide what
1269 to do based on the mode the user specified. */
1271 switch(agent_relay_mode
) {
1272 case forward_and_append
:
1274 case forward_untouched
:
1278 case forward_and_replace
:
1283 /* Skip over the agent option and start copying
1284 if we aren't copying already. */
1289 /* Skip over other options. */
1291 /* Fail if processing this option will exceed the
1292 * buffer(op[1] is malformed).
1294 nextop
= op
+ op
[1] + 2;
1301 size_t mlen
= op
[1] + 2;
1302 memmove(sp
, op
, mlen
);
1317 /* If it's not a DHCP packet, we're not supposed to touch it. */
1321 /* If the packet was padded out, we can store the agent option
1322 at the beginning of the padding. */
1324 if (end_pad
!= NULL
)
1328 /* Remember where the end of the packet was after parsing
1333 /* Sanity check. Had better not ever happen. */
1334 if ((ip
->circuit_id_len
> 255) ||(ip
->circuit_id_len
< 1))
1335 log_fatal("Circuit ID length %d out of range [1-255] on "
1336 "%s\n", ip
->circuit_id_len
, ip
->name
);
1337 optlen
= ip
->circuit_id_len
+ 2; /* RAI_CIRCUIT_ID + len */
1339 if (ip
->remote_id
) {
1340 if (ip
->remote_id_len
> 255 || ip
->remote_id_len
< 1)
1341 log_fatal("Remote ID length %d out of range [1-255] "
1342 "on %s\n", ip
->remote_id_len
, ip
->name
);
1343 optlen
+= ip
->remote_id_len
+ 2; /* RAI_REMOTE_ID + len */
1346 if (adding_link_select
) {
1356 /* We do not support relay option fragmenting(multiple options to
1357 * support an option data exceeding 255 bytes).
1359 if ((optlen
< 3) ||(optlen
> 255))
1360 log_fatal("Total agent option length(%u) out of range "
1361 "[3 - 255] on %s\n", optlen
, ip
->name
);
1364 * Is there room for the option, its code+len, and DHO_END?
1365 * If not, forward without adding the option.
1367 if (max
- sp
>= optlen
+ 3) {
1368 log_debug("Adding %d-byte relay agent option", optlen
+ 3);
1370 /* Okay, cons up *our* Relay Agent Information option. */
1371 *sp
++ = DHO_DHCP_AGENT_OPTIONS
;
1374 /* Copy in the circuit id... */
1375 *sp
++ = RAI_CIRCUIT_ID
;
1376 *sp
++ = ip
->circuit_id_len
;
1377 memcpy(sp
, ip
->circuit_id
, ip
->circuit_id_len
);
1378 sp
+= ip
->circuit_id_len
;
1380 /* Copy in remote ID... */
1381 if (ip
->remote_id
) {
1382 *sp
++ = RAI_REMOTE_ID
;
1383 *sp
++ = ip
->remote_id_len
;
1384 memcpy(sp
, ip
->remote_id
, ip
->remote_id_len
);
1385 sp
+= ip
->remote_id_len
;
1388 /* RFC3527: Use the inbound packet's interface address in
1389 * the link selection suboption and set the outbound giaddr
1390 * to the uplink address. */
1391 if (adding_link_select
) {
1392 *sp
++ = RAI_LINK_SELECT
;
1394 memcpy(sp
, &giaddr
.s_addr
, 4);
1396 packet
->giaddr
= uplink
->addresses
[0];
1397 log_debug ("Adding link selection suboption"
1398 " with addr: %s", inet_ntoa(giaddr
));
1402 /* draft-ietf-dhc-relay-port-10.txt section 5.1 */
1404 *sp
++ = RAI_RELAY_PORT
;
1409 ++agent_option_errors
;
1410 log_error("No room in packet (used %d of %d) "
1411 "for %d-byte relay agent option: omitted",
1412 (int) (sp
- ((u_int8_t
*) packet
)),
1413 (int) (max
- ((u_int8_t
*) packet
)),
1418 * Deposit an END option unless the packet is full (shouldn't
1424 /* Recalculate total packet length. */
1425 length
= sp
-((u_int8_t
*)packet
);
1427 /* Make sure the packet isn't short(this is unlikely, but WTH) */
1428 if (length
< BOOTP_MIN_LEN
) {
1429 memset(sp
, DHO_PAD
, BOOTP_MIN_LEN
- length
);
1430 return (BOOTP_MIN_LEN
);
1440 * Parse a downstream argument: [address%]interface[#index].
1442 static struct stream_list
*
1443 parse_downstream(char *arg
) {
1444 struct stream_list
*dp
, *up
;
1445 struct interface_info
*ifp
= NULL
;
1446 char *ifname
, *addr
, *iid
;
1447 isc_result_t status
;
1449 if (!supports_multiple_interfaces(ifp
) &&
1450 (downstreams
!= NULL
))
1451 log_fatal("No support for multiple interfaces.");
1453 /* Decode the argument. */
1454 ifname
= strchr(arg
, '%');
1455 if (ifname
== NULL
) {
1462 iid
= strchr(ifname
, '#');
1466 if (strlen(ifname
) >= sizeof(ifp
->name
)) {
1467 usage("Interface name '%s' too long", ifname
);
1470 /* Don't declare twice. */
1471 for (dp
= downstreams
; dp
; dp
= dp
->next
) {
1472 if (strcmp(ifname
, dp
->ifp
->name
) == 0)
1473 log_fatal("Down interface '%s' declared twice.",
1477 /* Share with up side? */
1478 for (up
= upstreams
; up
; up
= up
->next
) {
1479 if (strcmp(ifname
, up
->ifp
->name
) == 0) {
1480 log_info("parse_downstream: Interface '%s' is "
1481 "both down and up.", ifname
);
1487 /* New interface. */
1489 status
= interface_allocate(&ifp
, MDL
);
1490 if (status
!= ISC_R_SUCCESS
)
1491 log_fatal("%s: interface_allocate: %s",
1492 arg
, isc_result_totext(status
));
1493 strcpy(ifp
->name
, ifname
);
1495 interface_reference(&ifp
->next
, interfaces
, MDL
);
1496 interface_dereference(&interfaces
, MDL
);
1498 interface_reference(&interfaces
, ifp
, MDL
);
1500 ifp
->flags
|= INTERFACE_REQUESTED
| INTERFACE_DOWNSTREAM
;
1502 /* New downstream. */
1503 dp
= (struct stream_list
*) dmalloc(sizeof(*dp
), MDL
);
1505 log_fatal("No memory for downstream.");
1512 /* !addr case handled by setup. */
1513 if (addr
&& (inet_pton(AF_INET6
, addr
, &dp
->link
.sin6_addr
) <= 0))
1514 log_fatal("Bad link address '%s'", addr
);
1520 * Parse an upstream argument: [address]%interface.
1522 static struct stream_list
*
1523 parse_upstream(char *arg
) {
1524 struct stream_list
*up
, *dp
;
1525 struct interface_info
*ifp
= NULL
;
1526 char *ifname
, *addr
;
1527 isc_result_t status
;
1529 /* Decode the argument. */
1530 ifname
= strchr(arg
, '%');
1531 if (ifname
== NULL
) {
1533 addr
= All_DHCP_Servers
;
1538 if (strlen(ifname
) >= sizeof(ifp
->name
)) {
1539 log_fatal("Interface name '%s' too long", ifname
);
1542 /* Shared up interface? */
1543 for (up
= upstreams
; up
; up
= up
->next
) {
1544 if (strcmp(ifname
, up
->ifp
->name
) == 0) {
1549 for (dp
= downstreams
; dp
; dp
= dp
->next
) {
1550 if (strcmp(ifname
, dp
->ifp
->name
) == 0) {
1551 log_info("parse_upstream: Interface '%s' is "
1552 "both down and up.", ifname
);
1558 /* New interface. */
1560 status
= interface_allocate(&ifp
, MDL
);
1561 if (status
!= ISC_R_SUCCESS
)
1562 log_fatal("%s: interface_allocate: %s",
1563 arg
, isc_result_totext(status
));
1564 strcpy(ifp
->name
, ifname
);
1566 interface_reference(&ifp
->next
, interfaces
, MDL
);
1567 interface_dereference(&interfaces
, MDL
);
1569 interface_reference(&interfaces
, ifp
, MDL
);
1571 ifp
->flags
|= INTERFACE_REQUESTED
| INTERFACE_UPSTREAM
;
1574 up
= (struct stream_list
*) dmalloc(sizeof(*up
), MDL
);
1576 log_fatal("No memory for upstream.");
1580 if (inet_pton(AF_INET6
, addr
, &up
->link
.sin6_addr
) <= 0)
1581 log_fatal("Bad address %s", addr
);
1587 * Setup downstream interfaces.
1590 setup_streams(void) {
1591 struct stream_list
*dp
, *up
;
1593 isc_boolean_t link_is_set
;
1595 for (dp
= downstreams
; dp
; dp
= dp
->next
) {
1596 /* Check interface */
1597 if (dp
->ifp
->v6address_count
== 0)
1598 log_fatal("Interface '%s' has no IPv6 addresses.",
1601 /* Check/set link. */
1602 if (IN6_IS_ADDR_UNSPECIFIED(&dp
->link
.sin6_addr
))
1603 link_is_set
= ISC_FALSE
;
1605 link_is_set
= ISC_TRUE
;
1606 for (i
= 0; i
< dp
->ifp
->v6address_count
; i
++) {
1607 if (IN6_IS_ADDR_LINKLOCAL(&dp
->ifp
->v6addresses
[i
]))
1611 if (!memcmp(&dp
->ifp
->v6addresses
[i
],
1612 &dp
->link
.sin6_addr
,
1613 sizeof(dp
->link
.sin6_addr
)))
1616 if (i
== dp
->ifp
->v6address_count
)
1617 log_fatal("Interface %s does not have global IPv6 "
1618 "address assigned.", dp
->ifp
->name
);
1620 memcpy(&dp
->link
.sin6_addr
,
1621 &dp
->ifp
->v6addresses
[i
],
1622 sizeof(dp
->link
.sin6_addr
));
1624 /* Set interface-id. */
1626 dp
->id
= dp
->ifp
->index
;
1629 for (up
= upstreams
; up
; up
= up
->next
) {
1630 up
->link
.sin6_port
= local_port
;
1631 up
->link
.sin6_family
= AF_INET6
;
1633 up
->link
.sin6_len
= sizeof(up
->link
);
1636 if (up
->ifp
->v6address_count
== 0)
1637 log_fatal("Interface '%s' has no IPv6 addresses.",
1640 /* RFC 3315 Sec 20 - "If the relay agent relays messages to
1641 * the All_DHCP_Servers address or other multicast addresses,
1642 * it sets the Hop Limit field to 32." */
1643 if (IN6_IS_ADDR_MULTICAST(&up
->link
.sin6_addr
)) {
1644 set_multicast_hop_limit(up
->ifp
, HOP_COUNT_LIMIT
);
1650 * Add DHCPv6 agent options here.
1652 static const int required_forw_opts
[] = {
1655 #if defined(RELAY_PORT)
1656 D6O_RELAY_SOURCE_PORT
,
1663 * Process a packet upwards, i.e., from client to server.
1666 process_up6(struct packet
*packet
, struct stream_list
*dp
) {
1667 char forw_data
[65535];
1669 struct dhcpv6_relay_packet
*relay
;
1670 struct option_state
*opts
;
1671 struct stream_list
*up
;
1672 u_int16_t relay_client_port
= 0;
1674 /* Check if the message should be relayed to the server. */
1675 switch (packet
->dhcpv6_msg_type
) {
1676 case DHCPV6_SOLICIT
:
1677 case DHCPV6_REQUEST
:
1678 case DHCPV6_CONFIRM
:
1681 case DHCPV6_RELEASE
:
1682 case DHCPV6_DECLINE
:
1683 case DHCPV6_INFORMATION_REQUEST
:
1684 case DHCPV6_RELAY_FORW
:
1685 case DHCPV6_LEASEQUERY
:
1686 case DHCPV6_DHCPV4_QUERY
:
1687 log_info("Relaying %s from %s port %d going up.",
1688 dhcpv6_type_names
[packet
->dhcpv6_msg_type
],
1689 piaddr(packet
->client_addr
),
1690 ntohs(packet
->client_port
));
1693 case DHCPV6_ADVERTISE
:
1695 case DHCPV6_RECONFIGURE
:
1696 case DHCPV6_RELAY_REPL
:
1697 case DHCPV6_LEASEQUERY_REPLY
:
1698 case DHCPV6_DHCPV4_RESPONSE
:
1699 log_info("Discarding %s from %s port %d going up.",
1700 dhcpv6_type_names
[packet
->dhcpv6_msg_type
],
1701 piaddr(packet
->client_addr
),
1702 ntohs(packet
->client_port
));
1706 log_info("Unknown %d type from %s port %d going up.",
1707 packet
->dhcpv6_msg_type
,
1708 piaddr(packet
->client_addr
),
1709 ntohs(packet
->client_port
));
1713 /* Build the relay-forward header. */
1714 relay
= (struct dhcpv6_relay_packet
*) forw_data
;
1715 cursor
= offsetof(struct dhcpv6_relay_packet
, options
);
1716 relay
->msg_type
= DHCPV6_RELAY_FORW
;
1717 if (packet
->dhcpv6_msg_type
== DHCPV6_RELAY_FORW
) {
1718 if (packet
->dhcpv6_hop_count
>= max_hop_count
) {
1719 log_info("Hop count exceeded,");
1722 relay
->hop_count
= packet
->dhcpv6_hop_count
+ 1;
1724 memcpy(&relay
->link_address
, &dp
->link
.sin6_addr
, 16);
1726 /* On smart relay add: && !global. */
1727 if (!use_if_id
&& downstreams
->next
) {
1728 log_info("Shan't get back the interface.");
1731 memset(&relay
->link_address
, 0, 16);
1734 if (packet
->client_port
!= htons(547)) {
1735 relay_client_port
= packet
->client_port
;
1738 relay
->hop_count
= 0;
1741 memcpy(&relay
->link_address
, &dp
->link
.sin6_addr
, 16);
1743 memcpy(&relay
->peer_address
, packet
->client_addr
.iabuf
, 16);
1745 /* Get an option state. */
1747 if (!option_state_allocate(&opts
, MDL
)) {
1748 log_fatal("No memory for upwards options.");
1751 /* Add an interface-id (if used). */
1757 } else if (!downstreams
->next
) {
1758 if_id
= downstreams
->id
;
1760 log_info("Don't know the interface.");
1761 option_state_dereference(&opts
, MDL
);
1765 if (!save_option_buffer(&dhcpv6_universe
, opts
,
1766 NULL
, (unsigned char *) &if_id
,
1768 D6O_INTERFACE_ID
, 0)) {
1769 log_error("Can't save interface-id.");
1770 option_state_dereference(&opts
, MDL
);
1775 /* Add a subscriber-id if desired. */
1776 /* This is for testing rather than general use */
1777 if (dhcrelay_sub_id
!= NULL
) {
1778 if (!save_option_buffer(&dhcpv6_universe
, opts
, NULL
,
1779 (unsigned char *) dhcrelay_sub_id
,
1780 strlen(dhcrelay_sub_id
),
1781 D6O_SUBSCRIBER_ID
, 0)) {
1782 log_error("Can't save subsriber-id.");
1783 option_state_dereference(&opts
, MDL
);
1789 #if defined(RELAY_PORT)
1791 * If we use a non-547 UDP source port or if we have received
1792 * from a downstream relay agent uses a non-547 port, we need
1793 * to include the RELAY-SOURCE-PORT option. The "Downstream
1794 * UDP Port" field value in the option allow us to send
1795 * relay-reply message back to the downstream relay agent
1796 * with the correct UDP source port.
1798 if (relay_port
|| relay_client_port
) {
1799 if (!save_option_buffer(&dhcpv6_universe
, opts
, NULL
,
1800 (unsigned char *) &relay_client_port
,
1802 D6O_RELAY_SOURCE_PORT
, 0)) {
1803 log_error("Can't save relay-source-port.");
1804 option_state_dereference(&opts
, MDL
);
1809 /* Avoid unused but set warning, */
1810 (void)(relay_client_port
);
1813 /* Add the relay-msg carrying the packet. */
1814 if (!save_option_buffer(&dhcpv6_universe
, opts
,
1815 NULL
, (unsigned char *) packet
->raw
,
1816 packet
->packet_length
,
1817 D6O_RELAY_MSG
, 0)) {
1818 log_error("Can't save relay-msg.");
1819 option_state_dereference(&opts
, MDL
);
1823 /* Finish the relay-forward message. */
1824 cursor
+= store_options6(forw_data
+ cursor
,
1825 sizeof(forw_data
) - cursor
,
1827 required_forw_opts
, NULL
);
1828 option_state_dereference(&opts
, MDL
);
1830 /* Send it to all upstreams. */
1831 for (up
= upstreams
; up
; up
= up
->next
) {
1832 send_packet6(up
->ifp
, (unsigned char *) forw_data
,
1833 (size_t) cursor
, &up
->link
);
1838 * Process a packet downwards, i.e., from server to client.
1841 process_down6(struct packet
*packet
) {
1842 struct stream_list
*dp
;
1843 struct option_cache
*oc
;
1844 struct data_string relay_msg
;
1845 const struct dhcpv6_packet
*msg
;
1846 struct data_string if_id
;
1847 #if defined(RELAY_PORT)
1848 struct data_string down_port
;
1850 struct sockaddr_in6 to
;
1853 /* The packet must be a relay-reply message. */
1854 if (packet
->dhcpv6_msg_type
!= DHCPV6_RELAY_REPL
) {
1855 if (packet
->dhcpv6_msg_type
< dhcpv6_type_name_max
)
1856 log_info("Discarding %s from %s port %d going down.",
1857 dhcpv6_type_names
[packet
->dhcpv6_msg_type
],
1858 piaddr(packet
->client_addr
),
1859 ntohs(packet
->client_port
));
1861 log_info("Unknown %d type from %s port %d going down.",
1862 packet
->dhcpv6_msg_type
,
1863 piaddr(packet
->client_addr
),
1864 ntohs(packet
->client_port
));
1869 memset(&relay_msg
, 0, sizeof(relay_msg
));
1870 memset(&if_id
, 0, sizeof(if_id
));
1871 #if defined(RELAY_PORT)
1872 memset(&down_port
, 0, sizeof(down_port
));
1874 memset(&to
, 0, sizeof(to
));
1875 to
.sin6_family
= AF_INET6
;
1877 to
.sin6_len
= sizeof(to
);
1879 to
.sin6_port
= remote_port
;
1882 /* Get the relay-msg option (carrying the message to relay). */
1883 oc
= lookup_option(&dhcpv6_universe
, packet
->options
, D6O_RELAY_MSG
);
1885 log_info("No relay-msg.");
1888 if (!evaluate_option_cache(&relay_msg
, packet
, NULL
, NULL
,
1889 packet
->options
, NULL
,
1890 &global_scope
, oc
, MDL
) ||
1891 (relay_msg
.len
< offsetof(struct dhcpv6_packet
, options
))) {
1892 log_error("Can't evaluate relay-msg.");
1895 msg
= (const struct dhcpv6_packet
*) relay_msg
.data
;
1897 /* Get the interface-id (if exists) and the downstream. */
1898 oc
= lookup_option(&dhcpv6_universe
, packet
->options
,
1903 if (!evaluate_option_cache(&if_id
, packet
, NULL
, NULL
,
1904 packet
->options
, NULL
,
1905 &global_scope
, oc
, MDL
) ||
1906 (if_id
.len
!= sizeof(int))) {
1907 log_info("Can't evaluate interface-id.");
1910 memcpy(&if_index
, if_id
.data
, sizeof(int));
1911 for (dp
= downstreams
; dp
; dp
= dp
->next
) {
1912 if (dp
->id
== if_index
)
1917 /* Require an interface-id. */
1918 log_info("No interface-id.");
1921 for (dp
= downstreams
; dp
; dp
= dp
->next
) {
1922 /* Get the first matching one. */
1923 if (!memcmp(&dp
->link
.sin6_addr
,
1924 &packet
->dhcpv6_link_address
,
1925 sizeof(struct in6_addr
)))
1929 /* Why bother when there is no choice. */
1930 if (!dp
&& downstreams
&& !downstreams
->next
)
1933 log_info("Can't find the down interface.");
1936 memcpy(peer
.iabuf
, &packet
->dhcpv6_peer_address
, peer
.len
);
1937 to
.sin6_addr
= packet
->dhcpv6_peer_address
;
1939 /* Check if we should relay the carried message. */
1940 switch (msg
->msg_type
) {
1941 /* Relay-Reply of for another relay, not a client. */
1942 case DHCPV6_RELAY_REPL
:
1943 to
.sin6_port
= local_port
;
1945 #if defined(RELAY_PORT)
1946 oc
= lookup_option(&dhcpv6_universe
, packet
->options
,
1947 D6O_RELAY_SOURCE_PORT
);
1949 u_int16_t down_relay_port
;
1951 memset(&down_port
, 0, sizeof(down_port
));
1952 if (!evaluate_option_cache(&down_port
, packet
, NULL
,
1953 NULL
, packet
->options
, NULL
,
1954 &global_scope
, oc
, MDL
) ||
1955 (down_port
.len
!= sizeof(u_int16_t
))) {
1956 log_info("Can't evaluate down "
1957 "relay-source-port.");
1960 memcpy(&down_relay_port
, down_port
.data
,
1963 * If the down_relay_port value is non-zero,
1964 * that means our downstream relay agent uses
1965 * a non-547 UDP source port sending
1966 * relay-forw message to us. We need to use
1967 * the same UDP port sending reply back.
1969 if (down_relay_port
) {
1970 to
.sin6_port
= down_relay_port
;
1977 case DHCPV6_ADVERTISE
:
1979 case DHCPV6_RECONFIGURE
:
1980 case DHCPV6_RELAY_FORW
:
1981 case DHCPV6_LEASEQUERY_REPLY
:
1982 case DHCPV6_DHCPV4_RESPONSE
:
1983 log_info("Relaying %s to %s port %d down.",
1984 dhcpv6_type_names
[msg
->msg_type
],
1986 ntohs(to
.sin6_port
));
1989 case DHCPV6_SOLICIT
:
1990 case DHCPV6_REQUEST
:
1991 case DHCPV6_CONFIRM
:
1994 case DHCPV6_RELEASE
:
1995 case DHCPV6_DECLINE
:
1996 case DHCPV6_INFORMATION_REQUEST
:
1997 case DHCPV6_LEASEQUERY
:
1998 case DHCPV6_DHCPV4_QUERY
:
1999 log_info("Discarding %s to %s port %d down.",
2000 dhcpv6_type_names
[msg
->msg_type
],
2002 ntohs(to
.sin6_port
));
2006 log_info("Unknown %d type to %s port %d down.",
2009 ntohs(to
.sin6_port
));
2013 /* Send the message to the downstream. */
2014 send_packet6(dp
->ifp
, (unsigned char *) relay_msg
.data
,
2015 (size_t) relay_msg
.len
, &to
);
2018 if (relay_msg
.data
!= NULL
)
2019 data_string_forget(&relay_msg
, MDL
);
2020 if (if_id
.data
!= NULL
)
2021 data_string_forget(&if_id
, MDL
);
2025 * Called by the dispatch packet handler with a decoded packet.
2028 dhcpv6(struct packet
*packet
) {
2029 struct stream_list
*dp
;
2031 /* Try all relay-replies downwards. */
2032 if (packet
->dhcpv6_msg_type
== DHCPV6_RELAY_REPL
) {
2033 process_down6(packet
);
2036 /* Others are candidates to go up if they come from down. */
2037 for (dp
= downstreams
; dp
; dp
= dp
->next
) {
2038 if (packet
->interface
!= dp
->ifp
)
2040 process_up6(packet
, dp
);
2043 /* Relay-forward could work from an unknown interface. */
2044 if (packet
->dhcpv6_msg_type
== DHCPV6_RELAY_FORW
) {
2045 process_up6(packet
, NULL
);
2049 log_info("Can't process packet from interface '%s'.",
2050 packet
->interface
->name
);
2054 /* Stub routines needed for linking with DHCP libraries. */
2056 bootp(struct packet
*packet
) {
2061 dhcp(struct packet
*packet
) {
2065 #if defined(DHCPv6) && defined(DHCP4o6)
2066 isc_result_t
dhcpv4o6_handler(omapi_object_t
*h
)
2068 return ISC_R_NOTIMPLEMENTED
;
2073 classify(struct packet
*p
, struct class *c
) {
2078 check_collection(struct packet
*p
, struct lease
*l
, struct collection
*c
) {
2083 find_class(struct class **class, const char *c1
, const char *c2
, int i
) {
2084 return ISC_R_NOTFOUND
;
2088 parse_allow_deny(struct option_cache
**oc
, struct parse
*p
, int i
) {
2093 dhcp_set_control_state(control_object_state_t oldstate
,
2094 control_object_state_t newstate
) {
2097 if (newstate
!= server_shutdown
)
2098 return ISC_R_SUCCESS
;
2100 /* Log shutdown on signal. */
2101 log_info("Received signal %d, initiating shutdown.", shutdown_signal
);
2103 if (no_pid_file
== ISC_FALSE
)
2104 (void) unlink(path_dhcrelay_pid
);
2106 if (!no_daemon
&& dfd
[0] != -1 && dfd
[1] != -1) {
2107 IGNORE_RET(write(dfd
[1], &buf
, 1));
2108 (void) close(dfd
[1]);
2109 dfd
[0] = dfd
[1] = -1;
2116 * \brief Allocate an interface as requested with a given set of flags
2118 * The requested interface is allocated, its flags field is set to
2119 * INTERFACE_REQUESTED OR'd with the given flags, and then added to
2120 * the list of interfaces.
2122 * \param name - name of the requested interface
2123 * \param flags - additional flags for the interface
2127 void request_v4_interface(const char* name
, int flags
) {
2128 struct interface_info
*tmp
= NULL
;
2129 int len
= strlen(name
);
2130 isc_result_t status
;
2132 if (len
>= sizeof(tmp
->name
)) {
2133 log_fatal("%s: interface name too long (is %d)", name
, len
);
2136 status
= interface_allocate(&tmp
, MDL
);
2137 if (status
!= ISC_R_SUCCESS
) {
2138 log_fatal("%s: interface_allocate: %s", name
,
2139 isc_result_totext(status
));
2142 log_debug("Requesting: %s as upstream: %c downstream: %c", name
,
2143 (flags
& INTERFACE_UPSTREAM
? 'Y' : 'N'),
2144 (flags
& INTERFACE_DOWNSTREAM
? 'Y' : 'N'));
2146 memcpy(tmp
->name
, name
, len
);
2147 interface_snorf(tmp
, (INTERFACE_REQUESTED
| flags
));
2148 interface_dereference(&tmp
, MDL
);
2150 #endif /* UNIT_TEST */