| 1 |
|
|---|
| 2 |
|
|---|
| 3 |
|
|---|
| 4 |
|
|---|
| 5 |
|
|---|
| 6 |
|
|---|
| 7 |
|
|---|
| 8 |
|
|---|
| 9 |
|
|---|
| 10 |
|
|---|
| 11 |
|
|---|
| 12 |
|
|---|
| 13 |
|
|---|
| 14 |
|
|---|
| 15 |
|
|---|
| 16 |
import socket |
|---|
| 17 |
from config import conf |
|---|
| 18 |
from utils6 import * |
|---|
| 19 |
from arch import * |
|---|
| 20 |
|
|---|
| 21 |
|
|---|
| 22 |
class Route6: |
|---|
| 23 |
|
|---|
| 24 |
def __init__(self): |
|---|
| 25 |
self.invalidate_cache() |
|---|
| 26 |
self.resync() |
|---|
| 27 |
|
|---|
| 28 |
def invalidate_cache(self): |
|---|
| 29 |
self.cache = {} |
|---|
| 30 |
|
|---|
| 31 |
def flush(self): |
|---|
| 32 |
self.invalidate_cache() |
|---|
| 33 |
self.routes = [] |
|---|
| 34 |
|
|---|
| 35 |
def resync(self): |
|---|
| 36 |
|
|---|
| 37 |
|
|---|
| 38 |
self.invalidate_cache() |
|---|
| 39 |
self.routes = read_routes6() |
|---|
| 40 |
if self.routes == []: |
|---|
| 41 |
log_loading.info("No IPv6 support in kernel") |
|---|
| 42 |
|
|---|
| 43 |
def __repr__(self): |
|---|
| 44 |
rtlst = [('Destination', 'Next Hop', "iface", "src candidates")] |
|---|
| 45 |
|
|---|
| 46 |
for net,msk,gw,iface,cset in self.routes: |
|---|
| 47 |
rtlst.append(('%s/%i'% (net,msk), gw, iface, ", ".join(cset))) |
|---|
| 48 |
|
|---|
| 49 |
colwidth = map(lambda x: max(map(lambda y: len(y), x)), apply(zip, rtlst)) |
|---|
| 50 |
fmt = " ".join(map(lambda x: "%%-%ds"%x, colwidth)) |
|---|
| 51 |
rt = "\n".join(map(lambda x: fmt % x, rtlst)) |
|---|
| 52 |
|
|---|
| 53 |
return rt |
|---|
| 54 |
|
|---|
| 55 |
|
|---|
| 56 |
|
|---|
| 57 |
|
|---|
| 58 |
|
|---|
| 59 |
|
|---|
| 60 |
def make_route(self, dst, gw=None, dev=None): |
|---|
| 61 |
"""Internal function : create a route for 'dst' via 'gw'. |
|---|
| 62 |
""" |
|---|
| 63 |
prefix, plen = (dst.split("/")+["128"])[:2] |
|---|
| 64 |
plen = int(plen) |
|---|
| 65 |
|
|---|
| 66 |
if gw is None: |
|---|
| 67 |
gw = "::" |
|---|
| 68 |
if dev is None: |
|---|
| 69 |
dev, ifaddr, x = self.route(gw) |
|---|
| 70 |
else: |
|---|
| 71 |
|
|---|
| 72 |
|
|---|
| 73 |
lifaddr = in6_getifaddr() |
|---|
| 74 |
devaddrs = filter(lambda x: x[2] == dev, lifaddr) |
|---|
| 75 |
ifaddr = construct_source_candidate_set(prefix, plen, devaddrs) |
|---|
| 76 |
|
|---|
| 77 |
return (prefix, plen, gw, dev, ifaddr) |
|---|
| 78 |
|
|---|
| 79 |
|
|---|
| 80 |
def add(self, *args, **kargs): |
|---|
| 81 |
"""Ex: |
|---|
| 82 |
add(dst="2001:db8:cafe:f000::/56") |
|---|
| 83 |
add(dst="2001:db8:cafe:f000::/56", gw="2001:db8:cafe::1") |
|---|
| 84 |
add(dst="2001:db8:cafe:f000::/64", gw="2001:db8:cafe::1", dev="eth0") |
|---|
| 85 |
""" |
|---|
| 86 |
self.invalidate_cache() |
|---|
| 87 |
self.routes.append(self.make_route(*args, **kargs)) |
|---|
| 88 |
|
|---|
| 89 |
|
|---|
| 90 |
def delt(self, dst, gw=None): |
|---|
| 91 |
""" Ex: |
|---|
| 92 |
delt(dst="::/0") |
|---|
| 93 |
delt(dst="2001:db8:cafe:f000::/56") |
|---|
| 94 |
delt(dst="2001:db8:cafe:f000::/56", gw="2001:db8:deca::1") |
|---|
| 95 |
""" |
|---|
| 96 |
tmp = dst+"/128" |
|---|
| 97 |
dst, plen = tmp.split('/')[:2] |
|---|
| 98 |
dst = in6_ptop(dst) |
|---|
| 99 |
plen = int(plen) |
|---|
| 100 |
l = filter(lambda x: in6_ptop(x[0]) == dst and x[1] == plen, self.routes) |
|---|
| 101 |
if gw: |
|---|
| 102 |
gw = in6_ptop(gw) |
|---|
| 103 |
l = filter(lambda x: in6_ptop(x[0]) == gw, self.routes) |
|---|
| 104 |
if len(l) == 0: |
|---|
| 105 |
warning("No matching route found") |
|---|
| 106 |
elif len(l) > 1: |
|---|
| 107 |
warning("Found more than one match. Aborting.") |
|---|
| 108 |
else: |
|---|
| 109 |
i=self.routes.index(l[0]) |
|---|
| 110 |
self.invalidate_cache() |
|---|
| 111 |
del(self.routes[i]) |
|---|
| 112 |
|
|---|
| 113 |
def ifchange(self, iff, addr): |
|---|
| 114 |
the_addr, the_plen = (addr.split("/")+["128"])[:2] |
|---|
| 115 |
the_plen = int(the_plen) |
|---|
| 116 |
|
|---|
| 117 |
naddr = inet_pton(socket.AF_INET6, the_addr) |
|---|
| 118 |
nmask = in6_cidr2mask(the_plen) |
|---|
| 119 |
the_net = inet_ntop(socket.AF_INET6, in6_and(nmask,naddr)) |
|---|
| 120 |
|
|---|
| 121 |
for i in range(len(self.routes)): |
|---|
| 122 |
net,plen,gw,iface,addr = self.routes[i] |
|---|
| 123 |
if iface != iff: |
|---|
| 124 |
continue |
|---|
| 125 |
if gw == '::': |
|---|
| 126 |
self.routes[i] = (the_net,the_plen,gw,iface,the_addr) |
|---|
| 127 |
else: |
|---|
| 128 |
self.routes[i] = (net,the_plen,gw,iface,the_addr) |
|---|
| 129 |
self.invalidate_cache() |
|---|
| 130 |
ip6_neigh_cache.flush() |
|---|
| 131 |
|
|---|
| 132 |
def ifdel(self, iff): |
|---|
| 133 |
""" removes all route entries that uses 'iff' interface. """ |
|---|
| 134 |
new_routes=[] |
|---|
| 135 |
for rt in self.routes: |
|---|
| 136 |
if rt[3] != iff: |
|---|
| 137 |
new_routes.append(rt) |
|---|
| 138 |
self.invalidate_cache() |
|---|
| 139 |
self.routes = new_routes |
|---|
| 140 |
|
|---|
| 141 |
|
|---|
| 142 |
def ifadd(self, iff, addr): |
|---|
| 143 |
""" |
|---|
| 144 |
Add an interface 'iff' with provided address into routing table. |
|---|
| 145 |
|
|---|
| 146 |
Ex: ifadd('eth0', '2001:bd8:cafe:1::1/64') will add following entry into |
|---|
| 147 |
Scapy6 internal routing table: |
|---|
| 148 |
|
|---|
| 149 |
Destination Next Hop iface Def src @ |
|---|
| 150 |
2001:bd8:cafe:1::/64 :: eth0 2001:bd8:cafe:1::1 |
|---|
| 151 |
|
|---|
| 152 |
prefix length value can be omitted. In that case, a value of 128 |
|---|
| 153 |
will be used. |
|---|
| 154 |
""" |
|---|
| 155 |
addr, plen = (addr.split("/")+["128"])[:2] |
|---|
| 156 |
addr = in6_ptop(addr) |
|---|
| 157 |
plen = int(plen) |
|---|
| 158 |
naddr = inet_pton(socket.AF_INET6, addr) |
|---|
| 159 |
nmask = in6_cidr2mask(plen) |
|---|
| 160 |
prefix = inet_ntop(socket.AF_INET6, in6_and(nmask,naddr)) |
|---|
| 161 |
self.invalidate_cache() |
|---|
| 162 |
self.routes.append((prefix,plen,'::',iff,[addr])) |
|---|
| 163 |
|
|---|
| 164 |
def route(self, dst, dev=None): |
|---|
| 165 |
""" |
|---|
| 166 |
Provide best route to IPv6 destination address, based on Scapy6 |
|---|
| 167 |
internal routing table content. |
|---|
| 168 |
|
|---|
| 169 |
When a set of address is passed (e.g. 2001:db8:cafe:*::1-5) an address |
|---|
| 170 |
of the set is used. Be aware of that behavior when using wildcards in |
|---|
| 171 |
upper parts of addresses ! |
|---|
| 172 |
|
|---|
| 173 |
If 'dst' parameter is a FQDN, name resolution is performed and result |
|---|
| 174 |
is used. |
|---|
| 175 |
|
|---|
| 176 |
if optional 'dev' parameter is provided a specific interface, filtering |
|---|
| 177 |
is performed to limit search to route associated to that interface. |
|---|
| 178 |
""" |
|---|
| 179 |
|
|---|
| 180 |
dst = dst.split("/")[0] |
|---|
| 181 |
savedst = dst |
|---|
| 182 |
dst = dst.replace("*","0") |
|---|
| 183 |
l = dst.find("-") |
|---|
| 184 |
while l >= 0: |
|---|
| 185 |
m = (dst[l:]+":").find(":") |
|---|
| 186 |
dst = dst[:l]+dst[l+m:] |
|---|
| 187 |
l = dst.find("-") |
|---|
| 188 |
|
|---|
| 189 |
try: |
|---|
| 190 |
inet_pton(socket.AF_INET6, dst) |
|---|
| 191 |
except socket.error: |
|---|
| 192 |
dst = socket.getaddrinfo(savedst, None, socket.AF_INET6)[0][-1][0] |
|---|
| 193 |
|
|---|
| 194 |
|
|---|
| 195 |
|
|---|
| 196 |
k = dst |
|---|
| 197 |
if dev is not None: |
|---|
| 198 |
k = dst + "%%" + dev |
|---|
| 199 |
if k in self.cache: |
|---|
| 200 |
return self.cache[k] |
|---|
| 201 |
|
|---|
| 202 |
pathes = [] |
|---|
| 203 |
|
|---|
| 204 |
|
|---|
| 205 |
|
|---|
| 206 |
|
|---|
| 207 |
|
|---|
| 208 |
for p, plen, gw, iface, cset in self.routes: |
|---|
| 209 |
if dev is not None and iface != dev: |
|---|
| 210 |
continue |
|---|
| 211 |
if in6_isincluded(dst, p, plen): |
|---|
| 212 |
pathes.append((plen, (iface, cset, gw))) |
|---|
| 213 |
elif (in6_ismlladdr(dst) and in6_islladdr(p) and in6_islladdr(cset[0])): |
|---|
| 214 |
pathes.append((plen, (iface, cset, gw))) |
|---|
| 215 |
|
|---|
| 216 |
if not pathes: |
|---|
| 217 |
warning("No route found for IPv6 destination %s (no default route?)" % dst) |
|---|
| 218 |
return (LOOPBACK_NAME, "::", "::") |
|---|
| 219 |
|
|---|
| 220 |
pathes.sort() |
|---|
| 221 |
pathes.reverse() |
|---|
| 222 |
|
|---|
| 223 |
best_plen = pathes[0][0] |
|---|
| 224 |
pathes = filter(lambda x: x[0] == best_plen, pathes) |
|---|
| 225 |
|
|---|
| 226 |
res = [] |
|---|
| 227 |
for p in pathes: |
|---|
| 228 |
tmp = p[1] |
|---|
| 229 |
srcaddr = get_source_addr_from_candidate_set(dst, p[1][1]) |
|---|
| 230 |
if srcaddr is not None: |
|---|
| 231 |
res.append((p[0], (tmp[0], srcaddr, tmp[2]))) |
|---|
| 232 |
|
|---|
| 233 |
|
|---|
| 234 |
|
|---|
| 235 |
|
|---|
| 236 |
|
|---|
| 237 |
|
|---|
| 238 |
|
|---|
| 239 |
|
|---|
| 240 |
|
|---|
| 241 |
|
|---|
| 242 |
|
|---|
| 243 |
if len(res) > 1: |
|---|
| 244 |
tmp = [] |
|---|
| 245 |
if in6_isgladdr(dst) and in6_isaddr6to4(dst): |
|---|
| 246 |
|
|---|
| 247 |
|
|---|
| 248 |
tmp = filter(lambda x: in6_isaddr6to4(x[1][1]), res) |
|---|
| 249 |
elif in6_ismaddr(dst) or in6_islladdr(dst): |
|---|
| 250 |
|
|---|
| 251 |
tmp = filter(lambda x: x[1][0] == conf.iface6, res) |
|---|
| 252 |
|
|---|
| 253 |
if tmp: |
|---|
| 254 |
res = tmp |
|---|
| 255 |
|
|---|
| 256 |
|
|---|
| 257 |
k = dst |
|---|
| 258 |
if dev is not None: |
|---|
| 259 |
k = dst + "%%" + dev |
|---|
| 260 |
self.cache[k] = res[0][1] |
|---|
| 261 |
|
|---|
| 262 |
return res[0][1] |
|---|
| 263 |
|
|---|
| 264 |
conf.route6 = Route6() |
|---|
| 265 |
|
|---|
| 266 |
_res = conf.route6.route("::/0") |
|---|
| 267 |
if _res: |
|---|
| 268 |
iff, gw, addr = _res |
|---|
| 269 |
conf.iface6 = iff |
|---|
| 270 |
del(_res) |
|---|
| 271 |
|
|---|