1 /* $NetBSD: discover.c,v 1.5 2022/04/03 01:10:58 christos Exp $ */
2
3 /* discover.c
4
5 Find and identify the network interfaces. */
6
7 /*
8 * Copyright (C) 2004-2022 Internet Systems Consortium, Inc. ("ISC")
9 * Copyright (c) 1995-2003 by Internet Software Consortium
10 *
11 * This Source Code Form is subject to the terms of the Mozilla Public
12 * License, v. 2.0. If a copy of the MPL was not distributed with this
13 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
16 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
17 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
18 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
19 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
20 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
21 * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
22 *
23 * Internet Systems Consortium, Inc.
24 * PO Box 360
25 * Newmarket, NH 03857 USA
26 * <info@isc.org>
27 * https://www.isc.org/
28 *
29 */
30
31 #include <sys/cdefs.h>
32 __RCSID("$NetBSD: discover.c,v 1.5 2022/04/03 01:10:58 christos Exp $");
33
34 #include "dhcpd.h"
35
36 /* length of line we can read from the IF file, 256 is too small in some cases */
37 #define IF_LINE_LENGTH 1024
38
39 #define BSD_COMP /* needed on Solaris for SIOCGLIFNUM */
40 #include <sys/ioctl.h>
41 #include <errno.h>
42
43 #ifdef HAVE_NET_IF6_H
44 # include <net/if6.h>
45 #endif
46
47 struct interface_info *interfaces = 0;
48 struct interface_info *dummy_interfaces = 0;
49 struct interface_info *fallback_interface = 0;
50
51 int interfaces_invalidated;
52 int quiet_interface_discovery;
53 u_int16_t local_port = 0;
54 u_int16_t remote_port = 0;
55 u_int16_t relay_port = 0;
56 int dhcpv4_over_dhcpv6 = 0;
57 int (*dhcp_interface_setup_hook) (struct interface_info *, struct iaddr *);
58 int (*dhcp_interface_discovery_hook) (struct interface_info *);
59 isc_result_t (*dhcp_interface_startup_hook) (struct interface_info *);
60 int (*dhcp_interface_shutdown_hook) (struct interface_info *);
61
62 struct in_addr limited_broadcast;
63
64 int local_family = AF_INET;
65 struct in_addr local_address;
66
67 #ifdef DHCPv6
68 /*
69 * Another clear abuse of the fact that undefined IP addresses are all zeroes.
70 */
71 struct in6_addr local_address6;
72 int bind_local_address6 = 0;
73 #endif /* DHCPv6 */
74
75 void (*bootp_packet_handler) (struct interface_info *,
76 struct dhcp_packet *, unsigned,
77 unsigned int,
78 struct iaddr, struct hardware *);
79
80 #ifdef DHCPv6
81 void (*dhcpv6_packet_handler)(struct interface_info *,
82 const char *, int,
83 int, const struct iaddr *,
84 isc_boolean_t);
85 #endif /* DHCPv6 */
86
87
88 omapi_object_type_t *dhcp_type_interface;
89 #if defined (TRACING)
90 trace_type_t *interface_trace;
91 trace_type_t *inpacket_trace;
92 trace_type_t *outpacket_trace;
93 #endif
94 struct interface_info **interface_vector;
95 int interface_count;
96 int interface_max;
97
OMAPI_OBJECT_ALLOC(interface,struct interface_info,dhcp_type_interface)98 OMAPI_OBJECT_ALLOC (interface, struct interface_info, dhcp_type_interface)
99
100 isc_result_t interface_setup ()
101 {
102 isc_result_t status;
103 status = omapi_object_type_register (&dhcp_type_interface,
104 "interface",
105 dhcp_interface_set_value,
106 dhcp_interface_get_value,
107 dhcp_interface_destroy,
108 dhcp_interface_signal_handler,
109 dhcp_interface_stuff_values,
110 dhcp_interface_lookup,
111 dhcp_interface_create,
112 dhcp_interface_remove,
113 0, 0, 0,
114 sizeof (struct interface_info),
115 interface_initialize, RC_MISC);
116 if (status != ISC_R_SUCCESS)
117 log_fatal ("Can't register interface object type: %s",
118 isc_result_totext (status));
119
120 return status;
121 }
122
123 #if defined (TRACING)
interface_trace_setup()124 void interface_trace_setup ()
125 {
126 interface_trace = trace_type_register ("interface", (void *)0,
127 trace_interface_input,
128 trace_interface_stop, MDL);
129 inpacket_trace = trace_type_register ("inpacket", (void *)0,
130 trace_inpacket_input,
131 trace_inpacket_stop, MDL);
132 outpacket_trace = trace_type_register ("outpacket", (void *)0,
133 trace_outpacket_input,
134 trace_outpacket_stop, MDL);
135 }
136 #endif
137
interface_initialize(omapi_object_t * ipo,const char * file,int line)138 isc_result_t interface_initialize (omapi_object_t *ipo,
139 const char *file, int line)
140 {
141 struct interface_info *ip = (struct interface_info *)ipo;
142 ip -> rfdesc = ip -> wfdesc = -1;
143 return ISC_R_SUCCESS;
144 }
145
146
147 /*
148 * Scanning for Interfaces
149 * -----------------------
150 *
151 * To find interfaces, we create an iterator that abstracts out most
152 * of the platform specifics. Use is fairly straightforward:
153 *
154 * - begin_iface_scan() starts the process.
155 * - Use next_iface() until it returns 0.
156 * - end_iface_scan() performs any necessary cleanup.
157 *
158 * We check for errors on each call to next_iface(), which returns a
159 * description of the error as a string if any occurs.
160 *
161 * We currently have code for Solaris and Linux. Other systems need
162 * to have code written.
163 *
164 * NOTE: the long-term goal is to use the interface code from BIND 9.
165 */
166
167 #if defined(SIOCGLIFCONF) && defined(SIOCGLIFNUM) && defined(SIOCGLIFFLAGS)
168
169 /* HP/UX doesn't define struct lifconf, instead they define struct
170 * if_laddrconf. Similarly, 'struct lifreq' and 'struct lifaddrreq'.
171 */
172 #ifdef ISC_PLATFORM_HAVEIF_LADDRCONF
173 # define lifc_len iflc_len
174 # define lifc_buf iflc_buf
175 # define lifc_req iflc_req
176 # define LIFCONF if_laddrconf
177 #else
178 # define ISC_HAVE_LIFC_FAMILY 1
179 # define ISC_HAVE_LIFC_FLAGS 1
180 # define LIFCONF lifconf
181 #endif
182
183 #ifdef ISC_PLATFORM_HAVEIF_LADDRREQ
184 # define lifr_addr iflr_addr
185 # define lifr_name iflr_name
186 # define lifr_dstaddr iflr_dstaddr
187 # define lifr_flags iflr_flags
188 # define sockaddr_storage sockaddr_ext
189 # define ss_family sa_family
190 # define LIFREQ if_laddrreq
191 #else
192 # define LIFREQ lifreq
193 #endif
194
195 #ifndef IF_NAMESIZE
196 # if defined(LIFNAMSIZ)
197 # define IF_NAMESIZE LIFNAMSIZ
198 # elif defined(IFNAMSIZ)
199 # define IF_NAMESIZE IFNAMSIZ
200 # else
201 # define IF_NAMESIZE 16
202 # endif
203 #endif
204 #elif !defined(__linux) && !defined(HAVE_IFADDRS_H)
205 # define SIOCGLIFCONF SIOCGIFCONF
206 # define SIOCGLIFFLAGS SIOCGIFFLAGS
207 # define LIFREQ ifreq
208 # define LIFCONF ifconf
209 # define lifr_name ifr_name
210 # define lifr_addr ifr_addr
211 # define lifr_flags ifr_flags
212 # define lifc_len ifc_len
213 # define lifc_buf ifc_buf
214 # define lifc_req ifc_req
215 #ifdef _AIX
216 # define ss_family __ss_family
217 #endif
218 #endif
219
220 #if defined(SIOCGLIFCONF) && defined(SIOCGLIFFLAGS)
221 /*
222 * Solaris support
223 * ---------------
224 *
225 * The SIOCGLIFCONF ioctl() are the extension that you need to use
226 * on Solaris to get information about IPv6 addresses.
227 *
228 * Solaris' extended interface is documented in the if_tcp man page.
229 */
230
231 /*
232 * Structure holding state about the scan.
233 */
234 struct iface_conf_list {
235 int sock; /* file descriptor used to get information */
236 int num; /* total number of interfaces */
237 struct LIFCONF conf; /* structure used to get information */
238 int next; /* next interface to retrieve when iterating */
239 };
240
241 /*
242 * Structure used to return information about a specific interface.
243 */
244 struct iface_info {
245 char name[IF_NAMESIZE+1]; /* name of the interface, e.g. "bge0" */
246 struct sockaddr_storage addr; /* address information */
247 isc_uint64_t flags; /* interface flags, e.g. IFF_LOOPBACK */
248 };
249
250 /*
251 * Start a scan of interfaces.
252 *
253 * The iface_conf_list structure maintains state for this process.
254 */
255 static int
begin_iface_scan(struct iface_conf_list * ifaces)256 begin_iface_scan(struct iface_conf_list *ifaces) {
257 #ifdef ISC_PLATFORM_HAVELIFNUM
258 struct lifnum lifnum;
259 #else
260 int lifnum;
261 #endif
262
263 ifaces->sock = socket(local_family, SOCK_DGRAM, IPPROTO_UDP);
264 if (ifaces->sock < 0) {
265 log_error("Error creating socket to list interfaces; %m");
266 return 0;
267 }
268
269 memset(&lifnum, 0, sizeof(lifnum));
270 #ifdef ISC_PLATFORM_HAVELIFNUM
271 lifnum.lifn_family = AF_UNSPEC;
272 #endif
273 #ifdef SIOCGLIFNUM
274 if (ioctl(ifaces->sock, SIOCGLIFNUM, &lifnum) < 0) {
275 log_error("Error finding total number of interfaces; %m");
276 close(ifaces->sock);
277 ifaces->sock = -1;
278 return 0;
279 }
280
281 #ifdef ISC_PLATFORM_HAVELIFNUM
282 ifaces->num = lifnum.lifn_count;
283 #else
284 ifaces->num = lifnum;
285 #endif
286 #else
287 ifaces->num = 64;
288 #endif /* SIOCGLIFNUM */
289
290 memset(&ifaces->conf, 0, sizeof(ifaces->conf));
291 #ifdef ISC_HAVE_LIFC_FAMILY
292 ifaces->conf.lifc_family = AF_UNSPEC;
293 #endif
294 ifaces->conf.lifc_len = ifaces->num * sizeof(struct LIFREQ);
295 ifaces->conf.lifc_buf = dmalloc(ifaces->conf.lifc_len, MDL);
296 if (ifaces->conf.lifc_buf == NULL) {
297 log_fatal("Out of memory getting interface list.");
298 }
299
300 if (ioctl(ifaces->sock, SIOCGLIFCONF, &ifaces->conf) < 0) {
301 log_error("Error getting interfaces configuration list; %m");
302 dfree(ifaces->conf.lifc_buf, MDL);
303 close(ifaces->sock);
304 ifaces->sock = -1;
305 return 0;
306 }
307
308 ifaces->next = 0;
309
310 return 1;
311 }
312
313 /*
314 * Retrieve the next interface.
315 *
316 * Returns information in the info structure.
317 * Sets err to 1 if there is an error, otherwise 0.
318 */
319 static int
next_iface(struct iface_info * info,int * err,struct iface_conf_list * ifaces)320 next_iface(struct iface_info *info, int *err, struct iface_conf_list *ifaces) {
321 struct LIFREQ *p;
322 struct LIFREQ tmp;
323 isc_boolean_t foundif;
324 #if defined(sun) || defined(__linux)
325 /* Pointer used to remove interface aliases. */
326 char *s;
327 #endif
328
329 do {
330 foundif = ISC_FALSE;
331
332 if (ifaces->next >= ifaces->num) {
333 *err = 0;
334 return 0;
335 }
336
337 p = ifaces->conf.lifc_req;
338 p += ifaces->next;
339
340 if (strlen(p->lifr_name) >= sizeof(info->name)) {
341 *err = 1;
342 log_error("Interface name '%s' too long", p->lifr_name);
343 return 0;
344 }
345
346 /* Reject if interface address family does not match */
347 if (p->lifr_addr.ss_family != local_family) {
348 ifaces->next++;
349 continue;
350 }
351
352 memset(info, 0, sizeof(struct iface_info));
353 strncpy(info->name, p->lifr_name, sizeof(info->name) - 1);
354 memcpy(&info->addr, &p->lifr_addr, sizeof(p->lifr_addr));
355
356 #if defined(sun) || defined(__linux)
357 /* interface aliases look like "eth0:1" or "wlan1:3" */
358 s = strchr(info->name, ':');
359 if (s != NULL) {
360 *s = '\0';
361 }
362 #endif /* defined(sun) || defined(__linux) */
363
364 foundif = ISC_TRUE;
365 } while ((foundif == ISC_FALSE) ||
366 (strncmp(info->name, "dummy", 5) == 0));
367
368 memset(&tmp, 0, sizeof(tmp));
369 strncpy(tmp.lifr_name, info->name, sizeof(tmp.lifr_name) - 1);
370 if (ioctl(ifaces->sock, SIOCGLIFFLAGS, &tmp) < 0) {
371 log_error("Error getting interface flags for '%s'; %m",
372 p->lifr_name);
373 *err = 1;
374 return 0;
375 }
376 info->flags = tmp.lifr_flags;
377
378 ifaces->next++;
379 *err = 0;
380 return 1;
381 }
382
383 /*
384 * End scan of interfaces.
385 */
386 static void
end_iface_scan(struct iface_conf_list * ifaces)387 end_iface_scan(struct iface_conf_list *ifaces) {
388 dfree(ifaces->conf.lifc_buf, MDL);
389 close(ifaces->sock);
390 ifaces->sock = -1;
391 }
392
393 #else
394
395 /*
396 * BSD/Linux support
397 * -----------
398 *
399 * FreeBSD, NetBSD, OpenBSD, OS X/macOS and Linux all have the getifaddrs()
400 * function.
401 *
402 * The getifaddrs() man page describes the use.
403 */
404
405 #include <ifaddrs.h>
406
407 /*
408 * Structure holding state about the scan.
409 */
410 struct iface_conf_list {
411 struct ifaddrs *head; /* beginning of the list */
412 struct ifaddrs *next; /* current position in the list */
413 };
414
415 /*
416 * Structure used to return information about a specific interface.
417 */
418 struct iface_info {
419 char name[IFNAMSIZ]; /* name of the interface, e.g. "bge0" */
420 struct sockaddr_storage addr; /* address information */
421 isc_uint64_t flags; /* interface flags, e.g. IFF_LOOPBACK */
422 };
423
424 /*
425 * Start a scan of interfaces.
426 *
427 * The iface_conf_list structure maintains state for this process.
428 */
429 static int
begin_iface_scan(struct iface_conf_list * ifaces)430 begin_iface_scan(struct iface_conf_list *ifaces) {
431 if (getifaddrs(&ifaces->head) != 0) {
432 log_error("Error getting interfaces; %m");
433 return 0;
434 }
435 ifaces->next = ifaces->head;
436 return 1;
437 }
438
439 /*
440 * Retrieve the next interface.
441 *
442 * Returns information in the info structure.
443 * Sets err to 1 if there is an error, otherwise 0.
444 */
445 static int
next_iface(struct iface_info * info,int * err,struct iface_conf_list * ifaces)446 next_iface(struct iface_info *info, int *err, struct iface_conf_list *ifaces) {
447 size_t sa_len = 0;
448
449 if (ifaces->next == NULL) {
450 *err = 0;
451 return 0;
452 }
453 if (strlen(ifaces->next->ifa_name) >= sizeof(info->name)) {
454 log_error("Interface name '%s' too long",
455 ifaces->next->ifa_name);
456 *err = 1;
457 return 0;
458 }
459 memset(info, 0, sizeof(struct iface_info));
460 strncpy(info->name, ifaces->next->ifa_name, sizeof(info->name) - 1);
461 memset(&info->addr, 0 , sizeof(info->addr));
462 /*
463 * getifaddrs() can on Linux with some interfaces like PPP or TEQL
464 * result in a record with no address (ifa_addr).
465 */
466 if (ifaces->next->ifa_addr != NULL) {
467 /* Linux lacks the sa_len member in struct sockaddr. */
468 #if defined(__linux)
469 if (ifaces->next->ifa_addr->sa_family == AF_INET)
470 sa_len = sizeof(struct sockaddr_in);
471 else if (ifaces->next->ifa_addr->sa_family == AF_INET6)
472 sa_len = sizeof(struct sockaddr_in6);
473 #else
474 sa_len = ifaces->next->ifa_addr->sa_len;
475 #endif
476 memcpy(&info->addr, ifaces->next->ifa_addr, sa_len);
477 }
478 info->flags = ifaces->next->ifa_flags;
479 ifaces->next = ifaces->next->ifa_next;
480 *err = 0;
481 return 1;
482 }
483
484 /*
485 * End scan of interfaces.
486 */
487 static void
end_iface_scan(struct iface_conf_list * ifaces)488 end_iface_scan(struct iface_conf_list *ifaces) {
489 freeifaddrs(ifaces->head);
490 ifaces->head = NULL;
491 ifaces->next = NULL;
492 }
493 #endif
494
495 /* XXX: perhaps create drealloc() rather than do it manually */
496 static void
add_ipv4_addr_to_interface(struct interface_info * iface,const struct in_addr * addr)497 add_ipv4_addr_to_interface(struct interface_info *iface,
498 const struct in_addr *addr) {
499 /*
500 * We don't expect a lot of addresses per IPv4 interface, so
501 * we use 4, as our "chunk size" for collecting addresses.
502 */
503 if (iface->addresses == NULL) {
504 iface->addresses = dmalloc(4 * sizeof(struct in_addr), MDL);
505 if (iface->addresses == NULL) {
506 log_fatal("Out of memory saving IPv4 address "
507 "on interface.");
508 }
509 iface->address_count = 0;
510 iface->address_max = 4;
511 } else if (iface->address_count >= iface->address_max) {
512 struct in_addr *tmp;
513 int new_max;
514
515 new_max = iface->address_max + 4;
516 tmp = dmalloc(new_max * sizeof(struct in_addr), MDL);
517 if (tmp == NULL) {
518 log_fatal("Out of memory saving IPv4 address "
519 "on interface.");
520 }
521 memcpy(tmp,
522 iface->addresses,
523 iface->address_max * sizeof(struct in_addr));
524 dfree(iface->addresses, MDL);
525 iface->addresses = tmp;
526 iface->address_max = new_max;
527 }
528 iface->addresses[iface->address_count++] = *addr;
529 }
530
531 #ifdef DHCPv6
532 /* XXX: perhaps create drealloc() rather than do it manually */
533 static void
add_ipv6_addr_to_interface(struct interface_info * iface,const struct in6_addr * addr)534 add_ipv6_addr_to_interface(struct interface_info *iface,
535 const struct in6_addr *addr) {
536 /*
537 * Each IPv6 interface will have at least two IPv6 addresses,
538 * and likely quite a few more. So we use 8, as our "chunk size" for
539 * collecting addresses.
540 */
541 if (iface->v6addresses == NULL) {
542 iface->v6addresses = dmalloc(8 * sizeof(struct in6_addr), MDL);
543 if (iface->v6addresses == NULL) {
544 log_fatal("Out of memory saving IPv6 address "
545 "on interface.");
546 }
547 iface->v6address_count = 0;
548 iface->v6address_max = 8;
549 } else if (iface->v6address_count >= iface->v6address_max) {
550 struct in6_addr *tmp;
551 int new_max;
552
553 new_max = iface->v6address_max + 8;
554 tmp = dmalloc(new_max * sizeof(struct in6_addr), MDL);
555 if (tmp == NULL) {
556 log_fatal("Out of memory saving IPv6 address "
557 "on interface.");
558 }
559 memcpy(tmp,
560 iface->v6addresses,
561 iface->v6address_max * sizeof(struct in6_addr));
562 dfree(iface->v6addresses, MDL);
563 iface->v6addresses = tmp;
564 iface->v6address_max = new_max;
565 }
566 iface->v6addresses[iface->v6address_count++] = *addr;
567 }
568 #endif /* DHCPv6 */
569
570 /* Use the SIOCGIFCONF ioctl to get a list of all the attached interfaces.
571 For each interface that's of type INET and not the loopback interface,
572 register that interface with the network I/O software, figure out what
573 subnet it's on, and add it to the list of interfaces. */
574
575 void
discover_interfaces(int state)576 discover_interfaces(int state) {
577 struct iface_conf_list ifaces;
578 struct iface_info info;
579 int err;
580
581 struct interface_info *tmp;
582 struct interface_info *last, *next;
583
584 #ifdef DHCPv6
585 char abuf[sizeof("ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255")];
586 #endif /* DHCPv6 */
587
588
589 struct subnet *subnet;
590 int ir;
591 isc_result_t status;
592 int wifcount = 0;
593 #ifdef RELAY_PORT
594 int updone = 0;
595 int downdone = 0;
596 #endif
597
598 static int setup_fallback = 0;
599
600 if (!begin_iface_scan(&ifaces)) {
601 log_fatal("Can't get list of interfaces.");
602 }
603
604 /* If we already have a list of interfaces, and we're running as
605 a DHCP server, the interfaces were requested. */
606 if (interfaces && (state == DISCOVER_SERVER ||
607 state == DISCOVER_RELAY ||
608 state == DISCOVER_REQUESTED))
609 ir = 0;
610 else if (state == DISCOVER_UNCONFIGURED)
611 ir = INTERFACE_REQUESTED | INTERFACE_AUTOMATIC;
612 else {
613 ir = INTERFACE_REQUESTED;
614 if (state == DISCOVER_RELAY && local_family == AF_INET) {
615 /* We're a v4 relay without specifically requested
616 * interfaces, so mark them all as bidirectional. */
617 ir |= INTERFACE_STREAMS;
618 }
619 }
620
621 /* Cycle through the list of interfaces looking for IP addresses. */
622 while (next_iface(&info, &err, &ifaces)) {
623
624 /* See if we've seen an interface that matches this one. */
625 for (tmp = interfaces; tmp; tmp = tmp->next) {
626 if (!strcmp(tmp->name, info.name))
627 break;
628 }
629
630 /* Skip non broadcast interfaces (plus loopback and
631 point-to-point in case an OS incorrectly marks them
632 as broadcast). Also skip down interfaces unless we're
633 trying to get a list of configurable interfaces. */
634 if ((((local_family == AF_INET &&
635 !(info.flags & IFF_BROADCAST)) ||
636 #ifdef DHCPv6
637 (local_family == AF_INET6 &&
638 !(info.flags & IFF_MULTICAST)) ||
639 #endif
640 info.flags & IFF_LOOPBACK ||
641 info.flags & IFF_POINTOPOINT) && !tmp) ||
642 (!(info.flags & IFF_UP) &&
643 state != DISCOVER_UNCONFIGURED))
644 continue;
645
646 /* If there isn't already an interface by this name,
647 allocate one. */
648 if (tmp == NULL) {
649 status = interface_allocate(&tmp, MDL);
650 if (status != ISC_R_SUCCESS) {
651 log_fatal("Error allocating interface %s: %s",
652 info.name, isc_result_totext(status));
653 }
654
655 memcpy(tmp->name, info.name, sizeof(tmp->name));
656
657 interface_snorf(tmp, ir);
658 interface_dereference(&tmp, MDL);
659 tmp = interfaces; /* XXX */
660 }
661
662 if (dhcp_interface_discovery_hook) {
663 (*dhcp_interface_discovery_hook)(tmp);
664 }
665
666 if ((info.addr.ss_family == AF_INET) &&
667 (local_family == AF_INET)) {
668 struct sockaddr_in *a = (struct sockaddr_in*)&info.addr;
669 struct iaddr addr;
670
671 /* We don't want the loopback interface. */
672 if (a->sin_addr.s_addr == htonl(INADDR_LOOPBACK) &&
673 ((tmp->flags & INTERFACE_AUTOMATIC) &&
674 ((state == DISCOVER_SERVER) ||
675 (state == DISCOVER_SERVER46))))
676 continue;
677
678 /* If the only address we have is 0.0.0.0, we
679 shouldn't consider the interface configured. */
680 if (a->sin_addr.s_addr != htonl(INADDR_ANY))
681 tmp->configured = 1;
682
683 add_ipv4_addr_to_interface(tmp, &a->sin_addr);
684
685 /* invoke the setup hook */
686 addr.len = 4;
687 memcpy(addr.iabuf, &a->sin_addr.s_addr, addr.len);
688 if (dhcp_interface_setup_hook) {
689 (*dhcp_interface_setup_hook)(tmp, &addr);
690 }
691 }
692 #ifdef DHCPv6
693 else if ((info.addr.ss_family == AF_INET6) &&
694 (local_family == AF_INET6)) {
695 struct sockaddr_in6 *a =
696 (struct sockaddr_in6*)&info.addr;
697 struct iaddr addr;
698
699 /* We don't want the loopback interface. */
700 if (IN6_IS_ADDR_LOOPBACK(&a->sin6_addr) &&
701 ((tmp->flags & INTERFACE_AUTOMATIC) &&
702 ((state == DISCOVER_SERVER) ||
703 (state == DISCOVER_SERVER46))))
704 continue;
705
706 /* If the only address we have is 0.0.0.0, we
707 shouldn't consider the interface configured. */
708 if (IN6_IS_ADDR_UNSPECIFIED(&a->sin6_addr))
709 tmp->configured = 1;
710
711 add_ipv6_addr_to_interface(tmp, &a->sin6_addr);
712
713 /* invoke the setup hook */
714 addr.len = 16;
715 memcpy(addr.iabuf, &a->sin6_addr, addr.len);
716 if (dhcp_interface_setup_hook) {
717 (*dhcp_interface_setup_hook)(tmp, &addr);
718 }
719 }
720 #endif /* DHCPv6 */
721 }
722
723 if (err) {
724 log_fatal("Error getting interface information.");
725 }
726
727 end_iface_scan(&ifaces);
728
729
730 /* Mock-up an 'ifp' structure which is no longer used in the
731 * new interface-sensing code, but is used in higher layers
732 * (for example to sense fallback interfaces).
733 */
734 for (tmp = interfaces ; tmp != NULL ; tmp = tmp->next) {
735 if (tmp->ifp == NULL) {
736 struct ifreq *tif;
737
738 tif = (struct ifreq *)dmalloc(sizeof(struct ifreq),
739 MDL);
740 if (tif == NULL)
741 log_fatal("no space for ifp mockup.");
742 strcpy(tif->ifr_name, tmp->name);
743 tmp->ifp = tif;
744 }
745 }
746
747
748 /* If we're just trying to get a list of interfaces that we might
749 be able to configure, we can quit now. */
750 if (state == DISCOVER_UNCONFIGURED) {
751 return;
752 }
753
754 /* Weed out the interfaces that did not have IP addresses. */
755 tmp = last = next = NULL;
756 if (interfaces)
757 interface_reference (&tmp, interfaces, MDL);
758 while (tmp) {
759 if (next)
760 interface_dereference (&next, MDL);
761 if (tmp -> next)
762 interface_reference (&next, tmp -> next, MDL);
763 /* skip interfaces that are running already */
764 if (tmp -> flags & INTERFACE_RUNNING) {
765 interface_dereference(&tmp, MDL);
766 if(next)
767 interface_reference(&tmp, next, MDL);
768 continue;
769 }
770 if ((tmp -> flags & INTERFACE_AUTOMATIC) &&
771 state == DISCOVER_REQUESTED)
772 tmp -> flags &= ~(INTERFACE_AUTOMATIC |
773 INTERFACE_REQUESTED);
774
775 #ifdef DHCPv6
776 if (!(tmp->flags & INTERFACE_REQUESTED)) {
777 #else
778 if (!tmp -> ifp || !(tmp -> flags & INTERFACE_REQUESTED)) {
779 #endif /* DHCPv6 */
780 if ((tmp -> flags & INTERFACE_REQUESTED) != ir)
781 log_fatal ("%s: not found", tmp -> name);
782 if (!last) {
783 if (interfaces)
784 interface_dereference (&interfaces,
785 MDL);
786 if (next)
787 interface_reference (&interfaces, next, MDL);
788 } else {
789 interface_dereference (&last -> next, MDL);
790 if (next)
791 interface_reference (&last -> next,
792 next, MDL);
793 }
794 if (tmp -> next)
795 interface_dereference (&tmp -> next, MDL);
796
797 /* Remember the interface in case we need to know
798 about it later. */
799 if (dummy_interfaces) {
800 interface_reference (&tmp -> next,
801 dummy_interfaces, MDL);
802 interface_dereference (&dummy_interfaces, MDL);
803 }
804 interface_reference (&dummy_interfaces, tmp, MDL);
805 interface_dereference (&tmp, MDL);
806 if (next)
807 interface_reference (&tmp, next, MDL);
808 continue;
809 }
810 last = tmp;
811
812 /* We must have a subnet declaration for each interface. */
813 if (!tmp->shared_network && (state == DISCOVER_SERVER)) {
814 log_error("%s", "");
815 if (local_family == AF_INET) {
816 log_error("No subnet declaration for %s (%s).",
817 tmp->name,
818 (tmp->addresses == NULL) ?
819 "no IPv4 addresses" :
820 inet_ntoa(tmp->addresses[0]));
821 #ifdef DHCPv6
822 } else {
823 if (tmp->v6addresses != NULL) {
824 inet_ntop(AF_INET6,
825 &tmp->v6addresses[0],
826 abuf,
827 sizeof(abuf));
828 } else {
829 strcpy(abuf, "no IPv6 addresses");
830 }
831 log_error("No subnet6 declaration for %s (%s).",
832 tmp->name,
833 abuf);
834 #endif /* DHCPv6 */
835 }
836 if (supports_multiple_interfaces(tmp)) {
837 log_error ("** Ignoring requests on %s. %s",
838 tmp -> name, "If this is not what");
839 log_error (" you want, please write %s",
840 #ifdef DHCPv6
841 (local_family != AF_INET) ?
842 "a subnet6 declaration" :
843 #endif
844 "a subnet declaration");
845 log_error (" in your dhcpd.conf file %s",
846 "for the network segment");
847 log_error (" to %s %s %s",
848 "which interface",
849 tmp -> name, "is attached. **");
850 log_error ("%s", "");
851 goto next;
852 } else {
853 log_error ("You must write a %s",
854 #ifdef DHCPv6
855 (local_family != AF_INET) ?
856 "subnet6 declaration for this" :
857 #endif
858 "subnet declaration for this");
859 log_error ("subnet. You cannot prevent %s",
860 "the DHCP server");
861 log_error ("from listening on this subnet %s",
862 "because your");
863 log_fatal ("operating system does not %s.",
864 "support this capability");
865 }
866 }
867
868 /* Find subnets that don't have valid interface
869 addresses... */
870 for (subnet = (tmp -> shared_network
871 ? tmp -> shared_network -> subnets
872 : (struct subnet *)0);
873 subnet; subnet = subnet -> next_sibling) {
874 /* Set the interface address for this subnet
875 to the first address we found. */
876 if (subnet->interface_address.len == 0) {
877 if (tmp->address_count > 0) {
878 subnet->interface_address.len = 4;
879 memcpy(subnet->interface_address.iabuf,
880 &tmp->addresses[0].s_addr, 4);
881 } else if (tmp->v6address_count > 0) {
882 subnet->interface_address.len = 16;
883 memcpy(subnet->interface_address.iabuf,
884 &tmp->v6addresses[0].s6_addr,
885 16);
886 } else {
887 /* XXX: should be one */
888 log_error("%s missing an interface "
889 "address", tmp->name);
890 continue;
891 }
892 }
893 }
894
895 /* Flag the index as not having been set, so that the
896 interface registerer can set it or not as it chooses. */
897 tmp -> index = -1;
898
899 /* Register the interface... */
900 switch (local_family) {
901 case AF_INET:
902 if (!dhcpv4_over_dhcpv6) {
903 if_register_receive(tmp);
904 if_register_send(tmp);
905 } else {
906 /* get_hw_addr() was called by register. */
907 get_hw_addr(tmp->name, &tmp->hw_address);
908 }
909 break;
910 #ifdef DHCPv6
911 case AF_INET6:
912 if ((state == DISCOVER_SERVER) ||
913 (state == DISCOVER_RELAY)) {
914 if_register6(tmp, 1);
915 } else if (state == DISCOVER_SERVER46) {
916 /* get_hw_addr() was called by if_register*6
917 so now we have to call it explicitly
918 to not leave the hardware address unknown
919 (some code expects it cannot be. */
920 get_hw_addr(tmp->name, &tmp->hw_address);
921 } else {
922 if_register_linklocal6(tmp);
923 }
924 break;
925 #endif /* DHCPv6 */
926 }
927
928 interface_stash (tmp);
929 wifcount++;
930 #if defined (F_SETFD)
931 /* if_register*() are no longer always called so
932 descriptors must be checked. */
933 if ((tmp -> rfdesc >= 0) &&
934 (fcntl (tmp -> rfdesc, F_SETFD, 1) < 0))
935 log_error ("Can't set close-on-exec on %s: %m",
936 tmp -> name);
937 if ((tmp -> wfdesc != tmp -> rfdesc) &&
938 (tmp -> wfdesc >= 0) &&
939 (fcntl (tmp -> wfdesc, F_SETFD, 1) < 0))
940 log_error ("Can't set close-on-exec on %s: %m",
941 tmp -> name);
942 #endif
943 next:
944 interface_dereference (&tmp, MDL);
945 if (next)
946 interface_reference (&tmp, next, MDL);
947 }
948
949 /*
950 * Now register all the remaining interfaces as protocols.
951 * We register with omapi to allow for control of the interface,
952 * we've already registered the fd or socket with the socket
953 * manager as part of if_register_receive().
954 */
955 for (tmp = interfaces; tmp; tmp = tmp -> next) {
956 /* not if it's been registered before */
957 if (tmp -> flags & INTERFACE_RUNNING)
958 continue;
959 if (tmp -> rfdesc == -1)
960 continue;
961 switch (local_family) {
962 #ifdef DHCPv6
963 case AF_INET6:
964 #ifdef RELAY_PORT
965 #define UPSTREAM(ifp) \
966 ((ifp->flags & INTERFACE_STREAMS) == INTERFACE_UPSTREAM)
967 #define DOWNSTREAM(ifp) \
968 ((ifp->flags & INTERFACE_STREAMS) == INTERFACE_DOWNSTREAM)
969
970 if (relay_port) {
971 /*
972 * The normal IPv6 relay only needs one
973 * socket as long as we find an interface.
974 * When user relay port is defined, and we
975 * have two different UDP ports. One to
976 * receive from DHCP client with port 547,
977 * and the other is user defined for sending
978 * to the server or upstream relay agent.
979 * Thus we need to register sockets for one
980 * upstream and one downstream interfaces.
981 */
982 if (updone && UPSTREAM(tmp))
983 continue;
984 if (downdone && DOWNSTREAM(tmp))
985 continue;
986 }
987 #endif
988 status = omapi_register_io_object((omapi_object_t *)tmp,
989 if_readsocket,
990 0, got_one_v6, 0, 0);
991 #ifdef RELAY_PORT
992 if (UPSTREAM(tmp))
993 updone++;
994 else
995 downdone++;
996 #endif
997 break;
998 #endif /* DHCPv6 */
999 case AF_INET:
1000 default:
1001 status = omapi_register_io_object((omapi_object_t *)tmp,
1002 if_readsocket,
1003 0, got_one, 0, 0);
1004 break;
1005 }
1006
1007 if (status != ISC_R_SUCCESS)
1008 log_fatal ("Can't register I/O handle for %s: %s",
1009 tmp -> name, isc_result_totext (status));
1010
1011 #if defined(DHCPv6)
1012 /* Only register the first interface for V6, since
1013 * servers and relays all use the same socket.
1014 * XXX: This has some messy side effects if we start
1015 * dynamically adding and removing interfaces, but
1016 * we're well beyond that point in terms of mess.
1017 */
1018 if (((state == DISCOVER_SERVER) || (state == DISCOVER_RELAY))
1019 && (local_family == AF_INET6)
1020 #if defined(RELAY_PORT)
1021 && ((relay_port == 0) || (updone && downdone))
1022 #endif
1023 )
1024 break;
1025 #endif
1026 } /* for (tmp = interfaces; ... */
1027
1028 if (state == DISCOVER_SERVER && wifcount == 0) {
1029 log_info ("%s", "");
1030 log_fatal ("Not configured to listen on any interfaces!");
1031 }
1032
1033 if ((local_family == AF_INET) &&
1034 !setup_fallback && !dhcpv4_over_dhcpv6) {
1035 setup_fallback = 1;
1036 maybe_setup_fallback();
1037 }
1038
1039 #if defined (F_SETFD)
1040 if (fallback_interface) {
1041 if (fcntl (fallback_interface -> rfdesc, F_SETFD, 1) < 0)
1042 log_error ("Can't set close-on-exec on fallback: %m");
1043 if (fallback_interface -> rfdesc != fallback_interface -> wfdesc) {
1044 if (fcntl (fallback_interface -> wfdesc, F_SETFD, 1) < 0)
1045 log_error ("Can't set close-on-exec on fallback: %m");
1046 }
1047 }
1048 #endif /* F_SETFD */
1049 }
1050
1051 int if_readsocket (h)
1052 omapi_object_t *h;
1053 {
1054 struct interface_info *ip;
1055
1056 if (h -> type != dhcp_type_interface)
1057 return -1;
1058 ip = (struct interface_info *)h;
1059 return ip -> rfdesc;
1060 }
1061
1062 int setup_fallback (struct interface_info **fp, const char *file, int line)
1063 {
1064 isc_result_t status;
1065
1066 status = interface_allocate (&fallback_interface, file, line);
1067 if (status != ISC_R_SUCCESS)
1068 log_fatal ("Error allocating fallback interface: %s",
1069 isc_result_totext (status));
1070 strcpy (fallback_interface -> name, "fallback");
1071 if (dhcp_interface_setup_hook)
1072 (*dhcp_interface_setup_hook) (fallback_interface,
1073 (struct iaddr *)0);
1074 status = interface_reference (fp, fallback_interface, file, line);
1075
1076 fallback_interface -> index = -1;
1077 interface_stash (fallback_interface);
1078 return status == ISC_R_SUCCESS;
1079 }
1080
1081 void reinitialize_interfaces ()
1082 {
1083 struct interface_info *ip;
1084
1085 for (ip = interfaces; ip; ip = ip -> next) {
1086 if_reinitialize_receive (ip);
1087 if_reinitialize_send (ip);
1088 }
1089
1090 if (fallback_interface)
1091 if_reinitialize_send (fallback_interface);
1092
1093 interfaces_invalidated = 1;
1094 }
1095
1096 isc_result_t got_one (h)
1097 omapi_object_t *h;
1098 {
1099 struct sockaddr_in from;
1100 struct hardware hfrom;
1101 struct iaddr ifrom;
1102 int result;
1103 union {
1104 unsigned char packbuf [4095]; /* Packet input buffer.
1105 Must be as large as largest
1106 possible MTU. */
1107 struct dhcp_packet packet;
1108 } u;
1109 struct interface_info *ip;
1110
1111 if (h -> type != dhcp_type_interface)
1112 return DHCP_R_INVALIDARG;
1113 ip = (struct interface_info *)h;
1114
1115 again:
1116 if ((result =
1117 receive_packet (ip, u.packbuf, sizeof u, &from, &hfrom)) < 0) {
1118 log_error ("receive_packet failed on %s: %m", ip -> name);
1119 return ISC_R_UNEXPECTED;
1120 }
1121 if (result == 0)
1122 return ISC_R_UNEXPECTED;
1123
1124 /*
1125 * If we didn't at least get the fixed portion of the BOOTP
1126 * packet, drop the packet.
1127 * Previously we allowed packets with no sname or filename
1128 * as we were aware of at least one client that did. But
1129 * a bug caused short packets to not work and nobody has
1130 * complained, it seems rational to tighten up that
1131 * restriction.
1132 */
1133 if (result < DHCP_FIXED_NON_UDP)
1134 return ISC_R_UNEXPECTED;
1135
1136 #if defined(IP_PKTINFO) && defined(IP_RECVPKTINFO) && defined(USE_V4_PKTINFO)
1137 {
1138 /* We retrieve the ifindex from the unused hfrom variable */
1139 unsigned int ifindex;
1140
1141 memcpy(&ifindex, hfrom.hbuf, sizeof (ifindex));
1142
1143 /*
1144 * Seek forward from the first interface to find the matching
1145 * source interface by interface index.
1146 */
1147 ip = interfaces;
1148 while ((ip != NULL) && (if_nametoindex(ip->name) != ifindex))
1149 ip = ip->next;
1150 if (ip == NULL)
1151 return ISC_R_NOTFOUND;
1152 }
1153 #endif
1154
1155 if (bootp_packet_handler) {
1156 ifrom.len = 4;
1157 memcpy (ifrom.iabuf, &from.sin_addr, ifrom.len);
1158
1159 (*bootp_packet_handler) (ip, &u.packet, (unsigned)result,
1160 from.sin_port, ifrom, &hfrom);
1161 }
1162
1163 /* If there is buffered data, read again. This is for, e.g.,
1164 bpf, which may return two packets at once. */
1165 if (ip -> rbuf_offset != ip -> rbuf_len)
1166 goto again;
1167 return ISC_R_SUCCESS;
1168 }
1169
1170 #ifdef DHCPv6
1171 isc_result_t
1172 got_one_v6(omapi_object_t *h) {
1173 struct sockaddr_in6 from;
1174 struct in6_addr to;
1175 struct iaddr ifrom;
1176 int result;
1177 char buf[65536]; /* maximum size for a UDP packet is 65536 */
1178 struct interface_info *ip;
1179 int is_unicast;
1180 unsigned int if_idx = 0;
1181
1182 if (h->type != dhcp_type_interface) {
1183 return DHCP_R_INVALIDARG;
1184 }
1185 ip = (struct interface_info *)h;
1186
1187 result = receive_packet6(ip, (unsigned char *)buf, sizeof(buf),
1188 &from, &to, &if_idx);
1189 if (result < 0) {
1190 log_error("receive_packet6() failed on %s: %m", ip->name);
1191 return ISC_R_UNEXPECTED;
1192 }
1193
1194 /* 0 is 'any' interface. */
1195 if (if_idx == 0)
1196 return ISC_R_NOTFOUND;
1197
1198 if (dhcpv6_packet_handler != NULL) {
1199 /*
1200 * If a packet is not multicast, we assume it is unicast.
1201 */
1202 if (IN6_IS_ADDR_MULTICAST(&to)) {
1203 is_unicast = ISC_FALSE;
1204 } else {
1205 is_unicast = ISC_TRUE;
1206 }
1207
1208 ifrom.len = 16;
1209 memcpy(ifrom.iabuf, &from.sin6_addr, ifrom.len);
1210
1211 /* Seek forward to find the matching source interface. */
1212 ip = interfaces;
1213 while ((ip != NULL) && (if_nametoindex(ip->name) != if_idx))
1214 ip = ip->next;
1215
1216 if (ip == NULL)
1217 return ISC_R_NOTFOUND;
1218
1219 (*dhcpv6_packet_handler)(ip, buf,
1220 result, from.sin6_port,
1221 &ifrom, is_unicast);
1222 }
1223
1224 return ISC_R_SUCCESS;
1225 }
1226 #endif /* DHCPv6 */
1227
1228 isc_result_t dhcp_interface_set_value (omapi_object_t *h,
1229 omapi_object_t *id,
1230 omapi_data_string_t *name,
1231 omapi_typed_data_t *value)
1232 {
1233 struct interface_info *interface;
1234 isc_result_t status;
1235
1236 if (h -> type != dhcp_type_interface)
1237 return DHCP_R_INVALIDARG;
1238 interface = (struct interface_info *)h;
1239
1240 if (!omapi_ds_strcmp (name, "name")) {
1241 if ((value -> type == omapi_datatype_data ||
1242 value -> type == omapi_datatype_string) &&
1243 value -> u.buffer.len < sizeof interface -> name) {
1244 memcpy (interface -> name,
1245 value -> u.buffer.value,
1246 value -> u.buffer.len);
1247 interface -> name [value -> u.buffer.len] = 0;
1248 } else
1249 return DHCP_R_INVALIDARG;
1250 return ISC_R_SUCCESS;
1251 }
1252
1253 /* Try to find some inner object that can take the value. */
1254 if (h -> inner && h -> inner -> type -> set_value) {
1255 status = ((*(h -> inner -> type -> set_value))
1256 (h -> inner, id, name, value));
1257 if (status == ISC_R_SUCCESS || status == DHCP_R_UNCHANGED)
1258 return status;
1259 }
1260
1261 return ISC_R_NOTFOUND;
1262 }
1263
1264
1265 isc_result_t dhcp_interface_get_value (omapi_object_t *h,
1266 omapi_object_t *id,
1267 omapi_data_string_t *name,
1268 omapi_value_t **value)
1269 {
1270 return ISC_R_NOTIMPLEMENTED;
1271 }
1272
1273 isc_result_t dhcp_interface_destroy (omapi_object_t *h,
1274 const char *file, int line)
1275 {
1276 struct interface_info *interface;
1277
1278 if (h -> type != dhcp_type_interface)
1279 return DHCP_R_INVALIDARG;
1280 interface = (struct interface_info *)h;
1281
1282 if (interface -> ifp) {
1283 dfree (interface -> ifp, file, line);
1284 interface -> ifp = 0;
1285 }
1286 if (interface -> next)
1287 interface_dereference (&interface -> next, file, line);
1288 if (interface -> rbuf) {
1289 dfree (interface -> rbuf, file, line);
1290 interface -> rbuf = (unsigned char *)0;
1291 }
1292 if (interface -> client)
1293 interface -> client = (struct client_state *)0;
1294
1295 if (interface -> shared_network)
1296 omapi_object_dereference ((void *)
1297 &interface -> shared_network, MDL);
1298
1299 return ISC_R_SUCCESS;
1300 }
1301
1302 isc_result_t dhcp_interface_signal_handler (omapi_object_t *h,
1303 const char *name, va_list ap)
1304 {
1305 struct interface_info *ip, *interface;
1306 isc_result_t status;
1307
1308 if (h -> type != dhcp_type_interface)
1309 return DHCP_R_INVALIDARG;
1310 interface = (struct interface_info *)h;
1311
1312 /* If it's an update signal, see if the interface is dead right
1313 now, or isn't known at all, and if that's the case, revive it. */
1314 if (!strcmp (name, "update")) {
1315 for (ip = dummy_interfaces; ip; ip = ip -> next)
1316 if (ip == interface)
1317 break;
1318 if (ip && dhcp_interface_startup_hook)
1319 return (*dhcp_interface_startup_hook) (ip);
1320
1321 for (ip = interfaces; ip; ip = ip -> next)
1322 if (ip == interface)
1323 break;
1324 if (!ip && dhcp_interface_startup_hook)
1325 return (*dhcp_interface_startup_hook) (ip);
1326 }
1327
1328 /* Try to find some inner object that can take the value. */
1329 if (h -> inner && h -> inner -> type -> signal_handler) {
1330 status = ((*(h -> inner -> type -> signal_handler))
1331 (h -> inner, name, ap));
1332 if (status == ISC_R_SUCCESS)
1333 return status;
1334 }
1335 return ISC_R_NOTFOUND;
1336 }
1337
1338 isc_result_t dhcp_interface_stuff_values (omapi_object_t *c,
1339 omapi_object_t *id,
1340 omapi_object_t *h)
1341 {
1342 struct interface_info *interface;
1343 isc_result_t status;
1344
1345 if (h -> type != dhcp_type_interface)
1346 return DHCP_R_INVALIDARG;
1347 interface = (struct interface_info *)h;
1348
1349 /* Write out all the values. */
1350
1351 status = omapi_connection_put_name (c, "state");
1352 if (status != ISC_R_SUCCESS)
1353 return status;
1354 if ((interface->flags & INTERFACE_REQUESTED) != 0)
1355 status = omapi_connection_put_string (c, "up");
1356 else
1357 status = omapi_connection_put_string (c, "down");
1358 if (status != ISC_R_SUCCESS)
1359 return status;
1360
1361 /* Write out the inner object, if any. */
1362 if (h -> inner && h -> inner -> type -> stuff_values) {
1363 status = ((*(h -> inner -> type -> stuff_values))
1364 (c, id, h -> inner));
1365 if (status == ISC_R_SUCCESS)
1366 return status;
1367 }
1368
1369 return ISC_R_SUCCESS;
1370 }
1371
1372 isc_result_t dhcp_interface_lookup (omapi_object_t **ip,
1373 omapi_object_t *id,
1374 omapi_object_t *ref)
1375 {
1376 omapi_value_t *tv = (omapi_value_t *)0;
1377 isc_result_t status;
1378 struct interface_info *interface;
1379
1380 if (!ref)
1381 return DHCP_R_NOKEYS;
1382
1383 /* First see if we were sent a handle. */
1384 status = omapi_get_value_str (ref, id, "handle", &tv);
1385 if (status == ISC_R_SUCCESS) {
1386 status = omapi_handle_td_lookup (ip, tv -> value);
1387
1388 omapi_value_dereference (&tv, MDL);
1389 if (status != ISC_R_SUCCESS)
1390 return status;
1391
1392 /* Don't return the object if the type is wrong. */
1393 if ((*ip) -> type != dhcp_type_interface) {
1394 omapi_object_dereference (ip, MDL);
1395 return DHCP_R_INVALIDARG;
1396 }
1397 }
1398
1399 /* Now look for an interface name. */
1400 status = omapi_get_value_str (ref, id, "name", &tv);
1401 if (status == ISC_R_SUCCESS) {
1402 char *s;
1403 unsigned len;
1404 for (interface = interfaces; interface;
1405 interface = interface -> next) {
1406 s = memchr (interface -> name, 0, IFNAMSIZ);
1407 if (s)
1408 len = s - &interface -> name [0];
1409 else
1410 len = IFNAMSIZ;
1411 if ((tv -> value -> u.buffer.len == len &&
1412 !memcmp (interface -> name,
1413 (char *)tv -> value -> u.buffer.value,
1414 len)))
1415 break;
1416 }
1417 if (!interface) {
1418 for (interface = dummy_interfaces;
1419 interface; interface = interface -> next) {
1420 s = memchr (interface -> name, 0, IFNAMSIZ);
1421 if (s)
1422 len = s - &interface -> name [0];
1423 else
1424 len = IFNAMSIZ;
1425 if ((tv -> value -> u.buffer.len == len &&
1426 !memcmp (interface -> name,
1427 (char *)
1428 tv -> value -> u.buffer.value,
1429 len)))
1430 break;
1431 }
1432 }
1433
1434 omapi_value_dereference (&tv, MDL);
1435 if (*ip && *ip != (omapi_object_t *)interface) {
1436 omapi_object_dereference (ip, MDL);
1437 return DHCP_R_KEYCONFLICT;
1438 } else if (!interface) {
1439 if (*ip)
1440 omapi_object_dereference (ip, MDL);
1441 return ISC_R_NOTFOUND;
1442 } else if (!*ip)
1443 omapi_object_reference (ip,
1444 (omapi_object_t *)interface,
1445 MDL);
1446 }
1447
1448 /* If we get to here without finding an interface, no valid key was
1449 specified. */
1450 if (!*ip)
1451 return DHCP_R_NOKEYS;
1452 return ISC_R_SUCCESS;
1453 }
1454
1455 /* actually just go discover the interface */
1456 isc_result_t dhcp_interface_create (omapi_object_t **lp,
1457 omapi_object_t *id)
1458 {
1459 struct interface_info *hp;
1460 isc_result_t status;
1461
1462 hp = (struct interface_info *)0;
1463 status = interface_allocate (&hp, MDL);
1464 if (status != ISC_R_SUCCESS)
1465 return status;
1466 hp -> flags = INTERFACE_REQUESTED;
1467 status = interface_reference ((struct interface_info **)lp, hp, MDL);
1468 interface_dereference (&hp, MDL);
1469 return status;
1470 }
1471
1472 isc_result_t dhcp_interface_remove (omapi_object_t *lp,
1473 omapi_object_t *id)
1474 {
1475 struct interface_info *interface, *ip, *last;
1476
1477 interface = (struct interface_info *)lp;
1478
1479 /* remove from interfaces */
1480 last = 0;
1481 for (ip = interfaces; ip; ip = ip -> next) {
1482 if (ip == interface) {
1483 if (last) {
1484 interface_dereference (&last -> next, MDL);
1485 if (ip -> next)
1486 interface_reference (&last -> next,
1487 ip -> next, MDL);
1488 } else {
1489 interface_dereference (&interfaces, MDL);
1490 if (ip -> next)
1491 interface_reference (&interfaces,
1492 ip -> next, MDL);
1493 }
1494 if (ip -> next)
1495 interface_dereference (&ip -> next, MDL);
1496 break;
1497 }
1498 last = ip;
1499 }
1500 if (!ip)
1501 return ISC_R_NOTFOUND;
1502
1503 /* add the interface to the dummy_interface list */
1504 if (dummy_interfaces) {
1505 interface_reference (&interface -> next,
1506 dummy_interfaces, MDL);
1507 interface_dereference (&dummy_interfaces, MDL);
1508 }
1509 interface_reference (&dummy_interfaces, interface, MDL);
1510
1511 /* do a DHCPRELEASE */
1512 if (dhcp_interface_shutdown_hook)
1513 (*dhcp_interface_shutdown_hook) (interface);
1514
1515 /* remove the io object */
1516 omapi_unregister_io_object ((omapi_object_t *)interface);
1517
1518 switch(local_family) {
1519 #ifdef DHCPv6
1520 case AF_INET6:
1521 if_deregister6(interface);
1522 break;
1523 #endif /* DHCPv6 */
1524 case AF_INET:
1525 default:
1526 if_deregister_send(interface);
1527 if_deregister_receive(interface);
1528 break;
1529 }
1530
1531 return ISC_R_SUCCESS;
1532 }
1533
1534 void interface_stash (struct interface_info *tptr)
1535 {
1536 struct interface_info **vec;
1537 int delta;
1538
1539 /* If the registerer didn't assign an index, assign one now. */
1540 if (tptr -> index == -1) {
1541 tptr -> index = interface_count++;
1542 while (tptr -> index < interface_max &&
1543 interface_vector [tptr -> index])
1544 tptr -> index = interface_count++;
1545 }
1546
1547 if (interface_max <= tptr -> index) {
1548 delta = tptr -> index - interface_max + 10;
1549 vec = dmalloc ((interface_max + delta) *
1550 sizeof (struct interface_info *), MDL);
1551 if (!vec) {
1552 log_error ("interface_stash: allocation failed ");
1553 return;
1554 }
1555
1556 memset (&vec [interface_max], 0,
1557 (sizeof (struct interface_info *)) * delta);
1558 interface_max += delta;
1559 if (interface_vector) {
1560 memcpy (vec, interface_vector,
1561 (interface_count *
1562 sizeof (struct interface_info *)));
1563 dfree (interface_vector, MDL);
1564 }
1565
1566 interface_vector = vec;
1567 }
1568
1569 interface_reference (&interface_vector [tptr -> index], tptr, MDL);
1570 if (tptr -> index >= interface_count)
1571 interface_count = tptr -> index + 1;
1572 #if defined (TRACING)
1573 trace_interface_register (interface_trace, tptr);
1574 #endif
1575 }
1576
1577 void interface_snorf (struct interface_info *tmp, int ir)
1578 {
1579 tmp -> circuit_id = (u_int8_t *)tmp -> name;
1580 tmp -> circuit_id_len = strlen (tmp -> name);
1581 tmp -> remote_id = 0;
1582 tmp -> remote_id_len = 0;
1583 tmp -> flags = ir;
1584 if (interfaces) {
1585 interface_reference (&tmp -> next,
1586 interfaces, MDL);
1587 interface_dereference (&interfaces, MDL);
1588 }
1589 interface_reference (&interfaces, tmp, MDL);
1590 }
1591