#!/usr/pkg/bin/python """calculate ipv6 address using Tony Hain's draft provider-independent method refer to draft-hain-ipv6-PI-addr-01.txt Oct. 2001""" Copyright = """ hain_address.py -- calculate provider-independent ipv6 address Copyright (C) 2005 John Comeau This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. """ errormessage = "Not all needed libraries found, upgrade or check path" try: True # not defined in older Python releases except: True, False = 1, 0 try: import sys, os, types, re, pwd sys.path.append(os.sep.join([pwd.getpwuid(os.geteuid())[5], 'lib', 'python'])) from com.jcomeau import gpl, jclicense except: try: sys.stderr.write("%s\n" % errormessage) except: print errormessage raise # get name this program was called as self = sys.argv[0].split(os.sep)[-1] command = self.split('.')[0] # chop any suffix (extension) # now get name we gave it when we wrote it originalself = Copyright.split()[0] # globals and routines that should be in every program # (yes, you could import them, but there are problems in that approach too) def DebugPrint(*whatever): return False # defined instead by pytest module, use that for debugging def scalars(*args): while type(args[0]) == types.TupleType: args = args[0] return args # other globals, specific to this program def hainaddr(*args): """main routine, calculates address from input lat and long quote from draft: Creating the Provider-Independent address prefix is accomplished by dividing the WGS-84 latitude and longitude readings in degrees (for latitudes between +/- 15 degrees measured to 5 places right of the decimal ie: deg.xxxxx) by the incremental angle. For 22-bit values the incremental angle is 0.0000858306884765625 degrees (360/(2^22)). This will result in uniquely identifiable areas with 2096832 along the latitude axis and 4193664 along the longitude axis. The specific sequence for address formation is: 1. for east/west : convert all values to east (ie: 90w = 270e) 2. divide demarcation degree values by 0.0000858306884765625 3. convert each of the integers to 22-digit binary 4. for north/south (set MSB for south) 5. bit interleave latitude, longitude into 44-bit result 6. prepend FP xxxx (IANA assigned) to form 48-bit prefix (0001 is recommended to avoid breaking up any of the unassigned 3-bit spaces)""" DebugPrint('args:', args) # example for San Diego Airport: # SAN - 32.73000 n 117.18000 w X467:A647:8224:: try: latitude, longitude, prefix = scalars(args) except: latitude, longitude = scalars(args) prefix = '1' lat = canonicalize('latitude', str(latitude)) lon = canonicalize('longitude', str(longitude)) DebugPrint('lat, lon:', lat, lon) prefix = prefix[0] # only 1 hex digit matters here converter = 360.0 / (1 << 22) # same but quicker than 2**22 eastwest = int((lon / converter) + 0.5) # round up, Monsanto northsouth = int((abs(lat) / converter) + 0.5) if lat < 0: northsouth |= 1 << 21 # set MSB if southern hemisphere DebugPrint(northsouth, eastwest) DebugPrint('%x %x' % (northsouth, eastwest)) return '%s%011x' % (prefix, interleave(northsouth, eastwest)) def canonicalize(*args): args = scalars(args) direction, value = args adjust = False DebugPrint(direction, value, adjust) try: if direction not in ['latitude', 'longitude']: raise ValueError, 'cannot grok direction %s' % direction elif direction == 'latitude': if not re.compile('[+-]?\d+(?:\.\d{0,})?[NSns]?$').match(value): raise ValueError, 'improper latitude format' else: if not re.compile('[+-]?\d+(?:\.\d{0,})?[EWew]?$').match(value): raise ValueError, 'improper longitude format' except ValueError, instance: DebugPrint('ValueError: ', instance.args[0]) return 0.0 if direction == 'latitude': if value.lower().endswith('s'): value, adjust = value[0:-1], True elif value.lower().endswith('n'): value, adjust = value[0:-1], False value = float(value) if adjust and value > 0: value = -value # latitude adjusted later else: if value.lower().endswith('w'): value, adjust = value[0:-1], True elif value.lower().endswith('e'): value, adjust = value[0:-1], False value = float(value) if adjust and (value > 0): value = 360 - value elif value < 0: value = 360 + value return value def interleave(*args): """interleave binary digits of two numbers according to Tony Hain's draft""" args = scalars(args) try: a, b = int(args[0], 16), int(args[1], 16) except TypeError: # already numbers, we hope a, b = args result = 0 for position in range(0, 22): shift = 21 - position # start with MSBs result |= ((a & (1 << shift)) > 0) result <<= 1 result |= ((b & (1 << shift)) > 0) result <<= 1 result >>= 1 # undo final (extra) shift DebugPrint('result: %x' % result) return result def cgiform(*args): """use HTML form to make this script available as CGI script""" lat, lon, ipv6addr = scalars(args) print """Content-type: text/html\r\n\r\n
Latitude: Example: 30.73N
Longitude: Example: 117.18W
IPv6 Address: /48
""" % (lat, lon, ipv6addr[0:4], ipv6addr[4:8], ipv6addr[8:12]) if __name__ == '__main__': # if this program was imported by another, the above test will fail, # and this following code won't be used... if os.getenv('SERVER_NAME'): import cgitb, cgi cgitb.enable() cgivars=cgi.parse() if cgivars.has_key('lat'): lat, lon = cgivars['lat'], cgivars['lon'] cgiform(lat, lon, hainaddr(lat, lon)) else: cgiform(0, 0, hainaddr(0, 0)) else: if len(sys.argv) >= 3: print hainaddr(tuple(sys.argv[1:])) else: sys.stderr.write('Usage: %s LATITUDE LONGITUDE\n') else: # if you want something to be done on import, do it here; otherwise pass pass