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