Merge "ledpatterns: add an led error code for channel 16"
diff --git a/bouncer/Makefile b/bouncer/Makefile
index bda3d81..db66253 100644
--- a/bouncer/Makefile
+++ b/bouncer/Makefile
@@ -1,6 +1,8 @@
default: all
+INSTALL?=install
BINDIR=$(DESTDIR)/bin
+LIBDIR=$(DESTDIR)/usr/bouncer
GPYLINT=$(shell \
if which gpylint >/dev/null; then \
echo gpylint; \
@@ -8,26 +10,21 @@
echo 'echo "(gpylint-missing)" >&2'; \
fi \
)
+NOINSTALL=options.py
-TARGETS=authorizer hash_mac_addr http_bouncer
-
-HOST_TARGETS=$(addprefix host-,$(TARGETS))
-
-all: $(TARGETS) $(HOST_TARGETS)
+all:
install:
- mkdir -p $(BINDIR)
- cp $(TARGETS) $(BINDIR)
+ mkdir -p $(LIBDIR) $(BINDIR)
+ $(INSTALL) -m 0644 $(filter-out $(NOINSTALL) $(TARGETS), $(wildcard *.py)) $(LIBDIR)/
+ for t in authorizer hash_mac_addr http_bouncer; do \
+ $(INSTALL) -m 0755 $$t.py $(LIBDIR)/; \
+ ln -fs /usr/bouncer/$$t.py $(BINDIR)/$$t; \
+ done
install-libs:
@echo "No libs to install."
-%: %.py
- ln -s $< $@
-
-host-%: %.py
- ln -s $< $@
-
TESTS = $(wildcard test-*.sh) $(wildcard test-*.py) $(wildcard *_test.py)
runtests: all $(TESTS)
set -e; \
diff --git a/bouncer/authorizer.py b/bouncer/authorizer.py
index f355917..0c5e0b1 100755
--- a/bouncer/authorizer.py
+++ b/bouncer/authorizer.py
@@ -43,6 +43,7 @@
MAX_TRIES = 300
+in_progress_users = {}
known_users = {}
@@ -55,13 +56,22 @@
return x | y
+def is_valid_acceptance(response_obj):
+ accepted_time = response_obj.get('accepted')
+ return accepted_time + (opt.max_age * 86400) > time.time()
+
+
+def allow_mac_rule(mac_addr):
+ # iptables, unlike other Linux utilities, capitalizes MAC addresses
+ return ('-m', 'mac', '--mac-source', mac_addr.upper(), '-j', 'ACCEPT')
+
+
class Checker(object):
"""Manage checking and polling for Terms of Service acceptance."""
- def __init__(self, mac_addr, hashed_mac_addr, url):
+ def __init__(self, mac_addr, url):
self.mac_addr = mac_addr
- self.hashed_mac_addr = hashed_mac_addr
- self.url = url % {'mac': hashed_mac_addr}
+ self.url = url % {'mac': hash_mac_addr.hash_mac_addr(self.mac_addr)}
self.tries = 0
self.callback = None
@@ -69,41 +79,43 @@
"""Check if a remote service knows about a device with a supplied MAC."""
logging.info('Checking TOS for %s', self.mac_addr)
http_client = tornado.httpclient.HTTPClient()
- response = http_client.fetch(self.url, ca_certs=opt.ca_certs)
- response_obj = tornado.escape.json_decode(response.body)
- accepted_time = response_obj.get('accepted')
self.tries += 1
- accepted = False
- if accepted_time:
- if accepted_time + (opt.max_age * 86400) > time.time():
- accepted = True
- if self.callback: self.callback.stop()
- logging.info('TOS accepted for %s', self.mac_addr)
+ try:
+ response = http_client.fetch(self.url, ca_certs=opt.ca_certs)
+ response_obj = tornado.escape.json_decode(response.body)
+ valid = is_valid_acceptance(response_obj)
+ except tornado.httpclient.HTTPError as e:
+ logging.warning('Error checking authorization: %r', e)
+ valid = False
- known_users[self.mac_addr] = response_obj
- result = ip46tables('-A', opt.filter_chain, '-m', 'mac',
- '--mac-source', self.mac_addr, '-j', 'ACCEPT')
- result |= ip46tables('-t', 'nat', '-A', opt.nat_chain, '-m', 'mac',
- '--mac-source', self.mac_addr, '-j', 'ACCEPT')
- if result:
- logging.error('Could not update firewall for device %s',
- self.mac_addr)
- else:
- logging.info('TOS accepted too long ago for %s: %r',
- self.mac_addr, accepted_time)
+ if valid:
+ logging.info('TOS accepted for %s', self.mac_addr)
- elif self.callback and self.tries > MAX_TRIES:
- if not accepted:
- logging.info('TOS not accepted for %s before timeout.',
- self.mac_addr)
- self.callback.stop()
+ known_users[self.mac_addr] = response_obj
+ result = ip46tables('-A', opt.filter_chain,
+ *allow_mac_rule(self.mac_addr))
+ result |= ip46tables('-t', 'nat', '-A', opt.nat_chain,
+ *allow_mac_rule(self.mac_addr))
+ if result:
+ logging.error('Could not update firewall for device %s',
+ self.mac_addr)
- return response, accepted
+ if valid or self.tries > MAX_TRIES:
+ if self.callback:
+ self.callback.stop()
+ if self.mac_addr in in_progress_users:
+ del in_progress_users[self.mac_addr]
+ else:
+ in_progress_users[self.mac_addr] = self
+ self.poll()
+
+ return response
def poll(self):
- self.callback = tornado.ioloop.PeriodicCallback(self.check, 1000)
- self.callback.start()
+ if not self.callback:
+ self.callback = tornado.ioloop.PeriodicCallback(self.check, 1000)
+ self.callback.start()
def accept(connection, unused_address):
@@ -112,27 +124,46 @@
maybe_mac_addr = cf.readline().strip()
try:
- mac_addr, hashed_mac_addr = hash_mac_addr.hash_mac_addr(maybe_mac_addr)
+ mac_addr = hash_mac_addr.normalize_mac_addr(maybe_mac_addr)
except ValueError:
logging.warning('can only check authorization for a MAC address.')
cf.write('{}')
return
if mac_addr in known_users:
- logging.info('TOS accepted (cached) for %s', mac_addr)
cached_response = known_users[mac_addr]
- cached_response['cached'] = True
- cf.write(tornado.escape.json_encode(cached_response))
- return
+ if is_valid_acceptance(cached_response):
+ logging.info('TOS accepted (cached) for %s', mac_addr)
+ cached_response['cached'] = True
+ cf.write(tornado.escape.json_encode(cached_response))
+ return
- checker = Checker(mac_addr, hashed_mac_addr, opt.url)
- response, accepted = checker.check()
- if not accepted:
- checker.poll()
+ if mac_addr in in_progress_users:
+ checker = in_progress_users[mac_addr]
+ else:
+ checker = Checker(mac_addr, opt.url)
+ response = checker.check()
cf.write(response.body)
+def expire_cache():
+ """Remove users whose authorization has expired from the cache."""
+ expired_users = set(mac_addr for mac_addr, cached_response
+ in known_users.items()
+ if not is_valid_acceptance(cached_response))
+
+ for mac_addr in expired_users:
+ logging.info('Removing expired user %s', mac_addr)
+ del known_users[mac_addr]
+
+ result = ip46tables('-D', opt.filter_chain, *allow_mac_rule(mac_addr))
+ result |= ip46tables('-t', 'nat', '-D', opt.nat_chain,
+ *allow_mac_rule(mac_addr))
+ if result:
+ logging.warning('Error removing expired user %s !', mac_addr)
+
+
if __name__ == '__main__':
o = options.Options(optspec)
opt, flags, extra = o.parse(sys.argv[1:])
@@ -157,3 +188,6 @@
logging.info('Started authorizer.')
ioloop.start()
+ expirer = tornado.ioloop.PeriodicCallback(expire_cache, 60 * 60 * 1000)
+ expirer.start()
+
diff --git a/bouncer/hash_mac_addr.py b/bouncer/hash_mac_addr.py
index 961bf10..23b45d1 100755
--- a/bouncer/hash_mac_addr.py
+++ b/bouncer/hash_mac_addr.py
@@ -15,13 +15,16 @@
"""
-def hash_mac_addr(maybe_mac_addr):
+def normalize_mac_addr(maybe_mac_addr):
if re.match('([0-9A-Fa-f]{2}:){5}[0-9A-Fa-f]{2}$', maybe_mac_addr):
- mac_addr = maybe_mac_addr.lower()
+ return maybe_mac_addr.lower()
else:
raise ValueError('%r not a MAC address' % maybe_mac_addr)
- return mac_addr, hashlib.sha1(mac_addr).hexdigest()
+
+def hash_mac_addr(maybe_mac_addr):
+ mac_addr = normalize_mac_addr(maybe_mac_addr)
+ return hashlib.sha1(mac_addr).hexdigest()
if __name__ == '__main__':
@@ -32,7 +35,7 @@
o.usage()
try:
- _, hashed_mac_addr = hash_mac_addr(str(opt.addr))
+ hashed_mac_addr = hash_mac_addr(str(opt.addr))
print hashed_mac_addr
except ValueError as e:
print >>sys.stderr, 'error:', e.message
diff --git a/bouncer/http_bouncer.py b/bouncer/http_bouncer.py
index 380da4b..5fe0a53 100755
--- a/bouncer/http_bouncer.py
+++ b/bouncer/http_bouncer.py
@@ -68,8 +68,7 @@
else:
if self.substitute_mac:
mac = mac_for_ip(self.request.remote_ip)
- _, hashed_mac = hash_mac_addr.hash_mac_addr(mac)
- self.redirect(opt.url % {'mac': hashed_mac})
+ self.redirect(opt.url % {'mac': hash_mac_addr.hash_mac_addr(mac)})
if opt.unix_path:
try:
diff --git a/bouncer/test-hash_mac_addr.sh b/bouncer/test-hash_mac_addr.sh
index 3c84424..db6f10b 100755
--- a/bouncer/test-hash_mac_addr.sh
+++ b/bouncer/test-hash_mac_addr.sh
@@ -4,7 +4,7 @@
WVSTART "hash_mac_addr test"
-HASH_MAC_ADDR=./host-hash_mac_addr
+HASH_MAC_ADDR=./hash_mac_addr.py
WVFAIL $HASH_MAC_ADDR
WVFAIL $HASH_MAC_ADDR -a nonsense
diff --git a/bouncer/test-http_bouncer.sh b/bouncer/test-http_bouncer.sh
index dedd742..b5bd72a 100755
--- a/bouncer/test-http_bouncer.sh
+++ b/bouncer/test-http_bouncer.sh
@@ -5,7 +5,7 @@
. ./wvtest/wvtest.sh
-HTTP_BOUNCER=./host-http_bouncer
+HTTP_BOUNCER=./http_bouncer.py
PORT="1337"
URL="http://example.com"
diff --git a/conman/connection_manager.py b/conman/connection_manager.py
index 8307efd..c27ef8d 100755
--- a/conman/connection_manager.py
+++ b/conman/connection_manager.py
@@ -620,9 +620,6 @@
route = ifc.current_routes().get('default', None)
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):
diff --git a/conman/interface.py b/conman/interface.py
index cdeaf67..b20709a 100755
--- a/conman/interface.py
+++ b/conman/interface.py
@@ -500,6 +500,7 @@
except wpactrl.error as e:
logging.error('wpa_control STATUS request failed %s args %s',
e.message, e.args)
+ lines = self.wpa_cli_status().splitlines()
for line in lines:
if '=' not in line:
continue
@@ -553,6 +554,15 @@
return (self.wpa_status().get('wpa_state', None) == 'COMPLETED' and
self.wpa_status().get('key_mgmt', None) == 'NONE')
+ # TODO(rofrankel): Remove this if and when the wpactrl failures are fixed.
+ def wpa_cli_status(self):
+ """Fallback for wpa_supplicant control interface status requests."""
+ try:
+ return subprocess.check_output(['wpa_cli', '-i', self.name, 'status'])
+ except subprocess.CalledProcessError:
+ logging.error('wpa_cli status request failed')
+ return ''
+
class FrenzyWPACtrl(object):
"""A WPACtrl for Frenzy devices.
diff --git a/conman/interface_test.py b/conman/interface_test.py
index dd5e037..e8f0876 100755
--- a/conman/interface_test.py
+++ b/conman/interface_test.py
@@ -164,12 +164,7 @@
if request_type == 'STATUS':
if self.request_status_fails:
raise wpactrl.error('test error')
- 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'
+ return self.wpa_cli_status_testonly()
else:
raise ValueError('Invalid request_type %s' % request_type)
@@ -198,6 +193,14 @@
if not os.path.exists(self._socket):
raise wpactrl.error(msg)
+ def wpa_cli_status_testonly(self):
+ 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'
+
class Wifi(FakeInterfaceMixin, interface.Wifi):
"""Fake Wifi for testing."""
@@ -247,6 +250,11 @@
self._secure_testonly = False
super(Wifi, self).detach_wpa_control()
+ def wpa_cli_status(self):
+ # This is just a convenient way of keeping things dry; the actual wpa_cli
+ # status makes a subprocess call which returns the same string.
+ return self._wpa_control.wpa_cli_status_testonly()
+
def start_wpa_supplicant_testonly(self, path):
wpa_socket = os.path.join(path, self.name)
logging.debug('Starting fake wpa_supplicant for %s: %s',
@@ -296,12 +304,6 @@
self.add_terminating_event()
super(FrenzyWPACtrl, self).detach()
- def request(self, request_type):
- if request_type == 'STATUS' and self.request_status_fails:
- raise wpactrl.error('test error')
-
- return super(FrenzyWPACtrl, self).request(request_type)
-
class FrenzyWifi(FakeInterfaceMixin, interface.FrenzyWifi):
WPACtrl = FrenzyWPACtrl
@@ -520,7 +522,7 @@
wvtest.WVPASSNE(w.wpa_status(), {})
w._wpa_control.request_status_fails = True
- wvtest.WVPASSEQ(w.wpa_status(), {})
+ wvtest.WVPASSNE(w.wpa_status(), {})
# The wpa_supplicant process disconnects and terminates.
wpa_control.add_disconnected_event()
diff --git a/hnvram/hnvram_main.c b/hnvram/hnvram_main.c
index eb0f306..1148bb0 100644
--- a/hnvram/hnvram_main.c
+++ b/hnvram/hnvram_main.c
@@ -92,6 +92,8 @@
{"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},
+ {"ANDROID_ACTIVE_PARTITION", NVRAM_FIELD_ANDROID_ACTIVE_PARTITION,
+ HNVRAM_STRING},
};
const hnvram_field_t* get_nvram_field(const char* name) {
diff --git a/logupload/client/debian/init b/logupload/client/debian/init
index b349b5e..bbf7cad 100755
--- a/logupload/client/debian/init
+++ b/logupload/client/debian/init
@@ -36,10 +36,11 @@
# Our hostnames are something like hostname.cluster.whatever.com.
# We want the hostname.cluster part to be part of the certname, but
# sadly the name field in our certs isn't super happy about that, so
- # let's use _ instead of dot, where relevant.
+ # let's use _ instead of dot and strip hyphens, where relevant.
hostname -f |
sed -e 's/\([^.]*\.[^.]*\).*/\1/' \
- -e 's/\./_/g' |
+ -e 's/\./_/g' \
+ -e 's/-//g' |
atomic_stdin /tmp/serial
cd /
upload-logs-loop </dev/null &