Merge "platform: Update buttonmon to support GFLT300"
diff --git a/cmds/Makefile b/cmds/Makefile
index 60c1b5e..3d53395 100644
--- a/cmds/Makefile
+++ b/cmds/Makefile
@@ -63,13 +63,15 @@
endif
ifeq ($(BUILD_SSDP),y)
-TARGETS += ssdptax
+TARGETS += ssdptax dialcheck
HOST_TEST_TARGETS += host-test-ssdptax.sh
+HOST_TEST_TARGETS += host-test-dialcheck.sh
endif
ifeq ($(BUILD_DNSSD),y)
# Don't bother building for host
ARCH_TARGETS += dnssd_hosts
+SCRIPT_TARGETS += castcheck
endif
ifeq ($(BUILD_IBEACON),y)
@@ -116,6 +118,7 @@
for n in $(SCRIPT_TARGETS); do \
test ! -f $$n.$(BR2_TARGET_GENERIC_PLATFORM_NAME) || \
cp -f $$n.$(BR2_TARGET_GENERIC_PLATFORM_NAME) $(BINDIR)/$$n; \
+ test ! -f $$n || cp -f $$n $(BINDIR)/$$n; \
done
install-libs:
@@ -202,6 +205,10 @@
ssdptax: LIBS += -lcurl -lnl-3 -lstdc++ -lm
host-ssdptax: host-ssdptax.o host-l2utils.o
host-ssdptax: LIBS += $(HOST_LIBS) -lcurl -lnl-3 -lstdc++ -lm
+dialcheck: dialcheck.o
+dialcheck: LIBS += -lstdc++ -lm
+host-dialcheck: host-dialcheck.o
+host-dialcheck: LIBS += $(HOST_LIBS) -lstdc++ -lm
statpitcher.o: device_stats.pb.o
statpitcher: LIBS+=-L$(DESTDIR)$(PREFIX)/usr/lib -lprotobuf-lite -lpthread -lstdc++
statpitcher: device_stats.pb.o statpitcher.o
diff --git a/cmds/avahi-browse-fake.sh b/cmds/avahi-browse-fake.sh
new file mode 100755
index 0000000..c7ed603
--- /dev/null
+++ b/cmds/avahi-browse-fake.sh
@@ -0,0 +1,8 @@
+#!/bin/bash
+
+echo '+;br0;IPv4;GFiber\032TV\032Box1001;_googlecast._tcp;local'
+echo '=;br0;IPv4;GFiber\032TV\032Box1001;_googlecast._tcp;local;GFiber\032TV\032Box1001.local;1.1.1.1;8009;"rs=" "bs=FFFFFFFFFFFF" "st=2" "ca=4101" "fn=GFiber TV Box1001" "ic=/setup/icon.png" "md=GFiber TV Box" "ve=05" "rm=RMRMRMRMRMRMRMRMRM" "id=0123456789abcdef0123456789abcdef"'
+echo '+;br0;IPv4;GFiber\032TV\032Box1002;_googlecast._tcp;local'
+echo '=;br0;IPv4;GFiber\032TV\032Box1002;_googlecast._tcp;local;GFiber\032TV\032Box1002.local;3.3.3.3;8009;"rs=" "bs=FFFFFFFFFFFF" "st=2" "ca=4101" "fn=GFiber TV Box1002" "ic=/setup/icon.png" "md=GFiber TV Box" "ve=05" "rm=RMRMRMRMRMRMRMRMRM" "id=0123456789abcdef0123456789abcdef"'
+echo '+;br0;IPv4;GFiber\032TV\032Box1003;_googlecast._tcp;local'
+echo '=;br0;IPv4;GFiber\032TV\032Box1003;_googlecast._tcp;local;GFiber\032TV\032Box1003.local;2.2.2.2;8009;"rs=" "bs=FFFFFFFFFFFF" "st=2" "ca=4101" "fn=GFiber TV Box1003" "ic=/setup/icon.png" "md=GFiber TV Box" "ve=05" "rm=RMRMRMRMRMRMRMRMRM" "id=0123456789abcdef0123456789abcdef"'
diff --git a/cmds/castcheck b/cmds/castcheck
new file mode 100755
index 0000000..70080ec
--- /dev/null
+++ b/cmds/castcheck
@@ -0,0 +1,19 @@
+#!/bin/sh
+
+AVAHI=avahi-browse
+
+while getopts "a:" option
+do
+ case $option in
+ a) AVAHI="$OPTARG" ;;
+ esac
+done
+
+cast_devices=
+while IFS=";" read ip; do
+ cast_devices="$cast_devices $ip"
+done<<EOT
+$($AVAHI -tpvlr _googlecast._tcp | grep "^=" | cut -d";" -f8 | sort)
+EOT
+
+echo "Cast responses from:$cast_devices"
diff --git a/cmds/dialcheck-test-server.py b/cmds/dialcheck-test-server.py
new file mode 100644
index 0000000..2637384
--- /dev/null
+++ b/cmds/dialcheck-test-server.py
@@ -0,0 +1,60 @@
+#!/usr/bin/python
+"""Fake SSDP server for unit tests.
+
+"""
+
+import errno
+import os
+import signal
+import socket
+import SocketServer
+import struct
+import sys
+
+
+notify = """LOCATION: http://1.1.1.1:1/test.xml\r\n
+CACHE-CONTROL: max-age=1800\r\n
+EXT:\r\n
+SERVER: test_ssdp/1.0\r\n
+ST: urn:dial-multiscreen-org:service:dial:1\r\n
+USN: uuid:number::urn:dial-multiscreen-org:service:dial:1\r\n"""
+
+
+class SSDPHandler(SocketServer.BaseRequestHandler):
+ def handle(self):
+ self.request[1].sendto(notify, self.client_address)
+
+
+def check_pid(pid):
+ try:
+ os.kill(pid, 0)
+ except OSError as e:
+ if e.errno == errno.ESRCH:
+ return False
+ return True
+
+
+def timeout(unused_signum, unused_frame):
+ ppid = os.getppid()
+ if ppid == 1 or not check_pid(ppid):
+ print 'timed out!'
+ sys.exit(2)
+ else:
+ signal.alarm(1)
+
+
+def main():
+ signal.signal(signal.SIGALRM, timeout)
+ signal.alarm(1)
+ SocketServer.UDPServer.allow_reuse_address = True
+ s = SocketServer.UDPServer(('', 0), SSDPHandler)
+ s.socket.setsockopt(socket.IPPROTO_IP, socket.IP_ADD_MEMBERSHIP,
+ socket.inet_aton('239.255.255.250') + socket.inet_aton('0.0.0.0'))
+ sn = s.socket.getsockname()
+ port = sn[1]
+ open(sys.argv[1], "w").write(str(port))
+ s.handle_request()
+
+
+if __name__ == '__main__':
+ main()
diff --git a/cmds/dialcheck.cc b/cmds/dialcheck.cc
new file mode 100644
index 0000000..17f8fbd
--- /dev/null
+++ b/cmds/dialcheck.cc
@@ -0,0 +1,320 @@
+/*
+ * Copyright 2016 Google Inc. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * dialcheck
+ *
+ * Check for nearby devices supporting the DIAL protocol.
+ */
+
+#include <arpa/inet.h>
+#include <asm/types.h>
+#include <ctype.h>
+#include <getopt.h>
+#include <net/if.h>
+#include <netinet/in.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/un.h>
+#include <unistd.h>
+
+#include <iostream>
+#include <set>
+#include <tr1/unordered_map>
+
+
+typedef std::set<std::string> ResultsSet;
+int timeout_secs = 10;
+
+
+/* SSDP Discover packet */
+int ssdp_port = 1900;
+int ssdp_loop = 0;
+#define SSDP_IP4 "239.255.255.250"
+#define SSDP_IP6 "FF02::C"
+const char discover_template[] = "M-SEARCH * HTTP/1.1\r\n"
+ "HOST: %s:%d\r\n"
+ "MAN: \"ssdp:discover\"\r\n"
+ "MX: 2\r\n"
+ "USER-AGENT: dialcheck/1.0\r\n"
+ "ST: urn:dial-multiscreen-org:service:dial:1\r\n\r\n";
+
+
+int get_ipv4_ssdp_socket()
+{
+ int s;
+ int reuse = 1;
+ struct sockaddr_in sin;
+ struct ip_mreq mreq;
+ struct ip_mreqn mreqn;
+
+ if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
+ perror("socket SOCK_DGRAM");
+ exit(1);
+ }
+
+ if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (char *)&reuse, sizeof(reuse))) {
+ perror("setsockopt SO_REUSEADDR");
+ exit(1);
+ }
+
+ if (setsockopt(s, IPPROTO_IP, IP_MULTICAST_LOOP,
+ &ssdp_loop, sizeof(ssdp_loop))) {
+ perror("setsockopt IP_MULTICAST_LOOP");
+ exit(1);
+ }
+
+ memset(&sin, 0, sizeof(sin));
+ sin.sin_family = AF_INET;
+ sin.sin_port = htons(ssdp_port);
+ sin.sin_addr.s_addr = INADDR_ANY;
+ if (bind(s, (struct sockaddr*)&sin, sizeof(sin))) {
+ perror("bind");
+ exit(1);
+ }
+
+ memset(&mreqn, 0, sizeof(mreqn));
+ mreqn.imr_ifindex = if_nametoindex("br0");
+ if (setsockopt(s, IPPROTO_IP, IP_MULTICAST_IF, &mreqn, sizeof(mreqn))) {
+ perror("IP_MULTICAST_IF");
+ exit(1);
+ }
+
+ memset(&mreq, 0, sizeof(mreq));
+ mreq.imr_multiaddr.s_addr = inet_addr(SSDP_IP4);
+ if (setsockopt(s, IPPROTO_IP, IP_ADD_MEMBERSHIP,
+ (char *)&mreq, sizeof(mreq))) {
+ perror("IP_ADD_MEMBERSHIP");
+ exit(1);
+ }
+
+ return s;
+}
+
+
+void send_ssdp_ip4_request(int s)
+{
+ struct sockaddr_in sin;
+ char buf[1024];
+ ssize_t len;
+
+ snprintf(buf, sizeof(buf), discover_template, SSDP_IP4, ssdp_port);
+ memset(&sin, 0, sizeof(sin));
+ sin.sin_family = AF_INET;
+ sin.sin_port = htons(ssdp_port);
+ sin.sin_addr.s_addr = inet_addr(SSDP_IP4);
+ len = strlen(buf);
+ if (sendto(s, buf, len, 0, (struct sockaddr*)&sin, sizeof(sin)) != len) {
+ perror("sendto multicast IPv4");
+ exit(1);
+ }
+}
+
+
+int get_ipv6_ssdp_socket()
+{
+ int s;
+ int reuse = 1;
+ int loop = 0;
+ struct sockaddr_in6 sin6;
+ struct ipv6_mreq mreq;
+ int idx;
+ int hops;
+
+ if ((s = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) {
+ perror("socket SOCK_DGRAM");
+ exit(1);
+ }
+
+ if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (char *)&reuse, sizeof(reuse))) {
+ perror("setsockopt SO_REUSEADDR");
+ exit(1);
+ }
+
+ if (setsockopt(s, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, &loop, sizeof(loop))) {
+ perror("setsockopt IPV6_MULTICAST_LOOP");
+ exit(1);
+ }
+
+ memset(&sin6, 0, sizeof(sin6));
+ sin6.sin6_family = AF_INET6;
+ sin6.sin6_port = htons(ssdp_port);
+ sin6.sin6_addr = in6addr_any;
+ if (bind(s, (struct sockaddr*)&sin6, sizeof(sin6))) {
+ perror("bind");
+ exit(1);
+ }
+
+ idx = if_nametoindex("br0");
+ if (setsockopt(s, IPPROTO_IPV6, IPV6_MULTICAST_IF, &idx, sizeof(idx))) {
+ perror("IP_MULTICAST_IF");
+ exit(1);
+ }
+
+ hops = 2;
+ if (setsockopt(s, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, &hops, sizeof(hops))) {
+ perror("IPV6_MULTICAST_HOPS");
+ exit(1);
+ }
+
+ memset(&mreq, 0, sizeof(mreq));
+ mreq.ipv6mr_interface = idx;
+ if (inet_pton(AF_INET6, SSDP_IP6, &mreq.ipv6mr_multiaddr) != 1) {
+ fprintf(stderr, "ERR: inet_pton(%s) failed", SSDP_IP6);
+ exit(1);
+ }
+ if (setsockopt(s, IPPROTO_IPV6, IPV6_JOIN_GROUP, &mreq, sizeof(mreq)) < 0) {
+ perror("ERR: setsockopt(IPV6_JOIN_GROUP)");
+ exit(1);
+ }
+
+ return s;
+}
+
+
+void send_ssdp_ip6_request(int s)
+{
+ struct sockaddr_in6 sin6;
+ char buf[1024];
+ ssize_t len;
+
+ snprintf(buf, sizeof(buf), discover_template, SSDP_IP6, ssdp_port);
+ memset(&sin6, 0, sizeof(sin6));
+ sin6.sin6_family = AF_INET6;
+ sin6.sin6_port = htons(ssdp_port);
+ if (inet_pton(AF_INET6, SSDP_IP6, &sin6.sin6_addr) != 1) {
+ fprintf(stderr, "ERR: inet_pton(%s) failed", SSDP_IP6);
+ exit(1);
+ }
+ len = strlen(buf);
+ if (sendto(s, buf, len, 0, (struct sockaddr*)&sin6, sizeof(sin6)) != len) {
+ perror("sendto multicast IPv6");
+ exit(1);
+ }
+}
+
+
+std::string handle_ssdp_response(int s, int family)
+{
+ char buffer[4096];
+ char ipbuf[INET6_ADDRSTRLEN];
+ ssize_t pktlen;
+ struct sockaddr from;
+ socklen_t len = sizeof(from);
+
+ pktlen = recvfrom(s, buffer, sizeof(buffer), 0, &from, &len);
+ if (pktlen <= 0) {
+ return std::string("");
+ }
+
+ if (family == AF_INET) {
+ struct sockaddr_in *sin = (struct sockaddr_in *)&from;
+ inet_ntop(AF_INET, &sin->sin_addr, ipbuf, sizeof(ipbuf));
+ return std::string(ipbuf);
+ } else if (family == AF_INET6) {
+ struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)&from;
+ inet_ntop(AF_INET6, &sin6->sin6_addr, ipbuf, sizeof(ipbuf));
+ return std::string(ipbuf);
+ }
+
+ return std::string("");
+}
+
+
+/* Wait for SSDP NOTIFY messages to arrive. */
+ResultsSet listen_for_responses(int s4, int s6)
+{
+ ResultsSet results;
+ struct timeval tv;
+ fd_set rfds;
+ int maxfd = (s4 > s6) ? s4 : s6;
+
+ memset(&tv, 0, sizeof(tv));
+ tv.tv_sec = timeout_secs;
+ tv.tv_usec = 0;
+
+ FD_ZERO(&rfds);
+ FD_SET(s4, &rfds);
+ FD_SET(s6, &rfds);
+
+ while (select(maxfd + 1, &rfds, NULL, NULL, &tv) > 0) {
+ if (FD_ISSET(s4, &rfds)) {
+ std::string ip = handle_ssdp_response(s4, AF_INET);
+ if (!ip.empty()) {
+ results.insert(ip);
+ }
+ }
+ if (FD_ISSET(s6, &rfds)) {
+ std::string ip = handle_ssdp_response(s6, AF_INET6);
+ if (!ip.empty()) {
+ results.insert(ip);
+ }
+ }
+
+ FD_ZERO(&rfds);
+ FD_SET(s4, &rfds);
+ FD_SET(s6, &rfds);
+ }
+
+ return results;
+}
+
+
+void usage(char *progname) {
+ fprintf(stderr, "usage: %s [-t port]\nwhere:\n", progname);
+ fprintf(stderr, "\t-t port: test mode, send to localhost port\n");
+ exit(1);
+}
+
+
+int main(int argc, char **argv)
+{
+ int c;
+ int s4, s6;
+
+ setlinebuf(stdout);
+ alarm(30);
+
+ while ((c = getopt(argc, argv, "t:")) != -1) {
+ switch(c) {
+ case 't':
+ timeout_secs = 1;
+ ssdp_port = atoi(optarg);
+ ssdp_loop = 1;
+ break;
+ default: usage(argv[0]); break;
+ }
+ }
+
+ s4 = get_ipv4_ssdp_socket();
+ send_ssdp_ip4_request(s4);
+ s6 = get_ipv6_ssdp_socket();
+ send_ssdp_ip6_request(s6);
+ ResultsSet IPs = listen_for_responses(s4, s6);
+
+ std::string output("DIAL responses from: ");
+ for (ResultsSet::const_iterator ii = IPs.begin(); ii != IPs.end(); ++ii) {
+ output.append(*ii);
+ output.append(" ");
+ }
+ std::cout << output << std::endl;
+
+ exit(0);
+}
diff --git a/cmds/host-test-dialcheck.sh b/cmds/host-test-dialcheck.sh
new file mode 100755
index 0000000..2eb8a8a
--- /dev/null
+++ b/cmds/host-test-dialcheck.sh
@@ -0,0 +1,20 @@
+#!/bin/bash
+#
+# Copyright 2016 Google Inc. All Rights Reserved.
+
+. ./wvtest/wvtest.sh
+
+PORTFILE="/tmp/dialcheck.test.$$.port"
+OUTFILE="/tmp/dialcheck.test.$$.output"
+
+WVSTART "dialcheck test"
+
+rm -f "$PORTFILE" "$OUTFILE"
+python ./dialcheck-test-server.py "$PORTFILE" &
+for i in $(seq 50); do if [ ! -f "$PORTFILE" ]; then sleep 0.1; fi; done
+
+port=$(cat "$PORTFILE")
+# Dial response will come from the IP address of the builder.
+WVPASS ./host-dialcheck -t "$port" >"$OUTFILE"
+WVPASS grep "DIAL responses from: " "$OUTFILE"
+rm -f "$PORTFILE" "$OUTFILE"
diff --git a/cmds/host-test-ssdptax.sh b/cmds/host-test-ssdptax.sh
index ebb3ae7..584401e 100755
--- a/cmds/host-test-ssdptax.sh
+++ b/cmds/host-test-ssdptax.sh
@@ -6,20 +6,34 @@
SSDP=./host-ssdptax
FIFO="/tmp/ssdptax.test.$$"
+OUTFILE="/tmp/ssdptax.test.$$.output"
WVSTART "ssdptax test"
python ./ssdptax-test-server.py "$FIFO" 1 &
sleep 0.5
-WVPASSEQ "$($SSDP -t $FIFO)" "ssdp 00:00:00:00:00:00 Test Device;Google Fiber ssdptax"
-rm "$FIFO"
+WVPASS $SSDP -t "$FIFO" >"$OUTFILE"
+WVPASS grep -q "ssdp 00:00:00:00:00:00 Test Device;Google Fiber ssdptax" "$OUTFILE"
+echo quitquitquit | nc -U "$FIFO"
+rm -f "$FIFO" "$OUTFILE"
python ./ssdptax-test-server.py "$FIFO" 2 &
sleep 0.5
-WVPASSEQ "$($SSDP -t $FIFO)" "ssdp 00:00:00:00:00:00 REDACTED;server type"
-rm "$FIFO"
+WVPASS $SSDP -t "$FIFO" >"$OUTFILE"
+WVPASS grep -q "ssdp 00:00:00:00:00:00 REDACTED;server type" "$OUTFILE"
+echo quitquitquit | nc -U "$FIFO"
+rm -f "$FIFO" "$OUTFILE"
python ./ssdptax-test-server.py "$FIFO" 3 &
sleep 0.5
-WVPASSEQ "$($SSDP -t $FIFO)" "ssdp 00:00:00:00:00:00 Unknown;server type"
-rm "$FIFO"
+WVPASS $SSDP -t "$FIFO" >"$OUTFILE"
+WVPASS grep -q "ssdp 00:00:00:00:00:00 Unknown;server type" "$OUTFILE"
+echo quitquitquit | nc -U "$FIFO"
+rm -f "$FIFO" "$OUTFILE"
+
+python ./ssdptax-test-server.py "$FIFO" 4 &
+sleep 0.5
+WVPASS $SSDP -t "$FIFO" >"$OUTFILE"
+WVPASS grep -q "ssdp 00:00:00:00:00:00 Test Device;Google Fiber ssdptax multicast" "$OUTFILE"
+echo quitquitquit | nc -U "$FIFO"
+rm -f "$FIFO" "$OUTFILE"
diff --git a/cmds/ssdptax-test-server.py b/cmds/ssdptax-test-server.py
index 54831d4..c86283a 100644
--- a/cmds/ssdptax-test-server.py
+++ b/cmds/ssdptax-test-server.py
@@ -5,7 +5,9 @@
import BaseHTTPServer
import socket
+import SocketServer
import sys
+import threading
text_device_xml = """<root>
@@ -31,45 +33,107 @@
<device></device></root>"""
-xml = ['']
+ssdp_device_xml = """<root>
+ <specVersion><major>1</major><minor>0</minor></specVersion>
+ <device><friendlyName>Test Device</friendlyName>
+ <manufacturer>Google Fiber</manufacturer>
+ <modelDescription>Unit Test</modelDescription>
+ <modelName>ssdptax multicast</modelName>
+</device></root>"""
-class XmlHandler(BaseHTTPServer.BaseHTTPRequestHandler):
+notify_template = 'NOTIFY\r\nHOST:239.255.255.250:1900\r\nLOCATION:%s\r\n'
+notify_text = ['']
+
+
+minissdpd_response = ['']
+keep_running = [True]
+
+
+class HttpHandler(BaseHTTPServer.BaseHTTPRequestHandler):
+ """Respond to an HHTP GET for SSDP DeviceInfo."""
+
def do_GET(self):
self.send_response(200)
self.send_header('Content-type','text/xml')
self.end_headers()
- self.wfile.write(xml[0])
+ if self.path.endswith('text_device_xml'):
+ self.wfile.write(text_device_xml)
+ if self.path.endswith('email_address_xml'):
+ self.wfile.write(email_address_xml)
+ if self.path.endswith('no_friendlyname_xml'):
+ self.wfile.write(no_friendlyname_xml)
+ if self.path.endswith('ssdp_device_xml'):
+ self.wfile.write(ssdp_device_xml)
+
+
+class ThreadingHTTPServer(SocketServer.ThreadingMixIn, BaseHTTPServer.HTTPServer):
+ pass
+
+
+class UnixHandler(SocketServer.StreamRequestHandler):
+ """Respond to a command on MiniSSDPd's Unix socket."""
+
+ def handle(self):
+ data = self.request.recv(8192)
+ if 'quitquitquit' in data:
+ print 'Received quitquitquit, exiting...'
+ keep_running[0] = False
+ return
+ else:
+ self.request.sendall(bytearray(minissdpd_response[0]))
+
+
+class UdpHandler(SocketServer.DatagramRequestHandler):
+ def handle(self):
+ self.request[1].sendto(bytearray(notify_text[0]), self.client_address)
+
+
+class ThreadingUdpServer(SocketServer.ThreadingUDPServer):
+ allow_reuse_address = True
def main():
- un = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
- un.bind(sys.argv[1])
- un.listen(1)
- conn, _ = un.accept()
-
+ socketpath = sys.argv[1]
testnum = int(sys.argv[2])
if testnum == 1:
- xml[0] = text_device_xml
+ pathend = 'text_device_xml'
if testnum == 2:
- xml[0] = email_address_xml
+ pathend = 'email_address_xml'
if testnum == 3:
- xml[0] = no_friendlyname_xml
+ pathend = 'no_friendlyname_xml'
+ if testnum == 4:
+ pathend = 'ssdp_device_xml'
- s = BaseHTTPServer.HTTPServer(("", 0), XmlHandler)
- sn = s.socket.getsockname()
+ h = ThreadingHTTPServer(("", 0), HttpHandler)
+ sn = h.socket.getsockname()
port = sn[1]
- url = 'http://127.0.0.1:%d/foo.xml' % port
+ url = 'http://127.0.0.1:%d/%s' % (port, pathend)
st = 'server type'
uuid = 'uuid goes here'
- data = [1]
- data.extend([len(url)] + list(url))
- data.extend([len(st)] + list(st))
- data.extend([len(uuid)] + list(uuid))
+ if testnum == 4:
+ minissdpd_response[0] = [0]
+ else:
+ minissdpd_response[0] = [1]
+ minissdpd_response[0].extend([len(url)] + list(url))
+ minissdpd_response[0].extend([len(st)] + list(st))
+ minissdpd_response[0].extend([len(uuid)] + list(uuid))
+ notify_text[0] = notify_template % url
- _ = conn.recv(8192)
- conn.sendall(bytearray(data))
- s.handle_request()
+ h_thread = threading.Thread(target=h.serve_forever)
+ h_thread.daemon = True
+ h_thread.start()
+
+ d = ThreadingUdpServer(('', 1900), UdpHandler)
+ d.socket.setsockopt(socket.IPPROTO_IP, socket.IP_ADD_MEMBERSHIP,
+ socket.inet_aton('239.255.255.250') + socket.inet_aton('0.0.0.0'))
+ d_thread = threading.Thread(target=d.serve_forever)
+ d_thread.daemon = True
+ d_thread.start()
+
+ u = SocketServer.UnixStreamServer(socketpath, UnixHandler)
+ while keep_running[0]:
+ u.handle_request()
if __name__ == '__main__':
diff --git a/cmds/ssdptax.cc b/cmds/ssdptax.cc
index 2a06c7a..d2663fc 100644
--- a/cmds/ssdptax.cc
+++ b/cmds/ssdptax.cc
@@ -30,6 +30,7 @@
#include <ctype.h>
#include <curl/curl.h>
#include <getopt.h>
+#include <net/if.h>
#include <netinet/in.h>
#include <regex.h>
#include <stdio.h>
@@ -43,6 +44,7 @@
#include <iostream>
#include <set>
+#include <tr1/unordered_map>
#include "l2utils.h"
@@ -68,10 +70,11 @@
typedef struct ssdp_info {
ssdp_info(): srv_type(), url(), friendlyName(), ipaddr(),
- manufacturer(), model(), failed(0) {}
+ manufacturer(), model(), buffer(), failed(0) {}
ssdp_info(const ssdp_info& s): srv_type(s.srv_type), url(s.url),
friendlyName(s.friendlyName), ipaddr(s.ipaddr),
- manufacturer(s.manufacturer), model(s.model), failed(s.failed) {}
+ manufacturer(s.manufacturer), model(s.model),
+ buffer(s.buffer), failed(s.failed) {}
std::string srv_type;
std::string url;
std::string friendlyName;
@@ -84,6 +87,24 @@
} ssdp_info_t;
+typedef std::tr1::unordered_map<std::string, ssdp_info_t*> ResponsesMap;
+
+
+int ssdp_loop = 0;
+
+
+/* SSDP Discover packet */
+#define SSDP_PORT 1900
+#define SSDP_IP4 "239.255.255.250"
+#define SSDP_IP6 "ff02::c"
+const char discover_template[] = "M-SEARCH * HTTP/1.1\r\n"
+ "HOST: %s:%d\r\n"
+ "MAN: \"ssdp:discover\"\r\n"
+ "MX: 2\r\n"
+ "USER-AGENT: ssdptax/1.0\r\n"
+ "ST: %s\r\n\r\n";
+
+
static void strncpy_limited(char *dst, size_t dstlen,
const char *src, size_t srclen)
{
@@ -104,6 +125,13 @@
}
+static time_t monotime(void) {
+ struct timespec ts;
+ clock_gettime(CLOCK_MONOTONIC, &ts);
+ return ts.tv_sec;
+}
+
+
/*
* Send a request to minissdpd. Returns a std::string containing
* minissdpd's response.
@@ -124,19 +152,19 @@
if (s < 0) {
perror("socket AF_UNIX failed");
- exit(1);
+ return rc;
}
memset(&addr, 0, sizeof(addr));
addr.sun_family = AF_UNIX;
strncpy(addr.sun_path, sock_path, sizeof(addr.sun_path));
- if(connect(s, (struct sockaddr *)&addr, sizeof(struct sockaddr_un)) < 0) {
+ if (connect(s, (struct sockaddr *)&addr, sizeof(struct sockaddr_un)) < 0) {
perror("connect to minisspd failed");
- exit(1);
+ return rc;
}
if ((buffer = (char *)malloc(siz)) == NULL) {
fprintf(stderr, "malloc(%zu) failed\n", siz);
- exit(1);
+ return rc;
}
memset(buffer, 0, siz);
@@ -147,7 +175,8 @@
p += device_len;
if (write(s, buffer, p - buffer) < 0) {
perror("write to minissdpd failed");
- exit(1);
+ free(buffer);
+ return rc;
}
FD_ZERO(&readfds);
@@ -157,18 +186,174 @@
if (select(s + 1, &readfds, NULL, NULL, &tv) < 1) {
fprintf(stderr, "select failed\n");
- exit(1);
+ free(buffer);
+ return rc;
}
if ((len = read(s, buffer, siz)) < 0) {
perror("read from minissdpd failed");
- exit(1);
+ free(buffer);
+ return rc;
}
close(s);
rc = std::string(buffer, len);
free(buffer);
- return(rc);
+ return rc;
+}
+
+
+int get_ipv4_ssdp_socket()
+{
+ int s;
+ int reuse = 1;
+ struct sockaddr_in sin;
+ struct ip_mreq mreq;
+ struct ip_mreqn mreqn;
+
+ if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
+ perror("socket SOCK_DGRAM");
+ exit(1);
+ }
+
+ if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (char *)&reuse, sizeof(reuse))) {
+ perror("setsockopt SO_REUSEADDR");
+ exit(1);
+ }
+
+ if (setsockopt(s, IPPROTO_IP, IP_MULTICAST_LOOP,
+ &ssdp_loop, sizeof(ssdp_loop))) {
+ perror("setsockopt IP_MULTICAST_LOOP");
+ exit(1);
+ }
+
+ memset(&sin, 0, sizeof(sin));
+ sin.sin_family = AF_INET;
+ sin.sin_port = htons(SSDP_PORT);
+ sin.sin_addr.s_addr = INADDR_ANY;
+ if (bind(s, (struct sockaddr*)&sin, sizeof(sin))) {
+ perror("bind");
+ exit(1);
+ }
+
+ memset(&mreqn, 0, sizeof(mreqn));
+ mreqn.imr_ifindex = if_nametoindex("br0");
+ if (setsockopt(s, IPPROTO_IP, IP_MULTICAST_IF, &mreqn, sizeof(mreqn))) {
+ perror("IP_MULTICAST_IF");
+ exit(1);
+ }
+
+ memset(&mreq, 0, sizeof(mreq));
+ mreq.imr_multiaddr.s_addr = inet_addr(SSDP_IP4);
+ if (setsockopt(s, IPPROTO_IP, IP_ADD_MEMBERSHIP,
+ (char *)&mreq, sizeof(mreq))) {
+ perror("IP_ADD_MEMBERSHIP");
+ exit(1);
+ }
+
+ return s;
+}
+
+
+void send_ssdp_ip4_request(int s, const char *search)
+{
+ struct sockaddr_in sin;
+ char buf[1024];
+ ssize_t len;
+
+ snprintf(buf, sizeof(buf), discover_template, SSDP_IP4, SSDP_PORT, search);
+ memset(&sin, 0, sizeof(sin));
+ sin.sin_family = AF_INET;
+ sin.sin_port = htons(SSDP_PORT);
+ sin.sin_addr.s_addr = inet_addr(SSDP_IP4);
+ len = strlen(buf);
+ if (sendto(s, buf, len, 0, (struct sockaddr*)&sin, sizeof(sin)) != len) {
+ perror("sendto multicast IPv4");
+ exit(1);
+ }
+}
+
+
+int get_ipv6_ssdp_socket()
+{
+ int s;
+ int reuse = 1;
+ struct sockaddr_in6 sin6;
+ struct ipv6_mreq mreq;
+ int idx;
+ int hops;
+
+ if ((s = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) {
+ perror("socket SOCK_DGRAM");
+ exit(1);
+ }
+
+ if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (char *)&reuse, sizeof(reuse))) {
+ perror("setsockopt SO_REUSEADDR");
+ exit(1);
+ }
+
+ if (setsockopt(s, IPPROTO_IPV6, IPV6_MULTICAST_LOOP,
+ &ssdp_loop, sizeof(ssdp_loop))) {
+ perror("setsockopt IPV6_MULTICAST_LOOP");
+ exit(1);
+ }
+
+ memset(&sin6, 0, sizeof(sin6));
+ sin6.sin6_family = AF_INET6;
+ sin6.sin6_port = htons(SSDP_PORT);
+ sin6.sin6_addr = in6addr_any;
+ if (bind(s, (struct sockaddr*)&sin6, sizeof(sin6))) {
+ perror("bind");
+ exit(1);
+ }
+
+ idx = if_nametoindex("br0");
+ if (setsockopt(s, IPPROTO_IPV6, IPV6_MULTICAST_IF, &idx, sizeof(idx))) {
+ perror("IP_MULTICAST_IF");
+ exit(1);
+ }
+
+ hops = 2;
+ if (setsockopt(s, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, &hops, sizeof(hops))) {
+ perror("IPV6_MULTICAST_HOPS");
+ exit(1);
+ }
+
+ memset(&mreq, 0, sizeof(mreq));
+ mreq.ipv6mr_interface = idx;
+ if (inet_pton(AF_INET6, SSDP_IP6, &mreq.ipv6mr_multiaddr) != 1) {
+ fprintf(stderr, "ERR: inet_pton(%s) failed", SSDP_IP6);
+ exit(1);
+ }
+ if (setsockopt(s, IPPROTO_IPV6, IPV6_JOIN_GROUP, &mreq, sizeof(mreq)) < 0) {
+ perror("ERR: setsockopt(IPV6_JOIN_GROUP)");
+ exit(1);
+ }
+
+ return s;
+}
+
+
+void send_ssdp_ip6_request(int s, const char *search)
+{
+ struct sockaddr_in6 sin6;
+ char buf[1024];
+ ssize_t len;
+
+ snprintf(buf, sizeof(buf), discover_template, SSDP_IP6, SSDP_PORT, search);
+ memset(&sin6, 0, sizeof(sin6));
+ sin6.sin6_family = AF_INET6;
+ sin6.sin6_port = htons(SSDP_PORT);
+ if (inet_pton(AF_INET6, SSDP_IP6, &sin6.sin6_addr) != 1) {
+ fprintf(stderr, "ERR: inet_pton(%s) failed", SSDP_IP6);
+ exit(1);
+ }
+ len = strlen(buf);
+ if (sendto(s, buf, len, 0, (struct sockaddr*)&sin6, sizeof(sin6)) != len) {
+ perror("sendto multicast IPv6");
+ exit(1);
+ }
}
@@ -389,8 +574,102 @@
}
+std::string trim(std::string s)
+{
+ size_t start = s.find_first_not_of(" \t\v\f\b\r\n");
+ if (std::string::npos != start && 0 != start) s = s.erase(0, start);
+
+ size_t end = s.find_last_not_of(" \t\v\f\b\r\n");
+ if (std::string::npos != end) s = s.substr(0, end + 1);
+
+ return s;
+}
+
+
+void parse_ssdp_response(int s, ResponsesMap &responses)
+{
+ ssdp_info_t *info = new ssdp_info_t;
+ char buffer[4096];
+ char *p, *saveptr, *strtok_pos;
+ ssize_t pktlen;
+
+ memset(buffer, 0, sizeof(buffer));
+ pktlen = recv(s, buffer, sizeof(buffer) - 1, 0);
+ if (pktlen < 0 || (size_t)pktlen >= sizeof(buffer)) {
+ fprintf(stderr, "error receiving SSDP response, pktlen=%zd\n", pktlen);
+ delete info;
+ /* not fatal, just return */
+ return;
+ }
+ buffer[pktlen] = '\0';
+ strtok_pos = buffer;
+
+ while ((p = strtok_r(strtok_pos, "\r\n", &saveptr)) != NULL) {
+ if (strlen(p) > 9 && strncasecmp(p, "location:", 9) == 0) {
+ char urlbuf[512];
+ p += 9;
+ strncpy_limited(urlbuf, sizeof(urlbuf), p, strlen(p));
+ info->url = trim(std::string(urlbuf, strlen(urlbuf)));
+ } else if (strlen(p) > 7 && strncasecmp(p, "server:", 7) == 0) {
+ char srv_type_buf[256];
+ p += 7;
+ strncpy_limited(srv_type_buf, sizeof(srv_type_buf), p, strlen(p));
+ info->srv_type = trim(std::string(srv_type_buf, strlen(srv_type_buf)));
+ }
+ strtok_pos = NULL;
+ }
+
+ if (info->url.length() && responses.find(info->url) == responses.end()) {
+ fetch_device_info(info->url, info);
+ responses[info->url] = info;
+ } else {
+ delete info;
+ }
+}
+
+
+/* Wait for SSDP NOTIFY messages to arrive. */
+#define TIMEOUT_SECS 5
+void listen_for_responses(int s4, int s6, ResponsesMap &responses)
+{
+ struct timeval tv;
+ fd_set rfds;
+ int maxfd = (s4 > s6) ? s4 : s6;
+ time_t start = monotime();
+
+ memset(&tv, 0, sizeof(tv));
+ tv.tv_sec = TIMEOUT_SECS;
+ tv.tv_usec = 0;
+
+ FD_ZERO(&rfds);
+ FD_SET(s4, &rfds);
+ FD_SET(s6, &rfds);
+
+ while (select(maxfd + 1, &rfds, NULL, NULL, &tv) > 0) {
+ time_t end = monotime();
+ if (FD_ISSET(s4, &rfds)) {
+ parse_ssdp_response(s4, responses);
+ }
+ if (FD_ISSET(s6, &rfds)) {
+ parse_ssdp_response(s6, responses);
+ }
+
+ FD_ZERO(&rfds);
+ FD_SET(s4, &rfds);
+ FD_SET(s6, &rfds);
+
+ if ((end - start) > TIMEOUT_SECS) {
+ /* even on a network filled with SSDP packets,
+ * return after TIMEOUT_SECS. */
+ break;
+ }
+ }
+}
+
+
void usage(char *progname) {
- printf("usage: %s [-t /path/to/fifo]\n", progname);
+ printf("usage: %s [-t /path/to/fifo] [-s search]\n", progname);
+ printf("\t-s\tserver type to search for (default ssdp:all)\n");
printf("\t-t\ttest mode, use a fake path instead of minissdpd.\n");
exit(1);
}
@@ -399,11 +678,11 @@
int main(int argc, char **argv)
{
std::string buffer;
- typedef std::tr1::unordered_map<std::string, ssdp_info_t*> ResponsesMap;
ResponsesMap responses;
L2Map l2map;
- int c, num;
+ int c, s4, s6;
const char *sock_path = SOCK_PATH;
+ const char *search = "ssdp:all";
setlinebuf(stdout);
alarm(30);
@@ -413,28 +692,52 @@
exit(1);
}
- while ((c = getopt(argc, argv, "t:")) != -1) {
+ while ((c = getopt(argc, argv, "s:t:")) != -1) {
switch(c) {
- case 't': sock_path = optarg; break;
+ case 's': search = optarg; break;
+ case 't':
+ sock_path = optarg;
+ ssdp_loop = 1;
+ break;
default: usage(argv[0]); break;
}
}
- buffer = request_from_ssdpd(sock_path, 3, "ssdp:all");
- num = buffer.c_str()[0];
- buffer.erase(0, 1);
- while ((num-- > 0) && buffer.length() > 0) {
- ssdp_info_t *info = new ssdp_info_t;
+ /* Request the list from MiniSSDPd */
+ buffer = request_from_ssdpd(sock_path, 3, search);
+ if (!buffer.empty()) {
+ int num = buffer.c_str()[0];
+ buffer.erase(0, 1);
+ while ((num-- > 0) && buffer.length() > 0) {
+ ssdp_info_t *info = new ssdp_info_t;
- parse_minissdpd_response(buffer, info->url, info->srv_type);
- if (info->url.length() && responses.find(info->url) == responses.end()) {
- fetch_device_info(info->url, info);
- responses[info->url] = info;
- } else {
- delete info;
+ parse_minissdpd_response(buffer, info->url, info->srv_type);
+ if (info->url.length() && responses.find(info->url) == responses.end()) {
+ fetch_device_info(info->url, info);
+ responses[info->url] = info;
+ } else {
+ delete info;
+ }
}
+
+ /* Capture the ARP table in its current state. */
+ get_l2_map(&l2map);
}
+ /* Supplement what we got from MiniSSDPd by sending
+ * our own M-SEARCH and listening for responses. */
+ s4 = get_ipv4_ssdp_socket();
+ send_ssdp_ip4_request(s4, search);
+ s6 = get_ipv6_ssdp_socket();
+ send_ssdp_ip6_request(s6, search);
+ listen_for_responses(s4, s6, responses);
+ close(s4);
+ s4 = -1;
+ close(s6);
+ s6 = -1;
+
+ /* Capture any new ARP table entries which appeared after sending
+ * our own M-SEARCH. */
get_l2_map(&l2map);
typedef std::set<std::string> ResultsSet;
diff --git a/cmds/test-castcheck.sh b/cmds/test-castcheck.sh
new file mode 100755
index 0000000..7aa3be2
--- /dev/null
+++ b/cmds/test-castcheck.sh
@@ -0,0 +1,7 @@
+#!/bin/bash
+. ./wvtest/wvtest.sh
+
+WVSTART "castcheck test"
+CASTCHECK="./castcheck -a ./avahi-browse-fake.sh"
+
+WVPASSEQ "$($CASTCHECK)" "Cast responses from: 1.1.1.1 2.2.2.2 3.3.3.3"
diff --git a/conman/connection_manager.py b/conman/connection_manager.py
index 6ccdaa2..040e812 100755
--- a/conman/connection_manager.py
+++ b/conman/connection_manager.py
@@ -10,6 +10,7 @@
import os
import random
import re
+import socket
import subprocess
import time
@@ -19,11 +20,18 @@
import pyinotify
import cycler
+import experiment
import interface
import iw
import status
+HOSTNAME = socket.gethostname()
+TMP_HOSTS = '/tmp/hosts'
+
+experiment.register('WifiNo2GClient')
+
+
class FileChangeHandler(pyinotify.ProcessEvent):
"""Connects pyinotify events to ConnectionManager."""
@@ -89,13 +97,15 @@
raise ValueError('Command file does not specify SSID')
if self.wifi.initial_ssid == self.ssid:
- logging.debug('Connected to WLAN at startup')
+ logging.info('Connected to WLAN at startup')
@property
def client_up(self):
wpa_status = self.wifi.wpa_status()
return (wpa_status.get('wpa_state') == 'COMPLETED'
- and wpa_status.get('ssid') == self.ssid)
+ # NONE indicates we're on a provisioning network; anything else
+ # suggests we're already on the WLAN.
+ and wpa_status.get('key_mgmt') != 'NONE')
def start_access_point(self):
"""Start an access point."""
@@ -111,7 +121,7 @@
try:
subprocess.check_output(self.command, stderr=subprocess.STDOUT)
self.access_point_up = True
- logging.debug('Started %s GHz AP', self.band)
+ logging.info('Started %s GHz AP', self.band)
except subprocess.CalledProcessError as e:
logging.error('Failed to start access point: %s', e.output)
@@ -126,18 +136,31 @@
try:
subprocess.check_output(command, stderr=subprocess.STDOUT)
self.access_point_up = False
- logging.debug('Stopped %s GHz AP', self.band)
+ logging.info('Stopped %s GHz AP', self.band)
except subprocess.CalledProcessError as e:
logging.error('Failed to stop access point: %s', e.output)
return
def start_client(self):
"""Join the WLAN as a client."""
+ if experiment.enabled('WifiNo2GClient') and self.band == '2.4':
+ logging.info('WifiNo2GClient enabled; not starting 2.4 GHz client.')
+ return
+
up = self.client_up
if up:
logging.debug('Wifi client already started on %s GHz', self.band)
return
+ if self._actually_start_client():
+ self._post_start_client()
+
+ def _actually_start_client(self):
+ """Actually run wifi setclient.
+
+ Returns:
+ Whether the command succeeded.
+ """
command = self.WIFI_SETCLIENT + ['--ssid', self.ssid, '--band', self.band]
env = dict(os.environ)
if self.passphrase:
@@ -147,8 +170,11 @@
subprocess.check_output(command, stderr=subprocess.STDOUT, env=env)
except subprocess.CalledProcessError as e:
logging.error('Failed to start wifi client: %s', e.output)
- return
+ return False
+ return True
+
+ def _post_start_client(self):
self._status.connected_to_wlan = True
logging.info('Started wifi client on %s GHz', self.band)
self.wifi.attach_wpa_control(self._wpa_control_interface)
@@ -165,7 +191,7 @@
stderr=subprocess.STDOUT)
# TODO(rofrankel): Make this work for dual-radio devices.
self._status.connected_to_wlan = False
- logging.debug('Stopped wifi client on %s GHz', self.band)
+ logging.info('Stopped wifi client on %s GHz', self.band)
except subprocess.CalledProcessError as e:
logging.error('Failed to stop wifi client: %s', e.output)
@@ -192,6 +218,7 @@
IP_LINK = ['ip', 'link']
IFPLUGD_ACTION = ['/etc/ifplugd/ifplugd.action']
BINWIFI = ['wifi']
+ UPLOAD_LOGS_AND_WAIT = ['timeout', '60', 'upload-logs-and-wait']
def __init__(self,
bridge_interface='br0',
@@ -201,7 +228,7 @@
wpa_control_interface='/var/run/wpa_supplicant',
run_duration_s=1, interface_update_period=5,
wifi_scan_period_s=120, wlan_retry_s=15, acs_update_wait_s=10,
- bssid_cycle_length_s=30):
+ dhcp_wait_s=10, bssid_cycle_length_s=30):
self._tmp_dir = tmp_dir
self._config_dir = config_dir
@@ -214,8 +241,10 @@
self._wifi_scan_period_s = wifi_scan_period_s
self._wlan_retry_s = wlan_retry_s
self._acs_update_wait_s = acs_update_wait_s
+ self._dhcp_wait_s = dhcp_wait_s
self._bssid_cycle_length_s = bssid_cycle_length_s
self._wlan_configuration = {}
+ self._try_to_upload_logs = False
# Make sure all necessary directories exist.
for directory in (self._tmp_dir, self._config_dir, self._moca_tmp_dir,
@@ -294,7 +323,7 @@
# the routing table.
for ifc in [self.bridge] + self.wifi:
ifc.initialize()
- logging.debug('%s initialized', ifc.name)
+ logging.info('%s initialized', ifc.name)
self._interface_update_counter = 0
self._try_wlan_after = {'5': 0, '2.4': 0}
@@ -425,7 +454,7 @@
if self._connected_to_wlan(wifi):
self._status.connected_to_wlan = True
logging.debug('Connected to WLAN on %s, nothing else to do.', wifi.name)
- return
+ break
# This interface is not connected to the WLAN, so scan for potential
# routes to the ACS for provisioning.
@@ -443,10 +472,10 @@
for band in wifi.bands:
wlan_configuration = self._wlan_configuration.get(band, None)
if wlan_configuration and time.time() > self._try_wlan_after[band]:
- logging.debug('Trying to join WLAN on %s.', wifi.name)
+ logging.info('Trying to join WLAN on %s.', wifi.name)
wlan_configuration.start_client()
if self._connected_to_wlan(wifi):
- logging.debug('Joined WLAN on %s.', wifi.name)
+ logging.info('Joined WLAN on %s.', wifi.name)
self._status.connected_to_wlan = True
self._try_wlan_after[band] = 0
break
@@ -464,10 +493,15 @@
logging.debug('Unable to join WLAN on %s', wifi.name)
self._status.connected_to_wlan = False
if self.acs():
- logging.debug('Connected to ACS on %s', wifi.name)
- wifi.last_successful_bss_info = getattr(wifi,
- 'last_attempted_bss_info',
- None)
+ logging.debug('Connected to ACS')
+ if self._try_to_upload_logs:
+ self._try_upload_logs()
+ self._try_to_upload_logs = False
+
+ if wifi.acs():
+ wifi.last_successful_bss_info = getattr(wifi,
+ 'last_attempted_bss_info',
+ None)
now = time.time()
if (self._wlan_configuration and
hasattr(wifi, 'waiting_for_acs_since')):
@@ -489,8 +523,21 @@
# If we didn't manage to join the WLAN and we don't have an ACS
# connection, we should try to establish one.
else:
- logging.debug('Not connected to ACS on %s', wifi.name)
- self._try_next_bssid(wifi)
+ # If we are associated but waiting for a DHCP lease, try again later.
+ now = time.time()
+ connected_to_open = (
+ wifi.wpa_status().get('wpa_state', None) == 'COMPLETED' and
+ wifi.wpa_status().get('key_mgmt', None) == 'NONE')
+ wait_for_dhcp = (
+ not wifi.gateway() and
+ hasattr(wifi, 'waiting_for_dhcp_since') and
+ now - wifi.waiting_for_dhcp_since < self._dhcp_wait_s)
+ if connected_to_open and wait_for_dhcp:
+ logging.debug('Waiting for DHCP lease after %ds.',
+ now - wifi.waiting_for_acs_since)
+ else:
+ logging.debug('Not connected to ACS')
+ self._try_next_bssid(wifi)
time.sleep(max(0, self._run_duration_s - (time.time() - start_time)))
@@ -529,6 +576,37 @@
self.acs()
self.internet()
+ # Update /etc/hosts (depends on routing table)
+ self._update_tmp_hosts()
+
+ def _update_tmp_hosts(self):
+ """Update the contents of /tmp/hosts."""
+ lowest_metric_interface = None
+ for ifc in [self.bridge] + self.wifi:
+ route = ifc.current_route()
+ if route:
+ metric = route.get('metric', 0)
+ # Skip temporary connection_check routes.
+ if metric == '99':
+ continue
+ candidate = (metric, ifc)
+ if (lowest_metric_interface is None or
+ candidate < lowest_metric_interface):
+ lowest_metric_interface = candidate
+
+ ip_line = ''
+ if lowest_metric_interface:
+ ip = lowest_metric_interface[1].get_ip_address()
+ ip_line = '%s %s\n' % (ip, HOSTNAME) if ip else ''
+
+ new_tmp_hosts = '%s127.0.0.1 localhost' % ip_line
+
+ if not os.path.exists(TMP_HOSTS) or open(TMP_HOSTS).read() != new_tmp_hosts:
+ tmp_hosts_tmp_filename = TMP_HOSTS + '.tmp'
+ tmp_hosts_tmp = open(tmp_hosts_tmp_filename, 'w')
+ tmp_hosts_tmp.write(new_tmp_hosts)
+ os.rename(tmp_hosts_tmp_filename, TMP_HOSTS)
+
def handle_event(self, path, filename, deleted):
if deleted:
self._handle_deleted_file(path, filename)
@@ -577,7 +655,7 @@
if filename == self.ETHERNET_STATUS_FILE:
try:
self.bridge.ethernet = bool(int(contents))
- logging.debug('Ethernet %s', 'up' if self.bridge.ethernet else 'down')
+ logging.info('Ethernet %s', 'up' if self.bridge.ethernet else 'down')
except ValueError:
logging.error('Status file contents should be 0 or 1, not %s',
contents)
@@ -600,7 +678,7 @@
wifi = self.wifi_for_band(band)
if wifi and band in self._wlan_configuration:
self._wlan_configuration[band].access_point = True
- logging.debug('AP enabled for %s GHz', band)
+ logging.info('AP enabled for %s GHz', band)
elif path == self._tmp_dir:
if filename.startswith(self.GATEWAY_FILE_PREFIX):
@@ -608,8 +686,8 @@
ifc = self.interface_by_name(interface_name)
if ifc:
ifc.set_gateway_ip(contents)
- logging.debug('Received gateway %r for interface %s', contents,
- ifc.name)
+ logging.info('Received gateway %r for interface %s', contents,
+ ifc.name)
elif path == self._moca_tmp_dir:
match = re.match(r'^%s\d+$' % self.MOCA_NODE_FILE_PREFIX, filename)
@@ -683,16 +761,19 @@
last_successful_bss_info = getattr(wifi, 'last_successful_bss_info', None)
bss_info = last_successful_bss_info or wifi.cycler.next()
if bss_info is not None:
- logging.debug('Attempting to connect to SSID %s for provisioning',
- bss_info.ssid)
+ logging.info('Attempting to connect to SSID %s (%s) for provisioning',
+ bss_info.ssid, bss_info.bssid)
self._status.trying_open = True
+ wifi.set_gateway_ip(None)
connected = self._try_bssid(wifi, bss_info)
if connected:
self._status.connected_to_open = True
now = time.time()
wifi.waiting_for_acs_since = now
+ wifi.waiting_for_dhcp_since = now
wifi.complain_about_acs_at = now + 5
logging.info('Attempting to provision via SSID %s', bss_info.ssid)
+ self._try_to_upload_logs = True
# If we can no longer connect to this, it's no longer successful.
elif bss_info == last_successful_bss_info:
wifi.last_successful_bss_info = None
@@ -734,7 +815,7 @@
wlan_configuration.access_point = os.path.exists(ap_file)
self._wlan_configuration[band] = wlan_configuration
self._status.have_config = True
- logging.debug('Updated WLAN configuration for %s GHz', band)
+ logging.info('Updated WLAN configuration for %s GHz', band)
self._update_access_point(wlan_configuration)
def _update_access_point(self, wlan_configuration):
@@ -788,6 +869,11 @@
subprocess.check_output(self.BINWIFI + list(command),
stderr=subprocess.STDOUT)
+ def _try_upload_logs(self):
+ logging.info('Attempting to upload logs')
+ if subprocess.call(self.UPLOAD_LOGS_AND_WAIT) != 0:
+ logging.error('Failed to upload logs')
+
def _wifi_show():
try:
diff --git a/conman/connection_manager_test.py b/conman/connection_manager_test.py
index 954e2e3..1f90f96 100755
--- a/conman/connection_manager_test.py
+++ b/conman/connection_manager_test.py
@@ -9,6 +9,7 @@
import time
import connection_manager
+import experiment_testutils
import interface_test
import iw
import status
@@ -180,22 +181,25 @@
WIFI_SETCLIENT = ['echo', 'setclient']
WIFI_STOPCLIENT = ['echo', 'stopclient']
- def start_client(self):
- client_was_up = self.client_up
- was_attached = self.wifi.attached()
+ def _actually_start_client(self):
+ self.client_was_up = self.client_up
+ self.was_attached = self.wifi.attached()
+ self.wifi._secure_testonly = True
# Do this before calling the super method so that the attach call at the end
# succeeds.
- if not client_was_up and not was_attached:
+ if not self.client_was_up and not self.was_attached:
self.wifi._initial_ssid_testonly = self.ssid
self.wifi.start_wpa_supplicant_testonly(self._wpa_control_interface)
- super(WLANConfiguration, self).start_client()
+ return True
- if not client_was_up:
+ def _post_start_client(self):
+ if not self.client_was_up:
self.wifi.set_connection_check_result('succeed')
- if was_attached:
+ if self.was_attached:
self.wifi._wpa_control.ssid_testonly = self.ssid
+ self.wifi._wpa_control.secure_testonly = True
self.wifi.add_connected_event()
# Normally, wpa_supplicant would bring up the client interface, which
@@ -263,6 +267,7 @@
IFUP = ['echo', 'ifup']
IFPLUGD_ACTION = ['echo', 'ifplugd.action']
BINWIFI = ['echo', 'wifi']
+ UPLOAD_LOGS_AND_WAIT = ['echo', 'upload-logs-and-wait']
def __init__(self, *args, **kwargs):
self._binwifi_commands = []
@@ -295,12 +300,16 @@
self.can_connect_to_s3 = True
# Will s2 fail rather than providing ACS access?
self.s2_fail = False
+ # Will s3 fail to acquire a DHCP lease?
+ self.dhcp_failure_on_s3 = False
+ self.log_upload_count = 0
def create_wifi_interfaces(self):
super(ConnectionManager, self).create_wifi_interfaces()
for wifi in self.wifi_interfaces_already_up:
# pylint: disable=protected-access
self.interface_by_name(wifi)._initial_ssid_testonly = 'my ssid'
+ self.interface_by_name(wifi)._secure_testonly = True
@property
def IP_LINK(self):
@@ -316,20 +325,23 @@
wifi.add_terminating_event()
def _try_bssid(self, wifi, bss_info):
+ wifi.add_disconnected_event()
self.last_provisioning_attempt = bss_info
super(ConnectionManager, self)._try_bssid(wifi, bss_info)
- def connect(connection_check_result):
+ def connect(connection_check_result, dhcp_failure=False):
# pylint: disable=protected-access
if wifi.attached():
- wifi._wpa_control._ssid_testonly = bss_info.ssid
+ wifi._wpa_control.ssid_testonly = bss_info.ssid
+ wifi._wpa_control.secure_testonly = False
wifi.add_connected_event()
else:
wifi._initial_ssid_testonly = bss_info.ssid
+ wifi._secure_testonly = False
wifi.start_wpa_supplicant_testonly(self._wpa_control_interface)
wifi.set_connection_check_result(connection_check_result)
- self.ifplugd_action(wifi.name, True)
+ self.ifplugd_action(wifi.name, True, dhcp_failure)
if bss_info and bss_info.ssid == 's1':
connect('fail')
@@ -340,7 +352,7 @@
return True
if bss_info and bss_info.ssid == 's3' and self.can_connect_to_s3:
- connect('restricted')
+ connect('restricted', self.dhcp_failure_on_s3)
return True
return False
@@ -372,14 +384,14 @@
super(ConnectionManager, self)._wifi_scan(wifi)
wifi.wifi_scan_counter += 1
- def ifplugd_action(self, interface_name, up):
+ def ifplugd_action(self, interface_name, up, dhcp_failure=False):
# Typically, when moca comes up, conman calls ifplugd.action, which writes
# this file. Also, when conman starts, it calls ifplugd.action for eth0.
self.write_interface_status_file(interface_name, '1' if up else '0')
# ifplugd calls run-dhclient, which results in a gateway file if the link is
# up (and working).
- if up:
+ if up and not dhcp_failure:
self.write_gateway_file('br0' if interface_name in ('eth0', 'moca0')
else interface_name)
@@ -401,6 +413,10 @@
return self._wlan_configuration[band].client_up
+ def _try_upload_logs(self):
+ self.log_upload_count += 1
+ return super(ConnectionManager, self)._try_upload_logs()
+
# Test methods
def delete_wlan_config(self, band):
@@ -493,6 +509,10 @@
os.unlink(ap_filename)
+def check_tmp_hosts(expected_contents):
+ wvtest.WVPASSEQ(open(connection_manager.TMP_HOSTS).read(), expected_contents)
+
+
def connection_manager_test(radio_config, wlan_configs=None,
quantenna_interfaces=None, **cm_kwargs):
"""Returns a decorator that does ConnectionManager test boilerplate."""
@@ -507,6 +527,7 @@
interface_update_period = 5
wifi_scan_period = 15
wifi_scan_period_s = run_duration_s * wifi_scan_period
+ dhcp_wait_s = .5
# pylint: disable=protected-access
old_wifi_show = connection_manager._wifi_show
@@ -518,6 +539,7 @@
try:
# No initial state.
+ connection_manager.TMP_HOSTS = tempfile.mktemp()
tmp_dir = tempfile.mkdtemp()
config_dir = tempfile.mkdtemp()
os.mkdir(os.path.join(tmp_dir, 'interfaces'))
@@ -540,14 +562,18 @@
run_duration_s=run_duration_s,
interface_update_period=interface_update_period,
wifi_scan_period_s=wifi_scan_period_s,
- bssid_cycle_length_s=0.05,
+ dhcp_wait_s=dhcp_wait_s,
+ bssid_cycle_length_s=1,
**cm_kwargs)
c.test_interface_update_period = interface_update_period
c.test_wifi_scan_period = wifi_scan_period
+ c.test_dhcp_wait_s = dhcp_wait_s
f(c)
finally:
+ if os.path.exists(connection_manager.TMP_HOSTS):
+ os.unlink(connection_manager.TMP_HOSTS)
shutil.rmtree(tmp_dir)
shutil.rmtree(config_dir)
shutil.rmtree(moca_tmp_dir)
@@ -585,6 +611,7 @@
wvtest.WVPASS(c.internet())
wvtest.WVPASS(c.has_status_files([status.P.CAN_REACH_ACS,
status.P.CAN_REACH_INTERNET]))
+ hostname = connection_manager.HOSTNAME
c.run_once()
wvtest.WVPASS(c.acs())
@@ -648,12 +675,15 @@
wvtest.WVFAIL(c.acs())
wvtest.WVFAIL(c.internet())
wvtest.WVFAIL(c.bridge.current_route())
+ check_tmp_hosts('127.0.0.1 localhost')
# Now there are some scan results.
c.interface_with_scan_results = c.wifi_for_band(band).name
# Wait for a scan, plus 3 cycles, so that s2 will have been tried.
c.run_until_scan(band)
- for _ in range(3):
+ wvtest.WVPASSEQ(c.log_upload_count, 0)
+ c.wifi_for_band(band).ip_testonly = '192.168.1.100'
+ for _ in range(len(c.wifi_for_band(band).cycler)):
c.run_once()
wvtest.WVPASS(c.has_status_files([status.P.CONNECTED_TO_OPEN]))
@@ -667,8 +697,11 @@
wvtest.WVPASS(c.internet())
wvtest.WVFAIL(c.client_up(band))
wvtest.WVPASS(c.wifi_for_band(band).current_route())
+ wvtest.WVPASSEQ(c.log_upload_count, 1)
# Disable scan results again.
c.interface_with_scan_results = None
+ c.run_until_interface_update()
+ check_tmp_hosts('192.168.1.100 %s\n127.0.0.1 localhost' % hostname)
# Now, create a WLAN configuration which should be connected to.
ssid = 'wlan'
@@ -706,6 +739,9 @@
wvtest.WVPASS(c.has_status_files([status.P.CONNECTED_TO_OPEN]))
wvtest.WVPASSEQ(c.last_provisioning_attempt.ssid, 's3')
wvtest.WVPASSEQ(c.last_provisioning_attempt.bssid, 'ff:ee:dd:cc:bb:aa')
+ # The log upload happens on the next main loop after joining s3.
+ c.run_once()
+ wvtest.WVPASSEQ(c.log_upload_count, 2)
# Now, recreate the same WLAN configuration, which should be connected to.
# Also, test that atomic writes/renames work.
@@ -725,16 +761,20 @@
wvtest.WVPASS(c.client_up(band))
wvtest.WVPASS(c.wifi_for_band(band).current_route())
wvtest.WVFAIL(c.bridge.current_route())
+ c.run_until_interface_update()
+ check_tmp_hosts('192.168.1.100 %s\n127.0.0.1 localhost' % hostname)
# Now bring up the bridge. We should remove the wifi connection and start
# an AP.
c.set_ethernet(True)
c.bridge.set_connection_check_result('succeed')
+ c.bridge.ip_testonly = '192.168.1.101'
c.run_until_interface_update()
wvtest.WVPASS(c.access_point_up(band))
wvtest.WVFAIL(c.client_up(band))
wvtest.WVFAIL(c.wifi_for_band(band).current_route())
wvtest.WVPASS(c.bridge.current_route())
+ check_tmp_hosts('192.168.1.101 %s\n127.0.0.1 localhost' % hostname)
# Now move (rather than delete) the configuration file. The AP should go
# away, and we should not be able to join the WLAN. Routes should not be
@@ -765,6 +805,7 @@
c.run_until_interface_update()
wvtest.WVFAIL(c.acs())
wvtest.WVFAIL(c.internet())
+ check_tmp_hosts('127.0.0.1 localhost')
# s3 is not what the cycler would suggest trying next.
wvtest.WVPASSNE('s3', c.wifi_for_band(band).cycler.peek())
# Run only once, so that only one BSS can be tried. It should be the s3 one,
@@ -773,6 +814,8 @@
wvtest.WVPASS(c.acs())
# Make sure we didn't scan on `band`.
wvtest.WVPASSEQ(scan_count_for_band, c.wifi_for_band(band).wifi_scan_counter)
+ c.run_once()
+ wvtest.WVPASSEQ(c.log_upload_count, 3)
# Now re-create the WLAN config, connect to the WLAN, and make sure that s3 is
# unset as last_successful_bss_info, since it is no longer available.
@@ -815,6 +858,8 @@
c.run_once()
s2_bss = iw.BssInfo('01:23:45:67:89:ab', 's2')
wvtest.WVPASSEQ(c.wifi_for_band(band).last_successful_bss_info, s2_bss)
+ c.run_once()
+ wvtest.WVPASSEQ(c.log_upload_count, 4)
c.s2_fail = True
c.write_wlan_config(band, ssid, psk)
@@ -830,6 +875,32 @@
c.run_until_interface_update()
wvtest.WVPASSEQ(c.wifi_for_band(band).last_successful_bss_info, None)
+ # Test that we wait dhcp_wait_s seconds for a DHCP lease before trying the
+ # next BSSID. The scan results contain an s3 AP with vendor IEs that fails to
+ # send a DHCP lease. This ensures that s3 will be tried before any other AP,
+ # which lets us force a timeout and proceed to the next AP.
+ del c.wifi_for_band(band).cycler
+ c.interface_with_scan_results = c.wifi_for_band(band).name
+ c.scan_results_include_hidden = True
+ c.can_connect_to_s3 = True
+ c.dhcp_failure_on_s3 = True
+ # First iteration: check that we try s3.
+ c.run_until_scan(band)
+ last_bss_info = c.wifi_for_band(band).last_attempted_bss_info
+ wvtest.WVPASSEQ(last_bss_info.ssid, 's3')
+ wvtest.WVPASSEQ(last_bss_info.bssid, 'ff:ee:dd:cc:bb:aa')
+ # Second iteration: check that we try s3 again since there's no gateway yet.
+ c.run_once()
+ last_bss_info = c.wifi_for_band(band).last_attempted_bss_info
+ wvtest.WVPASSEQ(last_bss_info.ssid, 's3')
+ wvtest.WVPASSEQ(last_bss_info.bssid, 'ff:ee:dd:cc:bb:aa')
+ # Third iteration: sleep for dhcp_wait_s and check that we try another AP.
+ time.sleep(c.test_dhcp_wait_s)
+ c.run_once()
+ last_bss_info = c.wifi_for_band(band).last_attempted_bss_info
+ wvtest.WVPASSNE(last_bss_info.ssid, 's3')
+ wvtest.WVPASSNE(last_bss_info.bssid, 'ff:ee:dd:cc:bb:aa')
+
@wvtest.wvtest
@connection_manager_test(WIFI_SHOW_OUTPUT_MARVELL8897)
@@ -967,6 +1038,8 @@
wvtest.WVFAIL(c.bridge.current_route())
wvtest.WVPASS(c.wifi_for_band('2.4').current_route())
wvtest.WVFAIL(c.wifi_for_band('5').current_route())
+ c.run_once()
+ wvtest.WVPASSEQ(c.log_upload_count, 1)
@wvtest.wvtest
@@ -1059,6 +1132,8 @@
wvtest.WVFAIL(c.bridge.current_route())
wvtest.WVPASS(c.wifi_for_band('2.4').current_route())
wvtest.WVPASS(c.wifi_for_band('5').current_route())
+ c.run_once()
+ wvtest.WVPASSEQ(c.log_upload_count, 1)
@wvtest.wvtest
@@ -1144,5 +1219,29 @@
in c._binwifi_commands)
+@wvtest.wvtest
+@connection_manager_test(WIFI_SHOW_OUTPUT_MARVELL8897)
+def connection_manager_conman_no_2g_wlan(c):
+ unused_raii = experiment_testutils.MakeExperimentDirs()
+
+ # First, establish that we connect on 2.4 without the experiment, to make sure
+ # this test doesn't spuriously pass.
+ c.write_wlan_config('2.4', 'my ssid', 'my psk')
+ c.run_once()
+ wvtest.WVPASS(c.client_up('2.4'))
+
+ # Now, force a disconnect by deleting the config.
+ c.delete_wlan_config('2.4')
+ c.run_once()
+ wvtest.WVFAIL(c.client_up('2.4'))
+
+ # Now enable the experiment, recreate the config, and make sure we don't
+ # connect.
+ experiment_testutils.enable('WifiNo2GClient')
+ c.write_wlan_config('2.4', 'my ssid', 'my psk')
+ c.run_once()
+ wvtest.WVFAIL(c.client_up('2.4'))
+
+
if __name__ == '__main__':
wvtest.wvtest_main()
diff --git a/conman/interface.py b/conman/interface.py
index 7e37306..e172a82 100755
--- a/conman/interface.py
+++ b/conman/interface.py
@@ -28,6 +28,7 @@
CONNECTION_CHECK = 'connection_check'
IP_ROUTE = ['ip', 'route']
+ IP_ADDR_SHOW = ['ip', 'addr', 'show', 'dev']
def __init__(self, name, metric):
self.name = name
@@ -57,18 +58,18 @@
"""
# Until initialized, we want to act as if the interface is down.
if not self._initialized:
- logging.debug('%s not initialized; not running connection_check%s',
- self.name, ' (ACS)' if check_acs else '')
+ logging.info('%s not initialized; not running connection_check%s',
+ self.name, ' (ACS)' if check_acs else '')
return None
if not self.links:
- logging.debug('Connection check for %s failed due to no links', self.name)
+ logging.info('Connection check for %s failed due to no links', self.name)
return False
logging.debug('Gateway IP for %s is %s', self.name, self._gateway_ip)
if self._gateway_ip is None:
- logging.debug('Connection check for %s failed due to no gateway IP',
- self.name)
+ logging.info('Connection check%s for %s failed due to no gateway IP',
+ ' (ACS)' if check_acs else '', self.name)
return False
# Temporarily add a route to make sure the connection check can be run.
@@ -90,10 +91,10 @@
with open(os.devnull, 'w') as devnull:
result = subprocess.call(cmd, stdout=devnull, stderr=devnull) == 0
- logging.debug('Connection check%s for %s %s',
- ' (ACS)' if check_acs else '',
- self.name,
- 'passed' if result else 'failed')
+ logging.info('Connection check%s for %s %s',
+ ' (ACS)' if check_acs else '',
+ self.name,
+ 'passed' if result else 'failed')
# Delete the temporary route.
if added_temporary_route:
@@ -105,6 +106,9 @@
return result
+ def gateway(self):
+ return self._gateway_ip
+
def acs(self):
if self._has_acs is None:
self._has_acs = self._connection_check(check_acs=True)
@@ -175,8 +179,8 @@
def _ip_route(self, *args):
if not self._initialized:
- logging.debug('Not initialized, not running %s %s',
- ' '.join(self.IP_ROUTE), ' '.join(args))
+ logging.info('Not initialized, not running %s %s',
+ ' '.join(self.IP_ROUTE), ' '.join(args))
return ''
return self._really_ip_route(*args)
@@ -190,8 +194,20 @@
e.message)
return ''
+ def _ip_addr_show(self):
+ try:
+ return subprocess.check_output(self.IP_ADDR_SHOW + [self.name])
+ except subprocess.CalledProcessError as e:
+ logging.error('Could not get IP address for %s: %s', self.name, e.message)
+ return None
+
+ def get_ip_address(self):
+ match = re.search(r'^\s*inet (?P<IP>\d+\.\d+\.\d+\.\d+)',
+ self._ip_addr_show(), re.MULTILINE)
+ return match and match.group('IP') or None
+
def set_gateway_ip(self, gateway_ip):
- logging.debug('New gateway IP %s for %s', gateway_ip, self.name)
+ logging.info('New gateway IP %s for %s', gateway_ip, self.name)
self._gateway_ip = gateway_ip
self.update_routes()
@@ -203,10 +219,10 @@
had_links = bool(self.links)
if is_up:
- logging.debug('%s gained link %s', self.name, link)
+ logging.info('%s gained link %s', self.name, link)
self.links.add(link)
else:
- logging.debug('%s lost link %s', self.name, link)
+ logging.info('%s lost link %s', self.name, link)
self.links.remove(link)
# If a link goes away, we may have lost access to something but not gained
@@ -319,9 +335,9 @@
failure_s = self._acs_session_failure_s()
if (experiment.enabled('WifiSimulateWireless')
and failure_s < MAX_ACS_FAILURE_S):
- logging.debug('WifiSimulateWireless: failing bridge connection check (no '
- 'ACS contact for %d seconds, max %d seconds)',
- failure_s, MAX_ACS_FAILURE_S)
+ logging.info('WifiSimulateWireless: failing bridge connection check%s '
+ '(no ACS contact for %d seconds, max %d seconds)',
+ ' (ACS)' if check_acs else '', failure_s, MAX_ACS_FAILURE_S)
return False
return super(Bridge, self)._connection_check(check_acs)
@@ -379,14 +395,17 @@
return True
socket = os.path.join(path, self.name)
+ logging.debug('%s socket is %s', self.name, socket)
try:
self._wpa_control = self.get_wpa_control(socket)
self._wpa_control.attach()
+ logging.debug('%s successfully attached', self.name)
except wpactrl.error as e:
logging.error('Error attaching to wpa_supplicant: %s', e)
return False
status = self.wpa_status()
+ logging.debug('%s status after attaching is %s', self.name, status)
self.wpa_supplicant = status.get('wpa_state') == 'COMPLETED'
if not self._initialized:
self.initial_ssid = status.get('ssid')
@@ -403,17 +422,21 @@
status = {}
if self._wpa_control and self._wpa_control.attached:
+ logging.debug('%s ctrl_iface_path %s',
+ self, self._wpa_control.ctrl_iface_path)
lines = []
try:
lines = self._wpa_control.request('STATUS').splitlines()
- except wpactrl.error:
- logging.error('wpa_control STATUS request failed')
+ except wpactrl.error as e:
+ logging.error('wpa_control STATUS request failed %s args %s',
+ e.message, e.args)
for line in lines:
if '=' not in line:
continue
k, v = line.strip().split('=', 1)
status[k] = v
+ logging.debug('%s wpa status is %s', self.name, status)
return status
def get_wpa_control(self, socket):
@@ -468,17 +491,22 @@
WIFIINFO_PATH = '/tmp/wifi/wifiinfo'
def __init__(self, socket):
- self._interface = os.path.split(socket)[-1]
+ self.ctrl_iface_path, self._interface = os.path.split(socket)
# State from QCSAPI and wifi_files.
self._client_mode = False
self._ssid = None
self._status = None
+ self._security = None
self._events = []
def _qcsapi(self, *command):
- return subprocess.check_output(['qcsapi'] + list(command)).strip()
+ try:
+ return subprocess.check_output(['qcsapi'] + list(command)).strip()
+ except subprocess.CalledProcessError as e:
+ logging.error('QCSAPI call failed: %s: %s', e, e.output)
+ raise
def attach(self):
self._update()
@@ -500,6 +528,7 @@
client_mode = self._qcsapi('get_mode', 'wifi0') == 'Station'
ssid = self._qcsapi('get_ssid', 'wifi0')
status = self._qcsapi('get_status', 'wifi0')
+ security = self._qcsapi('ssid_get_authentication_mode', 'wifi0', ssid)
except subprocess.CalledProcessError:
# If QCSAPI failed, skip update.
return
@@ -529,6 +558,7 @@
self._client_mode = client_mode
self._ssid = ssid
self._status = status
+ self._security = security
def recv(self):
return self._events.pop(0)
@@ -544,7 +574,8 @@
if not self._client_mode or not self._ssid:
return ''
- return 'wpa_state=COMPLETED\nssid=%s' % self._ssid
+ return ('wpa_state=COMPLETED\nssid=%s\nkey_mgmt=%s' %
+ (self._ssid, self._security or 'NONE'))
class FrenzyWifi(Wifi):
diff --git a/conman/interface_test.py b/conman/interface_test.py
index 4c7d52b..13dcf14 100755
--- a/conman/interface_test.py
+++ b/conman/interface_test.py
@@ -22,6 +22,13 @@
from wvtest import wvtest
+# pylint: disable=line-too-long
+_IP_ADDR_SHOW_TPL = """4: {name}: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP qlen 1000
+ inet {ip}/21 brd 100.100.55.255 scope global {name}
+ valid_lft forever preferred_lft forever
+"""
+
+
class FakeInterfaceMixin(object):
"""Replace Interface methods which interact with the system."""
@@ -29,6 +36,7 @@
super(FakeInterfaceMixin, self).__init__(*args, **kwargs)
self.set_connection_check_result('succeed')
self.routing_table = {}
+ self.ip_testonly = None
def set_connection_check_result(self, result):
if result in ['succeed', 'fail', 'restricted']:
@@ -63,6 +71,12 @@
del self.routing_table[k]
break
+ def _ip_addr_show(self):
+ if self.ip_testonly:
+ return _IP_ADDR_SHOW_TPL.format(name=self.name, ip=self.ip_testonly)
+
+ return ''
+
class Bridge(FakeInterfaceMixin, interface.Bridge):
pass
@@ -78,6 +92,7 @@
self.attached = False
self.connected = False
self.ssid_testonly = None
+ self.secure_testonly = False
self.request_status_fails = False
def pending(self):
@@ -96,6 +111,7 @@
def detach(self):
self.attached = False
self.ssid_testonly = None
+ self.secure_testonly = False
self.connected = False
self.check_socket_exists('wpactrl_detach failed')
@@ -103,11 +119,19 @@
if request_type == 'STATUS':
if self.request_status_fails:
raise wpactrl.error('test error')
- return ('foo\nwpa_state=COMPLETED\nssid=%s\nbar' % self.ssid_testonly
- if self.connected else 'foo')
+ if self.connected:
+ return ('foo\nwpa_state=COMPLETED\nssid=%s\nkey_mgmt=%s\nbar' %
+ (self.ssid_testonly,
+ 'WPA2-PSK' if self.secure_testonly else 'NONE'))
+ else:
+ return 'wpa_state=SCANNING\naddress=12:34:56:78:90:ab'
else:
raise ValueError('Invalid request_type %s' % request_type)
+ @property
+ def ctrl_iface_path(self):
+ return os.path.split(self._socket)[0]
+
# Below methods are not part of WPACtrl.
def add_event(self, event):
@@ -142,6 +166,7 @@
def __init__(self, *args, **kwargs):
super(Wifi, self).__init__(*args, **kwargs)
self._initial_ssid_testonly = None
+ self._secure_testonly = False
def attach_wpa_control(self, path):
if self._initial_ssid_testonly and self._wpa_control:
@@ -153,6 +178,7 @@
if self._initial_ssid_testonly:
result.connected = True
result.ssid_testonly = self._initial_ssid_testonly
+ result.secure_testonly = self._secure_testonly
return result
def add_connected_event(self):
@@ -161,16 +187,19 @@
def add_disconnected_event(self):
self._initial_ssid_testonly = None
+ self._secure_testonly = False
if self.attached():
self._wpa_control.add_disconnected_event()
def add_terminating_event(self):
self._initial_ssid_testonly = None
+ self._secure_testonly = False
if self.attached():
self._wpa_control.add_terminating_event()
def detach_wpa_control(self):
self._initial_ssid_testonly = None
+ self._secure_testonly = False
super(Wifi, self).detach_wpa_control()
def start_wpa_supplicant_testonly(self, path):
@@ -191,6 +220,7 @@
def __init__(self, *args, **kwargs):
super(FrenzyWPACtrl, self).__init__(*args, **kwargs)
self.ssid_testonly = None
+ self.secure_testonly = False
self.request_status_fails = False
def _qcsapi(self, *command):
@@ -199,15 +229,21 @@
def add_connected_event(self):
self.fake_qcsapi['get_mode'] = 'Station'
self.fake_qcsapi['get_ssid'] = self.ssid_testonly
+ security = 'PSKAuthentication' if self.secure_testonly else 'NONE'
+ self.fake_qcsapi['ssid_get_authentication_mode'] = security
def add_disconnected_event(self):
self.ssid_testonly = None
+ self.secure_testonly = False
self.fake_qcsapi['get_ssid'] = None
+ self.fake_qcsapi['ssid_get_authentication_mode'] = 'NONE'
def add_terminating_event(self):
self.ssid_testonly = None
+ self.secure_testonly = False
self.fake_qcsapi['get_ssid'] = None
self.fake_qcsapi['get_mode'] = 'AP'
+ self.fake_qcsapi['ssid_get_authentication_mode'] = 'NONE'
def detach(self):
self.add_terminating_event()
@@ -226,12 +262,14 @@
def __init__(self, *args, **kwargs):
super(FrenzyWifi, self).__init__(*args, **kwargs)
self._initial_ssid_testonly = None
+ self._secure_testonly = False
self.fake_qcsapi = {}
def attach_wpa_control(self, *args, **kwargs):
super(FrenzyWifi, self).attach_wpa_control(*args, **kwargs)
if self._wpa_control:
self._wpa_control.ssid_testonly = self._initial_ssid_testonly
+ self._wpa_control.secure_testonly = self._secure_testonly
if self._initial_ssid_testonly:
self._wpa_control.add_connected_event()
@@ -241,6 +279,7 @@
if self._initial_ssid_testonly:
result.fake_qcsapi['get_mode'] = 'Station'
result.ssid_testonly = self._initial_ssid_testonly
+ result.secure_testonly = self._secure_testonly
result.add_connected_event()
return result
@@ -250,16 +289,19 @@
def add_disconnected_event(self):
self._initial_ssid_testonly = None
+ self._secure_testonly = False
if self.attached():
self._wpa_control.add_disconnected_event()
def add_terminating_event(self):
self._initial_ssid_testonly = None
+ self._secure_testonly = False
if self.attached():
self._wpa_control.add_terminating_event()
def detach_wpa_control(self):
self._initial_ssid_testonly = None
+ self._secure_testonly = False
super(FrenzyWifi, self).detach_wpa_control()
def start_wpa_supplicant_testonly(self, unused_path):
@@ -337,6 +379,10 @@
wvtest.WVPASS(b.current_route())
wvtest.WVPASS(os.path.exists(autoprov_filepath))
+ wvtest.WVFAIL(b.get_ip_address())
+ b.ip_testonly = '192.168.1.100'
+ wvtest.WVPASSEQ(b.get_ip_address(), '192.168.1.100')
+
finally:
shutil.rmtree(tmp_dir)
diff --git a/conman/iw.py b/conman/iw.py
index fd56e32..f2e15d8 100755
--- a/conman/iw.py
+++ b/conman/iw.py
@@ -6,19 +6,10 @@
import subprocess
-FIBER_OUI = 'f4:f5:e8'
-DEFAULT_GFIBERSETUP_SSID = 'GFiberSetupAutomation'
-
-
-def _scan(band, **kwargs):
- try:
- return subprocess.check_output(('wifi', 'scan', '-b', band), **kwargs)
- except subprocess.CalledProcessError:
- return ''
-
-
-GFIBER_OUIS = ['f4:f5:e8']
+GFIBER_VENDOR_IE_OUI = 'f4:f5:e8'
+GFIBER_OUIS = ['00:1a:11', 'f4:f5:e8', 'f8:8f:ca']
VENDOR_IE_FEATURE_ID_AUTOPROVISIONING = '01'
+DEFAULT_GFIBERSETUP_SSID = 'GFiberSetupAutomation'
_BSSID_RE = r'BSS (?P<BSSID>([0-9a-f]{2}:?){6})\(on .*\)'
@@ -28,6 +19,13 @@
'data:(?P<data>( [0-9a-f]{2})+)')
+def _scan(band, **kwargs):
+ try:
+ return subprocess.check_output(('wifi', 'scan', '-b', band), **kwargs)
+ except subprocess.CalledProcessError:
+ return ''
+
+
class BssInfo(object):
"""Contains info about a BSS, parsed from 'iw scan'."""
@@ -119,7 +117,7 @@
continue
for oui, data in bss_info.vendor_ies:
- if oui == FIBER_OUI:
+ if oui == GFIBER_VENDOR_IE_OUI:
octets = data.split()
if octets[0] == '03' and not bss_info.ssid:
bss_info.ssid = ''.join(octets[1:]).decode('hex')
@@ -138,7 +136,7 @@
def _bssid_priority(bss_info):
result = 4 if bss_info.bssid[:8] in GFIBER_OUIS else 2
for oui, data in bss_info.vendor_ies:
- if (oui in GFIBER_OUIS and
+ if (oui == GFIBER_VENDOR_IE_OUI and
data.startswith(VENDOR_IE_FEATURE_ID_AUTOPROVISIONING)):
result = 5
diff --git a/conman/iw_test.py b/conman/iw_test.py
index 3bb3cf7..55b2e7b 100755
--- a/conman/iw_test.py
+++ b/conman/iw_test.py
@@ -553,7 +553,7 @@
Vendor specific: OUI 00:11:22, data: 01 23 45 67
Vendor specific: OUI f4:f5:e8, data: 01
Vendor specific: OUI f4:f5:e8, data: 03 47 46 69 62 65 72 53 65 74 75 70 41 75 74 6f 6d 61 74 69 6f 6e
-BSS f4:f5:e8:f1:36:43(on wcli0)
+BSS 00:1a:11:f1:36:43(on wcli0)
TSF: 12499150000 usec (0d, 03:28:19)
freq: 2437
beacon interval: 100 TUs
@@ -646,7 +646,7 @@
vendor_ies=[test_ie, provisioning_ie,
ssid_ie])
provisioning_bss_info_frenzy = iw.BssInfo(ssid=iw.DEFAULT_GFIBERSETUP_SSID,
- bssid='f4:f5:e8:f1:36:43',
+ bssid='00:1a:11:f1:36:43',
rssi=-66)
wvtest.WVPASSEQ(
diff --git a/craftui/craftui b/craftui/craftui
index 9d2a17a..2250595 100755
--- a/craftui/craftui
+++ b/craftui/craftui
@@ -3,6 +3,8 @@
pycode=/bin/craftui.py
cw=/usr/catawampus
devcw=../../../../vendor/google/catawampus
+tornado=
+devtornado=../../../../vendor/opensource/tornado
localwww=./www
# in developer environment if vendor/google/catawapus is above us
@@ -18,6 +20,7 @@
# if running from developer desktop, use simulated data
if [ -n "$sim" ]; then
cw="$devcw"
+ tornado="$devtornado"
args="$args --http-port=$((8888+2*($sim-1)))"
args="$args --https-port=$((8889+2*($sim-1)))"
args="$args --sim=./sim$sim"
@@ -43,5 +46,5 @@
exit 1
done
-export PYTHONPATH="$cw/tr/vendor/tornado:$cw/tr/vendor/curtain:$PYTHONPATH"
+export PYTHONPATH="$tornado:$cw/tr/vendor/curtain:$PYTHONPATH"
exec python -u $debug $pycode $args $httpsmode
diff --git a/gpio-mailbox/Makefile b/gpio-mailbox/Makefile
index 1dc26a4..eaaee20 100644
--- a/gpio-mailbox/Makefile
+++ b/gpio-mailbox/Makefile
@@ -29,8 +29,6 @@
CFLAGS += -DGFIBER_LT
else ifeq ($(BR2_TARGET_GENERIC_PLATFORM_NAME),gflt200)
CFLAGS += -DGFIBER_LT
-else ifeq ($(BR2_TARGET_GENERIC_PLATFORM_NAME),gflt300)
- CFLAGS += -DGFIBER_LT
else ifeq ($(BR2_TARGET_GENERIC_PLATFORM_NAME),gfmn100)
CFLAGS += -DWINDCHARGER
else ifeq ($(BR2_TARGET_GENERIC_PLATFORM_NAME),gfch100)
diff --git a/gpio-mailbox/TEST.gpio-mailbox b/gpio-mailbox/TEST.gpio-mailbox
index 3cd5dee..edc8f60 100644
--- a/gpio-mailbox/TEST.gpio-mailbox
+++ b/gpio-mailbox/TEST.gpio-mailbox
@@ -1,4 +1,4 @@
-rm -rf /tmp/gpio /tmp/led
+rm -rf /tmp/gpio /tmp/leds
mkdir -p /tmp/gpio
echo x5 0 1 0 2 0 0x0f > /tmp/gpio/leds
diff --git a/gpio-mailbox/broadcom.c b/gpio-mailbox/broadcom.c
index 575f197..a53e51e 100644
--- a/gpio-mailbox/broadcom.c
+++ b/gpio-mailbox/broadcom.c
@@ -614,6 +614,15 @@
}
}
+static void *mmap_(void* addr, size_t size, int prot, int flags, int fd,
+ off_t offset) {
+#ifdef __ANDROID__
+ return mmap64(addr, size, prot, flags, fd, (off64_t)(uint64_t)(uint32_t)offset);
+#else
+ return mmap(addr, size, prot, flags, fd, offset);
+#endif
+}
+
static int platform_init(struct platform_info* p) {
platform_cleanup();
@@ -623,8 +632,8 @@
return -1;
}
mmap_size = p->mmap_size;
- mmap_addr = mmap(NULL, mmap_size, PROT_READ | PROT_WRITE, MAP_SHARED,
- mmap_fd, p->mmap_base);
+ mmap_addr = mmap_(NULL, mmap_size, PROT_READ | PROT_WRITE, MAP_SHARED,
+ mmap_fd, p->mmap_base);
if (mmap_addr == MAP_FAILED) {
perror("mmap");
platform_cleanup();
diff --git a/gpio-mailbox/gfch100.c b/gpio-mailbox/gfch100.c
index 73d97bd..2cf1f23 100644
--- a/gpio-mailbox/gfch100.c
+++ b/gpio-mailbox/gfch100.c
@@ -18,15 +18,13 @@
#define GPIO_OUT "out"
/* GPIO_ACTIVITY LED is blue on Chimera. */
-#define GPIO_ACTIVITY "30"
-#define GPIO_RED "31"
+#define GPIO_ACTIVITY "/led_activity"
+#define GPIO_RED "/led_red"
-#define GPIO_BASE_DIR "/sys/class/gpio"
-#define GPIO_EXPORT GPIO_BASE_DIR "/export"
+#define GPIO_BASE_DIR "/dev/gpio"
-#define GPIO_DIR(n) GPIO_BASE_DIR "/gpio" n
+#define GPIO_DIR(n) GPIO_BASE_DIR n
-#define GPIO_DIRECTION(dir) dir "/direction"
#define GPIO_VALUE(dir) dir "/value"
struct PinHandle_s {
@@ -38,9 +36,7 @@
};
struct sysgpio {
- const char* export_value;
const char* value_path;
- const char* direction_path;
};
struct platform_info {
@@ -57,13 +53,9 @@
.value_path = "/sys/class/hwmon/hwmon0/temp1_input",
},
.led_red = {
- .export_value = GPIO_RED,
- .direction_path = GPIO_DIRECTION(GPIO_DIR(GPIO_RED)),
.value_path = GPIO_VALUE(GPIO_DIR(GPIO_RED)),
},
.led_activity = {
- .export_value = GPIO_ACTIVITY,
- .direction_path = GPIO_DIRECTION(GPIO_DIR(GPIO_ACTIVITY)),
.value_path = GPIO_VALUE(GPIO_DIR(GPIO_ACTIVITY)),
},
}
@@ -89,16 +81,6 @@
perror("calloc(PinHandle)");
return NULL;
}
-
- // initialize leds to match boot values
- write_file_string(GPIO_EXPORT, GPIO_RED);
- write_file_string(platform->led_red.direction_path, GPIO_OUT);
- write_file_string(platform->led_red.value_path, GPIO_OFF);
-
- write_file_string(GPIO_EXPORT, GPIO_ACTIVITY);
- write_file_string(platform->led_activity.direction_path, GPIO_OUT);
- write_file_string(platform->led_activity.value_path, GPIO_ON);
-
return handle;
}
diff --git a/taxonomy/dhcp.py b/taxonomy/dhcp.py
index 0354ebd..26ecb82 100644
--- a/taxonomy/dhcp.py
+++ b/taxonomy/dhcp.py
@@ -59,6 +59,7 @@
'1,3,6,15,119,95,252,44,46,47': ['ipodtouch1'],
'252,3,42,15,6,1,12': ['lgtv', 'tizen'],
+ '252,3,42,6,1,12': ['tizen'],
'1,3,6,15,119,95,252,44,46,101': ['macos'],
'1,3,6,15,119,95,252,44,46': ['macos'],
@@ -74,8 +75,9 @@
'1,28,2,3,15,6,12': ['tivo'],
- '1,3,6,12,15,28,42': ['viziotv', 'wemo', 'directv'],
+ '1,3,6,12,15,28,42': ['viziotv', 'wemo', 'directv', 'samsungtv'],
'1,3,6,12,15,28,40,41,42': ['viziotv', 'kindle'],
+ '1,3,6,12,15,17,23,28,29,31,33,40,41,42': ['viziotv'],
'1,3,6,15,28,33': ['wii'],
'1,3,6,15': ['wii', 'xbox'],
diff --git a/taxonomy/ethernet.py b/taxonomy/ethernet.py
index 00be466..85a4522 100644
--- a/taxonomy/ethernet.py
+++ b/taxonomy/ethernet.py
@@ -51,6 +51,8 @@
'd8:50:e6': ['asus'],
'f8:32:e4': ['asus'],
+ '58:67:1a': ['barnes&noble'],
+
'30:8c:fb': ['dropcam'],
'00:1a:11': ['google'],
@@ -245,6 +247,7 @@
'b8:57:d8': ['samsung'],
'b8:5a:73': ['samsung'],
'b8:5e:7b': ['samsung'],
+ 'bc:14:85': ['samsung'],
'bc:20:a4': ['samsung'],
'bc:72:b1': ['samsung'],
'bc:8c:cd': ['samsung'],
@@ -279,8 +282,12 @@
'58:48:22': ['sony'],
'b4:52:7e': ['sony'],
+ '10:08:c1': ['toshiba'],
+
'a4:8d:3b': ['vizio'],
+ 'b4:79:a7': ['wink'],
+
'00:24:e4': ['withings'],
'64:cc:2e': ['xiaomi'],
diff --git a/taxonomy/pcaptest.py b/taxonomy/pcaptest.py
index 2b864e1..2b5d90f 100644
--- a/taxonomy/pcaptest.py
+++ b/taxonomy/pcaptest.py
@@ -45,13 +45,22 @@
('', './testdata/pcaps/Samsung Exhibit 2.4GHz.pcap'),
('', './testdata/pcaps/Samsung Fascinate 2.4GHz.pcap'),
('', './testdata/pcaps/Samsung Galaxy Tab 2 2.4GHz.pcap'),
+ ('', './testdata/pcaps/Samsung Galaxy 4G 2.4GHz SGH-T959V.pcap'),
('', './testdata/pcaps/Samsung Infuse 5GHz.pcap'),
('', './testdata/pcaps/Samsung Vibrant 2.4GHz.pcap'),
+ ('', './testdata/pcaps/Sony Ericsson Xperia X10 2.4GHz.pcap'),
# Names where the identified species doesn't exactly match the filename,
# usually because multiple devices are too similar to distinguish. We name
# the file for the specific device which was captured, and add an entry
# here for the best identification which we can manage.
+ ('Amazon Kindle', './testdata/pcaps/Amazon Kindle 4th gen 2.4GHz 9023.pcap'),
+ ('Amazon Kindle', './testdata/pcaps/Amazon Kindle 4th gen 2.4GHz B00E.pcap'),
+ ('Amazon Kindle', './testdata/pcaps/Amazon Kindle Paperwhite 2012 2.4GHz B024.pcap'),
+ ('Amazon Kindle', './testdata/pcaps/Amazon Kindle Touch 2.4GHz Broadcast Probe B011.pcap'),
+ ('Amazon Kindle', './testdata/pcaps/Amazon Kindle Touch 2.4GHz Specific Probe B011.pcap'),
+ ('Amazon Kindle', './testdata/pcaps/Amazon Kindle Voyage 2.4GHz B013.pcap'),
+ ('Amazon Kindle', './testdata/pcaps/Amazon Kindle Voyage 2.4GHz B054.pcap'),
('iPad 1st or 2nd gen', './testdata/pcaps/iPad 1st gen 5GHz.pcap'),
('iPad 1st or 2nd gen', './testdata/pcaps/iPad 2nd gen 5GHz.pcap'),
('iPad 4th gen or Air 1st gen', './testdata/pcaps/iPad (4th gen) 5GHz.pcap'),
diff --git a/taxonomy/testdata/dhcp.leases b/taxonomy/testdata/dhcp.leases
index 9bdc396..ab83a43 100644
--- a/taxonomy/testdata/dhcp.leases
+++ b/taxonomy/testdata/dhcp.leases
@@ -56,3 +56,16 @@
1432237016 a4:8d:3b:00:00:00 192.168.42.45 VizioSmartTV
1432237016 00:11:d9:00:00:00 192.168.42.46 TiVoBOLT
1432237016 ac:3a:7a:00:00:00 192.168.42.47 Roku3-4230
+1432237016 d4:63:fe:00:00:00 192.168.42.48 LGSmartTV
+1432237016 bc:14:85:00:00:00 192.168.42.49 SamsungTizenTV
+1432237016 78:bd:bc:00:00:00 192.168.42.50 SamsungTizenTV
+1432237016 54:88:0e:00:00:00 192.168.42.51 SamsungLED75TV
+1432237016 bc:30:7d:00:00:00 192.168.42.52 PanasonicTV
+1432237016 60:12:8b:00:00:00 192.168.42.53 CanonPixma
+1432237016 88:87:17:00:00:00 192.168.42.54 CanonPixma
+1432237016 cc:95:d7:00:00:00 192.168.42.55 VizioTV
+1432237016 c0:f2:fb:00:00:00 192.168.42.56 iPaadMini3
+1432237016 04:52:f3:00:00:00 192.168.42.57 iPaadMini4
+1432237016 a4:d1:d2:00:00:00 192.168.42.58 iPaadOldiOS
+1432237016 70:48:0f:00:00:00 192.168.42.59 iPadPro12_9
+1432237016 6c:c2:17:00:00:00 192.168.42.60 HPPrinter
diff --git a/taxonomy/testdata/dhcp.signatures b/taxonomy/testdata/dhcp.signatures
index e9ef842..1fcfa61 100644
--- a/taxonomy/testdata/dhcp.signatures
+++ b/taxonomy/testdata/dhcp.signatures
@@ -48,3 +48,16 @@
a4:8d:3b:00:00:00 1,3,6,12,15,28,42
00:11:d9:00:00:00 1,28,2,3,15,6,12
ac:3a:7a:00:00:00 1,3,6,15,12
+d4:63:fe:00:00:00 252,3,42,15,6,1,12
+bc:14:85:00:00:00 252,3,42,6,1,12
+78:bd:bc:00:00:00 252,3,42,6,1,12
+54:88:0e:00:00:00 1,3,6,12,15,28,42
+bc:30:7d:00:00:00 58,59,6,15,51,54,1,3
+60:12:8b:00:00:00 1,3,6,15,44,47
+88:87:17:00:00:00 1,3,6,15,44,47
+cc:95:d7:00:00:00 1,3,6,12,15,17,23,28,29,31,33,40,41,42
+c0:f2:fb:00:00:00 1,3,6,15,119,252
+04:52:f3:00:00:00 1,3,6,15,119,252
+a4:d1:d2:00:00:00 1,3,6,15,119,252
+70:48:0f:00:00:00 1,3,6,15,119,252
+6c:c2:17:00:00:00 6,3,1,15,66,67,13,44,12,81,252
diff --git a/taxonomy/testdata/pcaps/Amazon Kindle 4th gen 2.4GHz 9023.pcap b/taxonomy/testdata/pcaps/Amazon Kindle 4th gen 2.4GHz 9023.pcap
new file mode 100644
index 0000000..7e26437
--- /dev/null
+++ b/taxonomy/testdata/pcaps/Amazon Kindle 4th gen 2.4GHz 9023.pcap
Binary files differ
diff --git a/taxonomy/testdata/pcaps/Amazon Kindle Voyage, Paperwhite, or 4th gen 2.4GHz 4th gen B00E.pcap b/taxonomy/testdata/pcaps/Amazon Kindle 4th gen 2.4GHz B00E.pcap
similarity index 100%
rename from taxonomy/testdata/pcaps/Amazon Kindle Voyage, Paperwhite, or 4th gen 2.4GHz 4th gen B00E.pcap
rename to taxonomy/testdata/pcaps/Amazon Kindle 4th gen 2.4GHz B00E.pcap
Binary files differ
diff --git "a/taxonomy/testdata/pcaps/Amazon Kindle Fire 7\" \0502015 edition\051 2.4GHz 5V98LN GFRG2x0.pcap" "b/taxonomy/testdata/pcaps/Amazon Kindle Fire 7\" \0502015 edition\051 2.4GHz 5V98LN GFRG2x0.pcap"
new file mode 100644
index 0000000..d768c82
--- /dev/null
+++ "b/taxonomy/testdata/pcaps/Amazon Kindle Fire 7\" \0502015 edition\051 2.4GHz 5V98LN GFRG2x0.pcap"
Binary files differ
diff --git "a/taxonomy/testdata/pcaps/Amazon Kindle Fire 7\" \0502015 edition\051 2.4GHz Broadcast Probe 5V98LN.pcap" "b/taxonomy/testdata/pcaps/Amazon Kindle Fire 7\" \0502015 edition\051 2.4GHz Broadcast Probe 5V98LN.pcap"
new file mode 100644
index 0000000..c595fbb
--- /dev/null
+++ "b/taxonomy/testdata/pcaps/Amazon Kindle Fire 7\" \0502015 edition\051 2.4GHz Broadcast Probe 5V98LN.pcap"
Binary files differ
diff --git a/taxonomy/testdata/pcaps/Amazon Kindle Voyage, Paperwhite, or 4th gen 2.4GHz Paperwhite B024.pcap b/taxonomy/testdata/pcaps/Amazon Kindle Paperwhite 2012 2.4GHz B024.pcap
similarity index 100%
rename from taxonomy/testdata/pcaps/Amazon Kindle Voyage, Paperwhite, or 4th gen 2.4GHz Paperwhite B024.pcap
rename to taxonomy/testdata/pcaps/Amazon Kindle Paperwhite 2012 2.4GHz B024.pcap
Binary files differ
diff --git a/taxonomy/testdata/pcaps/Amazon Kindle Touch 2.4GHz Broadcast Probe B011.pcap b/taxonomy/testdata/pcaps/Amazon Kindle Touch 2.4GHz Broadcast Probe B011.pcap
new file mode 100644
index 0000000..677d9e8
--- /dev/null
+++ b/taxonomy/testdata/pcaps/Amazon Kindle Touch 2.4GHz Broadcast Probe B011.pcap
Binary files differ
diff --git a/taxonomy/testdata/pcaps/Amazon Kindle Touch 2.4GHz Specific Probe B011.pcap b/taxonomy/testdata/pcaps/Amazon Kindle Touch 2.4GHz Specific Probe B011.pcap
new file mode 100644
index 0000000..75cb5d0
--- /dev/null
+++ b/taxonomy/testdata/pcaps/Amazon Kindle Touch 2.4GHz Specific Probe B011.pcap
Binary files differ
diff --git a/taxonomy/testdata/pcaps/Amazon Kindle Voyage, Paperwhite, or 4th gen 2.4GHz Voyage B013.pcap b/taxonomy/testdata/pcaps/Amazon Kindle Voyage 2.4GHz B013.pcap
similarity index 100%
rename from taxonomy/testdata/pcaps/Amazon Kindle Voyage, Paperwhite, or 4th gen 2.4GHz Voyage B013.pcap
rename to taxonomy/testdata/pcaps/Amazon Kindle Voyage 2.4GHz B013.pcap
Binary files differ
diff --git a/taxonomy/testdata/pcaps/Amazon Kindle Voyage, Paperwhite, or 4th gen 2.4GHz Voyage B054.pcap b/taxonomy/testdata/pcaps/Amazon Kindle Voyage 2.4GHz B054.pcap
similarity index 100%
rename from taxonomy/testdata/pcaps/Amazon Kindle Voyage, Paperwhite, or 4th gen 2.4GHz Voyage B054.pcap
rename to taxonomy/testdata/pcaps/Amazon Kindle Voyage 2.4GHz B054.pcap
Binary files differ
diff --git a/taxonomy/testdata/pcaps/Barnes & Noble Nook Color 2.4GHz BNRV200.pcap b/taxonomy/testdata/pcaps/Barnes & Noble Nook Color 2.4GHz BNRV200.pcap
new file mode 100644
index 0000000..eee871c
--- /dev/null
+++ b/taxonomy/testdata/pcaps/Barnes & Noble Nook Color 2.4GHz BNRV200.pcap
Binary files differ
diff --git a/taxonomy/testdata/pcaps/Canon Printer 2.4GHz Large Broadcast Probe MX492.pcap b/taxonomy/testdata/pcaps/Canon Printer 2.4GHz Large Broadcast Probe MX492.pcap
new file mode 100644
index 0000000..b9fcd4b
--- /dev/null
+++ b/taxonomy/testdata/pcaps/Canon Printer 2.4GHz Large Broadcast Probe MX492.pcap
Binary files differ
diff --git a/taxonomy/testdata/pcaps/Canon Printer 2.4GHz MX410.pcap b/taxonomy/testdata/pcaps/Canon Printer 2.4GHz MX410.pcap
new file mode 100644
index 0000000..514eac4
--- /dev/null
+++ b/taxonomy/testdata/pcaps/Canon Printer 2.4GHz MX410.pcap
Binary files differ
diff --git a/taxonomy/testdata/pcaps/Canon Printer 2.4GHz Specific Probe MX492.pcap b/taxonomy/testdata/pcaps/Canon Printer 2.4GHz Specific Probe MX492.pcap
new file mode 100644
index 0000000..a361567
--- /dev/null
+++ b/taxonomy/testdata/pcaps/Canon Printer 2.4GHz Specific Probe MX492.pcap
Binary files differ
diff --git a/taxonomy/testdata/pcaps/Canon Printer 2.4GHz Very Large Broadcast Probe MX492.pcap b/taxonomy/testdata/pcaps/Canon Printer 2.4GHz Very Large Broadcast Probe MX492.pcap
new file mode 100644
index 0000000..12b33a1
--- /dev/null
+++ b/taxonomy/testdata/pcaps/Canon Printer 2.4GHz Very Large Broadcast Probe MX492.pcap
Binary files differ
diff --git a/taxonomy/testdata/pcaps/HP Printer 2.4GHz OfficeJet Pro 8610.pcap b/taxonomy/testdata/pcaps/HP Printer 2.4GHz OfficeJet Pro 8610.pcap
new file mode 100644
index 0000000..c62092f
--- /dev/null
+++ b/taxonomy/testdata/pcaps/HP Printer 2.4GHz OfficeJet Pro 8610.pcap
Binary files differ
diff --git a/taxonomy/testdata/pcaps/LG Smart TV 2.4GHz Broadcast Probe 55UH7700-UB.pcap b/taxonomy/testdata/pcaps/LG Smart TV 2.4GHz Broadcast Probe 55UH7700-UB.pcap
new file mode 100644
index 0000000..e25b5cb
--- /dev/null
+++ b/taxonomy/testdata/pcaps/LG Smart TV 2.4GHz Broadcast Probe 55UH7700-UB.pcap
Binary files differ
diff --git a/taxonomy/testdata/pcaps/LG Smart TV 2.4GHz Specific Probe 55UH7700-UB.pcap b/taxonomy/testdata/pcaps/LG Smart TV 2.4GHz Specific Probe 55UH7700-UB.pcap
new file mode 100644
index 0000000..e6cf5bd
--- /dev/null
+++ b/taxonomy/testdata/pcaps/LG Smart TV 2.4GHz Specific Probe 55UH7700-UB.pcap
Binary files differ
diff --git a/taxonomy/testdata/pcaps/LG Smart TV 5GHz Broadcast Probe 55UH7700-UB.pcap b/taxonomy/testdata/pcaps/LG Smart TV 5GHz Broadcast Probe 55UH7700-UB.pcap
new file mode 100644
index 0000000..a0eb75d
--- /dev/null
+++ b/taxonomy/testdata/pcaps/LG Smart TV 5GHz Broadcast Probe 55UH7700-UB.pcap
Binary files differ
diff --git a/taxonomy/testdata/pcaps/LG Smart TV 5GHz Specific Probe 55UH7700-UB.pcap b/taxonomy/testdata/pcaps/LG Smart TV 5GHz Specific Probe 55UH7700-UB.pcap
new file mode 100644
index 0000000..f576a6c
--- /dev/null
+++ b/taxonomy/testdata/pcaps/LG Smart TV 5GHz Specific Probe 55UH7700-UB.pcap
Binary files differ
diff --git a/taxonomy/testdata/pcaps/Nexus 5X 2.4GHz Broadcast Probe htcap 01ad.pcap b/taxonomy/testdata/pcaps/Nexus 5X 2.4GHz Broadcast Probe htcap 01ad.pcap
new file mode 100644
index 0000000..e871e93
--- /dev/null
+++ b/taxonomy/testdata/pcaps/Nexus 5X 2.4GHz Broadcast Probe htcap 01ad.pcap
Binary files differ
diff --git a/taxonomy/testdata/pcaps/Nexus 5X 2.4GHz Specific Probe htcap 01ad.pcap b/taxonomy/testdata/pcaps/Nexus 5X 2.4GHz Specific Probe htcap 01ad.pcap
new file mode 100644
index 0000000..85c56e0
--- /dev/null
+++ b/taxonomy/testdata/pcaps/Nexus 5X 2.4GHz Specific Probe htcap 01ad.pcap
Binary files differ
diff --git a/taxonomy/testdata/pcaps/Nexus 5X 5GHz Broadcast Probe htcap 01ad.pcap b/taxonomy/testdata/pcaps/Nexus 5X 5GHz Broadcast Probe htcap 01ad.pcap
new file mode 100644
index 0000000..9a44d6b
--- /dev/null
+++ b/taxonomy/testdata/pcaps/Nexus 5X 5GHz Broadcast Probe htcap 01ad.pcap
Binary files differ
diff --git a/taxonomy/testdata/pcaps/Nexus 5X 5GHz Small Specific Probe.pcap b/taxonomy/testdata/pcaps/Nexus 5X 5GHz Small Specific Probe.pcap
new file mode 100644
index 0000000..f9cfce7
--- /dev/null
+++ b/taxonomy/testdata/pcaps/Nexus 5X 5GHz Small Specific Probe.pcap
Binary files differ
diff --git a/taxonomy/testdata/pcaps/Nexus 5X 5GHz Specific Probe htcap 01ad.pcap b/taxonomy/testdata/pcaps/Nexus 5X 5GHz Specific Probe htcap 01ad.pcap
new file mode 100644
index 0000000..aef7521
--- /dev/null
+++ b/taxonomy/testdata/pcaps/Nexus 5X 5GHz Specific Probe htcap 01ad.pcap
Binary files differ
diff --git a/taxonomy/testdata/pcaps/Panasonic TV 2.4GHz TC-58AX800U Broadcast Probe.pcap b/taxonomy/testdata/pcaps/Panasonic TV 2.4GHz TC-58AX800U Broadcast Probe.pcap
new file mode 100644
index 0000000..25d831a
--- /dev/null
+++ b/taxonomy/testdata/pcaps/Panasonic TV 2.4GHz TC-58AX800U Broadcast Probe.pcap
Binary files differ
diff --git a/taxonomy/testdata/pcaps/Panasonic TV 2.4GHz TC-58AX800U Specific Probe.pcap b/taxonomy/testdata/pcaps/Panasonic TV 2.4GHz TC-58AX800U Specific Probe.pcap
new file mode 100644
index 0000000..6a64e36
--- /dev/null
+++ b/taxonomy/testdata/pcaps/Panasonic TV 2.4GHz TC-58AX800U Specific Probe.pcap
Binary files differ
diff --git a/taxonomy/testdata/pcaps/Panasonic TV 5GHz TC-58AX800U Broadcast Probe.pcap b/taxonomy/testdata/pcaps/Panasonic TV 5GHz TC-58AX800U Broadcast Probe.pcap
new file mode 100644
index 0000000..023077a
--- /dev/null
+++ b/taxonomy/testdata/pcaps/Panasonic TV 5GHz TC-58AX800U Broadcast Probe.pcap
Binary files differ
diff --git a/taxonomy/testdata/pcaps/Panasonic TV 5GHz TC-58AX800U Specific Probe.pcap b/taxonomy/testdata/pcaps/Panasonic TV 5GHz TC-58AX800U Specific Probe.pcap
new file mode 100644
index 0000000..562e8a2
--- /dev/null
+++ b/taxonomy/testdata/pcaps/Panasonic TV 5GHz TC-58AX800U Specific Probe.pcap
Binary files differ
diff --git a/taxonomy/testdata/pcaps/Samsung Galaxy 4G 2.4GHz SGH-T959V.pcap b/taxonomy/testdata/pcaps/Samsung Galaxy 4G 2.4GHz SGH-T959V.pcap
new file mode 100644
index 0000000..9a28122
--- /dev/null
+++ b/taxonomy/testdata/pcaps/Samsung Galaxy 4G 2.4GHz SGH-T959V.pcap
Binary files differ
diff --git a/taxonomy/testdata/pcaps/Samsung Smart TV 2.4GHz LED75 Broadcast Probe.pcap b/taxonomy/testdata/pcaps/Samsung Smart TV 2.4GHz LED75 Broadcast Probe.pcap
new file mode 100644
index 0000000..4a958c2
--- /dev/null
+++ b/taxonomy/testdata/pcaps/Samsung Smart TV 2.4GHz LED75 Broadcast Probe.pcap
Binary files differ
diff --git a/taxonomy/testdata/pcaps/Samsung Smart TV 2.4GHz LED75 Specific Probe.pcap b/taxonomy/testdata/pcaps/Samsung Smart TV 2.4GHz LED75 Specific Probe.pcap
new file mode 100644
index 0000000..9c0ea3d
--- /dev/null
+++ b/taxonomy/testdata/pcaps/Samsung Smart TV 2.4GHz LED75 Specific Probe.pcap
Binary files differ
diff --git a/taxonomy/testdata/pcaps/Samsung Smart TV 2.4GHz UN40JU6500 Broadcast Probe.pcap b/taxonomy/testdata/pcaps/Samsung Smart TV 2.4GHz UN40JU6500 Broadcast Probe.pcap
new file mode 100644
index 0000000..c84bb4e
--- /dev/null
+++ b/taxonomy/testdata/pcaps/Samsung Smart TV 2.4GHz UN40JU6500 Broadcast Probe.pcap
Binary files differ
diff --git a/taxonomy/testdata/pcaps/Samsung Smart TV 2.4GHz UN40JU6500 Specific Probe.pcap b/taxonomy/testdata/pcaps/Samsung Smart TV 2.4GHz UN40JU6500 Specific Probe.pcap
new file mode 100644
index 0000000..17b1931
--- /dev/null
+++ b/taxonomy/testdata/pcaps/Samsung Smart TV 2.4GHz UN40JU6500 Specific Probe.pcap
Binary files differ
diff --git a/taxonomy/testdata/pcaps/Samsung Smart TV 2.4GHz UN55JS9000 Broadcast Probe.pcap b/taxonomy/testdata/pcaps/Samsung Smart TV 2.4GHz UN55JS9000 Broadcast Probe.pcap
new file mode 100644
index 0000000..bae308d
--- /dev/null
+++ b/taxonomy/testdata/pcaps/Samsung Smart TV 2.4GHz UN55JS9000 Broadcast Probe.pcap
Binary files differ
diff --git a/taxonomy/testdata/pcaps/Samsung Smart TV 2.4GHz UN55JS9000 Specific Probe.pcap b/taxonomy/testdata/pcaps/Samsung Smart TV 2.4GHz UN55JS9000 Specific Probe.pcap
new file mode 100644
index 0000000..eaaae3e
--- /dev/null
+++ b/taxonomy/testdata/pcaps/Samsung Smart TV 2.4GHz UN55JS9000 Specific Probe.pcap
Binary files differ
diff --git a/taxonomy/testdata/pcaps/Samsung Smart TV 2.4GHz.pcap b/taxonomy/testdata/pcaps/Samsung Smart TV 2.4GHz UN60F6300AF.pcap
similarity index 100%
rename from taxonomy/testdata/pcaps/Samsung Smart TV 2.4GHz.pcap
rename to taxonomy/testdata/pcaps/Samsung Smart TV 2.4GHz UN60F6300AF.pcap
Binary files differ
diff --git a/taxonomy/testdata/pcaps/Samsung Smart TV 5GHz LED75 Broadcast Probe.pcap b/taxonomy/testdata/pcaps/Samsung Smart TV 5GHz LED75 Broadcast Probe.pcap
new file mode 100644
index 0000000..d3556c2
--- /dev/null
+++ b/taxonomy/testdata/pcaps/Samsung Smart TV 5GHz LED75 Broadcast Probe.pcap
Binary files differ
diff --git a/taxonomy/testdata/pcaps/Samsung Smart TV 5GHz LED75 Specific Probe.pcap b/taxonomy/testdata/pcaps/Samsung Smart TV 5GHz LED75 Specific Probe.pcap
new file mode 100644
index 0000000..19675e7
--- /dev/null
+++ b/taxonomy/testdata/pcaps/Samsung Smart TV 5GHz LED75 Specific Probe.pcap
Binary files differ
diff --git a/taxonomy/testdata/pcaps/Samsung Smart TV 5GHz UN40JU6500 Broadcast Probe.pcap b/taxonomy/testdata/pcaps/Samsung Smart TV 5GHz UN40JU6500 Broadcast Probe.pcap
new file mode 100644
index 0000000..6ea8b14
--- /dev/null
+++ b/taxonomy/testdata/pcaps/Samsung Smart TV 5GHz UN40JU6500 Broadcast Probe.pcap
Binary files differ
diff --git a/taxonomy/testdata/pcaps/Samsung Smart TV 5GHz UN40JU6500 Specific Probe.pcap b/taxonomy/testdata/pcaps/Samsung Smart TV 5GHz UN40JU6500 Specific Probe.pcap
new file mode 100644
index 0000000..1f5a8e5
--- /dev/null
+++ b/taxonomy/testdata/pcaps/Samsung Smart TV 5GHz UN40JU6500 Specific Probe.pcap
Binary files differ
diff --git a/taxonomy/testdata/pcaps/Samsung Smart TV 5GHz UN46ES7100F Broadcast Probe.pcap b/taxonomy/testdata/pcaps/Samsung Smart TV 5GHz UN46ES7100F Broadcast Probe.pcap
new file mode 100644
index 0000000..f2a4ab1
--- /dev/null
+++ b/taxonomy/testdata/pcaps/Samsung Smart TV 5GHz UN46ES7100F Broadcast Probe.pcap
Binary files differ
diff --git a/taxonomy/testdata/pcaps/Samsung Smart TV 5GHz UN46ES7100F Specific Probe.pcap b/taxonomy/testdata/pcaps/Samsung Smart TV 5GHz UN46ES7100F Specific Probe.pcap
new file mode 100644
index 0000000..3cbf5eb
--- /dev/null
+++ b/taxonomy/testdata/pcaps/Samsung Smart TV 5GHz UN46ES7100F Specific Probe.pcap
Binary files differ
diff --git a/taxonomy/testdata/pcaps/Samsung Smart TV 5GHz UN55JS9000 Broadcast Probe.pcap b/taxonomy/testdata/pcaps/Samsung Smart TV 5GHz UN55JS9000 Broadcast Probe.pcap
new file mode 100644
index 0000000..8d7cd18
--- /dev/null
+++ b/taxonomy/testdata/pcaps/Samsung Smart TV 5GHz UN55JS9000 Broadcast Probe.pcap
Binary files differ
diff --git a/taxonomy/testdata/pcaps/Samsung Smart TV 5GHz UN55JS9000 Specific Probe.pcap b/taxonomy/testdata/pcaps/Samsung Smart TV 5GHz UN55JS9000 Specific Probe.pcap
new file mode 100644
index 0000000..2f941b5
--- /dev/null
+++ b/taxonomy/testdata/pcaps/Samsung Smart TV 5GHz UN55JS9000 Specific Probe.pcap
Binary files differ
diff --git a/taxonomy/testdata/pcaps/Samsung Smart TV 5GHz.pcap b/taxonomy/testdata/pcaps/Samsung Smart TV 5GHz UN60F6300AF.pcap
similarity index 100%
rename from taxonomy/testdata/pcaps/Samsung Smart TV 5GHz.pcap
rename to taxonomy/testdata/pcaps/Samsung Smart TV 5GHz UN60F6300AF.pcap
Binary files differ
diff --git a/taxonomy/testdata/pcaps/Sony Bravia TV 2.4GHz Broadcast Probe XBR-49X850B.pcap b/taxonomy/testdata/pcaps/Sony Bravia TV 2.4GHz Broadcast Probe XBR-49X850B.pcap
new file mode 100644
index 0000000..0284c95
--- /dev/null
+++ b/taxonomy/testdata/pcaps/Sony Bravia TV 2.4GHz Broadcast Probe XBR-49X850B.pcap
Binary files differ
diff --git a/taxonomy/testdata/pcaps/Sony Bravia TV 2.4GHz Broadcast Probe XBR-55X850C.pcap b/taxonomy/testdata/pcaps/Sony Bravia TV 2.4GHz Broadcast Probe XBR-55X850C.pcap
new file mode 100644
index 0000000..a3fcae1
--- /dev/null
+++ b/taxonomy/testdata/pcaps/Sony Bravia TV 2.4GHz Broadcast Probe XBR-55X850C.pcap
Binary files differ
diff --git a/taxonomy/testdata/pcaps/Sony Bravia TV 2.4GHz Specific Probe XBR-49X850B.pcap b/taxonomy/testdata/pcaps/Sony Bravia TV 2.4GHz Specific Probe XBR-49X850B.pcap
new file mode 100644
index 0000000..976e3bb
--- /dev/null
+++ b/taxonomy/testdata/pcaps/Sony Bravia TV 2.4GHz Specific Probe XBR-49X850B.pcap
Binary files differ
diff --git a/taxonomy/testdata/pcaps/Sony Bravia TV 2.4GHz Specific Probe XBR-55X850C.pcap b/taxonomy/testdata/pcaps/Sony Bravia TV 2.4GHz Specific Probe XBR-55X850C.pcap
new file mode 100644
index 0000000..7a91a9b
--- /dev/null
+++ b/taxonomy/testdata/pcaps/Sony Bravia TV 2.4GHz Specific Probe XBR-55X850C.pcap
Binary files differ
diff --git a/taxonomy/testdata/pcaps/Sony Bravia TV 2015 model 2.4GHz Broadcast Probe XBR-55X900C.pcap b/taxonomy/testdata/pcaps/Sony Bravia TV 2015 model 2.4GHz Broadcast Probe XBR-55X900C.pcap
new file mode 100644
index 0000000..017f6e1
--- /dev/null
+++ b/taxonomy/testdata/pcaps/Sony Bravia TV 2015 model 2.4GHz Broadcast Probe XBR-55X900C.pcap
Binary files differ
diff --git a/taxonomy/testdata/pcaps/Sony Bravia TV 2015 model 2.4GHz Specific Probe XBR-55X900C.pcap b/taxonomy/testdata/pcaps/Sony Bravia TV 2015 model 2.4GHz Specific Probe XBR-55X900C.pcap
new file mode 100644
index 0000000..423fb96
--- /dev/null
+++ b/taxonomy/testdata/pcaps/Sony Bravia TV 2015 model 2.4GHz Specific Probe XBR-55X900C.pcap
Binary files differ
diff --git a/taxonomy/testdata/pcaps/Sony Bravia TV 2015 model 5GHz Broadcast Probe XBR-55X900C.pcap b/taxonomy/testdata/pcaps/Sony Bravia TV 2015 model 5GHz Broadcast Probe XBR-55X900C.pcap
new file mode 100644
index 0000000..45fc9d9
--- /dev/null
+++ b/taxonomy/testdata/pcaps/Sony Bravia TV 2015 model 5GHz Broadcast Probe XBR-55X900C.pcap
Binary files differ
diff --git a/taxonomy/testdata/pcaps/Sony Bravia TV 2015 model 5GHz Specific Probe XBR-55X900C.pcap b/taxonomy/testdata/pcaps/Sony Bravia TV 2015 model 5GHz Specific Probe XBR-55X900C.pcap
new file mode 100644
index 0000000..925cda6
--- /dev/null
+++ b/taxonomy/testdata/pcaps/Sony Bravia TV 2015 model 5GHz Specific Probe XBR-55X900C.pcap
Binary files differ
diff --git a/taxonomy/testdata/pcaps/Sony Bravia TV 5GHz Broadcast Probe XBR-49X850B.pcap b/taxonomy/testdata/pcaps/Sony Bravia TV 5GHz Broadcast Probe XBR-49X850B.pcap
new file mode 100644
index 0000000..2dfaf35
--- /dev/null
+++ b/taxonomy/testdata/pcaps/Sony Bravia TV 5GHz Broadcast Probe XBR-49X850B.pcap
Binary files differ
diff --git a/taxonomy/testdata/pcaps/Sony Bravia TV 5GHz Broadcast Probe XBR-55X850B.pcap b/taxonomy/testdata/pcaps/Sony Bravia TV 5GHz Broadcast Probe XBR-55X850B.pcap
new file mode 100644
index 0000000..7c961dd
--- /dev/null
+++ b/taxonomy/testdata/pcaps/Sony Bravia TV 5GHz Broadcast Probe XBR-55X850B.pcap
Binary files differ
diff --git a/taxonomy/testdata/pcaps/Sony Bravia TV 5GHz Specific Probe XBR-49X850B.pcap b/taxonomy/testdata/pcaps/Sony Bravia TV 5GHz Specific Probe XBR-49X850B.pcap
new file mode 100644
index 0000000..aa4ec4d
--- /dev/null
+++ b/taxonomy/testdata/pcaps/Sony Bravia TV 5GHz Specific Probe XBR-49X850B.pcap
Binary files differ
diff --git a/taxonomy/testdata/pcaps/Sony Bravia TV 5GHz Specific Probe XBR-55X850B.pcap b/taxonomy/testdata/pcaps/Sony Bravia TV 5GHz Specific Probe XBR-55X850B.pcap
new file mode 100644
index 0000000..3068a62
--- /dev/null
+++ b/taxonomy/testdata/pcaps/Sony Bravia TV 5GHz Specific Probe XBR-55X850B.pcap
Binary files differ
diff --git a/taxonomy/testdata/pcaps/Sony Ericsson Xperia X10 2.4GHz.pcap b/taxonomy/testdata/pcaps/Sony Ericsson Xperia X10 2.4GHz.pcap
new file mode 100644
index 0000000..9dee0c4
--- /dev/null
+++ b/taxonomy/testdata/pcaps/Sony Ericsson Xperia X10 2.4GHz.pcap
Binary files differ
diff --git a/taxonomy/testdata/pcaps/Toshiba Smart TV 2.4GHz Broadcast Probe 40L3400U.pcap b/taxonomy/testdata/pcaps/Toshiba Smart TV 2.4GHz Broadcast Probe 40L3400U.pcap
new file mode 100644
index 0000000..a5fc0e7
--- /dev/null
+++ b/taxonomy/testdata/pcaps/Toshiba Smart TV 2.4GHz Broadcast Probe 40L3400U.pcap
Binary files differ
diff --git a/taxonomy/testdata/pcaps/Toshiba Smart TV 2.4GHz Specific Probe 40L3400U.pcap b/taxonomy/testdata/pcaps/Toshiba Smart TV 2.4GHz Specific Probe 40L3400U.pcap
new file mode 100644
index 0000000..926c99e
--- /dev/null
+++ b/taxonomy/testdata/pcaps/Toshiba Smart TV 2.4GHz Specific Probe 40L3400U.pcap
Binary files differ
diff --git a/taxonomy/testdata/pcaps/Toshiba Smart TV 5GHz Broadcast Probe 40L3400U.pcap b/taxonomy/testdata/pcaps/Toshiba Smart TV 5GHz Broadcast Probe 40L3400U.pcap
new file mode 100644
index 0000000..625f35a
--- /dev/null
+++ b/taxonomy/testdata/pcaps/Toshiba Smart TV 5GHz Broadcast Probe 40L3400U.pcap
Binary files differ
diff --git a/taxonomy/testdata/pcaps/Toshiba Smart TV 5GHz Specific Probe 40L3400U.pcap b/taxonomy/testdata/pcaps/Toshiba Smart TV 5GHz Specific Probe 40L3400U.pcap
new file mode 100644
index 0000000..7da522f
--- /dev/null
+++ b/taxonomy/testdata/pcaps/Toshiba Smart TV 5GHz Specific Probe 40L3400U.pcap
Binary files differ
diff --git a/taxonomy/testdata/pcaps/Vizio Smart TV 2.4GHz Large Broadcast Probe P602ui-B3.pcap b/taxonomy/testdata/pcaps/Vizio Smart TV 2.4GHz Large Broadcast Probe P602ui-B3.pcap
new file mode 100644
index 0000000..62f1748
--- /dev/null
+++ b/taxonomy/testdata/pcaps/Vizio Smart TV 2.4GHz Large Broadcast Probe P602ui-B3.pcap
Binary files differ
diff --git a/taxonomy/testdata/pcaps/Vizio Smart TV 2.4GHz Small Broadcast Probe P602ui-B3.pcap b/taxonomy/testdata/pcaps/Vizio Smart TV 2.4GHz Small Broadcast Probe P602ui-B3.pcap
new file mode 100644
index 0000000..513b9b6
--- /dev/null
+++ b/taxonomy/testdata/pcaps/Vizio Smart TV 2.4GHz Small Broadcast Probe P602ui-B3.pcap
Binary files differ
diff --git a/taxonomy/testdata/pcaps/Vizio Smart TV 2.4GHz Specific Probe P602ui-B3.pcap b/taxonomy/testdata/pcaps/Vizio Smart TV 2.4GHz Specific Probe P602ui-B3.pcap
new file mode 100644
index 0000000..e662f82
--- /dev/null
+++ b/taxonomy/testdata/pcaps/Vizio Smart TV 2.4GHz Specific Probe P602ui-B3.pcap
Binary files differ
diff --git a/taxonomy/testdata/pcaps/Vizio Smart TV 5GHz P602ui-B3 Broadcast Probe.pcap b/taxonomy/testdata/pcaps/Vizio Smart TV 5GHz P602ui-B3 Broadcast Probe.pcap
new file mode 100644
index 0000000..d278248
--- /dev/null
+++ b/taxonomy/testdata/pcaps/Vizio Smart TV 5GHz P602ui-B3 Broadcast Probe.pcap
Binary files differ
diff --git a/taxonomy/testdata/pcaps/Vizio Smart TV 5GHz P602ui-B3 Specific Probe.pcap b/taxonomy/testdata/pcaps/Vizio Smart TV 5GHz P602ui-B3 Specific Probe.pcap
new file mode 100644
index 0000000..b1784dc
--- /dev/null
+++ b/taxonomy/testdata/pcaps/Vizio Smart TV 5GHz P602ui-B3 Specific Probe.pcap
Binary files differ
diff --git a/taxonomy/testdata/pcaps/Wink Hub 2.4GHz Broadcast Probe.pcap b/taxonomy/testdata/pcaps/Wink Hub 2.4GHz Broadcast Probe.pcap
new file mode 100644
index 0000000..bc78522
--- /dev/null
+++ b/taxonomy/testdata/pcaps/Wink Hub 2.4GHz Broadcast Probe.pcap
Binary files differ
diff --git a/taxonomy/testdata/pcaps/Wink Hub 2.4GHz Specific Probe.pcap b/taxonomy/testdata/pcaps/Wink Hub 2.4GHz Specific Probe.pcap
new file mode 100644
index 0000000..1d98058
--- /dev/null
+++ b/taxonomy/testdata/pcaps/Wink Hub 2.4GHz Specific Probe.pcap
Binary files differ
diff --git a/taxonomy/testdata/pcaps/Xiaomi Mi 5 2.4GHz Broadcast Probe.pcap b/taxonomy/testdata/pcaps/Xiaomi Mi 5 2.4GHz Broadcast Probe.pcap
new file mode 100644
index 0000000..1bf9860
--- /dev/null
+++ b/taxonomy/testdata/pcaps/Xiaomi Mi 5 2.4GHz Broadcast Probe.pcap
Binary files differ
diff --git a/taxonomy/testdata/pcaps/Xiaomi Mi 5 2.4GHz Specific Probe.pcap b/taxonomy/testdata/pcaps/Xiaomi Mi 5 2.4GHz Specific Probe.pcap
new file mode 100644
index 0000000..247e5da
--- /dev/null
+++ b/taxonomy/testdata/pcaps/Xiaomi Mi 5 2.4GHz Specific Probe.pcap
Binary files differ
diff --git a/taxonomy/testdata/pcaps/Xiaomi Mi 5 5GHz Broadcast Probe.pcap b/taxonomy/testdata/pcaps/Xiaomi Mi 5 5GHz Broadcast Probe.pcap
new file mode 100644
index 0000000..afd7217
--- /dev/null
+++ b/taxonomy/testdata/pcaps/Xiaomi Mi 5 5GHz Broadcast Probe.pcap
Binary files differ
diff --git a/taxonomy/testdata/pcaps/Xiaomi Mi 5 5GHz Specific Probe.pcap b/taxonomy/testdata/pcaps/Xiaomi Mi 5 5GHz Specific Probe.pcap
new file mode 100644
index 0000000..20c9e07
--- /dev/null
+++ b/taxonomy/testdata/pcaps/Xiaomi Mi 5 5GHz Specific Probe.pcap
Binary files differ
diff --git a/taxonomy/testdata/pcaps/iPad 1st or 2nd gen 5GHz older iOS Broadcast Probe.pcap b/taxonomy/testdata/pcaps/iPad 1st or 2nd gen 5GHz older iOS Broadcast Probe.pcap
new file mode 100644
index 0000000..03be132
--- /dev/null
+++ b/taxonomy/testdata/pcaps/iPad 1st or 2nd gen 5GHz older iOS Broadcast Probe.pcap
Binary files differ
diff --git a/taxonomy/testdata/pcaps/iPad 1st or 2nd gen 5GHz older iOS Specific Probe.pcap b/taxonomy/testdata/pcaps/iPad 1st or 2nd gen 5GHz older iOS Specific Probe.pcap
new file mode 100644
index 0000000..0398616
--- /dev/null
+++ b/taxonomy/testdata/pcaps/iPad 1st or 2nd gen 5GHz older iOS Specific Probe.pcap
Binary files differ
diff --git a/taxonomy/testdata/pcaps/iPad Mini 3rd gen 2.4GHz MH392LL.pcap b/taxonomy/testdata/pcaps/iPad Mini 3rd gen 2.4GHz MH392LL.pcap
new file mode 100644
index 0000000..5c2b5b7
--- /dev/null
+++ b/taxonomy/testdata/pcaps/iPad Mini 3rd gen 2.4GHz MH392LL.pcap
Binary files differ
diff --git a/taxonomy/testdata/pcaps/iPad Mini 3rd gen 5GHz MH392LL.pcap b/taxonomy/testdata/pcaps/iPad Mini 3rd gen 5GHz MH392LL.pcap
new file mode 100644
index 0000000..7ad8491
--- /dev/null
+++ b/taxonomy/testdata/pcaps/iPad Mini 3rd gen 5GHz MH392LL.pcap
Binary files differ
diff --git a/taxonomy/testdata/pcaps/iPad Mini 4th gen 5GHz MK6L2LL Broadcast Probe.pcap b/taxonomy/testdata/pcaps/iPad Mini 4th gen 5GHz MK6L2LL Broadcast Probe.pcap
new file mode 100644
index 0000000..29b07ca
--- /dev/null
+++ b/taxonomy/testdata/pcaps/iPad Mini 4th gen 5GHz MK6L2LL Broadcast Probe.pcap
Binary files differ
diff --git a/taxonomy/testdata/pcaps/iPad Mini 4th gen 5GHz MK6L2LL Specific Probe.pcap b/taxonomy/testdata/pcaps/iPad Mini 4th gen 5GHz MK6L2LL Specific Probe.pcap
new file mode 100644
index 0000000..9801c46
--- /dev/null
+++ b/taxonomy/testdata/pcaps/iPad Mini 4th gen 5GHz MK6L2LL Specific Probe.pcap
Binary files differ
diff --git a/taxonomy/wifi.py b/taxonomy/wifi.py
index aa9a0cb..6b48061 100644
--- a/taxonomy/wifi.py
+++ b/taxonomy/wifi.py
@@ -74,7 +74,7 @@
'wifi4|probe:0,1,50|assoc:0,1,50,221(0050f2,2)|oui:amazon':
('Amazon Kindle', 'Keyboard 3', '2.4GHz'),
'wifi4|probe:0,1,50,45,htcap:002c,htagg:01,htmcs:000000ff|assoc:0,1,50,45,48,221(0050f2,2),htcap:002c,htagg:01,htmcs:000000ff|oui:amazon':
- ('Amazon Kindle', 'Voyage, Paperwhite, or 4th gen', '2.4GHz'),
+ ('Amazon Kindle', '', '2.4GHz'),
'wifi4|probe:0,1,50,3,45,221(0050f2,8),htcap:1130,htagg:18,htmcs:000000ff|assoc:0,1,50,48,45,221(0050f2,2),htcap:1130,htagg:18,htmcs:000000ff|oui:amazon':
('Amazon Kindle', 'Fire 7" (2011 edition)', '2.4GHz'),
@@ -121,6 +121,9 @@
'wifi4|probe:0,1,50,3,45,127,107,221(001018,2),221(00904c,51),221(0050f2,8),htcap:0020,htagg:1a,htmcs:000000ff,extcap:00000804|assoc:0,1,48,50,45,70,221(001018,2),221(00904c,51),221(0050f2,2),htcap:0020,htagg:1a,htmcs:000000ff|os:ios':
('Apple Watch', '', '2.4GHz'),
+ 'wifi4|probe:0,1,50,45,htcap:1030,htagg:18,htmcs:000000ff|assoc:0,1,50,46,48,45,221(0050f2,2),htcap:1030,htagg:18,htmcs:000000ff|oui:barnes&noble':
+ ('Barnes & Noble Nook', 'Color', '2.4GHz'),
+
'wifi4|probe:0,1,50,221(0050f2,4)|assoc:0,1,50,45,221(0050f2,2),48,htcap:000c,htagg:1b,htmcs:000000ff|os:wemo':
('Belkin WeMo', 'Switch', '2.4GHz'),
@@ -136,10 +139,17 @@
'wifi4|probe:0,1,50,45,3,221(001018,2),221(00904c,51),htcap:112c,htagg:19,htmcs:000000ff|assoc:0,1,48,50,45,221(001018,2),221(00904c,51),221(0050f2,2),htcap:112c,htagg:19,htmcs:000000ff|os:brotherprinter':
('Brother Printer', '', '2.4GHz'),
+ # MX410, probably others
'wifi4|probe:0,1,3,45,50,htcap:007e,htagg:00,htmcs:000000ff|assoc:0,1,45,48,50,221(0050f2,2),htcap:000c,htagg:1b,htmcs:000000ff|os:canonprinter':
('Canon Printer', '', '2.4GHz'),
+ # MX492, probably others
'wifi4|probe:0,1,3,45,50,htcap:007e,htagg:00,htmcs:000000ff|assoc:0,1,48,50,221(0050f2,2),45,htcap:000c,htagg:1b,htmcs:000000ff|os:canonprinter':
('Canon Printer', '', '2.4GHz'),
+ # MX492 has been seen to send these massive Probe packets. Likely other models, too.
+ 'wifi4|probe:64,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,10,127,11,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,extcap:|assoc:0,1,48,50,221(0050f2,2),45,htcap:000c,htagg:1b,htmcs:000000ff|os:canonprinter':
+ ('Canon Printer', '', '2.4GHz'),
+ 'wifi4|probe:0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0|assoc:0,1,48,50,221(0050f2,2),45,htcap:000c,htagg:1b,htmcs:000000ff|os:canonprinter':
+ ('Canon Printer', '', '2.4GHz'),
'wifi4|probe:0,1,45,191,htcap:11e2,htagg:17,htmcs:0000ffff,vhtcap:038071a0,vhtrxmcs:0000fffa,vhttxmcs:0000fffa|assoc:0,1,48,45,127,191,221(0050f2,2),htcap:11e6,htagg:17,htmcs:0000ffff,vhtcap:038001a0,vhtrxmcs:0000fffa,vhttxmcs:0000fffa,extcap:0000000000000040|os:chromeos':
('Chromebook', 'Pixel 2', '5GHz'),
@@ -268,6 +278,8 @@
'wifi4|probe:0,1,45,221(001018,2),221(00904c,51),htcap:080c,htagg:1b,htmcs:000000ff|assoc:0,1,33,36,48,45,221(001018,2),221(00904c,51),221(0050f2,2),htcap:080c,htagg:1b,htmcs:000000ff,txpow:1008|os:ios':
('iPad', '1st or 2nd gen', '5GHz'),
+ 'wifi4|probe:0,1,45,221(001018,2),htcap:080c,htagg:1b,htmcs:000000ff|assoc:0,1,33,36,48,45,221(001018,2),221(00904c,51),htcap:080c,htagg:1b,htmcs:000000ff,txpow:1008|os:ios':
+ ('iPad', '1st or 2nd gen', '5GHz'),
'wifi4|probe:0,1,45,221(001018,2),221(00904c,51),htcap:0800,htagg:1b,htmcs:000000ff|assoc:0,1,33,36,48,45,221(001018,2),221(00904c,51),221(0050f2,2),htcap:0800,htagg:1b,htmcs:000000ff,txpow:1008|os:ios':
('iPad', '1st or 2nd gen', '5GHz'),
'wifi4|probe:0,1,50,45,221(001018,2),221(00904c,51),htcap:180c,htagg:1b,htmcs:000000ff|assoc:0,1,33,36,48,50,45,221(001018,2),221(00904c,51),221(0050f2,2),htcap:180c,htagg:1b,htmcs:000000ff,txpow:1008|os:ios':
@@ -334,6 +346,20 @@
'wifi4|probe:0,1,50,3,45,127,107,221(001018,2),221(00904c,51),221(0050f2,8),htcap:01bc,htagg:1b,htmcs:0000ffff,extcap:00000804|assoc:0,1,33,36,48,50,45,70,221(001018,2),221(00904c,51),221(0050f2,2),htcap:01bc,htagg:1b,htmcs:0000ffff,txpow:1603|os:ios':
('iPad Mini', '2nd gen', '2.4GHz'),
+ 'wifi4|probe:0,1,45,127,107,221(001018,2),221(00904c,51),221(0050f2,8),htcap:01fe,htagg:1b,htmcs:0000ffff,extcap:00000804|assoc:0,1,33,36,48,45,221(001018,2),221(00904c,51),221(0050f2,2),htcap:01fe,htagg:1b,htmcs:0000ffff,txpow:e001|os:ios':
+ ('iPad Mini', '3rd gen', '5GHz'),
+ 'wifi4|probe:0,1,45,127,107,221(001018,2),221(00904c,51),221(0050f2,8),htcap:01fe,htagg:1b,htmcs:0000ffff,extcap:00000804|assoc:0,1,33,36,48,45,70,221(001018,2),221(00904c,51),221(0050f2,2),htcap:01fe,htagg:1b,htmcs:0000ffff,txpow:e001|os:ios':
+ ('iPad Mini', '3rd gen', '5GHz'),
+ 'wifi4|probe:0,1,50,3,45,127,107,221(001018,2),221(00904c,51),221(0050f2,8),htcap:01bc,htagg:1b,htmcs:0000ffff,extcap:00000804|assoc:0,1,33,36,48,50,45,221(001018,2),221(00904c,51),221(0050f2,2),htcap:01bc,htagg:1b,htmcs:0000ffff,txpow:1201|os:ios':
+ ('iPad Mini', '3rd gen', '2.4GHz'),
+ 'wifi4|probe:0,1,50,3,45,127,107,221(001018,2),221(00904c,51),221(0050f2,8),htcap:01bc,htagg:1b,htmcs:0000ffff,extcap:00000804|assoc:0,1,33,36,48,50,45,70,221(001018,2),221(00904c,51),221(0050f2,2),htcap:01bc,htagg:1b,htmcs:0000ffff,txpow:1201|os:ios':
+ ('iPad Mini', '3rd gen', '2.4GHz'),
+
+ 'wifi4|probe:0,1,45,127,107,191,221(0050f2,8),221(001018,2),htcap:006f,htagg:17,htmcs:0000ffff,vhtcap:0f815832,vhtrxmcs:0000fffa,vhttxmcs:0000fffa,extcap:0400088400000040|assoc:0,1,33,36,45,127,191,221(001018,2),221(0050f2,2),htcap:006f,htagg:17,htmcs:0000ffff,vhtcap:0f815832,vhtrxmcs:0000fffa,vhttxmcs:0000fffa,txpow:e002,extcap:0400000000000040|os:ios':
+ ('iPad Mini', '4th gen', '5GHz'),
+ 'wifi4|probe:0,1,45,127,107,191,221(0050f2,8),221(001018,2),htcap:006f,htagg:17,htmcs:0000ffff,vhtcap:0f815832,vhtrxmcs:0000fffa,vhttxmcs:0000fffa,extcap:0400088400000040|assoc:0,1,33,36,48,70,45,127,191,221(001018,2),221(0050f2,2),htcap:006f,htagg:17,htmcs:0000ffff,vhtcap:0f815832,vhtrxmcs:0000fffa,vhttxmcs:0000fffa,txpow:e002,extcap:0400000000000040|os:ios':
+ ('iPad Mini', '4th gen', '5GHz'),
+
'wifi4|probe:0,1,3,50|assoc:0,1,48,50|os:ios':
('iPhone 2', '', '2.4GHz'),
@@ -501,8 +527,16 @@
'wifi4|probe:0,1,3,45,221(0050f2,8),221(0050f2,4),221(506f9a,9),htcap:016e,htagg:03,htmcs:000000ff,wps:LG_V400|assoc:0,1,33,36,48,70,45,221(0050f2,2),127,htcap:016e,htagg:03,htmcs:000000ff,txpow:170d,extcap:00000a0200000000':
('LG Pad', 'v400', '5GHz'),
+ 'wifi4|probe:0,1,45,191,221(001018,2),htcap:006f,htagg:17,htmcs:0000ffff,vhtcap:0f815832,vhtrxmcs:0000fffa,vhttxmcs:0000fffa|assoc:0,1,33,36,48,45,191,221(001018,2),221(0050f2,2),htcap:006f,htagg:17,htmcs:0000ffff,vhtcap:0f815832,vhtrxmcs:0000fffa,vhttxmcs:0000fffa,txpow:e009|os:lgtv':
+ ('LG Smart TV', '', '5GHz'),
+ 'wifi4|probe:0,1,45,191,221(0050f2,4),221(506f9a,9),221(001018,2),htcap:006f,htagg:17,htmcs:0000ffff,vhtcap:0f815832,vhtrxmcs:0000fffa,vhttxmcs:0000fffa,wps:_|assoc:0,1,33,36,48,45,191,221(001018,2),221(0050f2,2),htcap:006f,htagg:17,htmcs:0000ffff,vhtcap:0f815832,vhtrxmcs:0000fffa,vhttxmcs:0000fffa,txpow:e009|os:lgtv':
+ ('LG Smart TV', '', '5GHz'),
+ 'wifi4|probe:0,1,50,3,45,127,221(0050f2,4),221(506f9a,9),221(001018,2),htcap:002d,htagg:17,htmcs:0000ffff,extcap:0000000000000040,wps:_|assoc:0,1,50,33,36,48,45,221(001018,2),221(0050f2,2),htcap:002d,htagg:17,htmcs:0000ffff,txpow:1209|os:lgtv':
+ ('LG Smart TV', '', '2.4GHz'),
'wifi4|probe:0,1,50,3,45,127,221(001018,2),221(00904c,51),htcap:11ac,htagg:16,htmcs:0000ffff,extcap:0000000000000040|assoc:0,1,33,36,48,50,45,127,221(001018,2),221(0050f2,2),htcap:11ac,htagg:16,htmcs:0000ffff,txpow:140a,extcap:0000000000000040|os:lgtv':
('LG Smart TV', '', '2.4GHz'),
+ 'wifi4|probe:0,1,50,3,45,221(0050f2,4),221(506f9a,9),221(001018,2),htcap:002d,htagg:17,htmcs:0000ffff,wps:_|assoc:0,1,50,33,36,48,45,221(001018,2),221(0050f2,2),htcap:002d,htagg:17,htmcs:0000ffff,txpow:1209|os:lgtv':
+ ('LG Smart TV', '', '2.4GHz'),
'wifi4|probe:0,1,50,3,45,221(0050f2,8),221(0050f2,4),221(506f9a,9),htcap:012c,htagg:03,htmcs:000000ff,wps:LGLS660|assoc:0,1,50,48,45,221(0050f2,2),htcap:012c,htagg:03,htmcs:000000ff':
('LG Tribute', '', '2.4GHz'),
@@ -607,6 +641,8 @@
('Nexus 5X', '', '5GHz'),
'wifi4|probe:0,1,127,45,191,htcap:01ef,htagg:03,htmcs:0000ffff,vhtcap:338061b2,vhtrxmcs:030cfffa,vhttxmcs:030cfffa,extcap:00000a020100004080|assoc:0,1,33,36,48,70,45,221(0050f2,2),191,127,htcap:01ef,htagg:03,htmcs:0000ffff,vhtcap:339071b2,vhtrxmcs:030cfffa,vhttxmcs:030cfffa,txpow:1e08,extcap:000000000000004080|oui:lg':
('Nexus 5X', '', '5GHz'),
+ 'wifi4|probe:0,1,127,45,191,htcap:01ad,htagg:03,htmcs:0000ffff,vhtcap:338061b2,vhtrxmcs:030cfffa,vhttxmcs:030cfffa,extcap:00000a020100004080|assoc:0,1,33,36,48,70,45,221(0050f2,2),191,127,htcap:01ef,htagg:03,htmcs:0000ffff,vhtcap:339071b2,vhtrxmcs:030cfffa,vhttxmcs:030cfffa,txpow:1e08,extcap:000000000000004080|oui:lg':
+ ('Nexus 5X', '', '5GHz'),
'wifi4|probe:0,1,127,extcap:00000a020100004080|assoc:0,1,33,36,48,70,45,221(0050f2,2),191,127,htcap:01ef,htagg:03,htmcs:0000ffff,vhtcap:339071b2,vhtrxmcs:030cfffa,vhttxmcs:030cfffa,txpow:1e08,extcap:000000000000004080|oui:lg':
('Nexus 5X', '', '5GHz'),
'wifi4|probe:0,1,45,221(0050f2,8),191,127,htcap:01ef,htagg:03,htmcs:0000ffff,vhtcap:339071b2,vhtrxmcs:030cfffa,vhttxmcs:030cfffa,extcap:000000000000004080|assoc:0,1,33,36,48,70,45,221(0050f2,2),191,127,htcap:01ef,htagg:03,htmcs:0000ffff,vhtcap:339071b2,vhtrxmcs:030cfffa,vhttxmcs:030cfffa,txpow:1e08,extcap:000000000000004080|oui:lg':
@@ -619,6 +655,8 @@
('Nexus 5X', '', '2.4GHz'),
'wifi4|probe:0,1,50,127,45,191,htcap:01ef,htagg:03,htmcs:0000ffff,vhtcap:338061b2,vhtrxmcs:030cfffa,vhttxmcs:030cfffa,extcap:00000a020100004080|assoc:0,1,50,33,48,70,45,221(0050f2,2),127,htcap:01ad,htagg:03,htmcs:0000ffff,txpow:1e08,extcap:000000000000000080|oui:lg':
('Nexus 5X', '', '2.4GHz'),
+ 'wifi4|probe:0,1,50,127,45,191,htcap:01ad,htagg:03,htmcs:0000ffff,vhtcap:338061b2,vhtrxmcs:030cfffa,vhttxmcs:030cfffa,extcap:00000a020100004080|assoc:0,1,50,33,48,70,45,221(0050f2,2),127,htcap:01ad,htagg:03,htmcs:0000ffff,txpow:1e08,extcap:000000000000000080|oui:lg':
+ ('Nexus 5X', '', '2.4GHz'),
'wifi4|probe:0,1,50,127,extcap:00000a020100004080|assoc:0,1,50,33,48,70,45,221(0050f2,2),127,htcap:01ad,htagg:03,htmcs:0000ffff,txpow:1e08,extcap:000000000000000080|oui:lg':
('Nexus 5X', '', '2.4GHz'),
'wifi4|probe:0,1,50,3,45,221(0050f2,8),127,htcap:01ad,htagg:03,htmcs:0000ffff,extcap:000000000000000080|assoc:0,1,50,33,48,70,45,221(0050f2,2),127,htcap:01ad,htagg:03,htmcs:0000ffff,txpow:1e08,extcap:000000000000000080|oui:lg':
@@ -741,6 +779,10 @@
'wifi4|probe:0,1,50,3,45,221(0050f2,8),htcap:012c,htagg:03,htmcs:000000ff|assoc:0,1,50,33,48,70,45,221(0050f2,2),127,htcap:012c,htagg:03,htmcs:000000ff,txpow:170d,extcap:00000a0200000000|oui:oneplus':
('Oneplus', 'X', '2.4GHz'),
+ 'wifi4|probe:0,1,45,221(0050f2,4),htcap:11ee,htagg:02,htmcs:0000ffff,wps:WPS_STA|assoc:0,1,33,36,48,221(0050f2,2),45,127,htcap:11ee,htagg:02,htmcs:0000ffff,txpow:0b00,extcap:01|os:panasonictv':
+ ('Panasonic TV', '', '5GHz'),
+ 'wifi4|probe:0,1,50,45,221(0050f2,4),htcap:01ac,htagg:02,htmcs:0000ffff,wps:WPS_STA|assoc:0,1,50,221(0050f2,2),45,127,htcap:01ac,htagg:02,htmcs:0000ffff,extcap:01|os:panasonictv':
+ ('Panasonic TV', '', '2.4GHz'),
'wifi4|probe:0,1,50,48|assoc:0,1,33,36,50,221(0050f2,2),45,221(00037f,1),221(00037f,4),48,htcap:1004,htagg:1b,htmcs:0000ffff,txpow:0f0f|os:panasonictv':
('Panasonic TV', '', '2.4GHz'),
'wifi4|probe:0,1,50,45,221(0050f2,4),htcap:01ad,htagg:02,htmcs:0000ffff,wps:WPS_SUPPLICANT_STATION|assoc:0,1,50,45,48,221(0050f2,2),htcap:01ad,htagg:02,htmcs:0000ffff|os:panasonictv':
@@ -1005,12 +1047,30 @@
'wifi4|probe:0,1,45,htcap:11ee,htagg:02,htmcs:0000ffff|assoc:0,1,45,127,33,36,48,221(0050f2,2),htcap:11ee,htagg:02,htmcs:0000ffff,txpow:1100,extcap:01|os:samsungtv':
('Samsung Smart TV', '', '5GHz'),
+ # UN55JS9000, probably more
+ 'wifi4|probe:0,1,45,127,191,221(001018,2),htcap:006f,htagg:17,htmcs:0000ffff,vhtcap:0f815832,vhtrxmcs:0000fffa,vhttxmcs:0000fffa,extcap:0000000000000040|assoc:0,1,33,36,48,45,127,191,221(001018,2),221(0050f2,2),htcap:006f,htagg:17,htmcs:0000ffff,vhtcap:0f815832,vhtrxmcs:0000fffa,vhttxmcs:0000fffa,txpow:e009,extcap:0000000000000040|os:tizen':
+ ('Samsung Smart TV', '', '5GHz'),
+ # LED75
+ 'wifi4|probe:0,1,45,221(002d25,32),htcap:11ee,htagg:02,htmcs:0000ffff|assoc:0,1,33,36,221(0050f2,2),45,127,htcap:11ee,htagg:02,htmcs:0000ffff,txpow:0e00,extcap:01|os:samsungtv':
+ ('Samsung Smart TV', '', '5GHz'),
+ # UN46ES7100F
+ 'wifi4|probe:0,1,45,221(002d25,32),htcap:11ee,htagg:02,htmcs:0000ffff|assoc:0,1,33,36,48,221(0050f2,2),45,127,htcap:11ee,htagg:02,htmcs:0000ffff,txpow:0e00,extcap:01|os:samsungtv':
+ ('Samsung Smart TV', '', '5GHz'),
+ # UN40JU6500
+ 'wifi4|probe:0,1,45,191,221(001018,2),htcap:006f,htagg:17,htmcs:0000ffff,vhtcap:0f815832,vhtrxmcs:0000fffa,vhttxmcs:0000fffa|assoc:0,1,33,36,48,45,191,221(001018,2),221(0050f2,2),htcap:006f,htagg:17,htmcs:0000ffff,vhtcap:0f815832,vhtrxmcs:0000fffa,vhttxmcs:0000fffa,txpow:e009|os:tizen':
+ ('Samsung Smart TV', '', '5GHz'),
'wifi4|probe:0,1,50,45,htcap:01ac,htagg:02,htmcs:0000ffff|assoc:0,1,50,45,127,48,221(0050f2,2),htcap:01ac,htagg:02,htmcs:0000ffff,extcap:01|os:samsungtv':
('Samsung Smart TV', '', '2.4GHz'),
'wifi4|probe:0,1,50,45,221(002d25,32),htcap:01ac,htagg:02,htmcs:0000ffff|assoc:0,1,50,48,221(0050f2,2),45,127,htcap:01ac,htagg:02,htmcs:0000ffff,extcap:01|os:samsungtv':
('Samsung Smart TV', '', '2.4GHz'),
'wifi4|probe:0,1,50,45,htcap:0120,htagg:02,htmcs:000000ff|assoc:0,1,50,48,221(0050f2,2),45,127,htcap:0120,htagg:02,htmcs:000000ff,extcap:01|os:samsungtv':
('Samsung Smart TV', '', '2.4GHz'),
+ # UN40JU6500, probably more
+ 'wifi4|probe:0,1,50,3,45,127,221(001018,2),htcap:002d,htagg:17,htmcs:0000ffff,extcap:0000000000000040|assoc:0,1,50,33,36,48,45,221(001018,2),221(0050f2,2),htcap:002d,htagg:17,htmcs:0000ffff,txpow:1209|os:tizen':
+ ('Samsung Smart TV', '', '2.4GHz'),
+ # UN40JU6500, probably more
+ 'wifi4|probe:0,1,50,3,45,221(001018,2),htcap:002d,htagg:17,htmcs:0000ffff|assoc:0,1,50,33,36,48,45,221(001018,2),221(0050f2,2),htcap:002d,htagg:17,htmcs:0000ffff,txpow:1209|os:tizen':
+ ('Samsung Smart TV', '', '2.4GHz'),
'wifi4|probe:0,1,50,3,45,htcap:11ef,htagg:1b,htmcs:0000ffff|assoc:0,1,50,48,45,221(0050f2,2),htcap:11ef,htagg:1b,htmcs:0000ffff|oui:sling':
('Slingbox', '500', '2.4GHz'),
@@ -1021,6 +1081,12 @@
('Sony Bravia TV', '2015 model', '5GHz'),
'wifi4|probe:0,1,221(0050f2,4),221(506f9a,10),221(506f9a,9),wps:BRAVIA_2015|assoc:0,1,45,127,221(000c43,6),221(0050f2,2),48,127,htcap:01ef,htagg:13,htmcs:0000ffff,extcap:00000a02':
('Sony Bravia TV', '2015 model', '5GHz'),
+ 'wifi4|probe:0,1,45,191,221(0050f2,4),221(506f9a,10),221(506f9a,9),htcap:11ef,htagg:13,htmcs:0000ffff,vhtcap:31c139b0,vhtrxmcs:030cfffa,vhttxmcs:030cfffa,wps:BRAVIA_4K_2015|assoc:0,1,45,191,127,221(000c43,6),221(0050f2,2),48,127,htcap:006f,htagg:13,htmcs:0000ffff,vhtcap:31c001b0,vhtrxmcs:030cfffa,vhttxmcs:030cfffa,extcap:00000a02':
+ ('Sony Bravia TV', '2015 model', '5GHz'),
+ 'wifi4|probe:0,1,45,221(0050f2,4),htcap:11ee,htagg:02,htmcs:0000ffff,wps:Sony_BRAVIA|assoc:0,1,33,36,48,221(0050f2,2),45,127,htcap:11ee,htagg:02,htmcs:0000ffff,txpow:0c00,extcap:01':
+ ('Sony Bravia TV', '', '5GHz'),
+ 'wifi4|probe:0,1,221(0050f2,4),221(506f9a,10),221(506f9a,9),wps:BRAVIA_4K_2015|assoc:0,1,45,191,127,221(000c43,6),221(0050f2,2),48,127,htcap:006f,htagg:13,htmcs:0000ffff,vhtcap:31c001b0,vhtrxmcs:030cfffa,vhttxmcs:030cfffa,extcap:00000a02':
+ ('Sony Bravia TV', '2015 model', '5GHz'),
'wifi4|probe:0,1,50,45,221(0050f2,4),221(506f9a,10),221(506f9a,9),htcap:01ad,htagg:02,htmcs:0000ffff,wps:Sony_BRAVIA|assoc:0,1,50,45,127,48,221(0050f2,2),htcap:01ad,htagg:02,htmcs:0000ffff,extcap:01':
('Sony Bravia TV', '', '2.4GHz'),
'wifi4|probe:0,1,50,45,221(0050f2,4),htcap:01ac,htagg:02,htmcs:0000ffff,wps:Sony_BRAVIA|assoc:0,1,50,48,221(0050f2,2),45,127,htcap:01ac,htagg:02,htmcs:0000ffff,extcap:01':
@@ -1033,6 +1099,8 @@
('Sony Bravia TV', '2015 model', '2.4GHz'),
'wifi4|probe:0,1,50,221(0050f2,4),221(506f9a,10),221(506f9a,9),wps:BRAVIA_2015|assoc:0,1,50,45,127,221(000c43,6),221(0050f2,2),48,127,htcap:01ad,htagg:13,htmcs:0000ffff,extcap:00000a02':
('Sony Bravia TV', '2015 model', '2.4GHz'),
+ 'wifi4|probe:0,1,50,45,127,221(0050f2,4),221(506f9a,10),221(506f9a,9),htcap:11ef,htagg:13,htmcs:0000ffff,extcap:00,wps:BRAVIA_4K_2015|assoc:0,1,50,45,127,221(000c43,6),221(0050f2,2),48,127,htcap:008c,htagg:13,htmcs:0000ffff,extcap:00000a02':
+ ('Sony Bravia TV', '2015 model', '2.4GHz'),
'wifi4|probe:0,1,3,45,221(0050f2,8),191,htcap:016e,htagg:03,htmcs:000000ff,vhtcap:31800120,vhtrxmcs:0000fffc,vhttxmcs:0000fffc|assoc:0,1,33,36,48,70,45,221(0050f2,2),127,htcap:012c,htagg:03,htmcs:000000ff|oui:sony':
('Sony Xperia', 'Z Ultra', '5GHz'),
@@ -1077,6 +1145,21 @@
'wifi4|probe:0,1,50,221(001018,2)|assoc:0,1,48,50,221(001018,2)|os:tivo':
('TiVo', 'Series3 or Series4', '2.4GHz'),
+ 'wifi4|probe:0,1,50,45,221(0050f2,4),htcap:106e,htagg:13,htmcs:0000ffff,wps:Ralink_Wireless_Linux_Client|assoc:0,1,45,127,221(000c43,6),221(0050f2,2),48,htcap:000e,htagg:13,htmcs:0000ffff,extcap:00|oui:toshiba':
+ ('Toshiba Smart TV', '', '5GHz'),
+ 'wifi4|probe:0,1,221(0050f2,4),wps:Ralink_Wireless_Linux_Client|assoc:0,1,45,127,221(000c43,6),221(0050f2,2),48,htcap:000e,htagg:13,htmcs:0000ffff,extcap:00|oui:toshiba':
+ ('Toshiba Smart TV', '', '5GHz'),
+ 'wifi4|probe:0,1,50,221(0050f2,4),wps:Ralink_Wireless_Linux_Client|assoc:0,1,50,45,127,221(000c43,6),221(0050f2,2),48,htcap:000c,htagg:13,htmcs:0000ffff,extcap:01|oui:toshiba':
+ ('Toshiba Smart TV', '', '2.4GHz'),
+ 'wifi4|probe:0,1,50,45,127,221(0050f2,4),htcap:106e,htagg:13,htmcs:0000ffff,extcap:00,wps:Ralink_Wireless_Linux_Client|assoc:0,1,50,45,127,221(000c43,6),221(0050f2,2),48,htcap:000c,htagg:13,htmcs:0000ffff,extcap:01|oui:toshiba':
+ ('Toshiba Smart TV', '', '2.4GHz'),
+
+ # P602ui-B3
+ 'wifi4|probe:0,1,50,221(0050f2,4),wps:_|assoc:0,33,36,1,48,221(0050f2,2),45,127,htcap:106e,htagg:1f,htmcs:0000ffff,txpow:150d,extcap:0000000000000000|os:viziotv':
+ ('Vizio Smart TV', '', '5GHz'),
+ # P602ui-B3
+ 'wifi4|probe:0,1,50|assoc:0,33,36,1,48,221(0050f2,2),45,127,htcap:106e,htagg:1f,htmcs:0000ffff,txpow:150d,extcap:0000000000000000|os:viziotv':
+ ('Vizio Smart TV', '', '5GHz'),
'wifi4|probe:0,1,45,191,221(0050f2,4),221(506f9a,9),221(001018,2),htcap:01ef,htagg:17,htmcs:0000ffff,vhtcap:0f8159b2,vhtrxmcs:0000fffa,vhttxmcs:0000fffa,wps:_|assoc:0,1,33,36,48,45,191,221(001018,2),221(0050f2,2),htcap:01ef,htagg:17,htmcs:0000ffff,vhtcap:0f8159b2,vhtrxmcs:0000fffa,vhttxmcs:0000fffa,txpow:e002|oui:vizio':
('Vizio SmartCast TV', '', '2.4GHz'),
'wifi4|probe:0,1,50,221(0050f2,4),wps:Ralink_Wireless_Linux_Client|assoc:0,1,50,45,127,221(000c43,6),221(0050f2,2),48,htcap:000c,htagg:12,htmcs:000000ff,extcap:01000000|os:viziotv':
@@ -1091,6 +1174,12 @@
('Vizio Smart TV', '', '2.4GHz'),
'wifi4|probe:0,1,50,48|assoc:0,1,50,221(0050f2,2),45,51,127,48,htcap:012c,htagg:1b,htmcs:000000ff,extcap:01|os:viziotv':
('Vizio Smart TV', '', '2.4GHz'),
+ # P602ui-B3
+ 'wifi4|probe:0,1,50,221(0050f2,4),wps:_|assoc:0,1,50,48,221(0050f2,2),45,127,htcap:122c,htagg:1f,htmcs:0000ffff,extcap:0000000000000000|os:viziotv':
+ ('Vizio Smart TV', '', '2.4GHz'),
+ # P602ui-B3
+ 'wifi4|probe:0,1,50|assoc:0,1,50,48,221(0050f2,2),45,127,htcap:122c,htagg:1f,htmcs:0000ffff,extcap:0000000000000000|os:viziotv':
+ ('Vizio Smart TV', '', '2.4GHz'),
'wifi4|probe:0,1,3,45,221(0050f2,8),htcap:016e,htagg:03,htmcs:000000ff|assoc:0,1,33,36,45,221(0050f2,2),htcap:016e,htagg:03,htmcs:000000ff,txpow:110d|oui:vizio':
('Vizio Tablet', 'XR6P', '5GHz'),
@@ -1103,6 +1192,9 @@
'wifi4|probe:0,1,50,45,3,221(00904c,51),htcap:100c,htagg:19,htmcs:000000ff|assoc:0,1,48,50,45,221(00904c,51),221(0050f2,2),htcap:100c,htagg:19,htmcs:000000ff|os:wii':
('Wii-U', '', '2.4GHz'),
+ 'wifi4|probe:0,1,50,45,3,221(001018,2),221(00904c,51),htcap:1020,htagg:1a,htmcs:000000ff|assoc:0,1,33,36,48,50,45,221(001018,2),221(00904c,51),221(0050f2,2),htcap:1020,htagg:1a,htmcs:000000ff,txpow:1009|oui:wink':
+ ('Wink Hub', '', '2.4GHz'),
+
'wifi4|probe:0,1,50,45,3,221(001018,2),221(00904c,51),htcap:110c,htagg:19,htmcs:000000ff|assoc:0,1,48,50,45,221(001018,2),221(00904c,51),221(0050f2,2),htcap:110c,htagg:19,htmcs:000000ff|oui:withings':
('Withings Scale', '', '2.4GHz'),
@@ -1135,6 +1227,15 @@
'wifi4|probe:0,1,50,3,45,221(0050f2,8),htcap:012c,htagg:03,htmcs:000000ff|assoc:0,1,50,33,48,70,45,221(0050f2,2),htcap:012c,htagg:03,htmcs:000000ff,txpow:170d|oui:xiaomi':
('Xiaomi Redmi', '3', '2.4GHz'),
+ 'wifi4|probe:0,1,45,221(0050f2,8),191,127,htcap:016f,htagg:1f,htmcs:000000ff,vhtcap:33907132,vhtrxmcs:0186fffe,vhttxmcs:0186fffe,extcap:040000000000004080|assoc:0,1,33,36,48,70,45,221(0050f2,2),191,127,htcap:016f,htagg:1f,htmcs:000000ff,vhtcap:33907132,vhtrxmcs:0186fffe,vhttxmcs:0186fffe,txpow:1408,extcap:040000000000004080|oui:xiaomi':
+ ('Xiaomi Mi', '5', '5GHz'),
+ 'wifi4|probe:0,1,45,191,3,127,htcap:016f,htagg:df,htmcs:000000ff,vhtcap:33800132,vhtrxmcs:0186fffe,vhttxmcs:0186fffe,extcap:04000a020100004080|assoc:0,1,33,36,48,70,45,221(0050f2,2),191,127,htcap:016f,htagg:1f,htmcs:000000ff,vhtcap:33907132,vhtrxmcs:0186fffe,vhttxmcs:0186fffe,txpow:1408,extcap:040000000000004080|oui:xiaomi':
+ ('Xiaomi Mi', '5', '5GHz'),
+ 'wifi4|probe:0,1,50,45,191,3,127,htcap:016f,htagg:df,htmcs:000000ff,vhtcap:33800132,vhtrxmcs:0186fffe,vhttxmcs:0186fffe,extcap:04000a020100004080|assoc:0,1,50,33,48,70,45,221(0050f2,2),127,htcap:012d,htagg:1f,htmcs:000000ff,txpow:1408,extcap:040000000000000080|oui:xiaomi':
+ ('Xiaomi Mi', '5', '2.4GHz'),
+ 'wifi4|probe:0,1,50,3,45,221(0050f2,8),127,htcap:012d,htagg:1f,htmcs:000000ff,extcap:040000000000000080|assoc:0,1,50,33,48,70,45,221(0050f2,2),127,htcap:012d,htagg:1f,htmcs:000000ff,txpow:1408,extcap:040000000000000080|oui:xiaomi':
+ ('Xiaomi Mi', '5', '2.4GHz'),
+
'wifi4|probe:0,1,50,221(0050f2,4),221(506f9a,9),wps:Z820|assoc:0,1,50,45,48,127,221(0050f2,2),htcap:1172,htagg:03,htmcs:000000ff,extcap:01':
('ZTE Obsidian', '', '2.4GHz'),
}
diff --git a/waveguide/clientinfo.py b/waveguide/clientinfo.py
index 28cd643..8173084 100644
--- a/waveguide/clientinfo.py
+++ b/waveguide/clientinfo.py
@@ -30,6 +30,36 @@
try:
with open(os.path.join(FINGERPRINTS_DIR, mac)) as f:
signature = f.read()
- return ';'.join(taxonomy.identify_wifi_device(signature, mac))
+ (genus, species, perf) = taxonomy.identify_wifi_device(signature, mac)
+
+ # Preserve older output format of chipset;model;performance. We no
+ # longer track chipsets, but we output the leading ';' separator to
+ # maintain compatibility with the format.
+ #
+ # For example, in the old code:
+ # unknown: SHA:c1...7b;Unknown;802.11n n:2,w:40
+ # known: BCM4329;iPad (1st/2nd gen);802.11n n:1,w:20
+ #
+ # In the current code, in the unknown case:
+ # genus = 'SHA:c1...7b', species = 'Unknown', perf = '802.11n n:2,w:40'
+ # SHA:c1...7b;Unknown;802.11n n:2,w:40
+ #
+ # In the current code, known, with species information:
+ # genus = 'iPad', species = '(1st/2nd gen)', perf = '802.11n n:1,w:20'
+ # ;iPad (1st/2nd gen);802.11n n:1,w:20
+ #
+ # In the current code, known, no specific species:
+ # genus = 'Samsung Galaxy S6', species = '', perf = '802.11ac n:2,w:80'
+ # ;Samsung Galaxy S6;802.11ac n:2,w:80
+ # We don't want an extra space at the end of the model, so we need to be
+ # careful about a join of the empty species.
+ # ;Samsung Galaxy S6 ;802.11ac n:2,w:80
+
+ if genus.startswith('SHA:'):
+ return genus + ';' + species + ';' + perf
+ elif species:
+ return ';' + genus + ' ' + species + ';' + perf
+ else:
+ return ';' + genus + ';' + perf
except IOError:
return None
diff --git a/wifi/autochannel.py b/wifi/autochannel.py
index 51b4d00..c669c9a 100644
--- a/wifi/autochannel.py
+++ b/wifi/autochannel.py
@@ -65,6 +65,12 @@
% (band, autotype, width))
+def get_all_frequencies(band):
+ """Get all 802.11 frequencies for the given band."""
+ return get_permitted_frequencies(band, 'OVERLAP' if band == '2.4' else 'ANY',
+ '20').split()
+
+
def scan(interface, band, autotype, width):
"""Do an autochannel scan and return the recommended channel.
diff --git a/wifi/autochannel_test.py b/wifi/autochannel_test.py
index a53725f..7198cb5 100755
--- a/wifi/autochannel_test.py
+++ b/wifi/autochannel_test.py
@@ -22,5 +22,17 @@
wvtest.WVEXCEPT(ValueError, autochannel.get_permitted_frequencies, *case)
+@wvtest.wvtest
+def get_all_frequencies_test():
+ wvtest.WVPASSEQ(['2412', '2417', '2422', '2427', '2432', '2437', '2442',
+ '2447', '2452', '2457', '2462'],
+ autochannel.get_all_frequencies('2.4'))
+
+ wvtest.WVPASSEQ(['5180', '5200', '5220', '5240', '5745', '5765', '5785',
+ '5805', '5825', '5260', '5280', '5300', '5320', '5500',
+ '5520', '5540', '5560', '5580', '5660', '5680', '5700'],
+ autochannel.get_all_frequencies('5'))
+
+
if __name__ == '__main__':
wvtest.wvtest_main()
diff --git a/wifi/configs.py b/wifi/configs.py
index 3377a08..743fe10 100644
--- a/wifi/configs.py
+++ b/wifi/configs.py
@@ -6,6 +6,8 @@
import Crypto.Protocol.KDF
+# pylint: disable=g-bad-import-order
+import autochannel
import experiment
import utils
@@ -373,10 +375,13 @@
utils.validate_and_sanitize_bssid(opt.bssid))
network_block = make_network_block(network_block_lines)
+ freq_list = ','.join(autochannel.get_all_frequencies(opt.band))
+
lines = [
'ctrl_interface=/var/run/wpa_supplicant',
'ap_scan=1',
'autoscan=exponential:1:30',
+ 'freq_list=' + freq_list,
network_block
]
return '\n'.join(lines)
diff --git a/wifi/configs_test.py b/wifi/configs_test.py
index ab5d6c7..64e05c6 100755
--- a/wifi/configs_test.py
+++ b/wifi/configs_test.py
@@ -10,27 +10,36 @@
from wvtest import wvtest
+_FREQ_LIST = {
+ '2.4': '2412,2417,2422,2427,2432,2437,2442,2447,2452,2457,2462',
+ '5': ('5180,5200,5220,5240,5745,5765,5785,5805,5825,5260,5280,5300,5320,'
+ '5500,5520,5540,5560,5580,5660,5680,5700'),
+}
+
+
_WPA_SUPPLICANT_CONFIG = """ctrl_interface=/var/run/wpa_supplicant
ap_scan=1
autoscan=exponential:1:30
-network={
+freq_list={freq_list}
+network={{
\tssid="some ssid"
\t#psk="some passphrase"
\tpsk=41821f7ca3ea5d85beea7644ed7e0fefebd654177fa06c26fbdfdc3c599a317f
\tscan_ssid=1
-}
+}}
"""
_WPA_SUPPLICANT_CONFIG_BSSID = """ctrl_interface=/var/run/wpa_supplicant
ap_scan=1
autoscan=exponential:1:30
-network={
+freq_list={freq_list}
+network={{
\tssid="some ssid"
\t#psk="some passphrase"
\tpsk=41821f7ca3ea5d85beea7644ed7e0fefebd654177fa06c26fbdfdc3c599a317f
\tscan_ssid=1
\tbssid=12:34:56:78:90:ab
-}
+}}
"""
# pylint: disable=g-backslash-continuation
@@ -38,12 +47,13 @@
"""ctrl_interface=/var/run/wpa_supplicant
ap_scan=1
autoscan=exponential:1:30
-network={
+freq_list={freq_list}
+network={{
\tssid="some ssid"
\tkey_mgmt=NONE
\tscan_ssid=1
\tbssid=12:34:56:78:90:ab
-}
+}}
"""
@@ -54,24 +64,30 @@
"Can't test generate_wpa_supplicant_config without wpa_passphrase.")
return
- opt = FakeOptDict()
- config = configs.generate_wpa_supplicant_config(
- 'some ssid', 'some passphrase', opt)
- wvtest.WVPASSEQ(_WPA_SUPPLICANT_CONFIG, config)
+ for band in ('2.4', '5'):
+ opt = FakeOptDict()
+ opt.band = band
+ got = configs.generate_wpa_supplicant_config(
+ 'some ssid', 'some passphrase', opt)
+ want = _WPA_SUPPLICANT_CONFIG.format(freq_list=_FREQ_LIST[band])
+ wvtest.WVPASSEQ(want, got)
- opt.bssid = 'TotallyNotValid'
- wvtest.WVEXCEPT(utils.BinWifiException,
- configs.generate_wpa_supplicant_config,
- 'some ssid', 'some passphrase', opt)
+ opt.bssid = 'TotallyNotValid'
+ wvtest.WVEXCEPT(utils.BinWifiException,
+ configs.generate_wpa_supplicant_config,
+ 'some ssid', 'some passphrase', opt)
- opt.bssid = '12:34:56:78:90:Ab'
- config = configs.generate_wpa_supplicant_config(
- 'some ssid', 'some passphrase', opt)
- wvtest.WVPASSEQ(_WPA_SUPPLICANT_CONFIG_BSSID, config)
+ opt.bssid = '12:34:56:78:90:Ab'
+ got = configs.generate_wpa_supplicant_config(
+ 'some ssid', 'some passphrase', opt)
+ want = _WPA_SUPPLICANT_CONFIG_BSSID.format(freq_list=_FREQ_LIST[band])
+ wvtest.WVPASSEQ(want, got)
- config = configs.generate_wpa_supplicant_config(
- 'some ssid', None, opt)
- wvtest.WVPASSEQ(_WPA_SUPPLICANT_CONFIG_BSSID_UNSECURED, config)
+ got = configs.generate_wpa_supplicant_config(
+ 'some ssid', None, opt)
+ want = _WPA_SUPPLICANT_CONFIG_BSSID_UNSECURED.format(
+ freq_list=_FREQ_LIST[band])
+ wvtest.WVPASSEQ(want, got)
_PHY_INFO = """Wiphy phy0
diff --git a/wifi/quantenna.py b/wifi/quantenna.py
index 39dfabf..1408574 100755
--- a/wifi/quantenna.py
+++ b/wifi/quantenna.py
@@ -50,8 +50,8 @@
return None, None, None, None
-def _set_link_state(hif, state):
- subprocess.check_output(['ip', 'link', 'set', 'dev', hif, state])
+def _ifplugd_action(hif, state):
+ subprocess.check_output(['/etc/ifplugd/ifplugd.action', hif, state])
def _parse_scan_result(line):
@@ -145,7 +145,7 @@
_qcsapi('vlan_config', 'pcie0', 'trunk', vlan)
_qcsapi('block_bss', lif, 0)
- _set_link_state(hif, 'up')
+ _ifplugd_action(hif, 'up')
except:
stop_ap_wifi(opt)
raise
@@ -188,7 +188,7 @@
_qcsapi('vlan_config', 'pcie0', 'enable')
_qcsapi('vlan_config', 'pcie0', 'trunk', vlan)
- _set_link_state(hif, 'up')
+ _ifplugd_action(hif, 'up')
except:
stop_client_wifi(opt)
raise
@@ -207,7 +207,7 @@
except subprocess.CalledProcessError:
pass
- _set_link_state(hif, 'down')
+ _ifplugd_action(hif, 'down')
return True
@@ -223,7 +223,7 @@
except subprocess.CalledProcessError:
pass
- _set_link_state(hif, 'down')
+ _ifplugd_action(hif, 'down')
return True
diff --git a/wifi/wifi.py b/wifi/wifi.py
index b0ef7f9..8797633 100755
--- a/wifi/wifi.py
+++ b/wifi/wifi.py
@@ -542,9 +542,20 @@
('hostapd_cli', '-i', interface, 'status'), no_stdout=True) == 0
-def _is_wpa_supplicant_running(interface):
+def _wpa_cli(program, interface, command):
return utils.subprocess_quiet(
- ('wpa_cli', '-i', interface, 'status'), no_stdout=True) == 0
+ (program, '-i', interface, command), no_stdout=True) == 0
+
+
+def _is_wpa_supplicant_running(interface):
+ return _wpa_cli('wpa_cli', interface, 'status')
+
+
+def _reconfigure_wpa_supplicant(interface):
+ if not _wpa_cli('wpa_cli', interface, 'reconfigure'):
+ return False
+
+ return _wait_for_wpa_supplicant_to_associate(interface)
def _hostapd_debug_options():
@@ -653,6 +664,38 @@
return None
+def _wait_for_wpa_supplicant_to_associate(interface):
+ """Wait for wpa_supplicant to associate.
+
+ If it does not associate within a certain period of time, terminate it.
+
+ Args:
+ interface: The interface on which wpa_supplicant is running.
+
+ Raises:
+ BinWifiException: if wpa_supplicant fails to associate and
+ also cannot be stopped to cleanup after the failure.
+
+ Returns:
+ Whether wpa_supplicant associated within the timeout.
+ """
+ utils.log('Waiting for wpa_supplicant to connect')
+ for _ in xrange(100):
+ if _get_wpa_state(interface) == 'COMPLETED':
+ utils.log('ok')
+ return True
+ sys.stderr.write('.')
+ time.sleep(0.1)
+
+ utils.log('wpa_supplicant did not connect.')
+ if not _stop_wpa_supplicant(interface):
+ raise utils.BinWifiException(
+ "Couldn't stop wpa_supplicant after it failed to connect. "
+ "Consider killing it manually.")
+
+ return False
+
+
def _start_wpa_supplicant(interface, config_filename):
"""Starts a babysat wpa_supplicant.
@@ -704,21 +747,7 @@
else:
return False
- utils.log('Waiting for wpa_supplicant to connect')
- for _ in xrange(100):
- if _get_wpa_state(interface) == 'COMPLETED':
- utils.log('ok')
- return True
- sys.stderr.write('.')
- time.sleep(0.1)
-
- utils.log('wpa_supplicant did not connect.')
- if not _stop_wpa_supplicant(interface):
- raise utils.BinWifiException(
- "Couldn't stop wpa_supplicant after it failed to connect. "
- "Consider killing it manually.")
-
- return False
+ return _wait_for_wpa_supplicant_to_associate(interface)
def _maybe_restart_hostapd(interface, config, opt):
@@ -777,8 +806,7 @@
def _restart_hostapd(band):
"""Restart hostapd from previous options.
- Only used by _maybe_restart_wpa_supplicant, to restart hostapd after stopping
- it.
+ Only used by _set_wpa_supplicant_config, to restart hostapd after stopping it.
Args:
band: The band on which to restart hostapd.
@@ -797,7 +825,7 @@
_run(argv)
-def _maybe_restart_wpa_supplicant(interface, config, opt):
+def _set_wpa_supplicant_config(interface, config, opt):
"""Starts or restarts wpa_supplicant unless doing so would be a no-op.
The no-op case (i.e. wpa_supplicant is already running with an equivalent
@@ -826,11 +854,12 @@
except IOError:
pass
- if not _is_wpa_supplicant_running(interface):
+ already_running = _is_wpa_supplicant_running(interface)
+ if not already_running:
utils.log('wpa_supplicant not running yet, starting.')
elif current_config != config:
# TODO(rofrankel): Consider using wpa_cli reconfigure here.
- utils.log('wpa_supplicant config changed, restarting.')
+ utils.log('wpa_supplicant config changed, reconfiguring.')
elif opt.force_restart:
utils.log('Forced restart requested.')
forced = True
@@ -838,12 +867,12 @@
utils.log('wpa_supplicant-%s already configured and running', interface)
return True
- if not _stop_wpa_supplicant(interface):
- raise utils.BinWifiException("Couldn't stop wpa_supplicant")
-
if not forced:
utils.atomic_write(tmp_config_filename, config)
+ # TODO(rofrankel): Consider removing all the restart hostapd stuff when
+ # b/30140131 is resolved. hostapd seems to keep working without being
+ # restarted, at least on Camaro.
restart_hostapd = False
ap_interface = iw.find_interface_from_band(band, iw.INTERFACE_TYPE.ap,
opt.interface_suffix)
@@ -852,13 +881,15 @@
opt_without_persist = options.OptDict({})
opt_without_persist.persist = False
opt_without_persist.band = opt.band
- # Code review: Will AP and client always have the same suffix?
opt_without_persist.interface_suffix = opt.interface_suffix
if not stop_ap_wifi(opt_without_persist):
raise utils.BinWifiException(
"Couldn't stop hostapd to start wpa_supplicant.")
- if not _start_wpa_supplicant(interface, tmp_config_filename):
+ if already_running:
+ if not _reconfigure_wpa_supplicant(interface):
+ raise utils.BinWifiException('Failed to reconfigure wpa_supplicant.')
+ elif not _start_wpa_supplicant(interface, tmp_config_filename):
raise utils.BinWifiException(
'wpa_supplicant failed to start. Look at wpa_supplicant logs for '
'details.')
@@ -934,7 +965,7 @@
('ip', 'link', 'set', interface, 'address', mac_address))
wpa_config = configs.generate_wpa_supplicant_config(opt.ssid, psk, opt)
- if not _maybe_restart_wpa_supplicant(interface, wpa_config, opt):
+ if not _set_wpa_supplicant_config(interface, wpa_config, opt):
return False
return True