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