Merge "conman: Export status when trying to join WLAN fails."
diff --git a/cmds/Makefile b/cmds/Makefile
index 4f7db2b..24bc8a1 100644
--- a/cmds/Makefile
+++ b/cmds/Makefile
@@ -32,6 +32,7 @@
diskbench \
dnsck \
freemegs \
+ gfhd254_reboot \
gstatic \
http_bouncer \
ionice \
diff --git a/cmds/device_stats.proto b/cmds/device_stats.proto
index 30a344e..4f47b5e 100644
--- a/cmds/device_stats.proto
+++ b/cmds/device_stats.proto
@@ -17,5 +17,8 @@
// Device serial number.
optional string serial = 5;
+
+ // Public ipv6 address of onu
+ optional string ipv6 = 6;
};
diff --git a/cmds/gfhd254_reboot.c b/cmds/gfhd254_reboot.c
new file mode 100644
index 0000000..fdc7e32
--- /dev/null
+++ b/cmds/gfhd254_reboot.c
@@ -0,0 +1,65 @@
+// GFHD254 has a bug where software reset doesn't reset the entire
+// chip, some state in the SAGE engine isn't getting reset. This
+// drives a gpio that connects back to the chips own external reset
+// pin, resetting the chip with this pin works around the issue as
+// the SAGE engine is completely reset in this path.
+
+#include <fcntl.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#define REG_BASE 0xf0410000
+#define REG_SIZE 0x8000
+
+
+#define GPIO_DATA (0x7404 / 4)
+#define GPIO_IODIR (0x7408 / 4)
+#define CTRL_MUX_0 (0x0700 / 4)
+#define CTRL_MUX_1 (0x0704 / 4)
+
+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
+}
+
+// TODO(jnewlin): Revist this after the exact gpio being used
+// is settled on.
+
+int main() {
+ int fd = open("/dev/mem", O_RDWR);
+ volatile uint32_t* reg;
+
+ if (fd < 0) {
+ perror("mmap");
+ return 1;
+ }
+
+ reg = mmap_(NULL, REG_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED,
+ fd, REG_BASE);
+ if (reg == MAP_FAILED) {
+ perror("mmap");
+ return 1;
+ }
+
+ // Set the pin mux to gpio, value of zero selects gpio mode, this
+ // is the reset value so this is probably not required, but just
+ // in case.
+ reg[CTRL_MUX_0] &= ~((0xf << 8) | (0xf << 12)); // aon_gio2 and 3
+ reg[CTRL_MUX_1] &= ~(0xf << 4); // aon_gio9
+
+
+ // Set the direction to be an output and drive it low.
+ reg[GPIO_IODIR] &= ~((1 << 2) | (1 << 3) | (1 << 9));
+ reg[GPIO_DATA] &= ~((1 << 2) | (1 << 3) | (1 << 9));
+
+ return 0;
+}
diff --git a/cmds/statcatcher.cc b/cmds/statcatcher.cc
index 64db2b6..bfd7033 100644
--- a/cmds/statcatcher.cc
+++ b/cmds/statcatcher.cc
@@ -142,7 +142,8 @@
"onu_acs_contacted": %s,
"onu_acs_contact_time": "%lld",
"onu_uptime": %lld,
-"onu_serial": "%s"
+"onu_serial": "%s",
+"onu_ipv6": "%s"
})";
FILE *f = fopen(tmp_file.c_str(), "w");
if (!f) {
@@ -155,7 +156,8 @@
status.acs_contacted() ? "true" : "false",
status.acs_contact_time(),
status.uptime(),
- status.serial().c_str());
+ status.serial().c_str(),
+ status.ipv6().c_str());
fclose(f);
if (rename(tmp_file.c_str(), stat_file.c_str()) != 0) {
diff --git a/cmds/statpitcher.cc b/cmds/statpitcher.cc
index 412be5d..990666d 100644
--- a/cmds/statpitcher.cc
+++ b/cmds/statpitcher.cc
@@ -16,8 +16,10 @@
#include <fstream>
#include <iostream>
+#include <sstream>
#include <string>
#include <vector>
+#include <memory>
#include "device_stats.pb.h"
@@ -91,6 +93,71 @@
return static_cast<int64_t>(up);
}
+std::string IPAddress() {
+ std::ifstream infile;
+ infile.open("/proc/net/if_inet6");
+
+ if (!infile.good()) {
+ perror("error reading ipv6 from file");
+ exit(1);
+ }
+
+ std::string line;
+ int found = 0;
+ while (!infile.eof()) {
+ getline(infile, line);
+ // Want Ipv6 address on man interface
+ if (line.find("man") == std::string::npos) {
+ continue;
+ }
+ // Avoid local ipv6
+ if (line.substr(0, 4) == "0100" || // Discard prefix RFC 6666
+ line.substr(0, 2) == "fc" || // Unique local addresses
+ line.substr(0, 2) == "fd" ||
+ line.substr(0, 4) == "fe80" || // Link-local addresses
+ line.substr(0, 4) == "fec0") { // Old, deprecated local address range
+ continue;
+ }
+ found = 1;
+ break;
+ }
+
+ infile.close();
+ if (!found || line.size() < 32) {
+ perror("ipv6 address on man not found in file");
+ return "::1";
+ }
+
+ // Add colons
+ std::stringstream ipv6;
+ line = line.substr(0, 32);
+ for (unsigned int i = 0; i < line.size(); i++) {
+ if (i != 0 && i % 4 == 0) {
+ ipv6 << ':';
+ }
+ ipv6 << line[i];
+ }
+
+ // Format canonically
+ struct in6_addr ipv6_struct;
+ if (!inet_pton(AF_INET6, ipv6.str().c_str(), &ipv6_struct)) {
+ std::string errmsg = "unable to parse ipv6 address to inet_pton: " +
+ ipv6.str();
+ perror(errmsg.c_str());
+ exit(1);
+ }
+ char address[INET6_ADDRSTRLEN];
+ if (!inet_ntop(AF_INET6, &ipv6_struct, address, INET6_ADDRSTRLEN)) {
+ std::string errmsg = "unable to parse ipv6 address from inet_pton struct "
+ "created from: " + ipv6.str();
+ perror(errmsg.c_str());
+ exit(1);
+ }
+
+ std::string result(address);
+ return result;
+}
+
void MakePacket(std::vector<uint8_t>* pkt) {
devstatus::Status status;
@@ -101,6 +168,7 @@
status.set_acs_contact_time(acs_contact_time);
status.set_uptime(Uptime());
status.set_serial(serial_number);
+ status.set_ipv6(IPAddress());
pkt->resize(status.ByteSize());
status.SerializeToArray(&(*pkt)[0], status.ByteSize());
diff --git a/cmds/test-http_bouncer.sh b/cmds/test-http_bouncer.sh
index 4129d52..9cc0d9e 100755
--- a/cmds/test-http_bouncer.sh
+++ b/cmds/test-http_bouncer.sh
@@ -40,10 +40,6 @@
INPUTS[3]=$(printf "\n\n"; printf "$SENTINEL")
OUTPUTS[3]=$(printf "HTTP/1.0 302 Found\r\nLocation: $URL\r\n\r\n"; printf "$SENTINEL")
-INPUTS[4]=$(printf "GET /GIAG2.crl HTTP/1.0\r\nHost: pki.google.com\r\n\r\n"; printf "$SENTINEL")
-OUTPUTS[4]=$(curl "http://pki.google.com/GIAG2.crl"; printf "$SENTINEL")
-STRIP_HEADER[4]=1
-
WVSTART "http_bouncer test"
# fail with no arguments
@@ -59,10 +55,13 @@
i=0
while [ $i -lt ${#INPUTS[@]} ]; do
output=$(echo -n "${INPUTS[$i]}" | nc localhost $PORT; printf "$SENTINEL")
- if [ ${STRIP_HEADER[$i]} ]; then
- output=$(echo -n "$output" | sed '1,/^\r$/d')
- fi
-
WVPASSEQ "$output" "${OUTPUTS[$i]}"
i=$(expr $i + 1)
done
+
+# Make sure we can download a CRL even through the bouncer.
+# Some Internet Explorer versions will refuse to connect if we can't.
+WVPASS printf "GET /GIAG2.crl HTTP/1.0\r\nHost: pki.google.com\r\n\r\n" |\
+ nc localhost $PORT |\
+ sed '1,/^\r$/d' |\
+ openssl crl -inform DER
diff --git a/cmds/wifi_files.c b/cmds/wifi_files.c
index 49e77c6..1700cda 100644
--- a/cmds/wifi_files.c
+++ b/cmds/wifi_files.c
@@ -77,6 +77,11 @@
* client for a while longer than that.
*/
typedef struct client_state {
+ #define MAC_STR_LEN 18
+ char macstr[MAC_STR_LEN];
+ #define IFNAME_STR_LEN 16
+ char ifname[IFNAME_STR_LEN];
+
double inactive_since;
uint64_t rx_drop64;
@@ -106,14 +111,22 @@
uint32_t tx_failed;
uint32_t expected_mbps;
- int sample_index;
#define MAX_SAMPLE_INDEX 150
+ int rx_sample_index;
uint8_t rx_ht_mcs_samples[MAX_SAMPLE_INDEX];
uint8_t rx_vht_mcs_samples[MAX_SAMPLE_INDEX];
uint8_t rx_width_samples[MAX_SAMPLE_INDEX];
uint8_t rx_ht_nss_samples[MAX_SAMPLE_INDEX];
uint8_t rx_vht_nss_samples[MAX_SAMPLE_INDEX];
- uint8_t short_gi_samples[MAX_SAMPLE_INDEX];
+ uint8_t rx_short_gi_samples[MAX_SAMPLE_INDEX];
+
+ int tx_sample_index;
+ uint8_t tx_ht_mcs_samples[MAX_SAMPLE_INDEX];
+ uint8_t tx_vht_mcs_samples[MAX_SAMPLE_INDEX];
+ uint8_t tx_width_samples[MAX_SAMPLE_INDEX];
+ uint8_t tx_ht_nss_samples[MAX_SAMPLE_INDEX];
+ uint8_t tx_vht_nss_samples[MAX_SAMPLE_INDEX];
+ uint8_t tx_short_gi_samples[MAX_SAMPLE_INDEX];
/*
* Clients spend a lot of time mostly idle, where they
@@ -131,7 +144,14 @@
uint8_t rx_width;
uint8_t rx_ht_nss;
uint8_t rx_vht_nss;
- uint8_t short_gi;
+ uint8_t rx_short_gi;
+
+ uint8_t tx_ht_mcs;
+ uint8_t tx_vht_mcs;
+ uint8_t tx_width;
+ uint8_t tx_ht_nss;
+ uint8_t tx_vht_nss;
+ uint8_t tx_short_gi;
/* Track the largest value we've ever seen from this client. This
* shows client capabilities, even if current interference
@@ -141,7 +161,14 @@
uint8_t rx_max_width;
uint8_t rx_max_ht_nss;
uint8_t rx_max_vht_nss;
- uint8_t ever_short_gi;
+ uint8_t ever_rx_short_gi;
+
+ uint8_t tx_max_ht_mcs;
+ uint8_t tx_max_vht_mcs;
+ uint8_t tx_max_width;
+ uint8_t tx_max_ht_nss;
+ uint8_t tx_max_vht_nss;
+ uint8_t ever_tx_short_gi;
int8_t signal;
int8_t signal_avg;
@@ -153,11 +180,6 @@
uint8_t mfp:1;
uint8_t tdls_peer:1;
uint8_t preamble_length:1;
-
- #define MAC_STR_LEN 18
- char macstr[MAC_STR_LEN];
- #define IFNAME_STR_LEN 16
- char ifname[IFNAME_STR_LEN];
} client_state_t;
@@ -176,6 +198,16 @@
static FILE *wifi_info_handle = NULL;
+static void ClearClientStateCounters(client_state_t *state)
+{
+ char macstr[MAC_STR_LEN];
+
+ memcpy(macstr, state->macstr, sizeof(macstr));
+ memset(state, 0, sizeof(*state));
+ memcpy(state->macstr, macstr, sizeof(state->macstr));
+}
+
+
static int GetIfIndex(const char *ifname)
{
int fd;
@@ -349,7 +381,7 @@
}
-static void GetRxMCS(struct nlattr *attr,
+static void GetMCS(struct nlattr *attr,
int *mcs, int *vht_mcs, int *width, int *short_gi, int *vht_nss)
{
int w160 = 0, w80_80 = 0, w80 = 0, w40 = 0;
@@ -418,7 +450,7 @@
}
-static int RxHtMcsToNss(int rxmcs)
+static int HtMcsToNss(int rxmcs)
{
/* https://en.wikipedia.org/wiki/IEEE_802.11n-2009 */
switch(rxmcs) {
@@ -488,6 +520,12 @@
mac = (uint8_t *)nla_data(tb[NL80211_ATTR_MAC]);
state = FindClientState(mac);
+
+ if (strcasecmp(state->ifname, ifname) != 0) {
+ /* Client moved from one interface to another */
+ ClearClientStateCounters(state);
+ }
+
state->last_seen = monotime();
snprintf(state->ifname, sizeof(state->ifname), "%s", ifname);
@@ -502,20 +540,20 @@
}
if (si[NL80211_STA_INFO_RX_BITRATE]) {
- int rx_ht_mcs=0, rx_vht_mcs=0, rx_vht_nss=0, rx_width=0, short_gi=0;
+ int rx_ht_mcs=0, rx_vht_mcs=0, rx_vht_nss=0, rx_width=0, rx_short_gi=0;
int ht_nss;
- int n = state->sample_index + 1;
+ int n = state->rx_sample_index + 1;
if (n >= MAX_SAMPLE_INDEX) n = 0;
state->rx_bitrate = GetBitrate(si[NL80211_STA_INFO_RX_BITRATE]);
- GetRxMCS(si[NL80211_STA_INFO_RX_BITRATE], &rx_ht_mcs, &rx_vht_mcs,
- &rx_width, &short_gi, &rx_vht_nss);
+ GetMCS(si[NL80211_STA_INFO_RX_BITRATE], &rx_ht_mcs, &rx_vht_mcs,
+ &rx_width, &rx_short_gi, &rx_vht_nss);
state->rx_ht_mcs_samples[n] = rx_ht_mcs;
if (rx_ht_mcs > state->rx_max_ht_mcs) state->rx_max_ht_mcs = rx_ht_mcs;
- ht_nss = RxHtMcsToNss(rx_ht_mcs);
+ ht_nss = HtMcsToNss(rx_ht_mcs);
state->rx_ht_nss_samples[n] = ht_nss;
if (ht_nss > state->rx_max_ht_nss) state->rx_max_ht_nss = ht_nss;
@@ -525,13 +563,13 @@
state->rx_vht_nss_samples[n] = rx_vht_nss;
if (rx_vht_nss > state->rx_max_vht_nss) state->rx_max_vht_nss = rx_vht_nss;
- state->short_gi_samples[n] = short_gi;
- if (short_gi) state->ever_short_gi = 1;
+ state->rx_short_gi_samples[n] = rx_short_gi;
+ if (rx_short_gi) state->ever_rx_short_gi = 1;
state->rx_width_samples[n] = rx_width;
if (rx_width > state->rx_max_width) state->rx_max_width = rx_width;
- state->sample_index = n;
+ state->rx_sample_index = n;
}
if (si[NL80211_STA_INFO_RX_BYTES]) {
uint32_t last_rx_bytes = state->rx_bytes;
@@ -544,7 +582,36 @@
state->rx_packets64 += (state->rx_packets - last_rx_packets);
}
if (si[NL80211_STA_INFO_TX_BITRATE]) {
+ int tx_ht_mcs=0, tx_vht_mcs=0, tx_vht_nss=0, tx_width=0, tx_short_gi=0;
+ int ht_nss;
+ int n = state->tx_sample_index + 1;
+
+ if (n >= MAX_SAMPLE_INDEX) n = 0;
+
state->tx_bitrate = GetBitrate(si[NL80211_STA_INFO_TX_BITRATE]);
+ GetMCS(si[NL80211_STA_INFO_TX_BITRATE], &tx_ht_mcs, &tx_vht_mcs,
+ &tx_width, &tx_short_gi, &tx_vht_nss);
+
+ state->tx_ht_mcs_samples[n] = tx_ht_mcs;
+ if (tx_ht_mcs > state->tx_max_ht_mcs) state->tx_max_ht_mcs = tx_ht_mcs;
+
+ ht_nss = HtMcsToNss(tx_ht_mcs);
+ state->tx_ht_nss_samples[n] = ht_nss;
+ if (ht_nss > state->tx_max_ht_nss) state->tx_max_ht_nss = ht_nss;
+
+ state->tx_vht_mcs_samples[n] = tx_vht_mcs;
+ if (tx_vht_mcs > state->tx_max_vht_mcs) state->tx_max_vht_mcs = tx_vht_mcs;
+
+ state->tx_vht_nss_samples[n] = tx_vht_nss;
+ if (tx_vht_nss > state->tx_max_vht_nss) state->tx_max_vht_nss = tx_vht_nss;
+
+ state->tx_short_gi_samples[n] = tx_short_gi;
+ if (tx_short_gi) state->ever_tx_short_gi = 1;
+
+ state->tx_width_samples[n] = tx_width;
+ if (tx_width > state->tx_max_width) state->tx_max_width = tx_width;
+
+ state->tx_sample_index = n;
}
if (si[NL80211_STA_INFO_TX_BYTES]) {
uint32_t last_tx_bytes = state->tx_bytes;
@@ -655,7 +722,9 @@
client_state_t *state = (client_state_t *)value;
int i;
uint8_t rx_ht_mcs=0, rx_vht_mcs=0, rx_width=0, rx_ht_nss=0;
- uint8_t rx_vht_nss=0, short_gi=0;
+ uint8_t rx_vht_nss=0, rx_short_gi=0;
+ uint8_t tx_ht_mcs=0, tx_vht_mcs=0, tx_width=0, tx_ht_nss=0;
+ uint8_t tx_vht_nss=0, tx_short_gi=0;
for (i = 0; i < MAX_SAMPLE_INDEX; ++i) {
if (state->rx_ht_mcs_samples[i] > rx_ht_mcs) {
@@ -673,8 +742,27 @@
if (state->rx_vht_nss_samples[i] > rx_vht_nss) {
rx_vht_nss = state->rx_vht_nss_samples[i];
}
- if (state->short_gi_samples[i] > short_gi) {
- short_gi = state->short_gi_samples[i];
+ if (state->rx_short_gi_samples[i] > rx_short_gi) {
+ rx_short_gi = state->rx_short_gi_samples[i];
+ }
+
+ if (state->tx_ht_mcs_samples[i] > tx_ht_mcs) {
+ tx_ht_mcs = state->tx_ht_mcs_samples[i];
+ }
+ if (state->tx_vht_mcs_samples[i] > tx_vht_mcs) {
+ tx_vht_mcs = state->tx_vht_mcs_samples[i];
+ }
+ if (state->tx_width_samples[i] > tx_width) {
+ tx_width = state->tx_width_samples[i];
+ }
+ if (state->tx_ht_nss_samples[i] > tx_ht_nss) {
+ tx_ht_nss = state->tx_ht_nss_samples[i];
+ }
+ if (state->tx_vht_nss_samples[i] > tx_vht_nss) {
+ tx_vht_nss = state->tx_vht_nss_samples[i];
+ }
+ if (state->tx_short_gi_samples[i] > tx_short_gi) {
+ tx_short_gi = state->tx_short_gi_samples[i];
}
}
@@ -683,7 +771,14 @@
state->rx_width = rx_width;
state->rx_ht_nss = rx_ht_nss;
state->rx_vht_nss = rx_vht_nss;
- state->short_gi = short_gi;
+ state->rx_short_gi = rx_short_gi;
+
+ state->tx_ht_mcs = tx_ht_mcs;
+ state->tx_vht_mcs = tx_vht_mcs;
+ state->tx_width = tx_width;
+ state->tx_ht_nss = tx_ht_nss;
+ state->tx_vht_nss = tx_vht_nss;
+ state->tx_short_gi = tx_short_gi;
}
@@ -738,8 +833,8 @@
fprintf(f, " \"rx max vht_nss\": %u,\n", state->rx_max_vht_nss);
#define BOOL(x) (x ? "true" : "false")
- fprintf(f, " \"rx SHORT_GI\": %s,\n", BOOL(state->short_gi));
- fprintf(f, " \"rx SHORT_GI seen\": %s,\n", BOOL(state->ever_short_gi));
+ fprintf(f, " \"rx SHORT_GI\": %s,\n", BOOL(state->rx_short_gi));
+ fprintf(f, " \"rx SHORT_GI seen\": %s,\n", BOOL(state->ever_rx_short_gi));
#undef BOOL
fprintf(f, " \"signal\": %hhd,\n", state->signal);
@@ -797,6 +892,8 @@
"%s %s %ld %" PRIu64 ",%" PRIu64 ",%" PRIu64 ",%" PRIu64 ",%" PRIu64
" %c,%hhd,%hhd,%u,%u,%u,%u,%u,%d"
" %u,%u,%u,%u,%u,%d"
+ " %u,%u,%u,%u,%u,%d"
+ " %u,%u,%u,%u,%u,%d"
"\n",
state->macstr, state->ifname,
((mono_now - state->last_seen) + (state->inactive_msec / 1000)),
@@ -810,12 +907,21 @@
state->signal, state->signal_avg,
state->rx_ht_mcs, state->rx_ht_nss,
state->rx_vht_mcs, state->rx_vht_nss,
- state->rx_width, state->short_gi,
+ state->rx_width, state->rx_short_gi,
/* information about the maximum we've ever seen from this client. */
state->rx_max_ht_mcs, state->rx_max_ht_nss,
state->rx_max_vht_mcs, state->rx_max_vht_nss,
- state->rx_max_width, state->ever_short_gi);
+ state->rx_max_width, state->ever_rx_short_gi,
+
+ state->tx_ht_mcs, state->tx_ht_nss,
+ state->tx_vht_mcs, state->tx_vht_nss,
+ state->tx_width, state->tx_short_gi,
+
+ /* information about the maximum we've ever seen from this client. */
+ state->tx_max_ht_mcs, state->tx_max_ht_nss,
+ state->tx_max_vht_mcs, state->tx_max_vht_nss,
+ state->tx_max_width, state->ever_tx_short_gi);
}
@@ -878,8 +984,9 @@
if ((data[i] <= 0x1f) || !isprint(data[i])) {
fprintf(f, "\\u00%02x", data[i]);
} else {
- fprintf(f, "%c", data[i]); break;
+ fprintf(f, "%c", data[i]);
}
+ break;
}
}
}
diff --git a/cmds/wifi_files_test.c b/cmds/wifi_files_test.c
index 9d48dd1..902cd73 100644
--- a/cmds/wifi_files_test.c
+++ b/cmds/wifi_files_test.c
@@ -50,6 +50,27 @@
}
+void testPrintSsidEscapedQuoteBackslash()
+{
+ FILE *f = tmpfile();
+ char buf[32];
+ const uint8_t ssid[] = {'"', '\\'}; /* not NUL terminated. */
+ const uint8_t expected[] = {'\\', '"', '\\', '\\'};
+
+ printf("Testing \"%s\" in %s:\n", __FUNCTION__, __FILE__);
+ memset(buf, 0, sizeof(buf));
+ TEST_ASSERT(f != NULL);
+ print_ssid_escaped(f, sizeof(ssid), ssid);
+ fflush(f);
+ rewind(f);
+ TEST_ASSERT(fread(buf, 1, sizeof(buf), f) > 0);
+ printf("%s\n", buf);
+ TEST_ASSERT(memcmp(buf, expected, sizeof(expected)) == 0);
+ fclose(f);
+ printf("! %s:%d\t%s\tok\n", __FILE__, __LINE__, __FUNCTION__);
+}
+
+
void testFrequencyToChannel()
{
printf("Testing \"%s\" in %s:\n", __FUNCTION__, __FILE__);
@@ -171,6 +192,7 @@
clients = g_hash_table_new(g_str_hash, g_str_equal);
testPrintSsidEscaped();
+ testPrintSsidEscapedQuoteBackslash();
testFrequencyToChannel();
testClientStateToJson();
testAgeOutClients();
diff --git a/conman/connection_manager.py b/conman/connection_manager.py
index ff47820..3808458 100755
--- a/conman/connection_manager.py
+++ b/conman/connection_manager.py
@@ -213,6 +213,7 @@
COMMAND_FILE_REGEXP = WLAN_FILE_REGEXP_FMT % COMMAND_FILE_PREFIX
ACCESS_POINT_FILE_REGEXP = WLAN_FILE_REGEXP_FMT % ACCESS_POINT_FILE_PREFIX
GATEWAY_FILE_PREFIX = 'gateway.'
+ SUBNET_FILE_PREFIX = 'subnet.'
MOCA_NODE_FILE_PREFIX = 'node'
WIFI_SETCLIENT = ['wifi', 'setclient']
IFUP = ['ifup']
@@ -296,6 +297,7 @@
self._wpa_control_interface)
for path, prefix in ((self._tmp_dir, self.GATEWAY_FILE_PREFIX),
+ (self._tmp_dir, self.SUBNET_FILE_PREFIX),
(self._interface_status_dir, ''),
(self._moca_tmp_dir, self.MOCA_NODE_FILE_PREFIX),
(self._config_dir, self.COMMAND_FILE_PREFIX)):
@@ -584,7 +586,7 @@
"""Update the contents of /tmp/hosts."""
lowest_metric_interface = None
for ifc in [self.bridge] + self.wifi:
- route = ifc.current_route()
+ route = ifc.current_routes().get('default', None)
if route:
metric = route.get('metric', 0)
# Skip temporary connection_check routes.
@@ -690,6 +692,14 @@
logging.info('Received gateway %r for interface %s', contents,
ifc.name)
+ if filename.startswith(self.SUBNET_FILE_PREFIX):
+ interface_name = filename.split(self.SUBNET_FILE_PREFIX)[-1]
+ ifc = self.interface_by_name(interface_name)
+ if ifc:
+ ifc.set_subnet(contents)
+ logging.info('Received subnet %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)
if match:
diff --git a/conman/connection_manager_test.py b/conman/connection_manager_test.py
index 1f90f96..271cac7 100755
--- a/conman/connection_manager_test.py
+++ b/conman/connection_manager_test.py
@@ -208,11 +208,12 @@
#
# 1) Write an interface status file.
# 2) Call run-dhclient, which would call dhclient-script, which would
- # write a gateway file.
+ # call ipapply, which would write gateway and subnet files.
#
# Fake both of these things instead.
self.write_interface_status_file('1')
self.write_gateway_file()
+ self.write_subnet_file()
def stop_client(self):
client_was_up = self.client_up
@@ -233,6 +234,13 @@
# This value doesn't matter to conman, so it's fine to hard code it here.
f.write('192.168.1.1')
+ def write_subnet_file(self):
+ subnet_file = os.path.join(self.tmp_dir,
+ self.subnet_file_prefix + self.wifi.name)
+ with open(subnet_file, 'w') as f:
+ # This value doesn't matter to conman, so it's fine to hard code it here.
+ f.write('192.168.1.0/24')
+
def write_interface_status_file(self, value):
status_file = os.path.join(self.interface_status_dir, self.wifi.name)
with open(status_file, 'w') as f:
@@ -374,6 +382,7 @@
wlan_configuration.tmp_dir = self._tmp_dir
wlan_configuration.interface_status_dir = self._interface_status_dir
wlan_configuration.gateway_file_prefix = self.GATEWAY_FILE_PREFIX
+ wlan_configuration.subnet_file_prefix = self.SUBNET_FILE_PREFIX
super(ConnectionManager, self)._update_wlan_configuration(
wlan_configuration)
@@ -394,6 +403,8 @@
if up and not dhcp_failure:
self.write_gateway_file('br0' if interface_name in ('eth0', 'moca0')
else interface_name)
+ self.write_subnet_file('br0' if interface_name in ('eth0', 'moca0')
+ else interface_name)
def _binwifi(self, *command):
super(ConnectionManager, self)._binwifi(*command)
@@ -438,6 +449,13 @@
# This value doesn't matter to conman, so it's fine to hard code it here.
f.write('192.168.1.1')
+ def write_subnet_file(self, interface_name):
+ subnet_file = os.path.join(self._tmp_dir,
+ self.SUBNET_FILE_PREFIX + interface_name)
+ with open(subnet_file, 'w') as f:
+ # This value doesn't matter to conman, so it's fine to hard code it here.
+ f.write('192.168.1.0/24')
+
def write_interface_status_file(self, interface_name, value):
status_file = os.path.join(self._interface_status_dir, interface_name)
with open(status_file, 'w') as f:
@@ -616,10 +634,10 @@
c.run_once()
wvtest.WVPASS(c.acs())
wvtest.WVPASS(c.internet())
- wvtest.WVPASS(c.bridge.current_route())
+ wvtest.WVPASS(c.bridge.current_routes())
wvtest.WVPASS(os.path.exists(acs_autoprov_filepath))
for wifi in c.wifi:
- wvtest.WVFAIL(wifi.current_route())
+ wvtest.WVFAIL(wifi.current_routes_normal_testonly())
wvtest.WVFAIL(c.has_status_files([status.P.CONNECTED_TO_WLAN,
status.P.HAVE_CONFIG]))
@@ -628,7 +646,7 @@
c.run_once()
wvtest.WVFAIL(c.acs())
wvtest.WVFAIL(c.internet())
- wvtest.WVFAIL(c.bridge.current_route())
+ wvtest.WVFAIL(c.bridge.current_routes_normal_testonly())
wvtest.WVFAIL(os.path.exists(acs_autoprov_filepath))
wvtest.WVFAIL(c.has_status_files([status.P.CAN_REACH_ACS,
status.P.CAN_REACH_INTERNET]))
@@ -638,35 +656,35 @@
c.run_once()
wvtest.WVPASS(c.acs())
wvtest.WVPASS(c.internet())
- wvtest.WVPASS(c.bridge.current_route())
+ wvtest.WVPASS(c.bridge.current_routes())
# Bring up ethernet, access via both moca and ethernet.
c.set_ethernet(True)
c.run_once()
wvtest.WVPASS(c.acs())
wvtest.WVPASS(c.internet())
- wvtest.WVPASS(c.bridge.current_route())
+ wvtest.WVPASS(c.bridge.current_routes())
# Bring down moca, still have access via ethernet.
c.set_moca(False)
c.run_once()
wvtest.WVPASS(c.acs())
wvtest.WVPASS(c.internet())
- wvtest.WVPASS(c.bridge.current_route())
+ wvtest.WVPASS(c.bridge.current_routes())
# The bridge interfaces are up, but they can't reach anything.
c.bridge.set_connection_check_result('fail')
c.run_until_interface_update()
wvtest.WVFAIL(c.acs())
wvtest.WVFAIL(c.internet())
- wvtest.WVFAIL(c.bridge.current_route())
+ wvtest.WVFAIL(c.bridge.current_routes_normal_testonly())
# Now c connects to a restricted network.
c.bridge.set_connection_check_result('restricted')
c.run_until_interface_update()
wvtest.WVPASS(c.acs())
wvtest.WVFAIL(c.internet())
- wvtest.WVPASS(c.bridge.current_route())
+ wvtest.WVPASS(c.bridge.current_routes())
# Now the wired connection goes away.
c.set_ethernet(False)
@@ -674,7 +692,9 @@
c.run_once()
wvtest.WVFAIL(c.acs())
wvtest.WVFAIL(c.internet())
- wvtest.WVFAIL(c.bridge.current_route())
+ # We have no links, so we should have no routes (not even low priority ones),
+ # and /tmp/hosts should only contain a line for localhost.
+ wvtest.WVFAIL(c.bridge.current_routes())
check_tmp_hosts('127.0.0.1 localhost')
# Now there are some scan results.
@@ -696,7 +716,7 @@
wvtest.WVPASS(c.acs())
wvtest.WVPASS(c.internet())
wvtest.WVFAIL(c.client_up(band))
- wvtest.WVPASS(c.wifi_for_band(band).current_route())
+ wvtest.WVPASS(c.wifi_for_band(band).current_routes())
wvtest.WVPASSEQ(c.log_upload_count, 1)
# Disable scan results again.
c.interface_with_scan_results = None
@@ -710,7 +730,7 @@
c.disable_access_point(band)
c.run_once()
wvtest.WVPASS(c.client_up(band))
- wvtest.WVPASS(c.wifi_for_band(band).current_route())
+ wvtest.WVPASS(c.wifi_for_band(band).current_routes())
wvtest.WVPASS(c.has_status_files([status.P.CONNECTED_TO_WLAN]))
# Kill wpa_supplicant. conman should restart it.
@@ -751,7 +771,7 @@
c.disable_access_point(band)
c.run_once()
wvtest.WVPASS(c.client_up(band))
- wvtest.WVPASS(c.wifi_for_band(band).current_route())
+ wvtest.WVPASS(c.wifi_for_band(band).current_routes())
wvtest.WVPASS(c.has_status_files([status.P.CONNECTED_TO_WLAN]))
# Now enable the AP. Since we have no wired connection, this should have no
@@ -759,8 +779,8 @@
c.enable_access_point(band)
c.run_once()
wvtest.WVPASS(c.client_up(band))
- wvtest.WVPASS(c.wifi_for_band(band).current_route())
- wvtest.WVFAIL(c.bridge.current_route())
+ wvtest.WVPASS(c.wifi_for_band(band).current_routes())
+ wvtest.WVFAIL(c.bridge.current_routes_normal_testonly())
c.run_until_interface_update()
check_tmp_hosts('192.168.1.100 %s\n127.0.0.1 localhost' % hostname)
@@ -772,8 +792,8 @@
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())
+ wvtest.WVFAIL(c.wifi_for_band(band).current_routes())
+ wvtest.WVPASS(c.bridge.current_routes())
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
@@ -785,8 +805,8 @@
c.run_once()
wvtest.WVFAIL(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())
+ wvtest.WVFAIL(c.wifi_for_band(band).current_routes_normal_testonly())
+ wvtest.WVPASS(c.bridge.current_routes())
wvtest.WVFAIL(c.has_status_files([status.P.HAVE_CONFIG]))
# Now move it back, and the AP should come back.
@@ -794,8 +814,8 @@
c.run_once()
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())
+ wvtest.WVFAIL(c.wifi_for_band(band).current_routes_normal_testonly())
+ wvtest.WVPASS(c.bridge.current_routes())
# Now delete the config and bring down the bridge and make sure we reprovision
# via the last working BSS.
@@ -805,7 +825,9 @@
c.run_until_interface_update()
wvtest.WVFAIL(c.acs())
wvtest.WVFAIL(c.internet())
- check_tmp_hosts('127.0.0.1 localhost')
+ # We still have a link and might be wrong about the connection_check, so
+ # /tmp/hosts should still contain a line for this hostname.
+ check_tmp_hosts('192.168.1.101 %s\n127.0.0.1 localhost' % hostname)
# 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,
@@ -979,11 +1001,11 @@
c.run_once()
wvtest.WVPASS(c.access_point_up('2.4'))
wvtest.WVPASS(c.access_point_up('5'))
- wvtest.WVPASS(c.bridge.current_route())
+ wvtest.WVPASS(c.bridge.current_routes())
wvtest.WVFAIL(c.client_up('2.4'))
wvtest.WVFAIL(c.client_up('5'))
- wvtest.WVFAIL(c.wifi_for_band('2.4').current_route())
- wvtest.WVFAIL(c.wifi_for_band('5').current_route())
+ wvtest.WVFAIL(c.wifi_for_band('2.4').current_routes_normal_testonly())
+ wvtest.WVFAIL(c.wifi_for_band('5').current_routes_normal_testonly())
# Disable the 2.4 GHz AP, make sure the 5 GHz AP stays up. 2.4 GHz should
# join the WLAN.
@@ -992,9 +1014,9 @@
wvtest.WVFAIL(c.access_point_up('2.4'))
wvtest.WVPASS(c.access_point_up('5'))
wvtest.WVPASS(c.client_up('2.4'))
- wvtest.WVPASS(c.bridge.current_route())
- wvtest.WVPASS(c.wifi_for_band('2.4').current_route())
- wvtest.WVFAIL(c.wifi_for_band('5').current_route())
+ wvtest.WVPASS(c.bridge.current_routes())
+ wvtest.WVPASS(c.wifi_for_band('2.4').current_routes())
+ wvtest.WVFAIL(c.wifi_for_band('5').current_routes_normal_testonly())
# Delete the 2.4 GHz WLAN configuration; it should leave the WLAN but nothing
# else should change.
@@ -1003,9 +1025,9 @@
wvtest.WVFAIL(c.access_point_up('2.4'))
wvtest.WVPASS(c.access_point_up('5'))
wvtest.WVFAIL(c.client_up('2.4'))
- wvtest.WVPASS(c.bridge.current_route())
- wvtest.WVFAIL(c.wifi_for_band('2.4').current_route())
- wvtest.WVFAIL(c.wifi_for_band('5').current_route())
+ wvtest.WVPASS(c.bridge.current_routes())
+ wvtest.WVFAIL(c.wifi_for_band('2.4').current_routes_normal_testonly())
+ wvtest.WVFAIL(c.wifi_for_band('5').current_routes_normal_testonly())
# Disable the wired connection and remove the WLAN configurations. Both
# radios should scan. Wait for 5 GHz to scan, then enable scan results for
@@ -1014,18 +1036,18 @@
c.set_ethernet(False)
c.run_once()
wvtest.WVFAIL(c.acs())
- wvtest.WVFAIL(c.bridge.current_route())
- wvtest.WVFAIL(c.wifi_for_band('2.4').current_route())
- wvtest.WVFAIL(c.wifi_for_band('5').current_route())
+ wvtest.WVFAIL(c.bridge.current_routes_normal_testonly())
+ wvtest.WVFAIL(c.wifi_for_band('2.4').current_routes_normal_testonly())
+ wvtest.WVFAIL(c.wifi_for_band('5').current_routes_normal_testonly())
# The 5 GHz scan has no results.
c.run_until_scan('5')
c.run_once()
c.run_until_interface_update()
wvtest.WVFAIL(c.acs())
- wvtest.WVFAIL(c.bridge.current_route())
- wvtest.WVFAIL(c.wifi_for_band('2.4').current_route())
- wvtest.WVFAIL(c.wifi_for_band('5').current_route())
+ wvtest.WVFAIL(c.bridge.current_routes_normal_testonly())
+ wvtest.WVFAIL(c.wifi_for_band('2.4').current_routes_normal_testonly())
+ wvtest.WVFAIL(c.wifi_for_band('5').current_routes_normal_testonly())
# The next 2.4 GHz scan will have results.
c.interface_with_scan_results = c.wifi_for_band('2.4').name
@@ -1035,9 +1057,9 @@
c.run_once()
c.run_until_interface_update()
wvtest.WVPASS(c.acs())
- 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())
+ wvtest.WVFAIL(c.bridge.current_routes_normal_testonly())
+ wvtest.WVPASS(c.wifi_for_band('2.4').current_routes())
+ wvtest.WVFAIL(c.wifi_for_band('5').current_routes_normal_testonly())
c.run_once()
wvtest.WVPASSEQ(c.log_upload_count, 1)
@@ -1085,9 +1107,9 @@
c.run_once()
wvtest.WVFAIL(c.access_point_up('2.4'))
wvtest.WVPASS(c.access_point_up('5'))
- wvtest.WVPASS(c.bridge.current_route())
- wvtest.WVFAIL(c.wifi_for_band('2.4').current_route())
- wvtest.WVFAIL(c.wifi_for_band('5').current_route())
+ wvtest.WVPASS(c.bridge.current_routes())
+ wvtest.WVFAIL(c.wifi_for_band('2.4').current_routes_normal_testonly())
+ wvtest.WVFAIL(c.wifi_for_band('5').current_routes_normal_testonly())
# Disable the 2.4 GHz AP; nothing should change. The 2.4 GHz client should
# not be up because the same radio is being used to run a 5 GHz AP.
@@ -1096,9 +1118,9 @@
wvtest.WVFAIL(c.access_point_up('2.4'))
wvtest.WVPASS(c.access_point_up('5'))
wvtest.WVFAIL(c.client_up('2.4'))
- wvtest.WVPASS(c.bridge.current_route())
- wvtest.WVFAIL(c.wifi_for_band('2.4').current_route())
- wvtest.WVFAIL(c.wifi_for_band('5').current_route())
+ wvtest.WVPASS(c.bridge.current_routes())
+ wvtest.WVFAIL(c.wifi_for_band('2.4').current_routes_normal_testonly())
+ wvtest.WVFAIL(c.wifi_for_band('5').current_routes_normal_testonly())
# Delete the 2.4 GHz WLAN configuration; nothing should change.
c.delete_wlan_config('2.4')
@@ -1106,9 +1128,9 @@
wvtest.WVFAIL(c.access_point_up('2.4'))
wvtest.WVPASS(c.access_point_up('5'))
wvtest.WVFAIL(c.client_up('2.4'))
- wvtest.WVPASS(c.bridge.current_route())
- wvtest.WVFAIL(c.wifi_for_band('2.4').current_route())
- wvtest.WVFAIL(c.wifi_for_band('5').current_route())
+ wvtest.WVPASS(c.bridge.current_routes())
+ wvtest.WVFAIL(c.wifi_for_band('2.4').current_routes_normal_testonly())
+ wvtest.WVFAIL(c.wifi_for_band('5').current_routes_normal_testonly())
# Disable the wired connection and remove the WLAN configurations. There
# should be a single scan that leads to ACS access. (It doesn't matter which
@@ -1118,9 +1140,9 @@
c.set_ethernet(False)
c.run_once()
wvtest.WVFAIL(c.acs())
- wvtest.WVFAIL(c.bridge.current_route())
- wvtest.WVFAIL(c.wifi_for_band('2.4').current_route())
- wvtest.WVFAIL(c.wifi_for_band('5').current_route())
+ wvtest.WVFAIL(c.bridge.current_routes_normal_testonly())
+ wvtest.WVFAIL(c.wifi_for_band('2.4').current_routes_normal_testonly())
+ wvtest.WVFAIL(c.wifi_for_band('5').current_routes_normal_testonly())
# The scan will have results that will lead to ACS access.
c.interface_with_scan_results = c.wifi_for_band('2.4').name
@@ -1129,9 +1151,9 @@
c.run_once()
c.run_until_interface_update()
wvtest.WVPASS(c.acs())
- 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())
+ wvtest.WVFAIL(c.bridge.current_routes_normal_testonly())
+ wvtest.WVPASS(c.wifi_for_band('2.4').current_routes())
+ wvtest.WVPASS(c.wifi_for_band('5').current_routes())
c.run_once()
wvtest.WVPASSEQ(c.log_upload_count, 1)
@@ -1176,7 +1198,7 @@
c.run_once()
wvtest.WVPASS(c.wifi_for_band('2.4').acs())
wvtest.WVPASS(c.wifi_for_band('2.4').internet())
- wvtest.WVPASS(c.wifi_for_band('2.4').current_route())
+ wvtest.WVPASS(c.wifi_for_band('2.4').current_routes())
@wvtest.wvtest
@@ -1189,7 +1211,7 @@
c: The ConnectionManager set up by @connection_manager_test.
"""
wvtest.WVPASS(c._connected_to_wlan(c.wifi_for_band('2.4')))
- wvtest.WVPASS(c.wifi_for_band('2.4').current_route)
+ wvtest.WVPASS(c.wifi_for_band('2.4').current_routes())
@wvtest.wvtest
diff --git a/conman/interface.py b/conman/interface.py
index e172a82..cd4d323 100755
--- a/conman/interface.py
+++ b/conman/interface.py
@@ -7,6 +7,10 @@
import re
import subprocess
+# This has to be called before another module calls it with a higher log level.
+# pylint: disable=g-import-not-at-top
+logging.basicConfig(level=logging.DEBUG)
+
import experiment
import wpactrl
@@ -15,6 +19,8 @@
METRIC_24GHZ = 22
METRIC_TEMPORARY_CONNECTION_CHECK = 99
+RFC2385_MULTICAST_ROUTE = '239.0.0.0/8'
+
experiment.register('WifiSimulateWireless')
CWMP_PATH = '/tmp/cwmp'
MAX_ACS_FAILURE_S = 60
@@ -30,7 +36,7 @@
IP_ROUTE = ['ip', 'route']
IP_ADDR_SHOW = ['ip', 'addr', 'show', 'dev']
- def __init__(self, name, metric):
+ def __init__(self, name, base_metric):
self.name = name
# Currently connected links for this interface, e.g. ethernet.
@@ -40,13 +46,18 @@
self._has_acs = None
self._has_internet = None
- # The gateway IP for this interface.
+ self._subnet = None
self._gateway_ip = None
- self.metric = metric
+ self.base_metric = base_metric
+ self.metric_offset = 0
# Until this is set True, the routing table will not be touched.
self._initialized = False
+ @property
+ def metric(self):
+ return str(int(self.base_metric) + self.metric_offset)
+
def _connection_check(self, check_acs):
"""Check this interface's connection status.
@@ -76,9 +87,12 @@
# Give it a high metric so that it won't interfere with normal default
# routes.
added_temporary_route = False
- if not self.current_route():
- logging.debug('Adding temporary connection check route for dev %s',
+ if 'default' not in self.current_routes():
+ logging.debug('Adding temporary connection check routes for dev %s',
self.name)
+ self._ip_route('add', self._gateway_ip,
+ 'dev', self.name,
+ 'metric', str(METRIC_TEMPORARY_CONNECTION_CHECK))
self._ip_route('add', 'default',
'via', self._gateway_ip,
'dev', self.name,
@@ -98,11 +112,14 @@
# Delete the temporary route.
if added_temporary_route:
- logging.debug('Deleting temporary connection check route for dev %s',
+ logging.debug('Deleting temporary connection check routes for dev %s',
self.name)
self._ip_route('del', 'default',
'dev', self.name,
'metric', str(METRIC_TEMPORARY_CONNECTION_CHECK))
+ self._ip_route('del', self._gateway_ip,
+ 'dev', self.name,
+ 'metric', str(METRIC_TEMPORARY_CONNECTION_CHECK))
return result
@@ -121,11 +138,10 @@
return self._has_internet
- def add_route(self):
- """Adds a default route for this interface.
+ def add_routes(self):
+ """Update default routes for this interface.
- First, checks whether an equivalent route already exists, and if so,
- returns.
+ Remove any stale routes and add any missing desired routes.
"""
if self.metric is None:
logging.info('Cannot add route for %s without a metric.', self.name)
@@ -135,45 +151,88 @@
logging.info('Cannot add route for %s without a gateway IP.', self.name)
return
- # If the current default route is the same, there is nothing to do. If it
+ # If the current routes are the same, there is nothing to do. If either
# exists but is different, delete it before adding an updated one.
- current = self.current_route()
- if current:
- if (current.get('via', None) == self._gateway_ip and
- current.get('metric', None) == str(self.metric)):
- return
- else:
- self.delete_route()
+ current = self.current_routes()
+ default = current.get('default', {})
+ if ((default.get('via', None), default.get('metric', None)) !=
+ (self._gateway_ip, str(self.metric))):
+ logging.debug('Adding default route for dev %s', self.name)
+ self.delete_route('default')
+ self._ip_route('add', 'default',
+ 'via', self._gateway_ip,
+ 'dev', self.name,
+ 'metric', str(self.metric))
- logging.debug('Adding default route for dev %s', self.name)
- self._ip_route('add', 'default',
- 'via', self._gateway_ip,
- 'dev', self.name,
- 'metric', str(self.metric))
+ subnet = current.get('subnet', {})
+ if (self._subnet and
+ (subnet.get('via', None), subnet.get('metric', None)) !=
+ (self._gateway_ip, str(self.metric))):
+ logging.debug('Adding subnet route for dev %s', self.name)
+ self.delete_route('subnet')
+ self._ip_route('add', self._subnet,
+ 'dev', self.name,
+ 'metric', str(self.metric))
- def delete_route(self):
- while self.current_route():
- logging.debug('Deleting default route for dev %s', self.name)
- self._ip_route('del', 'default',
- 'dev', self.name)
+ # RFC2365 multicast route.
+ if current.get('multicast', {}).get('metric', None) != str(self.metric):
+ logging.debug('Adding multicast route for dev %s', self.name)
+ self.delete_route('multicast')
+ self._ip_route('add', RFC2385_MULTICAST_ROUTE,
+ 'dev', self.name,
+ 'metric', str(self.metric))
- def current_route(self):
- """Read the current default route for this interface.
+ def delete_route(self, *args):
+ """Delete default and/or subnet routes for this interface.
+
+ Args:
+ *args: Which routes to delete. Must be at least one of 'default',
+ 'subnet', 'multicast'.
+
+ Raises:
+ ValueError: If neither default nor subnet is True.
+ """
+ args = set(args)
+ args &= set(('default', 'subnet', 'multicast'))
+ if not args:
+ raise ValueError(
+ 'Must specify at least one of default, subnet, multicast to delete.')
+
+ for route_type in args:
+ while route_type in self.current_routes():
+ logging.debug('Deleting %s route for dev %s', route_type, self.name)
+ self._ip_route('del', self.current_routes()[route_type]['route'],
+ 'dev', self.name)
+
+ def current_routes(self):
+ """Read the current routes for this interface.
Returns:
- A dict containing the gateway [and metric] of the route, or an empty dict
- if there is currently no default route for this interface.
+ A dict mapping 'default' and/or 'subnet' to a dict containing the gateway
+ [and metric] of the route. Only contains keys for routes that are
+ present.
"""
result = {}
for line in self._ip_route().splitlines():
- if line.startswith('default') and 'dev %s' % self.name in line:
- key = None
+ if 'dev %s' % self.name in line:
+ if line.startswith('default'):
+ route_type = 'default'
+ elif re.search(r'/\d{1,2}$', line.split()[0]):
+ route_type = 'subnet'
+ else:
+ continue
+ route = {}
+ key = 'route'
for token in line.split():
if token in ['via', 'metric']:
key = token
elif key:
- result[key] = token
+ if key == 'route' and token == RFC2385_MULTICAST_ROUTE:
+ route_type = 'multicast'
+ route[key] = token
key = None
+ if route:
+ result[route_type] = route
return result
@@ -209,7 +268,12 @@
def set_gateway_ip(self, gateway_ip):
logging.info('New gateway IP %s for %s', gateway_ip, self.name)
self._gateway_ip = gateway_ip
- self.update_routes()
+ self.update_routes(expire_cache=True)
+
+ def set_subnet(self, subnet):
+ logging.info('New subnet %s for %s', subnet, self.name)
+ self._subnet = subnet
+ self.update_routes(expire_cache=True)
def _set_link_status(self, link, is_up):
"""Set whether a link is up or not."""
@@ -243,36 +307,51 @@
def update_routes(self, expire_cache=True):
"""Update this interface's routes.
- If the interface has gained ACS or internet access, add a route. If it had
- either and now has neither, delete the route.
+ If the interface has ACS or internet access, prioritize its routes. If it
+ doesn't but has a link, deprioritize the routes. If it has no links, delete
+ the routes.
Args:
expire_cache: If true, force a recheck of connection status before
- deciding whether to add or remove routes.
+ deciding how to prioritize routes.
"""
logging.debug('Updating routes for %s', self.name)
- maybe_had_acs = self._has_acs
- maybe_had_internet = self._has_internet
-
if expire_cache:
self.expire_connection_status_cache()
- has_acs = self.acs()
- has_internet = self.internet()
+ if self.acs() or self.internet():
+ self.prioritize_routes()
+ else:
+ # If we still have a link, just deprioritize the routes, in case we're
+ # wrong about the connection check. If there's no actual link, then
+ # really delete the routes.
+ if self.links:
+ self.deprioritize_routes()
+ else:
+ self.delete_route('default', 'subnet', 'multicast')
- # This is a little confusing: We want to try adding a route if we _may_
- # have gone from no access to some access, and we want to try deleting the
- # route if we _may_ have lost *all* access. So the first condition checks
- # for truthiness but the elif checks for explicit Falsity (i.e. excluding
- # the None/unknown case).
- had_access = maybe_had_acs or maybe_had_internet
- # pylint: disable=g-explicit-bool-comparison
- maybe_had_access = maybe_had_acs != False or maybe_had_internet != False
- has_access = has_acs or has_internet
- if not had_access and has_access:
- self.add_route()
- elif maybe_had_access and not has_access:
- self.delete_route()
+ def prioritize_routes(self):
+ """When connection check succeeds, route priority (metric) should be normal.
+
+ This is the inverse of deprioritize_routes.
+ """
+ if not self._initialized:
+ return
+ logging.info('%s routes have normal priority', self.name)
+ self.metric_offset = 0
+ self.add_routes()
+
+ def deprioritize_routes(self):
+ """When connection check fails, deprioritize routes by increasing metric.
+
+ This is conservative alternative to deleting routes, in case we are mistaken
+ about route not providing a useful connection.
+ """
+ if not self._initialized:
+ return
+ logging.info('%s routes have low priority', self.name)
+ self.metric_offset = 50
+ self.add_routes()
def initialize(self):
"""Tell the interface it has its initial state.
@@ -319,16 +398,22 @@
self._moca_stations.remove(node_id)
self.moca = bool(self._moca_stations)
- def add_route(self):
+ def prioritize_routes(self):
"""We only want ACS autoprovisioning when we're using a wired route."""
- super(Bridge, self).add_route()
+ super(Bridge, self).prioritize_routes()
open(self._acs_autoprovisioning_filepath, 'w')
- def delete_route(self):
+ def deprioritize_routes(self, *args, **kwargs):
"""We only want ACS autoprovisioning when we're using a wired route."""
if os.path.exists(self._acs_autoprovisioning_filepath):
os.unlink(self._acs_autoprovisioning_filepath)
- super(Bridge, self).delete_route()
+ super(Bridge, self).deprioritize_routes(*args, **kwargs)
+
+ def delete_route(self, *args, **kwargs):
+ """We only want ACS autoprovisioning when we're using a wired route."""
+ if os.path.exists(self._acs_autoprovisioning_filepath):
+ os.unlink(self._acs_autoprovisioning_filepath)
+ super(Bridge, self).delete_route(*args, **kwargs)
def _connection_check(self, check_acs):
"""Support for WifiSimulateWireless."""
@@ -528,7 +613,8 @@
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)
+ security = (self._qcsapi('ssid_get_authentication_mode', 'wifi0', ssid)
+ if ssid else None)
except subprocess.CalledProcessError:
# If QCSAPI failed, skip update.
return
diff --git a/conman/interface_test.py b/conman/interface_test.py
index 13dcf14..e8ab4ca 100755
--- a/conman/interface_test.py
+++ b/conman/interface_test.py
@@ -48,14 +48,18 @@
def _really_ip_route(self, *args):
if not args:
return '\n'.join(self.routing_table.values() +
- ['1.2.3.4/24 dev %s proto kernel scope link' % self.name,
+ ['1.2.3.4/24 dev fake0 proto kernel scope link',
+ # Non-subnet route, e.g. to NFS host.
+ '1.2.3.1 dev %s proto kernel scope link' % self.name,
'default via 1.2.3.4 dev fake0',
'random junk'])
metric = None
if 'metric' in args:
metric = args[args.index('metric') + 1]
- key = (self.name, metric)
+ if args[0] in ('add', 'del'):
+ route = args[1]
+ key = (self.name, route, metric)
if args[0] == 'add' and key not in self.routing_table:
logging.debug('Adding route for %r', key)
self.routing_table[key] = ' '.join(args[1:])
@@ -63,10 +67,10 @@
if key in self.routing_table:
logging.debug('Deleting route for %r', key)
del self.routing_table[key]
- elif key[1] is None:
+ elif key[2] is None:
# pylint: disable=g-builtin-op
for k in self.routing_table.keys():
- if k[0] == key[0]:
+ if k[:-1] == key[:-1]:
logging.debug('Deleting route for %r (generalized from %s)', k, key)
del self.routing_table[k]
break
@@ -77,6 +81,10 @@
return ''
+ def current_routes_normal_testonly(self):
+ result = self.current_routes()
+ return {k: v for k, v in result.iteritems() if int(v.get('metric', 0)) < 50}
+
class Bridge(FakeInterfaceMixin, interface.Bridge):
pass
@@ -330,53 +338,65 @@
wvtest.WVFAIL(b.acs())
wvtest.WVFAIL(b.internet())
- wvtest.WVFAIL(b.current_route())
+ wvtest.WVFAIL(b.current_routes())
+ wvtest.WVFAIL(b.current_routes_normal_testonly())
wvtest.WVFAIL(os.path.exists(autoprov_filepath))
b.add_moca_station(0)
+ wvtest.WVFAIL(os.path.exists(autoprov_filepath))
b.set_gateway_ip('192.168.1.1')
+ b.set_subnet('192.168.1.0/24')
+ wvtest.WVFAIL(os.path.exists(autoprov_filepath))
# Everything should fail because the interface is not initialized.
wvtest.WVFAIL(b.acs())
wvtest.WVFAIL(b.internet())
- wvtest.WVFAIL(b.current_route())
+ wvtest.WVFAIL(b.current_routes_normal_testonly())
wvtest.WVFAIL(os.path.exists(autoprov_filepath))
b.initialize()
wvtest.WVPASS(b.acs())
wvtest.WVPASS(b.internet())
- wvtest.WVPASS(b.current_route())
+ current_routes = b.current_routes()
+ wvtest.WVPASSEQ(len(current_routes), 3)
+ wvtest.WVPASS('default' in current_routes)
+ wvtest.WVPASS('subnet' in current_routes)
+ wvtest.WVPASS('multicast' in current_routes)
wvtest.WVPASS(os.path.exists(autoprov_filepath))
b.add_moca_station(1)
wvtest.WVPASS(b.acs())
wvtest.WVPASS(b.internet())
- wvtest.WVPASS(b.current_route())
+ wvtest.WVPASSEQ(len(b.current_routes()), 3)
wvtest.WVPASS(os.path.exists(autoprov_filepath))
b.remove_moca_station(0)
b.remove_moca_station(1)
wvtest.WVFAIL(b.acs())
wvtest.WVFAIL(b.internet())
- wvtest.WVFAIL(b.current_route())
+ # We have no links, so should have no routes.
+ wvtest.WVFAIL(b.current_routes())
wvtest.WVFAIL(os.path.exists(autoprov_filepath))
b.add_moca_station(2)
wvtest.WVPASS(b.acs())
wvtest.WVPASS(b.internet())
- wvtest.WVPASS(b.current_route())
+ wvtest.WVPASSEQ(len(b.current_routes()), 3)
wvtest.WVPASS(os.path.exists(autoprov_filepath))
b.set_connection_check_result('fail')
b.update_routes()
wvtest.WVFAIL(b.acs())
wvtest.WVFAIL(b.internet())
- wvtest.WVFAIL(b.current_route())
+ # We have links but the connection check failed, so we should only have a
+ # low priority route, i.e. metric at least 50.
+ wvtest.WVPASSEQ(len(b.current_routes()), 3)
+ wvtest.WVFAIL(b.current_routes_normal_testonly())
wvtest.WVFAIL(os.path.exists(autoprov_filepath))
b.set_connection_check_result('restricted')
b.update_routes()
wvtest.WVPASS(b.acs())
wvtest.WVFAIL(b.internet())
- wvtest.WVPASS(b.current_route())
+ wvtest.WVPASSEQ(len(b.current_routes()), 3)
wvtest.WVPASS(os.path.exists(autoprov_filepath))
wvtest.WVFAIL(b.get_ip_address())
diff --git a/hnvram/hnvram_main.c b/hnvram/hnvram_main.c
index b850048..323d62e 100644
--- a/hnvram/hnvram_main.c
+++ b/hnvram/hnvram_main.c
@@ -83,6 +83,7 @@
{"LASER_CHANNEL", NVRAM_FIELD_LASER_CHANNEL, HNVRAM_STRING},
{"MAC_ADDR_PON", NVRAM_FIELD_MAC_ADDR_PON, HNVRAM_MAC},
{"PRODUCTION_UNIT", NVRAM_FIELD_PRODUCTION_UNIT, HNVRAM_STRING},
+ {"BOOT_TARGET", NVRAM_FIELD_BOOT_TARGET, HNVRAM_STRING},
};
const hnvram_field_t* get_nvram_field(const char* name) {
diff --git a/ledpattern/Makefile b/ledpattern/Makefile
index 705a398..ad3456d 100644
--- a/ledpattern/Makefile
+++ b/ledpattern/Makefile
@@ -1,7 +1,7 @@
default:
-PREFIX=/
-BINDIR=$(DESTDIR)$(PREFIX)/bin
+ETCDIR=$(DESTDIR)/etc
+BINDIR=$(DESTDIR)/bin
PYTHON?=python
all:
@@ -9,6 +9,9 @@
install:
mkdir -p $(BINDIR)
cp ledpattern.py $(BINDIR)/ledpattern
+ cp ledtapcode.sh $(BINDIR)/ledtapcode
+ cp ledpatterns $(ETCDIR)/ledpatterns
+ chmod +x $(BINDIR)/ledtapcode
install-libs:
@echo "No libs to install."
diff --git a/ledpattern/ledpatterns b/ledpattern/ledpatterns
new file mode 100644
index 0000000..2e0ab63
--- /dev/null
+++ b/ledpattern/ledpatterns
@@ -0,0 +1,12 @@
+HALTED,P,R
+NO_LASER_CHANNEL,P,P
+SET_LASER_FAILED,P,R,R
+LOSLOF_ALARM,P,R,B
+OTHER_ALARM,P,R,P
+GPON_INITIAL,P,B,R
+GPON_STANDBY,P,B,P
+GPON_SERIAL,P,P,R
+GPON_RANGING,P,P,B
+WAIT_ACS,P,B,B
+ALL_OK,P,B,B,B
+UNKNOWN_ERROR,P,R,R,R
diff --git a/ledpattern/ledtapcode.sh b/ledpattern/ledtapcode.sh
new file mode 100755
index 0000000..6841f2f
--- /dev/null
+++ b/ledpattern/ledtapcode.sh
@@ -0,0 +1,107 @@
+#!/bin/sh
+
+. /etc/utils.sh
+
+LEDPATTERN="ledpattern /etc/ledpatterns"
+SYSFS_GPON_PATH="/sys/devices/platform/gpon"
+MONITOR_PATH="/tmp/gpio/ledcontrol"
+LASER_STATUS_FILE="/tmp/laser_i2c_status"
+ALARM_GPON_FILE="$SYSFS_GPON_PATH/info/alarmGpon"
+GPON_INFO_FILE="$SYSFS_GPON_PATH/info/infoGpon"
+HALTED_FILE="$MONITOR_PATH/halted"
+HW_FAILURE="$MONITOR_PATH/hardware_failure"
+LASER_CHANNEL_FILE="$SYSFS_GPON_PATH/misc/laserChannel"
+ACS_FILE="$MONITOR_PATH/acsconnected"
+
+PlayPatternAndExit()
+{
+ state="$1"
+ # ledpattern takes care of all the LED management and state selection.
+ result="$($LEDPATTERN $state)"
+ if [ "$?" -ne 0 ]; then
+ echo "Failed to display pattern $state: $result"
+ exit 1
+ fi
+ exit 0
+}
+
+if [ ! -f "$ALARM_GPON_FILE" ]; then
+ echo "$ALARM_GPON_FILE does not exist"
+ PlayPatternAndExit UNKNOWN_ERROR
+fi
+
+if [ ! -f "$GPON_INFO_FILE" ]; then
+ echo "$GPON_INFO_FILE does not exist"
+ PlayPatternAndExit UNKNOWN_ERROR
+fi
+
+if [ ! -f "$LASER_CHANNEL_FILE" ]; then
+ echo "$LASER_CHANNEL_FILE does not exist"
+ PlayPatternAndExit UNKNOWN_ERROR
+fi
+
+# It is a valid state that there may not be a LASER_STATUS_FILE yet.
+if [ -f "$LASER_STATUS_FILE" ]; then
+ laser_status=$(cat "$LASER_STATUS_FILE")
+ if [ "$laser_status" -ne 0 ]; then
+ echo "Playing SET_LASER_FAILED pattern"
+ PlayPatternAndExit SET_LASER_FAILED
+ fi
+fi
+
+if [ -f "$HW_FAILURE" ]; then
+ echo "Playing HALTED pattern on HW_FAILURE"
+ PlayPatternAndExit HALTED
+fi
+
+if [ -f "$HALTED_FILE" ]; then
+ echo "Playing HALTED pattern on HALTED_FILE"
+ PlayPatternAndExit HALTED
+fi
+
+# Chop the table headers off the output using tail, otherwise grep gets
+# confused later.
+alarm_info=$(cat "$ALARM_GPON_FILE" | tail -n+7)
+los_output=$(echo "$alarm_info" | grep "LOS" | grep "ON")
+lof_output=$(echo "$alarm_info" | grep "LOF" | grep "ON")
+if [ -n "$los_output" ] || [ -n "$lof_output" ]; then
+ echo "Playing LOSLOF_ALARM pattern"
+ PlayPatternAndExit LOSLOF_ALARM
+fi
+other_alarm=$(echo "$alarm_info" | grep "ON")
+if [ -n "$other_alarm" ]; then
+ echo "Playing OTHER_ALARM pattern"
+ PlayPatternAndExit OTHER_ALARM
+fi
+
+gpon_info=$(cat "$GPON_INFO_FILE" | grep "ONU STATE")
+if contains "$gpon_info" "INITIAL"; then
+ echo "Playing GPON_INITIAL pattern"
+ PlayPatternAndExit GPON_INITIAL
+elif contains "$gpon_info" "STANDBY"; then
+ echo "Playing GPON_STANDBY pattern"
+ PlayPatternAndExit GPON_STANDBY
+elif contains "$gpon_info" "SERIAL"; then
+ echo "Playing GPON_SERIAL pattern"
+ PlayPatternAndExit GPON_SERIAL
+elif contains "$gpon_info" "RANGING"; then
+ echo "Playing GPON_RANGING pattern"
+ PlayPatternAndExit GPON_RANGING
+fi
+
+laser_channel=$(cat "$LASER_CHANNEL_FILE")
+if [ ! -f "$ACS_FILE" ] && [ "$laser_channel" -eq "-1" ]; then
+ echo "Playing NO_LASER_CHANNEL pattern"
+ PlayPatternAndExit NO_LASER_CHANNEL
+elif [ ! -f "$ACS_FILE" ] && [ $laser_channel -ne "-1" ]; then
+ echo "Playing WAIT_ACS pattern"
+ PlayPatternAndExit WAIT_ACS
+elif [ -f "$ACS_FILE" ] && [ $laser_channel -ne "-1" ]; then
+ echo "Playing ALL_OK pattern"
+ PlayPatternAndExit ALL_OK
+else
+ # If we get all the way here and nothing triggered on the way then this really
+ # is an unknown error...
+ echo "Nothing triggered? Playing UNKNOWN_ERROR pattern..."
+ PlayPatternAndExit UNKNOWN_ERROR
+fi
diff --git a/wifi/iw.py b/wifi/iw.py
index ae2a8b6..c0bbf57 100644
--- a/wifi/iw.py
+++ b/wifi/iw.py
@@ -226,14 +226,26 @@
return interface
-def find_all_interfaces_from_phy(phy):
+def find_all_interfaces_from_phy(phy, interface_type=None):
+ """Finds the names of all interfaces on a given phy.
+
+ Args:
+ phy: The name of a phy, e.g. 'phy0'.
+ interface_type: An INTERFACE_TYPE value (optional).
+
+ Returns:
+ A list of all interfaces found.
+ """
interfaces = []
- for interface_type in INTERFACE_TYPE:
+ interface_types = INTERFACE_TYPE
+ if interface_type:
+ interface_types = [interface_type]
+ for interface_type in interface_types:
pattern = re.compile(r'w%s[0-9]\w*\Z' % re.escape(interface_type))
interfaces.extend(interface for interface
in dev_parsed()[phy]['interfaces']
if pattern.match(interface))
- return interfaces
+ return set(interfaces)
def find_interface_from_band(band, interface_type, interface_suffix):
@@ -254,6 +266,23 @@
return find_interface_from_phy(phy, interface_type, interface_suffix)
+def find_all_interfaces_from_band(band, interface_type=None):
+ """Finds the names of all interface on a given band.
+
+ Args:
+ band: The band for which you want the interface.
+ interface_type: An INTERFACE_TYPE value (optional).
+
+ Returns:
+ A list of all interfaces found.
+ """
+ phy = find_phy(band, 'auto')
+ if phy is None:
+ return []
+
+ return find_all_interfaces_from_phy(phy, interface_type)
+
+
def find_width_and_channel(interface):
"""Finds the width and channel being used by a given interface.
diff --git a/wifi/iw_test.py b/wifi/iw_test.py
index 4a7ef4c..2293954 100755
--- a/wifi/iw_test.py
+++ b/wifi/iw_test.py
@@ -514,6 +514,19 @@
@wvtest.wvtest
+def find_all_interfaces_from_phy_test():
+ wvtest.WVPASSEQ(set(['wlan0', 'wlan0_portal', 'wcli0']),
+ iw.find_all_interfaces_from_phy('phy0'))
+ wvtest.WVPASSEQ(set(['wlan0', 'wlan0_portal']),
+ iw.find_all_interfaces_from_phy('phy0', iw.INTERFACE_TYPE.ap))
+ wvtest.WVPASSEQ(set(['wcli0']),
+ iw.find_all_interfaces_from_phy('phy0',
+ iw.INTERFACE_TYPE.client))
+ wvtest.WVPASSEQ(set(['wlan1', 'wlan1_portal']),
+ iw.find_all_interfaces_from_phy('phy1'))
+
+
+@wvtest.wvtest
def find_interface_from_band_test():
wvtest.WVPASSEQ('wlan0',
iw.find_interface_from_band('2.4', iw.INTERFACE_TYPE.ap, ''))
@@ -529,6 +542,19 @@
@wvtest.wvtest
+def find_all_interfaces_from_band_test():
+ wvtest.WVPASSEQ(set(['wlan0', 'wlan0_portal', 'wcli0']),
+ iw.find_all_interfaces_from_band('2.4'))
+ wvtest.WVPASSEQ(set(['wlan0', 'wlan0_portal']),
+ iw.find_all_interfaces_from_band('2.4', iw.INTERFACE_TYPE.ap))
+ wvtest.WVPASSEQ(set(['wcli0']),
+ iw.find_all_interfaces_from_band('2.4',
+ iw.INTERFACE_TYPE.client))
+ wvtest.WVPASSEQ(set(['wlan1', 'wlan1_portal']),
+ iw.find_all_interfaces_from_band('5'))
+
+
+@wvtest.wvtest
def info_parsed_test():
wvtest.WVPASSEQ({
'wdev': '0x3',
diff --git a/wifi/wifi.py b/wifi/wifi.py
index 8797633..142010c 100755
--- a/wifi/wifi.py
+++ b/wifi/wifi.py
@@ -50,7 +50,7 @@
X,extra-short-timeouts Use shorter key rotations; 1=rotate PTK, 2=rotate often
Y,yottasecond-timeouts Don't rotate any keys: PTK, GTK, or GMK
P,persist For set commands, persist options so we can restore them with 'wifi restore'. For stop commands, remove persisted options.
-S,interface-suffix= Interface suffix []
+S,interface-suffix= Interface suffix (defaults to ALL for stop commands; use NONE to specify no suffix) []
lock-timeout= How long, in seconds, to wait for another /bin/wifi process to finish before giving up. [60]
scan-ap-force (Scan only) scan when in AP mode
scan-passive (Scan only) do not probe, scan passively
@@ -381,18 +381,25 @@
if band == '5' and quantenna.stop_ap_wifi(opt):
continue
- interface = iw.find_interface_from_band(
- band, iw.INTERFACE_TYPE.ap, opt.interface_suffix)
- if interface is None:
- utils.log('No AP interface for %s GHz; nothing to stop', band)
+ interfaces = []
+ if opt.interface_suffix == 'ALL':
+ interfaces = iw.find_all_interfaces_from_band(band, iw.INTERFACE_TYPE.ap)
+ else:
+ interface = iw.find_interface_from_band(
+ band, iw.INTERFACE_TYPE.ap, opt.interface_suffix)
+ if interface:
+ interfaces = [interface]
+ if not interfaces:
+ utils.log('No AP interfaces for %s GHz; nothing to stop', band)
continue
- if _stop_hostapd(interface):
- if opt.persist:
- persist.delete_options('hostapd', band)
- else:
- utils.log('Failed to stop hostapd on interface %s', interface)
- success = False
+ for interface in interfaces:
+ if _stop_hostapd(interface):
+ if opt.persist:
+ persist.delete_options('hostapd', band)
+ else:
+ utils.log('Failed to stop hostapd on interface %s', interface)
+ success = False
return success
@@ -989,18 +996,26 @@
if band == '5' and quantenna.stop_client_wifi(opt):
continue
- interface = iw.find_interface_from_band(
- band, iw.INTERFACE_TYPE.client, opt.interface_suffix)
- if interface is None:
- utils.log('No client interface for %s GHz; nothing to stop', band)
+ interfaces = []
+ if opt.interface_suffix == 'ALL':
+ interfaces = iw.find_all_interfaces_from_band(
+ band, iw.INTERFACE_TYPE.client)
+ else:
+ interface = iw.find_interface_from_band(
+ band, iw.INTERFACE_TYPE.client, opt.interface_suffix)
+ if interface:
+ interfaces = [interface]
+ if not interfaces:
+ utils.log('No client interfaces for %s GHz; nothing to stop', band)
continue
- if _stop_wpa_supplicant(interface):
- if opt.persist:
- persist.delete_options('wpa_supplicant', band)
- else:
- utils.log('Failed to stop wpa_supplicant on interface %s', interface)
- success = False
+ for interface in interfaces:
+ if _stop_wpa_supplicant(interface):
+ if opt.persist:
+ persist.delete_options('wpa_supplicant', band)
+ else:
+ utils.log('Failed to stop wpa_supplicant on interface %s', interface)
+ success = False
return success
@@ -1044,10 +1059,18 @@
parser.fatal('Must specify a command (see usage for details).')
return 1
+ command = extra[0]
+
# set and setclient have a different default for -b.
- if extra[0].startswith('set') and ' ' in opt.band:
+ if command.startswith('set') and ' ' in opt.band:
opt.band = '2.4'
+ if command == 'off' or command.startswith('stop'):
+ if not opt.interface_suffix:
+ opt.interface_suffix = 'ALL'
+ elif opt.interface_suffix == 'NONE':
+ opt.interface_suffix = ''
+
try:
function = {
'set': set_wifi,