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