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