blob: 0e89b8fbc133a25e2e2e35b5d512448441e42c9e [file] [log] [blame]
Sam Leffler6969d1d2010-03-15 16:07:11 -07001# 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
3# found in the LICENSE file.
4
Paul Stewart310928c2010-09-07 11:54:11 -07005import logging, re, time
Paul Stewartc9628b32010-08-11 13:03:51 -07006from autotest_lib.client.common_lib import error
Sam Leffler19bb0a72010-04-12 08:51:08 -07007
8def isLinuxRouter(router):
9 router_uname = router.run('uname').stdout
10 return re.search('Linux', router_uname)
11
Sam Leffler6969d1d2010-03-15 16:07:11 -070012class LinuxRouter(object):
13 """
14 Linux/mac80211-style WiFi Router support for WiFiTest class.
15
16 This class implements test methods/steps that communicate with a
17 router implemented with Linux/mac80211. The router must
18 be pre-configured to enable ssh access and have a mac80211-based
19 wireless device. We also assume hostapd 0.7.x and iw are present
20 and any necessary modules are pre-loaded.
21 """
22
23
24 def __init__(self, host, params, defssid):
Nebojsa Sabovic4cc2ce92010-04-21 15:08:01 -070025 # Command locations.
Nebojsa Sabovic138ff912010-04-06 15:47:42 -070026 self.cmd_iw = "/usr/sbin/iw"
27 self.cmd_ip = "/usr/sbin/ip"
28 self.cmd_brctl = "/usr/sbin/brctl"
29 self.cmd_hostapd = "/usr/sbin/hostapd"
30
Nebojsa Sabovic4cc2ce92010-04-21 15:08:01 -070031 # Router host.
Sam Leffler6969d1d2010-03-15 16:07:11 -070032 self.router = host
Nebojsa Sabovic4cc2ce92010-04-21 15:08:01 -070033
34 # Network interfaces.
Nebojsa Sabovic36c10e92010-04-28 12:37:20 -070035 self.bridgeif = params.get('bridgedev', "br-lan")
Paul Stewartc9628b32010-08-11 13:03:51 -070036 self.wiredif = params.get('wiredev', "eth0")
Nebojsa Sabovic60ae1462010-05-07 16:14:45 -070037 self.wlanif2 = "wlan2"
38 self.wlanif5 = "wlan5"
Nebojsa Sabovic4cc2ce92010-04-21 15:08:01 -070039
Paul Stewartc9628b32010-08-11 13:03:51 -070040 # Parse the output of 'iw phy' and find a device for each frequency
Nebojsa Sabovic60ae1462010-05-07 16:14:45 -070041 if "phydev2" not in params:
Nebojsa Sabovic138ff912010-04-06 15:47:42 -070042 output = self.router.run("%s list" % self.cmd_iw).stdout
Paul Stewartc9628b32010-08-11 13:03:51 -070043 re_wiphy = re.compile("Wiphy (.*)")
44 re_mhz = re.compile("(\d+) MHz")
45 in_phy = False
46 self.phydev2 = None
47 self.phydev5 = None
Nebojsa Sabovic138ff912010-04-06 15:47:42 -070048 for line in output.splitlines():
Paul Stewartc9628b32010-08-11 13:03:51 -070049 match_wiphy = re_wiphy.match(line)
50 if match_wiphy:
51 in_phy = True
52 widevname = match_wiphy.group(1)
53 elif in_phy:
54 if line[0] == '\t':
55 match_mhz = re_mhz.search(line)
56 if match_mhz:
57 mhz = int(match_mhz.group(1))
58 if self.phydev2 is None and \
59 mhz in range(2402,2472,5):
60 self.phydev2 = widevname
61 elif self.phydev5 is None and \
62 mhz in range(5100,6000,20):
63 self.phydev5 = widevname
64 if None not in (self.phydev2, self.phydev5):
65 break
66 else:
67 in_phy = False
Nebojsa Sabovic138ff912010-04-06 15:47:42 -070068 else:
Nebojsa Sabovicbae8fd42010-04-23 13:47:23 -070069 raise error.TestFail("No Wireless NIC detected on the device")
Nebojsa Sabovic138ff912010-04-06 15:47:42 -070070 else:
Nebojsa Sabovic60ae1462010-05-07 16:14:45 -070071 self.phydev2 = params['phydev2']
72 self.phydev5 = params.get('phydev5', self.phydev2)
Nebojsa Sabovic138ff912010-04-06 15:47:42 -070073
Nebojsa Sabovic4cc2ce92010-04-21 15:08:01 -070074
75 # hostapd configuration persists throughout the test, subsequent
76 # 'config' commands only modify it.
Paul Stewart7cb1f062010-06-10 15:46:20 -070077 self.defssid = defssid
Nebojsa Sabovic4cc2ce92010-04-21 15:08:01 -070078 self.hostapd = {
79 'configured': False,
Nebojsa Sabovic60ae1462010-05-07 16:14:45 -070080 'file': "/tmp/hostapd-test.conf",
Nebojsa Sabovic4cc2ce92010-04-21 15:08:01 -070081 'driver': "nl80211",
82 'conf': {
83 'ssid': defssid,
Nebojsa Sabovic4a7a5a12010-04-29 14:29:51 -070084 'bridge': self.bridgeif,
85 'hw_mode': 'g'
Nebojsa Sabovic4cc2ce92010-04-21 15:08:01 -070086 }
87 }
Paul Stewartc2b3de82011-03-03 14:45:31 -080088 self.station = {
89 'configured': False,
90 'conf': {
91 'ssid': defssid,
92 }
93 }
Nebojsa Sabovic4cc2ce92010-04-21 15:08:01 -070094 # Kill hostapd if already running.
95 self.router.run("pkill hostapd >/dev/null 2>&1", ignore_status=True)
96
97 # Remove all bridges.
98 output = self.router.run("%s show" % self.cmd_brctl).stdout
99 test = re.compile("^(\S+).*")
100 for line in output.splitlines()[1:]:
101 m = test.match(line)
102 if m:
103 device = m.group(1)
104 self.router.run("%s link set %s down" % (self.cmd_ip, device))
105 self.router.run("%s delbr %s" % (self.cmd_brctl, device))
106
107 # Remove all wifi devices.
108 output = self.router.run("%s dev" % self.cmd_iw).stdout
109 test = re.compile("[\s]*Interface (.*)")
110 for line in output.splitlines():
111 m = test.match(line)
112 if m:
113 device = m.group(1)
114 self.router.run("%s link set %s down" % (self.cmd_ip, device))
115 self.router.run("%s dev %s del" % (self.cmd_iw, device))
Sam Leffler6969d1d2010-03-15 16:07:11 -0700116
Nebojsa Sabovicbc245c62010-04-28 16:58:50 -0700117 # Place us in the US by default
118 self.router.run("%s reg set US" % self.cmd_iw)
Sam Leffler6969d1d2010-03-15 16:07:11 -0700119
120 def create(self, params):
121 """ Create a wifi device of the specified type """
122 #
123 # AP mode is handled entirely by hostapd so we only
124 # have to setup others (mapping the bsd type to what
125 # iw wants)
126 #
127 # map from bsd types to iw types
Paul Stewartc2b3de82011-03-03 14:45:31 -0800128 self.apmode = params['type'] in ("ap", "hostap")
129 if not self.apmode:
130 self.station['type'] = params['type']
Nebojsa Sabovic4cc2ce92010-04-21 15:08:01 -0700131 phytype = {
Sam Leffler6969d1d2010-03-15 16:07:11 -0700132 "sta" : "managed",
133 "monitor" : "monitor",
134 "adhoc" : "adhoc",
135 "ibss" : "ibss",
Nebojsa Sabovic138ff912010-04-06 15:47:42 -0700136 "ap" : "managed", # NB: handled by hostapd
137 "hostap" : "managed", # NB: handled by hostapd
Sam Leffler6969d1d2010-03-15 16:07:11 -0700138 "mesh" : "mesh",
139 "wds" : "wds",
140 }[params['type']]
Nebojsa Sabovic4cc2ce92010-04-21 15:08:01 -0700141
Nebojsa Sabovic60ae1462010-05-07 16:14:45 -0700142 self.router.run("%s phy %s interface add %s type %s" %
143 (self.cmd_iw, self.phydev2, self.wlanif2, phytype))
144 self.router.run("%s phy %s interface add %s type %s" %
145 (self.cmd_iw, self.phydev5, self.wlanif5, phytype))
Sam Leffler6969d1d2010-03-15 16:07:11 -0700146
147
148 def destroy(self, params):
149 """ Destroy a previously created device """
Nebojsa Sabovic4cc2ce92010-04-21 15:08:01 -0700150 # For linux, this is the same as deconfig.
151 self.deconfig(params)
152
Sam Leffler6969d1d2010-03-15 16:07:11 -0700153
154
Paul Stewartc2b3de82011-03-03 14:45:31 -0800155 def hostap_config(self, params):
Sam Leffler6969d1d2010-03-15 16:07:11 -0700156 """ Configure the AP per test requirements """
157
Paul Stewart45338d22010-10-21 10:57:02 -0700158 multi_interface = 'multi_interface' in params
159 if multi_interface:
160 params.pop('multi_interface')
Paul Stewartc2b3de82011-03-03 14:45:31 -0800161 elif self.hostapd['configured'] or self.station['configured']:
Nebojsa Sabovic4cc2ce92010-04-21 15:08:01 -0700162 self.deconfig({})
163
Paul Stewartc2b3de82011-03-03 14:45:31 -0800164 # Construct the hostapd.conf file and start hostapd.
165 conf = self.hostapd['conf']
166 tx_power_params = {}
167 htcaps = set()
Nebojsa Sabovic4cc2ce92010-04-21 15:08:01 -0700168
Paul Stewartc2b3de82011-03-03 14:45:31 -0800169 conf['driver'] = params.get('hostapd_driver',
170 self.hostapd['driver'])
Nebojsa Sabovic4cc2ce92010-04-21 15:08:01 -0700171
Paul Stewartc2b3de82011-03-03 14:45:31 -0800172 for k, v in params.iteritems():
173 if k == 'ssid':
174 conf['ssid'] = v
175 elif k == 'ssid_suffix':
176 conf['ssid'] = self.defssid + v
177 elif k == 'channel':
178 freq = int(v)
Nebojsa Sabovic4cc2ce92010-04-21 15:08:01 -0700179
Paul Stewartc2b3de82011-03-03 14:45:31 -0800180 # 2.4GHz
181 if freq <= 2484:
182 # Make sure hw_mode is set
183 if conf.get('hw_mode') == 'a':
Nebojsa Sabovic4cc2ce92010-04-21 15:08:01 -0700184 conf['hw_mode'] = 'g'
Paul Stewartc2b3de82011-03-03 14:45:31 -0800185
186 # Freq = 5 * chan + 2407, except channel 14
187 if freq == 2484:
188 conf['channel'] = 14
189 else:
190 conf['channel'] = (freq - 2407) / 5
191 # 5GHz
Sam Leffler6969d1d2010-03-15 16:07:11 -0700192 else:
Paul Stewartc2b3de82011-03-03 14:45:31 -0800193 # Make sure hw_mode is set
194 conf['hw_mode'] = 'a'
195 # Freq = 5 * chan + 4000
196 if freq < 5000:
197 conf['channel'] = (freq - 4000) / 5
198 # Freq = 5 * chan + 5000
199 else:
200 conf['channel'] = (freq - 5000) / 5
Sam Leffler6969d1d2010-03-15 16:07:11 -0700201
Paul Stewartc2b3de82011-03-03 14:45:31 -0800202 elif k == 'country':
203 conf['country_code'] = v
204 elif k == 'dotd':
205 conf['ieee80211d'] = 1
206 elif k == '-dotd':
207 conf['ieee80211d'] = 0
208 elif k == 'mode':
209 if v == '11a':
210 conf['hw_mode'] = 'a'
211 elif v == '11g':
212 conf['hw_mode'] = 'g'
213 elif v == '11b':
214 conf['hw_mode'] = 'b'
215 elif v == '11n':
216 conf['ieee80211n'] = 1
217 elif k == 'bintval':
218 conf['beacon_int'] = v
219 elif k == 'dtimperiod':
220 conf['dtim_period'] = v
221 elif k == 'rtsthreshold':
222 conf['rts_threshold'] = v
223 elif k == 'fragthreshold':
224 conf['fragm_threshold'] = v
225 elif k == 'shortpreamble':
226 conf['preamble'] = 1
227 elif k == 'authmode':
228 if v == "open":
229 conf['auth_algs'] = 1
230 elif v == "shared":
231 conf['auth_algs'] = 2
232 elif k == 'hidessid':
233 conf['ignore_broadcast_ssid'] = 1
234 elif k == 'wme':
235 conf['wmm_enabled'] = 1
236 elif k == '-wme':
237 conf['wmm_enabled'] = 0
238 elif k == 'deftxkey':
239 conf['wep_default_key'] = v
240 elif k == 'ht20':
241 htcaps.add('') # NB: ensure 802.11n setup below
242 conf['wmm_enabled'] = 1
243 elif k == 'ht40':
244 htcaps.add('[HT40-]')
245 htcaps.add('[HT40+]')
246 conf['wmm_enabled'] = 1
247 elif k == 'shortgi':
248 htcaps.add('[SHORT-GI-20]')
249 htcaps.add('[SHORT-GI-40]')
250 elif k == 'pureg':
251 pass # TODO(sleffler) need hostapd support
252 elif k == 'puren':
253 pass # TODO(sleffler) need hostapd support
254 elif k == 'protmode':
255 pass # TODO(sleffler) need hostapd support
256 elif k == 'ht':
257 htcaps.add('') # NB: ensure 802.11n setup below
258 elif k == 'htprotmode':
259 pass # TODO(sleffler) need hostapd support
260 elif k == 'rifs':
261 pass # TODO(sleffler) need hostapd support
262 elif k == 'wepmode':
263 pass # NB: meaningless for hostapd; ignore
264 elif k == '-ampdu':
265 pass # TODO(sleffler) need hostapd support
266 elif k == 'txpower':
267 tx_power_params['power'] = v
Nebojsa Sabovic60ae1462010-05-07 16:14:45 -0700268 else:
Paul Stewartc2b3de82011-03-03 14:45:31 -0800269 conf[k] = v
Nebojsa Sabovic60ae1462010-05-07 16:14:45 -0700270
Paul Stewartc2b3de82011-03-03 14:45:31 -0800271 # Aggregate ht_capab.
272 if htcaps:
273 conf['ieee80211n'] = 1
274 conf['ht_capab'] = ''.join(htcaps)
Nebojsa Sabovic4cc2ce92010-04-21 15:08:01 -0700275
Paul Stewartc2b3de82011-03-03 14:45:31 -0800276 # Figure out the correct interface.
277 if conf.get('hw_mode', 'b') == 'a':
278 conf['interface'] = self.wlanif5
279 else:
280 conf['interface'] = self.wlanif2
Paul Stewart9877fe42010-12-10 08:28:21 -0800281
Paul Stewartc2b3de82011-03-03 14:45:31 -0800282 # Generate hostapd.conf.
283 self.router.run("cat <<EOF >%s\n%s\nEOF\n" %
284 (self.hostapd['file'], '\n'.join(
285 "%s=%s" % kv for kv in conf.iteritems())))
286
287 if not multi_interface:
288 logging.info("Initializing bridge...")
289 self.router.run("%s addbr %s" %
290 (self.cmd_brctl, self.bridgeif))
291 self.router.run("%s setfd %s %d" %
292 (self.cmd_brctl, self.bridgeif, 0))
293 self.router.run("%s stp %s %d" %
294 (self.cmd_brctl, self.bridgeif, 0))
295
296 # Run hostapd.
297 logging.info("Starting hostapd...")
298 self.router.run("%s -B %s" %
299 (self.cmd_hostapd, self.hostapd['file']))
Nebojsa Sabovic4cc2ce92010-04-21 15:08:01 -0700300
Nebojsa Sabovic60ae1462010-05-07 16:14:45 -0700301
Paul Stewartc2b3de82011-03-03 14:45:31 -0800302 # Set up the bridge.
303 if not multi_interface:
304 logging.info("Setting up the bridge...")
305 self.router.run("%s addif %s %s" %
306 (self.cmd_brctl, self.bridgeif, self.wiredif))
307 self.router.run("%s link set %s up" %
308 (self.cmd_ip, self.wiredif))
309 self.router.run("%s link set %s up" %
310 (self.cmd_ip, self.bridgeif))
311 self.hostapd['interface'] = conf['interface']
312 else:
313 tx_power_params['interface'] = conf['interface']
Paul Stewart1ae854b2011-02-08 15:10:14 -0800314
Paul Stewartc2b3de82011-03-03 14:45:31 -0800315 # Configure transmit power
316 self.set_txpower(tx_power_params)
Nebojsa Sabovic138ff912010-04-06 15:47:42 -0700317
Paul Stewartc2b3de82011-03-03 14:45:31 -0800318 logging.info("AP configured.")
Sam Leffler6969d1d2010-03-15 16:07:11 -0700319
Nebojsa Sabovic4cc2ce92010-04-21 15:08:01 -0700320 self.hostapd['configured'] = True
Sam Leffler6969d1d2010-03-15 16:07:11 -0700321
322
Paul Stewartc2b3de82011-03-03 14:45:31 -0800323 def station_config(self, params):
324 multi_interface = 'multi_interface' in params
325 if multi_interface:
326 params.pop('multi_interface')
327 elif self.station['configured'] or self.hostapd['configured']:
328 self.deconfig({})
329
330 interface = self.wlanif2
331 conf = self.station['conf']
332 for k, v in params.iteritems():
333 if k == 'ssid_suffix':
334 conf['ssid'] = self.defssid + v
335 elif k == 'channel':
336 freq = int(v)
337 if freq > 2484:
338 interface = self.wlanif5
339 elif k == 'mode':
340 if v == '11a':
341 interface = self.wlanif5
342 else:
343 conf[k] = v
344
345 if not multi_interface:
346 logging.info("Initializing bridge...")
347 self.router.run("%s addbr %s" %
348 (self.cmd_brctl, self.bridgeif))
349 self.router.run("%s setfd %s %d" %
350 (self.cmd_brctl, self.bridgeif, 0))
351 self.router.run("%s stp %s %d" %
352 (self.cmd_brctl, self.bridgeif, 0))
353
354 # Run interface configuration commands
355 for k, v in conf.iteritems():
356 if k != 'ssid':
357 self.router.run("%s dev %s set %s %s" %
358 (self.cmd_iw, interface, k, v))
359
360 # Connect the station
361 self.router.run("%s link set %s up" % (self.cmd_ip, interface))
362 connect_cmd = ('ibss join' if self.station['type'] == 'ibss'
363 else 'connect')
364 self.router.run("%s dev %s %s %s %d" %
365 (self.cmd_iw, interface, connect_cmd,
366 conf['ssid'], freq))
367
368 # Add wireless interface to the bridge
369 self.router.run("%s addif %s %s" %
370 (self.cmd_brctl, self.bridgeif, interface))
371
372 # Add interface to the bridge.
373 # Bring up the bridge
374 if not multi_interface:
375 logging.info("Setting up the bridge...")
376 self.router.run("%s addif %s %s" %
377 (self.cmd_brctl, self.bridgeif, self.wiredif))
378 self.router.run("%s link set %s up" %
379 (self.cmd_ip, self.wiredif))
380 self.router.run("%s link set %s up" %
381 (self.cmd_ip, self.bridgeif))
382
383 self.station['configured'] = True
384 self.station['interface'] = interface
385
386
387 def config(self, params):
388 if self.apmode:
389 self.hostap_config(params)
390 else:
391 self.station_config(params)
392
393
Sam Leffler6969d1d2010-03-15 16:07:11 -0700394 def deconfig(self, params):
Nebojsa Sabovic4cc2ce92010-04-21 15:08:01 -0700395 """ De-configure the AP (will also bring wlan and the bridge down) """
Sam Leffler6969d1d2010-03-15 16:07:11 -0700396
Paul Stewartc2b3de82011-03-03 14:45:31 -0800397 if not self.hostapd['configured'] and not self.station['configured']:
Nebojsa Sabovic4cc2ce92010-04-21 15:08:01 -0700398 return
Sam Leffler6969d1d2010-03-15 16:07:11 -0700399
Nebojsa Sabovic4cc2ce92010-04-21 15:08:01 -0700400 # Taking down hostapd takes wlan0 and mon.wlan0 down.
Paul Stewartc2b3de82011-03-03 14:45:31 -0800401 if self.hostapd['configured']:
402 self.router.run("pkill hostapd >/dev/null 2>&1", ignore_status=True)
403# self.router.run("rm -f %s" % self.hostapd['file'])
404 if self.station['configured']:
405 if self.station['type'] == 'ibss':
406 self.router.run("%s dev %s ibss leave" %
407 (self.cmd_iw, self.station['interface']))
408 else:
409 self.router.run("%s dev %s disconnect" %
410 (self.cmd_iw, self.station['interface']))
411 self.router.run("%s link set %s down" % (self.cmd_ip,
412 self.station['interface']))
Nebojsa Sabovic4cc2ce92010-04-21 15:08:01 -0700413
Paul Stewart310928c2010-09-07 11:54:11 -0700414 # Try a couple times to remove the bridge; hostapd may still be exiting
415 for attempt in range(3):
Paul Stewart66dcb082010-09-08 16:42:12 -0700416 self.router.run("%s link set %s down" %
417 (self.cmd_ip, self.bridgeif), ignore_status=True)
418
Paul Stewart310928c2010-09-07 11:54:11 -0700419 result = self.router.run("%s delbr %s" %
420 (self.cmd_brctl, self.bridgeif),
421 ignore_status=True)
Paul Stewartaf5847e2010-09-21 12:02:03 -0700422 if not result.stderr or 'No such device' in result.stderr:
Paul Stewart310928c2010-09-07 11:54:11 -0700423 break
424 time.sleep(1)
425 else:
426 raise error.TestFail("Unable to delete bridge %s: %s" %
427 (self.bridgeif, result.stderr))
428
Nebojsa Sabovic4cc2ce92010-04-21 15:08:01 -0700429
430 self.hostapd['configured'] = False
Paul Stewartc2b3de82011-03-03 14:45:31 -0800431 self.station['configured'] = False
Paul Stewart7cb1f062010-06-10 15:46:20 -0700432
433
434 def get_ssid(self):
435 return self.hostapd['conf']['ssid']
Paul Stewart98022e22010-10-22 10:33:14 -0700436
437
438 def set_txpower(self, params):
439 self.router.run("%s dev %s set txpower %s" %
440 (self.cmd_iw, params.get('interface',
441 self.hostapd['interface']),
442 params.get('power', 'auto')))