OLD | NEW |
1 # Copyright (c) 2010 The Chromium OS Authors. All rights reserved. | 1 # Copyright (c) 2010 The Chromium OS Authors. All rights reserved. |
2 # Use of this source code is governed by a BSD-style license that can be | 2 # Use of this source code is governed by a BSD-style license that can be |
3 # found in the LICENSE file. | 3 # found in the LICENSE file. |
4 | 4 |
5 import re | 5 import re |
6 | 6 |
7 def isLinuxRouter(router): | 7 def isLinuxRouter(router): |
8 router_uname = router.run('uname').stdout | 8 router_uname = router.run('uname').stdout |
9 return re.search('Linux', router_uname) | 9 return re.search('Linux', router_uname) |
10 | 10 |
11 class LinuxRouter(object): | 11 class LinuxRouter(object): |
12 """ | 12 """ |
13 Linux/mac80211-style WiFi Router support for WiFiTest class. | 13 Linux/mac80211-style WiFi Router support for WiFiTest class. |
14 | 14 |
15 This class implements test methods/steps that communicate with a | 15 This class implements test methods/steps that communicate with a |
16 router implemented with Linux/mac80211. The router must | 16 router implemented with Linux/mac80211. The router must |
17 be pre-configured to enable ssh access and have a mac80211-based | 17 be pre-configured to enable ssh access and have a mac80211-based |
18 wireless device. We also assume hostapd 0.7.x and iw are present | 18 wireless device. We also assume hostapd 0.7.x and iw are present |
19 and any necessary modules are pre-loaded. | 19 and any necessary modules are pre-loaded. |
20 """ | 20 """ |
21 | 21 |
22 | 22 |
23 def __init__(self, host, params, defssid): | 23 def __init__(self, host, params, defssid): |
| 24 # Command locations. |
24 self.cmd_iw = "/usr/sbin/iw" | 25 self.cmd_iw = "/usr/sbin/iw" |
25 self.cmd_ip = "/usr/sbin/ip" | 26 self.cmd_ip = "/usr/sbin/ip" |
26 self.cmd_brctl = "/usr/sbin/brctl" | 27 self.cmd_brctl = "/usr/sbin/brctl" |
27 self.cmd_hostapd = "/usr/sbin/hostapd" | 28 self.cmd_hostapd = "/usr/sbin/hostapd" |
28 | 29 |
| 30 # Router host. |
29 self.router = host | 31 self.router = host |
30 # default to 1st available wireless nic | 32 |
| 33 # Network interfaces. |
| 34 self.bridgeif = params.get('bridgeif', "br-lan") |
| 35 self.wiredif = params.get('wiredif', "eth1") |
| 36 self.wlanif = "wlan0" |
| 37 |
| 38 # Default to 1st available wireless phy. |
31 if "phydev" not in params: | 39 if "phydev" not in params: |
32 output = self.router.run("%s list" % self.cmd_iw).stdout | 40 output = self.router.run("%s list" % self.cmd_iw).stdout |
33 wifitest = re.compile("Wiphy (.*)") | 41 test = re.compile("Wiphy (.*)") |
34 for line in output.splitlines(): | 42 for line in output.splitlines(): |
35 m = wifitest.match(line) | 43 m = test.match(line) |
36 if m: | 44 if m: |
37 self.phydev = m.group(1) | 45 self.phydev = m.group(1) |
38 break | 46 break |
39 else: | 47 else: |
40 raise Exception("No Wireless NIC detected on the device") | 48 raise Exception("No Wireless NIC detected on the device") |
41 else: | 49 else: |
42 self.phydev = params['phydev'] | 50 self.phydev = params['phydev'] |
43 | 51 |
44 self.hostapd_conf = "/tmp/%s.conf" % self.phydev | 52 |
45 self.hostapd_driver = "nl80211" | 53 # hostapd configuration persists throughout the test, subsequent |
46 self.phytype = None | 54 # 'config' commands only modify it. |
47 self.bridgeif = params.get("bridgeif", "br-lan") | 55 self.hostapd = { |
48 self.wlanif = "wlan0" | 56 'configured': False, |
49 self.defssid = defssid; | 57 'file': "/tmp/%s.conf" % self.phydev, |
| 58 'driver': "nl80211", |
| 59 'conf': { |
| 60 'ssid': defssid, |
| 61 'interface': self.wlanif, |
| 62 'bridge': self.bridgeif |
| 63 } |
| 64 } |
| 65 |
| 66 # Kill hostapd if already running. |
| 67 self.router.run("pkill hostapd >/dev/null 2>&1", ignore_status=True) |
| 68 |
| 69 # Remove all bridges. |
| 70 output = self.router.run("%s show" % self.cmd_brctl).stdout |
| 71 test = re.compile("^(\S+).*") |
| 72 for line in output.splitlines()[1:]: |
| 73 m = test.match(line) |
| 74 if m: |
| 75 device = m.group(1) |
| 76 self.router.run("%s link set %s down" % (self.cmd_ip, device)) |
| 77 self.router.run("%s delbr %s" % (self.cmd_brctl, device)) |
| 78 |
| 79 # Remove all wifi devices. |
| 80 output = self.router.run("%s dev" % self.cmd_iw).stdout |
| 81 test = re.compile("[\s]*Interface (.*)") |
| 82 for line in output.splitlines(): |
| 83 m = test.match(line) |
| 84 if m: |
| 85 device = m.group(1) |
| 86 self.router.run("%s link set %s down" % (self.cmd_ip, device)) |
| 87 self.router.run("%s dev %s del" % (self.cmd_iw, device)) |
50 | 88 |
51 | 89 |
52 def create(self, params): | 90 def create(self, params): |
53 """ Create a wifi device of the specified type """ | 91 """ Create a wifi device of the specified type """ |
54 # | 92 # |
55 # AP mode is handled entirely by hostapd so we only | 93 # AP mode is handled entirely by hostapd so we only |
56 # have to setup others (mapping the bsd type to what | 94 # have to setup others (mapping the bsd type to what |
57 # iw wants) | 95 # iw wants) |
58 # | 96 # |
59 # map from bsd types to iw types | 97 # map from bsd types to iw types |
60 if params['type'] == "ap" or params['type'] == "hostap": | 98 if params['type'] == "ap" or params['type'] == "hostap": |
61 self.apmode = True | 99 self.apmode = True |
62 self.phytype = { | 100 phytype = { |
63 "sta" : "managed", | 101 "sta" : "managed", |
64 "monitor" : "monitor", | 102 "monitor" : "monitor", |
65 "adhoc" : "adhoc", | 103 "adhoc" : "adhoc", |
66 "ibss" : "ibss", | 104 "ibss" : "ibss", |
67 "ap" : "managed", # NB: handled by hostapd | 105 "ap" : "managed", # NB: handled by hostapd |
68 "hostap" : "managed", # NB: handled by hostapd | 106 "hostap" : "managed", # NB: handled by hostapd |
69 "mesh" : "mesh", | 107 "mesh" : "mesh", |
70 "wds" : "wds", | 108 "wds" : "wds", |
71 }[params['type']] | 109 }[params['type']] |
72 phydev = params.get('phydev', self.phydev) | 110 phydev = params.get('phydev', self.phydev) |
73 self.router.run("%s phy %s interface add %s type %s" % | 111 self.router.run("%s phy %s interface add %s type %s" % |
74 (self.cmd_iw, phydev, self.wlanif, self.phytype)) | 112 (self.cmd_iw, phydev, self.wlanif, phytype)) |
| 113 |
75 | 114 |
76 | 115 |
77 def destroy(self, params): | 116 def destroy(self, params): |
78 """ Destroy a previously created device """ | 117 """ Destroy a previously created device """ |
79 self.router.run("%s dev %s del" % (self.cmd_iw, self.wlanif)) | 118 # For linux, this is the same as deconfig. |
| 119 self.deconfig(params) |
| 120 |
80 | 121 |
81 | 122 |
82 def config(self, params): | 123 def config(self, params): |
83 """ Configure the AP per test requirements """ | 124 """ Configure the AP per test requirements """ |
84 | 125 |
| 126 if self.hostapd['configured']: |
| 127 self.deconfig({}) |
| 128 |
85 if self.apmode: | 129 if self.apmode: |
86 # construct the hostapd.conf file and start hostapd | 130 # Construct the hostapd.conf file and start hostapd. |
87 hostapd_args = ["interface=%s" % self.wlanif] | 131 conf = self.hostapd['conf'] |
88 hostapd_args.append("bridge=%s" % self.bridgeif) | 132 htcaps = set() |
89 hostapd_args.append("driver=%s" % | 133 |
90 params.get("hostapd_driver", self.hostapd_driver)) | 134 conf['driver'] = params.get('hostapd_driver', self.hostapd['driver']
) |
91 if 'ssid' not in params: | 135 |
92 params['ssid'] = self.defssid | |
93 wmm = 0 | |
94 htcaps = None | |
95 for k, v in params.iteritems(): | 136 for k, v in params.iteritems(): |
96 if k == 'ssid': | 137 if k == 'ssid': |
97 hostapd_args.append("ssid=%s" % v) | 138 conf['ssid'] = v |
98 elif k == 'channel': | 139 elif k == 'channel': |
99 freq = int(v) | 140 freq = int(v) |
100 if freq >= 2412 and freq <= 2472: | 141 |
101 chan = 1 + (freq - 2412) / 5 | 142 # 2.4GHz |
102 elif freq == 2484: | 143 if freq < 2500: |
103 chan = 14 | 144 if conf['hw_mode'] == 'a': |
104 elif freq >= 4915 and freq <= 4980: | 145 conf['hw_mode'] = 'b' |
105 chan = 183 + (freq - 4915) / 5 | 146 |
106 elif freq >= 5035 and freq <= 5825: | 147 # Freq = 5 * chan + 2407 |
107 chan = 7 + (freq - 5025) / 5 | 148 if freq >= 2412 and freq <= 2472: |
| 149 conf['channel'] = (freq - 2407) / 5 |
| 150 # Channel 14 is an exception |
| 151 elif freq == 2484: |
| 152 conf['channel'] = 14 |
| 153 # 5GHz |
108 else: | 154 else: |
109 chan = -1 | 155 conf['hw_mode'] = 'a' |
110 hostapd_args.append("channel=%s" % chan) | 156 # Freq = 5 * chan + 4000 |
| 157 if freq >= 4915 and freq <= 4980: |
| 158 conf['channel'] = 183 + (freq - 4915) / 5 |
| 159 # Freq = 5 * chan + 5000 |
| 160 elif freq >= 5035 and freq <= 5825: |
| 161 conf['channel'] = (freq - 5000) / 5 |
| 162 |
111 elif k == 'country': | 163 elif k == 'country': |
112 hostapd_args.append("country_code=%s" % v) | 164 conf['country_code'] = v |
113 elif k == 'dotd': | 165 elif k == 'dotd': |
114 hostapd_args.append("ieee80211d=1") | 166 conf['ieee80211d'] = 1 |
115 elif k == '-dotd': | 167 elif k == '-dotd': |
116 hostapd_args.append("ieee80211d=0") | 168 conf['ieee80211d'] = 0 |
117 elif k == 'mode': | 169 elif k == 'mode': |
118 if v == '11a': | 170 if v == '11a': |
119 hostapd_args.append("hw_mode=a") | 171 conf['hw_mode'] = 'a' |
120 elif v == '11g': | 172 elif v == '11g': |
121 hostapd_args.append("hw_mode=g") | 173 conf['hw_mode'] = 'g' |
122 elif v == '11b': | 174 elif v == '11b': |
123 hostapd_args.append("hw_mode=b") | 175 conf['hw_mode'] = 'b' |
124 elif v == '11n': | 176 elif v == '11n': |
125 hostapd_args.append("ieee80211n=1") | 177 conf['ieee80211n'] = 1 |
126 elif k == 'bintval': | 178 elif k == 'bintval': |
127 hostapd_args.append("beacon_int=%s" % v) | 179 conf['beacon_int'] = v |
128 elif k == 'dtimperiod': | 180 elif k == 'dtimperiod': |
129 hostapd_args.append("dtim_period=%s" % v) | 181 conf['dtim_period'] = v |
130 elif k == 'rtsthreshold': | 182 elif k == 'rtsthreshold': |
131 hostapd_args.append("rts_threshold=%s" % v) | 183 conf['rts_threshold'] = v |
132 elif k == 'fragthreshold': | 184 elif k == 'fragthreshold': |
133 hostapd_args.append("fragm_threshold=%s" % v) | 185 conf['fragm_threshold'] = v |
134 elif k == 'shortpreamble': | 186 elif k == 'shortpreamble': |
135 hostapd_args.append("preamble=1") | 187 conf['preamble'] = 1 |
136 elif k == 'authmode': | 188 elif k == 'authmode': |
137 if v == 'open': | 189 if v == "open": |
138 hostapd_args.append("auth_algs=1") | 190 conf['auth_algs'] = 1 |
139 elif v == 'shared': | 191 elif v == "shared": |
140 hostapd_args.append("auth_algs=2") | 192 conf['auth_algs'] = 2 |
141 elif k == 'hidessid': | 193 elif k == 'hidessid': |
142 hostapd_args.append("ignore_broadcast_ssid=1") | 194 conf['ignore_broadcast_ssid'] = 1 |
143 elif k == 'wme': | 195 elif k == 'wme': |
144 wmm = 1; | 196 conf['wmm_enabled'] = 1 |
145 elif k == '-wme': | 197 elif k == '-wme': |
146 wmm = 0; | 198 conf['wmm_enabled'] = 0 |
147 elif k == 'deftxkey': | 199 elif k == 'deftxkey': |
148 hostapd_args.append("wep_default_key=%s" % v) | 200 conf['wep_default_key'] = v |
149 elif k == 'ht20': | 201 elif k == 'ht20': |
150 htcaps.append("") | 202 conf['wmm_enabled'] = 1 |
151 wmm = 1; | |
152 elif k == 'ht40': | 203 elif k == 'ht40': |
153 htcaps.append("[HT40-][HT40+]") | 204 htcaps.add('[HT40-]') |
154 wmm = 1 | 205 htcaps.add('[HT40+]') |
155 # XXX no support elif k == 'rifs': | 206 conf['wmm_enabled'] = 1 |
156 elif k == 'shortgi': | 207 elif k == 'shortgi': |
157 htcaps.append("[SHORT-GI-20][SHORT-GI-40]") | 208 htcaps.add('[SHORT-GI-20]') |
| 209 htcaps.add('[SHORT-GI-40]') |
| 210 elif k == 'pureg' or k == 'puren' or k == 'wepmode' or k == 'rif
s': |
| 211 # no support |
| 212 pass |
158 else: | 213 else: |
159 hostapd_args.append("%s=%s" % (k, v)) | 214 conf[k] = v |
160 | 215 |
161 if htcaps is not None: | 216 # Aggregate ht_capab. |
162 hostapd_args.append("ieee80211n=1") | 217 if htcaps: |
163 hostapd_args.append("ht_capab=%s" % htcaps) | 218 conf['ieee80211n'] = 1 |
164 hostapd_args.append("wmm_enabled=%d" % wmm) | 219 conf['ht_capab'] = ''.join(htcaps) |
165 | 220 |
| 221 # Generate hostapd.conf. |
166 self.router.run("cat <<EOF >%s\n%s\nEOF\n" % | 222 self.router.run("cat <<EOF >%s\n%s\nEOF\n" % |
167 (self.hostapd_conf, "\n".join(hostapd_args))) | 223 (self.hostapd['file'], '\n'.join( |
| 224 "%s=%s" % kv for kv in conf.iteritems()))) |
| 225 |
| 226 # Run hostapd. |
168 self.router.run("%s -B %s" % | 227 self.router.run("%s -B %s" % |
169 (self.cmd_hostapd, self.hostapd_conf)) | 228 (self.cmd_hostapd, self.hostapd['file'])) |
| 229 |
| 230 # Set up the bridge. |
| 231 self.router.run("%s setfd %s %d" % |
| 232 (self.cmd_brctl, self.bridgeif, 0)) |
| 233 self.router.run("%s addif %s %s" % |
| 234 (self.cmd_brctl, self.bridgeif, self.wiredif)) |
| 235 self.router.run("%s link set %s up" % |
| 236 (self.cmd_ip, self.wiredif)) |
| 237 self.router.run("%s link set %s up" % |
| 238 (self.cmd_ip, self.bridgeif)) |
170 | 239 |
171 # else: | 240 # else: |
172 # # use iw to manually configure interface | 241 # # use iw to manually configure interface |
173 | 242 |
| 243 self.hostapd['configured'] = True |
174 | 244 |
175 | 245 |
176 def deconfig(self, params): | 246 def deconfig(self, params): |
177 """ De-configure the AP (typically marks wlanif down) """ | 247 """ De-configure the AP (will also bring wlan and the bridge down) """ |
178 | 248 |
179 self.router.run("%s link set %s down" % (self.cmd_ip, self.wlanif)) | 249 if not self.hostapd['configured']: |
180 if self.hostapd_conf is not None: | 250 return |
181 self.router.run("pkill hostapd >/dev/null 2>&1") | |
182 self.router.run("rm -f %s" % self.hostapd_conf) | |
183 self.hostapd_conf = None | |
184 | 251 |
| 252 # Taking down hostapd takes wlan0 and mon.wlan0 down. |
| 253 self.router.run("pkill hostapd >/dev/null 2>&1", ignore_status=True) |
| 254 # self.router.run("rm -f %s" % self.hostapd['file']) |
| 255 |
| 256 # Tear down the bridge. |
| 257 self.router.run("%s link set %s down" % (self.cmd_ip, self.bridgeif), |
| 258 ignore_status=True) |
| 259 self.router.run("%s delbr %s" % (self.cmd_brctl, self.bridgeif), |
| 260 ignore_status=True) |
| 261 |
| 262 self.hostapd['configured'] = False |
185 | 263 |
186 def client_check_config(self, params): | 264 def client_check_config(self, params): |
187 """ | 265 """ |
188 Check network configuration on client to verify parameters | 266 Check network configuration on client to verify parameters |
189 have been negotiated during the connection to the router. | 267 have been negotiated during the connection to the router. |
190 """ | 268 """ |
191 # XXX fill in | 269 # XXX fill in |
OLD | NEW |