From bbe664ca53f0c9ec431f059747bcf8596a2d03b0 Mon Sep 17 00:00:00 2001 From: Akkana Peck Date: Sun, 25 Mar 2012 19:32:39 -0700 Subject: [PATCH] Support both old and new ifconfig output formats; better reliability for open wi-fi 802.11b connections; monitor success of starting dhcp client; don't run ifup twice on debian; rename 'add' option to 'multi'; better method documentation. --- netscheme | 57 +++++++++++++++++++++++++++++------------ netutils.html | 67 +++++++++++++++++++++--------------------------- netutils.py | 70 +++++++++++++++++++++++++++++++++------------------ 3 files changed, 115 insertions(+), 79 deletions(-) diff --git a/netscheme b/netscheme index a9c1879..d68fe2c 100755 --- a/netscheme +++ b/netscheme @@ -1,6 +1,7 @@ #! /usr/bin/env python # -# Copyright 2010 by Akkana Peck akkana@shallowsky.com +# Network scheme-setting tool, version 1.4. +# Copyright 2010-2012 by Akkana Peck akkana@shallowsky.com # ... share and enjoy under the GPLv2 or (at your option) later. # @@ -93,7 +94,7 @@ class NetScheme : return s def dump(self, filename=None) : - print "dump(", filename, ")" + print 'dump(', filename, ')' if filename == None : filename = homedirfile(".netscheme") print "Appending new scheme to", filename @@ -111,6 +112,7 @@ class NetScheme : up_ifaces = netutils.get_interfaces(True) for i in up_ifaces : print "Taking", i, "down with ifdown" + i.ifconfig_down() subprocess.call(["ifdown", i.name]) if add : @@ -143,7 +145,6 @@ auto %s""" % (iface.name) print "Encryption:", self.encryption if self.encryption == "wpa" or self.encryption == "wpa2" : - print "It's WPA" if self.essid : print >>fp, "wpa-ssid", self.essid if self.key : @@ -186,8 +187,8 @@ auto %s""" % (iface.name) if self.essid : iwargs.append("essid") iwargs.append(self.essid) - #iwargs.append("mode") - #iwargs.append("managed") + iwargs.append("mode") + iwargs.append("managed") if accesspoint and accesspoint.address : iwargs.append("ap") iwargs.append(accesspoint.address) @@ -197,11 +198,16 @@ auto %s""" % (iface.name) else : iwargs.append("key") iwargs.append("off") - iwargs.append("enc") - iwargs.append("off") + #iwargs.append("enc") + #iwargs.append("off") + iwargs.append("channel") + iwargs.append("auto") print "Calling", iwargs subprocess.call(iwargs) + print "Called iwconfig: now it says" + os.system("iwconfig") + elif self.encryption == "wpa" : tempfp = tempfile.NamedTemporaryFile(prefix="wpasup", delete=False) @@ -215,19 +221,22 @@ key_mgmt=WPA-PSK psk="%s" }""" % (self.essid, self.key) tempfp.close() - print "Starting wpa_supplicant" subprocess.Popen(["wpa_supplicant", "wpa_supplicant", "-Dwext", "-i", iface.name, "-c", tempname]) # Is 1 second long enough for wpa_supplicant to open the file? # 2 might be safer. What a drag. time.sleep(2) - print "removing tmp file" os.unlink(tempname) if self.dhcp : print "Getting dhcp" - subprocess.call(["dhcpcd", "-G", "-C", "resolv.conf", iface.name]) - #subprocess.call(["dhclient", "-v", iface.name]) + try : + subprocess.check_call(["dhcpcd", "-G", "-C", "resolv.conf", + iface.name]) + #subprocess.call(["dhclient", "-v", iface.name]) + except subprocess.CalledProcessError, e : + print "DHCP failed, error", e.returncode + iface.ifconfig_down() else : if not self.ip : @@ -256,6 +265,7 @@ psk="%s" # find the first wireless interface for i in all : print "Trying interface", i.name + if i.wireless: print i.name, "is wireless" # The clause from hell. What this does is: # - If the scheme has an encryption setting (including "open") # then it's a wireless scheme and needs a wireless interface. @@ -307,8 +317,17 @@ psk="%s" # Mark the interface up (and call ifup too, if applicable) subprocess.call(["ifconfig", iface.name, "up"]) + # This somehow ends up being the second time it's called. + # So don't do that! if is_deb : - subprocess.call(["ifup", iface.name]) + # At one point I thought it was a good idea to call ifup + # explicitly. But at least with some cards, ifup associates + # with the accesspoint and calls DHCP -- all of which is + # about to be done again from service networking restart. + # And calling it the second time can fail (e.g. for Broadcom + # BCM4313 where the driver needs to be reloaded before + # each association). + #subprocess.call(["ifup", iface.name]) subprocess.call(["service", "networking", "restart"]) # Try to make the routing tables sane: @@ -323,7 +342,6 @@ psk="%s" if r.dest == 'default' or r.dest == '0.0.0.0' : defaults.append(r) print "Default route:", r - print "============" if len(defaults) > 1 : wired_route = None # Find the first wired route @@ -415,6 +433,10 @@ def find_and_set_scheme(newscheme, add=False) : scheme.set_scheme(add) return + # XXX TO DO: use python soundex module if available + # to see if there's a similarly named scheme -- + # for instance, "Coffee Bean" instead of "coffeebean". + print "No scheme named", newscheme aplist = netutils.get_accesspoints() @@ -435,6 +457,8 @@ def find_and_set_scheme(newscheme, add=False) : if ans == 'y' or ans == 'Y' : scheme.dump() + return + # # A couple of default schemes: # @@ -472,9 +496,9 @@ if __name__ == "__main__" : default=False, help="Reset the connection without changing scheme") parser.add_option("-m", "--multi", - action="store_true", dest="add", + action="store_true", dest="multi", default=False, - help="Add a new interface without bringing down current one") + help="Multiple interfaces: add a new interface without bringing down current one") parser.add_option("-s", "--save", action="store_true", dest="save_new_scheme", default=False, @@ -511,6 +535,7 @@ if __name__ == "__main__" : newscheme.interface = config.get(scheme, 'interface') Schemes.append(newscheme) + # Schemes are read, options are read, time to do stuff. if (options.list_schemes) : list_schemes() elif (options.list_accesspoints) : @@ -521,6 +546,6 @@ if __name__ == "__main__" : #print parser.usage print_current_scheme() else : - find_and_set_scheme(args[0], options.add) + find_and_set_scheme(args[0], options.multi) except KeyboardInterrupt : print "Interrupt" diff --git a/netutils.html b/netutils.html index 14d670d..20163c1 100644 --- a/netutils.html +++ b/netutils.html @@ -1,33 +1,20 @@ -Python: module netutils-1.3 +Python: module netutils  
netutils
 
