]> git.ipfire.org Git - thirdparty/dhcp.git/blame - relay/dhcrelay.c
Documentation cleanup covering multiple tickets
[thirdparty/dhcp.git] / relay / dhcrelay.c
CommitLineData
747ec13b
TL
1/* dhcrelay.c
2
3 DHCP/BOOTP Relay Agent. */
4
5/*
ae566556 6 * Copyright(c) 2004-2009 by Internet Systems Consortium, Inc.("ISC")
7de20a95 7 * Copyright(c) 1997-2003 by Internet Software Consortium
747ec13b 8 *
98311e4b
DH
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.
747ec13b 12 *
98311e4b
DH
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.
747ec13b 20 *
98311e4b
DH
21 * Internet Systems Consortium, Inc.
22 * 950 Charter Street
23 * Redwood City, CA 94063
24 * <info@isc.org>
2c85ac9b 25 * https://www.isc.org/
49733f31 26 *
98311e4b 27 * This software has been written for Internet Systems Consortium
49733f31 28 * by Ted Lemon in cooperation with Vixie Enterprises and Nominum, Inc.
98311e4b 29 * To learn more about Internet Systems Consortium, see
2c85ac9b 30 * ``https://www.isc.org/''. To learn more about Vixie Enterprises,
49733f31
TL
31 * see ``http://www.vix.com''. To learn more about Nominum, Inc., see
32 * ``http://www.nominum.com''.
747ec13b
TL
33 */
34
747ec13b 35#include "dhcpd.h"
fe5b0fdd 36#include <syslog.h>
be62cf06 37#include <sys/time.h>
747ec13b 38
747ec13b
TL
39TIME default_lease_time = 43200; /* 12 hours... */
40TIME max_lease_time = 86400; /* 24 hours... */
7de20a95
EH
41struct tree_cache *global_options[256];
42
43struct option *requested_opts[2];
747ec13b 44
aaf053e3
TL
45/* Needed to prevent linking against conflex.c. */
46int lexline;
47int lexchar;
48char *token_line;
49char *tlname;
50
b1b7b521 51const char *path_dhcrelay_pid = _PATH_DHCRELAY_PID;
e7012e3f 52
866428dd
TL
53int bogus_agent_drops = 0; /* Packets dropped because agent option
54 field was specified and we're not relaying
55 packets that already have an agent option
56 specified. */
57int bogus_giaddr_drops = 0; /* Packets sent to us to relay back to a
58 client, but with a bogus giaddr. */
59int client_packets_relayed = 0; /* Packets relayed from client to server. */
60int server_packet_errors = 0; /* Errors sending packets to servers. */
61int server_packets_relayed = 0; /* Packets relayed from server to client. */
62int client_packet_errors = 0; /* Errors sending packets to clients. */
63
64int add_agent_options = 0; /* If nonzero, add relay agent options. */
d352732e
EH
65
66int agent_option_errors = 0; /* Number of packets forwarded without
67 agent options because there was no room. */
866428dd 68int drop_agent_mismatches = 0; /* If nonzero, drop server replies that
98311e4b 69 don't have matching circuit-id's. */
866428dd
TL
70int corrupt_agent_options = 0; /* Number of packets dropped because
71 relay agent information option was bad. */
72int missing_agent_option = 0; /* Number of packets dropped because no
73 RAI option matching our ID was found. */
74int bad_circuit_id = 0; /* Circuit ID option in matching RAI option
75 did not match any known circuit ID. */
76int missing_circuit_id = 0; /* Circuit ID option in matching RAI option
77 was missing. */
98311e4b
DH
78int max_hop_count = 10; /* Maximum hop count */
79
7de20a95
EH
80#ifdef DHCPv6
81 /* Force use of DHCPv6 interface-id option. */
82isc_boolean_t use_if_id = ISC_FALSE;
83#endif
866428dd
TL
84
85 /* Maximum size of a packet with agent options added. */
d352732e 86int dhcp_max_agent_option_packet_length = DHCP_MTU_MIN;
866428dd
TL
87
88 /* What to do about packets we're asked to relay that
89 already have a relay option: */
90enum { forward_and_append, /* Forward and append our own relay option. */
91 forward_and_replace, /* Forward, but replace theirs with ours. */
92 forward_untouched, /* Forward without changes. */
93 discard } agent_relay_mode = forward_and_replace;
94
747ec13b
TL
95u_int16_t local_port;
96u_int16_t remote_port;
747ec13b 97
78c553c4
DH
98/* Relay agent server list. */
99struct server_list {
100 struct server_list *next;
101 struct sockaddr_in to;
102} *servers;
103
7de20a95
EH
104#ifdef DHCPv6
105struct stream_list {
106 struct stream_list *next;
107 struct interface_info *ifp;
108 struct sockaddr_in6 link;
109 int id;
110} *downstreams, *upstreams;
111
112static struct stream_list *parse_downstream(char *);
113static struct stream_list *parse_upstream(char *);
114static void setup_streams(void);
115#endif
116
117static void do_relay4(struct interface_info *, struct dhcp_packet *,
118 unsigned int, unsigned int, struct iaddr,
d352732e 119 struct hardware *);
7de20a95 120static int add_relay_agent_options(struct interface_info *,
d352732e
EH
121 struct dhcp_packet *, unsigned,
122 struct in_addr);
7de20a95 123static int find_interface_by_agent_option(struct dhcp_packet *,
d352732e 124 struct interface_info **, u_int8_t *, int);
7de20a95 125static int strip_relay_agent_options(struct interface_info *,
d352732e
EH
126 struct interface_info **,
127 struct dhcp_packet *, unsigned);
7de20a95 128
ae566556
SR
129static const char copyright[] =
130"Copyright 2004-2009 Internet Systems Consortium.";
131static const char arr[] = "All rights reserved.";
132static const char message[] =
133"Internet Systems Consortium DHCP Relay Agent";
134static const char url[] =
2c85ac9b 135"For info, please visit https://www.isc.org/software/dhcp/";
7de20a95
EH
136
137#ifdef DHCPv6
138#define DHCRELAY_USAGE \
139"Usage: dhcrelay [-4] [-d] [-q] [-a] [-D]\n"\
140" [-A <length>] [-c <hops>] [-p <port>]\n" \
141" [-m append|replace|forward|discard]\n" \
142" [-i interface0 [ ... -i interfaceN]\n" \
143" server0 [ ... serverN]\n\n" \
144" dhcrelay -6 [-d] [-q] [-I] [-c <hops>] [-p <port>]\n" \
145" -l lower0 [ ... -l lowerN]\n" \
146" -u upper0 [ ... -u upperN]\n" \
147" lower (client link): [address%%]interface[#index]\n" \
148" upper (server link): [address%%]interface"
149#else
150#define DHCRELAY_USAGE \
151"Usage: dhcrelay [-d] [-q] [-a] [-D] [-A <length>] [-c <hops>] [-p <port>]\n" \
152" [-m append|replace|forward|discard]\n" \
153" [-i interface0 [ ... -i interfaceN]\n" \
154" server0 [ ... serverN]\n\n"
155#endif
156
157static void usage() {
158 log_fatal(DHCRELAY_USAGE);
159}
d2bc90bd 160
98bd7ca0
DH
161int
162main(int argc, char **argv) {
7de20a95 163 isc_result_t status;
747ec13b 164 struct servent *ent;
78c553c4 165 struct server_list *sp = NULL;
d352732e 166 struct interface_info *tmp = NULL;
4c750c58
PS
167 char *service_local = NULL, *service_remote = NULL;
168 u_int16_t port_local = 0, port_remote = 0;
7de20a95
EH
169 int no_daemon = 0, quiet = 0;
170 int fd;
171 int i;
172#ifdef DHCPv6
d352732e 173 struct stream_list *sl = NULL;
7de20a95
EH
174 int local_family_set = 0;
175#endif
747ec13b 176
d352732e
EH
177 /* Make sure that file descriptors 0(stdin), 1,(stdout), and
178 2(stderr) are open. To do this, we assume that when we
179 open a file the lowest available file descriptor is used. */
180 fd = open("/dev/null", O_RDWR);
181 if (fd == 0)
182 fd = open("/dev/null", O_RDWR);
183 if (fd == 1)
184 fd = open("/dev/null", O_RDWR);
185 if (fd == 2)
186 log_perror = 0; /* No sense logging to /dev/null. */
187 else if (fd != -1)
188 close(fd);
dfb6c5aa 189
7de20a95 190 openlog("dhcrelay", LOG_NDELAY, LOG_DAEMON);
747ec13b 191
fe5b0fdd 192#if !defined(DEBUG)
7de20a95 193 setlogmask(LOG_UPTO(LOG_INFO));
747ec13b
TL
194#endif
195
98bf1607
SR
196 /* Set up the isc and dns library managers */
197 status = dhcp_context_create();
198 if (status != ISC_R_SUCCESS)
199 log_fatal("Can't initialize context: %s",
200 isc_result_totext(status));
201
6cecb7c5 202 /* Set up the OMAPI. */
7de20a95 203 status = omapi_init();
6cecb7c5 204 if (status != ISC_R_SUCCESS)
7de20a95
EH
205 log_fatal("Can't initialize OMAPI: %s",
206 isc_result_totext(status));
6cecb7c5
TL
207
208 /* Set up the OMAPI wrappers for the interface object. */
7de20a95 209 interface_setup();
6cecb7c5 210
747ec13b 211 for (i = 1; i < argc; i++) {
7de20a95
EH
212 if (!strcmp(argv[i], "-4")) {
213#ifdef DHCPv6
214 if (local_family_set && (local_family == AF_INET6)) {
215 usage();
e7012e3f 216 }
7de20a95
EH
217 local_family_set = 1;
218 local_family = AF_INET;
219 } else if (!strcmp(argv[i], "-6")) {
220 if (local_family_set && (local_family == AF_INET)) {
221 usage();
222 }
223 local_family_set = 1;
224 local_family = AF_INET6;
225#endif
226 } else if (!strcmp(argv[i], "-d")) {
227 no_daemon = 1;
228 } else if (!strcmp(argv[i], "-q")) {
e7012e3f
TL
229 quiet = 1;
230 quiet_interface_discovery = 1;
7de20a95
EH
231 } else if (!strcmp(argv[i], "-p")) {
232 if (++i == argc)
233 usage();
59112e84 234 local_port = validate_port(argv[i]);
7de20a95
EH
235 log_debug("binding to user-specified port %d",
236 ntohs(local_port));
237 } else if (!strcmp(argv[i], "-c")) {
98311e4b
DH
238 int hcount;
239 if (++i == argc)
7de20a95 240 usage();
98311e4b
DH
241 hcount = atoi(argv[i]);
242 if (hcount <= 255)
243 max_hop_count= hcount;
244 else
7de20a95
EH
245 usage();
246 } else if (!strcmp(argv[i], "-i")) {
247#ifdef DHCPv6
248 if (local_family_set && (local_family == AF_INET6)) {
249 usage();
250 }
251 local_family_set = 1;
252 local_family = AF_INET;
253#endif
254 status = interface_allocate(&tmp, MDL);
255 if (status != ISC_R_SUCCESS)
256 log_fatal("%s: interface_allocate: %s",
257 argv[i],
258 isc_result_totext(status));
259 if (++i == argc) {
260 usage();
261 }
262 strcpy(tmp->name, argv[i]);
263 interface_snorf(tmp, INTERFACE_REQUESTED);
264 interface_dereference(&tmp, MDL);
265 } else if (!strcmp(argv[i], "-a")) {
266#ifdef DHCPv6
267 if (local_family_set && (local_family == AF_INET6)) {
268 usage();
269 }
270 local_family_set = 1;
271 local_family = AF_INET;
272#endif
273 add_agent_options = 1;
274 } else if (!strcmp(argv[i], "-A")) {
275#ifdef DHCPv6
276 if (local_family_set && (local_family == AF_INET6)) {
277 usage();
278 }
279 local_family_set = 1;
280 local_family = AF_INET;
281#endif
866428dd 282 if (++i == argc)
7de20a95 283 usage();
d352732e 284
7de20a95 285 dhcp_max_agent_option_packet_length = atoi(argv[i]);
d352732e
EH
286
287 if (dhcp_max_agent_option_packet_length > DHCP_MTU_MAX)
288 log_fatal("%s: packet length exceeds "
289 "longest possible MTU\n",
290 argv[i]);
7de20a95
EH
291 } else if (!strcmp(argv[i], "-m")) {
292#ifdef DHCPv6
293 if (local_family_set && (local_family == AF_INET6)) {
294 usage();
295 }
296 local_family_set = 1;
297 local_family = AF_INET;
298#endif
866428dd 299 if (++i == argc)
7de20a95
EH
300 usage();
301 if (!strcasecmp(argv[i], "append")) {
866428dd 302 agent_relay_mode = forward_and_append;
7de20a95 303 } else if (!strcasecmp(argv[i], "replace")) {
866428dd 304 agent_relay_mode = forward_and_replace;
7de20a95 305 } else if (!strcasecmp(argv[i], "forward")) {
866428dd 306 agent_relay_mode = forward_untouched;
7de20a95 307 } else if (!strcasecmp(argv[i], "discard")) {
866428dd
TL
308 agent_relay_mode = discard;
309 } else
7de20a95
EH
310 usage();
311 } else if (!strcmp(argv[i], "-D")) {
312#ifdef DHCPv6
313 if (local_family_set && (local_family == AF_INET6)) {
314 usage();
315 }
316 local_family_set = 1;
317 local_family = AF_INET;
318#endif
866428dd 319 drop_agent_mismatches = 1;
7de20a95
EH
320#ifdef DHCPv6
321 } else if (!strcmp(argv[i], "-I")) {
322 if (local_family_set && (local_family == AF_INET)) {
323 usage();
324 }
325 local_family_set = 1;
326 local_family = AF_INET6;
327 use_if_id = ISC_TRUE;
328 } else if (!strcmp(argv[i], "-l")) {
329 if (local_family_set && (local_family == AF_INET)) {
330 usage();
331 }
332 local_family_set = 1;
333 local_family = AF_INET6;
d352732e
EH
334 if (downstreams != NULL)
335 use_if_id = ISC_TRUE;
7de20a95
EH
336 if (++i == argc)
337 usage();
d352732e
EH
338 sl = parse_downstream(argv[i]);
339 sl->next = downstreams;
340 downstreams = sl;
7de20a95
EH
341 } else if (!strcmp(argv[i], "-u")) {
342 if (local_family_set && (local_family == AF_INET)) {
343 usage();
344 }
345 local_family_set = 1;
346 local_family = AF_INET6;
347 if (++i == argc)
348 usage();
d352732e
EH
349 sl = parse_upstream(argv[i]);
350 sl->next = upstreams;
351 upstreams = sl;
7de20a95
EH
352#endif
353 } else if (!strcmp(argv[i], "--version")) {
354 log_info("isc-dhcrelay-%s", PACKAGE_VERSION);
355 exit(0);
356 } else if (!strcmp(argv[i], "--help") ||
357 !strcmp(argv[i], "-h")) {
358 log_info(DHCRELAY_USAGE);
359 exit(0);
360 } else if (argv[i][0] == '-') {
361 usage();
747ec13b 362 } else {
78c553c4 363 struct hostent *he;
7de20a95
EH
364 struct in_addr ia, *iap = NULL;
365
366#ifdef DHCPv6
367 if (local_family_set && (local_family == AF_INET6)) {
368 usage();
369 }
370 local_family_set = 1;
371 local_family = AF_INET;
372#endif
373 if (inet_aton(argv[i], &ia)) {
78c553c4
DH
374 iap = &ia;
375 } else {
7de20a95 376 he = gethostbyname(argv[i]);
78c553c4 377 if (!he) {
7de20a95 378 log_error("%s: host unknown", argv[i]);
78c553c4
DH
379 } else {
380 iap = ((struct in_addr *)
7de20a95 381 he->h_addr_list[0]);
78c553c4
DH
382 }
383 }
7de20a95 384
78c553c4
DH
385 if (iap) {
386 sp = ((struct server_list *)
7de20a95 387 dmalloc(sizeof *sp, MDL));
78c553c4 388 if (!sp)
7de20a95
EH
389 log_fatal("no memory for server.\n");
390 sp->next = servers;
78c553c4 391 servers = sp;
7de20a95 392 memcpy(&sp->to.sin_addr, iap, sizeof *iap);
78c553c4 393 }
747ec13b
TL
394 }
395 }
e7012e3f 396
d352732e
EH
397 if (local_family == AF_INET) {
398 path_dhcrelay_pid = getenv("PATH_DHCRELAY_PID");
399 if (path_dhcrelay_pid == NULL)
400 path_dhcrelay_pid = _PATH_DHCRELAY_PID;
401 }
7de20a95 402#ifdef DHCPv6
d352732e
EH
403 else {
404 path_dhcrelay_pid = getenv("PATH_DHCRELAY6_PID");
405 if (path_dhcrelay_pid == NULL)
406 path_dhcrelay_pid = _PATH_DHCRELAY6_PID;
407 }
7de20a95 408#endif
cfa7212d 409
d2bc90bd 410 if (!quiet) {
7de20a95
EH
411 log_info("%s %s", message, PACKAGE_VERSION);
412 log_info(copyright);
413 log_info(arr);
414 log_info(url);
bc62fb2a 415 } else {
c4f74946 416 quiet = 0;
bc62fb2a
TL
417 log_perror = 0;
418 }
d2bc90bd 419
7de20a95 420 /* Set default port */
d352732e 421 if (local_family == AF_INET) {
9733358a
EH
422 service_local = "bootps";
423 service_remote = "bootpc";
d352732e 424 port_local = htons(67);
9733358a 425 port_remote = htons(68);
d352732e 426 }
7de20a95 427#ifdef DHCPv6
d352732e
EH
428 else {
429 service_local = "dhcpv6-server";
430 service_remote = "dhcpv6-client";
431 port_local = htons(547);
432 port_remote = htons(546);
433 }
7de20a95 434#endif
747ec13b 435
7de20a95 436 if (!local_port) {
d352732e
EH
437 ent = getservbyname(service_local, "udp");
438 if (ent)
439 local_port = ent->s_port;
440 else
441 local_port = port_local;
442
443 ent = getservbyname(service_remote, "udp");
444 if (ent)
445 remote_port = ent->s_port;
446 else
447 remote_port = port_remote;
448
449 endservent();
450 }
451
452 if (local_family == AF_INET) {
453 /* We need at least one server */
454 if (servers == NULL) {
455 log_fatal("No servers specified.");
456 }
457
458
459 /* Set up the server sockaddrs. */
460 for (sp = servers; sp; sp = sp->next) {
461 sp->to.sin_port = local_port;
462 sp->to.sin_family = AF_INET;
747ec13b 463#ifdef HAVE_SA_LEN
d352732e 464 sp->to.sin_len = sizeof sp->to;
7de20a95 465#endif
d352732e
EH
466 }
467 }
7de20a95 468#ifdef DHCPv6
d352732e 469 else {
7de20a95
EH
470 unsigned code;
471
d352732e
EH
472 /* We need at least one upstream and one downstream interface */
473 if (upstreams == NULL || downstreams == NULL) {
474 log_info("Must specify at least one lower "
475 "and one upper interface.\n");
476 usage();
477 }
7de20a95
EH
478
479 /* Set up the initial dhcp option universe. */
480 initialize_common_option_spaces();
481
482 /* Check requested options. */
483 code = D6O_RELAY_MSG;
484 if (!option_code_hash_lookup(&requested_opts[0],
485 dhcpv6_universe.code_hash,
486 &code, 0, MDL))
487 log_fatal("Unable to find the RELAY_MSG "
488 "option definition.");
489 code = D6O_INTERFACE_ID;
490 if (!option_code_hash_lookup(&requested_opts[1],
491 dhcpv6_universe.code_hash,
492 &code, 0, MDL))
493 log_fatal("Unable to find the INTERFACE_ID "
494 "option definition.");
d352732e 495 }
747ec13b 496#endif
747ec13b
TL
497
498 /* Get the current time... */
be62cf06 499 gettimeofday(&cur_tv, NULL);
747ec13b
TL
500
501 /* Discover all the network interfaces. */
7de20a95 502 discover_interfaces(DISCOVER_RELAY);
747ec13b 503
7de20a95
EH
504#ifdef DHCPv6
505 if (local_family == AF_INET6)
d352732e 506 setup_streams();
7de20a95 507#endif
84228fed 508
e7012e3f
TL
509 /* Become a daemon... */
510 if (!no_daemon) {
511 int pid;
512 FILE *pf;
513 int pfdesc;
514
515 log_perror = 0;
516
517 if ((pid = fork()) < 0)
7de20a95 518 log_fatal("Can't fork daemon: %m");
e7012e3f 519 else if (pid)
7de20a95 520 exit(0);
e7012e3f 521
7de20a95 522 pfdesc = open(path_dhcrelay_pid,
e7012e3f
TL
523 O_CREAT | O_TRUNC | O_WRONLY, 0644);
524
525 if (pfdesc < 0) {
7de20a95 526 log_error("Can't create %s: %m", path_dhcrelay_pid);
e7012e3f 527 } else {
7de20a95 528 pf = fdopen(pfdesc, "w");
e7012e3f 529 if (!pf)
7de20a95 530 log_error("Can't fdopen %s: %m",
e7012e3f
TL
531 path_dhcrelay_pid);
532 else {
7de20a95
EH
533 fprintf(pf, "%ld\n",(long)getpid());
534 fclose(pf);
e7012e3f
TL
535 }
536 }
537
7de20a95
EH
538 close(0);
539 close(1);
540 close(2);
541 pid = setsid();
a546f2a7 542
ae566556 543 IGNORE_RET (chdir("/"));
e7012e3f
TL
544 }
545
7de20a95 546 /* Set up the packet handler... */
d352732e
EH
547 if (local_family == AF_INET)
548 bootp_packet_handler = do_relay4;
7de20a95 549#ifdef DHCPv6
d352732e
EH
550 else
551 dhcpv6_packet_handler = do_packet6;
7de20a95
EH
552#endif
553
747ec13b 554 /* Start dispatching packets and timeouts... */
7de20a95 555 dispatch();
84228fed 556
d352732e 557 /* Not reached */
7de20a95 558 return (0);
747ec13b
TL
559}
560
7de20a95
EH
561static void
562do_relay4(struct interface_info *ip, struct dhcp_packet *packet,
563 unsigned int length, unsigned int from_port, struct iaddr from,
564 struct hardware *hfrom) {
747ec13b
TL
565 struct server_list *sp;
566 struct sockaddr_in to;
567 struct interface_info *out;
98311e4b 568 struct hardware hto, *htop;
747ec13b 569
7de20a95
EH
570 if (packet->hlen > sizeof packet->chaddr) {
571 log_info("Discarding packet with invalid hlen.");
60dab809
TL
572 return;
573 }
574
866428dd
TL
575 /* Find the interface that corresponds to the giaddr
576 in the packet. */
7de20a95
EH
577 if (packet->giaddr.s_addr) {
578 for (out = interfaces; out; out = out->next) {
98bd7ca0
DH
579 int i;
580
581 for (i = 0 ; i < out->address_count ; i++ ) {
582 if (out->addresses[i].s_addr ==
583 packet->giaddr.s_addr)
584 i = -1;
585 break;
586 }
587
588 if (i == -1)
78c553c4 589 break;
866428dd
TL
590 }
591 } else {
7de20a95 592 out = NULL;
866428dd
TL
593 }
594
747ec13b 595 /* If it's a bootreply, forward it to the client. */
7de20a95
EH
596 if (packet->op == BOOTREPLY) {
597 if (!(packet->flags & htons(BOOTP_BROADCAST)) &&
598 can_unicast_without_arp(out)) {
599 to.sin_addr = packet->yiaddr;
75661c60 600 to.sin_port = remote_port;
98311e4b
DH
601
602 /* and hardware address is not broadcast */
603 htop = &hto;
d2bc90bd 604 } else {
7de20a95 605 to.sin_addr.s_addr = htonl(INADDR_BROADCAST);
75661c60 606 to.sin_port = remote_port;
98311e4b
DH
607
608 /* hardware address is broadcast */
609 htop = NULL;
747ec13b
TL
610 }
611 to.sin_family = AF_INET;
612#ifdef HAVE_SA_LEN
613 to.sin_len = sizeof to;
614#endif
615
7de20a95
EH
616 memcpy(&hto.hbuf[1], packet->chaddr, packet->hlen);
617 hto.hbuf[0] = packet->htype;
618 hto.hlen = packet->hlen + 1;
747ec13b 619
866428dd
TL
620 /* Wipe out the agent relay options and, if possible, figure
621 out which interface to use based on the contents of the
622 option that we put on the request to which the server is
623 replying. */
624 if (!(length =
7de20a95 625 strip_relay_agent_options(ip, &out, packet, length)))
866428dd
TL
626 return;
627
747ec13b 628 if (!out) {
7de20a95
EH
629 log_error("Packet to bogus giaddr %s.\n",
630 inet_ntoa(packet->giaddr));
866428dd 631 ++bogus_giaddr_drops;
747ec13b
TL
632 return;
633 }
634
98bd7ca0
DH
635 if (send_packet(out, NULL, packet, length, out->addresses[0],
636 &to, htop) < 0) {
866428dd
TL
637 ++server_packet_errors;
638 } else {
7de20a95
EH
639 log_debug("Forwarded BOOTREPLY for %s to %s",
640 print_hw_addr(packet->htype, packet->hlen,
641 packet->chaddr),
642 inet_ntoa(to.sin_addr));
75661c60 643
866428dd
TL
644 ++server_packets_relayed;
645 }
747ec13b
TL
646 return;
647 }
648
866428dd
TL
649 /* If giaddr matches one of our addresses, ignore the packet -
650 we just sent it. */
651 if (out)
652 return;
653
654 /* Add relay agent options if indicated. If something goes wrong,
655 drop the packet. */
98bd7ca0
DH
656 if (!(length = add_relay_agent_options(ip, packet, length,
657 ip->addresses[0])))
75661c60 658 return;
75661c60 659
866428dd
TL
660 /* If giaddr is not already set, Set it so the server can
661 figure out what net it's from and so that we can later
662 forward the response to the correct net. If it's already
663 set, the response will be sent directly to the relay agent
664 that set giaddr, so we won't see it. */
7de20a95 665 if (!packet->giaddr.s_addr)
98bd7ca0 666 packet->giaddr = ip->addresses[0];
7de20a95
EH
667 if (packet->hops < max_hop_count)
668 packet->hops = packet->hops + 1;
98311e4b
DH
669 else
670 return;
75661c60 671
747ec13b
TL
672 /* Otherwise, it's a BOOTREQUEST, so forward it to all the
673 servers. */
7de20a95 674 for (sp = servers; sp; sp = sp->next) {
98bd7ca0
DH
675 if (send_packet((fallback_interface
676 ? fallback_interface : interfaces),
677 NULL, packet, length, ip->addresses[0],
678 &sp->to, NULL) < 0) {
866428dd 679 ++client_packet_errors;
747ec13b 680 } else {
7de20a95
EH
681 log_debug("Forwarded BOOTREQUEST for %s to %s",
682 print_hw_addr(packet->htype, packet->hlen,
683 packet->chaddr),
684 inet_ntoa(sp->to.sin_addr));
866428dd 685 ++client_packets_relayed;
747ec13b
TL
686 }
687 }
78c553c4 688
747ec13b
TL
689}
690
866428dd 691/* Strip any Relay Agent Information options from the DHCP packet
98311e4b
DH
692 option buffer. If there is a circuit ID suboption, look up the
693 outgoing interface based upon it. */
866428dd 694
7de20a95
EH
695static int
696strip_relay_agent_options(struct interface_info *in,
d352732e
EH
697 struct interface_info **out,
698 struct dhcp_packet *packet,
699 unsigned length) {
866428dd 700 int is_dhcp = 0;
88cd8aca 701 u_int8_t *op, *nextop, *sp, *max;
866428dd
TL
702 int good_agent_option = 0;
703 int status;
704
705 /* If we're not adding agent options to packets, we're not taking
706 them out either. */
707 if (!add_agent_options)
7de20a95 708 return (length);
866428dd
TL
709
710 /* If there's no cookie, it's a bootp packet, so we should just
711 forward it unchanged. */
7de20a95
EH
712 if (memcmp(packet->options, DHCP_OPTIONS_COOKIE, 4))
713 return (length);
866428dd
TL
714
715 max = ((u_int8_t *)packet) + length;
7de20a95 716 sp = op = &packet->options[4];
866428dd
TL
717
718 while (op < max) {
7de20a95 719 switch(*op) {
866428dd
TL
720 /* Skip padding... */
721 case DHO_PAD:
722 if (sp != op)
723 *sp = *op;
724 ++op;
725 ++sp;
726 continue;
727
728 /* If we see a message type, it's a DHCP packet. */
729 case DHO_DHCP_MESSAGE_TYPE:
730 is_dhcp = 1;
731 goto skip;
732 break;
733
734 /* Quit immediately if we hit an End option. */
735 case DHO_END:
736 if (sp != op)
737 *sp++ = *op++;
738 goto out;
739
740 case DHO_DHCP_AGENT_OPTIONS:
741 /* We shouldn't see a relay agent option in a
742 packet before we've seen the DHCP packet type,
743 but if we do, we have to leave it alone. */
744 if (!is_dhcp)
745 goto skip;
746
88cd8aca
DH
747 /* Do not process an agent option if it exceeds the
748 * buffer. Fail this packet.
749 */
750 nextop = op + op[1] + 2;
751 if (nextop > max)
7de20a95 752 return (0);
88cd8aca 753
7de20a95
EH
754 status = find_interface_by_agent_option(packet,
755 out, op + 2,
756 op[1]);
866428dd 757 if (status == -1 && drop_agent_mismatches)
7de20a95 758 return (0);
866428dd
TL
759 if (status)
760 good_agent_option = 1;
88cd8aca 761 op = nextop;
866428dd
TL
762 break;
763
764 skip:
765 /* Skip over other options. */
766 default:
88cd8aca 767 /* Fail if processing this option will exceed the
7de20a95 768 * buffer(op[1] is malformed).
88cd8aca
DH
769 */
770 nextop = op + op[1] + 2;
771 if (nextop > max)
7de20a95 772 return (0);
88cd8aca
DH
773
774 if (sp != op) {
775 memmove(sp, op, op[1] + 2);
776 sp += op[1] + 2;
777 op = nextop;
778 } else
779 op = sp = nextop;
780
866428dd
TL
781 break;
782 }
783 }
784 out:
785
786 /* If it's not a DHCP packet, we're not supposed to touch it. */
787 if (!is_dhcp)
7de20a95 788 return (length);
866428dd
TL
789
790 /* If none of the agent options we found matched, or if we didn't
791 find any agent options, count this packet as not having any
792 matching agent options, and if we're relying on agent options
793 to determine the outgoing interface, drop the packet. */
794
795 if (!good_agent_option) {
796 ++missing_agent_option;
797 if (drop_agent_mismatches)
7de20a95 798 return (0);
866428dd
TL
799 }
800
801 /* Adjust the length... */
802 if (sp != op) {
7de20a95 803 length = sp -((u_int8_t *)packet);
866428dd 804
7de20a95 805 /* Make sure the packet isn't short(this is unlikely,
d352732e 806 but WTH) */
866428dd 807 if (length < BOOTP_MIN_LEN) {
7de20a95 808 memset(sp, DHO_PAD, BOOTP_MIN_LEN - length);
866428dd
TL
809 length = BOOTP_MIN_LEN;
810 }
811 }
7de20a95 812 return (length);
866428dd
TL
813}
814
815
816/* Find an interface that matches the circuit ID specified in the
817 Relay Agent Information option. If one is found, store it through
818 the pointer given; otherwise, leave the existing pointer alone.
819
820 We actually deviate somewhat from the current specification here:
821 if the option buffer is corrupt, we suggest that the caller not
822 respond to this packet. If the circuit ID doesn't match any known
823 interface, we suggest that the caller to drop the packet. Only if
824 we find a circuit ID that matches an existing interface do we tell
825 the caller to go ahead and process the packet. */
826
7de20a95
EH
827static int
828find_interface_by_agent_option(struct dhcp_packet *packet,
d352732e
EH
829 struct interface_info **out,
830 u_int8_t *buf, int len) {
639b70fa 831 int i = 0;
866428dd 832 u_int8_t *circuit_id = 0;
98311e4b 833 unsigned circuit_id_len = 0;
866428dd
TL
834 struct interface_info *ip;
835
836 while (i < len) {
837 /* If the next agent option overflows the end of the
838 packet, the agent option buffer is corrupt. */
839 if (i + 1 == len ||
7de20a95 840 i + buf[i + 1] + 2 > len) {
866428dd 841 ++corrupt_agent_options;
7de20a95 842 return (-1);
866428dd 843 }
7de20a95 844 switch(buf[i]) {
866428dd
TL
845 /* Remember where the circuit ID is... */
846 case RAI_CIRCUIT_ID:
7de20a95
EH
847 circuit_id = &buf[i + 2];
848 circuit_id_len = buf[i + 1];
866428dd
TL
849 i += circuit_id_len + 2;
850 continue;
851
852 default:
7de20a95 853 i += buf[i + 1] + 2;
866428dd
TL
854 break;
855 }
856 }
857
858 /* If there's no circuit ID, it's not really ours, tell the caller
859 it's no good. */
860 if (!circuit_id) {
861 ++missing_circuit_id;
7de20a95 862 return (-1);
866428dd
TL
863 }
864
865 /* Scan the interface list looking for an interface whose
866 name matches the one specified in circuit_id. */
867
7de20a95
EH
868 for (ip = interfaces; ip; ip = ip->next) {
869 if (ip->circuit_id &&
870 ip->circuit_id_len == circuit_id_len &&
871 !memcmp(ip->circuit_id, circuit_id, circuit_id_len))
866428dd
TL
872 break;
873 }
874
875 /* If we got a match, use it. */
876 if (ip) {
877 *out = ip;
7de20a95 878 return (1);
866428dd
TL
879 }
880
881 /* If we didn't get a match, the circuit ID was bogus. */
882 ++bad_circuit_id;
7de20a95 883 return (-1);
866428dd
TL
884}
885
7de20a95
EH
886/*
887 * Examine a packet to see if it's a candidate to have a Relay
888 * Agent Information option tacked onto its tail. If it is, tack
889 * the option on.
890 */
891static int
892add_relay_agent_options(struct interface_info *ip, struct dhcp_packet *packet,
d352732e
EH
893 unsigned length, struct in_addr giaddr) {
894 int is_dhcp = 0, mms;
88cd8aca
DH
895 unsigned optlen;
896 u_int8_t *op, *nextop, *sp, *max, *end_pad = NULL;
866428dd
TL
897
898 /* If we're not adding agent options to packets, we can skip
899 this. */
900 if (!add_agent_options)
7de20a95 901 return (length);
866428dd
TL
902
903 /* If there's no cookie, it's a bootp packet, so we should just
904 forward it unchanged. */
88cd8aca 905 if (memcmp(packet->options, DHCP_OPTIONS_COOKIE, 4))
7de20a95 906 return (length);
866428dd 907
d352732e 908 max = ((u_int8_t *)packet) + dhcp_max_agent_option_packet_length;
88cd8aca
DH
909
910 /* Commence processing after the cookie. */
911 sp = op = &packet->options[4];
866428dd
TL
912
913 while (op < max) {
7de20a95 914 switch(*op) {
866428dd
TL
915 /* Skip padding... */
916 case DHO_PAD:
88cd8aca
DH
917 /* Remember the first pad byte so we can commandeer
918 * padded space.
919 *
920 * XXX: Is this really a good idea? Sure, we can
921 * seemingly reduce the packet while we're looking,
922 * but if the packet was signed by the client then
7de20a95 923 * this padding is part of the checksum(RFC3118),
88cd8aca
DH
924 * and its nonpresence would break authentication.
925 */
926 if (end_pad == NULL)
927 end_pad = sp;
928
866428dd 929 if (sp != op)
88cd8aca
DH
930 *sp++ = *op++;
931 else
932 sp = ++op;
933
866428dd
TL
934 continue;
935
936 /* If we see a message type, it's a DHCP packet. */
937 case DHO_DHCP_MESSAGE_TYPE:
938 is_dhcp = 1;
939 goto skip;
d352732e
EH
940
941 /*
942 * If there's a maximum message size option, we
943 * should pay attention to it
944 */
945 case DHO_DHCP_MAX_MESSAGE_SIZE:
946 mms = ntohs(*(op + 2));
947 if (mms < dhcp_max_agent_option_packet_length &&
948 mms >= DHCP_MTU_MIN)
949 max = ((u_int8_t *)packet) + mms;
950 goto skip;
866428dd
TL
951
952 /* Quit immediately if we hit an End option. */
953 case DHO_END:
954 goto out;
955
956 case DHO_DHCP_AGENT_OPTIONS:
957 /* We shouldn't see a relay agent option in a
958 packet before we've seen the DHCP packet type,
959 but if we do, we have to leave it alone. */
960 if (!is_dhcp)
961 goto skip;
88cd8aca
DH
962
963 end_pad = NULL;
866428dd
TL
964
965 /* There's already a Relay Agent Information option
966 in this packet. How embarrassing. Decide what
967 to do based on the mode the user specified. */
968
7de20a95 969 switch(agent_relay_mode) {
866428dd
TL
970 case forward_and_append:
971 goto skip;
972 case forward_untouched:
7de20a95 973 return (length);
866428dd 974 case discard:
7de20a95 975 return (0);
866428dd
TL
976 case forward_and_replace:
977 default:
978 break;
979 }
980
981 /* Skip over the agent option and start copying
982 if we aren't copying already. */
88cd8aca 983 op += op[1] + 2;
866428dd
TL
984 break;
985
986 skip:
987 /* Skip over other options. */
988 default:
88cd8aca 989 /* Fail if processing this option will exceed the
7de20a95 990 * buffer(op[1] is malformed).
88cd8aca
DH
991 */
992 nextop = op + op[1] + 2;
993 if (nextop > max)
7de20a95 994 return (0);
88cd8aca
DH
995
996 end_pad = NULL;
997
998 if (sp != op) {
999 memmove(sp, op, op[1] + 2);
1000 sp += op[1] + 2;
1001 op = nextop;
1002 } else
1003 op = sp = nextop;
1004
866428dd
TL
1005 break;
1006 }
1007 }
1008 out:
1009
1010 /* If it's not a DHCP packet, we're not supposed to touch it. */
1011 if (!is_dhcp)
7de20a95 1012 return (length);
866428dd
TL
1013
1014 /* If the packet was padded out, we can store the agent option
1015 at the beginning of the padding. */
1016
88cd8aca 1017 if (end_pad != NULL)
866428dd
TL
1018 sp = end_pad;
1019
1020 /* Remember where the end of the packet was after parsing
1021 it. */
1022 op = sp;
1023
88cd8aca 1024 /* Sanity check. Had better not ever happen. */
7de20a95
EH
1025 if ((ip->circuit_id_len > 255) ||(ip->circuit_id_len < 1))
1026 log_fatal("Circuit ID length %d out of range [1-255] on "
88cd8aca
DH
1027 "%s\n", ip->circuit_id_len, ip->name);
1028 optlen = ip->circuit_id_len + 2; /* RAI_CIRCUIT_ID + len */
1029
1030 if (ip->remote_id) {
1031 if (ip->remote_id_len > 255 || ip->remote_id_len < 1)
7de20a95 1032 log_fatal("Remote ID length %d out of range [1-255] "
88cd8aca
DH
1033 "on %s\n", ip->circuit_id_len, ip->name);
1034 optlen += ip->remote_id_len + 2; /* RAI_REMOTE_ID + len */
1035 }
1036
7de20a95 1037 /* We do not support relay option fragmenting(multiple options to
88cd8aca
DH
1038 * support an option data exceeding 255 bytes).
1039 */
7de20a95
EH
1040 if ((optlen < 3) ||(optlen > 255))
1041 log_fatal("Total agent option length(%u) out of range "
88cd8aca
DH
1042 "[3 - 255] on %s\n", optlen, ip->name);
1043
d352732e
EH
1044 /*
1045 * Is there room for the option, its code+len, and DHO_END?
1046 * If not, forward without adding the option.
1047 */
1048 if (max - sp >= optlen + 3) {
1049 log_debug("Adding %d-byte relay agent option", optlen + 3);
1050
1051 /* Okay, cons up *our* Relay Agent Information option. */
1052 *sp++ = DHO_DHCP_AGENT_OPTIONS;
1053 *sp++ = optlen;
1054
1055 /* Copy in the circuit id... */
1056 *sp++ = RAI_CIRCUIT_ID;
1057 *sp++ = ip->circuit_id_len;
1058 memcpy(sp, ip->circuit_id, ip->circuit_id_len);
1059 sp += ip->circuit_id_len;
1060
1061 /* Copy in remote ID... */
1062 if (ip->remote_id) {
1063 *sp++ = RAI_REMOTE_ID;
1064 *sp++ = ip->remote_id_len;
1065 memcpy(sp, ip->remote_id, ip->remote_id_len);
1066 sp += ip->remote_id_len;
1067 }
1068 } else {
1069 ++agent_option_errors;
1070 log_error("No room in packet (used %d of %d) "
1071 "for %d-byte relay agent option: omitted",
1072 (int) (sp - ((u_int8_t *) packet)),
1073 (int) (max - ((u_int8_t *) packet)),
1074 optlen + 3);
866428dd
TL
1075 }
1076
d352732e
EH
1077 /*
1078 * Deposit an END option unless the packet is full (shouldn't
1079 * be possible).
1080 */
1081 if (sp < max)
1082 *sp++ = DHO_END;
866428dd
TL
1083
1084 /* Recalculate total packet length. */
7de20a95 1085 length = sp -((u_int8_t *)packet);
866428dd 1086
7de20a95 1087 /* Make sure the packet isn't short(this is unlikely, but WTH) */
866428dd 1088 if (length < BOOTP_MIN_LEN) {
88cd8aca 1089 memset(sp, DHO_PAD, BOOTP_MIN_LEN - length);
7de20a95
EH
1090 return (BOOTP_MIN_LEN);
1091 }
1092
1093 return (length);
1094}
1095
1096#ifdef DHCPv6
1097/*
1098 * Parse a downstream argument: [address%]interface[#index].
1099 */
1100static struct stream_list *
1101parse_downstream(char *arg) {
1102 struct stream_list *dp, *up;
1103 struct interface_info *ifp = NULL;
1104 char *ifname, *addr, *iid;
1105 isc_result_t status;
1106
1107 if (!supports_multiple_interfaces(ifp) &&
1108 (downstreams != NULL))
1109 log_fatal("No support for multiple interfaces.");
1110
1111 /* Decode the argument. */
1112 ifname = strchr(arg, '%');
1113 if (ifname == NULL) {
1114 ifname = arg;
1115 addr = NULL;
1116 } else {
1117 *ifname++ = '\0';
1118 addr = arg;
1119 }
1120 iid = strchr(ifname, '#');
1121 if (iid != NULL) {
1122 *iid++ = '\0';
1123 }
1124 if (strlen(ifname) >= sizeof(ifp->name)) {
1125 log_error("Interface name '%s' too long", ifname);
1126 usage();
1127 }
1128
1129 /* Don't declare twice. */
1130 for (dp = downstreams; dp; dp = dp->next) {
1131 if (strcmp(ifname, dp->ifp->name) == 0)
1132 log_fatal("Down interface '%s' declared twice.",
1133 ifname);
1134 }
1135
1136 /* Share with up side? */
1137 for (up = upstreams; up; up = up->next) {
1138 if (strcmp(ifname, up->ifp->name) == 0) {
1139 log_info("Interface '%s' is both down and up.",
1140 ifname);
1141 ifp = up->ifp;
1142 break;
1143 }
1144 }
1145
1146 /* New interface. */
1147 if (ifp == NULL) {
1148 status = interface_allocate(&ifp, MDL);
1149 if (status != ISC_R_SUCCESS)
1150 log_fatal("%s: interface_allocate: %s",
1151 arg, isc_result_totext(status));
1152 strcpy(ifp->name, ifname);
1153 if (interfaces) {
1154 interface_reference(&ifp->next, interfaces, MDL);
1155 interface_dereference(&interfaces, MDL);
1156 }
1157 interface_reference(&interfaces, ifp, MDL);
1158 ifp->flags |= INTERFACE_REQUESTED | INTERFACE_DOWNSTREAM;
1159 }
1160
1161 /* New downstream. */
1162 dp = (struct stream_list *) dmalloc(sizeof(*dp), MDL);
1163 if (!dp)
1164 log_fatal("No memory for downstream.");
1165 dp->ifp = ifp;
1166 if (iid != NULL) {
1167 dp->id = atoi(iid);
1168 } else {
1169 dp->id = -1;
1170 }
1171 /* !addr case handled by setup. */
1172 if (addr && (inet_pton(AF_INET6, addr, &dp->link.sin6_addr) <= 0))
1173 log_fatal("Bad link address '%s'", addr);
1174
1175 return dp;
1176}
1177
1178/*
1179 * Parse an upstream argument: [address]%interface.
1180 */
1181static struct stream_list *
1182parse_upstream(char *arg) {
1183 struct stream_list *up, *dp;
1184 struct interface_info *ifp = NULL;
1185 char *ifname, *addr;
1186 isc_result_t status;
1187
1188 /* Decode the argument. */
1189 ifname = strchr(arg, '%');
1190 if (ifname == NULL) {
1191 ifname = arg;
1192 addr = All_DHCP_Servers;
1193 } else {
1194 *ifname++ = '\0';
1195 addr = arg;
1196 }
1197 if (strlen(ifname) >= sizeof(ifp->name)) {
1198 log_fatal("Interface name '%s' too long", ifname);
1199 }
1200
1201 /* Shared up interface? */
1202 for (up = upstreams; up; up = up->next) {
1203 if (strcmp(ifname, up->ifp->name) == 0) {
1204 ifp = up->ifp;
1205 break;
1206 }
1207 }
1208 for (dp = downstreams; dp; dp = dp->next) {
1209 if (strcmp(ifname, dp->ifp->name) == 0) {
1210 ifp = dp->ifp;
1211 break;
1212 }
1213 }
1214
1215 /* New interface. */
1216 if (ifp == NULL) {
1217 status = interface_allocate(&ifp, MDL);
1218 if (status != ISC_R_SUCCESS)
1219 log_fatal("%s: interface_allocate: %s",
1220 arg, isc_result_totext(status));
1221 strcpy(ifp->name, ifname);
1222 if (interfaces) {
1223 interface_reference(&ifp->next, interfaces, MDL);
1224 interface_dereference(&interfaces, MDL);
1225 }
1226 interface_reference(&interfaces, ifp, MDL);
1227 ifp->flags |= INTERFACE_REQUESTED | INTERFACE_UPSTREAM;
1228 }
1229
1230 /* New upstream. */
1231 up = (struct stream_list *) dmalloc(sizeof(*up), MDL);
1232 if (up == NULL)
1233 log_fatal("No memory for upstream.");
1234
1235 up->ifp = ifp;
1236
1237 if (inet_pton(AF_INET6, addr, &up->link.sin6_addr) <= 0)
1238 log_fatal("Bad address %s", addr);
1239
1240 return up;
1241}
1242
1243/*
1244 * Setup downstream interfaces.
1245 */
1246static void
1247setup_streams(void) {
1248 struct stream_list *dp, *up;
1249 int i;
1250 isc_boolean_t link_is_set;
1251
1252 for (dp = downstreams; dp; dp = dp->next) {
1253 /* Check interface */
1254 if (dp->ifp->v6address_count == 0)
d352732e
EH
1255 log_fatal("Interface '%s' has no IPv6 addresses.",
1256 dp->ifp->name);
7de20a95
EH
1257
1258 /* Check/set link. */
1259 if (IN6_IS_ADDR_UNSPECIFIED(&dp->link.sin6_addr))
1260 link_is_set = ISC_FALSE;
1261 else
1262 link_is_set = ISC_TRUE;
1263 for (i = 0; i < dp->ifp->v6address_count; i++) {
1264 if (IN6_IS_ADDR_LINKLOCAL(&dp->ifp->v6addresses[i]))
1265 continue;
1266 if (!link_is_set)
1267 break;
1268 if (!memcmp(&dp->ifp->v6addresses[i],
d352732e
EH
1269 &dp->link.sin6_addr,
1270 sizeof(dp->link.sin6_addr)))
7de20a95
EH
1271 break;
1272 }
1273 if (i == dp->ifp->v6address_count)
1274 log_fatal("Can't find link address for interface '%s'.",
1275 dp->ifp->name);
1276 if (!link_is_set)
1277 memcpy(&dp->link.sin6_addr,
1278 &dp->ifp->v6addresses[i],
1279 sizeof(dp->link.sin6_addr));
1280
1281 /* Set interface-id. */
1282 if (dp->id == -1)
1283 dp->id = dp->ifp->index;
1284 }
1285
1286 for (up = upstreams; up; up = up->next) {
1287 up->link.sin6_port = local_port;
1288 up->link.sin6_family = AF_INET6;
1289#ifdef HAVE_SA_LEN
1290 up->link.sin6_len = sizeof(up->link);
1291#endif
1292
1293 if (up->ifp->v6address_count == 0)
1294 log_fatal("Interface '%s' has no IPv6 addresses.",
1295 up->ifp->name);
1296 }
1297}
1298
1299/*
1300 * Add DHCPv6 agent options here.
1301 */
1302static const int required_forw_opts[] = {
1303 D6O_INTERFACE_ID,
1304 D6O_RELAY_MSG,
1305 0
1306};
1307
1308/*
1309 * Process a packet upwards, i.e., from client to server.
1310 */
1311static void
1312process_up6(struct packet *packet, struct stream_list *dp) {
1313 char forw_data[65535];
1314 unsigned cursor;
1315 struct dhcpv6_relay_packet *relay;
1316 struct option_state *opts;
1317 struct stream_list *up;
1318
1319 /* Check if the message should be relayed to the server. */
1320 switch (packet->dhcpv6_msg_type) {
1321 case DHCPV6_SOLICIT:
1322 case DHCPV6_REQUEST:
1323 case DHCPV6_CONFIRM:
1324 case DHCPV6_RENEW:
1325 case DHCPV6_REBIND:
1326 case DHCPV6_RELEASE:
1327 case DHCPV6_DECLINE:
1328 case DHCPV6_INFORMATION_REQUEST:
1329 case DHCPV6_RELAY_FORW:
1330 case DHCPV6_LEASEQUERY:
1331 log_info("Relaying %s from %s port %d going up.",
1332 dhcpv6_type_names[packet->dhcpv6_msg_type],
1333 piaddr(packet->client_addr),
1334 ntohs(packet->client_port));
1335 break;
1336
1337 case DHCPV6_ADVERTISE:
1338 case DHCPV6_REPLY:
1339 case DHCPV6_RECONFIGURE:
1340 case DHCPV6_RELAY_REPL:
1341 case DHCPV6_LEASEQUERY_REPLY:
1342 log_info("Discarding %s from %s port %d going up.",
1343 dhcpv6_type_names[packet->dhcpv6_msg_type],
1344 piaddr(packet->client_addr),
1345 ntohs(packet->client_port));
1346 return;
1347
1348 default:
1349 log_info("Unknown %d type from %s port %d going up.",
1350 packet->dhcpv6_msg_type,
1351 piaddr(packet->client_addr),
1352 ntohs(packet->client_port));
1353 return;
1354 }
1355
1356 /* Build the relay-forward header. */
1357 relay = (struct dhcpv6_relay_packet *) forw_data;
a3528574 1358 cursor = offsetof(struct dhcpv6_relay_packet, options);
7de20a95
EH
1359 relay->msg_type = DHCPV6_RELAY_FORW;
1360 if (packet->dhcpv6_msg_type == DHCPV6_RELAY_FORW) {
1361 if (packet->dhcpv6_hop_count >= max_hop_count) {
1362 log_info("Hop count exceeded,");
1363 return;
1364 }
1365 relay->hop_count = packet->dhcpv6_hop_count + 1;
1366 if (dp) {
1367 memcpy(&relay->link_address, &dp->link.sin6_addr, 16);
1368 } else {
1369 /* On smart relay add: && !global. */
1370 if (!use_if_id && downstreams->next) {
1371 log_info("Shan't get back the interface.");
1372 return;
1373 }
1374 memset(&relay->link_address, 0, 16);
1375 }
1376 } else {
1377 relay->hop_count = 0;
1378 if (!dp)
1379 return;
1380 memcpy(&relay->link_address, &dp->link.sin6_addr, 16);
1381 }
1382 memcpy(&relay->peer_address, packet->client_addr.iabuf, 16);
1383
1384 /* Get an option state. */
1385 opts = NULL;
1386 if (!option_state_allocate(&opts, MDL)) {
1387 log_fatal("No memory for upwards options.");
1388 }
1389
1390 /* Add an interface-id (if used). */
1391 if (use_if_id) {
1392 int if_id;
1393
1394 if (dp) {
1395 if_id = dp->id;
ebf076fe 1396 } else if (!downstreams->next) {
7de20a95
EH
1397 if_id = downstreams->id;
1398 } else {
1399 log_info("Don't know the interface.");
1400 option_state_dereference(&opts, MDL);
1401 return;
1402 }
1403
1404 if (!save_option_buffer(&dhcpv6_universe, opts,
1405 NULL, (unsigned char *) &if_id,
1406 sizeof(int),
1407 D6O_INTERFACE_ID, 0)) {
1408 log_error("Can't save interface-id.");
1409 option_state_dereference(&opts, MDL);
1410 return;
1411 }
1412 }
1413
1414 /* Add the relay-msg carrying the packet. */
1415 if (!save_option_buffer(&dhcpv6_universe, opts,
1416 NULL, (unsigned char *) packet->raw,
1417 packet->packet_length,
1418 D6O_RELAY_MSG, 0)) {
1419 log_error("Can't save relay-msg.");
1420 option_state_dereference(&opts, MDL);
1421 return;
1422 }
1423
1424 /* Finish the relay-forward message. */
1425 cursor += store_options6(forw_data + cursor,
1426 sizeof(forw_data) - cursor,
1427 opts, packet,
1428 required_forw_opts, NULL);
1429 option_state_dereference(&opts, MDL);
1430
1431 /* Send it to all upstreams. */
1432 for (up = upstreams; up; up = up->next) {
1433 send_packet6(up->ifp, (unsigned char *) forw_data,
1434 (size_t) cursor, &up->link);
1435 }
1436}
1437
1438/*
1439 * Process a packet downwards, i.e., from server to client.
1440 */
1441static void
1442process_down6(struct packet *packet) {
1443 struct stream_list *dp;
1444 struct option_cache *oc;
1445 struct data_string relay_msg;
1446 const struct dhcpv6_packet *msg;
1447 struct data_string if_id;
1448 struct sockaddr_in6 to;
1449 struct iaddr peer;
1450
1451 /* The packet must be a relay-reply message. */
1452 if (packet->dhcpv6_msg_type != DHCPV6_RELAY_REPL) {
1453 if (packet->dhcpv6_msg_type < dhcpv6_type_name_max)
1454 log_info("Discarding %s from %s port %d going down.",
1455 dhcpv6_type_names[packet->dhcpv6_msg_type],
1456 piaddr(packet->client_addr),
1457 ntohs(packet->client_port));
1458 else
1459 log_info("Unknown %d type from %s port %d going down.",
1460 packet->dhcpv6_msg_type,
1461 piaddr(packet->client_addr),
1462 ntohs(packet->client_port));
1463 return;
1464 }
1465
1466 /* Inits. */
1467 memset(&relay_msg, 0, sizeof(relay_msg));
1468 memset(&if_id, 0, sizeof(if_id));
1469 memset(&to, 0, sizeof(to));
1470 to.sin6_family = AF_INET6;
1471#ifdef HAVE_SA_LEN
1472 to.sin6_len = sizeof(to);
1473#endif
1474 to.sin6_port = remote_port;
1475 peer.len = 16;
1476
1477 /* Get the relay-msg option (carrying the message to relay). */
1478 oc = lookup_option(&dhcpv6_universe, packet->options, D6O_RELAY_MSG);
1479 if (oc == NULL) {
1480 log_info("No relay-msg.");
1481 return;
1482 }
1483 if (!evaluate_option_cache(&relay_msg, packet, NULL, NULL,
1484 packet->options, NULL,
1485 &global_scope, oc, MDL) ||
69c1bf48 1486 (relay_msg.len < offsetof(struct dhcpv6_packet, options))) {
7de20a95
EH
1487 log_error("Can't evaluate relay-msg.");
1488 return;
1489 }
1490 msg = (const struct dhcpv6_packet *) relay_msg.data;
1491
1492 /* Get the interface-id (if exists) and the downstream. */
1493 oc = lookup_option(&dhcpv6_universe, packet->options,
1494 D6O_INTERFACE_ID);
1495 if (oc != NULL) {
1496 int if_index;
1497
1498 if (!evaluate_option_cache(&if_id, packet, NULL, NULL,
1499 packet->options, NULL,
1500 &global_scope, oc, MDL) ||
1501 (if_id.len != sizeof(int))) {
1502 log_info("Can't evaluate interface-id.");
1503 goto cleanup;
1504 }
1505 memcpy(&if_index, if_id.data, sizeof(int));
1506 for (dp = downstreams; dp; dp = dp->next) {
1507 if (dp->id == if_index)
1508 break;
1509 }
1510 } else {
1511 if (use_if_id) {
1512 /* Require an interface-id. */
1513 log_info("No interface-id.");
1514 goto cleanup;
1515 }
1516 for (dp = downstreams; dp; dp = dp->next) {
1517 /* Get the first matching one. */
1518 if (!memcmp(&dp->link.sin6_addr,
d352732e 1519 &packet->dhcpv6_link_address,
7de20a95
EH
1520 sizeof(struct in6_addr)))
1521 break;
1522 }
1523 }
1524 /* Why bother when there is no choice. */
1525 if (!dp && !downstreams->next)
1526 dp = downstreams;
1527 if (!dp) {
1528 log_info("Can't find the down interface.");
1529 goto cleanup;
1530 }
1531 memcpy(peer.iabuf, &packet->dhcpv6_peer_address, peer.len);
1532 to.sin6_addr = packet->dhcpv6_peer_address;
1533
1534 /* Check if we should relay the carried message. */
1535 switch (msg->msg_type) {
1536 /* Relay-Reply of for another relay, not a client. */
1537 case DHCPV6_RELAY_REPL:
1538 to.sin6_port = local_port;
1539 /* Fall into: */
1540
1541 case DHCPV6_ADVERTISE:
1542 case DHCPV6_REPLY:
1543 case DHCPV6_RECONFIGURE:
1544 case DHCPV6_RELAY_FORW:
1545 case DHCPV6_LEASEQUERY_REPLY:
1546 log_info("Relaying %s to %s port %d down.",
1547 dhcpv6_type_names[msg->msg_type],
1548 piaddr(peer),
1549 ntohs(to.sin6_port));
1550 break;
1551
1552 case DHCPV6_SOLICIT:
1553 case DHCPV6_REQUEST:
1554 case DHCPV6_CONFIRM:
1555 case DHCPV6_RENEW:
1556 case DHCPV6_REBIND:
1557 case DHCPV6_RELEASE:
1558 case DHCPV6_DECLINE:
1559 case DHCPV6_INFORMATION_REQUEST:
1560 case DHCPV6_LEASEQUERY:
1561 log_info("Discarding %s to %s port %d down.",
1562 dhcpv6_type_names[msg->msg_type],
1563 piaddr(peer),
1564 ntohs(to.sin6_port));
1565 goto cleanup;
1566
1567 default:
1568 log_info("Unknown %d type to %s port %d down.",
1569 msg->msg_type,
1570 piaddr(peer),
1571 ntohs(to.sin6_port));
1572 goto cleanup;
1573 }
1574
1575 /* Send the message to the downstream. */
1576 send_packet6(dp->ifp, (unsigned char *) relay_msg.data,
1577 (size_t) relay_msg.len, &to);
1578
1579 cleanup:
1580 if (relay_msg.data != NULL)
1581 data_string_forget(&relay_msg, MDL);
1582 if (if_id.data != NULL)
1583 data_string_forget(&if_id, MDL);
1584}
1585
1586/*
1587 * Called by the dispatch packet handler with a decoded packet.
1588 */
1589void
1590dhcpv6(struct packet *packet) {
1591 struct stream_list *dp;
1592
1593 /* Try all relay-replies downwards. */
1594 if (packet->dhcpv6_msg_type == DHCPV6_RELAY_REPL) {
1595 process_down6(packet);
1596 return;
1597 }
1598 /* Others are candidates to go up if they come from down. */
1599 for (dp = downstreams; dp; dp = dp->next) {
1600 if (packet->interface != dp->ifp)
1601 continue;
1602 process_up6(packet, dp);
1603 return;
1604 }
1605 /* Relay-forward could work from an unknown interface. */
1606 if (packet->dhcpv6_msg_type == DHCPV6_RELAY_FORW) {
1607 process_up6(packet, NULL);
1608 return;
866428dd
TL
1609 }
1610
7de20a95 1611 log_info("Can't process packet from interface '%s'.",
d352732e 1612 packet->interface->name);
7de20a95
EH
1613}
1614#endif
1615
1616/* Stub routines needed for linking with DHCP libraries. */
1617void
1618bootp(struct packet *packet) {
d352732e 1619 return;
7de20a95
EH
1620}
1621
1622void
1623dhcp(struct packet *packet) {
d352732e 1624 return;
7de20a95
EH
1625}
1626
1627void
1628classify(struct packet *p, struct class *c) {
d352732e 1629 return;
7de20a95
EH
1630}
1631
1632int
1633check_collection(struct packet *p, struct lease *l, struct collection *c) {
1634 return 0;
1635}
1636
1637isc_result_t
1638find_class(struct class **class, const char *c1, const char *c2, int i) {
1639 return ISC_R_NOTFOUND;
1640}
1641
1642int
1643parse_allow_deny(struct option_cache **oc, struct parse *p, int i) {
1644 return 0;
1645}
1646
1647isc_result_t
1648dhcp_set_control_state(control_object_state_t oldstate,
1649 control_object_state_t newstate) {
1650 return ISC_R_SUCCESS;
866428dd 1651}