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