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. */
65 int agent_option_errors
= 0; /* Number of packets forwarded without
66 agent options because there was no room. */
67 int drop_agent_mismatches
= 0; /* If nonzero, drop server replies that
68 don't have matching circuit-id's. */
69 int corrupt_agent_options
= 0; /* Number of packets dropped because
70 relay agent information option was bad. */
71 int missing_agent_option
= 0; /* Number of packets dropped because no
72 RAI option matching our ID was found. */
73 int bad_circuit_id
= 0; /* Circuit ID option in matching RAI option
74 did not match any known circuit ID. */
75 int missing_circuit_id
= 0; /* Circuit ID option in matching RAI option
77 int max_hop_count
= 10; /* Maximum hop count */
80 /* Force use of DHCPv6 interface-id option. */
81 isc_boolean_t use_if_id
= ISC_FALSE
;
84 /* Maximum size of a packet with agent options added. */
85 int dhcp_max_agent_option_packet_length
= DHCP_MTU_MIN
;
87 /* What to do about packets we're asked to relay that
88 already have a relay option: */
89 enum { forward_and_append
, /* Forward and append our own relay option. */
90 forward_and_replace
, /* Forward, but replace theirs with ours. */
91 forward_untouched
, /* Forward without changes. */
92 discard
} agent_relay_mode
= forward_and_replace
;
95 u_int16_t remote_port
;
97 /* Relay agent server list. */
99 struct server_list
*next
;
100 struct sockaddr_in to
;
105 struct stream_list
*next
;
106 struct interface_info
*ifp
;
107 struct sockaddr_in6 link
;
109 } *downstreams
, *upstreams
;
111 static struct stream_list
*parse_downstream(char *);
112 static struct stream_list
*parse_upstream(char *);
113 static void setup_streams(void);
116 * A pointer to a subscriber id to add to the message we forward.
117 * This is primarily for testing purposes as we only have one id
118 * for the entire relay and don't determine one per client which
119 * would be more useful.
121 char *dhcrelay_sub_id
= NULL
;
124 static void do_relay4(struct interface_info
*, struct dhcp_packet
*,
125 unsigned int, unsigned int, struct iaddr
,
127 static int add_relay_agent_options(struct interface_info
*,
128 struct dhcp_packet
*, unsigned,
130 static int find_interface_by_agent_option(struct dhcp_packet
*,
131 struct interface_info
**, u_int8_t
*, int);
132 static int strip_relay_agent_options(struct interface_info
*,
133 struct interface_info
**,
134 struct dhcp_packet
*, unsigned);
136 static const char copyright
[] =
137 "Copyright 2004-2015 Internet Systems Consortium.";
138 static const char arr
[] = "All rights reserved.";
139 static const char message
[] =
140 "Internet Systems Consortium DHCP Relay Agent";
141 static const char url
[] =
142 "For info, please visit https://www.isc.org/software/dhcp/";
147 #define DHCRELAY_USAGE \
148 "Usage: %s [-4] [-d] [-q] [-a] [-D]\n"\
149 " [-A <length>] [-c <hops>] [-p <port>]\n" \
150 " [-pf <pid-file>] [--no-pid]\n"\
151 " [-m append|replace|forward|discard]\n" \
152 " [-i interface0 [ ... -i interfaceN]\n" \
153 " server0 [ ... serverN]\n\n" \
154 " %s -6 [-d] [-q] [-I] [-c <hops>] [-p <port>]\n" \
155 " [-pf <pid-file>] [--no-pid]\n" \
156 " [-s <subscriber-id>]\n" \
157 " -l lower0 [ ... -l lowerN]\n" \
158 " -u upper0 [ ... -u upperN]\n" \
159 " lower (client link): [address%%]interface[#index]\n" \
160 " upper (server link): [address%%]interface"
162 #define DHCRELAY_USAGE \
163 "Usage: %s [-d] [-q] [-a] [-D] [-A <length>] [-c <hops>] [-p <port>]\n" \
164 " [-pf <pid-file>] [--no-pid]\n" \
165 " [-m append|replace|forward|discard]\n" \
166 " [-i interface0 [ ... -i interfaceN]\n" \
167 " server0 [ ... serverN]\n\n"
172 * \brief Print the generic usage message
174 * If the user has provided an incorrect command line print out
175 * the description of the command line. The arguments provide
176 * a way for the caller to request more specific information about
177 * the error be printed as well. Mostly this will be that some
178 * comamnd doesn't include its argument.
180 * \param sfmt - The basic string and format for the specific error
181 * \param sarg - Generally the offending argument from the comamnd line.
185 static const char use_noarg
[] = "No argument for command: %s";
186 static const char use_badproto
[] = "Protocol already set, %s inappropriate";
187 static const char use_v4command
[] = "Command not used for DHCPv6: %s";
188 static const char use_v6command
[] = "Command not used for DHCPv4: %s";
191 usage(const char *sfmt
, const char *sarg
) {
193 /* If desired print out the specific error message */
194 #ifdef PRINT_SPECIFIC_CL_ERRORS
196 log_error(sfmt
, sarg
);
199 log_fatal(DHCRELAY_USAGE
,
201 isc_file_basename(progname
),
203 isc_file_basename(progname
));
207 main(int argc
, char **argv
) {
210 struct server_list
*sp
= NULL
;
211 struct interface_info
*tmp
= NULL
;
212 char *service_local
= NULL
, *service_remote
= NULL
;
213 u_int16_t port_local
= 0, port_remote
= 0;
214 int no_daemon
= 0, quiet
= 0;
218 struct stream_list
*sl
= NULL
;
219 int local_family_set
= 0;
223 progname
= "dhcrelay";
228 /* Make sure that file descriptors 0(stdin), 1,(stdout), and
229 2(stderr) are open. To do this, we assume that when we
230 open a file the lowest available file descriptor is used. */
231 fd
= open("/dev/null", O_RDWR
);
233 fd
= open("/dev/null", O_RDWR
);
235 fd
= open("/dev/null", O_RDWR
);
237 log_perror
= 0; /* No sense logging to /dev/null. */
241 openlog(isc_file_basename(progname
), DHCP_LOG_OPTIONS
, LOG_DAEMON
);
244 setlogmask(LOG_UPTO(LOG_INFO
));
247 /* Set up the isc and dns library managers */
248 status
= dhcp_context_create(DHCP_CONTEXT_PRE_DB
| DHCP_CONTEXT_POST_DB
,
250 if (status
!= ISC_R_SUCCESS
)
251 log_fatal("Can't initialize context: %s",
252 isc_result_totext(status
));
254 /* Set up the OMAPI. */
255 status
= omapi_init();
256 if (status
!= ISC_R_SUCCESS
)
257 log_fatal("Can't initialize OMAPI: %s",
258 isc_result_totext(status
));
260 /* Set up the OMAPI wrappers for the interface object. */
263 for (i
= 1; i
< argc
; i
++) {
264 if (!strcmp(argv
[i
], "-4")) {
266 if (local_family_set
&& (local_family
== AF_INET6
)) {
267 usage(use_badproto
, "-4");
269 local_family_set
= 1;
270 local_family
= AF_INET
;
271 } else if (!strcmp(argv
[i
], "-6")) {
272 if (local_family_set
&& (local_family
== AF_INET
)) {
273 usage(use_badproto
, "-6");
275 local_family_set
= 1;
276 local_family
= AF_INET6
;
278 } else if (!strcmp(argv
[i
], "-d")) {
280 } else if (!strcmp(argv
[i
], "-q")) {
282 quiet_interface_discovery
= 1;
283 } else if (!strcmp(argv
[i
], "-p")) {
285 usage(use_noarg
, argv
[i
-1]);
286 local_port
= validate_port(argv
[i
]);
287 log_debug("binding to user-specified port %d",
289 } else if (!strcmp(argv
[i
], "-c")) {
292 usage(use_noarg
, argv
[i
-1]);
293 hcount
= atoi(argv
[i
]);
295 max_hop_count
= hcount
;
297 usage("Bad hop count to -c: %s", argv
[i
]);
298 } else if (!strcmp(argv
[i
], "-i")) {
300 if (local_family_set
&& (local_family
== AF_INET6
)) {
301 usage(use_v4command
, argv
[i
]);
303 local_family_set
= 1;
304 local_family
= AF_INET
;
307 usage(use_noarg
, argv
[i
-1]);
309 if (strlen(argv
[i
]) >= sizeof(tmp
->name
)) {
310 log_fatal("%s: interface name too long "
312 argv
[i
], (long)strlen(argv
[i
]));
314 status
= interface_allocate(&tmp
, MDL
);
315 if (status
!= ISC_R_SUCCESS
) {
316 log_fatal("%s: interface_allocate: %s",
318 isc_result_totext(status
));
320 strcpy(tmp
->name
, argv
[i
]);
321 interface_snorf(tmp
, INTERFACE_REQUESTED
);
322 interface_dereference(&tmp
, MDL
);
323 } else if (!strcmp(argv
[i
], "-a")) {
325 if (local_family_set
&& (local_family
== AF_INET6
)) {
326 usage(use_v4command
, argv
[i
]);
328 local_family_set
= 1;
329 local_family
= AF_INET
;
331 add_agent_options
= 1;
332 } else if (!strcmp(argv
[i
], "-A")) {
334 if (local_family_set
&& (local_family
== AF_INET6
)) {
335 usage(use_v4command
, argv
[i
]);
337 local_family_set
= 1;
338 local_family
= AF_INET
;
341 usage(use_noarg
, argv
[i
-1]);
343 dhcp_max_agent_option_packet_length
= atoi(argv
[i
]);
345 if (dhcp_max_agent_option_packet_length
> DHCP_MTU_MAX
)
346 log_fatal("%s: packet length exceeds "
347 "longest possible MTU\n",
349 } else if (!strcmp(argv
[i
], "-m")) {
351 if (local_family_set
&& (local_family
== AF_INET6
)) {
352 usage(use_v4command
, argv
[i
]);
354 local_family_set
= 1;
355 local_family
= AF_INET
;
358 usage(use_noarg
, argv
[i
-1]);
359 if (!strcasecmp(argv
[i
], "append")) {
360 agent_relay_mode
= forward_and_append
;
361 } else if (!strcasecmp(argv
[i
], "replace")) {
362 agent_relay_mode
= forward_and_replace
;
363 } else if (!strcasecmp(argv
[i
], "forward")) {
364 agent_relay_mode
= forward_untouched
;
365 } else if (!strcasecmp(argv
[i
], "discard")) {
366 agent_relay_mode
= discard
;
368 usage("Unknown argument to -m: %s", argv
[i
]);
369 } else if (!strcmp(argv
[i
], "-D")) {
371 if (local_family_set
&& (local_family
== AF_INET6
)) {
372 usage(use_v4command
, argv
[i
]);
374 local_family_set
= 1;
375 local_family
= AF_INET
;
377 drop_agent_mismatches
= 1;
379 } else if (!strcmp(argv
[i
], "-I")) {
380 if (local_family_set
&& (local_family
== AF_INET
)) {
381 usage(use_v6command
, argv
[i
]);
383 local_family_set
= 1;
384 local_family
= AF_INET6
;
385 use_if_id
= ISC_TRUE
;
386 } else if (!strcmp(argv
[i
], "-l")) {
387 if (local_family_set
&& (local_family
== AF_INET
)) {
388 usage(use_v6command
, argv
[i
]);
390 local_family_set
= 1;
391 local_family
= AF_INET6
;
392 if (downstreams
!= NULL
)
393 use_if_id
= ISC_TRUE
;
395 usage(use_noarg
, argv
[i
-1]);
396 sl
= parse_downstream(argv
[i
]);
397 sl
->next
= downstreams
;
399 } else if (!strcmp(argv
[i
], "-u")) {
400 if (local_family_set
&& (local_family
== AF_INET
)) {
401 usage(use_v6command
, argv
[i
]);
403 local_family_set
= 1;
404 local_family
= AF_INET6
;
406 usage(use_noarg
, argv
[i
-1]);
407 sl
= parse_upstream(argv
[i
]);
408 sl
->next
= upstreams
;
410 } else if (!strcmp(argv
[i
], "-s")) {
411 if (local_family_set
&& (local_family
== AF_INET
)) {
412 usage(use_v6command
, argv
[i
]);
414 local_family_set
= 1;
415 local_family
= AF_INET6
;
417 usage(use_noarg
, argv
[i
-1]);
418 dhcrelay_sub_id
= argv
[i
];
420 } else if (!strcmp(argv
[i
], "-pf")) {
422 usage(use_noarg
, argv
[i
-1]);
423 path_dhcrelay_pid
= argv
[i
];
424 no_dhcrelay_pid
= ISC_TRUE
;
425 } else if (!strcmp(argv
[i
], "--no-pid")) {
426 no_pid_file
= ISC_TRUE
;
427 } else if (!strcmp(argv
[i
], "--version")) {
428 log_info("isc-dhcrelay-%s", PACKAGE_VERSION
);
430 } else if (!strcmp(argv
[i
], "--help") ||
431 !strcmp(argv
[i
], "-h")) {
432 log_info(DHCRELAY_USAGE
,
434 isc_file_basename(progname
),
436 isc_file_basename(progname
));
438 } else if (argv
[i
][0] == '-') {
439 usage("Unknown command: %s", argv
[i
]);
442 struct in_addr ia
, *iap
= NULL
;
445 if (local_family_set
&& (local_family
== AF_INET6
)) {
446 usage(use_v4command
, argv
[i
]);
448 local_family_set
= 1;
449 local_family
= AF_INET
;
451 if (inet_aton(argv
[i
], &ia
)) {
454 he
= gethostbyname(argv
[i
]);
456 log_error("%s: host unknown", argv
[i
]);
458 iap
= ((struct in_addr
*)
464 sp
= ((struct server_list
*)
465 dmalloc(sizeof *sp
, MDL
));
467 log_fatal("no memory for server.\n");
470 memcpy(&sp
->to
.sin_addr
, iap
, sizeof *iap
);
476 * If the user didn't specify a pid file directly
477 * find one from environment variables or defaults
479 if (no_dhcrelay_pid
== ISC_FALSE
) {
480 if (local_family
== AF_INET
) {
481 path_dhcrelay_pid
= getenv("PATH_DHCRELAY_PID");
482 if (path_dhcrelay_pid
== NULL
)
483 path_dhcrelay_pid
= _PATH_DHCRELAY_PID
;
487 path_dhcrelay_pid
= getenv("PATH_DHCRELAY6_PID");
488 if (path_dhcrelay_pid
== NULL
)
489 path_dhcrelay_pid
= _PATH_DHCRELAY6_PID
;
495 log_info("%s %s", message
, PACKAGE_VERSION
);
502 /* Set default port */
503 if (local_family
== AF_INET
) {
504 service_local
= "bootps";
505 service_remote
= "bootpc";
506 port_local
= htons(67);
507 port_remote
= htons(68);
511 service_local
= "dhcpv6-server";
512 service_remote
= "dhcpv6-client";
513 port_local
= htons(547);
514 port_remote
= htons(546);
519 ent
= getservbyname(service_local
, "udp");
521 local_port
= ent
->s_port
;
523 local_port
= port_local
;
525 ent
= getservbyname(service_remote
, "udp");
527 remote_port
= ent
->s_port
;
529 remote_port
= port_remote
;
534 if (local_family
== AF_INET
) {
535 /* We need at least one server */
536 if (servers
== NULL
) {
537 log_fatal("No servers specified.");
541 /* Set up the server sockaddrs. */
542 for (sp
= servers
; sp
; sp
= sp
->next
) {
543 sp
->to
.sin_port
= local_port
;
544 sp
->to
.sin_family
= AF_INET
;
546 sp
->to
.sin_len
= sizeof sp
->to
;
554 /* We need at least one upstream and one downstream interface */
555 if (upstreams
== NULL
|| downstreams
== NULL
) {
556 log_info("Must specify at least one lower "
557 "and one upper interface.\n");
561 /* Set up the initial dhcp option universe. */
562 initialize_common_option_spaces();
564 /* Check requested options. */
565 code
= D6O_RELAY_MSG
;
566 if (!option_code_hash_lookup(&requested_opts
[0],
567 dhcpv6_universe
.code_hash
,
569 log_fatal("Unable to find the RELAY_MSG "
570 "option definition.");
571 code
= D6O_INTERFACE_ID
;
572 if (!option_code_hash_lookup(&requested_opts
[1],
573 dhcpv6_universe
.code_hash
,
575 log_fatal("Unable to find the INTERFACE_ID "
576 "option definition.");
580 /* Get the current time... */
581 gettimeofday(&cur_tv
, NULL
);
583 /* Discover all the network interfaces. */
584 discover_interfaces(DISCOVER_RELAY
);
587 if (local_family
== AF_INET6
)
591 /* Become a daemon... */
599 if ((pid
= fork()) < 0)
600 log_fatal("Can't fork daemon: %m");
604 if (no_pid_file
== ISC_FALSE
) {
605 pfdesc
= open(path_dhcrelay_pid
,
606 O_CREAT
| O_TRUNC
| O_WRONLY
, 0644);
609 log_error("Can't create %s: %m",
612 pf
= fdopen(pfdesc
, "w");
614 log_error("Can't fdopen %s: %m",
617 fprintf(pf
, "%ld\n",(long)getpid());
628 IGNORE_RET (chdir("/"));
631 /* Set up the packet handler... */
632 if (local_family
== AF_INET
)
633 bootp_packet_handler
= do_relay4
;
636 dhcpv6_packet_handler
= do_packet6
;
639 #if defined(ENABLE_GENTLE_SHUTDOWN)
640 /* no signal handlers until we deal with the side effects */
641 /* install signal handlers */
642 signal(SIGINT
, dhcp_signal_handler
); /* control-c */
643 signal(SIGTERM
, dhcp_signal_handler
); /* kill */
646 /* Start dispatching packets and timeouts... */
649 /* In fact dispatch() never returns. */
654 do_relay4(struct interface_info
*ip
, struct dhcp_packet
*packet
,
655 unsigned int length
, unsigned int from_port
, struct iaddr from
,
656 struct hardware
*hfrom
) {
657 struct server_list
*sp
;
658 struct sockaddr_in to
;
659 struct interface_info
*out
;
660 struct hardware hto
, *htop
;
662 if (packet
->hlen
> sizeof packet
->chaddr
) {
663 log_info("Discarding packet with invalid hlen, received on "
664 "%s interface.", ip
->name
);
667 if (ip
->address_count
< 1 || ip
->addresses
== NULL
) {
668 log_info("Discarding packet received on %s interface that "
669 "has no IPv4 address assigned.", ip
->name
);
673 /* Find the interface that corresponds to the giaddr
675 if (packet
->giaddr
.s_addr
) {
676 for (out
= interfaces
; out
; out
= out
->next
) {
679 for (i
= 0 ; i
< out
->address_count
; i
++ ) {
680 if (out
->addresses
[i
].s_addr
==
681 packet
->giaddr
.s_addr
) {
694 /* If it's a bootreply, forward it to the client. */
695 if (packet
->op
== BOOTREPLY
) {
696 if (!(packet
->flags
& htons(BOOTP_BROADCAST
)) &&
697 can_unicast_without_arp(out
)) {
698 to
.sin_addr
= packet
->yiaddr
;
699 to
.sin_port
= remote_port
;
701 /* and hardware address is not broadcast */
704 to
.sin_addr
.s_addr
= htonl(INADDR_BROADCAST
);
705 to
.sin_port
= remote_port
;
707 /* hardware address is broadcast */
710 to
.sin_family
= AF_INET
;
712 to
.sin_len
= sizeof to
;
715 memcpy(&hto
.hbuf
[1], packet
->chaddr
, packet
->hlen
);
716 hto
.hbuf
[0] = packet
->htype
;
717 hto
.hlen
= packet
->hlen
+ 1;
719 /* Wipe out the agent relay options and, if possible, figure
720 out which interface to use based on the contents of the
721 option that we put on the request to which the server is
724 strip_relay_agent_options(ip
, &out
, packet
, length
)))
728 log_error("Packet to bogus giaddr %s.\n",
729 inet_ntoa(packet
->giaddr
));
730 ++bogus_giaddr_drops
;
734 if (send_packet(out
, NULL
, packet
, length
, out
->addresses
[0],
736 ++server_packet_errors
;
738 log_debug("Forwarded BOOTREPLY for %s to %s",
739 print_hw_addr(packet
->htype
, packet
->hlen
,
741 inet_ntoa(to
.sin_addr
));
743 ++server_packets_relayed
;
748 /* If giaddr matches one of our addresses, ignore the packet -
753 /* Add relay agent options if indicated. If something goes wrong,
755 if (!(length
= add_relay_agent_options(ip
, packet
, length
,
759 /* If giaddr is not already set, Set it so the server can
760 figure out what net it's from and so that we can later
761 forward the response to the correct net. If it's already
762 set, the response will be sent directly to the relay agent
763 that set giaddr, so we won't see it. */
764 if (!packet
->giaddr
.s_addr
)
765 packet
->giaddr
= ip
->addresses
[0];
766 if (packet
->hops
< max_hop_count
)
767 packet
->hops
= packet
->hops
+ 1;
771 /* Otherwise, it's a BOOTREQUEST, so forward it to all the
773 for (sp
= servers
; sp
; sp
= sp
->next
) {
774 if (send_packet((fallback_interface
775 ? fallback_interface
: interfaces
),
776 NULL
, packet
, length
, ip
->addresses
[0],
777 &sp
->to
, NULL
) < 0) {
778 ++client_packet_errors
;
780 log_debug("Forwarded BOOTREQUEST for %s to %s",
781 print_hw_addr(packet
->htype
, packet
->hlen
,
783 inet_ntoa(sp
->to
.sin_addr
));
784 ++client_packets_relayed
;
790 /* Strip any Relay Agent Information options from the DHCP packet
791 option buffer. If there is a circuit ID suboption, look up the
792 outgoing interface based upon it. */
795 strip_relay_agent_options(struct interface_info
*in
,
796 struct interface_info
**out
,
797 struct dhcp_packet
*packet
,
800 u_int8_t
*op
, *nextop
, *sp
, *max
;
801 int good_agent_option
= 0;
804 /* If we're not adding agent options to packets, we're not taking
806 if (!add_agent_options
)
809 /* If there's no cookie, it's a bootp packet, so we should just
810 forward it unchanged. */
811 if (memcmp(packet
->options
, DHCP_OPTIONS_COOKIE
, 4))
814 max
= ((u_int8_t
*)packet
) + length
;
815 sp
= op
= &packet
->options
[4];
819 /* Skip padding... */
827 /* If we see a message type, it's a DHCP packet. */
828 case DHO_DHCP_MESSAGE_TYPE
:
833 /* Quit immediately if we hit an End option. */
839 case DHO_DHCP_AGENT_OPTIONS
:
840 /* We shouldn't see a relay agent option in a
841 packet before we've seen the DHCP packet type,
842 but if we do, we have to leave it alone. */
846 /* Do not process an agent option if it exceeds the
847 * buffer. Fail this packet.
849 nextop
= op
+ op
[1] + 2;
853 status
= find_interface_by_agent_option(packet
,
856 if (status
== -1 && drop_agent_mismatches
)
859 good_agent_option
= 1;
864 /* Skip over other options. */
866 /* Fail if processing this option will exceed the
867 * buffer(op[1] is malformed).
869 nextop
= op
+ op
[1] + 2;
874 memmove(sp
, op
, op
[1] + 2);
885 /* If it's not a DHCP packet, we're not supposed to touch it. */
889 /* If none of the agent options we found matched, or if we didn't
890 find any agent options, count this packet as not having any
891 matching agent options, and if we're relying on agent options
892 to determine the outgoing interface, drop the packet. */
894 if (!good_agent_option
) {
895 ++missing_agent_option
;
896 if (drop_agent_mismatches
)
900 /* Adjust the length... */
902 length
= sp
-((u_int8_t
*)packet
);
904 /* Make sure the packet isn't short(this is unlikely,
906 if (length
< BOOTP_MIN_LEN
) {
907 memset(sp
, DHO_PAD
, BOOTP_MIN_LEN
- length
);
908 length
= BOOTP_MIN_LEN
;
915 /* Find an interface that matches the circuit ID specified in the
916 Relay Agent Information option. If one is found, store it through
917 the pointer given; otherwise, leave the existing pointer alone.
919 We actually deviate somewhat from the current specification here:
920 if the option buffer is corrupt, we suggest that the caller not
921 respond to this packet. If the circuit ID doesn't match any known
922 interface, we suggest that the caller to drop the packet. Only if
923 we find a circuit ID that matches an existing interface do we tell
924 the caller to go ahead and process the packet. */
927 find_interface_by_agent_option(struct dhcp_packet
*packet
,
928 struct interface_info
**out
,
929 u_int8_t
*buf
, int len
) {
931 u_int8_t
*circuit_id
= 0;
932 unsigned circuit_id_len
= 0;
933 struct interface_info
*ip
;
936 /* If the next agent option overflows the end of the
937 packet, the agent option buffer is corrupt. */
939 i
+ buf
[i
+ 1] + 2 > len
) {
940 ++corrupt_agent_options
;
944 /* Remember where the circuit ID is... */
946 circuit_id
= &buf
[i
+ 2];
947 circuit_id_len
= buf
[i
+ 1];
948 i
+= circuit_id_len
+ 2;
957 /* If there's no circuit ID, it's not really ours, tell the caller
960 ++missing_circuit_id
;
964 /* Scan the interface list looking for an interface whose
965 name matches the one specified in circuit_id. */
967 for (ip
= interfaces
; ip
; ip
= ip
->next
) {
968 if (ip
->circuit_id
&&
969 ip
->circuit_id_len
== circuit_id_len
&&
970 !memcmp(ip
->circuit_id
, circuit_id
, circuit_id_len
))
974 /* If we got a match, use it. */
980 /* If we didn't get a match, the circuit ID was bogus. */
986 * Examine a packet to see if it's a candidate to have a Relay
987 * Agent Information option tacked onto its tail. If it is, tack
991 add_relay_agent_options(struct interface_info
*ip
, struct dhcp_packet
*packet
,
992 unsigned length
, struct in_addr giaddr
) {
993 int is_dhcp
= 0, mms
;
995 u_int8_t
*op
, *nextop
, *sp
, *max
, *end_pad
= NULL
;
997 /* If we're not adding agent options to packets, we can skip
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
) + dhcp_max_agent_option_packet_length
;
1009 /* Commence processing after the cookie. */
1010 sp
= op
= &packet
->options
[4];
1014 /* Skip padding... */
1016 /* Remember the first pad byte so we can commandeer
1019 * XXX: Is this really a good idea? Sure, we can
1020 * seemingly reduce the packet while we're looking,
1021 * but if the packet was signed by the client then
1022 * this padding is part of the checksum(RFC3118),
1023 * and its nonpresence would break authentication.
1025 if (end_pad
== NULL
)
1035 /* If we see a message type, it's a DHCP packet. */
1036 case DHO_DHCP_MESSAGE_TYPE
:
1041 * If there's a maximum message size option, we
1042 * should pay attention to it
1044 case DHO_DHCP_MAX_MESSAGE_SIZE
:
1045 mms
= ntohs(*(op
+ 2));
1046 if (mms
< dhcp_max_agent_option_packet_length
&&
1047 mms
>= DHCP_MTU_MIN
)
1048 max
= ((u_int8_t
*)packet
) + mms
;
1051 /* Quit immediately if we hit an End option. */
1055 case DHO_DHCP_AGENT_OPTIONS
:
1056 /* We shouldn't see a relay agent option in a
1057 packet before we've seen the DHCP packet type,
1058 but if we do, we have to leave it alone. */
1064 /* There's already a Relay Agent Information option
1065 in this packet. How embarrassing. Decide what
1066 to do based on the mode the user specified. */
1068 switch(agent_relay_mode
) {
1069 case forward_and_append
:
1071 case forward_untouched
:
1075 case forward_and_replace
:
1080 /* Skip over the agent option and start copying
1081 if we aren't copying already. */
1086 /* Skip over other options. */
1088 /* Fail if processing this option will exceed the
1089 * buffer(op[1] is malformed).
1091 nextop
= op
+ op
[1] + 2;
1098 memmove(sp
, op
, op
[1] + 2);
1109 /* If it's not a DHCP packet, we're not supposed to touch it. */
1113 /* If the packet was padded out, we can store the agent option
1114 at the beginning of the padding. */
1116 if (end_pad
!= NULL
)
1120 /* Remember where the end of the packet was after parsing
1125 /* Sanity check. Had better not ever happen. */
1126 if ((ip
->circuit_id_len
> 255) ||(ip
->circuit_id_len
< 1))
1127 log_fatal("Circuit ID length %d out of range [1-255] on "
1128 "%s\n", ip
->circuit_id_len
, ip
->name
);
1129 optlen
= ip
->circuit_id_len
+ 2; /* RAI_CIRCUIT_ID + len */
1131 if (ip
->remote_id
) {
1132 if (ip
->remote_id_len
> 255 || ip
->remote_id_len
< 1)
1133 log_fatal("Remote ID length %d out of range [1-255] "
1134 "on %s\n", ip
->circuit_id_len
, ip
->name
);
1135 optlen
+= ip
->remote_id_len
+ 2; /* RAI_REMOTE_ID + len */
1138 /* We do not support relay option fragmenting(multiple options to
1139 * support an option data exceeding 255 bytes).
1141 if ((optlen
< 3) ||(optlen
> 255))
1142 log_fatal("Total agent option length(%u) out of range "
1143 "[3 - 255] on %s\n", optlen
, ip
->name
);
1146 * Is there room for the option, its code+len, and DHO_END?
1147 * If not, forward without adding the option.
1149 if (max
- sp
>= optlen
+ 3) {
1150 log_debug("Adding %d-byte relay agent option", optlen
+ 3);
1152 /* Okay, cons up *our* Relay Agent Information option. */
1153 *sp
++ = DHO_DHCP_AGENT_OPTIONS
;
1156 /* Copy in the circuit id... */
1157 *sp
++ = RAI_CIRCUIT_ID
;
1158 *sp
++ = ip
->circuit_id_len
;
1159 memcpy(sp
, ip
->circuit_id
, ip
->circuit_id_len
);
1160 sp
+= ip
->circuit_id_len
;
1162 /* Copy in remote ID... */
1163 if (ip
->remote_id
) {
1164 *sp
++ = RAI_REMOTE_ID
;
1165 *sp
++ = ip
->remote_id_len
;
1166 memcpy(sp
, ip
->remote_id
, ip
->remote_id_len
);
1167 sp
+= ip
->remote_id_len
;
1170 ++agent_option_errors
;
1171 log_error("No room in packet (used %d of %d) "
1172 "for %d-byte relay agent option: omitted",
1173 (int) (sp
- ((u_int8_t
*) packet
)),
1174 (int) (max
- ((u_int8_t
*) packet
)),
1179 * Deposit an END option unless the packet is full (shouldn't
1185 /* Recalculate total packet length. */
1186 length
= sp
-((u_int8_t
*)packet
);
1188 /* Make sure the packet isn't short(this is unlikely, but WTH) */
1189 if (length
< BOOTP_MIN_LEN
) {
1190 memset(sp
, DHO_PAD
, BOOTP_MIN_LEN
- length
);
1191 return (BOOTP_MIN_LEN
);
1199 * Parse a downstream argument: [address%]interface[#index].
1201 static struct stream_list
*
1202 parse_downstream(char *arg
) {
1203 struct stream_list
*dp
, *up
;
1204 struct interface_info
*ifp
= NULL
;
1205 char *ifname
, *addr
, *iid
;
1206 isc_result_t status
;
1208 if (!supports_multiple_interfaces(ifp
) &&
1209 (downstreams
!= NULL
))
1210 log_fatal("No support for multiple interfaces.");
1212 /* Decode the argument. */
1213 ifname
= strchr(arg
, '%');
1214 if (ifname
== NULL
) {
1221 iid
= strchr(ifname
, '#');
1225 if (strlen(ifname
) >= sizeof(ifp
->name
)) {
1226 usage("Interface name '%s' too long", ifname
);
1229 /* Don't declare twice. */
1230 for (dp
= downstreams
; dp
; dp
= dp
->next
) {
1231 if (strcmp(ifname
, dp
->ifp
->name
) == 0)
1232 log_fatal("Down interface '%s' declared twice.",
1236 /* Share with up side? */
1237 for (up
= upstreams
; up
; up
= up
->next
) {
1238 if (strcmp(ifname
, up
->ifp
->name
) == 0) {
1239 log_info("parse_downstream: Interface '%s' is "
1240 "both down and up.", ifname
);
1246 /* New interface. */
1248 status
= interface_allocate(&ifp
, MDL
);
1249 if (status
!= ISC_R_SUCCESS
)
1250 log_fatal("%s: interface_allocate: %s",
1251 arg
, isc_result_totext(status
));
1252 strcpy(ifp
->name
, ifname
);
1254 interface_reference(&ifp
->next
, interfaces
, MDL
);
1255 interface_dereference(&interfaces
, MDL
);
1257 interface_reference(&interfaces
, ifp
, MDL
);
1259 ifp
->flags
|= INTERFACE_REQUESTED
| INTERFACE_DOWNSTREAM
;
1261 /* New downstream. */
1262 dp
= (struct stream_list
*) dmalloc(sizeof(*dp
), MDL
);
1264 log_fatal("No memory for downstream.");
1271 /* !addr case handled by setup. */
1272 if (addr
&& (inet_pton(AF_INET6
, addr
, &dp
->link
.sin6_addr
) <= 0))
1273 log_fatal("Bad link address '%s'", addr
);
1279 * Parse an upstream argument: [address]%interface.
1281 static struct stream_list
*
1282 parse_upstream(char *arg
) {
1283 struct stream_list
*up
, *dp
;
1284 struct interface_info
*ifp
= NULL
;
1285 char *ifname
, *addr
;
1286 isc_result_t status
;
1288 /* Decode the argument. */
1289 ifname
= strchr(arg
, '%');
1290 if (ifname
== NULL
) {
1292 addr
= All_DHCP_Servers
;
1297 if (strlen(ifname
) >= sizeof(ifp
->name
)) {
1298 log_fatal("Interface name '%s' too long", ifname
);
1301 /* Shared up interface? */
1302 for (up
= upstreams
; up
; up
= up
->next
) {
1303 if (strcmp(ifname
, up
->ifp
->name
) == 0) {
1308 for (dp
= downstreams
; dp
; dp
= dp
->next
) {
1309 if (strcmp(ifname
, dp
->ifp
->name
) == 0) {
1310 log_info("parse_upstream: Interface '%s' is "
1311 "both down and up.", ifname
);
1317 /* New interface. */
1319 status
= interface_allocate(&ifp
, MDL
);
1320 if (status
!= ISC_R_SUCCESS
)
1321 log_fatal("%s: interface_allocate: %s",
1322 arg
, isc_result_totext(status
));
1323 strcpy(ifp
->name
, ifname
);
1325 interface_reference(&ifp
->next
, interfaces
, MDL
);
1326 interface_dereference(&interfaces
, MDL
);
1328 interface_reference(&interfaces
, ifp
, MDL
);
1330 ifp
->flags
|= INTERFACE_REQUESTED
| INTERFACE_UPSTREAM
;
1333 up
= (struct stream_list
*) dmalloc(sizeof(*up
), MDL
);
1335 log_fatal("No memory for upstream.");
1339 if (inet_pton(AF_INET6
, addr
, &up
->link
.sin6_addr
) <= 0)
1340 log_fatal("Bad address %s", addr
);
1346 * Setup downstream interfaces.
1349 setup_streams(void) {
1350 struct stream_list
*dp
, *up
;
1352 isc_boolean_t link_is_set
;
1354 for (dp
= downstreams
; dp
; dp
= dp
->next
) {
1355 /* Check interface */
1356 if (dp
->ifp
->v6address_count
== 0)
1357 log_fatal("Interface '%s' has no IPv6 addresses.",
1360 /* Check/set link. */
1361 if (IN6_IS_ADDR_UNSPECIFIED(&dp
->link
.sin6_addr
))
1362 link_is_set
= ISC_FALSE
;
1364 link_is_set
= ISC_TRUE
;
1365 for (i
= 0; i
< dp
->ifp
->v6address_count
; i
++) {
1366 if (IN6_IS_ADDR_LINKLOCAL(&dp
->ifp
->v6addresses
[i
]))
1370 if (!memcmp(&dp
->ifp
->v6addresses
[i
],
1371 &dp
->link
.sin6_addr
,
1372 sizeof(dp
->link
.sin6_addr
)))
1375 if (i
== dp
->ifp
->v6address_count
)
1376 log_fatal("Interface %s does not have global IPv6 "
1377 "address assigned.", dp
->ifp
->name
);
1379 memcpy(&dp
->link
.sin6_addr
,
1380 &dp
->ifp
->v6addresses
[i
],
1381 sizeof(dp
->link
.sin6_addr
));
1383 /* Set interface-id. */
1385 dp
->id
= dp
->ifp
->index
;
1388 for (up
= upstreams
; up
; up
= up
->next
) {
1389 up
->link
.sin6_port
= local_port
;
1390 up
->link
.sin6_family
= AF_INET6
;
1392 up
->link
.sin6_len
= sizeof(up
->link
);
1395 if (up
->ifp
->v6address_count
== 0)
1396 log_fatal("Interface '%s' has no IPv6 addresses.",
1399 /* RFC 3315 Sec 20 - "If the relay agent relays messages to
1400 * the All_DHCP_Servers address or other multicast addresses,
1401 * it sets the Hop Limit field to 32." */
1402 if (IN6_IS_ADDR_MULTICAST(&up
->link
.sin6_addr
)) {
1403 set_multicast_hop_limit(up
->ifp
, HOP_COUNT_LIMIT
);
1409 * Add DHCPv6 agent options here.
1411 static const int required_forw_opts
[] = {
1419 * Process a packet upwards, i.e., from client to server.
1422 process_up6(struct packet
*packet
, struct stream_list
*dp
) {
1423 char forw_data
[65535];
1425 struct dhcpv6_relay_packet
*relay
;
1426 struct option_state
*opts
;
1427 struct stream_list
*up
;
1429 /* Check if the message should be relayed to the server. */
1430 switch (packet
->dhcpv6_msg_type
) {
1431 case DHCPV6_SOLICIT
:
1432 case DHCPV6_REQUEST
:
1433 case DHCPV6_CONFIRM
:
1436 case DHCPV6_RELEASE
:
1437 case DHCPV6_DECLINE
:
1438 case DHCPV6_INFORMATION_REQUEST
:
1439 case DHCPV6_RELAY_FORW
:
1440 case DHCPV6_LEASEQUERY
:
1441 log_info("Relaying %s from %s port %d going up.",
1442 dhcpv6_type_names
[packet
->dhcpv6_msg_type
],
1443 piaddr(packet
->client_addr
),
1444 ntohs(packet
->client_port
));
1447 case DHCPV6_ADVERTISE
:
1449 case DHCPV6_RECONFIGURE
:
1450 case DHCPV6_RELAY_REPL
:
1451 case DHCPV6_LEASEQUERY_REPLY
:
1452 log_info("Discarding %s from %s port %d going up.",
1453 dhcpv6_type_names
[packet
->dhcpv6_msg_type
],
1454 piaddr(packet
->client_addr
),
1455 ntohs(packet
->client_port
));
1459 log_info("Unknown %d type from %s port %d going up.",
1460 packet
->dhcpv6_msg_type
,
1461 piaddr(packet
->client_addr
),
1462 ntohs(packet
->client_port
));
1466 /* Build the relay-forward header. */
1467 relay
= (struct dhcpv6_relay_packet
*) forw_data
;
1468 cursor
= offsetof(struct dhcpv6_relay_packet
, options
);
1469 relay
->msg_type
= DHCPV6_RELAY_FORW
;
1470 if (packet
->dhcpv6_msg_type
== DHCPV6_RELAY_FORW
) {
1471 if (packet
->dhcpv6_hop_count
>= max_hop_count
) {
1472 log_info("Hop count exceeded,");
1475 relay
->hop_count
= packet
->dhcpv6_hop_count
+ 1;
1477 memcpy(&relay
->link_address
, &dp
->link
.sin6_addr
, 16);
1479 /* On smart relay add: && !global. */
1480 if (!use_if_id
&& downstreams
->next
) {
1481 log_info("Shan't get back the interface.");
1484 memset(&relay
->link_address
, 0, 16);
1487 relay
->hop_count
= 0;
1490 memcpy(&relay
->link_address
, &dp
->link
.sin6_addr
, 16);
1492 memcpy(&relay
->peer_address
, packet
->client_addr
.iabuf
, 16);
1494 /* Get an option state. */
1496 if (!option_state_allocate(&opts
, MDL
)) {
1497 log_fatal("No memory for upwards options.");
1500 /* Add an interface-id (if used). */
1506 } else if (!downstreams
->next
) {
1507 if_id
= downstreams
->id
;
1509 log_info("Don't know the interface.");
1510 option_state_dereference(&opts
, MDL
);
1514 if (!save_option_buffer(&dhcpv6_universe
, opts
,
1515 NULL
, (unsigned char *) &if_id
,
1517 D6O_INTERFACE_ID
, 0)) {
1518 log_error("Can't save interface-id.");
1519 option_state_dereference(&opts
, MDL
);
1524 /* Add a subscriber-id if desired. */
1525 /* This is for testing rather than general use */
1526 if (dhcrelay_sub_id
!= NULL
) {
1527 if (!save_option_buffer(&dhcpv6_universe
, opts
, NULL
,
1528 (unsigned char *) dhcrelay_sub_id
,
1529 strlen(dhcrelay_sub_id
),
1530 D6O_SUBSCRIBER_ID
, 0)) {
1531 log_error("Can't save subsriber-id.");
1532 option_state_dereference(&opts
, MDL
);
1538 /* Add the relay-msg carrying the packet. */
1539 if (!save_option_buffer(&dhcpv6_universe
, opts
,
1540 NULL
, (unsigned char *) packet
->raw
,
1541 packet
->packet_length
,
1542 D6O_RELAY_MSG
, 0)) {
1543 log_error("Can't save relay-msg.");
1544 option_state_dereference(&opts
, MDL
);
1548 /* Finish the relay-forward message. */
1549 cursor
+= store_options6(forw_data
+ cursor
,
1550 sizeof(forw_data
) - cursor
,
1552 required_forw_opts
, NULL
);
1553 option_state_dereference(&opts
, MDL
);
1555 /* Send it to all upstreams. */
1556 for (up
= upstreams
; up
; up
= up
->next
) {
1557 send_packet6(up
->ifp
, (unsigned char *) forw_data
,
1558 (size_t) cursor
, &up
->link
);
1563 * Process a packet downwards, i.e., from server to client.
1566 process_down6(struct packet
*packet
) {
1567 struct stream_list
*dp
;
1568 struct option_cache
*oc
;
1569 struct data_string relay_msg
;
1570 const struct dhcpv6_packet
*msg
;
1571 struct data_string if_id
;
1572 struct sockaddr_in6 to
;
1575 /* The packet must be a relay-reply message. */
1576 if (packet
->dhcpv6_msg_type
!= DHCPV6_RELAY_REPL
) {
1577 if (packet
->dhcpv6_msg_type
< dhcpv6_type_name_max
)
1578 log_info("Discarding %s from %s port %d going down.",
1579 dhcpv6_type_names
[packet
->dhcpv6_msg_type
],
1580 piaddr(packet
->client_addr
),
1581 ntohs(packet
->client_port
));
1583 log_info("Unknown %d type from %s port %d going down.",
1584 packet
->dhcpv6_msg_type
,
1585 piaddr(packet
->client_addr
),
1586 ntohs(packet
->client_port
));
1591 memset(&relay_msg
, 0, sizeof(relay_msg
));
1592 memset(&if_id
, 0, sizeof(if_id
));
1593 memset(&to
, 0, sizeof(to
));
1594 to
.sin6_family
= AF_INET6
;
1596 to
.sin6_len
= sizeof(to
);
1598 to
.sin6_port
= remote_port
;
1601 /* Get the relay-msg option (carrying the message to relay). */
1602 oc
= lookup_option(&dhcpv6_universe
, packet
->options
, D6O_RELAY_MSG
);
1604 log_info("No relay-msg.");
1607 if (!evaluate_option_cache(&relay_msg
, packet
, NULL
, NULL
,
1608 packet
->options
, NULL
,
1609 &global_scope
, oc
, MDL
) ||
1610 (relay_msg
.len
< offsetof(struct dhcpv6_packet
, options
))) {
1611 log_error("Can't evaluate relay-msg.");
1614 msg
= (const struct dhcpv6_packet
*) relay_msg
.data
;
1616 /* Get the interface-id (if exists) and the downstream. */
1617 oc
= lookup_option(&dhcpv6_universe
, packet
->options
,
1622 if (!evaluate_option_cache(&if_id
, packet
, NULL
, NULL
,
1623 packet
->options
, NULL
,
1624 &global_scope
, oc
, MDL
) ||
1625 (if_id
.len
!= sizeof(int))) {
1626 log_info("Can't evaluate interface-id.");
1629 memcpy(&if_index
, if_id
.data
, sizeof(int));
1630 for (dp
= downstreams
; dp
; dp
= dp
->next
) {
1631 if (dp
->id
== if_index
)
1636 /* Require an interface-id. */
1637 log_info("No interface-id.");
1640 for (dp
= downstreams
; dp
; dp
= dp
->next
) {
1641 /* Get the first matching one. */
1642 if (!memcmp(&dp
->link
.sin6_addr
,
1643 &packet
->dhcpv6_link_address
,
1644 sizeof(struct in6_addr
)))
1648 /* Why bother when there is no choice. */
1649 if (!dp
&& downstreams
&& !downstreams
->next
)
1652 log_info("Can't find the down interface.");
1655 memcpy(peer
.iabuf
, &packet
->dhcpv6_peer_address
, peer
.len
);
1656 to
.sin6_addr
= packet
->dhcpv6_peer_address
;
1658 /* Check if we should relay the carried message. */
1659 switch (msg
->msg_type
) {
1660 /* Relay-Reply of for another relay, not a client. */
1661 case DHCPV6_RELAY_REPL
:
1662 to
.sin6_port
= local_port
;
1665 case DHCPV6_ADVERTISE
:
1667 case DHCPV6_RECONFIGURE
:
1668 case DHCPV6_RELAY_FORW
:
1669 case DHCPV6_LEASEQUERY_REPLY
:
1670 log_info("Relaying %s to %s port %d down.",
1671 dhcpv6_type_names
[msg
->msg_type
],
1673 ntohs(to
.sin6_port
));
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_LEASEQUERY
:
1685 log_info("Discarding %s to %s port %d down.",
1686 dhcpv6_type_names
[msg
->msg_type
],
1688 ntohs(to
.sin6_port
));
1692 log_info("Unknown %d type to %s port %d down.",
1695 ntohs(to
.sin6_port
));
1699 /* Send the message to the downstream. */
1700 send_packet6(dp
->ifp
, (unsigned char *) relay_msg
.data
,
1701 (size_t) relay_msg
.len
, &to
);
1704 if (relay_msg
.data
!= NULL
)
1705 data_string_forget(&relay_msg
, MDL
);
1706 if (if_id
.data
!= NULL
)
1707 data_string_forget(&if_id
, MDL
);
1711 * Called by the dispatch packet handler with a decoded packet.
1714 dhcpv6(struct packet
*packet
) {
1715 struct stream_list
*dp
;
1717 /* Try all relay-replies downwards. */
1718 if (packet
->dhcpv6_msg_type
== DHCPV6_RELAY_REPL
) {
1719 process_down6(packet
);
1722 /* Others are candidates to go up if they come from down. */
1723 for (dp
= downstreams
; dp
; dp
= dp
->next
) {
1724 if (packet
->interface
!= dp
->ifp
)
1726 process_up6(packet
, dp
);
1729 /* Relay-forward could work from an unknown interface. */
1730 if (packet
->dhcpv6_msg_type
== DHCPV6_RELAY_FORW
) {
1731 process_up6(packet
, NULL
);
1735 log_info("Can't process packet from interface '%s'.",
1736 packet
->interface
->name
);
1740 /* Stub routines needed for linking with DHCP libraries. */
1742 bootp(struct packet
*packet
) {
1747 dhcp(struct packet
*packet
) {
1752 classify(struct packet
*p
, struct class *c
) {
1757 check_collection(struct packet
*p
, struct lease
*l
, struct collection
*c
) {
1762 find_class(struct class **class, const char *c1
, const char *c2
, int i
) {
1763 return ISC_R_NOTFOUND
;
1767 parse_allow_deny(struct option_cache
**oc
, struct parse
*p
, int i
) {
1772 dhcp_set_control_state(control_object_state_t oldstate
,
1773 control_object_state_t newstate
) {
1774 if (newstate
!= server_shutdown
)
1775 return ISC_R_SUCCESS
;
1777 if (no_pid_file
== ISC_FALSE
)
1778 (void) unlink(path_dhcrelay_pid
);