]>
Commit | Line | Data |
---|---|---|
747ec13b TL |
1 | /* dhcrelay.c |
2 | ||
3 | DHCP/BOOTP Relay Agent. */ | |
4 | ||
5 | /* | |
88cd8aca | 6 | * Copyright (c) 2004-2006 by Internet Systems Consortium, Inc. ("ISC") |
98311e4b | 7 | * Copyright (c) 1997-2003 by Internet Software Consortium |
747ec13b | 8 | * |
98311e4b DH |
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. | |
747ec13b | 12 | * |
98311e4b DH |
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. | |
747ec13b | 20 | * |
98311e4b DH |
21 | * Internet Systems Consortium, Inc. |
22 | * 950 Charter Street | |
23 | * Redwood City, CA 94063 | |
24 | * <info@isc.org> | |
25 | * http://www.isc.org/ | |
49733f31 | 26 | * |
98311e4b | 27 | * This software has been written for Internet Systems Consortium |
49733f31 | 28 | * by Ted Lemon in cooperation with Vixie Enterprises and Nominum, Inc. |
98311e4b | 29 | * To learn more about Internet Systems Consortium, see |
49733f31 TL |
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''. | |
747ec13b TL |
33 | */ |
34 | ||
35 | #ifndef lint | |
339b0231 | 36 | static char ocopyright[] = |
88cd8aca | 37 | "$Id: dhcrelay.c,v 1.54 2006/02/24 23:16:30 dhankins Exp $ Copyright (c) 2004-2006 Internet Systems Consortium. All rights reserved.\n"; |
747ec13b TL |
38 | #endif /* not lint */ |
39 | ||
40 | #include "dhcpd.h" | |
31020a36 | 41 | #include "version.h" |
747ec13b TL |
42 | |
43 | static void usage PROTO ((void)); | |
44 | ||
747ec13b TL |
45 | TIME default_lease_time = 43200; /* 12 hours... */ |
46 | TIME max_lease_time = 86400; /* 24 hours... */ | |
47 | struct tree_cache *global_options [256]; | |
48 | ||
aaf053e3 TL |
49 | /* Needed to prevent linking against conflex.c. */ |
50 | int lexline; | |
51 | int lexchar; | |
52 | char *token_line; | |
53 | char *tlname; | |
54 | ||
b1b7b521 | 55 | const char *path_dhcrelay_pid = _PATH_DHCRELAY_PID; |
e7012e3f | 56 | |
866428dd TL |
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 | |
60 | specified. */ | |
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. */ | |
67 | ||
68 | int add_agent_options = 0; /* If nonzero, add relay agent options. */ | |
69 | int drop_agent_mismatches = 0; /* If nonzero, drop server replies that | |
98311e4b | 70 | don't have matching circuit-id's. */ |
866428dd TL |
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 | |
78 | was missing. */ | |
98311e4b DH |
79 | int max_hop_count = 10; /* Maximum hop count */ |
80 | ||
866428dd TL |
81 | |
82 | /* Maximum size of a packet with agent options added. */ | |
83 | int dhcp_max_agent_option_packet_length = 576; | |
84 | ||
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; | |
91 | ||
747ec13b TL |
92 | u_int16_t local_port; |
93 | u_int16_t remote_port; | |
747ec13b TL |
94 | |
95 | struct server_list { | |
96 | struct server_list *next; | |
97 | struct sockaddr_in to; | |
98 | } *servers; | |
99 | ||
98311e4b | 100 | static char copyright [] = "Copyright 2004-2005 Internet Systems Consortium."; |
d2bc90bd | 101 | static char arr [] = "All rights reserved."; |
98311e4b DH |
102 | static char message [] = "Internet Systems Consortium DHCP Relay Agent"; |
103 | static char url [] = "For info, please visit http://www.isc.org/sw/dhcp/"; | |
d2bc90bd | 104 | |
747ec13b TL |
105 | int main (argc, argv, envp) |
106 | int argc; | |
107 | char **argv, **envp; | |
108 | { | |
109 | int i; | |
110 | struct servent *ent; | |
e7012e3f TL |
111 | struct server_list *sp = (struct server_list *)0; |
112 | int no_daemon = 0; | |
113 | int quiet = 0; | |
628beb0e | 114 | isc_result_t status; |
cfa7212d | 115 | char *s; |
747ec13b | 116 | |
dfb6c5aa TL |
117 | /* Make sure we have stdin, stdout and stderr. */ |
118 | i = open ("/dev/null", O_RDWR); | |
119 | if (i == 0) | |
120 | i = open ("/dev/null", O_RDWR); | |
121 | if (i == 1) { | |
122 | i = open ("/dev/null", O_RDWR); | |
123 | log_perror = 0; /* No sense logging to /dev/null. */ | |
124 | } else if (i != -1) | |
125 | close (i); | |
126 | ||
747ec13b TL |
127 | #ifdef SYSLOG_4_2 |
128 | openlog ("dhcrelay", LOG_NDELAY); | |
129 | log_priority = LOG_DAEMON; | |
130 | #else | |
131 | openlog ("dhcrelay", LOG_NDELAY, LOG_DAEMON); | |
132 | #endif | |
133 | ||
134 | #if !(defined (DEBUG) || defined (SYSLOG_4_2)) | |
135 | setlogmask (LOG_UPTO (LOG_INFO)); | |
136 | #endif | |
137 | ||
6cecb7c5 TL |
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)); | |
143 | ||
144 | /* Set up the OMAPI wrappers for the interface object. */ | |
145 | interface_setup (); | |
146 | ||
747ec13b TL |
147 | for (i = 1; i < argc; i++) { |
148 | if (!strcmp (argv [i], "-p")) { | |
149 | if (++i == argc) | |
150 | usage (); | |
151 | local_port = htons (atoi (argv [i])); | |
8ae2d595 | 152 | log_debug ("binding to user-specified port %d", |
747ec13b TL |
153 | ntohs (local_port)); |
154 | } else if (!strcmp (argv [i], "-d")) { | |
155 | no_daemon = 1; | |
156 | } else if (!strcmp (argv [i], "-i")) { | |
e7012e3f | 157 | struct interface_info *tmp = |
a9ef57b1 TL |
158 | (struct interface_info *)0; |
159 | status = interface_allocate (&tmp, MDL); | |
160 | if (status != ISC_R_SUCCESS) | |
161 | log_fatal ("%s: interface_allocate: %s", | |
162 | argv [i], | |
163 | isc_result_totext (status)); | |
e7012e3f TL |
164 | if (++i == argc) { |
165 | usage (); | |
166 | } | |
e7012e3f | 167 | strcpy (tmp -> name, argv [i]); |
f4c06dad | 168 | interface_snorf (tmp, INTERFACE_REQUESTED); |
a9ef57b1 | 169 | interface_dereference (&tmp, MDL); |
e7012e3f TL |
170 | } else if (!strcmp (argv [i], "-q")) { |
171 | quiet = 1; | |
172 | quiet_interface_discovery = 1; | |
866428dd TL |
173 | } else if (!strcmp (argv [i], "-a")) { |
174 | add_agent_options = 1; | |
98311e4b DH |
175 | } else if (!strcmp (argv [i], "-c")) { |
176 | int hcount; | |
177 | if (++i == argc) | |
178 | usage (); | |
179 | hcount = atoi(argv[i]); | |
180 | if (hcount <= 255) | |
181 | max_hop_count= hcount; | |
182 | else | |
183 | usage (); | |
866428dd TL |
184 | } else if (!strcmp (argv [i], "-A")) { |
185 | if (++i == argc) | |
186 | usage (); | |
187 | dhcp_max_agent_option_packet_length = atoi (argv [i]); | |
188 | } else if (!strcmp (argv [i], "-m")) { | |
189 | if (++i == argc) | |
190 | usage (); | |
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; | |
199 | } else | |
200 | usage (); | |
201 | } else if (!strcmp (argv [i], "-D")) { | |
202 | drop_agent_mismatches = 1; | |
747ec13b TL |
203 | } else if (argv [i][0] == '-') { |
204 | usage (); | |
36dd644c TL |
205 | } else if (!strcmp (argv [i], "--version")) { |
206 | log_info ("isc-dhcrelay-%s", DHCP_VERSION); | |
207 | exit (0); | |
747ec13b TL |
208 | } else { |
209 | struct hostent *he; | |
efce05d0 TL |
210 | struct in_addr ia, *iap = (struct in_addr *)0; |
211 | if (inet_aton (argv [i], &ia)) { | |
212 | iap = &ia; | |
75661c60 | 213 | } else { |
efce05d0 TL |
214 | he = gethostbyname (argv [i]); |
215 | if (!he) { | |
bc62fb2a TL |
216 | log_error ("%s: host unknown", |
217 | argv [i]); | |
efce05d0 TL |
218 | } else { |
219 | iap = ((struct in_addr *) | |
220 | he -> h_addr_list [0]); | |
221 | } | |
222 | } | |
223 | if (iap) { | |
4bd8800e TL |
224 | sp = ((struct server_list *) |
225 | dmalloc (sizeof *sp, MDL)); | |
75661c60 | 226 | if (!sp) |
8ae2d595 | 227 | log_fatal ("no memory for server.\n"); |
75661c60 TL |
228 | sp -> next = servers; |
229 | servers = sp; | |
230 | memcpy (&sp -> to.sin_addr, | |
efce05d0 | 231 | iap, sizeof *iap); |
747ec13b | 232 | } |
747ec13b TL |
233 | } |
234 | } | |
e7012e3f | 235 | |
cfa7212d TL |
236 | if ((s = getenv ("PATH_DHCRELAY_PID"))) { |
237 | path_dhcrelay_pid = s; | |
238 | } | |
239 | ||
d2bc90bd | 240 | if (!quiet) { |
31020a36 | 241 | log_info ("%s %s", message, DHCP_VERSION); |
8ae2d595 TL |
242 | log_info (copyright); |
243 | log_info (arr); | |
8ae2d595 | 244 | log_info (url); |
bc62fb2a | 245 | } else { |
c4f74946 | 246 | quiet = 0; |
bc62fb2a TL |
247 | log_perror = 0; |
248 | } | |
d2bc90bd | 249 | |
747ec13b TL |
250 | /* Default to the DHCP/BOOTP port. */ |
251 | if (!local_port) { | |
75661c60 | 252 | ent = getservbyname ("dhcps", "udp"); |
747ec13b | 253 | if (!ent) |
75661c60 | 254 | local_port = htons (67); |
747ec13b TL |
255 | else |
256 | local_port = ent -> s_port; | |
257 | endservent (); | |
258 | } | |
75661c60 | 259 | remote_port = htons (ntohs (local_port) + 1); |
747ec13b TL |
260 | |
261 | /* We need at least one server. */ | |
262 | if (!sp) { | |
263 | usage (); | |
264 | } | |
265 | ||
266 | /* Set up the server sockaddrs. */ | |
267 | for (sp = servers; sp; sp = sp -> next) { | |
5afe2747 | 268 | sp -> to.sin_port = local_port; |
747ec13b TL |
269 | sp -> to.sin_family = AF_INET; |
270 | #ifdef HAVE_SA_LEN | |
271 | sp -> to.sin_len = sizeof sp -> to; | |
272 | #endif | |
273 | } | |
274 | ||
275 | /* Get the current time... */ | |
276 | GET_TIME (&cur_time); | |
277 | ||
278 | /* Discover all the network interfaces. */ | |
75661c60 | 279 | discover_interfaces (DISCOVER_RELAY); |
747ec13b | 280 | |
84228fed TL |
281 | /* Set up the bootp packet handler... */ |
282 | bootp_packet_handler = relay; | |
283 | ||
e7012e3f TL |
284 | /* Become a daemon... */ |
285 | if (!no_daemon) { | |
286 | int pid; | |
287 | FILE *pf; | |
288 | int pfdesc; | |
289 | ||
290 | log_perror = 0; | |
291 | ||
292 | if ((pid = fork()) < 0) | |
8ae2d595 | 293 | log_fatal ("can't fork daemon: %m"); |
e7012e3f TL |
294 | else if (pid) |
295 | exit (0); | |
296 | ||
297 | pfdesc = open (path_dhcrelay_pid, | |
298 | O_CREAT | O_TRUNC | O_WRONLY, 0644); | |
299 | ||
300 | if (pfdesc < 0) { | |
8ae2d595 | 301 | log_error ("Can't create %s: %m", path_dhcrelay_pid); |
e7012e3f TL |
302 | } else { |
303 | pf = fdopen (pfdesc, "w"); | |
304 | if (!pf) | |
8ae2d595 | 305 | log_error ("Can't fdopen %s: %m", |
e7012e3f TL |
306 | path_dhcrelay_pid); |
307 | else { | |
19ea90f7 | 308 | fprintf (pf, "%ld\n", (long)getpid ()); |
e7012e3f TL |
309 | fclose (pf); |
310 | } | |
311 | } | |
312 | ||
313 | close (0); | |
314 | close (1); | |
315 | close (2); | |
316 | pid = setsid (); | |
317 | } | |
318 | ||
747ec13b | 319 | /* Start dispatching packets and timeouts... */ |
84228fed TL |
320 | dispatch (); |
321 | ||
747ec13b TL |
322 | /*NOTREACHED*/ |
323 | return 0; | |
324 | } | |
325 | ||
f90c6527 | 326 | void relay (ip, packet, length, from_port, from, hfrom) |
747ec13b | 327 | struct interface_info *ip; |
60dab809 | 328 | struct dhcp_packet *packet; |
b1b7b521 | 329 | unsigned length; |
15c1fd2c | 330 | unsigned int from_port; |
84228fed TL |
331 | struct iaddr from; |
332 | struct hardware *hfrom; | |
747ec13b TL |
333 | { |
334 | struct server_list *sp; | |
335 | struct sockaddr_in to; | |
336 | struct interface_info *out; | |
98311e4b | 337 | struct hardware hto, *htop; |
747ec13b | 338 | |
60dab809 | 339 | if (packet -> hlen > sizeof packet -> chaddr) { |
8ae2d595 | 340 | log_info ("Discarding packet with invalid hlen."); |
60dab809 TL |
341 | return; |
342 | } | |
343 | ||
866428dd TL |
344 | /* Find the interface that corresponds to the giaddr |
345 | in the packet. */ | |
346 | if (packet -> giaddr.s_addr) { | |
347 | for (out = interfaces; out; out = out -> next) { | |
348 | if (!memcmp (&out -> primary_address, | |
349 | &packet -> giaddr, | |
350 | sizeof packet -> giaddr)) | |
351 | break; | |
352 | } | |
353 | } else { | |
354 | out = (struct interface_info *)0; | |
355 | } | |
356 | ||
747ec13b TL |
357 | /* If it's a bootreply, forward it to the client. */ |
358 | if (packet -> op == BOOTREPLY) { | |
d2bc90bd | 359 | if (!(packet -> flags & htons (BOOTP_BROADCAST)) && |
764b75a3 | 360 | can_unicast_without_arp (out)) { |
90ddb81d | 361 | to.sin_addr = packet -> yiaddr; |
75661c60 | 362 | to.sin_port = remote_port; |
98311e4b DH |
363 | |
364 | /* and hardware address is not broadcast */ | |
365 | htop = &hto; | |
d2bc90bd | 366 | } else { |
747ec13b | 367 | to.sin_addr.s_addr = htonl (INADDR_BROADCAST); |
75661c60 | 368 | to.sin_port = remote_port; |
98311e4b DH |
369 | |
370 | /* hardware address is broadcast */ | |
371 | htop = NULL; | |
747ec13b TL |
372 | } |
373 | to.sin_family = AF_INET; | |
374 | #ifdef HAVE_SA_LEN | |
375 | to.sin_len = sizeof to; | |
376 | #endif | |
377 | ||
9e9b2261 TL |
378 | memcpy (&hto.hbuf [1], packet -> chaddr, packet -> hlen); |
379 | hto.hbuf [0] = packet -> htype; | |
380 | hto.hlen = packet -> hlen + 1; | |
747ec13b | 381 | |
866428dd TL |
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 | |
385 | replying. */ | |
386 | if (!(length = | |
387 | strip_relay_agent_options (ip, &out, packet, length))) | |
388 | return; | |
389 | ||
747ec13b | 390 | if (!out) { |
8ae2d595 | 391 | log_error ("packet to bogus giaddr %s.\n", |
747ec13b | 392 | inet_ntoa (packet -> giaddr)); |
866428dd | 393 | ++bogus_giaddr_drops; |
747ec13b TL |
394 | return; |
395 | } | |
396 | ||
397 | if (send_packet (out, | |
398 | (struct packet *)0, | |
399 | packet, length, out -> primary_address, | |
98311e4b | 400 | &to, htop) < 0) { |
866428dd TL |
401 | ++server_packet_errors; |
402 | } else { | |
8ae2d595 | 403 | log_debug ("forwarded BOOTREPLY for %s to %s", |
75661c60 TL |
404 | print_hw_addr (packet -> htype, packet -> hlen, |
405 | packet -> chaddr), | |
406 | inet_ntoa (to.sin_addr)); | |
407 | ||
866428dd TL |
408 | ++server_packets_relayed; |
409 | } | |
747ec13b TL |
410 | return; |
411 | } | |
412 | ||
866428dd TL |
413 | /* If giaddr matches one of our addresses, ignore the packet - |
414 | we just sent it. */ | |
415 | if (out) | |
416 | return; | |
417 | ||
418 | /* Add relay agent options if indicated. If something goes wrong, | |
419 | drop the packet. */ | |
420 | if (!(length = add_relay_agent_options (ip, packet, length, | |
421 | ip -> primary_address))) | |
75661c60 | 422 | return; |
75661c60 | 423 | |
866428dd TL |
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; | |
98311e4b DH |
431 | if (packet -> hops < max_hop_count) |
432 | packet -> hops = packet -> hops + 1; | |
433 | else | |
434 | return; | |
75661c60 | 435 | |
747ec13b TL |
436 | /* Otherwise, it's a BOOTREQUEST, so forward it to all the |
437 | servers. */ | |
438 | for (sp = servers; sp; sp = sp -> next) { | |
d2bc90bd TL |
439 | if (send_packet ((fallback_interface |
440 | ? fallback_interface : interfaces), | |
747ec13b TL |
441 | (struct packet *)0, |
442 | packet, length, ip -> primary_address, | |
d2bc90bd | 443 | &sp -> to, (struct hardware *)0) < 0) { |
866428dd | 444 | ++client_packet_errors; |
747ec13b | 445 | } else { |
8ae2d595 | 446 | log_debug ("forwarded BOOTREQUEST for %s to %s", |
747ec13b TL |
447 | print_hw_addr (packet -> htype, packet -> hlen, |
448 | packet -> chaddr), | |
449 | inet_ntoa (sp -> to.sin_addr)); | |
866428dd | 450 | ++client_packets_relayed; |
747ec13b TL |
451 | } |
452 | } | |
453 | ||
454 | } | |
455 | ||
456 | static void usage () | |
457 | { | |
98311e4b DH |
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]]"); | |
747ec13b TL |
463 | } |
464 | ||
747ec13b TL |
465 | int write_lease (lease) |
466 | struct lease *lease; | |
467 | { | |
468 | return 1; | |
469 | } | |
470 | ||
628beb0e TL |
471 | int write_host (host) |
472 | struct host_decl *host; | |
473 | { | |
474 | return 1; | |
475 | } | |
476 | ||
747ec13b TL |
477 | int commit_leases () |
478 | { | |
479 | return 1; | |
480 | } | |
481 | ||
482 | void bootp (packet) | |
483 | struct packet *packet; | |
484 | { | |
485 | } | |
486 | ||
487 | void dhcp (packet) | |
488 | struct packet *packet; | |
489 | { | |
490 | } | |
f90c6527 | 491 | |
20916cae TL |
492 | int find_subnet (struct subnet **sp, |
493 | struct iaddr addr, const char *file, int line) | |
f90c6527 | 494 | { |
20916cae | 495 | return 0; |
f90c6527 | 496 | } |
866428dd | 497 | |
1a48c9c3 TL |
498 | #if defined (DEBUG) |
499 | int check_collection (struct packet *p, struct lease *l, | |
500 | struct collection *c) | |
501 | { | |
502 | return 0; | |
503 | } | |
504 | ||
505 | void classify (struct packet *p, struct class *c) | |
506 | { | |
507 | } | |
508 | ||
509 | isc_result_t find_class (struct class **class, const char *c1, | |
510 | const char *c2, int i) | |
511 | { | |
512 | return ISC_R_NOTFOUND; | |
513 | } | |
514 | ||
515 | int parse_allow_deny (struct option_cache **oc, struct parse *p, int i) | |
516 | { | |
517 | return 0; | |
518 | } | |
519 | ||
98311e4b DH |
520 | /* As a wise man once said in dhcpctl/omshell.c: */ |
521 | /* Sigh */ | |
522 | isc_result_t dhcp_set_control_state (control_object_state_t oldstate, | |
523 | control_object_state_t newstate) | |
524 | { | |
525 | return ISC_R_SUCCESS; | |
526 | } | |
527 | ||
1a48c9c3 TL |
528 | #endif |
529 | ||
866428dd | 530 | /* Strip any Relay Agent Information options from the DHCP packet |
98311e4b DH |
531 | option buffer. If there is a circuit ID suboption, look up the |
532 | outgoing interface based upon it. */ | |
866428dd TL |
533 | |
534 | int strip_relay_agent_options (in, out, packet, length) | |
535 | struct interface_info *in, **out; | |
536 | struct dhcp_packet *packet; | |
b1b7b521 | 537 | unsigned length; |
866428dd TL |
538 | { |
539 | int is_dhcp = 0; | |
88cd8aca | 540 | u_int8_t *op, *nextop, *sp, *max; |
866428dd TL |
541 | int good_agent_option = 0; |
542 | int status; | |
543 | ||
544 | /* If we're not adding agent options to packets, we're not taking | |
545 | them out either. */ | |
546 | if (!add_agent_options) | |
547 | return length; | |
548 | ||
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)) | |
552 | return length; | |
553 | ||
554 | max = ((u_int8_t *)packet) + length; | |
555 | sp = op = &packet -> options [4]; | |
556 | ||
557 | while (op < max) { | |
558 | switch (*op) { | |
559 | /* Skip padding... */ | |
560 | case DHO_PAD: | |
561 | if (sp != op) | |
562 | *sp = *op; | |
563 | ++op; | |
564 | ++sp; | |
565 | continue; | |
566 | ||
567 | /* If we see a message type, it's a DHCP packet. */ | |
568 | case DHO_DHCP_MESSAGE_TYPE: | |
569 | is_dhcp = 1; | |
570 | goto skip; | |
571 | break; | |
572 | ||
573 | /* Quit immediately if we hit an End option. */ | |
574 | case DHO_END: | |
575 | if (sp != op) | |
576 | *sp++ = *op++; | |
577 | goto out; | |
578 | ||
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. */ | |
583 | if (!is_dhcp) | |
584 | goto skip; | |
585 | ||
88cd8aca DH |
586 | /* Do not process an agent option if it exceeds the |
587 | * buffer. Fail this packet. | |
588 | */ | |
589 | nextop = op + op[1] + 2; | |
590 | if (nextop > max) | |
591 | return 0; | |
592 | ||
866428dd TL |
593 | status = find_interface_by_agent_option (packet, |
594 | out, op + 2, | |
595 | op [1]); | |
596 | if (status == -1 && drop_agent_mismatches) | |
597 | return 0; | |
598 | if (status) | |
599 | good_agent_option = 1; | |
88cd8aca | 600 | op = nextop; |
866428dd TL |
601 | break; |
602 | ||
603 | skip: | |
604 | /* Skip over other options. */ | |
605 | default: | |
88cd8aca DH |
606 | /* Fail if processing this option will exceed the |
607 | * buffer (op[1] is malformed). | |
608 | */ | |
609 | nextop = op + op[1] + 2; | |
610 | if (nextop > max) | |
611 | return 0; | |
612 | ||
613 | if (sp != op) { | |
614 | memmove(sp, op, op[1] + 2); | |
615 | sp += op[1] + 2; | |
616 | op = nextop; | |
617 | } else | |
618 | op = sp = nextop; | |
619 | ||
866428dd TL |
620 | break; |
621 | } | |
622 | } | |
623 | out: | |
624 | ||
625 | /* If it's not a DHCP packet, we're not supposed to touch it. */ | |
626 | if (!is_dhcp) | |
627 | return length; | |
628 | ||
629 | /* If none of the agent options we found matched, or if we didn't | |
630 | find any agent options, count this packet as not having any | |
631 | matching agent options, and if we're relying on agent options | |
632 | to determine the outgoing interface, drop the packet. */ | |
633 | ||
634 | if (!good_agent_option) { | |
635 | ++missing_agent_option; | |
636 | if (drop_agent_mismatches) | |
637 | return 0; | |
638 | } | |
639 | ||
640 | /* Adjust the length... */ | |
641 | if (sp != op) { | |
642 | length = sp - ((u_int8_t *)packet); | |
643 | ||
644 | /* Make sure the packet isn't short (this is unlikely, | |
645 | but WTH) */ | |
646 | if (length < BOOTP_MIN_LEN) { | |
88cd8aca | 647 | memset (sp, DHO_PAD, BOOTP_MIN_LEN - length); |
866428dd TL |
648 | length = BOOTP_MIN_LEN; |
649 | } | |
650 | } | |
651 | return length; | |
652 | } | |
653 | ||
654 | ||
655 | /* Find an interface that matches the circuit ID specified in the | |
656 | Relay Agent Information option. If one is found, store it through | |
657 | the pointer given; otherwise, leave the existing pointer alone. | |
658 | ||
659 | We actually deviate somewhat from the current specification here: | |
660 | if the option buffer is corrupt, we suggest that the caller not | |
661 | respond to this packet. If the circuit ID doesn't match any known | |
662 | interface, we suggest that the caller to drop the packet. Only if | |
663 | we find a circuit ID that matches an existing interface do we tell | |
664 | the caller to go ahead and process the packet. */ | |
665 | ||
666 | int find_interface_by_agent_option (packet, out, buf, len) | |
667 | struct dhcp_packet *packet; | |
668 | struct interface_info **out; | |
669 | u_int8_t *buf; | |
670 | int len; | |
671 | { | |
639b70fa | 672 | int i = 0; |
866428dd | 673 | u_int8_t *circuit_id = 0; |
98311e4b | 674 | unsigned circuit_id_len = 0; |
866428dd TL |
675 | struct interface_info *ip; |
676 | ||
677 | while (i < len) { | |
678 | /* If the next agent option overflows the end of the | |
679 | packet, the agent option buffer is corrupt. */ | |
680 | if (i + 1 == len || | |
681 | i + buf [i + 1] + 2 > len) { | |
682 | ++corrupt_agent_options; | |
683 | return -1; | |
684 | } | |
685 | switch (buf [i]) { | |
686 | /* Remember where the circuit ID is... */ | |
687 | case RAI_CIRCUIT_ID: | |
688 | circuit_id = &buf [i + 2]; | |
689 | circuit_id_len = buf [i + 1]; | |
690 | i += circuit_id_len + 2; | |
691 | continue; | |
692 | ||
693 | default: | |
694 | i += buf [i + 1] + 2; | |
695 | break; | |
696 | } | |
697 | } | |
698 | ||
699 | /* If there's no circuit ID, it's not really ours, tell the caller | |
700 | it's no good. */ | |
701 | if (!circuit_id) { | |
702 | ++missing_circuit_id; | |
703 | return -1; | |
704 | } | |
705 | ||
706 | /* Scan the interface list looking for an interface whose | |
707 | name matches the one specified in circuit_id. */ | |
708 | ||
709 | for (ip = interfaces; ip; ip = ip -> next) { | |
710 | if (ip -> circuit_id && | |
711 | ip -> circuit_id_len == circuit_id_len && | |
712 | !memcmp (ip -> circuit_id, circuit_id, circuit_id_len)) | |
713 | break; | |
714 | } | |
715 | ||
716 | /* If we got a match, use it. */ | |
717 | if (ip) { | |
718 | *out = ip; | |
719 | return 1; | |
720 | } | |
721 | ||
722 | /* If we didn't get a match, the circuit ID was bogus. */ | |
723 | ++bad_circuit_id; | |
724 | return -1; | |
725 | } | |
726 | ||
727 | /* Examine a packet to see if it's a candidate to have a Relay | |
728 | Agent Information option tacked onto its tail. If it is, tack | |
729 | the option on. */ | |
730 | ||
731 | int add_relay_agent_options (ip, packet, length, giaddr) | |
732 | struct interface_info *ip; | |
733 | struct dhcp_packet *packet; | |
b1b7b521 | 734 | unsigned length; |
866428dd TL |
735 | struct in_addr giaddr; |
736 | { | |
737 | int is_dhcp = 0, agent_options_present = 0; | |
88cd8aca DH |
738 | unsigned optlen; |
739 | u_int8_t *op, *nextop, *sp, *max, *end_pad = NULL; | |
866428dd TL |
740 | |
741 | /* If we're not adding agent options to packets, we can skip | |
742 | this. */ | |
743 | if (!add_agent_options) | |
744 | return length; | |
745 | ||
746 | /* If there's no cookie, it's a bootp packet, so we should just | |
747 | forward it unchanged. */ | |
88cd8aca | 748 | if (memcmp(packet->options, DHCP_OPTIONS_COOKIE, 4)) |
866428dd TL |
749 | return length; |
750 | ||
751 | max = ((u_int8_t *)packet) + length; | |
88cd8aca DH |
752 | |
753 | /* Commence processing after the cookie. */ | |
754 | sp = op = &packet->options[4]; | |
866428dd TL |
755 | |
756 | while (op < max) { | |
757 | switch (*op) { | |
758 | /* Skip padding... */ | |
759 | case DHO_PAD: | |
88cd8aca DH |
760 | /* Remember the first pad byte so we can commandeer |
761 | * padded space. | |
762 | * | |
763 | * XXX: Is this really a good idea? Sure, we can | |
764 | * seemingly reduce the packet while we're looking, | |
765 | * but if the packet was signed by the client then | |
766 | * this padding is part of the checksum (RFC3118), | |
767 | * and its nonpresence would break authentication. | |
768 | */ | |
769 | if (end_pad == NULL) | |
770 | end_pad = sp; | |
771 | ||
866428dd | 772 | if (sp != op) |
88cd8aca DH |
773 | *sp++ = *op++; |
774 | else | |
775 | sp = ++op; | |
776 | ||
866428dd TL |
777 | continue; |
778 | ||
779 | /* If we see a message type, it's a DHCP packet. */ | |
780 | case DHO_DHCP_MESSAGE_TYPE: | |
781 | is_dhcp = 1; | |
782 | goto skip; | |
783 | break; | |
784 | ||
785 | /* Quit immediately if we hit an End option. */ | |
786 | case DHO_END: | |
787 | goto out; | |
788 | ||
789 | case DHO_DHCP_AGENT_OPTIONS: | |
790 | /* We shouldn't see a relay agent option in a | |
791 | packet before we've seen the DHCP packet type, | |
792 | but if we do, we have to leave it alone. */ | |
793 | if (!is_dhcp) | |
794 | goto skip; | |
88cd8aca DH |
795 | |
796 | end_pad = NULL; | |
866428dd TL |
797 | |
798 | /* There's already a Relay Agent Information option | |
799 | in this packet. How embarrassing. Decide what | |
800 | to do based on the mode the user specified. */ | |
801 | ||
802 | switch (agent_relay_mode) { | |
803 | case forward_and_append: | |
804 | goto skip; | |
805 | case forward_untouched: | |
806 | return length; | |
807 | case discard: | |
808 | return 0; | |
809 | case forward_and_replace: | |
810 | default: | |
811 | break; | |
812 | } | |
813 | ||
814 | /* Skip over the agent option and start copying | |
815 | if we aren't copying already. */ | |
88cd8aca | 816 | op += op[1] + 2; |
866428dd TL |
817 | break; |
818 | ||
819 | skip: | |
820 | /* Skip over other options. */ | |
821 | default: | |
88cd8aca DH |
822 | /* Fail if processing this option will exceed the |
823 | * buffer (op[1] is malformed). | |
824 | */ | |
825 | nextop = op + op[1] + 2; | |
826 | if (nextop > max) | |
827 | return 0; | |
828 | ||
829 | end_pad = NULL; | |
830 | ||
831 | if (sp != op) { | |
832 | memmove(sp, op, op[1] + 2); | |
833 | sp += op[1] + 2; | |
834 | op = nextop; | |
835 | } else | |
836 | op = sp = nextop; | |
837 | ||
866428dd TL |
838 | break; |
839 | } | |
840 | } | |
841 | out: | |
842 | ||
843 | /* If it's not a DHCP packet, we're not supposed to touch it. */ | |
844 | if (!is_dhcp) | |
845 | return length; | |
846 | ||
847 | /* If the packet was padded out, we can store the agent option | |
848 | at the beginning of the padding. */ | |
849 | ||
88cd8aca | 850 | if (end_pad != NULL) |
866428dd TL |
851 | sp = end_pad; |
852 | ||
853 | /* Remember where the end of the packet was after parsing | |
854 | it. */ | |
855 | op = sp; | |
856 | ||
88cd8aca DH |
857 | /* Sanity check. Had better not ever happen. */ |
858 | if ((ip->circuit_id_len > 255) || (ip->circuit_id_len < 1)) | |
859 | log_fatal("circuit id length %d out of range [1-255] on " | |
860 | "%s\n", ip->circuit_id_len, ip->name); | |
861 | optlen = ip->circuit_id_len + 2; /* RAI_CIRCUIT_ID + len */ | |
862 | ||
863 | if (ip->remote_id) { | |
864 | if (ip->remote_id_len > 255 || ip->remote_id_len < 1) | |
865 | log_fatal("remote id length %d out of range [1-255] " | |
866 | "on %s\n", ip->circuit_id_len, ip->name); | |
867 | optlen += ip->remote_id_len + 2; /* RAI_REMOTE_ID + len */ | |
868 | } | |
869 | ||
870 | /* We do not support relay option fragmenting (multiple options to | |
871 | * support an option data exceeding 255 bytes). | |
872 | */ | |
873 | if ((optlen < 3) || (optlen > 255)) | |
874 | log_fatal ("total agent option length (%u) out of range " | |
875 | "[3 - 255] on %s\n", optlen, ip->name); | |
876 | ||
877 | /* Is there room for the option, its code+len, and DHO_END? */ | |
878 | if ((sp > max) || (max - sp < optlen + 3)) | |
879 | return 0; | |
866428dd TL |
880 | |
881 | /* Okay, cons up *our* Relay Agent Information option. */ | |
882 | *sp++ = DHO_DHCP_AGENT_OPTIONS; | |
88cd8aca | 883 | *sp++ = optlen; |
866428dd TL |
884 | |
885 | /* Copy in the circuit id... */ | |
886 | *sp++ = RAI_CIRCUIT_ID; | |
88cd8aca DH |
887 | *sp++ = ip->circuit_id_len; |
888 | memcpy(sp, ip->circuit_id, ip->circuit_id_len); | |
889 | sp += ip->circuit_id_len; | |
866428dd TL |
890 | |
891 | /* Copy in remote ID... */ | |
88cd8aca | 892 | if (ip->remote_id) { |
866428dd | 893 | *sp++ = RAI_REMOTE_ID; |
88cd8aca DH |
894 | *sp++ = ip->remote_id_len; |
895 | memcpy(sp, ip->remote_id, ip->remote_id_len); | |
896 | sp += ip->remote_id_len; | |
866428dd TL |
897 | } |
898 | ||
88cd8aca | 899 | /* Deposit an END option. */ |
866428dd TL |
900 | *sp++ = DHO_END; |
901 | ||
902 | /* Recalculate total packet length. */ | |
903 | length = sp - ((u_int8_t *)packet); | |
904 | ||
905 | /* Make sure the packet isn't short (this is unlikely, but WTH) */ | |
906 | if (length < BOOTP_MIN_LEN) { | |
88cd8aca DH |
907 | memset(sp, DHO_PAD, BOOTP_MIN_LEN - length); |
908 | return BOOTP_MIN_LEN; | |
866428dd TL |
909 | } |
910 | ||
911 | return length; | |
912 | } |