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