]> git.ipfire.org Git - thirdparty/dhcp.git/blame - relay/dhcrelay.c
[master] Pre-release tag prep for 4.4.2: version #, copyright etc...
[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
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 *
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
747ec13b
TL
98u_int16_t local_port;
99u_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
238 * comamnd doesn't include its argument.
239 *
240 * \param sfmt - The basic string and format for the specific error
241 * \param sarg - Generally the offending argument from the comamnd line.
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
0a2f9a62
TM
1436#ifndef UNIT_TEST
1437
7de20a95
EH
1438#ifdef DHCPv6
1439/*
1440 * Parse a downstream argument: [address%]interface[#index].
1441 */
1442static struct stream_list *
1443parse_downstream(char *arg) {
1444 struct stream_list *dp, *up;
1445 struct interface_info *ifp = NULL;
1446 char *ifname, *addr, *iid;
1447 isc_result_t status;
1448
1449 if (!supports_multiple_interfaces(ifp) &&
1450 (downstreams != NULL))
1451 log_fatal("No support for multiple interfaces.");
1452
1453 /* Decode the argument. */
1454 ifname = strchr(arg, '%');
1455 if (ifname == NULL) {
1456 ifname = arg;
1457 addr = NULL;
1458 } else {
1459 *ifname++ = '\0';
1460 addr = arg;
1461 }
1462 iid = strchr(ifname, '#');
1463 if (iid != NULL) {
1464 *iid++ = '\0';
1465 }
1466 if (strlen(ifname) >= sizeof(ifp->name)) {
b2cb745b 1467 usage("Interface name '%s' too long", ifname);
7de20a95
EH
1468 }
1469
1470 /* Don't declare twice. */
1471 for (dp = downstreams; dp; dp = dp->next) {
1472 if (strcmp(ifname, dp->ifp->name) == 0)
1473 log_fatal("Down interface '%s' declared twice.",
1474 ifname);
1475 }
1476
1477 /* Share with up side? */
1478 for (up = upstreams; up; up = up->next) {
1479 if (strcmp(ifname, up->ifp->name) == 0) {
c2b5b5e8
TM
1480 log_info("parse_downstream: Interface '%s' is "
1481 "both down and up.", ifname);
7de20a95
EH
1482 ifp = up->ifp;
1483 break;
1484 }
1485 }
1486
1487 /* New interface. */
1488 if (ifp == NULL) {
1489 status = interface_allocate(&ifp, MDL);
1490 if (status != ISC_R_SUCCESS)
1491 log_fatal("%s: interface_allocate: %s",
1492 arg, isc_result_totext(status));
1493 strcpy(ifp->name, ifname);
1494 if (interfaces) {
1495 interface_reference(&ifp->next, interfaces, MDL);
1496 interface_dereference(&interfaces, MDL);
1497 }
1498 interface_reference(&interfaces, ifp, MDL);
7de20a95 1499 }
c2b5b5e8 1500 ifp->flags |= INTERFACE_REQUESTED | INTERFACE_DOWNSTREAM;
7de20a95
EH
1501
1502 /* New downstream. */
1503 dp = (struct stream_list *) dmalloc(sizeof(*dp), MDL);
1504 if (!dp)
1505 log_fatal("No memory for downstream.");
1506 dp->ifp = ifp;
1507 if (iid != NULL) {
1508 dp->id = atoi(iid);
1509 } else {
1510 dp->id = -1;
1511 }
1512 /* !addr case handled by setup. */
1513 if (addr && (inet_pton(AF_INET6, addr, &dp->link.sin6_addr) <= 0))
1514 log_fatal("Bad link address '%s'", addr);
1515
1516 return dp;
1517}
1518
1519/*
1520 * Parse an upstream argument: [address]%interface.
1521 */
1522static struct stream_list *
1523parse_upstream(char *arg) {
1524 struct stream_list *up, *dp;
1525 struct interface_info *ifp = NULL;
1526 char *ifname, *addr;
1527 isc_result_t status;
1528
1529 /* Decode the argument. */
1530 ifname = strchr(arg, '%');
1531 if (ifname == NULL) {
1532 ifname = arg;
1533 addr = All_DHCP_Servers;
1534 } else {
1535 *ifname++ = '\0';
1536 addr = arg;
1537 }
1538 if (strlen(ifname) >= sizeof(ifp->name)) {
1539 log_fatal("Interface name '%s' too long", ifname);
1540 }
1541
1542 /* Shared up interface? */
1543 for (up = upstreams; up; up = up->next) {
1544 if (strcmp(ifname, up->ifp->name) == 0) {
1545 ifp = up->ifp;
1546 break;
1547 }
1548 }
1549 for (dp = downstreams; dp; dp = dp->next) {
1550 if (strcmp(ifname, dp->ifp->name) == 0) {
c2b5b5e8
TM
1551 log_info("parse_upstream: Interface '%s' is "
1552 "both down and up.", ifname);
7de20a95
EH
1553 ifp = dp->ifp;
1554 break;
1555 }
1556 }
1557
1558 /* New interface. */
1559 if (ifp == NULL) {
1560 status = interface_allocate(&ifp, MDL);
1561 if (status != ISC_R_SUCCESS)
1562 log_fatal("%s: interface_allocate: %s",
1563 arg, isc_result_totext(status));
1564 strcpy(ifp->name, ifname);
1565 if (interfaces) {
1566 interface_reference(&ifp->next, interfaces, MDL);
1567 interface_dereference(&interfaces, MDL);
1568 }
1569 interface_reference(&interfaces, ifp, MDL);
7de20a95 1570 }
c2b5b5e8 1571 ifp->flags |= INTERFACE_REQUESTED | INTERFACE_UPSTREAM;
7de20a95
EH
1572
1573 /* New upstream. */
1574 up = (struct stream_list *) dmalloc(sizeof(*up), MDL);
1575 if (up == NULL)
1576 log_fatal("No memory for upstream.");
1577
1578 up->ifp = ifp;
1579
1580 if (inet_pton(AF_INET6, addr, &up->link.sin6_addr) <= 0)
1581 log_fatal("Bad address %s", addr);
1582
1583 return up;
1584}
1585
1586/*
1587 * Setup downstream interfaces.
1588 */
1589static void
1590setup_streams(void) {
1591 struct stream_list *dp, *up;
1592 int i;
1593 isc_boolean_t link_is_set;
1594
1595 for (dp = downstreams; dp; dp = dp->next) {
1596 /* Check interface */
1597 if (dp->ifp->v6address_count == 0)
d352732e
EH
1598 log_fatal("Interface '%s' has no IPv6 addresses.",
1599 dp->ifp->name);
7de20a95
EH
1600
1601 /* Check/set link. */
1602 if (IN6_IS_ADDR_UNSPECIFIED(&dp->link.sin6_addr))
1603 link_is_set = ISC_FALSE;
1604 else
1605 link_is_set = ISC_TRUE;
1606 for (i = 0; i < dp->ifp->v6address_count; i++) {
1607 if (IN6_IS_ADDR_LINKLOCAL(&dp->ifp->v6addresses[i]))
1608 continue;
1609 if (!link_is_set)
1610 break;
1611 if (!memcmp(&dp->ifp->v6addresses[i],
d352732e
EH
1612 &dp->link.sin6_addr,
1613 sizeof(dp->link.sin6_addr)))
7de20a95
EH
1614 break;
1615 }
1616 if (i == dp->ifp->v6address_count)
c6455252
TM
1617 log_fatal("Interface %s does not have global IPv6 "
1618 "address assigned.", dp->ifp->name);
7de20a95
EH
1619 if (!link_is_set)
1620 memcpy(&dp->link.sin6_addr,
1621 &dp->ifp->v6addresses[i],
1622 sizeof(dp->link.sin6_addr));
1623
1624 /* Set interface-id. */
1625 if (dp->id == -1)
1626 dp->id = dp->ifp->index;
1627 }
1628
1629 for (up = upstreams; up; up = up->next) {
1630 up->link.sin6_port = local_port;
1631 up->link.sin6_family = AF_INET6;
1632#ifdef HAVE_SA_LEN
1633 up->link.sin6_len = sizeof(up->link);
1634#endif
1635
1636 if (up->ifp->v6address_count == 0)
1637 log_fatal("Interface '%s' has no IPv6 addresses.",
1638 up->ifp->name);
c2b5b5e8
TM
1639
1640 /* RFC 3315 Sec 20 - "If the relay agent relays messages to
1641 * the All_DHCP_Servers address or other multicast addresses,
1642 * it sets the Hop Limit field to 32." */
1643 if (IN6_IS_ADDR_MULTICAST(&up->link.sin6_addr)) {
1644 set_multicast_hop_limit(up->ifp, HOP_COUNT_LIMIT);
1645 }
7de20a95
EH
1646 }
1647}
1648
1649/*
1650 * Add DHCPv6 agent options here.
1651 */
1652static const int required_forw_opts[] = {
1653 D6O_INTERFACE_ID,
619304cd 1654 D6O_SUBSCRIBER_ID,
563f0b8a
FD
1655#if defined(RELAY_PORT)
1656 D6O_RELAY_SOURCE_PORT,
1657#endif
7de20a95
EH
1658 D6O_RELAY_MSG,
1659 0
1660};
1661
1662/*
1663 * Process a packet upwards, i.e., from client to server.
1664 */
1665static void
1666process_up6(struct packet *packet, struct stream_list *dp) {
1667 char forw_data[65535];
1668 unsigned cursor;
1669 struct dhcpv6_relay_packet *relay;
1670 struct option_state *opts;
1671 struct stream_list *up;
563f0b8a 1672 u_int16_t relay_client_port = 0;
7de20a95
EH
1673
1674 /* Check if the message should be relayed to the server. */
1675 switch (packet->dhcpv6_msg_type) {
1676 case DHCPV6_SOLICIT:
1677 case DHCPV6_REQUEST:
1678 case DHCPV6_CONFIRM:
1679 case DHCPV6_RENEW:
1680 case DHCPV6_REBIND:
1681 case DHCPV6_RELEASE:
1682 case DHCPV6_DECLINE:
1683 case DHCPV6_INFORMATION_REQUEST:
1684 case DHCPV6_RELAY_FORW:
1685 case DHCPV6_LEASEQUERY:
785c1a51 1686 case DHCPV6_DHCPV4_QUERY:
7de20a95
EH
1687 log_info("Relaying %s from %s port %d going up.",
1688 dhcpv6_type_names[packet->dhcpv6_msg_type],
1689 piaddr(packet->client_addr),
1690 ntohs(packet->client_port));
1691 break;
1692
1693 case DHCPV6_ADVERTISE:
1694 case DHCPV6_REPLY:
1695 case DHCPV6_RECONFIGURE:
1696 case DHCPV6_RELAY_REPL:
1697 case DHCPV6_LEASEQUERY_REPLY:
785c1a51 1698 case DHCPV6_DHCPV4_RESPONSE:
7de20a95
EH
1699 log_info("Discarding %s from %s port %d going up.",
1700 dhcpv6_type_names[packet->dhcpv6_msg_type],
1701 piaddr(packet->client_addr),
1702 ntohs(packet->client_port));
1703 return;
1704
1705 default:
1706 log_info("Unknown %d type from %s port %d going up.",
1707 packet->dhcpv6_msg_type,
1708 piaddr(packet->client_addr),
1709 ntohs(packet->client_port));
1710 return;
1711 }
1712
1713 /* Build the relay-forward header. */
1714 relay = (struct dhcpv6_relay_packet *) forw_data;
a3528574 1715 cursor = offsetof(struct dhcpv6_relay_packet, options);
7de20a95
EH
1716 relay->msg_type = DHCPV6_RELAY_FORW;
1717 if (packet->dhcpv6_msg_type == DHCPV6_RELAY_FORW) {
1718 if (packet->dhcpv6_hop_count >= max_hop_count) {
1719 log_info("Hop count exceeded,");
1720 return;
1721 }
1722 relay->hop_count = packet->dhcpv6_hop_count + 1;
1723 if (dp) {
1724 memcpy(&relay->link_address, &dp->link.sin6_addr, 16);
1725 } else {
1726 /* On smart relay add: && !global. */
1727 if (!use_if_id && downstreams->next) {
1728 log_info("Shan't get back the interface.");
1729 return;
1730 }
1731 memset(&relay->link_address, 0, 16);
1732 }
563f0b8a
FD
1733
1734 if (packet->client_port != htons(547)) {
1735 relay_client_port = packet->client_port;
1736 }
7de20a95
EH
1737 } else {
1738 relay->hop_count = 0;
1739 if (!dp)
1740 return;
1741 memcpy(&relay->link_address, &dp->link.sin6_addr, 16);
1742 }
1743 memcpy(&relay->peer_address, packet->client_addr.iabuf, 16);
1744
1745 /* Get an option state. */
1746 opts = NULL;
1747 if (!option_state_allocate(&opts, MDL)) {
1748 log_fatal("No memory for upwards options.");
1749 }
0a2f9a62 1750
7de20a95
EH
1751 /* Add an interface-id (if used). */
1752 if (use_if_id) {
1753 int if_id;
1754
1755 if (dp) {
1756 if_id = dp->id;
ebf076fe 1757 } else if (!downstreams->next) {
7de20a95
EH
1758 if_id = downstreams->id;
1759 } else {
1760 log_info("Don't know the interface.");
1761 option_state_dereference(&opts, MDL);
1762 return;
1763 }
1764
1765 if (!save_option_buffer(&dhcpv6_universe, opts,
1766 NULL, (unsigned char *) &if_id,
1767 sizeof(int),
1768 D6O_INTERFACE_ID, 0)) {
1769 log_error("Can't save interface-id.");
1770 option_state_dereference(&opts, MDL);
1771 return;
1772 }
1773 }
1774
619304cd
SR
1775 /* Add a subscriber-id if desired. */
1776 /* This is for testing rather than general use */
1777 if (dhcrelay_sub_id != NULL) {
1778 if (!save_option_buffer(&dhcpv6_universe, opts, NULL,
1779 (unsigned char *) dhcrelay_sub_id,
1780 strlen(dhcrelay_sub_id),
1781 D6O_SUBSCRIBER_ID, 0)) {
1782 log_error("Can't save subsriber-id.");
1783 option_state_dereference(&opts, MDL);
1784 return;
1785 }
1786 }
0a2f9a62 1787
619304cd 1788
563f0b8a
FD
1789#if defined(RELAY_PORT)
1790 /*
1791 * If we use a non-547 UDP source port or if we have received
1792 * from a downstream relay agent uses a non-547 port, we need
1793 * to include the RELAY-SOURCE-PORT option. The "Downstream
1794 * UDP Port" field value in the option allow us to send
1795 * relay-reply message back to the downstream relay agent
1796 * with the correct UDP source port.
1797 */
1798 if (relay_port || relay_client_port) {
1799 if (!save_option_buffer(&dhcpv6_universe, opts, NULL,
1800 (unsigned char *) &relay_client_port,
1801 sizeof(u_int16_t),
1802 D6O_RELAY_SOURCE_PORT, 0)) {
1803 log_error("Can't save relay-source-port.");
1804 option_state_dereference(&opts, MDL);
1805 return;
1806 }
1807 }
1808#else
1809 /* Avoid unused but set warning, */
1810 (void)(relay_client_port);
1811#endif
1812
7de20a95
EH
1813 /* Add the relay-msg carrying the packet. */
1814 if (!save_option_buffer(&dhcpv6_universe, opts,
1815 NULL, (unsigned char *) packet->raw,
1816 packet->packet_length,
1817 D6O_RELAY_MSG, 0)) {
1818 log_error("Can't save relay-msg.");
1819 option_state_dereference(&opts, MDL);
1820 return;
1821 }
1822
1823 /* Finish the relay-forward message. */
1824 cursor += store_options6(forw_data + cursor,
1825 sizeof(forw_data) - cursor,
0a2f9a62 1826 opts, packet,
7de20a95
EH
1827 required_forw_opts, NULL);
1828 option_state_dereference(&opts, MDL);
1829
1830 /* Send it to all upstreams. */
1831 for (up = upstreams; up; up = up->next) {
1832 send_packet6(up->ifp, (unsigned char *) forw_data,
1833 (size_t) cursor, &up->link);
1834 }
1835}
0a2f9a62 1836
7de20a95
EH
1837/*
1838 * Process a packet downwards, i.e., from server to client.
1839 */
1840static void
1841process_down6(struct packet *packet) {
1842 struct stream_list *dp;
1843 struct option_cache *oc;
1844 struct data_string relay_msg;
1845 const struct dhcpv6_packet *msg;
1846 struct data_string if_id;
563f0b8a
FD
1847#if defined(RELAY_PORT)
1848 struct data_string down_port;
1849#endif
7de20a95
EH
1850 struct sockaddr_in6 to;
1851 struct iaddr peer;
1852
1853 /* The packet must be a relay-reply message. */
1854 if (packet->dhcpv6_msg_type != DHCPV6_RELAY_REPL) {
1855 if (packet->dhcpv6_msg_type < dhcpv6_type_name_max)
1856 log_info("Discarding %s from %s port %d going down.",
1857 dhcpv6_type_names[packet->dhcpv6_msg_type],
1858 piaddr(packet->client_addr),
1859 ntohs(packet->client_port));
1860 else
1861 log_info("Unknown %d type from %s port %d going down.",
1862 packet->dhcpv6_msg_type,
1863 piaddr(packet->client_addr),
1864 ntohs(packet->client_port));
1865 return;
1866 }
1867
1868 /* Inits. */
1869 memset(&relay_msg, 0, sizeof(relay_msg));
1870 memset(&if_id, 0, sizeof(if_id));
563f0b8a
FD
1871#if defined(RELAY_PORT)
1872 memset(&down_port, 0, sizeof(down_port));
1873#endif
7de20a95
EH
1874 memset(&to, 0, sizeof(to));
1875 to.sin6_family = AF_INET6;
1876#ifdef HAVE_SA_LEN
1877 to.sin6_len = sizeof(to);
1878#endif
1879 to.sin6_port = remote_port;
1880 peer.len = 16;
1881
1882 /* Get the relay-msg option (carrying the message to relay). */
1883 oc = lookup_option(&dhcpv6_universe, packet->options, D6O_RELAY_MSG);
1884 if (oc == NULL) {
1885 log_info("No relay-msg.");
1886 return;
1887 }
1888 if (!evaluate_option_cache(&relay_msg, packet, NULL, NULL,
1889 packet->options, NULL,
1890 &global_scope, oc, MDL) ||
69c1bf48 1891 (relay_msg.len < offsetof(struct dhcpv6_packet, options))) {
7de20a95 1892 log_error("Can't evaluate relay-msg.");
0cd94b5e 1893 goto cleanup;
7de20a95
EH
1894 }
1895 msg = (const struct dhcpv6_packet *) relay_msg.data;
1896
1897 /* Get the interface-id (if exists) and the downstream. */
1898 oc = lookup_option(&dhcpv6_universe, packet->options,
1899 D6O_INTERFACE_ID);
1900 if (oc != NULL) {
1901 int if_index;
1902
1903 if (!evaluate_option_cache(&if_id, packet, NULL, NULL,
1904 packet->options, NULL,
1905 &global_scope, oc, MDL) ||
1906 (if_id.len != sizeof(int))) {
1907 log_info("Can't evaluate interface-id.");
1908 goto cleanup;
1909 }
1910 memcpy(&if_index, if_id.data, sizeof(int));
1911 for (dp = downstreams; dp; dp = dp->next) {
1912 if (dp->id == if_index)
1913 break;
1914 }
1915 } else {
1916 if (use_if_id) {
1917 /* Require an interface-id. */
1918 log_info("No interface-id.");
1919 goto cleanup;
1920 }
1921 for (dp = downstreams; dp; dp = dp->next) {
1922 /* Get the first matching one. */
1923 if (!memcmp(&dp->link.sin6_addr,
d352732e 1924 &packet->dhcpv6_link_address,
7de20a95
EH
1925 sizeof(struct in6_addr)))
1926 break;
1927 }
1928 }
1929 /* Why bother when there is no choice. */
dc9d7b08 1930 if (!dp && downstreams && !downstreams->next)
7de20a95
EH
1931 dp = downstreams;
1932 if (!dp) {
1933 log_info("Can't find the down interface.");
1934 goto cleanup;
1935 }
1936 memcpy(peer.iabuf, &packet->dhcpv6_peer_address, peer.len);
1937 to.sin6_addr = packet->dhcpv6_peer_address;
1938
1939 /* Check if we should relay the carried message. */
1940 switch (msg->msg_type) {
1941 /* Relay-Reply of for another relay, not a client. */
1942 case DHCPV6_RELAY_REPL:
1943 to.sin6_port = local_port;
563f0b8a
FD
1944
1945#if defined(RELAY_PORT)
1946 oc = lookup_option(&dhcpv6_universe, packet->options,
1947 D6O_RELAY_SOURCE_PORT);
1948 if (oc != NULL) {
1949 u_int16_t down_relay_port;
1950
1951 memset(&down_port, 0, sizeof(down_port));
1952 if (!evaluate_option_cache(&down_port, packet, NULL,
1953 NULL, packet->options, NULL,
1954 &global_scope, oc, MDL) ||
1955 (down_port.len != sizeof(u_int16_t))) {
1956 log_info("Can't evaluate down "
1957 "relay-source-port.");
1958 goto cleanup;
1959 }
1960 memcpy(&down_relay_port, down_port.data,
1961 sizeof(u_int16_t));
1962 /*
1963 * If the down_relay_port value is non-zero,
1964 * that means our downstream relay agent uses
1965 * a non-547 UDP source port sending
1966 * relay-forw message to us. We need to use
1967 * the same UDP port sending reply back.
1968 */
1969 if (down_relay_port) {
1970 to.sin6_port = down_relay_port;
1971 }
1972 }
1973#endif
1974
7de20a95
EH
1975 /* Fall into: */
1976
1977 case DHCPV6_ADVERTISE:
1978 case DHCPV6_REPLY:
1979 case DHCPV6_RECONFIGURE:
1980 case DHCPV6_RELAY_FORW:
1981 case DHCPV6_LEASEQUERY_REPLY:
785c1a51 1982 case DHCPV6_DHCPV4_RESPONSE:
7de20a95
EH
1983 log_info("Relaying %s to %s port %d down.",
1984 dhcpv6_type_names[msg->msg_type],
1985 piaddr(peer),
1986 ntohs(to.sin6_port));
1987 break;
1988
1989 case DHCPV6_SOLICIT:
1990 case DHCPV6_REQUEST:
1991 case DHCPV6_CONFIRM:
1992 case DHCPV6_RENEW:
1993 case DHCPV6_REBIND:
1994 case DHCPV6_RELEASE:
1995 case DHCPV6_DECLINE:
1996 case DHCPV6_INFORMATION_REQUEST:
1997 case DHCPV6_LEASEQUERY:
785c1a51 1998 case DHCPV6_DHCPV4_QUERY:
7de20a95
EH
1999 log_info("Discarding %s to %s port %d down.",
2000 dhcpv6_type_names[msg->msg_type],
2001 piaddr(peer),
2002 ntohs(to.sin6_port));
2003 goto cleanup;
2004
2005 default:
2006 log_info("Unknown %d type to %s port %d down.",
2007 msg->msg_type,
2008 piaddr(peer),
2009 ntohs(to.sin6_port));
2010 goto cleanup;
2011 }
2012
2013 /* Send the message to the downstream. */
2014 send_packet6(dp->ifp, (unsigned char *) relay_msg.data,
2015 (size_t) relay_msg.len, &to);
2016
2017 cleanup:
2018 if (relay_msg.data != NULL)
2019 data_string_forget(&relay_msg, MDL);
2020 if (if_id.data != NULL)
2021 data_string_forget(&if_id, MDL);
2022}
2023
2024/*
2025 * Called by the dispatch packet handler with a decoded packet.
2026 */
2027void
2028dhcpv6(struct packet *packet) {
2029 struct stream_list *dp;
2030
2031 /* Try all relay-replies downwards. */
2032 if (packet->dhcpv6_msg_type == DHCPV6_RELAY_REPL) {
2033 process_down6(packet);
2034 return;
2035 }
2036 /* Others are candidates to go up if they come from down. */
2037 for (dp = downstreams; dp; dp = dp->next) {
2038 if (packet->interface != dp->ifp)
2039 continue;
2040 process_up6(packet, dp);
2041 return;
2042 }
2043 /* Relay-forward could work from an unknown interface. */
2044 if (packet->dhcpv6_msg_type == DHCPV6_RELAY_FORW) {
2045 process_up6(packet, NULL);
2046 return;
866428dd
TL
2047 }
2048
7de20a95 2049 log_info("Can't process packet from interface '%s'.",
d352732e 2050 packet->interface->name);
7de20a95
EH
2051}
2052#endif
2053
2054/* Stub routines needed for linking with DHCP libraries. */
2055void
2056bootp(struct packet *packet) {
d352732e 2057 return;
7de20a95
EH
2058}
2059
2060void
2061dhcp(struct packet *packet) {
d352732e 2062 return;
7de20a95
EH
2063}
2064
e4dcd805
FD
2065#if defined(DHCPv6) && defined(DHCP4o6)
2066isc_result_t dhcpv4o6_handler(omapi_object_t *h)
2067{
2068 return ISC_R_NOTIMPLEMENTED;
2069}
2070#endif
2071
7de20a95
EH
2072void
2073classify(struct packet *p, struct class *c) {
d352732e 2074 return;
7de20a95
EH
2075}
2076
2077int
2078check_collection(struct packet *p, struct lease *l, struct collection *c) {
2079 return 0;
2080}
2081
2082isc_result_t
2083find_class(struct class **class, const char *c1, const char *c2, int i) {
2084 return ISC_R_NOTFOUND;
2085}
2086
2087int
2088parse_allow_deny(struct option_cache **oc, struct parse *p, int i) {
2089 return 0;
2090}
2091
2092isc_result_t
2093dhcp_set_control_state(control_object_state_t oldstate,
2094 control_object_state_t newstate) {
29b23207
FD
2095 char buf = 0;
2096
0895c955
SR
2097 if (newstate != server_shutdown)
2098 return ISC_R_SUCCESS;
f950de77 2099
0cd94b5e
TM
2100 /* Log shutdown on signal. */
2101 log_info("Received signal %d, initiating shutdown.", shutdown_signal);
2102
f950de77
SR
2103 if (no_pid_file == ISC_FALSE)
2104 (void) unlink(path_dhcrelay_pid);
2105
29b23207
FD
2106 if (!no_daemon && dfd[0] != -1 && dfd[1] != -1) {
2107 IGNORE_RET(write(dfd[1], &buf, 1));
2108 (void) close(dfd[1]);
2109 dfd[0] = dfd[1] = -1;
2110 }
0895c955 2111 exit(0);
866428dd 2112}
edd6d888
TM
2113
2114/*!
2115 *
2116 * \brief Allocate an interface as requested with a given set of flags
2117 *
2118 * The requested interface is allocated, its flags field is set to
2119 * INTERFACE_REQUESTED OR'd with the given flags, and then added to
2120 * the list of interfaces.
2121 *
2122 * \param name - name of the requested interface
2123 * \param flags - additional flags for the interface
2124 *
2125 * \return Nothing
2126 */
2127void request_v4_interface(const char* name, int flags) {
2128 struct interface_info *tmp = NULL;
2129 int len = strlen(name);
2130 isc_result_t status;
2131
2132 if (len >= sizeof(tmp->name)) {
2133 log_fatal("%s: interface name too long (is %d)", name, len);
2134 }
2135
2136 status = interface_allocate(&tmp, MDL);
2137 if (status != ISC_R_SUCCESS) {
2138 log_fatal("%s: interface_allocate: %s", name,
2139 isc_result_totext(status));
2140 }
2141
2142 log_debug("Requesting: %s as upstream: %c downstream: %c", name,
2143 (flags & INTERFACE_UPSTREAM ? 'Y' : 'N'),
2144 (flags & INTERFACE_DOWNSTREAM ? 'Y' : 'N'));
2145
753d458b 2146 memcpy(tmp->name, name, len);
edd6d888
TM
2147 interface_snorf(tmp, (INTERFACE_REQUESTED | flags));
2148 interface_dereference(&tmp, MDL);
2149}
0a2f9a62 2150#endif /* UNIT_TEST */