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