Merge "platform: Added ability to add/remove hnvram vars"
diff --git a/conman/connection_manager.py b/conman/connection_manager.py
index 040e812..3808458 100755
--- a/conman/connection_manager.py
+++ b/conman/connection_manager.py
@@ -170,6 +170,7 @@
subprocess.check_output(command, stderr=subprocess.STDOUT, env=env)
except subprocess.CalledProcessError as e:
logging.error('Failed to start wifi client: %s', e.output)
+ self._status.wlan_failed = True
return False
return True
@@ -212,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']
@@ -295,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)):
@@ -583,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.
@@ -689,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 707890b..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."""
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/conman/status.py b/conman/status.py
index e21dc01..7f75682 100644
--- a/conman/status.py
+++ b/conman/status.py
@@ -20,6 +20,7 @@
TRYING_OPEN = 'TRYING_OPEN'
TRYING_WLAN = 'TRYING_WLAN'
+ WLAN_FAILED = 'WLAN_FAILED'
CONNECTED_TO_OPEN = 'CONNECTED_TO_OPEN'
CONNECTED_TO_WLAN = 'CONNECTED_TO_WLAN'
HAVE_CONFIG = 'HAVE_CONFIG'
@@ -43,6 +44,10 @@
(),
(P.TRYING_OPEN, P.CONNECTED_TO_OPEN, P.CONNECTED_TO_WLAN)
),
+ P.WLAN_FAILED: (
+ (),
+ (P.TRYING_WLAN, P.CONNECTED_TO_WLAN)
+ ),
P.CONNECTED_TO_OPEN: (
(),
(P.CONNECTED_TO_WLAN, P.TRYING_OPEN, P.TRYING_WLAN)
diff --git a/ginstall/ginstall.py b/ginstall/ginstall.py
index 5db5493..81be82a 100755
--- a/ginstall/ginstall.py
+++ b/ginstall/ginstall.py
@@ -625,6 +625,14 @@
'gftv200-39-pre1 and before.')
+def CheckMultiLoader(manifest):
+ """Check if this ginstall image supports platform-named loaders."""
+ multiloader = manifest.get('multiloader')
+ if not multiloader:
+ return False
+ return True
+
+
class ProgressBar(object):
"""Progress bar that prints one dot per 1MB."""
@@ -863,6 +871,12 @@
CheckMinimumVersion(manifest)
CheckMisc(manifest)
+ loader_bin_list = ['loader.img', 'loader.bin']
+ loader_sig_list = ['loader.sig']
+ if CheckMultiLoader(manifest):
+ loader_bin_list = ['loader.%s.bin' % GetPlatform().lower()]
+ loader_sig_list = ['loader.%s.sig' % GetPlatform().lower()]
+
uloader = loader = None
uloadersig = FileWithSecureHash(StringIO.StringIO(''), 'badsig')
loadersig = FileWithSecureHash(StringIO.StringIO(''), 'badsig')
@@ -878,10 +892,10 @@
elif ti.name.startswith('rootfs.'):
fh = FileWithSecureHash(tar.extractfile(ti), secure_hash)
InstallRootfs(fh, partition)
- elif ti.name in ['loader.img', 'loader.bin']:
+ elif ti.name in loader_bin_list:
buf = StringIO.StringIO(tar.extractfile(ti).read())
loader = FileWithSecureHash(buf, secure_hash)
- elif ti.name == 'loader.sig':
+ elif ti.name in loader_sig_list:
buf = StringIO.StringIO(tar.extractfile(ti).read())
loadersig = FileWithSecureHash(buf, secure_hash)
elif ti.name == 'uloader.img':
@@ -927,7 +941,10 @@
pass
try:
- return open(path)
+ if path == '-':
+ return sys.stdin
+ else:
+ return open(path)
except ValueError:
pass
diff --git a/ginstall/ginstall_test.py b/ginstall/ginstall_test.py
index 8deac64..a4e3c8a 100755
--- a/ginstall/ginstall_test.py
+++ b/ginstall/ginstall_test.py
@@ -21,6 +21,7 @@
import shutil
import StringIO
import struct
+import sys
import tempfile
import unittest
import ginstall
@@ -365,6 +366,21 @@
total = ginstall.GetMemTotal()
self.assertTrue(total < 4*1e9)
+ def testOpenPathOrUrl(self):
+ # URL
+ two_oh_four = ginstall.OpenPathOrUrl('http://www.gstatic.com/generate_204')
+ self.assertEqual(204, two_oh_four.getcode())
+
+ # on-disk file
+ on_disk_file = tempfile.NamedTemporaryFile()
+ testdata = os.urandom(16)
+ on_disk_file.write(testdata)
+ on_disk_file.flush()
+ self.assertEqual(ginstall.OpenPathOrUrl(on_disk_file.name).read(), testdata)
+
+ # stdin (-)
+ self.assertEqual(ginstall.OpenPathOrUrl('-'), sys.stdin)
+
if __name__ == '__main__':
unittest.main()
diff --git a/ginstall/install_test.sh b/ginstall/install_test.sh
index 4338bac..ec372c5 100755
--- a/ginstall/install_test.sh
+++ b/ginstall/install_test.sh
@@ -5,6 +5,7 @@
tmpdir="$(mktemp -d)"
export PATH="$tmpdir/bin:${PATH}"
export GINSTALL_OUT_FILE="$tmpdir/out"
+psiz=$(stat --format=%s testdata/img/loader.gflt110.bin)
lsiz=$(stat --format=%s testdata/img/loader.img)
ksiz=$(stat --format=%s testdata/img/kernel.img)
rsiz=$(stat --format=%s testdata/img/rootfs.img)
@@ -195,6 +196,45 @@
+# GFLT110 with gflt110.bin loader.
+echo; echo; echo GFLT110 with platform loader
+setup_fakeroot GFLT110
+expected="\
+psback
+logos ginstall
+flash_unlock ${tmpdir}/dev/mtd6
+flash_erase --quiet ${tmpdir}/dev/mtd6 0 0
+flash_unlock ${tmpdir}/dev/mtd0
+flash_erase --quiet ${tmpdir}/dev/mtd0 0 0
+hnvram -q -w ACTIVATED_KERNEL_NAME=kernel0"
+
+WVPASS ./ginstall.py --basepath="$tmpdir" --tar=./testdata/img/image_gflt110_platform_loader.gi --partition=primary --skiploadersig
+WVPASSEQ "$expected" "$(cat $GINSTALL_OUT_FILE)"
+WVPASS cmp --bytes="$psiz" "${tmpdir}/dev/mtd0" testdata/img/loader.gflt110.bin
+WVPASS cmp --bytes="$ksiz" "${tmpdir}/dev/mtd6" testdata/img/kernel.img
+
+
+
+# GFLT110 with both loaders with the MANIFEST containing "multiloader: 1"
+echo; echo; echo GFLT110 with both loaders
+setup_fakeroot GFLT110
+expected="\
+psback
+logos ginstall
+flash_unlock ${tmpdir}/dev/mtd6
+flash_erase --quiet ${tmpdir}/dev/mtd6 0 0
+flash_unlock ${tmpdir}/dev/mtd0
+flash_erase --quiet ${tmpdir}/dev/mtd0 0 0
+hnvram -q -w ACTIVATED_KERNEL_NAME=kernel0"
+
+WVPASS ./ginstall.py --basepath="$tmpdir" --tar=./testdata/img/image_gflt110_both_loaders.gi --partition=primary --skiploadersig
+WVPASSEQ "$expected" "$(cat $GINSTALL_OUT_FILE)"
+WVFAIL cmp --bytes="$lsiz" "${tmpdir}/dev/mtd0" testdata/img/loader.bin
+WVPASS cmp --bytes="$psiz" "${tmpdir}/dev/mtd0" testdata/img/loader.gflt110.bin
+WVPASS cmp --bytes="$ksiz" "${tmpdir}/dev/mtd6" testdata/img/kernel.img
+
+
+
echo; echo; echo MANIFEST with Bad checksums
setup_fakeroot GFHD100
echo "This should not be touched" >"${tmpdir}/dev/mtd0"
diff --git a/ginstall/testdata/img/image_gflt110_both_loaders.gi b/ginstall/testdata/img/image_gflt110_both_loaders.gi
new file mode 100644
index 0000000..62f2a03
--- /dev/null
+++ b/ginstall/testdata/img/image_gflt110_both_loaders.gi
Binary files differ
diff --git a/ginstall/testdata/img/image_gflt110_platform_loader.gi b/ginstall/testdata/img/image_gflt110_platform_loader.gi
new file mode 100644
index 0000000..8d7ed9b
--- /dev/null
+++ b/ginstall/testdata/img/image_gflt110_platform_loader.gi
Binary files differ
diff --git a/ginstall/testdata/img/loader.gflt110.bin b/ginstall/testdata/img/loader.gflt110.bin
new file mode 100644
index 0000000..a95b6ae
--- /dev/null
+++ b/ginstall/testdata/img/loader.gflt110.bin
@@ -0,0 +1 @@
+gflt110.bin
\ No newline at end of file
diff --git a/signing/S99readallfiles b/signing/S99readallfiles
index 87b97e0..00838fa 100755
--- a/signing/S99readallfiles
+++ b/signing/S99readallfiles
@@ -27,8 +27,11 @@
case "$1" in
start)
(
- nice -n 19 readallfiles -q / &&
- clear_failure_count
+ if is-fiberjack; then
+ nice -n 19 readallfiles -q /config && clear_failure_count
+ else
+ nice -n 19 readallfiles -q / && clear_failure_count
+ fi
) 2>&1 | logos readall &
;;
stop)
diff --git a/wifi/configs.py b/wifi/configs.py
index 743fe10..97a27ce 100644
--- a/wifi/configs.py
+++ b/wifi/configs.py
@@ -375,7 +375,7 @@
utils.validate_and_sanitize_bssid(opt.bssid))
network_block = make_network_block(network_block_lines)
- freq_list = ','.join(autochannel.get_all_frequencies(opt.band))
+ freq_list = ' '.join(autochannel.get_all_frequencies(opt.band))
lines = [
'ctrl_interface=/var/run/wpa_supplicant',
diff --git a/wifi/configs_test.py b/wifi/configs_test.py
index 64e05c6..016fc27 100755
--- a/wifi/configs_test.py
+++ b/wifi/configs_test.py
@@ -11,9 +11,9 @@
_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'),
+ '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'),
}