Merge "conman: Enable/disable ACS autoprovisioning."
diff --git a/cmds/dhcp-rogue.c b/cmds/dhcp-rogue.c
index 6fc16c3..8d590bf 100644
--- a/cmds/dhcp-rogue.c
+++ b/cmds/dhcp-rogue.c
@@ -22,8 +22,13 @@
#include <arpa/inet.h>
#include <fcntl.h>
#include <getopt.h>
+#include <net/ethernet.h>
#include <net/if.h>
#include <net/if_arp.h>
+#include <net/if_packet.h>
+#include <netinet/ip.h>
+#include <netinet/udp.h>
+#include <netpacket/packet.h>
#include <stdint.h>
#include <string.h>
#include <stdio.h>
@@ -31,9 +36,15 @@
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/stat.h>
+#include <time.h>
#include <unistd.h>
+
+#define DHCP_SERVER_PORT 67
+#define DHCP_CLIENT_PORT 68
+
struct dhcp_message {
+ /* DHCP packet */
uint8_t op;
#define OP_BOOTREQUEST 1
uint8_t htype;
@@ -53,20 +64,32 @@
char file[128];
uint8_t magic[4];
uint8_t type[3];
-};
+ uint8_t end;
+} __attribute__ ((__packed__));
-int create_socket(const char *ifname)
+
+struct dhcp_packet {
+ struct ether_header eth;
+ struct ip ip;
+ struct udphdr udp;
+ struct dhcp_message dhcp;
+} __attribute__ ((__packed__));
+
+
+struct udp_checksum_helper {
+ uint32_t ip_src;
+ uint32_t ip_dst;
+ uint8_t rsvd;
+ uint8_t ip_p;
+ uint16_t udp_len;
+ struct udphdr udp;
+ struct dhcp_message dhcp;
+} __attribute__ ((__packed__));
+
+
+void bind_socket_to_device(int s, const char *ifname)
{
- int s = socket(AF_INET, SOCK_DGRAM, 0);
- struct sockaddr_in sin;
struct ifreq ifr;
- int enable = 1;
- struct timeval tv;
-
- if (s < 0) {
- perror("socket(AF_INET)");
- exit(1);
- }
memset(&ifr, 0, sizeof(ifr));
snprintf(ifr.ifr_name, sizeof(ifr.ifr_name), "%s", ifname);
@@ -76,6 +99,23 @@
perror("SO_BINDTODEVICE");
exit(1);
}
+}
+
+
+int create_udp_socket(const char *ifname)
+{
+ int s;
+ struct sockaddr_in sin;
+ int enable = 1;
+ int ipttl = 2;
+ struct timeval tv;
+
+ if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
+ perror("socket(SOCK_DGRAM)");
+ exit(1);
+ }
+
+ bind_socket_to_device(s, ifname);
if (setsockopt(s, SOL_SOCKET, SO_BROADCAST, &enable, sizeof(enable))) {
perror("SO_BROADCAST");
@@ -87,24 +127,22 @@
exit(1);
}
- tv.tv_sec = 5;
+ tv.tv_sec = 15;
tv.tv_usec = 0;
if (setsockopt(s, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv))) {
perror("SO_RCVTIMEO");
exit(1);
}
- memset(&ifr, 0, sizeof(ifr));
- snprintf(ifr.ifr_name, sizeof(ifr.ifr_name), "%s", ifname);
-
- if (ioctl(s, SIOCGIFADDR, &ifr) < 0) {
- perror("SIOCGIFADDR");
+ if (setsockopt(s, IPPROTO_IP, IP_TTL, &ipttl, sizeof(ipttl))) {
+ perror("IP_TTL");
exit(1);
}
memset(&sin, 0, sizeof(sin));
sin.sin_family = AF_INET;
- sin.sin_port = htons(68);
+ sin.sin_addr.s_addr=htonl(INADDR_ANY);
+ sin.sin_port = htons(DHCP_CLIENT_PORT);
if (bind(s, (struct sockaddr *)&sin, sizeof(sin)) < 0) {
perror("bind");
@@ -114,6 +152,28 @@
return s;
}
+
+int create_raw_socket(const char *ifname)
+{
+ int s;
+ int enable = 1;
+
+ if ((s = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_IP))) < 0) {
+ perror("socket(PF_PACKET)");
+ exit(1);
+ }
+
+ bind_socket_to_device(s, ifname);
+
+ if (setsockopt(s, SOL_SOCKET, SO_BROADCAST, &enable, sizeof(enable))) {
+ perror("SO_BROADCAST");
+ exit(1);
+ }
+
+ return s;
+}
+
+
void get_chaddr(uint8_t *chaddr, int s, const char *ifname)
{
struct ifreq ifr;
@@ -134,71 +194,245 @@
memcpy(chaddr, ifr.ifr_hwaddr.sa_data, 6);
}
-void send_dhcp_discover(int s, const char *ifname)
+uint16_t ipsum(const uint8_t *data, int len)
{
- struct dhcp_message msg;
- struct sockaddr_in sin;
- socklen_t slen = sizeof(sin);
+ uint32_t sum = 0;
+ const uint16_t *p = (const uint16_t *)data;
- memset(&msg, 0, sizeof(msg));
- msg.op = OP_BOOTREQUEST;
- msg.htype = HTYPE_ETHERNET;
- msg.hlen = 6;
- msg.xid = 0;
- msg.flags = htons(FLAGS_BROADCAST);
- get_chaddr(msg.chaddr, s, ifname);
- snprintf(msg.sname, sizeof(msg.sname), "%s", "rogue_dhcp_server_detection");
- msg.magic[0] = 99; /* DHCP magic number, RFC 2133 */
- msg.magic[1] = 130;
- msg.magic[2] = 83;
- msg.magic[3] = 99;
- msg.type[0] = 53; /* option 53, DHCP type. */
- msg.type[1] = 1; /* length = 1 */
- msg.type[2] = 1; /* DHCPDISCOVER */
+ while (len > 1) {
+ sum += *p++;
+ len -= 2;
+ }
+
+ if (len) {
+ const uint8_t *p8 = (const uint8_t *)p;
+ sum += (uint16_t) *p8;
+ }
+
+ while (sum >> 16) {
+ sum = (sum & 0xFFFF) + (sum >> 16);
+ }
+
+ return ~sum;
+}
+
+
+int getifindex(int s, const char *ifname)
+{
+ struct ifreq ifr;
+
+ memset(&ifr, 0, sizeof(ifr));
+ snprintf(ifr.ifr_name, sizeof(ifr.ifr_name), "%s", ifname);
+
+ if (ioctl(s, SIOCGIFINDEX, &ifr) < 0) {
+ char errbuf[128];
+ snprintf(errbuf, sizeof(errbuf), "SIOCGIFINDEX %s", ifname);
+ perror(errbuf);
+ exit(1);
+ }
+
+ return ifr.ifr_ifindex;
+}
+
+
+void insert_udp_checksum(struct dhcp_packet *pkt)
+{
+ struct udp_checksum_helper csum_helper;
+
+ memset(&csum_helper, 0, sizeof(csum_helper));
+ memcpy(&csum_helper.udp, &pkt->udp, sizeof(csum_helper.udp));
+ memcpy(&csum_helper.dhcp, &pkt->dhcp, sizeof(csum_helper.dhcp));
+ csum_helper.ip_src = pkt->ip.ip_src.s_addr;
+ csum_helper.ip_dst = pkt->ip.ip_dst.s_addr;
+ csum_helper.ip_p = pkt->ip.ip_p;
+ csum_helper.udp_len = pkt->udp.len;
+ pkt->udp.check = ipsum((const uint8_t *)&csum_helper, sizeof(csum_helper));
+}
+
+
+void send_dhcp_discover(int udp_sock, const char *ifname)
+{
+ int s = create_raw_socket(ifname);
+ struct dhcp_packet pkt;
+ struct sockaddr_ll sll;
+ socklen_t slen = sizeof(sll);
+ struct sockaddr_in sin;
+
+ memset(&pkt, 0, sizeof(pkt));
+ memset(&pkt.eth.ether_dhost, 0xff, sizeof(pkt.eth.ether_dhost));
+ get_chaddr(pkt.eth.ether_shost, s, ifname);
+ pkt.eth.ether_type = htons(ETH_P_IP);
+
+ pkt.ip.ip_v = 4;
+ pkt.ip.ip_hl = 5;
+ pkt.ip.ip_ttl = 2;
+ pkt.ip.ip_p = 17;
+ inet_pton(AF_INET, "0.0.0.0", &pkt.ip.ip_src);
+ inet_pton(AF_INET, "255.255.255.255", &pkt.ip.ip_dst);
+ pkt.ip.ip_len = htons(sizeof(pkt.ip) + sizeof(pkt.udp) + sizeof(pkt.dhcp));
+ pkt.ip.ip_sum = ipsum((const uint8_t *)&pkt.ip, sizeof(pkt.ip));
+
+ pkt.udp.source = htons(DHCP_CLIENT_PORT);
+ pkt.udp.dest = htons(DHCP_SERVER_PORT);
+ pkt.udp.len = htons(sizeof(pkt.udp) + sizeof(pkt.dhcp));
+ pkt.udp.check = htons(0);
+
+ pkt.dhcp.op = OP_BOOTREQUEST;
+ pkt.dhcp.htype = HTYPE_ETHERNET;
+ pkt.dhcp.hlen = 6;
+ pkt.dhcp.xid = htonl(time(NULL));
+ pkt.dhcp.secs = htons(1);
+ pkt.dhcp.flags = htons(FLAGS_BROADCAST);
+ get_chaddr(pkt.dhcp.chaddr, s, ifname);
+ snprintf(pkt.dhcp.sname, sizeof(pkt.dhcp.sname), "%s",
+ "rogue_dhcp_server_detection");
+ pkt.dhcp.magic[0] = 99; /* DHCP magic number, RFC 2133 */
+ pkt.dhcp.magic[1] = 130;
+ pkt.dhcp.magic[2] = 83;
+ pkt.dhcp.magic[3] = 99;
+ pkt.dhcp.type[0] = 53; /* option 53, DHCP type. */
+ pkt.dhcp.type[1] = 1; /* length = 1 */
+ pkt.dhcp.type[2] = 1; /* DHCPDISCOVER */
+ pkt.dhcp.end = 0xff; /* End option */
+
+ insert_udp_checksum(&pkt);
+
+ memset(&sll, 0, sizeof(sll));
+ sll.sll_family = AF_PACKET;
+ memset(&sll.sll_addr, 0xff, ETH_ALEN);
+ sll.sll_halen = ETH_ALEN;
+ sll.sll_ifindex = getifindex(s, ifname);
+ sll.sll_pkttype = PACKET_BROADCAST;
+
+ if (sendto(s, &pkt, sizeof(pkt), 0,
+ (const struct sockaddr *)&sll, slen) < 0) {
+ perror("sendto");
+ exit(1);
+ }
+
+ close(s);
+
+ /*
+ * We send two DHCP requests. The PF_PACKET socket above
+ * sends a packet with a soruce IP address of 0.0.0.0, and
+ * sends it straight to the Ethernet link such that the
+ * local dnsmasq does not see it.
+ *
+ * We send another one here using a PF_INET socket, which
+ * will have a source IP address of this node, and which will
+ * also be copied to the local dnsmasq.
+ */
memset(&sin, 0, sizeof(sin));
sin.sin_family = AF_INET;
sin.sin_addr.s_addr = htonl(INADDR_BROADCAST);
- sin.sin_port = htons(67);
+ sin.sin_port = htons(DHCP_SERVER_PORT);
+ slen = sizeof(sin);
- if (sendto(s, &msg, sizeof(msg), 0,
+ if (sendto(udp_sock, &pkt.dhcp, sizeof(pkt.dhcp), 0,
(const struct sockaddr *)&sin, slen) < 0) {
- perror("sendto enable");
+ perror("sendto");
exit(1);
}
}
+
+static int cmp_in_addr_p(const void *p1, const void *p2)
+{
+ const struct in_addr *i1 = (const struct in_addr *)p1;
+ const struct in_addr *i2 = (const struct in_addr *)p2;
+
+ if (i1->s_addr == i2->s_addr) {
+ return 0;
+ } else if (i1->s_addr < i2->s_addr) {
+ return -1;
+ } else {
+ return 1;
+ }
+}
+
+
void receive_dhcp_offers(int s)
{
struct sockaddr_in sin;
socklen_t slen = sizeof(sin);
- uint8_t buf[2048];
+ uint8_t pktbuf[2048];
+ #define MAX_RESPONSES 4
+ struct in_addr responses[MAX_RESPONSES];
+ int nresponses = 0;
memset(&sin, 0, sizeof(sin));
- while (recvfrom(s, buf, sizeof(buf), 0, &sin, &slen) > 0) {
- char ipbuf[64];
- inet_ntop(AF_INET, &sin.sin_addr, ipbuf, sizeof(ipbuf));
- printf("DHCP response from %s\n", ipbuf);
+ memset(responses, 0, sizeof(responses));
+
+ while (recvfrom(s, pktbuf, sizeof(pktbuf), 0, &sin, &slen) > 0) {
+ int duplicate = 0;
+ int i;
+
+ if (nresponses >= MAX_RESPONSES) {
+ break;
+ }
+
+ for (i = 0; i < MAX_RESPONSES; ++i) {
+ if (responses[i].s_addr == sin.sin_addr.s_addr) {
+ duplicate = 1;
+ break;
+ }
+ }
+
+ if (!duplicate) {
+ responses[nresponses].s_addr = sin.sin_addr.s_addr;
+ nresponses++;
+ }
+ }
+
+ if (nresponses == 0) {
+ printf("Received 0 DHCP responses.\n");
+ } else {
+ char outbuf[(MAX_RESPONSES * (INET_ADDRSTRLEN + 1)) + 1];
+ int i;
+
+ qsort(responses, nresponses, sizeof(responses[0]), cmp_in_addr_p);
+
+ outbuf[0] = '\0';
+ for (i = 0; i < nresponses; ++i) {
+ int len = strlen(outbuf);
+ int lim = sizeof(outbuf) - len;
+
+ if (i > 0) {
+ strcat(outbuf, ",");
+ len = strlen(outbuf);
+ lim = sizeof(outbuf) - len;
+ }
+
+ inet_ntop(AF_INET, &responses[i], outbuf + len, lim);
+ }
+
+ /*
+ * Yes, this will print "Received 1 DHCP responses". It
+ * complicates any matching code to make the 's' optional,
+ * for no benefit. OCD will have to find a way to cope.
+ */
+ printf("Received %d DHCP responses from: %s\n", nresponses, outbuf);
}
}
void usage(const char *progname)
{
- printf("usage: %s [-i br0]\n", progname);
- printf("\t-i: name of the interface to probe for rogue DHCP servers.\n");
+ fprintf(stderr, "usage: %s [-i br0] [-l]\n", progname);
+ fprintf(stderr, "\t-i: name of the interface to probe for DHCP servers.\n");
+ fprintf(stderr, "\t-l: show a response from localhost\n");
exit(1);
}
int main(int argc, char **argv)
{
- int s;
const char *interface = "br0";
struct option long_options[] = {
{"interface", required_argument, 0, 'i'},
- {0, 0, 0, 0},
+ {0, 0, 0, 0},
};
+ int s, c;
- int c;
while ((c = getopt_long(argc, argv, "i:", long_options, NULL)) != -1) {
switch (c) {
case 'i':
@@ -211,7 +445,8 @@
}
}
- s = create_socket(interface);
+ setlinebuf(stdout);
+ s = create_udp_socket(interface);
send_dhcp_discover(s, interface);
receive_dhcp_offers(s);
}
diff --git a/conman/connection_manager.py b/conman/connection_manager.py
index 5faa789..708954f 100755
--- a/conman/connection_manager.py
+++ b/conman/connection_manager.py
@@ -85,6 +85,10 @@
if self.ssid is None:
raise ValueError('Command file does not specify SSID')
+ if self.wifi.initial_ssid == self.ssid:
+ logging.debug('Connected to WLAN at startup')
+ self.client_up = True
+
def start_access_point(self):
"""Start an access point."""
@@ -241,11 +245,21 @@
# If the ethernet file doesn't exist for any reason when conman starts,
# check explicitly and run ifplugd.action to create the file.
- if not os.path.exists(os.path.join(self._interface_status_dir,
- self.ETHERNET_STATUS_FILE)):
- ethernet_up = self.is_ethernet_up()
- self.bridge.ethernet = ethernet_up
+ if not os.path.exists(os.path.join(self._interface_status_dir, 'eth0')):
+ ethernet_up = self.is_interface_up('eth0')
self.ifplugd_action('eth0', ethernet_up)
+ self.bridge.ethernet = ethernet_up
+
+ # Do the same for wifi interfaces , but rather than explicitly setting that
+ # the wpa_supplicant link is up, attempt to attach to the wpa_supplicant
+ # control interface.
+ for wifi in self.wifi:
+ if not os.path.exists(
+ os.path.join(self._interface_status_dir, wifi.name)):
+ wifi_up = self.is_interface_up(wifi.name)
+ self.ifplugd_action(wifi.name, wifi_up)
+ if wifi_up:
+ wifi.attach_wpa_control(self._wpa_control_interface)
for path, prefix in ((self._status_dir, self.GATEWAY_FILE_PREFIX),
(self._interface_status_dir, ''),
@@ -263,13 +277,16 @@
self._interface_update_counter = 0
self._try_wlan_after = {'5': 0, '2.4': 0}
- def is_ethernet_up(self):
- """Explicitly check whether ethernet is up.
+ def is_interface_up(self, interface_name):
+ """Explicitly check whether an interface is up.
Only used on startup, and only if ifplugd file is missing.
+ Args:
+ interface_name: The name of the interface to check.
+
Returns:
- Whether the ethernet link is up.
+ Whether the interface is up.
"""
try:
lines = subprocess.check_output(self.IP_LINK).splitlines()
@@ -277,7 +294,7 @@
raise EnvironmentError('Failed to call "ip link": %r', e.message)
for line in lines:
- if 'eth0' in line and 'LOWER_UP' in line:
+ if interface_name in line and 'LOWER_UP' in line:
return True
return False
diff --git a/conman/connection_manager_test.py b/conman/connection_manager_test.py
index 0764ece..335f7ee 100755
--- a/conman/connection_manager_test.py
+++ b/conman/connection_manager_test.py
@@ -193,13 +193,39 @@
WIFI_SETCLIENT = ['echo', 'setclient']
IFUP = ['echo', 'ifup']
IFPLUGD_ACTION = ['echo', 'ifplugd.action']
- # This simulates the output of 'ip link' when eth0 is up.
- IP_LINK = ['echo', 'eth0 LOWER_UP']
def __init__(self, *args, **kwargs):
+ self.interfaces_already_up = kwargs.pop('__test_interfaces_already_up',
+ ['eth0'])
+
+ wifi_interfaces_already_up = [ifc for ifc in self.interfaces_already_up
+ if ifc.startswith('wcli')]
+ for wifi in wifi_interfaces_already_up:
+ # wcli1 is always 5 GHz. wcli0 always *includes* 2.4.
+ band = '5' if wifi == 'wcli1' else '2.4'
+ # This will happen in the super function, but in order for
+ # write_wlan_config to work we have to do it now. This has to happen
+ # before the super function so that the files exist before the inotify
+ # registration.
+ self._config_dir = kwargs['config_dir']
+ self.write_wlan_config(band, 'my ssid', 'passphrase')
+
+ # Also create the wpa_supplicant socket to which to attach.
+ open(os.path.join(kwargs['wpa_control_interface'], wifi), 'w')
+
super(ConnectionManager, self).__init__(*args, **kwargs)
+
+ for wifi in wifi_interfaces_already_up:
+ # pylint: disable=protected-access
+ self.interface_by_name(wifi)._initially_connected = True
+
self.scan_has_results = False
+ @property
+ def IP_LINK(self):
+ return ['echo'] + ['%s LOWER_UP' % ifc
+ for ifc in self.interfaces_already_up]
+
def _update_access_point(self, wlan_configuration):
client_was_up = wlan_configuration.client_up
super(ConnectionManager, self)._update_access_point(wlan_configuration)
@@ -356,7 +382,7 @@
self.run_once()
-def connection_manager_test(radio_config):
+def connection_manager_test(radio_config, **cm_kwargs):
"""Returns a decorator that does ConnectionManager test boilerplate."""
def inner(f):
"""The actual decorator."""
@@ -388,7 +414,8 @@
wpa_control_interface=wpa_control_interface,
run_duration_s=run_duration_s,
interface_update_period=interface_update_period,
- wifi_scan_period_s=wifi_scan_period_s)
+ wifi_scan_period_s=wifi_scan_period_s,
+ **cm_kwargs)
c.test_interface_update_period = interface_update_period
c.test_wifi_scan_period = wifi_scan_period
@@ -727,6 +754,7 @@
wvtest.WVPASS(c.wifi_for_band('5').current_route())
+
@wvtest.wvtest
@connection_manager_test(WIFI_SHOW_OUTPUT_ONE_RADIO_NO_5GHZ)
def connection_manager_test_one_radio_no_5ghz(c):
@@ -764,5 +792,18 @@
wvtest.WVPASS(c.wifi_for_band('2.4').current_route())
+@wvtest.wvtest
+@connection_manager_test(WIFI_SHOW_OUTPUT_ONE_RADIO,
+ __test_interfaces_already_up=['eth0', 'wcli0'])
+def connection_manager_test_wifi_already_up(c):
+ """Test ConnectionManager when wifi is already up.
+
+ Args:
+ c: The ConnectionManager set up by @connection_manager_test.
+ """
+ wvtest.WVPASS(c._connected_to_wlan(c.wifi_for_band('2.4')))
+ wvtest.WVPASS(c.wifi_for_band('2.4').current_route)
+
+
if __name__ == '__main__':
wvtest.wvtest_main()
diff --git a/conman/interface.py b/conman/interface.py
index 13cadc5..0f42e20 100755
--- a/conman/interface.py
+++ b/conman/interface.py
@@ -319,6 +319,7 @@
self.bands = kwargs.pop('bands', [])
super(Wifi, self).__init__(*args, **kwargs)
self._wpa_control = None
+ self.initial_ssid = None
@property
def wpa_supplicant(self):
@@ -344,8 +345,14 @@
logging.error('Error attaching to wpa_supplicant: %s', e)
return
- self.wpa_supplicant = ('wpa_state=COMPLETED' in
- self._wpa_control.request('STATUS'))
+ for line in self._wpa_control.request('STATUS').splitlines():
+ if '=' not in line:
+ continue
+ key, value = line.split('=', 1)
+ if key == 'wpa_state':
+ self.wpa_supplicant = value == 'COMPLETED'
+ elif key == 'ssid' and not self._initialized:
+ self.initial_ssid = value
def get_wpa_control(self, socket):
return wpactrl.WPACtrl(socket)
@@ -380,3 +387,10 @@
break
self.update_routes()
+
+ def initialize(self):
+ """Unset self.initial_ssid, which is only relevant during initialization."""
+
+ self.initial_ssid = None
+ super(Wifi, self).initialize()
+
diff --git a/conman/interface_test.py b/conman/interface_test.py
index f15764f..f6e03d2 100755
--- a/conman/interface_test.py
+++ b/conman/interface_test.py
@@ -89,7 +89,8 @@
def request(self, request_type):
if request_type == 'STATUS':
- return 'foo\nwpa_state=COMPLETED\nbar' if self.connected else 'foo'
+ return ('foo\nwpa_state=COMPLETED\nssid=my ssid\nbar' if self.connected
+ else 'foo')
else:
raise ValueError('Invalid request_type %s' % request_type)
@@ -234,11 +235,15 @@
w.detach_wpa_control()
# pylint: disable=protected-access
w._initially_connected = True
+ w._initialized = False
w.attach_wpa_control(wpa_path)
wpa_control = w._wpa_control
# wpa_supplicant was already connected when we attached.
wvtest.WVPASS(w.wpa_supplicant)
+ wvtest.WVPASSEQ(w.initial_ssid, 'my ssid')
+ w.initialize()
+ wvtest.WVPASSEQ(w.initial_ssid, None)
# The wpa_supplicant process disconnects and terminates.
wpa_control.add_event(Wifi.DISCONNECTED_EVENT)
diff --git a/jsonpoll/jsonpoll.py b/jsonpoll/jsonpoll.py
index 9e78b8e..7e0c7ea 100755
--- a/jsonpoll/jsonpoll.py
+++ b/jsonpoll/jsonpoll.py
@@ -31,7 +31,7 @@
jsonpoll [options]
--
host= host to connect to [localhost]
-port= port to connect to [8000]
+port= port to connect to [8080]
i,interval= poll interval in seconds [15]
"""
diff --git a/taxonomy/ethernet.py b/taxonomy/ethernet.py
index f8966f3..d0aaf45 100644
--- a/taxonomy/ethernet.py
+++ b/taxonomy/ethernet.py
@@ -61,6 +61,7 @@
'7c:61:93': ['htc'],
'84:7a:88': ['htc'],
'90:e7:c4': ['htc'],
+ 'a0:f4:50': ['htc'],
'b4:ce:f6': ['htc'],
'd8:b3:77': ['htc'],
'e8:99:c4': ['htc'],
@@ -110,6 +111,7 @@
'14:7d:c5': ['murata'],
'1c:99:4c': ['murata'],
'20:02:af': ['murata'],
+ '40:f3:08': ['murata'],
'44:a7:cf': ['murata'],
'5c:da:d4': ['murata'],
'78:4b:87': ['murata'],
diff --git a/taxonomy/pcaptest.py b/taxonomy/pcaptest.py
index a44a7b6..c7863fb 100644
--- a/taxonomy/pcaptest.py
+++ b/taxonomy/pcaptest.py
@@ -25,6 +25,9 @@
('Unknown', './testdata/pcaps/HTC Evo 2.4GHz.pcap'),
('Unknown', './testdata/pcaps/HTC Incredible 2.4GHz.pcap'),
('Unknown', './testdata/pcaps/HTC Inspire 2.4GHz.pcap'),
+ ('Unknown', './testdata/pcaps/HTC One V 2.4GHz.pcap'),
+ ('Unknown', './testdata/pcaps/HTC One X 2.4GHz.pcap'),
+ ('Unknown', './testdata/pcaps/HTC One X 5GHz.pcap'),
('Unknown', './testdata/pcaps/HTC Sensation 2.4GHz.pcap'),
('Unknown', './testdata/pcaps/HTC Thunderbolt 2.4GHz.pcap'),
('Unknown', './testdata/pcaps/HTC Titan 2.4GHz.pcap'),
@@ -46,7 +49,6 @@
('Unknown', './testdata/pcaps/Samsung Exhibit 2.4GHz.pcap'),
('Unknown', './testdata/pcaps/Samsung Fascinate 2.4GHz.pcap'),
('Unknown', './testdata/pcaps/Samsung Galaxy Tab 2 2.4GHz.pcap'),
- ('Unknown', './testdata/pcaps/Samsung Infuse 2.4GHz.pcap'),
('Unknown', './testdata/pcaps/Samsung Infuse 5GHz.pcap'),
('Unknown', './testdata/pcaps/Samsung Vibrant 2.4GHz.pcap'),
('Unknown', './testdata/pcaps/Sony Xperia Z5 2.4GHz.pcap'),
@@ -57,6 +59,10 @@
# work for these, instead we add them explicitly.
('iPad (1st/2nd gen)', './testdata/pcaps/iPad 1st gen 5GHz.pcap'),
('iPad (1st/2nd gen)', './testdata/pcaps/iPad 2nd gen 5GHz.pcap'),
+ ('iPad (4th gen or Air)', './testdata/pcaps/iPad (4th gen) 5GHz.pcap'),
+ ('iPad (4th gen or Air)', './testdata/pcaps/iPad (4th gen) 2.4GHz.pcap'),
+ ('iPad (4th gen or Air)', './testdata/pcaps/iPad Air 5GHz.pcap'),
+ ('iPad (4th gen or Air)', './testdata/pcaps/iPad Air 2.4GHz.pcap'),
('iPhone 6/6+', './testdata/pcaps/iPhone 6 5GHz.pcap'),
('iPhone 6/6+', './testdata/pcaps/iPhone 6+ 5GHz.pcap'),
('iPhone 6s/6s+', './testdata/pcaps/iPhone 6s 2.4GHz.pcap'),
@@ -65,6 +71,8 @@
('iPhone 6s/6s+', './testdata/pcaps/iPhone 6s+ 5GHz.pcap'),
('iPod Touch 1st/2nd gen', './testdata/pcaps/iPod Touch 1st gen 2.4GHz.pcap'),
('Nest Thermostat v1/v2', './testdata/pcaps/Nest Thermostat 2.4GHz.pcap'),
+ ('Samsung Galaxy S2 or Infuse', './testdata/pcaps/Samsung Galaxy S2 2.4GHz.pcap'),
+ ('Samsung Galaxy S2 or Infuse', './testdata/pcaps/Samsung Infuse 2.4GHz.pcap'),
]
diff --git a/taxonomy/testdata/dhcp.leases b/taxonomy/testdata/dhcp.leases
index 4bbfb3d..64aa1ca 100644
--- a/taxonomy/testdata/dhcp.leases
+++ b/taxonomy/testdata/dhcp.leases
@@ -45,3 +45,4 @@
1432237016 04:0c:ce:cf:40:2c 192.168.42.35 MacbookAir2010
1432237016 8c:2d:aa:9c:ce:0f 192.168.42.36 iPood-5
1432237016 dc:86:d8:a0:c8:de 192.168.42.37 iPhoone-5c
+1432237016 54:ae:27:32:ef:7f 192.168.42.38 iPaad-Air-1
diff --git a/taxonomy/testdata/dhcp.signatures b/taxonomy/testdata/dhcp.signatures
index 5a1ef1d..4831315 100644
--- a/taxonomy/testdata/dhcp.signatures
+++ b/taxonomy/testdata/dhcp.signatures
@@ -37,3 +37,4 @@
04:0c:ce:cf:40:2c 1,3,6,15,119,95,252,44,46
8c:2d:aa:9c:ce:0f 1,3,6,15,119,252
dc:86:d8:a0:c8:de 1,3,6,15,119,252
+54:ae:27:32:ef:7f 1,3,6,15,119,252
diff --git a/taxonomy/testdata/pcaps/HTC One V 2.4GHz.pcap b/taxonomy/testdata/pcaps/HTC One V 2.4GHz.pcap
new file mode 100644
index 0000000..5efec50
--- /dev/null
+++ b/taxonomy/testdata/pcaps/HTC One V 2.4GHz.pcap
Binary files differ
diff --git a/taxonomy/testdata/pcaps/HTC One X 2.4GHz.pcap b/taxonomy/testdata/pcaps/HTC One X 2.4GHz.pcap
new file mode 100644
index 0000000..e57a6b2
--- /dev/null
+++ b/taxonomy/testdata/pcaps/HTC One X 2.4GHz.pcap
Binary files differ
diff --git a/taxonomy/testdata/pcaps/HTC One X 5GHz.pcap b/taxonomy/testdata/pcaps/HTC One X 5GHz.pcap
new file mode 100644
index 0000000..9a25012
--- /dev/null
+++ b/taxonomy/testdata/pcaps/HTC One X 5GHz.pcap
Binary files differ
diff --git a/taxonomy/testdata/pcaps/Moto X 2.4GHz Broadcast.pcap b/taxonomy/testdata/pcaps/Moto X 2.4GHz Broadcast.pcap
new file mode 100644
index 0000000..045ec57
--- /dev/null
+++ b/taxonomy/testdata/pcaps/Moto X 2.4GHz Broadcast.pcap
Binary files differ
diff --git a/taxonomy/testdata/pcaps/Moto X 2.4GHz Specific.pcap b/taxonomy/testdata/pcaps/Moto X 2.4GHz Specific.pcap
new file mode 100644
index 0000000..0399b6c
--- /dev/null
+++ b/taxonomy/testdata/pcaps/Moto X 2.4GHz Specific.pcap
Binary files differ
diff --git a/taxonomy/testdata/pcaps/Moto X 2.4GHz.pcap b/taxonomy/testdata/pcaps/Moto X 2.4GHz.pcap
new file mode 100644
index 0000000..40e0ca1
--- /dev/null
+++ b/taxonomy/testdata/pcaps/Moto X 2.4GHz.pcap
Binary files differ
diff --git a/taxonomy/testdata/pcaps/Moto X 5GHz.pcap b/taxonomy/testdata/pcaps/Moto X 5GHz.pcap
new file mode 100644
index 0000000..dffcc63
--- /dev/null
+++ b/taxonomy/testdata/pcaps/Moto X 5GHz.pcap
Binary files differ
diff --git a/taxonomy/testdata/pcaps/Samsung Galaxy S2 2.4GHz.pcap b/taxonomy/testdata/pcaps/Samsung Galaxy S2 2.4GHz.pcap
new file mode 100644
index 0000000..e772fa8
--- /dev/null
+++ b/taxonomy/testdata/pcaps/Samsung Galaxy S2 2.4GHz.pcap
Binary files differ
diff --git a/taxonomy/testdata/pcaps/Samsung Galaxy S2 5GHz.pcap b/taxonomy/testdata/pcaps/Samsung Galaxy S2 5GHz.pcap
new file mode 100644
index 0000000..035fc85
--- /dev/null
+++ b/taxonomy/testdata/pcaps/Samsung Galaxy S2 5GHz.pcap
Binary files differ
diff --git a/taxonomy/testdata/pcaps/Samsung Galaxy S4 2.4GHz I9505.pcap b/taxonomy/testdata/pcaps/Samsung Galaxy S4 2.4GHz I9505.pcap
new file mode 100644
index 0000000..83832ba
--- /dev/null
+++ b/taxonomy/testdata/pcaps/Samsung Galaxy S4 2.4GHz I9505.pcap
Binary files differ
diff --git a/taxonomy/testdata/pcaps/Samsung Galaxy S4 5GHz I9505.pcap b/taxonomy/testdata/pcaps/Samsung Galaxy S4 5GHz I9505.pcap
new file mode 100644
index 0000000..ed78fc2
--- /dev/null
+++ b/taxonomy/testdata/pcaps/Samsung Galaxy S4 5GHz I9505.pcap
Binary files differ
diff --git "a/taxonomy/testdata/pcaps/iPad \0504th gen\051 2.4GHz.pcap" "b/taxonomy/testdata/pcaps/iPad \0504th gen\051 2.4GHz.pcap"
new file mode 100644
index 0000000..7eb0924
--- /dev/null
+++ "b/taxonomy/testdata/pcaps/iPad \0504th gen\051 2.4GHz.pcap"
Binary files differ
diff --git a/taxonomy/testdata/pcaps/iPad Air 2.4GHz.pcap b/taxonomy/testdata/pcaps/iPad Air 2.4GHz.pcap
new file mode 100644
index 0000000..6d6fabb
--- /dev/null
+++ b/taxonomy/testdata/pcaps/iPad Air 2.4GHz.pcap
Binary files differ
diff --git a/taxonomy/testdata/pcaps/iPad Air 5GHz.pcap b/taxonomy/testdata/pcaps/iPad Air 5GHz.pcap
new file mode 100644
index 0000000..fac188d
--- /dev/null
+++ b/taxonomy/testdata/pcaps/iPad Air 5GHz.pcap
Binary files differ
diff --git a/taxonomy/wifi.py b/taxonomy/wifi.py
index b7cfaa6..c312c05 100644
--- a/taxonomy/wifi.py
+++ b/taxonomy/wifi.py
@@ -179,21 +179,21 @@
('BCM4330', 'iPad (3rd gen)', '2.4GHz'),
'wifi3|probe:0,1,45,127,107,221(001018,2),221(00904c,51),221(0050f2,8),htcap:01fe,htagg:1b,htmcs:0000ffff,intwrk:0f,extcap:00000804|assoc:0,1,33,36,48,45,221(001018,2),221(00904c,51),221(0050f2,2),cap:0011,htcap:01fe,htagg:1b,htmcs:0000ffff,txpow:e708|os:ios':
- ('BCM4334', 'iPad (4th gen)', '5GHz'),
+ ('BCM4334', 'iPad (4th gen or Air)', '5GHz'),
'wifi3|probe:0,1,45,127,107,221(001018,2),221(00904c,51),221(0050f2,8),htcap:01fe,htagg:1b,htmcs:0000ffff,intwrk:0f,extcap:00000804|assoc:0,1,33,36,48,45,70,221(001018,2),221(00904c,51),221(0050f2,2),cap:0011,htcap:01fe,htagg:1b,htmcs:0000ffff,txpow:e708|os:ios':
- ('BCM4334', 'iPad (4th gen)', '5GHz'),
+ ('BCM4334', 'iPad (4th gen or Air)', '5GHz'),
'wifi3|probe:0,1,45,127,107,221(001018,2),221(00904c,51),221(0050f2,8),htcap:01fe,htagg:1b,htmcs:0000ffff,intwrk:ff,extcap:00000804|assoc:0,1,33,36,48,45,221(001018,2),221(00904c,51),221(0050f2,2),cap:0011,htcap:01fe,htagg:1b,htmcs:0000ffff,txpow:e708|os:ios':
- ('BCM4334', 'iPad (4th gen)', '5GHz'),
+ ('BCM4334', 'iPad (4th gen or Air)', '5GHz'),
'wifi3|probe:0,1,45,127,107,221(001018,2),221(00904c,51),221(0050f2,8),htcap:01fe,htagg:1b,htmcs:0000ffff,intwrk:ff,extcap:00000804|assoc:0,1,33,36,48,45,70,221(001018,2),221(00904c,51),221(0050f2,2),cap:0011,htcap:01fe,htagg:1b,htmcs:0000ffff,txpow:e708|os:ios':
- ('BCM4334', 'iPad (4th gen)', '5GHz'),
+ ('BCM4334', 'iPad (4th gen or Air)', '5GHz'),
'wifi3|probe:0,1,50,3,45,127,107,221(001018,2),221(00904c,51),221(0050f2,8),htcap:01bc,htagg:1b,htmcs:0000ffff,intwrk:0f,extcap:00000804|assoc:0,1,33,36,48,50,45,221(001018,2),221(00904c,51),221(0050f2,2),cap:0431,htcap:01bc,htagg:1b,htmcs:0000ffff,txpow:1805|os:ios':
- ('BCM4334', 'iPad (4th gen)', '2.4GHz'),
+ ('BCM4334', 'iPad (4th gen or Air)', '2.4GHz'),
'wifi3|probe:0,1,50,3,45,127,107,221(001018,2),221(00904c,51),221(0050f2,8),htcap:01bc,htagg:1b,htmcs:0000ffff,intwrk:0f,extcap:00000804|assoc:0,1,33,36,48,50,45,70,221(001018,2),221(00904c,51),221(0050f2,2),cap:0431,htcap:01bc,htagg:1b,htmcs:0000ffff,txpow:1805|os:ios':
- ('BCM4334', 'iPad (4th gen)', '2.4GHz'),
+ ('BCM4334', 'iPad (4th gen or Air)', '2.4GHz'),
'wifi3|probe:0,1,50,3,45,127,107,221(001018,2),221(00904c,51),221(0050f2,8),htcap:01bc,htagg:1b,htmcs:0000ffff,intwrk:ff,extcap:00000804|assoc:0,1,33,36,48,50,45,221(001018,2),221(00904c,51),221(0050f2,2),cap:0431,htcap:01bc,htagg:1b,htmcs:0000ffff,txpow:1805|os:ios':
- ('BCM4334', 'iPad (4th gen)', '2.4GHz'),
+ ('BCM4334', 'iPad (4th gen or Air)', '2.4GHz'),
'wifi3|probe:0,1,50,3,45,127,107,221(001018,2),221(00904c,51),221(0050f2,8),htcap:01bc,htagg:1b,htmcs:0000ffff,intwrk:ff,extcap:00000804|assoc:0,1,33,36,48,50,45,70,221(001018,2),221(00904c,51),221(0050f2,2),cap:0431,htcap:01bc,htagg:1b,htmcs:0000ffff,txpow:1805|os:ios':
- ('BCM4334', 'iPad (4th gen)', '2.4GHz'),
+ ('BCM4334', 'iPad (4th gen or Air)', '2.4GHz'),
'wifi|probe:0,1,45,127,107,221(001018,2),221(00904c,51),221(0050f2,8),htcap:01fe|assoc:0,1,33,36,48,45,70,221(001018,2),221(00904c,51),221(0050f2,2),htcap:01fe|os:ios':
('BCM43241', 'iPad Air (1st gen)', '5GHz'),
@@ -382,6 +382,8 @@
'wifi3|probe:0,1,45,221(00904c,51),htcap:09ef,htagg:1b,htmcs:0000ffff|assoc:0,1,33,36,48,45,221(00904c,51),221(0050f2,2),cap:0011,htcap:09ef,htagg:1b,htmcs:0000ffff,txpow:0005|os:macos':
('BCM4331', 'MacBook Pro 17" late 2011 (A1297)', '5GHz'),
+ 'wifi3|probe:0,1,3,45,221(00904c,51),htcap:09ef,htagg:1b,htmcs:0000ffff|assoc:0,1,33,36,48,45,221(00904c,51),221(0050f2,2),cap:0011,htcap:09ef,htagg:1b,htmcs:0000ffff,txpow:0005|os:macos':
+ ('BCM4331', 'MacBook Pro 17" late 2011 (A1297)', '5GHz'),
'wifi3|probe:0,1,50,3,45,221(00904c,51),htcap:19ad,htagg:1b,htmcs:0000ffff|assoc:0,1,33,36,48,50,45,221(00904c,51),221(0050f2,2),cap:0431,htcap:19ad,htagg:1b,htmcs:0000ffff,txpow:1305|os:macos':
('BCM4331', 'MacBook Pro 17" late 2011 (A1297)', '2.4GHz'),
@@ -413,9 +415,11 @@
'wifi3|probe:0,1,50,3,45,221(0050f2,8),htcap:012c,htagg:03,htmcs:000000ff|assoc:0,1,50,48,45,221(0050f2,2),127,cap:8431,htcap:012c,htagg:03,htmcs:000000ff,extcap:00000a02|oui:motorola':
('QCA_WCN3620', 'Moto G or Moto X', '2.4GHz'),
- 'wifi|probe:0,1,45,221(0050f2,8),191,htcap:016e,vhtcap:31800120|assoc:0,1,33,36,48,45,221(0050f2,2),191,127,htcap:016e,vhtcap:31800120|oui:motorola':
+ 'wifi3|probe:0,1,45,221(0050f2,8),191,htcap:016e,htagg:03,htmcs:000000ff,vhtcap:31800120,vhtrxmcs:0000fffe,vhttxmcs:0000fffe|assoc:0,1,48,45,221(0050f2,2),191,127,127,cap:0431,htcap:016e,htagg:03,htmcs:000000ff,vhtcap:31800120,vhtrxmcs:0000fffe,vhttxmcs:0000fffe,extcap:00000a02|oui:motorola':
('QCA_WCN3680', 'Moto X', '5GHz'),
- 'wifi|probe:0,1,50,3,45,221(0050f2,8),191,htcap:012c,vhtcap:31800120|assoc:0,1,50,48,45,221(0050f2,2),htcap:012c|oui:motorola':
+ 'wifi3|probe:0,1,50,3,45,221(0050f2,8),htcap:012c,htagg:03,htmcs:000000ff|assoc:0,1,50,48,45,221(0050f2,2),127,cap:0431,htcap:012c,htagg:03,htmcs:000000ff,extcap:00000a02|oui:motorola':
+ ('QCA_WCN3680', 'Moto X', '2.4GHz'),
+ 'wifi3|probe:0,1,50,3,45,221(0050f2,8),191,htcap:012c,htagg:03,htmcs:000000ff,vhtcap:31800120,vhtrxmcs:0000fffe,vhttxmcs:0000fffe|assoc:0,1,50,48,45,221(0050f2,2),127,cap:0431,htcap:012c,htagg:03,htmcs:000000ff,extcap:00000a02|oui:motorola':
('QCA_WCN3680', 'Moto X', '2.4GHz'),
'wifi3|probe:0,1,3,45,221(0050f2,8),191,htcap:016e,htagg:03,htmcs:000000ff,vhtcap:31805120,vhtrxmcs:0000fffe,vhttxmcs:0000fffe|assoc:0,1,48,45,221(0050f2,2),191,127,127,cap:8431,htcap:016e,htagg:03,htmcs:000000ff,vhtcap:31805120,vhtrxmcs:0000fffe,vhttxmcs:0000fffe,extcap:00000a02|oui:motorola':
@@ -658,6 +662,23 @@
'wifi3|probe:0,1,50,3,45,127,107,221(506f9a,16),221(00904c,4),221(0050f2,8),221(001018,2),htcap:01ad,htagg:17,htmcs:0000ffff,intwrk:0f,extcap:00080f8401400040|assoc:0,1,50,33,36,48,45,221(001018,2),221(0050f2,2),cap:0431,htcap:01ad,htagg:17,htmcs:0000ffff,txpow:1202|oui:murata':
('BCM4359', 'Samsung Galaxy Note 5', '2.4GHz'),
+ 'wifi3|probe:0,1,45,221(001018,2),221(00904c,51),htcap:000c,htagg:19,htmcs:000000ff|assoc:0,1,33,36,48,45,221(001018,2),221(00904c,51),221(0050f2,2),cap:0011,htcap:000c,htagg:19,htmcs:000000ff,txpow:0f0a|oui:samsung':
+ ('', 'Samsung Galaxy S2', '5GHz'),
+ 'wifi3|probe:0,1,45,221(001018,2),221(00904c,51),htcap:000c,htagg:19,htmcs:000000ff|assoc:0,1,33,36,48,45,221(001018,2),221(00904c,51),221(0050f2,2),cap:0011,htcap:000c,htagg:19,htmcs:000000ff,txpow:0f0a|oui:murata':
+ ('', 'Samsung Galaxy S2', '5GHz'),
+ 'wifi3|probe:0,1,45,3,221(001018,2),221(00904c,51),htcap:000c,htagg:19,htmcs:000000ff|assoc:0,1,33,36,48,45,221(001018,2),221(00904c,51),221(0050f2,2),cap:0011,htcap:000c,htagg:19,htmcs:000000ff,txpow:0f0a|oui:samsung':
+ ('', 'Samsung Galaxy S2', '5GHz'),
+ 'wifi3|probe:0,1,45,3,221(001018,2),221(00904c,51),htcap:000c,htagg:19,htmcs:000000ff|assoc:0,1,33,36,48,45,221(001018,2),221(00904c,51),221(0050f2,2),cap:0011,htcap:000c,htagg:19,htmcs:000000ff,txpow:0f0a|oui:murata':
+ ('', 'Samsung Galaxy S2', '5GHz'),
+ 'wifi3|probe:0,1,50,45,221(001018,2),221(00904c,51),htcap:100c,htagg:19,htmcs:000000ff|assoc:0,1,33,36,48,50,45,221(001018,2),221(00904c,51),221(0050f2,2),cap:0431,htcap:100c,htagg:19,htmcs:000000ff,txpow:120a|oui:samsung':
+ ('', 'Samsung Galaxy S2 or Infuse','2.4GHz'),
+ 'wifi3|probe:0,1,50,45,221(001018,2),221(00904c,51),htcap:100c,htagg:19,htmcs:000000ff|assoc:0,1,33,36,48,50,45,221(001018,2),221(00904c,51),221(0050f2,2),cap:0431,htcap:100c,htagg:19,htmcs:000000ff,txpow:120a|oui:murata':
+ ('', 'Samsung Galaxy S2 or Infuse','2.4GHz'),
+ 'wifi3|probe:0,1,50,45,3,221(001018,2),221(00904c,51),htcap:100c,htagg:19,htmcs:000000ff|assoc:0,1,33,36,48,50,45,221(001018,2),221(00904c,51),221(0050f2,2),cap:0431,htcap:100c,htagg:19,htmcs:000000ff,txpow:120a|oui:samsung':
+ ('', 'Samsung Galaxy S2 or Infuse','2.4GHz'),
+ 'wifi3|probe:0,1,50,45,3,221(001018,2),221(00904c,51),htcap:100c,htagg:19,htmcs:000000ff|assoc:0,1,33,36,48,50,45,221(001018,2),221(00904c,51),221(0050f2,2),cap:0431,htcap:100c,htagg:19,htmcs:000000ff,txpow:120a|oui:murata':
+ ('', 'Samsung Galaxy S2 or Infuse','2.4GHz'),
+
'wifi3|probe:0,1,45,3,221(001018,2),221(00904c,51),htcap:000c,htagg:19,htmcs:000000ff|assoc:0,1,33,36,48,45,221(001018,2),221(00904c,51),221(0050f2,2),cap:0011,htcap:000c,htagg:19,htmcs:000000ff,txpow:0f09|oui:samsung':
('', 'Samsung Galaxy S2+', '5GHz'),
'wifi3|probe:0,1,45,3,221(001018,2),221(00904c,51),htcap:000c,htagg:19,htmcs:000000ff|assoc:0,1,33,36,48,45,221(001018,2),221(00904c,51),221(0050f2,2),cap:0011,htcap:000c,htagg:19,htmcs:000000ff,txpow:0f09|oui:murata':
@@ -712,6 +733,10 @@
('BCM4335', 'Samsung Galaxy S4', '2.4GHz'),
'wifi3|probe:0,1,50,45,127,221(001018,2),221(00904c,51),221(00904c,4),221(0050f2,8),htcap:102d,htagg:17,htmcs:000000ff,extcap:0000080000000040|assoc:0,1,33,36,48,50,45,221(001018,2),221(0050f2,2),cap:0431,htcap:102d,htagg:17,htmcs:000000ff,txpow:1201|oui:murata':
('BCM4335', 'Samsung Galaxy S4', '2.4GHz'),
+ 'wifi3|probe:0,1,50,45,127,107,221(506f9a,16),221(001018,2),221(00904c,51),221(00904c,4),221(0050f2,8),htcap:102d,htagg:17,htmcs:000000ff,intwrk:0f,extcap:0000088000400040|assoc:0,1,33,36,48,50,45,127,107,221(001018,2),221(0050f2,2),cap:0431,htcap:102d,htagg:17,htmcs:000000ff,txpow:1201,intwrk:0f,extcap:000000800040|oui:samsung':
+ ('BCM4335', 'Samsung Galaxy S4', '2.4GHz'),
+ 'wifi3|probe:0,1,50,45,127,107,221(506f9a,16),221(001018,2),221(00904c,51),221(00904c,4),221(0050f2,8),htcap:102d,htagg:17,htmcs:000000ff,intwrk:0f,extcap:0000088000400040|assoc:0,1,33,36,48,50,45,127,107,221(001018,2),221(0050f2,2),cap:0431,htcap:102d,htagg:17,htmcs:000000ff,txpow:1201,intwrk:0f,extcap:000000800040|oui:murata':
+ ('BCM4335', 'Samsung Galaxy S4', '2.4GHz'),
'wifi3|probe:0,1,45,127,107,191,221(506f9a,16),221(00904c,4),221(0050f2,8),221(001018,2),htcap:006f,htagg:17,htmcs:0000ffff,vhtcap:0f815832,vhtrxmcs:0000fffa,vhttxmcs:0000fffa,intwrk:0f,extcap:0000088001400040|assoc:0,1,33,36,48,45,127,107,191,221(00904c,4),221(001018,2),221(0050f2,2),cap:0011,htcap:006f,htagg:17,htmcs:0000ffff,vhtcap:0f815832,vhtrxmcs:0000fffa,vhttxmcs:0000fffa,txpow:e20b,intwrk:0f,extcap:0000088001400040|oui:samsung':
('BCM4354', 'Samsung Galaxy S5', '5GHz'),
diff --git a/waveguide/fake/taxonomy/9c:d9:17:00:00:02 b/waveguide/fake/taxonomy/9c:d9:17:00:00:02
index 43be77d..8d6402c 100644
--- a/waveguide/fake/taxonomy/9c:d9:17:00:00:02
+++ b/waveguide/fake/taxonomy/9c:d9:17:00:00:02
@@ -1 +1 @@
-wifi|probe:0,1,45,221(0050f2,8),191,htcap:016e,vhtcap:31800120|assoc:0,1,33,36,48,45,221(0050f2,2),191,127,htcap:016e,vhtcap:31800120
+wifi3|probe:0,1,50,3,45,221(0050f2,8),htcap:012c,htagg:03,htmcs:000000ff|assoc:0,1,50,48,45,221(0050f2,2),127,cap:0431,htcap:012c,htagg:03,htmcs:000000ff,extcap:00000a02
diff --git a/wifi/quantenna.py b/wifi/quantenna.py
index 7ad4079..10af967 100755
--- a/wifi/quantenna.py
+++ b/wifi/quantenna.py
@@ -9,29 +9,52 @@
import utils
+ALREADY_MEMBER_FMT = ('device %s is already a member of a bridge; '
+ "can't enslave it to bridge %s.")
+NOT_MEMBER_FMT = 'device %s is not a slave of %s'
+
+
def _get_interface():
- return subprocess.check_output(['get-quantenna-interface']).strip() or None
+ return subprocess.check_output(['get-quantenna-interface']).strip()
-def _get_qcsapi():
- # qcsapi_pcie_static runs on PCIe hosts, e.g. GFRG250.
- # call_qcsapi runs on the LHOST, e.g. GFEX250.
- return next((qcsapi for qcsapi in ['qcsapi_pcie_static', 'call_qcsapi']
- if utils.subprocess_quiet(['runnable', qcsapi]) == 0), None)
-
-
-def _get_mac_address():
- var = {'wlan0': 'MAC_ADDR_WIFI', 'wlan1': 'MAC_ADDR_WIFI2'}[_get_interface()]
+def _get_mac_address(interface):
+ try:
+ var = {'wlan0': 'MAC_ADDR_WIFI', 'wlan1': 'MAC_ADDR_WIFI2'}[interface]
+ except KeyError:
+ raise utils.BinWifiException('no MAC address for %s in hnvram' % interface)
return subprocess.check_output(['hnvram', '-rq', var]).strip()
def _qcsapi(*args):
- return subprocess.check_output([_get_qcsapi()] + list(args)).strip()
+ return subprocess.check_output(['qcsapi'] + list(args)).strip()
+
+
+def _brctl(*args):
+ return subprocess.check_output(['brctl'] + list(args),
+ stderr=subprocess.STDOUT).strip()
+
+
+def _set_interface_in_bridge(bridge, interface, want_in_bridge):
+ """Add/remove Quantenna interface from/to the bridge."""
+ if want_in_bridge:
+ command = 'addif'
+ error_fmt = ALREADY_MEMBER_FMT
+ else:
+ command = 'delif'
+ error_fmt = NOT_MEMBER_FMT
+
+ try:
+ _brctl(command, bridge, interface)
+ except subprocess.CalledProcessError as e:
+ if error_fmt % (interface, bridge) not in e.output:
+ raise utils.BinWifiException(e.output)
def _set(mode, opt):
"""Enable wifi."""
- if not _get_interface() or not _get_qcsapi():
+ interface = _get_interface()
+ if not interface:
return False
_qcsapi('rfenable', '0')
@@ -47,7 +70,7 @@
for param, value in config.iteritems():
_qcsapi('update_config_param', 'wifi0', param, value)
- _qcsapi('set_mac_addr', 'wifi0', _get_mac_address())
+ _qcsapi('set_mac_addr', 'wifi0', _get_mac_address(interface))
if int(_qcsapi('is_startprod_done')):
_qcsapi('reload_in_mode', 'wifi0', mode)
@@ -61,12 +84,14 @@
raise utils.BinWifiException('startprod timed out')
if mode == 'ap':
+ _set_interface_in_bridge(opt.bridge, interface, True)
_qcsapi('set_ssid', 'wifi0', opt.ssid)
_qcsapi('set_passphrase', 'wifi0', '0', os.environ['WIFI_PSK'])
_qcsapi('set_option', 'wifi0', 'ssid_broadcast',
'0' if opt.hidden_mode else '1')
_qcsapi('rfenable', '1')
elif mode == 'sta':
+ _set_interface_in_bridge(opt.bridge, interface, False)
_qcsapi('create_ssid', 'wifi0', opt.ssid)
_qcsapi('ssid_set_passphrase', 'wifi0', opt.ssid, '0',
os.environ['WIFI_CLIENT_PSK'])
@@ -79,7 +104,7 @@
def _stop(_):
"""Disable wifi."""
- if not _get_interface() or not _get_qcsapi():
+ if not _get_interface():
return False
_qcsapi('rfenable', '0')
diff --git a/wifi/quantenna_test.py b/wifi/quantenna_test.py
index 1f99d41..72f0333 100755
--- a/wifi/quantenna_test.py
+++ b/wifi/quantenna_test.py
@@ -3,6 +3,8 @@
"""Tests for quantenna.py."""
import os
+from subprocess import CalledProcessError
+
from configs_test import FakeOptDict
import quantenna
from wvtest import wvtest
@@ -17,14 +19,39 @@
return '1\n' if ['startprod', 'wifi0'] in calls else '0\n'
-def set_fakes(interface='wlan1', qcsapi='qcsapi_pcie_static'):
+bridge_interfaces = set()
+
+
+def fake_brctl(*args):
+ bridge = args[-2]
+ wvtest.WVPASS(bridge == 'br0')
+ interface = args[-1]
+ if 'addif' in args:
+ if interface in bridge_interfaces:
+ raise CalledProcessError(
+ returncode=1, cmd=['brctl'] + list(args),
+ output=quantenna.ALREADY_MEMBER_FMT % (interface, bridge))
+ bridge_interfaces.add(interface)
+ return
+
+ if 'delif' in args:
+ if interface not in bridge_interfaces:
+ raise CalledProcessError(
+ returncode=1, cmd=['brctl'] + list(args),
+ output=quantenna.NOT_MEMBER_FMT % (interface, bridge))
+ bridge_interfaces.remove(interface)
+ return
+
+
+def set_fakes(interface='wlan1'):
del calls[:]
+ bridge_interfaces.clear()
os.environ['WIFI_PSK'] = 'wifi_psk'
os.environ['WIFI_CLIENT_PSK'] = 'wifi_client_psk'
quantenna._get_interface = lambda: interface
- quantenna._get_qcsapi = lambda: qcsapi
- quantenna._get_mac_address = lambda: '00:11:22:33:44:55'
+ quantenna._get_mac_address = lambda _: '00:11:22:33:44:55'
quantenna._qcsapi = fake_qcsapi
+ quantenna._brctl = fake_brctl
def matching_calls_indices(accept):
@@ -40,13 +67,12 @@
wvtest.WVFAIL(quantenna.stop_ap_wifi(opt))
wvtest.WVFAIL(quantenna.stop_client_wifi(opt))
wvtest.WVPASSEQ(calls, [])
- set_fakes(qcsapi='')
wvtest.WVFAIL(quantenna.set_wifi(opt))
wvtest.WVFAIL(quantenna.set_client_wifi(opt))
wvtest.WVFAIL(quantenna.stop_ap_wifi(opt))
wvtest.WVFAIL(quantenna.stop_client_wifi(opt))
wvtest.WVPASSEQ(calls, [])
- set_fakes(interface='', qcsapi='')
+ set_fakes(interface='')
wvtest.WVFAIL(quantenna.set_wifi(opt))
wvtest.WVFAIL(quantenna.set_client_wifi(opt))
wvtest.WVFAIL(quantenna.stop_ap_wifi(opt))
@@ -57,10 +83,12 @@
@wvtest.wvtest
def set_wifi_test():
opt = FakeOptDict()
+ opt.bridge = 'br0'
set_fakes()
# Run set_wifi for the first time.
wvtest.WVPASS(quantenna.set_wifi(opt))
+ wvtest.WVPASS('wlan1' in bridge_interfaces)
# 'rfenable 0' must be run first so that a live interface is not being
# modified.
@@ -104,6 +132,7 @@
opt.width = '80'
new_calls_start = len(calls)
wvtest.WVPASS(quantenna.set_client_wifi(opt))
+ wvtest.WVFAIL('wlan1' in bridge_interfaces)
# Clear old calls.
del calls[:new_calls_start]
@@ -143,6 +172,13 @@
wvtest.WVPASSLT(rim, i[0])
wvtest.WVPASSLT(i[-1], calls.index(['apply_security_config', 'wifi0']))
+ # Make sure subsequent equivalent calls don't fail despite the redundant
+ # bridge changes.
+ wvtest.WVPASS(quantenna.set_client_wifi(opt))
+ wvtest.WVPASS(quantenna.set_client_wifi(opt))
+ wvtest.WVPASS(quantenna.set_wifi(opt))
+ wvtest.WVPASS(quantenna.set_wifi(opt))
+
@wvtest.wvtest
def stop_wifi_test():