Brain-dead Wifi Selector

(K)ubuntu's NetworkManager is not good enough when the APs are half-broken (e.g., don't associate, don't give DHCP leases or don't route). Too often it simply stops responding, when the user clearly sees that the network is not working.

Manual (open) wireless network configuration is simple:

  iwconfig (iface) essid (essid) ap (mac)
  dhclient (iface)

... except that you need to parse iwlist (iface) scan for the relevant data identifying the AP. This quickly becomes tedious when there are n-teen cells in the scan results and most of them are broken.

Here is a really shoddy script that replaces NetworkManager with a trivial interactive loop to attempt to connect to one of the detected AP.

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
#!/usr/bin/python
# -*- coding: utf-8 -*-

"""
  szym / Oct 2009

  NetworkManager is not good enough when the APs are half-broken
  (e.g., don't associate, don't give DHCP leases or don't route).

  This is a really shoddy script that replaces NetworkManager.
  Manual network configuration is simple:
    iwconfig {iface} essid {essid} ap {mac}
    dhclient {iface}
  ... except that you need to parse `iwlist {iface} scan` for the
  relevant data. This quickly becomes tedious when there are
  n-teen cells in the scan results and most of them are broken.
"""

from os import popen, system

class WifiSelector:
  def __init__(self, iface):
    # test iface first
    #if(system("ls /sys/class/net/%s/wireless >/dev/null" % iface) != 0):
    #  raise Exception("%s is not wireless" % iface)
    self.iface = iface
    self.aps = []


  def scan(self):
    """ iwlist {iface} scan
        parse output and store in aps
    """
    aps = []
    cur = None
    for l in popen("iwlist %s scan" % self.iface):
      l = l.strip()
      if (not l):
        continue
      if (l.startswith('Cell')):
        if (cur != None):
          aps.append(cur)
        keys = [ 'essid','mode','chan','key','q','sig','noise','last' ]
        cur = dict([(x, None) for x in keys])
        cur['last'] = -1
        cur['ap'] = l.split()[4]
      if (cur == None):
        continue
      if (l.startswith('ESSID')):
        cur['essid'] = l.split(':')[1]
      if (l.startswith('Mode')):
        cur['mode'] = l.split(':')[1]
      if (l.startswith('Channel')):
        cur['chan'] = int(l.split(':')[1])
      if (l.startswith('Encryption')):
        cur['key'] = l.split(':')[1]
      if (l.startswith('Quality')):
        # Quality=15/100  Signal level:-84 dBm  Noise level=-90 dBm
        ls = l.replace(':', ' ').replace('/', ' ').replace('=', ' ').split()
        cur['q'] = int(ls[1])
        cur['sig'] = int(ls[5])
        #cur['noise'] = int(ls[9])
      if (l.startswith('Extra: Last beacon:')):
        #Extra: Last beacon: 1888ms ago
        cur['last'] = int(l.split()[3][:-2])
    self.aps = aps

  def show(self):
    """ pretty print aps
    """
    for i, ap in enumerate(self.aps):
      print i, ("%(mode)-6s %(key)-3s %(q)2d %(essid)-30s "
                "%(chan)4s %(ap)s %(last)5d" % ap)

  def sortq(self):
    """ sort aps by quality
    """
    self.aps.sort(key=lambda d: d['q'], reverse=True)

  def refresh(self):
    self.scan()
    self.sortq()
    self.show()

  def select(self, i):
    """ attempt to associate with aps[i]
    """
    ap = self.aps[i]
    cmd = "iwconfig "+self.iface+" mode %(mode)s essid %(essid)s ap %(ap)s" % ap
    print cmd
    system(cmd)
    system("dhclient %s" % self.iface)

  def kill_nm(self):
    if (system("/etc/init.d/network-manager status") == 0):
      system("/etc/init.d/network-manager stop")
      system("ifconfig %s up" % self.iface)

def main(args):
  from sys import stderr
  if (len(args) > 1 and args[1] == '-h'):
    print >> stderr, "Usage: %s {iface}" % args[0]
    return -1

  if len(args) < 2:
    iface = 'wlan0'
  else:
    iface = args[1]
  ws = WifiSelector(iface)
  #ws.kill_nm()
  ws.refresh()

  while(True):
    try:
      which = raw_input('select (default: refresh): ')
    except:
      break
    if len(which) == 0:
      ws.refresh()
    elif (int(which) >= 0 and int(which) < len(ws.aps)):
      ws.select(int(which))

if (__name__ == '__main__'):
  from sys import argv
  main(argv)
blog comments powered by Disqus