Package flumotion :: Package common :: Module netutils
[hide private]

Source Code for Module flumotion.common.netutils

  1  # -*- Mode: Python; test-case-name: flumotion.test.test_common_messages -*- 
  2  # vi:si:et:sw=4:sts=4:ts=4 
  3  # 
  4  # Flumotion - a streaming media server 
  5  # Copyright (C) 2004,2005,2006,2007 Fluendo, S.L. (www.fluendo.com). 
  6  # All rights reserved. 
  7   
  8  # This file may be distributed and/or modified under the terms of 
  9  # the GNU General Public License version 2 as published by 
 10  # the Free Software Foundation. 
 11  # This file is distributed without any warranty; without even the implied 
 12  # warranty of merchantability or fitness for a particular purpose. 
 13  # See "LICENSE.GPL" in the source distribution for more information. 
 14   
 15  # Licensees having purchased or holding a valid Flumotion Advanced 
 16  # Streaming Server license may use this file in accordance with the 
 17  # Flumotion Advanced Streaming Server Commercial License Agreement. 
 18  # See "LICENSE.Flumotion" in the source distribution for more information. 
 19   
 20  # Headers in this file shall remain intact. 
 21   
 22  """ 
 23  Miscellaneous network functions for use in flumotion. 
 24  """ 
 25   
 26  import socket 
 27  import fcntl 
 28  import struct 
 29  import array 
 30  import re 
 31   
 32  from flumotion.common import avltree 
 33   
 34  # Thanks to Paul Cannon, see  
 35  # http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/439093 
 36  #  
 37  # WARNING: Horribly linux-specific. Horribly IPv4 specific. Also, just horrible. 
 38   
