]>
Commit | Line | Data |
---|---|---|
98bd7ca0 | 1 | /* discover.c |
fa226d8f | 2 | |
98bd7ca0 | 3 | Find and identify the network interfaces. */ |
fa226d8f TL |
4 | |
5 | /* | |
dccb6edf | 6 | * Copyright (c) 2004-2008 by Internet Systems Consortium, Inc. ("ISC") |
98311e4b | 7 | * Copyright (c) 1995-2003 by Internet Software Consortium |
fa226d8f | 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. | |
fa226d8f | 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. | |
fa226d8f | 20 | * |
98311e4b DH |
21 | * Internet Systems Consortium, Inc. |
22 | * 950 Charter Street | |
23 | * Redwood City, CA 94063 | |
24 | * <info@isc.org> | |
25 | * http://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 |
49733f31 TL |
30 | * ``http://www.isc.org/''. To learn more about Vixie Enterprises, |
31 | * see ``http://www.vix.com''. To learn more about Nominum, Inc., see | |
32 | * ``http://www.nominum.com''. | |
fa226d8f TL |
33 | */ |
34 | ||
fa226d8f | 35 | #include "dhcpd.h" |
fe5b0fdd DH |
36 | |
37 | #define BSD_COMP /* needed on Solaris for SIOCGLIFNUM */ | |
fa226d8f | 38 | #include <sys/ioctl.h> |
fe5b0fdd | 39 | #include <errno.h> |
fa226d8f | 40 | |
8da06bb1 DH |
41 | #ifdef HAVE_NET_IF6_H |
42 | # include <net/if6.h> | |
43 | #endif | |
44 | ||
d2bc90bd | 45 | struct interface_info *interfaces, *dummy_interfaces, *fallback_interface; |
d758ad8c | 46 | int interfaces_invalidated; |
fa226d8f | 47 | int quiet_interface_discovery; |
38287932 TL |
48 | u_int16_t local_port; |
49 | u_int16_t remote_port; | |
20916cae TL |
50 | int (*dhcp_interface_setup_hook) (struct interface_info *, struct iaddr *); |
51 | int (*dhcp_interface_discovery_hook) (struct interface_info *); | |
cf75fef2 | 52 | isc_result_t (*dhcp_interface_startup_hook) (struct interface_info *); |
a034015f | 53 | int (*dhcp_interface_shutdown_hook) (struct interface_info *); |
38287932 TL |
54 | |
55 | struct in_addr limited_broadcast; | |
98bd7ca0 | 56 | |
4ba58919 | 57 | int local_family = AF_INET; |
38287932 | 58 | struct in_addr local_address; |
fe5b0fdd DH |
59 | |
60 | #ifdef DHCPv6 | |
98bd7ca0 | 61 | struct in6_addr local_address6; |
fe5b0fdd | 62 | #endif /* DHCPv6 */ |
fa226d8f TL |
63 | |
64 | void (*bootp_packet_handler) PROTO ((struct interface_info *, | |
b1b7b521 TL |
65 | struct dhcp_packet *, unsigned, |
66 | unsigned int, | |
fa226d8f | 67 | struct iaddr, struct hardware *)); |
fe5b0fdd DH |
68 | |
69 | #ifdef DHCPv6 | |
98bd7ca0 DH |
70 | void (*dhcpv6_packet_handler)(struct interface_info *, |
71 | const char *, int, | |
72 | int, const struct iaddr *, | |
73 | isc_boolean_t); | |
fe5b0fdd | 74 | #endif /* DHCPv6 */ |
98bd7ca0 | 75 | |
fa226d8f | 76 | |
acc21512 | 77 | omapi_object_type_t *dhcp_type_interface; |
fc33f8c5 TL |
78 | #if defined (TRACING) |
79 | trace_type_t *interface_trace; | |
80 | trace_type_t *inpacket_trace; | |
81 | trace_type_t *outpacket_trace; | |
82 | #endif | |
83 | struct interface_info **interface_vector; | |
84 | int interface_count; | |
85 | int interface_max; | |
acc21512 | 86 | |
20916cae TL |
87 | OMAPI_OBJECT_ALLOC (interface, struct interface_info, dhcp_type_interface) |
88 | ||
a034015f TL |
89 | isc_result_t interface_setup () |
90 | { | |
91 | isc_result_t status; | |
92 | status = omapi_object_type_register (&dhcp_type_interface, | |
93 | "interface", | |
94 | dhcp_interface_set_value, | |
95 | dhcp_interface_get_value, | |
96 | dhcp_interface_destroy, | |
97 | dhcp_interface_signal_handler, | |
98 | dhcp_interface_stuff_values, | |
99 | dhcp_interface_lookup, | |
100 | dhcp_interface_create, | |
101 | dhcp_interface_remove, | |
102 | 0, 0, 0, | |
103 | sizeof (struct interface_info), | |
98311e4b | 104 | interface_initialize, RC_MISC); |
a034015f TL |
105 | if (status != ISC_R_SUCCESS) |
106 | log_fatal ("Can't register interface object type: %s", | |
107 | isc_result_totext (status)); | |
fc33f8c5 | 108 | |
a034015f TL |
109 | return status; |
110 | } | |
111 | ||
fc33f8c5 TL |
112 | #if defined (TRACING) |
113 | void interface_trace_setup () | |
114 | { | |
115 | interface_trace = trace_type_register ("interface", (void *)0, | |
116 | trace_interface_input, | |
117 | trace_interface_stop, MDL); | |
118 | inpacket_trace = trace_type_register ("inpacket", (void *)0, | |
119 | trace_inpacket_input, | |
120 | trace_inpacket_stop, MDL); | |
121 | outpacket_trace = trace_type_register ("outpacket", (void *)0, | |
122 | trace_outpacket_input, | |
123 | trace_outpacket_stop, MDL); | |
124 | } | |
125 | #endif | |
126 | ||
08921fe0 TL |
127 | isc_result_t interface_initialize (omapi_object_t *ipo, |
128 | const char *file, int line) | |
129 | { | |
130 | struct interface_info *ip = (struct interface_info *)ipo; | |
131 | ip -> rfdesc = ip -> wfdesc = -1; | |
132 | return ISC_R_SUCCESS; | |
133 | } | |
134 | ||
fa226d8f | 135 | |
98bd7ca0 DH |
136 | /* |
137 | * Scanning for Interfaces | |
138 | * ----------------------- | |
139 | * | |
140 | * To find interfaces, we create an iterator that abstracts out most | |
141 | * of the platform specifics. Use is fairly straightforward: | |
142 | * | |
143 | * - begin_iface_scan() starts the process. | |
144 | * - Use next_iface() until it returns 0. | |
145 | * - end_iface_scan() performs any necessary cleanup. | |
146 | * | |
147 | * We check for errors on each call to next_iface(), which returns a | |
148 | * description of the error as a string if any occurs. | |
149 | * | |
150 | * We currently have code for Solaris and Linux. Other systems need | |
151 | * to have code written. | |
152 | * | |
153 | * NOTE: the long-term goal is to use the interface code from BIND 9. | |
154 | */ | |
155 | ||
8da06bb1 DH |
156 | #if defined(SIOCGLIFCONF) && defined(SIOCGLIFNUM) && defined(SIOCGLIFFLAGS) |
157 | ||
158 | /* HP/UX doesn't define struct lifconf, instead they define struct | |
159 | * if_laddrconf. Similarly, 'struct lifreq' and 'struct lifaddrreq'. | |
160 | */ | |
161 | #ifdef ISC_PLATFORM_HAVEIF_LADDRCONF | |
162 | # define lifc_len iflc_len | |
163 | # define lifc_buf iflc_buf | |
164 | # define lifc_req iflc_req | |
165 | # define LIFCONF if_laddrconf | |
166 | #else | |
167 | # define ISC_HAVE_LIFC_FAMILY 1 | |
168 | # define ISC_HAVE_LIFC_FLAGS 1 | |
169 | # define LIFCONF lifconf | |
170 | #endif | |
171 | ||
172 | #ifdef ISC_PLATFORM_HAVEIF_LADDRREQ | |
173 | # define lifr_addr iflr_addr | |
174 | # define lifr_name iflr_name | |
175 | # define lifr_dstaddr iflr_dstaddr | |
176 | # define lifr_flags iflr_flags | |
177 | # define sockaddr_storage sockaddr_ext | |
178 | # define ss_family sa_family | |
179 | # define LIFREQ if_laddrreq | |
180 | #else | |
181 | # define LIFREQ lifreq | |
182 | #endif | |
183 | ||
184 | #ifndef IF_NAMESIZE | |
185 | # if defined(LIFNAMSIZ) | |
186 | # define IF_NAMESIZE LIFNAMSIZ | |
187 | # elseif defined(IFNAMSIZ) | |
188 | # define IF_NAMESIZE IFNAMSIZ | |
189 | # else | |
190 | # define IF_NAMESIZE 16 | |
191 | # endif | |
192 | #endif | |
b8c0eda0 MA |
193 | #elif !defined(__linux) && !defined(HAVE_IFADDRS_H) |
194 | # define SIOCGLIFCONF SIOCGIFCONF | |
195 | # define SIOCGLIFFLAGS SIOCGIFFLAGS | |
196 | # define LIFREQ ifreq | |
197 | # define LIFCONF ifconf | |
198 | # define lifr_name ifr_name | |
199 | # define lifr_addr ifr_addr | |
200 | # define lifr_flags ifr_flags | |
201 | # define lifc_len ifc_len | |
202 | # define lifc_buf ifc_buf | |
203 | # define lifc_req ifc_req | |
204 | #ifdef _AIX | |
205 | # define ss_family __ss_family | |
206 | #endif | |
207 | #endif | |
8da06bb1 | 208 | |
b8c0eda0 | 209 | #if defined(SIOCGLIFCONF) && defined(SIOCGLIFFLAGS) |
98bd7ca0 DH |
210 | /* |
211 | * Solaris support | |
212 | * --------------- | |
213 | * | |
214 | * The SIOCGLIFCONF ioctl() are the extension that you need to use | |
215 | * on Solaris to get information about IPv6 addresses. | |
216 | * | |
217 | * Solaris' extended interface is documented in the if_tcp man page. | |
218 | */ | |
219 | ||
220 | /* | |
221 | * Structure holding state about the scan. | |
222 | */ | |
223 | struct iface_conf_list { | |
224 | int sock; /* file descriptor used to get information */ | |
225 | int num; /* total number of interfaces */ | |
8da06bb1 | 226 | struct LIFCONF conf; /* structure used to get information */ |
98bd7ca0 DH |
227 | int next; /* next interface to retrieve when iterating */ |
228 | }; | |
229 | ||
230 | /* | |
231 | * Structure used to return information about a specific interface. | |
232 | */ | |
233 | struct iface_info { | |
8da06bb1 | 234 | char name[IF_NAMESIZE+1]; /* name of the interface, e.g. "bge0" */ |
98bd7ca0 DH |
235 | struct sockaddr_storage addr; /* address information */ |
236 | isc_uint64_t flags; /* interface flags, e.g. IFF_LOOPBACK */ | |
237 | }; | |
238 | ||
239 | /* | |
240 | * Start a scan of interfaces. | |
241 | * | |
242 | * The iface_conf_list structure maintains state for this process. | |
243 | */ | |
244 | int | |
245 | begin_iface_scan(struct iface_conf_list *ifaces) { | |
8da06bb1 | 246 | #ifdef ISC_PLATFORM_HAVELIFNUM |
98bd7ca0 | 247 | struct lifnum lifnum; |
8da06bb1 DH |
248 | #else |
249 | int lifnum; | |
250 | #endif | |
98bd7ca0 | 251 | |
97050349 | 252 | ifaces->sock = socket(local_family, SOCK_DGRAM, IPPROTO_UDP); |
98bd7ca0 DH |
253 | if (ifaces->sock < 0) { |
254 | log_error("Error creating socket to list interfaces; %m"); | |
255 | return 0; | |
256 | } | |
257 | ||
258 | memset(&lifnum, 0, sizeof(lifnum)); | |
8da06bb1 | 259 | #ifdef ISC_PLATFORM_HAVELIFNUM |
98bd7ca0 | 260 | lifnum.lifn_family = AF_UNSPEC; |
8da06bb1 | 261 | #endif |
b8c0eda0 | 262 | #ifdef SIOCGLIFNUM |
98bd7ca0 DH |
263 | if (ioctl(ifaces->sock, SIOCGLIFNUM, &lifnum) < 0) { |
264 | log_error("Error finding total number of interfaces; %m"); | |
265 | close(ifaces->sock); | |
266 | ifaces->sock = -1; | |
267 | return 0; | |
268 | } | |
269 | ||
8da06bb1 | 270 | #ifdef ISC_PLATFORM_HAVELIFNUM |
98bd7ca0 | 271 | ifaces->num = lifnum.lifn_count; |
8da06bb1 DH |
272 | #else |
273 | ifaces->num = lifnum; | |
274 | #endif | |
b8c0eda0 MA |
275 | #else |
276 | ifaces->num = 64; | |
277 | #endif /* SIOCGLIFNUM */ | |
8da06bb1 | 278 | |
98bd7ca0 | 279 | memset(&ifaces->conf, 0, sizeof(ifaces->conf)); |
8da06bb1 | 280 | #ifdef ISC_HAVE_LIFC_FAMILY |
98bd7ca0 | 281 | ifaces->conf.lifc_family = AF_UNSPEC; |
8da06bb1 DH |
282 | #endif |
283 | ifaces->conf.lifc_len = ifaces->num * sizeof(struct LIFREQ); | |
98bd7ca0 DH |
284 | ifaces->conf.lifc_buf = dmalloc(ifaces->conf.lifc_len, MDL); |
285 | if (ifaces->conf.lifc_buf == NULL) { | |
286 | log_fatal("Out of memory getting interface list."); | |
287 | } | |
288 | ||
289 | if (ioctl(ifaces->sock, SIOCGLIFCONF, &ifaces->conf) < 0) { | |
290 | log_error("Error getting interfaces configuration list; %m"); | |
291 | dfree(ifaces->conf.lifc_buf, MDL); | |
292 | close(ifaces->sock); | |
293 | ifaces->sock = -1; | |
294 | return 0; | |
295 | } | |
296 | ||
297 | ifaces->next = 0; | |
298 | ||
299 | return 1; | |
300 | } | |
301 | ||
302 | /* | |
303 | * Retrieve the next interface. | |
304 | * | |
305 | * Returns information in the info structure. | |
99fe695e | 306 | * Sets err to 1 if there is an error, otherwise 0. |
98bd7ca0 DH |
307 | */ |
308 | int | |
309 | next_iface(struct iface_info *info, int *err, struct iface_conf_list *ifaces) { | |
8da06bb1 DH |
310 | struct LIFREQ *p; |
311 | struct LIFREQ tmp; | |
7285af30 DH |
312 | #if defined(sun) || defined(__linux) |
313 | /* Pointer used to remove interface aliases. */ | |
fa226d8f | 314 | char *s; |
7285af30 | 315 | #endif |
acc21512 | 316 | |
98bd7ca0 DH |
317 | do { |
318 | if (ifaces->next >= ifaces->num) { | |
319 | *err = 0; | |
320 | return 0; | |
321 | } | |
fa226d8f | 322 | |
98bd7ca0 DH |
323 | p = ifaces->conf.lifc_req; |
324 | p += ifaces->next; | |
c73ced69 | 325 | |
98bd7ca0 DH |
326 | if (strlen(p->lifr_name) >= sizeof(info->name)) { |
327 | *err = 1; | |
328 | log_error("Interface name '%s' too long", p->lifr_name); | |
329 | return 0; | |
330 | } | |
331 | strcpy(info->name, p->lifr_name); | |
b8c0eda0 MA |
332 | memset(&info->addr, 0, sizeof(info->addr)); |
333 | memcpy(&info->addr, &p->lifr_addr, sizeof(p->lifr_addr)); | |
c73ced69 | 334 | |
fe5b0fdd | 335 | #if defined(sun) || defined(__linux) |
98bd7ca0 DH |
336 | /* interface aliases look like "eth0:1" or "wlan1:3" */ |
337 | s = strchr(info->name, ':'); | |
338 | if (s != NULL) { | |
339 | *s = '\0'; | |
340 | } | |
fe5b0fdd | 341 | #endif /* defined(sun) || defined(__linux) */ |
98bd7ca0 | 342 | |
98bd7ca0 | 343 | } while (strncmp(info->name, "dummy", 5) == 0); |
98bd7ca0 DH |
344 | |
345 | memset(&tmp, 0, sizeof(tmp)); | |
346 | strcpy(tmp.lifr_name, info->name); | |
347 | if (ioctl(ifaces->sock, SIOCGLIFFLAGS, &tmp) < 0) { | |
348 | log_error("Error getting interface flags for '%s'; %m", | |
349 | p->lifr_name); | |
350 | *err = 1; | |
351 | return 0; | |
352 | } | |
353 | info->flags = tmp.lifr_flags; | |
354 | ||
355 | ifaces->next++; | |
356 | *err = 0; | |
357 | return 1; | |
358 | } | |
359 | ||
360 | /* | |
361 | * End scan of interfaces. | |
362 | */ | |
363 | void | |
364 | end_iface_scan(struct iface_conf_list *ifaces) { | |
365 | dfree(ifaces->conf.lifc_buf, MDL); | |
366 | close(ifaces->sock); | |
367 | ifaces->sock = -1; | |
368 | } | |
369 | ||
370 | #elif __linux /* !HAVE_SIOCGLIFCONF */ | |
371 | /* | |
372 | * Linux support | |
373 | * ------------- | |
374 | * | |
375 | * In Linux, we use the /proc pseudo-filesystem to get information | |
376 | * about interfaces, along with selected ioctl() calls. | |
377 | * | |
378 | * Linux low level access is documented in the netdevice man page. | |
379 | */ | |
c73ced69 | 380 | |
98bd7ca0 DH |
381 | /* |
382 | * Structure holding state about the scan. | |
383 | */ | |
384 | struct iface_conf_list { | |
385 | int sock; /* file descriptor used to get information */ | |
386 | FILE *fp; /* input from /proc/net/dev */ | |
387 | #ifdef DHCPv6 | |
388 | FILE *fp6; /* input from /proc/net/if_inet6 */ | |
615a574a | 389 | #endif |
98bd7ca0 | 390 | }; |
c73ced69 | 391 | |
98bd7ca0 DH |
392 | /* |
393 | * Structure used to return information about a specific interface. | |
394 | */ | |
395 | struct iface_info { | |
396 | char name[IFNAMSIZ]; /* name of the interface, e.g. "eth0" */ | |
397 | struct sockaddr_storage addr; /* address information */ | |
398 | isc_uint64_t flags; /* interface flags, e.g. IFF_LOOPBACK */ | |
399 | }; | |
400 | ||
401 | /* | |
402 | * Start a scan of interfaces. | |
403 | * | |
404 | * The iface_conf_list structure maintains state for this process. | |
405 | */ | |
406 | int | |
407 | begin_iface_scan(struct iface_conf_list *ifaces) { | |
408 | char buf[256]; | |
409 | int len; | |
410 | int i; | |
fa226d8f | 411 | |
fe5b0fdd | 412 | ifaces->fp = fopen("/proc/net/dev", "r"); |
98bd7ca0 | 413 | if (ifaces->fp == NULL) { |
fe5b0fdd | 414 | log_error("Error opening '/proc/net/dev' to list interfaces"); |
98bd7ca0 DH |
415 | return 0; |
416 | } | |
fa226d8f | 417 | |
98bd7ca0 DH |
418 | /* |
419 | * The first 2 lines are header information, so read and ignore them. | |
420 | */ | |
421 | for (i=0; i<2; i++) { | |
422 | if (fgets(buf, sizeof(buf), ifaces->fp) == NULL) { | |
fe5b0fdd | 423 | log_error("Error reading headers from '/proc/net/dev'"); |
98bd7ca0 DH |
424 | fclose(ifaces->fp); |
425 | ifaces->fp = NULL; | |
426 | return 0; | |
427 | } | |
428 | len = strlen(buf); | |
429 | if ((len <= 0) || (buf[len-1] != '\n')) { | |
fe5b0fdd | 430 | log_error("Bad header line in '/proc/net/dev'"); |
98bd7ca0 DH |
431 | fclose(ifaces->fp); |
432 | ifaces->fp = NULL; | |
433 | return 0; | |
434 | } | |
435 | } | |
436 | ||
437 | ifaces->sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); | |
438 | if (ifaces->sock < 0) { | |
439 | log_error("Error creating socket to list interfaces; %m"); | |
440 | fclose(ifaces->fp); | |
441 | ifaces->fp = NULL; | |
442 | return 0; | |
443 | } | |
444 | ||
445 | #ifdef DHCPv6 | |
446 | ifaces->fp6 = fopen("/proc/net/if_inet6", "r"); | |
447 | if (ifaces->fp6 == NULL) { | |
448 | log_error("Error opening '/proc/net/if_inet6' to " | |
449 | "list IPv6 interfaces; %m"); | |
450 | close(ifaces->sock); | |
451 | ifaces->sock = -1; | |
452 | fclose(ifaces->fp); | |
453 | ifaces->fp = NULL; | |
454 | return 0; | |
31bbee78 TL |
455 | } |
456 | #endif | |
457 | ||
98bd7ca0 DH |
458 | return 1; |
459 | } | |
460 | ||
461 | /* | |
462 | * Read our IPv4 interfaces from /proc/net/dev. | |
463 | * | |
464 | * The file looks something like this: | |
465 | * | |
466 | * Inter-| Receive ... | |
467 | * face |bytes packets errs drop fifo frame ... | |
468 | * lo: 1580562 4207 0 0 0 0 ... | |
469 | * eth0: 0 0 0 0 0 0 ... | |
470 | * eth1:1801552440 37895 0 14 0 ... | |
471 | * | |
472 | * We only care about the interface name, which is at the start of | |
473 | * each line. | |
474 | * | |
475 | * We use an ioctl() to get the address and flags for each interface. | |
476 | */ | |
477 | static int | |
478 | next_iface4(struct iface_info *info, int *err, struct iface_conf_list *ifaces) { | |
479 | char buf[256]; | |
480 | int len; | |
481 | char *p; | |
482 | char *name; | |
483 | struct ifreq tmp; | |
484 | ||
485 | /* | |
486 | * Loop exits when we find an interface that has an address, or | |
487 | * when we run out of interfaces. | |
488 | */ | |
489 | for (;;) { | |
490 | do { | |
491 | /* | |
492 | * Read the next line in the file. | |
493 | */ | |
494 | if (fgets(buf, sizeof(buf), ifaces->fp) == NULL) { | |
495 | if (ferror(ifaces->fp)) { | |
496 | *err = 1; | |
497 | log_error("Error reading interface " | |
498 | "information"); | |
499 | } else { | |
500 | *err = 0; | |
501 | } | |
502 | return 0; | |
503 | } | |
504 | ||
505 | /* | |
506 | * Make sure the line is a nice, | |
507 | * newline-terminated line. | |
508 | */ | |
509 | len = strlen(buf); | |
510 | if ((len <= 0) || (buf[len-1] != '\n')) { | |
511 | log_error("Bad line reading interface " | |
512 | "information"); | |
513 | *err = 1; | |
514 | return 0; | |
515 | } | |
516 | ||
517 | /* | |
518 | * Figure out our name. | |
519 | */ | |
520 | p = strrchr(buf, ':'); | |
521 | if (p == NULL) { | |
522 | log_error("Bad line reading interface " | |
523 | "information (no colon)"); | |
524 | *err = 1; | |
525 | return 0; | |
526 | } | |
527 | *p = '\0'; | |
528 | name = buf; | |
529 | while (isspace(*name)) { | |
530 | name++; | |
531 | } | |
532 | ||
533 | /* | |
534 | * Copy our name into our interface structure. | |
535 | */ | |
536 | len = p - name; | |
537 | if (len >= sizeof(info->name)) { | |
538 | *err = 1; | |
539 | log_error("Interface name '%s' too long", name); | |
540 | return 0; | |
541 | } | |
542 | strcpy(info->name, name); | |
543 | ||
544 | #ifdef ALIAS_NAMED_PERMUTED | |
545 | /* interface aliases look like "eth0:1" or "wlan1:3" */ | |
546 | s = strchr(info->name, ':'); | |
547 | if (s != NULL) { | |
548 | *s = '\0'; | |
549 | } | |
615a574a | 550 | #endif |
98bd7ca0 DH |
551 | |
552 | #ifdef SKIP_DUMMY_INTERFACES | |
553 | } while (strncmp(info->name, "dummy", 5) == 0); | |
554 | #else | |
555 | } while (0); | |
615a574a | 556 | #endif |
98bd7ca0 DH |
557 | |
558 | memset(&tmp, 0, sizeof(tmp)); | |
559 | strcpy(tmp.ifr_name, name); | |
560 | if (ioctl(ifaces->sock, SIOCGIFADDR, &tmp) < 0) { | |
561 | if (errno == EADDRNOTAVAIL) { | |
562 | continue; | |
563 | } | |
564 | log_error("Error getting interface address " | |
565 | "for '%s'; %m", name); | |
566 | *err = 1; | |
567 | return 0; | |
568 | } | |
569 | memcpy(&info->addr, &tmp.ifr_addr, sizeof(tmp.ifr_addr)); | |
570 | ||
571 | memset(&tmp, 0, sizeof(tmp)); | |
572 | strcpy(tmp.ifr_name, name); | |
573 | if (ioctl(ifaces->sock, SIOCGIFFLAGS, &tmp) < 0) { | |
574 | log_error("Error getting interface flags for '%s'; %m", | |
575 | name); | |
576 | *err = 1; | |
577 | return 0; | |
578 | } | |
579 | info->flags = tmp.ifr_flags; | |
580 | ||
581 | *err = 0; | |
582 | return 1; | |
583 | } | |
584 | } | |
585 | ||
586 | #ifdef DHCPv6 | |
587 | /* | |
588 | * Read our IPv6 interfaces from /proc/net/if_inet6. | |
589 | * | |
590 | * The file looks something like this: | |
591 | * | |
592 | * fe80000000000000025056fffec00008 05 40 20 80 vmnet8 | |
593 | * 00000000000000000000000000000001 01 80 10 80 lo | |
594 | * fe80000000000000025056fffec00001 06 40 20 80 vmnet1 | |
595 | * 200108881936000202166ffffe497d9b 03 40 00 00 eth1 | |
596 | * fe8000000000000002166ffffe497d9b 03 40 20 80 eth1 | |
597 | * | |
598 | * We get IPv6 address from the start, the interface name from the end, | |
599 | * and ioctl() to get flags. | |
600 | */ | |
601 | static int | |
602 | next_iface6(struct iface_info *info, int *err, struct iface_conf_list *ifaces) { | |
603 | char buf[256]; | |
604 | int len; | |
605 | char *p; | |
606 | char *name; | |
607 | int i; | |
608 | struct sockaddr_in6 addr; | |
609 | struct ifreq tmp; | |
610 | ||
611 | do { | |
612 | /* | |
613 | * Read the next line in the file. | |
614 | */ | |
615 | if (fgets(buf, sizeof(buf), ifaces->fp6) == NULL) { | |
616 | if (ferror(ifaces->fp6)) { | |
617 | *err = 1; | |
618 | log_error("Error reading IPv6 " | |
619 | "interface information"); | |
620 | } else { | |
621 | *err = 0; | |
622 | } | |
623 | return 0; | |
624 | } | |
625 | ||
626 | /* | |
627 | * Make sure the line is a nice, newline-terminated line. | |
628 | */ | |
629 | len = strlen(buf); | |
630 | if ((len <= 0) || (buf[len-1] != '\n')) { | |
631 | log_error("Bad line reading IPv6 " | |
632 | "interface information"); | |
633 | *err = 1; | |
634 | return 0; | |
635 | } | |
636 | ||
637 | /* | |
638 | * Figure out our name. | |
639 | */ | |
640 | buf[--len] = '\0'; | |
641 | p = strrchr(buf, ' '); | |
642 | if (p == NULL) { | |
643 | log_error("Bad line reading IPv6 interface " | |
644 | "information (no space)"); | |
645 | *err = 1; | |
646 | return 0; | |
647 | } | |
648 | name = p+1; | |
649 | ||
650 | /* | |
651 | * Copy our name into our interface structure. | |
652 | */ | |
653 | len = strlen(name); | |
654 | if (len >= sizeof(info->name)) { | |
655 | *err = 1; | |
656 | log_error("IPv6 interface name '%s' too long", name); | |
657 | return 0; | |
658 | } | |
659 | strcpy(info->name, name); | |
660 | ||
661 | #ifdef SKIP_DUMMY_INTERFACES | |
662 | } while (strncmp(info->name, "dummy", 5) == 0); | |
663 | #else | |
664 | } while (0); | |
665 | #endif | |
666 | ||
667 | /* | |
668 | * Double-check we start with the IPv6 address. | |
669 | */ | |
670 | for (i=0; i<32; i++) { | |
671 | if (!isxdigit(buf[i]) || isupper(buf[i])) { | |
672 | *err = 1; | |
673 | log_error("Bad line reading IPv6 interface address " | |
674 | "for '%s'", name); | |
675 | return 0; | |
676 | } | |
677 | } | |
678 | ||
679 | /* | |
680 | * Load our socket structure. | |
681 | */ | |
682 | memset(&addr, 0, sizeof(addr)); | |
683 | addr.sin6_family = AF_INET6; | |
684 | for (i=0; i<16; i++) { | |
685 | unsigned char byte; | |
686 | static const char hex[] = "0123456789abcdef"; | |
687 | byte = ((index(hex, buf[i * 2]) - hex) << 4) | | |
688 | (index(hex, buf[i * 2 + 1]) - hex); | |
689 | addr.sin6_addr.s6_addr[i] = byte; | |
690 | } | |
691 | memcpy(&info->addr, &addr, sizeof(addr)); | |
692 | ||
693 | /* | |
694 | * Get our flags. | |
695 | */ | |
696 | memset(&tmp, 0, sizeof(tmp)); | |
697 | strcpy(tmp.ifr_name, name); | |
698 | if (ioctl(ifaces->sock, SIOCGIFFLAGS, &tmp) < 0) { | |
699 | log_error("Error getting interface flags for '%s'; %m", name); | |
700 | *err = 1; | |
701 | return 0; | |
702 | } | |
703 | info->flags = tmp.ifr_flags; | |
704 | ||
705 | *err = 0; | |
706 | return 1; | |
707 | } | |
708 | #endif /* DHCPv6 */ | |
709 | ||
710 | /* | |
711 | * Retrieve the next interface. | |
712 | * | |
713 | * Returns information in the info structure. | |
99fe695e | 714 | * Sets err to 1 if there is an error, otherwise 0. |
98bd7ca0 DH |
715 | */ |
716 | int | |
717 | next_iface(struct iface_info *info, int *err, struct iface_conf_list *ifaces) { | |
718 | if (next_iface4(info, err, ifaces)) { | |
719 | return 1; | |
720 | } | |
721 | #ifdef DHCPv6 | |
722 | if (!(*err)) { | |
723 | return next_iface6(info, err, ifaces); | |
724 | } | |
725 | #endif | |
726 | return 0; | |
727 | } | |
728 | ||
729 | /* | |
730 | * End scan of interfaces. | |
731 | */ | |
732 | void | |
733 | end_iface_scan(struct iface_conf_list *ifaces) { | |
734 | fclose(ifaces->fp); | |
735 | ifaces->fp = NULL; | |
736 | close(ifaces->sock); | |
737 | ifaces->sock = -1; | |
738 | #ifdef DHCPv6 | |
739 | fclose(ifaces->fp6); | |
740 | ifaces->fp6 = NULL; | |
741 | #endif | |
742 | } | |
b8c0eda0 | 743 | #else |
99fe695e SK |
744 | |
745 | /* | |
746 | * BSD support | |
747 | * ----------- | |
748 | * | |
749 | * FreeBSD, NetBSD, OpenBSD, and OS X all have the getifaddrs() | |
750 | * function. | |
751 | * | |
752 | * The getifaddrs() man page describes the use. | |
753 | */ | |
754 | ||
755 | #include <ifaddrs.h> | |
756 | ||
757 | /* | |
758 | * Structure holding state about the scan. | |
759 | */ | |
760 | struct iface_conf_list { | |
761 | struct ifaddrs *head; /* beginning of the list */ | |
762 | struct ifaddrs *next; /* current position in the list */ | |
763 | }; | |
764 | ||
765 | /* | |
766 | * Structure used to return information about a specific interface. | |
767 | */ | |
768 | struct iface_info { | |
769 | char name[IFNAMSIZ]; /* name of the interface, e.g. "bge0" */ | |
770 | struct sockaddr_storage addr; /* address information */ | |
771 | isc_uint64_t flags; /* interface flags, e.g. IFF_LOOPBACK */ | |
772 | }; | |
773 | ||
774 | /* | |
775 | * Start a scan of interfaces. | |
776 | * | |
777 | * The iface_conf_list structure maintains state for this process. | |
778 | */ | |
779 | int | |
780 | begin_iface_scan(struct iface_conf_list *ifaces) { | |
781 | if (getifaddrs(&ifaces->head) != 0) { | |
782 | log_error("Error getting interfaces; %m"); | |
783 | return 0; | |
784 | } | |
785 | ifaces->next = ifaces->head; | |
786 | return 1; | |
787 | } | |
788 | ||
789 | /* | |
790 | * Retrieve the next interface. | |
791 | * | |
792 | * Returns information in the info structure. | |
793 | * Sets err to 1 if there is an error, otherwise 0. | |
794 | */ | |
795 | int | |
796 | next_iface(struct iface_info *info, int *err, struct iface_conf_list *ifaces) { | |
797 | if (ifaces->next == NULL) { | |
798 | *err = 0; | |
799 | return 0; | |
800 | } | |
801 | if (strlen(ifaces->next->ifa_name) >= sizeof(info->name)) { | |
802 | log_error("Interface name '%s' too long", | |
803 | ifaces->next->ifa_name); | |
804 | *err = 1; | |
805 | return 0; | |
806 | } | |
807 | strcpy(info->name, ifaces->next->ifa_name); | |
808 | memcpy(&info->addr, ifaces->next->ifa_addr, | |
809 | ifaces->next->ifa_addr->sa_len); | |
810 | info->flags = ifaces->next->ifa_flags; | |
811 | ifaces->next = ifaces->next->ifa_next; | |
812 | *err = 0; | |
813 | return 1; | |
814 | } | |
815 | ||
816 | /* | |
817 | * End scan of interfaces. | |
818 | */ | |
819 | void | |
820 | end_iface_scan(struct iface_conf_list *ifaces) { | |
821 | freeifaddrs(ifaces->head); | |
822 | ifaces->head = NULL; | |
823 | ifaces->next = NULL; | |
824 | } | |
98bd7ca0 DH |
825 | #endif |
826 | ||
827 | /* XXX: perhaps create drealloc() rather than do it manually */ | |
828 | void | |
829 | add_ipv4_addr_to_interface(struct interface_info *iface, | |
830 | const struct in_addr *addr) { | |
831 | /* | |
832 | * We don't expect a lot of addresses per IPv4 interface, so | |
833 | * we use 4, as our "chunk size" for collecting addresses. | |
834 | */ | |
835 | if (iface->addresses == NULL) { | |
836 | iface->addresses = dmalloc(4 * sizeof(struct in_addr), MDL); | |
837 | if (iface->addresses == NULL) { | |
838 | log_fatal("Out of memory saving IPv4 address " | |
839 | "on interface."); | |
840 | } | |
841 | iface->address_count = 0; | |
842 | iface->address_max = 4; | |
843 | } else if (iface->address_count >= iface->address_max) { | |
844 | struct in_addr *tmp; | |
845 | int new_max; | |
846 | ||
847 | new_max = iface->address_max + 4; | |
848 | tmp = dmalloc(new_max * sizeof(struct in_addr), MDL); | |
849 | if (tmp == NULL) { | |
850 | log_fatal("Out of memory saving IPv4 address " | |
851 | "on interface."); | |
852 | } | |
853 | memcpy(tmp, | |
854 | iface->addresses, | |
855 | iface->address_max * sizeof(struct in_addr)); | |
856 | dfree(iface->addresses, MDL); | |
857 | iface->addresses = tmp; | |
858 | iface->address_max = new_max; | |
859 | } | |
860 | iface->addresses[iface->address_count++] = *addr; | |
861 | } | |
862 | ||
fe5b0fdd | 863 | #ifdef DHCPv6 |
98bd7ca0 DH |
864 | /* XXX: perhaps create drealloc() rather than do it manually */ |
865 | void | |
866 | add_ipv6_addr_to_interface(struct interface_info *iface, | |
867 | const struct in6_addr *addr) { | |
868 | /* | |
869 | * Each IPv6 interface will have at least two IPv6 addresses, | |
870 | * and likely quite a few more. So we use 8, as our "chunk size" for | |
871 | * collecting addresses. | |
872 | */ | |
873 | if (iface->v6addresses == NULL) { | |
874 | iface->v6addresses = dmalloc(8 * sizeof(struct in6_addr), MDL); | |
875 | if (iface->v6addresses == NULL) { | |
876 | log_fatal("Out of memory saving IPv6 address " | |
877 | "on interface."); | |
878 | } | |
879 | iface->v6address_count = 0; | |
880 | iface->v6address_max = 8; | |
881 | } else if (iface->v6address_count >= iface->v6address_max) { | |
882 | struct in6_addr *tmp; | |
883 | int new_max; | |
884 | ||
885 | new_max = iface->v6address_max + 8; | |
886 | tmp = dmalloc(new_max * sizeof(struct in6_addr), MDL); | |
887 | if (tmp == NULL) { | |
888 | log_fatal("Out of memory saving IPv6 address " | |
889 | "on interface."); | |
890 | } | |
891 | memcpy(tmp, | |
892 | iface->v6addresses, | |
893 | iface->v6address_max * sizeof(struct in6_addr)); | |
894 | dfree(iface->v6addresses, MDL); | |
895 | iface->v6addresses = tmp; | |
896 | iface->v6address_max = new_max; | |
897 | } | |
898 | iface->v6addresses[iface->v6address_count++] = *addr; | |
899 | } | |
fe5b0fdd | 900 | #endif /* DHCPv6 */ |
98bd7ca0 DH |
901 | |
902 | /* Use the SIOCGIFCONF ioctl to get a list of all the attached interfaces. | |
903 | For each interface that's of type INET and not the loopback interface, | |
904 | register that interface with the network I/O software, figure out what | |
905 | subnet it's on, and add it to the list of interfaces. */ | |
906 | ||
907 | void | |
908 | discover_interfaces(int state) { | |
909 | struct iface_conf_list ifaces; | |
910 | struct iface_info info; | |
911 | int err; | |
912 | ||
28868515 | 913 | struct interface_info *tmp; |
98bd7ca0 DH |
914 | struct interface_info *last, *next; |
915 | ||
28868515 SK |
916 | #ifdef DHCPv6 |
917 | char abuf[sizeof("ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255")]; | |
918 | #endif /* DHCPv6 */ | |
919 | ||
98bd7ca0 DH |
920 | |
921 | struct subnet *subnet; | |
922 | int ir; | |
923 | isc_result_t status; | |
924 | int wifcount = 0; | |
925 | ||
926 | static int setup_fallback = 0; | |
927 | ||
928 | if (!begin_iface_scan(&ifaces)) { | |
929 | log_fatal("Can't get list of interfaces."); | |
d902d52e | 930 | } |
615a574a | 931 | |
fa226d8f TL |
932 | /* If we already have a list of interfaces, and we're running as |
933 | a DHCP server, the interfaces were requested. */ | |
934 | if (interfaces && (state == DISCOVER_SERVER || | |
935 | state == DISCOVER_RELAY || | |
936 | state == DISCOVER_REQUESTED)) | |
937 | ir = 0; | |
938 | else if (state == DISCOVER_UNCONFIGURED) | |
939 | ir = INTERFACE_REQUESTED | INTERFACE_AUTOMATIC; | |
940 | else | |
941 | ir = INTERFACE_REQUESTED; | |
942 | ||
d2bc90bd | 943 | /* Cycle through the list of interfaces looking for IP addresses. */ |
98bd7ca0 | 944 | while (next_iface(&info, &err, &ifaces)) { |
fa226d8f | 945 | |
5c0ba9a1 | 946 | /* See if we've seen an interface that matches this one. */ |
98bd7ca0 DH |
947 | for (tmp = interfaces; tmp; tmp = tmp->next) { |
948 | if (!strcmp(tmp->name, info.name)) | |
5c0ba9a1 | 949 | break; |
98bd7ca0 | 950 | } |
5c0ba9a1 | 951 | |
98311e4b DH |
952 | /* Skip non broadcast interfaces (plus loopback and |
953 | point-to-point in case an OS incorrectly marks them | |
954 | as broadcast). Also skip down interfaces unless we're | |
955 | trying to get a list of configurable interfaces. */ | |
98bd7ca0 DH |
956 | if (((!(info.flags & IFF_BROADCAST) || |
957 | info.flags & IFF_LOOPBACK || | |
958 | info.flags & IFF_POINTOPOINT) && !tmp) || | |
959 | (!(info.flags & IFF_UP) && | |
fa226d8f TL |
960 | state != DISCOVER_UNCONFIGURED)) |
961 | continue; | |
962 | ||
fa226d8f TL |
963 | /* If there isn't already an interface by this name, |
964 | allocate one. */ | |
98bd7ca0 DH |
965 | if (tmp == NULL) { |
966 | status = interface_allocate(&tmp, MDL); | |
967 | if (status != ISC_R_SUCCESS) { | |
968 | log_fatal("Error allocating interface %s: %s", | |
969 | info.name, isc_result_totext(status)); | |
970 | } | |
971 | strcpy(tmp->name, info.name); | |
972 | interface_snorf(tmp, ir); | |
973 | interface_dereference(&tmp, MDL); | |
fc33f8c5 | 974 | tmp = interfaces; /* XXX */ |
347de8bd TL |
975 | } |
976 | ||
98bd7ca0 DH |
977 | if (dhcp_interface_discovery_hook) { |
978 | (*dhcp_interface_discovery_hook)(tmp); | |
979 | } | |
20916cae | 980 | |
98bd7ca0 DH |
981 | if ((info.addr.ss_family == AF_INET) && |
982 | (local_family == AF_INET)) { | |
983 | struct sockaddr_in *a = (struct sockaddr_in*)&info.addr; | |
fa226d8f TL |
984 | struct iaddr addr; |
985 | ||
98bd7ca0 DH |
986 | /* We don't want the loopback interface. */ |
987 | if (a->sin_addr.s_addr == htonl(INADDR_LOOPBACK) && | |
988 | ((tmp->flags & INTERFACE_AUTOMATIC) && | |
989 | state == DISCOVER_SERVER)) | |
990 | continue; | |
991 | ||
992 | /* If the only address we have is 0.0.0.0, we | |
993 | shouldn't consider the interface configured. */ | |
994 | if (a->sin_addr.s_addr != htonl(INADDR_ANY)) | |
995 | tmp->configured = 1; | |
996 | ||
997 | add_ipv4_addr_to_interface(tmp, &a->sin_addr); | |
998 | ||
98bd7ca0 DH |
999 | /* invoke the setup hook */ |
1000 | addr.len = 4; | |
1001 | memcpy(addr.iabuf, &a->sin_addr.s_addr, addr.len); | |
1002 | if (dhcp_interface_setup_hook) { | |
1003 | (*dhcp_interface_setup_hook)(tmp, &addr); | |
1004 | } | |
1005 | } | |
fe5b0fdd | 1006 | #ifdef DHCPv6 |
98bd7ca0 DH |
1007 | else if ((info.addr.ss_family == AF_INET6) && |
1008 | (local_family == AF_INET6)) { | |
1009 | struct sockaddr_in6 *a = | |
1010 | (struct sockaddr_in6*)&info.addr; | |
1011 | struct iaddr addr; | |
fa226d8f TL |
1012 | |
1013 | /* We don't want the loopback interface. */ | |
98bd7ca0 DH |
1014 | if (IN6_IS_ADDR_LOOPBACK(&a->sin6_addr) && |
1015 | ((tmp->flags & INTERFACE_AUTOMATIC) && | |
acc21512 | 1016 | state == DISCOVER_SERVER)) |
78c553c4 | 1017 | continue; |
fa226d8f | 1018 | |
98bd7ca0 DH |
1019 | /* If the only address we have is 0.0.0.0, we |
1020 | shouldn't consider the interface configured. */ | |
1021 | if (IN6_IS_ADDR_UNSPECIFIED(&a->sin6_addr)) | |
1022 | tmp->configured = 1; | |
fa226d8f | 1023 | |
98bd7ca0 DH |
1024 | add_ipv6_addr_to_interface(tmp, &a->sin6_addr); |
1025 | ||
98bd7ca0 DH |
1026 | /* invoke the setup hook */ |
1027 | addr.len = 16; | |
1028 | memcpy(addr.iabuf, &a->sin6_addr, addr.len); | |
1029 | if (dhcp_interface_setup_hook) { | |
1030 | (*dhcp_interface_setup_hook)(tmp, &addr); | |
1031 | } | |
fa226d8f | 1032 | } |
fe5b0fdd | 1033 | #endif /* DHCPv6 */ |
fa226d8f TL |
1034 | } |
1035 | ||
98bd7ca0 DH |
1036 | if (err) { |
1037 | log_fatal("Error getting interface information."); | |
1038 | } | |
1039 | ||
1040 | end_iface_scan(&ifaces); | |
d902d52e | 1041 | |
d2bc90bd | 1042 | |
4ba58919 DH |
1043 | /* Mock-up an 'ifp' structure which is no longer used in the |
1044 | * new interface-sensing code, but is used in higher layers | |
1045 | * (for example to sense fallback interfaces). | |
1046 | */ | |
1047 | for (tmp = interfaces ; tmp != NULL ; tmp = tmp->next) { | |
1048 | if (tmp->ifp == NULL) { | |
1049 | struct ifreq *tif; | |
1050 | ||
1051 | tif = (struct ifreq *)dmalloc(sizeof(struct ifreq), | |
1052 | MDL); | |
1053 | if (tif == NULL) | |
1054 | log_fatal("no space for ifp mockup."); | |
1055 | strcpy(tif->ifr_name, tmp->name); | |
1056 | tmp->ifp = tif; | |
1057 | } | |
1058 | } | |
1059 | ||
1060 | ||
fa226d8f TL |
1061 | /* If we're just trying to get a list of interfaces that we might |
1062 | be able to configure, we can quit now. */ | |
bdcaf7b9 | 1063 | if (state == DISCOVER_UNCONFIGURED) { |
fa226d8f | 1064 | return; |
bdcaf7b9 | 1065 | } |
fa226d8f TL |
1066 | |
1067 | /* Weed out the interfaces that did not have IP addresses. */ | |
98bd7ca0 | 1068 | tmp = last = next = NULL; |
20916cae TL |
1069 | if (interfaces) |
1070 | interface_reference (&tmp, interfaces, MDL); | |
1071 | while (tmp) { | |
1072 | if (next) | |
1073 | interface_dereference (&next, MDL); | |
1074 | if (tmp -> next) | |
1075 | interface_reference (&next, tmp -> next, MDL); | |
347de8bd | 1076 | /* skip interfaces that are running already */ |
98311e4b DH |
1077 | if (tmp -> flags & INTERFACE_RUNNING) { |
1078 | interface_dereference(&tmp, MDL); | |
1079 | if(next) | |
1080 | interface_reference(&tmp, next, MDL); | |
347de8bd | 1081 | continue; |
98311e4b | 1082 | } |
fa226d8f TL |
1083 | if ((tmp -> flags & INTERFACE_AUTOMATIC) && |
1084 | state == DISCOVER_REQUESTED) | |
1085 | tmp -> flags &= ~(INTERFACE_AUTOMATIC | | |
1086 | INTERFACE_REQUESTED); | |
fe5b0fdd DH |
1087 | |
1088 | #ifdef DHCPv6 | |
98bd7ca0 | 1089 | if (!(tmp->flags & INTERFACE_REQUESTED)) { |
fe5b0fdd DH |
1090 | #else |
1091 | if (!tmp -> ifp || !(tmp -> flags & INTERFACE_REQUESTED)) { | |
1092 | #endif /* DHCPv6 */ | |
fa226d8f | 1093 | if ((tmp -> flags & INTERFACE_REQUESTED) != ir) |
8ae2d595 | 1094 | log_fatal ("%s: not found", tmp -> name); |
20916cae TL |
1095 | if (!last) { |
1096 | if (interfaces) | |
1097 | interface_dereference (&interfaces, | |
1098 | MDL); | |
fc33f8c5 | 1099 | if (next) |
20916cae TL |
1100 | interface_reference (&interfaces, next, MDL); |
1101 | } else { | |
1102 | interface_dereference (&last -> next, MDL); | |
fc33f8c5 TL |
1103 | if (next) |
1104 | interface_reference (&last -> next, | |
1105 | next, MDL); | |
20916cae TL |
1106 | } |
1107 | if (tmp -> next) | |
1108 | interface_dereference (&tmp -> next, MDL); | |
fa226d8f TL |
1109 | |
1110 | /* Remember the interface in case we need to know | |
1111 | about it later. */ | |
20916cae TL |
1112 | if (dummy_interfaces) { |
1113 | interface_reference (&tmp -> next, | |
1114 | dummy_interfaces, MDL); | |
1115 | interface_dereference (&dummy_interfaces, MDL); | |
1116 | } | |
1117 | interface_reference (&dummy_interfaces, tmp, MDL); | |
1118 | interface_dereference (&tmp, MDL); | |
1119 | if (next) | |
1120 | interface_reference (&tmp, next, MDL); | |
fa226d8f TL |
1121 | continue; |
1122 | } | |
1123 | last = tmp; | |
1124 | ||
fa226d8f | 1125 | /* We must have a subnet declaration for each interface. */ |
98bd7ca0 DH |
1126 | if (!tmp->shared_network && (state == DISCOVER_SERVER)) { |
1127 | log_error("%s", ""); | |
1128 | if (local_family == AF_INET) { | |
1129 | log_error("No subnet declaration for %s (%s).", | |
1130 | tmp->name, | |
f796f70a DH |
1131 | (tmp->addresses == NULL) ? |
1132 | "no IPv4 addresses" : | |
1133 | inet_ntoa(tmp->addresses[0])); | |
fe5b0fdd | 1134 | #ifdef DHCPv6 |
98bd7ca0 DH |
1135 | } else { |
1136 | if (tmp->v6addresses != NULL) { | |
1137 | inet_ntop(AF_INET6, | |
1138 | &tmp->v6addresses[0], | |
1139 | abuf, | |
1140 | sizeof(abuf)); | |
1141 | } else { | |
f796f70a | 1142 | strcpy(abuf, "no IPv6 addresses"); |
98bd7ca0 | 1143 | } |
00a002fc | 1144 | log_error("No subnet6 declaration for %s (%s).", |
98bd7ca0 DH |
1145 | tmp->name, |
1146 | abuf); | |
fe5b0fdd | 1147 | #endif /* DHCPv6 */ |
98bd7ca0 DH |
1148 | } |
1149 | if (supports_multiple_interfaces(tmp)) { | |
21fd88ea TL |
1150 | log_error ("** Ignoring requests on %s. %s", |
1151 | tmp -> name, "If this is not what"); | |
1152 | log_error (" you want, please write %s", | |
00a002fc MA |
1153 | #ifdef DHCPv6 |
1154 | (local_family != AF_INET) ? | |
1155 | "a subnet6 declaration" : | |
1156 | #endif | |
21fd88ea TL |
1157 | "a subnet declaration"); |
1158 | log_error (" in your dhcpd.conf file %s", | |
1159 | "for the network segment"); | |
1160 | log_error (" to %s %s %s", | |
5cefe5e5 | 1161 | "which interface", |
21fd88ea TL |
1162 | tmp -> name, "is attached. **"); |
1163 | log_error ("%s", ""); | |
5cefe5e5 TL |
1164 | goto next; |
1165 | } else { | |
00a002fc MA |
1166 | log_error ("You must write a %s", |
1167 | #ifdef DHCPv6 | |
1168 | (local_family != AF_INET) ? | |
1169 | "subnet6 declaration for this" : | |
1170 | #endif | |
1171 | "subnet declaration for this"); | |
5cefe5e5 TL |
1172 | log_error ("subnet. You cannot prevent %s", |
1173 | "the DHCP server"); | |
1174 | log_error ("from listening on this subnet %s", | |
1175 | "because your"); | |
1176 | log_fatal ("operating system does not %s.", | |
1177 | "support this capability"); | |
1178 | } | |
74f45f96 | 1179 | } |
fa226d8f TL |
1180 | |
1181 | /* Find subnets that don't have valid interface | |
1182 | addresses... */ | |
1183 | for (subnet = (tmp -> shared_network | |
1184 | ? tmp -> shared_network -> subnets | |
1185 | : (struct subnet *)0); | |
1186 | subnet; subnet = subnet -> next_sibling) { | |
97050349 SK |
1187 | /* Set the interface address for this subnet |
1188 | to the first address we found. */ | |
1189 | if (subnet->interface_address.len == 0) { | |
1190 | if (tmp->address_count > 0) { | |
1191 | subnet->interface_address.len = 4; | |
1192 | memcpy(subnet->interface_address.iabuf, | |
1193 | &tmp->addresses[0].s_addr, 4); | |
1194 | } else if (tmp->v6address_count > 0) { | |
1195 | subnet->interface_address.len = 16; | |
1196 | memcpy(subnet->interface_address.iabuf, | |
1197 | &tmp->v6addresses[0].s6_addr, | |
1198 | 16); | |
1199 | } else { | |
1200 | /* XXX: should be one */ | |
1201 | log_error("%s missing an interface " | |
1202 | "address", tmp->name); | |
1203 | continue; | |
1204 | } | |
fa226d8f TL |
1205 | } |
1206 | } | |
1207 | ||
fc33f8c5 TL |
1208 | /* Flag the index as not having been set, so that the |
1209 | interface registerer can set it or not as it chooses. */ | |
1210 | tmp -> index = -1; | |
1211 | ||
fa226d8f | 1212 | /* Register the interface... */ |
98bd7ca0 DH |
1213 | if (local_family == AF_INET) { |
1214 | if_register_receive(tmp); | |
1215 | if_register_send(tmp); | |
fe5b0fdd | 1216 | #ifdef DHCPv6 |
98bd7ca0 | 1217 | } else { |
7de20a95 EH |
1218 | if ((state == DISCOVER_SERVER) || |
1219 | (state == DISCOVER_RELAY)) { | |
98bd7ca0 DH |
1220 | if_register6(tmp, 1); |
1221 | } else { | |
1222 | if_register6(tmp, 0); | |
1223 | } | |
fe5b0fdd | 1224 | #endif /* DHCPv6 */ |
98bd7ca0 | 1225 | } |
fc33f8c5 TL |
1226 | |
1227 | interface_stash (tmp); | |
d902d52e | 1228 | wifcount++; |
fe5b0fdd | 1229 | #if defined (F_SETFD) |
d4417aa1 TL |
1230 | if (fcntl (tmp -> rfdesc, F_SETFD, 1) < 0) |
1231 | log_error ("Can't set close-on-exec on %s: %m", | |
1232 | tmp -> name); | |
1233 | if (tmp -> rfdesc != tmp -> wfdesc) { | |
1234 | if (fcntl (tmp -> wfdesc, F_SETFD, 1) < 0) | |
1235 | log_error ("Can't set close-on-exec on %s: %m", | |
1236 | tmp -> name); | |
1237 | } | |
1238 | #endif | |
5cefe5e5 | 1239 | next: |
20916cae TL |
1240 | interface_dereference (&tmp, MDL); |
1241 | if (next) | |
1242 | interface_reference (&tmp, next, MDL); | |
fa226d8f TL |
1243 | } |
1244 | ||
1245 | /* Now register all the remaining interfaces as protocols. */ | |
acc21512 | 1246 | for (tmp = interfaces; tmp; tmp = tmp -> next) { |
347de8bd TL |
1247 | /* not if it's been registered before */ |
1248 | if (tmp -> flags & INTERFACE_RUNNING) | |
1249 | continue; | |
08921fe0 TL |
1250 | if (tmp -> rfdesc == -1) |
1251 | continue; | |
06eb8bab SK |
1252 | #ifdef DHCPv6 |
1253 | if (local_family == AF_INET6) { | |
98bd7ca0 DH |
1254 | status = omapi_register_io_object((omapi_object_t *)tmp, |
1255 | if_readsocket, | |
06eb8bab | 1256 | 0, got_one_v6, 0, 0); |
98bd7ca0 | 1257 | } else { |
06eb8bab SK |
1258 | #else |
1259 | { | |
1260 | #endif /* DHCPv6 */ | |
98bd7ca0 DH |
1261 | status = omapi_register_io_object((omapi_object_t *)tmp, |
1262 | if_readsocket, | |
06eb8bab | 1263 | 0, got_one, 0, 0); |
98bd7ca0 | 1264 | } |
acc21512 TL |
1265 | if (status != ISC_R_SUCCESS) |
1266 | log_fatal ("Can't register I/O handle for %s: %s", | |
1267 | tmp -> name, isc_result_totext (status)); | |
ecddae64 DH |
1268 | |
1269 | #if defined(DHCPv6) | |
1270 | /* Only register the first interface for V6, since they all | |
1271 | * use the same socket. XXX: This has some messy side | |
1272 | * effects if we start dynamically adding and removing | |
1273 | * interfaces, but we're well beyond that point in terms of | |
1274 | * mess. | |
1275 | */ | |
1276 | if (local_family == AF_INET6) | |
1277 | break; | |
1278 | #endif | |
acc21512 | 1279 | } |
fa226d8f | 1280 | |
d902d52e TL |
1281 | if (state == DISCOVER_SERVER && wifcount == 0) { |
1282 | log_info ("%s", ""); | |
1283 | log_fatal ("Not configured to listen on any interfaces!"); | |
1284 | } | |
1285 | ||
98bd7ca0 | 1286 | if ((local_family == AF_INET) && !setup_fallback) { |
bdcaf7b9 | 1287 | setup_fallback = 1; |
98bd7ca0 | 1288 | maybe_setup_fallback(); |
bdcaf7b9 TL |
1289 | } |
1290 | ||
fe5b0fdd | 1291 | #if defined (F_SETFD) |
d4417aa1 TL |
1292 | if (fallback_interface) { |
1293 | if (fcntl (fallback_interface -> rfdesc, F_SETFD, 1) < 0) | |
1294 | log_error ("Can't set close-on-exec on fallback: %m"); | |
1295 | if (fallback_interface -> rfdesc != fallback_interface -> wfdesc) { | |
1296 | if (fcntl (fallback_interface -> wfdesc, F_SETFD, 1) < 0) | |
1297 | log_error ("Can't set close-on-exec on fallback: %m"); | |
1298 | } | |
1299 | } | |
fe5b0fdd | 1300 | #endif /* F_SETFD */ |
d2bc90bd TL |
1301 | } |
1302 | ||
acc21512 TL |
1303 | int if_readsocket (h) |
1304 | omapi_object_t *h; | |
1305 | { | |
1306 | struct interface_info *ip; | |
1307 | ||
1308 | if (h -> type != dhcp_type_interface) | |
0a759381 | 1309 | return -1; |
acc21512 TL |
1310 | ip = (struct interface_info *)h; |
1311 | return ip -> rfdesc; | |
1312 | } | |
1313 | ||
20916cae | 1314 | int setup_fallback (struct interface_info **fp, const char *file, int line) |
d2bc90bd | 1315 | { |
20916cae TL |
1316 | isc_result_t status; |
1317 | ||
1318 | status = interface_allocate (&fallback_interface, file, line); | |
1319 | if (status != ISC_R_SUCCESS) | |
1320 | log_fatal ("Error allocating fallback interface: %s", | |
1321 | isc_result_totext (status)); | |
d2bc90bd | 1322 | strcpy (fallback_interface -> name, "fallback"); |
20916cae TL |
1323 | if (dhcp_interface_setup_hook) |
1324 | (*dhcp_interface_setup_hook) (fallback_interface, | |
1325 | (struct iaddr *)0); | |
1326 | status = interface_reference (fp, fallback_interface, file, line); | |
fc33f8c5 | 1327 | |
fbd9c67b | 1328 | fallback_interface -> index = -1; |
fc33f8c5 | 1329 | interface_stash (fallback_interface); |
20916cae | 1330 | return status == ISC_R_SUCCESS; |
fa226d8f TL |
1331 | } |
1332 | ||
1333 | void reinitialize_interfaces () | |
1334 | { | |
1335 | struct interface_info *ip; | |
1336 | ||
1337 | for (ip = interfaces; ip; ip = ip -> next) { | |
1338 | if_reinitialize_receive (ip); | |
1339 | if_reinitialize_send (ip); | |
1340 | } | |
1341 | ||
d2bc90bd TL |
1342 | if (fallback_interface) |
1343 | if_reinitialize_send (fallback_interface); | |
fa226d8f TL |
1344 | |
1345 | interfaces_invalidated = 1; | |
1346 | } | |
d2bc90bd | 1347 | |
acc21512 TL |
1348 | isc_result_t got_one (h) |
1349 | omapi_object_t *h; | |
fa226d8f TL |
1350 | { |
1351 | struct sockaddr_in from; | |
1352 | struct hardware hfrom; | |
1353 | struct iaddr ifrom; | |
1354 | int result; | |
1355 | union { | |
1356 | unsigned char packbuf [4095]; /* Packet input buffer. | |
1357 | Must be as large as largest | |
1358 | possible MTU. */ | |
1359 | struct dhcp_packet packet; | |
1360 | } u; | |
acc21512 TL |
1361 | struct interface_info *ip; |
1362 | ||
1363 | if (h -> type != dhcp_type_interface) | |
1364 | return ISC_R_INVALIDARG; | |
1365 | ip = (struct interface_info *)h; | |
fa226d8f | 1366 | |
a034015f | 1367 | again: |
fa226d8f TL |
1368 | if ((result = |
1369 | receive_packet (ip, u.packbuf, sizeof u, &from, &hfrom)) < 0) { | |
8ae2d595 | 1370 | log_error ("receive_packet failed on %s: %m", ip -> name); |
acc21512 | 1371 | return ISC_R_UNEXPECTED; |
fa226d8f TL |
1372 | } |
1373 | if (result == 0) | |
acc21512 | 1374 | return ISC_R_UNEXPECTED; |
fa226d8f | 1375 | |
bb404b74 TL |
1376 | /* If we didn't at least get the fixed portion of the BOOTP |
1377 | packet, drop the packet. We're allowing packets with no | |
1378 | sname or filename, because we're aware of at least one | |
1379 | client that sends such packets, but this definitely falls | |
1380 | into the category of being forgiving. */ | |
1381 | if (result < DHCP_FIXED_NON_UDP - DHCP_SNAME_LEN - DHCP_FILE_LEN) | |
1382 | return ISC_R_UNEXPECTED; | |
1383 | ||
fa226d8f TL |
1384 | if (bootp_packet_handler) { |
1385 | ifrom.len = 4; | |
1386 | memcpy (ifrom.iabuf, &from.sin_addr, ifrom.len); | |
1387 | ||
b1b7b521 | 1388 | (*bootp_packet_handler) (ip, &u.packet, (unsigned)result, |
fa226d8f TL |
1389 | from.sin_port, ifrom, &hfrom); |
1390 | } | |
a034015f TL |
1391 | |
1392 | /* If there is buffered data, read again. This is for, e.g., | |
1393 | bpf, which may return two packets at once. */ | |
1394 | if (ip -> rbuf_offset != ip -> rbuf_len) | |
1395 | goto again; | |
acc21512 TL |
1396 | return ISC_R_SUCCESS; |
1397 | } | |
1398 | ||
fe5b0fdd | 1399 | #ifdef DHCPv6 |
98bd7ca0 DH |
1400 | isc_result_t |
1401 | got_one_v6(omapi_object_t *h) { | |
1402 | struct sockaddr_in6 from; | |
1403 | struct in6_addr to; | |
1404 | struct iaddr ifrom; | |
1405 | int result; | |
1406 | char buf[65536]; /* maximum size for a UDP packet is 65536 */ | |
1407 | struct interface_info *ip; | |
1408 | int is_unicast; | |
ecddae64 | 1409 | unsigned int if_idx = 0; |
98bd7ca0 DH |
1410 | |
1411 | if (h->type != dhcp_type_interface) { | |
1412 | return ISC_R_INVALIDARG; | |
1413 | } | |
1414 | ip = (struct interface_info *)h; | |
1415 | ||
28868515 | 1416 | result = receive_packet6(ip, (unsigned char *)buf, sizeof(buf), |
ecddae64 | 1417 | &from, &to, &if_idx); |
98bd7ca0 DH |
1418 | if (result < 0) { |
1419 | log_error("receive_packet6() failed on %s: %m", ip->name); | |
1420 | return ISC_R_UNEXPECTED; | |
1421 | } | |
1422 | ||
ecddae64 DH |
1423 | /* 0 is 'any' interface. */ |
1424 | if (if_idx == 0) | |
1425 | return ISC_R_NOTFOUND; | |
1426 | ||
98bd7ca0 DH |
1427 | if (dhcpv6_packet_handler != NULL) { |
1428 | /* | |
1429 | * If a packet is not multicast, we assume it is unicast. | |
1430 | */ | |
1431 | if (IN6_IS_ADDR_MULTICAST(&to)) { | |
1432 | is_unicast = ISC_FALSE; | |
1433 | } else { | |
1434 | is_unicast = ISC_TRUE; | |
1435 | } | |
1436 | ||
1437 | ifrom.len = 16; | |
1438 | memcpy(ifrom.iabuf, &from.sin6_addr, ifrom.len); | |
1439 | ||
ecddae64 DH |
1440 | /* Seek forward to find the matching source interface. */ |
1441 | while ((ip != NULL) && (if_nametoindex(ip->name) != if_idx)) | |
1442 | ip = ip->next; | |
1443 | ||
1444 | if (ip == NULL) | |
1445 | return ISC_R_NOTFOUND; | |
1446 | ||
98bd7ca0 DH |
1447 | (*dhcpv6_packet_handler)(ip, buf, |
1448 | result, from.sin6_port, | |
1449 | &ifrom, is_unicast); | |
1450 | } | |
1451 | ||
1452 | return ISC_R_SUCCESS; | |
1453 | } | |
fe5b0fdd | 1454 | #endif /* DHCPv6 */ |
98bd7ca0 | 1455 | |
a034015f TL |
1456 | isc_result_t dhcp_interface_set_value (omapi_object_t *h, |
1457 | omapi_object_t *id, | |
1458 | omapi_data_string_t *name, | |
1459 | omapi_typed_data_t *value) | |
acc21512 | 1460 | { |
a034015f TL |
1461 | struct interface_info *interface; |
1462 | isc_result_t status; | |
a034015f | 1463 | |
acc21512 TL |
1464 | if (h -> type != dhcp_type_interface) |
1465 | return ISC_R_INVALIDARG; | |
a034015f TL |
1466 | interface = (struct interface_info *)h; |
1467 | ||
1468 | if (!omapi_ds_strcmp (name, "name")) { | |
1469 | if ((value -> type == omapi_datatype_data || | |
1470 | value -> type == omapi_datatype_string) && | |
1471 | value -> u.buffer.len < sizeof interface -> name) { | |
1472 | memcpy (interface -> name, | |
1473 | value -> u.buffer.value, | |
1474 | value -> u.buffer.len); | |
1475 | interface -> name [value -> u.buffer.len] = 0; | |
1476 | } else | |
1477 | return ISC_R_INVALIDARG; | |
1478 | return ISC_R_SUCCESS; | |
1479 | } | |
1480 | ||
1481 | /* Try to find some inner object that can take the value. */ | |
1482 | if (h -> inner && h -> inner -> type -> set_value) { | |
1483 | status = ((*(h -> inner -> type -> set_value)) | |
1484 | (h -> inner, id, name, value)); | |
1485 | if (status == ISC_R_SUCCESS || status == ISC_R_UNCHANGED) | |
1486 | return status; | |
1487 | } | |
1488 | ||
acc21512 TL |
1489 | return ISC_R_NOTFOUND; |
1490 | } | |
1491 | ||
a034015f TL |
1492 | |
1493 | isc_result_t dhcp_interface_get_value (omapi_object_t *h, | |
1494 | omapi_object_t *id, | |
1495 | omapi_data_string_t *name, | |
1496 | omapi_value_t **value) | |
acc21512 | 1497 | { |
a034015f TL |
1498 | return ISC_R_NOTIMPLEMENTED; |
1499 | } | |
1500 | ||
1501 | isc_result_t dhcp_interface_destroy (omapi_object_t *h, | |
1502 | const char *file, int line) | |
1503 | { | |
1504 | struct interface_info *interface; | |
a034015f | 1505 | |
acc21512 TL |
1506 | if (h -> type != dhcp_type_interface) |
1507 | return ISC_R_INVALIDARG; | |
a034015f TL |
1508 | interface = (struct interface_info *)h; |
1509 | ||
d758ad8c | 1510 | if (interface -> ifp) { |
a034015f | 1511 | dfree (interface -> ifp, file, line); |
d758ad8c TL |
1512 | interface -> ifp = 0; |
1513 | } | |
1514 | if (interface -> next) | |
1515 | interface_dereference (&interface -> next, file, line); | |
1516 | if (interface -> rbuf) { | |
1517 | dfree (interface -> rbuf, file, line); | |
1518 | interface -> rbuf = (unsigned char *)0; | |
1519 | } | |
1520 | if (interface -> client) | |
1521 | interface -> client = (struct client_state *)0; | |
1522 | ||
1523 | if (interface -> shared_network) | |
1524 | omapi_object_dereference ((omapi_object_t **) | |
1525 | &interface -> shared_network, MDL); | |
1526 | ||
a034015f | 1527 | return ISC_R_SUCCESS; |
acc21512 TL |
1528 | } |
1529 | ||
a034015f TL |
1530 | isc_result_t dhcp_interface_signal_handler (omapi_object_t *h, |
1531 | const char *name, va_list ap) | |
acc21512 | 1532 | { |
a034015f | 1533 | struct interface_info *ip, *interface; |
a034015f TL |
1534 | isc_result_t status; |
1535 | ||
1536 | if (h -> type != dhcp_type_interface) | |
acc21512 | 1537 | return ISC_R_INVALIDARG; |
a034015f TL |
1538 | interface = (struct interface_info *)h; |
1539 | ||
cf75fef2 TL |
1540 | /* If it's an update signal, see if the interface is dead right |
1541 | now, or isn't known at all, and if that's the case, revive it. */ | |
1542 | if (!strcmp (name, "update")) { | |
1543 | for (ip = dummy_interfaces; ip; ip = ip -> next) | |
1544 | if (ip == interface) | |
1545 | break; | |
1546 | if (ip && dhcp_interface_startup_hook) | |
1547 | return (*dhcp_interface_startup_hook) (ip); | |
a034015f | 1548 | |
cf75fef2 TL |
1549 | for (ip = interfaces; ip; ip = ip -> next) |
1550 | if (ip == interface) | |
1551 | break; | |
1552 | if (!ip && dhcp_interface_startup_hook) | |
1553 | return (*dhcp_interface_startup_hook) (ip); | |
a034015f | 1554 | } |
a034015f | 1555 | |
a034015f TL |
1556 | /* Try to find some inner object that can take the value. */ |
1557 | if (h -> inner && h -> inner -> type -> get_value) { | |
1558 | status = ((*(h -> inner -> type -> signal_handler)) | |
1559 | (h -> inner, name, ap)); | |
1560 | if (status == ISC_R_SUCCESS) | |
1561 | return status; | |
1562 | } | |
acc21512 TL |
1563 | return ISC_R_NOTFOUND; |
1564 | } | |
1565 | ||
a034015f TL |
1566 | isc_result_t dhcp_interface_stuff_values (omapi_object_t *c, |
1567 | omapi_object_t *id, | |
1568 | omapi_object_t *h) | |
acc21512 | 1569 | { |
a034015f TL |
1570 | struct interface_info *interface; |
1571 | isc_result_t status; | |
acc21512 | 1572 | |
acc21512 TL |
1573 | if (h -> type != dhcp_type_interface) |
1574 | return ISC_R_INVALIDARG; | |
a034015f TL |
1575 | interface = (struct interface_info *)h; |
1576 | ||
1577 | /* Write out all the values. */ | |
1578 | ||
1579 | status = omapi_connection_put_name (c, "state"); | |
1580 | if (status != ISC_R_SUCCESS) | |
1581 | return status; | |
c1e6c832 | 1582 | if ((interface->flags & INTERFACE_REQUESTED) != 0) |
a034015f TL |
1583 | status = omapi_connection_put_string (c, "up"); |
1584 | else | |
1585 | status = omapi_connection_put_string (c, "down"); | |
1586 | if (status != ISC_R_SUCCESS) | |
1587 | return status; | |
1588 | ||
1589 | /* Write out the inner object, if any. */ | |
1590 | if (h -> inner && h -> inner -> type -> stuff_values) { | |
1591 | status = ((*(h -> inner -> type -> stuff_values)) | |
1592 | (c, id, h -> inner)); | |
1593 | if (status == ISC_R_SUCCESS) | |
1594 | return status; | |
1595 | } | |
1596 | ||
acc21512 TL |
1597 | return ISC_R_SUCCESS; |
1598 | } | |
1599 | ||
a034015f TL |
1600 | isc_result_t dhcp_interface_lookup (omapi_object_t **ip, |
1601 | omapi_object_t *id, | |
1602 | omapi_object_t *ref) | |
acc21512 | 1603 | { |
a034015f TL |
1604 | omapi_value_t *tv = (omapi_value_t *)0; |
1605 | isc_result_t status; | |
1606 | struct interface_info *interface; | |
1607 | ||
d758ad8c TL |
1608 | if (!ref) |
1609 | return ISC_R_NOKEYS; | |
1610 | ||
a034015f TL |
1611 | /* First see if we were sent a handle. */ |
1612 | status = omapi_get_value_str (ref, id, "handle", &tv); | |
1613 | if (status == ISC_R_SUCCESS) { | |
1614 | status = omapi_handle_td_lookup (ip, tv -> value); | |
1615 | ||
1616 | omapi_value_dereference (&tv, MDL); | |
1617 | if (status != ISC_R_SUCCESS) | |
1618 | return status; | |
1619 | ||
1620 | /* Don't return the object if the type is wrong. */ | |
1621 | if ((*ip) -> type != dhcp_type_interface) { | |
1622 | omapi_object_dereference (ip, MDL); | |
1623 | return ISC_R_INVALIDARG; | |
1624 | } | |
1625 | } | |
1626 | ||
1627 | /* Now look for an interface name. */ | |
1628 | status = omapi_get_value_str (ref, id, "name", &tv); | |
1629 | if (status == ISC_R_SUCCESS) { | |
cf75fef2 TL |
1630 | char *s; |
1631 | unsigned len; | |
a034015f TL |
1632 | for (interface = interfaces; interface; |
1633 | interface = interface -> next) { | |
cf75fef2 TL |
1634 | s = memchr (interface -> name, 0, IFNAMSIZ); |
1635 | if (s) | |
1636 | len = s - &interface -> name [0]; | |
1637 | else | |
1638 | len = IFNAMSIZ; | |
1639 | if ((tv -> value -> u.buffer.len == len && | |
1640 | !memcmp (interface -> name, | |
1641 | (char *)tv -> value -> u.buffer.value, | |
1642 | len))) | |
a034015f TL |
1643 | break; |
1644 | } | |
cf75fef2 TL |
1645 | if (!interface) { |
1646 | for (interface = dummy_interfaces; | |
1647 | interface; interface = interface -> next) { | |
1648 | s = memchr (interface -> name, 0, IFNAMSIZ); | |
1649 | if (s) | |
1650 | len = s - &interface -> name [0]; | |
1651 | else | |
1652 | len = IFNAMSIZ; | |
1653 | if ((tv -> value -> u.buffer.len == len && | |
1654 | !memcmp (interface -> name, | |
1655 | (char *) | |
1656 | tv -> value -> u.buffer.value, | |
1657 | len))) | |
1658 | break; | |
1659 | } | |
1660 | } | |
1661 | ||
a034015f TL |
1662 | omapi_value_dereference (&tv, MDL); |
1663 | if (*ip && *ip != (omapi_object_t *)interface) { | |
1664 | omapi_object_dereference (ip, MDL); | |
1665 | return ISC_R_KEYCONFLICT; | |
1666 | } else if (!interface) { | |
1667 | if (*ip) | |
1668 | omapi_object_dereference (ip, MDL); | |
1669 | return ISC_R_NOTFOUND; | |
1670 | } else if (!*ip) | |
a034015f TL |
1671 | omapi_object_reference (ip, |
1672 | (omapi_object_t *)interface, | |
1673 | MDL); | |
1674 | } | |
1675 | ||
1676 | /* If we get to here without finding an interface, no valid key was | |
1677 | specified. */ | |
1678 | if (!*ip) | |
1679 | return ISC_R_NOKEYS; | |
1680 | return ISC_R_SUCCESS; | |
1681 | } | |
1682 | ||
1683 | /* actually just go discover the interface */ | |
1684 | isc_result_t dhcp_interface_create (omapi_object_t **lp, | |
1685 | omapi_object_t *id) | |
1686 | { | |
1687 | struct interface_info *hp; | |
1688 | isc_result_t status; | |
acc21512 | 1689 | |
a034015f TL |
1690 | hp = (struct interface_info *)0; |
1691 | status = interface_allocate (&hp, MDL); | |
1692 | if (status != ISC_R_SUCCESS) | |
1693 | return status; | |
1694 | hp -> flags = INTERFACE_REQUESTED; | |
1695 | status = interface_reference ((struct interface_info **)lp, hp, MDL); | |
1696 | interface_dereference (&hp, MDL); | |
1697 | return status; | |
fa226d8f TL |
1698 | } |
1699 | ||
a034015f TL |
1700 | isc_result_t dhcp_interface_remove (omapi_object_t *lp, |
1701 | omapi_object_t *id) | |
1702 | { | |
1703 | struct interface_info *interface, *ip, *last; | |
1704 | ||
1705 | interface = (struct interface_info *)lp; | |
1706 | ||
1707 | /* remove from interfaces */ | |
1708 | last = 0; | |
1709 | for (ip = interfaces; ip; ip = ip -> next) { | |
1710 | if (ip == interface) { | |
1711 | if (last) { | |
1712 | interface_dereference (&last -> next, MDL); | |
1713 | if (ip -> next) | |
1714 | interface_reference (&last -> next, | |
1715 | ip -> next, MDL); | |
1716 | } else { | |
1717 | interface_dereference (&interfaces, MDL); | |
1718 | if (ip -> next) | |
1719 | interface_reference (&interfaces, | |
1720 | ip -> next, MDL); | |
1721 | } | |
1722 | if (ip -> next) | |
1723 | interface_dereference (&ip -> next, MDL); | |
1724 | break; | |
1725 | } | |
1726 | last = ip; | |
1727 | } | |
1728 | if (!ip) | |
1729 | return ISC_R_NOTFOUND; | |
1730 | ||
1731 | /* add the interface to the dummy_interface list */ | |
1732 | if (dummy_interfaces) { | |
1733 | interface_reference (&interface -> next, | |
1734 | dummy_interfaces, MDL); | |
1735 | interface_dereference (&dummy_interfaces, MDL); | |
1736 | } | |
1737 | interface_reference (&dummy_interfaces, interface, MDL); | |
1738 | ||
1739 | /* do a DHCPRELEASE */ | |
1740 | if (dhcp_interface_shutdown_hook) | |
1741 | (*dhcp_interface_shutdown_hook) (interface); | |
1742 | ||
1743 | /* remove the io object */ | |
1744 | omapi_unregister_io_object ((omapi_object_t *)interface); | |
1745 | ||
98bd7ca0 DH |
1746 | if (local_family == AF_INET) { |
1747 | if_deregister_send(interface); | |
1748 | if_deregister_receive(interface); | |
fe5b0fdd | 1749 | #ifdef DHCPv6 |
98bd7ca0 DH |
1750 | } else { |
1751 | if_deregister6(interface); | |
fe5b0fdd | 1752 | #endif /* DHCPv6 */ |
98bd7ca0 | 1753 | } |
a034015f TL |
1754 | |
1755 | return ISC_R_SUCCESS; | |
1756 | } | |
fc33f8c5 TL |
1757 | |
1758 | void interface_stash (struct interface_info *tptr) | |
1759 | { | |
1760 | struct interface_info **vec; | |
1761 | int delta; | |
1762 | ||
1763 | /* If the registerer didn't assign an index, assign one now. */ | |
1764 | if (tptr -> index == -1) { | |
1765 | tptr -> index = interface_count++; | |
1766 | while (tptr -> index < interface_max && | |
1767 | interface_vector [tptr -> index]) | |
1768 | tptr -> index = interface_count++; | |
1769 | } | |
1770 | ||
1771 | if (interface_max <= tptr -> index) { | |
1772 | delta = tptr -> index - interface_max + 10; | |
1773 | vec = dmalloc ((interface_max + delta) * | |
1774 | sizeof (struct interface_info *), MDL); | |
1775 | if (!vec) | |
1776 | return; | |
1777 | memset (&vec [interface_max], 0, | |
1778 | (sizeof (struct interface_info *)) * delta); | |
1779 | interface_max += delta; | |
1780 | if (interface_vector) { | |
1781 | memcpy (vec, interface_vector, | |
1782 | (interface_count * | |
1783 | sizeof (struct interface_info *))); | |
1784 | dfree (interface_vector, MDL); | |
1785 | } | |
1786 | interface_vector = vec; | |
1787 | } | |
d758ad8c | 1788 | interface_reference (&interface_vector [tptr -> index], tptr, MDL); |
fc33f8c5 TL |
1789 | if (tptr -> index >= interface_count) |
1790 | interface_count = tptr -> index + 1; | |
1791 | #if defined (TRACING) | |
1792 | trace_interface_register (interface_trace, tptr); | |
1793 | #endif | |
1794 | } | |
1795 | ||
1796 | void interface_snorf (struct interface_info *tmp, int ir) | |
1797 | { | |
1798 | tmp -> circuit_id = (u_int8_t *)tmp -> name; | |
1799 | tmp -> circuit_id_len = strlen (tmp -> name); | |
1800 | tmp -> remote_id = 0; | |
1801 | tmp -> remote_id_len = 0; | |
1802 | tmp -> flags = ir; | |
1803 | if (interfaces) { | |
1804 | interface_reference (&tmp -> next, | |
1805 | interfaces, MDL); | |
1806 | interface_dereference (&interfaces, MDL); | |
1807 | } | |
1808 | interface_reference (&interfaces, tmp, MDL); | |
1809 | } |