2
IPy - class and tools for handling of IPv4 and IPv6 addresses and networks.
3
See README file for learn how to use IPy.
5
Further Information might be available at:
6
http://software.inl.fr/trac/trac.cgi/wiki/IPy
9
# $HeadURL: https://svn.inl.fr/inl-svn/src/tools/ipy/tags/IPy-0.70/IPy.py $
10
# $Id: IPy.py 19309 2009-10-29 10:21:13Z haypo $
12
__rcsid__ = '$Id: IPy.py 19309 2009-10-29 10:21:13Z haypo $'
17
# Definition of the Ranges for IPv4 IPs
18
# this should include www.iana.org/assignments/ipv4-address-space
19
# and www.iana.org/assignments/multicast-addresses
21
'0': 'PUBLIC', # fall back
22
'00000000': 'PRIVATE', # 0/8
23
'00001010': 'PRIVATE', # 10/8
24
'01111111': 'PRIVATE', # 127.0/8
25
'1': 'PUBLIC', # fall back
26
'1010100111111110': 'PRIVATE', # 169.254/16
27
'101011000001': 'PRIVATE', # 172.16/12
28
'1100000010101000': 'PRIVATE', # 192.168/16
29
'11011111': 'RESERVED', # 223/8
30
'111': 'RESERVED' # 224/3
33
# Definition of the Ranges for IPv6 IPs
34
# see also www.iana.org/assignments/ipv6-address-space,
35
# www.iana.org/assignments/ipv6-tla-assignments,
36
# www.iana.org/assignments/ipv6-multicast-addresses,
37
# www.iana.org/assignments/ipv6-anycast-addresses
39
'00000000' : 'RESERVED', # ::/8
40
'00000001' : 'UNASSIGNED', # 100::/8
41
'0000001' : 'NSAP', # 200::/7
42
'0000010' : 'IPX', # 400::/7
43
'0000011' : 'UNASSIGNED', # 600::/7
44
'00001' : 'UNASSIGNED', # 800::/5
45
'0001' : 'UNASSIGNED', # 1000::/4
46
'0010000000000000' : 'RESERVED', # 2000::/16 Reserved
47
'0010000000000001' : 'ASSIGNABLE', # 2001::/16 Sub-TLA Assignments [RFC2450]
48
'00100000000000010000000': 'ASSIGNABLE IANA', # 2001:0000::/29 - 2001:01F8::/29 IANA
49
'00100000000000010000001': 'ASSIGNABLE APNIC', # 2001:0200::/29 - 2001:03F8::/29 APNIC
50
'00100000000000010000010': 'ASSIGNABLE ARIN', # 2001:0400::/29 - 2001:05F8::/29 ARIN
51
'00100000000000010000011': 'ASSIGNABLE RIPE', # 2001:0600::/29 - 2001:07F8::/29 RIPE NCC
52
'0010000000000010' : '6TO4', # 2002::/16 "6to4" [RFC3056]
53
'0011111111111110' : '6BONE', # 3FFE::/16 6bone Testing [RFC2471]
54
'0011111111111111' : 'RESERVED', # 3FFF::/16 Reserved
55
'010' : 'GLOBAL-UNICAST', # 4000::/3
56
'011' : 'UNASSIGNED', # 6000::/3
57
'100' : 'GEO-UNICAST', # 8000::/3
58
'101' : 'UNASSIGNED', # A000::/3
59
'110' : 'UNASSIGNED', # C000::/3
60
'1110' : 'UNASSIGNED', # E000::/4
61
'11110' : 'UNASSIGNED', # F000::/5
62
'111110' : 'UNASSIGNED', # F800::/6
63
'1111110' : 'UNASSIGNED', # FC00::/7
64
'111111100' : 'UNASSIGNED', # FE00::/9
65
'1111111010' : 'LINKLOCAL', # FE80::/10
66
'1111111011' : 'SITELOCAL', # FEC0::/10
67
'11111111' : 'MULTICAST', # FF00::/8
68
'0' * 96 : 'IPV4COMP', # ::/96
69
'0' * 80 + '1' * 16 : 'IPV4MAP', # ::FFFF:0:0/96
70
'0' * 128 : 'UNSPECIFIED', # ::/128
71
'0' * 127 + '1' : 'LOOPBACK' # ::1/128
76
"""Handling of IP addresses returning integers.
78
Use class IP instead because some features are not implemented for
81
def __init__(self, data, ipversion=0, make_net=0):
82
"""Create an instance of an IP object.
84
Data can be a network specification or a single IP. IP
85
addresses can be specified in all forms understood by
86
parseAddress(). The size of a network can be specified as
88
/prefixlen a.b.c.0/24 2001:658:22a:cafe::/64
89
-lastIP a.b.c.0-a.b.c.255 2001:658:22a:cafe::-2001:658:22a:cafe:ffff:ffff:ffff:ffff
90
/decimal netmask a.b.c.d/255.255.255.0 not supported for IPv6
92
If no size specification is given a size of 1 address (/32 for
93
IPv4 and /128 for IPv6) is assumed.
95
If make_net is True, an IP address will be transformed into the network
96
address by applying the specified netmask.
98
>>> print IP('127.0.0.0/8')
100
>>> print IP('127.0.0.0/255.0.0.0')
102
>>> print IP('127.0.0.0-127.255.255.255')
104
>>> print IP('127.0.0.1/255.0.0.0', make_net=True)
107
See module documentation for more examples.
110
# Print no Prefixlen for /32 and /128
111
self.NoPrefixForSingleIp = 1
113
# Do we want prefix printed by default? see _printPrefix()
114
self.WantPrefixLen = None
119
# handling of non string values in constructor
120
if type(data) == types.IntType or type(data) == types.LongType:
123
if self.ip < 0x100000000L:
132
raise ValueError, "only IPv4 and IPv6 supported"
133
self._ipversion = ipversion
134
self._prefixlen = prefixlen
135
# handle IP instance as an parameter
136
elif isinstance(data, IPint):
137
self._ipversion = data._ipversion
138
self._prefixlen = data._prefixlen
142
# splitting of a string into IP and prefixlen et. al.
145
# a.b.c.0-a.b.c.255 specification ?
147
(self.ip, parsedVersion) = parseAddress(ip)
148
if parsedVersion != 4:
149
raise ValueError, "first-last notation only allowed for IPv4"
150
(last, lastversion) = parseAddress(last)
152
raise ValueError, "last address should be IPv4, too"
154
raise ValueError, "last address should be larger than first"
155
size = last - self.ip
156
netbits = _count1Bits(size)
157
# make sure the broadcast is the same as the last ip
158
# otherwise it will return /16 for something like:
159
# 192.168.0.0-192.168.191.255
160
if IP('%s/%s' % (ip, 32-netbits)).broadcast().int() != last:
162
"the range %s is not on a network boundary." % data
165
# if no prefix is given use defaults
170
raise ValueError, "only one '/' allowed in IP Address"
173
if prefixlen.find('.') != -1:
174
# check if the user might have used a netmask like
175
# a.b.c.d/255.255.255.0
176
(netmask, vers) = parseAddress(prefixlen)
178
raise ValueError, "netmask must be IPv4"
179
prefixlen = _netmaskToPrefixlen(netmask)
181
raise ValueError, "only one '-' allowed in IP Address"
183
raise ValueError, "can't parse"
185
(self.ip, parsedVersion) = parseAddress(ip)
187
ipversion = parsedVersion
190
prefixlen = 32 - netbits
192
prefixlen = 128 - netbits
194
raise ValueError, "only IPv4 and IPv6 supported"
195
self._ipversion = ipversion
196
self._prefixlen = int(prefixlen)
199
self.ip = self.ip & _prefixlenToNetmask(self._prefixlen, self._ipversion)
201
if not _checkNetaddrWorksWithPrefixlen(self.ip,
202
self._prefixlen, self._ipversion):
203
raise ValueError, "%s has invalid prefix length (%s)" % (repr(self), self._prefixlen)
206
"""Return the first / base / network addess as an (long) integer.
210
>>> "%X" % IP('10.0.0.0/8').int()
216
"""Return the IP version of this Object.
218
>>> IP('10.0.0.0/8').version()
220
>>> IP('::1').version()
223
return self._ipversion
226
"""Returns Network Prefixlen.
228
>>> IP('10.0.0.0/8').prefixlen()
231
return self._prefixlen
235
Return the base (first) address of a network as an (long) integer.
241
Return the broadcast (last) address of a network as an (long) integer.
243
The same as IP[-1]."""
244
return self.int() + self.len() - 1
246
def _printPrefix(self, want):
247
"""Prints Prefixlen/Netmask.
249
Not really. In fact it is our universal Netmask/Prefixlen printer.
250
This is considered an internal function.
252
want == 0 / None don't return anything 1.2.3.0
253
want == 1 /prefix 1.2.3.0/24
254
want == 2 /netmask 1.2.3.0/255.255.255.0
255
want == 3 -lastip 1.2.3.0-1.2.3.255
258
if (self._ipversion == 4 and self._prefixlen == 32) or \
259
(self._ipversion == 6 and self._prefixlen == 128):
260
if self.NoPrefixForSingleIp:
263
want = self.WantPrefixLen
268
# this should work with IP and IPint
269
netmask = self.netmask()
270
if type(netmask) != types.IntType \
271
and type(netmask) != types.LongType:
272
netmask = netmask.int()
273
return "/%s" % (intToIp(netmask, self._ipversion))
275
return "-%s" % (intToIp(self.ip + self.len() - 1, self._ipversion))
278
return "/%d" % (self._prefixlen)
282
# We have different flavours to convert to:
283
# strFullsize 127.0.0.1 2001:0658:022a:cafe:0200:c0ff:fe8d:08fa
284
# strNormal 127.0.0.1 2001:658:22a:cafe:200:c0ff:fe8d:08fa
285
# strCompressed 127.0.0.1 2001:658:22a:cafe::1
286
# strHex 0x7F000001L 0x20010658022ACAFE0200C0FFFE8D08FA
287
# strDec 2130706433 42540616829182469433547974687817795834
289
def strBin(self, wantprefixlen = None):
290
"""Return a string representation as a binary value.
292
>>> print IP('127.0.0.1').strBin()
293
01111111000000000000000000000001
297
if self._ipversion == 4:
299
elif self._ipversion == 6:
302
raise ValueError, "only IPv4 and IPv6 supported"
304
if self.WantPrefixLen == None and wantprefixlen == None:
306
ret = _intToBin(self.ip)
307
return '0' * (bits - len(ret)) + ret + self._printPrefix(wantprefixlen)
309
def strCompressed(self, wantprefixlen = None):
310
"""Return a string representation in compressed format using '::' Notation.
312
>>> IP('127.0.0.1').strCompressed()
314
>>> IP('2001:0658:022a:cafe:0200::1').strCompressed()
315
'2001:658:22a:cafe:200::1'
316
>>> IP('ffff:ffff:ffff:ffff:ffff:f:f:fffc/127').strCompressed()
317
'ffff:ffff:ffff:ffff:ffff:f:f:fffc/127'
320
if self.WantPrefixLen == None and wantprefixlen == None:
323
if self._ipversion == 4:
324
return self.strFullsize(wantprefixlen)
326
if self.ip >> 32 == 0xffff:
327
ipv4 = intToIp(self.ip & 0xffffffff, 4)
328
text = "::ffff:" + ipv4 + self._printPrefix(wantprefixlen)
330
# find the longest sequence of '0'
331
hextets = [int(x, 16) for x in self.strFullsize(0).split(':')]
332
# every element of followingzeros will contain the number of zeros
333
# following the corresponding element of hextets
334
followingzeros = [0] * 8
335
for i in range(len(hextets)):
336
followingzeros[i] = _countFollowingZeros(hextets[i:])
337
# compressionpos is the position where we can start removing zeros
338
compressionpos = followingzeros.index(max(followingzeros))
339
if max(followingzeros) > 1:
340
# genererate string with the longest number of zeros cut out
341
# now we need hextets as strings
342
hextets = [x for x in self.strNormal(0).split(':')]
343
while compressionpos < len(hextets) and hextets[compressionpos] == '0':
344
del(hextets[compressionpos])
345
hextets.insert(compressionpos, '')
346
if compressionpos + 1 >= len(hextets):
348
if compressionpos == 0:
349
hextets = [''] + hextets
350
return ':'.join(hextets) + self._printPrefix(wantprefixlen)
352
return self.strNormal(0) + self._printPrefix(wantprefixlen)
354
def strNormal(self, wantprefixlen = None):
355
"""Return a string representation in the usual format.
357
>>> print IP('127.0.0.1').strNormal()
359
>>> print IP('2001:0658:022a:cafe:0200::1').strNormal()
360
2001:658:22a:cafe:200:0:0:1
363
if self.WantPrefixLen == None and wantprefixlen == None:
366
if self._ipversion == 4:
367
ret = self.strFullsize(0)
368
elif self._ipversion == 6:
369
ret = ':'.join([hex(x)[2:] for x in [int(x, 16) for x in self.strFullsize(0).split(':')]])
371
raise ValueError, "only IPv4 and IPv6 supported"
375
return ret + self._printPrefix(wantprefixlen)
377
def strFullsize(self, wantprefixlen = None):
378
"""Return a string representation in the non-mangled format.
380
>>> print IP('127.0.0.1').strFullsize()
382
>>> print IP('2001:0658:022a:cafe:0200::1').strFullsize()
383
2001:0658:022a:cafe:0200:0000:0000:0001
386
if self.WantPrefixLen == None and wantprefixlen == None:
389
return intToIp(self.ip, self._ipversion).lower() + self._printPrefix(wantprefixlen)
391
def strHex(self, wantprefixlen = None):
392
"""Return a string representation in hex format in lower case.
394
>>> IP('127.0.0.1').strHex()
396
>>> IP('2001:0658:022a:cafe:0200::1').strHex()
397
'0x20010658022acafe0200000000000001'
400
if self.WantPrefixLen == None and wantprefixlen == None:
406
return x.lower() + self._printPrefix(wantprefixlen)
408
def strDec(self, wantprefixlen = None):
409
"""Return a string representation in decimal format.
411
>>> print IP('127.0.0.1').strDec()
413
>>> print IP('2001:0658:022a:cafe:0200::1').strDec()
414
42540616829182469433547762482097946625
417
if self.WantPrefixLen == None and wantprefixlen == None:
423
return x + self._printPrefix(wantprefixlen)
426
"""Return a description of the IP type ('PRIVATE', 'RESERVERD', etc).
428
>>> print IP('127.0.0.1').iptype()
430
>>> print IP('192.168.1.1').iptype()
432
>>> print IP('195.185.1.2').iptype()
434
>>> print IP('::1').iptype()
436
>>> print IP('2001:0658:022a:cafe:0200::1').iptype()
439
The type information for IPv6 is out of sync with reality.
442
# this could be greatly improved
444
if self._ipversion == 4:
446
elif self._ipversion == 6:
449
raise ValueError, "only IPv4 and IPv6 supported"
452
for i in range(len(bits), 0, -1):
453
if iprange.has_key(bits[:i]):
454
return iprange[bits[:i]]
459
"""Return netmask as an integer.
461
>>> "%X" % IP('195.185.0.0/16').netmask().int()
465
# TODO: unify with prefixlenToNetmask?
466
if self._ipversion == 4:
467
locallen = 32 - self._prefixlen
468
elif self._ipversion == 6:
469
locallen = 128 - self._prefixlen
471
raise ValueError, "only IPv4 and IPv6 supported"
473
return ((2L ** self._prefixlen) - 1) << locallen
476
def strNetmask(self):
477
"""Return netmask as an string. Mostly useful for IPv6.
479
>>> print IP('195.185.0.0/16').strNetmask()
481
>>> print IP('2001:0658:022a:cafe::0/64').strNetmask()
485
# TODO: unify with prefixlenToNetmask?
486
if self._ipversion == 4:
487
locallen = 32 - self._prefixlen
488
return intToIp(((2L ** self._prefixlen) - 1) << locallen, 4)
489
elif self._ipversion == 6:
490
locallen = 128 - self._prefixlen
491
return "/%d" % self._prefixlen
493
raise ValueError, "only IPv4 and IPv6 supported"
496
"""Return the length of a subnet.
498
>>> print IP('195.185.1.0/28').len()
500
>>> print IP('195.185.1.0/24').len()
504
if self._ipversion == 4:
505
locallen = 32 - self._prefixlen
506
elif self._ipversion == 6:
507
locallen = 128 - self._prefixlen
509
raise ValueError, "only IPv4 and IPv6 supported"
511
return 2L ** locallen
514
def __nonzero__(self):
515
"""All IPy objects should evaluate to true in boolean context.
516
Ordinarily they do, but if handling a default route expressed as
517
0.0.0.0/0, the __len__() of the object becomes 0, which is used
518
as the boolean value of the object.
524
"""Return the length of a subnet.
526
Called to implement the built-in function len().
527
It breaks with IPv6 Networks. Anybody knows how to fix this."""
529
# Python < 2.2 has this silly restriction which breaks IPv6
530
# how about Python >= 2.2 ... ouch - it persists!
532
return int(self.len())
535
def __getitem__(self, key):
536
"""Called to implement evaluation of self[key].
538
>>> ip=IP('127.0.0.0/30')
552
if type(key) != types.IntType and type(key) != types.LongType:
555
if abs(key) <= self.len():
556
key = self.len() - abs(key)
560
if key >= self.len():
563
return self.ip + long(key)
567
def __contains__(self, item):
568
"""Called to implement membership test operators.
570
Should return true if item is in self, false otherwise. Item
571
can be other IP-objects, strings or ints.
573
>>> IP('195.185.1.1').strHex()
575
>>> 0xC3B90101L in IP('195.185.1.0/24')
577
>>> '127.0.0.1' in IP('127.0.0.0/24')
579
>>> IP('127.0.0.0/24') in IP('127.0.0.0/25')
584
if item.ip >= self.ip and item.ip < self.ip + self.len() - item.len() + 1:
590
def overlaps(self, item):
591
"""Check if two IP address ranges overlap.
593
Returns 0 if the two ranges don't overlap, 1 if the given
594
range overlaps at the end and -1 if it does at the beginning.
596
>>> IP('192.168.0.0/23').overlaps('192.168.1.0/24')
598
>>> IP('192.168.0.0/23').overlaps('192.168.1.255')
600
>>> IP('192.168.0.0/23').overlaps('192.168.2.0')
602
>>> IP('192.168.1.0/24').overlaps('192.168.0.0/23')
607
if item.ip >= self.ip and item.ip < self.ip + self.len():
609
elif self.ip >= item.ip and self.ip < item.ip + item.len():
616
"""Dispatch to the prefered String Representation.
618
Used to implement str(IP)."""
620
return self.strCompressed()
624
"""Print a representation of the Object.
626
Used to implement repr(IP). Returns a string which evaluates
627
to an identical Object (without the wantprefixlen stuff - see
630
>>> print repr(IP('10.0.0.0/24'))
634
return("IPint('%s')" % (self.strCompressed(1)))
637
def __cmp__(self, other):
638
"""Called by comparison operations.
640
Should return a negative integer if self < other, zero if self
641
== other, a positive integer if self > other.
643
Networks with different prefixlen are considered non-equal.
644
Networks with the same prefixlen and differing addresses are
645
considered non equal but are compared by their base address
646
integer value to aid sorting of IP objects.
648
The version of Objects is not put into consideration.
650
>>> IP('10.0.0.0/24') > IP('10.0.0.0')
652
>>> IP('10.0.0.0/24') < IP('10.0.0.0')
654
>>> IP('10.0.0.0/24') < IP('12.0.0.0/24')
656
>>> IP('10.0.0.0/24') > IP('12.0.0.0/24')
661
# Im not really sure if this is "the right thing to do"
662
if self._prefixlen < other.prefixlen():
663
return (other.prefixlen() - self._prefixlen)
664
elif self._prefixlen > other.prefixlen():
666
# Fixed bySamuel Krempp <krempp@crans.ens-cachan.fr>:
668
# The bug is quite obvious really (as 99% bugs are once
669
# spotted, isn't it ? ;-) Because of precedence of
670
# multiplication by -1 over the substraction, prefixlen
671
# differences were causing the __cmp__ function to always
672
# return positive numbers, thus the function was failing
673
# the basic assumptions for a __cmp__ function.
675
# Namely we could have (a > b AND b > a), when the
676
# prefixlen of a and b are different. (eg let
677
# a=IP("1.0.0.0/24"); b=IP("2.0.0.0/16");) thus, anything
678
# could happen when launching a sort algorithm..
679
# everything's in order with the trivial, attached patch.
681
return (self._prefixlen - other.prefixlen()) * -1
683
if self.ip < other.ip:
685
elif self.ip > other.ip:
687
elif self._ipversion != other._ipversion:
688
# IP('0.0.0.0'), IP('::/0')
689
return cmp(self._ipversion, other._ipversion)
695
"""Called for the key object for dictionary operations, and by
696
the built-in function hash(). Should return a 32-bit integer
697
usable as a hash value for dictionary operations. The only
698
required property is that objects which compare equal have the
701
>>> IP('10.0.0.0/24').__hash__()
708
thehash = thehash ^ (ip & 0x7fffffff)
710
thehash = thehash ^ self._prefixlen
715
"""Class for handling IP addresses and networks."""
718
"""Return the base (first) address of a network as an IP object.
722
>>> IP('10.0.0.0/8').net()
725
return IP(IPint.net(self), ipversion=self._ipversion)
728
"""Return the broadcast (last) address of a network as an IP object.
732
>>> IP('10.0.0.0/8').broadcast()
735
return IP(IPint.broadcast(self))
738
"""Return netmask as an IP object.
740
>>> IP('10.0.0.0/8').netmask()
743
return IP(IPint.netmask(self))
746
def reverseNames(self):
747
"""Return a list with values forming the reverse lookup.
749
>>> IP('213.221.113.87/32').reverseNames()
750
['87.113.221.213.in-addr.arpa.']
751
>>> IP('213.221.112.224/30').reverseNames()
752
['224.112.221.213.in-addr.arpa.', '225.112.221.213.in-addr.arpa.', '226.112.221.213.in-addr.arpa.', '227.112.221.213.in-addr.arpa.']
753
>>> IP('127.0.0.0/24').reverseNames()
754
['0.0.127.in-addr.arpa.']
755
>>> IP('127.0.0.0/23').reverseNames()
756
['0.0.127.in-addr.arpa.', '1.0.127.in-addr.arpa.']
757
>>> IP('127.0.0.0/16').reverseNames()
758
['0.127.in-addr.arpa.']
759
>>> IP('127.0.0.0/15').reverseNames()
760
['0.127.in-addr.arpa.', '1.127.in-addr.arpa.']
761
>>> IP('128.0.0.0/8').reverseNames()
762
['128.in-addr.arpa.']
763
>>> IP('128.0.0.0/7').reverseNames()
764
['128.in-addr.arpa.', '129.in-addr.arpa.']
765
>>> IP('::1:2').reverseNames()
766
['2.0.0.0.1.ip6.arpa.']
769
if self._ipversion == 4:
771
# TODO: Refactor. Add support for IPint objects
772
if self.len() < 2**8:
774
ret.append(x.reverseName())
775
elif self.len() < 2**16L:
776
for i in range(0, self.len(), 2**8):
777
ret.append(self[i].reverseName()[2:])
778
elif self.len() < 2**24L:
779
for i in range(0, self.len(), 2**16):
780
ret.append(self[i].reverseName()[4:])
782
for i in range(0, self.len(), 2**24):
783
ret.append(self[i].reverseName()[6:])
785
elif self._ipversion == 6:
786
s = hex(self.ip)[2:].lower()
789
if self._prefixlen % 4 != 0:
790
raise NotImplementedError, "can't create IPv6 reverse names at sub nibble level"
794
first_nibble_index = int(32 - (self._prefixlen / 4)) * 2
795
return ["%s.ip6.arpa." % s[first_nibble_index:]]
797
raise ValueError, "only IPv4 and IPv6 supported"
801
def reverseName(self):
802
"""Return the value for reverse lookup/PTR records as RFC 2317 look alike.
804
RFC 2317 is an ugly hack which only works for sub-/24 e.g. not
805
for /23. Do not use it. Better set up a zone for every
806
address. See reverseName for a way to achieve that.
808
>>> print IP('195.185.1.1').reverseName()
809
1.1.185.195.in-addr.arpa.
810
>>> print IP('195.185.1.0/28').reverseName()
811
0-15.1.185.195.in-addr.arpa.
812
>>> IP('::1:2').reverseName()
813
'2.0.0.0.1.ip6.arpa.'
816
if self._ipversion == 4:
817
s = self.strFullsize(0)
820
first_byte_index = int(4 - (self._prefixlen / 8))
821
if self._prefixlen % 8 != 0:
822
nibblepart = "%s-%s" % (s[3-(self._prefixlen / 8)], intToIp(self.ip + self.len() - 1, 4).split('.')[-1])
823
if nibblepart[-1] == 'l':
824
nibblepart = nibblepart[:-1]
829
s = '.'.join(s[first_byte_index:])
830
return "%s%s.in-addr.arpa." % (nibblepart, s)
832
elif self._ipversion == 6:
833
s = hex(self.ip)[2:].lower()
836
if self._prefixlen % 4 != 0:
837
nibblepart = "%s-%s" % (s[self._prefixlen:], hex(self.ip + self.len() - 1)[2:].lower())
838
if nibblepart[-1] == 'l':
839
nibblepart = nibblepart[:-1]
846
first_nibble_index = int(32 - (self._prefixlen / 4)) * 2
847
return "%s%s.ip6.arpa." % (nibblepart, s[first_nibble_index:])
849
raise ValueError, "only IPv4 and IPv6 supported"
851
def make_net(self, netmask):
852
"""Transform a single IP address into a network specification by
853
applying the given netmask.
855
Returns a new IP instance.
857
>>> print IP('127.0.0.1').make_net('255.0.0.0')
860
if '/' in str(netmask):
861
raise ValueError, "invalid netmask (%s)" % netmask
862
return IP('%s/%s' % (self, netmask), make_net=True)
864
def __getitem__(self, key):
865
"""Called to implement evaluation of self[key].
867
>>> ip=IP('127.0.0.0/30')
877
>>> print str(ip[-1])
880
return IP(IPint.__getitem__(self, key))
883
"""Print a representation of the Object.
889
return("IP('%s')" % (self.strCompressed(1)))
891
def __add__(self, other):
892
"""Emulate numeric objects through network aggregation"""
893
if self.prefixlen() != other.prefixlen():
894
raise ValueError, "Only networks with the same prefixlen can be added."
895
if self.prefixlen < 1:
896
raise ValueError, "Networks with a prefixlen longer than /1 can't be added."
897
if self.version() != other.version():
898
raise ValueError, "Only networks with the same IP version can be added."
900
# fixed by Skinny Puppy <skin_pup-IPy@happypoo.com>
901
return other.__add__(self)
904
ret._prefixlen = self.prefixlen() - 1
908
def _parseAddressIPv6(ipstr):
910
Internal function used by parseAddress() to parse IPv6 address with ':'.
912
>>> _parseAddressIPv6('::')
914
>>> _parseAddressIPv6('::1')
916
>>> _parseAddressIPv6('0:0:0:0:0:0:0:1')
918
>>> _parseAddressIPv6('0:0:0::0:0:1')
920
>>> _parseAddressIPv6('0:0:0:0:0:0:0:0')
922
>>> _parseAddressIPv6('0:0:0::0:0:0')
925
>>> _parseAddressIPv6('FEDC:BA98:7654:3210:FEDC:BA98:7654:3210')
926
338770000845734292534325025077361652240L
927
>>> _parseAddressIPv6('1080:0000:0000:0000:0008:0800:200C:417A')
928
21932261930451111902915077091070067066L
929
>>> _parseAddressIPv6('1080:0:0:0:8:800:200C:417A')
930
21932261930451111902915077091070067066L
931
>>> _parseAddressIPv6('1080:0::8:800:200C:417A')
932
21932261930451111902915077091070067066L
933
>>> _parseAddressIPv6('1080::8:800:200C:417A')
934
21932261930451111902915077091070067066L
935
>>> _parseAddressIPv6('FF01:0:0:0:0:0:0:43')
936
338958331222012082418099330867817087043L
937
>>> _parseAddressIPv6('FF01:0:0::0:0:43')
938
338958331222012082418099330867817087043L
939
>>> _parseAddressIPv6('FF01::43')
940
338958331222012082418099330867817087043L
941
>>> _parseAddressIPv6('0:0:0:0:0:0:13.1.68.3')
943
>>> _parseAddressIPv6('::13.1.68.3')
945
>>> _parseAddressIPv6('0:0:0:0:0:FFFF:129.144.52.38')
947
>>> _parseAddressIPv6('::FFFF:129.144.52.38')
949
>>> _parseAddressIPv6('1080:0:0:0:8:800:200C:417A')
950
21932261930451111902915077091070067066L
951
>>> _parseAddressIPv6('1080::8:800:200C:417A')
952
21932261930451111902915077091070067066L
953
>>> _parseAddressIPv6('::1:2:3:4:5:6')
954
1208962713947218704138246L
955
>>> _parseAddressIPv6('1:2:3:4:5:6::')
956
5192455318486707404433266432802816L
959
# Split string into a list, example:
960
# '1080:200C::417A' => ['1080', '200C', '417A'] and fill_pos=2
961
# and fill_pos is the position of '::' in the list
965
while index < len(ipstr):
967
if text.startswith("::"):
968
if fill_pos is not None:
969
# Invalid IPv6, eg. '1::2::'
970
raise ValueError("%r: Invalid IPv6 address: more than one '::'" % ipstr)
971
fill_pos = len(items)
976
# Invalid IPv6, eg. '1::2:'
977
raise ValueError("%r: Invalid IPv6 address" % ipstr)
979
items.append(text[:pos])
980
if text[pos:pos+2] == "::":
985
if index == len(ipstr):
986
# Invalid IPv6, eg. '1::2:'
987
raise ValueError("%r: Invalid IPv6 address" % ipstr)
992
if items and '.' in items[-1]:
993
# IPv6 ending with IPv4 like '::ffff:192.168.0.1'
994
if not (fill_pos <= len(items)-1):
995
# Invalid IPv6: 'ffff:192.168.0.1::'
996
raise ValueError("%r: Invalid IPv6 address: '::' after IPv4" % ipstr)
997
value = parseAddress(items[-1])[0]
998
items = items[:-1] + ["%04x" % (value >> 16), "%04x" % (value & 0xffff)]
1000
# Expand fill_pos to fill with '0'
1001
# ['1','2'] with fill_pos=1 => ['1', '0', '0', '0', '0', '0', '0', '2']
1002
if fill_pos is not None:
1003
diff = 8 - len(items)
1005
raise ValueError("%r: Invalid IPv6 address: '::' is not needed" % ipstr)
1006
items = items[:fill_pos] + ['0']*diff + items[fill_pos:]
1008
# Here we have a list of 8 strings
1010
# Invalid IPv6, eg. '1:2:3'
1011
raise ValueError("%r: Invalid IPv6 address: should have 8 hextets" % ipstr)
1013
# Convert strings to long integer
1018
item = int(item, 16)
1019
error = not(0 <= item <= 0xFFFF)
1023
raise ValueError("%r: Invalid IPv6 address: invalid hexlet %r" % (ipstr, item))
1024
value = (value << 16) + item
1028
def parseAddress(ipstr):
1030
Parse a string and return the corresponding IP address (as integer)
1031
and a guess of the IP version.
1033
Following address formats are recognized:
1035
>>> parseAddress('0x0123456789abcdef') # IPv4 if <= 0xffffffff else IPv6
1036
(81985529216486895L, 6)
1037
>>> parseAddress('123.123.123.123') # IPv4
1039
>>> parseAddress('123.123') # 0-padded IPv4
1041
>>> parseAddress('1080:0000:0000:0000:0008:0800:200C:417A')
1042
(21932261930451111902915077091070067066L, 6)
1043
>>> parseAddress('1080:0:0:0:8:800:200C:417A')
1044
(21932261930451111902915077091070067066L, 6)
1045
>>> parseAddress('1080:0::8:800:200C:417A')
1046
(21932261930451111902915077091070067066L, 6)
1047
>>> parseAddress('::1')
1049
>>> parseAddress('::')
1051
>>> parseAddress('0:0:0:0:0:FFFF:129.144.52.38')
1052
(281472855454758L, 6)
1053
>>> parseAddress('::13.1.68.3')
1055
>>> parseAddress('::FFFF:129.144.52.38')
1056
(281472855454758L, 6)
1059
if ipstr.startswith('0x'):
1060
ret = long(ipstr[2:], 16)
1061
if ret > 0xffffffffffffffffffffffffffffffffL:
1062
raise ValueError, "%r: IP Address can't be bigger than 2^128" % (ipstr)
1063
if ret < 0x100000000L:
1068
if ipstr.find(':') != -1:
1069
return (_parseAddressIPv6(ipstr), 6)
1071
elif len(ipstr) == 32:
1072
# assume IPv6 in pure hexadecimal notation
1073
return (long(ipstr, 16), 6)
1075
elif ipstr.find('.') != -1 or (len(ipstr) < 4 and int(ipstr) < 256):
1076
# assume IPv4 ('127' gets interpreted as '127.0.0.0')
1077
bytes = ipstr.split('.')
1079
raise ValueError, "IPv4 Address with more than 4 bytes"
1080
bytes += ['0'] * (4 - len(bytes))
1081
bytes = [long(x) for x in bytes]
1083
if x > 255 or x < 0:
1084
raise ValueError, "%r: single byte must be 0 <= byte < 256" % (ipstr)
1085
return ((bytes[0] << 24) + (bytes[1] << 16) + (bytes[2] << 8) + bytes[3], 4)
1088
# we try to interprete it as a decimal digit -
1089
# this ony works for numbers > 255 ... others
1090
# will be interpreted as IPv4 first byte
1091
ret = long(ipstr, 10)
1092
if ret > 0xffffffffffffffffffffffffffffffffL:
1093
raise ValueError, "IP Address can't be bigger than 2^128"
1094
if ret <= 0xffffffffL:
1100
def intToIp(ip, version):
1101
"""Transform an integer string into an IP address."""
1103
# just to be sure and hoping for Python 2.22
1107
raise ValueError, "IPs can't be negative: %d" % (ip)
1111
if ip > 0xffffffffL:
1112
raise ValueError, "IPv4 Addresses can't be larger than 0xffffffff: %s" % (hex(ip))
1114
ret = str(ip & 0xffL) + '.' + ret
1118
if ip > 0xffffffffffffffffffffffffffffffffL:
1119
raise ValueError, "IPv6 Addresses can't be larger than 0xffffffffffffffffffffffffffffffff: %s" % (hex(ip))
1120
l = '0' * 32 + hex(ip)[2:-1]
1121
for x in range(1, 33):
1127
raise ValueError, "only IPv4 and IPv6 supported"
1131
def _ipVersionToLen(version):
1132
"""Return number of bits in address for a certain IP version.
1134
>>> _ipVersionToLen(4)
1136
>>> _ipVersionToLen(6)
1138
>>> _ipVersionToLen(5)
1139
Traceback (most recent call last):
1140
File "<stdin>", line 1, in ?
1141
File "IPy.py", line 1076, in _ipVersionToLen
1142
raise ValueError, "only IPv4 and IPv6 supported"
1143
ValueError: only IPv4 and IPv6 supported
1151
raise ValueError, "only IPv4 and IPv6 supported"
1154
def _countFollowingZeros(l):
1155
"""Return number of elements containing 0 at the beginning of the list."""
1161
return 1 + _countFollowingZeros(l[1:])
1164
_BitTable = {'0': '0000', '1': '0001', '2': '0010', '3': '0011',
1165
'4': '0100', '5': '0101', '6': '0110', '7': '0111',
1166
'8': '1000', '9': '1001', 'a': '1010', 'b': '1011',
1167
'c': '1100', 'd': '1101', 'e': '1110', 'f': '1111'}
1170
"""Return the binary representation of an integer as string."""
1173
raise ValueError, "Only positive values allowed"
1174
s = hex(val).lower()
1180
if not _BitTable.has_key(x):
1181
raise AssertionError, "hex() returned strange result"
1183
# remove leading zeros
1184
while ret[0] == '0' and len(ret) > 1:
1188
def _count1Bits(num):
1189
"""Find the highest bit set to 1 in an integer."""
1196
def _count0Bits(num):
1197
"""Find the highest bit set to 0 in an integer."""
1199
# this could be so easy if _count1Bits(~long(num)) would work as excepted
1202
raise ValueError, "Only positive Numbers please: %s" % (num)
1212
def _checkPrefix(ip, prefixlen, version):
1213
"""Check the validity of a prefix
1215
Checks if the variant part of a prefix only has 0s, and the length is
1218
>>> _checkPrefix(0x7f000000L, 24, 4)
1220
>>> _checkPrefix(0x7f000001L, 24, 4)
1222
>>> repr(_checkPrefix(0x7f000001L, -1, 4))
1224
>>> repr(_checkPrefix(0x7f000001L, 33, 4))
1228
# TODO: unify this v4/v6/invalid code in a function
1229
bits = _ipVersionToLen(version)
1231
if prefixlen < 0 or prefixlen > bits:
1237
zbits = _count0Bits(ip)
1238
if zbits < bits - prefixlen:
1244
def _checkNetmask(netmask, masklen):
1245
"""Checks if a netmask is expressable as a prefixlen."""
1250
# remove zero bits at the end
1251
while (num & 1) == 0 and bits != 0:
1256
# now check if the rest consists only of ones
1259
raise ValueError, "Netmask %s can't be expressed as an prefix." % (hex(netmask))
1264
def _checkNetaddrWorksWithPrefixlen(net, prefixlen, version):
1265
"""Check if a base addess of a network is compatible with a prefixlen"""
1266
if net & _prefixlenToNetmask(prefixlen, version) == net:
1272
def _netmaskToPrefixlen(netmask):
1273
"""Convert an Integer representing a netmask to a prefixlen.
1275
E.g. 0xffffff00 (255.255.255.0) returns 24
1278
netlen = _count0Bits(netmask)
1279
masklen = _count1Bits(netmask)
1280
_checkNetmask(netmask, masklen)
1281
return masklen - netlen
1284
def _prefixlenToNetmask(prefixlen, version):
1285
"""Return a mask of n bits as a long integer.
1287
From 'IP address conversion functions with the builtin socket module'
1289
http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/66517
1294
raise ValueError, "Prefixlen must be > 0"
1295
return ((2L<<prefixlen-1)-1) << (_ipVersionToLen(version) - prefixlen)
1298
if __name__ == "__main__":
1300
failure, nbtest = doctest.testmod()