| 1 |
|
|---|
| 2 |
|
|---|
| 3 |
|
|---|
| 4 |
|
|---|
| 5 |
|
|---|
| 6 |
|
|---|
| 7 |
|
|---|
| 8 |
|
|---|
| 9 |
|
|---|
| 10 |
from config import conf |
|---|
| 11 |
from data import * |
|---|
| 12 |
from utils import * |
|---|
| 13 |
|
|---|
| 14 |
|
|---|
| 15 |
def construct_source_candidate_set(addr, plen, laddr, loname): |
|---|
| 16 |
""" |
|---|
| 17 |
Given all addresses assigned to a specific interface ('laddr' parameter), |
|---|
| 18 |
this function returns the "candidate set" associated with 'addr/plen'. |
|---|
| 19 |
|
|---|
| 20 |
Basically, the function filters all interface addresses to keep only those |
|---|
| 21 |
that have the same scope as provided prefix. |
|---|
| 22 |
|
|---|
| 23 |
This is on this list of addresses that the source selection mechanism |
|---|
| 24 |
will then be performed to select the best source address associated |
|---|
| 25 |
with some specific destination that uses this prefix. |
|---|
| 26 |
""" |
|---|
| 27 |
|
|---|
| 28 |
cset = [] |
|---|
| 29 |
if in6_isgladdr(addr): |
|---|
| 30 |
cset = filter(lambda x: x[1] == IPV6_ADDR_GLOBAL, laddr) |
|---|
| 31 |
elif in6_islladdr(addr): |
|---|
| 32 |
cset = filter(lambda x: x[1] == IPV6_ADDR_LINKLOCAL, laddr) |
|---|
| 33 |
elif in6_issladdr(addr): |
|---|
| 34 |
cset = filter(lambda x: x[1] == IPV6_ADDR_SITELOCAL, laddr) |
|---|
| 35 |
elif in6_ismaddr(addr): |
|---|
| 36 |
if in6_ismnladdr(addr): |
|---|
| 37 |
cset = [('::1', 16, loname)] |
|---|
| 38 |
elif in6_ismgladdr(addr): |
|---|
| 39 |
cset = filter(lambda x: x[1] == IPV6_ADDR_GLOBAL, laddr) |
|---|
| 40 |
elif in6_ismlladdr(addr): |
|---|
| 41 |
cset = filter(lambda x: x[1] == IPV6_ADDR_LINKLOCAL, laddr) |
|---|
| 42 |
elif in6_ismsladdr(addr): |
|---|
| 43 |
cset = filter(lambda x: x[1] == IPV6_ADDR_SITELOCAL, laddr) |
|---|
| 44 |
elif addr == '::' and plen == 0: |
|---|
| 45 |
cset = filter(lambda x: x[1] == IPV6_ADDR_GLOBAL, laddr) |
|---|
| 46 |
cset = map(lambda x: x[0], cset) |
|---|
| 47 |
return cset |
|---|
| 48 |
|
|---|
| 49 |
def get_source_addr_from_candidate_set(dst, candidate_set): |
|---|
| 50 |
""" |
|---|
| 51 |
This function implement a limited version of source address selection |
|---|
| 52 |
algorithm defined in section 5 of RFC 3484. The format is very different |
|---|
| 53 |
from that described in the document because it operates on a set |
|---|
| 54 |
of candidate source address for some specific route. |
|---|
| 55 |
|
|---|
| 56 |
Rationale behind the implementation is to be able to make the right |
|---|
| 57 |
choice for a 6to4 destination when both a 6to4 address and a IPv6 native |
|---|
| 58 |
address are available for that interface. |
|---|
| 59 |
""" |
|---|
| 60 |
|
|---|
| 61 |
if len(candidate_set) == 0: |
|---|
| 62 |
|
|---|
| 63 |
return None |
|---|
| 64 |
|
|---|
| 65 |
if in6_isaddr6to4(dst): |
|---|
| 66 |
tmp = filter(lambda x: in6_isaddr6to4(x), candidate_set) |
|---|
| 67 |
if len(tmp) != 0: |
|---|
| 68 |
return tmp[0] |
|---|
| 69 |
|
|---|
| 70 |
return candidate_set[0] |
|---|
| 71 |
|
|---|
| 72 |
|
|---|
| 73 |
def find_ifaddr2(addr, plen, laddr): |
|---|
| 74 |
dstAddrType = in6_getAddrType(addr) |
|---|
| 75 |
|
|---|
| 76 |
if dstAddrType == IPV6_ADDR_UNSPECIFIED: |
|---|
| 77 |
return None |
|---|
| 78 |
|
|---|
| 79 |
if dstAddrType == IPV6_ADDR_LOOPBACK: |
|---|
| 80 |
return None |
|---|
| 81 |
|
|---|
| 82 |
tmp = [[]] + map(lambda (x,y,z): (in6_getAddrType(x), x, y, z), laddr) |
|---|
| 83 |
def filterSameScope(l, t): |
|---|
| 84 |
if (t[0] & dstAddrType & IPV6_ADDR_SCOPE_MASK) == 0: |
|---|
| 85 |
l.append(t) |
|---|
| 86 |
return l |
|---|
| 87 |
sameScope = reduce(filterSameScope, tmp) |
|---|
| 88 |
|
|---|
| 89 |
l = len(sameScope) |
|---|
| 90 |
if l == 1: |
|---|
| 91 |
return sameScope[0][1] |
|---|
| 92 |
|
|---|
| 93 |
elif l > 1: |
|---|
| 94 |
stfAddr = filter(lambda x: x[0] & IPV6_ADDR_6TO4, sameScope) |
|---|
| 95 |
nativeAddr = filter(lambda x: not (x[0] & IPV6_ADDR_6TO4), sameScope) |
|---|
| 96 |
|
|---|
| 97 |
if not (dstAddrType & IPV6_ADDR_6TO4): |
|---|
| 98 |
if len(nativeAddr) != 0: |
|---|
| 99 |
return nativeAddr[0][1] |
|---|
| 100 |
return stfAddr[0][1] |
|---|
| 101 |
|
|---|
| 102 |
else: |
|---|
| 103 |
if len(stfAddr) != 0: |
|---|
| 104 |
return stfAddr[0][1] |
|---|
| 105 |
return nativeAddr[0][1] |
|---|
| 106 |
else: |
|---|
| 107 |
return None |
|---|
| 108 |
|
|---|
| 109 |
|
|---|
| 110 |
|
|---|
| 111 |
|
|---|
| 112 |
def in6_getAddrType(addr): |
|---|
| 113 |
naddr = inet_pton(socket.AF_INET6, addr) |
|---|
| 114 |
paddr = inet_ntop(socket.AF_INET6, naddr) |
|---|
| 115 |
addrType = 0 |
|---|
| 116 |
|
|---|
| 117 |
|
|---|
| 118 |
if ((struct.unpack("B", naddr[0])[0] & 0xE0) == 0x20): |
|---|
| 119 |
addrType = (IPV6_ADDR_UNICAST | IPV6_ADDR_GLOBAL) |
|---|
| 120 |
if naddr[:2] == ' \x02': |
|---|
| 121 |
addrType |= IPV6_ADDR_6TO4 |
|---|
| 122 |
elif naddr[0] == '\xff': |
|---|
| 123 |
addrScope = paddr[3] |
|---|
| 124 |
if addrScope == '2': |
|---|
| 125 |
addrType = (IPV6_ADDR_LINKLOCAL | IPV6_ADDR_MULTICAST) |
|---|
| 126 |
elif addrScope == 'e': |
|---|
| 127 |
addrType = (IPV6_ADDR_GLOBAL | IPV6_ADDR_MULTICAST) |
|---|
| 128 |
else: |
|---|
| 129 |
addrType = (IPV6_ADDR_GLOBAL | IPV6_ADDR_MULTICAST) |
|---|
| 130 |
elif ((naddr[0] == '\xfe') and ((int(paddr[2], 16) & 0xC) == 0x8)): |
|---|
| 131 |
addrType = (IPV6_ADDR_UNICAST | IPV6_ADDR_LINKLOCAL) |
|---|
| 132 |
elif paddr == "::1": |
|---|
| 133 |
addrType = IPV6_ADDR_LOOPBACK |
|---|
| 134 |
elif paddr == "::": |
|---|
| 135 |
addrType = IPV6_ADDR_UNSPECIFIED |
|---|
| 136 |
else: |
|---|
| 137 |
|
|---|
| 138 |
|
|---|
| 139 |
addrType = (IPV6_ADDR_GLOBAL | IPV6_ADDR_UNICAST) |
|---|
| 140 |
|
|---|
| 141 |
return addrType |
|---|
| 142 |
|
|---|
| 143 |
def in6_mactoifaceid(mac, ulbit=None): |
|---|
| 144 |
""" |
|---|
| 145 |
Compute the interface ID in modified EUI-64 format associated |
|---|
| 146 |
to the Ethernet address provided as input. |
|---|
| 147 |
value taken by U/L bit in the interface identifier is basically |
|---|
| 148 |
the reversed value of that in given MAC address it can be forced |
|---|
| 149 |
to a specific value by using optional 'ulbit' parameter. |
|---|
| 150 |
""" |
|---|
| 151 |
if len(mac) != 17: return None |
|---|
| 152 |
m = "".join(mac.split(':')) |
|---|
| 153 |
if len(m) != 12: return None |
|---|
| 154 |
first = int(m[0:2], 16) |
|---|
| 155 |
if ulbit is None or not (ulbit == 0 or ulbit == 1): |
|---|
| 156 |
ulbit = [1,'-',0][first & 0x02] |
|---|
| 157 |
ulbit *= 2 |
|---|
| 158 |
first = "%.02x" % ((first & 0xFD) | ulbit) |
|---|
| 159 |
eui64 = first + m[2:4] + ":" + m[4:6] + "FF:FE" + m[6:8] + ":" + m[8:12] |
|---|
| 160 |
return eui64.upper() |
|---|
| 161 |
|
|---|
| 162 |
def in6_ifaceidtomac(ifaceid): |
|---|
| 163 |
""" |
|---|
| 164 |
Extract the mac address from provided iface ID. Iface ID is provided |
|---|
| 165 |
in printable format ("XXXX:XXFF:FEXX:XXXX", eventually compressed). None |
|---|
| 166 |
is returned on error. |
|---|
| 167 |
""" |
|---|
| 168 |
try: |
|---|
| 169 |
ifaceid = inet_pton(socket.AF_INET6, "::"+ifaceid)[8:16] |
|---|
| 170 |
except: |
|---|
| 171 |
return None |
|---|
| 172 |
if ifaceid[3:5] != '\xff\xfe': |
|---|
| 173 |
return None |
|---|
| 174 |
first = struct.unpack("B", ifaceid[:1])[0] |
|---|
| 175 |
ulbit = 2*[1,'-',0][first & 0x02] |
|---|
| 176 |
first = struct.pack("B", ((first & 0xFD) | ulbit)) |
|---|
| 177 |
oui = first + ifaceid[1:3] |
|---|
| 178 |
end = ifaceid[5:] |
|---|
| 179 |
l = map(lambda x: "%.02x" % struct.unpack("B", x)[0], list(oui+end)) |
|---|
| 180 |
return ":".join(l) |
|---|
| 181 |
|
|---|
| 182 |
def in6_addrtomac(addr): |
|---|
| 183 |
""" |
|---|
| 184 |
Extract the mac address from provided address. None is returned |
|---|
| 185 |
on error. |
|---|
| 186 |
""" |
|---|
| 187 |
mask = inet_pton(socket.AF_INET6, "::ffff:ffff:ffff:ffff") |
|---|
| 188 |
x = in6_and(mask, inet_pton(socket.AF_INET6, addr)) |
|---|
| 189 |
ifaceid = inet_ntop(socket.AF_INET6, x)[2:] |
|---|
| 190 |
return in6_ifaceidtomac(ifaceid) |
|---|
| 191 |
|
|---|
| 192 |
def in6_addrtovendor(addr): |
|---|
| 193 |
""" |
|---|
| 194 |
Extract the MAC address from a modified EUI-64 constructed IPv6 |
|---|
| 195 |
address provided and use the IANA oui.txt file to get the vendor. |
|---|
| 196 |
The database used for the conversion is the one loaded by Scapy, |
|---|
| 197 |
based on Wireshark (/usr/share/wireshark/wireshark/manuf) None |
|---|
| 198 |
is returned on error, "UNKNOWN" if the vendor is unknown. |
|---|
| 199 |
""" |
|---|
| 200 |
mac = in6_addrtomac(addr) |
|---|
| 201 |
if mac is None: |
|---|
| 202 |
return None |
|---|
| 203 |
|
|---|
| 204 |
res = conf.manufdb._get_manuf(mac) |
|---|
| 205 |
if len(res) == 17 and res.count(':') != 5: |
|---|
| 206 |
res = "UNKNOWN" |
|---|
| 207 |
|
|---|
| 208 |
return res |
|---|
| 209 |
|
|---|
| 210 |
def in6_getLinkScopedMcastAddr(addr, grpid=None, scope=2): |
|---|
| 211 |
""" |
|---|
| 212 |
Generate a Link-Scoped Multicast Address as described in RFC 4489. |
|---|
| 213 |
Returned value is in printable notation. |
|---|
| 214 |
|
|---|
| 215 |
'addr' parameter specifies the link-local address to use for generating |
|---|
| 216 |
Link-scoped multicast address IID. |
|---|
| 217 |
|
|---|
| 218 |
By default, the function returns a ::/96 prefix (aka last 32 bits of |
|---|
| 219 |
returned address are null). If a group id is provided through 'grpid' |
|---|
| 220 |
parameter, last 32 bits of the address are set to that value (accepted |
|---|
| 221 |
formats : '\x12\x34\x56\x78' or '12345678' or 0x12345678 or 305419896). |
|---|
| 222 |
|
|---|
| 223 |
By default, generated address scope is Link-Local (2). That value can |
|---|
| 224 |
be modified by passing a specific 'scope' value as an argument of the |
|---|
| 225 |
function. RFC 4489 only authorizes scope values <= 2. Enforcement |
|---|
| 226 |
is performed by the function (None will be returned). |
|---|
| 227 |
|
|---|
| 228 |
If no link-local address can be used to generate the Link-Scoped IPv6 |
|---|
| 229 |
Multicast address, or if another error occurs, None is returned. |
|---|
| 230 |
""" |
|---|
| 231 |
if not scope in [0, 1, 2]: |
|---|
| 232 |
return None |
|---|
| 233 |
try: |
|---|
| 234 |
if not in6_islladdr(addr): |
|---|
| 235 |
return None |
|---|
| 236 |
addr = inet_pton(socket.AF_INET6, addr) |
|---|
| 237 |
except: |
|---|
| 238 |
warning("in6_getLinkScopedMcastPrefix(): Invalid address provided") |
|---|
| 239 |
return None |
|---|
| 240 |
|
|---|
| 241 |
iid = addr[8:] |
|---|
| 242 |
|
|---|
| 243 |
if grpid is None: |
|---|
| 244 |
grpid = '\x00\x00\x00\x00' |
|---|
| 245 |
else: |
|---|
| 246 |
if type(grpid) is str: |
|---|
| 247 |
if len(grpid) == 8: |
|---|
| 248 |
try: |
|---|
| 249 |
grpid = int(grpid, 16) & 0xffffffff |
|---|
| 250 |
except: |
|---|
| 251 |
warning("in6_getLinkScopedMcastPrefix(): Invalid group id provided") |
|---|
| 252 |
return None |
|---|
| 253 |
elif len(grpid) == 4: |
|---|
| 254 |
try: |
|---|
| 255 |
grpid = struct.unpack("!I", grpid)[0] |
|---|
| 256 |
except: |
|---|
| 257 |
warning("in6_getLinkScopedMcastPrefix(): Invalid group id provided") |
|---|
| 258 |
return None |
|---|
| 259 |
grpid = struct.pack("!I", grpid) |
|---|
| 260 |
|
|---|
| 261 |
flgscope = struct.pack("B", 0xff & ((0x3 << 4) | scope)) |
|---|
| 262 |
plen = '\xff' |
|---|
| 263 |
res = '\x00' |
|---|
| 264 |
a = '\xff' + flgscope + res + plen + iid + grpid |
|---|
| 265 |
|
|---|
| 266 |
return inet_ntop(socket.AF_INET6, a) |
|---|
| 267 |
|
|---|
| 268 |
def in6_get6to4Prefix(addr): |
|---|
| 269 |
""" |
|---|
| 270 |
Returns the /48 6to4 prefix associated with provided IPv4 address |
|---|
| 271 |
On error, None is returned. No check is performed on public/private |
|---|
| 272 |
status of the address |
|---|
| 273 |
""" |
|---|
| 274 |
try: |
|---|
| 275 |
addr = inet_pton(socket.AF_INET, addr) |
|---|
| 276 |
addr = inet_ntop(socket.AF_INET6, '\x20\x02'+addr+'\x00'*10) |
|---|
| 277 |
except: |
|---|
| 278 |
return None |
|---|
| 279 |
return addr |
|---|
| 280 |
|
|---|
| 281 |
def in6_6to4ExtractAddr(addr): |
|---|
| 282 |
""" |
|---|
| 283 |
Extract IPv4 address embbeded in 6to4 address. Passed address must be |
|---|
| 284 |
a 6to4 addrees. None is returned on error. |
|---|
| 285 |
""" |
|---|
| 286 |
try: |
|---|
| 287 |
addr = inet_pton(socket.AF_INET6, addr) |
|---|
| 288 |
except: |
|---|
| 289 |
return None |
|---|
| 290 |
if addr[:2] != " \x02": |
|---|
| 291 |
return None |
|---|
| 292 |
return inet_ntop(socket.AF_INET, addr[2:6]) |
|---|
| 293 |
|
|---|
| 294 |
|
|---|
| 295 |
def in6_getLocalUniquePrefix(): |
|---|
| 296 |
""" |
|---|
| 297 |
Returns a pseudo-randomly generated Local Unique prefix. Function |
|---|
| 298 |
follows recommandation of Section 3.2.2 of RFC 4193 for prefix |
|---|
| 299 |
generation. |
|---|
| 300 |
""" |
|---|
| 301 |
|
|---|
| 302 |
|
|---|
| 303 |
|
|---|
| 304 |
|
|---|
| 305 |
|
|---|
| 306 |
|
|---|
| 307 |
|
|---|
| 308 |
|
|---|
| 309 |
|
|---|
| 310 |
|
|---|
| 311 |
|
|---|
| 312 |
tod = time.time() |
|---|
| 313 |
i = int(tod) |
|---|
| 314 |
j = int((tod - i)*(2**32)) |
|---|
| 315 |
tod = struct.pack("!II", i,j) |
|---|
| 316 |
|
|---|
| 317 |
rawmac = get_if_raw_hwaddr(conf.iface6)[1] |
|---|
| 318 |
mac = ":".join(map(lambda x: "%.02x" % ord(x), list(rawmac))) |
|---|
| 319 |
|
|---|
| 320 |
eui64 = inet_pton(socket.AF_INET6, '::' + in6_mactoifaceid(mac))[8:] |
|---|
| 321 |
import sha |
|---|
| 322 |
globalid = sha.new(tod+eui64).digest()[:5] |
|---|
| 323 |
return inet_ntop(socket.AF_INET6, '\xfd' + globalid + '\x00'*10) |
|---|
| 324 |
|
|---|
| 325 |
def in6_getRandomizedIfaceId(ifaceid, previous=None): |
|---|
| 326 |
""" |
|---|
| 327 |
Implements the interface ID generation algorithm described in RFC 3041. |
|---|
| 328 |
The function takes the Modified EUI-64 interface identifier generated |
|---|
| 329 |
as described in RFC 4291 and an optional previous history value (the |
|---|
| 330 |
first element of the output of this function). If no previous interface |
|---|
| 331 |
identifier is provided, a random one is generated. The function returns |
|---|
| 332 |
a tuple containing the randomized interface identifier and the history |
|---|
| 333 |
value (for possible future use). Input and output values are provided in |
|---|
| 334 |
a "printable" format as depicted below. |
|---|
| 335 |
|
|---|
| 336 |
ex: |
|---|
| 337 |
|
|---|
| 338 |
>>> in6_getRandomizedIfaceId('20b:93ff:feeb:2d3') |
|---|
| 339 |
('4c61:76ff:f46a:a5f3', 'd006:d540:db11:b092') |
|---|
| 340 |
|
|---|
| 341 |
>>> in6_getRandomizedIfaceId('20b:93ff:feeb:2d3', |
|---|
| 342 |
previous='d006:d540:db11:b092') |
|---|
| 343 |
('fe97:46fe:9871:bd38', 'eeed:d79c:2e3f:62e') |
|---|
| 344 |
""" |
|---|
| 345 |
|
|---|
| 346 |
s = "" |
|---|
| 347 |
if previous is None: |
|---|
| 348 |
d = "".join(map(chr, range(256))) |
|---|
| 349 |
for i in range(8): |
|---|
| 350 |
s += random.choice(d) |
|---|
| 351 |
previous = s |
|---|
| 352 |
s = inet_pton(socket.AF_INET6, "::"+ifaceid)[8:] + previous |
|---|
| 353 |
import md5 |
|---|
| 354 |
s = md5.new(s).digest() |
|---|
| 355 |
s1,s2 = s[:8],s[8:] |
|---|
| 356 |
s1 = chr(ord(s1[0]) | 0x04) + s1[1:] |
|---|
| 357 |
s1 = inet_ntop(socket.AF_INET6, "\xff"*8 + s1)[20:] |
|---|
| 358 |
s2 = inet_ntop(socket.AF_INET6, "\xff"*8 + s2)[20:] |
|---|
| 359 |
return (s1, s2) |
|---|
| 360 |
|
|---|
| 361 |
|
|---|
| 362 |
_rfc1924map = [ '0','1','2','3','4','5','6','7','8','9','A','B','C','D','E', |
|---|
| 363 |
'F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T', |
|---|
| 364 |
'U','V','W','X','Y','Z','a','b','c','d','e','f','g','h','i', |
|---|
| 365 |
'j','k','l','m','n','o','p','q','r','s','t','u','v','w','x', |
|---|
| 366 |
'y','z','!','#','$','%','&','(',')','*','+','-',';','<','=', |
|---|
| 367 |
'>','?','@','^','_','`','{','|','}','~' ] |
|---|
| 368 |
|
|---|
| 369 |
def in6_ctop(addr): |
|---|
| 370 |
""" |
|---|
| 371 |
Convert an IPv6 address in Compact Representation Notation |
|---|
| 372 |
(RFC 1924) to printable representation ;-) |
|---|
| 373 |
Returns None on error. |
|---|
| 374 |
""" |
|---|
| 375 |
if len(addr) != 20 or not reduce(lambda x,y: x and y, |
|---|
| 376 |
map(lambda x: x in _rfc1924map, addr)): |
|---|
| 377 |
return None |
|---|
| 378 |
i = 0 |
|---|
| 379 |
for c in addr: |
|---|
| 380 |
j = _rfc1924map.index(c) |
|---|
| 381 |
i = 85*i + j |
|---|
| 382 |
res = [] |
|---|
| 383 |
for j in range(4): |
|---|
| 384 |
res.append(struct.pack("!I", i%2**32)) |
|---|
| 385 |
i = i/(2**32) |
|---|
| 386 |
res.reverse() |
|---|
| 387 |
return inet_ntop(socket.AF_INET6, "".join(res)) |
|---|
| 388 |
|
|---|
| 389 |
def in6_ptoc(addr): |
|---|
| 390 |
""" |
|---|
| 391 |
Converts an IPv6 address in printable representation to RFC |
|---|
| 392 |
1924 Compact Representation ;-) |
|---|
| 393 |
Returns None on error. |
|---|
| 394 |
""" |
|---|
| 395 |
try: |
|---|
| 396 |
d=struct.unpack("!IIII", inet_pton(socket.AF_INET6, addr)) |
|---|
| 397 |
except: |
|---|
| 398 |
return None |
|---|
| 399 |
res = 0 |
|---|
| 400 |
m = [2**96, 2**64, 2**32, 1] |
|---|
| 401 |
for i in range(4): |
|---|
| 402 |
res += d[i]*m[i] |
|---|
| 403 |
rem = res |
|---|
| 404 |
res = [] |
|---|
| 405 |
while rem: |
|---|
| 406 |
res.append(_rfc1924map[rem%85]) |
|---|
| 407 |
rem = rem/85 |
|---|
| 408 |
res.reverse() |
|---|
| 409 |
return "".join(res) |
|---|
| 410 |
|
|---|
| 411 |
|
|---|
| 412 |
def in6_isaddr6to4(x): |
|---|
| 413 |
""" |
|---|
| 414 |
Return True if provided address (in printable format) is a 6to4 |
|---|
| 415 |
address (being in 2002::/16). |
|---|
| 416 |
""" |
|---|
| 417 |
x = inet_pton(socket.AF_INET6, x) |
|---|
| 418 |
return x[:2] == ' \x02' |
|---|
| 419 |
|
|---|
| 420 |
conf.teredoPrefix = "2001::" |
|---|
| 421 |
conf.teredoServerPort = 3544 |
|---|
| 422 |
|
|---|
| 423 |
def in6_isaddrTeredo(x): |
|---|
| 424 |
""" |
|---|
| 425 |
Return True if provided address is a Teredo, meaning it is under |
|---|
| 426 |
the /32 conf.teredoPrefix prefix value (by default, 2001::). |
|---|
| 427 |
Otherwise, False is returned. Address must be passed in printable |
|---|
| 428 |
format. |
|---|
| 429 |
""" |
|---|
| 430 |
our = inet_pton(socket.AF_INET6, x)[0:4] |
|---|
| 431 |
teredoPrefix = inet_pton(socket.AF_INET6, conf.teredoPrefix)[0:4] |
|---|
| 432 |
return teredoPrefix == our |
|---|
| 433 |
|
|---|
| 434 |
def teredoAddrExtractInfo(x): |
|---|
| 435 |
""" |
|---|
| 436 |
Extract information from a Teredo address. Return value is |
|---|
| 437 |
a 4-tuple made of IPv4 address of Teredo server, flag value (int), |
|---|
| 438 |
mapped address (non obfuscated) and mapped port (non obfuscated). |
|---|
| 439 |
No specific checks are performed on passed address. |
|---|
| 440 |
""" |
|---|
| 441 |
addr = inet_pton(socket.AF_INET6, x) |
|---|
| 442 |
server = inet_ntop(socket.AF_INET, addr[4:8]) |
|---|
| 443 |
flag = struct.unpack("!H",addr[8:10])[0] |
|---|
| 444 |
mappedport = struct.unpack("!H",strxor(addr[10:12],'\xff'*2))[0] |
|---|
| 445 |
mappedaddr = inet_ntop(socket.AF_INET, strxor(addr[12:16],'\xff'*4)) |
|---|
| 446 |
return server, flag, mappedaddr, mappedport |
|---|
| 447 |
|
|---|
| 448 |
def in6_iseui64(x): |
|---|
| 449 |
""" |
|---|
| 450 |
Return True if provided address has an interface identifier part |
|---|
| 451 |
created in modified EUI-64 format (meaning it matches *::*:*ff:fe*:*). |
|---|
| 452 |
Otherwise, False is returned. Address must be passed in printable |
|---|
| 453 |
format. |
|---|
| 454 |
""" |
|---|
| 455 |
eui64 = inet_pton(socket.AF_INET6, '::ff:fe00:0') |
|---|
| 456 |
x = in6_and(inet_pton(socket.AF_INET6, x), eui64) |
|---|
| 457 |
return x == eui64 |
|---|
| 458 |
|
|---|
| 459 |
def in6_isanycast(x): |
|---|
| 460 |
if in6_iseui64(x): |
|---|
| 461 |
s = '::fdff:ffff:ffff:ff80' |
|---|
| 462 |
x = in6_and(x, inet_pton(socket.AF_INET6, '::ffff:ffff:ffff:ff80')) |
|---|
| 463 |
x = in6_and(x, inet_pton(socket.AF_INET6, s)) |
|---|
| 464 |
return x == inet_pton(socket.AF_INET6, s) |
|---|
| 465 |
else: |
|---|
| 466 |
|
|---|
| 467 |
|
|---|
| 468 |
|
|---|
| 469 |
|
|---|
| 470 |
|
|---|
| 471 |
|
|---|
| 472 |
warning('in6_isanycast(): TODO not EUI-64') |
|---|
| 473 |
return 0 |
|---|
| 474 |
|
|---|
| 475 |
def _in6_bitops(a1, a2, operator=0): |
|---|
| 476 |
a1 = struct.unpack('4I', a1) |
|---|
| 477 |
a2 = struct.unpack('4I', a2) |
|---|
| 478 |
fop = [ lambda x,y: x | y, |
|---|
| 479 |
lambda x,y: x & y, |
|---|
| 480 |
lambda x,y: x ^ y |
|---|
| 481 |
] |
|---|
| 482 |
ret = map(fop[operator%len(fop)], a1, a2) |
|---|
| 483 |
t = ''.join(map(lambda x: struct.pack('I', x), ret)) |
|---|
| 484 |
return t |
|---|
| 485 |
|
|---|
| 486 |
def in6_or(a1, a2): |
|---|
| 487 |
""" |
|---|
| 488 |
Provides a bit to bit OR of provided addresses. They must be |
|---|
| 489 |
passed in network format. Return value is also an IPv6 address |
|---|
| 490 |
in network format. |
|---|
| 491 |
""" |
|---|
| 492 |
return _in6_bitops(a1, a2, 0) |
|---|
| 493 |
|
|---|
| 494 |
def in6_and(a1, a2): |
|---|
| 495 |
""" |
|---|
| 496 |
Provides a bit to bit AND of provided addresses. They must be |
|---|
| 497 |
passed in network format. Return value is also an IPv6 address |
|---|
| 498 |
in network format. |
|---|
| 499 |
""" |
|---|
| 500 |
return _in6_bitops(a1, a2, 1) |
|---|
| 501 |
|
|---|
| 502 |
def in6_xor(a1, a2): |
|---|
| 503 |
""" |
|---|
| 504 |
Provides a bit to bit XOR of provided addresses. They must be |
|---|
| 505 |
passed in network format. Return value is also an IPv6 address |
|---|
| 506 |
in network format. |
|---|
| 507 |
""" |
|---|
| 508 |
return _in6_bitops(a1, a2, 2) |
|---|
| 509 |
|
|---|
| 510 |
def in6_cidr2mask(m): |
|---|
| 511 |
""" |
|---|
| 512 |
Return the mask (bitstring) associated with provided length |
|---|
| 513 |
value. For instance if function is called on 48, return value is |
|---|
| 514 |
'\xff\xff\xff\xff\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'. |
|---|
| 515 |
|
|---|
| 516 |
""" |
|---|
| 517 |
if m > 128 or m < 0: |
|---|
| 518 |
raise Scapy_Exception("value provided to in6_cidr2mask outside [0, 128] domain (%d)" % m) |
|---|
| 519 |
|
|---|
| 520 |
t = [] |
|---|
| 521 |
for i in xrange(0, 4): |
|---|
| 522 |
t.append(max(0, 2**32 - 2**(32-min(32, m)))) |
|---|
| 523 |
m -= 32 |
|---|
| 524 |
|
|---|
| 525 |
return ''.join(map(lambda x: struct.pack('!I', x), t)) |
|---|
| 526 |
|
|---|
| 527 |
def in6_getnsma(a): |
|---|
| 528 |
""" |
|---|
| 529 |
Return link-local solicited-node multicast address for given |
|---|
| 530 |
address. Passed address must be provided in network format. |
|---|
| 531 |
Returned value is also in network format. |
|---|
| 532 |
""" |
|---|
| 533 |
|
|---|
| 534 |
r = in6_and(a, inet_pton(socket.AF_INET6, '::ff:ffff')) |
|---|
| 535 |
r = in6_or(inet_pton(socket.AF_INET6, 'ff02::1:ff00:0'), r) |
|---|
| 536 |
return r |
|---|
| 537 |
|
|---|
| 538 |
def in6_getnsmac(a): |
|---|
| 539 |
""" |
|---|
| 540 |
Return the multicast mac address associated with provided |
|---|
| 541 |
IPv6 address. Passed address must be in network format. |
|---|
| 542 |
""" |
|---|
| 543 |
|
|---|
| 544 |
a = struct.unpack('16B', a)[-4:] |
|---|
| 545 |
mac = '33:33:' |
|---|
| 546 |
mac += ':'.join(map(lambda x: '%.2x' %x, a)) |
|---|
| 547 |
return mac |
|---|
| 548 |
|
|---|
| 549 |
def in6_getha(prefix): |
|---|
| 550 |
""" |
|---|
| 551 |
Return the anycast address associated with all home agents on a given |
|---|
| 552 |
subnet. |
|---|
| 553 |
""" |
|---|
| 554 |
r = in6_and(inet_pton(socket.AF_INET6, prefix), in6_cidr2mask(64)) |
|---|
| 555 |
r = in6_or(r, inet_pton(socket.AF_INET6, '::fdff:ffff:ffff:fffe')) |
|---|
| 556 |
return inet_ntop(socket.AF_INET6, r) |
|---|
| 557 |
|
|---|
| 558 |
def in6_ptop(str): |
|---|
| 559 |
""" |
|---|
| 560 |
Normalizes IPv6 addresses provided in printable format, returning the |
|---|
| 561 |
same address in printable format. (2001:0db8:0:0::1 -> 2001:db8::1) |
|---|
| 562 |
""" |
|---|
| 563 |
return inet_ntop(socket.AF_INET6, inet_pton(socket.AF_INET6, str)) |
|---|
| 564 |
|
|---|
| 565 |
def in6_isincluded(addr, prefix, plen): |
|---|
| 566 |
""" |
|---|
| 567 |
Returns True when 'addr' belongs to prefix/plen. False otherwise. |
|---|
| 568 |
""" |
|---|
| 569 |
temp = inet_pton(socket.AF_INET6, addr) |
|---|
| 570 |
pref = in6_cidr2mask(plen) |
|---|
| 571 |
zero = inet_pton(socket.AF_INET6, prefix) |
|---|
| 572 |
return zero == in6_and(temp, pref) |
|---|
| 573 |
|
|---|
| 574 |
def in6_isdocaddr(str): |
|---|
| 575 |
""" |
|---|
| 576 |
Returns True if provided address in printable format belongs to |
|---|
| 577 |
2001:db8::/32 address space reserved for documentation (as defined |
|---|
| 578 |
in RFC 3849). |
|---|
| 579 |
""" |
|---|
| 580 |
return in6_isincluded(str, '2001:db8::', 32) |
|---|
| 581 |
|
|---|
| 582 |
def in6_islladdr(str): |
|---|
| 583 |
""" |
|---|
| 584 |
Returns True if provided address in printable format belongs to |
|---|
| 585 |
_allocated_ link-local unicast address space (fe80::/10) |
|---|
| 586 |
""" |
|---|
| 587 |
return in6_isincluded(str, 'fe80::', 10) |
|---|
| 588 |
|
|---|
| 589 |
def in6_issladdr(str): |
|---|
| 590 |
""" |
|---|
| 591 |
Returns True if provided address in printable format belongs to |
|---|
| 592 |
_allocated_ site-local address space (fec0::/10). This prefix has |
|---|
| 593 |
been deprecated, address being now reserved by IANA. Function |
|---|
| 594 |
will remain for historic reasons. |
|---|
| 595 |
""" |
|---|
| 596 |
return in6_isincluded(str, 'fec0::', 10) |
|---|
| 597 |
|
|---|
| 598 |
def in6_isuladdr(str): |
|---|
| 599 |
""" |
|---|
| 600 |
Returns True if provided address in printable format belongs to |
|---|
| 601 |
Unique local address space (fc00::/7). |
|---|
| 602 |
""" |
|---|
| 603 |
return in6_isincluded(str, 'fc::', 7) |
|---|
| 604 |
|
|---|
| 605 |
|
|---|
| 606 |
|
|---|
| 607 |
|
|---|
| 608 |
|
|---|
| 609 |
def in6_isgladdr(str): |
|---|
| 610 |
""" |
|---|
| 611 |
Returns True if provided address in printable format belongs to |
|---|
| 612 |
_allocated_ global address space (2000::/3). Please note that, |
|---|
| 613 |
Unique Local addresses (FC00::/7) are not part of global address |
|---|
| 614 |
space, and won't match. |
|---|
| 615 |
""" |
|---|
| 616 |
return in6_isincluded(str, '2000::', 3) |
|---|
| 617 |
|
|---|
| 618 |
def in6_ismaddr(str): |
|---|
| 619 |
""" |
|---|
| 620 |
Returns True if provided address in printable format belongs to |
|---|
| 621 |
allocated Multicast address space (ff00::/8). |
|---|
| 622 |
""" |
|---|
| 623 |
return in6_isincluded(str, 'ff00::', 8) |
|---|
| 624 |
|
|---|
| 625 |
def in6_ismnladdr(str): |
|---|
| 626 |
""" |
|---|
| 627 |
Returns True if address belongs to node-local multicast address |
|---|
| 628 |
space (ff01::/16) as defined in RFC |
|---|
| 629 |
""" |
|---|
| 630 |
return in6_isincluded(str, 'ff01::', 16) |
|---|
| 631 |
|
|---|
| 632 |
def in6_ismgladdr(str): |
|---|
| 633 |
""" |
|---|
| 634 |
Returns True if address belongs to global multicast address |
|---|
| 635 |
space (ff0e::/16). |
|---|
| 636 |
""" |
|---|
| 637 |
return in6_isincluded(str, 'ff0e::', 16) |
|---|
| 638 |
|
|---|
| 639 |
def in6_ismlladdr(str): |
|---|
| 640 |
""" |
|---|
| 641 |
Returns True if address balongs to link-local multicast address |
|---|
| 642 |
space (ff02::/16) |
|---|
| 643 |
""" |
|---|
| 644 |
return in6_isincluded(str, 'ff02::', 16) |
|---|
| 645 |
|
|---|
| 646 |
def in6_ismsladdr(str): |
|---|
| 647 |
""" |
|---|
| 648 |
Returns True if address belongs to site-local multicast address |
|---|
| 649 |
space (ff05::/16). Site local address space has been deprecated. |
|---|
| 650 |
Function remains for historic reasons. |
|---|
| 651 |
""" |
|---|
| 652 |
return in6_isincluded(str, 'ff05::', 16) |
|---|
| 653 |
|
|---|
| 654 |
def in6_isaddrllallnodes(str): |
|---|
| 655 |
""" |
|---|
| 656 |
Returns True if address is the link-local all-nodes multicast |
|---|
| 657 |
address (ff02::1). |
|---|
| 658 |
""" |
|---|
| 659 |
return (inet_pton(socket.AF_INET6, "ff02::1") == |
|---|
| 660 |
inet_pton(socket.AF_INET6, str)) |
|---|
| 661 |
|
|---|
| 662 |
def in6_isaddrllallservers(str): |
|---|
| 663 |
""" |
|---|
| 664 |
Returns True if address is the link-local all-servers multicast |
|---|
| 665 |
address (ff02::2). |
|---|
| 666 |
""" |
|---|
| 667 |
return (inet_pton(socket.AF_INET6, "ff02::2") == |
|---|
| 668 |
inet_pton(socket.AF_INET6, str)) |
|---|
| 669 |
|
|---|
| 670 |
|
|---|
| 671 |
def in6_getscope(addr): |
|---|
| 672 |
""" |
|---|
| 673 |
Returns the scope of the address. |
|---|
| 674 |
""" |
|---|
| 675 |
if in6_isgladdr(addr): |
|---|
| 676 |
scope = IPV6_ADDR_GLOBAL |
|---|
| 677 |
elif in6_islladdr(addr): |
|---|
| 678 |
scope = IPV6_ADDR_LINKLOCAL |
|---|
| 679 |
elif in6_issladdr(addr): |
|---|
| 680 |
scope = IPV6_ADDR_SITELOCAL |
|---|
| 681 |
elif in6_ismaddr(addr): |
|---|
| 682 |
scope = IPV6_ADDR_MULTICAST |
|---|
| 683 |
elif addr == '::1': |
|---|
| 684 |
scope = IPV6_ADDR_LOOPBACK |
|---|
| 685 |
else: |
|---|
| 686 |
scope = -1 |
|---|
| 687 |
return scope |
|---|