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