diff --git a/scapy/all.py b/scapy/all.py
index dd4f84b3ac1b3ce7adfe9ba9d07fcc23061fdf36..c76d08b583ccbfbd4147fdf4605a6a3dc0adb61d 100644
--- a/scapy/all.py
+++ b/scapy/all.py
@@ -20,6 +20,8 @@ from asn1packet import *
from utils import *
from route import *
+from utils6 import *
+from route6 import *
from sendrecv import *
from supersocket import *
from volatile import *
diff --git a/scapy/arch/linux.py b/scapy/arch/linux.py
index fed3efc52b6b9814627819bf0f925dcce242afee..5f99507a21d45910736d725cdf995f936d87bb23 100644
--- a/scapy/arch/linux.py
+++ b/scapy/arch/linux.py
@@ -250,7 +250,7 @@ def read_routes6():
cset = ['::1']
else:
devaddrs = filter(lambda x: x[2] == dev, lifaddr)
- cset = construct_source_candidate_set(d, dp, devaddrs)
+ cset = scapy.utils6.construct_source_candidate_set(d, dp, devaddrs)
if len(cset) != 0:
routes.append((d, dp, nh, dev, cset))
diff --git a/scapy/config.py b/scapy/config.py
index bb8d1d01e167767f507cd37e16931ae8be5c5e4d..a213cd2f187909d91fd972fa141efd4968c5d0b9 100644
--- a/scapy/config.py
+++ b/scapy/config.py
@@ -293,6 +293,7 @@ extensions_paths: path or list of paths where extensions are to be looked for
debug_match = 0
wepkey = ""
route = None # Filed by route.py
+ route6 = None # Filed by route6.py
auto_fragment = 1
debug_dissector = 0
color_theme = themes.DefaultTheme()
diff --git a/scapy/layers/inet6.py b/scapy/layers/inet6.py
index f23db6ee943034c49ff3bb144a9824d6a753887a..115f3278adbd27fc8183391c7abf80a4f83801d5 100644
--- a/scapy/layers/inet6.py
+++ b/scapy/layers/inet6.py
@@ -42,314 +42,6 @@ def get_cls(name, fallback_cls):
return globals().get(name, fallback_cls)
-
-#############################################################################
-#############################################################################
-### Routing/Interfaces stuff ###
-#############################################################################
-#############################################################################
-
-def construct_source_candidate_set(addr, plen, laddr):
- """
- Given all addresses assigned to a specific interface ('laddr' parameter),
- this function returns the "candidate set" associated with 'addr/plen'.
-
- Basically, the function filters all interface addresses to keep only those
- that have the same scope as provided prefix.
-
- This is on this list of addresses that the source selection mechanism
- will then be performed to select the best source address associated
- with some specific destination that uses this prefix.
- """
-
- cset = []
- if in6_isgladdr(addr):
- cset = filter(lambda x: x[1] == IPV6_ADDR_GLOBAL, laddr)
- elif in6_islladdr(addr):
- cset = filter(lambda x: x[1] == IPV6_ADDR_LINKLOCAL, laddr)
- elif in6_issladdr(addr):
- cset = filter(lambda x: x[1] == IPV6_ADDR_SITELOCAL, laddr)
- elif in6_ismaddr(addr):
- if in6_ismnladdr(addr):
- cset = [('::1', 16, LOOPBACK_NAME)]
- elif in6_ismgladdr(addr):
- cset = filter(lambda x: x[1] == IPV6_ADDR_GLOBAL, laddr)
- elif in6_ismlladdr(addr):
- cset = filter(lambda x: x[1] == IPV6_ADDR_LINKLOCAL, laddr)
- elif in6_ismsladdr(addr):
- cset = filter(lambda x: x[1] == IPV6_ADDR_SITELOCAL, laddr)
- elif addr == '::' and plen == 0:
- cset = filter(lambda x: x[1] == IPV6_ADDR_GLOBAL, laddr)
- cset = map(lambda x: x[0], cset)
- return cset
-
-def get_source_addr_from_candidate_set(dst, candidate_set):
- """
- This function implement a limited version of source address selection
- algorithm defined in section 5 of RFC 3484. The format is very different
- from that described in the document because it operates on a set
- of candidate source address for some specific route.
-
- Rationale behind the implementation is to be able to make the right
- choice for a 6to4 destination when both a 6to4 address and a IPv6 native
- address are available for that interface.
- """
-
- if len(candidate_set) == 0:
- # Should not happen
- return None
-
- if in6_isaddr6to4(dst):
- tmp = filter(lambda x: in6_isaddr6to4(x), candidate_set)
- if len(tmp) != 0:
- return tmp[0]
-
- return candidate_set[0]
-
-class Route6:
-
- def __init__(self):
- self.invalidate_cache()
- self.resync()
-
- def invalidate_cache(self):
- self.cache = {}
-
- def flush(self):
- self.invalidate_cache()
- self.routes = []
-
- def resync(self):
- # TODO : At the moment, resync will drop existing Teredo routes
- # if any. Change that ...
- self.invalidate_cache()
- self.routes = read_routes6()
- if self.routes == []:
- log_loading.info("No IPv6 support in kernel")
-
- def __repr__(self):
- rtlst = [('Destination', 'Next Hop', "iface", "src candidates")]
-
- for net,msk,gw,iface,cset in self.routes:
- rtlst.append(('%s/%i'% (net,msk), gw, iface, ", ".join(cset)))
-
- colwidth = map(lambda x: max(map(lambda y: len(y), x)), apply(zip, rtlst))
- fmt = " ".join(map(lambda x: "%%-%ds"%x, colwidth))
- rt = "\n".join(map(lambda x: fmt % x, rtlst))
-
- return rt
-
-
- # Unlike Scapy's Route.make_route() function, we do not have 'host' and 'net'
- # parameters. We only have a 'dst' parameter that accepts 'prefix' and
- # 'prefix/prefixlen' values.
- # WARNING: Providing a specific device will at the moment not work correctly.
- def make_route(self, dst, gw=None, dev=None):
- """Internal function : create a route for 'dst' via 'gw'.
- """
- prefix, plen = (dst.split("/")+["128"])[:2]
- plen = int(plen)
-
- if gw is None:
- gw = "::"
- if dev is None:
- dev, ifaddr, x = self.route(gw)
- else:
- # TODO: do better than that
- # replace that unique address by the list of all addresses
- lifaddr = in6_getifaddr()
- devaddrs = filter(lambda x: x[2] == dev, lifaddr)
- ifaddr = construct_source_candidate_set(prefix, plen, devaddrs)
-
- return (prefix, plen, gw, dev, ifaddr)
-
-
- def add(self, *args, **kargs):
- """Ex:
- add(dst="2001:db8:cafe:f000::/56")
- add(dst="2001:db8:cafe:f000::/56", gw="2001:db8:cafe::1")
- add(dst="2001:db8:cafe:f000::/64", gw="2001:db8:cafe::1", dev="eth0")
- """
- self.invalidate_cache()
- self.routes.append(self.make_route(*args, **kargs))
-
-
- def delt(self, dst, gw=None):
- """ Ex:
- delt(dst="::/0")
- delt(dst="2001:db8:cafe:f000::/56")
- delt(dst="2001:db8:cafe:f000::/56", gw="2001:db8:deca::1")
- """
- tmp = dst+"/128"
- dst, plen = tmp.split('/')[:2]
- dst = in6_ptop(dst)
- plen = int(plen)
- l = filter(lambda x: in6_ptop(x[0]) == dst and x[1] == plen, self.routes)
- if gw:
- gw = in6_ptop(gw)
- l = filter(lambda x: in6_ptop(x[0]) == gw, self.routes)
- if len(l) == 0:
- warning("No matching route found")
- elif len(l) > 1:
- warning("Found more than one match. Aborting.")
- else:
- i=self.routes.index(l[0])
- self.invalidate_cache()
- del(self.routes[i])
-
- def ifchange(self, iff, addr):
- the_addr, the_plen = (addr.split("/")+["128"])[:2]
- the_plen = int(the_plen)
-
- naddr = inet_pton(socket.AF_INET6, the_addr)
- nmask = in6_cidr2mask(the_plen)
- the_net = inet_ntop(socket.AF_INET6, in6_and(nmask,naddr))
-
- for i in range(len(self.routes)):
- net,plen,gw,iface,addr = self.routes[i]
- if iface != iff:
- continue
- if gw == '::':
- self.routes[i] = (the_net,the_plen,gw,iface,the_addr)
- else:
- self.routes[i] = (net,the_plen,gw,iface,the_addr)
- self.invalidate_cache()
- ip6_neigh_cache.flush()
-
- def ifdel(self, iff):
- """ removes all route entries that uses 'iff' interface. """
- new_routes=[]
- for rt in self.routes:
- if rt[3] != iff:
- new_routes.append(rt)
- self.invalidate_cache()
- self.routes = new_routes
-
-
- def ifadd(self, iff, addr):
- """
- Add an interface 'iff' with provided address into routing table.
-
- Ex: ifadd('eth0', '2001:bd8:cafe:1::1/64') will add following entry into
- Scapy6 internal routing table:
-
- Destination Next Hop iface Def src @
- 2001:bd8:cafe:1::/64 :: eth0 2001:bd8:cafe:1::1
-
- prefix length value can be omitted. In that case, a value of 128
- will be used.
- """
- addr, plen = (addr.split("/")+["128"])[:2]
- addr = in6_ptop(addr)
- plen = int(plen)
- naddr = inet_pton(socket.AF_INET6, addr)
- nmask = in6_cidr2mask(plen)
- prefix = inet_ntop(socket.AF_INET6, in6_and(nmask,naddr))
- self.invalidate_cache()
- self.routes.append((prefix,plen,'::',iff,[addr]))
-
- def route(self, dst, dev=None):
- """
- Provide best route to IPv6 destination address, based on Scapy6
- internal routing table content.
-
- When a set of address is passed (e.g. 2001:db8:cafe:*::1-5) an address
- of the set is used. Be aware of that behavior when using wildcards in
- upper parts of addresses !
-
- If 'dst' parameter is a FQDN, name resolution is performed and result
- is used.
-
- if optional 'dev' parameter is provided a specific interface, filtering
- is performed to limit search to route associated to that interface.
- """
- # Transform "2001:db8:cafe:*::1-5:0/120" to one IPv6 address of the set
- dst = dst.split("/")[0]
- savedst = dst # In case following inet_pton() fails
- dst = dst.replace("*","0")
- l = dst.find("-")
- while l >= 0:
- m = (dst[l:]+":").find(":")
- dst = dst[:l]+dst[l+m:]
- l = dst.find("-")
-
- try:
- inet_pton(socket.AF_INET6, dst)
- except socket.error:
- dst = socket.getaddrinfo(savedst, None, socket.AF_INET6)[0][-1][0]
- # TODO : Check if name resolution went well
-
- # Deal with dev-specific request for cache search
- k = dst
- if dev is not None:
- k = dst + "%%" + dev
- if k in self.cache:
- return self.cache[k]
-
- pathes = []
-
- # TODO : review all kinds of addresses (scope and *cast) to see
- # if we are able to cope with everything possible. I'm convinced
- # it's not the case.
- # -- arnaud
- for p, plen, gw, iface, cset in self.routes:
- if dev is not None and iface != dev:
- continue
- if in6_isincluded(dst, p, plen):
- pathes.append((plen, (iface, cset, gw)))
- elif (in6_ismlladdr(dst) and in6_islladdr(p) and in6_islladdr(cset[0])):
- pathes.append((plen, (iface, cset, gw)))
-
- if not pathes:
- warning("No route found for IPv6 destination %s (no default route?)" % dst)
- return (LOOPBACK_NAME, "::", "::") # XXX Linux specific
-
- pathes.sort()
- pathes.reverse()
-
- best_plen = pathes[0][0]
- pathes = filter(lambda x: x[0] == best_plen, pathes)
-
- res = []
- for p in pathes: # Here we select best source address for every route
- tmp = p[1]
- srcaddr = get_source_addr_from_candidate_set(dst, p[1][1])
- if srcaddr is not None:
- res.append((p[0], (tmp[0], srcaddr, tmp[2])))
-
- # Symptom : 2 routes with same weight (our weight is plen)
- # Solution :
- # - dst is unicast global. Check if it is 6to4 and we have a source
- # 6to4 address in those available
- # - dst is link local (unicast or multicast) and multiple output
- # interfaces are available. Take main one (conf.iface6)
- # - if none of the previous or ambiguity persists, be lazy and keep
- # first one
- # XXX TODO : in a _near_ future, include metric in the game
-
- if len(res) > 1:
- tmp = []
- if in6_isgladdr(dst) and in6_isaddr6to4(dst):
- # TODO : see if taking the longest match between dst and
- # every source addresses would provide better results
- tmp = filter(lambda x: in6_isaddr6to4(x[1][1]), res)
- elif in6_ismaddr(dst) or in6_islladdr(dst):
- # TODO : I'm sure we are not covering all addresses. Check that
- tmp = filter(lambda x: x[1][0] == conf.iface6, res)
-
- if tmp:
- res = tmp
-
- # Fill the cache (including dev-specific request)
- k = dst
- if dev is not None:
- k = dst + "%%" + dev
- self.cache[k] = res[0][1]
-
- return res[0][1]
-
-
-
##########################
## Neighbor cache stuff ##
##########################
@@ -5123,26 +4815,3 @@ bind_layers(IPv6, UDP, nh = socket.IPPROTO_UDP )
bind_layers(IP, IPv6, proto = socket.IPPROTO_IPV6 )
bind_layers(IPv6, IPv6, nh = socket.IPPROTO_IPV6 )
-#############################################################################
-### Conf overloading ###
-#############################################################################
-
-def get_working_if6():
- """
- try to guess the best interface for conf.iface6 by looking for the
- one used by default route if any.
- """
- res = conf.route6.route("::/0")
- if res:
- iff, gw, addr = res
- return iff
- return get_working_if()
-
-conf.route6 = Route6()
-conf.iface6 = get_working_if6()
-
-if __name__ == '__main__':
- interact(mydict=globals(), mybanner="IPv6 enabled")
-else:
- import __builtin__
- __builtin__.__dict__.update(globals())
diff --git a/scapy/route6.py b/scapy/route6.py
new file mode 100644
index 0000000000000000000000000000000000000000..3cdf9ab2ffde271bdd021bca249581c3a746fb45
--- /dev/null
+++ b/scapy/route6.py
@@ -0,0 +1,271 @@
+## This file is part of Scapy
+## See http://www.secdev.org/projects/scapy for more informations
+## Copyright (C) Philippe Biondi <phil@secdev.org>
+## This program is published under a GPLv2 license
+
+## Copyright (C) 2005 Guillaume Valadon <guedou@hongo.wide.ad.jp>
+## Arnaud Ebalard <arnaud.ebalard@eads.net>
+
+
+#############################################################################
+#############################################################################
+### Routing/Interfaces stuff ###
+#############################################################################
+#############################################################################
+
+import socket
+from config import conf
+from utils6 import *
+from arch import *
+
+
+class Route6:
+
+ def __init__(self):
+ self.invalidate_cache()
+ self.resync()
+
+ def invalidate_cache(self):
+ self.cache = {}
+
+ def flush(self):
+ self.invalidate_cache()
+ self.routes = []
+
+ def resync(self):
+ # TODO : At the moment, resync will drop existing Teredo routes
+ # if any. Change that ...
+ self.invalidate_cache()
+ self.routes = read_routes6()
+ if self.routes == []:
+ log_loading.info("No IPv6 support in kernel")
+
+ def __repr__(self):
+ rtlst = [('Destination', 'Next Hop', "iface", "src candidates")]
+
+ for net,msk,gw,iface,cset in self.routes:
+ rtlst.append(('%s/%i'% (net,msk), gw, iface, ", ".join(cset)))
+
+ colwidth = map(lambda x: max(map(lambda y: len(y), x)), apply(zip, rtlst))
+ fmt = " ".join(map(lambda x: "%%-%ds"%x, colwidth))
+ rt = "\n".join(map(lambda x: fmt % x, rtlst))
+
+ return rt
+
+
+ # Unlike Scapy's Route.make_route() function, we do not have 'host' and 'net'
+ # parameters. We only have a 'dst' parameter that accepts 'prefix' and
+ # 'prefix/prefixlen' values.
+ # WARNING: Providing a specific device will at the moment not work correctly.
+ def make_route(self, dst, gw=None, dev=None):
+ """Internal function : create a route for 'dst' via 'gw'.
+ """
+ prefix, plen = (dst.split("/")+["128"])[:2]
+ plen = int(plen)
+
+ if gw is None:
+ gw = "::"
+ if dev is None:
+ dev, ifaddr, x = self.route(gw)
+ else:
+ # TODO: do better than that
+ # replace that unique address by the list of all addresses
+ lifaddr = in6_getifaddr()
+ devaddrs = filter(lambda x: x[2] == dev, lifaddr)
+ ifaddr = construct_source_candidate_set(prefix, plen, devaddrs)
+
+ return (prefix, plen, gw, dev, ifaddr)
+
+
+ def add(self, *args, **kargs):
+ """Ex:
+ add(dst="2001:db8:cafe:f000::/56")
+ add(dst="2001:db8:cafe:f000::/56", gw="2001:db8:cafe::1")
+ add(dst="2001:db8:cafe:f000::/64", gw="2001:db8:cafe::1", dev="eth0")
+ """
+ self.invalidate_cache()
+ self.routes.append(self.make_route(*args, **kargs))
+
+
+ def delt(self, dst, gw=None):
+ """ Ex:
+ delt(dst="::/0")
+ delt(dst="2001:db8:cafe:f000::/56")
+ delt(dst="2001:db8:cafe:f000::/56", gw="2001:db8:deca::1")
+ """
+ tmp = dst+"/128"
+ dst, plen = tmp.split('/')[:2]
+ dst = in6_ptop(dst)
+ plen = int(plen)
+ l = filter(lambda x: in6_ptop(x[0]) == dst and x[1] == plen, self.routes)
+ if gw:
+ gw = in6_ptop(gw)
+ l = filter(lambda x: in6_ptop(x[0]) == gw, self.routes)
+ if len(l) == 0:
+ warning("No matching route found")
+ elif len(l) > 1:
+ warning("Found more than one match. Aborting.")
+ else:
+ i=self.routes.index(l[0])
+ self.invalidate_cache()
+ del(self.routes[i])
+
+ def ifchange(self, iff, addr):
+ the_addr, the_plen = (addr.split("/")+["128"])[:2]
+ the_plen = int(the_plen)
+
+ naddr = inet_pton(socket.AF_INET6, the_addr)
+ nmask = in6_cidr2mask(the_plen)
+ the_net = inet_ntop(socket.AF_INET6, in6_and(nmask,naddr))
+
+ for i in range(len(self.routes)):
+ net,plen,gw,iface,addr = self.routes[i]
+ if iface != iff:
+ continue
+ if gw == '::':
+ self.routes[i] = (the_net,the_plen,gw,iface,the_addr)
+ else:
+ self.routes[i] = (net,the_plen,gw,iface,the_addr)
+ self.invalidate_cache()
+ ip6_neigh_cache.flush()
+
+ def ifdel(self, iff):
+ """ removes all route entries that uses 'iff' interface. """
+ new_routes=[]
+ for rt in self.routes:
+ if rt[3] != iff:
+ new_routes.append(rt)
+ self.invalidate_cache()
+ self.routes = new_routes
+
+
+ def ifadd(self, iff, addr):
+ """
+ Add an interface 'iff' with provided address into routing table.
+
+ Ex: ifadd('eth0', '2001:bd8:cafe:1::1/64') will add following entry into
+ Scapy6 internal routing table:
+
+ Destination Next Hop iface Def src @
+ 2001:bd8:cafe:1::/64 :: eth0 2001:bd8:cafe:1::1
+
+ prefix length value can be omitted. In that case, a value of 128
+ will be used.
+ """
+ addr, plen = (addr.split("/")+["128"])[:2]
+ addr = in6_ptop(addr)
+ plen = int(plen)
+ naddr = inet_pton(socket.AF_INET6, addr)
+ nmask = in6_cidr2mask(plen)
+ prefix = inet_ntop(socket.AF_INET6, in6_and(nmask,naddr))
+ self.invalidate_cache()
+ self.routes.append((prefix,plen,'::',iff,[addr]))
+
+ def route(self, dst, dev=None):
+ """
+ Provide best route to IPv6 destination address, based on Scapy6
+ internal routing table content.
+
+ When a set of address is passed (e.g. 2001:db8:cafe:*::1-5) an address
+ of the set is used. Be aware of that behavior when using wildcards in
+ upper parts of addresses !
+
+ If 'dst' parameter is a FQDN, name resolution is performed and result
+ is used.
+
+ if optional 'dev' parameter is provided a specific interface, filtering
+ is performed to limit search to route associated to that interface.
+ """
+ # Transform "2001:db8:cafe:*::1-5:0/120" to one IPv6 address of the set
+ dst = dst.split("/")[0]
+ savedst = dst # In case following inet_pton() fails
+ dst = dst.replace("*","0")
+ l = dst.find("-")
+ while l >= 0:
+ m = (dst[l:]+":").find(":")
+ dst = dst[:l]+dst[l+m:]
+ l = dst.find("-")
+
+ try:
+ inet_pton(socket.AF_INET6, dst)
+ except socket.error:
+ dst = socket.getaddrinfo(savedst, None, socket.AF_INET6)[0][-1][0]
+ # TODO : Check if name resolution went well
+
+ # Deal with dev-specific request for cache search
+ k = dst
+ if dev is not None:
+ k = dst + "%%" + dev
+ if k in self.cache:
+ return self.cache[k]
+
+ pathes = []
+
+ # TODO : review all kinds of addresses (scope and *cast) to see
+ # if we are able to cope with everything possible. I'm convinced
+ # it's not the case.
+ # -- arnaud
+ for p, plen, gw, iface, cset in self.routes:
+ if dev is not None and iface != dev:
+ continue
+ if in6_isincluded(dst, p, plen):
+ pathes.append((plen, (iface, cset, gw)))
+ elif (in6_ismlladdr(dst) and in6_islladdr(p) and in6_islladdr(cset[0])):
+ pathes.append((plen, (iface, cset, gw)))
+
+ if not pathes:
+ warning("No route found for IPv6 destination %s (no default route?)" % dst)
+ return (LOOPBACK_NAME, "::", "::") # XXX Linux specific
+
+ pathes.sort()
+ pathes.reverse()
+
+ best_plen = pathes[0][0]
+ pathes = filter(lambda x: x[0] == best_plen, pathes)
+
+ res = []
+ for p in pathes: # Here we select best source address for every route
+ tmp = p[1]
+ srcaddr = get_source_addr_from_candidate_set(dst, p[1][1])
+ if srcaddr is not None:
+ res.append((p[0], (tmp[0], srcaddr, tmp[2])))
+
+ # Symptom : 2 routes with same weight (our weight is plen)
+ # Solution :
+ # - dst is unicast global. Check if it is 6to4 and we have a source
+ # 6to4 address in those available
+ # - dst is link local (unicast or multicast) and multiple output
+ # interfaces are available. Take main one (conf.iface6)
+ # - if none of the previous or ambiguity persists, be lazy and keep
+ # first one
+ # XXX TODO : in a _near_ future, include metric in the game
+
+ if len(res) > 1:
+ tmp = []
+ if in6_isgladdr(dst) and in6_isaddr6to4(dst):
+ # TODO : see if taking the longest match between dst and
+ # every source addresses would provide better results
+ tmp = filter(lambda x: in6_isaddr6to4(x[1][1]), res)
+ elif in6_ismaddr(dst) or in6_islladdr(dst):
+ # TODO : I'm sure we are not covering all addresses. Check that
+ tmp = filter(lambda x: x[1][0] == conf.iface6, res)
+
+ if tmp:
+ res = tmp
+
+ # Fill the cache (including dev-specific request)
+ k = dst
+ if dev is not None:
+ k = dst + "%%" + dev
+ self.cache[k] = res[0][1]
+
+ return res[0][1]
+
+conf.route6 = Route6()
+
+_res = conf.route6.route("::/0")
+if _res:
+ iff, gw, addr = _res
+ conf.iface6 = iff
+del(_res)
+
diff --git a/scapy/utils6.py b/scapy/utils6.py
index c3d67e38dd64e51ccb2ce66e2a3b76a66a979ada..51965428e55ac37c63a444482b4490848be017c1 100644
--- a/scapy/utils6.py
+++ b/scapy/utils6.py
@@ -12,6 +12,63 @@ from data import *
from utils import *
+def construct_source_candidate_set(addr, plen, laddr):
+ """
+ Given all addresses assigned to a specific interface ('laddr' parameter),
+ this function returns the "candidate set" associated with 'addr/plen'.
+
+ Basically, the function filters all interface addresses to keep only those
+ that have the same scope as provided prefix.
+
+ This is on this list of addresses that the source selection mechanism
+ will then be performed to select the best source address associated
+ with some specific destination that uses this prefix.
+ """
+
+ cset = []
+ if in6_isgladdr(addr):
+ cset = filter(lambda x: x[1] == IPV6_ADDR_GLOBAL, laddr)
+ elif in6_islladdr(addr):
+ cset = filter(lambda x: x[1] == IPV6_ADDR_LINKLOCAL, laddr)
+ elif in6_issladdr(addr):
+ cset = filter(lambda x: x[1] == IPV6_ADDR_SITELOCAL, laddr)
+ elif in6_ismaddr(addr):
+ if in6_ismnladdr(addr):
+ cset = [('::1', 16, LOOPBACK_NAME)]
+ elif in6_ismgladdr(addr):
+ cset = filter(lambda x: x[1] == IPV6_ADDR_GLOBAL, laddr)
+ elif in6_ismlladdr(addr):
+ cset = filter(lambda x: x[1] == IPV6_ADDR_LINKLOCAL, laddr)
+ elif in6_ismsladdr(addr):
+ cset = filter(lambda x: x[1] == IPV6_ADDR_SITELOCAL, laddr)
+ elif addr == '::' and plen == 0:
+ cset = filter(lambda x: x[1] == IPV6_ADDR_GLOBAL, laddr)
+ cset = map(lambda x: x[0], cset)
+ return cset
+
+def get_source_addr_from_candidate_set(dst, candidate_set):
+ """
+ This function implement a limited version of source address selection
+ algorithm defined in section 5 of RFC 3484. The format is very different
+ from that described in the document because it operates on a set
+ of candidate source address for some specific route.
+
+ Rationale behind the implementation is to be able to make the right
+ choice for a 6to4 destination when both a 6to4 address and a IPv6 native
+ address are available for that interface.
+ """
+
+ if len(candidate_set) == 0:
+ # Should not happen
+ return None
+
+ if in6_isaddr6to4(dst):
+ tmp = filter(lambda x: in6_isaddr6to4(x), candidate_set)
+ if len(tmp) != 0:
+ return tmp[0]
+
+ return candidate_set[0]
+
def find_ifaddr2(addr, plen, laddr):
dstAddrType = in6_getAddrType(addr)