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