etc/protocols - sync with NetBSD-8
[minix.git] / external / bsd / dhcp / dist / relay / dhcrelay.c
blobf771cafa286efa2e329fbe05ec5d877037ed64c4
1 /* $NetBSD: dhcrelay.c,v 1.5 2014/07/12 12:09:37 spz Exp $ */
2 /* dhcrelay.c
4 DHCP/BOOTP Relay Agent. */
6 /*
7 * Copyright(c) 2004-2014 by Internet Systems Consortium, Inc.("ISC")
8 * Copyright(c) 1997-2003 by Internet Software Consortium
10 * Permission to use, copy, modify, and distribute this software for any
11 * purpose with or without fee is hereby granted, provided that the above
12 * copyright notice and this permission notice appear in all copies.
14 * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
15 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
16 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
17 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
18 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
19 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
20 * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
22 * Internet Systems Consortium, Inc.
23 * 950 Charter Street
24 * Redwood City, CA 94063
25 * <info@isc.org>
26 * https://www.isc.org/
30 #include <sys/cdefs.h>
31 __RCSID("$NetBSD: dhcrelay.c,v 1.5 2014/07/12 12:09:37 spz Exp $");
33 #include "dhcpd.h"
34 #include <syslog.h>
35 #include <signal.h>
36 #include <sys/time.h>
38 TIME default_lease_time = 43200; /* 12 hours... */
39 TIME max_lease_time = 86400; /* 24 hours... */
40 struct tree_cache *global_options[256];
42 struct option *requested_opts[2];
44 /* Needed to prevent linking against conflex.c. */
45 int lexline;
46 int lexchar;
47 char *token_line;
48 char *tlname;
50 const char *path_dhcrelay_pid = _PATH_DHCRELAY_PID;
51 isc_boolean_t no_dhcrelay_pid = ISC_FALSE;
52 /* False (default) => we write and use a pid file */
53 isc_boolean_t no_pid_file = ISC_FALSE;
55 int bogus_agent_drops = 0; /* Packets dropped because agent option
56 field was specified and we're not relaying
57 packets that already have an agent option
58 specified. */
59 int bogus_giaddr_drops = 0; /* Packets sent to us to relay back to a
60 client, but with a bogus giaddr. */
61 int client_packets_relayed = 0; /* Packets relayed from client to server. */
62 int server_packet_errors = 0; /* Errors sending packets to servers. */
63 int server_packets_relayed = 0; /* Packets relayed from server to client. */
64 int client_packet_errors = 0; /* Errors sending packets to clients. */
66 int add_agent_options = 0; /* If nonzero, add relay agent options. */
68 int agent_option_errors = 0; /* Number of packets forwarded without
69 agent options because there was no room. */
70 int drop_agent_mismatches = 0; /* If nonzero, drop server replies that
71 don't have matching circuit-id's. */
72 int corrupt_agent_options = 0; /* Number of packets dropped because
73 relay agent information option was bad. */
74 int missing_agent_option = 0; /* Number of packets dropped because no
75 RAI option matching our ID was found. */
76 int bad_circuit_id = 0; /* Circuit ID option in matching RAI option
77 did not match any known circuit ID. */
78 int missing_circuit_id = 0; /* Circuit ID option in matching RAI option
79 was missing. */
80 int max_hop_count = 10; /* Maximum hop count */
82 #ifdef DHCPv6
83 /* Force use of DHCPv6 interface-id option. */
84 isc_boolean_t use_if_id = ISC_FALSE;
85 #endif
87 /* Maximum size of a packet with agent options added. */
88 int dhcp_max_agent_option_packet_length = DHCP_MTU_MIN;
90 /* What to do about packets we're asked to relay that
91 already have a relay option: */
92 enum { forward_and_append, /* Forward and append our own relay option. */
93 forward_and_replace, /* Forward, but replace theirs with ours. */
94 forward_untouched, /* Forward without changes. */
95 discard } agent_relay_mode = forward_and_replace;
97 u_int16_t local_port;
98 u_int16_t remote_port;
100 /* Relay agent server list. */
101 struct server_list {
102 struct server_list *next;
103 struct sockaddr_in to;
104 } *servers;
106 #ifdef DHCPv6
107 struct stream_list {
108 struct stream_list *next;
109 struct interface_info *ifp;
110 struct sockaddr_in6 link;
111 int id;
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;
125 #endif
127 static void do_relay4(struct interface_info *, struct dhcp_packet *,
128 unsigned int, unsigned int, struct iaddr,
129 struct hardware *);
130 static int add_relay_agent_options(struct interface_info *,
131 struct dhcp_packet *, unsigned,
132 struct in_addr);
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-2014 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/";
147 #ifdef DHCPv6
148 #define DHCRELAY_USAGE \
149 "Usage: dhcrelay [-4] [-d] [-q] [-a] [-D]\n"\
150 " [-A <length>] [-c <hops>] [-p <port>]\n" \
151 " [-pf <pid-file>] [--no-pid]\n"\
152 " [-m append|replace|forward|discard]\n" \
153 " [-i interface0 [ ... -i interfaceN]\n" \
154 " server0 [ ... serverN]\n\n" \
155 " dhcrelay -6 [-d] [-q] [-I] [-c <hops>] [-p <port>]\n" \
156 " [-pf <pid-file>] [--no-pid]\n" \
157 " [-s <subscriber-id>]\n" \
158 " -l lower0 [ ... -l lowerN]\n" \
159 " -u upper0 [ ... -u upperN]\n" \
160 " lower (client link): [address%%]interface[#index]\n" \
161 " upper (server link): [address%%]interface"
162 #else
163 #define DHCRELAY_USAGE \
164 "Usage: dhcrelay [-d] [-q] [-a] [-D] [-A <length>] [-c <hops>] [-p <port>]\n" \
165 " [-pf <pid-file>] [--no-pid]\n" \
166 " [-m append|replace|forward|discard]\n" \
167 " [-i interface0 [ ... -i interfaceN]\n" \
168 " server0 [ ... serverN]\n\n"
169 #endif
171 static void usage(void) {
172 log_fatal(DHCRELAY_USAGE);
175 int
176 main(int argc, char **argv) {
177 isc_result_t status;
178 struct servent *ent;
179 struct server_list *sp = NULL;
180 struct interface_info *tmp = NULL;
181 char *service_local = NULL, *service_remote = NULL;
182 u_int16_t port_local = 0, port_remote = 0;
183 int no_daemon = 0, quiet = 0;
184 int fd;
185 int i;
186 #ifdef DHCPv6
187 struct stream_list *sl = NULL;
188 int local_family_set = 0;
189 #endif
191 /* Make sure that file descriptors 0(stdin), 1,(stdout), and
192 2(stderr) are open. To do this, we assume that when we
193 open a file the lowest available file descriptor is used. */
194 fd = open("/dev/null", O_RDWR);
195 if (fd == 0)
196 fd = open("/dev/null", O_RDWR);
197 if (fd == 1)
198 fd = open("/dev/null", O_RDWR);
199 if (fd == 2)
200 log_perror = 0; /* No sense logging to /dev/null. */
201 else if (fd != -1)
202 close(fd);
204 openlog("dhcrelay", LOG_NDELAY, LOG_DAEMON);
206 #if !defined(DEBUG)
207 setlogmask(LOG_UPTO(LOG_INFO));
208 #endif
210 /* Set up the isc and dns library managers */
211 status = dhcp_context_create(DHCP_CONTEXT_PRE_DB | DHCP_CONTEXT_POST_DB,
212 NULL, NULL);
213 if (status != ISC_R_SUCCESS)
214 log_fatal("Can't initialize context: %s",
215 isc_result_totext(status));
217 /* Set up the OMAPI. */
218 status = omapi_init();
219 if (status != ISC_R_SUCCESS)
220 log_fatal("Can't initialize OMAPI: %s",
221 isc_result_totext(status));
223 /* Set up the OMAPI wrappers for the interface object. */
224 interface_setup();
226 for (i = 1; i < argc; i++) {
227 if (!strcmp(argv[i], "-4")) {
228 #ifdef DHCPv6
229 if (local_family_set && (local_family == AF_INET6)) {
230 usage();
232 local_family_set = 1;
233 local_family = AF_INET;
234 } else if (!strcmp(argv[i], "-6")) {
235 if (local_family_set && (local_family == AF_INET)) {
236 usage();
238 local_family_set = 1;
239 local_family = AF_INET6;
240 #endif
241 } else if (!strcmp(argv[i], "-d")) {
242 no_daemon = 1;
243 } else if (!strcmp(argv[i], "-q")) {
244 quiet = 1;
245 quiet_interface_discovery = 1;
246 } else if (!strcmp(argv[i], "-p")) {
247 if (++i == argc)
248 usage();
249 local_port = validate_port(argv[i]);
250 log_debug("binding to user-specified port %d",
251 ntohs(local_port));
252 } else if (!strcmp(argv[i], "-c")) {
253 int hcount;
254 if (++i == argc)
255 usage();
256 hcount = atoi(argv[i]);
257 if (hcount <= 255)
258 max_hop_count= hcount;
259 else
260 usage();
261 } else if (!strcmp(argv[i], "-i")) {
262 #ifdef DHCPv6
263 if (local_family_set && (local_family == AF_INET6)) {
264 usage();
266 local_family_set = 1;
267 local_family = AF_INET;
268 #endif
269 if (++i == argc) {
270 usage();
272 if (strlen(argv[i]) >= sizeof(tmp->name)) {
273 log_fatal("%s: interface name too long "
274 "(is %ld)",
275 argv[i], (long)strlen(argv[i]));
277 status = interface_allocate(&tmp, MDL);
278 if (status != ISC_R_SUCCESS) {
279 log_fatal("%s: interface_allocate: %s",
280 argv[i],
281 isc_result_totext(status));
283 strcpy(tmp->name, argv[i]);
284 interface_snorf(tmp, INTERFACE_REQUESTED);
285 interface_dereference(&tmp, MDL);
286 } else if (!strcmp(argv[i], "-a")) {
287 #ifdef DHCPv6
288 if (local_family_set && (local_family == AF_INET6)) {
289 usage();
291 local_family_set = 1;
292 local_family = AF_INET;
293 #endif
294 add_agent_options = 1;
295 } else if (!strcmp(argv[i], "-A")) {
296 #ifdef DHCPv6
297 if (local_family_set && (local_family == AF_INET6)) {
298 usage();
300 local_family_set = 1;
301 local_family = AF_INET;
302 #endif
303 if (++i == argc)
304 usage();
306 dhcp_max_agent_option_packet_length = atoi(argv[i]);
308 if (dhcp_max_agent_option_packet_length > DHCP_MTU_MAX)
309 log_fatal("%s: packet length exceeds "
310 "longest possible MTU\n",
311 argv[i]);
312 } else if (!strcmp(argv[i], "-m")) {
313 #ifdef DHCPv6
314 if (local_family_set && (local_family == AF_INET6)) {
315 usage();
317 local_family_set = 1;
318 local_family = AF_INET;
319 #endif
320 if (++i == argc)
321 usage();
322 if (!strcasecmp(argv[i], "append")) {
323 agent_relay_mode = forward_and_append;
324 } else if (!strcasecmp(argv[i], "replace")) {
325 agent_relay_mode = forward_and_replace;
326 } else if (!strcasecmp(argv[i], "forward")) {
327 agent_relay_mode = forward_untouched;
328 } else if (!strcasecmp(argv[i], "discard")) {
329 agent_relay_mode = discard;
330 } else
331 usage();
332 } else if (!strcmp(argv[i], "-D")) {
333 #ifdef DHCPv6
334 if (local_family_set && (local_family == AF_INET6)) {
335 usage();
337 local_family_set = 1;
338 local_family = AF_INET;
339 #endif
340 drop_agent_mismatches = 1;
341 #ifdef DHCPv6
342 } else if (!strcmp(argv[i], "-I")) {
343 if (local_family_set && (local_family == AF_INET)) {
344 usage();
346 local_family_set = 1;
347 local_family = AF_INET6;
348 use_if_id = ISC_TRUE;
349 } else if (!strcmp(argv[i], "-l")) {
350 if (local_family_set && (local_family == AF_INET)) {
351 usage();
353 local_family_set = 1;
354 local_family = AF_INET6;
355 if (downstreams != NULL)
356 use_if_id = ISC_TRUE;
357 if (++i == argc)
358 usage();
359 sl = parse_downstream(argv[i]);
360 sl->next = downstreams;
361 downstreams = sl;
362 } else if (!strcmp(argv[i], "-u")) {
363 if (local_family_set && (local_family == AF_INET)) {
364 usage();
366 local_family_set = 1;
367 local_family = AF_INET6;
368 if (++i == argc)
369 usage();
370 sl = parse_upstream(argv[i]);
371 sl->next = upstreams;
372 upstreams = sl;
373 } else if (!strcmp(argv[i], "-s")) {
374 if (local_family_set && (local_family == AF_INET)) {
375 usage();
377 local_family_set = 1;
378 local_family = AF_INET6;
379 if (++i == argc)
380 usage();
381 dhcrelay_sub_id = argv[i];
382 #endif
383 } else if (!strcmp(argv[i], "-pf")) {
384 if (++i == argc)
385 usage();
386 path_dhcrelay_pid = argv[i];
387 no_dhcrelay_pid = ISC_TRUE;
388 } else if (!strcmp(argv[i], "--no-pid")) {
389 no_pid_file = ISC_TRUE;
390 } else if (!strcmp(argv[i], "--version")) {
391 log_info("isc-dhcrelay-%s", PACKAGE_VERSION);
392 exit(0);
393 } else if (!strcmp(argv[i], "--help") ||
394 !strcmp(argv[i], "-h")) {
395 log_info(DHCRELAY_USAGE);
396 exit(0);
397 } else if (argv[i][0] == '-') {
398 usage();
399 } else {
400 struct hostent *he;
401 struct in_addr ia, *iap = NULL;
403 #ifdef DHCPv6
404 if (local_family_set && (local_family == AF_INET6)) {
405 usage();
407 local_family_set = 1;
408 local_family = AF_INET;
409 #endif
410 if (inet_aton(argv[i], &ia)) {
411 iap = &ia;
412 } else {
413 he = gethostbyname(argv[i]);
414 if (!he) {
415 log_error("%s: host unknown", argv[i]);
416 } else {
417 iap = ((struct in_addr *)
418 he->h_addr_list[0]);
422 if (iap) {
423 sp = ((struct server_list *)
424 dmalloc(sizeof *sp, MDL));
425 if (!sp)
426 log_fatal("no memory for server.\n");
427 sp->next = servers;
428 servers = sp;
429 memcpy(&sp->to.sin_addr, iap, sizeof *iap);
435 * If the user didn't specify a pid file directly
436 * find one from environment variables or defaults
438 if (no_dhcrelay_pid == ISC_FALSE) {
439 if (local_family == AF_INET) {
440 path_dhcrelay_pid = getenv("PATH_DHCRELAY_PID");
441 if (path_dhcrelay_pid == NULL)
442 path_dhcrelay_pid = _PATH_DHCRELAY_PID;
444 #ifdef DHCPv6
445 else {
446 path_dhcrelay_pid = getenv("PATH_DHCRELAY6_PID");
447 if (path_dhcrelay_pid == NULL)
448 path_dhcrelay_pid = _PATH_DHCRELAY6_PID;
450 #endif
453 if (!quiet) {
454 log_info("%s %s", message, PACKAGE_VERSION);
455 log_info(copyright);
456 log_info(arr);
457 log_info(url);
458 } else
459 log_perror = 0;
461 /* Set default port */
462 if (local_family == AF_INET) {
463 service_local = "bootps";
464 service_remote = "bootpc";
465 port_local = htons(67);
466 port_remote = htons(68);
468 #ifdef DHCPv6
469 else {
470 service_local = "dhcpv6-server";
471 service_remote = "dhcpv6-client";
472 port_local = htons(547);
473 port_remote = htons(546);
475 #endif
477 if (!local_port) {
478 ent = getservbyname(service_local, "udp");
479 if (ent)
480 local_port = ent->s_port;
481 else
482 local_port = port_local;
484 ent = getservbyname(service_remote, "udp");
485 if (ent)
486 remote_port = ent->s_port;
487 else
488 remote_port = port_remote;
490 endservent();
493 if (local_family == AF_INET) {
494 /* We need at least one server */
495 if (servers == NULL) {
496 log_fatal("No servers specified.");
500 /* Set up the server sockaddrs. */
501 for (sp = servers; sp; sp = sp->next) {
502 sp->to.sin_port = local_port;
503 sp->to.sin_family = AF_INET;
504 #ifdef HAVE_SA_LEN
505 sp->to.sin_len = sizeof sp->to;
506 #endif
509 #ifdef DHCPv6
510 else {
511 unsigned code;
513 /* We need at least one upstream and one downstream interface */
514 if (upstreams == NULL || downstreams == NULL) {
515 log_info("Must specify at least one lower "
516 "and one upper interface.\n");
517 usage();
520 /* Set up the initial dhcp option universe. */
521 initialize_common_option_spaces();
523 /* Check requested options. */
524 code = D6O_RELAY_MSG;
525 if (!option_code_hash_lookup(&requested_opts[0],
526 dhcpv6_universe.code_hash,
527 &code, 0, MDL))
528 log_fatal("Unable to find the RELAY_MSG "
529 "option definition.");
530 code = D6O_INTERFACE_ID;
531 if (!option_code_hash_lookup(&requested_opts[1],
532 dhcpv6_universe.code_hash,
533 &code, 0, MDL))
534 log_fatal("Unable to find the INTERFACE_ID "
535 "option definition.");
537 #endif
539 /* Get the current time... */
540 gettimeofday(&cur_tv, NULL);
542 /* Discover all the network interfaces. */
543 discover_interfaces(DISCOVER_RELAY);
545 #ifdef DHCPv6
546 if (local_family == AF_INET6)
547 setup_streams();
548 #endif
550 /* Become a daemon... */
551 if (!no_daemon) {
552 int pid;
553 FILE *pf;
554 int pfdesc;
556 log_perror = 0;
558 if ((pid = fork()) < 0)
559 log_fatal("Can't fork daemon: %m");
560 else if (pid)
561 exit(0);
563 if (no_pid_file == ISC_FALSE) {
564 pfdesc = open(path_dhcrelay_pid,
565 O_CREAT | O_TRUNC | O_WRONLY, 0644);
567 if (pfdesc < 0) {
568 log_error("Can't create %s: %m",
569 path_dhcrelay_pid);
570 } else {
571 pf = fdopen(pfdesc, "w");
572 if (!pf)
573 log_error("Can't fdopen %s: %m",
574 path_dhcrelay_pid);
575 else {
576 fprintf(pf, "%ld\n",(long)getpid());
577 fclose(pf);
582 (void) close(0);
583 (void) close(1);
584 (void) close(2);
585 (void) setsid();
587 IGNORE_RET (chdir("/"));
590 /* Set up the packet handler... */
591 if (local_family == AF_INET)
592 bootp_packet_handler = do_relay4;
593 #ifdef DHCPv6
594 else
595 dhcpv6_packet_handler = do_packet6;
596 #endif
598 /* install signal handlers */
599 signal(SIGINT, dhcp_signal_handler); /* control-c */
600 signal(SIGTERM, dhcp_signal_handler); /* kill */
602 /* Start dispatching packets and timeouts... */
603 dispatch();
605 /* In fact dispatch() never returns. */
606 return (0);
609 static void
610 do_relay4(struct interface_info *ip, struct dhcp_packet *packet,
611 unsigned int length, unsigned int from_port, struct iaddr from,
612 struct hardware *hfrom) {
613 struct server_list *sp;
614 struct sockaddr_in to;
615 struct interface_info *out;
616 struct hardware hto, *htop;
618 if (packet->hlen > sizeof packet->chaddr) {
619 log_info("Discarding packet with invalid hlen, received on "
620 "%s interface.", ip->name);
621 return;
623 if (ip->address_count < 1 || ip->addresses == NULL) {
624 log_info("Discarding packet received on %s interface that "
625 "has no IPv4 address assigned.", ip->name);
626 return;
629 /* Find the interface that corresponds to the giaddr
630 in the packet. */
631 if (packet->giaddr.s_addr) {
632 for (out = interfaces; out; out = out->next) {
633 int i;
635 for (i = 0 ; i < out->address_count ; i++ ) {
636 if (out->addresses[i].s_addr ==
637 packet->giaddr.s_addr) {
638 i = -1;
639 break;
643 if (i == -1)
644 break;
646 } else {
647 out = NULL;
650 /* If it's a bootreply, forward it to the client. */
651 if (packet->op == BOOTREPLY) {
652 if (!(packet->flags & htons(BOOTP_BROADCAST)) &&
653 can_unicast_without_arp(out)) {
654 to.sin_addr = packet->yiaddr;
655 to.sin_port = remote_port;
657 /* and hardware address is not broadcast */
658 htop = &hto;
659 } else {
660 to.sin_addr.s_addr = htonl(INADDR_BROADCAST);
661 to.sin_port = remote_port;
663 /* hardware address is broadcast */
664 htop = NULL;
666 to.sin_family = AF_INET;
667 #ifdef HAVE_SA_LEN
668 to.sin_len = sizeof to;
669 #endif
671 memcpy(&hto.hbuf[1], packet->chaddr, packet->hlen);
672 hto.hbuf[0] = packet->htype;
673 hto.hlen = packet->hlen + 1;
675 /* Wipe out the agent relay options and, if possible, figure
676 out which interface to use based on the contents of the
677 option that we put on the request to which the server is
678 replying. */
679 if (!(length =
680 strip_relay_agent_options(ip, &out, packet, length)))
681 return;
683 if (!out) {
684 log_error("Packet to bogus giaddr %s.\n",
685 inet_ntoa(packet->giaddr));
686 ++bogus_giaddr_drops;
687 return;
690 if (send_packet(out, NULL, packet, length, out->addresses[0],
691 &to, htop) < 0) {
692 ++server_packet_errors;
693 } else {
694 log_debug("Forwarded BOOTREPLY for %s to %s",
695 print_hw_addr(packet->htype, packet->hlen,
696 packet->chaddr),
697 inet_ntoa(to.sin_addr));
699 ++server_packets_relayed;
701 return;
704 /* If giaddr matches one of our addresses, ignore the packet -
705 we just sent it. */
706 if (out)
707 return;
709 /* Add relay agent options if indicated. If something goes wrong,
710 drop the packet. */
711 if (!(length = add_relay_agent_options(ip, packet, length,
712 ip->addresses[0])))
713 return;
715 /* If giaddr is not already set, Set it so the server can
716 figure out what net it's from and so that we can later
717 forward the response to the correct net. If it's already
718 set, the response will be sent directly to the relay agent
719 that set giaddr, so we won't see it. */
720 if (!packet->giaddr.s_addr)
721 packet->giaddr = ip->addresses[0];
722 if (packet->hops < max_hop_count)
723 packet->hops = packet->hops + 1;
724 else
725 return;
727 /* Otherwise, it's a BOOTREQUEST, so forward it to all the
728 servers. */
729 for (sp = servers; sp; sp = sp->next) {
730 if (send_packet((fallback_interface
731 ? fallback_interface : interfaces),
732 NULL, packet, length, ip->addresses[0],
733 &sp->to, NULL) < 0) {
734 ++client_packet_errors;
735 } else {
736 log_debug("Forwarded BOOTREQUEST for %s to %s",
737 print_hw_addr(packet->htype, packet->hlen,
738 packet->chaddr),
739 inet_ntoa(sp->to.sin_addr));
740 ++client_packets_relayed;
746 /* Strip any Relay Agent Information options from the DHCP packet
747 option buffer. If there is a circuit ID suboption, look up the
748 outgoing interface based upon it. */
750 static int
751 strip_relay_agent_options(struct interface_info *in,
752 struct interface_info **out,
753 struct dhcp_packet *packet,
754 unsigned length) {
755 int is_dhcp = 0;
756 u_int8_t *op, *nextop, *sp, *max;
757 int good_agent_option = 0;
758 int status;
760 /* If we're not adding agent options to packets, we're not taking
761 them out either. */
762 if (!add_agent_options)
763 return (length);
765 /* If there's no cookie, it's a bootp packet, so we should just
766 forward it unchanged. */
767 if (memcmp(packet->options, DHCP_OPTIONS_COOKIE, 4))
768 return (length);
770 max = ((u_int8_t *)packet) + length;
771 sp = op = &packet->options[4];
773 while (op < max) {
774 switch(*op) {
775 /* Skip padding... */
776 case DHO_PAD:
777 if (sp != op)
778 *sp = *op;
779 ++op;
780 ++sp;
781 continue;
783 /* If we see a message type, it's a DHCP packet. */
784 case DHO_DHCP_MESSAGE_TYPE:
785 is_dhcp = 1;
786 goto skip;
787 break;
789 /* Quit immediately if we hit an End option. */
790 case DHO_END:
791 if (sp != op)
792 *sp++ = *op++;
793 goto out;
795 case DHO_DHCP_AGENT_OPTIONS:
796 /* We shouldn't see a relay agent option in a
797 packet before we've seen the DHCP packet type,
798 but if we do, we have to leave it alone. */
799 if (!is_dhcp)
800 goto skip;
802 /* Do not process an agent option if it exceeds the
803 * buffer. Fail this packet.
805 nextop = op + op[1] + 2;
806 if (nextop > max)
807 return (0);
809 status = find_interface_by_agent_option(packet,
810 out, op + 2,
811 op[1]);
812 if (status == -1 && drop_agent_mismatches)
813 return (0);
814 if (status)
815 good_agent_option = 1;
816 op = nextop;
817 break;
819 skip:
820 /* Skip over other options. */
821 default:
822 /* Fail if processing this option will exceed the
823 * buffer(op[1] is malformed).
825 nextop = op + op[1] + 2;
826 if (nextop > max)
827 return (0);
829 if (sp != op) {
830 memmove(sp, op, op[1] + 2);
831 sp += op[1] + 2;
832 op = nextop;
833 } else
834 op = sp = nextop;
836 break;
839 out:
841 /* If it's not a DHCP packet, we're not supposed to touch it. */
842 if (!is_dhcp)
843 return (length);
845 /* If none of the agent options we found matched, or if we didn't
846 find any agent options, count this packet as not having any
847 matching agent options, and if we're relying on agent options
848 to determine the outgoing interface, drop the packet. */
850 if (!good_agent_option) {
851 ++missing_agent_option;
852 if (drop_agent_mismatches)
853 return (0);
856 /* Adjust the length... */
857 if (sp != op) {
858 length = sp -((u_int8_t *)packet);
860 /* Make sure the packet isn't short(this is unlikely,
861 but WTH) */
862 if (length < BOOTP_MIN_LEN) {
863 memset(sp, DHO_PAD, BOOTP_MIN_LEN - length);
864 length = BOOTP_MIN_LEN;
867 return (length);
871 /* Find an interface that matches the circuit ID specified in the
872 Relay Agent Information option. If one is found, store it through
873 the pointer given; otherwise, leave the existing pointer alone.
875 We actually deviate somewhat from the current specification here:
876 if the option buffer is corrupt, we suggest that the caller not
877 respond to this packet. If the circuit ID doesn't match any known
878 interface, we suggest that the caller to drop the packet. Only if
879 we find a circuit ID that matches an existing interface do we tell
880 the caller to go ahead and process the packet. */
882 static int
883 find_interface_by_agent_option(struct dhcp_packet *packet,
884 struct interface_info **out,
885 u_int8_t *buf, int len) {
886 int i = 0;
887 u_int8_t *circuit_id = 0;
888 unsigned circuit_id_len = 0;
889 struct interface_info *ip;
891 while (i < len) {
892 /* If the next agent option overflows the end of the
893 packet, the agent option buffer is corrupt. */
894 if (i + 1 == len ||
895 i + buf[i + 1] + 2 > len) {
896 ++corrupt_agent_options;
897 return (-1);
899 switch(buf[i]) {
900 /* Remember where the circuit ID is... */
901 case RAI_CIRCUIT_ID:
902 circuit_id = &buf[i + 2];
903 circuit_id_len = buf[i + 1];
904 i += circuit_id_len + 2;
905 continue;
907 default:
908 i += buf[i + 1] + 2;
909 break;
913 /* If there's no circuit ID, it's not really ours, tell the caller
914 it's no good. */
915 if (!circuit_id) {
916 ++missing_circuit_id;
917 return (-1);
920 /* Scan the interface list looking for an interface whose
921 name matches the one specified in circuit_id. */
923 for (ip = interfaces; ip; ip = ip->next) {
924 if (ip->circuit_id &&
925 ip->circuit_id_len == circuit_id_len &&
926 !memcmp(ip->circuit_id, circuit_id, circuit_id_len))
927 break;
930 /* If we got a match, use it. */
931 if (ip) {
932 *out = ip;
933 return (1);
936 /* If we didn't get a match, the circuit ID was bogus. */
937 ++bad_circuit_id;
938 return (-1);
942 * Examine a packet to see if it's a candidate to have a Relay
943 * Agent Information option tacked onto its tail. If it is, tack
944 * the option on.
947 #include <sys/cdefs.h>
948 __RCSID("$NetBSD: dhcrelay.c,v 1.5 2014/07/12 12:09:37 spz Exp $");
949 static int
950 add_relay_agent_options(struct interface_info *ip, struct dhcp_packet *packet,
951 unsigned length, struct in_addr giaddr) {
952 int is_dhcp = 0, mms;
953 unsigned optlen;
954 u_int8_t *op, *nextop, *sp, *max, *end_pad = NULL;
956 /* If we're not adding agent options to packets, we can skip
957 this. */
958 if (!add_agent_options)
959 return (length);
961 /* If there's no cookie, it's a bootp packet, so we should just
962 forward it unchanged. */
963 if (memcmp(packet->options, DHCP_OPTIONS_COOKIE, 4))
964 return (length);
966 max = ((u_int8_t *)packet) + dhcp_max_agent_option_packet_length;
968 /* Commence processing after the cookie. */
969 sp = op = &packet->options[4];
971 while (op < max) {
972 switch(*op) {
973 /* Skip padding... */
974 case DHO_PAD:
975 /* Remember the first pad byte so we can commandeer
976 * padded space.
978 * XXX: Is this really a good idea? Sure, we can
979 * seemingly reduce the packet while we're looking,
980 * but if the packet was signed by the client then
981 * this padding is part of the checksum(RFC3118),
982 * and its nonpresence would break authentication.
984 if (end_pad == NULL)
985 end_pad = sp;
987 if (sp != op)
988 *sp++ = *op++;
989 else
990 sp = ++op;
992 continue;
994 /* If we see a message type, it's a DHCP packet. */
995 case DHO_DHCP_MESSAGE_TYPE:
996 is_dhcp = 1;
997 goto skip;
1000 * If there's a maximum message size option, we
1001 * should pay attention to it
1003 case DHO_DHCP_MAX_MESSAGE_SIZE:
1004 mms = ntohs(*(op + 2));
1005 if (mms < dhcp_max_agent_option_packet_length &&
1006 mms >= DHCP_MTU_MIN)
1007 max = ((u_int8_t *)packet) + mms;
1008 goto skip;
1010 /* Quit immediately if we hit an End option. */
1011 case DHO_END:
1012 goto out;
1014 case DHO_DHCP_AGENT_OPTIONS:
1015 /* We shouldn't see a relay agent option in a
1016 packet before we've seen the DHCP packet type,
1017 but if we do, we have to leave it alone. */
1018 if (!is_dhcp)
1019 goto skip;
1021 end_pad = NULL;
1023 /* There's already a Relay Agent Information option
1024 in this packet. How embarrassing. Decide what
1025 to do based on the mode the user specified. */
1027 switch(agent_relay_mode) {
1028 case forward_and_append:
1029 goto skip;
1030 case forward_untouched:
1031 return (length);
1032 case discard:
1033 return (0);
1034 case forward_and_replace:
1035 default:
1036 break;
1039 /* Skip over the agent option and start copying
1040 if we aren't copying already. */
1041 op += op[1] + 2;
1042 break;
1044 skip:
1045 /* Skip over other options. */
1046 default:
1047 /* Fail if processing this option will exceed the
1048 * buffer(op[1] is malformed).
1050 nextop = op + op[1] + 2;
1051 if (nextop > max)
1052 return (0);
1054 end_pad = NULL;
1056 if (sp != op) {
1057 memmove(sp, op, op[1] + 2);
1058 sp += op[1] + 2;
1059 op = nextop;
1060 } else
1061 op = sp = nextop;
1063 break;
1066 out:
1068 /* If it's not a DHCP packet, we're not supposed to touch it. */
1069 if (!is_dhcp)
1070 return (length);
1072 /* If the packet was padded out, we can store the agent option
1073 at the beginning of the padding. */
1075 if (end_pad != NULL)
1076 sp = end_pad;
1078 #if 0
1079 /* Remember where the end of the packet was after parsing
1080 it. */
1081 op = sp;
1082 #endif
1084 /* Sanity check. Had better not ever happen. */
1085 if ((ip->circuit_id_len > 255) ||(ip->circuit_id_len < 1))
1086 log_fatal("Circuit ID length %d out of range [1-255] on "
1087 "%s\n", ip->circuit_id_len, ip->name);
1088 optlen = ip->circuit_id_len + 2; /* RAI_CIRCUIT_ID + len */
1090 if (ip->remote_id) {
1091 if (ip->remote_id_len > 255 || ip->remote_id_len < 1)
1092 log_fatal("Remote ID length %d out of range [1-255] "
1093 "on %s\n", ip->circuit_id_len, ip->name);
1094 optlen += ip->remote_id_len + 2; /* RAI_REMOTE_ID + len */
1097 /* We do not support relay option fragmenting(multiple options to
1098 * support an option data exceeding 255 bytes).
1100 if ((optlen < 3) ||(optlen > 255))
1101 log_fatal("Total agent option length(%u) out of range "
1102 "[3 - 255] on %s\n", optlen, ip->name);
1105 * Is there room for the option, its code+len, and DHO_END?
1106 * If not, forward without adding the option.
1108 if (max - sp >= optlen + 3) {
1109 log_debug("Adding %d-byte relay agent option", optlen + 3);
1111 /* Okay, cons up *our* Relay Agent Information option. */
1112 *sp++ = DHO_DHCP_AGENT_OPTIONS;
1113 *sp++ = optlen;
1115 /* Copy in the circuit id... */
1116 *sp++ = RAI_CIRCUIT_ID;
1117 *sp++ = ip->circuit_id_len;
1118 memcpy(sp, ip->circuit_id, ip->circuit_id_len);
1119 sp += ip->circuit_id_len;
1121 /* Copy in remote ID... */
1122 if (ip->remote_id) {
1123 *sp++ = RAI_REMOTE_ID;
1124 *sp++ = ip->remote_id_len;
1125 memcpy(sp, ip->remote_id, ip->remote_id_len);
1126 sp += ip->remote_id_len;
1128 } else {
1129 ++agent_option_errors;
1130 log_error("No room in packet (used %d of %d) "
1131 "for %d-byte relay agent option: omitted",
1132 (int) (sp - ((u_int8_t *) packet)),
1133 (int) (max - ((u_int8_t *) packet)),
1134 optlen + 3);
1138 * Deposit an END option unless the packet is full (shouldn't
1139 * be possible).
1141 if (sp < max)
1142 *sp++ = DHO_END;
1144 /* Recalculate total packet length. */
1145 length = sp -((u_int8_t *)packet);
1147 /* Make sure the packet isn't short(this is unlikely, but WTH) */
1148 if (length < BOOTP_MIN_LEN) {
1149 memset(sp, DHO_PAD, BOOTP_MIN_LEN - length);
1150 return (BOOTP_MIN_LEN);
1153 return (length);
1156 #ifdef DHCPv6
1158 * Parse a downstream argument: [address%]interface[#index].
1160 static struct stream_list *
1161 parse_downstream(char *arg) {
1162 struct stream_list *dp, *up;
1163 struct interface_info *ifp = NULL;
1164 char *ifname, *addr, *iid;
1165 isc_result_t status;
1167 if (!supports_multiple_interfaces(ifp) &&
1168 (downstreams != NULL))
1169 log_fatal("No support for multiple interfaces.");
1171 /* Decode the argument. */
1172 ifname = strchr(arg, '%');
1173 if (ifname == NULL) {
1174 ifname = arg;
1175 addr = NULL;
1176 } else {
1177 *ifname++ = '\0';
1178 addr = arg;
1180 iid = strchr(ifname, '#');
1181 if (iid != NULL) {
1182 *iid++ = '\0';
1184 if (strlen(ifname) >= sizeof(ifp->name)) {
1185 log_error("Interface name '%s' too long", ifname);
1186 usage();
1189 /* Don't declare twice. */
1190 for (dp = downstreams; dp; dp = dp->next) {
1191 if (strcmp(ifname, dp->ifp->name) == 0)
1192 log_fatal("Down interface '%s' declared twice.",
1193 ifname);
1196 /* Share with up side? */
1197 for (up = upstreams; up; up = up->next) {
1198 if (strcmp(ifname, up->ifp->name) == 0) {
1199 log_info("Interface '%s' is both down and up.",
1200 ifname);
1201 ifp = up->ifp;
1202 break;
1206 /* New interface. */
1207 if (ifp == NULL) {
1208 status = interface_allocate(&ifp, MDL);
1209 if (status != ISC_R_SUCCESS)
1210 log_fatal("%s: interface_allocate: %s",
1211 arg, isc_result_totext(status));
1212 strcpy(ifp->name, ifname);
1213 if (interfaces) {
1214 interface_reference(&ifp->next, interfaces, MDL);
1215 interface_dereference(&interfaces, MDL);
1217 interface_reference(&interfaces, ifp, MDL);
1218 ifp->flags |= INTERFACE_REQUESTED | INTERFACE_DOWNSTREAM;
1221 /* New downstream. */
1222 dp = (struct stream_list *) dmalloc(sizeof(*dp), MDL);
1223 if (!dp)
1224 log_fatal("No memory for downstream.");
1225 dp->ifp = ifp;
1226 if (iid != NULL) {
1227 dp->id = atoi(iid);
1228 } else {
1229 dp->id = -1;
1231 /* !addr case handled by setup. */
1232 if (addr && (inet_pton(AF_INET6, addr, &dp->link.sin6_addr) <= 0))
1233 log_fatal("Bad link address '%s'", addr);
1235 return dp;
1239 * Parse an upstream argument: [address]%interface.
1241 static struct stream_list *
1242 parse_upstream(char *arg) {
1243 struct stream_list *up, *dp;
1244 struct interface_info *ifp = NULL;
1245 char *ifname, *addr;
1246 isc_result_t status;
1248 /* Decode the argument. */
1249 ifname = strchr(arg, '%');
1250 if (ifname == NULL) {
1251 ifname = arg;
1252 addr = All_DHCP_Servers;
1253 } else {
1254 *ifname++ = '\0';
1255 addr = arg;
1257 if (strlen(ifname) >= sizeof(ifp->name)) {
1258 log_fatal("Interface name '%s' too long", ifname);
1261 /* Shared up interface? */
1262 for (up = upstreams; up; up = up->next) {
1263 if (strcmp(ifname, up->ifp->name) == 0) {
1264 ifp = up->ifp;
1265 break;
1268 for (dp = downstreams; dp; dp = dp->next) {
1269 if (strcmp(ifname, dp->ifp->name) == 0) {
1270 ifp = dp->ifp;
1271 break;
1275 /* New interface. */
1276 if (ifp == NULL) {
1277 status = interface_allocate(&ifp, MDL);
1278 if (status != ISC_R_SUCCESS)
1279 log_fatal("%s: interface_allocate: %s",
1280 arg, isc_result_totext(status));
1281 strcpy(ifp->name, ifname);
1282 if (interfaces) {
1283 interface_reference(&ifp->next, interfaces, MDL);
1284 interface_dereference(&interfaces, MDL);
1286 interface_reference(&interfaces, ifp, MDL);
1287 ifp->flags |= INTERFACE_REQUESTED | INTERFACE_UPSTREAM;
1290 /* New upstream. */
1291 up = (struct stream_list *) dmalloc(sizeof(*up), MDL);
1292 if (up == NULL)
1293 log_fatal("No memory for upstream.");
1295 up->ifp = ifp;
1297 if (inet_pton(AF_INET6, addr, &up->link.sin6_addr) <= 0)
1298 log_fatal("Bad address %s", addr);
1300 return up;
1304 * Setup downstream interfaces.
1306 static void
1307 setup_streams(void) {
1308 struct stream_list *dp, *up;
1309 int i;
1310 isc_boolean_t link_is_set;
1312 for (dp = downstreams; dp; dp = dp->next) {
1313 /* Check interface */
1314 if (dp->ifp->v6address_count == 0)
1315 log_fatal("Interface '%s' has no IPv6 addresses.",
1316 dp->ifp->name);
1318 /* Check/set link. */
1319 if (IN6_IS_ADDR_UNSPECIFIED(&dp->link.sin6_addr))
1320 link_is_set = ISC_FALSE;
1321 else
1322 link_is_set = ISC_TRUE;
1323 for (i = 0; i < dp->ifp->v6address_count; i++) {
1324 if (IN6_IS_ADDR_LINKLOCAL(&dp->ifp->v6addresses[i]))
1325 continue;
1326 if (!link_is_set)
1327 break;
1328 if (!memcmp(&dp->ifp->v6addresses[i],
1329 &dp->link.sin6_addr,
1330 sizeof(dp->link.sin6_addr)))
1331 break;
1333 if (i == dp->ifp->v6address_count)
1334 log_fatal("Interface %s does not have global IPv6 "
1335 "address assigned.", dp->ifp->name);
1336 if (!link_is_set)
1337 memcpy(&dp->link.sin6_addr,
1338 &dp->ifp->v6addresses[i],
1339 sizeof(dp->link.sin6_addr));
1341 /* Set interface-id. */
1342 if (dp->id == -1)
1343 dp->id = dp->ifp->index;
1346 for (up = upstreams; up; up = up->next) {
1347 up->link.sin6_port = local_port;
1348 up->link.sin6_family = AF_INET6;
1349 #ifdef HAVE_SA_LEN
1350 up->link.sin6_len = sizeof(up->link);
1351 #endif
1353 if (up->ifp->v6address_count == 0)
1354 log_fatal("Interface '%s' has no IPv6 addresses.",
1355 up->ifp->name);
1360 * Add DHCPv6 agent options here.
1362 static const int required_forw_opts[] = {
1363 D6O_INTERFACE_ID,
1364 D6O_SUBSCRIBER_ID,
1365 D6O_RELAY_MSG,
1370 * Process a packet upwards, i.e., from client to server.
1372 static void
1373 process_up6(struct packet *packet, struct stream_list *dp) {
1374 char forw_data[65535];
1375 unsigned cursor;
1376 struct dhcpv6_relay_packet *relay;
1377 struct option_state *opts;
1378 struct stream_list *up;
1380 /* Check if the message should be relayed to the server. */
1381 switch (packet->dhcpv6_msg_type) {
1382 case DHCPV6_SOLICIT:
1383 case DHCPV6_REQUEST:
1384 case DHCPV6_CONFIRM:
1385 case DHCPV6_RENEW:
1386 case DHCPV6_REBIND:
1387 case DHCPV6_RELEASE:
1388 case DHCPV6_DECLINE:
1389 case DHCPV6_INFORMATION_REQUEST:
1390 case DHCPV6_RELAY_FORW:
1391 case DHCPV6_LEASEQUERY:
1392 log_info("Relaying %s from %s port %d going up.",
1393 dhcpv6_type_names[packet->dhcpv6_msg_type],
1394 piaddr(packet->client_addr),
1395 ntohs(packet->client_port));
1396 break;
1398 case DHCPV6_ADVERTISE:
1399 case DHCPV6_REPLY:
1400 case DHCPV6_RECONFIGURE:
1401 case DHCPV6_RELAY_REPL:
1402 case DHCPV6_LEASEQUERY_REPLY:
1403 log_info("Discarding %s from %s port %d going up.",
1404 dhcpv6_type_names[packet->dhcpv6_msg_type],
1405 piaddr(packet->client_addr),
1406 ntohs(packet->client_port));
1407 return;
1409 default:
1410 log_info("Unknown %d type from %s port %d going up.",
1411 packet->dhcpv6_msg_type,
1412 piaddr(packet->client_addr),
1413 ntohs(packet->client_port));
1414 return;
1417 /* Build the relay-forward header. */
1418 relay = (struct dhcpv6_relay_packet *) forw_data;
1419 cursor = offsetof(struct dhcpv6_relay_packet, options);
1420 relay->msg_type = DHCPV6_RELAY_FORW;
1421 if (packet->dhcpv6_msg_type == DHCPV6_RELAY_FORW) {
1422 if (packet->dhcpv6_hop_count >= max_hop_count) {
1423 log_info("Hop count exceeded,");
1424 return;
1426 relay->hop_count = packet->dhcpv6_hop_count + 1;
1427 if (dp) {
1428 memcpy(&relay->link_address, &dp->link.sin6_addr, 16);
1429 } else {
1430 /* On smart relay add: && !global. */
1431 if (!use_if_id && downstreams->next) {
1432 log_info("Shan't get back the interface.");
1433 return;
1435 memset(&relay->link_address, 0, 16);
1437 } else {
1438 relay->hop_count = 0;
1439 if (!dp)
1440 return;
1441 memcpy(&relay->link_address, &dp->link.sin6_addr, 16);
1443 memcpy(&relay->peer_address, packet->client_addr.iabuf, 16);
1445 /* Get an option state. */
1446 opts = NULL;
1447 if (!option_state_allocate(&opts, MDL)) {
1448 log_fatal("No memory for upwards options.");
1451 /* Add an interface-id (if used). */
1452 if (use_if_id) {
1453 int if_id;
1455 if (dp) {
1456 if_id = dp->id;
1457 } else if (!downstreams->next) {
1458 if_id = downstreams->id;
1459 } else {
1460 log_info("Don't know the interface.");
1461 option_state_dereference(&opts, MDL);
1462 return;
1465 if (!save_option_buffer(&dhcpv6_universe, opts,
1466 NULL, (unsigned char *) &if_id,
1467 sizeof(int),
1468 D6O_INTERFACE_ID, 0)) {
1469 log_error("Can't save interface-id.");
1470 option_state_dereference(&opts, MDL);
1471 return;
1475 /* Add a subscriber-id if desired. */
1476 /* This is for testing rather than general use */
1477 if (dhcrelay_sub_id != NULL) {
1478 if (!save_option_buffer(&dhcpv6_universe, opts, NULL,
1479 (unsigned char *) dhcrelay_sub_id,
1480 strlen(dhcrelay_sub_id),
1481 D6O_SUBSCRIBER_ID, 0)) {
1482 log_error("Can't save subsriber-id.");
1483 option_state_dereference(&opts, MDL);
1484 return;
1489 /* Add the relay-msg carrying the packet. */
1490 if (!save_option_buffer(&dhcpv6_universe, opts,
1491 NULL, (unsigned char *) packet->raw,
1492 packet->packet_length,
1493 D6O_RELAY_MSG, 0)) {
1494 log_error("Can't save relay-msg.");
1495 option_state_dereference(&opts, MDL);
1496 return;
1499 /* Finish the relay-forward message. */
1500 cursor += store_options6(forw_data + cursor,
1501 sizeof(forw_data) - cursor,
1502 opts, packet,
1503 required_forw_opts, NULL);
1504 option_state_dereference(&opts, MDL);
1506 /* Send it to all upstreams. */
1507 for (up = upstreams; up; up = up->next) {
1508 send_packet6(up->ifp, (unsigned char *) forw_data,
1509 (size_t) cursor, &up->link);
1514 * Process a packet downwards, i.e., from server to client.
1516 static void
1517 process_down6(struct packet *packet) {
1518 struct stream_list *dp;
1519 struct option_cache *oc;
1520 struct data_string relay_msg;
1521 const struct dhcpv6_packet *msg;
1522 struct data_string if_id;
1523 struct sockaddr_in6 to;
1524 struct iaddr peer;
1526 /* The packet must be a relay-reply message. */
1527 if (packet->dhcpv6_msg_type != DHCPV6_RELAY_REPL) {
1528 if (packet->dhcpv6_msg_type < dhcpv6_type_name_max)
1529 log_info("Discarding %s from %s port %d going down.",
1530 dhcpv6_type_names[packet->dhcpv6_msg_type],
1531 piaddr(packet->client_addr),
1532 ntohs(packet->client_port));
1533 else
1534 log_info("Unknown %d type from %s port %d going down.",
1535 packet->dhcpv6_msg_type,
1536 piaddr(packet->client_addr),
1537 ntohs(packet->client_port));
1538 return;
1541 /* Inits. */
1542 memset(&relay_msg, 0, sizeof(relay_msg));
1543 memset(&if_id, 0, sizeof(if_id));
1544 memset(&to, 0, sizeof(to));
1545 to.sin6_family = AF_INET6;
1546 #ifdef HAVE_SA_LEN
1547 to.sin6_len = sizeof(to);
1548 #endif
1549 to.sin6_port = remote_port;
1550 peer.len = 16;
1552 /* Get the relay-msg option (carrying the message to relay). */
1553 oc = lookup_option(&dhcpv6_universe, packet->options, D6O_RELAY_MSG);
1554 if (oc == NULL) {
1555 log_info("No relay-msg.");
1556 return;
1558 if (!evaluate_option_cache(&relay_msg, packet, NULL, NULL,
1559 packet->options, NULL,
1560 &global_scope, oc, MDL) ||
1561 (relay_msg.len < offsetof(struct dhcpv6_packet, options))) {
1562 log_error("Can't evaluate relay-msg.");
1563 return;
1565 msg = (const struct dhcpv6_packet *) relay_msg.data;
1567 /* Get the interface-id (if exists) and the downstream. */
1568 oc = lookup_option(&dhcpv6_universe, packet->options,
1569 D6O_INTERFACE_ID);
1570 if (oc != NULL) {
1571 int if_index;
1573 if (!evaluate_option_cache(&if_id, packet, NULL, NULL,
1574 packet->options, NULL,
1575 &global_scope, oc, MDL) ||
1576 (if_id.len != sizeof(int))) {
1577 log_info("Can't evaluate interface-id.");
1578 goto cleanup;
1580 memcpy(&if_index, if_id.data, sizeof(int));
1581 for (dp = downstreams; dp; dp = dp->next) {
1582 if (dp->id == if_index)
1583 break;
1585 } else {
1586 if (use_if_id) {
1587 /* Require an interface-id. */
1588 log_info("No interface-id.");
1589 goto cleanup;
1591 for (dp = downstreams; dp; dp = dp->next) {
1592 /* Get the first matching one. */
1593 if (!memcmp(&dp->link.sin6_addr,
1594 &packet->dhcpv6_link_address,
1595 sizeof(struct in6_addr)))
1596 break;
1599 /* Why bother when there is no choice. */
1600 if (!dp && downstreams && !downstreams->next)
1601 dp = downstreams;
1602 if (!dp) {
1603 log_info("Can't find the down interface.");
1604 goto cleanup;
1606 memcpy(peer.iabuf, &packet->dhcpv6_peer_address, peer.len);
1607 to.sin6_addr = packet->dhcpv6_peer_address;
1609 /* Check if we should relay the carried message. */
1610 switch (msg->msg_type) {
1611 /* Relay-Reply of for another relay, not a client. */
1612 case DHCPV6_RELAY_REPL:
1613 to.sin6_port = local_port;
1614 /* Fall into: */
1616 case DHCPV6_ADVERTISE:
1617 case DHCPV6_REPLY:
1618 case DHCPV6_RECONFIGURE:
1619 case DHCPV6_RELAY_FORW:
1620 case DHCPV6_LEASEQUERY_REPLY:
1621 log_info("Relaying %s to %s port %d down.",
1622 dhcpv6_type_names[msg->msg_type],
1623 piaddr(peer),
1624 ntohs(to.sin6_port));
1625 break;
1627 case DHCPV6_SOLICIT:
1628 case DHCPV6_REQUEST:
1629 case DHCPV6_CONFIRM:
1630 case DHCPV6_RENEW:
1631 case DHCPV6_REBIND:
1632 case DHCPV6_RELEASE:
1633 case DHCPV6_DECLINE:
1634 case DHCPV6_INFORMATION_REQUEST:
1635 case DHCPV6_LEASEQUERY:
1636 log_info("Discarding %s to %s port %d down.",
1637 dhcpv6_type_names[msg->msg_type],
1638 piaddr(peer),
1639 ntohs(to.sin6_port));
1640 goto cleanup;
1642 default:
1643 log_info("Unknown %d type to %s port %d down.",
1644 msg->msg_type,
1645 piaddr(peer),
1646 ntohs(to.sin6_port));
1647 goto cleanup;
1650 /* Send the message to the downstream. */
1651 send_packet6(dp->ifp, (unsigned char *) relay_msg.data,
1652 (size_t) relay_msg.len, &to);
1654 cleanup:
1655 if (relay_msg.data != NULL)
1656 data_string_forget(&relay_msg, MDL);
1657 if (if_id.data != NULL)
1658 data_string_forget(&if_id, MDL);
1662 * Called by the dispatch packet handler with a decoded packet.
1664 void
1665 dhcpv6(struct packet *packet) {
1666 struct stream_list *dp;
1668 /* Try all relay-replies downwards. */
1669 if (packet->dhcpv6_msg_type == DHCPV6_RELAY_REPL) {
1670 process_down6(packet);
1671 return;
1673 /* Others are candidates to go up if they come from down. */
1674 for (dp = downstreams; dp; dp = dp->next) {
1675 if (packet->interface != dp->ifp)
1676 continue;
1677 process_up6(packet, dp);
1678 return;
1680 /* Relay-forward could work from an unknown interface. */
1681 if (packet->dhcpv6_msg_type == DHCPV6_RELAY_FORW) {
1682 process_up6(packet, NULL);
1683 return;
1686 log_info("Can't process packet from interface '%s'.",
1687 packet->interface->name);
1689 #endif
1691 /* Stub routines needed for linking with DHCP libraries. */
1692 void
1693 bootp(struct packet *packet) {
1694 return;
1697 void
1698 dhcp(struct packet *packet) {
1699 return;
1702 void
1703 classify(struct packet *p, struct class *c) {
1704 return;
1708 check_collection(struct packet *p, struct lease *l, struct collection *c) {
1709 return 0;
1712 isc_result_t
1713 find_class(struct class **class, const char *c1, const char *c2, int i) {
1714 return ISC_R_NOTFOUND;
1718 parse_allow_deny(struct option_cache **oc, struct parse *p, int i) {
1719 return 0;
1722 isc_result_t
1723 dhcp_set_control_state(control_object_state_t oldstate,
1724 control_object_state_t newstate) {
1725 if (newstate != server_shutdown)
1726 return ISC_R_SUCCESS;
1727 exit(0);