]>
Commit | Line | Data |
---|---|---|
747ec13b TL |
1 | /* dhcrelay.c |
2 | ||
3 | DHCP/BOOTP Relay Agent. */ | |
4 | ||
5 | /* | |
f39b6e00 TL |
6 | * Copyright (c) 1996-1999 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: | |
747ec13b | 12 | * |
f39b6e00 | 13 | * http://www.isc.org/isc-license-1.0.html. |
747ec13b | 14 | * |
f39b6e00 TL |
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. | |
747ec13b | 18 | * |
f39b6e00 TL |
19 | * Support and other services are available for ISC products - see |
20 | * http://www.isc.org for more information. | |
747ec13b TL |
21 | */ |
22 | ||
23 | #ifndef lint | |
339b0231 | 24 | static char ocopyright[] = |
f39b6e00 | 25 | "$Id: dhcrelay.c,v 1.23 1999/03/16 05:50:41 mellon Exp $ Copyright (c) 1997, 1998, 1999 The Internet Software Consortium. All rights reserved.\n"; |
747ec13b TL |
26 | #endif /* not lint */ |
27 | ||
28 | #include "dhcpd.h" | |
29 | ||
30 | static void usage PROTO ((void)); | |
31 | ||
32 | TIME cur_time; | |
33 | TIME default_lease_time = 43200; /* 12 hours... */ | |
34 | TIME max_lease_time = 86400; /* 24 hours... */ | |
35 | struct tree_cache *global_options [256]; | |
36 | ||
37 | int log_perror = 1; | |
38 | ||
aaf053e3 TL |
39 | /* Needed to prevent linking against conflex.c. */ |
40 | int lexline; | |
41 | int lexchar; | |
42 | char *token_line; | |
43 | char *tlname; | |
44 | ||
e7012e3f TL |
45 | char *path_dhcrelay_pid = _PATH_DHCRELAY_PID; |
46 | ||
866428dd TL |
47 | int bogus_agent_drops = 0; /* Packets dropped because agent option |
48 | field was specified and we're not relaying | |
49 | packets that already have an agent option | |
50 | specified. */ | |
51 | int bogus_giaddr_drops = 0; /* Packets sent to us to relay back to a | |
52 | client, but with a bogus giaddr. */ | |
53 | int client_packets_relayed = 0; /* Packets relayed from client to server. */ | |
54 | int server_packet_errors = 0; /* Errors sending packets to servers. */ | |
55 | int server_packets_relayed = 0; /* Packets relayed from server to client. */ | |
56 | int client_packet_errors = 0; /* Errors sending packets to clients. */ | |
57 | ||
58 | int add_agent_options = 0; /* If nonzero, add relay agent options. */ | |
59 | int drop_agent_mismatches = 0; /* If nonzero, drop server replies that | |
60 | don't contain a Relay Agent Information | |
61 | option whose Agent ID suboption matches | |
62 | our giaddr. */ | |
63 | int corrupt_agent_options = 0; /* Number of packets dropped because | |
64 | relay agent information option was bad. */ | |
65 | int missing_agent_option = 0; /* Number of packets dropped because no | |
66 | RAI option matching our ID was found. */ | |
67 | int bad_circuit_id = 0; /* Circuit ID option in matching RAI option | |
68 | did not match any known circuit ID. */ | |
69 | int missing_circuit_id = 0; /* Circuit ID option in matching RAI option | |
70 | was missing. */ | |
71 | ||
72 | /* Maximum size of a packet with agent options added. */ | |
73 | int dhcp_max_agent_option_packet_length = 576; | |
74 | ||
75 | /* What to do about packets we're asked to relay that | |
76 | already have a relay option: */ | |
77 | enum { forward_and_append, /* Forward and append our own relay option. */ | |
78 | forward_and_replace, /* Forward, but replace theirs with ours. */ | |
79 | forward_untouched, /* Forward without changes. */ | |
80 | discard } agent_relay_mode = forward_and_replace; | |
81 | ||
747ec13b TL |
82 | u_int16_t local_port; |
83 | u_int16_t remote_port; | |
84 | int log_priority; | |
85 | ||
86 | struct server_list { | |
87 | struct server_list *next; | |
88 | struct sockaddr_in to; | |
89 | } *servers; | |
90 | ||
d2bc90bd TL |
91 | static char copyright [] = |
92 | "Copyright 1997, 1998, 1999 The Internet Software Consortium."; | |
93 | static char arr [] = "All rights reserved."; | |
b340aa46 | 94 | static char message [] = "Internet Software Consortium DHCP Relay Agent V3.0-alpha 19990315"; |
d2bc90bd TL |
95 | static char contrib [] = "\nPlease contribute if you find this software useful."; |
96 | static char url [] = "For info, please visit http://www.isc.org/dhcp-contrib.html\n"; | |
97 | ||
747ec13b TL |
98 | int main (argc, argv, envp) |
99 | int argc; | |
100 | char **argv, **envp; | |
101 | { | |
102 | int i; | |
103 | struct servent *ent; | |
e7012e3f TL |
104 | struct server_list *sp = (struct server_list *)0; |
105 | int no_daemon = 0; | |
106 | int quiet = 0; | |
747ec13b TL |
107 | |
108 | #ifdef SYSLOG_4_2 | |
109 | openlog ("dhcrelay", LOG_NDELAY); | |
110 | log_priority = LOG_DAEMON; | |
111 | #else | |
112 | openlog ("dhcrelay", LOG_NDELAY, LOG_DAEMON); | |
113 | #endif | |
114 | ||
115 | #if !(defined (DEBUG) || defined (SYSLOG_4_2)) | |
116 | setlogmask (LOG_UPTO (LOG_INFO)); | |
117 | #endif | |
118 | ||
119 | for (i = 1; i < argc; i++) { | |
120 | if (!strcmp (argv [i], "-p")) { | |
121 | if (++i == argc) | |
122 | usage (); | |
123 | local_port = htons (atoi (argv [i])); | |
8ae2d595 | 124 | log_debug ("binding to user-specified port %d", |
747ec13b TL |
125 | ntohs (local_port)); |
126 | } else if (!strcmp (argv [i], "-d")) { | |
127 | no_daemon = 1; | |
128 | } else if (!strcmp (argv [i], "-i")) { | |
e7012e3f TL |
129 | struct interface_info *tmp = |
130 | ((struct interface_info *) | |
131 | dmalloc (sizeof *tmp, "specified_interface")); | |
132 | if (!tmp) | |
8ae2d595 | 133 | log_fatal ("Insufficient memory to %s %s", |
e7012e3f TL |
134 | "record interface", argv [i]); |
135 | if (++i == argc) { | |
136 | usage (); | |
137 | } | |
138 | memset (tmp, 0, sizeof *tmp); | |
139 | strcpy (tmp -> name, argv [i]); | |
140 | tmp -> next = interfaces; | |
141 | tmp -> flags = INTERFACE_REQUESTED; | |
142 | interfaces = tmp; | |
143 | } else if (!strcmp (argv [i], "-q")) { | |
144 | quiet = 1; | |
145 | quiet_interface_discovery = 1; | |
866428dd TL |
146 | } else if (!strcmp (argv [i], "-a")) { |
147 | add_agent_options = 1; | |
148 | } else if (!strcmp (argv [i], "-A")) { | |
149 | if (++i == argc) | |
150 | usage (); | |
151 | dhcp_max_agent_option_packet_length = atoi (argv [i]); | |
152 | } else if (!strcmp (argv [i], "-m")) { | |
153 | if (++i == argc) | |
154 | usage (); | |
155 | if (!strcasecmp (argv [i], "append")) { | |
156 | agent_relay_mode = forward_and_append; | |
157 | } else if (!strcasecmp (argv [i], "replace")) { | |
158 | agent_relay_mode = forward_and_replace; | |
159 | } else if (!strcasecmp (argv [i], "forward")) { | |
160 | agent_relay_mode = forward_untouched; | |
161 | } else if (!strcasecmp (argv [i], "discard")) { | |
162 | agent_relay_mode = discard; | |
163 | } else | |
164 | usage (); | |
165 | } else if (!strcmp (argv [i], "-D")) { | |
166 | drop_agent_mismatches = 1; | |
747ec13b TL |
167 | } else if (argv [i][0] == '-') { |
168 | usage (); | |
169 | } else { | |
170 | struct hostent *he; | |
efce05d0 TL |
171 | struct in_addr ia, *iap = (struct in_addr *)0; |
172 | if (inet_aton (argv [i], &ia)) { | |
173 | iap = &ia; | |
75661c60 | 174 | } else { |
efce05d0 TL |
175 | he = gethostbyname (argv [i]); |
176 | if (!he) { | |
8ae2d595 | 177 | log_error ("%s: host unknown", argv [i]); |
efce05d0 TL |
178 | } else { |
179 | iap = ((struct in_addr *) | |
180 | he -> h_addr_list [0]); | |
181 | } | |
182 | } | |
183 | if (iap) { | |
75661c60 TL |
184 | sp = (struct server_list *)malloc (sizeof *sp); |
185 | if (!sp) | |
8ae2d595 | 186 | log_fatal ("no memory for server.\n"); |
75661c60 TL |
187 | sp -> next = servers; |
188 | servers = sp; | |
189 | memcpy (&sp -> to.sin_addr, | |
efce05d0 | 190 | iap, sizeof *iap); |
747ec13b | 191 | } |
747ec13b TL |
192 | } |
193 | } | |
e7012e3f | 194 | |
d2bc90bd | 195 | if (!quiet) { |
8ae2d595 TL |
196 | log_info (message); |
197 | log_info (copyright); | |
198 | log_info (arr); | |
199 | log_info (contrib); | |
200 | log_info (url); | |
d2bc90bd TL |
201 | } |
202 | ||
747ec13b TL |
203 | /* Default to the DHCP/BOOTP port. */ |
204 | if (!local_port) { | |
75661c60 | 205 | ent = getservbyname ("dhcps", "udp"); |
747ec13b | 206 | if (!ent) |
75661c60 | 207 | local_port = htons (67); |
747ec13b TL |
208 | else |
209 | local_port = ent -> s_port; | |
210 | endservent (); | |
211 | } | |
75661c60 | 212 | remote_port = htons (ntohs (local_port) + 1); |
747ec13b TL |
213 | |
214 | /* We need at least one server. */ | |
215 | if (!sp) { | |
216 | usage (); | |
217 | } | |
218 | ||
219 | /* Set up the server sockaddrs. */ | |
220 | for (sp = servers; sp; sp = sp -> next) { | |
5afe2747 | 221 | sp -> to.sin_port = local_port; |
747ec13b TL |
222 | sp -> to.sin_family = AF_INET; |
223 | #ifdef HAVE_SA_LEN | |
224 | sp -> to.sin_len = sizeof sp -> to; | |
225 | #endif | |
226 | } | |
227 | ||
228 | /* Get the current time... */ | |
229 | GET_TIME (&cur_time); | |
230 | ||
231 | /* Discover all the network interfaces. */ | |
75661c60 | 232 | discover_interfaces (DISCOVER_RELAY); |
747ec13b | 233 | |
84228fed TL |
234 | /* Set up the bootp packet handler... */ |
235 | bootp_packet_handler = relay; | |
236 | ||
e7012e3f TL |
237 | /* Become a daemon... */ |
238 | if (!no_daemon) { | |
239 | int pid; | |
240 | FILE *pf; | |
241 | int pfdesc; | |
242 | ||
243 | log_perror = 0; | |
244 | ||
245 | if ((pid = fork()) < 0) | |
8ae2d595 | 246 | log_fatal ("can't fork daemon: %m"); |
e7012e3f TL |
247 | else if (pid) |
248 | exit (0); | |
249 | ||
250 | pfdesc = open (path_dhcrelay_pid, | |
251 | O_CREAT | O_TRUNC | O_WRONLY, 0644); | |
252 | ||
253 | if (pfdesc < 0) { | |
8ae2d595 | 254 | log_error ("Can't create %s: %m", path_dhcrelay_pid); |
e7012e3f TL |
255 | } else { |
256 | pf = fdopen (pfdesc, "w"); | |
257 | if (!pf) | |
8ae2d595 | 258 | log_error ("Can't fdopen %s: %m", |
e7012e3f TL |
259 | path_dhcrelay_pid); |
260 | else { | |
19ea90f7 | 261 | fprintf (pf, "%ld\n", (long)getpid ()); |
e7012e3f TL |
262 | fclose (pf); |
263 | } | |
264 | } | |
265 | ||
266 | close (0); | |
267 | close (1); | |
268 | close (2); | |
269 | pid = setsid (); | |
270 | } | |
271 | ||
747ec13b | 272 | /* Start dispatching packets and timeouts... */ |
84228fed TL |
273 | dispatch (); |
274 | ||
747ec13b TL |
275 | /*NOTREACHED*/ |
276 | return 0; | |
277 | } | |
278 | ||
f90c6527 | 279 | void relay (ip, packet, length, from_port, from, hfrom) |
747ec13b | 280 | struct interface_info *ip; |
60dab809 | 281 | struct dhcp_packet *packet; |
747ec13b | 282 | int length; |
15c1fd2c | 283 | unsigned int from_port; |
84228fed TL |
284 | struct iaddr from; |
285 | struct hardware *hfrom; | |
747ec13b TL |
286 | { |
287 | struct server_list *sp; | |
288 | struct sockaddr_in to; | |
289 | struct interface_info *out; | |
290 | struct hardware hto; | |
291 | ||
60dab809 | 292 | if (packet -> hlen > sizeof packet -> chaddr) { |
8ae2d595 | 293 | log_info ("Discarding packet with invalid hlen."); |
60dab809 TL |
294 | return; |
295 | } | |
296 | ||
866428dd TL |
297 | /* XXX Dave: |
298 | If you're using the circuit ID to figure out where to | |
299 | send the reply, you can delete the following code, | |
300 | but you still need to validate the giaddr and drop the | |
301 | packet if it's bogus. */ | |
302 | /* Find the interface that corresponds to the giaddr | |
303 | in the packet. */ | |
304 | if (packet -> giaddr.s_addr) { | |
305 | for (out = interfaces; out; out = out -> next) { | |
306 | if (!memcmp (&out -> primary_address, | |
307 | &packet -> giaddr, | |
308 | sizeof packet -> giaddr)) | |
309 | break; | |
310 | } | |
311 | } else { | |
312 | out = (struct interface_info *)0; | |
313 | } | |
314 | ||
747ec13b TL |
315 | /* If it's a bootreply, forward it to the client. */ |
316 | if (packet -> op == BOOTREPLY) { | |
d2bc90bd | 317 | if (!(packet -> flags & htons (BOOTP_BROADCAST)) && |
764b75a3 | 318 | can_unicast_without_arp (out)) { |
90ddb81d | 319 | to.sin_addr = packet -> yiaddr; |
75661c60 | 320 | to.sin_port = remote_port; |
d2bc90bd | 321 | } else { |
747ec13b | 322 | to.sin_addr.s_addr = htonl (INADDR_BROADCAST); |
75661c60 | 323 | to.sin_port = remote_port; |
747ec13b TL |
324 | } |
325 | to.sin_family = AF_INET; | |
326 | #ifdef HAVE_SA_LEN | |
327 | to.sin_len = sizeof to; | |
328 | #endif | |
329 | ||
330 | memcpy (hto.haddr, packet -> chaddr, | |
331 | (packet -> hlen > sizeof hto.haddr | |
332 | ? sizeof hto.haddr | |
333 | : packet -> hlen)); | |
334 | hto.htype = packet -> htype; | |
335 | ||
866428dd TL |
336 | /* Wipe out the agent relay options and, if possible, figure |
337 | out which interface to use based on the contents of the | |
338 | option that we put on the request to which the server is | |
339 | replying. */ | |
340 | if (!(length = | |
341 | strip_relay_agent_options (ip, &out, packet, length))) | |
342 | return; | |
343 | ||
747ec13b | 344 | if (!out) { |
8ae2d595 | 345 | log_error ("packet to bogus giaddr %s.\n", |
747ec13b | 346 | inet_ntoa (packet -> giaddr)); |
866428dd | 347 | ++bogus_giaddr_drops; |
747ec13b TL |
348 | return; |
349 | } | |
350 | ||
351 | if (send_packet (out, | |
352 | (struct packet *)0, | |
353 | packet, length, out -> primary_address, | |
866428dd | 354 | &to, &hto) < 0) { |
866428dd TL |
355 | ++server_packet_errors; |
356 | } else { | |
8ae2d595 | 357 | log_debug ("forwarded BOOTREPLY for %s to %s", |
75661c60 TL |
358 | print_hw_addr (packet -> htype, packet -> hlen, |
359 | packet -> chaddr), | |
360 | inet_ntoa (to.sin_addr)); | |
361 | ||
866428dd TL |
362 | ++server_packets_relayed; |
363 | } | |
747ec13b TL |
364 | return; |
365 | } | |
366 | ||
866428dd TL |
367 | /* If giaddr matches one of our addresses, ignore the packet - |
368 | we just sent it. */ | |
369 | if (out) | |
370 | return; | |
371 | ||
372 | /* Add relay agent options if indicated. If something goes wrong, | |
373 | drop the packet. */ | |
374 | if (!(length = add_relay_agent_options (ip, packet, length, | |
375 | ip -> primary_address))) | |
75661c60 | 376 | return; |
75661c60 | 377 | |
866428dd TL |
378 | /* If giaddr is not already set, Set it so the server can |
379 | figure out what net it's from and so that we can later | |
380 | forward the response to the correct net. If it's already | |
381 | set, the response will be sent directly to the relay agent | |
382 | that set giaddr, so we won't see it. */ | |
383 | if (!packet -> giaddr.s_addr) | |
384 | packet -> giaddr = ip -> primary_address; | |
75661c60 | 385 | |
747ec13b TL |
386 | /* Otherwise, it's a BOOTREQUEST, so forward it to all the |
387 | servers. */ | |
388 | for (sp = servers; sp; sp = sp -> next) { | |
d2bc90bd TL |
389 | if (send_packet ((fallback_interface |
390 | ? fallback_interface : interfaces), | |
747ec13b TL |
391 | (struct packet *)0, |
392 | packet, length, ip -> primary_address, | |
d2bc90bd | 393 | &sp -> to, (struct hardware *)0) < 0) { |
866428dd | 394 | ++client_packet_errors; |
747ec13b | 395 | } else { |
8ae2d595 | 396 | log_debug ("forwarded BOOTREQUEST for %s to %s", |
747ec13b TL |
397 | print_hw_addr (packet -> htype, packet -> hlen, |
398 | packet -> chaddr), | |
399 | inet_ntoa (sp -> to.sin_addr)); | |
866428dd | 400 | ++client_packets_relayed; |
747ec13b TL |
401 | } |
402 | } | |
403 | ||
404 | } | |
405 | ||
406 | static void usage () | |
407 | { | |
74f45f96 TL |
408 | log_fatal ("Usage: dhcrelay [-p <port>] [-d] [-D] [-i %s%s%s", |
409 | "interface]\n ", | |
866428dd TL |
410 | "[-q] [-a] [-A length] [-m append|replace|forward|discard]\n", |
411 | " [server1 [... serverN]]"); | |
747ec13b TL |
412 | } |
413 | ||
414 | void cleanup () | |
415 | { | |
416 | } | |
417 | ||
418 | int write_lease (lease) | |
419 | struct lease *lease; | |
420 | { | |
421 | return 1; | |
422 | } | |
423 | ||
424 | int commit_leases () | |
425 | { | |
426 | return 1; | |
427 | } | |
428 | ||
429 | void bootp (packet) | |
430 | struct packet *packet; | |
431 | { | |
432 | } | |
433 | ||
434 | void dhcp (packet) | |
435 | struct packet *packet; | |
436 | { | |
437 | } | |
f90c6527 TL |
438 | |
439 | struct subnet *find_subnet (addr) | |
440 | struct iaddr addr; | |
441 | { | |
442 | return (struct subnet *)0; | |
443 | } | |
866428dd TL |
444 | |
445 | /* Strip any Relay Agent Information options from the DHCP packet | |
446 | option buffer. If an RAI option is found whose Agent ID matches | |
447 | the giaddr (i.e., ours), try to look up the outgoing interface | |
448 | based on the circuit ID suboption. */ | |
449 | ||
450 | int strip_relay_agent_options (in, out, packet, length) | |
451 | struct interface_info *in, **out; | |
452 | struct dhcp_packet *packet; | |
453 | int length; | |
454 | { | |
455 | int is_dhcp = 0; | |
456 | u_int8_t *op, *sp, *max; | |
457 | int good_agent_option = 0; | |
458 | int status; | |
459 | ||
460 | /* If we're not adding agent options to packets, we're not taking | |
461 | them out either. */ | |
462 | if (!add_agent_options) | |
463 | return length; | |
464 | ||
465 | /* If there's no cookie, it's a bootp packet, so we should just | |
466 | forward it unchanged. */ | |
467 | if (memcmp (packet -> options, DHCP_OPTIONS_COOKIE, 4)) | |
468 | return length; | |
469 | ||
470 | max = ((u_int8_t *)packet) + length; | |
471 | sp = op = &packet -> options [4]; | |
472 | ||
473 | while (op < max) { | |
474 | switch (*op) { | |
475 | /* Skip padding... */ | |
476 | case DHO_PAD: | |
477 | if (sp != op) | |
478 | *sp = *op; | |
479 | ++op; | |
480 | ++sp; | |
481 | continue; | |
482 | ||
483 | /* If we see a message type, it's a DHCP packet. */ | |
484 | case DHO_DHCP_MESSAGE_TYPE: | |
485 | is_dhcp = 1; | |
486 | goto skip; | |
487 | break; | |
488 | ||
489 | /* Quit immediately if we hit an End option. */ | |
490 | case DHO_END: | |
491 | if (sp != op) | |
492 | *sp++ = *op++; | |
493 | goto out; | |
494 | ||
495 | case DHO_DHCP_AGENT_OPTIONS: | |
496 | /* We shouldn't see a relay agent option in a | |
497 | packet before we've seen the DHCP packet type, | |
498 | but if we do, we have to leave it alone. */ | |
499 | if (!is_dhcp) | |
500 | goto skip; | |
501 | ||
502 | status = find_interface_by_agent_option (packet, | |
503 | out, op + 2, | |
504 | op [1]); | |
505 | if (status == -1 && drop_agent_mismatches) | |
506 | return 0; | |
507 | if (status) | |
508 | good_agent_option = 1; | |
509 | op += op [1] + 2; | |
510 | break; | |
511 | ||
512 | skip: | |
513 | /* Skip over other options. */ | |
514 | default: | |
515 | if (sp != op) | |
516 | memcpy (sp, op, op [1] + 2); | |
517 | sp += op [1] + 2; | |
518 | op += op [1] + 2; | |
519 | break; | |
520 | } | |
521 | } | |
522 | out: | |
523 | ||
524 | /* If it's not a DHCP packet, we're not supposed to touch it. */ | |
525 | if (!is_dhcp) | |
526 | return length; | |
527 | ||
528 | /* If none of the agent options we found matched, or if we didn't | |
529 | find any agent options, count this packet as not having any | |
530 | matching agent options, and if we're relying on agent options | |
531 | to determine the outgoing interface, drop the packet. */ | |
532 | ||
533 | if (!good_agent_option) { | |
534 | ++missing_agent_option; | |
535 | if (drop_agent_mismatches) | |
536 | return 0; | |
537 | } | |
538 | ||
539 | /* Adjust the length... */ | |
540 | if (sp != op) { | |
541 | length = sp - ((u_int8_t *)packet); | |
542 | ||
543 | /* Make sure the packet isn't short (this is unlikely, | |
544 | but WTH) */ | |
545 | if (length < BOOTP_MIN_LEN) { | |
546 | memset (sp, 0, BOOTP_MIN_LEN - length); | |
547 | length = BOOTP_MIN_LEN; | |
548 | } | |
549 | } | |
550 | return length; | |
551 | } | |
552 | ||
553 | ||
554 | /* Find an interface that matches the circuit ID specified in the | |
555 | Relay Agent Information option. If one is found, store it through | |
556 | the pointer given; otherwise, leave the existing pointer alone. | |
557 | ||
558 | We actually deviate somewhat from the current specification here: | |
559 | if the option buffer is corrupt, we suggest that the caller not | |
560 | respond to this packet. If the circuit ID doesn't match any known | |
561 | interface, we suggest that the caller to drop the packet. Only if | |
562 | we find a circuit ID that matches an existing interface do we tell | |
563 | the caller to go ahead and process the packet. */ | |
564 | ||
565 | int find_interface_by_agent_option (packet, out, buf, len) | |
566 | struct dhcp_packet *packet; | |
567 | struct interface_info **out; | |
568 | u_int8_t *buf; | |
569 | int len; | |
570 | { | |
571 | int i; | |
572 | u_int8_t *circuit_id = 0; | |
573 | int circuit_id_len; | |
574 | struct interface_info *ip; | |
575 | ||
576 | while (i < len) { | |
577 | /* If the next agent option overflows the end of the | |
578 | packet, the agent option buffer is corrupt. */ | |
579 | if (i + 1 == len || | |
580 | i + buf [i + 1] + 2 > len) { | |
581 | ++corrupt_agent_options; | |
582 | return -1; | |
583 | } | |
584 | switch (buf [i]) { | |
585 | /* Remember where the circuit ID is... */ | |
586 | case RAI_CIRCUIT_ID: | |
587 | circuit_id = &buf [i + 2]; | |
588 | circuit_id_len = buf [i + 1]; | |
589 | i += circuit_id_len + 2; | |
590 | continue; | |
591 | ||
592 | default: | |
593 | i += buf [i + 1] + 2; | |
594 | break; | |
595 | } | |
596 | } | |
597 | ||
598 | /* If there's no circuit ID, it's not really ours, tell the caller | |
599 | it's no good. */ | |
600 | if (!circuit_id) { | |
601 | ++missing_circuit_id; | |
602 | return -1; | |
603 | } | |
604 | ||
605 | /* Scan the interface list looking for an interface whose | |
606 | name matches the one specified in circuit_id. */ | |
607 | ||
608 | for (ip = interfaces; ip; ip = ip -> next) { | |
609 | if (ip -> circuit_id && | |
610 | ip -> circuit_id_len == circuit_id_len && | |
611 | !memcmp (ip -> circuit_id, circuit_id, circuit_id_len)) | |
612 | break; | |
613 | } | |
614 | ||
615 | /* If we got a match, use it. */ | |
616 | if (ip) { | |
617 | *out = ip; | |
618 | return 1; | |
619 | } | |
620 | ||
621 | /* If we didn't get a match, the circuit ID was bogus. */ | |
622 | ++bad_circuit_id; | |
623 | return -1; | |
624 | } | |
625 | ||
626 | /* Examine a packet to see if it's a candidate to have a Relay | |
627 | Agent Information option tacked onto its tail. If it is, tack | |
628 | the option on. */ | |
629 | ||
630 | int add_relay_agent_options (ip, packet, length, giaddr) | |
631 | struct interface_info *ip; | |
632 | struct dhcp_packet *packet; | |
633 | struct in_addr giaddr; | |
634 | { | |
635 | int is_dhcp = 0, agent_options_present = 0; | |
636 | u_int8_t *op, *sp, *max, *end_pad = 0; | |
637 | ||
638 | /* If we're not adding agent options to packets, we can skip | |
639 | this. */ | |
640 | if (!add_agent_options) | |
641 | return length; | |
642 | ||
643 | /* If there's no cookie, it's a bootp packet, so we should just | |
644 | forward it unchanged. */ | |
645 | if (memcmp (packet -> options, DHCP_OPTIONS_COOKIE, 4)) | |
646 | return length; | |
647 | ||
648 | max = ((u_int8_t *)packet) + length; | |
649 | sp = op = &packet -> options [4]; | |
650 | ||
651 | while (op < max) { | |
652 | switch (*op) { | |
653 | /* Skip padding... */ | |
654 | case DHO_PAD: | |
655 | end_pad = sp; | |
656 | if (sp != op) | |
657 | *sp = *op; | |
658 | ++op; | |
659 | ++sp; | |
660 | continue; | |
661 | ||
662 | /* If we see a message type, it's a DHCP packet. */ | |
663 | case DHO_DHCP_MESSAGE_TYPE: | |
664 | is_dhcp = 1; | |
665 | goto skip; | |
666 | break; | |
667 | ||
668 | /* Quit immediately if we hit an End option. */ | |
669 | case DHO_END: | |
670 | goto out; | |
671 | ||
672 | case DHO_DHCP_AGENT_OPTIONS: | |
673 | /* We shouldn't see a relay agent option in a | |
674 | packet before we've seen the DHCP packet type, | |
675 | but if we do, we have to leave it alone. */ | |
676 | if (!is_dhcp) | |
677 | goto skip; | |
678 | end_pad = 0; | |
679 | ||
680 | /* There's already a Relay Agent Information option | |
681 | in this packet. How embarrassing. Decide what | |
682 | to do based on the mode the user specified. */ | |
683 | ||
684 | switch (agent_relay_mode) { | |
685 | case forward_and_append: | |
686 | goto skip; | |
687 | case forward_untouched: | |
688 | return length; | |
689 | case discard: | |
690 | return 0; | |
691 | case forward_and_replace: | |
692 | default: | |
693 | break; | |
694 | } | |
695 | ||
696 | /* Skip over the agent option and start copying | |
697 | if we aren't copying already. */ | |
698 | op += op [1] + 2; | |
699 | break; | |
700 | ||
701 | skip: | |
702 | /* Skip over other options. */ | |
703 | default: | |
704 | end_pad = 0; | |
705 | if (sp != op) | |
706 | memcpy (sp, op, op [1] + 2); | |
707 | sp += op [1] + 2; | |
708 | op += op [1] + 2; | |
709 | break; | |
710 | } | |
711 | } | |
712 | out: | |
713 | ||
714 | /* If it's not a DHCP packet, we're not supposed to touch it. */ | |
715 | if (!is_dhcp) | |
716 | return length; | |
717 | ||
718 | /* If the packet was padded out, we can store the agent option | |
719 | at the beginning of the padding. */ | |
720 | ||
721 | if (end_pad) | |
722 | sp = end_pad; | |
723 | ||
724 | /* Remember where the end of the packet was after parsing | |
725 | it. */ | |
726 | op = sp; | |
727 | ||
728 | /* XXX Is there room? */ | |
729 | ||
730 | /* Okay, cons up *our* Relay Agent Information option. */ | |
731 | *sp++ = DHO_DHCP_AGENT_OPTIONS; | |
732 | *sp++ = 0; /* Dunno... */ | |
733 | ||
734 | /* Copy in the circuit id... */ | |
735 | *sp++ = RAI_CIRCUIT_ID; | |
736 | /* Sanity check. Had better not every happen. */ | |
737 | if (ip -> circuit_id_len > 255 || ip -> circuit_id_len < 1) | |
8ae2d595 | 738 | log_fatal ("completely bogus circuit id length %d on %s\n", |
866428dd TL |
739 | ip -> circuit_id_len, ip -> name); |
740 | *sp++ = ip -> circuit_id_len; | |
741 | memcpy (sp, ip -> circuit_id, ip -> circuit_id_len); | |
742 | sp += ip -> circuit_id_len; | |
743 | ||
744 | /* Copy in remote ID... */ | |
745 | if (ip -> remote_id) { | |
746 | *sp++ = RAI_REMOTE_ID; | |
747 | if (ip -> remote_id_len > 255 || ip -> remote_id_len < 1) | |
8ae2d595 | 748 | log_fatal ("completely bogus remote id length %d on %s\n", |
866428dd TL |
749 | ip -> circuit_id_len, ip -> name); |
750 | *sp++ = ip -> remote_id_len; | |
751 | memcpy (sp, ip -> remote_id, ip -> remote_id_len); | |
752 | sp += ip -> remote_id_len; | |
753 | } | |
754 | ||
755 | /* Relay option's total length shouldn't ever get to be more than | |
756 | 257 bytes. */ | |
757 | if (sp - op > 257) | |
8ae2d595 | 758 | log_fatal ("total agent option length exceeds 257 (%d) on %s\n", |
866428dd TL |
759 | sp - op, ip -> name); |
760 | ||
761 | /* Calculate length of RAI option. */ | |
762 | op [1] = sp - op - 2; | |
763 | ||
764 | /* Deposit an END token. */ | |
765 | *sp++ = DHO_END; | |
766 | ||
767 | /* Recalculate total packet length. */ | |
768 | length = sp - ((u_int8_t *)packet); | |
769 | ||
770 | /* Make sure the packet isn't short (this is unlikely, but WTH) */ | |
771 | if (length < BOOTP_MIN_LEN) { | |
772 | memset (sp, 0, BOOTP_MIN_LEN - length); | |
773 | length = BOOTP_MIN_LEN; | |
774 | } | |
775 | ||
776 | return length; | |
777 | } |