3 DHCP/BOOTP Relay Agent. */
6 * Copyright(c) 2004-2013 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-2009 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
;
263 if (strlen(argv
[i
]) >= sizeof(tmp
->name
)) {
264 log_fatal("%s: interface name too long "
266 argv
[i
], (long)strlen(argv
[i
]));
268 status
= interface_allocate(&tmp
, MDL
);
269 if (status
!= ISC_R_SUCCESS
) {
270 log_fatal("%s: interface_allocate: %s",
272 isc_result_totext(status
));
274 strcpy(tmp
->name
, argv
[i
]);
275 interface_snorf(tmp
, INTERFACE_REQUESTED
);
276 interface_dereference(&tmp
, MDL
);
277 } else if (!strcmp(argv
[i
], "-a")) {
279 if (local_family_set
&& (local_family
== AF_INET6
)) {
282 local_family_set
= 1;
283 local_family
= AF_INET
;
285 add_agent_options
= 1;
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
;
297 dhcp_max_agent_option_packet_length
= atoi(argv
[i
]);
299 if (dhcp_max_agent_option_packet_length
> DHCP_MTU_MAX
)
300 log_fatal("%s: packet length exceeds "
301 "longest possible MTU\n",
303 } else if (!strcmp(argv
[i
], "-m")) {
305 if (local_family_set
&& (local_family
== AF_INET6
)) {
308 local_family_set
= 1;
309 local_family
= AF_INET
;
313 if (!strcasecmp(argv
[i
], "append")) {
314 agent_relay_mode
= forward_and_append
;
315 } else if (!strcasecmp(argv
[i
], "replace")) {
316 agent_relay_mode
= forward_and_replace
;
317 } else if (!strcasecmp(argv
[i
], "forward")) {
318 agent_relay_mode
= forward_untouched
;
319 } else if (!strcasecmp(argv
[i
], "discard")) {
320 agent_relay_mode
= discard
;
323 } else if (!strcmp(argv
[i
], "-D")) {
325 if (local_family_set
&& (local_family
== AF_INET6
)) {
328 local_family_set
= 1;
329 local_family
= AF_INET
;
331 drop_agent_mismatches
= 1;
333 } else if (!strcmp(argv
[i
], "-I")) {
334 if (local_family_set
&& (local_family
== AF_INET
)) {
337 local_family_set
= 1;
338 local_family
= AF_INET6
;
339 use_if_id
= ISC_TRUE
;
340 } else if (!strcmp(argv
[i
], "-l")) {
341 if (local_family_set
&& (local_family
== AF_INET
)) {
344 local_family_set
= 1;
345 local_family
= AF_INET6
;
346 if (downstreams
!= NULL
)
347 use_if_id
= ISC_TRUE
;
350 sl
= parse_downstream(argv
[i
]);
351 sl
->next
= downstreams
;
353 } else if (!strcmp(argv
[i
], "-u")) {
354 if (local_family_set
&& (local_family
== AF_INET
)) {
357 local_family_set
= 1;
358 local_family
= AF_INET6
;
361 sl
= parse_upstream(argv
[i
]);
362 sl
->next
= upstreams
;
365 } else if (!strcmp(argv
[i
], "-pf")) {
368 path_dhcrelay_pid
= argv
[i
];
369 no_dhcrelay_pid
= ISC_TRUE
;
370 } else if (!strcmp(argv
[i
], "--no-pid")) {
371 no_pid_file
= ISC_TRUE
;
372 } else if (!strcmp(argv
[i
], "--version")) {
373 log_info("isc-dhcrelay-%s", PACKAGE_VERSION
);
375 } else if (!strcmp(argv
[i
], "--help") ||
376 !strcmp(argv
[i
], "-h")) {
377 log_info(DHCRELAY_USAGE
);
379 } else if (argv
[i
][0] == '-') {
383 struct in_addr ia
, *iap
= NULL
;
386 if (local_family_set
&& (local_family
== AF_INET6
)) {
389 local_family_set
= 1;
390 local_family
= AF_INET
;
392 if (inet_aton(argv
[i
], &ia
)) {
395 he
= gethostbyname(argv
[i
]);
397 log_error("%s: host unknown", argv
[i
]);
399 iap
= ((struct in_addr
*)
405 sp
= ((struct server_list
*)
406 dmalloc(sizeof *sp
, MDL
));
408 log_fatal("no memory for server.\n");
411 memcpy(&sp
->to
.sin_addr
, iap
, sizeof *iap
);
417 * If the user didn't specify a pid file directly
418 * find one from environment variables or defaults
420 if (no_dhcrelay_pid
== ISC_FALSE
) {
421 if (local_family
== AF_INET
) {
422 path_dhcrelay_pid
= getenv("PATH_DHCRELAY_PID");
423 if (path_dhcrelay_pid
== NULL
)
424 path_dhcrelay_pid
= _PATH_DHCRELAY_PID
;
428 path_dhcrelay_pid
= getenv("PATH_DHCRELAY6_PID");
429 if (path_dhcrelay_pid
== NULL
)
430 path_dhcrelay_pid
= _PATH_DHCRELAY6_PID
;
436 log_info("%s %s", message
, PACKAGE_VERSION
);
443 /* Set default port */
444 if (local_family
== AF_INET
) {
445 service_local
= "bootps";
446 service_remote
= "bootpc";
447 port_local
= htons(67);
448 port_remote
= htons(68);
452 service_local
= "dhcpv6-server";
453 service_remote
= "dhcpv6-client";
454 port_local
= htons(547);
455 port_remote
= htons(546);
460 ent
= getservbyname(service_local
, "udp");
462 local_port
= ent
->s_port
;
464 local_port
= port_local
;
466 ent
= getservbyname(service_remote
, "udp");
468 remote_port
= ent
->s_port
;
470 remote_port
= port_remote
;
475 if (local_family
== AF_INET
) {
476 /* We need at least one server */
477 if (servers
== NULL
) {
478 log_fatal("No servers specified.");
482 /* Set up the server sockaddrs. */
483 for (sp
= servers
; sp
; sp
= sp
->next
) {
484 sp
->to
.sin_port
= local_port
;
485 sp
->to
.sin_family
= AF_INET
;
487 sp
->to
.sin_len
= sizeof sp
->to
;
495 /* We need at least one upstream and one downstream interface */
496 if (upstreams
== NULL
|| downstreams
== NULL
) {
497 log_info("Must specify at least one lower "
498 "and one upper interface.\n");
502 /* Set up the initial dhcp option universe. */
503 initialize_common_option_spaces();
505 /* Check requested options. */
506 code
= D6O_RELAY_MSG
;
507 if (!option_code_hash_lookup(&requested_opts
[0],
508 dhcpv6_universe
.code_hash
,
510 log_fatal("Unable to find the RELAY_MSG "
511 "option definition.");
512 code
= D6O_INTERFACE_ID
;
513 if (!option_code_hash_lookup(&requested_opts
[1],
514 dhcpv6_universe
.code_hash
,
516 log_fatal("Unable to find the INTERFACE_ID "
517 "option definition.");
521 /* Get the current time... */
522 gettimeofday(&cur_tv
, NULL
);
524 /* Discover all the network interfaces. */
525 discover_interfaces(DISCOVER_RELAY
);
528 if (local_family
== AF_INET6
)
532 /* Become a daemon... */
540 if ((pid
= fork()) < 0)
541 log_fatal("Can't fork daemon: %m");
545 if (no_pid_file
== ISC_FALSE
) {
546 pfdesc
= open(path_dhcrelay_pid
,
547 O_CREAT
| O_TRUNC
| O_WRONLY
, 0644);
550 log_error("Can't create %s: %m",
553 pf
= fdopen(pfdesc
, "w");
555 log_error("Can't fdopen %s: %m",
558 fprintf(pf
, "%ld\n",(long)getpid());
569 IGNORE_RET (chdir("/"));
572 /* Set up the packet handler... */
573 if (local_family
== AF_INET
)
574 bootp_packet_handler
= do_relay4
;
577 dhcpv6_packet_handler
= do_packet6
;
580 /* Start dispatching packets and timeouts... */
588 do_relay4(struct interface_info
*ip
, struct dhcp_packet
*packet
,
589 unsigned int length
, unsigned int from_port
, struct iaddr from
,
590 struct hardware
*hfrom
) {
591 struct server_list
*sp
;
592 struct sockaddr_in to
;
593 struct interface_info
*out
;
594 struct hardware hto
, *htop
;
596 if (packet
->hlen
> sizeof packet
->chaddr
) {
597 log_info("Discarding packet with invalid hlen, received on "
598 "%s interface.", ip
->name
);
601 if (ip
->address_count
< 1 || ip
->addresses
== NULL
) {
602 log_info("Discarding packet received on %s interface that "
603 "has no IPv4 address assigned.", ip
->name
);
607 /* Find the interface that corresponds to the giaddr
609 if (packet
->giaddr
.s_addr
) {
610 for (out
= interfaces
; out
; out
= out
->next
) {
613 for (i
= 0 ; i
< out
->address_count
; i
++ ) {
614 if (out
->addresses
[i
].s_addr
==
615 packet
->giaddr
.s_addr
) {
628 /* If it's a bootreply, forward it to the client. */
629 if (packet
->op
== BOOTREPLY
) {
630 if (!(packet
->flags
& htons(BOOTP_BROADCAST
)) &&
631 can_unicast_without_arp(out
)) {
632 to
.sin_addr
= packet
->yiaddr
;
633 to
.sin_port
= remote_port
;
635 /* and hardware address is not broadcast */
638 to
.sin_addr
.s_addr
= htonl(INADDR_BROADCAST
);
639 to
.sin_port
= remote_port
;
641 /* hardware address is broadcast */
644 to
.sin_family
= AF_INET
;
646 to
.sin_len
= sizeof to
;
649 memcpy(&hto
.hbuf
[1], packet
->chaddr
, packet
->hlen
);
650 hto
.hbuf
[0] = packet
->htype
;
651 hto
.hlen
= packet
->hlen
+ 1;
653 /* Wipe out the agent relay options and, if possible, figure
654 out which interface to use based on the contents of the
655 option that we put on the request to which the server is
658 strip_relay_agent_options(ip
, &out
, packet
, length
)))
662 log_error("Packet to bogus giaddr %s.\n",
663 inet_ntoa(packet
->giaddr
));
664 ++bogus_giaddr_drops
;
668 if (send_packet(out
, NULL
, packet
, length
, out
->addresses
[0],
670 ++server_packet_errors
;
672 log_debug("Forwarded BOOTREPLY for %s to %s",
673 print_hw_addr(packet
->htype
, packet
->hlen
,
675 inet_ntoa(to
.sin_addr
));
677 ++server_packets_relayed
;
682 /* If giaddr matches one of our addresses, ignore the packet -
687 /* Add relay agent options if indicated. If something goes wrong,
689 if (!(length
= add_relay_agent_options(ip
, packet
, length
,
693 /* If giaddr is not already set, Set it so the server can
694 figure out what net it's from and so that we can later
695 forward the response to the correct net. If it's already
696 set, the response will be sent directly to the relay agent
697 that set giaddr, so we won't see it. */
698 if (!packet
->giaddr
.s_addr
)
699 packet
->giaddr
= ip
->addresses
[0];
700 if (packet
->hops
< max_hop_count
)
701 packet
->hops
= packet
->hops
+ 1;
705 /* Otherwise, it's a BOOTREQUEST, so forward it to all the
707 for (sp
= servers
; sp
; sp
= sp
->next
) {
708 if (send_packet((fallback_interface
709 ? fallback_interface
: interfaces
),
710 NULL
, packet
, length
, ip
->addresses
[0],
711 &sp
->to
, NULL
) < 0) {
712 ++client_packet_errors
;
714 log_debug("Forwarded BOOTREQUEST for %s to %s",
715 print_hw_addr(packet
->htype
, packet
->hlen
,
717 inet_ntoa(sp
->to
.sin_addr
));
718 ++client_packets_relayed
;
724 /* Strip any Relay Agent Information options from the DHCP packet
725 option buffer. If there is a circuit ID suboption, look up the
726 outgoing interface based upon it. */
729 strip_relay_agent_options(struct interface_info
*in
,
730 struct interface_info
**out
,
731 struct dhcp_packet
*packet
,
734 u_int8_t
*op
, *nextop
, *sp
, *max
;
735 int good_agent_option
= 0;
738 /* If we're not adding agent options to packets, we're not taking
740 if (!add_agent_options
)
743 /* If there's no cookie, it's a bootp packet, so we should just
744 forward it unchanged. */
745 if (memcmp(packet
->options
, DHCP_OPTIONS_COOKIE
, 4))
748 max
= ((u_int8_t
*)packet
) + length
;
749 sp
= op
= &packet
->options
[4];
753 /* Skip padding... */
761 /* If we see a message type, it's a DHCP packet. */
762 case DHO_DHCP_MESSAGE_TYPE
:
767 /* Quit immediately if we hit an End option. */
773 case DHO_DHCP_AGENT_OPTIONS
:
774 /* We shouldn't see a relay agent option in a
775 packet before we've seen the DHCP packet type,
776 but if we do, we have to leave it alone. */
780 /* Do not process an agent option if it exceeds the
781 * buffer. Fail this packet.
783 nextop
= op
+ op
[1] + 2;
787 status
= find_interface_by_agent_option(packet
,
790 if (status
== -1 && drop_agent_mismatches
)
793 good_agent_option
= 1;
798 /* Skip over other options. */
800 /* Fail if processing this option will exceed the
801 * buffer(op[1] is malformed).
803 nextop
= op
+ op
[1] + 2;
808 memmove(sp
, op
, op
[1] + 2);
819 /* If it's not a DHCP packet, we're not supposed to touch it. */
823 /* If none of the agent options we found matched, or if we didn't
824 find any agent options, count this packet as not having any
825 matching agent options, and if we're relying on agent options
826 to determine the outgoing interface, drop the packet. */
828 if (!good_agent_option
) {
829 ++missing_agent_option
;
830 if (drop_agent_mismatches
)
834 /* Adjust the length... */
836 length
= sp
-((u_int8_t
*)packet
);
838 /* Make sure the packet isn't short(this is unlikely,
840 if (length
< BOOTP_MIN_LEN
) {
841 memset(sp
, DHO_PAD
, BOOTP_MIN_LEN
- length
);
842 length
= BOOTP_MIN_LEN
;
849 /* Find an interface that matches the circuit ID specified in the
850 Relay Agent Information option. If one is found, store it through
851 the pointer given; otherwise, leave the existing pointer alone.
853 We actually deviate somewhat from the current specification here:
854 if the option buffer is corrupt, we suggest that the caller not
855 respond to this packet. If the circuit ID doesn't match any known
856 interface, we suggest that the caller to drop the packet. Only if
857 we find a circuit ID that matches an existing interface do we tell
858 the caller to go ahead and process the packet. */
861 find_interface_by_agent_option(struct dhcp_packet
*packet
,
862 struct interface_info
**out
,
863 u_int8_t
*buf
, int len
) {
865 u_int8_t
*circuit_id
= 0;
866 unsigned circuit_id_len
= 0;
867 struct interface_info
*ip
;
870 /* If the next agent option overflows the end of the
871 packet, the agent option buffer is corrupt. */
873 i
+ buf
[i
+ 1] + 2 > len
) {
874 ++corrupt_agent_options
;
878 /* Remember where the circuit ID is... */
880 circuit_id
= &buf
[i
+ 2];
881 circuit_id_len
= buf
[i
+ 1];
882 i
+= circuit_id_len
+ 2;
891 /* If there's no circuit ID, it's not really ours, tell the caller
894 ++missing_circuit_id
;
898 /* Scan the interface list looking for an interface whose
899 name matches the one specified in circuit_id. */
901 for (ip
= interfaces
; ip
; ip
= ip
->next
) {
902 if (ip
->circuit_id
&&
903 ip
->circuit_id_len
== circuit_id_len
&&
904 !memcmp(ip
->circuit_id
, circuit_id
, circuit_id_len
))
908 /* If we got a match, use it. */
914 /* If we didn't get a match, the circuit ID was bogus. */
920 * Examine a packet to see if it's a candidate to have a Relay
921 * Agent Information option tacked onto its tail. If it is, tack
925 add_relay_agent_options(struct interface_info
*ip
, struct dhcp_packet
*packet
,
926 unsigned length
, struct in_addr giaddr
) {
927 int is_dhcp
= 0, mms
;
929 u_int8_t
*op
, *nextop
, *sp
, *max
, *end_pad
= NULL
;
931 /* If we're not adding agent options to packets, we can skip
933 if (!add_agent_options
)
936 /* If there's no cookie, it's a bootp packet, so we should just
937 forward it unchanged. */
938 if (memcmp(packet
->options
, DHCP_OPTIONS_COOKIE
, 4))
941 max
= ((u_int8_t
*)packet
) + dhcp_max_agent_option_packet_length
;
943 /* Commence processing after the cookie. */
944 sp
= op
= &packet
->options
[4];
948 /* Skip padding... */
950 /* Remember the first pad byte so we can commandeer
953 * XXX: Is this really a good idea? Sure, we can
954 * seemingly reduce the packet while we're looking,
955 * but if the packet was signed by the client then
956 * this padding is part of the checksum(RFC3118),
957 * and its nonpresence would break authentication.
969 /* If we see a message type, it's a DHCP packet. */
970 case DHO_DHCP_MESSAGE_TYPE
:
975 * If there's a maximum message size option, we
976 * should pay attention to it
978 case DHO_DHCP_MAX_MESSAGE_SIZE
:
979 mms
= ntohs(*(op
+ 2));
980 if (mms
< dhcp_max_agent_option_packet_length
&&
982 max
= ((u_int8_t
*)packet
) + mms
;
985 /* Quit immediately if we hit an End option. */
989 case DHO_DHCP_AGENT_OPTIONS
:
990 /* We shouldn't see a relay agent option in a
991 packet before we've seen the DHCP packet type,
992 but if we do, we have to leave it alone. */
998 /* There's already a Relay Agent Information option
999 in this packet. How embarrassing. Decide what
1000 to do based on the mode the user specified. */
1002 switch(agent_relay_mode
) {
1003 case forward_and_append
:
1005 case forward_untouched
:
1009 case forward_and_replace
:
1014 /* Skip over the agent option and start copying
1015 if we aren't copying already. */
1020 /* Skip over other options. */
1022 /* Fail if processing this option will exceed the
1023 * buffer(op[1] is malformed).
1025 nextop
= op
+ op
[1] + 2;
1032 memmove(sp
, op
, op
[1] + 2);
1043 /* If it's not a DHCP packet, we're not supposed to touch it. */
1047 /* If the packet was padded out, we can store the agent option
1048 at the beginning of the padding. */
1050 if (end_pad
!= NULL
)
1054 /* Remember where the end of the packet was after parsing
1059 /* Sanity check. Had better not ever happen. */
1060 if ((ip
->circuit_id_len
> 255) ||(ip
->circuit_id_len
< 1))
1061 log_fatal("Circuit ID length %d out of range [1-255] on "
1062 "%s\n", ip
->circuit_id_len
, ip
->name
);
1063 optlen
= ip
->circuit_id_len
+ 2; /* RAI_CIRCUIT_ID + len */
1065 if (ip
->remote_id
) {
1066 if (ip
->remote_id_len
> 255 || ip
->remote_id_len
< 1)
1067 log_fatal("Remote ID length %d out of range [1-255] "
1068 "on %s\n", ip
->circuit_id_len
, ip
->name
);
1069 optlen
+= ip
->remote_id_len
+ 2; /* RAI_REMOTE_ID + len */
1072 /* We do not support relay option fragmenting(multiple options to
1073 * support an option data exceeding 255 bytes).
1075 if ((optlen
< 3) ||(optlen
> 255))
1076 log_fatal("Total agent option length(%u) out of range "
1077 "[3 - 255] on %s\n", optlen
, ip
->name
);
1080 * Is there room for the option, its code+len, and DHO_END?
1081 * If not, forward without adding the option.
1083 if (max
- sp
>= optlen
+ 3) {
1084 log_debug("Adding %d-byte relay agent option", optlen
+ 3);
1086 /* Okay, cons up *our* Relay Agent Information option. */
1087 *sp
++ = DHO_DHCP_AGENT_OPTIONS
;
1090 /* Copy in the circuit id... */
1091 *sp
++ = RAI_CIRCUIT_ID
;
1092 *sp
++ = ip
->circuit_id_len
;
1093 memcpy(sp
, ip
->circuit_id
, ip
->circuit_id_len
);
1094 sp
+= ip
->circuit_id_len
;
1096 /* Copy in remote ID... */
1097 if (ip
->remote_id
) {
1098 *sp
++ = RAI_REMOTE_ID
;
1099 *sp
++ = ip
->remote_id_len
;
1100 memcpy(sp
, ip
->remote_id
, ip
->remote_id_len
);
1101 sp
+= ip
->remote_id_len
;
1104 ++agent_option_errors
;
1105 log_error("No room in packet (used %d of %d) "
1106 "for %d-byte relay agent option: omitted",
1107 (int) (sp
- ((u_int8_t
*) packet
)),
1108 (int) (max
- ((u_int8_t
*) packet
)),
1113 * Deposit an END option unless the packet is full (shouldn't
1119 /* Recalculate total packet length. */
1120 length
= sp
-((u_int8_t
*)packet
);
1122 /* Make sure the packet isn't short(this is unlikely, but WTH) */
1123 if (length
< BOOTP_MIN_LEN
) {
1124 memset(sp
, DHO_PAD
, BOOTP_MIN_LEN
- length
);
1125 return (BOOTP_MIN_LEN
);
1133 * Parse a downstream argument: [address%]interface[#index].
1135 static struct stream_list
*
1136 parse_downstream(char *arg
) {
1137 struct stream_list
*dp
, *up
;
1138 struct interface_info
*ifp
= NULL
;
1139 char *ifname
, *addr
, *iid
;
1140 isc_result_t status
;
1142 if (!supports_multiple_interfaces(ifp
) &&
1143 (downstreams
!= NULL
))
1144 log_fatal("No support for multiple interfaces.");
1146 /* Decode the argument. */
1147 ifname
= strchr(arg
, '%');
1148 if (ifname
== NULL
) {
1155 iid
= strchr(ifname
, '#');
1159 if (strlen(ifname
) >= sizeof(ifp
->name
)) {
1160 log_error("Interface name '%s' too long", ifname
);
1164 /* Don't declare twice. */
1165 for (dp
= downstreams
; dp
; dp
= dp
->next
) {
1166 if (strcmp(ifname
, dp
->ifp
->name
) == 0)
1167 log_fatal("Down interface '%s' declared twice.",
1171 /* Share with up side? */
1172 for (up
= upstreams
; up
; up
= up
->next
) {
1173 if (strcmp(ifname
, up
->ifp
->name
) == 0) {
1174 log_info("Interface '%s' is both down and up.",
1181 /* New interface. */
1183 status
= interface_allocate(&ifp
, MDL
);
1184 if (status
!= ISC_R_SUCCESS
)
1185 log_fatal("%s: interface_allocate: %s",
1186 arg
, isc_result_totext(status
));
1187 strcpy(ifp
->name
, ifname
);
1189 interface_reference(&ifp
->next
, interfaces
, MDL
);
1190 interface_dereference(&interfaces
, MDL
);
1192 interface_reference(&interfaces
, ifp
, MDL
);
1193 ifp
->flags
|= INTERFACE_REQUESTED
| INTERFACE_DOWNSTREAM
;
1196 /* New downstream. */
1197 dp
= (struct stream_list
*) dmalloc(sizeof(*dp
), MDL
);
1199 log_fatal("No memory for downstream.");
1206 /* !addr case handled by setup. */
1207 if (addr
&& (inet_pton(AF_INET6
, addr
, &dp
->link
.sin6_addr
) <= 0))
1208 log_fatal("Bad link address '%s'", addr
);
1214 * Parse an upstream argument: [address]%interface.
1216 static struct stream_list
*
1217 parse_upstream(char *arg
) {
1218 struct stream_list
*up
, *dp
;
1219 struct interface_info
*ifp
= NULL
;
1220 char *ifname
, *addr
;
1221 isc_result_t status
;
1223 /* Decode the argument. */
1224 ifname
= strchr(arg
, '%');
1225 if (ifname
== NULL
) {
1227 addr
= All_DHCP_Servers
;
1232 if (strlen(ifname
) >= sizeof(ifp
->name
)) {
1233 log_fatal("Interface name '%s' too long", ifname
);
1236 /* Shared up interface? */
1237 for (up
= upstreams
; up
; up
= up
->next
) {
1238 if (strcmp(ifname
, up
->ifp
->name
) == 0) {
1243 for (dp
= downstreams
; dp
; dp
= dp
->next
) {
1244 if (strcmp(ifname
, dp
->ifp
->name
) == 0) {
1250 /* New interface. */
1252 status
= interface_allocate(&ifp
, MDL
);
1253 if (status
!= ISC_R_SUCCESS
)
1254 log_fatal("%s: interface_allocate: %s",
1255 arg
, isc_result_totext(status
));
1256 strcpy(ifp
->name
, ifname
);
1258 interface_reference(&ifp
->next
, interfaces
, MDL
);
1259 interface_dereference(&interfaces
, MDL
);
1261 interface_reference(&interfaces
, ifp
, MDL
);
1262 ifp
->flags
|= INTERFACE_REQUESTED
| INTERFACE_UPSTREAM
;
1266 up
= (struct stream_list
*) dmalloc(sizeof(*up
), MDL
);
1268 log_fatal("No memory for upstream.");
1272 if (inet_pton(AF_INET6
, addr
, &up
->link
.sin6_addr
) <= 0)
1273 log_fatal("Bad address %s", addr
);
1279 * Setup downstream interfaces.
1282 setup_streams(void) {
1283 struct stream_list
*dp
, *up
;
1285 isc_boolean_t link_is_set
;
1287 for (dp
= downstreams
; dp
; dp
= dp
->next
) {
1288 /* Check interface */
1289 if (dp
->ifp
->v6address_count
== 0)
1290 log_fatal("Interface '%s' has no IPv6 addresses.",
1293 /* Check/set link. */
1294 if (IN6_IS_ADDR_UNSPECIFIED(&dp
->link
.sin6_addr
))
1295 link_is_set
= ISC_FALSE
;
1297 link_is_set
= ISC_TRUE
;
1298 for (i
= 0; i
< dp
->ifp
->v6address_count
; i
++) {
1299 if (IN6_IS_ADDR_LINKLOCAL(&dp
->ifp
->v6addresses
[i
]))
1303 if (!memcmp(&dp
->ifp
->v6addresses
[i
],
1304 &dp
->link
.sin6_addr
,
1305 sizeof(dp
->link
.sin6_addr
)))
1308 if (i
== dp
->ifp
->v6address_count
)
1309 log_fatal("Interface %s does not have global IPv6 "
1310 "address assigned.", dp
->ifp
->name
);
1312 memcpy(&dp
->link
.sin6_addr
,
1313 &dp
->ifp
->v6addresses
[i
],
1314 sizeof(dp
->link
.sin6_addr
));
1316 /* Set interface-id. */
1318 dp
->id
= dp
->ifp
->index
;
1321 for (up
= upstreams
; up
; up
= up
->next
) {
1322 up
->link
.sin6_port
= local_port
;
1323 up
->link
.sin6_family
= AF_INET6
;
1325 up
->link
.sin6_len
= sizeof(up
->link
);
1328 if (up
->ifp
->v6address_count
== 0)
1329 log_fatal("Interface '%s' has no IPv6 addresses.",
1335 * Add DHCPv6 agent options here.
1337 static const int required_forw_opts
[] = {
1344 * Process a packet upwards, i.e., from client to server.
1347 process_up6(struct packet
*packet
, struct stream_list
*dp
) {
1348 char forw_data
[65535];
1350 struct dhcpv6_relay_packet
*relay
;
1351 struct option_state
*opts
;
1352 struct stream_list
*up
;
1354 /* Check if the message should be relayed to the server. */
1355 switch (packet
->dhcpv6_msg_type
) {
1356 case DHCPV6_SOLICIT
:
1357 case DHCPV6_REQUEST
:
1358 case DHCPV6_CONFIRM
:
1361 case DHCPV6_RELEASE
:
1362 case DHCPV6_DECLINE
:
1363 case DHCPV6_INFORMATION_REQUEST
:
1364 case DHCPV6_RELAY_FORW
:
1365 case DHCPV6_LEASEQUERY
:
1366 log_info("Relaying %s from %s port %d going up.",
1367 dhcpv6_type_names
[packet
->dhcpv6_msg_type
],
1368 piaddr(packet
->client_addr
),
1369 ntohs(packet
->client_port
));
1372 case DHCPV6_ADVERTISE
:
1374 case DHCPV6_RECONFIGURE
:
1375 case DHCPV6_RELAY_REPL
:
1376 case DHCPV6_LEASEQUERY_REPLY
:
1377 log_info("Discarding %s from %s port %d going up.",
1378 dhcpv6_type_names
[packet
->dhcpv6_msg_type
],
1379 piaddr(packet
->client_addr
),
1380 ntohs(packet
->client_port
));
1384 log_info("Unknown %d type from %s port %d going up.",
1385 packet
->dhcpv6_msg_type
,
1386 piaddr(packet
->client_addr
),
1387 ntohs(packet
->client_port
));
1391 /* Build the relay-forward header. */
1392 relay
= (struct dhcpv6_relay_packet
*) forw_data
;
1393 cursor
= offsetof(struct dhcpv6_relay_packet
, options
);
1394 relay
->msg_type
= DHCPV6_RELAY_FORW
;
1395 if (packet
->dhcpv6_msg_type
== DHCPV6_RELAY_FORW
) {
1396 if (packet
->dhcpv6_hop_count
>= max_hop_count
) {
1397 log_info("Hop count exceeded,");
1400 relay
->hop_count
= packet
->dhcpv6_hop_count
+ 1;
1402 memcpy(&relay
->link_address
, &dp
->link
.sin6_addr
, 16);
1404 /* On smart relay add: && !global. */
1405 if (!use_if_id
&& downstreams
->next
) {
1406 log_info("Shan't get back the interface.");
1409 memset(&relay
->link_address
, 0, 16);
1412 relay
->hop_count
= 0;
1415 memcpy(&relay
->link_address
, &dp
->link
.sin6_addr
, 16);
1417 memcpy(&relay
->peer_address
, packet
->client_addr
.iabuf
, 16);
1419 /* Get an option state. */
1421 if (!option_state_allocate(&opts
, MDL
)) {
1422 log_fatal("No memory for upwards options.");
1425 /* Add an interface-id (if used). */
1431 } else if (!downstreams
->next
) {
1432 if_id
= downstreams
->id
;
1434 log_info("Don't know the interface.");
1435 option_state_dereference(&opts
, MDL
);
1439 if (!save_option_buffer(&dhcpv6_universe
, opts
,
1440 NULL
, (unsigned char *) &if_id
,
1442 D6O_INTERFACE_ID
, 0)) {
1443 log_error("Can't save interface-id.");
1444 option_state_dereference(&opts
, MDL
);
1449 /* Add the relay-msg carrying the packet. */
1450 if (!save_option_buffer(&dhcpv6_universe
, opts
,
1451 NULL
, (unsigned char *) packet
->raw
,
1452 packet
->packet_length
,
1453 D6O_RELAY_MSG
, 0)) {
1454 log_error("Can't save relay-msg.");
1455 option_state_dereference(&opts
, MDL
);
1459 /* Finish the relay-forward message. */
1460 cursor
+= store_options6(forw_data
+ cursor
,
1461 sizeof(forw_data
) - cursor
,
1463 required_forw_opts
, NULL
);
1464 option_state_dereference(&opts
, MDL
);
1466 /* Send it to all upstreams. */
1467 for (up
= upstreams
; up
; up
= up
->next
) {
1468 send_packet6(up
->ifp
, (unsigned char *) forw_data
,
1469 (size_t) cursor
, &up
->link
);
1474 * Process a packet downwards, i.e., from server to client.
1477 process_down6(struct packet
*packet
) {
1478 struct stream_list
*dp
;
1479 struct option_cache
*oc
;
1480 struct data_string relay_msg
;
1481 const struct dhcpv6_packet
*msg
;
1482 struct data_string if_id
;
1483 struct sockaddr_in6 to
;
1486 /* The packet must be a relay-reply message. */
1487 if (packet
->dhcpv6_msg_type
!= DHCPV6_RELAY_REPL
) {
1488 if (packet
->dhcpv6_msg_type
< dhcpv6_type_name_max
)
1489 log_info("Discarding %s from %s port %d going down.",
1490 dhcpv6_type_names
[packet
->dhcpv6_msg_type
],
1491 piaddr(packet
->client_addr
),
1492 ntohs(packet
->client_port
));
1494 log_info("Unknown %d type from %s port %d going down.",
1495 packet
->dhcpv6_msg_type
,
1496 piaddr(packet
->client_addr
),
1497 ntohs(packet
->client_port
));
1502 memset(&relay_msg
, 0, sizeof(relay_msg
));
1503 memset(&if_id
, 0, sizeof(if_id
));
1504 memset(&to
, 0, sizeof(to
));
1505 to
.sin6_family
= AF_INET6
;
1507 to
.sin6_len
= sizeof(to
);
1509 to
.sin6_port
= remote_port
;
1512 /* Get the relay-msg option (carrying the message to relay). */
1513 oc
= lookup_option(&dhcpv6_universe
, packet
->options
, D6O_RELAY_MSG
);
1515 log_info("No relay-msg.");
1518 if (!evaluate_option_cache(&relay_msg
, packet
, NULL
, NULL
,
1519 packet
->options
, NULL
,
1520 &global_scope
, oc
, MDL
) ||
1521 (relay_msg
.len
< offsetof(struct dhcpv6_packet
, options
))) {
1522 log_error("Can't evaluate relay-msg.");
1525 msg
= (const struct dhcpv6_packet
*) relay_msg
.data
;
1527 /* Get the interface-id (if exists) and the downstream. */
1528 oc
= lookup_option(&dhcpv6_universe
, packet
->options
,
1533 if (!evaluate_option_cache(&if_id
, packet
, NULL
, NULL
,
1534 packet
->options
, NULL
,
1535 &global_scope
, oc
, MDL
) ||
1536 (if_id
.len
!= sizeof(int))) {
1537 log_info("Can't evaluate interface-id.");
1540 memcpy(&if_index
, if_id
.data
, sizeof(int));
1541 for (dp
= downstreams
; dp
; dp
= dp
->next
) {
1542 if (dp
->id
== if_index
)
1547 /* Require an interface-id. */
1548 log_info("No interface-id.");
1551 for (dp
= downstreams
; dp
; dp
= dp
->next
) {
1552 /* Get the first matching one. */
1553 if (!memcmp(&dp
->link
.sin6_addr
,
1554 &packet
->dhcpv6_link_address
,
1555 sizeof(struct in6_addr
)))
1559 /* Why bother when there is no choice. */
1560 if (!dp
&& downstreams
&& !downstreams
->next
)
1563 log_info("Can't find the down interface.");
1566 memcpy(peer
.iabuf
, &packet
->dhcpv6_peer_address
, peer
.len
);
1567 to
.sin6_addr
= packet
->dhcpv6_peer_address
;
1569 /* Check if we should relay the carried message. */
1570 switch (msg
->msg_type
) {
1571 /* Relay-Reply of for another relay, not a client. */
1572 case DHCPV6_RELAY_REPL
:
1573 to
.sin6_port
= local_port
;
1576 case DHCPV6_ADVERTISE
:
1578 case DHCPV6_RECONFIGURE
:
1579 case DHCPV6_RELAY_FORW
:
1580 case DHCPV6_LEASEQUERY_REPLY
:
1581 log_info("Relaying %s to %s port %d down.",
1582 dhcpv6_type_names
[msg
->msg_type
],
1584 ntohs(to
.sin6_port
));
1587 case DHCPV6_SOLICIT
:
1588 case DHCPV6_REQUEST
:
1589 case DHCPV6_CONFIRM
:
1592 case DHCPV6_RELEASE
:
1593 case DHCPV6_DECLINE
:
1594 case DHCPV6_INFORMATION_REQUEST
:
1595 case DHCPV6_LEASEQUERY
:
1596 log_info("Discarding %s to %s port %d down.",
1597 dhcpv6_type_names
[msg
->msg_type
],
1599 ntohs(to
.sin6_port
));
1603 log_info("Unknown %d type to %s port %d down.",
1606 ntohs(to
.sin6_port
));
1610 /* Send the message to the downstream. */
1611 send_packet6(dp
->ifp
, (unsigned char *) relay_msg
.data
,
1612 (size_t) relay_msg
.len
, &to
);
1615 if (relay_msg
.data
!= NULL
)
1616 data_string_forget(&relay_msg
, MDL
);
1617 if (if_id
.data
!= NULL
)
1618 data_string_forget(&if_id
, MDL
);
1622 * Called by the dispatch packet handler with a decoded packet.
1625 dhcpv6(struct packet
*packet
) {
1626 struct stream_list
*dp
;
1628 /* Try all relay-replies downwards. */
1629 if (packet
->dhcpv6_msg_type
== DHCPV6_RELAY_REPL
) {
1630 process_down6(packet
);
1633 /* Others are candidates to go up if they come from down. */
1634 for (dp
= downstreams
; dp
; dp
= dp
->next
) {
1635 if (packet
->interface
!= dp
->ifp
)
1637 process_up6(packet
, dp
);
1640 /* Relay-forward could work from an unknown interface. */
1641 if (packet
->dhcpv6_msg_type
== DHCPV6_RELAY_FORW
) {
1642 process_up6(packet
, NULL
);
1646 log_info("Can't process packet from interface '%s'.",
1647 packet
->interface
->name
);
1651 /* Stub routines needed for linking with DHCP libraries. */
1653 bootp(struct packet
*packet
) {
1658 dhcp(struct packet
*packet
) {
1663 classify(struct packet
*p
, struct class *c
) {
1668 check_collection(struct packet
*p
, struct lease
*l
, struct collection
*c
) {
1673 find_class(struct class **class, const char *c1
, const char *c2
, int i
) {
1674 return ISC_R_NOTFOUND
;
1678 parse_allow_deny(struct option_cache
**oc
, struct parse
*p
, int i
) {
1683 dhcp_set_control_state(control_object_state_t oldstate
,
1684 control_object_state_t newstate
) {
1685 return ISC_R_SUCCESS
;