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:
parent
82fff2c711
commit
128d3c69fc
1 changed files with 92 additions and 38 deletions
|
@ -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);
|
||||||
|
|
Loading…
Reference in a new issue