]> git.ipfire.org Git - thirdparty/dhcp.git/blob - relay/dhcrelay.c
[master] Added "-id" command line argument to dhcrelay v4 mode
[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 int add_rfc3527_suboption = 0; /* If nonzero, add RFC3527 link selection sub-option. */
65
66 int agent_option_errors = 0; /* Number of packets forwarded without
67 agent options because there was no room. */
68 int drop_agent_mismatches = 0; /* If nonzero, drop server replies that
69 don't have matching circuit-id's. */
70 int corrupt_agent_options = 0; /* Number of packets dropped because
71 relay agent information option was bad. */
72 int missing_agent_option = 0; /* Number of packets dropped because no
73 RAI option matching our ID was found. */
74 int bad_circuit_id = 0; /* Circuit ID option in matching RAI option
75 did not match any known circuit ID. */
76 int missing_circuit_id = 0; /* Circuit ID option in matching RAI option
77 was missing. */
78 int max_hop_count = 10; /* Maximum hop count */
79
80 #ifdef DHCPv6
81 /* Force use of DHCPv6 interface-id option. */
82 isc_boolean_t use_if_id = ISC_FALSE;
83 #endif
84
85 /* Maximum size of a packet with agent options added. */
86 int dhcp_max_agent_option_packet_length = DHCP_MTU_MIN;
87
88 /* What to do about packets we're asked to relay that
89 already have a relay option: */
90 enum { forward_and_append, /* Forward and append our own relay option. */
91 forward_and_replace, /* Forward, but replace theirs with ours. */
92 forward_untouched, /* Forward without changes. */
93 discard } agent_relay_mode = forward_and_replace;
94
95 u_int16_t local_port;
96 u_int16_t remote_port;
97
98 /* Relay agent server list. */
99 struct server_list {
100 struct server_list *next;
101 struct sockaddr_in to;
102 } *servers;
103
104 struct interface_info *uplink = NULL;
105
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;
113
114 static struct stream_list *parse_downstream(char *);
115 static struct stream_list *parse_upstream(char *);
116 static void setup_streams(void);
117
118 /*
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.
123 */
124 char *dhcrelay_sub_id = NULL;
125 #endif
126
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);
138
139 static void request_v4_interface(const char* name, int flags);
140
141 static const char copyright[] =
142 "Copyright 2004-2016 Internet Systems Consortium.";
143 static const char arr[] = "All rights reserved.";
144 static const char message[] =
145 "Internet Systems Consortium DHCP Relay Agent";
146 static const char url[] =
147 "For info, please visit https://www.isc.org/software/dhcp/";
148
149 char *progname;
150
151 #ifdef DHCPv6
152 #define DHCRELAY_USAGE \
153 "Usage: %s [-4] [-d] [-q] [-a] [-D]\n"\
154 " [-A <length>] [-c <hops>] [-p <port>]\n" \
155 " [-pf <pid-file>] [--no-pid]\n"\
156 " [-m append|replace|forward|discard]\n" \
157 " [-i interface0 [ ... -i interfaceN]\n" \
158 " [-iu interface0 [ ... -iu interfaceN]\n" \
159 " [-id interface0 [ ... -id interfaceN]\n" \
160 " [-U interface]\n" \
161 " server0 [ ... serverN]\n\n" \
162 " %s -6 [-d] [-q] [-I] [-c <hops>] [-p <port>]\n" \
163 " [-pf <pid-file>] [--no-pid]\n" \
164 " [-s <subscriber-id>]\n" \
165 " -l lower0 [ ... -l lowerN]\n" \
166 " -u upper0 [ ... -u upperN]\n" \
167 " lower (client link): [address%%]interface[#index]\n" \
168 " upper (server link): [address%%]interface"
169 #else
170 #define DHCRELAY_USAGE \
171 "Usage: %s [-d] [-q] [-a] [-D] [-A <length>] [-c <hops>] [-p <port>]\n" \
172 " [-pf <pid-file>] [--no-pid]\n" \
173 " [-m append|replace|forward|discard]\n" \
174 " [-i interface0 [ ... -i interfaceN]\n" \
175 " [-iu interface0 [ ... -iu interfaceN]\n" \
176 " [-id interface0 [ ... -id interfaceN]\n" \
177 " [-U interface]\n" \
178 " server0 [ ... serverN]\n\n"
179 #endif
180
181 /*!
182 *
183 * \brief Print the generic usage message
184 *
185 * If the user has provided an incorrect command line print out
186 * the description of the command line. The arguments provide
187 * a way for the caller to request more specific information about
188 * the error be printed as well. Mostly this will be that some
189 * comamnd doesn't include its argument.
190 *
191 * \param sfmt - The basic string and format for the specific error
192 * \param sarg - Generally the offending argument from the comamnd line.
193 *
194 * \return Nothing
195 */
196 static const char use_noarg[] = "No argument for command: %s";
197 #ifdef DHCPv6
198 static const char use_badproto[] = "Protocol already set, %s inappropriate";
199 static const char use_v4command[] = "Command not used for DHCPv6: %s";
200 static const char use_v6command[] = "Command not used for DHCPv4: %s";
201 #endif
202
203 static void
204 usage(const char *sfmt, const char *sarg) {
205
206 /* If desired print out the specific error message */
207 #ifdef PRINT_SPECIFIC_CL_ERRORS
208 if (sfmt != NULL)
209 log_error(sfmt, sarg);
210 #endif
211
212 log_fatal(DHCRELAY_USAGE,
213 #ifdef DHCPv6
214 isc_file_basename(progname),
215 #endif
216 isc_file_basename(progname));
217 }
218
219 int
220 main(int argc, char **argv) {
221 isc_result_t status;
222 struct servent *ent;
223 struct server_list *sp = NULL;
224 char *service_local = NULL, *service_remote = NULL;
225 u_int16_t port_local = 0, port_remote = 0;
226 int no_daemon = 0, quiet = 0;
227 int fd;
228 int i;
229 #ifdef DHCPv6
230 struct stream_list *sl = NULL;
231 int local_family_set = 0;
232 #endif
233
234 #ifdef OLD_LOG_NAME
235 progname = "dhcrelay";
236 #else
237 progname = argv[0];
238 #endif
239
240 /* Make sure that file descriptors 0(stdin), 1,(stdout), and
241 2(stderr) are open. To do this, we assume that when we
242 open a file the lowest available file descriptor is used. */
243 fd = open("/dev/null", O_RDWR);
244 if (fd == 0)
245 fd = open("/dev/null", O_RDWR);
246 if (fd == 1)
247 fd = open("/dev/null", O_RDWR);
248 if (fd == 2)
249 log_perror = 0; /* No sense logging to /dev/null. */
250 else if (fd != -1)
251 close(fd);
252
253 openlog(isc_file_basename(progname), DHCP_LOG_OPTIONS, LOG_DAEMON);
254
255 #if !defined(DEBUG)
256 setlogmask(LOG_UPTO(LOG_INFO));
257 #endif
258
259 /* Set up the isc and dns library managers */
260 status = dhcp_context_create(DHCP_CONTEXT_PRE_DB | DHCP_CONTEXT_POST_DB,
261 NULL, NULL);
262 if (status != ISC_R_SUCCESS)
263 log_fatal("Can't initialize context: %s",
264 isc_result_totext(status));
265
266 /* Set up the OMAPI. */
267 status = omapi_init();
268 if (status != ISC_R_SUCCESS)
269 log_fatal("Can't initialize OMAPI: %s",
270 isc_result_totext(status));
271
272 /* Set up the OMAPI wrappers for the interface object. */
273 interface_setup();
274
275 for (i = 1; i < argc; i++) {
276 if (!strcmp(argv[i], "-4")) {
277 #ifdef DHCPv6
278 if (local_family_set && (local_family == AF_INET6)) {
279 usage(use_badproto, "-4");
280 }
281 local_family_set = 1;
282 local_family = AF_INET;
283 } else if (!strcmp(argv[i], "-6")) {
284 if (local_family_set && (local_family == AF_INET)) {
285 usage(use_badproto, "-6");
286 }
287 local_family_set = 1;
288 local_family = AF_INET6;
289 #endif
290 } else if (!strcmp(argv[i], "-d")) {
291 no_daemon = 1;
292 } else if (!strcmp(argv[i], "-q")) {
293 quiet = 1;
294 quiet_interface_discovery = 1;
295 } else if (!strcmp(argv[i], "-p")) {
296 if (++i == argc)
297 usage(use_noarg, argv[i-1]);
298 local_port = validate_port(argv[i]);
299 log_debug("binding to user-specified port %d",
300 ntohs(local_port));
301 } else if (!strcmp(argv[i], "-c")) {
302 int hcount;
303 if (++i == argc)
304 usage(use_noarg, argv[i-1]);
305 hcount = atoi(argv[i]);
306 if (hcount <= 255)
307 max_hop_count= hcount;
308 else
309 usage("Bad hop count to -c: %s", argv[i]);
310 } else if (!strcmp(argv[i], "-i")) {
311 #ifdef DHCPv6
312 if (local_family_set && (local_family == AF_INET6)) {
313 usage(use_v4command, argv[i]);
314 }
315 local_family_set = 1;
316 local_family = AF_INET;
317 #endif
318 if (++i == argc) {
319 usage(use_noarg, argv[i-1]);
320 }
321
322 request_v4_interface(argv[i], INTERFACE_STREAMS);
323 } else if (!strcmp(argv[i], "-iu")) {
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 if (++i == argc) {
332 usage(use_noarg, argv[i-1]);
333 }
334
335 request_v4_interface(argv[i], INTERFACE_UPSTREAM);
336 } else if (!strcmp(argv[i], "-id")) {
337 #ifdef DHCPv6
338 if (local_family_set && (local_family == AF_INET6)) {
339 usage(use_v4command, argv[i]);
340 }
341 local_family_set = 1;
342 local_family = AF_INET;
343 #endif
344 if (++i == argc) {
345 usage(use_noarg, argv[i-1]);
346 }
347
348 request_v4_interface(argv[i], INTERFACE_DOWNSTREAM);
349 } else if (!strcmp(argv[i], "-a")) {
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 add_agent_options = 1;
358 } else if (!strcmp(argv[i], "-A")) {
359 #ifdef DHCPv6
360 if (local_family_set && (local_family == AF_INET6)) {
361 usage(use_v4command, argv[i]);
362 }
363 local_family_set = 1;
364 local_family = AF_INET;
365 #endif
366 if (++i == argc)
367 usage(use_noarg, argv[i-1]);
368
369 dhcp_max_agent_option_packet_length = atoi(argv[i]);
370
371 if (dhcp_max_agent_option_packet_length > DHCP_MTU_MAX)
372 log_fatal("%s: packet length exceeds "
373 "longest possible MTU\n",
374 argv[i]);
375 } else if (!strcmp(argv[i], "-m")) {
376 #ifdef DHCPv6
377 if (local_family_set && (local_family == AF_INET6)) {
378 usage(use_v4command, argv[i]);
379 }
380 local_family_set = 1;
381 local_family = AF_INET;
382 #endif
383 if (++i == argc)
384 usage(use_noarg, argv[i-1]);
385 if (!strcasecmp(argv[i], "append")) {
386 agent_relay_mode = forward_and_append;
387 } else if (!strcasecmp(argv[i], "replace")) {
388 agent_relay_mode = forward_and_replace;
389 } else if (!strcasecmp(argv[i], "forward")) {
390 agent_relay_mode = forward_untouched;
391 } else if (!strcasecmp(argv[i], "discard")) {
392 agent_relay_mode = discard;
393 } else
394 usage("Unknown argument to -m: %s", argv[i]);
395 } else if (!strcmp(argv [i], "-U")) {
396 if (++i == argc)
397 usage(use_noarg, argv[i-1]);
398
399 if (uplink) {
400 usage("more than one uplink (-U) specified: %s"
401 ,argv[i]);
402 }
403
404 /* Allocate the uplink interface */
405 status = interface_allocate(&uplink, MDL);
406 if (status != ISC_R_SUCCESS) {
407 log_fatal("%s: uplink interface_allocate: %s",
408 argv[i], isc_result_totext(status));
409 }
410
411 if (strlen(argv[i]) >= sizeof(uplink->name)) {
412 log_fatal("%s: uplink name too long,"
413 " it cannot exceed: %ld characters",
414 argv[i], (long)(sizeof(uplink->name) - 1));
415 }
416
417 uplink->name[sizeof(uplink->name) - 1] = 0x00;
418 strncpy(uplink->name, argv[i],
419 sizeof(uplink->name) - 1);
420 interface_snorf(uplink, (INTERFACE_REQUESTED |
421 INTERFACE_STREAMS));
422
423 /* Turn on -a, in case they don't do so explicitly */
424 add_agent_options = 1;
425 add_rfc3527_suboption = 1;
426 } else if (!strcmp(argv[i], "-D")) {
427 #ifdef DHCPv6
428 if (local_family_set && (local_family == AF_INET6)) {
429 usage(use_v4command, argv[i]);
430 }
431 local_family_set = 1;
432 local_family = AF_INET;
433 #endif
434 drop_agent_mismatches = 1;
435 #ifdef DHCPv6
436 } else if (!strcmp(argv[i], "-I")) {
437 if (local_family_set && (local_family == AF_INET)) {
438 usage(use_v6command, argv[i]);
439 }
440 local_family_set = 1;
441 local_family = AF_INET6;
442 use_if_id = ISC_TRUE;
443 } else if (!strcmp(argv[i], "-l")) {
444 if (local_family_set && (local_family == AF_INET)) {
445 usage(use_v6command, argv[i]);
446 }
447 local_family_set = 1;
448 local_family = AF_INET6;
449 if (downstreams != NULL)
450 use_if_id = ISC_TRUE;
451 if (++i == argc)
452 usage(use_noarg, argv[i-1]);
453 sl = parse_downstream(argv[i]);
454 sl->next = downstreams;
455 downstreams = sl;
456 } else if (!strcmp(argv[i], "-u")) {
457 if (local_family_set && (local_family == AF_INET)) {
458 usage(use_v6command, argv[i]);
459 }
460 local_family_set = 1;
461 local_family = AF_INET6;
462 if (++i == argc)
463 usage(use_noarg, argv[i-1]);
464 sl = parse_upstream(argv[i]);
465 sl->next = upstreams;
466 upstreams = sl;
467 } else if (!strcmp(argv[i], "-s")) {
468 if (local_family_set && (local_family == AF_INET)) {
469 usage(use_v6command, argv[i]);
470 }
471 local_family_set = 1;
472 local_family = AF_INET6;
473 if (++i == argc)
474 usage(use_noarg, argv[i-1]);
475 dhcrelay_sub_id = argv[i];
476 #endif
477 } else if (!strcmp(argv[i], "-pf")) {
478 if (++i == argc)
479 usage(use_noarg, argv[i-1]);
480 path_dhcrelay_pid = argv[i];
481 no_dhcrelay_pid = ISC_TRUE;
482 } else if (!strcmp(argv[i], "--no-pid")) {
483 no_pid_file = ISC_TRUE;
484 } else if (!strcmp(argv[i], "--version")) {
485 log_info("isc-dhcrelay-%s", PACKAGE_VERSION);
486 exit(0);
487 } else if (!strcmp(argv[i], "--help") ||
488 !strcmp(argv[i], "-h")) {
489 log_info(DHCRELAY_USAGE,
490 #ifdef DHCPv6
491 isc_file_basename(progname),
492 #endif
493 isc_file_basename(progname));
494 exit(0);
495 } else if (argv[i][0] == '-') {
496 usage("Unknown command: %s", argv[i]);
497 } else {
498 struct hostent *he;
499 struct in_addr ia, *iap = NULL;
500
501 #ifdef DHCPv6
502 if (local_family_set && (local_family == AF_INET6)) {
503 usage(use_v4command, argv[i]);
504 }
505 local_family_set = 1;
506 local_family = AF_INET;
507 #endif
508 if (inet_aton(argv[i], &ia)) {
509 iap = &ia;
510 } else {
511 he = gethostbyname(argv[i]);
512 if (!he) {
513 log_error("%s: host unknown", argv[i]);
514 } else {
515 iap = ((struct in_addr *)
516 he->h_addr_list[0]);
517 }
518 }
519
520 if (iap) {
521 sp = ((struct server_list *)
522 dmalloc(sizeof *sp, MDL));
523 if (!sp)
524 log_fatal("no memory for server.\n");
525 sp->next = servers;
526 servers = sp;
527 memcpy(&sp->to.sin_addr, iap, sizeof *iap);
528 }
529 }
530 }
531
532 /*
533 * If the user didn't specify a pid file directly
534 * find one from environment variables or defaults
535 */
536 if (no_dhcrelay_pid == ISC_FALSE) {
537 if (local_family == AF_INET) {
538 path_dhcrelay_pid = getenv("PATH_DHCRELAY_PID");
539 if (path_dhcrelay_pid == NULL)
540 path_dhcrelay_pid = _PATH_DHCRELAY_PID;
541 }
542 #ifdef DHCPv6
543 else {
544 path_dhcrelay_pid = getenv("PATH_DHCRELAY6_PID");
545 if (path_dhcrelay_pid == NULL)
546 path_dhcrelay_pid = _PATH_DHCRELAY6_PID;
547 }
548 #endif
549 }
550
551 if (!quiet) {
552 log_info("%s %s", message, PACKAGE_VERSION);
553 log_info(copyright);
554 log_info(arr);
555 log_info(url);
556 } else
557 log_perror = 0;
558
559 /* Set default port */
560 if (local_family == AF_INET) {
561 service_local = "bootps";
562 service_remote = "bootpc";
563 port_local = htons(67);
564 port_remote = htons(68);
565 }
566 #ifdef DHCPv6
567 else {
568 service_local = "dhcpv6-server";
569 service_remote = "dhcpv6-client";
570 port_local = htons(547);
571 port_remote = htons(546);
572 }
573 #endif
574
575 if (!local_port) {
576 ent = getservbyname(service_local, "udp");
577 if (ent)
578 local_port = ent->s_port;
579 else
580 local_port = port_local;
581
582 ent = getservbyname(service_remote, "udp");
583 if (ent)
584 remote_port = ent->s_port;
585 else
586 remote_port = port_remote;
587
588 endservent();
589 }
590
591 if (local_family == AF_INET) {
592 /* We need at least one server */
593 if (servers == NULL) {
594 log_fatal("No servers specified.");
595 }
596
597
598 /* Set up the server sockaddrs. */
599 for (sp = servers; sp; sp = sp->next) {
600 sp->to.sin_port = local_port;
601 sp->to.sin_family = AF_INET;
602 #ifdef HAVE_SA_LEN
603 sp->to.sin_len = sizeof sp->to;
604 #endif
605 }
606 }
607 #ifdef DHCPv6
608 else {
609 unsigned code;
610
611 /* We need at least one upstream and one downstream interface */
612 if (upstreams == NULL || downstreams == NULL) {
613 log_info("Must specify at least one lower "
614 "and one upper interface.\n");
615 usage(NULL, NULL);
616 }
617
618 /* Set up the initial dhcp option universe. */
619 initialize_common_option_spaces();
620
621 /* Check requested options. */
622 code = D6O_RELAY_MSG;
623 if (!option_code_hash_lookup(&requested_opts[0],
624 dhcpv6_universe.code_hash,
625 &code, 0, MDL))
626 log_fatal("Unable to find the RELAY_MSG "
627 "option definition.");
628 code = D6O_INTERFACE_ID;
629 if (!option_code_hash_lookup(&requested_opts[1],
630 dhcpv6_universe.code_hash,
631 &code, 0, MDL))
632 log_fatal("Unable to find the INTERFACE_ID "
633 "option definition.");
634 }
635 #endif
636
637 /* Get the current time... */
638 gettimeofday(&cur_tv, NULL);
639
640 /* Discover all the network interfaces. */
641 discover_interfaces(DISCOVER_RELAY);
642
643 #ifdef DHCPv6
644 if (local_family == AF_INET6)
645 setup_streams();
646 #endif
647
648 /* Become a daemon... */
649 if (!no_daemon) {
650 int pid;
651 FILE *pf;
652 int pfdesc;
653
654 log_perror = 0;
655
656 if ((pid = fork()) < 0)
657 log_fatal("Can't fork daemon: %m");
658 else if (pid)
659 exit(0);
660
661 if (no_pid_file == ISC_FALSE) {
662 pfdesc = open(path_dhcrelay_pid,
663 O_CREAT | O_TRUNC | O_WRONLY, 0644);
664
665 if (pfdesc < 0) {
666 log_error("Can't create %s: %m",
667 path_dhcrelay_pid);
668 } else {
669 pf = fdopen(pfdesc, "w");
670 if (!pf)
671 log_error("Can't fdopen %s: %m",
672 path_dhcrelay_pid);
673 else {
674 fprintf(pf, "%ld\n",(long)getpid());
675 fclose(pf);
676 }
677 }
678 }
679
680 (void) close(0);
681 (void) close(1);
682 (void) close(2);
683 (void) setsid();
684
685 IGNORE_RET (chdir("/"));
686 }
687
688 /* Set up the packet handler... */
689 if (local_family == AF_INET)
690 bootp_packet_handler = do_relay4;
691 #ifdef DHCPv6
692 else
693 dhcpv6_packet_handler = do_packet6;
694 #endif
695
696 #if defined(ENABLE_GENTLE_SHUTDOWN)
697 /* no signal handlers until we deal with the side effects */
698 /* install signal handlers */
699 signal(SIGINT, dhcp_signal_handler); /* control-c */
700 signal(SIGTERM, dhcp_signal_handler); /* kill */
701 #endif
702
703 /* Start dispatching packets and timeouts... */
704 dispatch();
705
706 /* In fact dispatch() never returns. */
707 return (0);
708 }
709
710 static void
711 do_relay4(struct interface_info *ip, struct dhcp_packet *packet,
712 unsigned int length, unsigned int from_port, struct iaddr from,
713 struct hardware *hfrom) {
714 struct server_list *sp;
715 struct sockaddr_in to;
716 struct interface_info *out;
717 struct hardware hto, *htop;
718
719 if (packet->hlen > sizeof packet->chaddr) {
720 log_info("Discarding packet with invalid hlen, received on "
721 "%s interface.", ip->name);
722 return;
723 }
724 if (ip->address_count < 1 || ip->addresses == NULL) {
725 log_info("Discarding packet received on %s interface that "
726 "has no IPv4 address assigned.", ip->name);
727 return;
728 }
729
730 /* Find the interface that corresponds to the giaddr
731 in the packet. */
732 if (packet->giaddr.s_addr) {
733 for (out = interfaces; out; out = out->next) {
734 int i;
735
736 for (i = 0 ; i < out->address_count ; i++ ) {
737 if (out->addresses[i].s_addr ==
738 packet->giaddr.s_addr) {
739 i = -1;
740 break;
741 }
742 }
743
744 if (i == -1)
745 break;
746 }
747 } else {
748 out = NULL;
749 }
750
751 /* If it's a bootreply, forward it to the client. */
752 if (packet->op == BOOTREPLY) {
753 if (!(ip->flags & INTERFACE_UPSTREAM)) {
754 log_debug("Dropping reply received on %s", ip->name);
755 return;
756 }
757
758 if (!(packet->flags & htons(BOOTP_BROADCAST)) &&
759 can_unicast_without_arp(out)) {
760 to.sin_addr = packet->yiaddr;
761 to.sin_port = remote_port;
762
763 /* and hardware address is not broadcast */
764 htop = &hto;
765 } else {
766 to.sin_addr.s_addr = htonl(INADDR_BROADCAST);
767 to.sin_port = remote_port;
768
769 /* hardware address is broadcast */
770 htop = NULL;
771 }
772 to.sin_family = AF_INET;
773 #ifdef HAVE_SA_LEN
774 to.sin_len = sizeof to;
775 #endif
776
777 memcpy(&hto.hbuf[1], packet->chaddr, packet->hlen);
778 hto.hbuf[0] = packet->htype;
779 hto.hlen = packet->hlen + 1;
780
781 /* Wipe out the agent relay options and, if possible, figure
782 out which interface to use based on the contents of the
783 option that we put on the request to which the server is
784 replying. */
785 if (!(length =
786 strip_relay_agent_options(ip, &out, packet, length)))
787 return;
788
789 if (!out) {
790 log_error("Packet to bogus giaddr %s.\n",
791 inet_ntoa(packet->giaddr));
792 ++bogus_giaddr_drops;
793 return;
794 }
795
796 if (send_packet(out, NULL, packet, length, out->addresses[0],
797 &to, htop) < 0) {
798 ++server_packet_errors;
799 } else {
800 log_debug("Forwarded BOOTREPLY for %s to %s",
801 print_hw_addr(packet->htype, packet->hlen,
802 packet->chaddr),
803 inet_ntoa(to.sin_addr));
804
805 ++server_packets_relayed;
806 }
807 return;
808 }
809
810 /* If giaddr matches one of our addresses, ignore the packet -
811 we just sent it. */
812 if (out)
813 return;
814
815 if (!(ip->flags & INTERFACE_DOWNSTREAM)) {
816 log_debug("Dropping request received on %s", ip->name);
817 return;
818 }
819
820 /* Add relay agent options if indicated. If something goes wrong,
821 * drop the packet. Note this may set packet->giaddr if RFC3527
822 * is enabled. */
823 if (!(length = add_relay_agent_options(ip, packet, length,
824 ip->addresses[0])))
825 return;
826
827 /* If giaddr is not already set, Set it so the server can
828 figure out what net it's from and so that we can later
829 forward the response to the correct net. If it's already
830 set, the response will be sent directly to the relay agent
831 that set giaddr, so we won't see it. */
832 if (!packet->giaddr.s_addr)
833 packet->giaddr = ip->addresses[0];
834 if (packet->hops < max_hop_count)
835 packet->hops = packet->hops + 1;
836 else
837 return;
838
839 /* Otherwise, it's a BOOTREQUEST, so forward it to all the
840 servers. */
841 for (sp = servers; sp; sp = sp->next) {
842 if (send_packet((fallback_interface
843 ? fallback_interface : interfaces),
844 NULL, packet, length, ip->addresses[0],
845 &sp->to, NULL) < 0) {
846 ++client_packet_errors;
847 } else {
848 log_debug("Forwarded BOOTREQUEST for %s to %s",
849 print_hw_addr(packet->htype, packet->hlen,
850 packet->chaddr),
851 inet_ntoa(sp->to.sin_addr));
852 ++client_packets_relayed;
853 }
854 }
855
856 }
857
858 /* Strip any Relay Agent Information options from the DHCP packet
859 option buffer. If there is a circuit ID suboption, look up the
860 outgoing interface based upon it. */
861
862 static int
863 strip_relay_agent_options(struct interface_info *in,
864 struct interface_info **out,
865 struct dhcp_packet *packet,
866 unsigned length) {
867 int is_dhcp = 0;
868 u_int8_t *op, *nextop, *sp, *max;
869 int good_agent_option = 0;
870 int status;
871
872 /* If we're not adding agent options to packets, we're not taking
873 them out either. */
874 if (!add_agent_options)
875 return (length);
876
877 /* If there's no cookie, it's a bootp packet, so we should just
878 forward it unchanged. */
879 if (memcmp(packet->options, DHCP_OPTIONS_COOKIE, 4))
880 return (length);
881
882 max = ((u_int8_t *)packet) + length;
883 sp = op = &packet->options[4];
884
885 while (op < max) {
886 switch(*op) {
887 /* Skip padding... */
888 case DHO_PAD:
889 if (sp != op)
890 *sp = *op;
891 ++op;
892 ++sp;
893 continue;
894
895 /* If we see a message type, it's a DHCP packet. */
896 case DHO_DHCP_MESSAGE_TYPE:
897 is_dhcp = 1;
898 goto skip;
899 break;
900
901 /* Quit immediately if we hit an End option. */
902 case DHO_END:
903 if (sp != op)
904 *sp++ = *op++;
905 goto out;
906
907 case DHO_DHCP_AGENT_OPTIONS:
908 /* We shouldn't see a relay agent option in a
909 packet before we've seen the DHCP packet type,
910 but if we do, we have to leave it alone. */
911 if (!is_dhcp)
912 goto skip;
913
914 /* Do not process an agent option if it exceeds the
915 * buffer. Fail this packet.
916 */
917 nextop = op + op[1] + 2;
918 if (nextop > max)
919 return (0);
920
921 status = find_interface_by_agent_option(packet,
922 out, op + 2,
923 op[1]);
924 if (status == -1 && drop_agent_mismatches)
925 return (0);
926 if (status)
927 good_agent_option = 1;
928 op = nextop;
929 break;
930
931 skip:
932 /* Skip over other options. */
933 default:
934 /* Fail if processing this option will exceed the
935 * buffer(op[1] is malformed).
936 */
937 nextop = op + op[1] + 2;
938 if (nextop > max)
939 return (0);
940
941 if (sp != op) {
942 memmove(sp, op, op[1] + 2);
943 sp += op[1] + 2;
944 op = nextop;
945 } else
946 op = sp = nextop;
947
948 break;
949 }
950 }
951 out:
952
953 /* If it's not a DHCP packet, we're not supposed to touch it. */
954 if (!is_dhcp)
955 return (length);
956
957 /* If none of the agent options we found matched, or if we didn't
958 find any agent options, count this packet as not having any
959 matching agent options, and if we're relying on agent options
960 to determine the outgoing interface, drop the packet. */
961
962 if (!good_agent_option) {
963 ++missing_agent_option;
964 if (drop_agent_mismatches)
965 return (0);
966 }
967
968 /* Adjust the length... */
969 if (sp != op) {
970 length = sp -((u_int8_t *)packet);
971
972 /* Make sure the packet isn't short(this is unlikely,
973 but WTH) */
974 if (length < BOOTP_MIN_LEN) {
975 memset(sp, DHO_PAD, BOOTP_MIN_LEN - length);
976 length = BOOTP_MIN_LEN;
977 }
978 }
979 return (length);
980 }
981
982
983 /* Find an interface that matches the circuit ID specified in the
984 Relay Agent Information option. If one is found, store it through
985 the pointer given; otherwise, leave the existing pointer alone.
986
987 We actually deviate somewhat from the current specification here:
988 if the option buffer is corrupt, we suggest that the caller not
989 respond to this packet. If the circuit ID doesn't match any known
990 interface, we suggest that the caller to drop the packet. Only if
991 we find a circuit ID that matches an existing interface do we tell
992 the caller to go ahead and process the packet. */
993
994 static int
995 find_interface_by_agent_option(struct dhcp_packet *packet,
996 struct interface_info **out,
997 u_int8_t *buf, int len) {
998 int i = 0;
999 u_int8_t *circuit_id = 0;
1000 unsigned circuit_id_len = 0;
1001 struct interface_info *ip;
1002
1003 while (i < len) {
1004 /* If the next agent option overflows the end of the
1005 packet, the agent option buffer is corrupt. */
1006 if (i + 1 == len ||
1007 i + buf[i + 1] + 2 > len) {
1008 ++corrupt_agent_options;
1009 return (-1);
1010 }
1011 switch(buf[i]) {
1012 /* Remember where the circuit ID is... */
1013 case RAI_CIRCUIT_ID:
1014 circuit_id = &buf[i + 2];
1015 circuit_id_len = buf[i + 1];
1016 i += circuit_id_len + 2;
1017 continue;
1018
1019 default:
1020 i += buf[i + 1] + 2;
1021 break;
1022 }
1023 }
1024
1025 /* If there's no circuit ID, it's not really ours, tell the caller
1026 it's no good. */
1027 if (!circuit_id) {
1028 ++missing_circuit_id;
1029 return (-1);
1030 }
1031
1032 /* Scan the interface list looking for an interface whose
1033 name matches the one specified in circuit_id. */
1034
1035 for (ip = interfaces; ip; ip = ip->next) {
1036 if (ip->circuit_id &&
1037 ip->circuit_id_len == circuit_id_len &&
1038 !memcmp(ip->circuit_id, circuit_id, circuit_id_len))
1039 break;
1040 }
1041
1042 /* If we got a match, use it. */
1043 if (ip) {
1044 *out = ip;
1045 return (1);
1046 }
1047
1048 /* If we didn't get a match, the circuit ID was bogus. */
1049 ++bad_circuit_id;
1050 return (-1);
1051 }
1052
1053 /*
1054 * Examine a packet to see if it's a candidate to have a Relay
1055 * Agent Information option tacked onto its tail. If it is, tack
1056 * the option on.
1057 */
1058 static int
1059 add_relay_agent_options(struct interface_info *ip, struct dhcp_packet *packet,
1060 unsigned length, struct in_addr giaddr) {
1061 int is_dhcp = 0, mms;
1062 unsigned optlen;
1063 u_int8_t *op, *nextop, *sp, *max, *end_pad = NULL;
1064 int adding_link_select;
1065
1066 /* If we're not adding agent options to packets, we can skip
1067 this. */
1068 if (!add_agent_options)
1069 return (length);
1070
1071 /* If there's no cookie, it's a bootp packet, so we should just
1072 forward it unchanged. */
1073 if (memcmp(packet->options, DHCP_OPTIONS_COOKIE, 4))
1074 return (length);
1075
1076 max = ((u_int8_t *)packet) + dhcp_max_agent_option_packet_length;
1077
1078 /* Add link selection suboption if enabled and we're the first relay */
1079 adding_link_select = (add_rfc3527_suboption
1080 && (packet->giaddr.s_addr == 0));
1081
1082 /* Commence processing after the cookie. */
1083 sp = op = &packet->options[4];
1084
1085 while (op < max) {
1086 switch(*op) {
1087 /* Skip padding... */
1088 case DHO_PAD:
1089 /* Remember the first pad byte so we can commandeer
1090 * padded space.
1091 *
1092 * XXX: Is this really a good idea? Sure, we can
1093 * seemingly reduce the packet while we're looking,
1094 * but if the packet was signed by the client then
1095 * this padding is part of the checksum(RFC3118),
1096 * and its nonpresence would break authentication.
1097 */
1098 if (end_pad == NULL)
1099 end_pad = sp;
1100
1101 if (sp != op)
1102 *sp++ = *op++;
1103 else
1104 sp = ++op;
1105
1106 continue;
1107
1108 /* If we see a message type, it's a DHCP packet. */
1109 case DHO_DHCP_MESSAGE_TYPE:
1110 is_dhcp = 1;
1111 goto skip;
1112
1113 /*
1114 * If there's a maximum message size option, we
1115 * should pay attention to it
1116 */
1117 case DHO_DHCP_MAX_MESSAGE_SIZE:
1118 mms = ntohs(*(op + 2));
1119 if (mms < dhcp_max_agent_option_packet_length &&
1120 mms >= DHCP_MTU_MIN)
1121 max = ((u_int8_t *)packet) + mms;
1122 goto skip;
1123
1124 /* Quit immediately if we hit an End option. */
1125 case DHO_END:
1126 goto out;
1127
1128 case DHO_DHCP_AGENT_OPTIONS:
1129 /* We shouldn't see a relay agent option in a
1130 packet before we've seen the DHCP packet type,
1131 but if we do, we have to leave it alone. */
1132 if (!is_dhcp)
1133 goto skip;
1134
1135 end_pad = NULL;
1136
1137 /* There's already a Relay Agent Information option
1138 in this packet. How embarrassing. Decide what
1139 to do based on the mode the user specified. */
1140
1141 switch(agent_relay_mode) {
1142 case forward_and_append:
1143 goto skip;
1144 case forward_untouched:
1145 return (length);
1146 case discard:
1147 return (0);
1148 case forward_and_replace:
1149 default:
1150 break;
1151 }
1152
1153 /* Skip over the agent option and start copying
1154 if we aren't copying already. */
1155 op += op[1] + 2;
1156 break;
1157
1158 skip:
1159 /* Skip over other options. */
1160 default:
1161 /* Fail if processing this option will exceed the
1162 * buffer(op[1] is malformed).
1163 */
1164 nextop = op + op[1] + 2;
1165 if (nextop > max)
1166 return (0);
1167
1168 end_pad = NULL;
1169
1170 if (sp != op) {
1171 memmove(sp, op, op[1] + 2);
1172 sp += op[1] + 2;
1173 op = nextop;
1174 } else
1175 op = sp = nextop;
1176
1177 break;
1178 }
1179 }
1180 out:
1181
1182 /* If it's not a DHCP packet, we're not supposed to touch it. */
1183 if (!is_dhcp)
1184 return (length);
1185
1186 /* If the packet was padded out, we can store the agent option
1187 at the beginning of the padding. */
1188
1189 if (end_pad != NULL)
1190 sp = end_pad;
1191
1192 #if 0
1193 /* Remember where the end of the packet was after parsing
1194 it. */
1195 op = sp;
1196 #endif
1197
1198 /* Sanity check. Had better not ever happen. */
1199 if ((ip->circuit_id_len > 255) ||(ip->circuit_id_len < 1))
1200 log_fatal("Circuit ID length %d out of range [1-255] on "
1201 "%s\n", ip->circuit_id_len, ip->name);
1202 optlen = ip->circuit_id_len + 2; /* RAI_CIRCUIT_ID + len */
1203
1204 if (ip->remote_id) {
1205 if (ip->remote_id_len > 255 || ip->remote_id_len < 1)
1206 log_fatal("Remote ID length %d out of range [1-255] "
1207 "on %s\n", ip->remote_id_len, ip->name);
1208 optlen += ip->remote_id_len + 2; /* RAI_REMOTE_ID + len */
1209 }
1210
1211 if (adding_link_select) {
1212 optlen += 6;
1213 }
1214
1215 /* We do not support relay option fragmenting(multiple options to
1216 * support an option data exceeding 255 bytes).
1217 */
1218 if ((optlen < 3) ||(optlen > 255))
1219 log_fatal("Total agent option length(%u) out of range "
1220 "[3 - 255] on %s\n", optlen, ip->name);
1221
1222 /*
1223 * Is there room for the option, its code+len, and DHO_END?
1224 * If not, forward without adding the option.
1225 */
1226 if (max - sp >= optlen + 3) {
1227 log_debug("Adding %d-byte relay agent option", optlen + 3);
1228
1229 /* Okay, cons up *our* Relay Agent Information option. */
1230 *sp++ = DHO_DHCP_AGENT_OPTIONS;
1231 *sp++ = optlen;
1232
1233 /* Copy in the circuit id... */
1234 *sp++ = RAI_CIRCUIT_ID;
1235 *sp++ = ip->circuit_id_len;
1236 memcpy(sp, ip->circuit_id, ip->circuit_id_len);
1237 sp += ip->circuit_id_len;
1238
1239 /* Copy in remote ID... */
1240 if (ip->remote_id) {
1241 *sp++ = RAI_REMOTE_ID;
1242 *sp++ = ip->remote_id_len;
1243 memcpy(sp, ip->remote_id, ip->remote_id_len);
1244 sp += ip->remote_id_len;
1245 }
1246
1247 /* RFC3527: Use the inbound packet's interface address in
1248 * the link selection suboption and set the outbound giaddr
1249 * to the uplink address. */
1250 if (adding_link_select) {
1251 *sp++ = RAI_LINK_SELECT;
1252 *sp++ = 4u;
1253 memcpy(sp, &giaddr.s_addr, 4);
1254 sp += 4;
1255 packet->giaddr = uplink->addresses[0];
1256 log_debug ("Adding link selection suboption"
1257 " with addr: %s", inet_ntoa(giaddr));
1258 }
1259 } else {
1260 ++agent_option_errors;
1261 log_error("No room in packet (used %d of %d) "
1262 "for %d-byte relay agent option: omitted",
1263 (int) (sp - ((u_int8_t *) packet)),
1264 (int) (max - ((u_int8_t *) packet)),
1265 optlen + 3);
1266 }
1267
1268 /*
1269 * Deposit an END option unless the packet is full (shouldn't
1270 * be possible).
1271 */
1272 if (sp < max)
1273 *sp++ = DHO_END;
1274
1275 /* Recalculate total packet length. */
1276 length = sp -((u_int8_t *)packet);
1277
1278 /* Make sure the packet isn't short(this is unlikely, but WTH) */
1279 if (length < BOOTP_MIN_LEN) {
1280 memset(sp, DHO_PAD, BOOTP_MIN_LEN - length);
1281 return (BOOTP_MIN_LEN);
1282 }
1283
1284 return (length);
1285 }
1286
1287 #ifdef DHCPv6
1288 /*
1289 * Parse a downstream argument: [address%]interface[#index].
1290 */
1291 static struct stream_list *
1292 parse_downstream(char *arg) {
1293 struct stream_list *dp, *up;
1294 struct interface_info *ifp = NULL;
1295 char *ifname, *addr, *iid;
1296 isc_result_t status;
1297
1298 if (!supports_multiple_interfaces(ifp) &&
1299 (downstreams != NULL))
1300 log_fatal("No support for multiple interfaces.");
1301
1302 /* Decode the argument. */
1303 ifname = strchr(arg, '%');
1304 if (ifname == NULL) {
1305 ifname = arg;
1306 addr = NULL;
1307 } else {
1308 *ifname++ = '\0';
1309 addr = arg;
1310 }
1311 iid = strchr(ifname, '#');
1312 if (iid != NULL) {
1313 *iid++ = '\0';
1314 }
1315 if (strlen(ifname) >= sizeof(ifp->name)) {
1316 usage("Interface name '%s' too long", ifname);
1317 }
1318
1319 /* Don't declare twice. */
1320 for (dp = downstreams; dp; dp = dp->next) {
1321 if (strcmp(ifname, dp->ifp->name) == 0)
1322 log_fatal("Down interface '%s' declared twice.",
1323 ifname);
1324 }
1325
1326 /* Share with up side? */
1327 for (up = upstreams; up; up = up->next) {
1328 if (strcmp(ifname, up->ifp->name) == 0) {
1329 log_info("parse_downstream: Interface '%s' is "
1330 "both down and up.", ifname);
1331 ifp = up->ifp;
1332 break;
1333 }
1334 }
1335
1336 /* New interface. */
1337 if (ifp == NULL) {
1338 status = interface_allocate(&ifp, MDL);
1339 if (status != ISC_R_SUCCESS)
1340 log_fatal("%s: interface_allocate: %s",
1341 arg, isc_result_totext(status));
1342 strcpy(ifp->name, ifname);
1343 if (interfaces) {
1344 interface_reference(&ifp->next, interfaces, MDL);
1345 interface_dereference(&interfaces, MDL);
1346 }
1347 interface_reference(&interfaces, ifp, MDL);
1348 }
1349 ifp->flags |= INTERFACE_REQUESTED | INTERFACE_DOWNSTREAM;
1350
1351 /* New downstream. */
1352 dp = (struct stream_list *) dmalloc(sizeof(*dp), MDL);
1353 if (!dp)
1354 log_fatal("No memory for downstream.");
1355 dp->ifp = ifp;
1356 if (iid != NULL) {
1357 dp->id = atoi(iid);
1358 } else {
1359 dp->id = -1;
1360 }
1361 /* !addr case handled by setup. */
1362 if (addr && (inet_pton(AF_INET6, addr, &dp->link.sin6_addr) <= 0))
1363 log_fatal("Bad link address '%s'", addr);
1364
1365 return dp;
1366 }
1367
1368 /*
1369 * Parse an upstream argument: [address]%interface.
1370 */
1371 static struct stream_list *
1372 parse_upstream(char *arg) {
1373 struct stream_list *up, *dp;
1374 struct interface_info *ifp = NULL;
1375 char *ifname, *addr;
1376 isc_result_t status;
1377
1378 /* Decode the argument. */
1379 ifname = strchr(arg, '%');
1380 if (ifname == NULL) {
1381 ifname = arg;
1382 addr = All_DHCP_Servers;
1383 } else {
1384 *ifname++ = '\0';
1385 addr = arg;
1386 }
1387 if (strlen(ifname) >= sizeof(ifp->name)) {
1388 log_fatal("Interface name '%s' too long", ifname);
1389 }
1390
1391 /* Shared up interface? */
1392 for (up = upstreams; up; up = up->next) {
1393 if (strcmp(ifname, up->ifp->name) == 0) {
1394 ifp = up->ifp;
1395 break;
1396 }
1397 }
1398 for (dp = downstreams; dp; dp = dp->next) {
1399 if (strcmp(ifname, dp->ifp->name) == 0) {
1400 log_info("parse_upstream: Interface '%s' is "
1401 "both down and up.", ifname);
1402 ifp = dp->ifp;
1403 break;
1404 }
1405 }
1406
1407 /* New interface. */
1408 if (ifp == NULL) {
1409 status = interface_allocate(&ifp, MDL);
1410 if (status != ISC_R_SUCCESS)
1411 log_fatal("%s: interface_allocate: %s",
1412 arg, isc_result_totext(status));
1413 strcpy(ifp->name, ifname);
1414 if (interfaces) {
1415 interface_reference(&ifp->next, interfaces, MDL);
1416 interface_dereference(&interfaces, MDL);
1417 }
1418 interface_reference(&interfaces, ifp, MDL);
1419 }
1420 ifp->flags |= INTERFACE_REQUESTED | INTERFACE_UPSTREAM;
1421
1422 /* New upstream. */
1423 up = (struct stream_list *) dmalloc(sizeof(*up), MDL);
1424 if (up == NULL)
1425 log_fatal("No memory for upstream.");
1426
1427 up->ifp = ifp;
1428
1429 if (inet_pton(AF_INET6, addr, &up->link.sin6_addr) <= 0)
1430 log_fatal("Bad address %s", addr);
1431
1432 return up;
1433 }
1434
1435 /*
1436 * Setup downstream interfaces.
1437 */
1438 static void
1439 setup_streams(void) {
1440 struct stream_list *dp, *up;
1441 int i;
1442 isc_boolean_t link_is_set;
1443
1444 for (dp = downstreams; dp; dp = dp->next) {
1445 /* Check interface */
1446 if (dp->ifp->v6address_count == 0)
1447 log_fatal("Interface '%s' has no IPv6 addresses.",
1448 dp->ifp->name);
1449
1450 /* Check/set link. */
1451 if (IN6_IS_ADDR_UNSPECIFIED(&dp->link.sin6_addr))
1452 link_is_set = ISC_FALSE;
1453 else
1454 link_is_set = ISC_TRUE;
1455 for (i = 0; i < dp->ifp->v6address_count; i++) {
1456 if (IN6_IS_ADDR_LINKLOCAL(&dp->ifp->v6addresses[i]))
1457 continue;
1458 if (!link_is_set)
1459 break;
1460 if (!memcmp(&dp->ifp->v6addresses[i],
1461 &dp->link.sin6_addr,
1462 sizeof(dp->link.sin6_addr)))
1463 break;
1464 }
1465 if (i == dp->ifp->v6address_count)
1466 log_fatal("Interface %s does not have global IPv6 "
1467 "address assigned.", dp->ifp->name);
1468 if (!link_is_set)
1469 memcpy(&dp->link.sin6_addr,
1470 &dp->ifp->v6addresses[i],
1471 sizeof(dp->link.sin6_addr));
1472
1473 /* Set interface-id. */
1474 if (dp->id == -1)
1475 dp->id = dp->ifp->index;
1476 }
1477
1478 for (up = upstreams; up; up = up->next) {
1479 up->link.sin6_port = local_port;
1480 up->link.sin6_family = AF_INET6;
1481 #ifdef HAVE_SA_LEN
1482 up->link.sin6_len = sizeof(up->link);
1483 #endif
1484
1485 if (up->ifp->v6address_count == 0)
1486 log_fatal("Interface '%s' has no IPv6 addresses.",
1487 up->ifp->name);
1488
1489 /* RFC 3315 Sec 20 - "If the relay agent relays messages to
1490 * the All_DHCP_Servers address or other multicast addresses,
1491 * it sets the Hop Limit field to 32." */
1492 if (IN6_IS_ADDR_MULTICAST(&up->link.sin6_addr)) {
1493 set_multicast_hop_limit(up->ifp, HOP_COUNT_LIMIT);
1494 }
1495 }
1496 }
1497
1498 /*
1499 * Add DHCPv6 agent options here.
1500 */
1501 static const int required_forw_opts[] = {
1502 D6O_INTERFACE_ID,
1503 D6O_SUBSCRIBER_ID,
1504 D6O_RELAY_MSG,
1505 0
1506 };
1507
1508 /*
1509 * Process a packet upwards, i.e., from client to server.
1510 */
1511 static void
1512 process_up6(struct packet *packet, struct stream_list *dp) {
1513 char forw_data[65535];
1514 unsigned cursor;
1515 struct dhcpv6_relay_packet *relay;
1516 struct option_state *opts;
1517 struct stream_list *up;
1518
1519 /* Check if the message should be relayed to the server. */
1520 switch (packet->dhcpv6_msg_type) {
1521 case DHCPV6_SOLICIT:
1522 case DHCPV6_REQUEST:
1523 case DHCPV6_CONFIRM:
1524 case DHCPV6_RENEW:
1525 case DHCPV6_REBIND:
1526 case DHCPV6_RELEASE:
1527 case DHCPV6_DECLINE:
1528 case DHCPV6_INFORMATION_REQUEST:
1529 case DHCPV6_RELAY_FORW:
1530 case DHCPV6_LEASEQUERY:
1531 case DHCPV6_DHCPV4_QUERY:
1532 log_info("Relaying %s from %s port %d going up.",
1533 dhcpv6_type_names[packet->dhcpv6_msg_type],
1534 piaddr(packet->client_addr),
1535 ntohs(packet->client_port));
1536 break;
1537
1538 case DHCPV6_ADVERTISE:
1539 case DHCPV6_REPLY:
1540 case DHCPV6_RECONFIGURE:
1541 case DHCPV6_RELAY_REPL:
1542 case DHCPV6_LEASEQUERY_REPLY:
1543 case DHCPV6_DHCPV4_RESPONSE:
1544 log_info("Discarding %s from %s port %d going up.",
1545 dhcpv6_type_names[packet->dhcpv6_msg_type],
1546 piaddr(packet->client_addr),
1547 ntohs(packet->client_port));
1548 return;
1549
1550 default:
1551 log_info("Unknown %d type from %s port %d going up.",
1552 packet->dhcpv6_msg_type,
1553 piaddr(packet->client_addr),
1554 ntohs(packet->client_port));
1555 return;
1556 }
1557
1558 /* Build the relay-forward header. */
1559 relay = (struct dhcpv6_relay_packet *) forw_data;
1560 cursor = offsetof(struct dhcpv6_relay_packet, options);
1561 relay->msg_type = DHCPV6_RELAY_FORW;
1562 if (packet->dhcpv6_msg_type == DHCPV6_RELAY_FORW) {
1563 if (packet->dhcpv6_hop_count >= max_hop_count) {
1564 log_info("Hop count exceeded,");
1565 return;
1566 }
1567 relay->hop_count = packet->dhcpv6_hop_count + 1;
1568 if (dp) {
1569 memcpy(&relay->link_address, &dp->link.sin6_addr, 16);
1570 } else {
1571 /* On smart relay add: && !global. */
1572 if (!use_if_id && downstreams->next) {
1573 log_info("Shan't get back the interface.");
1574 return;
1575 }
1576 memset(&relay->link_address, 0, 16);
1577 }
1578 } else {
1579 relay->hop_count = 0;
1580 if (!dp)
1581 return;
1582 memcpy(&relay->link_address, &dp->link.sin6_addr, 16);
1583 }
1584 memcpy(&relay->peer_address, packet->client_addr.iabuf, 16);
1585
1586 /* Get an option state. */
1587 opts = NULL;
1588 if (!option_state_allocate(&opts, MDL)) {
1589 log_fatal("No memory for upwards options.");
1590 }
1591
1592 /* Add an interface-id (if used). */
1593 if (use_if_id) {
1594 int if_id;
1595
1596 if (dp) {
1597 if_id = dp->id;
1598 } else if (!downstreams->next) {
1599 if_id = downstreams->id;
1600 } else {
1601 log_info("Don't know the interface.");
1602 option_state_dereference(&opts, MDL);
1603 return;
1604 }
1605
1606 if (!save_option_buffer(&dhcpv6_universe, opts,
1607 NULL, (unsigned char *) &if_id,
1608 sizeof(int),
1609 D6O_INTERFACE_ID, 0)) {
1610 log_error("Can't save interface-id.");
1611 option_state_dereference(&opts, MDL);
1612 return;
1613 }
1614 }
1615
1616 /* Add a subscriber-id if desired. */
1617 /* This is for testing rather than general use */
1618 if (dhcrelay_sub_id != NULL) {
1619 if (!save_option_buffer(&dhcpv6_universe, opts, NULL,
1620 (unsigned char *) dhcrelay_sub_id,
1621 strlen(dhcrelay_sub_id),
1622 D6O_SUBSCRIBER_ID, 0)) {
1623 log_error("Can't save subsriber-id.");
1624 option_state_dereference(&opts, MDL);
1625 return;
1626 }
1627 }
1628
1629
1630 /* Add the relay-msg carrying the packet. */
1631 if (!save_option_buffer(&dhcpv6_universe, opts,
1632 NULL, (unsigned char *) packet->raw,
1633 packet->packet_length,
1634 D6O_RELAY_MSG, 0)) {
1635 log_error("Can't save relay-msg.");
1636 option_state_dereference(&opts, MDL);
1637 return;
1638 }
1639
1640 /* Finish the relay-forward message. */
1641 cursor += store_options6(forw_data + cursor,
1642 sizeof(forw_data) - cursor,
1643 opts, packet,
1644 required_forw_opts, NULL);
1645 option_state_dereference(&opts, MDL);
1646
1647 /* Send it to all upstreams. */
1648 for (up = upstreams; up; up = up->next) {
1649 send_packet6(up->ifp, (unsigned char *) forw_data,
1650 (size_t) cursor, &up->link);
1651 }
1652 }
1653
1654 /*
1655 * Process a packet downwards, i.e., from server to client.
1656 */
1657 static void
1658 process_down6(struct packet *packet) {
1659 struct stream_list *dp;
1660 struct option_cache *oc;
1661 struct data_string relay_msg;
1662 const struct dhcpv6_packet *msg;
1663 struct data_string if_id;
1664 struct sockaddr_in6 to;
1665 struct iaddr peer;
1666
1667 /* The packet must be a relay-reply message. */
1668 if (packet->dhcpv6_msg_type != DHCPV6_RELAY_REPL) {
1669 if (packet->dhcpv6_msg_type < dhcpv6_type_name_max)
1670 log_info("Discarding %s from %s port %d going down.",
1671 dhcpv6_type_names[packet->dhcpv6_msg_type],
1672 piaddr(packet->client_addr),
1673 ntohs(packet->client_port));
1674 else
1675 log_info("Unknown %d type from %s port %d going down.",
1676 packet->dhcpv6_msg_type,
1677 piaddr(packet->client_addr),
1678 ntohs(packet->client_port));
1679 return;
1680 }
1681
1682 /* Inits. */
1683 memset(&relay_msg, 0, sizeof(relay_msg));
1684 memset(&if_id, 0, sizeof(if_id));
1685 memset(&to, 0, sizeof(to));
1686 to.sin6_family = AF_INET6;
1687 #ifdef HAVE_SA_LEN
1688 to.sin6_len = sizeof(to);
1689 #endif
1690 to.sin6_port = remote_port;
1691 peer.len = 16;
1692
1693 /* Get the relay-msg option (carrying the message to relay). */
1694 oc = lookup_option(&dhcpv6_universe, packet->options, D6O_RELAY_MSG);
1695 if (oc == NULL) {
1696 log_info("No relay-msg.");
1697 return;
1698 }
1699 if (!evaluate_option_cache(&relay_msg, packet, NULL, NULL,
1700 packet->options, NULL,
1701 &global_scope, oc, MDL) ||
1702 (relay_msg.len < offsetof(struct dhcpv6_packet, options))) {
1703 log_error("Can't evaluate relay-msg.");
1704 return;
1705 }
1706 msg = (const struct dhcpv6_packet *) relay_msg.data;
1707
1708 /* Get the interface-id (if exists) and the downstream. */
1709 oc = lookup_option(&dhcpv6_universe, packet->options,
1710 D6O_INTERFACE_ID);
1711 if (oc != NULL) {
1712 int if_index;
1713
1714 if (!evaluate_option_cache(&if_id, packet, NULL, NULL,
1715 packet->options, NULL,
1716 &global_scope, oc, MDL) ||
1717 (if_id.len != sizeof(int))) {
1718 log_info("Can't evaluate interface-id.");
1719 goto cleanup;
1720 }
1721 memcpy(&if_index, if_id.data, sizeof(int));
1722 for (dp = downstreams; dp; dp = dp->next) {
1723 if (dp->id == if_index)
1724 break;
1725 }
1726 } else {
1727 if (use_if_id) {
1728 /* Require an interface-id. */
1729 log_info("No interface-id.");
1730 goto cleanup;
1731 }
1732 for (dp = downstreams; dp; dp = dp->next) {
1733 /* Get the first matching one. */
1734 if (!memcmp(&dp->link.sin6_addr,
1735 &packet->dhcpv6_link_address,
1736 sizeof(struct in6_addr)))
1737 break;
1738 }
1739 }
1740 /* Why bother when there is no choice. */
1741 if (!dp && downstreams && !downstreams->next)
1742 dp = downstreams;
1743 if (!dp) {
1744 log_info("Can't find the down interface.");
1745 goto cleanup;
1746 }
1747 memcpy(peer.iabuf, &packet->dhcpv6_peer_address, peer.len);
1748 to.sin6_addr = packet->dhcpv6_peer_address;
1749
1750 /* Check if we should relay the carried message. */
1751 switch (msg->msg_type) {
1752 /* Relay-Reply of for another relay, not a client. */
1753 case DHCPV6_RELAY_REPL:
1754 to.sin6_port = local_port;
1755 /* Fall into: */
1756
1757 case DHCPV6_ADVERTISE:
1758 case DHCPV6_REPLY:
1759 case DHCPV6_RECONFIGURE:
1760 case DHCPV6_RELAY_FORW:
1761 case DHCPV6_LEASEQUERY_REPLY:
1762 case DHCPV6_DHCPV4_RESPONSE:
1763 log_info("Relaying %s to %s port %d down.",
1764 dhcpv6_type_names[msg->msg_type],
1765 piaddr(peer),
1766 ntohs(to.sin6_port));
1767 break;
1768
1769 case DHCPV6_SOLICIT:
1770 case DHCPV6_REQUEST:
1771 case DHCPV6_CONFIRM:
1772 case DHCPV6_RENEW:
1773 case DHCPV6_REBIND:
1774 case DHCPV6_RELEASE:
1775 case DHCPV6_DECLINE:
1776 case DHCPV6_INFORMATION_REQUEST:
1777 case DHCPV6_LEASEQUERY:
1778 case DHCPV6_DHCPV4_QUERY:
1779 log_info("Discarding %s to %s port %d down.",
1780 dhcpv6_type_names[msg->msg_type],
1781 piaddr(peer),
1782 ntohs(to.sin6_port));
1783 goto cleanup;
1784
1785 default:
1786 log_info("Unknown %d type to %s port %d down.",
1787 msg->msg_type,
1788 piaddr(peer),
1789 ntohs(to.sin6_port));
1790 goto cleanup;
1791 }
1792
1793 /* Send the message to the downstream. */
1794 send_packet6(dp->ifp, (unsigned char *) relay_msg.data,
1795 (size_t) relay_msg.len, &to);
1796
1797 cleanup:
1798 if (relay_msg.data != NULL)
1799 data_string_forget(&relay_msg, MDL);
1800 if (if_id.data != NULL)
1801 data_string_forget(&if_id, MDL);
1802 }
1803
1804 /*
1805 * Called by the dispatch packet handler with a decoded packet.
1806 */
1807 void
1808 dhcpv6(struct packet *packet) {
1809 struct stream_list *dp;
1810
1811 /* Try all relay-replies downwards. */
1812 if (packet->dhcpv6_msg_type == DHCPV6_RELAY_REPL) {
1813 process_down6(packet);
1814 return;
1815 }
1816 /* Others are candidates to go up if they come from down. */
1817 for (dp = downstreams; dp; dp = dp->next) {
1818 if (packet->interface != dp->ifp)
1819 continue;
1820 process_up6(packet, dp);
1821 return;
1822 }
1823 /* Relay-forward could work from an unknown interface. */
1824 if (packet->dhcpv6_msg_type == DHCPV6_RELAY_FORW) {
1825 process_up6(packet, NULL);
1826 return;
1827 }
1828
1829 log_info("Can't process packet from interface '%s'.",
1830 packet->interface->name);
1831 }
1832 #endif
1833
1834 /* Stub routines needed for linking with DHCP libraries. */
1835 void
1836 bootp(struct packet *packet) {
1837 return;
1838 }
1839
1840 void
1841 dhcp(struct packet *packet) {
1842 return;
1843 }
1844
1845 void
1846 classify(struct packet *p, struct class *c) {
1847 return;
1848 }
1849
1850 int
1851 check_collection(struct packet *p, struct lease *l, struct collection *c) {
1852 return 0;
1853 }
1854
1855 isc_result_t
1856 find_class(struct class **class, const char *c1, const char *c2, int i) {
1857 return ISC_R_NOTFOUND;
1858 }
1859
1860 int
1861 parse_allow_deny(struct option_cache **oc, struct parse *p, int i) {
1862 return 0;
1863 }
1864
1865 isc_result_t
1866 dhcp_set_control_state(control_object_state_t oldstate,
1867 control_object_state_t newstate) {
1868 if (newstate != server_shutdown)
1869 return ISC_R_SUCCESS;
1870
1871 if (no_pid_file == ISC_FALSE)
1872 (void) unlink(path_dhcrelay_pid);
1873
1874 exit(0);
1875 }
1876
1877 /*!
1878 *
1879 * \brief Allocate an interface as requested with a given set of flags
1880 *
1881 * The requested interface is allocated, its flags field is set to
1882 * INTERFACE_REQUESTED OR'd with the given flags, and then added to
1883 * the list of interfaces.
1884 *
1885 * \param name - name of the requested interface
1886 * \param flags - additional flags for the interface
1887 *
1888 * \return Nothing
1889 */
1890 void request_v4_interface(const char* name, int flags) {
1891 struct interface_info *tmp = NULL;
1892 int len = strlen(name);
1893 isc_result_t status;
1894
1895 if (len >= sizeof(tmp->name)) {
1896 log_fatal("%s: interface name too long (is %d)", name, len);
1897 }
1898
1899 status = interface_allocate(&tmp, MDL);
1900 if (status != ISC_R_SUCCESS) {
1901 log_fatal("%s: interface_allocate: %s", name,
1902 isc_result_totext(status));
1903 }
1904
1905 log_debug("Requesting: %s as upstream: %c downstream: %c", name,
1906 (flags & INTERFACE_UPSTREAM ? 'Y' : 'N'),
1907 (flags & INTERFACE_DOWNSTREAM ? 'Y' : 'N'));
1908
1909 strncpy(tmp->name, name, len);
1910 interface_snorf(tmp, (INTERFACE_REQUESTED | flags));
1911 interface_dereference(&tmp, MDL);
1912 }