3 DHCP/BOOTP Relay Agent. */
6 * Copyright (c) 1996-2000 Internet Software Consortium.
7 * Use is subject to license terms which appear in the file named
8 * ISC-LICENSE that should have accompanied this file when you
9 * received it. If a file named ISC-LICENSE did not accompany this
10 * file, or you are not sure the one you have is correct, you may
11 * obtain an applicable copy of the license at:
13 * http://www.isc.org/isc-license-1.0.html.
15 * This file is part of the ISC DHCP distribution. The documentation
16 * associated with this file is listed in the file DOCUMENTATION,
17 * included in the top-level directory of this release.
19 * Support and other services are available for ISC products - see
20 * http://www.isc.org for more information.
24 static char ocopyright
[] =
25 "$Id: dhcrelay.c,v 1.39 2000/01/26 15:15:31 mellon Exp $ Copyright (c) 1997, 1998, 1999 The Internet Software Consortium. All rights reserved.\n";
31 static void usage
PROTO ((void));
34 TIME default_lease_time
= 43200; /* 12 hours... */
35 TIME max_lease_time
= 86400; /* 24 hours... */
36 struct tree_cache
*global_options
[256];
38 /* Needed to prevent linking against conflex.c. */
44 const char *path_dhcrelay_pid
= _PATH_DHCRELAY_PID
;
46 int bogus_agent_drops
= 0; /* Packets dropped because agent option
47 field was specified and we're not relaying
48 packets that already have an agent option
50 int bogus_giaddr_drops
= 0; /* Packets sent to us to relay back to a
51 client, but with a bogus giaddr. */
52 int client_packets_relayed
= 0; /* Packets relayed from client to server. */
53 int server_packet_errors
= 0; /* Errors sending packets to servers. */
54 int server_packets_relayed
= 0; /* Packets relayed from server to client. */
55 int client_packet_errors
= 0; /* Errors sending packets to clients. */
57 int add_agent_options
= 0; /* If nonzero, add relay agent options. */
58 int drop_agent_mismatches
= 0; /* If nonzero, drop server replies that
59 don't contain a Relay Agent Information
60 option whose Agent ID suboption matches
62 int corrupt_agent_options
= 0; /* Number of packets dropped because
63 relay agent information option was bad. */
64 int missing_agent_option
= 0; /* Number of packets dropped because no
65 RAI option matching our ID was found. */
66 int bad_circuit_id
= 0; /* Circuit ID option in matching RAI option
67 did not match any known circuit ID. */
68 int missing_circuit_id
= 0; /* Circuit ID option in matching RAI option
71 /* Maximum size of a packet with agent options added. */
72 int dhcp_max_agent_option_packet_length
= 576;
74 /* What to do about packets we're asked to relay that
75 already have a relay option: */
76 enum { forward_and_append
, /* Forward and append our own relay option. */
77 forward_and_replace
, /* Forward, but replace theirs with ours. */
78 forward_untouched
, /* Forward without changes. */
79 discard
} agent_relay_mode
= forward_and_replace
;
82 u_int16_t remote_port
;
85 struct server_list
*next
;
86 struct sockaddr_in to
;
89 static char copyright
[] =
90 "Copyright 1997, 1998, 1999 The Internet Software Consortium.";
91 static char arr
[] = "All rights reserved.";
92 static char message
[] = "Internet Software Consortium DHCP Relay Agent";
93 static char contrib
[] = "\nPlease contribute if you find this software useful.";
94 static char url
[] = "For info, please visit http://www.isc.org/dhcp-contrib.html\n";
96 int main (argc
, argv
, envp
)
102 struct server_list
*sp
= (struct server_list
*)0;
108 openlog ("dhcrelay", LOG_NDELAY
);
109 log_priority
= LOG_DAEMON
;
111 openlog ("dhcrelay", LOG_NDELAY
, LOG_DAEMON
);
114 #if !(defined (DEBUG) || defined (SYSLOG_4_2))
115 setlogmask (LOG_UPTO (LOG_INFO
));
118 for (i
= 1; i
< argc
; i
++) {
119 if (!strcmp (argv
[i
], "-p")) {
122 local_port
= htons (atoi (argv
[i
]));
123 log_debug ("binding to user-specified port %d",
125 } else if (!strcmp (argv
[i
], "-d")) {
127 } else if (!strcmp (argv
[i
], "-i")) {
128 struct interface_info
*tmp
=
129 ((struct interface_info
*)
130 dmalloc (sizeof *tmp
, MDL
));
132 log_fatal ("Insufficient memory to %s %s",
133 "record interface", argv
[i
]);
137 memset (tmp
, 0, sizeof *tmp
);
138 strcpy (tmp
-> name
, argv
[i
]);
139 tmp
-> next
= interfaces
;
140 tmp
-> flags
= INTERFACE_REQUESTED
;
142 } else if (!strcmp (argv
[i
], "-q")) {
144 quiet_interface_discovery
= 1;
145 } else if (!strcmp (argv
[i
], "-a")) {
146 add_agent_options
= 1;
147 } else if (!strcmp (argv
[i
], "-A")) {
150 dhcp_max_agent_option_packet_length
= atoi (argv
[i
]);
151 } else if (!strcmp (argv
[i
], "-m")) {
154 if (!strcasecmp (argv
[i
], "append")) {
155 agent_relay_mode
= forward_and_append
;
156 } else if (!strcasecmp (argv
[i
], "replace")) {
157 agent_relay_mode
= forward_and_replace
;
158 } else if (!strcasecmp (argv
[i
], "forward")) {
159 agent_relay_mode
= forward_untouched
;
160 } else if (!strcasecmp (argv
[i
], "discard")) {
161 agent_relay_mode
= discard
;
164 } else if (!strcmp (argv
[i
], "-D")) {
165 drop_agent_mismatches
= 1;
166 } else if (argv
[i
][0] == '-') {
170 struct in_addr ia
, *iap
= (struct in_addr
*)0;
171 if (inet_aton (argv
[i
], &ia
)) {
174 he
= gethostbyname (argv
[i
]);
176 log_error ("%s: host unknown",
179 iap
= ((struct in_addr
*)
180 he
-> h_addr_list
[0]);
184 sp
= ((struct server_list
*)
185 dmalloc (sizeof *sp
, MDL
));
187 log_fatal ("no memory for server.\n");
188 sp
-> next
= servers
;
190 memcpy (&sp
-> to
.sin_addr
,
197 log_info ("%s %s", message
, DHCP_VERSION
);
198 log_info (copyright
);
207 /* Default to the DHCP/BOOTP port. */
209 ent
= getservbyname ("dhcps", "udp");
211 local_port
= htons (67);
213 local_port
= ent
-> s_port
;
216 remote_port
= htons (ntohs (local_port
) + 1);
218 /* We need at least one server. */
223 /* Set up the server sockaddrs. */
224 for (sp
= servers
; sp
; sp
= sp
-> next
) {
225 sp
-> to
.sin_port
= local_port
;
226 sp
-> to
.sin_family
= AF_INET
;
228 sp
-> to
.sin_len
= sizeof sp
-> to
;
232 /* Get the current time... */
233 GET_TIME (&cur_time
);
235 /* Set up the OMAPI. */
236 status
= omapi_init ();
237 if (status
!= ISC_R_SUCCESS
)
238 log_fatal ("Can't initialize OMAPI: %s",
239 isc_result_totext (status
));
241 /* Discover all the network interfaces. */
242 discover_interfaces (DISCOVER_RELAY
);
244 /* Set up the bootp packet handler... */
245 bootp_packet_handler
= relay
;
247 /* Become a daemon... */
255 if ((pid
= fork()) < 0)
256 log_fatal ("can't fork daemon: %m");
260 pfdesc
= open (path_dhcrelay_pid
,
261 O_CREAT
| O_TRUNC
| O_WRONLY
, 0644);
264 log_error ("Can't create %s: %m", path_dhcrelay_pid
);
266 pf
= fdopen (pfdesc
, "w");
268 log_error ("Can't fdopen %s: %m",
271 fprintf (pf
, "%ld\n", (long)getpid ());
282 /* Start dispatching packets and timeouts... */
289 void relay (ip
, packet
, length
, from_port
, from
, hfrom
)
290 struct interface_info
*ip
;
291 struct dhcp_packet
*packet
;
293 unsigned int from_port
;
295 struct hardware
*hfrom
;
297 struct server_list
*sp
;
298 struct sockaddr_in to
;
299 struct interface_info
*out
;
302 if (packet
-> hlen
> sizeof packet
-> chaddr
) {
303 log_info ("Discarding packet with invalid hlen.");
308 If you're using the circuit ID to figure out where to
309 send the reply, you can delete the following code,
310 but you still need to validate the giaddr and drop the
311 packet if it's bogus. */
312 /* Find the interface that corresponds to the giaddr
314 if (packet
-> giaddr
.s_addr
) {
315 for (out
= interfaces
; out
; out
= out
-> next
) {
316 if (!memcmp (&out
-> primary_address
,
318 sizeof packet
-> giaddr
))
322 out
= (struct interface_info
*)0;
325 /* If it's a bootreply, forward it to the client. */
326 if (packet
-> op
== BOOTREPLY
) {
327 if (!(packet
-> flags
& htons (BOOTP_BROADCAST
)) &&
328 can_unicast_without_arp (out
)) {
329 to
.sin_addr
= packet
-> yiaddr
;
330 to
.sin_port
= remote_port
;
332 to
.sin_addr
.s_addr
= htonl (INADDR_BROADCAST
);
333 to
.sin_port
= remote_port
;
335 to
.sin_family
= AF_INET
;
337 to
.sin_len
= sizeof to
;
340 memcpy (&hto
.hbuf
[1], packet
-> chaddr
, packet
-> hlen
);
341 hto
.hbuf
[0] = packet
-> htype
;
342 hto
.hlen
= packet
-> hlen
+ 1;
344 /* Wipe out the agent relay options and, if possible, figure
345 out which interface to use based on the contents of the
346 option that we put on the request to which the server is
349 strip_relay_agent_options (ip
, &out
, packet
, length
)))
353 log_error ("packet to bogus giaddr %s.\n",
354 inet_ntoa (packet
-> giaddr
));
355 ++bogus_giaddr_drops
;
359 if (send_packet (out
,
361 packet
, length
, out
-> primary_address
,
363 ++server_packet_errors
;
365 log_debug ("forwarded BOOTREPLY for %s to %s",
366 print_hw_addr (packet
-> htype
, packet
-> hlen
,
368 inet_ntoa (to
.sin_addr
));
370 ++server_packets_relayed
;
375 /* If giaddr matches one of our addresses, ignore the packet -
380 /* Add relay agent options if indicated. If something goes wrong,
382 if (!(length
= add_relay_agent_options (ip
, packet
, length
,
383 ip
-> primary_address
)))
386 /* If giaddr is not already set, Set it so the server can
387 figure out what net it's from and so that we can later
388 forward the response to the correct net. If it's already
389 set, the response will be sent directly to the relay agent
390 that set giaddr, so we won't see it. */
391 if (!packet
-> giaddr
.s_addr
)
392 packet
-> giaddr
= ip
-> primary_address
;
394 /* Otherwise, it's a BOOTREQUEST, so forward it to all the
396 for (sp
= servers
; sp
; sp
= sp
-> next
) {
397 if (send_packet ((fallback_interface
398 ? fallback_interface
: interfaces
),
400 packet
, length
, ip
-> primary_address
,
401 &sp
-> to
, (struct hardware
*)0) < 0) {
402 ++client_packet_errors
;
404 log_debug ("forwarded BOOTREQUEST for %s to %s",
405 print_hw_addr (packet
-> htype
, packet
-> hlen
,
407 inet_ntoa (sp
-> to
.sin_addr
));
408 ++client_packets_relayed
;
416 log_fatal ("Usage: dhcrelay [-p <port>] [-d] [-D] [-i %s%s%s",
418 "[-q] [-a] [-A length] [-m append|replace|forward|discard]\n",
419 " [server1 [... serverN]]");
422 int write_lease (lease
)
428 int write_host (host
)
429 struct host_decl
*host
;
440 struct packet
*packet
;
445 struct packet
*packet
;
449 struct subnet
*find_subnet (addr
)
452 return (struct subnet
*)0;
455 /* Strip any Relay Agent Information options from the DHCP packet
456 option buffer. If an RAI option is found whose Agent ID matches
457 the giaddr (i.e., ours), try to look up the outgoing interface
458 based on the circuit ID suboption. */
460 int strip_relay_agent_options (in
, out
, packet
, length
)
461 struct interface_info
*in
, **out
;
462 struct dhcp_packet
*packet
;
466 u_int8_t
*op
, *sp
, *max
;
467 int good_agent_option
= 0;
470 /* If we're not adding agent options to packets, we're not taking
472 if (!add_agent_options
)
475 /* If there's no cookie, it's a bootp packet, so we should just
476 forward it unchanged. */
477 if (memcmp (packet
-> options
, DHCP_OPTIONS_COOKIE
, 4))
480 max
= ((u_int8_t
*)packet
) + length
;
481 sp
= op
= &packet
-> options
[4];
485 /* Skip padding... */
493 /* If we see a message type, it's a DHCP packet. */
494 case DHO_DHCP_MESSAGE_TYPE
:
499 /* Quit immediately if we hit an End option. */
505 case DHO_DHCP_AGENT_OPTIONS
:
506 /* We shouldn't see a relay agent option in a
507 packet before we've seen the DHCP packet type,
508 but if we do, we have to leave it alone. */
512 status
= find_interface_by_agent_option (packet
,
515 if (status
== -1 && drop_agent_mismatches
)
518 good_agent_option
= 1;
523 /* Skip over other options. */
526 memcpy (sp
, op
, (unsigned)(op
[1] + 2));
534 /* If it's not a DHCP packet, we're not supposed to touch it. */
538 /* If none of the agent options we found matched, or if we didn't
539 find any agent options, count this packet as not having any
540 matching agent options, and if we're relying on agent options
541 to determine the outgoing interface, drop the packet. */
543 if (!good_agent_option
) {
544 ++missing_agent_option
;
545 if (drop_agent_mismatches
)
549 /* Adjust the length... */
551 length
= sp
- ((u_int8_t
*)packet
);
553 /* Make sure the packet isn't short (this is unlikely,
555 if (length
< BOOTP_MIN_LEN
) {
556 memset (sp
, 0, BOOTP_MIN_LEN
- length
);
557 length
= BOOTP_MIN_LEN
;
564 /* Find an interface that matches the circuit ID specified in the
565 Relay Agent Information option. If one is found, store it through
566 the pointer given; otherwise, leave the existing pointer alone.
568 We actually deviate somewhat from the current specification here:
569 if the option buffer is corrupt, we suggest that the caller not
570 respond to this packet. If the circuit ID doesn't match any known
571 interface, we suggest that the caller to drop the packet. Only if
572 we find a circuit ID that matches an existing interface do we tell
573 the caller to go ahead and process the packet. */
575 int find_interface_by_agent_option (packet
, out
, buf
, len
)
576 struct dhcp_packet
*packet
;
577 struct interface_info
**out
;
582 u_int8_t
*circuit_id
= 0;
583 unsigned circuit_id_len
;
584 struct interface_info
*ip
;
587 /* If the next agent option overflows the end of the
588 packet, the agent option buffer is corrupt. */
590 i
+ buf
[i
+ 1] + 2 > len
) {
591 ++corrupt_agent_options
;
595 /* Remember where the circuit ID is... */
597 circuit_id
= &buf
[i
+ 2];
598 circuit_id_len
= buf
[i
+ 1];
599 i
+= circuit_id_len
+ 2;
603 i
+= buf
[i
+ 1] + 2;
608 /* If there's no circuit ID, it's not really ours, tell the caller
611 ++missing_circuit_id
;
615 /* Scan the interface list looking for an interface whose
616 name matches the one specified in circuit_id. */
618 for (ip
= interfaces
; ip
; ip
= ip
-> next
) {
619 if (ip
-> circuit_id
&&
620 ip
-> circuit_id_len
== circuit_id_len
&&
621 !memcmp (ip
-> circuit_id
, circuit_id
, circuit_id_len
))
625 /* If we got a match, use it. */
631 /* If we didn't get a match, the circuit ID was bogus. */
636 /* Examine a packet to see if it's a candidate to have a Relay
637 Agent Information option tacked onto its tail. If it is, tack
640 int add_relay_agent_options (ip
, packet
, length
, giaddr
)
641 struct interface_info
*ip
;
642 struct dhcp_packet
*packet
;
644 struct in_addr giaddr
;
646 int is_dhcp
= 0, agent_options_present
= 0;
647 u_int8_t
*op
, *sp
, *max
, *end_pad
= 0;
649 /* If we're not adding agent options to packets, we can skip
651 if (!add_agent_options
)
654 /* If there's no cookie, it's a bootp packet, so we should just
655 forward it unchanged. */
656 if (memcmp (packet
-> options
, DHCP_OPTIONS_COOKIE
, 4))
659 max
= ((u_int8_t
*)packet
) + length
;
660 sp
= op
= &packet
-> options
[4];
664 /* Skip padding... */
673 /* If we see a message type, it's a DHCP packet. */
674 case DHO_DHCP_MESSAGE_TYPE
:
679 /* Quit immediately if we hit an End option. */
683 case DHO_DHCP_AGENT_OPTIONS
:
684 /* We shouldn't see a relay agent option in a
685 packet before we've seen the DHCP packet type,
686 but if we do, we have to leave it alone. */
691 /* There's already a Relay Agent Information option
692 in this packet. How embarrassing. Decide what
693 to do based on the mode the user specified. */
695 switch (agent_relay_mode
) {
696 case forward_and_append
:
698 case forward_untouched
:
702 case forward_and_replace
:
707 /* Skip over the agent option and start copying
708 if we aren't copying already. */
713 /* Skip over other options. */
717 memcpy (sp
, op
, (unsigned)(op
[1] + 2));
725 /* If it's not a DHCP packet, we're not supposed to touch it. */
729 /* If the packet was padded out, we can store the agent option
730 at the beginning of the padding. */
735 /* Remember where the end of the packet was after parsing
739 /* XXX Is there room? */
741 /* Okay, cons up *our* Relay Agent Information option. */
742 *sp
++ = DHO_DHCP_AGENT_OPTIONS
;
743 *sp
++ = 0; /* Dunno... */
745 /* Copy in the circuit id... */
746 *sp
++ = RAI_CIRCUIT_ID
;
747 /* Sanity check. Had better not every happen. */
748 if (ip
-> circuit_id_len
> 255 || ip
-> circuit_id_len
< 1)
749 log_fatal ("completely bogus circuit id length %d on %s\n",
750 ip
-> circuit_id_len
, ip
-> name
);
751 *sp
++ = ip
-> circuit_id_len
;
752 memcpy (sp
, ip
-> circuit_id
, ip
-> circuit_id_len
);
753 sp
+= ip
-> circuit_id_len
;
755 /* Copy in remote ID... */
756 if (ip
-> remote_id
) {
757 *sp
++ = RAI_REMOTE_ID
;
758 if (ip
-> remote_id_len
> 255 || ip
-> remote_id_len
< 1)
759 log_fatal ("bogus remote id length %d on %s\n",
760 ip
-> circuit_id_len
, ip
-> name
);
761 *sp
++ = ip
-> remote_id_len
;
762 memcpy (sp
, ip
-> remote_id
, ip
-> remote_id_len
);
763 sp
+= ip
-> remote_id_len
;
766 /* Relay option's total length shouldn't ever get to be more than
769 log_fatal ("total agent option length exceeds 257 (%d) on %s\n",
770 sp
- op
, ip
-> name
);
772 /* Calculate length of RAI option. */
773 op
[1] = sp
- op
- 2;
775 /* Deposit an END token. */
778 /* Recalculate total packet length. */
779 length
= sp
- ((u_int8_t
*)packet
);
781 /* Make sure the packet isn't short (this is unlikely, but WTH) */
782 if (length
< BOOTP_MIN_LEN
) {
783 memset (sp
, 0, BOOTP_MIN_LEN
- length
);
784 length
= BOOTP_MIN_LEN
;