2
0
mirror of https://gitlab.isc.org/isc-projects/dhcp synced 2025-08-22 18:07:25 +00:00
isc-dhcp/common/discover.c

1839 lines
48 KiB
C
Raw Normal View History

2007-05-08 23:05:22 +00:00
/* discover.c
1998-11-06 00:19:56 +00:00
2007-05-08 23:05:22 +00:00
Find and identify the network interfaces. */
1998-11-06 00:19:56 +00:00
/*
* Copyright (c) 2004-2006 by Internet Systems Consortium, Inc. ("ISC")
* Copyright (c) 1995-2003 by Internet Software Consortium
1998-11-06 00:19:56 +00:00
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
1998-11-06 00:19:56 +00:00
*
* THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
* OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1998-11-06 00:19:56 +00:00
*
* Internet Systems Consortium, Inc.
* 950 Charter Street
* Redwood City, CA 94063
* <info@isc.org>
* http://www.isc.org/
2000-03-17 04:00:32 +00:00
*
* This software has been written for Internet Systems Consortium
2000-03-17 04:00:32 +00:00
* by Ted Lemon in cooperation with Vixie Enterprises and Nominum, Inc.
* To learn more about Internet Systems Consortium, see
2000-03-17 04:00:32 +00:00
* ``http://www.isc.org/''. To learn more about Vixie Enterprises,
* see ``http://www.vix.com''. To learn more about Nominum, Inc., see
* ``http://www.nominum.com''.
1998-11-06 00:19:56 +00:00
*/
#ifndef lint
static char copyright[] =
2007-05-08 23:05:22 +00:00
"$Id: discover.c,v 1.53 2007/05/08 23:05:20 dhankins Exp $ Copyright (c) 2004-2006 Internet Systems Consortium. All rights reserved.\n";
1998-11-06 00:19:56 +00:00
#endif /* not lint */
#include "dhcpd.h"
#include <sys/ioctl.h>
1999-02-14 19:04:05 +00:00
struct interface_info *interfaces, *dummy_interfaces, *fallback_interface;
int interfaces_invalidated;
1998-11-06 00:19:56 +00:00
int quiet_interface_discovery;
u_int16_t local_port;
u_int16_t remote_port;
int (*dhcp_interface_setup_hook) (struct interface_info *, struct iaddr *);
int (*dhcp_interface_discovery_hook) (struct interface_info *);
2001-04-05 20:50:19 +00:00
isc_result_t (*dhcp_interface_startup_hook) (struct interface_info *);
int (*dhcp_interface_shutdown_hook) (struct interface_info *);
struct in_addr limited_broadcast;
2007-05-08 23:05:22 +00:00
int local_family = AF_INET6;
struct in_addr local_address;
2007-05-08 23:05:22 +00:00
struct in6_addr local_address6;
1998-11-06 00:19:56 +00:00
void (*bootp_packet_handler) PROTO ((struct interface_info *,
struct dhcp_packet *, unsigned,
unsigned int,
1998-11-06 00:19:56 +00:00
struct iaddr, struct hardware *));
2007-05-08 23:05:22 +00:00
void (*dhcpv6_packet_handler)(struct interface_info *,
const char *, int,
int, const struct iaddr *,
isc_boolean_t);
1998-11-06 00:19:56 +00:00
1999-09-08 01:44:08 +00:00
omapi_object_type_t *dhcp_type_interface;
#if defined (TRACING)
trace_type_t *interface_trace;
trace_type_t *inpacket_trace;
trace_type_t *outpacket_trace;
#endif
struct interface_info **interface_vector;
int interface_count;
int interface_max;
1999-09-08 01:44:08 +00:00
OMAPI_OBJECT_ALLOC (interface, struct interface_info, dhcp_type_interface)
isc_result_t interface_setup ()
{
isc_result_t status;
status = omapi_object_type_register (&dhcp_type_interface,
"interface",
dhcp_interface_set_value,
dhcp_interface_get_value,
dhcp_interface_destroy,
dhcp_interface_signal_handler,
dhcp_interface_stuff_values,
dhcp_interface_lookup,
dhcp_interface_create,
dhcp_interface_remove,
0, 0, 0,
sizeof (struct interface_info),
interface_initialize, RC_MISC);
if (status != ISC_R_SUCCESS)
log_fatal ("Can't register interface object type: %s",
isc_result_totext (status));
return status;
}
#if defined (TRACING)
void interface_trace_setup ()
{
interface_trace = trace_type_register ("interface", (void *)0,
trace_interface_input,
trace_interface_stop, MDL);
inpacket_trace = trace_type_register ("inpacket", (void *)0,
trace_inpacket_input,
trace_inpacket_stop, MDL);
outpacket_trace = trace_type_register ("outpacket", (void *)0,
trace_outpacket_input,
trace_outpacket_stop, MDL);
}
#endif
isc_result_t interface_initialize (omapi_object_t *ipo,
const char *file, int line)
{
struct interface_info *ip = (struct interface_info *)ipo;
ip -> rfdesc = ip -> wfdesc = -1;
return ISC_R_SUCCESS;
}
1998-11-06 00:19:56 +00:00
2007-05-08 23:05:22 +00:00
/*
* Scanning for Interfaces
* -----------------------
*
* To find interfaces, we create an iterator that abstracts out most
* of the platform specifics. Use is fairly straightforward:
*
* - begin_iface_scan() starts the process.
* - Use next_iface() until it returns 0.
* - end_iface_scan() performs any necessary cleanup.
*
* We check for errors on each call to next_iface(), which returns a
* description of the error as a string if any occurs.
*
* We currently have code for Solaris and Linux. Other systems need
* to have code written.
*
* NOTE: the long-term goal is to use the interface code from BIND 9.
*/
#if defined(SIOCGLIFNUM)
/*
* Solaris support
* ---------------
*
* The SIOCGLIFCONF ioctl() are the extension that you need to use
* on Solaris to get information about IPv6 addresses.
*
* Solaris' extended interface is documented in the if_tcp man page.
*/
/*
* Structure holding state about the scan.
*/
struct iface_conf_list {
int sock; /* file descriptor used to get information */
int num; /* total number of interfaces */
struct lifconf conf; /* structure used to get information */
int next; /* next interface to retrieve when iterating */
};
/*
* Structure used to return information about a specific interface.
*/
struct iface_info {
char name[LIFNAMSIZ]; /* name of the interface, e.g. "bge0" */
struct sockaddr_storage addr; /* address information */
isc_uint64_t flags; /* interface flags, e.g. IFF_LOOPBACK */
};
/*
* Start a scan of interfaces.
*
* The iface_conf_list structure maintains state for this process.
*/
int
begin_iface_scan(struct iface_conf_list *ifaces) {
struct lifnum lifnum;
ifaces->sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
if (ifaces->sock < 0) {
log_error("Error creating socket to list interfaces; %m");
return 0;
}
memset(&lifnum, 0, sizeof(lifnum));
lifnum.lifn_family = AF_UNSPEC;
if (ioctl(ifaces->sock, SIOCGLIFNUM, &lifnum) < 0) {
log_error("Error finding total number of interfaces; %m");
close(ifaces->sock);
ifaces->sock = -1;
return 0;
}
ifaces->num = lifnum.lifn_count;
memset(&ifaces->conf, 0, sizeof(ifaces->conf));
ifaces->conf.lifc_family = AF_UNSPEC;
ifaces->conf.lifc_len = ifaces->num * sizeof(struct lifreq);
ifaces->conf.lifc_buf = dmalloc(ifaces->conf.lifc_len, MDL);
if (ifaces->conf.lifc_buf == NULL) {
log_fatal("Out of memory getting interface list.");
}
if (ioctl(ifaces->sock, SIOCGLIFCONF, &ifaces->conf) < 0) {
log_error("Error getting interfaces configuration list; %m");
dfree(ifaces->conf.lifc_buf, MDL);
close(ifaces->sock);
ifaces->sock = -1;
return 0;
}
ifaces->next = 0;
return 1;
}
/*
* Retrieve the next interface.
*
* Returns information in the info structure.
* Sets err to 1 if there is an error, otherwise 1.
*/
int
next_iface(struct iface_info *info, int *err, struct iface_conf_list *ifaces) {
struct lifreq *p;
struct lifreq tmp;
1998-11-06 00:19:56 +00:00
char *s;
1999-09-08 01:44:08 +00:00
2007-05-08 23:05:22 +00:00
do {
if (ifaces->next >= ifaces->num) {
*err = 0;
return 0;
}
1998-11-06 00:19:56 +00:00
2007-05-08 23:05:22 +00:00
p = ifaces->conf.lifc_req;
p += ifaces->next;
2007-05-08 23:05:22 +00:00
if (strlen(p->lifr_name) >= sizeof(info->name)) {
*err = 1;
log_error("Interface name '%s' too long", p->lifr_name);
return 0;
}
strcpy(info->name, p->lifr_name);
info->addr = p->lifr_addr;
2007-05-08 23:05:22 +00:00
#ifdef ALIAS_NAMES_PERMUTED
/* interface aliases look like "eth0:1" or "wlan1:3" */
s = strchr(info->name, ':');
if (s != NULL) {
*s = '\0';
}
#endif
#ifdef SKIP_DUMMY_INTERFACES
} while (strncmp(info->name, "dummy", 5) == 0);
2001-05-02 06:36:54 +00:00
#else
2007-05-08 23:05:22 +00:00
} while (0);
#endif
memset(&tmp, 0, sizeof(tmp));
strcpy(tmp.lifr_name, info->name);
if (ioctl(ifaces->sock, SIOCGLIFFLAGS, &tmp) < 0) {
log_error("Error getting interface flags for '%s'; %m",
p->lifr_name);
*err = 1;
return 0;
}
info->flags = tmp.lifr_flags;
ifaces->next++;
*err = 0;
return 1;
}
/*
* End scan of interfaces.
*/
void
end_iface_scan(struct iface_conf_list *ifaces) {
dfree(ifaces->conf.lifc_buf, MDL);
close(ifaces->sock);
ifaces->sock = -1;
}
#elif __linux /* !HAVE_SIOCGLIFCONF */
/*
* Linux support
* -------------
*
* In Linux, we use the /proc pseudo-filesystem to get information
* about interfaces, along with selected ioctl() calls.
*
* Linux low level access is documented in the netdevice man page.
*/
2007-05-08 23:05:22 +00:00
/*
* Structure holding state about the scan.
*/
struct iface_conf_list {
int sock; /* file descriptor used to get information */
FILE *fp; /* input from /proc/net/dev */
#ifdef DHCPv6
FILE *fp6; /* input from /proc/net/if_inet6 */
2001-05-02 06:36:54 +00:00
#endif
2007-05-08 23:05:22 +00:00
};
2007-05-08 23:05:22 +00:00
/*
* Structure used to return information about a specific interface.
*/
struct iface_info {
char name[IFNAMSIZ]; /* name of the interface, e.g. "eth0" */
struct sockaddr_storage addr; /* address information */
isc_uint64_t flags; /* interface flags, e.g. IFF_LOOPBACK */
};
/*
* Start a scan of interfaces.
*
* The iface_conf_list structure maintains state for this process.
*/
int
begin_iface_scan(struct iface_conf_list *ifaces) {
char buf[256];
int len;
int i;
1998-11-06 00:19:56 +00:00
2007-05-08 23:05:22 +00:00
ifaces->fp = fopen(PROCDEV_DEVICE, "r");
if (ifaces->fp == NULL) {
log_error("Error opening '%s' to list interfaces",
PROCDEV_DEVICE);
return 0;
}
1998-11-06 00:19:56 +00:00
2007-05-08 23:05:22 +00:00
/*
* The first 2 lines are header information, so read and ignore them.
*/
for (i=0; i<2; i++) {
if (fgets(buf, sizeof(buf), ifaces->fp) == NULL) {
log_error("Error reading headers from '%s'",
PROCDEV_DEVICE);
fclose(ifaces->fp);
ifaces->fp = NULL;
return 0;
}
len = strlen(buf);
if ((len <= 0) || (buf[len-1] != '\n')) {
log_error("Bad header line in '%s'", PROCDEV_DEVICE);
fclose(ifaces->fp);
ifaces->fp = NULL;
return 0;
}
}
ifaces->sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
if (ifaces->sock < 0) {
log_error("Error creating socket to list interfaces; %m");
fclose(ifaces->fp);
ifaces->fp = NULL;
return 0;
}
#ifdef DHCPv6
ifaces->fp6 = fopen("/proc/net/if_inet6", "r");
if (ifaces->fp6 == NULL) {
log_error("Error opening '/proc/net/if_inet6' to "
"list IPv6 interfaces; %m");
close(ifaces->sock);
ifaces->sock = -1;
fclose(ifaces->fp);
ifaces->fp = NULL;
return 0;
2001-05-17 19:04:09 +00:00
}
#endif
2007-05-08 23:05:22 +00:00
return 1;
}
/*
* Read our IPv4 interfaces from /proc/net/dev.
*
* The file looks something like this:
*
* Inter-| Receive ...
* face |bytes packets errs drop fifo frame ...
* lo: 1580562 4207 0 0 0 0 ...
* eth0: 0 0 0 0 0 0 ...
* eth1:1801552440 37895 0 14 0 ...
*
* We only care about the interface name, which is at the start of
* each line.
*
* We use an ioctl() to get the address and flags for each interface.
*/
static int
next_iface4(struct iface_info *info, int *err, struct iface_conf_list *ifaces) {
char buf[256];
int len;
char *p;
char *name;
struct ifreq tmp;
/*
* Loop exits when we find an interface that has an address, or
* when we run out of interfaces.
*/
for (;;) {
do {
/*
* Read the next line in the file.
*/
if (fgets(buf, sizeof(buf), ifaces->fp) == NULL) {
if (ferror(ifaces->fp)) {
*err = 1;
log_error("Error reading interface "
"information");
} else {
*err = 0;
}
return 0;
}
/*
* Make sure the line is a nice,
* newline-terminated line.
*/
len = strlen(buf);
if ((len <= 0) || (buf[len-1] != '\n')) {
log_error("Bad line reading interface "
"information");
*err = 1;
return 0;
}
/*
* Figure out our name.
*/
p = strrchr(buf, ':');
if (p == NULL) {
log_error("Bad line reading interface "
"information (no colon)");
*err = 1;
return 0;
}
*p = '\0';
name = buf;
while (isspace(*name)) {
name++;
}
/*
* Copy our name into our interface structure.
*/
len = p - name;
if (len >= sizeof(info->name)) {
*err = 1;
log_error("Interface name '%s' too long", name);
return 0;
}
strcpy(info->name, name);
#ifdef ALIAS_NAMED_PERMUTED
/* interface aliases look like "eth0:1" or "wlan1:3" */
s = strchr(info->name, ':');
if (s != NULL) {
*s = '\0';
}
2001-05-02 06:36:54 +00:00
#endif
2007-05-08 23:05:22 +00:00
#ifdef SKIP_DUMMY_INTERFACES
} while (strncmp(info->name, "dummy", 5) == 0);
#else
} while (0);
2001-05-02 06:36:54 +00:00
#endif
2007-05-08 23:05:22 +00:00
memset(&tmp, 0, sizeof(tmp));
strcpy(tmp.ifr_name, name);
if (ioctl(ifaces->sock, SIOCGIFADDR, &tmp) < 0) {
if (errno == EADDRNOTAVAIL) {
continue;
}
log_error("Error getting interface address "
"for '%s'; %m", name);
*err = 1;
return 0;
}
memcpy(&info->addr, &tmp.ifr_addr, sizeof(tmp.ifr_addr));
memset(&tmp, 0, sizeof(tmp));
strcpy(tmp.ifr_name, name);
if (ioctl(ifaces->sock, SIOCGIFFLAGS, &tmp) < 0) {
log_error("Error getting interface flags for '%s'; %m",
name);
*err = 1;
return 0;
}
info->flags = tmp.ifr_flags;
*err = 0;
return 1;
}
}
#ifdef DHCPv6
/*
* Read our IPv6 interfaces from /proc/net/if_inet6.
*
* The file looks something like this:
*
* fe80000000000000025056fffec00008 05 40 20 80 vmnet8
* 00000000000000000000000000000001 01 80 10 80 lo
* fe80000000000000025056fffec00001 06 40 20 80 vmnet1
* 200108881936000202166ffffe497d9b 03 40 00 00 eth1
* fe8000000000000002166ffffe497d9b 03 40 20 80 eth1
*
* We get IPv6 address from the start, the interface name from the end,
* and ioctl() to get flags.
*/
static int
next_iface6(struct iface_info *info, int *err, struct iface_conf_list *ifaces) {
char buf[256];
int len;
char *p;
char *name;
int i;
struct sockaddr_in6 addr;
struct ifreq tmp;
do {
/*
* Read the next line in the file.
*/
if (fgets(buf, sizeof(buf), ifaces->fp6) == NULL) {
if (ferror(ifaces->fp6)) {
*err = 1;
log_error("Error reading IPv6 "
"interface information");
} else {
*err = 0;
}
return 0;
}
/*
* Make sure the line is a nice, newline-terminated line.
*/
len = strlen(buf);
if ((len <= 0) || (buf[len-1] != '\n')) {
log_error("Bad line reading IPv6 "
"interface information");
*err = 1;
return 0;
}
/*
* Figure out our name.
*/
buf[--len] = '\0';
p = strrchr(buf, ' ');
if (p == NULL) {
log_error("Bad line reading IPv6 interface "
"information (no space)");
*err = 1;
return 0;
}
name = p+1;
/*
* Copy our name into our interface structure.
*/
len = strlen(name);
if (len >= sizeof(info->name)) {
*err = 1;
log_error("IPv6 interface name '%s' too long", name);
return 0;
}
strcpy(info->name, name);
#ifdef SKIP_DUMMY_INTERFACES
} while (strncmp(info->name, "dummy", 5) == 0);
#else
} while (0);
#endif
/*
* Double-check we start with the IPv6 address.
*/
for (i=0; i<32; i++) {
if (!isxdigit(buf[i]) || isupper(buf[i])) {
*err = 1;
log_error("Bad line reading IPv6 interface address "
"for '%s'", name);
return 0;
}
}
/*
* Load our socket structure.
*/
memset(&addr, 0, sizeof(addr));
addr.sin6_family = AF_INET6;
for (i=0; i<16; i++) {
unsigned char byte;
static const char hex[] = "0123456789abcdef";
byte = ((index(hex, buf[i * 2]) - hex) << 4) |
(index(hex, buf[i * 2 + 1]) - hex);
addr.sin6_addr.s6_addr[i] = byte;
}
memcpy(&info->addr, &addr, sizeof(addr));
/*
* Get our flags.
*/
memset(&tmp, 0, sizeof(tmp));
strcpy(tmp.ifr_name, name);
if (ioctl(ifaces->sock, SIOCGIFFLAGS, &tmp) < 0) {
log_error("Error getting interface flags for '%s'; %m", name);
*err = 1;
return 0;
}
info->flags = tmp.ifr_flags;
*err = 0;
return 1;
}
#endif /* DHCPv6 */
/*
* Retrieve the next interface.
*
* Returns information in the info structure.
* Sets err to 1 if there is an error, otherwise 1.
*/
int
next_iface(struct iface_info *info, int *err, struct iface_conf_list *ifaces) {
if (next_iface4(info, err, ifaces)) {
return 1;
}
#ifdef DHCPv6
if (!(*err)) {
return next_iface6(info, err, ifaces);
}
#endif
return 0;
}
/*
* End scan of interfaces.
*/
void
end_iface_scan(struct iface_conf_list *ifaces) {
fclose(ifaces->fp);
ifaces->fp = NULL;
close(ifaces->sock);
ifaces->sock = -1;
#ifdef DHCPv6
fclose(ifaces->fp6);
ifaces->fp6 = NULL;
#endif
}
#else
/* XXX: need to define non-Solaris, non-Linux iterators */
#endif
/* XXX: perhaps create drealloc() rather than do it manually */
void
add_ipv4_addr_to_interface(struct interface_info *iface,
const struct in_addr *addr) {
/*
* We don't expect a lot of addresses per IPv4 interface, so
* we use 4, as our "chunk size" for collecting addresses.
*/
if (iface->addresses == NULL) {
iface->addresses = dmalloc(4 * sizeof(struct in_addr), MDL);
if (iface->addresses == NULL) {
log_fatal("Out of memory saving IPv4 address "
"on interface.");
}
iface->address_count = 0;
iface->address_max = 4;
} else if (iface->address_count >= iface->address_max) {
struct in_addr *tmp;
int new_max;
new_max = iface->address_max + 4;
tmp = dmalloc(new_max * sizeof(struct in_addr), MDL);
if (tmp == NULL) {
log_fatal("Out of memory saving IPv4 address "
"on interface.");
}
memcpy(tmp,
iface->addresses,
iface->address_max * sizeof(struct in_addr));
dfree(iface->addresses, MDL);
iface->addresses = tmp;
iface->address_max = new_max;
}
iface->addresses[iface->address_count++] = *addr;
}
/* XXX: perhaps create drealloc() rather than do it manually */
void
add_ipv6_addr_to_interface(struct interface_info *iface,
const struct in6_addr *addr) {
/*
* Each IPv6 interface will have at least two IPv6 addresses,
* and likely quite a few more. So we use 8, as our "chunk size" for
* collecting addresses.
*/
if (iface->v6addresses == NULL) {
iface->v6addresses = dmalloc(8 * sizeof(struct in6_addr), MDL);
if (iface->v6addresses == NULL) {
log_fatal("Out of memory saving IPv6 address "
"on interface.");
}
iface->v6address_count = 0;
iface->v6address_max = 8;
} else if (iface->v6address_count >= iface->v6address_max) {
struct in6_addr *tmp;
int new_max;
new_max = iface->v6address_max + 8;
tmp = dmalloc(new_max * sizeof(struct in6_addr), MDL);
if (tmp == NULL) {
log_fatal("Out of memory saving IPv6 address "
"on interface.");
}
memcpy(tmp,
iface->v6addresses,
iface->v6address_max * sizeof(struct in6_addr));
dfree(iface->v6addresses, MDL);
iface->v6addresses = tmp;
iface->v6address_max = new_max;
}
iface->v6addresses[iface->v6address_count++] = *addr;
}
/* Use the SIOCGIFCONF ioctl to get a list of all the attached interfaces.
For each interface that's of type INET and not the loopback interface,
register that interface with the network I/O software, figure out what
subnet it's on, and add it to the list of interfaces. */
void
discover_interfaces(int state) {
struct iface_conf_list ifaces;
struct iface_info info;
int err;
struct interface_info *tmp, *ip;
struct interface_info *last, *next;
char abuf[sizeof("ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255")];
struct subnet *subnet;
int ir;
isc_result_t status;
int wifcount = 0;
static int setup_fallback = 0;
if (!begin_iface_scan(&ifaces)) {
log_fatal("Can't get list of interfaces.");
}
2001-05-02 06:36:54 +00:00
1998-11-06 00:19:56 +00:00
/* If we already have a list of interfaces, and we're running as
a DHCP server, the interfaces were requested. */
if (interfaces && (state == DISCOVER_SERVER ||
state == DISCOVER_RELAY ||
state == DISCOVER_REQUESTED))
ir = 0;
else if (state == DISCOVER_UNCONFIGURED)
ir = INTERFACE_REQUESTED | INTERFACE_AUTOMATIC;
else
ir = INTERFACE_REQUESTED;
1999-02-14 19:04:05 +00:00
/* Cycle through the list of interfaces looking for IP addresses. */
2007-05-08 23:05:22 +00:00
while (next_iface(&info, &err, &ifaces)) {
1998-11-06 00:19:56 +00:00
/* See if we've seen an interface that matches this one. */
2007-05-08 23:05:22 +00:00
for (tmp = interfaces; tmp; tmp = tmp->next) {
if (!strcmp(tmp->name, info.name))
break;
2007-05-08 23:05:22 +00:00
}
/* Skip non broadcast interfaces (plus loopback and
point-to-point in case an OS incorrectly marks them
as broadcast). Also skip down interfaces unless we're
trying to get a list of configurable interfaces. */
2007-05-08 23:05:22 +00:00
if (((!(info.flags & IFF_BROADCAST) ||
info.flags & IFF_LOOPBACK ||
info.flags & IFF_POINTOPOINT) && !tmp) ||
(!(info.flags & IFF_UP) &&
1998-11-06 00:19:56 +00:00
state != DISCOVER_UNCONFIGURED))
continue;
/* If there isn't already an interface by this name,
allocate one. */
2007-05-08 23:05:22 +00:00
if (tmp == NULL) {
status = interface_allocate(&tmp, MDL);
if (status != ISC_R_SUCCESS) {
log_fatal("Error allocating interface %s: %s",
info.name, isc_result_totext(status));
}
strcpy(tmp->name, info.name);
interface_snorf(tmp, ir);
interface_dereference(&tmp, MDL);
tmp = interfaces; /* XXX */
}
2007-05-08 23:05:22 +00:00
if (dhcp_interface_discovery_hook) {
(*dhcp_interface_discovery_hook)(tmp);
}
1998-11-06 00:19:56 +00:00
/* If we have the capability, extract link information
2007-05-08 23:05:22 +00:00
and record it in. */
#ifdef HAVE_AF_LINK
2007-05-08 23:05:22 +00:00
if (info.addr.ss_family == AF_LINK) {
struct sockaddr_dl *d = (struct sockaddr_dl*)&info.addr;
tmp->hw_address.hlen = d->sdl_alen;
tmp->hw_address.hbuf[0] = HTYPE_ETHER; /* XXX */
memcpy(&tmp->hw_address.hbuf[1],
LLADDR(d),
tmp->hw_address.hlen);
tmp->hw_address.hlen++; /* for type. */
1998-11-06 00:19:56 +00:00
} else
#endif /* AF_LINK */
2007-05-08 23:05:22 +00:00
if ((info.addr.ss_family == AF_INET) &&
(local_family == AF_INET)) {
struct sockaddr_in *a = (struct sockaddr_in*)&info.addr;
1998-11-06 00:19:56 +00:00
struct iaddr addr;
2007-05-08 23:05:22 +00:00
/* We don't want the loopback interface. */
if (a->sin_addr.s_addr == htonl(INADDR_LOOPBACK) &&
((tmp->flags & INTERFACE_AUTOMATIC) &&
state == DISCOVER_SERVER))
continue;
/* If the only address we have is 0.0.0.0, we
shouldn't consider the interface configured. */
if (a->sin_addr.s_addr != htonl(INADDR_ANY))
tmp->configured = 1;
add_ipv4_addr_to_interface(tmp, &a->sin_addr);
/*
* XXX: We don't have ifreq in Solaris-land if we want IPv6. Fortunately,
* we don't actually need this for anything on Solaris.
*/
#if 0
/* If this is the first real IP address we've
found, keep a pointer to ifreq structure in
which we found it. */
if (!tmp -> ifp) {
#ifdef HAVE_SA_LEN
unsigned len = ((sizeof ifp -> ifr_name) +
ifp -> ifr_addr.sa_len);
#else
unsigned len = sizeof *ifp;
#endif
tif = (struct ifreq *)dmalloc (len, MDL);
if (!tif)
log_fatal ("no space for ifp.");
memcpy (tif, ifp, len);
tmp -> ifp = tif;
}
#endif /* 0 */
/* invoke the setup hook */
addr.len = 4;
memcpy(addr.iabuf, &a->sin_addr.s_addr, addr.len);
if (dhcp_interface_setup_hook) {
(*dhcp_interface_setup_hook)(tmp, &addr);
}
}
else if ((info.addr.ss_family == AF_INET6) &&
(local_family == AF_INET6)) {
struct sockaddr_in6 *a =
(struct sockaddr_in6*)&info.addr;
struct iaddr addr;
1998-11-06 00:19:56 +00:00
/* We don't want the loopback interface. */
2007-05-08 23:05:22 +00:00
if (IN6_IS_ADDR_LOOPBACK(&a->sin6_addr) &&
((tmp->flags & INTERFACE_AUTOMATIC) &&
1999-09-08 01:44:08 +00:00
state == DISCOVER_SERVER))
continue;
1998-11-06 00:19:56 +00:00
2007-05-08 23:05:22 +00:00
/* If the only address we have is 0.0.0.0, we
shouldn't consider the interface configured. */
if (IN6_IS_ADDR_UNSPECIFIED(&a->sin6_addr))
tmp->configured = 1;
1998-11-06 00:19:56 +00:00
2007-05-08 23:05:22 +00:00
add_ipv6_addr_to_interface(tmp, &a->sin6_addr);
/*
* XXX: We don't have ifreq in Solaris-land if we want IPv6. Fortunately,
* we don't actually need this for anything on Solaris.
*/
#if 0
1998-11-06 00:19:56 +00:00
/* If this is the first real IP address we've
found, keep a pointer to ifreq structure in
which we found it. */
if (!tmp -> ifp) {
#ifdef HAVE_SA_LEN
unsigned len = ((sizeof ifp -> ifr_name) +
ifp -> ifr_addr.sa_len);
1998-11-06 00:19:56 +00:00
#else
unsigned len = sizeof *ifp;
1998-11-06 00:19:56 +00:00
#endif
tif = (struct ifreq *)dmalloc (len, MDL);
1998-11-06 00:19:56 +00:00
if (!tif)
1999-09-08 01:44:08 +00:00
log_fatal ("no space for ifp.");
1998-11-06 00:19:56 +00:00
memcpy (tif, ifp, len);
tmp -> ifp = tif;
tmp -> primary_address = foo.sin_addr;
1998-11-06 00:19:56 +00:00
}
2007-05-08 23:05:22 +00:00
#endif /* 0 */
1998-11-06 00:19:56 +00:00
2007-05-08 23:05:22 +00:00
/* invoke the setup hook */
addr.len = 16;
memcpy(addr.iabuf, &a->sin6_addr, addr.len);
if (dhcp_interface_setup_hook) {
(*dhcp_interface_setup_hook)(tmp, &addr);
}
1998-11-06 00:19:56 +00:00
}
}
2007-05-08 23:05:22 +00:00
if (err) {
log_fatal("Error getting interface information.");
}
end_iface_scan(&ifaces);
2007-05-08 23:05:22 +00:00
#if 0
1999-02-14 19:04:05 +00:00
#if defined (LINUX_SLASHPROC_DISCOVERY)
/* On Linux, interfaces that don't have IP addresses don't
show up in the SIOCGIFCONF syscall. This only matters for
the DHCP client, of course - the relay agent and server
should only care about interfaces that are configured with
IP addresses anyway.
1999-02-14 19:04:05 +00:00
The PROCDEV_DEVICE (/proc/net/dev) is a kernel-supplied file
that, when read, prints a human readable network status. We
extract the names of the network devices by skipping the first
two lines (which are header) and then parsing off everything
up to the colon in each subsequent line - these lines start
with the interface name, then a colon, then a bunch of
statistics. */
1999-02-14 19:04:05 +00:00
if (state == DISCOVER_UNCONFIGURED) {
FILE *proc_dev;
char buffer [256];
int skip = 2;
proc_dev = fopen (PROCDEV_DEVICE, "r");
if (!proc_dev)
log_fatal ("%s: %m", PROCDEV_DEVICE);
1999-02-14 19:04:05 +00:00
while (fgets (buffer, sizeof buffer, proc_dev)) {
char *name = buffer;
char *sep;
/* Skip the first two blocks, which are header
lines. */
if (skip) {
--skip;
continue;
}
sep = strrchr (buffer, ':');
if (sep)
*sep = '\0';
while (*name == ' ')
name++;
/* See if we've seen an interface that matches
this one. */
for (tmp = interfaces; tmp; tmp = tmp -> next)
if (!strcmp (tmp -> name, name))
break;
/* If we found one, nothing more to do.. */
if (tmp)
continue;
strncpy (ifr.ifr_name, name, IFNAMSIZ);
/* Skip non broadcast interfaces (plus loopback and
* point-to-point in case an OS incorrectly marks them
* as broadcast).
*/
if ((ioctl (sock, SIOCGIFFLAGS, &ifr) < 0) ||
(!(ifr.ifr_flags & IFF_BROADCAST)) ||
(ifr.ifr_flags & IFF_LOOPBACK ) ||
(ifr.ifr_flags & IFF_POINTOPOINT))
continue;
1999-02-14 19:04:05 +00:00
/* Otherwise, allocate one. */
tmp = (struct interface_info *)0;
status = interface_allocate (&tmp, MDL);
if (status != ISC_R_SUCCESS)
log_fatal ("Can't allocate interface %s: %s",
name, isc_result_totext (status));
1999-02-14 19:04:05 +00:00
tmp -> flags = ir;
strncpy (tmp -> name, name, IFNAMSIZ);
if (interfaces) {
interface_reference (&tmp -> next,
interfaces, MDL);
interface_dereference (&interfaces, MDL);
}
interface_reference (&interfaces, tmp, MDL);
interface_dereference (&tmp, MDL);
tmp = interfaces;
if (dhcp_interface_discovery_hook)
(*dhcp_interface_discovery_hook) (tmp);
1999-02-14 19:04:05 +00:00
}
fclose (proc_dev);
}
#endif
2007-05-08 23:05:22 +00:00
#endif /* 0 */
1999-02-14 19:04:05 +00:00
/* Now cycle through all the interfaces we found, looking for
hardware addresses. */
2007-05-08 23:05:22 +00:00
/* XXX: The dlpi interface code will get this information in Solaris */
#if 0
#if defined (HAVE_SIOCGIFHWADDR) && !defined (HAVE_AF_LINK)
2007-05-08 23:05:22 +00:00
for (tmp = interfaces; tmp != NULL; tmp = tmp->next) {
1999-02-14 19:04:05 +00:00
struct ifreq ifr;
struct sockaddr sa;
int b, sk;
if (!tmp -> ifp) {
/* Make up an ifreq structure. */
tif = (struct ifreq *)dmalloc (sizeof (struct ifreq),
MDL);
1999-02-14 19:04:05 +00:00
if (!tif)
log_fatal ("no space to remember ifp.");
1999-02-14 19:04:05 +00:00
memset (tif, 0, sizeof (struct ifreq));
strcpy (tif -> ifr_name, tmp -> name);
tmp -> ifp = tif;
}
/* Read the hardware address from this interface. */
ifr = *tmp -> ifp;
if (ioctl (sock, SIOCGIFHWADDR, &ifr) < 0)
continue;
sa = *(struct sockaddr *)&ifr.ifr_hwaddr;
switch (sa.sa_family) {
#ifdef HAVE_ARPHRD_TUNNEL
case ARPHRD_TUNNEL:
/* ignore tunnel interfaces. */
#endif
1999-03-26 19:19:46 +00:00
#ifdef HAVE_ARPHRD_ROSE
case ARPHRD_ROSE:
#endif
#ifdef HAVE_ARPHRD_IRDA
case ARPHRD_IRDA:
/* ignore infrared interfaces. */
#endif
#ifdef HAVE_ARPHRD_SIT
case ARPHRD_SIT:
/* ignore IPv6-in-IPv4 interfaces. */
#endif
#ifdef HAVE_ARPHRD_IEEE1394
case ARPHRD_IEEE1394:
/* ignore IEEE1394 interfaces. */
#endif
#ifdef HAVE_ARPHRD_LOOPBACK
1999-02-14 19:04:05 +00:00
case ARPHRD_LOOPBACK:
/* ignore loopback interface */
break;
#endif
case ARPHRD_ETHER:
2000-01-25 01:04:21 +00:00
tmp -> hw_address.hlen = 7;
tmp -> hw_address.hbuf [0] = ARPHRD_ETHER;
memcpy (&tmp -> hw_address.hbuf [1], sa.sa_data, 6);
1999-02-14 19:04:05 +00:00
break;
1999-03-26 19:19:46 +00:00
#ifndef HAVE_ARPHRD_IEEE802
1999-02-14 19:04:05 +00:00
# define ARPHRD_IEEE802 HTYPE_IEEE802
#endif
#if defined (HAVE_ARPHRD_IEEE802_TR)
case ARPHRD_IEEE802_TR:
1999-02-14 19:04:05 +00:00
#endif
case ARPHRD_IEEE802:
2000-01-25 01:04:21 +00:00
tmp -> hw_address.hlen = 7;
tmp -> hw_address.hbuf [0] = ARPHRD_IEEE802;
memcpy (&tmp -> hw_address.hbuf [1], sa.sa_data, 6);
1999-02-14 19:04:05 +00:00
break;
1999-03-26 19:19:46 +00:00
#ifndef HAVE_ARPHRD_FDDI
1999-02-14 19:04:05 +00:00
# define ARPHRD_FDDI HTYPE_FDDI
#endif
case ARPHRD_FDDI:
2000-01-25 01:04:21 +00:00
tmp -> hw_address.hlen = 17;
tmp -> hw_address.hbuf [0] = HTYPE_FDDI; /* XXX */
memcpy (&tmp -> hw_address.hbuf [1], sa.sa_data, 16);
1999-02-14 19:04:05 +00:00
break;
#ifdef HAVE_ARPHRD_METRICOM
1999-02-14 19:04:05 +00:00
case ARPHRD_METRICOM:
2000-01-25 01:04:21 +00:00
tmp -> hw_address.hlen = 7;
tmp -> hw_address.hbuf [0] = ARPHRD_METRICOM;
memcpy (&tmp -> hw_address.hbuf [0], sa.sa_data, 6);
1999-03-26 19:19:46 +00:00
break;
#endif
#ifdef HAVE_ARPHRD_AX25
case ARPHRD_AX25:
2000-01-25 01:04:21 +00:00
tmp -> hw_address.hlen = 7;
tmp -> hw_address.hbuf [0] = ARPHRD_AX25;
memcpy (&tmp -> hw_address.hbuf [1], sa.sa_data, 6);
1999-03-26 19:19:46 +00:00
break;
#endif
#ifdef HAVE_ARPHRD_NETROM
case ARPHRD_NETROM:
2000-01-25 01:04:21 +00:00
tmp -> hw_address.hlen = 7;
tmp -> hw_address.hbuf [0] = ARPHRD_NETROM;
memcpy (&tmp -> hw_address.hbuf [1], sa.sa_data, 6);
1999-02-14 19:04:05 +00:00
break;
#endif
default:
log_error ("%s: unknown hardware address type %d",
ifr.ifr_name, sa.sa_family);
break;
1999-02-14 19:04:05 +00:00
}
}
#endif /* defined (HAVE_SIOCGIFHWADDR) && !defined (HAVE_AF_LINK) */
2007-05-08 23:05:22 +00:00
#endif /* 0 */
1999-02-14 19:04:05 +00:00
1998-11-06 00:19:56 +00:00
/* If we're just trying to get a list of interfaces that we might
be able to configure, we can quit now. */
if (state == DISCOVER_UNCONFIGURED) {
1998-11-06 00:19:56 +00:00
return;
}
1998-11-06 00:19:56 +00:00
/* Weed out the interfaces that did not have IP addresses. */
2007-05-08 23:05:22 +00:00
tmp = last = next = NULL;
if (interfaces)
interface_reference (&tmp, interfaces, MDL);
while (tmp) {
if (next)
interface_dereference (&next, MDL);
if (tmp -> next)
interface_reference (&next, tmp -> next, MDL);
/* skip interfaces that are running already */
if (tmp -> flags & INTERFACE_RUNNING) {
interface_dereference(&tmp, MDL);
if(next)
interface_reference(&tmp, next, MDL);
continue;
}
1998-11-06 00:19:56 +00:00
if ((tmp -> flags & INTERFACE_AUTOMATIC) &&
state == DISCOVER_REQUESTED)
tmp -> flags &= ~(INTERFACE_AUTOMATIC |
INTERFACE_REQUESTED);
2007-05-08 23:05:22 +00:00
/* XXX: no ifp in Solaris */
/* if (!tmp -> ifp || !(tmp -> flags & INTERFACE_REQUESTED)) {*/
if (!(tmp->flags & INTERFACE_REQUESTED)) {
1998-11-06 00:19:56 +00:00
if ((tmp -> flags & INTERFACE_REQUESTED) != ir)
log_fatal ("%s: not found", tmp -> name);
if (!last) {
if (interfaces)
interface_dereference (&interfaces,
MDL);
if (next)
interface_reference (&interfaces, next, MDL);
} else {
interface_dereference (&last -> next, MDL);
if (next)
interface_reference (&last -> next,
next, MDL);
}
if (tmp -> next)
interface_dereference (&tmp -> next, MDL);
1998-11-06 00:19:56 +00:00
/* Remember the interface in case we need to know
about it later. */
if (dummy_interfaces) {
interface_reference (&tmp -> next,
dummy_interfaces, MDL);
interface_dereference (&dummy_interfaces, MDL);
}
interface_reference (&dummy_interfaces, tmp, MDL);
interface_dereference (&tmp, MDL);
if (next)
interface_reference (&tmp, next, MDL);
1998-11-06 00:19:56 +00:00
continue;
}
last = tmp;
2007-05-08 23:05:22 +00:00
/* XXX: no ifp in Solaris */
/* memcpy (&foo, &tmp -> ifp -> ifr_addr,
sizeof tmp -> ifp -> ifr_addr);*/
1998-11-06 00:19:56 +00:00
/* We must have a subnet declaration for each interface. */
2007-05-08 23:05:22 +00:00
if (!tmp->shared_network && (state == DISCOVER_SERVER)) {
log_error("%s", "");
if (local_family == AF_INET) {
log_error("No subnet declaration for %s (%s).",
tmp->name,
inet_ntoa(tmp->addresses[0]));
} else {
if (tmp->v6addresses != NULL) {
inet_ntop(AF_INET6,
&tmp->v6addresses[0],
abuf,
sizeof(abuf));
} else {
strcpy(abuf, "no addresses");
}
log_error("No subnet declaration for %s (%s).",
tmp->name,
abuf);
}
if (supports_multiple_interfaces(tmp)) {
log_error ("** Ignoring requests on %s. %s",
tmp -> name, "If this is not what");
log_error (" you want, please write %s",
"a subnet declaration");
log_error (" in your dhcpd.conf file %s",
"for the network segment");
log_error (" to %s %s %s",
"which interface",
tmp -> name, "is attached. **");
log_error ("%s", "");
goto next;
} else {
log_error ("You must write a subnet %s",
" declaration for this");
log_error ("subnet. You cannot prevent %s",
"the DHCP server");
log_error ("from listening on this subnet %s",
"because your");
log_fatal ("operating system does not %s.",
"support this capability");
}
}
1998-11-06 00:19:56 +00:00
/* Find subnets that don't have valid interface
addresses... */
for (subnet = (tmp -> shared_network
? tmp -> shared_network -> subnets
: (struct subnet *)0);
subnet; subnet = subnet -> next_sibling) {
if (!subnet -> interface_address.len) {
/* Set the interface address for this subnet
to the first address we found. */
subnet -> interface_address.len = 4;
memcpy (subnet -> interface_address.iabuf,
2007-05-08 23:05:22 +00:00
&tmp->addresses[0].s_addr, 4);
1998-11-06 00:19:56 +00:00
}
}
/* Flag the index as not having been set, so that the
interface registerer can set it or not as it chooses. */
tmp -> index = -1;
1998-11-06 00:19:56 +00:00
/* Register the interface... */
2007-05-08 23:05:22 +00:00
if (local_family == AF_INET) {
if_register_receive(tmp);
if_register_send(tmp);
} else {
if (state == DISCOVER_SERVER) {
if_register6(tmp, 1);
} else {
if_register6(tmp, 0);
}
}
interface_stash (tmp);
wifcount++;
#if defined (HAVE_SETFD)
if (fcntl (tmp -> rfdesc, F_SETFD, 1) < 0)
log_error ("Can't set close-on-exec on %s: %m",
tmp -> name);
if (tmp -> rfdesc != tmp -> wfdesc) {
if (fcntl (tmp -> wfdesc, F_SETFD, 1) < 0)
log_error ("Can't set close-on-exec on %s: %m",
tmp -> name);
}
#endif
next:
interface_dereference (&tmp, MDL);
if (next)
interface_reference (&tmp, next, MDL);
1998-11-06 00:19:56 +00:00
}
/* Now register all the remaining interfaces as protocols. */
1999-09-08 01:44:08 +00:00
for (tmp = interfaces; tmp; tmp = tmp -> next) {
/* not if it's been registered before */
if (tmp -> flags & INTERFACE_RUNNING)
continue;
if (tmp -> rfdesc == -1)
continue;
2007-05-08 23:05:22 +00:00
if (local_family == AF_INET) {
status = omapi_register_io_object((omapi_object_t *)tmp,
if_readsocket,
0, got_one, 0, 0);
} else {
status = omapi_register_io_object((omapi_object_t *)tmp,
if_readsocket,
0, got_one_v6, 0, 0);
}
1999-09-08 01:44:08 +00:00
if (status != ISC_R_SUCCESS)
log_fatal ("Can't register I/O handle for %s: %s",
tmp -> name, isc_result_totext (status));
}
1998-11-06 00:19:56 +00:00
if (state == DISCOVER_SERVER && wifcount == 0) {
log_info ("%s", "");
log_fatal ("Not configured to listen on any interfaces!");
}
2007-05-08 23:05:22 +00:00
if ((local_family == AF_INET) && !setup_fallback) {
setup_fallback = 1;
2007-05-08 23:05:22 +00:00
maybe_setup_fallback();
}
#if defined (HAVE_SETFD)
if (fallback_interface) {
if (fcntl (fallback_interface -> rfdesc, F_SETFD, 1) < 0)
log_error ("Can't set close-on-exec on fallback: %m");
if (fallback_interface -> rfdesc != fallback_interface -> wfdesc) {
if (fcntl (fallback_interface -> wfdesc, F_SETFD, 1) < 0)
log_error ("Can't set close-on-exec on fallback: %m");
}
}
#endif
1999-02-14 19:04:05 +00:00
}
1999-09-08 01:44:08 +00:00
int if_readsocket (h)
omapi_object_t *h;
{
struct interface_info *ip;
if (h -> type != dhcp_type_interface)
return -1;
1999-09-08 01:44:08 +00:00
ip = (struct interface_info *)h;
return ip -> rfdesc;
}
int setup_fallback (struct interface_info **fp, const char *file, int line)
1999-02-14 19:04:05 +00:00
{
isc_result_t status;
status = interface_allocate (&fallback_interface, file, line);
if (status != ISC_R_SUCCESS)
log_fatal ("Error allocating fallback interface: %s",
isc_result_totext (status));
1999-02-14 19:04:05 +00:00
strcpy (fallback_interface -> name, "fallback");
if (dhcp_interface_setup_hook)
(*dhcp_interface_setup_hook) (fallback_interface,
(struct iaddr *)0);
status = interface_reference (fp, fallback_interface, file, line);
fallback_interface -> index = -1;
interface_stash (fallback_interface);
return status == ISC_R_SUCCESS;
1998-11-06 00:19:56 +00:00
}
void reinitialize_interfaces ()
{
struct interface_info *ip;
for (ip = interfaces; ip; ip = ip -> next) {
if_reinitialize_receive (ip);
if_reinitialize_send (ip);
}
1999-02-14 19:04:05 +00:00
if (fallback_interface)
if_reinitialize_send (fallback_interface);
1998-11-06 00:19:56 +00:00
interfaces_invalidated = 1;
}
1999-02-14 19:04:05 +00:00
1999-09-08 01:44:08 +00:00
isc_result_t got_one (h)
omapi_object_t *h;
1998-11-06 00:19:56 +00:00
{
struct sockaddr_in from;
struct hardware hfrom;
struct iaddr ifrom;
int result;
union {
unsigned char packbuf [4095]; /* Packet input buffer.
Must be as large as largest
possible MTU. */
struct dhcp_packet packet;
} u;
1999-09-08 01:44:08 +00:00
struct interface_info *ip;
if (h -> type != dhcp_type_interface)
return ISC_R_INVALIDARG;
ip = (struct interface_info *)h;
1998-11-06 00:19:56 +00:00
again:
1998-11-06 00:19:56 +00:00
if ((result =
receive_packet (ip, u.packbuf, sizeof u, &from, &hfrom)) < 0) {
log_error ("receive_packet failed on %s: %m", ip -> name);
1999-09-08 01:44:08 +00:00
return ISC_R_UNEXPECTED;
1998-11-06 00:19:56 +00:00
}
if (result == 0)
1999-09-08 01:44:08 +00:00
return ISC_R_UNEXPECTED;
1998-11-06 00:19:56 +00:00
/* If we didn't at least get the fixed portion of the BOOTP
packet, drop the packet. We're allowing packets with no
sname or filename, because we're aware of at least one
client that sends such packets, but this definitely falls
into the category of being forgiving. */
if (result < DHCP_FIXED_NON_UDP - DHCP_SNAME_LEN - DHCP_FILE_LEN)
return ISC_R_UNEXPECTED;
1998-11-06 00:19:56 +00:00
if (bootp_packet_handler) {
ifrom.len = 4;
memcpy (ifrom.iabuf, &from.sin_addr, ifrom.len);
(*bootp_packet_handler) (ip, &u.packet, (unsigned)result,
1998-11-06 00:19:56 +00:00
from.sin_port, ifrom, &hfrom);
}
/* If there is buffered data, read again. This is for, e.g.,
bpf, which may return two packets at once. */
if (ip -> rbuf_offset != ip -> rbuf_len)
goto again;
1999-09-08 01:44:08 +00:00
return ISC_R_SUCCESS;
}
2007-05-08 23:05:22 +00:00
isc_result_t
got_one_v6(omapi_object_t *h) {
struct sockaddr_in6 from;
struct in6_addr to;
struct iaddr ifrom;
int result;
char buf[65536]; /* maximum size for a UDP packet is 65536 */
struct interface_info *ip;
int is_unicast;
if (h->type != dhcp_type_interface) {
return ISC_R_INVALIDARG;
}
ip = (struct interface_info *)h;
result = receive_packet6(ip, buf, sizeof(buf), &from, &to);
if (result < 0) {
log_error("receive_packet6() failed on %s: %m", ip->name);
return ISC_R_UNEXPECTED;
}
if (dhcpv6_packet_handler != NULL) {
/*
* If a packet is not multicast, we assume it is unicast.
*/
if (IN6_IS_ADDR_MULTICAST(&to)) {
is_unicast = ISC_FALSE;
} else {
is_unicast = ISC_TRUE;
}
ifrom.len = 16;
memcpy(ifrom.iabuf, &from.sin6_addr, ifrom.len);
(*dhcpv6_packet_handler)(ip, buf,
result, from.sin6_port,
&ifrom, is_unicast);
}
return ISC_R_SUCCESS;
}
isc_result_t dhcp_interface_set_value (omapi_object_t *h,
omapi_object_t *id,
omapi_data_string_t *name,
omapi_typed_data_t *value)
1999-09-08 01:44:08 +00:00
{
struct interface_info *interface;
isc_result_t status;
int foo;
1999-09-08 01:44:08 +00:00
if (h -> type != dhcp_type_interface)
return ISC_R_INVALIDARG;
interface = (struct interface_info *)h;
if (!omapi_ds_strcmp (name, "name")) {
if ((value -> type == omapi_datatype_data ||
value -> type == omapi_datatype_string) &&
value -> u.buffer.len < sizeof interface -> name) {
memcpy (interface -> name,
value -> u.buffer.value,
value -> u.buffer.len);
interface -> name [value -> u.buffer.len] = 0;
} else
return ISC_R_INVALIDARG;
return ISC_R_SUCCESS;
}
/* Try to find some inner object that can take the value. */
if (h -> inner && h -> inner -> type -> set_value) {
status = ((*(h -> inner -> type -> set_value))
(h -> inner, id, name, value));
if (status == ISC_R_SUCCESS || status == ISC_R_UNCHANGED)
return status;
}
1999-09-08 01:44:08 +00:00
return ISC_R_NOTFOUND;
}
isc_result_t dhcp_interface_get_value (omapi_object_t *h,
omapi_object_t *id,
omapi_data_string_t *name,
omapi_value_t **value)
1999-09-08 01:44:08 +00:00
{
return ISC_R_NOTIMPLEMENTED;
}
isc_result_t dhcp_interface_destroy (omapi_object_t *h,
const char *file, int line)
{
struct interface_info *interface;
isc_result_t status;
1999-09-08 01:44:08 +00:00
if (h -> type != dhcp_type_interface)
return ISC_R_INVALIDARG;
interface = (struct interface_info *)h;
if (interface -> ifp) {
dfree (interface -> ifp, file, line);
interface -> ifp = 0;
}
if (interface -> next)
interface_dereference (&interface -> next, file, line);
if (interface -> rbuf) {
dfree (interface -> rbuf, file, line);
interface -> rbuf = (unsigned char *)0;
}
if (interface -> client)
interface -> client = (struct client_state *)0;
if (interface -> shared_network)
omapi_object_dereference ((omapi_object_t **)
&interface -> shared_network, MDL);
return ISC_R_SUCCESS;
1999-09-08 01:44:08 +00:00
}
isc_result_t dhcp_interface_signal_handler (omapi_object_t *h,
const char *name, va_list ap)
1999-09-08 01:44:08 +00:00
{
struct interface_info *ip, *interface;
struct client_config *config;
struct client_state *client;
isc_result_t status;
if (h -> type != dhcp_type_interface)
1999-09-08 01:44:08 +00:00
return ISC_R_INVALIDARG;
interface = (struct interface_info *)h;
2001-04-05 20:50:19 +00:00
/* If it's an update signal, see if the interface is dead right
now, or isn't known at all, and if that's the case, revive it. */
if (!strcmp (name, "update")) {
for (ip = dummy_interfaces; ip; ip = ip -> next)
if (ip == interface)
break;
if (ip && dhcp_interface_startup_hook)
return (*dhcp_interface_startup_hook) (ip);
2001-04-05 20:50:19 +00:00
for (ip = interfaces; ip; ip = ip -> next)
if (ip == interface)
break;
if (!ip && dhcp_interface_startup_hook)
return (*dhcp_interface_startup_hook) (ip);
}
/* Try to find some inner object that can take the value. */
if (h -> inner && h -> inner -> type -> get_value) {
status = ((*(h -> inner -> type -> signal_handler))
(h -> inner, name, ap));
if (status == ISC_R_SUCCESS)
return status;
}
1999-09-08 01:44:08 +00:00
return ISC_R_NOTFOUND;
}
isc_result_t dhcp_interface_stuff_values (omapi_object_t *c,
omapi_object_t *id,
omapi_object_t *h)
1999-09-08 01:44:08 +00:00
{
struct interface_info *interface;
isc_result_t status;
1999-09-08 01:44:08 +00:00
if (h -> type != dhcp_type_interface)
return ISC_R_INVALIDARG;
interface = (struct interface_info *)h;
/* Write out all the values. */
status = omapi_connection_put_name (c, "state");
if (status != ISC_R_SUCCESS)
return status;
if ((interface->flags & INTERFACE_REQUESTED) != 0)
status = omapi_connection_put_string (c, "up");
else
status = omapi_connection_put_string (c, "down");
if (status != ISC_R_SUCCESS)
return status;
/* Write out the inner object, if any. */
if (h -> inner && h -> inner -> type -> stuff_values) {
status = ((*(h -> inner -> type -> stuff_values))
(c, id, h -> inner));
if (status == ISC_R_SUCCESS)
return status;
}
1999-09-08 01:44:08 +00:00
return ISC_R_SUCCESS;
}
isc_result_t dhcp_interface_lookup (omapi_object_t **ip,
omapi_object_t *id,
omapi_object_t *ref)
1999-09-08 01:44:08 +00:00
{
omapi_value_t *tv = (omapi_value_t *)0;
isc_result_t status;
struct interface_info *interface;
if (!ref)
return ISC_R_NOKEYS;
/* First see if we were sent a handle. */
status = omapi_get_value_str (ref, id, "handle", &tv);
if (status == ISC_R_SUCCESS) {
status = omapi_handle_td_lookup (ip, tv -> value);
omapi_value_dereference (&tv, MDL);
if (status != ISC_R_SUCCESS)
return status;
/* Don't return the object if the type is wrong. */
if ((*ip) -> type != dhcp_type_interface) {
omapi_object_dereference (ip, MDL);
return ISC_R_INVALIDARG;
}
}
/* Now look for an interface name. */
status = omapi_get_value_str (ref, id, "name", &tv);
if (status == ISC_R_SUCCESS) {
2001-04-05 20:50:19 +00:00
char *s;
unsigned len;
for (interface = interfaces; interface;
interface = interface -> next) {
2001-04-05 20:50:19 +00:00
s = memchr (interface -> name, 0, IFNAMSIZ);
if (s)
len = s - &interface -> name [0];
else
len = IFNAMSIZ;
if ((tv -> value -> u.buffer.len == len &&
!memcmp (interface -> name,
(char *)tv -> value -> u.buffer.value,
len)))
break;
}
2001-04-05 20:50:19 +00:00
if (!interface) {
for (interface = dummy_interfaces;
interface; interface = interface -> next) {
s = memchr (interface -> name, 0, IFNAMSIZ);
if (s)
len = s - &interface -> name [0];
else
len = IFNAMSIZ;
if ((tv -> value -> u.buffer.len == len &&
!memcmp (interface -> name,
(char *)
tv -> value -> u.buffer.value,
len)))
break;
}
}
omapi_value_dereference (&tv, MDL);
if (*ip && *ip != (omapi_object_t *)interface) {
omapi_object_dereference (ip, MDL);
return ISC_R_KEYCONFLICT;
} else if (!interface) {
if (*ip)
omapi_object_dereference (ip, MDL);
return ISC_R_NOTFOUND;
} else if (!*ip)
omapi_object_reference (ip,
(omapi_object_t *)interface,
MDL);
}
/* If we get to here without finding an interface, no valid key was
specified. */
if (!*ip)
return ISC_R_NOKEYS;
return ISC_R_SUCCESS;
}
/* actually just go discover the interface */
isc_result_t dhcp_interface_create (omapi_object_t **lp,
omapi_object_t *id)
{
struct interface_info *hp;
isc_result_t status;
1999-09-08 01:44:08 +00:00
hp = (struct interface_info *)0;
status = interface_allocate (&hp, MDL);
if (status != ISC_R_SUCCESS)
return status;
hp -> flags = INTERFACE_REQUESTED;
status = interface_reference ((struct interface_info **)lp, hp, MDL);
interface_dereference (&hp, MDL);
return status;
1998-11-06 00:19:56 +00:00
}
isc_result_t dhcp_interface_remove (omapi_object_t *lp,
omapi_object_t *id)
{
struct interface_info *interface, *ip, *last;
interface = (struct interface_info *)lp;
/* remove from interfaces */
last = 0;
for (ip = interfaces; ip; ip = ip -> next) {
if (ip == interface) {
if (last) {
interface_dereference (&last -> next, MDL);
if (ip -> next)
interface_reference (&last -> next,
ip -> next, MDL);
} else {
interface_dereference (&interfaces, MDL);
if (ip -> next)
interface_reference (&interfaces,
ip -> next, MDL);
}
if (ip -> next)
interface_dereference (&ip -> next, MDL);
break;
}
last = ip;
}
if (!ip)
return ISC_R_NOTFOUND;
/* add the interface to the dummy_interface list */
if (dummy_interfaces) {
interface_reference (&interface -> next,
dummy_interfaces, MDL);
interface_dereference (&dummy_interfaces, MDL);
}
interface_reference (&dummy_interfaces, interface, MDL);
/* do a DHCPRELEASE */
if (dhcp_interface_shutdown_hook)
(*dhcp_interface_shutdown_hook) (interface);
/* remove the io object */
omapi_unregister_io_object ((omapi_object_t *)interface);
2007-05-08 23:05:22 +00:00
if (local_family == AF_INET) {
if_deregister_send(interface);
if_deregister_receive(interface);
} else {
if_deregister6(interface);
}
return ISC_R_SUCCESS;
}
void interface_stash (struct interface_info *tptr)
{
struct interface_info **vec;
int delta;
/* If the registerer didn't assign an index, assign one now. */
if (tptr -> index == -1) {
tptr -> index = interface_count++;
while (tptr -> index < interface_max &&
interface_vector [tptr -> index])
tptr -> index = interface_count++;
}
if (interface_max <= tptr -> index) {
delta = tptr -> index - interface_max + 10;
vec = dmalloc ((interface_max + delta) *
sizeof (struct interface_info *), MDL);
if (!vec)
return;
memset (&vec [interface_max], 0,
(sizeof (struct interface_info *)) * delta);
interface_max += delta;
if (interface_vector) {
memcpy (vec, interface_vector,
(interface_count *
sizeof (struct interface_info *)));
dfree (interface_vector, MDL);
}
interface_vector = vec;
}
interface_reference (&interface_vector [tptr -> index], tptr, MDL);
if (tptr -> index >= interface_count)
interface_count = tptr -> index + 1;
#if defined (TRACING)
trace_interface_register (interface_trace, tptr);
#endif
}
void interface_snorf (struct interface_info *tmp, int ir)
{
tmp -> circuit_id = (u_int8_t *)tmp -> name;
tmp -> circuit_id_len = strlen (tmp -> name);
tmp -> remote_id = 0;
tmp -> remote_id_len = 0;
tmp -> flags = ir;
if (interfaces) {
interface_reference (&tmp -> next,
interfaces, MDL);
interface_dereference (&interfaces, MDL);
}
interface_reference (&interfaces, tmp, MDL);
}