3 DHCP/BOOTP Relay Agent. */
6 * Copyright(c) 2004-2016 by Internet Systems Consortium, Inc.("ISC")
7 * Copyright(c) 1997-2003 by Internet Software Consortium
9 * Permission to use, copy, modify, and distribute this software for any
10 * purpose with or without fee is hereby granted, provided that the above
11 * copyright notice and this permission notice appear in all copies.
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 /* Force use of DHCPv6 interface-id option. */
82 isc_boolean_t use_if_id
= ISC_FALSE
;
85 /* Maximum size of a packet with agent options added. */
86 int dhcp_max_agent_option_packet_length
= DHCP_MTU_MIN
;
88 /* What to do about packets we're asked to relay that
89 already have a relay option: */
90 enum { forward_and_append
, /* Forward and append our own relay option. */
91 forward_and_replace
, /* Forward, but replace theirs with ours. */
92 forward_untouched
, /* Forward without changes. */
93 discard
} agent_relay_mode
= forward_and_replace
;
96 u_int16_t remote_port
;
98 /* Relay agent server list. */
100 struct server_list
*next
;
101 struct sockaddr_in to
;
104 struct interface_info
*uplink
= NULL
;
108 struct stream_list
*next
;
109 struct interface_info
*ifp
;
110 struct sockaddr_in6 link
;
112 } *downstreams
, *upstreams
;
114 static struct stream_list
*parse_downstream(char *);
115 static struct stream_list
*parse_upstream(char *);
116 static void setup_streams(void);
119 * A pointer to a subscriber id to add to the message we forward.
120 * This is primarily for testing purposes as we only have one id
121 * for the entire relay and don't determine one per client which
122 * would be more useful.
124 char *dhcrelay_sub_id
= NULL
;
127 static void do_relay4(struct interface_info
*, struct dhcp_packet
*,
128 unsigned int, unsigned int, struct iaddr
,
130 static int add_relay_agent_options(struct interface_info
*,
131 struct dhcp_packet
*, unsigned,
133 static int find_interface_by_agent_option(struct dhcp_packet
*,
134 struct interface_info
**, u_int8_t
*, int);
135 static int strip_relay_agent_options(struct interface_info
*,
136 struct interface_info
**,
137 struct dhcp_packet
*, unsigned);
139 static const char copyright
[] =
140 "Copyright 2004-2015 Internet Systems Consortium.";
141 static const char arr
[] = "All rights reserved.";
142 static const char message
[] =
143 "Internet Systems Consortium DHCP Relay Agent";
144 static const char url
[] =
145 "For info, please visit https://www.isc.org/software/dhcp/";
150 #define DHCRELAY_USAGE \
151 "Usage: %s [-4] [-d] [-q] [-a] [-D]\n"\
152 " [-A <length>] [-c <hops>] [-p <port>]\n" \
153 " [-pf <pid-file>] [--no-pid]\n"\
154 " [-m append|replace|forward|discard]\n" \
155 " [-i interface0 [ ... -i interfaceN]\n" \
156 " [-u interface]\n" \
157 " server0 [ ... serverN]\n\n" \
158 " %s -6 [-d] [-q] [-I] [-c <hops>] [-p <port>]\n" \
159 " [-pf <pid-file>] [--no-pid]\n" \
160 " [-s <subscriber-id>]\n" \
161 " -l lower0 [ ... -l lowerN]\n" \
162 " -u upper0 [ ... -u upperN]\n" \
163 " lower (client link): [address%%]interface[#index]\n" \
164 " upper (server link): [address%%]interface"
166 #define DHCRELAY_USAGE \
167 "Usage: %s [-d] [-q] [-a] [-D] [-A <length>] [-c <hops>] [-p <port>]\n" \
168 " [-pf <pid-file>] [--no-pid]\n" \
169 " [-m append|replace|forward|discard]\n" \
170 " [-i interface0 [ ... -i interfaceN]\n" \
171 " [-u interface]\n" \
172 " server0 [ ... serverN]\n\n"
177 * \brief Print the generic usage message
179 * If the user has provided an incorrect command line print out
180 * the description of the command line. The arguments provide
181 * a way for the caller to request more specific information about
182 * the error be printed as well. Mostly this will be that some
183 * comamnd doesn't include its argument.
185 * \param sfmt - The basic string and format for the specific error
186 * \param sarg - Generally the offending argument from the comamnd line.
190 static const char use_noarg
[] = "No argument for command: %s";
192 static const char use_badproto
[] = "Protocol already set, %s inappropriate";
193 static const char use_v4command
[] = "Command not used for DHCPv6: %s";
194 static const char use_v6command
[] = "Command not used for DHCPv4: %s";
198 usage(const char *sfmt
, const char *sarg
) {
200 /* If desired print out the specific error message */
201 #ifdef PRINT_SPECIFIC_CL_ERRORS
203 log_error(sfmt
, sarg
);
206 log_fatal(DHCRELAY_USAGE
,
208 isc_file_basename(progname
),
210 isc_file_basename(progname
));
214 main(int argc
, char **argv
) {
217 struct server_list
*sp
= NULL
;
218 struct interface_info
*tmp
= NULL
;
219 char *service_local
= NULL
, *service_remote
= NULL
;
220 u_int16_t port_local
= 0, port_remote
= 0;
221 int no_daemon
= 0, quiet
= 0;
225 struct stream_list
*sl
= NULL
;
226 int local_family_set
= 0;
230 progname
= "dhcrelay";
235 /* Make sure that file descriptors 0(stdin), 1,(stdout), and
236 2(stderr) are open. To do this, we assume that when we
237 open a file the lowest available file descriptor is used. */
238 fd
= open("/dev/null", O_RDWR
);
240 fd
= open("/dev/null", O_RDWR
);
242 fd
= open("/dev/null", O_RDWR
);
244 log_perror
= 0; /* No sense logging to /dev/null. */
248 openlog(isc_file_basename(progname
), DHCP_LOG_OPTIONS
, LOG_DAEMON
);
251 setlogmask(LOG_UPTO(LOG_INFO
));
254 /* Set up the isc and dns library managers */
255 status
= dhcp_context_create(DHCP_CONTEXT_PRE_DB
| DHCP_CONTEXT_POST_DB
,
257 if (status
!= ISC_R_SUCCESS
)
258 log_fatal("Can't initialize context: %s",
259 isc_result_totext(status
));
261 /* Set up the OMAPI. */
262 status
= omapi_init();
263 if (status
!= ISC_R_SUCCESS
)
264 log_fatal("Can't initialize OMAPI: %s",
265 isc_result_totext(status
));
267 /* Set up the OMAPI wrappers for the interface object. */
270 for (i
= 1; i
< argc
; i
++) {
271 if (!strcmp(argv
[i
], "-4")) {
273 if (local_family_set
&& (local_family
== AF_INET6
)) {
274 usage(use_badproto
, "-4");
276 local_family_set
= 1;
277 local_family
= AF_INET
;
278 } else if (!strcmp(argv
[i
], "-6")) {
279 if (local_family_set
&& (local_family
== AF_INET
)) {
280 usage(use_badproto
, "-6");
282 local_family_set
= 1;
283 local_family
= AF_INET6
;
285 } else if (!strcmp(argv
[i
], "-d")) {
287 } else if (!strcmp(argv
[i
], "-q")) {
289 quiet_interface_discovery
= 1;
290 } else if (!strcmp(argv
[i
], "-p")) {
292 usage(use_noarg
, argv
[i
-1]);
293 local_port
= validate_port(argv
[i
]);
294 log_debug("binding to user-specified port %d",
296 } else if (!strcmp(argv
[i
], "-c")) {
299 usage(use_noarg
, argv
[i
-1]);
300 hcount
= atoi(argv
[i
]);
302 max_hop_count
= hcount
;
304 usage("Bad hop count to -c: %s", argv
[i
]);
305 } else if (!strcmp(argv
[i
], "-i")) {
307 if (local_family_set
&& (local_family
== AF_INET6
)) {
308 usage(use_v4command
, argv
[i
]);
310 local_family_set
= 1;
311 local_family
= AF_INET
;
314 usage(use_noarg
, argv
[i
-1]);
316 if (strlen(argv
[i
]) >= sizeof(tmp
->name
)) {
317 log_fatal("%s: interface name too long "
319 argv
[i
], (long)strlen(argv
[i
]));
321 status
= interface_allocate(&tmp
, MDL
);
322 if (status
!= ISC_R_SUCCESS
) {
323 log_fatal("%s: interface_allocate: %s",
325 isc_result_totext(status
));
327 strcpy(tmp
->name
, argv
[i
]);
328 interface_snorf(tmp
, INTERFACE_REQUESTED
);
329 interface_dereference(&tmp
, MDL
);
330 } else if (!strcmp(argv
[i
], "-a")) {
332 if (local_family_set
&& (local_family
== AF_INET6
)) {
333 usage(use_v4command
, argv
[i
]);
335 local_family_set
= 1;
336 local_family
= AF_INET
;
338 add_agent_options
= 1;
339 } else if (!strcmp(argv
[i
], "-A")) {
341 if (local_family_set
&& (local_family
== AF_INET6
)) {
342 usage(use_v4command
, argv
[i
]);
344 local_family_set
= 1;
345 local_family
= AF_INET
;
348 usage(use_noarg
, argv
[i
-1]);
350 dhcp_max_agent_option_packet_length
= atoi(argv
[i
]);
352 if (dhcp_max_agent_option_packet_length
> DHCP_MTU_MAX
)
353 log_fatal("%s: packet length exceeds "
354 "longest possible MTU\n",
356 } else if (!strcmp(argv
[i
], "-m")) {
358 if (local_family_set
&& (local_family
== AF_INET6
)) {
359 usage(use_v4command
, argv
[i
]);
361 local_family_set
= 1;
362 local_family
= AF_INET
;
365 usage(use_noarg
, argv
[i
-1]);
366 if (!strcasecmp(argv
[i
], "append")) {
367 agent_relay_mode
= forward_and_append
;
368 } else if (!strcasecmp(argv
[i
], "replace")) {
369 agent_relay_mode
= forward_and_replace
;
370 } else if (!strcasecmp(argv
[i
], "forward")) {
371 agent_relay_mode
= forward_untouched
;
372 } else if (!strcasecmp(argv
[i
], "discard")) {
373 agent_relay_mode
= discard
;
375 usage("Unknown argument to -m: %s", argv
[i
]);
376 } else if (!strcmp(argv
[i
], "-u")) {
378 usage(use_noarg
, argv
[i
-1]);
381 usage("more than one uplink (-u) specified: %s"
385 /* Allocate the uplink interface */
386 status
= interface_allocate(&uplink
, MDL
);
387 if (status
!= ISC_R_SUCCESS
) {
388 log_fatal("%s: uplink interface_allocate: %s",
389 argv
[i
], isc_result_totext(status
));
392 if (strlen(argv
[i
]) >= sizeof(uplink
->name
)) {
393 log_fatal("%s: uplink name too long,"
394 " it cannot exceed: %ld characters",
395 argv
[i
], (long)(sizeof(uplink
->name
) - 1));
398 uplink
->name
[sizeof(uplink
->name
) - 1] = 0x00;
399 strncpy(uplink
->name
, argv
[i
],
400 sizeof(uplink
->name
) - 1);
401 interface_snorf(uplink
, INTERFACE_REQUESTED
);
403 /* Turn on -a, in case they don't do so explicitly */
404 add_agent_options
= 1;
405 add_rfc3527_suboption
= 1;
406 } else if (!strcmp(argv
[i
], "-D")) {
408 if (local_family_set
&& (local_family
== AF_INET6
)) {
409 usage(use_v4command
, argv
[i
]);
411 local_family_set
= 1;
412 local_family
= AF_INET
;
414 drop_agent_mismatches
= 1;
416 } else if (!strcmp(argv
[i
], "-I")) {
417 if (local_family_set
&& (local_family
== AF_INET
)) {
418 usage(use_v6command
, argv
[i
]);
420 local_family_set
= 1;
421 local_family
= AF_INET6
;
422 use_if_id
= ISC_TRUE
;
423 } else if (!strcmp(argv
[i
], "-l")) {
424 if (local_family_set
&& (local_family
== AF_INET
)) {
425 usage(use_v6command
, argv
[i
]);
427 local_family_set
= 1;
428 local_family
= AF_INET6
;
429 if (downstreams
!= NULL
)
430 use_if_id
= ISC_TRUE
;
432 usage(use_noarg
, argv
[i
-1]);
433 sl
= parse_downstream(argv
[i
]);
434 sl
->next
= downstreams
;
436 } else if (!strcmp(argv
[i
], "-u")) {
437 if (local_family_set
&& (local_family
== AF_INET
)) {
438 usage(use_v6command
, argv
[i
]);
440 local_family_set
= 1;
441 local_family
= AF_INET6
;
443 usage(use_noarg
, argv
[i
-1]);
444 sl
= parse_upstream(argv
[i
]);
445 sl
->next
= upstreams
;
447 } else if (!strcmp(argv
[i
], "-s")) {
448 if (local_family_set
&& (local_family
== AF_INET
)) {
449 usage(use_v6command
, argv
[i
]);
451 local_family_set
= 1;
452 local_family
= AF_INET6
;
454 usage(use_noarg
, argv
[i
-1]);
455 dhcrelay_sub_id
= argv
[i
];
457 } else if (!strcmp(argv
[i
], "-pf")) {
459 usage(use_noarg
, argv
[i
-1]);
460 path_dhcrelay_pid
= argv
[i
];
461 no_dhcrelay_pid
= ISC_TRUE
;
462 } else if (!strcmp(argv
[i
], "--no-pid")) {
463 no_pid_file
= ISC_TRUE
;
464 } else if (!strcmp(argv
[i
], "--version")) {
465 log_info("isc-dhcrelay-%s", PACKAGE_VERSION
);
467 } else if (!strcmp(argv
[i
], "--help") ||
468 !strcmp(argv
[i
], "-h")) {
469 log_info(DHCRELAY_USAGE
,
471 isc_file_basename(progname
),
473 isc_file_basename(progname
));
475 } else if (argv
[i
][0] == '-') {
476 usage("Unknown command: %s", argv
[i
]);
479 struct in_addr ia
, *iap
= NULL
;
482 if (local_family_set
&& (local_family
== AF_INET6
)) {
483 usage(use_v4command
, argv
[i
]);
485 local_family_set
= 1;
486 local_family
= AF_INET
;
488 if (inet_aton(argv
[i
], &ia
)) {
491 he
= gethostbyname(argv
[i
]);
493 log_error("%s: host unknown", argv
[i
]);
495 iap
= ((struct in_addr
*)
501 sp
= ((struct server_list
*)
502 dmalloc(sizeof *sp
, MDL
));
504 log_fatal("no memory for server.\n");
507 memcpy(&sp
->to
.sin_addr
, iap
, sizeof *iap
);
513 * If the user didn't specify a pid file directly
514 * find one from environment variables or defaults
516 if (no_dhcrelay_pid
== ISC_FALSE
) {
517 if (local_family
== AF_INET
) {
518 path_dhcrelay_pid
= getenv("PATH_DHCRELAY_PID");
519 if (path_dhcrelay_pid
== NULL
)
520 path_dhcrelay_pid
= _PATH_DHCRELAY_PID
;
524 path_dhcrelay_pid
= getenv("PATH_DHCRELAY6_PID");
525 if (path_dhcrelay_pid
== NULL
)
526 path_dhcrelay_pid
= _PATH_DHCRELAY6_PID
;
532 log_info("%s %s", message
, PACKAGE_VERSION
);
539 /* Set default port */
540 if (local_family
== AF_INET
) {
541 service_local
= "bootps";
542 service_remote
= "bootpc";
543 port_local
= htons(67);
544 port_remote
= htons(68);
548 service_local
= "dhcpv6-server";
549 service_remote
= "dhcpv6-client";
550 port_local
= htons(547);
551 port_remote
= htons(546);
556 ent
= getservbyname(service_local
, "udp");
558 local_port
= ent
->s_port
;
560 local_port
= port_local
;
562 ent
= getservbyname(service_remote
, "udp");
564 remote_port
= ent
->s_port
;
566 remote_port
= port_remote
;
571 if (local_family
== AF_INET
) {
572 /* We need at least one server */
573 if (servers
== NULL
) {
574 log_fatal("No servers specified.");
578 /* Set up the server sockaddrs. */
579 for (sp
= servers
; sp
; sp
= sp
->next
) {
580 sp
->to
.sin_port
= local_port
;
581 sp
->to
.sin_family
= AF_INET
;
583 sp
->to
.sin_len
= sizeof sp
->to
;
591 /* We need at least one upstream and one downstream interface */
592 if (upstreams
== NULL
|| downstreams
== NULL
) {
593 log_info("Must specify at least one lower "
594 "and one upper interface.\n");
598 /* Set up the initial dhcp option universe. */
599 initialize_common_option_spaces();
601 /* Check requested options. */
602 code
= D6O_RELAY_MSG
;
603 if (!option_code_hash_lookup(&requested_opts
[0],
604 dhcpv6_universe
.code_hash
,
606 log_fatal("Unable to find the RELAY_MSG "
607 "option definition.");
608 code
= D6O_INTERFACE_ID
;
609 if (!option_code_hash_lookup(&requested_opts
[1],
610 dhcpv6_universe
.code_hash
,
612 log_fatal("Unable to find the INTERFACE_ID "
613 "option definition.");
617 /* Get the current time... */
618 gettimeofday(&cur_tv
, NULL
);
620 /* Discover all the network interfaces. */
621 discover_interfaces(DISCOVER_RELAY
);
624 if (local_family
== AF_INET6
)
628 /* Become a daemon... */
636 if ((pid
= fork()) < 0)
637 log_fatal("Can't fork daemon: %m");
641 if (no_pid_file
== ISC_FALSE
) {
642 pfdesc
= open(path_dhcrelay_pid
,
643 O_CREAT
| O_TRUNC
| O_WRONLY
, 0644);
646 log_error("Can't create %s: %m",
649 pf
= fdopen(pfdesc
, "w");
651 log_error("Can't fdopen %s: %m",
654 fprintf(pf
, "%ld\n",(long)getpid());
665 IGNORE_RET (chdir("/"));
668 /* Set up the packet handler... */
669 if (local_family
== AF_INET
)
670 bootp_packet_handler
= do_relay4
;
673 dhcpv6_packet_handler
= do_packet6
;
676 #if defined(ENABLE_GENTLE_SHUTDOWN)
677 /* no signal handlers until we deal with the side effects */
678 /* install signal handlers */
679 signal(SIGINT
, dhcp_signal_handler
); /* control-c */
680 signal(SIGTERM
, dhcp_signal_handler
); /* kill */
683 /* Start dispatching packets and timeouts... */
686 /* In fact dispatch() never returns. */
691 do_relay4(struct interface_info
*ip
, struct dhcp_packet
*packet
,
692 unsigned int length
, unsigned int from_port
, struct iaddr from
,
693 struct hardware
*hfrom
) {
694 struct server_list
*sp
;
695 struct sockaddr_in to
;
696 struct interface_info
*out
;
697 struct hardware hto
, *htop
;
699 if (packet
->hlen
> sizeof packet
->chaddr
) {
700 log_info("Discarding packet with invalid hlen, received on "
701 "%s interface.", ip
->name
);
704 if (ip
->address_count
< 1 || ip
->addresses
== NULL
) {
705 log_info("Discarding packet received on %s interface that "
706 "has no IPv4 address assigned.", ip
->name
);
710 /* Find the interface that corresponds to the giaddr
712 if (packet
->giaddr
.s_addr
) {
713 for (out
= interfaces
; out
; out
= out
->next
) {
716 for (i
= 0 ; i
< out
->address_count
; i
++ ) {
717 if (out
->addresses
[i
].s_addr
==
718 packet
->giaddr
.s_addr
) {
731 /* If it's a bootreply, forward it to the client. */
732 if (packet
->op
== BOOTREPLY
) {
733 if (!(packet
->flags
& htons(BOOTP_BROADCAST
)) &&
734 can_unicast_without_arp(out
)) {
735 to
.sin_addr
= packet
->yiaddr
;
736 to
.sin_port
= remote_port
;
738 /* and hardware address is not broadcast */
741 to
.sin_addr
.s_addr
= htonl(INADDR_BROADCAST
);
742 to
.sin_port
= remote_port
;
744 /* hardware address is broadcast */
747 to
.sin_family
= AF_INET
;
749 to
.sin_len
= sizeof to
;
752 memcpy(&hto
.hbuf
[1], packet
->chaddr
, packet
->hlen
);
753 hto
.hbuf
[0] = packet
->htype
;
754 hto
.hlen
= packet
->hlen
+ 1;
756 /* Wipe out the agent relay options and, if possible, figure
757 out which interface to use based on the contents of the
758 option that we put on the request to which the server is
761 strip_relay_agent_options(ip
, &out
, packet
, length
)))
765 log_error("Packet to bogus giaddr %s.\n",
766 inet_ntoa(packet
->giaddr
));
767 ++bogus_giaddr_drops
;
771 if (send_packet(out
, NULL
, packet
, length
, out
->addresses
[0],
773 ++server_packet_errors
;
775 log_debug("Forwarded BOOTREPLY for %s to %s",
776 print_hw_addr(packet
->htype
, packet
->hlen
,
778 inet_ntoa(to
.sin_addr
));
780 ++server_packets_relayed
;
785 /* If giaddr matches one of our addresses, ignore the packet -
790 /* Add relay agent options if indicated. If something goes wrong,
791 * drop the packet. Note this may set packet->giaddr if RFC3527
793 if (!(length
= add_relay_agent_options(ip
, packet
, length
,
797 /* If giaddr is not already set, Set it so the server can
798 figure out what net it's from and so that we can later
799 forward the response to the correct net. If it's already
800 set, the response will be sent directly to the relay agent
801 that set giaddr, so we won't see it. */
802 if (!packet
->giaddr
.s_addr
)
803 packet
->giaddr
= ip
->addresses
[0];
804 if (packet
->hops
< max_hop_count
)
805 packet
->hops
= packet
->hops
+ 1;
809 /* Otherwise, it's a BOOTREQUEST, so forward it to all the
811 for (sp
= servers
; sp
; sp
= sp
->next
) {
812 if (send_packet((fallback_interface
813 ? fallback_interface
: interfaces
),
814 NULL
, packet
, length
, ip
->addresses
[0],
815 &sp
->to
, NULL
) < 0) {
816 ++client_packet_errors
;
818 log_debug("Forwarded BOOTREQUEST for %s to %s",
819 print_hw_addr(packet
->htype
, packet
->hlen
,
821 inet_ntoa(sp
->to
.sin_addr
));
822 ++client_packets_relayed
;
828 /* Strip any Relay Agent Information options from the DHCP packet
829 option buffer. If there is a circuit ID suboption, look up the
830 outgoing interface based upon it. */
833 strip_relay_agent_options(struct interface_info
*in
,
834 struct interface_info
**out
,
835 struct dhcp_packet
*packet
,
838 u_int8_t
*op
, *nextop
, *sp
, *max
;
839 int good_agent_option
= 0;
842 /* If we're not adding agent options to packets, we're not taking
844 if (!add_agent_options
)
847 /* If there's no cookie, it's a bootp packet, so we should just
848 forward it unchanged. */
849 if (memcmp(packet
->options
, DHCP_OPTIONS_COOKIE
, 4))
852 max
= ((u_int8_t
*)packet
) + length
;
853 sp
= op
= &packet
->options
[4];
857 /* Skip padding... */
865 /* If we see a message type, it's a DHCP packet. */
866 case DHO_DHCP_MESSAGE_TYPE
:
871 /* Quit immediately if we hit an End option. */
877 case DHO_DHCP_AGENT_OPTIONS
:
878 /* We shouldn't see a relay agent option in a
879 packet before we've seen the DHCP packet type,
880 but if we do, we have to leave it alone. */
884 /* Do not process an agent option if it exceeds the
885 * buffer. Fail this packet.
887 nextop
= op
+ op
[1] + 2;
891 status
= find_interface_by_agent_option(packet
,
894 if (status
== -1 && drop_agent_mismatches
)
897 good_agent_option
= 1;
902 /* Skip over other options. */
904 /* Fail if processing this option will exceed the
905 * buffer(op[1] is malformed).
907 nextop
= op
+ op
[1] + 2;
912 memmove(sp
, op
, op
[1] + 2);
923 /* If it's not a DHCP packet, we're not supposed to touch it. */
927 /* If none of the agent options we found matched, or if we didn't
928 find any agent options, count this packet as not having any
929 matching agent options, and if we're relying on agent options
930 to determine the outgoing interface, drop the packet. */
932 if (!good_agent_option
) {
933 ++missing_agent_option
;
934 if (drop_agent_mismatches
)
938 /* Adjust the length... */
940 length
= sp
-((u_int8_t
*)packet
);
942 /* Make sure the packet isn't short(this is unlikely,
944 if (length
< BOOTP_MIN_LEN
) {
945 memset(sp
, DHO_PAD
, BOOTP_MIN_LEN
- length
);
946 length
= BOOTP_MIN_LEN
;
953 /* Find an interface that matches the circuit ID specified in the
954 Relay Agent Information option. If one is found, store it through
955 the pointer given; otherwise, leave the existing pointer alone.
957 We actually deviate somewhat from the current specification here:
958 if the option buffer is corrupt, we suggest that the caller not
959 respond to this packet. If the circuit ID doesn't match any known
960 interface, we suggest that the caller to drop the packet. Only if
961 we find a circuit ID that matches an existing interface do we tell
962 the caller to go ahead and process the packet. */
965 find_interface_by_agent_option(struct dhcp_packet
*packet
,
966 struct interface_info
**out
,
967 u_int8_t
*buf
, int len
) {
969 u_int8_t
*circuit_id
= 0;
970 unsigned circuit_id_len
= 0;
971 struct interface_info
*ip
;
974 /* If the next agent option overflows the end of the
975 packet, the agent option buffer is corrupt. */
977 i
+ buf
[i
+ 1] + 2 > len
) {
978 ++corrupt_agent_options
;
982 /* Remember where the circuit ID is... */
984 circuit_id
= &buf
[i
+ 2];
985 circuit_id_len
= buf
[i
+ 1];
986 i
+= circuit_id_len
+ 2;
995 /* If there's no circuit ID, it's not really ours, tell the caller
998 ++missing_circuit_id
;
1002 /* Scan the interface list looking for an interface whose
1003 name matches the one specified in circuit_id. */
1005 for (ip
= interfaces
; ip
; ip
= ip
->next
) {
1006 if (ip
->circuit_id
&&
1007 ip
->circuit_id_len
== circuit_id_len
&&
1008 !memcmp(ip
->circuit_id
, circuit_id
, circuit_id_len
))
1012 /* If we got a match, use it. */
1018 /* If we didn't get a match, the circuit ID was bogus. */
1024 * Examine a packet to see if it's a candidate to have a Relay
1025 * Agent Information option tacked onto its tail. If it is, tack
1029 add_relay_agent_options(struct interface_info
*ip
, struct dhcp_packet
*packet
,
1030 unsigned length
, struct in_addr giaddr
) {
1031 int is_dhcp
= 0, mms
;
1033 u_int8_t
*op
, *nextop
, *sp
, *max
, *end_pad
= NULL
;
1034 int adding_link_select
;
1036 /* If we're not adding agent options to packets, we can skip
1038 if (!add_agent_options
)
1041 /* If there's no cookie, it's a bootp packet, so we should just
1042 forward it unchanged. */
1043 if (memcmp(packet
->options
, DHCP_OPTIONS_COOKIE
, 4))
1046 max
= ((u_int8_t
*)packet
) + dhcp_max_agent_option_packet_length
;
1048 /* Add link selection suboption if enabled and we're the first relay */
1049 adding_link_select
= (add_rfc3527_suboption
1050 && (packet
->giaddr
.s_addr
== 0));
1052 /* Commence processing after the cookie. */
1053 sp
= op
= &packet
->options
[4];
1057 /* Skip padding... */
1059 /* Remember the first pad byte so we can commandeer
1062 * XXX: Is this really a good idea? Sure, we can
1063 * seemingly reduce the packet while we're looking,
1064 * but if the packet was signed by the client then
1065 * this padding is part of the checksum(RFC3118),
1066 * and its nonpresence would break authentication.
1068 if (end_pad
== NULL
)
1078 /* If we see a message type, it's a DHCP packet. */
1079 case DHO_DHCP_MESSAGE_TYPE
:
1084 * If there's a maximum message size option, we
1085 * should pay attention to it
1087 case DHO_DHCP_MAX_MESSAGE_SIZE
:
1088 mms
= ntohs(*(op
+ 2));
1089 if (mms
< dhcp_max_agent_option_packet_length
&&
1090 mms
>= DHCP_MTU_MIN
)
1091 max
= ((u_int8_t
*)packet
) + mms
;
1094 /* Quit immediately if we hit an End option. */
1098 case DHO_DHCP_AGENT_OPTIONS
:
1099 /* We shouldn't see a relay agent option in a
1100 packet before we've seen the DHCP packet type,
1101 but if we do, we have to leave it alone. */
1107 /* There's already a Relay Agent Information option
1108 in this packet. How embarrassing. Decide what
1109 to do based on the mode the user specified. */
1111 switch(agent_relay_mode
) {
1112 case forward_and_append
:
1114 case forward_untouched
:
1118 case forward_and_replace
:
1123 /* Skip over the agent option and start copying
1124 if we aren't copying already. */
1129 /* Skip over other options. */
1131 /* Fail if processing this option will exceed the
1132 * buffer(op[1] is malformed).
1134 nextop
= op
+ op
[1] + 2;
1141 memmove(sp
, op
, op
[1] + 2);
1152 /* If it's not a DHCP packet, we're not supposed to touch it. */
1156 /* If the packet was padded out, we can store the agent option
1157 at the beginning of the padding. */
1159 if (end_pad
!= NULL
)
1163 /* Remember where the end of the packet was after parsing
1168 /* Sanity check. Had better not ever happen. */
1169 if ((ip
->circuit_id_len
> 255) ||(ip
->circuit_id_len
< 1))
1170 log_fatal("Circuit ID length %d out of range [1-255] on "
1171 "%s\n", ip
->circuit_id_len
, ip
->name
);
1172 optlen
= ip
->circuit_id_len
+ 2; /* RAI_CIRCUIT_ID + len */
1174 if (ip
->remote_id
) {
1175 if (ip
->remote_id_len
> 255 || ip
->remote_id_len
< 1)
1176 log_fatal("Remote ID length %d out of range [1-255] "
1177 "on %s\n", ip
->circuit_id_len
, ip
->name
);
1178 optlen
+= ip
->remote_id_len
+ 2; /* RAI_REMOTE_ID + len */
1181 if (adding_link_select
) {
1185 /* We do not support relay option fragmenting(multiple options to
1186 * support an option data exceeding 255 bytes).
1188 if ((optlen
< 3) ||(optlen
> 255))
1189 log_fatal("Total agent option length(%u) out of range "
1190 "[3 - 255] on %s\n", optlen
, ip
->name
);
1193 * Is there room for the option, its code+len, and DHO_END?
1194 * If not, forward without adding the option.
1196 if (max
- sp
>= optlen
+ 3) {
1197 log_debug("Adding %d-byte relay agent option", optlen
+ 3);
1199 /* Okay, cons up *our* Relay Agent Information option. */
1200 *sp
++ = DHO_DHCP_AGENT_OPTIONS
;
1203 /* Copy in the circuit id... */
1204 *sp
++ = RAI_CIRCUIT_ID
;
1205 *sp
++ = ip
->circuit_id_len
;
1206 memcpy(sp
, ip
->circuit_id
, ip
->circuit_id_len
);
1207 sp
+= ip
->circuit_id_len
;
1209 /* Copy in remote ID... */
1210 if (ip
->remote_id
) {
1211 *sp
++ = RAI_REMOTE_ID
;
1212 *sp
++ = ip
->remote_id_len
;
1213 memcpy(sp
, ip
->remote_id
, ip
->remote_id_len
);
1214 sp
+= ip
->remote_id_len
;
1217 /* RFC3527: Use the inbound packet's interface address in
1218 * the link selection suboption and set the outbound giaddr
1219 * to the uplink address. */
1220 if (adding_link_select
) {
1221 *sp
++ = RAI_LINK_SELECT
;
1223 memcpy(sp
, &giaddr
.s_addr
, 4);
1225 packet
->giaddr
= uplink
->addresses
[0];
1226 log_debug ("Adding link selection suboption"
1227 " with addr: %s", inet_ntoa(giaddr
));
1230 ++agent_option_errors
;
1231 log_error("No room in packet (used %d of %d) "
1232 "for %d-byte relay agent option: omitted",
1233 (int) (sp
- ((u_int8_t
*) packet
)),
1234 (int) (max
- ((u_int8_t
*) packet
)),
1239 * Deposit an END option unless the packet is full (shouldn't
1245 /* Recalculate total packet length. */
1246 length
= sp
-((u_int8_t
*)packet
);
1248 /* Make sure the packet isn't short(this is unlikely, but WTH) */
1249 if (length
< BOOTP_MIN_LEN
) {
1250 memset(sp
, DHO_PAD
, BOOTP_MIN_LEN
- length
);
1251 return (BOOTP_MIN_LEN
);
1259 * Parse a downstream argument: [address%]interface[#index].
1261 static struct stream_list
*
1262 parse_downstream(char *arg
) {
1263 struct stream_list
*dp
, *up
;
1264 struct interface_info
*ifp
= NULL
;
1265 char *ifname
, *addr
, *iid
;
1266 isc_result_t status
;
1268 if (!supports_multiple_interfaces(ifp
) &&
1269 (downstreams
!= NULL
))
1270 log_fatal("No support for multiple interfaces.");
1272 /* Decode the argument. */
1273 ifname
= strchr(arg
, '%');
1274 if (ifname
== NULL
) {
1281 iid
= strchr(ifname
, '#');
1285 if (strlen(ifname
) >= sizeof(ifp
->name
)) {
1286 usage("Interface name '%s' too long", ifname
);
1289 /* Don't declare twice. */
1290 for (dp
= downstreams
; dp
; dp
= dp
->next
) {
1291 if (strcmp(ifname
, dp
->ifp
->name
) == 0)
1292 log_fatal("Down interface '%s' declared twice.",
1296 /* Share with up side? */
1297 for (up
= upstreams
; up
; up
= up
->next
) {
1298 if (strcmp(ifname
, up
->ifp
->name
) == 0) {
1299 log_info("parse_downstream: Interface '%s' is "
1300 "both down and up.", ifname
);
1306 /* New interface. */
1308 status
= interface_allocate(&ifp
, MDL
);
1309 if (status
!= ISC_R_SUCCESS
)
1310 log_fatal("%s: interface_allocate: %s",
1311 arg
, isc_result_totext(status
));
1312 strcpy(ifp
->name
, ifname
);
1314 interface_reference(&ifp
->next
, interfaces
, MDL
);
1315 interface_dereference(&interfaces
, MDL
);
1317 interface_reference(&interfaces
, ifp
, MDL
);
1319 ifp
->flags
|= INTERFACE_REQUESTED
| INTERFACE_DOWNSTREAM
;
1321 /* New downstream. */
1322 dp
= (struct stream_list
*) dmalloc(sizeof(*dp
), MDL
);
1324 log_fatal("No memory for downstream.");
1331 /* !addr case handled by setup. */
1332 if (addr
&& (inet_pton(AF_INET6
, addr
, &dp
->link
.sin6_addr
) <= 0))
1333 log_fatal("Bad link address '%s'", addr
);
1339 * Parse an upstream argument: [address]%interface.
1341 static struct stream_list
*
1342 parse_upstream(char *arg
) {
1343 struct stream_list
*up
, *dp
;
1344 struct interface_info
*ifp
= NULL
;
1345 char *ifname
, *addr
;
1346 isc_result_t status
;
1348 /* Decode the argument. */
1349 ifname
= strchr(arg
, '%');
1350 if (ifname
== NULL
) {
1352 addr
= All_DHCP_Servers
;
1357 if (strlen(ifname
) >= sizeof(ifp
->name
)) {
1358 log_fatal("Interface name '%s' too long", ifname
);
1361 /* Shared up interface? */
1362 for (up
= upstreams
; up
; up
= up
->next
) {
1363 if (strcmp(ifname
, up
->ifp
->name
) == 0) {
1368 for (dp
= downstreams
; dp
; dp
= dp
->next
) {
1369 if (strcmp(ifname
, dp
->ifp
->name
) == 0) {
1370 log_info("parse_upstream: Interface '%s' is "
1371 "both down and up.", ifname
);
1377 /* New interface. */
1379 status
= interface_allocate(&ifp
, MDL
);
1380 if (status
!= ISC_R_SUCCESS
)
1381 log_fatal("%s: interface_allocate: %s",
1382 arg
, isc_result_totext(status
));
1383 strcpy(ifp
->name
, ifname
);
1385 interface_reference(&ifp
->next
, interfaces
, MDL
);
1386 interface_dereference(&interfaces
, MDL
);
1388 interface_reference(&interfaces
, ifp
, MDL
);
1390 ifp
->flags
|= INTERFACE_REQUESTED
| INTERFACE_UPSTREAM
;
1393 up
= (struct stream_list
*) dmalloc(sizeof(*up
), MDL
);
1395 log_fatal("No memory for upstream.");
1399 if (inet_pton(AF_INET6
, addr
, &up
->link
.sin6_addr
) <= 0)
1400 log_fatal("Bad address %s", addr
);
1406 * Setup downstream interfaces.
1409 setup_streams(void) {
1410 struct stream_list
*dp
, *up
;
1412 isc_boolean_t link_is_set
;
1414 for (dp
= downstreams
; dp
; dp
= dp
->next
) {
1415 /* Check interface */
1416 if (dp
->ifp
->v6address_count
== 0)
1417 log_fatal("Interface '%s' has no IPv6 addresses.",
1420 /* Check/set link. */
1421 if (IN6_IS_ADDR_UNSPECIFIED(&dp
->link
.sin6_addr
))
1422 link_is_set
= ISC_FALSE
;
1424 link_is_set
= ISC_TRUE
;
1425 for (i
= 0; i
< dp
->ifp
->v6address_count
; i
++) {
1426 if (IN6_IS_ADDR_LINKLOCAL(&dp
->ifp
->v6addresses
[i
]))
1430 if (!memcmp(&dp
->ifp
->v6addresses
[i
],
1431 &dp
->link
.sin6_addr
,
1432 sizeof(dp
->link
.sin6_addr
)))
1435 if (i
== dp
->ifp
->v6address_count
)
1436 log_fatal("Interface %s does not have global IPv6 "
1437 "address assigned.", dp
->ifp
->name
);
1439 memcpy(&dp
->link
.sin6_addr
,
1440 &dp
->ifp
->v6addresses
[i
],
1441 sizeof(dp
->link
.sin6_addr
));
1443 /* Set interface-id. */
1445 dp
->id
= dp
->ifp
->index
;
1448 for (up
= upstreams
; up
; up
= up
->next
) {
1449 up
->link
.sin6_port
= local_port
;
1450 up
->link
.sin6_family
= AF_INET6
;
1452 up
->link
.sin6_len
= sizeof(up
->link
);
1455 if (up
->ifp
->v6address_count
== 0)
1456 log_fatal("Interface '%s' has no IPv6 addresses.",
1459 /* RFC 3315 Sec 20 - "If the relay agent relays messages to
1460 * the All_DHCP_Servers address or other multicast addresses,
1461 * it sets the Hop Limit field to 32." */
1462 if (IN6_IS_ADDR_MULTICAST(&up
->link
.sin6_addr
)) {
1463 set_multicast_hop_limit(up
->ifp
, HOP_COUNT_LIMIT
);
1469 * Add DHCPv6 agent options here.
1471 static const int required_forw_opts
[] = {
1479 * Process a packet upwards, i.e., from client to server.
1482 process_up6(struct packet
*packet
, struct stream_list
*dp
) {
1483 char forw_data
[65535];
1485 struct dhcpv6_relay_packet
*relay
;
1486 struct option_state
*opts
;
1487 struct stream_list
*up
;
1489 /* Check if the message should be relayed to the server. */
1490 switch (packet
->dhcpv6_msg_type
) {
1491 case DHCPV6_SOLICIT
:
1492 case DHCPV6_REQUEST
:
1493 case DHCPV6_CONFIRM
:
1496 case DHCPV6_RELEASE
:
1497 case DHCPV6_DECLINE
:
1498 case DHCPV6_INFORMATION_REQUEST
:
1499 case DHCPV6_RELAY_FORW
:
1500 case DHCPV6_LEASEQUERY
:
1501 case DHCPV6_DHCPV4_QUERY
:
1502 log_info("Relaying %s from %s port %d going up.",
1503 dhcpv6_type_names
[packet
->dhcpv6_msg_type
],
1504 piaddr(packet
->client_addr
),
1505 ntohs(packet
->client_port
));
1508 case DHCPV6_ADVERTISE
:
1510 case DHCPV6_RECONFIGURE
:
1511 case DHCPV6_RELAY_REPL
:
1512 case DHCPV6_LEASEQUERY_REPLY
:
1513 case DHCPV6_DHCPV4_RESPONSE
:
1514 log_info("Discarding %s from %s port %d going up.",
1515 dhcpv6_type_names
[packet
->dhcpv6_msg_type
],
1516 piaddr(packet
->client_addr
),
1517 ntohs(packet
->client_port
));
1521 log_info("Unknown %d type from %s port %d going up.",
1522 packet
->dhcpv6_msg_type
,
1523 piaddr(packet
->client_addr
),
1524 ntohs(packet
->client_port
));
1528 /* Build the relay-forward header. */
1529 relay
= (struct dhcpv6_relay_packet
*) forw_data
;
1530 cursor
= offsetof(struct dhcpv6_relay_packet
, options
);
1531 relay
->msg_type
= DHCPV6_RELAY_FORW
;
1532 if (packet
->dhcpv6_msg_type
== DHCPV6_RELAY_FORW
) {
1533 if (packet
->dhcpv6_hop_count
>= max_hop_count
) {
1534 log_info("Hop count exceeded,");
1537 relay
->hop_count
= packet
->dhcpv6_hop_count
+ 1;
1539 memcpy(&relay
->link_address
, &dp
->link
.sin6_addr
, 16);
1541 /* On smart relay add: && !global. */
1542 if (!use_if_id
&& downstreams
->next
) {
1543 log_info("Shan't get back the interface.");
1546 memset(&relay
->link_address
, 0, 16);
1549 relay
->hop_count
= 0;
1552 memcpy(&relay
->link_address
, &dp
->link
.sin6_addr
, 16);
1554 memcpy(&relay
->peer_address
, packet
->client_addr
.iabuf
, 16);
1556 /* Get an option state. */
1558 if (!option_state_allocate(&opts
, MDL
)) {
1559 log_fatal("No memory for upwards options.");
1562 /* Add an interface-id (if used). */
1568 } else if (!downstreams
->next
) {
1569 if_id
= downstreams
->id
;
1571 log_info("Don't know the interface.");
1572 option_state_dereference(&opts
, MDL
);
1576 if (!save_option_buffer(&dhcpv6_universe
, opts
,
1577 NULL
, (unsigned char *) &if_id
,
1579 D6O_INTERFACE_ID
, 0)) {
1580 log_error("Can't save interface-id.");
1581 option_state_dereference(&opts
, MDL
);
1586 /* Add a subscriber-id if desired. */
1587 /* This is for testing rather than general use */
1588 if (dhcrelay_sub_id
!= NULL
) {
1589 if (!save_option_buffer(&dhcpv6_universe
, opts
, NULL
,
1590 (unsigned char *) dhcrelay_sub_id
,
1591 strlen(dhcrelay_sub_id
),
1592 D6O_SUBSCRIBER_ID
, 0)) {
1593 log_error("Can't save subsriber-id.");
1594 option_state_dereference(&opts
, MDL
);
1600 /* Add the relay-msg carrying the packet. */
1601 if (!save_option_buffer(&dhcpv6_universe
, opts
,
1602 NULL
, (unsigned char *) packet
->raw
,
1603 packet
->packet_length
,
1604 D6O_RELAY_MSG
, 0)) {
1605 log_error("Can't save relay-msg.");
1606 option_state_dereference(&opts
, MDL
);
1610 /* Finish the relay-forward message. */
1611 cursor
+= store_options6(forw_data
+ cursor
,
1612 sizeof(forw_data
) - cursor
,
1614 required_forw_opts
, NULL
);
1615 option_state_dereference(&opts
, MDL
);
1617 /* Send it to all upstreams. */
1618 for (up
= upstreams
; up
; up
= up
->next
) {
1619 send_packet6(up
->ifp
, (unsigned char *) forw_data
,
1620 (size_t) cursor
, &up
->link
);
1625 * Process a packet downwards, i.e., from server to client.
1628 process_down6(struct packet
*packet
) {
1629 struct stream_list
*dp
;
1630 struct option_cache
*oc
;
1631 struct data_string relay_msg
;
1632 const struct dhcpv6_packet
*msg
;
1633 struct data_string if_id
;
1634 struct sockaddr_in6 to
;
1637 /* The packet must be a relay-reply message. */
1638 if (packet
->dhcpv6_msg_type
!= DHCPV6_RELAY_REPL
) {
1639 if (packet
->dhcpv6_msg_type
< dhcpv6_type_name_max
)
1640 log_info("Discarding %s from %s port %d going down.",
1641 dhcpv6_type_names
[packet
->dhcpv6_msg_type
],
1642 piaddr(packet
->client_addr
),
1643 ntohs(packet
->client_port
));
1645 log_info("Unknown %d type from %s port %d going down.",
1646 packet
->dhcpv6_msg_type
,
1647 piaddr(packet
->client_addr
),
1648 ntohs(packet
->client_port
));
1653 memset(&relay_msg
, 0, sizeof(relay_msg
));
1654 memset(&if_id
, 0, sizeof(if_id
));
1655 memset(&to
, 0, sizeof(to
));
1656 to
.sin6_family
= AF_INET6
;
1658 to
.sin6_len
= sizeof(to
);
1660 to
.sin6_port
= remote_port
;
1663 /* Get the relay-msg option (carrying the message to relay). */
1664 oc
= lookup_option(&dhcpv6_universe
, packet
->options
, D6O_RELAY_MSG
);
1666 log_info("No relay-msg.");
1669 if (!evaluate_option_cache(&relay_msg
, packet
, NULL
, NULL
,
1670 packet
->options
, NULL
,
1671 &global_scope
, oc
, MDL
) ||
1672 (relay_msg
.len
< offsetof(struct dhcpv6_packet
, options
))) {
1673 log_error("Can't evaluate relay-msg.");
1676 msg
= (const struct dhcpv6_packet
*) relay_msg
.data
;
1678 /* Get the interface-id (if exists) and the downstream. */
1679 oc
= lookup_option(&dhcpv6_universe
, packet
->options
,
1684 if (!evaluate_option_cache(&if_id
, packet
, NULL
, NULL
,
1685 packet
->options
, NULL
,
1686 &global_scope
, oc
, MDL
) ||
1687 (if_id
.len
!= sizeof(int))) {
1688 log_info("Can't evaluate interface-id.");
1691 memcpy(&if_index
, if_id
.data
, sizeof(int));
1692 for (dp
= downstreams
; dp
; dp
= dp
->next
) {
1693 if (dp
->id
== if_index
)
1698 /* Require an interface-id. */
1699 log_info("No interface-id.");
1702 for (dp
= downstreams
; dp
; dp
= dp
->next
) {
1703 /* Get the first matching one. */
1704 if (!memcmp(&dp
->link
.sin6_addr
,
1705 &packet
->dhcpv6_link_address
,
1706 sizeof(struct in6_addr
)))
1710 /* Why bother when there is no choice. */
1711 if (!dp
&& downstreams
&& !downstreams
->next
)
1714 log_info("Can't find the down interface.");
1717 memcpy(peer
.iabuf
, &packet
->dhcpv6_peer_address
, peer
.len
);
1718 to
.sin6_addr
= packet
->dhcpv6_peer_address
;
1720 /* Check if we should relay the carried message. */
1721 switch (msg
->msg_type
) {
1722 /* Relay-Reply of for another relay, not a client. */
1723 case DHCPV6_RELAY_REPL
:
1724 to
.sin6_port
= local_port
;
1727 case DHCPV6_ADVERTISE
:
1729 case DHCPV6_RECONFIGURE
:
1730 case DHCPV6_RELAY_FORW
:
1731 case DHCPV6_LEASEQUERY_REPLY
:
1732 case DHCPV6_DHCPV4_RESPONSE
:
1733 log_info("Relaying %s to %s port %d down.",
1734 dhcpv6_type_names
[msg
->msg_type
],
1736 ntohs(to
.sin6_port
));
1739 case DHCPV6_SOLICIT
:
1740 case DHCPV6_REQUEST
:
1741 case DHCPV6_CONFIRM
:
1744 case DHCPV6_RELEASE
:
1745 case DHCPV6_DECLINE
:
1746 case DHCPV6_INFORMATION_REQUEST
:
1747 case DHCPV6_LEASEQUERY
:
1748 case DHCPV6_DHCPV4_QUERY
:
1749 log_info("Discarding %s to %s port %d down.",
1750 dhcpv6_type_names
[msg
->msg_type
],
1752 ntohs(to
.sin6_port
));
1756 log_info("Unknown %d type to %s port %d down.",
1759 ntohs(to
.sin6_port
));
1763 /* Send the message to the downstream. */
1764 send_packet6(dp
->ifp
, (unsigned char *) relay_msg
.data
,
1765 (size_t) relay_msg
.len
, &to
);
1768 if (relay_msg
.data
!= NULL
)
1769 data_string_forget(&relay_msg
, MDL
);
1770 if (if_id
.data
!= NULL
)
1771 data_string_forget(&if_id
, MDL
);
1775 * Called by the dispatch packet handler with a decoded packet.
1778 dhcpv6(struct packet
*packet
) {
1779 struct stream_list
*dp
;
1781 /* Try all relay-replies downwards. */
1782 if (packet
->dhcpv6_msg_type
== DHCPV6_RELAY_REPL
) {
1783 process_down6(packet
);
1786 /* Others are candidates to go up if they come from down. */
1787 for (dp
= downstreams
; dp
; dp
= dp
->next
) {
1788 if (packet
->interface
!= dp
->ifp
)
1790 process_up6(packet
, dp
);
1793 /* Relay-forward could work from an unknown interface. */
1794 if (packet
->dhcpv6_msg_type
== DHCPV6_RELAY_FORW
) {
1795 process_up6(packet
, NULL
);
1799 log_info("Can't process packet from interface '%s'.",
1800 packet
->interface
->name
);
1804 /* Stub routines needed for linking with DHCP libraries. */
1806 bootp(struct packet
*packet
) {
1811 dhcp(struct packet
*packet
) {
1816 classify(struct packet
*p
, struct class *c
) {
1821 check_collection(struct packet
*p
, struct lease
*l
, struct collection
*c
) {
1826 find_class(struct class **class, const char *c1
, const char *c2
, int i
) {
1827 return ISC_R_NOTFOUND
;
1831 parse_allow_deny(struct option_cache
**oc
, struct parse
*p
, int i
) {
1836 dhcp_set_control_state(control_object_state_t oldstate
,
1837 control_object_state_t newstate
) {
1838 if (newstate
!= server_shutdown
)
1839 return ISC_R_SUCCESS
;
1841 if (no_pid_file
== ISC_FALSE
)
1842 (void) unlink(path_dhcrelay_pid
);