Skip site navigation (1)Skip section navigation (2)
Date:      Mon, 31 Jul 2017 17:33:57 +0000 (UTC)
From:      Adrian Chadd <adrian@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org
Subject:   svn commit: r321800 - head/tools/tools/net80211/wlanwds
Message-ID:  <201707311733.v6VHXv7K052447@repo.freebsd.org>

next in thread | raw e-mail | index | archive | help
Author: adrian
Date: Mon Jul 31 17:33:57 2017
New Revision: 321800
URL: https://svnweb.freebsd.org/changeset/base/321800

Log:
  [wlanwds] allow for a DWDS AP VAP to be not be the first VAP on a NIC.
  
  The wlanwds code was just creating a clone VAP without specifying the MAC
  address to use for said clone VAP.  This meant that if an interface
  was cloned from an AP interface that wasn't the first created VAP
  (which shares the same MAC as the parent physical interface by default)
  then the cloned interface would have the wrong MAC and traffic wouldn't work.
  
  Besides chip bugs (ha!) this isn't a requirement.
  
  So, teach wlanwds to:
  
  * look up the link layer address for a given interface (which really should
    be a library interface, and will likely quickly become one);
  * use this when creating a cloned interface for a DWDS peer;
  * (net80211 already has the infrastructure to do this, it just needed to be
    used);
  * add some extra logging to see what MAC addresses, parent interfaces, etc
    are being created.
  
  Whilst here, add a reminder that I should extend this to include monitoring
  a specific VAP for DWDS updates rather than just the parent interface.
  
  This is the first step in allowing for multiple DWDS hops, which is a
  pre-requisite for adrian's house having wifi in the single upstairs room.
  
  Tested:
  
  * AR9380, DWDS AP + AP mode - with DWDS AP being the second VAP created
    with a different MAC address;
  * AR9331 (Carambola2), AP + DWDS STA;
  * passing traffic
  
  TODO:
  
  * fix 802.11s so this DWDS stuff is no longer required!

Modified:
  head/tools/tools/net80211/wlanwds/wlanwds.c

Modified: head/tools/tools/net80211/wlanwds/wlanwds.c
==============================================================================
--- head/tools/tools/net80211/wlanwds/wlanwds.c	Mon Jul 31 16:55:56 2017	(r321799)
+++ head/tools/tools/net80211/wlanwds/wlanwds.c	Mon Jul 31 17:33:57 2017	(r321800)
@@ -55,6 +55,9 @@
 #include <arpa/inet.h>
 #include <netdb.h>
 
+#include <net/if.h>
+#include <net/if_types.h>
+
 #include <ctype.h>
 #include <err.h>
 #include <errno.h>
@@ -90,7 +93,8 @@ static	void wds_discovery(const char *ifname,
 		const uint8_t bssid[IEEE80211_ADDR_LEN]);
 static	void wds_destroy(const char *ifname);
 static	void wds_leave(const uint8_t bssid[IEEE80211_ADDR_LEN]);
-static	int wds_vap_create(const char *ifname, struct wds *);
+static	int wds_vap_create(const char *ifname, uint8_t macaddr[ETHER_ADDR_LEN],
+	    struct wds *);
 static	int wds_vap_destroy(const char *ifname);
 
 static void
@@ -201,6 +205,11 @@ getparent(const char *ifname, char parent[IFNAMSIZ+1])
  * Check if the specified ifnet is one we're supposed to monitor.
  * The ifnet is assumed to be a vap; we find it's parent and check
  * it against the set of ifnet's specified on the command line.
+ *
+ * TODO: extend this to also optionally allow the specific DWDS
+ * VAP to be monitored, instead of assuming all VAPs on a parent
+ * physical interface are being monitored by this instance of
+ * wlanwds.
  */
 static int
 checkifnet(const char *ifname, int complain)
