3 DHCP/BOOTP Relay Agent. */
6 * Copyright (c) 2004-2006 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
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 * ``http://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''.
36 static char ocopyright
[] =
37 "$Id: dhcrelay.c,v 1.61 2007/05/19 18:47:15 dhankins Exp $ Copyright (c) 2004-2006 Internet Systems Consortium. All rights reserved.\n";
43 static void usage
PROTO ((void));
45 TIME default_lease_time
= 43200; /* 12 hours... */
46 TIME max_lease_time
= 86400; /* 24 hours... */
47 struct tree_cache
*global_options
[256];
49 /* Needed to prevent linking against conflex.c. */
55 const char *path_dhcrelay_pid
= _PATH_DHCRELAY_PID
;
57 int bogus_agent_drops
= 0; /* Packets dropped because agent option
58 field was specified and we're not relaying
59 packets that already have an agent option
61 int bogus_giaddr_drops
= 0; /* Packets sent to us to relay back to a
62 client, but with a bogus giaddr. */
63 int client_packets_relayed
= 0; /* Packets relayed from client to server. */
64 int server_packet_errors
= 0; /* Errors sending packets to servers. */
65 int server_packets_relayed
= 0; /* Packets relayed from server to client. */
66 int client_packet_errors
= 0; /* Errors sending packets to clients. */
68 int add_agent_options
= 0; /* If nonzero, add relay agent options. */
69 int drop_agent_mismatches
= 0; /* If nonzero, drop server replies that
70 don't have matching circuit-id's. */
71 int corrupt_agent_options
= 0; /* Number of packets dropped because
72 relay agent information option was bad. */
73 int missing_agent_option
= 0; /* Number of packets dropped because no
74 RAI option matching our ID was found. */
75 int bad_circuit_id
= 0; /* Circuit ID option in matching RAI option
76 did not match any known circuit ID. */
77 int missing_circuit_id
= 0; /* Circuit ID option in matching RAI option
79 int max_hop_count
= 10; /* Maximum hop count */
82 /* Maximum size of a packet with agent options added. */
83 int dhcp_max_agent_option_packet_length
= 576;
85 /* What to do about packets we're asked to relay that
86 already have a relay option: */
87 enum { forward_and_append
, /* Forward and append our own relay option. */
88 forward_and_replace
, /* Forward, but replace theirs with ours. */
89 forward_untouched
, /* Forward without changes. */
90 discard
} agent_relay_mode
= forward_and_replace
;
93 u_int16_t remote_port
;
95 /* Relay agent server list. */
97 struct server_list
*next
;
98 struct sockaddr_in to
;
101 static char copyright
[] = "Copyright 2004-2006 Internet Systems Consortium.";
102 static char arr
[] = "All rights reserved.";
103 static char message
[] = "Internet Systems Consortium DHCP Relay Agent";
104 static char url
[] = "For info, please visit http://www.isc.org/sw/dhcp/";
107 main(int argc
, char **argv
) {
111 struct server_list
*sp
= NULL
;
117 /* Make sure that file descriptors 0 (stdin), 1, (stdout), and
118 2 (stderr) are open. To do this, we assume that when we
119 open a file the lowest available file decriptor is used. */
120 fd
= open("/dev/null", O_RDWR
);
122 fd
= open("/dev/null", O_RDWR
);
124 fd
= open("/dev/null", O_RDWR
);
126 log_perror
= 0; /* No sense logging to /dev/null. */
130 openlog ("dhcrelay", LOG_NDELAY
, LOG_DAEMON
);
133 setlogmask (LOG_UPTO (LOG_INFO
));
136 /* Set up the OMAPI. */
137 status
= omapi_init ();
138 if (status
!= ISC_R_SUCCESS
)
139 log_fatal ("Can't initialize OMAPI: %s",
140 isc_result_totext (status
));
142 /* Set up the OMAPI wrappers for the interface object. */
145 for (i
= 1; i
< argc
; i
++) {
146 if (!strcmp (argv
[i
], "-p")) {
149 local_port
= htons (atoi (argv
[i
]));
150 log_debug ("binding to user-specified port %d",
152 } else if (!strcmp (argv
[i
], "-d")) {
154 } else if (!strcmp (argv
[i
], "-i")) {
155 struct interface_info
*tmp
=
156 (struct interface_info
*)0;
157 status
= interface_allocate (&tmp
, MDL
);
158 if (status
!= ISC_R_SUCCESS
)
159 log_fatal ("%s: interface_allocate: %s",
161 isc_result_totext (status
));
165 strcpy (tmp
-> name
, argv
[i
]);
166 interface_snorf (tmp
, INTERFACE_REQUESTED
);
167 interface_dereference (&tmp
, MDL
);
168 } else if (!strcmp (argv
[i
], "-q")) {
170 quiet_interface_discovery
= 1;
171 } else if (!strcmp (argv
[i
], "-a")) {
172 add_agent_options
= 1;
173 } else if (!strcmp (argv
[i
], "-c")) {
177 hcount
= atoi(argv
[i
]);
179 max_hop_count
= hcount
;
182 } else if (!strcmp (argv
[i
], "-A")) {
185 dhcp_max_agent_option_packet_length
= atoi (argv
[i
]);
186 } else if (!strcmp (argv
[i
], "-m")) {
189 if (!strcasecmp (argv
[i
], "append")) {
190 agent_relay_mode
= forward_and_append
;
191 } else if (!strcasecmp (argv
[i
], "replace")) {
192 agent_relay_mode
= forward_and_replace
;
193 } else if (!strcasecmp (argv
[i
], "forward")) {
194 agent_relay_mode
= forward_untouched
;
195 } else if (!strcasecmp (argv
[i
], "discard")) {
196 agent_relay_mode
= discard
;
199 } else if (!strcmp (argv
[i
], "-D")) {
200 drop_agent_mismatches
= 1;
201 } else if (argv
[i
][0] == '-') {
203 } else if (!strcmp (argv
[i
], "--version")) {
204 log_info ("isc-dhcrelay-%s", PACKAGE_VERSION
);
208 struct in_addr ia
, *iap
= (struct in_addr
*)0;
209 if (inet_aton (argv
[i
], &ia
)) {
212 he
= gethostbyname (argv
[i
]);
214 log_error ("%s: host unknown",
217 iap
= ((struct in_addr
*)
218 he
-> h_addr_list
[0]);
222 sp
= ((struct server_list
*)
223 dmalloc (sizeof *sp
, MDL
));
225 log_fatal ("no memory for server.\n");
226 sp
-> next
= servers
;
228 memcpy (&sp
-> to
.sin_addr
,
234 if ((s
= getenv ("PATH_DHCRELAY_PID"))) {
235 path_dhcrelay_pid
= s
;
239 log_info ("%s %s", message
, PACKAGE_VERSION
);
240 log_info (copyright
);
248 /* Default to the DHCP/BOOTP port. */
250 ent
= getservbyname ("dhcps", "udp");
252 local_port
= htons (67);
254 local_port
= ent
-> s_port
;
257 remote_port
= htons (ntohs (local_port
) + 1);
259 /* We need at least one server. */
264 /* Set up the server sockaddrs. */
265 for (sp
= servers
; sp
; sp
= sp
-> next
) {
266 sp
-> to
.sin_port
= local_port
;
267 sp
-> to
.sin_family
= AF_INET
;
269 sp
-> to
.sin_len
= sizeof sp
-> to
;
273 /* Get the current time... */
276 /* Discover all the network interfaces. */
277 discover_interfaces (DISCOVER_RELAY
);
279 /* Set up the bootp packet handler... */
280 bootp_packet_handler
= relay
;
282 /* Become a daemon... */
290 if ((pid
= fork()) < 0)
291 log_fatal ("can't fork daemon: %m");
295 pfdesc
= open (path_dhcrelay_pid
,
296 O_CREAT
| O_TRUNC
| O_WRONLY
, 0644);
299 log_error ("Can't create %s: %m", path_dhcrelay_pid
);
301 pf
= fdopen (pfdesc
, "w");
303 log_error ("Can't fdopen %s: %m",
306 fprintf (pf
, "%ld\n", (long)getpid ());
317 /* Start dispatching packets and timeouts... */
324 void relay (ip
, packet
, length
, from_port
, from
, hfrom
)
325 struct interface_info
*ip
;
326 struct dhcp_packet
*packet
;
328 unsigned int from_port
;
330 struct hardware
*hfrom
;
332 struct server_list
*sp
;
333 struct sockaddr_in to
;
334 struct interface_info
*out
;
335 struct hardware hto
, *htop
;
337 if (packet
-> hlen
> sizeof packet
-> chaddr
) {
338 log_info ("Discarding packet with invalid hlen.");
342 /* Find the interface that corresponds to the giaddr
344 if (packet
-> giaddr
.s_addr
) {
345 for (out
= interfaces
; out
; out
= out
-> next
) {
348 for (i
= 0 ; i
< out
->address_count
; i
++ ) {
349 if (out
->addresses
[i
].s_addr
==
350 packet
->giaddr
.s_addr
)
359 out
= (struct interface_info
*)0;
362 /* If it's a bootreply, forward it to the client. */
363 if (packet
-> op
== BOOTREPLY
) {
364 if (!(packet
-> flags
& htons (BOOTP_BROADCAST
)) &&
365 can_unicast_without_arp (out
)) {
366 to
.sin_addr
= packet
-> yiaddr
;
367 to
.sin_port
= remote_port
;
369 /* and hardware address is not broadcast */
372 to
.sin_addr
.s_addr
= htonl (INADDR_BROADCAST
);
373 to
.sin_port
= remote_port
;
375 /* hardware address is broadcast */
378 to
.sin_family
= AF_INET
;
380 to
.sin_len
= sizeof to
;
383 memcpy (&hto
.hbuf
[1], packet
-> chaddr
, packet
-> hlen
);
384 hto
.hbuf
[0] = packet
-> htype
;
385 hto
.hlen
= packet
-> hlen
+ 1;
387 /* Wipe out the agent relay options and, if possible, figure
388 out which interface to use based on the contents of the
389 option that we put on the request to which the server is
392 strip_relay_agent_options (ip
, &out
, packet
, length
)))
396 log_error ("packet to bogus giaddr %s.\n",
397 inet_ntoa (packet
-> giaddr
));
398 ++bogus_giaddr_drops
;
402 if (send_packet(out
, NULL
, packet
, length
, out
->addresses
[0],
404 ++server_packet_errors
;
406 log_debug ("forwarded BOOTREPLY for %s to %s",
407 print_hw_addr (packet
-> htype
, packet
-> hlen
,
409 inet_ntoa (to
.sin_addr
));
411 ++server_packets_relayed
;
416 /* If giaddr matches one of our addresses, ignore the packet -
421 /* Add relay agent options if indicated. If something goes wrong,
423 if (!(length
= add_relay_agent_options(ip
, packet
, length
,
427 /* If giaddr is not already set, Set it so the server can
428 figure out what net it's from and so that we can later
429 forward the response to the correct net. If it's already
430 set, the response will be sent directly to the relay agent
431 that set giaddr, so we won't see it. */
432 if (!packet
-> giaddr
.s_addr
)
433 packet
->giaddr
= ip
->addresses
[0];
434 if (packet
-> hops
< max_hop_count
)
435 packet
-> hops
= packet
-> hops
+ 1;
439 /* Otherwise, it's a BOOTREQUEST, so forward it to all the
441 for (sp
= servers
; sp
; sp
= sp
-> next
) {
442 if (send_packet((fallback_interface
443 ? fallback_interface
: interfaces
),
444 NULL
, packet
, length
, ip
->addresses
[0],
445 &sp
->to
, NULL
) < 0) {
446 ++client_packet_errors
;
448 log_debug ("forwarded BOOTREQUEST for %s to %s",
449 print_hw_addr (packet
-> htype
, packet
-> hlen
,
451 inet_ntoa (sp
-> to
.sin_addr
));
452 ++client_packets_relayed
;
460 log_fatal ("Usage: dhcrelay [-p <port>] [-d] [-D] [-i %s%s%s%s",
461 "interface] [-q] [-a]\n ",
462 "[-c count] [-A length] ",
463 "[-m append|replace|forward|discard]\n",
464 " [server1 [... serverN]]");
467 int write_lease (lease
)
473 int write_host (host
)
474 struct host_decl
*host
;
485 struct packet
*packet
;
490 struct packet
*packet
;
496 dhcpv6(struct packet
*packet
) {
497 /* XXX: should we warn or something here? */
501 int find_subnet (struct subnet
**sp
,
502 struct iaddr addr
, const char *file
, int line
)
508 int check_collection (struct packet
*p
, struct lease
*l
,
509 struct collection
*c
)
514 void classify (struct packet
*p
, struct class *c
)
518 isc_result_t
find_class (struct class **class, const char *c1
,
519 const char *c2
, int i
)
521 return ISC_R_NOTFOUND
;
524 int parse_allow_deny (struct option_cache
**oc
, struct parse
*p
, int i
)
529 /* As a wise man once said in dhcpctl/omshell.c: */
531 isc_result_t
dhcp_set_control_state (control_object_state_t oldstate
,
532 control_object_state_t newstate
)
534 return ISC_R_SUCCESS
;
539 /* Strip any Relay Agent Information options from the DHCP packet
540 option buffer. If there is a circuit ID suboption, look up the
541 outgoing interface based upon it. */
543 int strip_relay_agent_options (in
, out
, packet
, length
)
544 struct interface_info
*in
, **out
;
545 struct dhcp_packet
*packet
;
549 u_int8_t
*op
, *nextop
, *sp
, *max
;
550 int good_agent_option
= 0;
553 /* If we're not adding agent options to packets, we're not taking
555 if (!add_agent_options
)
558 /* If there's no cookie, it's a bootp packet, so we should just
559 forward it unchanged. */
560 if (memcmp (packet
-> options
, DHCP_OPTIONS_COOKIE
, 4))
563 max
= ((u_int8_t
*)packet
) + length
;
564 sp
= op
= &packet
-> options
[4];
568 /* Skip padding... */
576 /* If we see a message type, it's a DHCP packet. */
577 case DHO_DHCP_MESSAGE_TYPE
:
582 /* Quit immediately if we hit an End option. */
588 case DHO_DHCP_AGENT_OPTIONS
:
589 /* We shouldn't see a relay agent option in a
590 packet before we've seen the DHCP packet type,
591 but if we do, we have to leave it alone. */
595 /* Do not process an agent option if it exceeds the
596 * buffer. Fail this packet.
598 nextop
= op
+ op
[1] + 2;
602 status
= find_interface_by_agent_option (packet
,
605 if (status
== -1 && drop_agent_mismatches
)
608 good_agent_option
= 1;
613 /* Skip over other options. */
615 /* Fail if processing this option will exceed the
616 * buffer (op[1] is malformed).
618 nextop
= op
+ op
[1] + 2;
623 memmove(sp
, op
, op
[1] + 2);
634 /* If it's not a DHCP packet, we're not supposed to touch it. */
638 /* If none of the agent options we found matched, or if we didn't
639 find any agent options, count this packet as not having any
640 matching agent options, and if we're relying on agent options
641 to determine the outgoing interface, drop the packet. */
643 if (!good_agent_option
) {
644 ++missing_agent_option
;
645 if (drop_agent_mismatches
)
649 /* Adjust the length... */
651 length
= sp
- ((u_int8_t
*)packet
);
653 /* Make sure the packet isn't short (this is unlikely,
655 if (length
< BOOTP_MIN_LEN
) {
656 memset (sp
, DHO_PAD
, BOOTP_MIN_LEN
- length
);
657 length
= BOOTP_MIN_LEN
;
664 /* Find an interface that matches the circuit ID specified in the
665 Relay Agent Information option. If one is found, store it through
666 the pointer given; otherwise, leave the existing pointer alone.
668 We actually deviate somewhat from the current specification here:
669 if the option buffer is corrupt, we suggest that the caller not
670 respond to this packet. If the circuit ID doesn't match any known
671 interface, we suggest that the caller to drop the packet. Only if
672 we find a circuit ID that matches an existing interface do we tell
673 the caller to go ahead and process the packet. */
675 int find_interface_by_agent_option (packet
, out
, buf
, len
)
676 struct dhcp_packet
*packet
;
677 struct interface_info
**out
;
682 u_int8_t
*circuit_id
= 0;
683 unsigned circuit_id_len
= 0;
684 struct interface_info
*ip
;
687 /* If the next agent option overflows the end of the
688 packet, the agent option buffer is corrupt. */
690 i
+ buf
[i
+ 1] + 2 > len
) {
691 ++corrupt_agent_options
;
695 /* Remember where the circuit ID is... */
697 circuit_id
= &buf
[i
+ 2];
698 circuit_id_len
= buf
[i
+ 1];
699 i
+= circuit_id_len
+ 2;
703 i
+= buf
[i
+ 1] + 2;
708 /* If there's no circuit ID, it's not really ours, tell the caller
711 ++missing_circuit_id
;
715 /* Scan the interface list looking for an interface whose
716 name matches the one specified in circuit_id. */
718 for (ip
= interfaces
; ip
; ip
= ip
-> next
) {
719 if (ip
-> circuit_id
&&
720 ip
-> circuit_id_len
== circuit_id_len
&&
721 !memcmp (ip
-> circuit_id
, circuit_id
, circuit_id_len
))
725 /* If we got a match, use it. */
731 /* If we didn't get a match, the circuit ID was bogus. */
736 /* Examine a packet to see if it's a candidate to have a Relay
737 Agent Information option tacked onto its tail. If it is, tack
740 int add_relay_agent_options (ip
, packet
, length
, giaddr
)
741 struct interface_info
*ip
;
742 struct dhcp_packet
*packet
;
744 struct in_addr giaddr
;
746 int is_dhcp
= 0, agent_options_present
= 0;
748 u_int8_t
*op
, *nextop
, *sp
, *max
, *end_pad
= NULL
;
750 /* If we're not adding agent options to packets, we can skip
752 if (!add_agent_options
)
755 /* If there's no cookie, it's a bootp packet, so we should just
756 forward it unchanged. */
757 if (memcmp(packet
->options
, DHCP_OPTIONS_COOKIE
, 4))
760 max
= ((u_int8_t
*)packet
) + length
;
762 /* Commence processing after the cookie. */
763 sp
= op
= &packet
->options
[4];
767 /* Skip padding... */
769 /* Remember the first pad byte so we can commandeer
772 * XXX: Is this really a good idea? Sure, we can
773 * seemingly reduce the packet while we're looking,
774 * but if the packet was signed by the client then
775 * this padding is part of the checksum (RFC3118),
776 * and its nonpresence would break authentication.
788 /* If we see a message type, it's a DHCP packet. */
789 case DHO_DHCP_MESSAGE_TYPE
:
794 /* Quit immediately if we hit an End option. */
798 case DHO_DHCP_AGENT_OPTIONS
:
799 /* We shouldn't see a relay agent option in a
800 packet before we've seen the DHCP packet type,
801 but if we do, we have to leave it alone. */
807 /* There's already a Relay Agent Information option
808 in this packet. How embarrassing. Decide what
809 to do based on the mode the user specified. */
811 switch (agent_relay_mode
) {
812 case forward_and_append
:
814 case forward_untouched
:
818 case forward_and_replace
:
823 /* Skip over the agent option and start copying
824 if we aren't copying already. */
829 /* Skip over other options. */
831 /* Fail if processing this option will exceed the
832 * buffer (op[1] is malformed).
834 nextop
= op
+ op
[1] + 2;
841 memmove(sp
, op
, op
[1] + 2);
852 /* If it's not a DHCP packet, we're not supposed to touch it. */
856 /* If the packet was padded out, we can store the agent option
857 at the beginning of the padding. */
862 /* Remember where the end of the packet was after parsing
866 /* Sanity check. Had better not ever happen. */
867 if ((ip
->circuit_id_len
> 255) || (ip
->circuit_id_len
< 1))
868 log_fatal("circuit id length %d out of range [1-255] on "
869 "%s\n", ip
->circuit_id_len
, ip
->name
);
870 optlen
= ip
->circuit_id_len
+ 2; /* RAI_CIRCUIT_ID + len */
873 if (ip
->remote_id_len
> 255 || ip
->remote_id_len
< 1)
874 log_fatal("remote id length %d out of range [1-255] "
875 "on %s\n", ip
->circuit_id_len
, ip
->name
);
876 optlen
+= ip
->remote_id_len
+ 2; /* RAI_REMOTE_ID + len */
879 /* We do not support relay option fragmenting (multiple options to
880 * support an option data exceeding 255 bytes).
882 if ((optlen
< 3) || (optlen
> 255))
883 log_fatal ("total agent option length (%u) out of range "
884 "[3 - 255] on %s\n", optlen
, ip
->name
);
886 /* Is there room for the option, its code+len, and DHO_END? */
887 if ((sp
> max
) || (max
- sp
< optlen
+ 3))
890 /* Okay, cons up *our* Relay Agent Information option. */
891 *sp
++ = DHO_DHCP_AGENT_OPTIONS
;
894 /* Copy in the circuit id... */
895 *sp
++ = RAI_CIRCUIT_ID
;
896 *sp
++ = ip
->circuit_id_len
;
897 memcpy(sp
, ip
->circuit_id
, ip
->circuit_id_len
);
898 sp
+= ip
->circuit_id_len
;
900 /* Copy in remote ID... */
902 *sp
++ = RAI_REMOTE_ID
;
903 *sp
++ = ip
->remote_id_len
;
904 memcpy(sp
, ip
->remote_id
, ip
->remote_id_len
);
905 sp
+= ip
->remote_id_len
;
908 /* Deposit an END option. */
911 /* Recalculate total packet length. */
912 length
= sp
- ((u_int8_t
*)packet
);
914 /* Make sure the packet isn't short (this is unlikely, but WTH) */
915 if (length
< BOOTP_MIN_LEN
) {
916 memset(sp
, DHO_PAD
, BOOTP_MIN_LEN
- length
);
917 return BOOTP_MIN_LEN
;