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