Correct typo: vender -> vendor
[dragonfly.git] / contrib / dhcp-3.0 / relay / dhcrelay.c
1 /* dhcrelay.c
2
3    DHCP/BOOTP Relay Agent. */
4
5 /*
6  * Copyright (c) 2004 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.52.2.9 2004/11/24 17:39:18 dhankins Exp $ Copyright (c) 2004 Internet Systems Consortium.  All rights reserved.\n";
38 #endif /* not lint */
39
40 #include "dhcpd.h"
41 #include "version.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 struct server_list {
96         struct server_list *next;
97         struct sockaddr_in to;
98 } *servers;
99
100 static char copyright [] = "Copyright 2004 Internet Systems Consortium.";
101 static char arr [] = "All rights reserved.";
102 static char message [] = "Internet Systems Consortium DHCP Relay Agent";
103 static char url [] = "For info, please visit http://www.isc.org/sw/dhcp/";
104
105 int main (argc, argv, envp)
106         int argc;
107         char **argv, **envp;
108 {
109         int i;
110         struct servent *ent;
111         struct server_list *sp = (struct server_list *)0;
112         int no_daemon = 0;
113         int quiet = 0;
114         isc_result_t status;
115         char *s;
116
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
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
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
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]));
152                         log_debug ("binding to user-specified port %d",
153                                ntohs (local_port));
154                 } else if (!strcmp (argv [i], "-d")) {
155                         no_daemon = 1;
156                 } else if (!strcmp (argv [i], "-i")) {
157                         struct interface_info *tmp =
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));
164                         if (++i == argc) {
165                                 usage ();
166                         }
167                         strcpy (tmp -> name, argv [i]);
168                         interface_snorf (tmp, INTERFACE_REQUESTED);
169                         interface_dereference (&tmp, MDL);
170                 } else if (!strcmp (argv [i], "-q")) {
171                         quiet = 1;
172                         quiet_interface_discovery = 1;
173                 } else if (!strcmp (argv [i], "-a")) {
174                         add_agent_options = 1;
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 ();
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;
203                 } else if (argv [i][0] == '-') {
204                     usage ();
205                 } else if (!strcmp (argv [i], "--version")) {
206                         log_info ("isc-dhcrelay-%s", DHCP_VERSION);
207                         exit (0);
208                 } else {
209                         struct hostent *he;
210                         struct in_addr ia, *iap = (struct in_addr *)0;
211                         if (inet_aton (argv [i], &ia)) {
212                                 iap = &ia;
213                         } else {
214                                 he = gethostbyname (argv [i]);
215                                 if (!he) {
216                                         log_error ("%s: host unknown",
217                                                    argv [i]);
218                                 } else {
219                                         iap = ((struct in_addr *)
220                                                he -> h_addr_list [0]);
221                                 }
222                         }
223                         if (iap) {
224                                 sp = ((struct server_list *)
225                                       dmalloc (sizeof *sp, MDL));
226                                 if (!sp)
227                                         log_fatal ("no memory for server.\n");
228                                 sp -> next = servers;
229                                 servers = sp;
230                                 memcpy (&sp -> to.sin_addr,
231                                         iap, sizeof *iap);
232                         }
233                 }
234         }
235
236         if ((s = getenv ("PATH_DHCRELAY_PID"))) {
237                 path_dhcrelay_pid = s;
238         }
239
240         if (!quiet) {
241                 log_info ("%s %s", message, DHCP_VERSION);
242                 log_info (copyright);
243                 log_info (arr);
244                 log_info (url);
245         } else {
246                 quiet = 0;
247                 log_perror = 0;
248         }
249
250         /* Default to the DHCP/BOOTP port. */
251         if (!local_port) {
252                 ent = getservbyname ("dhcps", "udp");
253                 if (!ent)
254                         local_port = htons (67);
255                 else
256                         local_port = ent -> s_port;
257                 endservent ();
258         }
259         remote_port = htons (ntohs (local_port) + 1);
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) {
268                 sp -> to.sin_port = local_port;
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. */
279         discover_interfaces (DISCOVER_RELAY);
280
281         /* Set up the bootp packet handler... */
282         bootp_packet_handler = relay;
283
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)
293                         log_fatal ("can't fork daemon: %m");
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) {
301                         log_error ("Can't create %s: %m", path_dhcrelay_pid);
302                 } else {
303                         pf = fdopen (pfdesc, "w");
304                         if (!pf)
305                                 log_error ("Can't fdopen %s: %m",
306                                       path_dhcrelay_pid);
307                         else {
308                                 fprintf (pf, "%ld\n", (long)getpid ());
309                                 fclose (pf);
310                         }       
311                 }
312
313                 close (0);
314                 close (1);
315                 close (2);
316                 pid = setsid ();
317         }
318
319         /* Start dispatching packets and timeouts... */
320         dispatch ();
321
322         /*NOTREACHED*/
323         return 0;
324 }
325
326 void relay (ip, packet, length, from_port, from, hfrom)
327         struct interface_info *ip;
328         struct dhcp_packet *packet;
329         unsigned length;
330         unsigned int from_port;
331         struct iaddr from;
332         struct hardware *hfrom;
333 {
334         struct server_list *sp;
335         struct sockaddr_in to;
336         struct interface_info *out;
337         struct hardware hto, *htop;
338
339         if (packet -> hlen > sizeof packet -> chaddr) {
340                 log_info ("Discarding packet with invalid hlen.");
341                 return;
342         }
343
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
357         /* If it's a bootreply, forward it to the client. */
358         if (packet -> op == BOOTREPLY) {
359                 if (!(packet -> flags & htons (BOOTP_BROADCAST)) &&
360                         can_unicast_without_arp (out)) {
361                         to.sin_addr = packet -> yiaddr;
362                         to.sin_port = remote_port;
363
364                         /* and hardware address is not broadcast */
365                         htop = &hto;
366                 } else {
367                         to.sin_addr.s_addr = htonl (INADDR_BROADCAST);
368                         to.sin_port = remote_port;
369
370                         /* hardware address is broadcast */
371                         htop = NULL;
372                 }
373                 to.sin_family = AF_INET;
374 #ifdef HAVE_SA_LEN
375                 to.sin_len = sizeof to;
376 #endif
377
378                 memcpy (&hto.hbuf [1], packet -> chaddr, packet -> hlen);
379                 hto.hbuf [0] = packet -> htype;
380                 hto.hlen = packet -> hlen + 1;
381
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
390                 if (!out) {
391                         log_error ("packet to bogus giaddr %s.\n",
392                               inet_ntoa (packet -> giaddr));
393                         ++bogus_giaddr_drops;
394                         return;
395                 }
396
397                 if (send_packet (out,
398                                  (struct packet *)0,
399                                  packet, length, out -> primary_address,
400                                  &to, htop) < 0) {
401                         ++server_packet_errors;
402                 } else {
403                         log_debug ("forwarded BOOTREPLY for %s to %s",
404                                print_hw_addr (packet -> htype, packet -> hlen,
405                                               packet -> chaddr),
406                                inet_ntoa (to.sin_addr));
407
408                         ++server_packets_relayed;
409                 }
410                 return;
411         }
412
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)))
422                 return;
423
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;
431         if (packet -> hops < max_hop_count)
432                 packet -> hops = packet -> hops + 1;
433         else
434                 return;
435
436         /* Otherwise, it's a BOOTREQUEST, so forward it to all the
437            servers. */
438         for (sp = servers; sp; sp = sp -> next) {
439                 if (send_packet ((fallback_interface
440                                   ? fallback_interface : interfaces),
441                                  (struct packet *)0,
442                                  packet, length, ip -> primary_address,
443                                  &sp -> to, (struct hardware *)0) < 0) {
444                         ++client_packet_errors;
445                 } else {
446                         log_debug ("forwarded BOOTREQUEST for %s to %s",
447                                print_hw_addr (packet -> htype, packet -> hlen,
448                                               packet -> chaddr),
449                                inet_ntoa (sp -> to.sin_addr));
450                         ++client_packets_relayed;
451                 }
452         }
453                                  
454 }
455
456 static void usage ()
457 {
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]]");
463 }
464
465 int write_lease (lease)
466         struct lease *lease;
467 {
468         return 1;
469 }
470
471 int write_host (host)
472         struct host_decl *host;
473 {
474         return 1;
475 }
476
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 }
491
492 int find_subnet (struct subnet **sp,
493                  struct iaddr addr, const char *file, int line)
494 {
495         return 0;
496 }
497
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
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
528 #endif
529
530 /* Strip any Relay Agent Information options from the DHCP packet
531    option buffer.   If there is a circuit ID suboption, look up the
532    outgoing interface based upon it. */
533
534 int strip_relay_agent_options (in, out, packet, length)
535         struct interface_info *in, **out;
536         struct dhcp_packet *packet;
537         unsigned length;
538 {
539         int is_dhcp = 0;
540         u_int8_t *op, *sp, *max;
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
586                         status = find_interface_by_agent_option (packet,
587                                                                  out, op + 2,
588                                                                  op [1]);
589                         if (status == -1 && drop_agent_mismatches)
590                                 return 0;
591                         if (status)
592                                 good_agent_option = 1;
593                         op += op [1] + 2;
594                         break;
595
596                       skip:
597                         /* Skip over other options. */
598                       default:
599                         if (sp != op)
600                                 memcpy (sp, op, (unsigned)(op [1] + 2));
601                         sp += op [1] + 2;
602                         op += op [1] + 2;
603                         break;
604                 }
605         }
606       out:
607
608         /* If it's not a DHCP packet, we're not supposed to touch it. */
609         if (!is_dhcp)
610                 return length;
611
612         /* If none of the agent options we found matched, or if we didn't
613            find any agent options, count this packet as not having any
614            matching agent options, and if we're relying on agent options
615            to determine the outgoing interface, drop the packet. */
616
617         if (!good_agent_option) {
618                 ++missing_agent_option;
619                 if (drop_agent_mismatches)
620                         return 0;
621         }
622
623         /* Adjust the length... */
624         if (sp != op) {
625                 length = sp - ((u_int8_t *)packet);
626
627                 /* Make sure the packet isn't short (this is unlikely,
628                    but WTH) */
629                 if (length < BOOTP_MIN_LEN) {
630                         memset (sp, 0, BOOTP_MIN_LEN - length);
631                         length = BOOTP_MIN_LEN;
632                 }
633         }
634         return length;
635 }
636
637
638 /* Find an interface that matches the circuit ID specified in the
639    Relay Agent Information option.   If one is found, store it through
640    the pointer given; otherwise, leave the existing pointer alone.
641
642    We actually deviate somewhat from the current specification here:
643    if the option buffer is corrupt, we suggest that the caller not
644    respond to this packet.  If the circuit ID doesn't match any known
645    interface, we suggest that the caller to drop the packet.  Only if
646    we find a circuit ID that matches an existing interface do we tell
647    the caller to go ahead and process the packet. */
648
649 int find_interface_by_agent_option (packet, out, buf, len)
650         struct dhcp_packet *packet;
651         struct interface_info **out;
652         u_int8_t *buf;
653         int len;
654 {
655         int i = 0;
656         u_int8_t *circuit_id = 0;
657         unsigned circuit_id_len = 0;
658         struct interface_info *ip;
659
660         while (i < len) {
661                 /* If the next agent option overflows the end of the
662                    packet, the agent option buffer is corrupt. */
663                 if (i + 1 == len ||
664                     i + buf [i + 1] + 2 > len) {
665                         ++corrupt_agent_options;
666                         return -1;
667                 }
668                 switch (buf [i]) {
669                         /* Remember where the circuit ID is... */
670                       case RAI_CIRCUIT_ID:
671                         circuit_id = &buf [i + 2];
672                         circuit_id_len = buf [i + 1];
673                         i += circuit_id_len + 2;
674                         continue;
675
676                       default:
677                         i += buf [i + 1] + 2;
678                         break;
679                 }
680         }
681
682         /* If there's no circuit ID, it's not really ours, tell the caller
683            it's no good. */
684         if (!circuit_id) {
685                 ++missing_circuit_id;
686                 return -1;
687         }
688
689         /* Scan the interface list looking for an interface whose
690            name matches the one specified in circuit_id. */
691
692         for (ip = interfaces; ip; ip = ip -> next) {
693                 if (ip -> circuit_id &&
694                     ip -> circuit_id_len == circuit_id_len &&
695                     !memcmp (ip -> circuit_id, circuit_id, circuit_id_len))
696                         break;
697         }
698
699         /* If we got a match, use it. */
700         if (ip) {
701                 *out = ip;
702                 return 1;
703         }
704
705         /* If we didn't get a match, the circuit ID was bogus. */
706         ++bad_circuit_id;
707         return -1;
708 }
709
710 /* Examine a packet to see if it's a candidate to have a Relay
711    Agent Information option tacked onto its tail.   If it is, tack
712    the option on.  */
713
714 int add_relay_agent_options (ip, packet, length, giaddr)
715         struct interface_info *ip;
716         struct dhcp_packet *packet;
717         unsigned length;
718         struct in_addr giaddr;
719 {
720         int is_dhcp = 0, agent_options_present = 0;
721         u_int8_t *op, *sp, *max, *end_pad = 0;
722
723         /* If we're not adding agent options to packets, we can skip
724            this. */
725         if (!add_agent_options)
726                 return length;
727
728         /* If there's no cookie, it's a bootp packet, so we should just
729            forward it unchanged. */
730         if (memcmp (packet -> options, DHCP_OPTIONS_COOKIE, 4))
731                 return length;
732
733         max = ((u_int8_t *)packet) + length;
734         sp = op = &packet -> options [4];
735
736         while (op < max) {
737                 switch (*op) {
738                         /* Skip padding... */
739                       case DHO_PAD:
740                         end_pad = sp;
741                         if (sp != op)
742                                 *sp = *op;
743                         ++op;
744                         ++sp;
745                         continue;
746
747                         /* If we see a message type, it's a DHCP packet. */
748                       case DHO_DHCP_MESSAGE_TYPE:
749                         is_dhcp = 1;
750                         goto skip;
751                         break;
752
753                         /* Quit immediately if we hit an End option. */
754                       case DHO_END:
755                         goto out;
756
757                       case DHO_DHCP_AGENT_OPTIONS:
758                         /* We shouldn't see a relay agent option in a
759                            packet before we've seen the DHCP packet type,
760                            but if we do, we have to leave it alone. */
761                         if (!is_dhcp)
762                                 goto skip;
763                         end_pad = 0;
764
765                         /* There's already a Relay Agent Information option
766                            in this packet.   How embarrassing.   Decide what
767                            to do based on the mode the user specified. */
768
769                         switch (agent_relay_mode) {
770                               case forward_and_append:
771                                 goto skip;
772                               case forward_untouched:
773                                 return length;
774                               case discard:
775                                 return 0;
776                               case forward_and_replace:
777                               default:
778                                 break;
779                         }
780
781                         /* Skip over the agent option and start copying
782                            if we aren't copying already. */
783                         op += op [1] + 2;
784                         break;
785
786                       skip:
787                         /* Skip over other options. */
788                       default:
789                         end_pad = 0;
790                         if (sp != op)
791                                 memcpy (sp, op, (unsigned)(op [1] + 2));
792                         sp += op [1] + 2;
793                         op += op [1] + 2;
794                         break;
795                 }
796         }
797       out:
798
799         /* If it's not a DHCP packet, we're not supposed to touch it. */
800         if (!is_dhcp)
801                 return length;
802
803         /* If the packet was padded out, we can store the agent option
804            at the beginning of the padding. */
805
806         if (end_pad)
807                 sp = end_pad;
808
809         /* Remember where the end of the packet was after parsing
810            it. */
811         op = sp;
812
813         /* XXX Is there room? */
814
815         /* Okay, cons up *our* Relay Agent Information option. */
816         *sp++ = DHO_DHCP_AGENT_OPTIONS;
817         *sp++ = 0;      /* Dunno... */
818
819         /* Copy in the circuit id... */
820         *sp++ = RAI_CIRCUIT_ID;
821         /* Sanity check.   Had better not every happen. */
822         if (ip -> circuit_id_len > 255 || ip -> circuit_id_len < 1)
823                 log_fatal ("completely bogus circuit id length %d on %s\n",
824                        ip -> circuit_id_len, ip -> name);
825         *sp++ = ip -> circuit_id_len;
826         memcpy (sp, ip -> circuit_id, ip -> circuit_id_len);
827         sp += ip -> circuit_id_len;
828
829         /* Copy in remote ID... */
830         if (ip -> remote_id) {
831                 *sp++ = RAI_REMOTE_ID;
832                 if (ip -> remote_id_len > 255 || ip -> remote_id_len < 1)
833                         log_fatal ("bogus remote id length %d on %s\n",
834                                ip -> circuit_id_len, ip -> name);
835                 *sp++ = ip -> remote_id_len;
836                 memcpy (sp, ip -> remote_id, ip -> remote_id_len);
837                 sp += ip -> remote_id_len;
838         }
839
840         /* Relay option's total length shouldn't ever get to be more than
841            257 bytes. */
842         if (sp - op > 257)
843             log_fatal ("total agent option length exceeds 257 (%ld) on %s\n",
844                        (long)(sp - op), ip -> name);
845
846         /* Calculate length of RAI option. */
847         op [1] = sp - op - 2;
848
849         /* Deposit an END token. */
850         *sp++ = DHO_END;
851
852         /* Recalculate total packet length. */
853         length = sp - ((u_int8_t *)packet);
854
855         /* Make sure the packet isn't short (this is unlikely, but WTH) */
856         if (length < BOOTP_MIN_LEN) {
857                 memset (sp, 0, BOOTP_MIN_LEN - length);
858                 length = BOOTP_MIN_LEN;
859         }
860
861         return length;
862 }