From b09670abfb8ef52999c1fcb7022671c627c91e0c Mon Sep 17 00:00:00 2001 From: Jouni Malinen Date: Mon, 3 Jun 2019 16:20:57 +0300 Subject: [PATCH] macsec_linux: Hook QCA driver wrapper for hostapd MACsec Signed-off-by: Jouni Malinen --- src/drivers/driver_macsec_linux.c | 214 ++++++++++++++++++++++++++++++ 1 file changed, 214 insertions(+) diff --git a/src/drivers/driver_macsec_linux.c b/src/drivers/driver_macsec_linux.c index 9d981bb70..e922503fc 100644 --- a/src/drivers/driver_macsec_linux.c +++ b/src/drivers/driver_macsec_linux.c @@ -1,6 +1,7 @@ /* * Driver interaction with Linux MACsec kernel module * Copyright (c) 2016, Sabrina Dubroca and Red Hat, Inc. + * Copyright (c) 2019, The Linux Foundation * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -22,6 +23,7 @@ #include "utils/common.h" #include "utils/eloop.h" +#include "common/eapol_common.h" #include "pae/ieee802_1x_kay.h" #include "driver.h" #include "driver_wired_common.h" @@ -57,6 +59,7 @@ struct macsec_drv_data { char ifname[IFNAMSIZ + 1]; int ifi; int parent_ifi; + int use_pae_group_addr; Boolean created_link; @@ -1399,6 +1402,214 @@ static int macsec_drv_status(void *priv, char *buf, size_t buflen) } +#ifdef __linux__ + +static void macsec_drv_handle_data(void *ctx, unsigned char *buf, size_t len) +{ +#ifdef HOSTAPD + struct ieee8023_hdr *hdr; + u8 *pos, *sa; + size_t left; + union wpa_event_data event; + + /* must contain at least ieee8023_hdr 6 byte source, 6 byte dest, + * 2 byte ethertype */ + if (len < 14) { + wpa_printf(MSG_MSGDUMP, "%s: too short (%lu)", + __func__, (unsigned long) len); + return; + } + + hdr = (struct ieee8023_hdr *) buf; + + switch (ntohs(hdr->ethertype)) { + case ETH_P_PAE: + wpa_printf(MSG_MSGDUMP, "Received EAPOL packet"); + sa = hdr->src; + os_memset(&event, 0, sizeof(event)); + event.new_sta.addr = sa; + wpa_supplicant_event(ctx, EVENT_NEW_STA, &event); + + pos = (u8 *) (hdr + 1); + left = len - sizeof(*hdr); + drv_event_eapol_rx(ctx, sa, pos, left); + break; + + default: + wpa_printf(MSG_DEBUG, "Unknown ethertype 0x%04x in data frame", + ntohs(hdr->ethertype)); + break; + } +#endif /* HOSTAPD */ +} + + +static void macsec_drv_handle_read(int sock, void *eloop_ctx, void *sock_ctx) +{ + int len; + unsigned char buf[3000]; + + len = recv(sock, buf, sizeof(buf), 0); + if (len < 0) { + wpa_printf(MSG_ERROR, "macsec_linux: recv: %s", + strerror(errno)); + return; + } + + macsec_drv_handle_data(eloop_ctx, buf, len); +} + +#endif /* __linux__ */ + + +static int macsec_drv_init_sockets(struct macsec_drv_data *drv, u8 *own_addr) +{ +#ifdef __linux__ + struct ifreq ifr; + struct sockaddr_ll addr; + + drv->common.sock = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_PAE)); + if (drv->common.sock < 0) { + wpa_printf(MSG_ERROR, "socket[PF_PACKET,SOCK_RAW]: %s", + strerror(errno)); + return -1; + } + + if (eloop_register_read_sock(drv->common.sock, macsec_drv_handle_read, + drv->common.ctx, NULL)) { + wpa_printf(MSG_INFO, "Could not register read socket"); + return -1; + } + + os_memset(&ifr, 0, sizeof(ifr)); + os_strlcpy(ifr.ifr_name, drv->common.ifname, sizeof(ifr.ifr_name)); + if (ioctl(drv->common.sock, SIOCGIFINDEX, &ifr) != 0) { + wpa_printf(MSG_ERROR, "ioctl(SIOCGIFINDEX): %s", + strerror(errno)); + return -1; + } + + os_memset(&addr, 0, sizeof(addr)); + addr.sll_family = AF_PACKET; + addr.sll_ifindex = ifr.ifr_ifindex; + wpa_printf(MSG_DEBUG, "Opening raw packet socket for ifindex %d", + addr.sll_ifindex); + + if (bind(drv->common.sock, (struct sockaddr *) &addr, sizeof(addr)) < 0) + { + wpa_printf(MSG_ERROR, "bind: %s", strerror(errno)); + return -1; + } + + /* filter multicast address */ + if (wired_multicast_membership(drv->common.sock, ifr.ifr_ifindex, + pae_group_addr, 1) < 0) { + wpa_printf(MSG_ERROR, "wired: Failed to add multicast group " + "membership"); + return -1; + } + + os_memset(&ifr, 0, sizeof(ifr)); + os_strlcpy(ifr.ifr_name, drv->common.ifname, sizeof(ifr.ifr_name)); + if (ioctl(drv->common.sock, SIOCGIFHWADDR, &ifr) != 0) { + wpa_printf(MSG_ERROR, "ioctl(SIOCGIFHWADDR): %s", + strerror(errno)); + return -1; + } + + if (ifr.ifr_hwaddr.sa_family != ARPHRD_ETHER) { + wpa_printf(MSG_INFO, "Invalid HW-addr family 0x%04x", + ifr.ifr_hwaddr.sa_family); + return -1; + } + os_memcpy(own_addr, ifr.ifr_hwaddr.sa_data, ETH_ALEN); + + return 0; +#else /* __linux__ */ + return -1; +#endif /* __linux__ */ +} + + +static void * macsec_drv_hapd_init(struct hostapd_data *hapd, + struct wpa_init_params *params) +{ + struct macsec_drv_data *drv; + + drv = os_zalloc(sizeof(struct macsec_drv_data)); + if (drv == NULL) { + wpa_printf(MSG_INFO, + "Could not allocate memory for wired driver data"); + return NULL; + } + + drv->common.ctx = hapd; + os_strlcpy(drv->common.ifname, params->ifname, + sizeof(drv->common.ifname)); + drv->use_pae_group_addr = params->use_pae_group_addr; + + if (macsec_drv_init_sockets(drv, params->own_addr)) { + os_free(drv); + return NULL; + } + + return drv; +} + + +static void macsec_drv_hapd_deinit(void *priv) +{ + struct macsec_drv_data *drv = priv; + + if (drv->common.sock >= 0) { + eloop_unregister_read_sock(drv->common.sock); + close(drv->common.sock); + } + + os_free(drv); +} + + +static int macsec_drv_send_eapol(void *priv, const u8 *addr, + const u8 *data, size_t data_len, int encrypt, + const u8 *own_addr, u32 flags) +{ + struct macsec_drv_data *drv = priv; + struct ieee8023_hdr *hdr; + size_t len; + u8 *pos; + int res; + + len = sizeof(*hdr) + data_len; + hdr = os_zalloc(len); + if (hdr == NULL) { + wpa_printf(MSG_INFO, + "%s: malloc() failed (len=%lu)", + __func__, (unsigned long) len); + return -1; + } + + os_memcpy(hdr->dest, drv->use_pae_group_addr ? pae_group_addr : addr, + ETH_ALEN); + os_memcpy(hdr->src, own_addr, ETH_ALEN); + hdr->ethertype = htons(ETH_P_PAE); + + pos = (u8 *) (hdr + 1); + os_memcpy(pos, data, data_len); + + res = send(drv->common.sock, (u8 *) hdr, len, 0); + os_free(hdr); + + if (res < 0) { + wpa_printf(MSG_ERROR, + "%s: packet len: %lu - failed: send: %s", + __func__, (unsigned long) len, strerror(errno)); + } + + return res; +} + + const struct wpa_driver_ops wpa_driver_macsec_linux_ops = { .name = "macsec_linux", .desc = "MACsec Ethernet driver for Linux", @@ -1407,6 +1618,9 @@ const struct wpa_driver_ops wpa_driver_macsec_linux_ops = { .get_capa = driver_wired_get_capa, .init = macsec_drv_wpa_init, .deinit = macsec_drv_wpa_deinit, + .hapd_init = macsec_drv_hapd_init, + .hapd_deinit = macsec_drv_hapd_deinit, + .hapd_send_eapol = macsec_drv_send_eapol, .macsec_init = macsec_drv_macsec_init, .macsec_deinit = macsec_drv_macsec_deinit,