39 -def find_all_interface_names():
40 """ 41 Find the names of all available network interfaces 42 """ 43 ptr_size = len(struct.pack('P', 0)) 44 size = 24 + 2 * (ptr_size) 45 max_possible = 128 # arbitrary. raise if needed. 46 bytes = max_possible * size 47 s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) 48 names = array.array('B', '\0' * bytes) 49 outbytes = struct.unpack('iP', fcntl.ioctl( 50 s.fileno(), 51 0x8912, # SIOCGIFCONF 52 struct.pack('iP', bytes, names.buffer_info()[0]) 53 ))[0] 54 namestr = names.tostring() 55 return [namestr[i:i+size].split('\0', 1)[0] for i in range(0, outbytes, size)]
56
57 -def get_address_for_interface(ifname):
58 """ 59 Get the IP address for an interface 60 """ 61 s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) 62 return socket.inet_ntoa(fcntl.ioctl( 63 s.fileno(), 64 0x8915, # SIOCGIFADDR 65 struct.pack('256s', ifname[:15]) 66 )[20:24])
67
68 -def guess_public_ip():
69 """ 70 Attempt to guess a public IP for this system. 71 Returns "127.0.0.1" if it can't come up with anything better. 72 """ 73 # Iterate through them in some vaguely meaningful order. 74 interfaces = find_all_interface_names() 75 interfaces.sort() 76 77 for interface in interfaces: 78 # We have them sorted, so the first such we see will be eth0 79 if interface.startswith('eth'): 80 return get_address_for_interface(interface) 81 82 return '127.0.0.1'
83
84 -def guess_public_hostname():
85 """ 86 Attempt to guess a public hostname for this system. 87 """ 88 ip = guess_public_ip() 89 90 try: 91 return socket.gethostbyaddr(ip)[0] 92 except: 93 return ip
94
95 -def ipv4StringToInt(s):
96 try: 97 b1, b2, b3, b4 = map(int, s.split('.')) 98 except TypeError: 99 raise ValueError(s) 100 101 ret = 0 102 for n in b1, b2, b3, b4: 103 ret <<= 8 104 if n < 0 or n > 255: 105 raise ValueError(s) 106 ret += n 107 return ret
108
109 -def ipv4IntToString(n):
110 l = [] 111 for i in range(4): 112 l.append((n>>(i*8)) & 0xff) 113 l.reverse() 114 return '.'.join(map(str, l))
115
116 -def countTrailingZeroes32(n):
117 tz = 0 118 if n == 0: 119 # max of 32 bits 120 tz = 32 121 else: 122 while not (n & (1<<tz)): 123 tz += 1 124 return tz
125
126 -class RoutingTable(object):
127 - def fromFile(klass, f, requireNames=True, defaultRouteName='*default*'):
128 """Make a new routing table, populated from entries in an open 129 file object. 130 131 The entries are expected to have the form: 132 IP-ADDRESS/MASK-BITS ROUTE-NAME 133 134 The `#' character denotes a comment. Empty lines are allowed. 135 136 @param f: file from whence to read a routing table 137 @type f: open file object 138 @param requireNames: whether to require route names in the file 139 @type requireNames: boolean, default to True 140 @param defaultRouteName: default name to give to a route if it 141 does not have a name in the file; only 142 used if requireNames is False 143 @type defaultRouteName: anything, defaults to '*default*' 144 """ 145 comment = re.compile(r'^\s*#') 146 empty = re.compile(r'^\s*$') 147 entry = re.compile(r'^\s*' 148 r'(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})' 149 r'/' 150 r'(\d{1,2})' 151 r'(\s+([^\s](.*[^\s])?))?\s*$') 152 ret = klass() 153 n = 0 154 for line in f: 155 n += 1 156 if comment.match(line) or empty.match(line): 157 continue 158 m = entry.match(line) 159 if not m: 160 raise ValueError('While loading routing table from file' 161 ' %s: line %d: invalid syntax: %r' 162 % (f, n, line)) 163 route = m.group(4) 164 if route is None: 165 if requireNames: 166 raise ValueError('%s:%d: Missing required route name: %r' 167 % (f, n, line)) 168 else: 169 route = defaultRouteName 170 ret.addSubnet(route, m.group(1), int(m.group(2))) 171 if route not in ret.routeNames: 172 ret.routeNames.append(route) 173 174 return ret
175 fromFile = classmethod(fromFile) 176
177 - def __init__(self):
178 self.avltree = avltree.AVLTree() 179 self.routeNames = []
180
181 - def getRouteNames(self):
182 return self.routeNames
183
184 - def _parseSubnet(self, ipv4String, maskBits):
185 return (ipv4StringToInt(ipv4String), 186 ~((1 << (32 - maskBits)) - 1))
187
188 - def addSubnet(self, route, ipv4String, maskBits=32):
189 ipv4Int, mask = self._parseSubnet(ipv4String, maskBits) 190 if not ipv4Int & mask == ipv4Int: 191 raise ValueError('Net %s too specific for mask with %d bits' 192 % (ipv4String, maskBits)) 193 self.avltree.insert((mask, ipv4Int, route))
194
195 - def removeSubnet(self, route, ipv4String, maskBits=32):
196 ipv4Int, mask = self._parseSubnet(ipv4String, maskBits) 197 self.avltree.delete((mask, ipv4Int, route))
198
199 - def __iter__(self):
200 return self.avltree.iterreversed()
201
202 - def iterHumanReadable(self):
203 for mask, net, route in self: 204 yield route, ipv4IntToString(net), 32-countTrailingZeroes32(mask)
205
206 - def __len__(self):
207 return len(self.avltree)
208
209 - def route(self, ip):
210 if isinstance(ip, str): 211 ip = ipv4StringToInt(ip) 212 213 for netmask, net, route in self: 214 if ip & netmask == net: 215 return route 216 217 return None
218
219 - def route_iter(self, ip):
220 if isinstance(ip, str): 221 ip = ipv4StringToInt(ip) 222 for mask, net, route in self: 223 if ip & mask == net: 224 yield route 225 # Yield the default route 226 yield None
227