- 
netutils-1.3
index
/home/akkana/web/software/netscheme/netutils-1.3.py
+>index
/home/akkana/bin/netutils.py

netutils: a set of networking utilities for Python.
Copyright 2010 by Akkana Peck <akkana@shallowsky.com>
 ... share and enjoy under the GPLv2 or (at your option) later.
 
-Provides the following:

-Classes:
-  NetInterface: name, ip, broadcast, netmask, essid, encryption, wireless
-  AccessPoint : address, essid, encryption, quality, interface
-  Route : display or change network routing tables.

-Functions outside of classes:
-  get_interfaces(only_up=False): returns a list of all NetInterface.
-    If only_up, will only return the ones currently marked UP.
-  get_wireless_interfaces(): returns a list of wireless NetInterface.
-  get_accesspoints(): returns a list of visible AccessPoints.
-  ifdown_all(): take all interfaces down, killing any wpa_supplicant or dhcp
-  kill_by_name(namelist): Kill a any running processes that include any
-    of the names in namelist. Return a list of actual process names killed.

+Provides classes and functions to allow finding a wireless interface,
+querying for accesspoints, or establishing a connection.

@@ -47,16 +34,16 @@
       
-
AccessPoint -
Connection +
AccessPoint +
Connection
-
DebianConnection -
ManualConnection +
DebianConnection +
ManualConnection
-
NetInterface -
Route +
NetInterface +
Route

@@ -65,7 +52,7 @@ class AccessPoint - + - + + +
   One Cell or AccessPoint from iwlist output
 
One Cell or wireless access point from iwlist output
 
  Methods defined here:
__init__(self)
@@ -78,7 +65,10 @@
 
class Connection
    
   Make a connection. Subclass this with details of how the
+connection will actually be made.
 
  Methods defined here:
__init__(self, iface=None)
@@ -86,9 +76,12 @@ +class DebianConnection(Connection) - + + +
 
-class DebianConnection(Connection)
    
   Make a connection on a Debian system, using /etc/network/interfaces
+and service network restart (along with other helpers).
 
  Methods defined here:
__init__(self, iface=None)
@@ -101,9 +94,14 @@ +class ManualConnection(Connection) - + + +
 
-class ManualConnection(Connection)
    
   Make a connection by calling explicit programs like ifconfig,
+iwconfig, wpasupplicant, etc. In theory should work on any
+Linux machine, but some distros (especially Debian derivatives)
+may not work well and may need their own connection type.
 
  Methods defined here:
__init__(self, iface=None)
@@ -179,12 +177,5 @@
ifdown_all()
Take all current interfaces down.
kill_by_name(namelist)
Kills all running processes that start with any of the
strings in the given name list.
-

- - - - - -
 
-Data
       __warningregistry__ = {("Parent module 'netutils-1' not found while handling absolute import", <type 'exceptions.RuntimeWarning'>, 28): True}
+

