3 DHCP/BOOTP Relay Agent. */
6 * Copyright (c) 2004-2005 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$ Copyright (c) 2004-2005 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
;
96 struct server_list
*next
;
97 struct sockaddr_in to
;
100 static char copyright
[] = "Copyright 2004-2005 Internet Systems Consortium.";
101 static char arr
[] = "All rights reserved.";
102 static char message
[] = "Internet Systems Consortium DHCP Relay Agent";
103 static char url
[] = "For info, please visit http://www.isc.org/sw/dhcp/";
105 int main (argc
, argv
, envp
)
111 struct server_list
*sp
= (struct server_list
*)0;
117 /* Make sure we have stdin, stdout and stderr. */
121 me
= (struct Process
*)FindTask(NULL
);
125 i
= open ("/dev/null", O_RDWR
);
127 i
= open ("/dev/null", O_RDWR
);
129 i
= open ("/dev/null", O_RDWR
);
130 log_perror
= 0; /* No sense logging to /dev/null. */
135 openlog ("dhcrelay", LOG_NDELAY
);
136 log_priority
= LOG_DAEMON
;
138 openlog ("dhcrelay", LOG_NDELAY
, LOG_DAEMON
);
141 #if !(defined (DEBUG) || defined (SYSLOG_4_2))
142 setlogmask (LOG_UPTO (LOG_INFO
));
145 /* Set up the OMAPI. */
146 status
= omapi_init ();
147 if (status
!= ISC_R_SUCCESS
)
148 log_fatal ("Can't initialize OMAPI: %s",
149 isc_result_totext (status
));
151 /* Set up the OMAPI wrappers for the interface object. */
154 for (i
= 1; i
< argc
; i
++) {
155 if (!strcmp (argv
[i
], "-p")) {
158 local_port
= htons (atoi (argv
[i
]));
159 log_debug ("binding to user-specified port %d",
161 } else if (!strcmp (argv
[i
], "-d")) {
163 } else if (!strcmp (argv
[i
], "-i")) {
164 struct interface_info
*tmp
=
165 (struct interface_info
*)0;
166 status
= interface_allocate (&tmp
, MDL
);
167 if (status
!= ISC_R_SUCCESS
)
168 log_fatal ("%s: interface_allocate: %s",
170 isc_result_totext (status
));
174 strcpy (tmp
-> name
, argv
[i
]);
175 interface_snorf (tmp
, INTERFACE_REQUESTED
);
176 interface_dereference (&tmp
, MDL
);
177 } else if (!strcmp (argv
[i
], "-q")) {
179 quiet_interface_discovery
= 1;
180 } else if (!strcmp (argv
[i
], "-a")) {
181 add_agent_options
= 1;
182 } else if (!strcmp (argv
[i
], "-c")) {
186 hcount
= atoi(argv
[i
]);
188 max_hop_count
= hcount
;
191 } else if (!strcmp (argv
[i
], "-A")) {
194 dhcp_max_agent_option_packet_length
= atoi (argv
[i
]);
195 } else if (!strcmp (argv
[i
], "-m")) {
198 if (!strcasecmp (argv
[i
], "append")) {
199 agent_relay_mode
= forward_and_append
;
200 } else if (!strcasecmp (argv
[i
], "replace")) {
201 agent_relay_mode
= forward_and_replace
;
202 } else if (!strcasecmp (argv
[i
], "forward")) {
203 agent_relay_mode
= forward_untouched
;
204 } else if (!strcasecmp (argv
[i
], "discard")) {
205 agent_relay_mode
= discard
;
208 } else if (!strcmp (argv
[i
], "-D")) {
209 drop_agent_mismatches
= 1;
210 } else if (argv
[i
][0] == '-') {
212 } else if (!strcmp (argv
[i
], "--version")) {
213 log_info ("isc-dhcrelay-%s", DHCP_VERSION
);
217 struct in_addr ia
, *iap
= (struct in_addr
*)0;
218 if (inet_aton (argv
[i
], &ia
)) {
221 he
= gethostbyname (argv
[i
]);
223 log_error ("%s: host unknown",
226 iap
= ((struct in_addr
*)
227 he
-> h_addr_list
[0]);
231 sp
= ((struct server_list
*)
232 dmalloc (sizeof *sp
, MDL
));
234 log_fatal ("no memory for server.\n");
235 sp
-> next
= servers
;
237 memcpy (&sp
-> to
.sin_addr
,
243 if ((s
= getenv ("PATH_DHCRELAY_PID"))) {
244 path_dhcrelay_pid
= s
;
248 log_info ("%s %s", message
, DHCP_VERSION
);
249 log_info (copyright
);
257 /* Default to the DHCP/BOOTP port. */
259 ent
= getservbyname ("dhcps", "udp");
261 local_port
= htons (67);
263 local_port
= ent
-> s_port
;
266 remote_port
= htons (ntohs (local_port
) + 1);
268 /* We need at least one server. */
273 /* Set up the server sockaddrs. */
274 for (sp
= servers
; sp
; sp
= sp
-> next
) {
275 sp
-> to
.sin_port
= local_port
;
276 sp
-> to
.sin_family
= AF_INET
;
278 sp
-> to
.sin_len
= sizeof sp
-> to
;
282 /* Get the current time... */
283 GET_TIME (&cur_time
);
285 /* Discover all the network interfaces. */
286 discover_interfaces (DISCOVER_RELAY
);
288 /* Set up the bootp packet handler... */
289 bootp_packet_handler
= relay
;
291 /* Become a daemon... */
300 if ((pid
= fork()) < 0)
301 log_fatal ("can't fork daemon: %m");
305 pfdesc
= open (path_dhcrelay_pid
,
306 O_CREAT
| O_TRUNC
| O_WRONLY
, 0644);
309 log_error ("Can't create %s: %m", path_dhcrelay_pid
);
311 pf
= fdopen (pfdesc
, "w");
313 log_error ("Can't fdopen %s: %m",
316 fprintf (pf
, "%ld\n", (long)getpid ());
327 /* Start dispatching packets and timeouts... */
334 void relay (ip
, packet
, length
, from_port
, from
, hfrom
)
335 struct interface_info
*ip
;
336 struct dhcp_packet
*packet
;
338 unsigned int from_port
;
340 struct hardware
*hfrom
;
342 struct server_list
*sp
;
343 struct sockaddr_in to
;
344 struct interface_info
*out
;
345 struct hardware hto
, *htop
;
347 if (packet
-> hlen
> sizeof packet
-> chaddr
) {
348 log_info ("Discarding packet with invalid hlen.");
352 /* Find the interface that corresponds to the giaddr
354 if (packet
-> giaddr
.s_addr
) {
355 for (out
= interfaces
; out
; out
= out
-> next
) {
356 if (!memcmp (&out
-> primary_address
,
358 sizeof packet
-> giaddr
))
362 out
= (struct interface_info
*)0;
365 /* If it's a bootreply, forward it to the client. */
366 if (packet
-> op
== BOOTREPLY
) {
367 if (!(packet
-> flags
& htons (BOOTP_BROADCAST
)) &&
368 can_unicast_without_arp (out
)) {
369 to
.sin_addr
= packet
-> yiaddr
;
370 to
.sin_port
= remote_port
;
372 /* and hardware address is not broadcast */
375 to
.sin_addr
.s_addr
= htonl (INADDR_BROADCAST
);
376 to
.sin_port
= remote_port
;
378 /* hardware address is broadcast */
381 to
.sin_family
= AF_INET
;
383 to
.sin_len
= sizeof to
;
386 memcpy (&hto
.hbuf
[1], packet
-> chaddr
, packet
-> hlen
);
387 hto
.hbuf
[0] = packet
-> htype
;
388 hto
.hlen
= packet
-> hlen
+ 1;
390 /* Wipe out the agent relay options and, if possible, figure
391 out which interface to use based on the contents of the
392 option that we put on the request to which the server is
395 strip_relay_agent_options (ip
, &out
, packet
, length
)))
399 log_error ("packet to bogus giaddr %s.\n",
400 inet_ntoa (packet
-> giaddr
));
401 ++bogus_giaddr_drops
;
405 if (send_packet (out
,
407 packet
, length
, out
-> primary_address
,
409 ++server_packet_errors
;
411 log_debug ("forwarded BOOTREPLY for %s to %s",
412 print_hw_addr (packet
-> htype
, packet
-> hlen
,
414 inet_ntoa (to
.sin_addr
));
416 ++server_packets_relayed
;
421 /* If giaddr matches one of our addresses, ignore the packet -
426 /* Add relay agent options if indicated. If something goes wrong,
428 if (!(length
= add_relay_agent_options (ip
, packet
, length
,
429 ip
-> primary_address
)))
432 /* If giaddr is not already set, Set it so the server can
433 figure out what net it's from and so that we can later
434 forward the response to the correct net. If it's already
435 set, the response will be sent directly to the relay agent
436 that set giaddr, so we won't see it. */
437 if (!packet
-> giaddr
.s_addr
)
438 packet
-> giaddr
= ip
-> primary_address
;
439 if (packet
-> hops
< max_hop_count
)
440 packet
-> hops
= packet
-> hops
+ 1;
444 /* Otherwise, it's a BOOTREQUEST, so forward it to all the
446 for (sp
= servers
; sp
; sp
= sp
-> next
) {
447 if (send_packet ((fallback_interface
448 ? fallback_interface
: interfaces
),
450 packet
, length
, ip
-> primary_address
,
451 &sp
-> to
, (struct hardware
*)0) < 0) {
452 ++client_packet_errors
;
454 log_debug ("forwarded BOOTREQUEST for %s to %s",
455 print_hw_addr (packet
-> htype
, packet
-> hlen
,
457 inet_ntoa (sp
-> to
.sin_addr
));
458 ++client_packets_relayed
;
466 log_fatal ("Usage: dhcrelay [-p <port>] [-d] [-D] [-i %s%s%s%s",
467 "interface] [-q] [-a]\n ",
468 "[-c count] [-A length] ",
469 "[-m append|replace|forward|discard]\n",
470 " [server1 [... serverN]]");
473 int write_lease (lease
)
479 int write_host (host
)
480 struct host_decl
*host
;
491 struct packet
*packet
;
496 struct packet
*packet
;
500 int find_subnet (struct subnet
**sp
,
501 struct iaddr addr
, const char *file
, int line
)
507 int check_collection (struct packet
*p
, struct lease
*l
,
508 struct collection
*c
)
513 void classify (struct packet
*p
, struct class *c
)
517 isc_result_t
find_class (struct class **class, const char *c1
,
518 const char *c2
, int i
)
520 return ISC_R_NOTFOUND
;
523 int parse_allow_deny (struct option_cache
**oc
, struct parse
*p
, int i
)
528 /* As a wise man once said in dhcpctl/omshell.c: */
530 isc_result_t
dhcp_set_control_state (control_object_state_t oldstate
,
531 control_object_state_t newstate
)
533 return ISC_R_SUCCESS
;
538 /* Strip any Relay Agent Information options from the DHCP packet
539 option buffer. If there is a circuit ID suboption, look up the
540 outgoing interface based upon it. */
542 int strip_relay_agent_options (in
, out
, packet
, length
)
543 struct interface_info
*in
, **out
;
544 struct dhcp_packet
*packet
;
548 u_int8_t
*op
, *sp
, *max
;
549 int good_agent_option
= 0;
552 /* If we're not adding agent options to packets, we're not taking
554 if (!add_agent_options
)
557 /* If there's no cookie, it's a bootp packet, so we should just
558 forward it unchanged. */
559 if (memcmp (packet
-> options
, DHCP_OPTIONS_COOKIE
, 4))
562 max
= ((u_int8_t
*)packet
) + length
;
563 sp
= op
= &packet
-> options
[4];
567 /* Skip padding... */
575 /* If we see a message type, it's a DHCP packet. */
576 case DHO_DHCP_MESSAGE_TYPE
:
581 /* Quit immediately if we hit an End option. */
587 case DHO_DHCP_AGENT_OPTIONS
:
588 /* We shouldn't see a relay agent option in a
589 packet before we've seen the DHCP packet type,
590 but if we do, we have to leave it alone. */
594 status
= find_interface_by_agent_option (packet
,
597 if (status
== -1 && drop_agent_mismatches
)
600 good_agent_option
= 1;
605 /* Skip over other options. */
608 memcpy (sp
, op
, (unsigned)(op
[1] + 2));
616 /* If it's not a DHCP packet, we're not supposed to touch it. */
620 /* If none of the agent options we found matched, or if we didn't
621 find any agent options, count this packet as not having any
622 matching agent options, and if we're relying on agent options
623 to determine the outgoing interface, drop the packet. */
625 if (!good_agent_option
) {
626 ++missing_agent_option
;
627 if (drop_agent_mismatches
)
631 /* Adjust the length... */
633 length
= sp
- ((u_int8_t
*)packet
);
635 /* Make sure the packet isn't short (this is unlikely,
637 if (length
< BOOTP_MIN_LEN
) {
638 memset (sp
, 0, BOOTP_MIN_LEN
- length
);
639 length
= BOOTP_MIN_LEN
;
646 /* Find an interface that matches the circuit ID specified in the
647 Relay Agent Information option. If one is found, store it through
648 the pointer given; otherwise, leave the existing pointer alone.
650 We actually deviate somewhat from the current specification here:
651 if the option buffer is corrupt, we suggest that the caller not
652 respond to this packet. If the circuit ID doesn't match any known
653 interface, we suggest that the caller to drop the packet. Only if
654 we find a circuit ID that matches an existing interface do we tell
655 the caller to go ahead and process the packet. */
657 int find_interface_by_agent_option (packet
, out
, buf
, len
)
658 struct dhcp_packet
*packet
;
659 struct interface_info
**out
;
664 u_int8_t
*circuit_id
= 0;
665 unsigned circuit_id_len
= 0;
666 struct interface_info
*ip
;
669 /* If the next agent option overflows the end of the
670 packet, the agent option buffer is corrupt. */
672 i
+ buf
[i
+ 1] + 2 > len
) {
673 ++corrupt_agent_options
;
677 /* Remember where the circuit ID is... */
679 circuit_id
= &buf
[i
+ 2];
680 circuit_id_len
= buf
[i
+ 1];
681 i
+= circuit_id_len
+ 2;
685 i
+= buf
[i
+ 1] + 2;
690 /* If there's no circuit ID, it's not really ours, tell the caller
693 ++missing_circuit_id
;
697 /* Scan the interface list looking for an interface whose
698 name matches the one specified in circuit_id. */
700 for (ip
= interfaces
; ip
; ip
= ip
-> next
) {
701 if (ip
-> circuit_id
&&
702 ip
-> circuit_id_len
== circuit_id_len
&&
703 !memcmp (ip
-> circuit_id
, circuit_id
, circuit_id_len
))
707 /* If we got a match, use it. */
713 /* If we didn't get a match, the circuit ID was bogus. */
718 /* Examine a packet to see if it's a candidate to have a Relay
719 Agent Information option tacked onto its tail. If it is, tack
722 int add_relay_agent_options (ip
, packet
, length
, giaddr
)
723 struct interface_info
*ip
;
724 struct dhcp_packet
*packet
;
726 struct in_addr giaddr
;
728 int is_dhcp
= 0, agent_options_present
= 0;
729 u_int8_t
*op
, *sp
, *max
, *end_pad
= 0;
731 /* If we're not adding agent options to packets, we can skip
733 if (!add_agent_options
)
736 /* If there's no cookie, it's a bootp packet, so we should just
737 forward it unchanged. */
738 if (memcmp (packet
-> options
, DHCP_OPTIONS_COOKIE
, 4))
741 max
= ((u_int8_t
*)packet
) + length
;
742 sp
= op
= &packet
-> options
[4];
746 /* Skip padding... */
755 /* If we see a message type, it's a DHCP packet. */
756 case DHO_DHCP_MESSAGE_TYPE
:
761 /* Quit immediately if we hit an End option. */
765 case DHO_DHCP_AGENT_OPTIONS
:
766 /* We shouldn't see a relay agent option in a
767 packet before we've seen the DHCP packet type,
768 but if we do, we have to leave it alone. */
773 /* There's already a Relay Agent Information option
774 in this packet. How embarrassing. Decide what
775 to do based on the mode the user specified. */
777 switch (agent_relay_mode
) {
778 case forward_and_append
:
780 case forward_untouched
:
784 case forward_and_replace
:
789 /* Skip over the agent option and start copying
790 if we aren't copying already. */
795 /* Skip over other options. */
799 memcpy (sp
, op
, (unsigned)(op
[1] + 2));
807 /* If it's not a DHCP packet, we're not supposed to touch it. */
811 /* If the packet was padded out, we can store the agent option
812 at the beginning of the padding. */
817 /* Remember where the end of the packet was after parsing
821 /* XXX Is there room? */
823 /* Okay, cons up *our* Relay Agent Information option. */
824 *sp
++ = DHO_DHCP_AGENT_OPTIONS
;
825 *sp
++ = 0; /* Dunno... */
827 /* Copy in the circuit id... */
828 *sp
++ = RAI_CIRCUIT_ID
;
829 /* Sanity check. Had better not every happen. */
830 if (ip
-> circuit_id_len
> 255 || ip
-> circuit_id_len
< 1)
831 log_fatal ("completely bogus circuit id length %d on %s\n",
832 ip
-> circuit_id_len
, ip
-> name
);
833 *sp
++ = ip
-> circuit_id_len
;
834 memcpy (sp
, ip
-> circuit_id
, ip
-> circuit_id_len
);
835 sp
+= ip
-> circuit_id_len
;
837 /* Copy in remote ID... */
838 if (ip
-> remote_id
) {
839 *sp
++ = RAI_REMOTE_ID
;
840 if (ip
-> remote_id_len
> 255 || ip
-> remote_id_len
< 1)
841 log_fatal ("bogus remote id length %d on %s\n",
842 ip
-> circuit_id_len
, ip
-> name
);
843 *sp
++ = ip
-> remote_id_len
;
844 memcpy (sp
, ip
-> remote_id
, ip
-> remote_id_len
);
845 sp
+= ip
-> remote_id_len
;
848 /* Relay option's total length shouldn't ever get to be more than
851 log_fatal ("total agent option length exceeds 257 (%ld) on %s\n",
852 (long)(sp
- op
), ip
-> name
);
854 /* Calculate length of RAI option. */
855 op
[1] = sp
- op
- 2;
857 /* Deposit an END token. */
860 /* Recalculate total packet length. */
861 length
= sp
- ((u_int8_t
*)packet
);
863 /* Make sure the packet isn't short (this is unlikely, but WTH) */
864 if (length
< BOOTP_MIN_LEN
) {
865 memset (sp
, 0, BOOTP_MIN_LEN
- length
);
866 length
= BOOTP_MIN_LEN
;