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 """ 129 Make a new routing table, populated from entries in an open 130 file object. 131 132 The entries are expected to have the form: 133 IP-ADDRESS/MASK-BITS ROUTE-NAME 134 135 The `#' character denotes a comment. Empty lines are allowed. 136 137 @param f: file from whence to read a routing table 138 @type f: open file object 139 @param requireNames: whether to require route names in the file 140 @type requireNames: boolean, default to True 141 @param defaultRouteName: default name to give to a route if it 142 does not have a name in the file; only 143 used if requireNames is False 144 @type defaultRouteName: anything, defaults to '*default*' 145 """ 146 comment = re.compile(r'^\s*#') 147 empty = re.compile(r'^\s*$') 148 entry = re.compile(r'^\s*' 149 r'(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})' 150 r'/' 151 r'(\d{1,2})' 152 r'(\s+([^\s](.*[^\s])?))?\s*$') 153 ret = klass() 154 n = 0 155 for line in f: 156 n += 1 157 if comment.match(line) or empty.match(line): 158 continue 159 m = entry.match(line) 160 if not m: 161 raise ValueError('While loading routing table from file' 162 ' %s: line %d: invalid syntax: %r' 163 % (f, n, line)) 164 route = m.group(4) 165 if route is None: 166 if requireNames: 167 raise ValueError('%s:%d: Missing required route name: %r' 168 % (f, n, line)) 169 else: 170 route = defaultRouteName 171 ret.addSubnet(route, m.group(1), int(m.group(2))) 172 if route not in ret.routeNames: 173 ret.routeNames.append(route) 174 175 return ret
176 fromFile = classmethod(fromFile) 177
178 - def __init__(self):
179 self.avltree = avltree.AVLTree() 180 self.routeNames = []
181
182 - def getRouteNames(self):
183 return self.routeNames
184
185 - def _parseSubnet(self, ipv4String, maskBits):
186 return (ipv4StringToInt(ipv4String), 187 ~((1 << (32 - maskBits)) - 1))
188
189 - def addSubnet(self, route, ipv4String, maskBits=32):
190 ipv4Int, mask = self._parseSubnet(ipv4String, maskBits) 191 if not ipv4Int & mask == ipv4Int: 192 raise ValueError('Net %s too specific for mask with %d bits' 193 % (ipv4String, maskBits)) 194 self.avltree.insert((mask, ipv4Int, route))
195
196 - def removeSubnet(self, route, ipv4String, maskBits=32):
197 ipv4Int, mask = self._parseSubnet(ipv4String, maskBits) 198 self.avltree.delete((mask, ipv4Int, route))
199
200 - def __iter__(self):
201 return self.avltree.iterreversed()
202
203 - def iterHumanReadable(self):
204 for mask, net, route in self: 205 yield route, ipv4IntToString(net), 32-countTrailingZeroes32(mask)
206
207 - def __len__(self):
208 return len(self.avltree)
209
210 - def route(self, ip):
211 """ 212 Return the preferred route for this IP. 213 214 @param ip: The IP to use for routing decisions. 215 @type ip: An integer or string representing an IPv4 address 216 """ 217 if isinstance(ip, str): 218 ip = ipv4StringToInt(ip) 219 220 for netmask, net, route in self: 221 if ip & netmask == net: 222 return route 223 224 return None
225
226 - def route_iter(self, ip):
227 """ 228 Return an iterator yielding routes in order of preference. 229 230 @param ip: The IP to use for routing decisions. 231 @type ip: An integer or string representing an IPv4 address 232 """ 233 if isinstance(ip, str): 234 ip = ipv4StringToInt(ip) 235 for mask, net, route in self: 236 if ip & mask == net: 237 yield route 238 # Yield the default route 239 yield None
240