3 DHCP/BOOTP Relay Agent. */
6 * Copyright (c) 2004 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.52.2.9 2004/11/24 17:39:18 dhankins Exp $ Copyright (c) 2004 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 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. */
118 i
= open ("/dev/null", O_RDWR
);
120 i
= open ("/dev/null", O_RDWR
);
122 i
= open ("/dev/null", O_RDWR
);
123 log_perror
= 0; /* No sense logging to /dev/null. */
128 openlog ("dhcrelay", LOG_NDELAY
);
129 log_priority
= LOG_DAEMON
;
131 openlog ("dhcrelay", LOG_NDELAY
, LOG_DAEMON
);
134 #if !(defined (DEBUG) || defined (SYSLOG_4_2))
135 setlogmask (LOG_UPTO (LOG_INFO
));
138 /* Set up the OMAPI. */
139 status
= omapi_init ();
140 if (status
!= ISC_R_SUCCESS
)
141 log_fatal ("Can't initialize OMAPI: %s",
142 isc_result_totext (status
));
144 /* Set up the OMAPI wrappers for the interface object. */
147 for (i
= 1; i
< argc
; i
++) {
148 if (!strcmp (argv
[i
], "-p")) {
151 local_port
= htons (atoi (argv
[i
]));
152 log_debug ("binding to user-specified port %d",
154 } else if (!strcmp (argv
[i
], "-d")) {
156 } else if (!strcmp (argv
[i
], "-i")) {
157 struct interface_info
*tmp
=
158 (struct interface_info
*)0;
159 status
= interface_allocate (&tmp
, MDL
);
160 if (status
!= ISC_R_SUCCESS
)
161 log_fatal ("%s: interface_allocate: %s",
163 isc_result_totext (status
));
167 strcpy (tmp
-> name
, argv
[i
]);
168 interface_snorf (tmp
, INTERFACE_REQUESTED
);
169 interface_dereference (&tmp
, MDL
);
170 } else if (!strcmp (argv
[i
], "-q")) {
172 quiet_interface_discovery
= 1;
173 } else if (!strcmp (argv
[i
], "-a")) {
174 add_agent_options
= 1;
175 } else if (!strcmp (argv
[i
], "-c")) {
179 hcount
= atoi(argv
[i
]);
181 max_hop_count
= hcount
;
184 } else if (!strcmp (argv
[i
], "-A")) {
187 dhcp_max_agent_option_packet_length
= atoi (argv
[i
]);
188 } else if (!strcmp (argv
[i
], "-m")) {
191 if (!strcasecmp (argv
[i
], "append")) {
192 agent_relay_mode
= forward_and_append
;
193 } else if (!strcasecmp (argv
[i
], "replace")) {
194 agent_relay_mode
= forward_and_replace
;
195 } else if (!strcasecmp (argv
[i
], "forward")) {
196 agent_relay_mode
= forward_untouched
;
197 } else if (!strcasecmp (argv
[i
], "discard")) {
198 agent_relay_mode
= discard
;
201 } else if (!strcmp (argv
[i
], "-D")) {
202 drop_agent_mismatches
= 1;
203 } else if (argv
[i
][0] == '-') {
205 } else if (!strcmp (argv
[i
], "--version")) {
206 log_info ("isc-dhcrelay-%s", DHCP_VERSION
);
210 struct in_addr ia
, *iap
= (struct in_addr
*)0;
211 if (inet_aton (argv
[i
], &ia
)) {
214 he
= gethostbyname (argv
[i
]);
216 log_error ("%s: host unknown",
219 iap
= ((struct in_addr
*)
220 he
-> h_addr_list
[0]);
224 sp
= ((struct server_list
*)
225 dmalloc (sizeof *sp
, MDL
));
227 log_fatal ("no memory for server.\n");
228 sp
-> next
= servers
;
230 memcpy (&sp
-> to
.sin_addr
,
236 if ((s
= getenv ("PATH_DHCRELAY_PID"))) {
237 path_dhcrelay_pid
= s
;
241 log_info ("%s %s", message
, DHCP_VERSION
);
242 log_info (copyright
);
250 /* Default to the DHCP/BOOTP port. */
252 ent
= getservbyname ("dhcps", "udp");
254 local_port
= htons (67);
256 local_port
= ent
-> s_port
;
259 remote_port
= htons (ntohs (local_port
) + 1);
261 /* We need at least one server. */
266 /* Set up the server sockaddrs. */
267 for (sp
= servers
; sp
; sp
= sp
-> next
) {
268 sp
-> to
.sin_port
= local_port
;
269 sp
-> to
.sin_family
= AF_INET
;
271 sp
-> to
.sin_len
= sizeof sp
-> to
;
275 /* Get the current time... */
276 GET_TIME (&cur_time
);
278 /* Discover all the network interfaces. */
279 discover_interfaces (DISCOVER_RELAY
);
281 /* Set up the bootp packet handler... */
282 bootp_packet_handler
= relay
;
284 /* Become a daemon... */
292 if ((pid
= fork()) < 0)
293 log_fatal ("can't fork daemon: %m");
297 pfdesc
= open (path_dhcrelay_pid
,
298 O_CREAT
| O_TRUNC
| O_WRONLY
, 0644);
301 log_error ("Can't create %s: %m", path_dhcrelay_pid
);
303 pf
= fdopen (pfdesc
, "w");
305 log_error ("Can't fdopen %s: %m",
308 fprintf (pf
, "%ld\n", (long)getpid ());
319 /* Start dispatching packets and timeouts... */
326 void relay (ip
, packet
, length
, from_port
, from
, hfrom
)
327 struct interface_info
*ip
;
328 struct dhcp_packet
*packet
;
330 unsigned int from_port
;
332 struct hardware
*hfrom
;
334 struct server_list
*sp
;
335 struct sockaddr_in to
;
336 struct interface_info
*out
;
337 struct hardware hto
, *htop
;
339 if (packet
-> hlen
> sizeof packet
-> chaddr
) {
340 log_info ("Discarding packet with invalid hlen.");
344 /* Find the interface that corresponds to the giaddr
346 if (packet
-> giaddr
.s_addr
) {
347 for (out
= interfaces
; out
; out
= out
-> next
) {
348 if (!memcmp (&out
-> primary_address
,
350 sizeof packet
-> giaddr
))
354 out
= (struct interface_info
*)0;
357 /* If it's a bootreply, forward it to the client. */
358 if (packet
-> op
== BOOTREPLY
) {
359 if (!(packet
-> flags
& htons (BOOTP_BROADCAST
)) &&
360 can_unicast_without_arp (out
)) {
361 to
.sin_addr
= packet
-> yiaddr
;
362 to
.sin_port
= remote_port
;
364 /* and hardware address is not broadcast */
367 to
.sin_addr
.s_addr
= htonl (INADDR_BROADCAST
);
368 to
.sin_port
= remote_port
;
370 /* hardware address is broadcast */
373 to
.sin_family
= AF_INET
;
375 to
.sin_len
= sizeof to
;
378 memcpy (&hto
.hbuf
[1], packet
-> chaddr
, packet
-> hlen
);
379 hto
.hbuf
[0] = packet
-> htype
;
380 hto
.hlen
= packet
-> hlen
+ 1;
382 /* Wipe out the agent relay options and, if possible, figure
383 out which interface to use based on the contents of the
384 option that we put on the request to which the server is
387 strip_relay_agent_options (ip
, &out
, packet
, length
)))
391 log_error ("packet to bogus giaddr %s.\n",
392 inet_ntoa (packet
-> giaddr
));
393 ++bogus_giaddr_drops
;
397 if (send_packet (out
,
399 packet
, length
, out
-> primary_address
,
401 ++server_packet_errors
;
403 log_debug ("forwarded BOOTREPLY for %s to %s",
404 print_hw_addr (packet
-> htype
, packet
-> hlen
,
406 inet_ntoa (to
.sin_addr
));
408 ++server_packets_relayed
;
413 /* If giaddr matches one of our addresses, ignore the packet -
418 /* Add relay agent options if indicated. If something goes wrong,
420 if (!(length
= add_relay_agent_options (ip
, packet
, length
,
421 ip
-> primary_address
)))
424 /* If giaddr is not already set, Set it so the server can
425 figure out what net it's from and so that we can later
426 forward the response to the correct net. If it's already
427 set, the response will be sent directly to the relay agent
428 that set giaddr, so we won't see it. */
429 if (!packet
-> giaddr
.s_addr
)
430 packet
-> giaddr
= ip
-> primary_address
;
431 if (packet
-> hops
< max_hop_count
)
432 packet
-> hops
= packet
-> hops
+ 1;
436 /* Otherwise, it's a BOOTREQUEST, so forward it to all the
438 for (sp
= servers
; sp
; sp
= sp
-> next
) {
439 if (send_packet ((fallback_interface
440 ? fallback_interface
: interfaces
),
442 packet
, length
, ip
-> primary_address
,
443 &sp
-> to
, (struct hardware
*)0) < 0) {
444 ++client_packet_errors
;
446 log_debug ("forwarded BOOTREQUEST for %s to %s",
447 print_hw_addr (packet
-> htype
, packet
-> hlen
,
449 inet_ntoa (sp
-> to
.sin_addr
));
450 ++client_packets_relayed
;
458 log_fatal ("Usage: dhcrelay [-p <port>] [-d] [-D] [-i %s%s%s%s",
459 "interface] [-q] [-a]\n ",
460 "[-c count] [-A length] ",
461 "[-m append|replace|forward|discard]\n",
462 " [server1 [... serverN]]");
465 int write_lease (lease
)
471 int write_host (host
)
472 struct host_decl
*host
;
483 struct packet
*packet
;
488 struct packet
*packet
;
492 int find_subnet (struct subnet
**sp
,
493 struct iaddr addr
, const char *file
, int line
)
499 int check_collection (struct packet
*p
, struct lease
*l
,
500 struct collection
*c
)
505 void classify (struct packet
*p
, struct class *c
)
509 isc_result_t
find_class (struct class **class, const char *c1
,
510 const char *c2
, int i
)
512 return ISC_R_NOTFOUND
;
515 int parse_allow_deny (struct option_cache
**oc
, struct parse
*p
, int i
)
520 /* As a wise man once said in dhcpctl/omshell.c: */
522 isc_result_t
dhcp_set_control_state (control_object_state_t oldstate
,
523 control_object_state_t newstate
)
525 return ISC_R_SUCCESS
;
530 /* Strip any Relay Agent Information options from the DHCP packet
531 option buffer. If there is a circuit ID suboption, look up the
532 outgoing interface based upon it. */
534 int strip_relay_agent_options (in
, out
, packet
, length
)
535 struct interface_info
*in
, **out
;
536 struct dhcp_packet
*packet
;
540 u_int8_t
*op
, *sp
, *max
;
541 int good_agent_option
= 0;
544 /* If we're not adding agent options to packets, we're not taking
546 if (!add_agent_options
)
549 /* If there's no cookie, it's a bootp packet, so we should just
550 forward it unchanged. */
551 if (memcmp (packet
-> options
, DHCP_OPTIONS_COOKIE
, 4))
554 max
= ((u_int8_t
*)packet
) + length
;
555 sp
= op
= &packet
-> options
[4];
559 /* Skip padding... */
567 /* If we see a message type, it's a DHCP packet. */
568 case DHO_DHCP_MESSAGE_TYPE
:
573 /* Quit immediately if we hit an End option. */
579 case DHO_DHCP_AGENT_OPTIONS
:
580 /* We shouldn't see a relay agent option in a
581 packet before we've seen the DHCP packet type,
582 but if we do, we have to leave it alone. */
586 status
= find_interface_by_agent_option (packet
,
589 if (status
== -1 && drop_agent_mismatches
)
592 good_agent_option
= 1;
597 /* Skip over other options. */
600 memcpy (sp
, op
, (unsigned)(op
[1] + 2));
608 /* If it's not a DHCP packet, we're not supposed to touch it. */
612 /* If none of the agent options we found matched, or if we didn't
613 find any agent options, count this packet as not having any
614 matching agent options, and if we're relying on agent options
615 to determine the outgoing interface, drop the packet. */
617 if (!good_agent_option
) {
618 ++missing_agent_option
;
619 if (drop_agent_mismatches
)
623 /* Adjust the length... */
625 length
= sp
- ((u_int8_t
*)packet
);
627 /* Make sure the packet isn't short (this is unlikely,
629 if (length
< BOOTP_MIN_LEN
) {
630 memset (sp
, 0, BOOTP_MIN_LEN
- length
);
631 length
= BOOTP_MIN_LEN
;
638 /* Find an interface that matches the circuit ID specified in the
639 Relay Agent Information option. If one is found, store it through
640 the pointer given; otherwise, leave the existing pointer alone.
642 We actually deviate somewhat from the current specification here:
643 if the option buffer is corrupt, we suggest that the caller not
644 respond to this packet. If the circuit ID doesn't match any known
645 interface, we suggest that the caller to drop the packet. Only if
646 we find a circuit ID that matches an existing interface do we tell
647 the caller to go ahead and process the packet. */
649 int find_interface_by_agent_option (packet
, out
, buf
, len
)
650 struct dhcp_packet
*packet
;
651 struct interface_info
**out
;
656 u_int8_t
*circuit_id
= 0;
657 unsigned circuit_id_len
= 0;
658 struct interface_info
*ip
;
661 /* If the next agent option overflows the end of the
662 packet, the agent option buffer is corrupt. */
664 i
+ buf
[i
+ 1] + 2 > len
) {
665 ++corrupt_agent_options
;
669 /* Remember where the circuit ID is... */
671 circuit_id
= &buf
[i
+ 2];
672 circuit_id_len
= buf
[i
+ 1];
673 i
+= circuit_id_len
+ 2;
677 i
+= buf
[i
+ 1] + 2;
682 /* If there's no circuit ID, it's not really ours, tell the caller
685 ++missing_circuit_id
;
689 /* Scan the interface list looking for an interface whose
690 name matches the one specified in circuit_id. */
692 for (ip
= interfaces
; ip
; ip
= ip
-> next
) {
693 if (ip
-> circuit_id
&&
694 ip
-> circuit_id_len
== circuit_id_len
&&
695 !memcmp (ip
-> circuit_id
, circuit_id
, circuit_id_len
))
699 /* If we got a match, use it. */
705 /* If we didn't get a match, the circuit ID was bogus. */
710 /* Examine a packet to see if it's a candidate to have a Relay
711 Agent Information option tacked onto its tail. If it is, tack
714 int add_relay_agent_options (ip
, packet
, length
, giaddr
)
715 struct interface_info
*ip
;
716 struct dhcp_packet
*packet
;
718 struct in_addr giaddr
;
720 int is_dhcp
= 0, agent_options_present
= 0;
721 u_int8_t
*op
, *sp
, *max
, *end_pad
= 0;
723 /* If we're not adding agent options to packets, we can skip
725 if (!add_agent_options
)
728 /* If there's no cookie, it's a bootp packet, so we should just
729 forward it unchanged. */
730 if (memcmp (packet
-> options
, DHCP_OPTIONS_COOKIE
, 4))
733 max
= ((u_int8_t
*)packet
) + length
;
734 sp
= op
= &packet
-> options
[4];
738 /* Skip padding... */
747 /* If we see a message type, it's a DHCP packet. */
748 case DHO_DHCP_MESSAGE_TYPE
:
753 /* Quit immediately if we hit an End option. */
757 case DHO_DHCP_AGENT_OPTIONS
:
758 /* We shouldn't see a relay agent option in a
759 packet before we've seen the DHCP packet type,
760 but if we do, we have to leave it alone. */
765 /* There's already a Relay Agent Information option
766 in this packet. How embarrassing. Decide what
767 to do based on the mode the user specified. */
769 switch (agent_relay_mode
) {
770 case forward_and_append
:
772 case forward_untouched
:
776 case forward_and_replace
:
781 /* Skip over the agent option and start copying
782 if we aren't copying already. */
787 /* Skip over other options. */
791 memcpy (sp
, op
, (unsigned)(op
[1] + 2));
799 /* If it's not a DHCP packet, we're not supposed to touch it. */
803 /* If the packet was padded out, we can store the agent option
804 at the beginning of the padding. */
809 /* Remember where the end of the packet was after parsing
813 /* XXX Is there room? */
815 /* Okay, cons up *our* Relay Agent Information option. */
816 *sp
++ = DHO_DHCP_AGENT_OPTIONS
;
817 *sp
++ = 0; /* Dunno... */
819 /* Copy in the circuit id... */
820 *sp
++ = RAI_CIRCUIT_ID
;
821 /* Sanity check. Had better not every happen. */
822 if (ip
-> circuit_id_len
> 255 || ip
-> circuit_id_len
< 1)
823 log_fatal ("completely bogus circuit id length %d on %s\n",
824 ip
-> circuit_id_len
, ip
-> name
);
825 *sp
++ = ip
-> circuit_id_len
;
826 memcpy (sp
, ip
-> circuit_id
, ip
-> circuit_id_len
);
827 sp
+= ip
-> circuit_id_len
;
829 /* Copy in remote ID... */
830 if (ip
-> remote_id
) {
831 *sp
++ = RAI_REMOTE_ID
;
832 if (ip
-> remote_id_len
> 255 || ip
-> remote_id_len
< 1)
833 log_fatal ("bogus remote id length %d on %s\n",
834 ip
-> circuit_id_len
, ip
-> name
);
835 *sp
++ = ip
-> remote_id_len
;
836 memcpy (sp
, ip
-> remote_id
, ip
-> remote_id_len
);
837 sp
+= ip
-> remote_id_len
;
840 /* Relay option's total length shouldn't ever get to be more than
843 log_fatal ("total agent option length exceeds 257 (%ld) on %s\n",
844 (long)(sp
- op
), ip
-> name
);
846 /* Calculate length of RAI option. */
847 op
[1] = sp
- op
- 2;
849 /* Deposit an END token. */
852 /* Recalculate total packet length. */
853 length
= sp
- ((u_int8_t
*)packet
);
855 /* Make sure the packet isn't short (this is unlikely, but WTH) */
856 if (length
< BOOTP_MIN_LEN
) {
857 memset (sp
, 0, BOOTP_MIN_LEN
- length
);
858 length
= BOOTP_MIN_LEN
;