@@ -257,6 +266,70 @@ getbssid(int s, const char *ifname, uint8_t bssid[IEEE
 }
 
 /*
+ * Fetch the mac address configured for a given ifnet.
+ * (Note - the current link level address, NOT hwaddr.)
+ *
+ * This is currently, sigh, O(n) because there's no current kernel
+ * API that will do it for a single interface.
+ *
+ * Return 0 if successful, -1 if failure.
+ */
+static int
+getlladdr(const char *ifname, uint8_t macaddr[ETHER_ADDR_LEN])
+{
+	struct ifaddrs *ifap, *ifa;
+	struct sockaddr_dl *sdl;
+
+	if (getifaddrs(&ifap) < 0) {
+		warn("%s: getifaddrs", __func__);
+		return (-1);
+	}
+
+	/* Look for a matching interface */
+	for (ifa = ifap; ifa != NULL; ifa++) {
+		if (strcmp(ifname, ifa->ifa_name) != 0)
+			continue;
+
+		/* Found it - check if there's an ifa_addr */
+		if (ifa->ifa_addr == NULL) {
+			syslog(LOG_CRIT, "%s: ifname %s; ifa_addr is NULL\n",
+			    __func__, ifname);
+			goto err;
+		}
+
+		/* Check address family */
+		sdl = (struct sockaddr_dl *) ifa->ifa_addr;
+		if (sdl->sdl_type != IFT_ETHER) {
+			syslog(LOG_CRIT, "%s: %s: unknown aftype (%d)\n",
+			    __func__,
+			    ifname,
+			    sdl->sdl_type);
+			goto err;
+		}
+		if (sdl->sdl_alen != ETHER_ADDR_LEN) {
+			syslog(LOG_CRIT, "%s: %s: aflen too short (%d)\n",
+			    __func__,
+			    ifname,
+			    sdl->sdl_alen);
+			goto err;
+		}
+
+		/* Ok, found it */
+		memcpy(macaddr, (void *) LLADDR(sdl), ETHER_ADDR_LEN);
+		goto ok;
+	}
+	syslog(LOG_CRIT, "%s: couldn't find ifname %s\n", __func__, ifname);
+	/* FALLTHROUGH */
+err:
+	freeifaddrs(ifap);
+	return (-1);
+
+ok:
+	freeifaddrs(ifap);
+	return (0);
+}
+
+/*
  * Scan the system for WDS vaps associated with the ifnet's we're
  * supposed to monitor.  Any vaps are added to our internal table
  * so we can find them (and destroy them) on station leave.
@@ -374,6 +447,7 @@ wds_discovery(const char *ifname, const uint8_t bssid[
 	struct wds *p;
 	char parent[256];
 	char cmd[1024];
+	uint8_t macaddr[ETHER_ADDR_LEN];
 	int status;
 
 	for (p = wds; p != NULL; p = p->next)
@@ -388,13 +462,19 @@ wds_discovery(const char *ifname, const uint8_t bssid[
 		return;
 	}
 
+	if (getlladdr(ifname, macaddr) < 0) {
+		syslog(LOG_ERR, "%s: couldn't get lladdr for parent interface: %m",
+		    ifname);
+		return;
+	}
+
 	p = malloc(sizeof(struct wds));
 	if (p == NULL) {
 		syslog(LOG_ERR, "%s: malloc failed: %m", __func__);
 		return;
 	}
 	IEEE80211_ADDR_COPY(p->bssid, bssid);
-	if (wds_vap_create(parent, p) < 0) {
+	if (wds_vap_create(parent, macaddr, p) < 0) {
 		free(p);
 		return;
 	}
@@ -403,8 +483,11 @@ wds_discovery(const char *ifname, const uint8_t bssid[
 	 */
 	p->next = wds;
 	wds = p;
-	syslog(LOG_INFO, "[%s] create wds vap %s", ether_sprintf(bssid),
-	    p->ifname);
+	syslog(LOG_INFO, "[%s] create wds vap %s, parent %s (%s)",
+	    ether_sprintf(bssid),
+	    p->ifname,
+	    ifname,
+	    parent);
 	if (script != NULL) {
 		snprintf(cmd, sizeof(cmd), "%s %s", script, p->ifname);
 		status = system(cmd);
@@ -454,17 +537,35 @@ wds_leave(const uint8_t bssid[IEEE80211_ADDR_LEN])
 }
 
 static int
-wds_vap_create(const char *parent, struct wds *p)
+wds_vap_create(const char *parent, uint8_t macaddr[ETHER_ADDR_LEN],
+    struct wds *p)
 {
 	struct ieee80211_clone_params cp;
 	struct ifreq ifr;
 	int s, status;
+	char bssid_str[32], macaddr_str[32];
 
 	memset(&cp, 0, sizeof(cp));
+
+	/* Parent interface */
 	strncpy(cp.icp_parent, parent, IFNAMSIZ);
+
+	/* WDS interface */
 	cp.icp_opmode = IEEE80211_M_WDS;
+
+	/* BSSID for the current node */
 	IEEE80211_ADDR_COPY(cp.icp_bssid, p->bssid);
 
+	/*
+	 * Set the MAC address to match the actual interface
+	 * that we received the discovery event from.
+	 * That way we can run WDS on any VAP rather than
+	 * only the first VAP and then correctly set the
+	 * MAC address.
+	 */
+	cp.icp_flags |= IEEE80211_CLONE_MACADDR;
+	IEEE80211_ADDR_COPY(cp.icp_macaddr, macaddr);
+
 	memset(&ifr, 0, sizeof(ifr));
 	strncpy(ifr.ifr_name, "wlan", IFNAMSIZ);
 	ifr.ifr_data = (void *) &cp;
@@ -477,9 +578,10 @@ wds_vap_create(const char *parent, struct wds *p)
 			status = 0;
 		} else {
 			syslog(LOG_ERR, "SIOCIFCREATE2("
-			    "mode %u flags 0x%x parent %s bssid %s): %m",
+			    "mode %u flags 0x%x parent %s bssid %s macaddr %s): %m",
 			    cp.icp_opmode, cp.icp_flags, parent,
-			    ether_sprintf(cp.icp_bssid));
+			    ether_ntoa_r((void *) cp.icp_bssid, bssid_str),
+			    ether_ntoa_r((void *) cp.icp_macaddr, macaddr_str));
 		}
 		close(s);
 	} else



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201707311733.v6VHXv7K052447>