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