3 DHCP/BOOTP Relay Agent. */
6 * Copyright(c) 2004-2012 by Internet Systems Consortium, Inc.("ISC")
7 * Copyright(c) 1997-2003 by Internet Software Consortium
9 * Permission to use, copy, modify, and distribute this software for any
10 * purpose with or without fee is hereby granted, provided that the above
11 * copyright notice and this permission notice appear in all copies.
13 * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
14 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
15 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
16 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
17 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
18 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
19 * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
21 * Internet Systems Consortium, Inc.
23 * Redwood City, CA 94063
25 * https://www.isc.org/
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''.
39 TIME default_lease_time = 43200; /* 12 hours... */
40 TIME max_lease_time = 86400; /* 24 hours... */
41 struct tree_cache *global_options[256];
43 struct option *requested_opts[2];
45 /* Needed to prevent linking against conflex.c. */
51 const char *path_dhcrelay_pid = _PATH_DHCRELAY_PID;
52 isc_boolean_t no_dhcrelay_pid = ISC_FALSE;
53 /* False (default) => we write and use a pid file */
54 isc_boolean_t no_pid_file = ISC_FALSE;
56 int bogus_agent_drops = 0; /* Packets dropped because agent option
57 field was specified and we're not relaying
58 packets that already have an agent option
60 int bogus_giaddr_drops = 0; /* Packets sent to us to relay back to a
61 client, but with a bogus giaddr. */
62 int client_packets_relayed = 0; /* Packets relayed from client to server. */
63 int server_packet_errors = 0; /* Errors sending packets to servers. */
64 int server_packets_relayed = 0; /* Packets relayed from server to client. */
65 int client_packet_errors = 0; /* Errors sending packets to clients. */
67 int add_agent_options = 0; /* If nonzero, add relay agent options. */
69 int agent_option_errors = 0; /* Number of packets forwarded without
70 agent options because there was no room. */
71 int drop_agent_mismatches = 0; /* If nonzero, drop server replies that
72 don't have matching circuit-id's. */
73 int corrupt_agent_options = 0; /* Number of packets dropped because
74 relay agent information option was bad. */
75 int missing_agent_option = 0; /* Number of packets dropped because no
76 RAI option matching our ID was found. */
77 int bad_circuit_id = 0; /* Circuit ID option in matching RAI option
78 did not match any known circuit ID. */
79 int missing_circuit_id = 0; /* Circuit ID option in matching RAI option
81 int max_hop_count = 10; /* Maximum hop count */
84 /* Force use of DHCPv6 interface-id option. */
85 isc_boolean_t use_if_id = ISC_FALSE;
88 /* Maximum size of a packet with agent options added. */
89 int dhcp_max_agent_option_packet_length = DHCP_MTU_MIN;
91 /* What to do about packets we're asked to relay that
92 already have a relay option: */
93 enum { forward_and_append, /* Forward and append our own relay option. */
94 forward_and_replace, /* Forward, but replace theirs with ours. */
95 forward_untouched, /* Forward without changes. */
96 discard } agent_relay_mode = forward_and_replace;
99 u_int16_t remote_port;
101 /* Relay agent server list. */
103 struct server_list *next;
104 struct sockaddr_in to;
109 struct stream_list *next;
110 struct interface_info *ifp;
111 struct sockaddr_in6 link;
113 } *downstreams, *upstreams;
115 static struct stream_list *parse_downstream(char *);
116 static struct stream_list *parse_upstream(char *);
117 static void setup_streams(void);
120 static void do_relay4(struct interface_info *, struct dhcp_packet *,
121 unsigned int, unsigned int, struct iaddr,
123 static int add_relay_agent_options(struct interface_info *,
124 struct dhcp_packet *, unsigned,
126 static int find_interface_by_agent_option(struct dhcp_packet *,
127 struct interface_info **, u_int8_t *, int);
128 static int strip_relay_agent_options(struct interface_info *,
129 struct interface_info **,
130 struct dhcp_packet *, unsigned);
132 static const char copyright[] =
133 "Copyright 2004-2012 Internet Systems Consortium.";
134 static const char arr[] = "All rights reserved.";
135 static const char message[] =
136 "Internet Systems Consortium DHCP Relay Agent";
137 static const char url[] =
138 "For info, please visit https://www.isc.org/software/dhcp/";
141 #define DHCRELAY_USAGE \
142 "Usage: dhcrelay [-4] [-d] [-q] [-a] [-D]\n"\
143 " [-A <length>] [-c <hops>] [-p <port>]\n" \
144 " [-pf <pid-file>] [--no-pid]\n"\
145 " [-m append|replace|forward|discard]\n" \
146 " [-i interface0 [ ... -i interfaceN]\n" \
147 " server0 [ ... serverN]\n\n" \
148 " dhcrelay -6 [-d] [-q] [-I] [-c <hops>] [-p <port>]\n" \
149 " [-pf <pid-file>] [--no-pid]\n"\
150 " -l lower0 [ ... -l lowerN]\n" \
151 " -u upper0 [ ... -u upperN]\n" \
152 " lower (client link): [address%%]interface[#index]\n" \
153 " upper (server link): [address%%]interface"
155 #define DHCRELAY_USAGE \
156 "Usage: dhcrelay [-d] [-q] [-a] [-D] [-A <length>] [-c <hops>] [-p <port>]\n" \
157 " [-pf <pid-file>] [--no-pid]\n"\
158 " [-m append|replace|forward|discard]\n" \
159 " [-i interface0 [ ... -i interfaceN]\n" \
160 " server0 [ ... serverN]\n\n"
163 static void usage() {
164 log_fatal(DHCRELAY_USAGE);
168 main(int argc, char **argv) {
171 struct server_list *sp = NULL;
172 struct interface_info *tmp = NULL;
173 char *service_local = NULL, *service_remote = NULL;
174 u_int16_t port_local = 0, port_remote = 0;
175 int no_daemon = 0, quiet = 0;
179 struct stream_list *sl = NULL;
180 int local_family_set = 0;
183 /* Make sure that file descriptors 0(stdin), 1,(stdout), and
184 2(stderr) are open. To do this, we assume that when we
185 open a file the lowest available file descriptor is used. */
186 fd = open("/dev/null", O_RDWR);
188 fd = open("/dev/null", O_RDWR);
190 fd = open("/dev/null", O_RDWR);
192 log_perror = 0; /* No sense logging to /dev/null. */
196 openlog("dhcrelay", LOG_NDELAY, LOG_DAEMON);
199 setlogmask(LOG_UPTO(LOG_INFO));
202 /* Set up the isc and dns library managers */
203 status = dhcp_context_create();
204 if (status != ISC_R_SUCCESS)
205 log_fatal("Can't initialize context: %s",
206 isc_result_totext(status));
208 /* Set up the OMAPI. */
209 status = omapi_init();
210 if (status != ISC_R_SUCCESS)
211 log_fatal("Can't initialize OMAPI: %s",
212 isc_result_totext(status));
214 /* Set up the OMAPI wrappers for the interface object. */
217 for (i = 1; i < argc; i++) {
218 if (!strcmp(argv[i], "-4")) {
220 if (local_family_set && (local_family == AF_INET6)) {
223 local_family_set = 1;
224 local_family = AF_INET;
225 } else if (!strcmp(argv[i], "-6")) {
226 if (local_family_set && (local_family == AF_INET)) {
229 local_family_set = 1;
230 local_family = AF_INET6;
232 } else if (!strcmp(argv[i], "-d")) {
234 } else if (!strcmp(argv[i], "-q")) {
236 quiet_interface_discovery = 1;
237 } else if (!strcmp(argv[i], "-p")) {
240 local_port = validate_port(argv[i]);
241 log_debug("binding to user-specified port %d",
243 } else if (!strcmp(argv[i], "-c")) {
247 hcount = atoi(argv[i]);
249 max_hop_count= hcount;
252 } else if (!strcmp(argv[i], "-i")) {
254 if (local_family_set && (local_family == AF_INET6)) {
257 local_family_set = 1;
258 local_family = AF_INET;
260 status = interface_allocate(&tmp, MDL);
261 if (status != ISC_R_SUCCESS)
262 log_fatal("%s: interface_allocate: %s",
264 isc_result_totext(status));
268 strcpy(tmp->name, argv[i]);
269 interface_snorf(tmp, INTERFACE_REQUESTED);
270 interface_dereference(&tmp, MDL);
271 } else if (!strcmp(argv[i], "-a")) {
273 if (local_family_set && (local_family == AF_INET6)) {
276 local_family_set = 1;
277 local_family = AF_INET;
279 add_agent_options = 1;
280 } else if (!strcmp(argv[i], "-A")) {
282 if (local_family_set && (local_family == AF_INET6)) {
285 local_family_set = 1;
286 local_family = AF_INET;
291 dhcp_max_agent_option_packet_length = atoi(argv[i]);
293 if (dhcp_max_agent_option_packet_length > DHCP_MTU_MAX)
294 log_fatal("%s: packet length exceeds "
295 "longest possible MTU\n",
297 } else if (!strcmp(argv[i], "-m")) {
299 if (local_family_set && (local_family == AF_INET6)) {
302 local_family_set = 1;
303 local_family = AF_INET;
307 if (!strcasecmp(argv[i], "append")) {
308 agent_relay_mode = forward_and_append;
309 } else if (!strcasecmp(argv[i], "replace")) {
310 agent_relay_mode = forward_and_replace;
311 } else if (!strcasecmp(argv[i], "forward")) {
312 agent_relay_mode = forward_untouched;
313 } else if (!strcasecmp(argv[i], "discard")) {
314 agent_relay_mode = discard;
317 } else if (!strcmp(argv[i], "-D")) {
319 if (local_family_set && (local_family == AF_INET6)) {
322 local_family_set = 1;
323 local_family = AF_INET;
325 drop_agent_mismatches = 1;
327 } else if (!strcmp(argv[i], "-I")) {
328 if (local_family_set && (local_family == AF_INET)) {
331 local_family_set = 1;
332 local_family = AF_INET6;
333 use_if_id = ISC_TRUE;
334 } else if (!strcmp(argv[i], "-l")) {
335 if (local_family_set && (local_family == AF_INET)) {
338 local_family_set = 1;
339 local_family = AF_INET6;
340 if (downstreams != NULL)
341 use_if_id = ISC_TRUE;
344 sl = parse_downstream(argv[i]);
345 sl->next = downstreams;
347 } else if (!strcmp(argv[i], "-u")) {
348 if (local_family_set && (local_family == AF_INET)) {
351 local_family_set = 1;
352 local_family = AF_INET6;
355 sl = parse_upstream(argv[i]);
356 sl->next = upstreams;
359 } else if (!strcmp(argv[i], "-pf")) {
362 path_dhcrelay_pid = argv[i];
363 no_dhcrelay_pid = ISC_TRUE;
364 } else if (!strcmp(argv[i], "--no-pid")) {
365 no_pid_file = ISC_TRUE;
366 } else if (!strcmp(argv[i], "--version")) {
367 log_info("isc-dhcrelay-%s", PACKAGE_VERSION);
369 } else if (!strcmp(argv[i], "--help") ||
370 !strcmp(argv[i], "-h")) {
371 log_info(DHCRELAY_USAGE);
373 } else if (argv[i][0] == '-') {
377 struct in_addr ia, *iap = NULL;
380 if (local_family_set && (local_family == AF_INET6)) {
383 local_family_set = 1;
384 local_family = AF_INET;
386 if (inet_aton(argv[i], &ia)) {
389 he = gethostbyname(argv[i]);
391 log_error("%s: host unknown", argv[i]);
393 iap = ((struct in_addr *)
399 sp = ((struct server_list *)
400 dmalloc(sizeof *sp, MDL));
402 log_fatal("no memory for server.\n");
405 memcpy(&sp->to.sin_addr, iap, sizeof *iap);
411 * If the user didn't specify a pid file directly
412 * find one from environment variables or defaults
414 if (no_dhcrelay_pid == ISC_FALSE) {
415 if (local_family == AF_INET) {
416 path_dhcrelay_pid = getenv("PATH_DHCRELAY_PID");
417 if (path_dhcrelay_pid == NULL)
418 path_dhcrelay_pid = _PATH_DHCRELAY_PID;
422 path_dhcrelay_pid = getenv("PATH_DHCRELAY6_PID");
423 if (path_dhcrelay_pid == NULL)
424 path_dhcrelay_pid = _PATH_DHCRELAY6_PID;
430 log_info("%s %s", message, PACKAGE_VERSION);
439 /* Set default port */
440 if (local_family == AF_INET) {
441 service_local = "bootps";
442 service_remote = "bootpc";
443 port_local = htons(67);
444 port_remote = htons(68);
448 service_local = "dhcpv6-server";
449 service_remote = "dhcpv6-client";
450 port_local = htons(547);
451 port_remote = htons(546);
456 ent = getservbyname(service_local, "udp");
458 local_port = ent->s_port;
460 local_port = port_local;
462 ent = getservbyname(service_remote, "udp");
464 remote_port = ent->s_port;
466 remote_port = port_remote;
471 if (local_family == AF_INET) {
472 /* We need at least one server */
473 if (servers == NULL) {
474 log_fatal("No servers specified.");
478 /* Set up the server sockaddrs. */
479 for (sp = servers; sp; sp = sp->next) {
480 sp->to.sin_port = local_port;
481 sp->to.sin_family = AF_INET;
483 sp->to.sin_len = sizeof sp->to;
491 /* We need at least one upstream and one downstream interface */
492 if (upstreams == NULL || downstreams == NULL) {
493 log_info("Must specify at least one lower "
494 "and one upper interface.\n");
498 /* Set up the initial dhcp option universe. */
499 initialize_common_option_spaces();
501 /* Check requested options. */
502 code = D6O_RELAY_MSG;
503 if (!option_code_hash_lookup(&requested_opts[0],
504 dhcpv6_universe.code_hash,
506 log_fatal("Unable to find the RELAY_MSG "
507 "option definition.");
508 code = D6O_INTERFACE_ID;
509 if (!option_code_hash_lookup(&requested_opts[1],
510 dhcpv6_universe.code_hash,
512 log_fatal("Unable to find the INTERFACE_ID "
513 "option definition.");
517 /* Get the current time... */
518 gettimeofday(&cur_tv, NULL);
520 /* Discover all the network interfaces. */
521 discover_interfaces(DISCOVER_RELAY);
524 if (local_family == AF_INET6)
528 /* Become a daemon... */
536 if ((pid = fork()) < 0)
537 log_fatal("Can't fork daemon: %m");
541 if (no_pid_file == ISC_FALSE) {
542 pfdesc = open(path_dhcrelay_pid,
543 O_CREAT | O_TRUNC | O_WRONLY, 0644);
546 log_error("Can't create %s: %m",
549 pf = fdopen(pfdesc, "w");
551 log_error("Can't fdopen %s: %m",
554 fprintf(pf, "%ld\n",(long)getpid());
565 IGNORE_RET (chdir("/"));
568 /* Set up the packet handler... */
569 if (local_family == AF_INET)
570 bootp_packet_handler = do_relay4;
573 dhcpv6_packet_handler = do_packet6;
576 /* Start dispatching packets and timeouts... */
584 do_relay4(struct interface_info *ip, struct dhcp_packet *packet,
585 unsigned int length, unsigned int from_port, struct iaddr from,
586 struct hardware *hfrom) {
587 struct server_list *sp;
588 struct sockaddr_in to;
589 struct interface_info *out;
590 struct hardware hto, *htop;
592 if (packet->hlen > sizeof packet->chaddr) {
593 log_info("Discarding packet with invalid hlen.");
597 if (ip->address_count < 1 || ip->addresses == NULL) {
598 log_info("Discarding packet received on %s interface that "
599 "has no IPv4 address assigned.", ip->name);
603 /* Find the interface that corresponds to the giaddr
605 if (packet->giaddr.s_addr) {
606 for (out = interfaces; out; out = out->next) {
609 for (i = 0 ; i < out->address_count ; i++ ) {
610 if (out->addresses[i].s_addr ==
611 packet->giaddr.s_addr)
623 /* If it's a bootreply, forward it to the client. */
624 if (packet->op == BOOTREPLY) {
625 if (!(packet->flags & htons(BOOTP_BROADCAST)) &&
626 can_unicast_without_arp(out)) {
627 to.sin_addr = packet->yiaddr;
628 to.sin_port = remote_port;
630 /* and hardware address is not broadcast */
633 to.sin_addr.s_addr = htonl(INADDR_BROADCAST);
634 to.sin_port = remote_port;
636 /* hardware address is broadcast */
639 to.sin_family = AF_INET;
641 to.sin_len = sizeof to;
644 memcpy(&hto.hbuf[1], packet->chaddr, packet->hlen);
645 hto.hbuf[0] = packet->htype;
646 hto.hlen = packet->hlen + 1;
648 /* Wipe out the agent relay options and, if possible, figure
649 out which interface to use based on the contents of the
650 option that we put on the request to which the server is
653 strip_relay_agent_options(ip, &out, packet, length)))
657 log_error("Packet to bogus giaddr %s.\n",
658 inet_ntoa(packet->giaddr));
659 ++bogus_giaddr_drops;
663 if (send_packet(out, NULL, packet, length, out->addresses[0],
665 ++server_packet_errors;
667 log_debug("Forwarded BOOTREPLY for %s to %s",
668 print_hw_addr(packet->htype, packet->hlen,
670 inet_ntoa(to.sin_addr));
672 ++server_packets_relayed;
677 /* If giaddr matches one of our addresses, ignore the packet -
682 /* Add relay agent options if indicated. If something goes wrong,
684 if (!(length = add_relay_agent_options(ip, packet, length,
688 /* If giaddr is not already set, Set it so the server can
689 figure out what net it's from and so that we can later
690 forward the response to the correct net. If it's already
691 set, the response will be sent directly to the relay agent
692 that set giaddr, so we won't see it. */
693 if (!packet->giaddr.s_addr)
694 packet->giaddr = ip->addresses[0];
695 if (packet->hops < max_hop_count)
696 packet->hops = packet->hops + 1;
700 /* Otherwise, it's a BOOTREQUEST, so forward it to all the
702 for (sp = servers; sp; sp = sp->next) {
703 if (send_packet((fallback_interface
704 ? fallback_interface : interfaces),
705 NULL, packet, length, ip->addresses[0],
706 &sp->to, NULL) < 0) {
707 ++client_packet_errors;
709 log_debug("Forwarded BOOTREQUEST for %s to %s",
710 print_hw_addr(packet->htype, packet->hlen,
712 inet_ntoa(sp->to.sin_addr));
713 ++client_packets_relayed;
719 /* Strip any Relay Agent Information options from the DHCP packet
720 option buffer. If there is a circuit ID suboption, look up the
721 outgoing interface based upon it. */
724 strip_relay_agent_options(struct interface_info *in,
725 struct interface_info **out,
726 struct dhcp_packet *packet,
729 u_int8_t *op, *nextop, *sp, *max;
730 int good_agent_option = 0;
733 /* If we're not adding agent options to packets, we're not taking
735 if (!add_agent_options)
738 /* If there's no cookie, it's a bootp packet, so we should just
739 forward it unchanged. */
740 if (memcmp(packet->options, DHCP_OPTIONS_COOKIE, 4))
743 max = ((u_int8_t *)packet) + length;
744 sp = op = &packet->options[4];
748 /* Skip padding... */
756 /* If we see a message type, it's a DHCP packet. */
757 case DHO_DHCP_MESSAGE_TYPE:
762 /* Quit immediately if we hit an End option. */
768 case DHO_DHCP_AGENT_OPTIONS:
769 /* We shouldn't see a relay agent option in a
770 packet before we've seen the DHCP packet type,
771 but if we do, we have to leave it alone. */
775 /* Do not process an agent option if it exceeds the
776 * buffer. Fail this packet.
778 nextop = op + op[1] + 2;
782 status = find_interface_by_agent_option(packet,
785 if (status == -1 && drop_agent_mismatches)
788 good_agent_option = 1;
793 /* Skip over other options. */
795 /* Fail if processing this option will exceed the
796 * buffer(op[1] is malformed).
798 nextop = op + op[1] + 2;
803 memmove(sp, op, op[1] + 2);
814 /* If it's not a DHCP packet, we're not supposed to touch it. */
818 /* If none of the agent options we found matched, or if we didn't
819 find any agent options, count this packet as not having any
820 matching agent options, and if we're relying on agent options
821 to determine the outgoing interface, drop the packet. */
823 if (!good_agent_option) {
824 ++missing_agent_option;
825 if (drop_agent_mismatches)
829 /* Adjust the length... */
831 length = sp -((u_int8_t *)packet);
833 /* Make sure the packet isn't short(this is unlikely,
835 if (length < BOOTP_MIN_LEN) {
836 memset(sp, DHO_PAD, BOOTP_MIN_LEN - length);
837 length = BOOTP_MIN_LEN;
844 /* Find an interface that matches the circuit ID specified in the
845 Relay Agent Information option. If one is found, store it through
846 the pointer given; otherwise, leave the existing pointer alone.
848 We actually deviate somewhat from the current specification here:
849 if the option buffer is corrupt, we suggest that the caller not
850 respond to this packet. If the circuit ID doesn't match any known
851 interface, we suggest that the caller to drop the packet. Only if
852 we find a circuit ID that matches an existing interface do we tell
853 the caller to go ahead and process the packet. */
856 find_interface_by_agent_option(struct dhcp_packet *packet,
857 struct interface_info **out,
858 u_int8_t *buf, int len) {
860 u_int8_t *circuit_id = 0;
861 unsigned circuit_id_len = 0;
862 struct interface_info *ip;
865 /* If the next agent option overflows the end of the
866 packet, the agent option buffer is corrupt. */
868 i + buf[i + 1] + 2 > len) {
869 ++corrupt_agent_options;
873 /* Remember where the circuit ID is... */
875 circuit_id = &buf[i + 2];
876 circuit_id_len = buf[i + 1];
877 i += circuit_id_len + 2;
886 /* If there's no circuit ID, it's not really ours, tell the caller
889 ++missing_circuit_id;
893 /* Scan the interface list looking for an interface whose
894 name matches the one specified in circuit_id. */
896 for (ip = interfaces; ip; ip = ip->next) {
897 if (ip->circuit_id &&
898 ip->circuit_id_len == circuit_id_len &&
899 !memcmp(ip->circuit_id, circuit_id, circuit_id_len))
903 /* If we got a match, use it. */
909 /* If we didn't get a match, the circuit ID was bogus. */
915 * Examine a packet to see if it's a candidate to have a Relay
916 * Agent Information option tacked onto its tail. If it is, tack
920 add_relay_agent_options(struct interface_info *ip, struct dhcp_packet *packet,
921 unsigned length, struct in_addr giaddr) {
922 int is_dhcp = 0, mms;
924 u_int8_t *op, *nextop, *sp, *max, *end_pad = NULL;
926 /* If we're not adding agent options to packets, we can skip
928 if (!add_agent_options)
931 /* If there's no cookie, it's a bootp packet, so we should just
932 forward it unchanged. */
933 if (memcmp(packet->options, DHCP_OPTIONS_COOKIE, 4))
936 max = ((u_int8_t *)packet) + dhcp_max_agent_option_packet_length;
938 /* Commence processing after the cookie. */
939 sp = op = &packet->options[4];
943 /* Skip padding... */
945 /* Remember the first pad byte so we can commandeer
948 * XXX: Is this really a good idea? Sure, we can
949 * seemingly reduce the packet while we're looking,
950 * but if the packet was signed by the client then
951 * this padding is part of the checksum(RFC3118),
952 * and its nonpresence would break authentication.
964 /* If we see a message type, it's a DHCP packet. */
965 case DHO_DHCP_MESSAGE_TYPE:
970 * If there's a maximum message size option, we
971 * should pay attention to it
973 case DHO_DHCP_MAX_MESSAGE_SIZE:
974 mms = ntohs(*(op + 2));
975 if (mms < dhcp_max_agent_option_packet_length &&
977 max = ((u_int8_t *)packet) + mms;
980 /* Quit immediately if we hit an End option. */
984 case DHO_DHCP_AGENT_OPTIONS:
985 /* We shouldn't see a relay agent option in a
986 packet before we've seen the DHCP packet type,
987 but if we do, we have to leave it alone. */
993 /* There's already a Relay Agent Information option
994 in this packet. How embarrassing. Decide what
995 to do based on the mode the user specified. */
997 switch(agent_relay_mode) {
998 case forward_and_append:
1000 case forward_untouched:
1004 case forward_and_replace:
1009 /* Skip over the agent option and start copying
1010 if we aren't copying already. */
1015 /* Skip over other options. */
1017 /* Fail if processing this option will exceed the
1018 * buffer(op[1] is malformed).
1020 nextop = op + op[1] + 2;
1027 memmove(sp, op, op[1] + 2);
1038 /* If it's not a DHCP packet, we're not supposed to touch it. */
1042 /* If the packet was padded out, we can store the agent option
1043 at the beginning of the padding. */
1045 if (end_pad != NULL)
1048 /* Remember where the end of the packet was after parsing
1052 /* Sanity check. Had better not ever happen. */
1053 if ((ip->circuit_id_len > 255) ||(ip->circuit_id_len < 1))
1054 log_fatal("Circuit ID length %d out of range [1-255] on "
1055 "%s\n", ip->circuit_id_len, ip->name);
1056 optlen = ip->circuit_id_len + 2; /* RAI_CIRCUIT_ID + len */
1058 if (ip->remote_id) {
1059 if (ip->remote_id_len > 255 || ip->remote_id_len < 1)
1060 log_fatal("Remote ID length %d out of range [1-255] "
1061 "on %s\n", ip->circuit_id_len, ip->name);
1062 optlen += ip->remote_id_len + 2; /* RAI_REMOTE_ID + len */
1065 /* We do not support relay option fragmenting(multiple options to
1066 * support an option data exceeding 255 bytes).
1068 if ((optlen < 3) ||(optlen > 255))
1069 log_fatal("Total agent option length(%u) out of range "
1070 "[3 - 255] on %s\n", optlen, ip->name);
1073 * Is there room for the option, its code+len, and DHO_END?
1074 * If not, forward without adding the option.
1076 if (max - sp >= optlen + 3) {
1077 log_debug("Adding %d-byte relay agent option", optlen + 3);
1079 /* Okay, cons up *our* Relay Agent Information option. */
1080 *sp++ = DHO_DHCP_AGENT_OPTIONS;
1083 /* Copy in the circuit id... */
1084 *sp++ = RAI_CIRCUIT_ID;
1085 *sp++ = ip->circuit_id_len;
1086 memcpy(sp, ip->circuit_id, ip->circuit_id_len);
1087 sp += ip->circuit_id_len;
1089 /* Copy in remote ID... */
1090 if (ip->remote_id) {
1091 *sp++ = RAI_REMOTE_ID;
1092 *sp++ = ip->remote_id_len;
1093 memcpy(sp, ip->remote_id, ip->remote_id_len);
1094 sp += ip->remote_id_len;
1097 ++agent_option_errors;
1098 log_error("No room in packet (used %d of %d) "
1099 "for %d-byte relay agent option: omitted",
1100 (int) (sp - ((u_int8_t *) packet)),
1101 (int) (max - ((u_int8_t *) packet)),
1106 * Deposit an END option unless the packet is full (shouldn't
1112 /* Recalculate total packet length. */
1113 length = sp -((u_int8_t *)packet);
1115 /* Make sure the packet isn't short(this is unlikely, but WTH) */
1116 if (length < BOOTP_MIN_LEN) {
1117 memset(sp, DHO_PAD, BOOTP_MIN_LEN - length);
1118 return (BOOTP_MIN_LEN);
1126 * Parse a downstream argument: [address%]interface[#index].
1128 static struct stream_list *
1129 parse_downstream(char *arg) {
1130 struct stream_list *dp, *up;
1131 struct interface_info *ifp = NULL;
1132 char *ifname, *addr, *iid;
1133 isc_result_t status;
1135 if (!supports_multiple_interfaces(ifp) &&
1136 (downstreams != NULL))
1137 log_fatal("No support for multiple interfaces.");
1139 /* Decode the argument. */
1140 ifname = strchr(arg, '%');
1141 if (ifname == NULL) {
1148 iid = strchr(ifname, '#');
1152 if (strlen(ifname) >= sizeof(ifp->name)) {
1153 log_error("Interface name '%s' too long", ifname);
1157 /* Don't declare twice. */
1158 for (dp = downstreams; dp; dp = dp->next) {
1159 if (strcmp(ifname, dp->ifp->name) == 0)
1160 log_fatal("Down interface '%s' declared twice.",
1164 /* Share with up side? */
1165 for (up = upstreams; up; up = up->next) {
1166 if (strcmp(ifname, up->ifp->name) == 0) {
1167 log_info("Interface '%s' is both down and up.",
1174 /* New interface. */
1176 status = interface_allocate(&ifp, MDL);
1177 if (status != ISC_R_SUCCESS)
1178 log_fatal("%s: interface_allocate: %s",
1179 arg, isc_result_totext(status));
1180 strcpy(ifp->name, ifname);
1182 interface_reference(&ifp->next, interfaces, MDL);
1183 interface_dereference(&interfaces, MDL);
1185 interface_reference(&interfaces, ifp, MDL);
1186 ifp->flags |= INTERFACE_REQUESTED | INTERFACE_DOWNSTREAM;
1189 /* New downstream. */
1190 dp = (struct stream_list *) dmalloc(sizeof(*dp), MDL);
1192 log_fatal("No memory for downstream.");
1199 /* !addr case handled by setup. */
1200 if (addr && (inet_pton(AF_INET6, addr, &dp->link.sin6_addr) <= 0))
1201 log_fatal("Bad link address '%s'", addr);
1207 * Parse an upstream argument: [address]%interface.
1209 static struct stream_list *
1210 parse_upstream(char *arg) {
1211 struct stream_list *up, *dp;
1212 struct interface_info *ifp = NULL;
1213 char *ifname, *addr;
1214 isc_result_t status;
1216 /* Decode the argument. */
1217 ifname = strchr(arg, '%');
1218 if (ifname == NULL) {
1220 addr = All_DHCP_Servers;
1225 if (strlen(ifname) >= sizeof(ifp->name)) {
1226 log_fatal("Interface name '%s' too long", ifname);
1229 /* Shared up interface? */
1230 for (up = upstreams; up; up = up->next) {
1231 if (strcmp(ifname, up->ifp->name) == 0) {
1236 for (dp = downstreams; dp; dp = dp->next) {
1237 if (strcmp(ifname, dp->ifp->name) == 0) {
1243 /* New interface. */
1245 status = interface_allocate(&ifp, MDL);
1246 if (status != ISC_R_SUCCESS)
1247 log_fatal("%s: interface_allocate: %s",
1248 arg, isc_result_totext(status));
1249 strcpy(ifp->name, ifname);
1251 interface_reference(&ifp->next, interfaces, MDL);
1252 interface_dereference(&interfaces, MDL);
1254 interface_reference(&interfaces, ifp, MDL);
1255 ifp->flags |= INTERFACE_REQUESTED | INTERFACE_UPSTREAM;
1259 up = (struct stream_list *) dmalloc(sizeof(*up), MDL);
1261 log_fatal("No memory for upstream.");
1265 if (inet_pton(AF_INET6, addr, &up->link.sin6_addr) <= 0)
1266 log_fatal("Bad address %s", addr);
1272 * Setup downstream interfaces.
1275 setup_streams(void) {
1276 struct stream_list *dp, *up;
1278 isc_boolean_t link_is_set;
1280 for (dp = downstreams; dp; dp = dp->next) {
1281 /* Check interface */
1282 if (dp->ifp->v6address_count == 0)
1283 log_fatal("Interface '%s' has no IPv6 addresses.",
1286 /* Check/set link. */
1287 if (IN6_IS_ADDR_UNSPECIFIED(&dp->link.sin6_addr))
1288 link_is_set = ISC_FALSE;
1290 link_is_set = ISC_TRUE;
1291 for (i = 0; i < dp->ifp->v6address_count; i++) {
1292 if (IN6_IS_ADDR_LINKLOCAL(&dp->ifp->v6addresses[i]))
1296 if (!memcmp(&dp->ifp->v6addresses[i],
1297 &dp->link.sin6_addr,
1298 sizeof(dp->link.sin6_addr)))
1301 if (i == dp->ifp->v6address_count)
1302 log_fatal("Can't find link address for interface '%s'.",
1305 memcpy(&dp->link.sin6_addr,
1306 &dp->ifp->v6addresses[i],
1307 sizeof(dp->link.sin6_addr));
1309 /* Set interface-id. */
1311 dp->id = dp->ifp->index;
1314 for (up = upstreams; up; up = up->next) {
1315 up->link.sin6_port = local_port;
1316 up->link.sin6_family = AF_INET6;
1318 up->link.sin6_len = sizeof(up->link);
1321 if (up->ifp->v6address_count == 0)
1322 log_fatal("Interface '%s' has no IPv6 addresses.",
1328 * Add DHCPv6 agent options here.
1330 static const int required_forw_opts[] = {
1337 * Process a packet upwards, i.e., from client to server.
1340 process_up6(struct packet *packet, struct stream_list *dp) {
1341 char forw_data[65535];
1343 struct dhcpv6_relay_packet *relay;
1344 struct option_state *opts;
1345 struct stream_list *up;
1347 /* Check if the message should be relayed to the server. */
1348 switch (packet->dhcpv6_msg_type) {
1349 case DHCPV6_SOLICIT:
1350 case DHCPV6_REQUEST:
1351 case DHCPV6_CONFIRM:
1354 case DHCPV6_RELEASE:
1355 case DHCPV6_DECLINE:
1356 case DHCPV6_INFORMATION_REQUEST:
1357 case DHCPV6_RELAY_FORW:
1358 case DHCPV6_LEASEQUERY:
1359 log_info("Relaying %s from %s port %d going up.",
1360 dhcpv6_type_names[packet->dhcpv6_msg_type],
1361 piaddr(packet->client_addr),
1362 ntohs(packet->client_port));
1365 case DHCPV6_ADVERTISE:
1367 case DHCPV6_RECONFIGURE:
1368 case DHCPV6_RELAY_REPL:
1369 case DHCPV6_LEASEQUERY_REPLY:
1370 log_info("Discarding %s from %s port %d going up.",
1371 dhcpv6_type_names[packet->dhcpv6_msg_type],
1372 piaddr(packet->client_addr),
1373 ntohs(packet->client_port));
1377 log_info("Unknown %d type from %s port %d going up.",
1378 packet->dhcpv6_msg_type,
1379 piaddr(packet->client_addr),
1380 ntohs(packet->client_port));
1384 /* Build the relay-forward header. */
1385 relay = (struct dhcpv6_relay_packet *) forw_data;
1386 cursor = offsetof(struct dhcpv6_relay_packet, options);
1387 relay->msg_type = DHCPV6_RELAY_FORW;
1388 if (packet->dhcpv6_msg_type == DHCPV6_RELAY_FORW) {
1389 if (packet->dhcpv6_hop_count >= max_hop_count) {
1390 log_info("Hop count exceeded,");
1393 relay->hop_count = packet->dhcpv6_hop_count + 1;
1395 memcpy(&relay->link_address, &dp->link.sin6_addr, 16);
1397 /* On smart relay add: && !global. */
1398 if (!use_if_id && downstreams->next) {
1399 log_info("Shan't get back the interface.");
1402 memset(&relay->link_address, 0, 16);
1405 relay->hop_count = 0;
1408 memcpy(&relay->link_address, &dp->link.sin6_addr, 16);
1410 memcpy(&relay->peer_address, packet->client_addr.iabuf, 16);
1412 /* Get an option state. */
1414 if (!option_state_allocate(&opts, MDL)) {
1415 log_fatal("No memory for upwards options.");
1418 /* Add an interface-id (if used). */
1424 } else if (!downstreams->next) {
1425 if_id = downstreams->id;
1427 log_info("Don't know the interface.");
1428 option_state_dereference(&opts, MDL);
1432 if (!save_option_buffer(&dhcpv6_universe, opts,
1433 NULL, (unsigned char *) &if_id,
1435 D6O_INTERFACE_ID, 0)) {
1436 log_error("Can't save interface-id.");
1437 option_state_dereference(&opts, MDL);
1442 /* Add the relay-msg carrying the packet. */
1443 if (!save_option_buffer(&dhcpv6_universe, opts,
1444 NULL, (unsigned char *) packet->raw,
1445 packet->packet_length,
1446 D6O_RELAY_MSG, 0)) {
1447 log_error("Can't save relay-msg.");
1448 option_state_dereference(&opts, MDL);
1452 /* Finish the relay-forward message. */
1453 cursor += store_options6(forw_data + cursor,
1454 sizeof(forw_data) - cursor,
1456 required_forw_opts, NULL);
1457 option_state_dereference(&opts, MDL);
1459 /* Send it to all upstreams. */
1460 for (up = upstreams; up; up = up->next) {
1461 send_packet6(up->ifp, (unsigned char *) forw_data,
1462 (size_t) cursor, &up->link);
1467 * Process a packet downwards, i.e., from server to client.
1470 process_down6(struct packet *packet) {
1471 struct stream_list *dp;
1472 struct option_cache *oc;
1473 struct data_string relay_msg;
1474 const struct dhcpv6_packet *msg;
1475 struct data_string if_id;
1476 struct sockaddr_in6 to;
1479 /* The packet must be a relay-reply message. */
1480 if (packet->dhcpv6_msg_type != DHCPV6_RELAY_REPL) {
1481 if (packet->dhcpv6_msg_type < dhcpv6_type_name_max)
1482 log_info("Discarding %s from %s port %d going down.",
1483 dhcpv6_type_names[packet->dhcpv6_msg_type],
1484 piaddr(packet->client_addr),
1485 ntohs(packet->client_port));
1487 log_info("Unknown %d type from %s port %d going down.",
1488 packet->dhcpv6_msg_type,
1489 piaddr(packet->client_addr),
1490 ntohs(packet->client_port));
1495 memset(&relay_msg, 0, sizeof(relay_msg));
1496 memset(&if_id, 0, sizeof(if_id));
1497 memset(&to, 0, sizeof(to));
1498 to.sin6_family = AF_INET6;
1500 to.sin6_len = sizeof(to);
1502 to.sin6_port = remote_port;
1505 /* Get the relay-msg option (carrying the message to relay). */
1506 oc = lookup_option(&dhcpv6_universe, packet->options, D6O_RELAY_MSG);
1508 log_info("No relay-msg.");
1511 if (!evaluate_option_cache(&relay_msg, packet, NULL, NULL,
1512 packet->options, NULL,
1513 &global_scope, oc, MDL) ||
1514 (relay_msg.len < offsetof(struct dhcpv6_packet, options))) {
1515 log_error("Can't evaluate relay-msg.");
1518 msg = (const struct dhcpv6_packet *) relay_msg.data;
1520 /* Get the interface-id (if exists) and the downstream. */
1521 oc = lookup_option(&dhcpv6_universe, packet->options,
1526 if (!evaluate_option_cache(&if_id, packet, NULL, NULL,
1527 packet->options, NULL,
1528 &global_scope, oc, MDL) ||
1529 (if_id.len != sizeof(int))) {
1530 log_info("Can't evaluate interface-id.");
1533 memcpy(&if_index, if_id.data, sizeof(int));
1534 for (dp = downstreams; dp; dp = dp->next) {
1535 if (dp->id == if_index)
1540 /* Require an interface-id. */
1541 log_info("No interface-id.");
1544 for (dp = downstreams; dp; dp = dp->next) {
1545 /* Get the first matching one. */
1546 if (!memcmp(&dp->link.sin6_addr,
1547 &packet->dhcpv6_link_address,
1548 sizeof(struct in6_addr)))
1552 /* Why bother when there is no choice. */
1553 if (!dp && !downstreams->next)
1556 log_info("Can't find the down interface.");
1559 memcpy(peer.iabuf, &packet->dhcpv6_peer_address, peer.len);
1560 to.sin6_addr = packet->dhcpv6_peer_address;
1562 /* Check if we should relay the carried message. */
1563 switch (msg->msg_type) {
1564 /* Relay-Reply of for another relay, not a client. */
1565 case DHCPV6_RELAY_REPL:
1566 to.sin6_port = local_port;
1569 case DHCPV6_ADVERTISE:
1571 case DHCPV6_RECONFIGURE:
1572 case DHCPV6_RELAY_FORW:
1573 case DHCPV6_LEASEQUERY_REPLY:
1574 log_info("Relaying %s to %s port %d down.",
1575 dhcpv6_type_names[msg->msg_type],
1577 ntohs(to.sin6_port));
1580 case DHCPV6_SOLICIT:
1581 case DHCPV6_REQUEST:
1582 case DHCPV6_CONFIRM:
1585 case DHCPV6_RELEASE:
1586 case DHCPV6_DECLINE:
1587 case DHCPV6_INFORMATION_REQUEST:
1588 case DHCPV6_LEASEQUERY:
1589 log_info("Discarding %s to %s port %d down.",
1590 dhcpv6_type_names[msg->msg_type],
1592 ntohs(to.sin6_port));
1596 log_info("Unknown %d type to %s port %d down.",
1599 ntohs(to.sin6_port));
1603 /* Send the message to the downstream. */
1604 send_packet6(dp->ifp, (unsigned char *) relay_msg.data,
1605 (size_t) relay_msg.len, &to);
1608 if (relay_msg.data != NULL)
1609 data_string_forget(&relay_msg, MDL);
1610 if (if_id.data != NULL)
1611 data_string_forget(&if_id, MDL);
1615 * Called by the dispatch packet handler with a decoded packet.
1618 dhcpv6(struct packet *packet) {
1619 struct stream_list *dp;
1621 /* Try all relay-replies downwards. */
1622 if (packet->dhcpv6_msg_type == DHCPV6_RELAY_REPL) {
1623 process_down6(packet);
1626 /* Others are candidates to go up if they come from down. */
1627 for (dp = downstreams; dp; dp = dp->next) {
1628 if (packet->interface != dp->ifp)
1630 process_up6(packet, dp);
1633 /* Relay-forward could work from an unknown interface. */
1634 if (packet->dhcpv6_msg_type == DHCPV6_RELAY_FORW) {
1635 process_up6(packet, NULL);
1639 log_info("Can't process packet from interface '%s'.",
1640 packet->interface->name);
1644 /* Stub routines needed for linking with DHCP libraries. */
1646 bootp(struct packet *packet) {
1651 dhcp(struct packet *packet) {
1656 classify(struct packet *p, struct class *c) {
1661 check_collection(struct packet *p, struct lease *l, struct collection *c) {
1666 find_class(struct class **class, const char *c1, const char *c2, int i) {
1667 return ISC_R_NOTFOUND;
1671 parse_allow_deny(struct option_cache **oc, struct parse *p, int i) {
1676 dhcp_set_control_state(control_object_state_t oldstate,
1677 control_object_state_t newstate) {
1678 return ISC_R_SUCCESS;