HAMMER 33/many: Expand transaction processing, fix bug in B-Tree
[dragonfly.git] / contrib / dhcp-3.0 / relay / dhcrelay.c
blob5d18ebd59ffe91b239c3046361a5c425c05d2ef2
1 /* dhcrelay.c
3 DHCP/BOOTP Relay Agent. */
5 /*
6 * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC")
7 * Copyright (c) 1997-2003 by Internet Software Consortium
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.
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.
21 * Internet Systems Consortium, Inc.
22 * 950 Charter Street
23 * Redwood City, CA 94063
24 * <info@isc.org>
25 * http://www.isc.org/
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''.
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 */
40 #include "dhcpd.h"
41 #include "version.h"
43 static void usage PROTO ((void));
45 TIME default_lease_time = 43200; /* 12 hours... */
46 TIME max_lease_time = 86400; /* 24 hours... */
47 struct tree_cache *global_options [256];
49 /* Needed to prevent linking against conflex.c. */
50 int lexline;
51 int lexchar;
52 char *token_line;
53 char *tlname;
55 const char *path_dhcrelay_pid = _PATH_DHCRELAY_PID;
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. */
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 */
82 /* Maximum size of a packet with agent options added. */
83 int dhcp_max_agent_option_packet_length = 576;
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;
92 u_int16_t local_port;
93 u_int16_t remote_port;
95 struct server_list {
96 struct server_list *next;
97 struct sockaddr_in to;
98 } *servers;
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/";
105 int main (argc, argv, envp)
106 int argc;
107 char **argv, **envp;
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;
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);
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
134 #if !(defined (DEBUG) || defined (SYSLOG_4_2))
135 setlogmask (LOG_UPTO (LOG_INFO));
136 #endif
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));
144 /* Set up the OMAPI wrappers for the interface object. */
145 interface_setup ();
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 ();
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]);
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);
236 if ((s = getenv ("PATH_DHCRELAY_PID"))) {
237 path_dhcrelay_pid = s;
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;
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 ();
259 remote_port = htons (ntohs (local_port) + 1);
261 /* We need at least one server. */
262 if (!sp) {
263 usage ();
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
275 /* Get the current time... */
276 GET_TIME (&cur_time);
278 /* Discover all the network interfaces. */
279 discover_interfaces (DISCOVER_RELAY);
281 /* Set up the bootp packet handler... */
282 bootp_packet_handler = relay;
284 /* Become a daemon... */
285 if (!no_daemon) {
286 int pid;
287 FILE *pf;
288 int pfdesc;
290 log_perror = 0;
292 if ((pid = fork()) < 0)
293 log_fatal ("can't fork daemon: %m");
294 else if (pid)
295 exit (0);
297 pfdesc = open (path_dhcrelay_pid,
298 O_CREAT | O_TRUNC | O_WRONLY, 0644);
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);
313 close (0);
314 close (1);
315 close (2);
316 pid = setsid ();
319 /* Start dispatching packets and timeouts... */
320 dispatch ();
322 /*NOTREACHED*/
323 return 0;
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;
334 struct server_list *sp;
335 struct sockaddr_in to;
336 struct interface_info *out;
337 struct hardware hto, *htop;
339 if (packet -> hlen > sizeof packet -> chaddr) {
340 log_info ("Discarding packet with invalid hlen.");
341 return;
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;
353 } else {
354 out = (struct interface_info *)0;
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;
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;
370 /* hardware address is broadcast */
371 htop = NULL;
373 to.sin_family = AF_INET;
374 #ifdef HAVE_SA_LEN
375 to.sin_len = sizeof to;
376 #endif
378 memcpy (&hto.hbuf [1], packet -> chaddr, packet -> hlen);
379 hto.hbuf [0] = packet -> htype;
380 hto.hlen = packet -> hlen + 1;
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;
390 if (!out) {
391 log_error ("packet to bogus giaddr %s.\n",
392 inet_ntoa (packet -> giaddr));
393 ++bogus_giaddr_drops;
394 return;
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));
408 ++server_packets_relayed;
410 return;
413 /* If giaddr matches one of our addresses, ignore the packet -
414 we just sent it. */
415 if (out)
416 return;
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;
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;
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;
456 static void usage ()
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]]");
465 int write_lease (lease)
466 struct lease *lease;
468 return 1;
471 int write_host (host)
472 struct host_decl *host;
474 return 1;
477 int commit_leases ()
479 return 1;
482 void bootp (packet)
483 struct packet *packet;
487 void dhcp (packet)
488 struct packet *packet;
492 int find_subnet (struct subnet **sp,
493 struct iaddr addr, const char *file, int line)
495 return 0;
498 #if defined (DEBUG)
499 int check_collection (struct packet *p, struct lease *l,
500 struct collection *c)
502 return 0;
505 void classify (struct packet *p, struct class *c)
509 isc_result_t find_class (struct class **class, const char *c1,
510 const char *c2, int i)
512 return ISC_R_NOTFOUND;
515 int parse_allow_deny (struct option_cache **oc, struct parse *p, int i)
517 return 0;
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)
525 return ISC_R_SUCCESS;
528 #endif
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. */
534 int strip_relay_agent_options (in, out, packet, length)
535 struct interface_info *in, **out;
536 struct dhcp_packet *packet;
537 unsigned length;
539 int is_dhcp = 0;
540 u_int8_t *op, *sp, *max;
541 int good_agent_option = 0;
542 int status;
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;
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;
554 max = ((u_int8_t *)packet) + length;
555 sp = op = &packet -> options [4];
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;
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;
573 /* Quit immediately if we hit an End option. */
574 case DHO_END:
575 if (sp != op)
576 *sp++ = *op++;
577 goto out;
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;
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;
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;
606 out:
608 /* If it's not a DHCP packet, we're not supposed to touch it. */
609 if (!is_dhcp)
610 return length;
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. */
617 if (!good_agent_option) {
618 ++missing_agent_option;
619 if (drop_agent_mismatches)
620 return 0;
623 /* Adjust the length... */
624 if (sp != op) {
625 length = sp - ((u_int8_t *)packet);
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;
634 return length;
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.
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. */
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;
655 int i = 0;
656 u_int8_t *circuit_id = 0;
657 unsigned circuit_id_len = 0;
658 struct interface_info *ip;
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;
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;
676 default:
677 i += buf [i + 1] + 2;
678 break;
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;
689 /* Scan the interface list looking for an interface whose
690 name matches the one specified in circuit_id. */
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;
699 /* If we got a match, use it. */
700 if (ip) {
701 *out = ip;
702 return 1;
705 /* If we didn't get a match, the circuit ID was bogus. */
706 ++bad_circuit_id;
707 return -1;
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. */
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;
720 int is_dhcp = 0, agent_options_present = 0;
721 u_int8_t *op, *sp, *max, *end_pad = 0;
723 /* If we're not adding agent options to packets, we can skip
724 this. */
725 if (!add_agent_options)
726 return length;
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;
733 max = ((u_int8_t *)packet) + length;
734 sp = op = &packet -> options [4];
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;
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;
753 /* Quit immediately if we hit an End option. */
754 case DHO_END:
755 goto out;
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;
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. */
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;
781 /* Skip over the agent option and start copying
782 if we aren't copying already. */
783 op += op [1] + 2;
784 break;
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;
797 out:
799 /* If it's not a DHCP packet, we're not supposed to touch it. */
800 if (!is_dhcp)
801 return length;
803 /* If the packet was padded out, we can store the agent option
804 at the beginning of the padding. */
806 if (end_pad)
807 sp = end_pad;
809 /* Remember where the end of the packet was after parsing
810 it. */
811 op = sp;
813 /* XXX Is there room? */
815 /* Okay, cons up *our* Relay Agent Information option. */
816 *sp++ = DHO_DHCP_AGENT_OPTIONS;
817 *sp++ = 0; /* Dunno... */
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;
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;
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);
846 /* Calculate length of RAI option. */
847 op [1] = sp - op - 2;
849 /* Deposit an END token. */
850 *sp++ = DHO_END;
852 /* Recalculate total packet length. */
853 length = sp - ((u_int8_t *)packet);
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;
861 return length;