wpa_priv: Add support for multiple l2_packet connections

This is needed to be able to work with many wpa_supplicant use cases,
e.g., due to use of TDLS or RSN pre-authentication needing a separate
l2_packet socket.

Signed-off-by: Jouni Malinen <j@w1.fi>
This commit is contained in:
Jouni Malinen 2016-12-03 11:37:41 +02:00
parent 82fff2c711
commit 128d3c69fc

View file

@ -21,6 +21,7 @@
#include "common/privsep_commands.h" #include "common/privsep_commands.h"
#include "common/ieee802_11_defs.h" #include "common/ieee802_11_defs.h"
#define WPA_PRIV_MAX_L2 3
struct wpa_priv_interface { struct wpa_priv_interface {
struct wpa_priv_interface *next; struct wpa_priv_interface *next;
@ -37,9 +38,12 @@ struct wpa_priv_interface {
struct sockaddr_un drv_addr; struct sockaddr_un drv_addr;
int wpas_registered; int wpas_registered;
/* TODO: add support for multiple l2 connections */ struct l2_packet_data *l2[WPA_PRIV_MAX_L2];
struct l2_packet_data *l2; struct sockaddr_un l2_addr[WPA_PRIV_MAX_L2];
struct sockaddr_un l2_addr; struct wpa_priv_l2 {
struct wpa_priv_interface *parent;
int idx;
} l2_ctx[WPA_PRIV_MAX_L2];
}; };
struct wpa_priv_global { struct wpa_priv_global {
@ -50,6 +54,8 @@ struct wpa_priv_global {
static void wpa_priv_cmd_register(struct wpa_priv_interface *iface, static void wpa_priv_cmd_register(struct wpa_priv_interface *iface,
struct sockaddr_un *from) struct sockaddr_un *from)
{ {
int i;
if (iface->drv_priv) { if (iface->drv_priv) {
wpa_printf(MSG_DEBUG, "Cleaning up forgotten driver instance"); wpa_printf(MSG_DEBUG, "Cleaning up forgotten driver instance");
if (iface->driver->deinit) if (iface->driver->deinit)
@ -62,11 +68,13 @@ static void wpa_priv_cmd_register(struct wpa_priv_interface *iface,
iface->wpas_registered = 0; iface->wpas_registered = 0;
} }
if (iface->l2) { for (i = 0; i < WPA_PRIV_MAX_L2; i++) {
wpa_printf(MSG_DEBUG, "Cleaning up forgotten l2_packet " if (iface->l2[i]) {
"instance"); wpa_printf(MSG_DEBUG,
l2_packet_deinit(iface->l2); "Cleaning up forgotten l2_packet instance");
iface->l2 = NULL; l2_packet_deinit(iface->l2[i]);
iface->l2[i] = NULL;
}
} }
if (iface->driver->init2) { if (iface->driver->init2) {
@ -405,7 +413,8 @@ fail:
static void wpa_priv_l2_rx(void *ctx, const u8 *src_addr, const u8 *buf, static void wpa_priv_l2_rx(void *ctx, const u8 *src_addr, const u8 *buf,
size_t len) size_t len)
{ {
struct wpa_priv_interface *iface = ctx; struct wpa_priv_l2 *l2_ctx = ctx;
struct wpa_priv_interface *iface = l2_ctx->parent;
struct msghdr msg; struct msghdr msg;
struct iovec io[2]; struct iovec io[2];
@ -417,8 +426,8 @@ static void wpa_priv_l2_rx(void *ctx, const u8 *src_addr, const u8 *buf,
os_memset(&msg, 0, sizeof(msg)); os_memset(&msg, 0, sizeof(msg));
msg.msg_iov = io; msg.msg_iov = io;
msg.msg_iovlen = 2; msg.msg_iovlen = 2;
msg.msg_name = &iface->l2_addr; msg.msg_name = &iface->l2_addr[l2_ctx->idx];
msg.msg_namelen = sizeof(iface->l2_addr); msg.msg_namelen = sizeof(iface->l2_addr[l2_ctx->idx]);
if (sendmsg(iface->fd, &msg, 0) < 0) { if (sendmsg(iface->fd, &msg, 0) < 0) {
wpa_printf(MSG_ERROR, "sendmsg(l2 rx): %s", strerror(errno)); wpa_printf(MSG_ERROR, "sendmsg(l2 rx): %s", strerror(errno));
@ -426,6 +435,13 @@ static void wpa_priv_l2_rx(void *ctx, const u8 *src_addr, const u8 *buf,
} }
static int wpa_priv_allowed_l2_proto(u16 proto)
{
return proto == ETH_P_EAPOL || proto == ETH_P_RSN_PREAUTH ||
proto == ETH_P_80211_ENCAP;
}
static void wpa_priv_cmd_l2_register(struct wpa_priv_interface *iface, static void wpa_priv_cmd_l2_register(struct wpa_priv_interface *iface,
struct sockaddr_un *from, struct sockaddr_un *from,
void *buf, size_t len) void *buf, size_t len)
@ -434,6 +450,7 @@ static void wpa_priv_cmd_l2_register(struct wpa_priv_interface *iface,
u8 own_addr[ETH_ALEN]; u8 own_addr[ETH_ALEN];
int res; int res;
u16 proto; u16 proto;
int idx;
if (len != 2 * sizeof(int)) { if (len != 2 * sizeof(int)) {
wpa_printf(MSG_DEBUG, "Invalid l2_register length %lu", wpa_printf(MSG_DEBUG, "Invalid l2_register length %lu",
@ -442,50 +459,67 @@ static void wpa_priv_cmd_l2_register(struct wpa_priv_interface *iface,
} }
proto = reg_cmd[0]; proto = reg_cmd[0];
if (proto != ETH_P_EAPOL && proto != ETH_P_RSN_PREAUTH && if (!wpa_priv_allowed_l2_proto(proto)) {
proto != ETH_P_80211_ENCAP) {
wpa_printf(MSG_DEBUG, "Refused l2_packet connection for " wpa_printf(MSG_DEBUG, "Refused l2_packet connection for "
"ethertype 0x%x", proto); "ethertype 0x%x", proto);
return; return;
} }
if (iface->l2) { for (idx = 0; idx < WPA_PRIV_MAX_L2; idx++) {
wpa_printf(MSG_DEBUG, "Cleaning up forgotten l2_packet " if (!iface->l2[idx])
"instance"); break;
l2_packet_deinit(iface->l2); }
iface->l2 = NULL; if (idx == WPA_PRIV_MAX_L2) {
wpa_printf(MSG_DEBUG, "No free l2_packet connection found");
return;
} }
os_memcpy(&iface->l2_addr, from, sizeof(iface->l2_addr)); os_memcpy(&iface->l2_addr[idx], from, sizeof(iface->l2_addr[idx]));
iface->l2 = l2_packet_init(iface->ifname, NULL, proto, iface->l2_ctx[idx].idx = idx;
wpa_priv_l2_rx, iface, reg_cmd[1]); iface->l2_ctx[idx].parent = iface;
if (iface->l2 == NULL) { iface->l2[idx] = l2_packet_init(iface->ifname, NULL, proto,
wpa_priv_l2_rx, &iface->l2_ctx[idx],
reg_cmd[1]);
if (!iface->l2[idx]) {
wpa_printf(MSG_DEBUG, "Failed to initialize l2_packet " wpa_printf(MSG_DEBUG, "Failed to initialize l2_packet "
"instance for protocol %d", proto); "instance for protocol %d", proto);
return; return;
} }
if (l2_packet_get_own_addr(iface->l2, own_addr) < 0) { if (l2_packet_get_own_addr(iface->l2[idx], own_addr) < 0) {
wpa_printf(MSG_DEBUG, "Failed to get own address from " wpa_printf(MSG_DEBUG, "Failed to get own address from "
"l2_packet"); "l2_packet");
l2_packet_deinit(iface->l2); l2_packet_deinit(iface->l2[idx]);
iface->l2 = NULL; iface->l2[idx] = NULL;
return; return;
} }
res = sendto(iface->fd, own_addr, ETH_ALEN, 0, res = sendto(iface->fd, own_addr, ETH_ALEN, 0,
(struct sockaddr *) from, sizeof(*from)); (struct sockaddr *) from, sizeof(*from));
wpa_printf(MSG_DEBUG, "L2 registration: res=%d", res); wpa_printf(MSG_DEBUG, "L2 registration[idx=%d]: res=%d", idx, res);
} }
static void wpa_priv_cmd_l2_unregister(struct wpa_priv_interface *iface, static void wpa_priv_cmd_l2_unregister(struct wpa_priv_interface *iface,
struct sockaddr_un *from) struct sockaddr_un *from)
{ {
if (iface->l2) { int idx;
l2_packet_deinit(iface->l2);
iface->l2 = NULL; for (idx = 0; idx < WPA_PRIV_MAX_L2; idx++) {
if (os_memcmp(&iface->l2_addr[idx], from,
sizeof(struct sockaddr_un)) == 0)
break;
}
if (idx == WPA_PRIV_MAX_L2) {
wpa_printf(MSG_DEBUG,
"No registered l2_packet socket found for unregister request");
return;
}
if (iface->l2[idx]) {
l2_packet_deinit(iface->l2[idx]);
iface->l2[idx] = NULL;
} }
} }
@ -493,8 +527,12 @@ static void wpa_priv_cmd_l2_unregister(struct wpa_priv_interface *iface,
static void wpa_priv_cmd_l2_notify_auth_start(struct wpa_priv_interface *iface, static void wpa_priv_cmd_l2_notify_auth_start(struct wpa_priv_interface *iface,
struct sockaddr_un *from) struct sockaddr_un *from)
{ {
if (iface->l2) int idx;
l2_packet_notify_auth_start(iface->l2);
for (idx = 0; idx < WPA_PRIV_MAX_L2; idx++) {
if (iface->l2[idx])
l2_packet_notify_auth_start(iface->l2[idx]);
}
} }
@ -505,8 +543,20 @@ static void wpa_priv_cmd_l2_send(struct wpa_priv_interface *iface,
u8 *dst_addr; u8 *dst_addr;
u16 proto; u16 proto;
int res; int res;
int idx;
if (iface->l2 == NULL) for (idx = 0; idx < WPA_PRIV_MAX_L2; idx++) {
if (os_memcmp(&iface->l2_addr[idx], from,
sizeof(struct sockaddr_un)) == 0)
break;
}
if (idx == WPA_PRIV_MAX_L2) {
wpa_printf(MSG_DEBUG,
"No registered l2_packet socket found for send request");
return;
}
if (iface->l2[idx] == NULL)
return; return;
if (len < ETH_ALEN + 2) { if (len < ETH_ALEN + 2) {
@ -518,15 +568,15 @@ static void wpa_priv_cmd_l2_send(struct wpa_priv_interface *iface,
dst_addr = buf; dst_addr = buf;
os_memcpy(&proto, buf + ETH_ALEN, 2); os_memcpy(&proto, buf + ETH_ALEN, 2);
if (proto != ETH_P_EAPOL && proto != ETH_P_RSN_PREAUTH) { if (!wpa_priv_allowed_l2_proto(proto)) {
wpa_printf(MSG_DEBUG, "Refused l2_packet send for ethertype " wpa_printf(MSG_DEBUG, "Refused l2_packet send for ethertype "
"0x%x", proto); "0x%x", proto);
return; return;
} }
res = l2_packet_send(iface->l2, dst_addr, proto, buf + ETH_ALEN + 2, res = l2_packet_send(iface->l2[idx], dst_addr, proto,
len - ETH_ALEN - 2); buf + ETH_ALEN + 2, len - ETH_ALEN - 2);
wpa_printf(MSG_DEBUG, "L2 send: res=%d", res); wpa_printf(MSG_DEBUG, "L2 send[idx=%d]: res=%d", idx, res);
} }
@ -625,6 +675,8 @@ static void wpa_priv_receive(int sock, void *eloop_ctx, void *sock_ctx)
static void wpa_priv_interface_deinit(struct wpa_priv_interface *iface) static void wpa_priv_interface_deinit(struct wpa_priv_interface *iface)
{ {
int i;
if (iface->drv_priv && iface->driver->deinit) if (iface->drv_priv && iface->driver->deinit)
iface->driver->deinit(iface->drv_priv); iface->driver->deinit(iface->drv_priv);
@ -634,8 +686,10 @@ static void wpa_priv_interface_deinit(struct wpa_priv_interface *iface)
unlink(iface->sock_name); unlink(iface->sock_name);
} }
if (iface->l2) for (i = 0; i < WPA_PRIV_MAX_L2; i++) {
l2_packet_deinit(iface->l2); if (iface->l2[i])
l2_packet_deinit(iface->l2[i]);
}
os_free(iface->ifname); os_free(iface->ifname);
os_free(iface->driver_name); os_free(iface->driver_name);