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