BSD: If route socket overflows, sync drivers to system interfaces
Messages such as RTM_IFNFO or RTM_IFANNOUNCE could have been lost. As such, sync the state of our internal driver to the state of the system interfaces as reports by getifaddrs(2). This change requires the routing socket be placed in non-blocking mode. While here, set the routing and inet sockets to close on exec. BSDs that support SO_RERROR include NetBSD and DragonFly. There is a review underway to add this to FreeBSD. Signed-off-by: Roy Marples <roy@marples.name>
This commit is contained in:
parent
ba7542f62d
commit
a579642bc3
1 changed files with 111 additions and 2 deletions
|
@ -16,7 +16,9 @@
|
|||
#include "common/ieee802_11_defs.h"
|
||||
#include "common/wpa_common.h"
|
||||
|
||||
#include <ifaddrs.h>
|
||||
#include <net/if.h>
|
||||
#include <net/if_dl.h>
|
||||
#include <net/if_media.h>
|
||||
|
||||
#ifdef __NetBSD__
|
||||
|
@ -615,6 +617,108 @@ bsd_set_opt_ie(void *priv, const u8 *ie, size_t ie_len)
|
|||
return 0;
|
||||
}
|
||||
|
||||
#ifdef SO_RERROR
|
||||
static void
|
||||
bsd_route_overflow(int sock, void *ctx, struct bsd_driver_global *global)
|
||||
{
|
||||
char event_buf[2048]; /* max size of a single route(4) msg */
|
||||
int n;
|
||||
struct ifaddrs *ifaddrs, *ifa;
|
||||
struct bsd_driver_data *drv;
|
||||
struct sockaddr_dl *sdl;
|
||||
union wpa_event_data event;
|
||||
|
||||
/* We need to match the system state, so drain the route
|
||||
* socket to avoid stale messages. */
|
||||
do {
|
||||
n = read(sock, event_buf, sizeof(event_buf));
|
||||
} while (n != -1 || errno == ENOBUFS);
|
||||
|
||||
if (getifaddrs(&ifaddrs) == -1) {
|
||||
wpa_printf(MSG_ERROR, "%s getifaddrs() failed: %s",
|
||||
__func__, strerror(errno));
|
||||
return;
|
||||
}
|
||||
|
||||
/* add or update existing interfaces */
|
||||
for (ifa = ifaddrs; ifa != NULL; ifa = ifa->ifa_next) {
|
||||
if (ifa->ifa_addr == NULL ||
|
||||
ifa->ifa_addr->sa_family != AF_LINK)
|
||||
continue;
|
||||
sdl = (struct sockaddr_dl *) (void *) ifa->ifa_addr;
|
||||
drv = bsd_get_drvname(global, ifa->ifa_name);
|
||||
if (drv != NULL &&
|
||||
(drv->ifindex != sdl->sdl_index || drv->if_removed)) {
|
||||
wpa_printf(MSG_DEBUG,
|
||||
"RTM_IFANNOUNCE: Interface '%s' added",
|
||||
drv->ifname);
|
||||
drv->ifindex = sdl->sdl_index;
|
||||
drv->if_removed = 0;
|
||||
event.interface_status.ievent = EVENT_INTERFACE_ADDED;
|
||||
os_strlcpy(event.interface_status.ifname, ifa->ifa_name,
|
||||
sizeof(event.interface_status.ifname));
|
||||
wpa_supplicant_event(drv->ctx, EVENT_INTERFACE_STATUS,
|
||||
&event);
|
||||
}
|
||||
if (!drv &&
|
||||
(drv = bsd_get_drvindex(global, sdl->sdl_index)) != NULL) {
|
||||
/* Driver name is invalid */
|
||||
wpa_printf(MSG_DEBUG,
|
||||
"RTM_IFANNOUNCE: Interface '%s' removed",
|
||||
drv->ifname);
|
||||
drv->if_removed = 1;
|
||||
event.interface_status.ievent = EVENT_INTERFACE_REMOVED;
|
||||
os_strlcpy(event.interface_status.ifname, drv->ifname,
|
||||
sizeof(event.interface_status.ifname));
|
||||
wpa_supplicant_event(drv->ctx, EVENT_INTERFACE_STATUS,
|
||||
&event);
|
||||
}
|
||||
}
|
||||
|
||||
/* punt missing interfaces and update flags */
|
||||
dl_list_for_each(drv, &global->ifaces, struct bsd_driver_data, list) {
|
||||
for (ifa = ifaddrs; ifa != NULL; ifa = ifa->ifa_next) {
|
||||
if (ifa->ifa_addr == NULL ||
|
||||
ifa->ifa_addr->sa_family != AF_LINK)
|
||||
continue;
|
||||
sdl = (struct sockaddr_dl *) (void *) ifa->ifa_addr;
|
||||
if (os_strcmp(drv->ifname, ifa->ifa_name) == 0)
|
||||
break;
|
||||
}
|
||||
if (ifa == NULL && !drv->if_removed) {
|
||||
wpa_printf(MSG_DEBUG,
|
||||
"RTM_IFANNOUNCE: Interface '%s' removed",
|
||||
drv->ifname);
|
||||
drv->if_removed = 1;
|
||||
event.interface_status.ievent = EVENT_INTERFACE_REMOVED;
|
||||
os_strlcpy(event.interface_status.ifname, drv->ifname,
|
||||
sizeof(event.interface_status.ifname));
|
||||
wpa_supplicant_event(drv->ctx, EVENT_INTERFACE_STATUS,
|
||||
&event);
|
||||
}
|
||||
if (!ifa)
|
||||
continue;
|
||||
|
||||
if ((ifa->ifa_flags & IFF_UP) == 0 &&
|
||||
(drv->flags & IFF_UP) != 0) {
|
||||
wpa_printf(MSG_DEBUG, "RTM_IFINFO: Interface '%s' DOWN",
|
||||
drv->ifname);
|
||||
wpa_supplicant_event(drv->ctx, EVENT_INTERFACE_DISABLED,
|
||||
NULL);
|
||||
} else if ((ifa->ifa_flags & IFF_UP) != 0 &&
|
||||
(drv->flags & IFF_UP) == 0) {
|
||||
wpa_printf(MSG_DEBUG, "RTM_IFINFO: Interface '%s' UP",
|
||||
drv->ifname);
|
||||
wpa_supplicant_event(drv->ctx, EVENT_INTERFACE_ENABLED,
|
||||
NULL);
|
||||
}
|
||||
drv->flags = ifa->ifa_flags;
|
||||
}
|
||||
|
||||
freeifaddrs(ifaddrs);
|
||||
}
|
||||
#endif /* SO_RERROR */
|
||||
|
||||
static void
|
||||
bsd_wireless_event_receive(int sock, void *ctx, void *sock_ctx)
|
||||
{
|
||||
|
@ -635,6 +739,10 @@ bsd_wireless_event_receive(int sock, void *ctx, void *sock_ctx)
|
|||
if (errno != EINTR && errno != EAGAIN)
|
||||
wpa_printf(MSG_ERROR, "%s read() failed: %s",
|
||||
__func__, strerror(errno));
|
||||
#ifdef SO_RERROR
|
||||
if (errno == ENOBUFS)
|
||||
bsd_route_overflow(sock, ctx, sock_ctx);
|
||||
#endif /* SO_RERROR */
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -1568,14 +1676,15 @@ bsd_global_init(void *ctx)
|
|||
global->ctx = ctx;
|
||||
dl_list_init(&global->ifaces);
|
||||
|
||||
global->sock = socket(PF_INET, SOCK_DGRAM, 0);
|
||||
global->sock = socket(PF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0);
|
||||
if (global->sock < 0) {
|
||||
wpa_printf(MSG_ERROR, "socket[PF_INET,SOCK_DGRAM]: %s",
|
||||
strerror(errno));
|
||||
goto fail1;
|
||||
}
|
||||
|
||||
global->route = socket(PF_ROUTE, SOCK_RAW, 0);
|
||||
global->route = socket(PF_ROUTE,
|
||||
SOCK_RAW | SOCK_CLOEXEC | SOCK_NONBLOCK, 0);
|
||||
if (global->route < 0) {
|
||||
wpa_printf(MSG_ERROR, "socket[PF_ROUTE,SOCK_RAW]: %s",
|
||||
strerror(errno));
|
||||
|
|
Loading…
Reference in a new issue