\ No newline at end of file diff --git a/netutils.py b/netutils.py index ed4f66c..e5a6354 100755 --- a/netutils.py +++ b/netutils.py @@ -5,24 +5,11 @@ # ... share and enjoy under the GPLv2 or (at your option) later. """netutils: a set of networking utilities for Python. -Copyright 2010, 2011 by Akkana Peck +Copyright 2010 by Akkana Peck ... share and enjoy under the GPLv2 or (at your option) later. -Provides the following: - -Classes: - NetInterface: name, ip, broadcast, netmask, essid, encryption, wireless - AccessPoint : address, essid, encryption, quality, interface - Route : display or change network routing tables. - -Functions outside of classes: - get_interfaces(only_up=False): returns a list of all NetInterface. - If only_up, will only return the ones currently marked UP. - get_wireless_interfaces(): returns a list of wireless NetInterface. - get_accesspoints(): returns a list of visible AccessPoints. - ifdown_all(): take all interfaces down, killing any wpa_supplicant or dhcp - kill_by_name(namelist): Kill a any running processes that include any - of the names in namelist. Return a list of actual process names killed. +Provides classes and functions to allow finding a wireless interface, +querying for accesspoints, or establishing a connection. """ import os, subprocess, re, shutil @@ -73,6 +60,9 @@ def ifconfig_down(self) : class Connection : + """Make a connection. Subclass this with details of how the + connection will actually be made. + """ def __init__(self, iface=None) : if iface : self.iface = iface @@ -82,6 +72,11 @@ def __init__(self, iface=None) : self.essid = "unknown" class ManualConnection(Connection) : + """Make a connection by calling explicit programs like ifconfig, + iwconfig, wpasupplicant, etc. In theory should work on any + Linux machine, but some distros (especially Debian derivatives) + may not work well and may need their own connection type. + """ def __init__(self, iface=None): Connection.__init__(self, iface) @@ -128,6 +123,9 @@ def reset(self): self.essid = None class DebianConnection(Connection) : + """Make a connection on a Debian system, using /etc/network/interfaces + and service network restart (along with other helpers). + """ def __init__(self, iface=None) : Connection.__init__(self, iface) self.interfaces = "/etc/network/interfaces" @@ -196,7 +194,7 @@ def reset(self ): self.essid = "unknown" class AccessPoint : - """ One Cell or AccessPoint from iwlist output""" + """ One Cell or wireless access point from iwlist output""" def __init__(self) : self.clear() @@ -332,17 +330,28 @@ def get_interfaces(only_up=False, name=None) : if line[0] != ' ' : # It's a new interface. Should have a line like: # eth0 Link encap:Ethernet HWaddr 00:01:4A:98:F1:51 + # or else a line line: + # flags=4098 + # with no LOOPBACK flag. # We only want the encap:Ethernet lines, not others like # loopback, vpn, ipv6 etc. - if words[2] == 'encap:Ethernet' : + if words[2] == 'encap:Ethernet' or \ + words[1].startswith('flags') and not 'LOOPBACK' in words[1]: + if words[0].endswith(':') : + words[0] = words[0][0:-1] cur_iface = NetInterface(words[0]) ifaces.append(cur_iface) + + if words[2].startswith('flags') : # new format + cur_iface.up = ('UP' in words[2]) + else : cur_iface = None else : if not cur_iface : continue if words[0] == 'inet' : + # Old format: # inet addr:192.168.1.6 Bcast:192.168.1.255 Mask:255.255.255.0 match = re.search('addr:(\d+\.\d+\.\d+\.\d+)', line) if match : @@ -353,29 +362,36 @@ def get_interfaces(only_up=False, name=None) : match = re.search('Mask:(\d+\.\d+\.\d+\.\d+)', line) if match : cur_iface.netmask = match.group(1) + # New format: + # inet 127.0.0.1 netmask 255.0.0.0 + match = re.search('inet (\d+\.\d+\.\d+\.\d+)', line) + if match : + cur_iface.ip = match.group(1) + match = re.search('netmask (\d+\.\d+\.\d+\.\d+)', line) + if match : + cur_iface.netmask = match.group(1) + match = re.search('ether (..:..:..:..:..:..)', line) + if match : + cur_iface.mac = match.group(1) elif words[0] == 'UP' : cur_iface.up = True # Now we have the list of all interfaces. Find out which are wireless: proc = subprocess.Popen('iwconfig', shell=False, stdout=subprocess.PIPE, stderr=subprocess.PIPE) - # TEMPORARY, FOR TESTING ON DESKTOPS: - #proc = subprocess.Popen('cat /home/akkana/iwconfig.out', shell=True, - # stdout=subprocess.PIPE, stderr=subprocess.PIPE) stdout_str = proc.communicate()[0] stdout_list = stdout_str.split('\n') cur_iface = None for line in stdout_list : - # print "line", line if len(line) == 0 : continue if line[0] != ' ' : words = line.split() - # print "New interface", words[0] + #print "Wireless interface", words[0] for iface in ifaces : - # print "Checking", words[0], "against", iface.name + #print "Checking", words[0], "against", iface.name if iface.name == words[0] : - # print "It's in the list" + #print "It's in the list" cur_iface = iface cur_iface.wireless = True match = re.search('ESSID:"(.*)"', line) @@ -590,6 +606,10 @@ def kill_by_name(namelist) : # main if __name__ == "__main__" : + print "All interfaces:" for iface in get_interfaces() : print iface + print "Wireless interfaces:" + for iface in get_wireless_interfaces() : + print iface