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