Commit | Line | Data |
---|---|---|
6437945a IJ |
1 | """IP address set manipulation, built on top of ipaddr.py""" |
2 | ||
c215a4bc IJ |
3 | # This file is Free Software. It was originally written for secnet. |
4 | # | |
6437945a IJ |
5 | # Copyright 2014 Ian Jackson |
6 | # | |
c215a4bc IJ |
7 | # You may redistribute secnet as a whole and/or modify it under the |
8 | # terms of the GNU General Public License as published by the Free | |
9 | # Software Foundation; either version 3, or (at your option) any | |
10 | # later version. | |
6437945a | 11 | # |
c215a4bc IJ |
12 | # You may redistribute this file and/or modify it under the terms of |
13 | # the GNU General Public License as published by the Free Software | |
14 | # Foundation; either version 2, or (at your option) any later version. | |
15 | # Note however that this version of ipaddrset.py uses the Python | |
16 | # ipaddr library from Google, which is licenced only under the Apache | |
17 | # Licence, version 2.0, which is only compatible with the GNU GPL v3 | |
18 | # (or perhaps later versions), and not with the GNU GPL v2. | |
6437945a | 19 | # |
c215a4bc | 20 | # This software is distributed in the hope that it will be useful, |
6437945a IJ |
21 | # but WITHOUT ANY WARRANTY; without even the implied warranty of |
22 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
23 | # GNU General Public License for more details. | |
24 | # | |
25 | # You should have received a copy of the GNU General Public License | |
c215a4bc IJ |
26 | # along with this software; if not, see |
27 | # https://www.gnu.org/licenses/gpl.html. | |
6437945a IJ |
28 | |
29 | import ipaddr | |
30 | ||
31 | _vsns = [6,4] | |
32 | ||
33 | class IPAddressSet: | |
34 | "A set of IP addresses" | |
35 | ||
36 | # constructors | |
37 | def __init__(self,l=[]): | |
38 | "New set contains each ipaddr.IPNetwork in the sequence l" | |
39 | self._v = {} | |
40 | for v in _vsns: | |
41 | self._v[v] = [ ] | |
42 | self.append(l) | |
43 | ||
44 | # housekeeping and representation | |
45 | def _compact(self): | |
46 | for v in _vsns: | |
47 | self._v[v] = ipaddr.collapse_address_list(self._v[v]) | |
48 | def __repr__(self): | |
49 | return "IPAddressSet(%s)" % self.networks() | |
50 | def str(self,comma=",",none="-"): | |
51 | "Human-readable string with controllable delimiters" | |
52 | if self: | |
53 | return comma.join(map(str, self.networks())) | |
54 | else: | |
55 | return none | |
56 | def __str__(self): | |
57 | return self.str() | |
58 | ||
59 | # mutators | |
60 | def append(self,l): | |
61 | "Appends each ipaddr.IPNetwork in the sequence l to self" | |
62 | self._append(l) | |
63 | self._compact() | |
64 | ||
65 | def _append(self,l): | |
66 | "Appends each ipaddr.IPNetwork in the sequence l to self" | |
67 | for a in l: | |
68 | self._v[a.version].append(a) | |
69 | ||
70 | # enquirers including standard comparisons | |
71 | def __nonzero__(self): | |
72 | for v in _vsns: | |
73 | if self._v[v]: | |
74 | return True | |
75 | return False | |
76 | ||
77 | def __eq__(self,other): | |
78 | for v in _vsns: | |
79 | if self._v[v] != other._v[v]: | |
80 | return False | |
81 | return True | |
82 | def __ne__(self,other): return not self.__eq__(other) | |
83 | def __ge__(self,other): | |
84 | """True iff self completely contains IPAddressSet other""" | |
85 | for o in other: | |
86 | if not self._contains_net(o): | |
87 | return False | |
88 | return True | |
89 | def __le__(self,other): return other.__ge__(self) | |
90 | def __gt__(self,other): return self!=other and other.__ge__(self) | |
91 | def __lt__(self,other): return other.__gt__(self) | |
92 | ||
93 | def __cmp__(self,other): | |
94 | if self==other: return 0 | |
95 | if self>=other: return +1 | |
96 | if self<=other: return -1 | |
97 | return NotImplemented | |
98 | ||
99 | def __iter__(self): | |
100 | "Iterates over minimal list of distinct IPNetworks in this set" | |
101 | for v in _vsns: | |
102 | for i in self._v[v]: | |
103 | yield i | |
104 | ||
105 | def networks(self): | |
106 | "Returns miminal list of distinct IPNetworks in this set" | |
107 | return [i for i in self] | |
108 | ||
109 | # set operations | |
110 | def intersection(self,other): | |
111 | "Returns the intersection; does not modify self" | |
112 | r = IPAddressSet() | |
113 | for v in _vsns: | |
114 | for i in self._v[v]: | |
115 | for j in other._v[v]: | |
116 | if i.overlaps(j): | |
117 | if i.prefixlen > j.prefixlen: | |
118 | r._append([i]) | |
119 | else: | |
120 | r._append([j]) | |
121 | return r | |
122 | def union(self,other): | |
123 | "Returns the union; does not modify self" | |
124 | r = IPAddressSet() | |
125 | r._append(self.networks()) | |
126 | r._append(other.networks()) | |
127 | r._compact() | |
128 | return r | |
129 | ||
130 | def _contains_net(self,n): | |
131 | """True iff self completely contains IPNetwork n""" | |
132 | for i in self: | |
133 | if i.overlaps(n) and n.prefixlen >= i.prefixlen: | |
134 | return True | |
135 | return False | |
136 | ||
137 | def contains(self,thing): | |
138 | """Returns True iff self completely contains thing. | |
139 | thing may be an IPNetwork or an IPAddressSet""" | |
140 | try: | |
141 | v = [thing.version] | |
142 | except KeyError: | |
143 | v = None | |
144 | if v: | |
145 | return self._contains_net(ipaddr.IPNetwork(thing)) | |
146 | else: | |
147 | return self.__ge__(thing) | |
148 | ||
149 | def complete_set(): | |
150 | "Returns a set containing all addresses" | |
151 | s=IPAddressSet() | |
152 | for v in _vsns: | |
153 | a=ipaddr.IPAddress(0,v) | |
154 | n=ipaddr.IPNetwork("%s/0" % a) | |
155 | s.append([n]) | |
156 | return s |