]> git.ipfire.org Git - thirdparty/dhcp.git/blame - relay/dhcrelay.c
- Merge changes between 3.0.3RC1 and 3.0.4-BETA-3 into HEAD (silence
[thirdparty/dhcp.git] / relay / dhcrelay.c
CommitLineData
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 36static 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
43static void usage PROTO ((void));
44
747ec13b
TL
45TIME default_lease_time = 43200; /* 12 hours... */
46TIME max_lease_time = 86400; /* 24 hours... */
47struct tree_cache *global_options [256];
48
aaf053e3
TL
49/* Needed to prevent linking against conflex.c. */
50int lexline;
51int lexchar;
52char *token_line;
53char *tlname;
54
b1b7b521 55const char *path_dhcrelay_pid = _PATH_DHCRELAY_PID;
e7012e3f 56
866428dd
TL
57int 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. */
61int bogus_giaddr_drops = 0; /* Packets sent to us to relay back to a
62 client, but with a bogus giaddr. */
63int client_packets_relayed = 0; /* Packets relayed from client to server. */
64int server_packet_errors = 0; /* Errors sending packets to servers. */
65int server_packets_relayed = 0; /* Packets relayed from server to client. */
66int client_packet_errors = 0; /* Errors sending packets to clients. */
67
68int add_agent_options = 0; /* If nonzero, add relay agent options. */
69int drop_agent_mismatches = 0; /* If nonzero, drop server replies that
98311e4b 70 don't have matching circuit-id's. */
866428dd
TL
71int corrupt_agent_options = 0; /* Number of packets dropped because
72 relay agent information option was bad. */
73int missing_agent_option = 0; /* Number of packets dropped because no
74 RAI option matching our ID was found. */
75int bad_circuit_id = 0; /* Circuit ID option in matching RAI option
76 did not match any known circuit ID. */
77int missing_circuit_id = 0; /* Circuit ID option in matching RAI option
78 was missing. */
98311e4b
DH
79int max_hop_count = 10; /* Maximum hop count */
80
866428dd
TL
81
82 /* Maximum size of a packet with agent options added. */
83int 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: */
87enum { 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
92u_int16_t local_port;
93u_int16_t remote_port;
747ec13b
TL
94
95struct server_list {
96 struct server_list *next;
97 struct sockaddr_in to;
98} *servers;
99
98311e4b 100static char copyright [] = "Copyright 2004-2005 Internet Systems Consortium.";
d2bc90bd 101static char arr [] = "All rights reserved.";
98311e4b
DH
102static char message [] = "Internet Systems Consortium DHCP Relay Agent";
103static char url [] = "For info, please visit http://www.isc.org/sw/dhcp/";
d2bc90bd 104
747ec13b
TL
105int 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 326void 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
456static 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
465int write_lease (lease)
466 struct lease *lease;
467{
468 return 1;
469}
470
628beb0e
TL
471int write_host (host)
472 struct host_decl *host;
473{
474 return 1;
475}
476
747ec13b
TL
477int commit_leases ()
478{
479 return 1;
480}
481
482void bootp (packet)
483 struct packet *packet;
484{
485}
486
487void dhcp (packet)
488 struct packet *packet;
489{
490}
f90c6527 491
20916cae
TL
492int 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)
499int check_collection (struct packet *p, struct lease *l,
500 struct collection *c)
501{
502 return 0;
503}
504
505void classify (struct packet *p, struct class *c)
506{
507}
508
509isc_result_t find_class (struct class **class, const char *c1,
510 const char *c2, int i)
511{
512 return ISC_R_NOTFOUND;
513}
514
515int 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 */
522isc_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
534int 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
666int 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
731int 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}