1 /* $NetBSD: dhcrelay.c,v 1.5 2014/07/12 12:09:37 spz Exp $ */
4 DHCP/BOOTP Relay Agent. */
7 * Copyright(c) 2004-2014 by Internet Systems Consortium, Inc.("ISC")
8 * Copyright(c) 1997-2003 by Internet Software Consortium
10 * Permission to use, copy, modify, and distribute this software for any
11 * purpose with or without fee is hereby granted, provided that the above
12 * copyright notice and this permission notice appear in all copies.
14 * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
15 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
16 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
17 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
18 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
19 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
20 * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
22 * Internet Systems Consortium, Inc.
24 * Redwood City, CA 94063
26 * https://www.isc.org/
30 #include <sys/cdefs.h>
31 __RCSID("$NetBSD: dhcrelay.c,v 1.5 2014/07/12 12:09:37 spz Exp $");
38 TIME default_lease_time
= 43200; /* 12 hours... */
39 TIME max_lease_time
= 86400; /* 24 hours... */
40 struct tree_cache
*global_options
[256];
42 struct option
*requested_opts
[2];
44 /* Needed to prevent linking against conflex.c. */
50 const char *path_dhcrelay_pid
= _PATH_DHCRELAY_PID
;
51 isc_boolean_t no_dhcrelay_pid
= ISC_FALSE
;
52 /* False (default) => we write and use a pid file */
53 isc_boolean_t no_pid_file
= ISC_FALSE
;
55 int bogus_agent_drops
= 0; /* Packets dropped because agent option
56 field was specified and we're not relaying
57 packets that already have an agent option
59 int bogus_giaddr_drops
= 0; /* Packets sent to us to relay back to a
60 client, but with a bogus giaddr. */
61 int client_packets_relayed
= 0; /* Packets relayed from client to server. */
62 int server_packet_errors
= 0; /* Errors sending packets to servers. */
63 int server_packets_relayed
= 0; /* Packets relayed from server to client. */
64 int client_packet_errors
= 0; /* Errors sending packets to clients. */
66 int add_agent_options
= 0; /* If nonzero, add relay agent options. */
68 int agent_option_errors
= 0; /* Number of packets forwarded without
69 agent options because there was no room. */
70 int drop_agent_mismatches
= 0; /* If nonzero, drop server replies that
71 don't have matching circuit-id's. */
72 int corrupt_agent_options
= 0; /* Number of packets dropped because
73 relay agent information option was bad. */
74 int missing_agent_option
= 0; /* Number of packets dropped because no
75 RAI option matching our ID was found. */
76 int bad_circuit_id
= 0; /* Circuit ID option in matching RAI option
77 did not match any known circuit ID. */
78 int missing_circuit_id
= 0; /* Circuit ID option in matching RAI option
80 int max_hop_count
= 10; /* Maximum hop count */
83 /* Force use of DHCPv6 interface-id option. */
84 isc_boolean_t use_if_id
= ISC_FALSE
;
87 /* Maximum size of a packet with agent options added. */
88 int dhcp_max_agent_option_packet_length
= DHCP_MTU_MIN
;
90 /* What to do about packets we're asked to relay that
91 already have a relay option: */
92 enum { forward_and_append
, /* Forward and append our own relay option. */
93 forward_and_replace
, /* Forward, but replace theirs with ours. */
94 forward_untouched
, /* Forward without changes. */
95 discard
} agent_relay_mode
= forward_and_replace
;
98 u_int16_t remote_port
;
100 /* Relay agent server list. */
102 struct server_list
*next
;
103 struct sockaddr_in to
;
108 struct stream_list
*next
;
109 struct interface_info
*ifp
;
110 struct sockaddr_in6 link
;
112 } *downstreams
, *upstreams
;
114 static struct stream_list
*parse_downstream(char *);
115 static struct stream_list
*parse_upstream(char *);
116 static void setup_streams(void);
119 * A pointer to a subscriber id to add to the message we forward.
120 * This is primarily for testing purposes as we only have one id
121 * for the entire relay and don't determine one per client which
122 * would be more useful.
124 char *dhcrelay_sub_id
= NULL
;
127 static void do_relay4(struct interface_info
*, struct dhcp_packet
*,
128 unsigned int, unsigned int, struct iaddr
,
130 static int add_relay_agent_options(struct interface_info
*,
131 struct dhcp_packet
*, unsigned,
133 static int find_interface_by_agent_option(struct dhcp_packet
*,
134 struct interface_info
**, u_int8_t
*, int);
135 static int strip_relay_agent_options(struct interface_info
*,
136 struct interface_info
**,
137 struct dhcp_packet
*, unsigned);
139 static const char copyright
[] =
140 "Copyright 2004-2014 Internet Systems Consortium.";
141 static const char arr
[] = "All rights reserved.";
142 static const char message
[] =
143 "Internet Systems Consortium DHCP Relay Agent";
144 static const char url
[] =
145 "For info, please visit https://www.isc.org/software/dhcp/";
148 #define DHCRELAY_USAGE \
149 "Usage: dhcrelay [-4] [-d] [-q] [-a] [-D]\n"\
150 " [-A <length>] [-c <hops>] [-p <port>]\n" \
151 " [-pf <pid-file>] [--no-pid]\n"\
152 " [-m append|replace|forward|discard]\n" \
153 " [-i interface0 [ ... -i interfaceN]\n" \
154 " server0 [ ... serverN]\n\n" \
155 " dhcrelay -6 [-d] [-q] [-I] [-c <hops>] [-p <port>]\n" \
156 " [-pf <pid-file>] [--no-pid]\n" \
157 " [-s <subscriber-id>]\n" \
158 " -l lower0 [ ... -l lowerN]\n" \
159 " -u upper0 [ ... -u upperN]\n" \
160 " lower (client link): [address%%]interface[#index]\n" \
161 " upper (server link): [address%%]interface"
163 #define DHCRELAY_USAGE \
164 "Usage: dhcrelay [-d] [-q] [-a] [-D] [-A <length>] [-c <hops>] [-p <port>]\n" \
165 " [-pf <pid-file>] [--no-pid]\n" \
166 " [-m append|replace|forward|discard]\n" \
167 " [-i interface0 [ ... -i interfaceN]\n" \
168 " server0 [ ... serverN]\n\n"
171 static void usage(void) {
172 log_fatal(DHCRELAY_USAGE
);
176 main(int argc
, char **argv
) {
179 struct server_list
*sp
= NULL
;
180 struct interface_info
*tmp
= NULL
;
181 char *service_local
= NULL
, *service_remote
= NULL
;
182 u_int16_t port_local
= 0, port_remote
= 0;
183 int no_daemon
= 0, quiet
= 0;
187 struct stream_list
*sl
= NULL
;
188 int local_family_set
= 0;
191 /* Make sure that file descriptors 0(stdin), 1,(stdout), and
192 2(stderr) are open. To do this, we assume that when we
193 open a file the lowest available file descriptor is used. */
194 fd
= open("/dev/null", O_RDWR
);
196 fd
= open("/dev/null", O_RDWR
);
198 fd
= open("/dev/null", O_RDWR
);
200 log_perror
= 0; /* No sense logging to /dev/null. */
204 openlog("dhcrelay", LOG_NDELAY
, LOG_DAEMON
);
207 setlogmask(LOG_UPTO(LOG_INFO
));
210 /* Set up the isc and dns library managers */
211 status
= dhcp_context_create(DHCP_CONTEXT_PRE_DB
| DHCP_CONTEXT_POST_DB
,
213 if (status
!= ISC_R_SUCCESS
)
214 log_fatal("Can't initialize context: %s",
215 isc_result_totext(status
));
217 /* Set up the OMAPI. */
218 status
= omapi_init();
219 if (status
!= ISC_R_SUCCESS
)
220 log_fatal("Can't initialize OMAPI: %s",
221 isc_result_totext(status
));
223 /* Set up the OMAPI wrappers for the interface object. */
226 for (i
= 1; i
< argc
; i
++) {
227 if (!strcmp(argv
[i
], "-4")) {
229 if (local_family_set
&& (local_family
== AF_INET6
)) {
232 local_family_set
= 1;
233 local_family
= AF_INET
;
234 } else if (!strcmp(argv
[i
], "-6")) {
235 if (local_family_set
&& (local_family
== AF_INET
)) {
238 local_family_set
= 1;
239 local_family
= AF_INET6
;
241 } else if (!strcmp(argv
[i
], "-d")) {
243 } else if (!strcmp(argv
[i
], "-q")) {
245 quiet_interface_discovery
= 1;
246 } else if (!strcmp(argv
[i
], "-p")) {
249 local_port
= validate_port(argv
[i
]);
250 log_debug("binding to user-specified port %d",
252 } else if (!strcmp(argv
[i
], "-c")) {
256 hcount
= atoi(argv
[i
]);
258 max_hop_count
= hcount
;
261 } else if (!strcmp(argv
[i
], "-i")) {
263 if (local_family_set
&& (local_family
== AF_INET6
)) {
266 local_family_set
= 1;
267 local_family
= AF_INET
;
272 if (strlen(argv
[i
]) >= sizeof(tmp
->name
)) {
273 log_fatal("%s: interface name too long "
275 argv
[i
], (long)strlen(argv
[i
]));
277 status
= interface_allocate(&tmp
, MDL
);
278 if (status
!= ISC_R_SUCCESS
) {
279 log_fatal("%s: interface_allocate: %s",
281 isc_result_totext(status
));
283 strcpy(tmp
->name
, argv
[i
]);
284 interface_snorf(tmp
, INTERFACE_REQUESTED
);
285 interface_dereference(&tmp
, MDL
);
286 } else if (!strcmp(argv
[i
], "-a")) {
288 if (local_family_set
&& (local_family
== AF_INET6
)) {
291 local_family_set
= 1;
292 local_family
= AF_INET
;
294 add_agent_options
= 1;
295 } else if (!strcmp(argv
[i
], "-A")) {
297 if (local_family_set
&& (local_family
== AF_INET6
)) {
300 local_family_set
= 1;
301 local_family
= AF_INET
;
306 dhcp_max_agent_option_packet_length
= atoi(argv
[i
]);
308 if (dhcp_max_agent_option_packet_length
> DHCP_MTU_MAX
)
309 log_fatal("%s: packet length exceeds "
310 "longest possible MTU\n",
312 } else if (!strcmp(argv
[i
], "-m")) {
314 if (local_family_set
&& (local_family
== AF_INET6
)) {
317 local_family_set
= 1;
318 local_family
= AF_INET
;
322 if (!strcasecmp(argv
[i
], "append")) {
323 agent_relay_mode
= forward_and_append
;
324 } else if (!strcasecmp(argv
[i
], "replace")) {
325 agent_relay_mode
= forward_and_replace
;
326 } else if (!strcasecmp(argv
[i
], "forward")) {
327 agent_relay_mode
= forward_untouched
;
328 } else if (!strcasecmp(argv
[i
], "discard")) {
329 agent_relay_mode
= discard
;
332 } else if (!strcmp(argv
[i
], "-D")) {
334 if (local_family_set
&& (local_family
== AF_INET6
)) {
337 local_family_set
= 1;
338 local_family
= AF_INET
;
340 drop_agent_mismatches
= 1;
342 } else if (!strcmp(argv
[i
], "-I")) {
343 if (local_family_set
&& (local_family
== AF_INET
)) {
346 local_family_set
= 1;
347 local_family
= AF_INET6
;
348 use_if_id
= ISC_TRUE
;
349 } else if (!strcmp(argv
[i
], "-l")) {
350 if (local_family_set
&& (local_family
== AF_INET
)) {
353 local_family_set
= 1;
354 local_family
= AF_INET6
;
355 if (downstreams
!= NULL
)
356 use_if_id
= ISC_TRUE
;
359 sl
= parse_downstream(argv
[i
]);
360 sl
->next
= downstreams
;
362 } else if (!strcmp(argv
[i
], "-u")) {
363 if (local_family_set
&& (local_family
== AF_INET
)) {
366 local_family_set
= 1;
367 local_family
= AF_INET6
;
370 sl
= parse_upstream(argv
[i
]);
371 sl
->next
= upstreams
;
373 } else if (!strcmp(argv
[i
], "-s")) {
374 if (local_family_set
&& (local_family
== AF_INET
)) {
377 local_family_set
= 1;
378 local_family
= AF_INET6
;
381 dhcrelay_sub_id
= argv
[i
];
383 } else if (!strcmp(argv
[i
], "-pf")) {
386 path_dhcrelay_pid
= argv
[i
];
387 no_dhcrelay_pid
= ISC_TRUE
;
388 } else if (!strcmp(argv
[i
], "--no-pid")) {
389 no_pid_file
= ISC_TRUE
;
390 } else if (!strcmp(argv
[i
], "--version")) {
391 log_info("isc-dhcrelay-%s", PACKAGE_VERSION
);
393 } else if (!strcmp(argv
[i
], "--help") ||
394 !strcmp(argv
[i
], "-h")) {
395 log_info(DHCRELAY_USAGE
);
397 } else if (argv
[i
][0] == '-') {
401 struct in_addr ia
, *iap
= NULL
;
404 if (local_family_set
&& (local_family
== AF_INET6
)) {
407 local_family_set
= 1;
408 local_family
= AF_INET
;
410 if (inet_aton(argv
[i
], &ia
)) {
413 he
= gethostbyname(argv
[i
]);
415 log_error("%s: host unknown", argv
[i
]);
417 iap
= ((struct in_addr
*)
423 sp
= ((struct server_list
*)
424 dmalloc(sizeof *sp
, MDL
));
426 log_fatal("no memory for server.\n");
429 memcpy(&sp
->to
.sin_addr
, iap
, sizeof *iap
);
435 * If the user didn't specify a pid file directly
436 * find one from environment variables or defaults
438 if (no_dhcrelay_pid
== ISC_FALSE
) {
439 if (local_family
== AF_INET
) {
440 path_dhcrelay_pid
= getenv("PATH_DHCRELAY_PID");
441 if (path_dhcrelay_pid
== NULL
)
442 path_dhcrelay_pid
= _PATH_DHCRELAY_PID
;
446 path_dhcrelay_pid
= getenv("PATH_DHCRELAY6_PID");
447 if (path_dhcrelay_pid
== NULL
)
448 path_dhcrelay_pid
= _PATH_DHCRELAY6_PID
;
454 log_info("%s %s", message
, PACKAGE_VERSION
);
461 /* Set default port */
462 if (local_family
== AF_INET
) {
463 service_local
= "bootps";
464 service_remote
= "bootpc";
465 port_local
= htons(67);
466 port_remote
= htons(68);
470 service_local
= "dhcpv6-server";
471 service_remote
= "dhcpv6-client";
472 port_local
= htons(547);
473 port_remote
= htons(546);
478 ent
= getservbyname(service_local
, "udp");
480 local_port
= ent
->s_port
;
482 local_port
= port_local
;
484 ent
= getservbyname(service_remote
, "udp");
486 remote_port
= ent
->s_port
;
488 remote_port
= port_remote
;
493 if (local_family
== AF_INET
) {
494 /* We need at least one server */
495 if (servers
== NULL
) {
496 log_fatal("No servers specified.");
500 /* Set up the server sockaddrs. */
501 for (sp
= servers
; sp
; sp
= sp
->next
) {
502 sp
->to
.sin_port
= local_port
;
503 sp
->to
.sin_family
= AF_INET
;
505 sp
->to
.sin_len
= sizeof sp
->to
;
513 /* We need at least one upstream and one downstream interface */
514 if (upstreams
== NULL
|| downstreams
== NULL
) {
515 log_info("Must specify at least one lower "
516 "and one upper interface.\n");
520 /* Set up the initial dhcp option universe. */
521 initialize_common_option_spaces();
523 /* Check requested options. */
524 code
= D6O_RELAY_MSG
;
525 if (!option_code_hash_lookup(&requested_opts
[0],
526 dhcpv6_universe
.code_hash
,
528 log_fatal("Unable to find the RELAY_MSG "
529 "option definition.");
530 code
= D6O_INTERFACE_ID
;
531 if (!option_code_hash_lookup(&requested_opts
[1],
532 dhcpv6_universe
.code_hash
,
534 log_fatal("Unable to find the INTERFACE_ID "
535 "option definition.");
539 /* Get the current time... */
540 gettimeofday(&cur_tv
, NULL
);
542 /* Discover all the network interfaces. */
543 discover_interfaces(DISCOVER_RELAY
);
546 if (local_family
== AF_INET6
)
550 /* Become a daemon... */
558 if ((pid
= fork()) < 0)
559 log_fatal("Can't fork daemon: %m");
563 if (no_pid_file
== ISC_FALSE
) {
564 pfdesc
= open(path_dhcrelay_pid
,
565 O_CREAT
| O_TRUNC
| O_WRONLY
, 0644);
568 log_error("Can't create %s: %m",
571 pf
= fdopen(pfdesc
, "w");
573 log_error("Can't fdopen %s: %m",
576 fprintf(pf
, "%ld\n",(long)getpid());
587 IGNORE_RET (chdir("/"));
590 /* Set up the packet handler... */
591 if (local_family
== AF_INET
)
592 bootp_packet_handler
= do_relay4
;
595 dhcpv6_packet_handler
= do_packet6
;
598 /* install signal handlers */
599 signal(SIGINT
, dhcp_signal_handler
); /* control-c */
600 signal(SIGTERM
, dhcp_signal_handler
); /* kill */
602 /* Start dispatching packets and timeouts... */
605 /* In fact dispatch() never returns. */
610 do_relay4(struct interface_info
*ip
, struct dhcp_packet
*packet
,
611 unsigned int length
, unsigned int from_port
, struct iaddr from
,
612 struct hardware
*hfrom
) {
613 struct server_list
*sp
;
614 struct sockaddr_in to
;
615 struct interface_info
*out
;
616 struct hardware hto
, *htop
;
618 if (packet
->hlen
> sizeof packet
->chaddr
) {
619 log_info("Discarding packet with invalid hlen, received on "
620 "%s interface.", ip
->name
);
623 if (ip
->address_count
< 1 || ip
->addresses
== NULL
) {
624 log_info("Discarding packet received on %s interface that "
625 "has no IPv4 address assigned.", ip
->name
);
629 /* Find the interface that corresponds to the giaddr
631 if (packet
->giaddr
.s_addr
) {
632 for (out
= interfaces
; out
; out
= out
->next
) {
635 for (i
= 0 ; i
< out
->address_count
; i
++ ) {
636 if (out
->addresses
[i
].s_addr
==
637 packet
->giaddr
.s_addr
) {
650 /* If it's a bootreply, forward it to the client. */
651 if (packet
->op
== BOOTREPLY
) {
652 if (!(packet
->flags
& htons(BOOTP_BROADCAST
)) &&
653 can_unicast_without_arp(out
)) {
654 to
.sin_addr
= packet
->yiaddr
;
655 to
.sin_port
= remote_port
;
657 /* and hardware address is not broadcast */
660 to
.sin_addr
.s_addr
= htonl(INADDR_BROADCAST
);
661 to
.sin_port
= remote_port
;
663 /* hardware address is broadcast */
666 to
.sin_family
= AF_INET
;
668 to
.sin_len
= sizeof to
;
671 memcpy(&hto
.hbuf
[1], packet
->chaddr
, packet
->hlen
);
672 hto
.hbuf
[0] = packet
->htype
;
673 hto
.hlen
= packet
->hlen
+ 1;
675 /* Wipe out the agent relay options and, if possible, figure
676 out which interface to use based on the contents of the
677 option that we put on the request to which the server is
680 strip_relay_agent_options(ip
, &out
, packet
, length
)))
684 log_error("Packet to bogus giaddr %s.\n",
685 inet_ntoa(packet
->giaddr
));
686 ++bogus_giaddr_drops
;
690 if (send_packet(out
, NULL
, packet
, length
, out
->addresses
[0],
692 ++server_packet_errors
;
694 log_debug("Forwarded BOOTREPLY for %s to %s",
695 print_hw_addr(packet
->htype
, packet
->hlen
,
697 inet_ntoa(to
.sin_addr
));
699 ++server_packets_relayed
;
704 /* If giaddr matches one of our addresses, ignore the packet -
709 /* Add relay agent options if indicated. If something goes wrong,
711 if (!(length
= add_relay_agent_options(ip
, packet
, length
,
715 /* If giaddr is not already set, Set it so the server can
716 figure out what net it's from and so that we can later
717 forward the response to the correct net. If it's already
718 set, the response will be sent directly to the relay agent
719 that set giaddr, so we won't see it. */
720 if (!packet
->giaddr
.s_addr
)
721 packet
->giaddr
= ip
->addresses
[0];
722 if (packet
->hops
< max_hop_count
)
723 packet
->hops
= packet
->hops
+ 1;
727 /* Otherwise, it's a BOOTREQUEST, so forward it to all the
729 for (sp
= servers
; sp
; sp
= sp
->next
) {
730 if (send_packet((fallback_interface
731 ? fallback_interface
: interfaces
),
732 NULL
, packet
, length
, ip
->addresses
[0],
733 &sp
->to
, NULL
) < 0) {
734 ++client_packet_errors
;
736 log_debug("Forwarded BOOTREQUEST for %s to %s",
737 print_hw_addr(packet
->htype
, packet
->hlen
,
739 inet_ntoa(sp
->to
.sin_addr
));
740 ++client_packets_relayed
;
746 /* Strip any Relay Agent Information options from the DHCP packet
747 option buffer. If there is a circuit ID suboption, look up the
748 outgoing interface based upon it. */
751 strip_relay_agent_options(struct interface_info
*in
,
752 struct interface_info
**out
,
753 struct dhcp_packet
*packet
,
756 u_int8_t
*op
, *nextop
, *sp
, *max
;
757 int good_agent_option
= 0;
760 /* If we're not adding agent options to packets, we're not taking
762 if (!add_agent_options
)
765 /* If there's no cookie, it's a bootp packet, so we should just
766 forward it unchanged. */
767 if (memcmp(packet
->options
, DHCP_OPTIONS_COOKIE
, 4))
770 max
= ((u_int8_t
*)packet
) + length
;
771 sp
= op
= &packet
->options
[4];
775 /* Skip padding... */
783 /* If we see a message type, it's a DHCP packet. */
784 case DHO_DHCP_MESSAGE_TYPE
:
789 /* Quit immediately if we hit an End option. */
795 case DHO_DHCP_AGENT_OPTIONS
:
796 /* We shouldn't see a relay agent option in a
797 packet before we've seen the DHCP packet type,
798 but if we do, we have to leave it alone. */
802 /* Do not process an agent option if it exceeds the
803 * buffer. Fail this packet.
805 nextop
= op
+ op
[1] + 2;
809 status
= find_interface_by_agent_option(packet
,
812 if (status
== -1 && drop_agent_mismatches
)
815 good_agent_option
= 1;
820 /* Skip over other options. */
822 /* Fail if processing this option will exceed the
823 * buffer(op[1] is malformed).
825 nextop
= op
+ op
[1] + 2;
830 memmove(sp
, op
, op
[1] + 2);
841 /* If it's not a DHCP packet, we're not supposed to touch it. */
845 /* If none of the agent options we found matched, or if we didn't
846 find any agent options, count this packet as not having any
847 matching agent options, and if we're relying on agent options
848 to determine the outgoing interface, drop the packet. */
850 if (!good_agent_option
) {
851 ++missing_agent_option
;
852 if (drop_agent_mismatches
)
856 /* Adjust the length... */
858 length
= sp
-((u_int8_t
*)packet
);
860 /* Make sure the packet isn't short(this is unlikely,
862 if (length
< BOOTP_MIN_LEN
) {
863 memset(sp
, DHO_PAD
, BOOTP_MIN_LEN
- length
);
864 length
= BOOTP_MIN_LEN
;
871 /* Find an interface that matches the circuit ID specified in the
872 Relay Agent Information option. If one is found, store it through
873 the pointer given; otherwise, leave the existing pointer alone.
875 We actually deviate somewhat from the current specification here:
876 if the option buffer is corrupt, we suggest that the caller not
877 respond to this packet. If the circuit ID doesn't match any known
878 interface, we suggest that the caller to drop the packet. Only if
879 we find a circuit ID that matches an existing interface do we tell
880 the caller to go ahead and process the packet. */
883 find_interface_by_agent_option(struct dhcp_packet
*packet
,
884 struct interface_info
**out
,
885 u_int8_t
*buf
, int len
) {
887 u_int8_t
*circuit_id
= 0;
888 unsigned circuit_id_len
= 0;
889 struct interface_info
*ip
;
892 /* If the next agent option overflows the end of the
893 packet, the agent option buffer is corrupt. */
895 i
+ buf
[i
+ 1] + 2 > len
) {
896 ++corrupt_agent_options
;
900 /* Remember where the circuit ID is... */
902 circuit_id
= &buf
[i
+ 2];
903 circuit_id_len
= buf
[i
+ 1];
904 i
+= circuit_id_len
+ 2;
913 /* If there's no circuit ID, it's not really ours, tell the caller
916 ++missing_circuit_id
;
920 /* Scan the interface list looking for an interface whose
921 name matches the one specified in circuit_id. */
923 for (ip
= interfaces
; ip
; ip
= ip
->next
) {
924 if (ip
->circuit_id
&&
925 ip
->circuit_id_len
== circuit_id_len
&&
926 !memcmp(ip
->circuit_id
, circuit_id
, circuit_id_len
))
930 /* If we got a match, use it. */
936 /* If we didn't get a match, the circuit ID was bogus. */
942 * Examine a packet to see if it's a candidate to have a Relay
943 * Agent Information option tacked onto its tail. If it is, tack
947 #include <sys/cdefs.h>
948 __RCSID("$NetBSD: dhcrelay.c,v 1.5 2014/07/12 12:09:37 spz Exp $");
950 add_relay_agent_options(struct interface_info
*ip
, struct dhcp_packet
*packet
,
951 unsigned length
, struct in_addr giaddr
) {
952 int is_dhcp
= 0, mms
;
954 u_int8_t
*op
, *nextop
, *sp
, *max
, *end_pad
= NULL
;
956 /* If we're not adding agent options to packets, we can skip
958 if (!add_agent_options
)
961 /* If there's no cookie, it's a bootp packet, so we should just
962 forward it unchanged. */
963 if (memcmp(packet
->options
, DHCP_OPTIONS_COOKIE
, 4))
966 max
= ((u_int8_t
*)packet
) + dhcp_max_agent_option_packet_length
;
968 /* Commence processing after the cookie. */
969 sp
= op
= &packet
->options
[4];
973 /* Skip padding... */
975 /* Remember the first pad byte so we can commandeer
978 * XXX: Is this really a good idea? Sure, we can
979 * seemingly reduce the packet while we're looking,
980 * but if the packet was signed by the client then
981 * this padding is part of the checksum(RFC3118),
982 * and its nonpresence would break authentication.
994 /* If we see a message type, it's a DHCP packet. */
995 case DHO_DHCP_MESSAGE_TYPE
:
1000 * If there's a maximum message size option, we
1001 * should pay attention to it
1003 case DHO_DHCP_MAX_MESSAGE_SIZE
:
1004 mms
= ntohs(*(op
+ 2));
1005 if (mms
< dhcp_max_agent_option_packet_length
&&
1006 mms
>= DHCP_MTU_MIN
)
1007 max
= ((u_int8_t
*)packet
) + mms
;
1010 /* Quit immediately if we hit an End option. */
1014 case DHO_DHCP_AGENT_OPTIONS
:
1015 /* We shouldn't see a relay agent option in a
1016 packet before we've seen the DHCP packet type,
1017 but if we do, we have to leave it alone. */
1023 /* There's already a Relay Agent Information option
1024 in this packet. How embarrassing. Decide what
1025 to do based on the mode the user specified. */
1027 switch(agent_relay_mode
) {
1028 case forward_and_append
:
1030 case forward_untouched
:
1034 case forward_and_replace
:
1039 /* Skip over the agent option and start copying
1040 if we aren't copying already. */
1045 /* Skip over other options. */
1047 /* Fail if processing this option will exceed the
1048 * buffer(op[1] is malformed).
1050 nextop
= op
+ op
[1] + 2;
1057 memmove(sp
, op
, op
[1] + 2);
1068 /* If it's not a DHCP packet, we're not supposed to touch it. */
1072 /* If the packet was padded out, we can store the agent option
1073 at the beginning of the padding. */
1075 if (end_pad
!= NULL
)
1079 /* Remember where the end of the packet was after parsing
1084 /* Sanity check. Had better not ever happen. */
1085 if ((ip
->circuit_id_len
> 255) ||(ip
->circuit_id_len
< 1))
1086 log_fatal("Circuit ID length %d out of range [1-255] on "
1087 "%s\n", ip
->circuit_id_len
, ip
->name
);
1088 optlen
= ip
->circuit_id_len
+ 2; /* RAI_CIRCUIT_ID + len */
1090 if (ip
->remote_id
) {
1091 if (ip
->remote_id_len
> 255 || ip
->remote_id_len
< 1)
1092 log_fatal("Remote ID length %d out of range [1-255] "
1093 "on %s\n", ip
->circuit_id_len
, ip
->name
);
1094 optlen
+= ip
->remote_id_len
+ 2; /* RAI_REMOTE_ID + len */
1097 /* We do not support relay option fragmenting(multiple options to
1098 * support an option data exceeding 255 bytes).
1100 if ((optlen
< 3) ||(optlen
> 255))
1101 log_fatal("Total agent option length(%u) out of range "
1102 "[3 - 255] on %s\n", optlen
, ip
->name
);
1105 * Is there room for the option, its code+len, and DHO_END?
1106 * If not, forward without adding the option.
1108 if (max
- sp
>= optlen
+ 3) {
1109 log_debug("Adding %d-byte relay agent option", optlen
+ 3);
1111 /* Okay, cons up *our* Relay Agent Information option. */
1112 *sp
++ = DHO_DHCP_AGENT_OPTIONS
;
1115 /* Copy in the circuit id... */
1116 *sp
++ = RAI_CIRCUIT_ID
;
1117 *sp
++ = ip
->circuit_id_len
;
1118 memcpy(sp
, ip
->circuit_id
, ip
->circuit_id_len
);
1119 sp
+= ip
->circuit_id_len
;
1121 /* Copy in remote ID... */
1122 if (ip
->remote_id
) {
1123 *sp
++ = RAI_REMOTE_ID
;
1124 *sp
++ = ip
->remote_id_len
;
1125 memcpy(sp
, ip
->remote_id
, ip
->remote_id_len
);
1126 sp
+= ip
->remote_id_len
;
1129 ++agent_option_errors
;
1130 log_error("No room in packet (used %d of %d) "
1131 "for %d-byte relay agent option: omitted",
1132 (int) (sp
- ((u_int8_t
*) packet
)),
1133 (int) (max
- ((u_int8_t
*) packet
)),
1138 * Deposit an END option unless the packet is full (shouldn't
1144 /* Recalculate total packet length. */
1145 length
= sp
-((u_int8_t
*)packet
);
1147 /* Make sure the packet isn't short(this is unlikely, but WTH) */
1148 if (length
< BOOTP_MIN_LEN
) {
1149 memset(sp
, DHO_PAD
, BOOTP_MIN_LEN
- length
);
1150 return (BOOTP_MIN_LEN
);
1158 * Parse a downstream argument: [address%]interface[#index].
1160 static struct stream_list
*
1161 parse_downstream(char *arg
) {
1162 struct stream_list
*dp
, *up
;
1163 struct interface_info
*ifp
= NULL
;
1164 char *ifname
, *addr
, *iid
;
1165 isc_result_t status
;
1167 if (!supports_multiple_interfaces(ifp
) &&
1168 (downstreams
!= NULL
))
1169 log_fatal("No support for multiple interfaces.");
1171 /* Decode the argument. */
1172 ifname
= strchr(arg
, '%');
1173 if (ifname
== NULL
) {
1180 iid
= strchr(ifname
, '#');
1184 if (strlen(ifname
) >= sizeof(ifp
->name
)) {
1185 log_error("Interface name '%s' too long", ifname
);
1189 /* Don't declare twice. */
1190 for (dp
= downstreams
; dp
; dp
= dp
->next
) {
1191 if (strcmp(ifname
, dp
->ifp
->name
) == 0)
1192 log_fatal("Down interface '%s' declared twice.",
1196 /* Share with up side? */
1197 for (up
= upstreams
; up
; up
= up
->next
) {
1198 if (strcmp(ifname
, up
->ifp
->name
) == 0) {
1199 log_info("Interface '%s' is both down and up.",
1206 /* New interface. */
1208 status
= interface_allocate(&ifp
, MDL
);
1209 if (status
!= ISC_R_SUCCESS
)
1210 log_fatal("%s: interface_allocate: %s",
1211 arg
, isc_result_totext(status
));
1212 strcpy(ifp
->name
, ifname
);
1214 interface_reference(&ifp
->next
, interfaces
, MDL
);
1215 interface_dereference(&interfaces
, MDL
);
1217 interface_reference(&interfaces
, ifp
, MDL
);
1218 ifp
->flags
|= INTERFACE_REQUESTED
| INTERFACE_DOWNSTREAM
;
1221 /* New downstream. */
1222 dp
= (struct stream_list
*) dmalloc(sizeof(*dp
), MDL
);
1224 log_fatal("No memory for downstream.");
1231 /* !addr case handled by setup. */
1232 if (addr
&& (inet_pton(AF_INET6
, addr
, &dp
->link
.sin6_addr
) <= 0))
1233 log_fatal("Bad link address '%s'", addr
);
1239 * Parse an upstream argument: [address]%interface.
1241 static struct stream_list
*
1242 parse_upstream(char *arg
) {
1243 struct stream_list
*up
, *dp
;
1244 struct interface_info
*ifp
= NULL
;
1245 char *ifname
, *addr
;
1246 isc_result_t status
;
1248 /* Decode the argument. */
1249 ifname
= strchr(arg
, '%');
1250 if (ifname
== NULL
) {
1252 addr
= All_DHCP_Servers
;
1257 if (strlen(ifname
) >= sizeof(ifp
->name
)) {
1258 log_fatal("Interface name '%s' too long", ifname
);
1261 /* Shared up interface? */
1262 for (up
= upstreams
; up
; up
= up
->next
) {
1263 if (strcmp(ifname
, up
->ifp
->name
) == 0) {
1268 for (dp
= downstreams
; dp
; dp
= dp
->next
) {
1269 if (strcmp(ifname
, dp
->ifp
->name
) == 0) {
1275 /* New interface. */
1277 status
= interface_allocate(&ifp
, MDL
);
1278 if (status
!= ISC_R_SUCCESS
)
1279 log_fatal("%s: interface_allocate: %s",
1280 arg
, isc_result_totext(status
));
1281 strcpy(ifp
->name
, ifname
);
1283 interface_reference(&ifp
->next
, interfaces
, MDL
);
1284 interface_dereference(&interfaces
, MDL
);
1286 interface_reference(&interfaces
, ifp
, MDL
);
1287 ifp
->flags
|= INTERFACE_REQUESTED
| INTERFACE_UPSTREAM
;
1291 up
= (struct stream_list
*) dmalloc(sizeof(*up
), MDL
);
1293 log_fatal("No memory for upstream.");
1297 if (inet_pton(AF_INET6
, addr
, &up
->link
.sin6_addr
) <= 0)
1298 log_fatal("Bad address %s", addr
);
1304 * Setup downstream interfaces.
1307 setup_streams(void) {
1308 struct stream_list
*dp
, *up
;
1310 isc_boolean_t link_is_set
;
1312 for (dp
= downstreams
; dp
; dp
= dp
->next
) {
1313 /* Check interface */
1314 if (dp
->ifp
->v6address_count
== 0)
1315 log_fatal("Interface '%s' has no IPv6 addresses.",
1318 /* Check/set link. */
1319 if (IN6_IS_ADDR_UNSPECIFIED(&dp
->link
.sin6_addr
))
1320 link_is_set
= ISC_FALSE
;
1322 link_is_set
= ISC_TRUE
;
1323 for (i
= 0; i
< dp
->ifp
->v6address_count
; i
++) {
1324 if (IN6_IS_ADDR_LINKLOCAL(&dp
->ifp
->v6addresses
[i
]))
1328 if (!memcmp(&dp
->ifp
->v6addresses
[i
],
1329 &dp
->link
.sin6_addr
,
1330 sizeof(dp
->link
.sin6_addr
)))
1333 if (i
== dp
->ifp
->v6address_count
)
1334 log_fatal("Interface %s does not have global IPv6 "
1335 "address assigned.", dp
->ifp
->name
);
1337 memcpy(&dp
->link
.sin6_addr
,
1338 &dp
->ifp
->v6addresses
[i
],
1339 sizeof(dp
->link
.sin6_addr
));
1341 /* Set interface-id. */
1343 dp
->id
= dp
->ifp
->index
;
1346 for (up
= upstreams
; up
; up
= up
->next
) {
1347 up
->link
.sin6_port
= local_port
;
1348 up
->link
.sin6_family
= AF_INET6
;
1350 up
->link
.sin6_len
= sizeof(up
->link
);
1353 if (up
->ifp
->v6address_count
== 0)
1354 log_fatal("Interface '%s' has no IPv6 addresses.",
1360 * Add DHCPv6 agent options here.
1362 static const int required_forw_opts
[] = {
1370 * Process a packet upwards, i.e., from client to server.
1373 process_up6(struct packet
*packet
, struct stream_list
*dp
) {
1374 char forw_data
[65535];
1376 struct dhcpv6_relay_packet
*relay
;
1377 struct option_state
*opts
;
1378 struct stream_list
*up
;
1380 /* Check if the message should be relayed to the server. */
1381 switch (packet
->dhcpv6_msg_type
) {
1382 case DHCPV6_SOLICIT
:
1383 case DHCPV6_REQUEST
:
1384 case DHCPV6_CONFIRM
:
1387 case DHCPV6_RELEASE
:
1388 case DHCPV6_DECLINE
:
1389 case DHCPV6_INFORMATION_REQUEST
:
1390 case DHCPV6_RELAY_FORW
:
1391 case DHCPV6_LEASEQUERY
:
1392 log_info("Relaying %s from %s port %d going up.",
1393 dhcpv6_type_names
[packet
->dhcpv6_msg_type
],
1394 piaddr(packet
->client_addr
),
1395 ntohs(packet
->client_port
));
1398 case DHCPV6_ADVERTISE
:
1400 case DHCPV6_RECONFIGURE
:
1401 case DHCPV6_RELAY_REPL
:
1402 case DHCPV6_LEASEQUERY_REPLY
:
1403 log_info("Discarding %s from %s port %d going up.",
1404 dhcpv6_type_names
[packet
->dhcpv6_msg_type
],
1405 piaddr(packet
->client_addr
),
1406 ntohs(packet
->client_port
));
1410 log_info("Unknown %d type from %s port %d going up.",
1411 packet
->dhcpv6_msg_type
,
1412 piaddr(packet
->client_addr
),
1413 ntohs(packet
->client_port
));
1417 /* Build the relay-forward header. */
1418 relay
= (struct dhcpv6_relay_packet
*) forw_data
;
1419 cursor
= offsetof(struct dhcpv6_relay_packet
, options
);
1420 relay
->msg_type
= DHCPV6_RELAY_FORW
;
1421 if (packet
->dhcpv6_msg_type
== DHCPV6_RELAY_FORW
) {
1422 if (packet
->dhcpv6_hop_count
>= max_hop_count
) {
1423 log_info("Hop count exceeded,");
1426 relay
->hop_count
= packet
->dhcpv6_hop_count
+ 1;
1428 memcpy(&relay
->link_address
, &dp
->link
.sin6_addr
, 16);
1430 /* On smart relay add: && !global. */
1431 if (!use_if_id
&& downstreams
->next
) {
1432 log_info("Shan't get back the interface.");
1435 memset(&relay
->link_address
, 0, 16);
1438 relay
->hop_count
= 0;
1441 memcpy(&relay
->link_address
, &dp
->link
.sin6_addr
, 16);
1443 memcpy(&relay
->peer_address
, packet
->client_addr
.iabuf
, 16);
1445 /* Get an option state. */
1447 if (!option_state_allocate(&opts
, MDL
)) {
1448 log_fatal("No memory for upwards options.");
1451 /* Add an interface-id (if used). */
1457 } else if (!downstreams
->next
) {
1458 if_id
= downstreams
->id
;
1460 log_info("Don't know the interface.");
1461 option_state_dereference(&opts
, MDL
);
1465 if (!save_option_buffer(&dhcpv6_universe
, opts
,
1466 NULL
, (unsigned char *) &if_id
,
1468 D6O_INTERFACE_ID
, 0)) {
1469 log_error("Can't save interface-id.");
1470 option_state_dereference(&opts
, MDL
);
1475 /* Add a subscriber-id if desired. */
1476 /* This is for testing rather than general use */
1477 if (dhcrelay_sub_id
!= NULL
) {
1478 if (!save_option_buffer(&dhcpv6_universe
, opts
, NULL
,
1479 (unsigned char *) dhcrelay_sub_id
,
1480 strlen(dhcrelay_sub_id
),
1481 D6O_SUBSCRIBER_ID
, 0)) {
1482 log_error("Can't save subsriber-id.");
1483 option_state_dereference(&opts
, MDL
);
1489 /* Add the relay-msg carrying the packet. */
1490 if (!save_option_buffer(&dhcpv6_universe
, opts
,
1491 NULL
, (unsigned char *) packet
->raw
,
1492 packet
->packet_length
,
1493 D6O_RELAY_MSG
, 0)) {
1494 log_error("Can't save relay-msg.");
1495 option_state_dereference(&opts
, MDL
);
1499 /* Finish the relay-forward message. */
1500 cursor
+= store_options6(forw_data
+ cursor
,
1501 sizeof(forw_data
) - cursor
,
1503 required_forw_opts
, NULL
);
1504 option_state_dereference(&opts
, MDL
);
1506 /* Send it to all upstreams. */
1507 for (up
= upstreams
; up
; up
= up
->next
) {
1508 send_packet6(up
->ifp
, (unsigned char *) forw_data
,
1509 (size_t) cursor
, &up
->link
);
1514 * Process a packet downwards, i.e., from server to client.
1517 process_down6(struct packet
*packet
) {
1518 struct stream_list
*dp
;
1519 struct option_cache
*oc
;
1520 struct data_string relay_msg
;
1521 const struct dhcpv6_packet
*msg
;
1522 struct data_string if_id
;
1523 struct sockaddr_in6 to
;
1526 /* The packet must be a relay-reply message. */
1527 if (packet
->dhcpv6_msg_type
!= DHCPV6_RELAY_REPL
) {
1528 if (packet
->dhcpv6_msg_type
< dhcpv6_type_name_max
)
1529 log_info("Discarding %s from %s port %d going down.",
1530 dhcpv6_type_names
[packet
->dhcpv6_msg_type
],
1531 piaddr(packet
->client_addr
),
1532 ntohs(packet
->client_port
));
1534 log_info("Unknown %d type from %s port %d going down.",
1535 packet
->dhcpv6_msg_type
,
1536 piaddr(packet
->client_addr
),
1537 ntohs(packet
->client_port
));
1542 memset(&relay_msg
, 0, sizeof(relay_msg
));
1543 memset(&if_id
, 0, sizeof(if_id
));
1544 memset(&to
, 0, sizeof(to
));
1545 to
.sin6_family
= AF_INET6
;
1547 to
.sin6_len
= sizeof(to
);
1549 to
.sin6_port
= remote_port
;
1552 /* Get the relay-msg option (carrying the message to relay). */
1553 oc
= lookup_option(&dhcpv6_universe
, packet
->options
, D6O_RELAY_MSG
);
1555 log_info("No relay-msg.");
1558 if (!evaluate_option_cache(&relay_msg
, packet
, NULL
, NULL
,
1559 packet
->options
, NULL
,
1560 &global_scope
, oc
, MDL
) ||
1561 (relay_msg
.len
< offsetof(struct dhcpv6_packet
, options
))) {
1562 log_error("Can't evaluate relay-msg.");
1565 msg
= (const struct dhcpv6_packet
*) relay_msg
.data
;
1567 /* Get the interface-id (if exists) and the downstream. */
1568 oc
= lookup_option(&dhcpv6_universe
, packet
->options
,
1573 if (!evaluate_option_cache(&if_id
, packet
, NULL
, NULL
,
1574 packet
->options
, NULL
,
1575 &global_scope
, oc
, MDL
) ||
1576 (if_id
.len
!= sizeof(int))) {
1577 log_info("Can't evaluate interface-id.");
1580 memcpy(&if_index
, if_id
.data
, sizeof(int));
1581 for (dp
= downstreams
; dp
; dp
= dp
->next
) {
1582 if (dp
->id
== if_index
)
1587 /* Require an interface-id. */
1588 log_info("No interface-id.");
1591 for (dp
= downstreams
; dp
; dp
= dp
->next
) {
1592 /* Get the first matching one. */
1593 if (!memcmp(&dp
->link
.sin6_addr
,
1594 &packet
->dhcpv6_link_address
,
1595 sizeof(struct in6_addr
)))
1599 /* Why bother when there is no choice. */
1600 if (!dp
&& downstreams
&& !downstreams
->next
)
1603 log_info("Can't find the down interface.");
1606 memcpy(peer
.iabuf
, &packet
->dhcpv6_peer_address
, peer
.len
);
1607 to
.sin6_addr
= packet
->dhcpv6_peer_address
;
1609 /* Check if we should relay the carried message. */
1610 switch (msg
->msg_type
) {
1611 /* Relay-Reply of for another relay, not a client. */
1612 case DHCPV6_RELAY_REPL
:
1613 to
.sin6_port
= local_port
;
1616 case DHCPV6_ADVERTISE
:
1618 case DHCPV6_RECONFIGURE
:
1619 case DHCPV6_RELAY_FORW
:
1620 case DHCPV6_LEASEQUERY_REPLY
:
1621 log_info("Relaying %s to %s port %d down.",
1622 dhcpv6_type_names
[msg
->msg_type
],
1624 ntohs(to
.sin6_port
));
1627 case DHCPV6_SOLICIT
:
1628 case DHCPV6_REQUEST
:
1629 case DHCPV6_CONFIRM
:
1632 case DHCPV6_RELEASE
:
1633 case DHCPV6_DECLINE
:
1634 case DHCPV6_INFORMATION_REQUEST
:
1635 case DHCPV6_LEASEQUERY
:
1636 log_info("Discarding %s to %s port %d down.",
1637 dhcpv6_type_names
[msg
->msg_type
],
1639 ntohs(to
.sin6_port
));
1643 log_info("Unknown %d type to %s port %d down.",
1646 ntohs(to
.sin6_port
));
1650 /* Send the message to the downstream. */
1651 send_packet6(dp
->ifp
, (unsigned char *) relay_msg
.data
,
1652 (size_t) relay_msg
.len
, &to
);
1655 if (relay_msg
.data
!= NULL
)
1656 data_string_forget(&relay_msg
, MDL
);
1657 if (if_id
.data
!= NULL
)
1658 data_string_forget(&if_id
, MDL
);
1662 * Called by the dispatch packet handler with a decoded packet.
1665 dhcpv6(struct packet
*packet
) {
1666 struct stream_list
*dp
;
1668 /* Try all relay-replies downwards. */
1669 if (packet
->dhcpv6_msg_type
== DHCPV6_RELAY_REPL
) {
1670 process_down6(packet
);
1673 /* Others are candidates to go up if they come from down. */
1674 for (dp
= downstreams
; dp
; dp
= dp
->next
) {
1675 if (packet
->interface
!= dp
->ifp
)
1677 process_up6(packet
, dp
);
1680 /* Relay-forward could work from an unknown interface. */
1681 if (packet
->dhcpv6_msg_type
== DHCPV6_RELAY_FORW
) {
1682 process_up6(packet
, NULL
);
1686 log_info("Can't process packet from interface '%s'.",
1687 packet
->interface
->name
);
1691 /* Stub routines needed for linking with DHCP libraries. */
1693 bootp(struct packet
*packet
) {
1698 dhcp(struct packet
*packet
) {
1703 classify(struct packet
*p
, struct class *c
) {
1708 check_collection(struct packet
*p
, struct lease
*l
, struct collection
*c
) {
1713 find_class(struct class **class, const char *c1
, const char *c2
, int i
) {
1714 return ISC_R_NOTFOUND
;
1718 parse_allow_deny(struct option_cache
**oc
, struct parse
*p
, int i
) {
1723 dhcp_set_control_state(control_object_state_t oldstate
,
1724 control_object_state_t newstate
) {
1725 if (newstate
!= server_shutdown
)
1726 return ISC_R_SUCCESS
;