]> git.ipfire.org Git - thirdparty/dhcp.git/blob - relay/dhcrelay.c
[master] Added make tool and pkg-config tests to configure script
[thirdparty/dhcp.git] / relay / dhcrelay.c
1 /* dhcrelay.c
2
3 DHCP/BOOTP Relay Agent. */
4
5 /*
6 * Copyright(c) 2004-2016 by Internet Systems Consortium, Inc.("ISC")
7 * Copyright(c) 1997-2003 by Internet Software Consortium
8 *
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.
12 *
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.
20 *
21 * Internet Systems Consortium, Inc.
22 * 950 Charter Street
23 * Redwood City, CA 94063
24 * <info@isc.org>
25 * https://www.isc.org/
26 *
27 */
28
29 #include "dhcpd.h"
30 #include <syslog.h>
31 #include <signal.h>
32 #include <sys/time.h>
33 #include <isc/file.h>
34
35 TIME default_lease_time = 43200; /* 12 hours... */
36 TIME max_lease_time = 86400; /* 24 hours... */
37 struct tree_cache *global_options[256];
38
39 struct option *requested_opts[2];
40
41 /* Needed to prevent linking against conflex.c. */
42 int lexline;
43 int lexchar;
44 char *token_line;
45 char *tlname;
46
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;
51
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
55 specified. */
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. */
62
63 int add_agent_options = 0; /* If nonzero, add relay agent options. */
64
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
76 was missing. */
77 int max_hop_count = 10; /* Maximum hop count */
78
79 #ifdef DHCPv6
80 /* Force use of DHCPv6 interface-id option. */
81 isc_boolean_t use_if_id = ISC_FALSE;
82 #endif
83
84 /* Maximum size of a packet with agent options added. */
85 int dhcp_max_agent_option_packet_length = DHCP_MTU_MIN;
86
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;
93
94 u_int16_t local_port;
95 u_int16_t remote_port;
96
97 /* Relay agent server list. */
98 struct server_list {
99 struct server_list *next;
100 struct sockaddr_in to;
101 } *servers;
102
103 #ifdef DHCPv6
104 struct stream_list {
105 struct stream_list *next;
106 struct interface_info *ifp;
107 struct sockaddr_in6 link;
108 int id;
109 } *downstreams, *upstreams;
110
111 static struct stream_list *parse_downstream(char *);
112 static struct stream_list *parse_upstream(char *);
113 static void setup_streams(void);
114
115 /*
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.
120 */
121 char *dhcrelay_sub_id = NULL;
122 #endif
123
124 static void do_relay4(struct interface_info *, struct dhcp_packet *,
125 unsigned int, unsigned int, struct iaddr,
126 struct hardware *);
127 static int add_relay_agent_options(struct interface_info *,
128 struct dhcp_packet *, unsigned,
129 struct in_addr);
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);
135
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/";
143
144 char *progname;
145
146 #ifdef DHCPv6
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"
161 #else
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"
168 #endif
169
170 /*!
171 *
172 * \brief Print the generic usage message
173 *
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.
179 *
180 * \param sfmt - The basic string and format for the specific error
181 * \param sarg - Generally the offending argument from the comamnd line.
182 *
183 * \return Nothing
184 */
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";
189
190 static void
191 usage(const char *sfmt, const char *sarg) {
192
193 /* If desired print out the specific error message */
194 #ifdef PRINT_SPECIFIC_CL_ERRORS
195 if (sfmt != NULL)
196 log_error(sfmt, sarg);
197 #endif
198
199 log_fatal(DHCRELAY_USAGE,
200 #ifdef DHCPv6
201 isc_file_basename(progname),
202 #endif
203 isc_file_basename(progname));
204 }
205
206 int
207 main(int argc, char **argv) {
208 isc_result_t status;
209 struct servent *ent;
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;
215 int fd;
216 int i;
217 #ifdef DHCPv6
218 struct stream_list *sl = NULL;
219 int local_family_set = 0;
220 #endif
221
222 #ifdef OLD_LOG_NAME
223 progname = "dhcrelay";
224 #else
225 progname = argv[0];
226 #endif
227
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);
232 if (fd == 0)
233 fd = open("/dev/null", O_RDWR);
234 if (fd == 1)
235 fd = open("/dev/null", O_RDWR);
236 if (fd == 2)
237 log_perror = 0; /* No sense logging to /dev/null. */
238 else if (fd != -1)
239 close(fd);
240
241 openlog(isc_file_basename(progname), DHCP_LOG_OPTIONS, LOG_DAEMON);
242
243 #if !defined(DEBUG)
244 setlogmask(LOG_UPTO(LOG_INFO));
245 #endif
246
247 /* Set up the isc and dns library managers */
248 status = dhcp_context_create(DHCP_CONTEXT_PRE_DB | DHCP_CONTEXT_POST_DB,
249 NULL, NULL);
250 if (status != ISC_R_SUCCESS)
251 log_fatal("Can't initialize context: %s",
252 isc_result_totext(status));
253
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));
259
260 /* Set up the OMAPI wrappers for the interface object. */
261 interface_setup();
262
263 for (i = 1; i < argc; i++) {
264 if (!strcmp(argv[i], "-4")) {
265 #ifdef DHCPv6
266 if (local_family_set && (local_family == AF_INET6)) {
267 usage(use_badproto, "-4");
268 }
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");
274 }
275 local_family_set = 1;
276 local_family = AF_INET6;
277 #endif
278 } else if (!strcmp(argv[i], "-d")) {
279 no_daemon = 1;
280 } else if (!strcmp(argv[i], "-q")) {
281 quiet = 1;
282 quiet_interface_discovery = 1;
283 } else if (!strcmp(argv[i], "-p")) {
284 if (++i == argc)
285 usage(use_noarg, argv[i-1]);
286 local_port = validate_port(argv[i]);
287 log_debug("binding to user-specified port %d",
288 ntohs(local_port));
289 } else if (!strcmp(argv[i], "-c")) {
290 int hcount;
291 if (++i == argc)
292 usage(use_noarg, argv[i-1]);
293 hcount = atoi(argv[i]);
294 if (hcount <= 255)
295 max_hop_count= hcount;
296 else
297 usage("Bad hop count to -c: %s", argv[i]);
298 } else if (!strcmp(argv[i], "-i")) {
299 #ifdef DHCPv6
300 if (local_family_set && (local_family == AF_INET6)) {
301 usage(use_v4command, argv[i]);
302 }
303 local_family_set = 1;
304 local_family = AF_INET;
305 #endif
306 if (++i == argc) {
307 usage(use_noarg, argv[i-1]);
308 }
309 if (strlen(argv[i]) >= sizeof(tmp->name)) {
310 log_fatal("%s: interface name too long "
311 "(is %ld)",
312 argv[i], (long)strlen(argv[i]));
313 }
314 status = interface_allocate(&tmp, MDL);
315 if (status != ISC_R_SUCCESS) {
316 log_fatal("%s: interface_allocate: %s",
317 argv[i],
318 isc_result_totext(status));
319 }
320 strcpy(tmp->name, argv[i]);
321 interface_snorf(tmp, INTERFACE_REQUESTED);
322 interface_dereference(&tmp, MDL);
323 } else if (!strcmp(argv[i], "-a")) {
324 #ifdef DHCPv6
325 if (local_family_set && (local_family == AF_INET6)) {
326 usage(use_v4command, argv[i]);
327 }
328 local_family_set = 1;
329 local_family = AF_INET;
330 #endif
331 add_agent_options = 1;
332 } else if (!strcmp(argv[i], "-A")) {
333 #ifdef DHCPv6
334 if (local_family_set && (local_family == AF_INET6)) {
335 usage(use_v4command, argv[i]);
336 }
337 local_family_set = 1;
338 local_family = AF_INET;
339 #endif
340 if (++i == argc)
341 usage(use_noarg, argv[i-1]);
342
343 dhcp_max_agent_option_packet_length = atoi(argv[i]);
344
345 if (dhcp_max_agent_option_packet_length > DHCP_MTU_MAX)
346 log_fatal("%s: packet length exceeds "
347 "longest possible MTU\n",
348 argv[i]);
349 } else if (!strcmp(argv[i], "-m")) {
350 #ifdef DHCPv6
351 if (local_family_set && (local_family == AF_INET6)) {
352 usage(use_v4command, argv[i]);
353 }
354 local_family_set = 1;
355 local_family = AF_INET;
356 #endif
357 if (++i == argc)
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;
367 } else
368 usage("Unknown argument to -m: %s", argv[i]);
369 } else if (!strcmp(argv[i], "-D")) {
370 #ifdef DHCPv6
371 if (local_family_set && (local_family == AF_INET6)) {
372 usage(use_v4command, argv[i]);
373 }
374 local_family_set = 1;
375 local_family = AF_INET;
376 #endif
377 drop_agent_mismatches = 1;
378 #ifdef DHCPv6
379 } else if (!strcmp(argv[i], "-I")) {
380 if (local_family_set && (local_family == AF_INET)) {
381 usage(use_v6command, argv[i]);
382 }
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]);
389 }
390 local_family_set = 1;
391 local_family = AF_INET6;
392 if (downstreams != NULL)
393 use_if_id = ISC_TRUE;
394 if (++i == argc)
395 usage(use_noarg, argv[i-1]);
396 sl = parse_downstream(argv[i]);
397 sl->next = downstreams;
398 downstreams = sl;
399 } else if (!strcmp(argv[i], "-u")) {
400 if (local_family_set && (local_family == AF_INET)) {
401 usage(use_v6command, argv[i]);
402 }
403 local_family_set = 1;
404 local_family = AF_INET6;
405 if (++i == argc)
406 usage(use_noarg, argv[i-1]);
407 sl = parse_upstream(argv[i]);
408 sl->next = upstreams;
409 upstreams = sl;
410 } else if (!strcmp(argv[i], "-s")) {
411 if (local_family_set && (local_family == AF_INET)) {
412 usage(use_v6command, argv[i]);
413 }
414 local_family_set = 1;
415 local_family = AF_INET6;
416 if (++i == argc)
417 usage(use_noarg, argv[i-1]);
418 dhcrelay_sub_id = argv[i];
419 #endif
420 } else if (!strcmp(argv[i], "-pf")) {
421 if (++i == argc)
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);
429 exit(0);
430 } else if (!strcmp(argv[i], "--help") ||
431 !strcmp(argv[i], "-h")) {
432 log_info(DHCRELAY_USAGE,
433 #ifdef DHCPv6
434 isc_file_basename(progname),
435 #endif
436 isc_file_basename(progname));
437 exit(0);
438 } else if (argv[i][0] == '-') {
439 usage("Unknown command: %s", argv[i]);
440 } else {
441 struct hostent *he;
442 struct in_addr ia, *iap = NULL;
443
444 #ifdef DHCPv6
445 if (local_family_set && (local_family == AF_INET6)) {
446 usage(use_v4command, argv[i]);
447 }
448 local_family_set = 1;
449 local_family = AF_INET;
450 #endif
451 if (inet_aton(argv[i], &ia)) {
452 iap = &ia;
453 } else {
454 he = gethostbyname(argv[i]);
455 if (!he) {
456 log_error("%s: host unknown", argv[i]);
457 } else {
458 iap = ((struct in_addr *)
459 he->h_addr_list[0]);
460 }
461 }
462
463 if (iap) {
464 sp = ((struct server_list *)
465 dmalloc(sizeof *sp, MDL));
466 if (!sp)
467 log_fatal("no memory for server.\n");
468 sp->next = servers;
469 servers = sp;
470 memcpy(&sp->to.sin_addr, iap, sizeof *iap);
471 }
472 }
473 }
474
475 /*
476 * If the user didn't specify a pid file directly
477 * find one from environment variables or defaults
478 */
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;
484 }
485 #ifdef DHCPv6
486 else {
487 path_dhcrelay_pid = getenv("PATH_DHCRELAY6_PID");
488 if (path_dhcrelay_pid == NULL)
489 path_dhcrelay_pid = _PATH_DHCRELAY6_PID;
490 }
491 #endif
492 }
493
494 if (!quiet) {
495 log_info("%s %s", message, PACKAGE_VERSION);
496 log_info(copyright);
497 log_info(arr);
498 log_info(url);
499 } else
500 log_perror = 0;
501
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);
508 }
509 #ifdef DHCPv6
510 else {
511 service_local = "dhcpv6-server";
512 service_remote = "dhcpv6-client";
513 port_local = htons(547);
514 port_remote = htons(546);
515 }
516 #endif
517
518 if (!local_port) {
519 ent = getservbyname(service_local, "udp");
520 if (ent)
521 local_port = ent->s_port;
522 else
523 local_port = port_local;
524
525 ent = getservbyname(service_remote, "udp");
526 if (ent)
527 remote_port = ent->s_port;
528 else
529 remote_port = port_remote;
530
531 endservent();
532 }
533
534 if (local_family == AF_INET) {
535 /* We need at least one server */
536 if (servers == NULL) {
537 log_fatal("No servers specified.");
538 }
539
540
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;
545 #ifdef HAVE_SA_LEN
546 sp->to.sin_len = sizeof sp->to;
547 #endif
548 }
549 }
550 #ifdef DHCPv6
551 else {
552 unsigned code;
553
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");
558 usage(NULL, NULL);
559 }
560
561 /* Set up the initial dhcp option universe. */
562 initialize_common_option_spaces();
563
564 /* Check requested options. */
565 code = D6O_RELAY_MSG;
566 if (!option_code_hash_lookup(&requested_opts[0],
567 dhcpv6_universe.code_hash,
568 &code, 0, MDL))
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,
574 &code, 0, MDL))
575 log_fatal("Unable to find the INTERFACE_ID "
576 "option definition.");
577 }
578 #endif
579
580 /* Get the current time... */
581 gettimeofday(&cur_tv, NULL);
582
583 /* Discover all the network interfaces. */
584 discover_interfaces(DISCOVER_RELAY);
585
586 #ifdef DHCPv6
587 if (local_family == AF_INET6)
588 setup_streams();
589 #endif
590
591 /* Become a daemon... */
592 if (!no_daemon) {
593 int pid;
594 FILE *pf;
595 int pfdesc;
596
597 log_perror = 0;
598
599 if ((pid = fork()) < 0)
600 log_fatal("Can't fork daemon: %m");
601 else if (pid)
602 exit(0);
603
604 if (no_pid_file == ISC_FALSE) {
605 pfdesc = open(path_dhcrelay_pid,
606 O_CREAT | O_TRUNC | O_WRONLY, 0644);
607
608 if (pfdesc < 0) {
609 log_error("Can't create %s: %m",
610 path_dhcrelay_pid);
611 } else {
612 pf = fdopen(pfdesc, "w");
613 if (!pf)
614 log_error("Can't fdopen %s: %m",
615 path_dhcrelay_pid);
616 else {
617 fprintf(pf, "%ld\n",(long)getpid());
618 fclose(pf);
619 }
620 }
621 }
622
623 (void) close(0);
624 (void) close(1);
625 (void) close(2);
626 (void) setsid();
627
628 IGNORE_RET (chdir("/"));
629 }
630
631 /* Set up the packet handler... */
632 if (local_family == AF_INET)
633 bootp_packet_handler = do_relay4;
634 #ifdef DHCPv6
635 else
636 dhcpv6_packet_handler = do_packet6;
637 #endif
638
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 */
644 #endif
645
646 /* Start dispatching packets and timeouts... */
647 dispatch();
648
649 /* In fact dispatch() never returns. */
650 return (0);
651 }
652
653 static void
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;
661
662 if (packet->hlen > sizeof packet->chaddr) {
663 log_info("Discarding packet with invalid hlen, received on "
664 "%s interface.", ip->name);
665 return;
666 }
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);
670 return;
671 }
672
673 /* Find the interface that corresponds to the giaddr
674 in the packet. */
675 if (packet->giaddr.s_addr) {
676 for (out = interfaces; out; out = out->next) {
677 int i;
678
679 for (i = 0 ; i < out->address_count ; i++ ) {
680 if (out->addresses[i].s_addr ==
681 packet->giaddr.s_addr) {
682 i = -1;
683 break;
684 }
685 }
686
687 if (i == -1)
688 break;
689 }
690 } else {
691 out = NULL;
692 }
693
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;
700
701 /* and hardware address is not broadcast */
702 htop = &hto;
703 } else {
704 to.sin_addr.s_addr = htonl(INADDR_BROADCAST);
705 to.sin_port = remote_port;
706
707 /* hardware address is broadcast */
708 htop = NULL;
709 }
710 to.sin_family = AF_INET;
711 #ifdef HAVE_SA_LEN
712 to.sin_len = sizeof to;
713 #endif
714
715 memcpy(&hto.hbuf[1], packet->chaddr, packet->hlen);
716 hto.hbuf[0] = packet->htype;
717 hto.hlen = packet->hlen + 1;
718
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
722 replying. */
723 if (!(length =
724 strip_relay_agent_options(ip, &out, packet, length)))
725 return;
726
727 if (!out) {
728 log_error("Packet to bogus giaddr %s.\n",
729 inet_ntoa(packet->giaddr));
730 ++bogus_giaddr_drops;
731 return;
732 }
733
734 if (send_packet(out, NULL, packet, length, out->addresses[0],
735 &to, htop) < 0) {
736 ++server_packet_errors;
737 } else {
738 log_debug("Forwarded BOOTREPLY for %s to %s",
739 print_hw_addr(packet->htype, packet->hlen,
740 packet->chaddr),
741 inet_ntoa(to.sin_addr));
742
743 ++server_packets_relayed;
744 }
745 return;
746 }
747
748 /* If giaddr matches one of our addresses, ignore the packet -
749 we just sent it. */
750 if (out)
751 return;
752
753 /* Add relay agent options if indicated. If something goes wrong,
754 drop the packet. */
755 if (!(length = add_relay_agent_options(ip, packet, length,
756 ip->addresses[0])))
757 return;
758
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;
768 else
769 return;
770
771 /* Otherwise, it's a BOOTREQUEST, so forward it to all the
772 servers. */
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;
779 } else {
780 log_debug("Forwarded BOOTREQUEST for %s to %s",
781 print_hw_addr(packet->htype, packet->hlen,
782 packet->chaddr),
783 inet_ntoa(sp->to.sin_addr));
784 ++client_packets_relayed;
785 }
786 }
787
788 }
789
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. */
793
794 static int
795 strip_relay_agent_options(struct interface_info *in,
796 struct interface_info **out,
797 struct dhcp_packet *packet,
798 unsigned length) {
799 int is_dhcp = 0;
800 u_int8_t *op, *nextop, *sp, *max;
801 int good_agent_option = 0;
802 int status;
803
804 /* If we're not adding agent options to packets, we're not taking
805 them out either. */
806 if (!add_agent_options)
807 return (length);
808
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))
812 return (length);
813
814 max = ((u_int8_t *)packet) + length;
815 sp = op = &packet->options[4];
816
817 while (op < max) {
818 switch(*op) {
819 /* Skip padding... */
820 case DHO_PAD:
821 if (sp != op)
822 *sp = *op;
823 ++op;
824 ++sp;
825 continue;
826
827 /* If we see a message type, it's a DHCP packet. */
828 case DHO_DHCP_MESSAGE_TYPE:
829 is_dhcp = 1;
830 goto skip;
831 break;
832
833 /* Quit immediately if we hit an End option. */
834 case DHO_END:
835 if (sp != op)
836 *sp++ = *op++;
837 goto out;
838
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. */
843 if (!is_dhcp)
844 goto skip;
845
846 /* Do not process an agent option if it exceeds the
847 * buffer. Fail this packet.
848 */
849 nextop = op + op[1] + 2;
850 if (nextop > max)
851 return (0);
852
853 status = find_interface_by_agent_option(packet,
854 out, op + 2,
855 op[1]);
856 if (status == -1 && drop_agent_mismatches)
857 return (0);
858 if (status)
859 good_agent_option = 1;
860 op = nextop;
861 break;
862
863 skip:
864 /* Skip over other options. */
865 default:
866 /* Fail if processing this option will exceed the
867 * buffer(op[1] is malformed).
868 */
869 nextop = op + op[1] + 2;
870 if (nextop > max)
871 return (0);
872
873 if (sp != op) {
874 memmove(sp, op, op[1] + 2);
875 sp += op[1] + 2;
876 op = nextop;
877 } else
878 op = sp = nextop;
879
880 break;
881 }
882 }
883 out:
884
885 /* If it's not a DHCP packet, we're not supposed to touch it. */
886 if (!is_dhcp)
887 return (length);
888
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. */
893
894 if (!good_agent_option) {
895 ++missing_agent_option;
896 if (drop_agent_mismatches)
897 return (0);
898 }
899
900 /* Adjust the length... */
901 if (sp != op) {
902 length = sp -((u_int8_t *)packet);
903
904 /* Make sure the packet isn't short(this is unlikely,
905 but WTH) */
906 if (length < BOOTP_MIN_LEN) {
907 memset(sp, DHO_PAD, BOOTP_MIN_LEN - length);
908 length = BOOTP_MIN_LEN;
909 }
910 }
911 return (length);
912 }
913
914
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.
918
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. */
925
926 static int
927 find_interface_by_agent_option(struct dhcp_packet *packet,
928 struct interface_info **out,
929 u_int8_t *buf, int len) {
930 int i = 0;
931 u_int8_t *circuit_id = 0;
932 unsigned circuit_id_len = 0;
933 struct interface_info *ip;
934
935 while (i < len) {
936 /* If the next agent option overflows the end of the
937 packet, the agent option buffer is corrupt. */
938 if (i + 1 == len ||
939 i + buf[i + 1] + 2 > len) {
940 ++corrupt_agent_options;
941 return (-1);
942 }
943 switch(buf[i]) {
944 /* Remember where the circuit ID is... */
945 case RAI_CIRCUIT_ID:
946 circuit_id = &buf[i + 2];
947 circuit_id_len = buf[i + 1];
948 i += circuit_id_len + 2;
949 continue;
950
951 default:
952 i += buf[i + 1] + 2;
953 break;
954 }
955 }
956
957 /* If there's no circuit ID, it's not really ours, tell the caller
958 it's no good. */
959 if (!circuit_id) {
960 ++missing_circuit_id;
961 return (-1);
962 }
963
964 /* Scan the interface list looking for an interface whose
965 name matches the one specified in circuit_id. */
966
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))
971 break;
972 }
973
974 /* If we got a match, use it. */
975 if (ip) {
976 *out = ip;
977 return (1);
978 }
979
980 /* If we didn't get a match, the circuit ID was bogus. */
981 ++bad_circuit_id;
982 return (-1);
983 }
984
985 /*
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
988 * the option on.
989 */
990 static int
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;
994 unsigned optlen;
995 u_int8_t *op, *nextop, *sp, *max, *end_pad = NULL;
996
997 /* If we're not adding agent options to packets, we can skip
998 this. */
999 if (!add_agent_options)
1000 return (length);
1001
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))
1005 return (length);
1006
1007 max = ((u_int8_t *)packet) + dhcp_max_agent_option_packet_length;
1008
1009 /* Commence processing after the cookie. */
1010 sp = op = &packet->options[4];
1011
1012 while (op < max) {
1013 switch(*op) {
1014 /* Skip padding... */
1015 case DHO_PAD:
1016 /* Remember the first pad byte so we can commandeer
1017 * padded space.
1018 *
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.
1024 */
1025 if (end_pad == NULL)
1026 end_pad = sp;
1027
1028 if (sp != op)
1029 *sp++ = *op++;
1030 else
1031 sp = ++op;
1032
1033 continue;
1034
1035 /* If we see a message type, it's a DHCP packet. */
1036 case DHO_DHCP_MESSAGE_TYPE:
1037 is_dhcp = 1;
1038 goto skip;
1039
1040 /*
1041 * If there's a maximum message size option, we
1042 * should pay attention to it
1043 */
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;
1049 goto skip;
1050
1051 /* Quit immediately if we hit an End option. */
1052 case DHO_END:
1053 goto out;
1054
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. */
1059 if (!is_dhcp)
1060 goto skip;
1061
1062 end_pad = NULL;
1063
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. */
1067
1068 switch(agent_relay_mode) {
1069 case forward_and_append:
1070 goto skip;
1071 case forward_untouched:
1072 return (length);
1073 case discard:
1074 return (0);
1075 case forward_and_replace:
1076 default:
1077 break;
1078 }
1079
1080 /* Skip over the agent option and start copying
1081 if we aren't copying already. */
1082 op += op[1] + 2;
1083 break;
1084
1085 skip:
1086 /* Skip over other options. */
1087 default:
1088 /* Fail if processing this option will exceed the
1089 * buffer(op[1] is malformed).
1090 */
1091 nextop = op + op[1] + 2;
1092 if (nextop > max)
1093 return (0);
1094
1095 end_pad = NULL;
1096
1097 if (sp != op) {
1098 memmove(sp, op, op[1] + 2);
1099 sp += op[1] + 2;
1100 op = nextop;
1101 } else
1102 op = sp = nextop;
1103
1104 break;
1105 }
1106 }
1107 out:
1108
1109 /* If it's not a DHCP packet, we're not supposed to touch it. */
1110 if (!is_dhcp)
1111 return (length);
1112
1113 /* If the packet was padded out, we can store the agent option
1114 at the beginning of the padding. */
1115
1116 if (end_pad != NULL)
1117 sp = end_pad;
1118
1119 #if 0
1120 /* Remember where the end of the packet was after parsing
1121 it. */
1122 op = sp;
1123 #endif
1124
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 */
1130
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 */
1136 }
1137
1138 /* We do not support relay option fragmenting(multiple options to
1139 * support an option data exceeding 255 bytes).
1140 */
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);
1144
1145 /*
1146 * Is there room for the option, its code+len, and DHO_END?
1147 * If not, forward without adding the option.
1148 */
1149 if (max - sp >= optlen + 3) {
1150 log_debug("Adding %d-byte relay agent option", optlen + 3);
1151
1152 /* Okay, cons up *our* Relay Agent Information option. */
1153 *sp++ = DHO_DHCP_AGENT_OPTIONS;
1154 *sp++ = optlen;
1155
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;
1161
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;
1168 }
1169 } else {
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)),
1175 optlen + 3);
1176 }
1177
1178 /*
1179 * Deposit an END option unless the packet is full (shouldn't
1180 * be possible).
1181 */
1182 if (sp < max)
1183 *sp++ = DHO_END;
1184
1185 /* Recalculate total packet length. */
1186 length = sp -((u_int8_t *)packet);
1187
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);
1192 }
1193
1194 return (length);
1195 }
1196
1197 #ifdef DHCPv6
1198 /*
1199 * Parse a downstream argument: [address%]interface[#index].
1200 */
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;
1207
1208 if (!supports_multiple_interfaces(ifp) &&
1209 (downstreams != NULL))
1210 log_fatal("No support for multiple interfaces.");
1211
1212 /* Decode the argument. */
1213 ifname = strchr(arg, '%');
1214 if (ifname == NULL) {
1215 ifname = arg;
1216 addr = NULL;
1217 } else {
1218 *ifname++ = '\0';
1219 addr = arg;
1220 }
1221 iid = strchr(ifname, '#');
1222 if (iid != NULL) {
1223 *iid++ = '\0';
1224 }
1225 if (strlen(ifname) >= sizeof(ifp->name)) {
1226 usage("Interface name '%s' too long", ifname);
1227 }
1228
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.",
1233 ifname);
1234 }
1235
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);
1241 ifp = up->ifp;
1242 break;
1243 }
1244 }
1245
1246 /* New interface. */
1247 if (ifp == NULL) {
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);
1253 if (interfaces) {
1254 interface_reference(&ifp->next, interfaces, MDL);
1255 interface_dereference(&interfaces, MDL);
1256 }
1257 interface_reference(&interfaces, ifp, MDL);
1258 }
1259 ifp->flags |= INTERFACE_REQUESTED | INTERFACE_DOWNSTREAM;
1260
1261 /* New downstream. */
1262 dp = (struct stream_list *) dmalloc(sizeof(*dp), MDL);
1263 if (!dp)
1264 log_fatal("No memory for downstream.");
1265 dp->ifp = ifp;
1266 if (iid != NULL) {
1267 dp->id = atoi(iid);
1268 } else {
1269 dp->id = -1;
1270 }
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);
1274
1275 return dp;
1276 }
1277
1278 /*
1279 * Parse an upstream argument: [address]%interface.
1280 */
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;
1287
1288 /* Decode the argument. */
1289 ifname = strchr(arg, '%');
1290 if (ifname == NULL) {
1291 ifname = arg;
1292 addr = All_DHCP_Servers;
1293 } else {
1294 *ifname++ = '\0';
1295 addr = arg;
1296 }
1297 if (strlen(ifname) >= sizeof(ifp->name)) {
1298 log_fatal("Interface name '%s' too long", ifname);
1299 }
1300
1301 /* Shared up interface? */
1302 for (up = upstreams; up; up = up->next) {
1303 if (strcmp(ifname, up->ifp->name) == 0) {
1304 ifp = up->ifp;
1305 break;
1306 }
1307 }
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);
1312 ifp = dp->ifp;
1313 break;
1314 }
1315 }
1316
1317 /* New interface. */
1318 if (ifp == NULL) {
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);
1324 if (interfaces) {
1325 interface_reference(&ifp->next, interfaces, MDL);
1326 interface_dereference(&interfaces, MDL);
1327 }
1328 interface_reference(&interfaces, ifp, MDL);
1329 }
1330 ifp->flags |= INTERFACE_REQUESTED | INTERFACE_UPSTREAM;
1331
1332 /* New upstream. */
1333 up = (struct stream_list *) dmalloc(sizeof(*up), MDL);
1334 if (up == NULL)
1335 log_fatal("No memory for upstream.");
1336
1337 up->ifp = ifp;
1338
1339 if (inet_pton(AF_INET6, addr, &up->link.sin6_addr) <= 0)
1340 log_fatal("Bad address %s", addr);
1341
1342 return up;
1343 }
1344
1345 /*
1346 * Setup downstream interfaces.
1347 */
1348 static void
1349 setup_streams(void) {
1350 struct stream_list *dp, *up;
1351 int i;
1352 isc_boolean_t link_is_set;
1353
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.",
1358 dp->ifp->name);
1359
1360 /* Check/set link. */
1361 if (IN6_IS_ADDR_UNSPECIFIED(&dp->link.sin6_addr))
1362 link_is_set = ISC_FALSE;
1363 else
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]))
1367 continue;
1368 if (!link_is_set)
1369 break;
1370 if (!memcmp(&dp->ifp->v6addresses[i],
1371 &dp->link.sin6_addr,
1372 sizeof(dp->link.sin6_addr)))
1373 break;
1374 }
1375 if (i == dp->ifp->v6address_count)
1376 log_fatal("Interface %s does not have global IPv6 "
1377 "address assigned.", dp->ifp->name);
1378 if (!link_is_set)
1379 memcpy(&dp->link.sin6_addr,
1380 &dp->ifp->v6addresses[i],
1381 sizeof(dp->link.sin6_addr));
1382
1383 /* Set interface-id. */
1384 if (dp->id == -1)
1385 dp->id = dp->ifp->index;
1386 }
1387
1388 for (up = upstreams; up; up = up->next) {
1389 up->link.sin6_port = local_port;
1390 up->link.sin6_family = AF_INET6;
1391 #ifdef HAVE_SA_LEN
1392 up->link.sin6_len = sizeof(up->link);
1393 #endif
1394
1395 if (up->ifp->v6address_count == 0)
1396 log_fatal("Interface '%s' has no IPv6 addresses.",
1397 up->ifp->name);
1398
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);
1404 }
1405 }
1406 }
1407
1408 /*
1409 * Add DHCPv6 agent options here.
1410 */
1411 static const int required_forw_opts[] = {
1412 D6O_INTERFACE_ID,
1413 D6O_SUBSCRIBER_ID,
1414 D6O_RELAY_MSG,
1415 0
1416 };
1417
1418 /*
1419 * Process a packet upwards, i.e., from client to server.
1420 */
1421 static void
1422 process_up6(struct packet *packet, struct stream_list *dp) {
1423 char forw_data[65535];
1424 unsigned cursor;
1425 struct dhcpv6_relay_packet *relay;
1426 struct option_state *opts;
1427 struct stream_list *up;
1428
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:
1434 case DHCPV6_RENEW:
1435 case DHCPV6_REBIND:
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));
1445 break;
1446
1447 case DHCPV6_ADVERTISE:
1448 case DHCPV6_REPLY:
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));
1456 return;
1457
1458 default:
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));
1463 return;
1464 }
1465
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,");
1473 return;
1474 }
1475 relay->hop_count = packet->dhcpv6_hop_count + 1;
1476 if (dp) {
1477 memcpy(&relay->link_address, &dp->link.sin6_addr, 16);
1478 } else {
1479 /* On smart relay add: && !global. */
1480 if (!use_if_id && downstreams->next) {
1481 log_info("Shan't get back the interface.");
1482 return;
1483 }
1484 memset(&relay->link_address, 0, 16);
1485 }
1486 } else {
1487 relay->hop_count = 0;
1488 if (!dp)
1489 return;
1490 memcpy(&relay->link_address, &dp->link.sin6_addr, 16);
1491 }
1492 memcpy(&relay->peer_address, packet->client_addr.iabuf, 16);
1493
1494 /* Get an option state. */
1495 opts = NULL;
1496 if (!option_state_allocate(&opts, MDL)) {
1497 log_fatal("No memory for upwards options.");
1498 }
1499
1500 /* Add an interface-id (if used). */
1501 if (use_if_id) {
1502 int if_id;
1503
1504 if (dp) {
1505 if_id = dp->id;
1506 } else if (!downstreams->next) {
1507 if_id = downstreams->id;
1508 } else {
1509 log_info("Don't know the interface.");
1510 option_state_dereference(&opts, MDL);
1511 return;
1512 }
1513
1514 if (!save_option_buffer(&dhcpv6_universe, opts,
1515 NULL, (unsigned char *) &if_id,
1516 sizeof(int),
1517 D6O_INTERFACE_ID, 0)) {
1518 log_error("Can't save interface-id.");
1519 option_state_dereference(&opts, MDL);
1520 return;
1521 }
1522 }
1523
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);
1533 return;
1534 }
1535 }
1536
1537
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);
1545 return;
1546 }
1547
1548 /* Finish the relay-forward message. */
1549 cursor += store_options6(forw_data + cursor,
1550 sizeof(forw_data) - cursor,
1551 opts, packet,
1552 required_forw_opts, NULL);
1553 option_state_dereference(&opts, MDL);
1554
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);
1559 }
1560 }
1561
1562 /*
1563 * Process a packet downwards, i.e., from server to client.
1564 */
1565 static void
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;
1573 struct iaddr peer;
1574
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));
1582 else
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));
1587 return;
1588 }
1589
1590 /* Inits. */
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;
1595 #ifdef HAVE_SA_LEN
1596 to.sin6_len = sizeof(to);
1597 #endif
1598 to.sin6_port = remote_port;
1599 peer.len = 16;
1600
1601 /* Get the relay-msg option (carrying the message to relay). */
1602 oc = lookup_option(&dhcpv6_universe, packet->options, D6O_RELAY_MSG);
1603 if (oc == NULL) {
1604 log_info("No relay-msg.");
1605 return;
1606 }
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.");
1612 return;
1613 }
1614 msg = (const struct dhcpv6_packet *) relay_msg.data;
1615
1616 /* Get the interface-id (if exists) and the downstream. */
1617 oc = lookup_option(&dhcpv6_universe, packet->options,
1618 D6O_INTERFACE_ID);
1619 if (oc != NULL) {
1620 int if_index;
1621
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.");
1627 goto cleanup;
1628 }
1629 memcpy(&if_index, if_id.data, sizeof(int));
1630 for (dp = downstreams; dp; dp = dp->next) {
1631 if (dp->id == if_index)
1632 break;
1633 }
1634 } else {
1635 if (use_if_id) {
1636 /* Require an interface-id. */
1637 log_info("No interface-id.");
1638 goto cleanup;
1639 }
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)))
1645 break;
1646 }
1647 }
1648 /* Why bother when there is no choice. */
1649 if (!dp && downstreams && !downstreams->next)
1650 dp = downstreams;
1651 if (!dp) {
1652 log_info("Can't find the down interface.");
1653 goto cleanup;
1654 }
1655 memcpy(peer.iabuf, &packet->dhcpv6_peer_address, peer.len);
1656 to.sin6_addr = packet->dhcpv6_peer_address;
1657
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;
1663 /* Fall into: */
1664
1665 case DHCPV6_ADVERTISE:
1666 case DHCPV6_REPLY:
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],
1672 piaddr(peer),
1673 ntohs(to.sin6_port));
1674 break;
1675
1676 case DHCPV6_SOLICIT:
1677 case DHCPV6_REQUEST:
1678 case DHCPV6_CONFIRM:
1679 case DHCPV6_RENEW:
1680 case DHCPV6_REBIND:
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],
1687 piaddr(peer),
1688 ntohs(to.sin6_port));
1689 goto cleanup;
1690
1691 default:
1692 log_info("Unknown %d type to %s port %d down.",
1693 msg->msg_type,
1694 piaddr(peer),
1695 ntohs(to.sin6_port));
1696 goto cleanup;
1697 }
1698
1699 /* Send the message to the downstream. */
1700 send_packet6(dp->ifp, (unsigned char *) relay_msg.data,
1701 (size_t) relay_msg.len, &to);
1702
1703 cleanup:
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);
1708 }
1709
1710 /*
1711 * Called by the dispatch packet handler with a decoded packet.
1712 */
1713 void
1714 dhcpv6(struct packet *packet) {
1715 struct stream_list *dp;
1716
1717 /* Try all relay-replies downwards. */
1718 if (packet->dhcpv6_msg_type == DHCPV6_RELAY_REPL) {
1719 process_down6(packet);
1720 return;
1721 }
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)
1725 continue;
1726 process_up6(packet, dp);
1727 return;
1728 }
1729 /* Relay-forward could work from an unknown interface. */
1730 if (packet->dhcpv6_msg_type == DHCPV6_RELAY_FORW) {
1731 process_up6(packet, NULL);
1732 return;
1733 }
1734
1735 log_info("Can't process packet from interface '%s'.",
1736 packet->interface->name);
1737 }
1738 #endif
1739
1740 /* Stub routines needed for linking with DHCP libraries. */
1741 void
1742 bootp(struct packet *packet) {
1743 return;
1744 }
1745
1746 void
1747 dhcp(struct packet *packet) {
1748 return;
1749 }
1750
1751 void
1752 classify(struct packet *p, struct class *c) {
1753 return;
1754 }
1755
1756 int
1757 check_collection(struct packet *p, struct lease *l, struct collection *c) {
1758 return 0;
1759 }
1760
1761 isc_result_t
1762 find_class(struct class **class, const char *c1, const char *c2, int i) {
1763 return ISC_R_NOTFOUND;
1764 }
1765
1766 int
1767 parse_allow_deny(struct option_cache **oc, struct parse *p, int i) {
1768 return 0;
1769 }
1770
1771 isc_result_t
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;
1776
1777 if (no_pid_file == ISC_FALSE)
1778 (void) unlink(path_dhcrelay_pid);
1779
1780 exit(0);
1781 }