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