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