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";
187 static const char use_badproto
[] = "Protocol already set, %s inappropriate";
188 static const char use_v4command
[] = "Command not used for DHCPv6: %s";
189 static const char use_v6command
[] = "Command not used for DHCPv4: %s";
193 usage(const char *sfmt
, const char *sarg
) {
195 /* If desired print out the specific error message */
196 #ifdef PRINT_SPECIFIC_CL_ERRORS
198 log_error(sfmt
, sarg
);
201 log_fatal(DHCRELAY_USAGE
,
203 isc_file_basename(progname
),
205 isc_file_basename(progname
));
209 main(int argc
, char **argv
) {
212 struct server_list
*sp
= NULL
;
213 struct interface_info
*tmp
= NULL
;
214 char *service_local
= NULL
, *service_remote
= NULL
;
215 u_int16_t port_local
= 0, port_remote
= 0;
216 int no_daemon
= 0, quiet
= 0;
220 struct stream_list
*sl
= NULL
;
221 int local_family_set
= 0;
225 progname
= "dhcrelay";
230 /* Make sure that file descriptors 0(stdin), 1,(stdout), and
231 2(stderr) are open. To do this, we assume that when we
232 open a file the lowest available file descriptor is used. */
233 fd
= open("/dev/null", O_RDWR
);
235 fd
= open("/dev/null", O_RDWR
);
237 fd
= open("/dev/null", O_RDWR
);
239 log_perror
= 0; /* No sense logging to /dev/null. */
243 openlog(isc_file_basename(progname
), DHCP_LOG_OPTIONS
, LOG_DAEMON
);
246 setlogmask(LOG_UPTO(LOG_INFO
));
249 /* Set up the isc and dns library managers */
250 status
= dhcp_context_create(DHCP_CONTEXT_PRE_DB
| DHCP_CONTEXT_POST_DB
,
252 if (status
!= ISC_R_SUCCESS
)
253 log_fatal("Can't initialize context: %s",
254 isc_result_totext(status
));
256 /* Set up the OMAPI. */
257 status
= omapi_init();
258 if (status
!= ISC_R_SUCCESS
)
259 log_fatal("Can't initialize OMAPI: %s",
260 isc_result_totext(status
));
262 /* Set up the OMAPI wrappers for the interface object. */
265 for (i
= 1; i
< argc
; i
++) {
266 if (!strcmp(argv
[i
], "-4")) {
268 if (local_family_set
&& (local_family
== AF_INET6
)) {
269 usage(use_badproto
, "-4");
271 local_family_set
= 1;
272 local_family
= AF_INET
;
273 } else if (!strcmp(argv
[i
], "-6")) {
274 if (local_family_set
&& (local_family
== AF_INET
)) {
275 usage(use_badproto
, "-6");
277 local_family_set
= 1;
278 local_family
= AF_INET6
;
280 } else if (!strcmp(argv
[i
], "-d")) {
282 } else if (!strcmp(argv
[i
], "-q")) {
284 quiet_interface_discovery
= 1;
285 } else if (!strcmp(argv
[i
], "-p")) {
287 usage(use_noarg
, argv
[i
-1]);
288 local_port
= validate_port(argv
[i
]);
289 log_debug("binding to user-specified port %d",
291 } else if (!strcmp(argv
[i
], "-c")) {
294 usage(use_noarg
, argv
[i
-1]);
295 hcount
= atoi(argv
[i
]);
297 max_hop_count
= hcount
;
299 usage("Bad hop count to -c: %s", argv
[i
]);
300 } else if (!strcmp(argv
[i
], "-i")) {
302 if (local_family_set
&& (local_family
== AF_INET6
)) {
303 usage(use_v4command
, argv
[i
]);
305 local_family_set
= 1;
306 local_family
= AF_INET
;
309 usage(use_noarg
, argv
[i
-1]);
311 if (strlen(argv
[i
]) >= sizeof(tmp
->name
)) {
312 log_fatal("%s: interface name too long "
314 argv
[i
], (long)strlen(argv
[i
]));
316 status
= interface_allocate(&tmp
, MDL
);
317 if (status
!= ISC_R_SUCCESS
) {
318 log_fatal("%s: interface_allocate: %s",
320 isc_result_totext(status
));
322 strcpy(tmp
->name
, argv
[i
]);
323 interface_snorf(tmp
, INTERFACE_REQUESTED
);
324 interface_dereference(&tmp
, MDL
);
325 } else if (!strcmp(argv
[i
], "-a")) {
327 if (local_family_set
&& (local_family
== AF_INET6
)) {
328 usage(use_v4command
, argv
[i
]);
330 local_family_set
= 1;
331 local_family
= AF_INET
;
333 add_agent_options
= 1;
334 } else if (!strcmp(argv
[i
], "-A")) {
336 if (local_family_set
&& (local_family
== AF_INET6
)) {
337 usage(use_v4command
, argv
[i
]);
339 local_family_set
= 1;
340 local_family
= AF_INET
;
343 usage(use_noarg
, argv
[i
-1]);
345 dhcp_max_agent_option_packet_length
= atoi(argv
[i
]);
347 if (dhcp_max_agent_option_packet_length
> DHCP_MTU_MAX
)
348 log_fatal("%s: packet length exceeds "
349 "longest possible MTU\n",
351 } else if (!strcmp(argv
[i
], "-m")) {
353 if (local_family_set
&& (local_family
== AF_INET6
)) {
354 usage(use_v4command
, argv
[i
]);
356 local_family_set
= 1;
357 local_family
= AF_INET
;
360 usage(use_noarg
, argv
[i
-1]);
361 if (!strcasecmp(argv
[i
], "append")) {
362 agent_relay_mode
= forward_and_append
;
363 } else if (!strcasecmp(argv
[i
], "replace")) {
364 agent_relay_mode
= forward_and_replace
;
365 } else if (!strcasecmp(argv
[i
], "forward")) {
366 agent_relay_mode
= forward_untouched
;
367 } else if (!strcasecmp(argv
[i
], "discard")) {
368 agent_relay_mode
= discard
;
370 usage("Unknown argument to -m: %s", argv
[i
]);
371 } else if (!strcmp(argv
[i
], "-D")) {
373 if (local_family_set
&& (local_family
== AF_INET6
)) {
374 usage(use_v4command
, argv
[i
]);
376 local_family_set
= 1;
377 local_family
= AF_INET
;
379 drop_agent_mismatches
= 1;
381 } else if (!strcmp(argv
[i
], "-I")) {
382 if (local_family_set
&& (local_family
== AF_INET
)) {
383 usage(use_v6command
, argv
[i
]);
385 local_family_set
= 1;
386 local_family
= AF_INET6
;
387 use_if_id
= ISC_TRUE
;
388 } else if (!strcmp(argv
[i
], "-l")) {
389 if (local_family_set
&& (local_family
== AF_INET
)) {
390 usage(use_v6command
, argv
[i
]);
392 local_family_set
= 1;
393 local_family
= AF_INET6
;
394 if (downstreams
!= NULL
)
395 use_if_id
= ISC_TRUE
;
397 usage(use_noarg
, argv
[i
-1]);
398 sl
= parse_downstream(argv
[i
]);
399 sl
->next
= downstreams
;
401 } else if (!strcmp(argv
[i
], "-u")) {
402 if (local_family_set
&& (local_family
== AF_INET
)) {
403 usage(use_v6command
, argv
[i
]);
405 local_family_set
= 1;
406 local_family
= AF_INET6
;
408 usage(use_noarg
, argv
[i
-1]);
409 sl
= parse_upstream(argv
[i
]);
410 sl
->next
= upstreams
;
412 } else if (!strcmp(argv
[i
], "-s")) {
413 if (local_family_set
&& (local_family
== AF_INET
)) {
414 usage(use_v6command
, argv
[i
]);
416 local_family_set
= 1;
417 local_family
= AF_INET6
;
419 usage(use_noarg
, argv
[i
-1]);
420 dhcrelay_sub_id
= argv
[i
];
422 } else if (!strcmp(argv
[i
], "-pf")) {
424 usage(use_noarg
, argv
[i
-1]);
425 path_dhcrelay_pid
= argv
[i
];
426 no_dhcrelay_pid
= ISC_TRUE
;
427 } else if (!strcmp(argv
[i
], "--no-pid")) {
428 no_pid_file
= ISC_TRUE
;
429 } else if (!strcmp(argv
[i
], "--version")) {
430 log_info("isc-dhcrelay-%s", PACKAGE_VERSION
);
432 } else if (!strcmp(argv
[i
], "--help") ||
433 !strcmp(argv
[i
], "-h")) {
434 log_info(DHCRELAY_USAGE
,
436 isc_file_basename(progname
),
438 isc_file_basename(progname
));
440 } else if (argv
[i
][0] == '-') {
441 usage("Unknown command: %s", argv
[i
]);
444 struct in_addr ia
, *iap
= NULL
;
447 if (local_family_set
&& (local_family
== AF_INET6
)) {
448 usage(use_v4command
, argv
[i
]);
450 local_family_set
= 1;
451 local_family
= AF_INET
;
453 if (inet_aton(argv
[i
], &ia
)) {
456 he
= gethostbyname(argv
[i
]);
458 log_error("%s: host unknown", argv
[i
]);
460 iap
= ((struct in_addr
*)
466 sp
= ((struct server_list
*)
467 dmalloc(sizeof *sp
, MDL
));
469 log_fatal("no memory for server.\n");
472 memcpy(&sp
->to
.sin_addr
, iap
, sizeof *iap
);
478 * If the user didn't specify a pid file directly
479 * find one from environment variables or defaults
481 if (no_dhcrelay_pid
== ISC_FALSE
) {
482 if (local_family
== AF_INET
) {
483 path_dhcrelay_pid
= getenv("PATH_DHCRELAY_PID");
484 if (path_dhcrelay_pid
== NULL
)
485 path_dhcrelay_pid
= _PATH_DHCRELAY_PID
;
489 path_dhcrelay_pid
= getenv("PATH_DHCRELAY6_PID");
490 if (path_dhcrelay_pid
== NULL
)
491 path_dhcrelay_pid
= _PATH_DHCRELAY6_PID
;
497 log_info("%s %s", message
, PACKAGE_VERSION
);
504 /* Set default port */
505 if (local_family
== AF_INET
) {
506 service_local
= "bootps";
507 service_remote
= "bootpc";
508 port_local
= htons(67);
509 port_remote
= htons(68);
513 service_local
= "dhcpv6-server";
514 service_remote
= "dhcpv6-client";
515 port_local
= htons(547);
516 port_remote
= htons(546);
521 ent
= getservbyname(service_local
, "udp");
523 local_port
= ent
->s_port
;
525 local_port
= port_local
;
527 ent
= getservbyname(service_remote
, "udp");
529 remote_port
= ent
->s_port
;
531 remote_port
= port_remote
;
536 if (local_family
== AF_INET
) {
537 /* We need at least one server */
538 if (servers
== NULL
) {
539 log_fatal("No servers specified.");
543 /* Set up the server sockaddrs. */
544 for (sp
= servers
; sp
; sp
= sp
->next
) {
545 sp
->to
.sin_port
= local_port
;
546 sp
->to
.sin_family
= AF_INET
;
548 sp
->to
.sin_len
= sizeof sp
->to
;
556 /* We need at least one upstream and one downstream interface */
557 if (upstreams
== NULL
|| downstreams
== NULL
) {
558 log_info("Must specify at least one lower "
559 "and one upper interface.\n");
563 /* Set up the initial dhcp option universe. */
564 initialize_common_option_spaces();
566 /* Check requested options. */
567 code
= D6O_RELAY_MSG
;
568 if (!option_code_hash_lookup(&requested_opts
[0],
569 dhcpv6_universe
.code_hash
,
571 log_fatal("Unable to find the RELAY_MSG "
572 "option definition.");
573 code
= D6O_INTERFACE_ID
;
574 if (!option_code_hash_lookup(&requested_opts
[1],
575 dhcpv6_universe
.code_hash
,
577 log_fatal("Unable to find the INTERFACE_ID "
578 "option definition.");
582 /* Get the current time... */
583 gettimeofday(&cur_tv
, NULL
);
585 /* Discover all the network interfaces. */
586 discover_interfaces(DISCOVER_RELAY
);
589 if (local_family
== AF_INET6
)
593 /* Become a daemon... */
601 if ((pid
= fork()) < 0)
602 log_fatal("Can't fork daemon: %m");
606 if (no_pid_file
== ISC_FALSE
) {
607 pfdesc
= open(path_dhcrelay_pid
,
608 O_CREAT
| O_TRUNC
| O_WRONLY
, 0644);
611 log_error("Can't create %s: %m",
614 pf
= fdopen(pfdesc
, "w");
616 log_error("Can't fdopen %s: %m",
619 fprintf(pf
, "%ld\n",(long)getpid());
630 IGNORE_RET (chdir("/"));
633 /* Set up the packet handler... */
634 if (local_family
== AF_INET
)
635 bootp_packet_handler
= do_relay4
;
638 dhcpv6_packet_handler
= do_packet6
;
641 #if defined(ENABLE_GENTLE_SHUTDOWN)
642 /* no signal handlers until we deal with the side effects */
643 /* install signal handlers */
644 signal(SIGINT
, dhcp_signal_handler
); /* control-c */
645 signal(SIGTERM
, dhcp_signal_handler
); /* kill */
648 /* Start dispatching packets and timeouts... */
651 /* In fact dispatch() never returns. */
656 do_relay4(struct interface_info
*ip
, struct dhcp_packet
*packet
,
657 unsigned int length
, unsigned int from_port
, struct iaddr from
,
658 struct hardware
*hfrom
) {
659 struct server_list
*sp
;
660 struct sockaddr_in to
;
661 struct interface_info
*out
;
662 struct hardware hto
, *htop
;
664 if (packet
->hlen
> sizeof packet
->chaddr
) {
665 log_info("Discarding packet with invalid hlen, received on "
666 "%s interface.", ip
->name
);
669 if (ip
->address_count
< 1 || ip
->addresses
== NULL
) {
670 log_info("Discarding packet received on %s interface that "
671 "has no IPv4 address assigned.", ip
->name
);
675 /* Find the interface that corresponds to the giaddr
677 if (packet
->giaddr
.s_addr
) {
678 for (out
= interfaces
; out
; out
= out
->next
) {
681 for (i
= 0 ; i
< out
->address_count
; i
++ ) {
682 if (out
->addresses
[i
].s_addr
==
683 packet
->giaddr
.s_addr
) {
696 /* If it's a bootreply, forward it to the client. */
697 if (packet
->op
== BOOTREPLY
) {
698 if (!(packet
->flags
& htons(BOOTP_BROADCAST
)) &&
699 can_unicast_without_arp(out
)) {
700 to
.sin_addr
= packet
->yiaddr
;
701 to
.sin_port
= remote_port
;
703 /* and hardware address is not broadcast */
706 to
.sin_addr
.s_addr
= htonl(INADDR_BROADCAST
);
707 to
.sin_port
= remote_port
;
709 /* hardware address is broadcast */
712 to
.sin_family
= AF_INET
;
714 to
.sin_len
= sizeof to
;
717 memcpy(&hto
.hbuf
[1], packet
->chaddr
, packet
->hlen
);
718 hto
.hbuf
[0] = packet
->htype
;
719 hto
.hlen
= packet
->hlen
+ 1;
721 /* Wipe out the agent relay options and, if possible, figure
722 out which interface to use based on the contents of the
723 option that we put on the request to which the server is
726 strip_relay_agent_options(ip
, &out
, packet
, length
)))
730 log_error("Packet to bogus giaddr %s.\n",
731 inet_ntoa(packet
->giaddr
));
732 ++bogus_giaddr_drops
;
736 if (send_packet(out
, NULL
, packet
, length
, out
->addresses
[0],
738 ++server_packet_errors
;
740 log_debug("Forwarded BOOTREPLY for %s to %s",
741 print_hw_addr(packet
->htype
, packet
->hlen
,
743 inet_ntoa(to
.sin_addr
));
745 ++server_packets_relayed
;
750 /* If giaddr matches one of our addresses, ignore the packet -
755 /* Add relay agent options if indicated. If something goes wrong,
757 if (!(length
= add_relay_agent_options(ip
, packet
, length
,
761 /* If giaddr is not already set, Set it so the server can
762 figure out what net it's from and so that we can later
763 forward the response to the correct net. If it's already
764 set, the response will be sent directly to the relay agent
765 that set giaddr, so we won't see it. */
766 if (!packet
->giaddr
.s_addr
)
767 packet
->giaddr
= ip
->addresses
[0];
768 if (packet
->hops
< max_hop_count
)
769 packet
->hops
= packet
->hops
+ 1;
773 /* Otherwise, it's a BOOTREQUEST, so forward it to all the
775 for (sp
= servers
; sp
; sp
= sp
->next
) {
776 if (send_packet((fallback_interface
777 ? fallback_interface
: interfaces
),
778 NULL
, packet
, length
, ip
->addresses
[0],
779 &sp
->to
, NULL
) < 0) {
780 ++client_packet_errors
;
782 log_debug("Forwarded BOOTREQUEST for %s to %s",
783 print_hw_addr(packet
->htype
, packet
->hlen
,
785 inet_ntoa(sp
->to
.sin_addr
));
786 ++client_packets_relayed
;
792 /* Strip any Relay Agent Information options from the DHCP packet
793 option buffer. If there is a circuit ID suboption, look up the
794 outgoing interface based upon it. */
797 strip_relay_agent_options(struct interface_info
*in
,
798 struct interface_info
**out
,
799 struct dhcp_packet
*packet
,
802 u_int8_t
*op
, *nextop
, *sp
, *max
;
803 int good_agent_option
= 0;
806 /* If we're not adding agent options to packets, we're not taking
808 if (!add_agent_options
)
811 /* If there's no cookie, it's a bootp packet, so we should just
812 forward it unchanged. */
813 if (memcmp(packet
->options
, DHCP_OPTIONS_COOKIE
, 4))
816 max
= ((u_int8_t
*)packet
) + length
;
817 sp
= op
= &packet
->options
[4];
821 /* Skip padding... */
829 /* If we see a message type, it's a DHCP packet. */
830 case DHO_DHCP_MESSAGE_TYPE
:
835 /* Quit immediately if we hit an End option. */
841 case DHO_DHCP_AGENT_OPTIONS
:
842 /* We shouldn't see a relay agent option in a
843 packet before we've seen the DHCP packet type,
844 but if we do, we have to leave it alone. */
848 /* Do not process an agent option if it exceeds the
849 * buffer. Fail this packet.
851 nextop
= op
+ op
[1] + 2;
855 status
= find_interface_by_agent_option(packet
,
858 if (status
== -1 && drop_agent_mismatches
)
861 good_agent_option
= 1;
866 /* Skip over other options. */
868 /* Fail if processing this option will exceed the
869 * buffer(op[1] is malformed).
871 nextop
= op
+ op
[1] + 2;
876 memmove(sp
, op
, op
[1] + 2);
887 /* If it's not a DHCP packet, we're not supposed to touch it. */
891 /* If none of the agent options we found matched, or if we didn't
892 find any agent options, count this packet as not having any
893 matching agent options, and if we're relying on agent options
894 to determine the outgoing interface, drop the packet. */
896 if (!good_agent_option
) {
897 ++missing_agent_option
;
898 if (drop_agent_mismatches
)
902 /* Adjust the length... */
904 length
= sp
-((u_int8_t
*)packet
);
906 /* Make sure the packet isn't short(this is unlikely,
908 if (length
< BOOTP_MIN_LEN
) {
909 memset(sp
, DHO_PAD
, BOOTP_MIN_LEN
- length
);
910 length
= BOOTP_MIN_LEN
;
917 /* Find an interface that matches the circuit ID specified in the
918 Relay Agent Information option. If one is found, store it through
919 the pointer given; otherwise, leave the existing pointer alone.
921 We actually deviate somewhat from the current specification here:
922 if the option buffer is corrupt, we suggest that the caller not
923 respond to this packet. If the circuit ID doesn't match any known
924 interface, we suggest that the caller to drop the packet. Only if
925 we find a circuit ID that matches an existing interface do we tell
926 the caller to go ahead and process the packet. */
929 find_interface_by_agent_option(struct dhcp_packet
*packet
,
930 struct interface_info
**out
,
931 u_int8_t
*buf
, int len
) {
933 u_int8_t
*circuit_id
= 0;
934 unsigned circuit_id_len
= 0;
935 struct interface_info
*ip
;
938 /* If the next agent option overflows the end of the
939 packet, the agent option buffer is corrupt. */
941 i
+ buf
[i
+ 1] + 2 > len
) {
942 ++corrupt_agent_options
;
946 /* Remember where the circuit ID is... */
948 circuit_id
= &buf
[i
+ 2];
949 circuit_id_len
= buf
[i
+ 1];
950 i
+= circuit_id_len
+ 2;
959 /* If there's no circuit ID, it's not really ours, tell the caller
962 ++missing_circuit_id
;
966 /* Scan the interface list looking for an interface whose
967 name matches the one specified in circuit_id. */
969 for (ip
= interfaces
; ip
; ip
= ip
->next
) {
970 if (ip
->circuit_id
&&
971 ip
->circuit_id_len
== circuit_id_len
&&
972 !memcmp(ip
->circuit_id
, circuit_id
, circuit_id_len
))
976 /* If we got a match, use it. */
982 /* If we didn't get a match, the circuit ID was bogus. */
988 * Examine a packet to see if it's a candidate to have a Relay
989 * Agent Information option tacked onto its tail. If it is, tack
993 add_relay_agent_options(struct interface_info
*ip
, struct dhcp_packet
*packet
,
994 unsigned length
, struct in_addr giaddr
) {
995 int is_dhcp
= 0, mms
;
997 u_int8_t
*op
, *nextop
, *sp
, *max
, *end_pad
= NULL
;
999 /* If we're not adding agent options to packets, we can skip
1001 if (!add_agent_options
)
1004 /* If there's no cookie, it's a bootp packet, so we should just
1005 forward it unchanged. */
1006 if (memcmp(packet
->options
, DHCP_OPTIONS_COOKIE
, 4))
1009 max
= ((u_int8_t
*)packet
) + dhcp_max_agent_option_packet_length
;
1011 /* Commence processing after the cookie. */
1012 sp
= op
= &packet
->options
[4];
1016 /* Skip padding... */
1018 /* Remember the first pad byte so we can commandeer
1021 * XXX: Is this really a good idea? Sure, we can
1022 * seemingly reduce the packet while we're looking,
1023 * but if the packet was signed by the client then
1024 * this padding is part of the checksum(RFC3118),
1025 * and its nonpresence would break authentication.
1027 if (end_pad
== NULL
)
1037 /* If we see a message type, it's a DHCP packet. */
1038 case DHO_DHCP_MESSAGE_TYPE
:
1043 * If there's a maximum message size option, we
1044 * should pay attention to it
1046 case DHO_DHCP_MAX_MESSAGE_SIZE
:
1047 mms
= ntohs(*(op
+ 2));
1048 if (mms
< dhcp_max_agent_option_packet_length
&&
1049 mms
>= DHCP_MTU_MIN
)
1050 max
= ((u_int8_t
*)packet
) + mms
;
1053 /* Quit immediately if we hit an End option. */
1057 case DHO_DHCP_AGENT_OPTIONS
:
1058 /* We shouldn't see a relay agent option in a
1059 packet before we've seen the DHCP packet type,
1060 but if we do, we have to leave it alone. */
1066 /* There's already a Relay Agent Information option
1067 in this packet. How embarrassing. Decide what
1068 to do based on the mode the user specified. */
1070 switch(agent_relay_mode
) {
1071 case forward_and_append
:
1073 case forward_untouched
:
1077 case forward_and_replace
:
1082 /* Skip over the agent option and start copying
1083 if we aren't copying already. */
1088 /* Skip over other options. */
1090 /* Fail if processing this option will exceed the
1091 * buffer(op[1] is malformed).
1093 nextop
= op
+ op
[1] + 2;
1100 memmove(sp
, op
, op
[1] + 2);
1111 /* If it's not a DHCP packet, we're not supposed to touch it. */
1115 /* If the packet was padded out, we can store the agent option
1116 at the beginning of the padding. */
1118 if (end_pad
!= NULL
)
1122 /* Remember where the end of the packet was after parsing
1127 /* Sanity check. Had better not ever happen. */
1128 if ((ip
->circuit_id_len
> 255) ||(ip
->circuit_id_len
< 1))
1129 log_fatal("Circuit ID length %d out of range [1-255] on "
1130 "%s\n", ip
->circuit_id_len
, ip
->name
);
1131 optlen
= ip
->circuit_id_len
+ 2; /* RAI_CIRCUIT_ID + len */
1133 if (ip
->remote_id
) {
1134 if (ip
->remote_id_len
> 255 || ip
->remote_id_len
< 1)
1135 log_fatal("Remote ID length %d out of range [1-255] "
1136 "on %s\n", ip
->circuit_id_len
, ip
->name
);
1137 optlen
+= ip
->remote_id_len
+ 2; /* RAI_REMOTE_ID + len */
1140 /* We do not support relay option fragmenting(multiple options to
1141 * support an option data exceeding 255 bytes).
1143 if ((optlen
< 3) ||(optlen
> 255))
1144 log_fatal("Total agent option length(%u) out of range "
1145 "[3 - 255] on %s\n", optlen
, ip
->name
);
1148 * Is there room for the option, its code+len, and DHO_END?
1149 * If not, forward without adding the option.
1151 if (max
- sp
>= optlen
+ 3) {
1152 log_debug("Adding %d-byte relay agent option", optlen
+ 3);
1154 /* Okay, cons up *our* Relay Agent Information option. */
1155 *sp
++ = DHO_DHCP_AGENT_OPTIONS
;
1158 /* Copy in the circuit id... */
1159 *sp
++ = RAI_CIRCUIT_ID
;
1160 *sp
++ = ip
->circuit_id_len
;
1161 memcpy(sp
, ip
->circuit_id
, ip
->circuit_id_len
);
1162 sp
+= ip
->circuit_id_len
;
1164 /* Copy in remote ID... */
1165 if (ip
->remote_id
) {
1166 *sp
++ = RAI_REMOTE_ID
;
1167 *sp
++ = ip
->remote_id_len
;
1168 memcpy(sp
, ip
->remote_id
, ip
->remote_id_len
);
1169 sp
+= ip
->remote_id_len
;
1172 ++agent_option_errors
;
1173 log_error("No room in packet (used %d of %d) "
1174 "for %d-byte relay agent option: omitted",
1175 (int) (sp
- ((u_int8_t
*) packet
)),
1176 (int) (max
- ((u_int8_t
*) packet
)),
1181 * Deposit an END option unless the packet is full (shouldn't
1187 /* Recalculate total packet length. */
1188 length
= sp
-((u_int8_t
*)packet
);
1190 /* Make sure the packet isn't short(this is unlikely, but WTH) */
1191 if (length
< BOOTP_MIN_LEN
) {
1192 memset(sp
, DHO_PAD
, BOOTP_MIN_LEN
- length
);
1193 return (BOOTP_MIN_LEN
);
1201 * Parse a downstream argument: [address%]interface[#index].
1203 static struct stream_list
*
1204 parse_downstream(char *arg
) {
1205 struct stream_list
*dp
, *up
;
1206 struct interface_info
*ifp
= NULL
;
1207 char *ifname
, *addr
, *iid
;
1208 isc_result_t status
;
1210 if (!supports_multiple_interfaces(ifp
) &&
1211 (downstreams
!= NULL
))
1212 log_fatal("No support for multiple interfaces.");
1214 /* Decode the argument. */
1215 ifname
= strchr(arg
, '%');
1216 if (ifname
== NULL
) {
1223 iid
= strchr(ifname
, '#');
1227 if (strlen(ifname
) >= sizeof(ifp
->name
)) {
1228 usage("Interface name '%s' too long", ifname
);
1231 /* Don't declare twice. */
1232 for (dp
= downstreams
; dp
; dp
= dp
->next
) {
1233 if (strcmp(ifname
, dp
->ifp
->name
) == 0)
1234 log_fatal("Down interface '%s' declared twice.",
1238 /* Share with up side? */
1239 for (up
= upstreams
; up
; up
= up
->next
) {
1240 if (strcmp(ifname
, up
->ifp
->name
) == 0) {
1241 log_info("parse_downstream: Interface '%s' is "
1242 "both down and up.", ifname
);
1248 /* New interface. */
1250 status
= interface_allocate(&ifp
, MDL
);
1251 if (status
!= ISC_R_SUCCESS
)
1252 log_fatal("%s: interface_allocate: %s",
1253 arg
, isc_result_totext(status
));
1254 strcpy(ifp
->name
, ifname
);
1256 interface_reference(&ifp
->next
, interfaces
, MDL
);
1257 interface_dereference(&interfaces
, MDL
);
1259 interface_reference(&interfaces
, ifp
, MDL
);
1261 ifp
->flags
|= INTERFACE_REQUESTED
| INTERFACE_DOWNSTREAM
;
1263 /* New downstream. */
1264 dp
= (struct stream_list
*) dmalloc(sizeof(*dp
), MDL
);
1266 log_fatal("No memory for downstream.");
1273 /* !addr case handled by setup. */
1274 if (addr
&& (inet_pton(AF_INET6
, addr
, &dp
->link
.sin6_addr
) <= 0))
1275 log_fatal("Bad link address '%s'", addr
);
1281 * Parse an upstream argument: [address]%interface.
1283 static struct stream_list
*
1284 parse_upstream(char *arg
) {
1285 struct stream_list
*up
, *dp
;
1286 struct interface_info
*ifp
= NULL
;
1287 char *ifname
, *addr
;
1288 isc_result_t status
;
1290 /* Decode the argument. */
1291 ifname
= strchr(arg
, '%');
1292 if (ifname
== NULL
) {
1294 addr
= All_DHCP_Servers
;
1299 if (strlen(ifname
) >= sizeof(ifp
->name
)) {
1300 log_fatal("Interface name '%s' too long", ifname
);
1303 /* Shared up interface? */
1304 for (up
= upstreams
; up
; up
= up
->next
) {
1305 if (strcmp(ifname
, up
->ifp
->name
) == 0) {
1310 for (dp
= downstreams
; dp
; dp
= dp
->next
) {
1311 if (strcmp(ifname
, dp
->ifp
->name
) == 0) {
1312 log_info("parse_upstream: Interface '%s' is "
1313 "both down and up.", ifname
);
1319 /* New interface. */
1321 status
= interface_allocate(&ifp
, MDL
);
1322 if (status
!= ISC_R_SUCCESS
)
1323 log_fatal("%s: interface_allocate: %s",
1324 arg
, isc_result_totext(status
));
1325 strcpy(ifp
->name
, ifname
);
1327 interface_reference(&ifp
->next
, interfaces
, MDL
);
1328 interface_dereference(&interfaces
, MDL
);
1330 interface_reference(&interfaces
, ifp
, MDL
);
1332 ifp
->flags
|= INTERFACE_REQUESTED
| INTERFACE_UPSTREAM
;
1335 up
= (struct stream_list
*) dmalloc(sizeof(*up
), MDL
);
1337 log_fatal("No memory for upstream.");
1341 if (inet_pton(AF_INET6
, addr
, &up
->link
.sin6_addr
) <= 0)
1342 log_fatal("Bad address %s", addr
);
1348 * Setup downstream interfaces.
1351 setup_streams(void) {
1352 struct stream_list
*dp
, *up
;
1354 isc_boolean_t link_is_set
;
1356 for (dp
= downstreams
; dp
; dp
= dp
->next
) {
1357 /* Check interface */
1358 if (dp
->ifp
->v6address_count
== 0)
1359 log_fatal("Interface '%s' has no IPv6 addresses.",
1362 /* Check/set link. */
1363 if (IN6_IS_ADDR_UNSPECIFIED(&dp
->link
.sin6_addr
))
1364 link_is_set
= ISC_FALSE
;
1366 link_is_set
= ISC_TRUE
;
1367 for (i
= 0; i
< dp
->ifp
->v6address_count
; i
++) {
1368 if (IN6_IS_ADDR_LINKLOCAL(&dp
->ifp
->v6addresses
[i
]))
1372 if (!memcmp(&dp
->ifp
->v6addresses
[i
],
1373 &dp
->link
.sin6_addr
,
1374 sizeof(dp
->link
.sin6_addr
)))
1377 if (i
== dp
->ifp
->v6address_count
)
1378 log_fatal("Interface %s does not have global IPv6 "
1379 "address assigned.", dp
->ifp
->name
);
1381 memcpy(&dp
->link
.sin6_addr
,
1382 &dp
->ifp
->v6addresses
[i
],
1383 sizeof(dp
->link
.sin6_addr
));
1385 /* Set interface-id. */
1387 dp
->id
= dp
->ifp
->index
;
1390 for (up
= upstreams
; up
; up
= up
->next
) {
1391 up
->link
.sin6_port
= local_port
;
1392 up
->link
.sin6_family
= AF_INET6
;
1394 up
->link
.sin6_len
= sizeof(up
->link
);
1397 if (up
->ifp
->v6address_count
== 0)
1398 log_fatal("Interface '%s' has no IPv6 addresses.",
1401 /* RFC 3315 Sec 20 - "If the relay agent relays messages to
1402 * the All_DHCP_Servers address or other multicast addresses,
1403 * it sets the Hop Limit field to 32." */
1404 if (IN6_IS_ADDR_MULTICAST(&up
->link
.sin6_addr
)) {
1405 set_multicast_hop_limit(up
->ifp
, HOP_COUNT_LIMIT
);
1411 * Add DHCPv6 agent options here.
1413 static const int required_forw_opts
[] = {
1421 * Process a packet upwards, i.e., from client to server.
1424 process_up6(struct packet
*packet
, struct stream_list
*dp
) {
1425 char forw_data
[65535];
1427 struct dhcpv6_relay_packet
*relay
;
1428 struct option_state
*opts
;
1429 struct stream_list
*up
;
1431 /* Check if the message should be relayed to the server. */
1432 switch (packet
->dhcpv6_msg_type
) {
1433 case DHCPV6_SOLICIT
:
1434 case DHCPV6_REQUEST
:
1435 case DHCPV6_CONFIRM
:
1438 case DHCPV6_RELEASE
:
1439 case DHCPV6_DECLINE
:
1440 case DHCPV6_INFORMATION_REQUEST
:
1441 case DHCPV6_RELAY_FORW
:
1442 case DHCPV6_LEASEQUERY
:
1443 log_info("Relaying %s from %s port %d going up.",
1444 dhcpv6_type_names
[packet
->dhcpv6_msg_type
],
1445 piaddr(packet
->client_addr
),
1446 ntohs(packet
->client_port
));
1449 case DHCPV6_ADVERTISE
:
1451 case DHCPV6_RECONFIGURE
:
1452 case DHCPV6_RELAY_REPL
:
1453 case DHCPV6_LEASEQUERY_REPLY
:
1454 log_info("Discarding %s from %s port %d going up.",
1455 dhcpv6_type_names
[packet
->dhcpv6_msg_type
],
1456 piaddr(packet
->client_addr
),
1457 ntohs(packet
->client_port
));
1461 log_info("Unknown %d type from %s port %d going up.",
1462 packet
->dhcpv6_msg_type
,
1463 piaddr(packet
->client_addr
),
1464 ntohs(packet
->client_port
));
1468 /* Build the relay-forward header. */
1469 relay
= (struct dhcpv6_relay_packet
*) forw_data
;
1470 cursor
= offsetof(struct dhcpv6_relay_packet
, options
);
1471 relay
->msg_type
= DHCPV6_RELAY_FORW
;
1472 if (packet
->dhcpv6_msg_type
== DHCPV6_RELAY_FORW
) {
1473 if (packet
->dhcpv6_hop_count
>= max_hop_count
) {
1474 log_info("Hop count exceeded,");
1477 relay
->hop_count
= packet
->dhcpv6_hop_count
+ 1;
1479 memcpy(&relay
->link_address
, &dp
->link
.sin6_addr
, 16);
1481 /* On smart relay add: && !global. */
1482 if (!use_if_id
&& downstreams
->next
) {
1483 log_info("Shan't get back the interface.");
1486 memset(&relay
->link_address
, 0, 16);
1489 relay
->hop_count
= 0;
1492 memcpy(&relay
->link_address
, &dp
->link
.sin6_addr
, 16);
1494 memcpy(&relay
->peer_address
, packet
->client_addr
.iabuf
, 16);
1496 /* Get an option state. */
1498 if (!option_state_allocate(&opts
, MDL
)) {
1499 log_fatal("No memory for upwards options.");
1502 /* Add an interface-id (if used). */
1508 } else if (!downstreams
->next
) {
1509 if_id
= downstreams
->id
;
1511 log_info("Don't know the interface.");
1512 option_state_dereference(&opts
, MDL
);
1516 if (!save_option_buffer(&dhcpv6_universe
, opts
,
1517 NULL
, (unsigned char *) &if_id
,
1519 D6O_INTERFACE_ID
, 0)) {
1520 log_error("Can't save interface-id.");
1521 option_state_dereference(&opts
, MDL
);
1526 /* Add a subscriber-id if desired. */
1527 /* This is for testing rather than general use */
1528 if (dhcrelay_sub_id
!= NULL
) {
1529 if (!save_option_buffer(&dhcpv6_universe
, opts
, NULL
,
1530 (unsigned char *) dhcrelay_sub_id
,
1531 strlen(dhcrelay_sub_id
),
1532 D6O_SUBSCRIBER_ID
, 0)) {
1533 log_error("Can't save subsriber-id.");
1534 option_state_dereference(&opts
, MDL
);
1540 /* Add the relay-msg carrying the packet. */
1541 if (!save_option_buffer(&dhcpv6_universe
, opts
,
1542 NULL
, (unsigned char *) packet
->raw
,
1543 packet
->packet_length
,
1544 D6O_RELAY_MSG
, 0)) {
1545 log_error("Can't save relay-msg.");
1546 option_state_dereference(&opts
, MDL
);
1550 /* Finish the relay-forward message. */
1551 cursor
+= store_options6(forw_data
+ cursor
,
1552 sizeof(forw_data
) - cursor
,
1554 required_forw_opts
, NULL
);
1555 option_state_dereference(&opts
, MDL
);
1557 /* Send it to all upstreams. */
1558 for (up
= upstreams
; up
; up
= up
->next
) {
1559 send_packet6(up
->ifp
, (unsigned char *) forw_data
,
1560 (size_t) cursor
, &up
->link
);
1565 * Process a packet downwards, i.e., from server to client.
1568 process_down6(struct packet
*packet
) {
1569 struct stream_list
*dp
;
1570 struct option_cache
*oc
;
1571 struct data_string relay_msg
;
1572 const struct dhcpv6_packet
*msg
;
1573 struct data_string if_id
;
1574 struct sockaddr_in6 to
;
1577 /* The packet must be a relay-reply message. */
1578 if (packet
->dhcpv6_msg_type
!= DHCPV6_RELAY_REPL
) {
1579 if (packet
->dhcpv6_msg_type
< dhcpv6_type_name_max
)
1580 log_info("Discarding %s from %s port %d going down.",
1581 dhcpv6_type_names
[packet
->dhcpv6_msg_type
],
1582 piaddr(packet
->client_addr
),
1583 ntohs(packet
->client_port
));
1585 log_info("Unknown %d type from %s port %d going down.",
1586 packet
->dhcpv6_msg_type
,
1587 piaddr(packet
->client_addr
),
1588 ntohs(packet
->client_port
));
1593 memset(&relay_msg
, 0, sizeof(relay_msg
));
1594 memset(&if_id
, 0, sizeof(if_id
));
1595 memset(&to
, 0, sizeof(to
));
1596 to
.sin6_family
= AF_INET6
;
1598 to
.sin6_len
= sizeof(to
);
1600 to
.sin6_port
= remote_port
;
1603 /* Get the relay-msg option (carrying the message to relay). */
1604 oc
= lookup_option(&dhcpv6_universe
, packet
->options
, D6O_RELAY_MSG
);
1606 log_info("No relay-msg.");
1609 if (!evaluate_option_cache(&relay_msg
, packet
, NULL
, NULL
,
1610 packet
->options
, NULL
,
1611 &global_scope
, oc
, MDL
) ||
1612 (relay_msg
.len
< offsetof(struct dhcpv6_packet
, options
))) {
1613 log_error("Can't evaluate relay-msg.");
1616 msg
= (const struct dhcpv6_packet
*) relay_msg
.data
;
1618 /* Get the interface-id (if exists) and the downstream. */
1619 oc
= lookup_option(&dhcpv6_universe
, packet
->options
,
1624 if (!evaluate_option_cache(&if_id
, packet
, NULL
, NULL
,
1625 packet
->options
, NULL
,
1626 &global_scope
, oc
, MDL
) ||
1627 (if_id
.len
!= sizeof(int))) {
1628 log_info("Can't evaluate interface-id.");
1631 memcpy(&if_index
, if_id
.data
, sizeof(int));
1632 for (dp
= downstreams
; dp
; dp
= dp
->next
) {
1633 if (dp
->id
== if_index
)
1638 /* Require an interface-id. */
1639 log_info("No interface-id.");
1642 for (dp
= downstreams
; dp
; dp
= dp
->next
) {
1643 /* Get the first matching one. */
1644 if (!memcmp(&dp
->link
.sin6_addr
,
1645 &packet
->dhcpv6_link_address
,
1646 sizeof(struct in6_addr
)))
1650 /* Why bother when there is no choice. */
1651 if (!dp
&& downstreams
&& !downstreams
->next
)
1654 log_info("Can't find the down interface.");
1657 memcpy(peer
.iabuf
, &packet
->dhcpv6_peer_address
, peer
.len
);
1658 to
.sin6_addr
= packet
->dhcpv6_peer_address
;
1660 /* Check if we should relay the carried message. */
1661 switch (msg
->msg_type
) {
1662 /* Relay-Reply of for another relay, not a client. */
1663 case DHCPV6_RELAY_REPL
:
1664 to
.sin6_port
= local_port
;
1667 case DHCPV6_ADVERTISE
:
1669 case DHCPV6_RECONFIGURE
:
1670 case DHCPV6_RELAY_FORW
:
1671 case DHCPV6_LEASEQUERY_REPLY
:
1672 log_info("Relaying %s to %s port %d down.",
1673 dhcpv6_type_names
[msg
->msg_type
],
1675 ntohs(to
.sin6_port
));
1678 case DHCPV6_SOLICIT
:
1679 case DHCPV6_REQUEST
:
1680 case DHCPV6_CONFIRM
:
1683 case DHCPV6_RELEASE
:
1684 case DHCPV6_DECLINE
:
1685 case DHCPV6_INFORMATION_REQUEST
:
1686 case DHCPV6_LEASEQUERY
:
1687 log_info("Discarding %s to %s port %d down.",
1688 dhcpv6_type_names
[msg
->msg_type
],
1690 ntohs(to
.sin6_port
));
1694 log_info("Unknown %d type to %s port %d down.",
1697 ntohs(to
.sin6_port
));
1701 /* Send the message to the downstream. */
1702 send_packet6(dp
->ifp
, (unsigned char *) relay_msg
.data
,
1703 (size_t) relay_msg
.len
, &to
);
1706 if (relay_msg
.data
!= NULL
)
1707 data_string_forget(&relay_msg
, MDL
);
1708 if (if_id
.data
!= NULL
)
1709 data_string_forget(&if_id
, MDL
);
1713 * Called by the dispatch packet handler with a decoded packet.
1716 dhcpv6(struct packet
*packet
) {
1717 struct stream_list
*dp
;
1719 /* Try all relay-replies downwards. */
1720 if (packet
->dhcpv6_msg_type
== DHCPV6_RELAY_REPL
) {
1721 process_down6(packet
);
1724 /* Others are candidates to go up if they come from down. */
1725 for (dp
= downstreams
; dp
; dp
= dp
->next
) {
1726 if (packet
->interface
!= dp
->ifp
)
1728 process_up6(packet
, dp
);
1731 /* Relay-forward could work from an unknown interface. */
1732 if (packet
->dhcpv6_msg_type
== DHCPV6_RELAY_FORW
) {
1733 process_up6(packet
, NULL
);
1737 log_info("Can't process packet from interface '%s'.",
1738 packet
->interface
->name
);
1742 /* Stub routines needed for linking with DHCP libraries. */
1744 bootp(struct packet
*packet
) {
1749 dhcp(struct packet
*packet
) {
1754 classify(struct packet
*p
, struct class *c
) {
1759 check_collection(struct packet
*p
, struct lease
*l
, struct collection
*c
) {
1764 find_class(struct class **class, const char *c1
, const char *c2
, int i
) {
1765 return ISC_R_NOTFOUND
;
1769 parse_allow_deny(struct option_cache
**oc
, struct parse
*p
, int i
) {
1774 dhcp_set_control_state(control_object_state_t oldstate
,
1775 control_object_state_t newstate
) {
1776 if (newstate
!= server_shutdown
)
1777 return ISC_R_SUCCESS
;
1779 if (no_pid_file
== ISC_FALSE
)
1780 (void) unlink(path_dhcrelay_pid
);