]> git.ipfire.org Git - thirdparty/dhcp.git/blame - relay/dhcrelay.c
[#189] ISC address updated
[thirdparty/dhcp.git] / relay / dhcrelay.c
CommitLineData
747ec13b
TL
1/* dhcrelay.c
2
3 DHCP/BOOTP Relay Agent. */
4
5/*
109fa1d0 6 * Copyright(c) 2004-2020 by Internet Systems Consortium, Inc.("ISC")
7de20a95 7 * Copyright(c) 1997-2003 by Internet Software Consortium
747ec13b 8 *
7512d88b
TM
9 * This Source Code Form is subject to the terms of the Mozilla Public
10 * License, v. 2.0. If a copy of the MPL was not distributed with this
11 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
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 21 * Internet Systems Consortium, Inc.
429a56d7
TM
22 * PO Box 360
23 * Newmarket, NH 03857 USA
98311e4b 24 * <info@isc.org>
2c85ac9b 25 * https://www.isc.org/
49733f31 26 *
747ec13b
TL
27 */
28
747ec13b 29#include "dhcpd.h"
fe5b0fdd 30#include <syslog.h>
47e8308d 31#include <signal.h>
be62cf06 32#include <sys/time.h>
7acd8753 33#include <isc/file.h>
747ec13b 34
747ec13b
TL
35TIME default_lease_time = 43200; /* 12 hours... */
36TIME max_lease_time = 86400; /* 24 hours... */
7de20a95
EH
37struct tree_cache *global_options[256];
38
39struct option *requested_opts[2];
747ec13b 40
aaf053e3
TL
41/* Needed to prevent linking against conflex.c. */
42int lexline;
43int lexchar;
44char *token_line;
45char *tlname;
46
b1b7b521 47const char *path_dhcrelay_pid = _PATH_DHCRELAY_PID;
4a5bfeac
SR
48isc_boolean_t no_dhcrelay_pid = ISC_FALSE;
49/* False (default) => we write and use a pid file */
50isc_boolean_t no_pid_file = ISC_FALSE;
e7012e3f 51
866428dd
TL
52int bogus_agent_drops = 0; /* Packets dropped because agent option
53 field was specified and we're not relaying
54 packets that already have an agent option
55 specified. */
56int bogus_giaddr_drops = 0; /* Packets sent to us to relay back to a
57 client, but with a bogus giaddr. */
58int client_packets_relayed = 0; /* Packets relayed from client to server. */
59int server_packet_errors = 0; /* Errors sending packets to servers. */
60int server_packets_relayed = 0; /* Packets relayed from server to client. */
61int client_packet_errors = 0; /* Errors sending packets to clients. */
62
63int add_agent_options = 0; /* If nonzero, add relay agent options. */
6e7e6637 64int add_rfc3527_suboption = 0; /* If nonzero, add RFC3527 link selection sub-option. */
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
29b23207
FD
80int no_daemon = 0;
81int dfd[2] = { -1, -1 };
82
7de20a95
EH
83#ifdef DHCPv6
84 /* Force use of DHCPv6 interface-id option. */
85isc_boolean_t use_if_id = ISC_FALSE;
86#endif
866428dd
TL
87
88 /* Maximum size of a packet with agent options added. */
d352732e 89int dhcp_max_agent_option_packet_length = DHCP_MTU_MIN;
866428dd
TL
90
91 /* What to do about packets we're asked to relay that
92 already have a relay option: */
93enum { forward_and_append, /* Forward and append our own relay option. */
94 forward_and_replace, /* Forward, but replace theirs with ours. */
95 forward_untouched, /* Forward without changes. */
96 discard } agent_relay_mode = forward_and_replace;
97
6c7e6157
TM
98extern u_int16_t local_port;
99extern u_int16_t remote_port;
747ec13b 100
78c553c4
DH
101/* Relay agent server list. */
102struct server_list {
103 struct server_list *next;
104 struct sockaddr_in to;
105} *servers;
106
cbef159e 107struct interface_info *uplink = NULL;
6e7e6637 108
7de20a95
EH
109#ifdef DHCPv6
110struct stream_list {
111 struct stream_list *next;
112 struct interface_info *ifp;
113 struct sockaddr_in6 link;
114 int id;
115} *downstreams, *upstreams;
116
0a2f9a62 117#ifndef UNIT_TEST
7de20a95
EH
118static struct stream_list *parse_downstream(char *);
119static struct stream_list *parse_upstream(char *);
120static void setup_streams(void);
0a2f9a62 121#endif /* UNIT_TEST */
619304cd
SR
122
123/*
124 * A pointer to a subscriber id to add to the message we forward.
125 * This is primarily for testing purposes as we only have one id
126 * for the entire relay and don't determine one per client which
127 * would be more useful.
128 */
129char *dhcrelay_sub_id = NULL;
7de20a95
EH
130#endif
131
0a2f9a62 132#ifndef UNIT_TEST
7de20a95
EH
133static void do_relay4(struct interface_info *, struct dhcp_packet *,
134 unsigned int, unsigned int, struct iaddr,
d352732e 135 struct hardware *);
0a2f9a62 136#endif /* UNIT_TEST */
7de20a95 137
0a2f9a62
TM
138extern int add_relay_agent_options(struct interface_info *,
139 struct dhcp_packet *, unsigned,
140 struct in_addr);
141extern int find_interface_by_agent_option(struct dhcp_packet *,
142 struct interface_info **, u_int8_t *, int);
143
144extern int strip_relay_agent_options(struct interface_info *,
145 struct interface_info **,
146 struct dhcp_packet *, unsigned);
147
148#ifndef UNIT_TEST
edd6d888
TM
149static void request_v4_interface(const char* name, int flags);
150
ae566556 151static const char copyright[] =
109fa1d0 152"Copyright 2004-2020 Internet Systems Consortium.";
ae566556
SR
153static const char arr[] = "All rights reserved.";
154static const char message[] =
155"Internet Systems Consortium DHCP Relay Agent";
156static const char url[] =
2c85ac9b 157"For info, please visit https://www.isc.org/software/dhcp/";
7de20a95 158
7acd8753
FD
159char *progname;
160
7de20a95 161#ifdef DHCPv6
563f0b8a 162#ifdef RELAY_PORT
7de20a95 163#define DHCRELAY_USAGE \
563f0b8a
FD
164"Usage: %s [-4] [-d] [-q] [-a] [-D]\n" \
165" [-A <length>] [-c <hops>]\n" \
166" [-p <port> | -rp <relay-port>]\n" \
167" [-pf <pid-file>] [--no-pid]\n"\
168" [-m append|replace|forward|discard]\n" \
169" [-i interface0 [ ... -i interfaceN]\n" \
170" [-iu interface0 [ ... -iu interfaceN]\n" \
171" [-id interface0 [ ... -id interfaceN]\n" \
172" [-U interface]\n" \
173" server0 [ ... serverN]\n\n" \
174" %s -6 [-d] [-q] [-I] [-c <hops>]\n" \
175" [-p <port> | -rp <relay-port>]\n" \
176" [-pf <pid-file>] [--no-pid]\n" \
177" [-s <subscriber-id>]\n" \
178" -l lower0 [ ... -l lowerN]\n" \
179" -u upper0 [ ... -u upperN]\n" \
180" lower (client link): [address%%]interface[#index]\n" \
181" upper (server link): [address%%]interface\n\n" \
182" %s {--version|--help|-h}"
183#else
184#define DHCRELAY_USAGE \
185"Usage: %s [-4] [-d] [-q] [-a] [-D]\n" \
7de20a95 186" [-A <length>] [-c <hops>] [-p <port>]\n" \
4a5bfeac 187" [-pf <pid-file>] [--no-pid]\n"\
7de20a95
EH
188" [-m append|replace|forward|discard]\n" \
189" [-i interface0 [ ... -i interfaceN]\n" \
f2e70402 190" [-iu interface0 [ ... -iu interfaceN]\n" \
edd6d888 191" [-id interface0 [ ... -id interfaceN]\n" \
b12e0ca9 192" [-U interface]\n" \
7de20a95 193" server0 [ ... serverN]\n\n" \
7acd8753 194" %s -6 [-d] [-q] [-I] [-c <hops>] [-p <port>]\n" \
619304cd
SR
195" [-pf <pid-file>] [--no-pid]\n" \
196" [-s <subscriber-id>]\n" \
7de20a95
EH
197" -l lower0 [ ... -l lowerN]\n" \
198" -u upper0 [ ... -u upperN]\n" \
68dfa910
FD
199" lower (client link): [address%%]interface[#index]\n" \
200" upper (server link): [address%%]interface\n\n" \
201" %s {--version|--help|-h}"
563f0b8a
FD
202#endif
203#else /* !DHCPv6 */
204#ifdef RELAY_PORT
205#define DHCRELAY_USAGE \
206"Usage: %s [-d] [-q] [-a] [-D] [-A <length>] [-c <hops>]\n" \
207" [-p <port> | -rp <relay-port>]\n" \
208" [-pf <pid-file>] [--no-pid]\n" \
209" [-m append|replace|forward|discard]\n" \
210" [-i interface0 [ ... -i interfaceN]\n" \
211" [-iu interface0 [ ... -iu interfaceN]\n" \
212" [-id interface0 [ ... -id interfaceN]\n" \
213" [-U interface]\n" \
214" server0 [ ... serverN]\n\n" \
215" %s {--version|--help|-h}"
7de20a95
EH
216#else
217#define DHCRELAY_USAGE \
7acd8753 218"Usage: %s [-d] [-q] [-a] [-D] [-A <length>] [-c <hops>] [-p <port>]\n" \
619304cd 219" [-pf <pid-file>] [--no-pid]\n" \
7de20a95
EH
220" [-m append|replace|forward|discard]\n" \
221" [-i interface0 [ ... -i interfaceN]\n" \
f2e70402 222" [-iu interface0 [ ... -iu interfaceN]\n" \
edd6d888 223" [-id interface0 [ ... -id interfaceN]\n" \
b12e0ca9 224" [-U interface]\n" \
68dfa910
FD
225" server0 [ ... serverN]\n\n" \
226" %s {--version|--help|-h}"
7de20a95 227#endif
563f0b8a 228#endif
7de20a95 229
b2cb745b
SR
230/*!
231 *
232 * \brief Print the generic usage message
233 *
234 * If the user has provided an incorrect command line print out
235 * the description of the command line. The arguments provide
236 * a way for the caller to request more specific information about
237 * the error be printed as well. Mostly this will be that some
0c03eaef 238 * command doesn't include its argument.
b2cb745b
SR
239 *
240 * \param sfmt - The basic string and format for the specific error
0c03eaef 241 * \param sarg - Generally the offending argument from the command line.
b2cb745b
SR
242 *
243 * \return Nothing
244 */
245static const char use_noarg[] = "No argument for command: %s";
563f0b8a
FD
246#ifdef RELAY_PORT
247static const char use_port_defined[] = "Port already set, %s inappropriate";
248#if !defined (USE_BPF_RECEIVE) && !defined (USE_LPF_RECEIVE)
249static const char bpf_sock_support[] = "Only LPF and BPF are supported: %s";
250#endif
251#endif
eda1d0aa 252#ifdef DHCPv6
b2cb745b
SR
253static const char use_badproto[] = "Protocol already set, %s inappropriate";
254static const char use_v4command[] = "Command not used for DHCPv6: %s";
255static const char use_v6command[] = "Command not used for DHCPv4: %s";
eda1d0aa 256#endif
b2cb745b
SR
257
258static void
259usage(const char *sfmt, const char *sarg) {
fc4f62fe
TM
260 log_info("%s %s", message, PACKAGE_VERSION);
261 log_info(copyright);
262 log_info(arr);
263 log_info(url);
b2cb745b
SR
264
265 /* If desired print out the specific error message */
266#ifdef PRINT_SPECIFIC_CL_ERRORS
267 if (sfmt != NULL)
268 log_error(sfmt, sarg);
269#endif
270
7acd8753
FD
271 log_fatal(DHCRELAY_USAGE,
272#ifdef DHCPv6
273 isc_file_basename(progname),
274#endif
68dfa910 275 isc_file_basename(progname),
7acd8753 276 isc_file_basename(progname));
7de20a95 277}
d2bc90bd 278
0a2f9a62 279int
98bd7ca0 280main(int argc, char **argv) {
7de20a95 281 isc_result_t status;
747ec13b 282 struct servent *ent;
78c553c4 283 struct server_list *sp = NULL;
4c750c58
PS
284 char *service_local = NULL, *service_remote = NULL;
285 u_int16_t port_local = 0, port_remote = 0;
29b23207 286 int quiet = 0;
7de20a95
EH
287 int fd;
288 int i;
563f0b8a
FD
289#ifdef RELAY_PORT
290 int port_defined = 0;
291#endif
7de20a95 292#ifdef DHCPv6
d352732e 293 struct stream_list *sl = NULL;
7de20a95
EH
294 int local_family_set = 0;
295#endif
747ec13b 296
7acd8753
FD
297#ifdef OLD_LOG_NAME
298 progname = "dhcrelay";
299#else
300 progname = argv[0];
301#endif
302
d352732e
EH
303 /* Make sure that file descriptors 0(stdin), 1,(stdout), and
304 2(stderr) are open. To do this, we assume that when we
305 open a file the lowest available file descriptor is used. */
306 fd = open("/dev/null", O_RDWR);
307 if (fd == 0)
308 fd = open("/dev/null", O_RDWR);
309 if (fd == 1)
310 fd = open("/dev/null", O_RDWR);
311 if (fd == 2)
312 log_perror = 0; /* No sense logging to /dev/null. */
313 else if (fd != -1)
314 close(fd);
dfb6c5aa 315
7acd8753 316 openlog(isc_file_basename(progname), DHCP_LOG_OPTIONS, LOG_DAEMON);
747ec13b 317
fe5b0fdd 318#if !defined(DEBUG)
7de20a95 319 setlogmask(LOG_UPTO(LOG_INFO));
0a2f9a62 320#endif
747ec13b 321
29b23207
FD
322 /* Parse arguments changing no_daemon */
323 for (i = 1; i < argc; i++) {
324 if (!strcmp(argv[i], "-d")) {
325 no_daemon = 1;
326 } else if (!strcmp(argv[i], "--version")) {
327 log_info("isc-dhcrelay-%s", PACKAGE_VERSION);
328 exit(0);
329 } else if (!strcmp(argv[i], "--help") ||
330 !strcmp(argv[i], "-h")) {
331 log_info(DHCRELAY_USAGE,
332#ifdef DHCPv6
333 isc_file_basename(progname),
334#endif
68dfa910 335 isc_file_basename(progname),
29b23207
FD
336 isc_file_basename(progname));
337 exit(0);
338 }
339 }
340 /* When not forbidden prepare to become a daemon */
341 if (!no_daemon) {
342 int pid;
343
344 if (pipe(dfd) == -1)
345 log_fatal("Can't get pipe: %m");
346 if ((pid = fork ()) < 0)
347 log_fatal("Can't fork daemon: %m");
348 if (pid != 0) {
349 /* Parent: wait for the child to start */
350 int n;
351
352 (void) close(dfd[1]);
353 do {
354 char buf;
355
356 n = read(dfd[0], &buf, 1);
357 if (n == 1)
358 _exit(0);
359 } while (n == -1 && errno == EINTR);
360 _exit(1);
361 }
362 /* Child */
363 (void) close(dfd[0]);
364 }
365
366
98bf1607 367 /* Set up the isc and dns library managers */
ca22af89 368 status = dhcp_context_create(DHCP_CONTEXT_PRE_DB, NULL, NULL);
98bf1607
SR
369 if (status != ISC_R_SUCCESS)
370 log_fatal("Can't initialize context: %s",
371 isc_result_totext(status));
372
6cecb7c5 373 /* Set up the OMAPI. */
7de20a95 374 status = omapi_init();
6cecb7c5 375 if (status != ISC_R_SUCCESS)
7de20a95
EH
376 log_fatal("Can't initialize OMAPI: %s",
377 isc_result_totext(status));
6cecb7c5
TL
378
379 /* Set up the OMAPI wrappers for the interface object. */
7de20a95 380 interface_setup();
6cecb7c5 381
747ec13b 382 for (i = 1; i < argc; i++) {
7de20a95
EH
383 if (!strcmp(argv[i], "-4")) {
384#ifdef DHCPv6
385 if (local_family_set && (local_family == AF_INET6)) {
b2cb745b 386 usage(use_badproto, "-4");
e7012e3f 387 }
7de20a95
EH
388 local_family_set = 1;
389 local_family = AF_INET;
390 } else if (!strcmp(argv[i], "-6")) {
391 if (local_family_set && (local_family == AF_INET)) {
b2cb745b 392 usage(use_badproto, "-6");
7de20a95
EH
393 }
394 local_family_set = 1;
395 local_family = AF_INET6;
396#endif
397 } else if (!strcmp(argv[i], "-d")) {
29b23207 398 /* no_daemon = 1; */
7de20a95 399 } else if (!strcmp(argv[i], "-q")) {
e7012e3f
TL
400 quiet = 1;
401 quiet_interface_discovery = 1;
7de20a95
EH
402 } else if (!strcmp(argv[i], "-p")) {
403 if (++i == argc)
b2cb745b 404 usage(use_noarg, argv[i-1]);
563f0b8a
FD
405#ifdef RELAY_PORT
406 if (port_defined)
407 usage(use_port_defined, argv[i-1]);
408 port_defined = 1;
409#endif
59112e84 410 local_port = validate_port(argv[i]);
7de20a95
EH
411 log_debug("binding to user-specified port %d",
412 ntohs(local_port));
563f0b8a
FD
413#ifdef RELAY_PORT
414 } else if (!strcmp(argv[i], "-rp")) {
415 if (++i == argc)
416 usage(use_noarg, argv[i-1]);
417 if (port_defined)
418 usage(use_port_defined, argv[i-1]);
419 port_defined = 1;
420 relay_port = validate_port(argv[i]);
421 log_debug("binding to user-specified relay port %d",
422 ntohs(relay_port));
423 add_agent_options = 1;
424#endif
7de20a95 425 } else if (!strcmp(argv[i], "-c")) {
98311e4b
DH
426 int hcount;
427 if (++i == argc)
b2cb745b 428 usage(use_noarg, argv[i-1]);
98311e4b
DH
429 hcount = atoi(argv[i]);
430 if (hcount <= 255)
431 max_hop_count= hcount;
432 else
b2cb745b 433 usage("Bad hop count to -c: %s", argv[i]);
7de20a95
EH
434 } else if (!strcmp(argv[i], "-i")) {
435#ifdef DHCPv6
436 if (local_family_set && (local_family == AF_INET6)) {
b2cb745b 437 usage(use_v4command, argv[i]);
7de20a95
EH
438 }
439 local_family_set = 1;
440 local_family = AF_INET;
441#endif
0f750c4f 442 if (++i == argc) {
b2cb745b 443 usage(use_noarg, argv[i-1]);
0f750c4f 444 }
edd6d888
TM
445
446 request_v4_interface(argv[i], INTERFACE_STREAMS);
f2e70402
TM
447 } else if (!strcmp(argv[i], "-iu")) {
448#ifdef DHCPv6
449 if (local_family_set && (local_family == AF_INET6)) {
450 usage(use_v4command, argv[i]);
451 }
452 local_family_set = 1;
453 local_family = AF_INET;
454#endif
455 if (++i == argc) {
456 usage(use_noarg, argv[i-1]);
457 }
edd6d888
TM
458
459 request_v4_interface(argv[i], INTERFACE_UPSTREAM);
460 } else if (!strcmp(argv[i], "-id")) {
461#ifdef DHCPv6
462 if (local_family_set && (local_family == AF_INET6)) {
463 usage(use_v4command, argv[i]);
f2e70402 464 }
edd6d888
TM
465 local_family_set = 1;
466 local_family = AF_INET;
467#endif
468 if (++i == argc) {
469 usage(use_noarg, argv[i-1]);
f2e70402 470 }
edd6d888
TM
471
472 request_v4_interface(argv[i], INTERFACE_DOWNSTREAM);
7de20a95
EH
473 } else if (!strcmp(argv[i], "-a")) {
474#ifdef DHCPv6
475 if (local_family_set && (local_family == AF_INET6)) {
b2cb745b 476 usage(use_v4command, argv[i]);
7de20a95
EH
477 }
478 local_family_set = 1;
479 local_family = AF_INET;
480#endif
481 add_agent_options = 1;
482 } else if (!strcmp(argv[i], "-A")) {
483#ifdef DHCPv6
484 if (local_family_set && (local_family == AF_INET6)) {
b2cb745b 485 usage(use_v4command, argv[i]);
7de20a95
EH
486 }
487 local_family_set = 1;
488 local_family = AF_INET;
489#endif
866428dd 490 if (++i == argc)
b2cb745b 491 usage(use_noarg, argv[i-1]);
d352732e 492
7de20a95 493 dhcp_max_agent_option_packet_length = atoi(argv[i]);
d352732e
EH
494
495 if (dhcp_max_agent_option_packet_length > DHCP_MTU_MAX)
496 log_fatal("%s: packet length exceeds "
497 "longest possible MTU\n",
498 argv[i]);
7de20a95
EH
499 } else if (!strcmp(argv[i], "-m")) {
500#ifdef DHCPv6
501 if (local_family_set && (local_family == AF_INET6)) {
b2cb745b 502 usage(use_v4command, argv[i]);
7de20a95
EH
503 }
504 local_family_set = 1;
505 local_family = AF_INET;
506#endif
866428dd 507 if (++i == argc)
b2cb745b 508 usage(use_noarg, argv[i-1]);
7de20a95 509 if (!strcasecmp(argv[i], "append")) {
866428dd 510 agent_relay_mode = forward_and_append;
7de20a95 511 } else if (!strcasecmp(argv[i], "replace")) {
866428dd 512 agent_relay_mode = forward_and_replace;
7de20a95 513 } else if (!strcasecmp(argv[i], "forward")) {
866428dd 514 agent_relay_mode = forward_untouched;
7de20a95 515 } else if (!strcasecmp(argv[i], "discard")) {
866428dd
TL
516 agent_relay_mode = discard;
517 } else
b2cb745b 518 usage("Unknown argument to -m: %s", argv[i]);
b12e0ca9 519 } else if (!strcmp(argv [i], "-U")) {
6e7e6637
TM
520 if (++i == argc)
521 usage(use_noarg, argv[i-1]);
522
cbef159e 523 if (uplink) {
fb641d8e 524 usage("more than one uplink (-U) specified: %s"
cbef159e
TM
525 ,argv[i]);
526 }
527
6e7e6637
TM
528 /* Allocate the uplink interface */
529 status = interface_allocate(&uplink, MDL);
530 if (status != ISC_R_SUCCESS) {
531 log_fatal("%s: uplink interface_allocate: %s",
532 argv[i], isc_result_totext(status));
533 }
0a2f9a62 534
cbef159e
TM
535 if (strlen(argv[i]) >= sizeof(uplink->name)) {
536 log_fatal("%s: uplink name too long,"
537 " it cannot exceed: %ld characters",
0ba7d5fc 538 argv[i], (long)(sizeof(uplink->name) - 1));
cbef159e 539 }
6e7e6637 540
cbef159e
TM
541 uplink->name[sizeof(uplink->name) - 1] = 0x00;
542 strncpy(uplink->name, argv[i],
543 sizeof(uplink->name) - 1);
f2e70402
TM
544 interface_snorf(uplink, (INTERFACE_REQUESTED |
545 INTERFACE_STREAMS));
6e7e6637
TM
546
547 /* Turn on -a, in case they don't do so explicitly */
548 add_agent_options = 1;
549 add_rfc3527_suboption = 1;
7de20a95
EH
550 } else if (!strcmp(argv[i], "-D")) {
551#ifdef DHCPv6
552 if (local_family_set && (local_family == AF_INET6)) {
b2cb745b 553 usage(use_v4command, argv[i]);
7de20a95
EH
554 }
555 local_family_set = 1;
556 local_family = AF_INET;
557#endif
866428dd 558 drop_agent_mismatches = 1;
7de20a95
EH
559#ifdef DHCPv6
560 } else if (!strcmp(argv[i], "-I")) {
561 if (local_family_set && (local_family == AF_INET)) {
b2cb745b 562 usage(use_v6command, argv[i]);
7de20a95
EH
563 }
564 local_family_set = 1;
565 local_family = AF_INET6;
566 use_if_id = ISC_TRUE;
567 } else if (!strcmp(argv[i], "-l")) {
568 if (local_family_set && (local_family == AF_INET)) {
b2cb745b 569 usage(use_v6command, argv[i]);
7de20a95
EH
570 }
571 local_family_set = 1;
572 local_family = AF_INET6;
d352732e
EH
573 if (downstreams != NULL)
574 use_if_id = ISC_TRUE;
7de20a95 575 if (++i == argc)
b2cb745b 576 usage(use_noarg, argv[i-1]);
d352732e
EH
577 sl = parse_downstream(argv[i]);
578 sl->next = downstreams;
579 downstreams = sl;
7de20a95
EH
580 } else if (!strcmp(argv[i], "-u")) {
581 if (local_family_set && (local_family == AF_INET)) {
b2cb745b 582 usage(use_v6command, argv[i]);
7de20a95
EH
583 }
584 local_family_set = 1;
585 local_family = AF_INET6;
586 if (++i == argc)
b2cb745b 587 usage(use_noarg, argv[i-1]);
d352732e
EH
588 sl = parse_upstream(argv[i]);
589 sl->next = upstreams;
590 upstreams = sl;
619304cd
SR
591 } else if (!strcmp(argv[i], "-s")) {
592 if (local_family_set && (local_family == AF_INET)) {
b2cb745b 593 usage(use_v6command, argv[i]);
619304cd
SR
594 }
595 local_family_set = 1;
596 local_family = AF_INET6;
597 if (++i == argc)
b2cb745b 598 usage(use_noarg, argv[i-1]);
619304cd 599 dhcrelay_sub_id = argv[i];
7de20a95 600#endif
4a5bfeac
SR
601 } else if (!strcmp(argv[i], "-pf")) {
602 if (++i == argc)
b2cb745b 603 usage(use_noarg, argv[i-1]);
4a5bfeac
SR
604 path_dhcrelay_pid = argv[i];
605 no_dhcrelay_pid = ISC_TRUE;
606 } else if (!strcmp(argv[i], "--no-pid")) {
607 no_pid_file = ISC_TRUE;
7de20a95 608 } else if (argv[i][0] == '-') {
b2cb745b 609 usage("Unknown command: %s", argv[i]);
747ec13b 610 } else {
78c553c4 611 struct hostent *he;
7de20a95
EH
612 struct in_addr ia, *iap = NULL;
613
614#ifdef DHCPv6
615 if (local_family_set && (local_family == AF_INET6)) {
b2cb745b 616 usage(use_v4command, argv[i]);
7de20a95
EH
617 }
618 local_family_set = 1;
619 local_family = AF_INET;
620#endif
621 if (inet_aton(argv[i], &ia)) {
78c553c4
DH
622 iap = &ia;
623 } else {
7de20a95 624 he = gethostbyname(argv[i]);
78c553c4 625 if (!he) {
7de20a95 626 log_error("%s: host unknown", argv[i]);
78c553c4
DH
627 } else {
628 iap = ((struct in_addr *)
7de20a95 629 he->h_addr_list[0]);
78c553c4
DH
630 }
631 }
7de20a95 632
78c553c4
DH
633 if (iap) {
634 sp = ((struct server_list *)
7de20a95 635 dmalloc(sizeof *sp, MDL));
78c553c4 636 if (!sp)
7de20a95
EH
637 log_fatal("no memory for server.\n");
638 sp->next = servers;
78c553c4 639 servers = sp;
7de20a95 640 memcpy(&sp->to.sin_addr, iap, sizeof *iap);
78c553c4 641 }
747ec13b
TL
642 }
643 }
e7012e3f 644
563f0b8a
FD
645#if defined(RELAY_PORT) && \
646 !defined (USE_BPF_RECEIVE) && !defined (USE_LPF_RECEIVE)
647 if (relay_port && (local_family == AF_INET))
648 usage(bpf_sock_support, "-rp");
649#endif
650
4a5bfeac
SR
651 /*
652 * If the user didn't specify a pid file directly
653 * find one from environment variables or defaults
654 */
655 if (no_dhcrelay_pid == ISC_FALSE) {
656 if (local_family == AF_INET) {
657 path_dhcrelay_pid = getenv("PATH_DHCRELAY_PID");
658 if (path_dhcrelay_pid == NULL)
659 path_dhcrelay_pid = _PATH_DHCRELAY_PID;
660 }
7de20a95 661#ifdef DHCPv6
4a5bfeac
SR
662 else {
663 path_dhcrelay_pid = getenv("PATH_DHCRELAY6_PID");
664 if (path_dhcrelay_pid == NULL)
665 path_dhcrelay_pid = _PATH_DHCRELAY6_PID;
666 }
7de20a95 667#endif
4a5bfeac 668 }
cfa7212d 669
d2bc90bd 670 if (!quiet) {
7de20a95
EH
671 log_info("%s %s", message, PACKAGE_VERSION);
672 log_info(copyright);
673 log_info(arr);
674 log_info(url);
0a2f9a62 675 } else
bc62fb2a 676 log_perror = 0;
d2bc90bd 677
7de20a95 678 /* Set default port */
d352732e 679 if (local_family == AF_INET) {
9733358a
EH
680 service_local = "bootps";
681 service_remote = "bootpc";
d352732e 682 port_local = htons(67);
9733358a 683 port_remote = htons(68);
d352732e 684 }
7de20a95 685#ifdef DHCPv6
d352732e
EH
686 else {
687 service_local = "dhcpv6-server";
688 service_remote = "dhcpv6-client";
689 port_local = htons(547);
690 port_remote = htons(546);
691 }
7de20a95 692#endif
747ec13b 693
7de20a95 694 if (!local_port) {
d352732e
EH
695 ent = getservbyname(service_local, "udp");
696 if (ent)
697 local_port = ent->s_port;
698 else
699 local_port = port_local;
700
701 ent = getservbyname(service_remote, "udp");
702 if (ent)
703 remote_port = ent->s_port;
704 else
705 remote_port = port_remote;
706
707 endservent();
708 }
709
710 if (local_family == AF_INET) {
711 /* We need at least one server */
712 if (servers == NULL) {
713 log_fatal("No servers specified.");
714 }
715
716
717 /* Set up the server sockaddrs. */
718 for (sp = servers; sp; sp = sp->next) {
719 sp->to.sin_port = local_port;
720 sp->to.sin_family = AF_INET;
747ec13b 721#ifdef HAVE_SA_LEN
d352732e 722 sp->to.sin_len = sizeof sp->to;
7de20a95 723#endif
d352732e
EH
724 }
725 }
7de20a95 726#ifdef DHCPv6
d352732e 727 else {
7de20a95
EH
728 unsigned code;
729
d352732e
EH
730 /* We need at least one upstream and one downstream interface */
731 if (upstreams == NULL || downstreams == NULL) {
732 log_info("Must specify at least one lower "
733 "and one upper interface.\n");
b2cb745b 734 usage(NULL, NULL);
d352732e 735 }
7de20a95
EH
736
737 /* Set up the initial dhcp option universe. */
738 initialize_common_option_spaces();
739
740 /* Check requested options. */
741 code = D6O_RELAY_MSG;
742 if (!option_code_hash_lookup(&requested_opts[0],
743 dhcpv6_universe.code_hash,
744 &code, 0, MDL))
745 log_fatal("Unable to find the RELAY_MSG "
746 "option definition.");
747 code = D6O_INTERFACE_ID;
748 if (!option_code_hash_lookup(&requested_opts[1],
749 dhcpv6_universe.code_hash,
750 &code, 0, MDL))
751 log_fatal("Unable to find the INTERFACE_ID "
752 "option definition.");
d352732e 753 }
747ec13b 754#endif
747ec13b
TL
755
756 /* Get the current time... */
be62cf06 757 gettimeofday(&cur_tv, NULL);
747ec13b
TL
758
759 /* Discover all the network interfaces. */
7de20a95 760 discover_interfaces(DISCOVER_RELAY);
747ec13b 761
7de20a95
EH
762#ifdef DHCPv6
763 if (local_family == AF_INET6)
d352732e 764 setup_streams();
7de20a95 765#endif
84228fed 766
e7012e3f
TL
767 /* Become a daemon... */
768 if (!no_daemon) {
29b23207 769 char buf = 0;
e7012e3f
TL
770 FILE *pf;
771 int pfdesc;
772
773 log_perror = 0;
774
29b23207
FD
775 /* Signal parent we started successfully. */
776 if (dfd[0] != -1 && dfd[1] != -1) {
777 if (write(dfd[1], &buf, 1) != 1)
778 log_fatal("write to parent: %m");
779 (void) close(dfd[1]);
780 dfd[0] = dfd[1] = -1;
781 }
e7012e3f 782
29b23207 783 /* Create the pid file. */
4a5bfeac
SR
784 if (no_pid_file == ISC_FALSE) {
785 pfdesc = open(path_dhcrelay_pid,
786 O_CREAT | O_TRUNC | O_WRONLY, 0644);
787
788 if (pfdesc < 0) {
789 log_error("Can't create %s: %m",
790 path_dhcrelay_pid);
791 } else {
792 pf = fdopen(pfdesc, "w");
793 if (!pf)
794 log_error("Can't fdopen %s: %m",
795 path_dhcrelay_pid);
796 else {
797 fprintf(pf, "%ld\n",(long)getpid());
798 fclose(pf);
0a2f9a62 799 }
4a5bfeac 800 }
e7012e3f
TL
801 }
802
dc9d7b08
MA
803 (void) close(0);
804 (void) close(1);
805 (void) close(2);
806 (void) setsid();
a546f2a7 807
ae566556 808 IGNORE_RET (chdir("/"));
e7012e3f
TL
809 }
810
7de20a95 811 /* Set up the packet handler... */
d352732e
EH
812 if (local_family == AF_INET)
813 bootp_packet_handler = do_relay4;
7de20a95 814#ifdef DHCPv6
d352732e
EH
815 else
816 dhcpv6_packet_handler = do_packet6;
7de20a95
EH
817#endif
818
e9326fd0
SR
819#if defined(ENABLE_GENTLE_SHUTDOWN)
820 /* no signal handlers until we deal with the side effects */
47e8308d
SR
821 /* install signal handlers */
822 signal(SIGINT, dhcp_signal_handler); /* control-c */
823 signal(SIGTERM, dhcp_signal_handler); /* kill */
e9326fd0 824#endif
47e8308d 825
747ec13b 826 /* Start dispatching packets and timeouts... */
7de20a95 827 dispatch();
84228fed 828
0895c955 829 /* In fact dispatch() never returns. */
7de20a95 830 return (0);
747ec13b
TL
831}
832
7de20a95
EH
833static void
834do_relay4(struct interface_info *ip, struct dhcp_packet *packet,
835 unsigned int length, unsigned int from_port, struct iaddr from,
836 struct hardware *hfrom) {
747ec13b
TL
837 struct server_list *sp;
838 struct sockaddr_in to;
839 struct interface_info *out;
98311e4b 840 struct hardware hto, *htop;
747ec13b 841
7de20a95 842 if (packet->hlen > sizeof packet->chaddr) {
1185c766
TM
843 log_info("Discarding packet with invalid hlen, received on "
844 "%s interface.", ip->name);
845 return;
846 }
847 if (ip->address_count < 1 || ip->addresses == NULL) {
848 log_info("Discarding packet received on %s interface that "
849 "has no IPv4 address assigned.", ip->name);
60dab809
TL
850 return;
851 }
852
866428dd
TL
853 /* Find the interface that corresponds to the giaddr
854 in the packet. */
7de20a95
EH
855 if (packet->giaddr.s_addr) {
856 for (out = interfaces; out; out = out->next) {
98bd7ca0
DH
857 int i;
858
859 for (i = 0 ; i < out->address_count ; i++ ) {
860 if (out->addresses[i].s_addr ==
0f750c4f 861 packet->giaddr.s_addr) {
98bd7ca0
DH
862 i = -1;
863 break;
0f750c4f 864 }
98bd7ca0
DH
865 }
866
867 if (i == -1)
78c553c4 868 break;
866428dd
TL
869 }
870 } else {
7de20a95 871 out = NULL;
866428dd
TL
872 }
873
747ec13b 874 /* If it's a bootreply, forward it to the client. */
7de20a95 875 if (packet->op == BOOTREPLY) {
f2e70402
TM
876 if (!(ip->flags & INTERFACE_UPSTREAM)) {
877 log_debug("Dropping reply received on %s", ip->name);
878 return;
879 }
880
7de20a95
EH
881 if (!(packet->flags & htons(BOOTP_BROADCAST)) &&
882 can_unicast_without_arp(out)) {
883 to.sin_addr = packet->yiaddr;
75661c60 884 to.sin_port = remote_port;
98311e4b
DH
885
886 /* and hardware address is not broadcast */
887 htop = &hto;
d2bc90bd 888 } else {
7de20a95 889 to.sin_addr.s_addr = htonl(INADDR_BROADCAST);
75661c60 890 to.sin_port = remote_port;
98311e4b
DH
891
892 /* hardware address is broadcast */
893 htop = NULL;
747ec13b
TL
894 }
895 to.sin_family = AF_INET;
896#ifdef HAVE_SA_LEN
897 to.sin_len = sizeof to;
898#endif
899
7de20a95
EH
900 memcpy(&hto.hbuf[1], packet->chaddr, packet->hlen);
901 hto.hbuf[0] = packet->htype;
902 hto.hlen = packet->hlen + 1;
747ec13b 903
866428dd
TL
904 /* Wipe out the agent relay options and, if possible, figure
905 out which interface to use based on the contents of the
906 option that we put on the request to which the server is
907 replying. */
908 if (!(length =
7de20a95 909 strip_relay_agent_options(ip, &out, packet, length)))
866428dd
TL
910 return;
911
747ec13b 912 if (!out) {
7de20a95
EH
913 log_error("Packet to bogus giaddr %s.\n",
914 inet_ntoa(packet->giaddr));
866428dd 915 ++bogus_giaddr_drops;
747ec13b
TL
916 return;
917 }
918
98bd7ca0
DH
919 if (send_packet(out, NULL, packet, length, out->addresses[0],
920 &to, htop) < 0) {
866428dd
TL
921 ++server_packet_errors;
922 } else {
7de20a95
EH
923 log_debug("Forwarded BOOTREPLY for %s to %s",
924 print_hw_addr(packet->htype, packet->hlen,
925 packet->chaddr),
926 inet_ntoa(to.sin_addr));
75661c60 927
866428dd
TL
928 ++server_packets_relayed;
929 }
747ec13b
TL
930 return;
931 }
932
866428dd
TL
933 /* If giaddr matches one of our addresses, ignore the packet -
934 we just sent it. */
935 if (out)
936 return;
937
edd6d888
TM
938 if (!(ip->flags & INTERFACE_DOWNSTREAM)) {
939 log_debug("Dropping request received on %s", ip->name);
940 return;
941 }
942
866428dd 943 /* Add relay agent options if indicated. If something goes wrong,
6e7e6637
TM
944 * drop the packet. Note this may set packet->giaddr if RFC3527
945 * is enabled. */
98bd7ca0
DH
946 if (!(length = add_relay_agent_options(ip, packet, length,
947 ip->addresses[0])))
75661c60 948 return;
75661c60 949
866428dd
TL
950 /* If giaddr is not already set, Set it so the server can
951 figure out what net it's from and so that we can later
952 forward the response to the correct net. If it's already
953 set, the response will be sent directly to the relay agent
954 that set giaddr, so we won't see it. */
7de20a95 955 if (!packet->giaddr.s_addr)
98bd7ca0 956 packet->giaddr = ip->addresses[0];
7de20a95
EH
957 if (packet->hops < max_hop_count)
958 packet->hops = packet->hops + 1;
98311e4b
DH
959 else
960 return;
75661c60 961
747ec13b
TL
962 /* Otherwise, it's a BOOTREQUEST, so forward it to all the
963 servers. */
7de20a95 964 for (sp = servers; sp; sp = sp->next) {
98bd7ca0
DH
965 if (send_packet((fallback_interface
966 ? fallback_interface : interfaces),
967 NULL, packet, length, ip->addresses[0],
968 &sp->to, NULL) < 0) {
866428dd 969 ++client_packet_errors;
747ec13b 970 } else {
7de20a95
EH
971 log_debug("Forwarded BOOTREQUEST for %s to %s",
972 print_hw_addr(packet->htype, packet->hlen,
973 packet->chaddr),
974 inet_ntoa(sp->to.sin_addr));
866428dd 975 ++client_packets_relayed;
747ec13b
TL
976 }
977 }
0a2f9a62 978
747ec13b
TL
979}
980
0a2f9a62
TM
981#endif /* UNIT_TEST */
982
866428dd 983/* Strip any Relay Agent Information options from the DHCP packet
98311e4b
DH
984 option buffer. If there is a circuit ID suboption, look up the
985 outgoing interface based upon it. */
866428dd 986
0a2f9a62 987int
7de20a95 988strip_relay_agent_options(struct interface_info *in,
d352732e
EH
989 struct interface_info **out,
990 struct dhcp_packet *packet,
991 unsigned length) {
866428dd 992 int is_dhcp = 0;
88cd8aca 993 u_int8_t *op, *nextop, *sp, *max;
866428dd
TL
994 int good_agent_option = 0;
995 int status;
996
997 /* If we're not adding agent options to packets, we're not taking
998 them out either. */
999 if (!add_agent_options)
7de20a95 1000 return (length);
866428dd
TL
1001
1002 /* If there's no cookie, it's a bootp packet, so we should just
1003 forward it unchanged. */
7de20a95
EH
1004 if (memcmp(packet->options, DHCP_OPTIONS_COOKIE, 4))
1005 return (length);
866428dd
TL
1006
1007 max = ((u_int8_t *)packet) + length;
7de20a95 1008 sp = op = &packet->options[4];
866428dd
TL
1009
1010 while (op < max) {
7de20a95 1011 switch(*op) {
866428dd
TL
1012 /* Skip padding... */
1013 case DHO_PAD:
1014 if (sp != op)
1015 *sp = *op;
1016 ++op;
1017 ++sp;
1018 continue;
1019
1020 /* If we see a message type, it's a DHCP packet. */
1021 case DHO_DHCP_MESSAGE_TYPE:
1022 is_dhcp = 1;
1023 goto skip;
1024 break;
1025
1026 /* Quit immediately if we hit an End option. */
1027 case DHO_END:
1028 if (sp != op)
1029 *sp++ = *op++;
1030 goto out;
1031
1032 case DHO_DHCP_AGENT_OPTIONS:
1033 /* We shouldn't see a relay agent option in a
1034 packet before we've seen the DHCP packet type,
1035 but if we do, we have to leave it alone. */
1036 if (!is_dhcp)
1037 goto skip;
1038
88cd8aca
DH
1039 /* Do not process an agent option if it exceeds the
1040 * buffer. Fail this packet.
1041 */
1042 nextop = op + op[1] + 2;
1043 if (nextop > max)
7de20a95 1044 return (0);
88cd8aca 1045
7de20a95
EH
1046 status = find_interface_by_agent_option(packet,
1047 out, op + 2,
1048 op[1]);
866428dd 1049 if (status == -1 && drop_agent_mismatches)
7de20a95 1050 return (0);
866428dd
TL
1051 if (status)
1052 good_agent_option = 1;
88cd8aca 1053 op = nextop;
866428dd
TL
1054 break;
1055
1056 skip:
1057 /* Skip over other options. */
1058 default:
88cd8aca 1059 /* Fail if processing this option will exceed the
7de20a95 1060 * buffer(op[1] is malformed).
88cd8aca
DH
1061 */
1062 nextop = op + op[1] + 2;
1063 if (nextop > max)
7de20a95 1064 return (0);
88cd8aca
DH
1065
1066 if (sp != op) {
0a2f9a62
TM
1067 size_t mlen = op[1] + 2;
1068 memmove(sp, op, mlen);
1069 sp += mlen;
1070 if (sp > max) {
1071 return (0);
1072 }
1073
88cd8aca
DH
1074 op = nextop;
1075 } else
1076 op = sp = nextop;
1077
866428dd
TL
1078 break;
1079 }
1080 }
1081 out:
1082
1083 /* If it's not a DHCP packet, we're not supposed to touch it. */
1084 if (!is_dhcp)
7de20a95 1085 return (length);
866428dd
TL
1086
1087 /* If none of the agent options we found matched, or if we didn't
1088 find any agent options, count this packet as not having any
1089 matching agent options, and if we're relying on agent options
1090 to determine the outgoing interface, drop the packet. */
1091
1092 if (!good_agent_option) {
1093 ++missing_agent_option;
1094 if (drop_agent_mismatches)
7de20a95 1095 return (0);
866428dd
TL
1096 }
1097
1098 /* Adjust the length... */
1099 if (sp != op) {
7de20a95 1100 length = sp -((u_int8_t *)packet);
866428dd 1101
7de20a95 1102 /* Make sure the packet isn't short(this is unlikely,
d352732e 1103 but WTH) */
866428dd 1104 if (length < BOOTP_MIN_LEN) {
7de20a95 1105 memset(sp, DHO_PAD, BOOTP_MIN_LEN - length);
866428dd
TL
1106 length = BOOTP_MIN_LEN;
1107 }
1108 }
7de20a95 1109 return (length);
866428dd
TL
1110}
1111
1112
1113/* Find an interface that matches the circuit ID specified in the
1114 Relay Agent Information option. If one is found, store it through
1115 the pointer given; otherwise, leave the existing pointer alone.
1116
1117 We actually deviate somewhat from the current specification here:
1118 if the option buffer is corrupt, we suggest that the caller not
1119 respond to this packet. If the circuit ID doesn't match any known
1120 interface, we suggest that the caller to drop the packet. Only if
1121 we find a circuit ID that matches an existing interface do we tell
1122 the caller to go ahead and process the packet. */
1123
0a2f9a62 1124int
7de20a95 1125find_interface_by_agent_option(struct dhcp_packet *packet,
d352732e
EH
1126 struct interface_info **out,
1127 u_int8_t *buf, int len) {
639b70fa 1128 int i = 0;
866428dd 1129 u_int8_t *circuit_id = 0;
98311e4b 1130 unsigned circuit_id_len = 0;
866428dd
TL
1131 struct interface_info *ip;
1132
1133 while (i < len) {
1134 /* If the next agent option overflows the end of the
1135 packet, the agent option buffer is corrupt. */
1136 if (i + 1 == len ||
7de20a95 1137 i + buf[i + 1] + 2 > len) {
866428dd 1138 ++corrupt_agent_options;
7de20a95 1139 return (-1);
866428dd 1140 }
7de20a95 1141 switch(buf[i]) {
866428dd
TL
1142 /* Remember where the circuit ID is... */
1143 case RAI_CIRCUIT_ID:
7de20a95
EH
1144 circuit_id = &buf[i + 2];
1145 circuit_id_len = buf[i + 1];
866428dd
TL
1146 i += circuit_id_len + 2;
1147 continue;
1148
1149 default:
7de20a95 1150 i += buf[i + 1] + 2;
866428dd
TL
1151 break;
1152 }
1153 }
1154
1155 /* If there's no circuit ID, it's not really ours, tell the caller
1156 it's no good. */
1157 if (!circuit_id) {
1158 ++missing_circuit_id;
7de20a95 1159 return (-1);
866428dd
TL
1160 }
1161
1162 /* Scan the interface list looking for an interface whose
1163 name matches the one specified in circuit_id. */
1164
7de20a95
EH
1165 for (ip = interfaces; ip; ip = ip->next) {
1166 if (ip->circuit_id &&
1167 ip->circuit_id_len == circuit_id_len &&
1168 !memcmp(ip->circuit_id, circuit_id, circuit_id_len))
866428dd
TL
1169 break;
1170 }
1171
1172 /* If we got a match, use it. */
1173 if (ip) {
1174 *out = ip;
7de20a95 1175 return (1);
866428dd
TL
1176 }
1177
1178 /* If we didn't get a match, the circuit ID was bogus. */
1179 ++bad_circuit_id;
7de20a95 1180 return (-1);
866428dd
TL
1181}
1182
7de20a95
EH
1183/*
1184 * Examine a packet to see if it's a candidate to have a Relay
1185 * Agent Information option tacked onto its tail. If it is, tack
1186 * the option on.
1187 */
0a2f9a62 1188int
7de20a95 1189add_relay_agent_options(struct interface_info *ip, struct dhcp_packet *packet,
d352732e
EH
1190 unsigned length, struct in_addr giaddr) {
1191 int is_dhcp = 0, mms;
88cd8aca
DH
1192 unsigned optlen;
1193 u_int8_t *op, *nextop, *sp, *max, *end_pad = NULL;
6e7e6637 1194 int adding_link_select;
866428dd
TL
1195
1196 /* If we're not adding agent options to packets, we can skip
1197 this. */
1198 if (!add_agent_options)
7de20a95 1199 return (length);
866428dd
TL
1200
1201 /* If there's no cookie, it's a bootp packet, so we should just
1202 forward it unchanged. */
88cd8aca 1203 if (memcmp(packet->options, DHCP_OPTIONS_COOKIE, 4))
7de20a95 1204 return (length);
866428dd 1205
d352732e 1206 max = ((u_int8_t *)packet) + dhcp_max_agent_option_packet_length;
88cd8aca 1207
6e7e6637
TM
1208 /* Add link selection suboption if enabled and we're the first relay */
1209 adding_link_select = (add_rfc3527_suboption
1210 && (packet->giaddr.s_addr == 0));
1211
88cd8aca
DH
1212 /* Commence processing after the cookie. */
1213 sp = op = &packet->options[4];
866428dd
TL
1214
1215 while (op < max) {
7de20a95 1216 switch(*op) {
866428dd
TL
1217 /* Skip padding... */
1218 case DHO_PAD:
88cd8aca
DH
1219 /* Remember the first pad byte so we can commandeer
1220 * padded space.
1221 *
1222 * XXX: Is this really a good idea? Sure, we can
1223 * seemingly reduce the packet while we're looking,
1224 * but if the packet was signed by the client then
7de20a95 1225 * this padding is part of the checksum(RFC3118),
88cd8aca
DH
1226 * and its nonpresence would break authentication.
1227 */
1228 if (end_pad == NULL)
1229 end_pad = sp;
1230
866428dd 1231 if (sp != op)
88cd8aca
DH
1232 *sp++ = *op++;
1233 else
1234 sp = ++op;
1235
866428dd
TL
1236 continue;
1237
1238 /* If we see a message type, it's a DHCP packet. */
1239 case DHO_DHCP_MESSAGE_TYPE:
1240 is_dhcp = 1;
1241 goto skip;
d352732e
EH
1242
1243 /*
1244 * If there's a maximum message size option, we
1245 * should pay attention to it
1246 */
1247 case DHO_DHCP_MAX_MESSAGE_SIZE:
1248 mms = ntohs(*(op + 2));
1249 if (mms < dhcp_max_agent_option_packet_length &&
1250 mms >= DHCP_MTU_MIN)
1251 max = ((u_int8_t *)packet) + mms;
1252 goto skip;
866428dd
TL
1253
1254 /* Quit immediately if we hit an End option. */
1255 case DHO_END:
1256 goto out;
1257
1258 case DHO_DHCP_AGENT_OPTIONS:
1259 /* We shouldn't see a relay agent option in a
1260 packet before we've seen the DHCP packet type,
1261 but if we do, we have to leave it alone. */
1262 if (!is_dhcp)
1263 goto skip;
88cd8aca
DH
1264
1265 end_pad = NULL;
866428dd
TL
1266
1267 /* There's already a Relay Agent Information option
1268 in this packet. How embarrassing. Decide what
1269 to do based on the mode the user specified. */
1270
7de20a95 1271 switch(agent_relay_mode) {
866428dd
TL
1272 case forward_and_append:
1273 goto skip;
1274 case forward_untouched:
7de20a95 1275 return (length);
866428dd 1276 case discard:
7de20a95 1277 return (0);
866428dd
TL
1278 case forward_and_replace:
1279 default:
1280 break;
1281 }
1282
1283 /* Skip over the agent option and start copying
1284 if we aren't copying already. */
88cd8aca 1285 op += op[1] + 2;
866428dd
TL
1286 break;
1287
1288 skip:
1289 /* Skip over other options. */
1290 default:
88cd8aca 1291 /* Fail if processing this option will exceed the
7de20a95 1292 * buffer(op[1] is malformed).
88cd8aca
DH
1293 */
1294 nextop = op + op[1] + 2;
1295 if (nextop > max)
7de20a95 1296 return (0);
88cd8aca
DH
1297
1298 end_pad = NULL;
1299
1300 if (sp != op) {
0a2f9a62
TM
1301 size_t mlen = op[1] + 2;
1302 memmove(sp, op, mlen);
1303 sp += mlen;
1304 if (sp > max) {
1305 return (0);
1306 }
1307
88cd8aca
DH
1308 op = nextop;
1309 } else
1310 op = sp = nextop;
1311
866428dd
TL
1312 break;
1313 }
1314 }
1315 out:
1316
1317 /* If it's not a DHCP packet, we're not supposed to touch it. */
1318 if (!is_dhcp)
7de20a95 1319 return (length);
866428dd
TL
1320
1321 /* If the packet was padded out, we can store the agent option
1322 at the beginning of the padding. */
1323
88cd8aca 1324 if (end_pad != NULL)
866428dd
TL
1325 sp = end_pad;
1326
dc9d7b08 1327#if 0
866428dd
TL
1328 /* Remember where the end of the packet was after parsing
1329 it. */
1330 op = sp;
dc9d7b08 1331#endif
866428dd 1332
88cd8aca 1333 /* Sanity check. Had better not ever happen. */
7de20a95
EH
1334 if ((ip->circuit_id_len > 255) ||(ip->circuit_id_len < 1))
1335 log_fatal("Circuit ID length %d out of range [1-255] on "
88cd8aca
DH
1336 "%s\n", ip->circuit_id_len, ip->name);
1337 optlen = ip->circuit_id_len + 2; /* RAI_CIRCUIT_ID + len */
1338
1339 if (ip->remote_id) {
1340 if (ip->remote_id_len > 255 || ip->remote_id_len < 1)
7de20a95 1341 log_fatal("Remote ID length %d out of range [1-255] "
0b0a1399 1342 "on %s\n", ip->remote_id_len, ip->name);
88cd8aca
DH
1343 optlen += ip->remote_id_len + 2; /* RAI_REMOTE_ID + len */
1344 }
1345
6e7e6637
TM
1346 if (adding_link_select) {
1347 optlen += 6;
1348 }
1349
563f0b8a
FD
1350#ifdef RELAY_PORT
1351 if (relay_port) {
1352 optlen += 2;
1353 }
1354#endif
1355
7de20a95 1356 /* We do not support relay option fragmenting(multiple options to
88cd8aca
DH
1357 * support an option data exceeding 255 bytes).
1358 */
7de20a95
EH
1359 if ((optlen < 3) ||(optlen > 255))
1360 log_fatal("Total agent option length(%u) out of range "
88cd8aca
DH
1361 "[3 - 255] on %s\n", optlen, ip->name);
1362
d352732e
EH
1363 /*
1364 * Is there room for the option, its code+len, and DHO_END?
1365 * If not, forward without adding the option.
1366 */
1367 if (max - sp >= optlen + 3) {
1368 log_debug("Adding %d-byte relay agent option", optlen + 3);
1369
1370 /* Okay, cons up *our* Relay Agent Information option. */
1371 *sp++ = DHO_DHCP_AGENT_OPTIONS;
1372 *sp++ = optlen;
1373
1374 /* Copy in the circuit id... */
1375 *sp++ = RAI_CIRCUIT_ID;
1376 *sp++ = ip->circuit_id_len;
1377 memcpy(sp, ip->circuit_id, ip->circuit_id_len);
1378 sp += ip->circuit_id_len;
1379
1380 /* Copy in remote ID... */
1381 if (ip->remote_id) {
1382 *sp++ = RAI_REMOTE_ID;
1383 *sp++ = ip->remote_id_len;
1384 memcpy(sp, ip->remote_id, ip->remote_id_len);
1385 sp += ip->remote_id_len;
1386 }
6e7e6637
TM
1387
1388 /* RFC3527: Use the inbound packet's interface address in
1389 * the link selection suboption and set the outbound giaddr
1390 * to the uplink address. */
1391 if (adding_link_select) {
1392 *sp++ = RAI_LINK_SELECT;
1393 *sp++ = 4u;
1394 memcpy(sp, &giaddr.s_addr, 4);
1395 sp += 4;
1396 packet->giaddr = uplink->addresses[0];
1397 log_debug ("Adding link selection suboption"
1398 " with addr: %s", inet_ntoa(giaddr));
1399 }
563f0b8a
FD
1400
1401#ifdef RELAY_PORT
1402 /* draft-ietf-dhc-relay-port-10.txt section 5.1 */
1403 if (relay_port) {
1404 *sp++ = RAI_RELAY_PORT;
1405 *sp++ = 0u;
1406 }
1407#endif
d352732e
EH
1408 } else {
1409 ++agent_option_errors;
1410 log_error("No room in packet (used %d of %d) "
1411 "for %d-byte relay agent option: omitted",
1412 (int) (sp - ((u_int8_t *) packet)),
1413 (int) (max - ((u_int8_t *) packet)),
1414 optlen + 3);
866428dd
TL
1415 }
1416
d352732e
EH
1417 /*
1418 * Deposit an END option unless the packet is full (shouldn't
1419 * be possible).
1420 */
1421 if (sp < max)
1422 *sp++ = DHO_END;
866428dd
TL
1423
1424 /* Recalculate total packet length. */
7de20a95 1425 length = sp -((u_int8_t *)packet);
866428dd 1426
7de20a95 1427 /* Make sure the packet isn't short(this is unlikely, but WTH) */
866428dd 1428 if (length < BOOTP_MIN_LEN) {
88cd8aca 1429 memset(sp, DHO_PAD, BOOTP_MIN_LEN - length);
7de20a95
EH
1430 return (BOOTP_MIN_LEN);
1431 }
1432
1433 return (length);
1434}
1435
1436#ifdef DHCPv6
b8724f78 1437#ifndef UNIT_TEST
7de20a95
EH
1438/*
1439 * Parse a downstream argument: [address%]interface[#index].
1440 */
1441static struct stream_list *
1442parse_downstream(char *arg) {
1443 struct stream_list *dp, *up;
1444 struct interface_info *ifp = NULL;
1445 char *ifname, *addr, *iid;
1446 isc_result_t status;
1447
1448 if (!supports_multiple_interfaces(ifp) &&
1449 (downstreams != NULL))
1450 log_fatal("No support for multiple interfaces.");
1451
1452 /* Decode the argument. */
1453 ifname = strchr(arg, '%');
1454 if (ifname == NULL) {
1455 ifname = arg;
1456 addr = NULL;
1457 } else {
1458 *ifname++ = '\0';
1459 addr = arg;
1460 }
1461 iid = strchr(ifname, '#');
1462 if (iid != NULL) {
1463 *iid++ = '\0';
1464 }
1465 if (strlen(ifname) >= sizeof(ifp->name)) {
b2cb745b 1466 usage("Interface name '%s' too long", ifname);
7de20a95
EH
1467 }
1468
1469 /* Don't declare twice. */
1470 for (dp = downstreams; dp; dp = dp->next) {
1471 if (strcmp(ifname, dp->ifp->name) == 0)
1472 log_fatal("Down interface '%s' declared twice.",
1473 ifname);
1474 }
1475
1476 /* Share with up side? */
1477 for (up = upstreams; up; up = up->next) {
1478 if (strcmp(ifname, up->ifp->name) == 0) {
c2b5b5e8
TM
1479 log_info("parse_downstream: Interface '%s' is "
1480 "both down and up.", ifname);
7de20a95
EH
1481 ifp = up->ifp;
1482 break;
1483 }
1484 }
1485
1486 /* New interface. */
1487 if (ifp == NULL) {
1488 status = interface_allocate(&ifp, MDL);
1489 if (status != ISC_R_SUCCESS)
1490 log_fatal("%s: interface_allocate: %s",
1491 arg, isc_result_totext(status));
1492 strcpy(ifp->name, ifname);
1493 if (interfaces) {
1494 interface_reference(&ifp->next, interfaces, MDL);
1495 interface_dereference(&interfaces, MDL);
1496 }
1497 interface_reference(&interfaces, ifp, MDL);
7de20a95 1498 }
c2b5b5e8 1499 ifp->flags |= INTERFACE_REQUESTED | INTERFACE_DOWNSTREAM;
7de20a95
EH
1500
1501 /* New downstream. */
1502 dp = (struct stream_list *) dmalloc(sizeof(*dp), MDL);
1503 if (!dp)
1504 log_fatal("No memory for downstream.");
1505 dp->ifp = ifp;
1506 if (iid != NULL) {
1507 dp->id = atoi(iid);
1508 } else {
1509 dp->id = -1;
1510 }
1511 /* !addr case handled by setup. */
1512 if (addr && (inet_pton(AF_INET6, addr, &dp->link.sin6_addr) <= 0))
1513 log_fatal("Bad link address '%s'", addr);
1514
1515 return dp;
1516}
1517
1518/*
1519 * Parse an upstream argument: [address]%interface.
1520 */
1521static struct stream_list *
1522parse_upstream(char *arg) {
1523 struct stream_list *up, *dp;
1524 struct interface_info *ifp = NULL;
1525 char *ifname, *addr;
1526 isc_result_t status;
1527
1528 /* Decode the argument. */
1529 ifname = strchr(arg, '%');
1530 if (ifname == NULL) {
1531 ifname = arg;
1532 addr = All_DHCP_Servers;
1533 } else {
1534 *ifname++ = '\0';
1535 addr = arg;
1536 }
1537 if (strlen(ifname) >= sizeof(ifp->name)) {
1538 log_fatal("Interface name '%s' too long", ifname);
1539 }
1540
1541 /* Shared up interface? */
1542 for (up = upstreams; up; up = up->next) {
1543 if (strcmp(ifname, up->ifp->name) == 0) {
1544 ifp = up->ifp;
1545 break;
1546 }
1547 }
1548 for (dp = downstreams; dp; dp = dp->next) {
1549 if (strcmp(ifname, dp->ifp->name) == 0) {
c2b5b5e8
TM
1550 log_info("parse_upstream: Interface '%s' is "
1551 "both down and up.", ifname);
7de20a95
EH
1552 ifp = dp->ifp;
1553 break;
1554 }
1555 }
1556
1557 /* New interface. */
1558 if (ifp == NULL) {
1559 status = interface_allocate(&ifp, MDL);
1560 if (status != ISC_R_SUCCESS)
1561 log_fatal("%s: interface_allocate: %s",
1562 arg, isc_result_totext(status));
1563 strcpy(ifp->name, ifname);
1564 if (interfaces) {
1565 interface_reference(&ifp->next, interfaces, MDL);
1566 interface_dereference(&interfaces, MDL);
1567 }
1568 interface_reference(&interfaces, ifp, MDL);
7de20a95 1569 }
c2b5b5e8 1570 ifp->flags |= INTERFACE_REQUESTED | INTERFACE_UPSTREAM;
7de20a95
EH
1571
1572 /* New upstream. */
1573 up = (struct stream_list *) dmalloc(sizeof(*up), MDL);
1574 if (up == NULL)
1575 log_fatal("No memory for upstream.");
1576
1577 up->ifp = ifp;
1578
1579 if (inet_pton(AF_INET6, addr, &up->link.sin6_addr) <= 0)
1580 log_fatal("Bad address %s", addr);
1581
1582 return up;
1583}
1584
1585/*
1586 * Setup downstream interfaces.
1587 */
1588static void
1589setup_streams(void) {
1590 struct stream_list *dp, *up;
1591 int i;
1592 isc_boolean_t link_is_set;
1593
1594 for (dp = downstreams; dp; dp = dp->next) {
1595 /* Check interface */
1596 if (dp->ifp->v6address_count == 0)
d352732e
EH
1597 log_fatal("Interface '%s' has no IPv6 addresses.",
1598 dp->ifp->name);
7de20a95
EH
1599
1600 /* Check/set link. */
1601 if (IN6_IS_ADDR_UNSPECIFIED(&dp->link.sin6_addr))
1602 link_is_set = ISC_FALSE;
1603 else
1604 link_is_set = ISC_TRUE;
1605 for (i = 0; i < dp->ifp->v6address_count; i++) {
1606 if (IN6_IS_ADDR_LINKLOCAL(&dp->ifp->v6addresses[i]))
1607 continue;
1608 if (!link_is_set)
1609 break;
1610 if (!memcmp(&dp->ifp->v6addresses[i],
d352732e
EH
1611 &dp->link.sin6_addr,
1612 sizeof(dp->link.sin6_addr)))
7de20a95
EH
1613 break;
1614 }
1615 if (i == dp->ifp->v6address_count)
c6455252
TM
1616 log_fatal("Interface %s does not have global IPv6 "
1617 "address assigned.", dp->ifp->name);
7de20a95
EH
1618 if (!link_is_set)
1619 memcpy(&dp->link.sin6_addr,
1620 &dp->ifp->v6addresses[i],
1621 sizeof(dp->link.sin6_addr));
1622
1623 /* Set interface-id. */
1624 if (dp->id == -1)
1625 dp->id = dp->ifp->index;
1626 }
1627
1628 for (up = upstreams; up; up = up->next) {
1629 up->link.sin6_port = local_port;
1630 up->link.sin6_family = AF_INET6;
1631#ifdef HAVE_SA_LEN
1632 up->link.sin6_len = sizeof(up->link);
1633#endif
1634
1635 if (up->ifp->v6address_count == 0)
1636 log_fatal("Interface '%s' has no IPv6 addresses.",
1637 up->ifp->name);
c2b5b5e8
TM
1638
1639 /* RFC 3315 Sec 20 - "If the relay agent relays messages to
1640 * the All_DHCP_Servers address or other multicast addresses,
1641 * it sets the Hop Limit field to 32." */
1642 if (IN6_IS_ADDR_MULTICAST(&up->link.sin6_addr)) {
1643 set_multicast_hop_limit(up->ifp, HOP_COUNT_LIMIT);
1644 }
7de20a95
EH
1645 }
1646}
1647
1648/*
1649 * Add DHCPv6 agent options here.
1650 */
1651static const int required_forw_opts[] = {
1652 D6O_INTERFACE_ID,
619304cd 1653 D6O_SUBSCRIBER_ID,
563f0b8a
FD
1654#if defined(RELAY_PORT)
1655 D6O_RELAY_SOURCE_PORT,
1656#endif
7de20a95
EH
1657 D6O_RELAY_MSG,
1658 0
1659};
1660
1661/*
1662 * Process a packet upwards, i.e., from client to server.
1663 */
1664static void
1665process_up6(struct packet *packet, struct stream_list *dp) {
1666 char forw_data[65535];
1667 unsigned cursor;
1668 struct dhcpv6_relay_packet *relay;
1669 struct option_state *opts;
1670 struct stream_list *up;
563f0b8a 1671 u_int16_t relay_client_port = 0;
7de20a95
EH
1672
1673 /* Check if the message should be relayed to the server. */
1674 switch (packet->dhcpv6_msg_type) {
1675 case DHCPV6_SOLICIT:
1676 case DHCPV6_REQUEST:
1677 case DHCPV6_CONFIRM:
1678 case DHCPV6_RENEW:
1679 case DHCPV6_REBIND:
1680 case DHCPV6_RELEASE:
1681 case DHCPV6_DECLINE:
1682 case DHCPV6_INFORMATION_REQUEST:
1683 case DHCPV6_RELAY_FORW:
1684 case DHCPV6_LEASEQUERY:
785c1a51 1685 case DHCPV6_DHCPV4_QUERY:
7de20a95
EH
1686 log_info("Relaying %s from %s port %d going up.",
1687 dhcpv6_type_names[packet->dhcpv6_msg_type],
1688 piaddr(packet->client_addr),
1689 ntohs(packet->client_port));
1690 break;
1691
1692 case DHCPV6_ADVERTISE:
1693 case DHCPV6_REPLY:
1694 case DHCPV6_RECONFIGURE:
1695 case DHCPV6_RELAY_REPL:
1696 case DHCPV6_LEASEQUERY_REPLY:
785c1a51 1697 case DHCPV6_DHCPV4_RESPONSE:
7de20a95
EH
1698 log_info("Discarding %s from %s port %d going up.",
1699 dhcpv6_type_names[packet->dhcpv6_msg_type],
1700 piaddr(packet->client_addr),
1701 ntohs(packet->client_port));
1702 return;
1703
1704 default:
1705 log_info("Unknown %d type from %s port %d going up.",
1706 packet->dhcpv6_msg_type,
1707 piaddr(packet->client_addr),
1708 ntohs(packet->client_port));
1709 return;
1710 }
1711
1712 /* Build the relay-forward header. */
1713 relay = (struct dhcpv6_relay_packet *) forw_data;
a3528574 1714 cursor = offsetof(struct dhcpv6_relay_packet, options);
7de20a95
EH
1715 relay->msg_type = DHCPV6_RELAY_FORW;
1716 if (packet->dhcpv6_msg_type == DHCPV6_RELAY_FORW) {
1717 if (packet->dhcpv6_hop_count >= max_hop_count) {
1718 log_info("Hop count exceeded,");
1719 return;
1720 }
1721 relay->hop_count = packet->dhcpv6_hop_count + 1;
1722 if (dp) {
1723 memcpy(&relay->link_address, &dp->link.sin6_addr, 16);
1724 } else {
1725 /* On smart relay add: && !global. */
1726 if (!use_if_id && downstreams->next) {
1727 log_info("Shan't get back the interface.");
1728 return;
1729 }
1730 memset(&relay->link_address, 0, 16);
1731 }
563f0b8a
FD
1732
1733 if (packet->client_port != htons(547)) {
1734 relay_client_port = packet->client_port;
1735 }
7de20a95
EH
1736 } else {
1737 relay->hop_count = 0;
1738 if (!dp)
1739 return;
1740 memcpy(&relay->link_address, &dp->link.sin6_addr, 16);
1741 }
1742 memcpy(&relay->peer_address, packet->client_addr.iabuf, 16);
1743
1744 /* Get an option state. */
1745 opts = NULL;
1746 if (!option_state_allocate(&opts, MDL)) {
1747 log_fatal("No memory for upwards options.");
1748 }
0a2f9a62 1749
7de20a95
EH
1750 /* Add an interface-id (if used). */
1751 if (use_if_id) {
1752 int if_id;
1753
1754 if (dp) {
1755 if_id = dp->id;
ebf076fe 1756 } else if (!downstreams->next) {
7de20a95
EH
1757 if_id = downstreams->id;
1758 } else {
1759 log_info("Don't know the interface.");
1760 option_state_dereference(&opts, MDL);
1761 return;
1762 }
1763
1764 if (!save_option_buffer(&dhcpv6_universe, opts,
1765 NULL, (unsigned char *) &if_id,
1766 sizeof(int),
1767 D6O_INTERFACE_ID, 0)) {
1768 log_error("Can't save interface-id.");
1769 option_state_dereference(&opts, MDL);
1770 return;
1771 }
1772 }
1773
619304cd
SR
1774 /* Add a subscriber-id if desired. */
1775 /* This is for testing rather than general use */
1776 if (dhcrelay_sub_id != NULL) {
1777 if (!save_option_buffer(&dhcpv6_universe, opts, NULL,
1778 (unsigned char *) dhcrelay_sub_id,
1779 strlen(dhcrelay_sub_id),
1780 D6O_SUBSCRIBER_ID, 0)) {
1781 log_error("Can't save subsriber-id.");
1782 option_state_dereference(&opts, MDL);
1783 return;
1784 }
1785 }
0a2f9a62 1786
619304cd 1787
563f0b8a
FD
1788#if defined(RELAY_PORT)
1789 /*
1790 * If we use a non-547 UDP source port or if we have received
1791 * from a downstream relay agent uses a non-547 port, we need
1792 * to include the RELAY-SOURCE-PORT option. The "Downstream
1793 * UDP Port" field value in the option allow us to send
1794 * relay-reply message back to the downstream relay agent
1795 * with the correct UDP source port.
1796 */
1797 if (relay_port || relay_client_port) {
1798 if (!save_option_buffer(&dhcpv6_universe, opts, NULL,
1799 (unsigned char *) &relay_client_port,
1800 sizeof(u_int16_t),
1801 D6O_RELAY_SOURCE_PORT, 0)) {
1802 log_error("Can't save relay-source-port.");
1803 option_state_dereference(&opts, MDL);
1804 return;
1805 }
1806 }
1807#else
1808 /* Avoid unused but set warning, */
1809 (void)(relay_client_port);
1810#endif
1811
7de20a95
EH
1812 /* Add the relay-msg carrying the packet. */
1813 if (!save_option_buffer(&dhcpv6_universe, opts,
1814 NULL, (unsigned char *) packet->raw,
1815 packet->packet_length,
1816 D6O_RELAY_MSG, 0)) {
1817 log_error("Can't save relay-msg.");
1818 option_state_dereference(&opts, MDL);
1819 return;
1820 }
1821
1822 /* Finish the relay-forward message. */
1823 cursor += store_options6(forw_data + cursor,
1824 sizeof(forw_data) - cursor,
0a2f9a62 1825 opts, packet,
7de20a95
EH
1826 required_forw_opts, NULL);
1827 option_state_dereference(&opts, MDL);
1828
1829 /* Send it to all upstreams. */
1830 for (up = upstreams; up; up = up->next) {
1831 send_packet6(up->ifp, (unsigned char *) forw_data,
1832 (size_t) cursor, &up->link);
1833 }
1834}
0a2f9a62 1835
7de20a95
EH
1836/*
1837 * Process a packet downwards, i.e., from server to client.
1838 */
1839static void
1840process_down6(struct packet *packet) {
1841 struct stream_list *dp;
1842 struct option_cache *oc;
1843 struct data_string relay_msg;
1844 const struct dhcpv6_packet *msg;
1845 struct data_string if_id;
563f0b8a
FD
1846#if defined(RELAY_PORT)
1847 struct data_string down_port;
1848#endif
7de20a95
EH
1849 struct sockaddr_in6 to;
1850 struct iaddr peer;
1851
1852 /* The packet must be a relay-reply message. */
1853 if (packet->dhcpv6_msg_type != DHCPV6_RELAY_REPL) {
1854 if (packet->dhcpv6_msg_type < dhcpv6_type_name_max)
1855 log_info("Discarding %s from %s port %d going down.",
1856 dhcpv6_type_names[packet->dhcpv6_msg_type],
1857 piaddr(packet->client_addr),
1858 ntohs(packet->client_port));
1859 else
1860 log_info("Unknown %d type from %s port %d going down.",
1861 packet->dhcpv6_msg_type,
1862 piaddr(packet->client_addr),
1863 ntohs(packet->client_port));
1864 return;
1865 }
1866
1867 /* Inits. */
1868 memset(&relay_msg, 0, sizeof(relay_msg));
1869 memset(&if_id, 0, sizeof(if_id));
563f0b8a
FD
1870#if defined(RELAY_PORT)
1871 memset(&down_port, 0, sizeof(down_port));
1872#endif
7de20a95
EH
1873 memset(&to, 0, sizeof(to));
1874 to.sin6_family = AF_INET6;
1875#ifdef HAVE_SA_LEN
1876 to.sin6_len = sizeof(to);
1877#endif
1878 to.sin6_port = remote_port;
1879 peer.len = 16;
1880
1881 /* Get the relay-msg option (carrying the message to relay). */
1882 oc = lookup_option(&dhcpv6_universe, packet->options, D6O_RELAY_MSG);
1883 if (oc == NULL) {
1884 log_info("No relay-msg.");
1885 return;
1886 }
1887 if (!evaluate_option_cache(&relay_msg, packet, NULL, NULL,
1888 packet->options, NULL,
1889 &global_scope, oc, MDL) ||
69c1bf48 1890 (relay_msg.len < offsetof(struct dhcpv6_packet, options))) {
7de20a95 1891 log_error("Can't evaluate relay-msg.");
0cd94b5e 1892 goto cleanup;
7de20a95
EH
1893 }
1894 msg = (const struct dhcpv6_packet *) relay_msg.data;
1895
1896 /* Get the interface-id (if exists) and the downstream. */
1897 oc = lookup_option(&dhcpv6_universe, packet->options,
1898 D6O_INTERFACE_ID);
1899 if (oc != NULL) {
1900 int if_index;
1901
1902 if (!evaluate_option_cache(&if_id, packet, NULL, NULL,
1903 packet->options, NULL,
1904 &global_scope, oc, MDL) ||
1905 (if_id.len != sizeof(int))) {
1906 log_info("Can't evaluate interface-id.");
1907 goto cleanup;
1908 }
1909 memcpy(&if_index, if_id.data, sizeof(int));
1910 for (dp = downstreams; dp; dp = dp->next) {
1911 if (dp->id == if_index)
1912 break;
1913 }
1914 } else {
1915 if (use_if_id) {
1916 /* Require an interface-id. */
1917 log_info("No interface-id.");
1918 goto cleanup;
1919 }
1920 for (dp = downstreams; dp; dp = dp->next) {
1921 /* Get the first matching one. */
1922 if (!memcmp(&dp->link.sin6_addr,
d352732e 1923 &packet->dhcpv6_link_address,
7de20a95
EH
1924 sizeof(struct in6_addr)))
1925 break;
1926 }
1927 }
1928 /* Why bother when there is no choice. */
dc9d7b08 1929 if (!dp && downstreams && !downstreams->next)
7de20a95
EH
1930 dp = downstreams;
1931 if (!dp) {
1932 log_info("Can't find the down interface.");
1933 goto cleanup;
1934 }
1935 memcpy(peer.iabuf, &packet->dhcpv6_peer_address, peer.len);
1936 to.sin6_addr = packet->dhcpv6_peer_address;
1937
1938 /* Check if we should relay the carried message. */
1939 switch (msg->msg_type) {
1940 /* Relay-Reply of for another relay, not a client. */
1941 case DHCPV6_RELAY_REPL:
1942 to.sin6_port = local_port;
563f0b8a
FD
1943
1944#if defined(RELAY_PORT)
1945 oc = lookup_option(&dhcpv6_universe, packet->options,
1946 D6O_RELAY_SOURCE_PORT);
1947 if (oc != NULL) {
1948 u_int16_t down_relay_port;
1949
1950 memset(&down_port, 0, sizeof(down_port));
1951 if (!evaluate_option_cache(&down_port, packet, NULL,
1952 NULL, packet->options, NULL,
1953 &global_scope, oc, MDL) ||
1954 (down_port.len != sizeof(u_int16_t))) {
1955 log_info("Can't evaluate down "
1956 "relay-source-port.");
1957 goto cleanup;
1958 }
1959 memcpy(&down_relay_port, down_port.data,
1960 sizeof(u_int16_t));
1961 /*
1962 * If the down_relay_port value is non-zero,
1963 * that means our downstream relay agent uses
1964 * a non-547 UDP source port sending
1965 * relay-forw message to us. We need to use
1966 * the same UDP port sending reply back.
1967 */
1968 if (down_relay_port) {
1969 to.sin6_port = down_relay_port;
1970 }
1971 }
1972#endif
1973
7de20a95
EH
1974 /* Fall into: */
1975
1976 case DHCPV6_ADVERTISE:
1977 case DHCPV6_REPLY:
1978 case DHCPV6_RECONFIGURE:
1979 case DHCPV6_RELAY_FORW:
1980 case DHCPV6_LEASEQUERY_REPLY:
785c1a51 1981 case DHCPV6_DHCPV4_RESPONSE:
7de20a95
EH
1982 log_info("Relaying %s to %s port %d down.",
1983 dhcpv6_type_names[msg->msg_type],
1984 piaddr(peer),
1985 ntohs(to.sin6_port));
1986 break;
1987
1988 case DHCPV6_SOLICIT:
1989 case DHCPV6_REQUEST:
1990 case DHCPV6_CONFIRM:
1991 case DHCPV6_RENEW:
1992 case DHCPV6_REBIND:
1993 case DHCPV6_RELEASE:
1994 case DHCPV6_DECLINE:
1995 case DHCPV6_INFORMATION_REQUEST:
1996 case DHCPV6_LEASEQUERY:
785c1a51 1997 case DHCPV6_DHCPV4_QUERY:
7de20a95
EH
1998 log_info("Discarding %s to %s port %d down.",
1999 dhcpv6_type_names[msg->msg_type],
2000 piaddr(peer),
2001 ntohs(to.sin6_port));
2002 goto cleanup;
2003
2004 default:
2005 log_info("Unknown %d type to %s port %d down.",
2006 msg->msg_type,
2007 piaddr(peer),
2008 ntohs(to.sin6_port));
2009 goto cleanup;
2010 }
2011
2012 /* Send the message to the downstream. */
2013 send_packet6(dp->ifp, (unsigned char *) relay_msg.data,
2014 (size_t) relay_msg.len, &to);
2015
2016 cleanup:
2017 if (relay_msg.data != NULL)
2018 data_string_forget(&relay_msg, MDL);
2019 if (if_id.data != NULL)
2020 data_string_forget(&if_id, MDL);
2021}
b8724f78 2022#endif /* UNIT_TEST */
7de20a95
EH
2023
2024/*
2025 * Called by the dispatch packet handler with a decoded packet.
2026 */
2027void
2028dhcpv6(struct packet *packet) {
b8724f78 2029#ifndef UNIT_TEST
7de20a95
EH
2030 struct stream_list *dp;
2031
2032 /* Try all relay-replies downwards. */
2033 if (packet->dhcpv6_msg_type == DHCPV6_RELAY_REPL) {
2034 process_down6(packet);
2035 return;
2036 }
2037 /* Others are candidates to go up if they come from down. */
2038 for (dp = downstreams; dp; dp = dp->next) {
2039 if (packet->interface != dp->ifp)
2040 continue;
2041 process_up6(packet, dp);
2042 return;
2043 }
2044 /* Relay-forward could work from an unknown interface. */
2045 if (packet->dhcpv6_msg_type == DHCPV6_RELAY_FORW) {
2046 process_up6(packet, NULL);
2047 return;
866428dd
TL
2048 }
2049
7de20a95 2050 log_info("Can't process packet from interface '%s'.",
d352732e 2051 packet->interface->name);
b8724f78 2052#endif /* UNIT_TEST */
7de20a95 2053}
b8724f78 2054#endif /* DHCPv6 */
7de20a95
EH
2055
2056/* Stub routines needed for linking with DHCP libraries. */
2057void
2058bootp(struct packet *packet) {
d352732e 2059 return;
7de20a95
EH
2060}
2061
2062void
2063dhcp(struct packet *packet) {
d352732e 2064 return;
7de20a95
EH
2065}
2066
e4dcd805
FD
2067#if defined(DHCPv6) && defined(DHCP4o6)
2068isc_result_t dhcpv4o6_handler(omapi_object_t *h)
2069{
2070 return ISC_R_NOTIMPLEMENTED;
2071}
2072#endif
2073
7de20a95
EH
2074void
2075classify(struct packet *p, struct class *c) {
d352732e 2076 return;
7de20a95
EH
2077}
2078
2079int
2080check_collection(struct packet *p, struct lease *l, struct collection *c) {
2081 return 0;
2082}
2083
2084isc_result_t
2085find_class(struct class **class, const char *c1, const char *c2, int i) {
2086 return ISC_R_NOTFOUND;
2087}
2088
2089int
2090parse_allow_deny(struct option_cache **oc, struct parse *p, int i) {
2091 return 0;
2092}
2093
2094isc_result_t
2095dhcp_set_control_state(control_object_state_t oldstate,
2096 control_object_state_t newstate) {
29b23207
FD
2097 char buf = 0;
2098
0895c955
SR
2099 if (newstate != server_shutdown)
2100 return ISC_R_SUCCESS;
f950de77 2101
0cd94b5e
TM
2102 /* Log shutdown on signal. */
2103 log_info("Received signal %d, initiating shutdown.", shutdown_signal);
2104
f950de77
SR
2105 if (no_pid_file == ISC_FALSE)
2106 (void) unlink(path_dhcrelay_pid);
2107
29b23207
FD
2108 if (!no_daemon && dfd[0] != -1 && dfd[1] != -1) {
2109 IGNORE_RET(write(dfd[1], &buf, 1));
2110 (void) close(dfd[1]);
2111 dfd[0] = dfd[1] = -1;
2112 }
0895c955 2113 exit(0);
866428dd 2114}
edd6d888
TM
2115
2116/*!
2117 *
2118 * \brief Allocate an interface as requested with a given set of flags
2119 *
2120 * The requested interface is allocated, its flags field is set to
2121 * INTERFACE_REQUESTED OR'd with the given flags, and then added to
2122 * the list of interfaces.
2123 *
2124 * \param name - name of the requested interface
2125 * \param flags - additional flags for the interface
2126 *
2127 * \return Nothing
2128 */
2129void request_v4_interface(const char* name, int flags) {
2130 struct interface_info *tmp = NULL;
2131 int len = strlen(name);
2132 isc_result_t status;
2133
2134 if (len >= sizeof(tmp->name)) {
2135 log_fatal("%s: interface name too long (is %d)", name, len);
2136 }
2137
2138 status = interface_allocate(&tmp, MDL);
2139 if (status != ISC_R_SUCCESS) {
2140 log_fatal("%s: interface_allocate: %s", name,
2141 isc_result_totext(status));
2142 }
2143
2144 log_debug("Requesting: %s as upstream: %c downstream: %c", name,
2145 (flags & INTERFACE_UPSTREAM ? 'Y' : 'N'),
2146 (flags & INTERFACE_DOWNSTREAM ? 'Y' : 'N'));
2147
753d458b 2148 memcpy(tmp->name, name, len);
edd6d888
TM
2149 interface_snorf(tmp, (INTERFACE_REQUESTED | flags));
2150 interface_dereference(&tmp, MDL);
2151}