aboutsummaryrefslogtreecommitdiffstats
path: root/hostapd/driver.c
diff options
context:
space:
mode:
Diffstat (limited to 'hostapd/driver.c')
-rw-r--r--hostapd/driver.c200
1 files changed, 200 insertions, 0 deletions
diff --git a/hostapd/driver.c b/hostapd/driver.c
index a2e2650..a15266e 100644
--- a/hostapd/driver.c
+++ b/hostapd/driver.c
@@ -13,6 +13,8 @@
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
+#include <unistd.h>
+#include <errno.h>
#include <sys/ioctl.h>
#include <netinet/in.h>
@@ -31,6 +33,10 @@
#include "hostapd.h"
#include "driver.h"
#include "ieee802_1x.h"
+#include "eloop.h"
+#include "priv_netlink.h"
+#include "sta_info.h"
+#include "ieee802_11.h"
int hostapd_set_iface_flags(hostapd *hapd, int dev_up)
@@ -289,3 +295,197 @@ int hostapd_set_generic_elem(struct hostapd_data *hapd,
return res;
}
+
+
+static void hostapd_wireless_event_wireless_custom(struct hostapd_data *hapd,
+ char *custom)
+{
+ HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, "Custom wireless event: '%s'\n",
+ custom);
+
+ if (strncmp(custom, "MLME-MICHAELMICFAILURE.indication", 33) == 0) {
+ char *pos;
+ u8 addr[ETH_ALEN];
+ pos = strstr(custom, "addr=");
+ if (pos == NULL) {
+ HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL,
+ "MLME-MICHAELMICFAILURE.indication "
+ "without sender address ignored\n");
+ return;
+ }
+ pos += 5;
+ if (hwaddr_aton(pos, addr) == 0 &&
+ ap_get_sta(hapd, addr) != NULL) {
+ hostapd_logger(hapd, addr, HOSTAPD_MODULE_IEEE80211,
+ HOSTAPD_LEVEL_INFO,
+ "Michael MIC failure detected in "
+ "received frame");
+ ieee80211_michael_mic_failure(hapd);
+ } else {
+ HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL,
+ "MLME-MICHAELMICFAILURE.indication "
+ "for not associated STA (" MACSTR
+ ") ignored\n",
+ MAC2STR(addr));
+ return;
+ }
+ }
+}
+
+
+static void hostapd_wireless_event_wireless(struct hostapd_data *hapd,
+ char *data, int len)
+{
+ struct iw_event *iwe;
+ char *pos, *end, *custom, *buf;
+
+ pos = data;
+ end = data + len;
+
+ while (pos + IW_EV_LCP_LEN <= end) {
+ iwe = (struct iw_event *) pos;
+ HOSTAPD_DEBUG(HOSTAPD_DEBUG_VERBOSE, "Wireless event: "
+ "cmd=0x%x len=%d\n", iwe->cmd, iwe->len);
+ if (iwe->len <= IW_EV_LCP_LEN)
+ return;
+ switch (iwe->cmd) {
+ case IWEVCUSTOM:
+ custom = pos + IW_EV_POINT_LEN;
+ if (custom + iwe->u.data.length > end)
+ return;
+ buf = malloc(iwe->u.data.length + 1);
+ if (buf == NULL)
+ return;
+ memcpy(buf, custom, iwe->u.data.length);
+ buf[iwe->u.data.length] = '\0';
+ hostapd_wireless_event_wireless_custom(hapd, buf);
+ free(buf);
+ break;
+ }
+
+ pos += iwe->len;
+ }
+}
+
+
+static void hostapd_wireless_event_rtm_newlink(struct hostapd_data *hapd,
+ struct nlmsghdr *h, int len)
+{
+ struct ifinfomsg *ifi;
+ int attrlen, nlmsg_len, rta_len;
+ struct rtattr * attr;
+
+ if (len < sizeof(*ifi))
+ return;
+
+ ifi = NLMSG_DATA(h);
+
+ /* TODO: use ifi->ifi_index to filter out wireless events from other
+ * interfaces */
+
+ nlmsg_len = NLMSG_ALIGN(sizeof(struct ifinfomsg));
+
+ attrlen = h->nlmsg_len - nlmsg_len;
+ if (attrlen < 0)
+ return;
+
+ attr = (struct rtattr *) (((char *) ifi) + nlmsg_len);
+
+ rta_len = RTA_ALIGN(sizeof(struct rtattr));
+ while (RTA_OK(attr, attrlen)) {
+ if (attr->rta_type == IFLA_WIRELESS) {
+ hostapd_wireless_event_wireless(
+ hapd, ((char *) attr) + rta_len,
+ attr->rta_len - rta_len);
+ }
+ attr = RTA_NEXT(attr, attrlen);
+ }
+}
+
+
+static void hostapd_wireless_event_receive(int sock, void *eloop_ctx,
+ void *sock_ctx)
+{
+ char buf[256];
+ int left;
+ struct sockaddr_nl from;
+ socklen_t fromlen;
+ struct nlmsghdr *h;
+ struct hostapd_data *hapd = eloop_ctx;
+
+ fromlen = sizeof(from);
+ left = recvfrom(sock, buf, sizeof(buf), MSG_DONTWAIT,
+ (struct sockaddr *) &from, &fromlen);
+ if (left < 0) {
+ if (errno != EINTR && errno != EAGAIN)
+ perror("recvfrom(netlink)");
+ return;
+ }
+
+ h = (struct nlmsghdr *) buf;
+ while (left >= sizeof(*h)) {
+ int len, plen;
+
+ len = h->nlmsg_len;
+ plen = len - sizeof(*h);
+ if (len > left || plen < 0) {
+ printf("Malformed netlink message: "
+ "len=%d left=%d plen=%d\n",
+ len, left, plen);
+ break;
+ }
+
+ switch (h->nlmsg_type) {
+ case RTM_NEWLINK:
+ hostapd_wireless_event_rtm_newlink(hapd, h, plen);
+ break;
+ }
+
+ len = NLMSG_ALIGN(len);
+ left -= len;
+ h = (struct nlmsghdr *) ((char *) h + len);
+ }
+
+ if (left > 0) {
+ printf("%d extra bytes in the end of netlink message\n", left);
+ }
+}
+
+
+int hostapd_wireless_event_init(struct hostapd_data *hapd)
+{
+ int s;
+ struct sockaddr_nl local;
+
+ hapd->wext_sock = -1;
+
+ s = socket(PF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
+ if (s < 0) {
+ perror("socket(PF_NETLINK,SOCK_RAW,NETLINK_ROUTE)");
+ return -1;
+ }
+
+ memset(&local, 0, sizeof(local));
+ local.nl_family = AF_NETLINK;
+ local.nl_groups = RTMGRP_LINK;
+ if (bind(s, (struct sockaddr *) &local, sizeof(local)) < 0) {
+ perror("bind(netlink)");
+ close(s);
+ return -1;
+ }
+
+ eloop_register_read_sock(s, hostapd_wireless_event_receive, hapd,
+ NULL);
+ hapd->wext_sock = s;
+
+ return 0;
+}
+
+
+void hostapd_wireless_event_deinit(struct hostapd_data *hapd)
+{
+ if (hapd->wext_sock < 0)
+ return;
+ eloop_unregister_read_sock(hapd->wext_sock);
+ close(hapd->wext_sock);
+}