11ad4bbf39716a7c25161cb118f02344291e0c102Phil## This file is part of Scapy
21ad4bbf39716a7c25161cb118f02344291e0c102Phil## See http://www.secdev.org/projects/scapy for more informations
31ad4bbf39716a7c25161cb118f02344291e0c102Phil## Copyright (C) Philippe Biondi <phil@secdev.org>
41ad4bbf39716a7c25161cb118f02344291e0c102Phil## This program is published under a GPLv2 license
51ad4bbf39716a7c25161cb118f02344291e0c102Phil
60ce149b41a10223c75f33a135d0a7ddc6bd2e022Dirk Loss"""
70ce149b41a10223c75f33a135d0a7ddc6bd2e022Dirk LossCommon customizations for all Unix-like operating systems other than Linux
80ce149b41a10223c75f33a135d0a7ddc6bd2e022Dirk Loss"""
91ad4bbf39716a7c25161cb118f02344291e0c102Phil
101ad4bbf39716a7c25161cb118f02344291e0c102Philimport sys,os,struct,socket,time
111ad4bbf39716a7c25161cb118f02344291e0c102Philfrom fcntl import ioctl
126057906368d55634d11e1d19a5cca1f127595b11Robin Jarryimport socket
136057906368d55634d11e1d19a5cca1f127595b11Robin Jarry
146057906368d55634d11e1d19a5cca1f127595b11Robin Jarryfrom scapy.error import warning, log_interactive
1510ee9eb026c24397a8f2fc2127bb21bf94da71d7Philimport scapy.config
161ad4bbf39716a7c25161cb118f02344291e0c102Philimport scapy.utils
172066af02d4144547e3531c16a5ede1340b97938bGuillaume Valadonfrom scapy.utils6 import in6_getscope, construct_source_candidate_set
182066af02d4144547e3531c16a5ede1340b97938bGuillaume Valadonfrom scapy.utils6 import in6_isvalid, in6_ismlladdr, in6_ismnladdr
19431b04a95da67282a59bdf0a1898e106b18814d0gpotterfrom scapy.consts import FREEBSD, NETBSD, OPENBSD, SOLARIS, LOOPBACK_NAME
2035f97efc53839f6366918c251dba5df2ac553c8dGuillaume Valadonfrom scapy.arch import get_if_addr
216d6dba222509bec6a01b5f7414685b7d59bb7fc9Guillaume Valadonfrom scapy.config import conf
221ad4bbf39716a7c25161cb118f02344291e0c102Phil
231ad4bbf39716a7c25161cb118f02344291e0c102Phil
241ad4bbf39716a7c25161cb118f02344291e0c102Phil##################
251ad4bbf39716a7c25161cb118f02344291e0c102Phil## Routes stuff ##
261ad4bbf39716a7c25161cb118f02344291e0c102Phil##################
271ad4bbf39716a7c25161cb118f02344291e0c102Phil
2802ae08b4ad973184a167ffcfb10043dfeda1244dGuillaume Valadondef _guess_iface_name(netif):
2902ae08b4ad973184a167ffcfb10043dfeda1244dGuillaume Valadon    """
3002ae08b4ad973184a167ffcfb10043dfeda1244dGuillaume Valadon    We attempt to guess the name of interfaces that are truncated from the
3102ae08b4ad973184a167ffcfb10043dfeda1244dGuillaume Valadon    output of ifconfig -l.
3202ae08b4ad973184a167ffcfb10043dfeda1244dGuillaume Valadon    If there is only one possible candidate matching the interface name then we
3302ae08b4ad973184a167ffcfb10043dfeda1244dGuillaume Valadon    return it.
3402ae08b4ad973184a167ffcfb10043dfeda1244dGuillaume Valadon    If there are none or more, then we return None.
3502ae08b4ad973184a167ffcfb10043dfeda1244dGuillaume Valadon    """
3602ae08b4ad973184a167ffcfb10043dfeda1244dGuillaume Valadon    with os.popen('%s -l' % conf.prog.ifconfig) as fdesc:
3702ae08b4ad973184a167ffcfb10043dfeda1244dGuillaume Valadon        ifaces = fdesc.readline().strip().split(' ')
3802ae08b4ad973184a167ffcfb10043dfeda1244dGuillaume Valadon    matches = [iface for iface in ifaces if iface.startswith(netif)]
3902ae08b4ad973184a167ffcfb10043dfeda1244dGuillaume Valadon    if len(matches) == 1:
4002ae08b4ad973184a167ffcfb10043dfeda1244dGuillaume Valadon        return matches[0]
4102ae08b4ad973184a167ffcfb10043dfeda1244dGuillaume Valadon    return None
4202ae08b4ad973184a167ffcfb10043dfeda1244dGuillaume Valadon
431ad4bbf39716a7c25161cb118f02344291e0c102Phil
441ad4bbf39716a7c25161cb118f02344291e0c102Phildef read_routes():
4535f97efc53839f6366918c251dba5df2ac553c8dGuillaume Valadon    if SOLARIS:
461ad4bbf39716a7c25161cb118f02344291e0c102Phil        f=os.popen("netstat -rvn") # -f inet
4735f97efc53839f6366918c251dba5df2ac553c8dGuillaume Valadon    elif FREEBSD:
481ad4bbf39716a7c25161cb118f02344291e0c102Phil        f=os.popen("netstat -rnW") # -W to handle long interface names
491ad4bbf39716a7c25161cb118f02344291e0c102Phil    else:
501ad4bbf39716a7c25161cb118f02344291e0c102Phil        f=os.popen("netstat -rn") # -f inet
511ad4bbf39716a7c25161cb118f02344291e0c102Phil    ok = 0
521ad4bbf39716a7c25161cb118f02344291e0c102Phil    mtu_present = False
53963c58e7bf45216aa225f56b721aa9261fd9fe5aPhil    prio_present = False
541ad4bbf39716a7c25161cb118f02344291e0c102Phil    routes = []
5535125c23da6e8137a570583e754da9b918c8a63bPhil    pending_if = []
561ad4bbf39716a7c25161cb118f02344291e0c102Phil    for l in f.readlines():
571ad4bbf39716a7c25161cb118f02344291e0c102Phil        if not l:
581ad4bbf39716a7c25161cb118f02344291e0c102Phil            break
591ad4bbf39716a7c25161cb118f02344291e0c102Phil        l = l.strip()
601ad4bbf39716a7c25161cb118f02344291e0c102Phil        if l.find("----") >= 0: # a separation line
611ad4bbf39716a7c25161cb118f02344291e0c102Phil            continue
62963c58e7bf45216aa225f56b721aa9261fd9fe5aPhil        if not ok:
63963c58e7bf45216aa225f56b721aa9261fd9fe5aPhil            if l.find("Destination") >= 0:
64963c58e7bf45216aa225f56b721aa9261fd9fe5aPhil                ok = 1
65e787b56616650d08fec1a9fa702d7a09943c91fePierre LALET                mtu_present = "Mtu" in l
66e787b56616650d08fec1a9fa702d7a09943c91fePierre LALET                prio_present = "Prio" in l
67e787b56616650d08fec1a9fa702d7a09943c91fePierre LALET                refs_present = "Refs" in l
681ad4bbf39716a7c25161cb118f02344291e0c102Phil            continue
691ad4bbf39716a7c25161cb118f02344291e0c102Phil        if not l:
701ad4bbf39716a7c25161cb118f02344291e0c102Phil            break
7135f97efc53839f6366918c251dba5df2ac553c8dGuillaume Valadon        if SOLARIS:
7235125c23da6e8137a570583e754da9b918c8a63bPhil            lspl = l.split()
7335125c23da6e8137a570583e754da9b918c8a63bPhil            if len(lspl) == 10:
7435125c23da6e8137a570583e754da9b918c8a63bPhil                dest,mask,gw,netif,mxfrg,rtt,ref,flg = lspl[:8]
7535125c23da6e8137a570583e754da9b918c8a63bPhil            else: # missing interface
7635125c23da6e8137a570583e754da9b918c8a63bPhil                dest,mask,gw,mxfrg,rtt,ref,flg = lspl[:7]
7735125c23da6e8137a570583e754da9b918c8a63bPhil                netif=None
781ad4bbf39716a7c25161cb118f02344291e0c102Phil        else:
79963c58e7bf45216aa225f56b721aa9261fd9fe5aPhil            rt = l.split()
80963c58e7bf45216aa225f56b721aa9261fd9fe5aPhil            dest,gw,flg = rt[:3]
81e787b56616650d08fec1a9fa702d7a09943c91fePierre LALET            netif = rt[4 + mtu_present + prio_present + refs_present]
821ad4bbf39716a7c25161cb118f02344291e0c102Phil        if flg.find("Lc") >= 0:
830d92fc9fbb58df9cc4bbeb007bf65020fe1aa092Victor Pfautz            continue
841ad4bbf39716a7c25161cb118f02344291e0c102Phil        if dest == "default":
85291400c1b6f65363e33cc982aaf0d43d31cc424egpotter            dest = 0
86291400c1b6f65363e33cc982aaf0d43d31cc424egpotter            netmask = 0
871ad4bbf39716a7c25161cb118f02344291e0c102Phil        else:
8835f97efc53839f6366918c251dba5df2ac553c8dGuillaume Valadon            if SOLARIS:
891ad4bbf39716a7c25161cb118f02344291e0c102Phil                netmask = scapy.utils.atol(mask)
901ad4bbf39716a7c25161cb118f02344291e0c102Phil            elif "/" in dest:
911ad4bbf39716a7c25161cb118f02344291e0c102Phil                dest,netmask = dest.split("/")
921ad4bbf39716a7c25161cb118f02344291e0c102Phil                netmask = scapy.utils.itom(int(netmask))
931ad4bbf39716a7c25161cb118f02344291e0c102Phil            else:
941ad4bbf39716a7c25161cb118f02344291e0c102Phil                netmask = scapy.utils.itom((dest.count(".") + 1) * 8)
951ad4bbf39716a7c25161cb118f02344291e0c102Phil            dest += ".0"*(3-dest.count("."))
961ad4bbf39716a7c25161cb118f02344291e0c102Phil            dest = scapy.utils.atol(dest)
973fc82d65918606609ab12a449982e37891e1ade3gpotter        # XXX: TODO: add metrics for unix.py (use -e option on netstat)
983fc82d65918606609ab12a449982e37891e1ade3gpotter        metric = 1
991ad4bbf39716a7c25161cb118f02344291e0c102Phil        if not "G" in flg:
1001ad4bbf39716a7c25161cb118f02344291e0c102Phil            gw = '0.0.0.0'
10135125c23da6e8137a570583e754da9b918c8a63bPhil        if netif is not None:
10202ae08b4ad973184a167ffcfb10043dfeda1244dGuillaume Valadon            try:
10335f97efc53839f6366918c251dba5df2ac553c8dGuillaume Valadon                ifaddr = get_if_addr(netif)
1043fc82d65918606609ab12a449982e37891e1ade3gpotter                routes.append((dest,netmask, gw, netif, ifaddr, metric))
10502ae08b4ad973184a167ffcfb10043dfeda1244dGuillaume Valadon            except OSError as exc:
10602ae08b4ad973184a167ffcfb10043dfeda1244dGuillaume Valadon                if exc.message == 'Device not configured':
10702ae08b4ad973184a167ffcfb10043dfeda1244dGuillaume Valadon                    # This means the interface name is probably truncated by
10802ae08b4ad973184a167ffcfb10043dfeda1244dGuillaume Valadon                    # netstat -nr. We attempt to guess it's name and if not we
10902ae08b4ad973184a167ffcfb10043dfeda1244dGuillaume Valadon                    # ignore it.
11002ae08b4ad973184a167ffcfb10043dfeda1244dGuillaume Valadon                    guessed_netif = _guess_iface_name(netif)
11102ae08b4ad973184a167ffcfb10043dfeda1244dGuillaume Valadon                    if guessed_netif is not None:
11235f97efc53839f6366918c251dba5df2ac553c8dGuillaume Valadon                        ifaddr = get_if_addr(guessed_netif)
1133fc82d65918606609ab12a449982e37891e1ade3gpotter                        routes.append((dest, netmask, gw, guessed_netif, ifaddr, metric))
11402ae08b4ad973184a167ffcfb10043dfeda1244dGuillaume Valadon                    else:
115edd4e843008c637bf3d1a2c8274a8429ac2a4185gpotter                        warning("Could not guess partial interface name: %s", netif)
11602ae08b4ad973184a167ffcfb10043dfeda1244dGuillaume Valadon                else:
11702ae08b4ad973184a167ffcfb10043dfeda1244dGuillaume Valadon                    raise
11835125c23da6e8137a570583e754da9b918c8a63bPhil        else:
11935125c23da6e8137a570583e754da9b918c8a63bPhil            pending_if.append((dest,netmask,gw))
1201ad4bbf39716a7c25161cb118f02344291e0c102Phil    f.close()
12135125c23da6e8137a570583e754da9b918c8a63bPhil
12235125c23da6e8137a570583e754da9b918c8a63bPhil    # On Solaris, netstat does not provide output interfaces for some routes
12335125c23da6e8137a570583e754da9b918c8a63bPhil    # We need to parse completely the routing table to route their gw and
12435125c23da6e8137a570583e754da9b918c8a63bPhil    # know their output interface
12535125c23da6e8137a570583e754da9b918c8a63bPhil    for dest,netmask,gw in pending_if:
12635125c23da6e8137a570583e754da9b918c8a63bPhil        gw_l = scapy.utils.atol(gw)
12735125c23da6e8137a570583e754da9b918c8a63bPhil        max_rtmask,gw_if,gw_if_addr, = 0,None,None
12835125c23da6e8137a570583e754da9b918c8a63bPhil        for rtdst,rtmask,_,rtif,rtaddr in routes[:]:
12935125c23da6e8137a570583e754da9b918c8a63bPhil            if gw_l & rtmask == rtdst:
13035125c23da6e8137a570583e754da9b918c8a63bPhil                if rtmask >= max_rtmask:
13135125c23da6e8137a570583e754da9b918c8a63bPhil                    max_rtmask = rtmask
13235125c23da6e8137a570583e754da9b918c8a63bPhil                    gw_if = rtif
13335125c23da6e8137a570583e754da9b918c8a63bPhil                    gw_if_addr = rtaddr
1343fc82d65918606609ab12a449982e37891e1ade3gpotter        # XXX: TODO add metrics
1353fc82d65918606609ab12a449982e37891e1ade3gpotter        metric = 1
13635125c23da6e8137a570583e754da9b918c8a63bPhil        if gw_if:
1373fc82d65918606609ab12a449982e37891e1ade3gpotter            routes.append((dest,netmask, gw, gw_if, gw_if_addr, metric))
13835125c23da6e8137a570583e754da9b918c8a63bPhil        else:
139edd4e843008c637bf3d1a2c8274a8429ac2a4185gpotter            warning("Did not find output interface to reach gateway %s", gw)
1400d92fc9fbb58df9cc4bbeb007bf65020fe1aa092Victor Pfautz
1411ad4bbf39716a7c25161cb118f02344291e0c102Phil    return routes
1421ad4bbf39716a7c25161cb118f02344291e0c102Phil
143706c5f38284d9d57cc8f7a957b8577513b97bc16Phil############
144706c5f38284d9d57cc8f7a957b8577513b97bc16Phil### IPv6 ###
145706c5f38284d9d57cc8f7a957b8577513b97bc16Phil############
146706c5f38284d9d57cc8f7a957b8577513b97bc16Phil
147cb32e5344e075f97776c8043ffd312c2d3ce3477Guillaume Valadondef _in6_getifaddr(ifname):
148cb32e5344e075f97776c8043ffd312c2d3ce3477Guillaume Valadon    """
149cb32e5344e075f97776c8043ffd312c2d3ce3477Guillaume Valadon    Returns a list of IPv6 addresses configured on the interface ifname.
150cb32e5344e075f97776c8043ffd312c2d3ce3477Guillaume Valadon    """
151cb32e5344e075f97776c8043ffd312c2d3ce3477Guillaume Valadon
152cb32e5344e075f97776c8043ffd312c2d3ce3477Guillaume Valadon    # Get the output of ifconfig
153cb32e5344e075f97776c8043ffd312c2d3ce3477Guillaume Valadon    try:
154cb32e5344e075f97776c8043ffd312c2d3ce3477Guillaume Valadon        f = os.popen("%s %s" % (conf.prog.ifconfig, ifname))
155d51edef8530fe1e944f13eb65ef863c2d7f04b1dgpotter    except OSError as msg:
156cb32e5344e075f97776c8043ffd312c2d3ce3477Guillaume Valadon        log_interactive.warning("Failed to execute ifconfig.")
157cb32e5344e075f97776c8043ffd312c2d3ce3477Guillaume Valadon        return []
158cb32e5344e075f97776c8043ffd312c2d3ce3477Guillaume Valadon
159cb32e5344e075f97776c8043ffd312c2d3ce3477Guillaume Valadon    # Iterate over lines and extract IPv6 addresses
160af70bfcbd63dca4b743b3b2b76210dc33d8679c1Guillaume Valadon    ret = []
16199e2f18c2b0187dcd9d21e13df44f981de4925f2Guillaume Valadon    for line in f:
162af70bfcbd63dca4b743b3b2b76210dc33d8679c1Guillaume Valadon        if "inet6" in line:
16399e2f18c2b0187dcd9d21e13df44f981de4925f2Guillaume Valadon            addr = line.rstrip().split(None, 2)[1] # The second element is the IPv6 address
164cb32e5344e075f97776c8043ffd312c2d3ce3477Guillaume Valadon        else:
165cb32e5344e075f97776c8043ffd312c2d3ce3477Guillaume Valadon            continue
166af70bfcbd63dca4b743b3b2b76210dc33d8679c1Guillaume Valadon        if '%' in line: # Remove the interface identifier if present
16799e2f18c2b0187dcd9d21e13df44f981de4925f2Guillaume Valadon            addr = addr.split("%", 1)[0]
168cb32e5344e075f97776c8043ffd312c2d3ce3477Guillaume Valadon
169cb32e5344e075f97776c8043ffd312c2d3ce3477Guillaume Valadon        # Check if it is a valid IPv6 address
170cb32e5344e075f97776c8043ffd312c2d3ce3477Guillaume Valadon        try:
171cb32e5344e075f97776c8043ffd312c2d3ce3477Guillaume Valadon            socket.inet_pton(socket.AF_INET6, addr)
172cb32e5344e075f97776c8043ffd312c2d3ce3477Guillaume Valadon        except:
173cb32e5344e075f97776c8043ffd312c2d3ce3477Guillaume Valadon            continue
174cb32e5344e075f97776c8043ffd312c2d3ce3477Guillaume Valadon
175cb32e5344e075f97776c8043ffd312c2d3ce3477Guillaume Valadon        # Get the scope and keep the address
1762066af02d4144547e3531c16a5ede1340b97938bGuillaume Valadon        scope = in6_getscope(addr)
177cb32e5344e075f97776c8043ffd312c2d3ce3477Guillaume Valadon        ret.append((addr, scope, ifname))
178cb32e5344e075f97776c8043ffd312c2d3ce3477Guillaume Valadon
179cb32e5344e075f97776c8043ffd312c2d3ce3477Guillaume Valadon    return ret
180cb32e5344e075f97776c8043ffd312c2d3ce3477Guillaume Valadon
1810d92fc9fbb58df9cc4bbeb007bf65020fe1aa092Victor Pfautzdef in6_getifaddr():
182706c5f38284d9d57cc8f7a957b8577513b97bc16Phil    """
183706c5f38284d9d57cc8f7a957b8577513b97bc16Phil    Returns a list of 3-tuples of the form (addr, scope, iface) where
184706c5f38284d9d57cc8f7a957b8577513b97bc16Phil    'addr' is the address of scope 'scope' associated to the interface
185cb32e5344e075f97776c8043ffd312c2d3ce3477Guillaume Valadon    'iface'.
186706c5f38284d9d57cc8f7a957b8577513b97bc16Phil
187706c5f38284d9d57cc8f7a957b8577513b97bc16Phil    This is the list of all addresses of all interfaces available on
188706c5f38284d9d57cc8f7a957b8577513b97bc16Phil    the system.
189706c5f38284d9d57cc8f7a957b8577513b97bc16Phil    """
190706c5f38284d9d57cc8f7a957b8577513b97bc16Phil
191cb32e5344e075f97776c8043ffd312c2d3ce3477Guillaume Valadon    # List all network interfaces
19235f97efc53839f6366918c251dba5df2ac553c8dGuillaume Valadon    if OPENBSD:
193a7d199a93e4b8deeefaf4e85fd553ec0a1c002daDaniel Jakots        try:
194a7d199a93e4b8deeefaf4e85fd553ec0a1c002daDaniel Jakots            f = os.popen("%s" % conf.prog.ifconfig)
195d51edef8530fe1e944f13eb65ef863c2d7f04b1dgpotter        except OSError as msg:
1960d92fc9fbb58df9cc4bbeb007bf65020fe1aa092Victor Pfautz            log_interactive.warning("Failed to execute ifconfig.")
1970d92fc9fbb58df9cc4bbeb007bf65020fe1aa092Victor Pfautz            return []
198a7d199a93e4b8deeefaf4e85fd553ec0a1c002daDaniel Jakots
199a7d199a93e4b8deeefaf4e85fd553ec0a1c002daDaniel Jakots        # Get the list of network interfaces
200a7d199a93e4b8deeefaf4e85fd553ec0a1c002daDaniel Jakots        splitted_line = []
201a7d199a93e4b8deeefaf4e85fd553ec0a1c002daDaniel Jakots        for l in f:
202a7d199a93e4b8deeefaf4e85fd553ec0a1c002daDaniel Jakots            if "flags" in l:
203a7d199a93e4b8deeefaf4e85fd553ec0a1c002daDaniel Jakots                iface = l.split()[0].rstrip(':')
204a7d199a93e4b8deeefaf4e85fd553ec0a1c002daDaniel Jakots                splitted_line.append(iface)
205a7d199a93e4b8deeefaf4e85fd553ec0a1c002daDaniel Jakots
206a7d199a93e4b8deeefaf4e85fd553ec0a1c002daDaniel Jakots    else: # FreeBSD, NetBSD or Darwin
207a7d199a93e4b8deeefaf4e85fd553ec0a1c002daDaniel Jakots        try:
2080d92fc9fbb58df9cc4bbeb007bf65020fe1aa092Victor Pfautz            f = os.popen("%s -l" % conf.prog.ifconfig)
209d51edef8530fe1e944f13eb65ef863c2d7f04b1dgpotter        except OSError as msg:
2100d92fc9fbb58df9cc4bbeb007bf65020fe1aa092Victor Pfautz            log_interactive.warning("Failed to execute ifconfig.")
2110d92fc9fbb58df9cc4bbeb007bf65020fe1aa092Victor Pfautz            return []
212a7d199a93e4b8deeefaf4e85fd553ec0a1c002daDaniel Jakots
213a7d199a93e4b8deeefaf4e85fd553ec0a1c002daDaniel Jakots        # Get the list of network interfaces
214a7d199a93e4b8deeefaf4e85fd553ec0a1c002daDaniel Jakots        splitted_line = f.readline().rstrip().split()
215cb32e5344e075f97776c8043ffd312c2d3ce3477Guillaume Valadon
216706c5f38284d9d57cc8f7a957b8577513b97bc16Phil    ret = []
217af70bfcbd63dca4b743b3b2b76210dc33d8679c1Guillaume Valadon    for i in splitted_line:
2180d92fc9fbb58df9cc4bbeb007bf65020fe1aa092Victor Pfautz        ret += _in6_getifaddr(i)
2190d92fc9fbb58df9cc4bbeb007bf65020fe1aa092Victor Pfautz    return ret
220706c5f38284d9d57cc8f7a957b8577513b97bc16Phil
2212066af02d4144547e3531c16a5ede1340b97938bGuillaume Valadon
222706c5f38284d9d57cc8f7a957b8577513b97bc16Phildef read_routes6():
2232066af02d4144547e3531c16a5ede1340b97938bGuillaume Valadon    """Return a list of IPv6 routes than can be used by Scapy."""
2242066af02d4144547e3531c16a5ede1340b97938bGuillaume Valadon
2252066af02d4144547e3531c16a5ede1340b97938bGuillaume Valadon    # Call netstat to retrieve IPv6 routes
2262066af02d4144547e3531c16a5ede1340b97938bGuillaume Valadon    fd_netstat = os.popen("netstat -rn -f inet6")
2272066af02d4144547e3531c16a5ede1340b97938bGuillaume Valadon
2280d92fc9fbb58df9cc4bbeb007bf65020fe1aa092Victor Pfautz    # List interfaces IPv6 addresses
2292066af02d4144547e3531c16a5ede1340b97938bGuillaume Valadon    lifaddr = in6_getifaddr()
2302066af02d4144547e3531c16a5ede1340b97938bGuillaume Valadon    if not lifaddr:
2312066af02d4144547e3531c16a5ede1340b97938bGuillaume Valadon        return []
2322066af02d4144547e3531c16a5ede1340b97938bGuillaume Valadon
2332066af02d4144547e3531c16a5ede1340b97938bGuillaume Valadon    # Routes header information
2342066af02d4144547e3531c16a5ede1340b97938bGuillaume Valadon    got_header = False
235963c58e7bf45216aa225f56b721aa9261fd9fe5aPhil    mtu_present = False
236963c58e7bf45216aa225f56b721aa9261fd9fe5aPhil    prio_present = False
2372066af02d4144547e3531c16a5ede1340b97938bGuillaume Valadon
2382066af02d4144547e3531c16a5ede1340b97938bGuillaume Valadon    # Parse the routes
239706c5f38284d9d57cc8f7a957b8577513b97bc16Phil    routes = []
2402066af02d4144547e3531c16a5ede1340b97938bGuillaume Valadon    for line in fd_netstat.readlines():
2412066af02d4144547e3531c16a5ede1340b97938bGuillaume Valadon
2422066af02d4144547e3531c16a5ede1340b97938bGuillaume Valadon        # Parse the routes header and try to identify extra columns
2432066af02d4144547e3531c16a5ede1340b97938bGuillaume Valadon        if not got_header:
2442066af02d4144547e3531c16a5ede1340b97938bGuillaume Valadon            if "Destination" == line[:11]:
2452066af02d4144547e3531c16a5ede1340b97938bGuillaume Valadon                got_header = True
2462066af02d4144547e3531c16a5ede1340b97938bGuillaume Valadon                mtu_present = "Mtu" in line
2472066af02d4144547e3531c16a5ede1340b97938bGuillaume Valadon                prio_present = "Prio" in line
248706c5f38284d9d57cc8f7a957b8577513b97bc16Phil            continue
2492066af02d4144547e3531c16a5ede1340b97938bGuillaume Valadon
2502066af02d4144547e3531c16a5ede1340b97938bGuillaume Valadon        # Parse a route entry according to the operating system
2512066af02d4144547e3531c16a5ede1340b97938bGuillaume Valadon        splitted_line = line.split()
25235f97efc53839f6366918c251dba5df2ac553c8dGuillaume Valadon        if OPENBSD or NETBSD:
2532066af02d4144547e3531c16a5ede1340b97938bGuillaume Valadon            index = 5 + mtu_present + prio_present
2542066af02d4144547e3531c16a5ede1340b97938bGuillaume Valadon            if len(splitted_line) < index:
2552066af02d4144547e3531c16a5ede1340b97938bGuillaume Valadon                warning("Not enough columns in route entry !")
2562066af02d4144547e3531c16a5ede1340b97938bGuillaume Valadon                continue
2572066af02d4144547e3531c16a5ede1340b97938bGuillaume Valadon            destination, next_hop, flags = splitted_line[:3]
2582066af02d4144547e3531c16a5ede1340b97938bGuillaume Valadon            dev = splitted_line[index]
2592066af02d4144547e3531c16a5ede1340b97938bGuillaume Valadon        else:
2600d92fc9fbb58df9cc4bbeb007bf65020fe1aa092Victor Pfautz            # FREEBSD or DARWIN
2612066af02d4144547e3531c16a5ede1340b97938bGuillaume Valadon            if len(splitted_line) < 4:
2622066af02d4144547e3531c16a5ede1340b97938bGuillaume Valadon                warning("Not enough columns in route entry !")
2632066af02d4144547e3531c16a5ede1340b97938bGuillaume Valadon                continue
2642066af02d4144547e3531c16a5ede1340b97938bGuillaume Valadon            destination, next_hop, flags, dev = splitted_line[:4]
2652066af02d4144547e3531c16a5ede1340b97938bGuillaume Valadon
2663fc82d65918606609ab12a449982e37891e1ade3gpotter        # XXX: TODO: add metrics for unix.py (use -e option on netstat)
2673fc82d65918606609ab12a449982e37891e1ade3gpotter        metric = 1
2683fc82d65918606609ab12a449982e37891e1ade3gpotter
2692066af02d4144547e3531c16a5ede1340b97938bGuillaume Valadon        # Check flags
2702066af02d4144547e3531c16a5ede1340b97938bGuillaume Valadon        if not "U" in flags:  # usable route
2712066af02d4144547e3531c16a5ede1340b97938bGuillaume Valadon            continue
272ed95a6a3cea25583d339e564b7c93bdbcbbf9483Guillaume Valadon        if "R" in flags:  # Host or net unreachable
273706c5f38284d9d57cc8f7a957b8577513b97bc16Phil            continue
2742066af02d4144547e3531c16a5ede1340b97938bGuillaume Valadon        if "m" in flags:  # multicast address
2752066af02d4144547e3531c16a5ede1340b97938bGuillaume Valadon            # Note: multicast routing is handled in Route6.route()
276706c5f38284d9d57cc8f7a957b8577513b97bc16Phil            continue
277706c5f38284d9d57cc8f7a957b8577513b97bc16Phil
2782066af02d4144547e3531c16a5ede1340b97938bGuillaume Valadon        # Replace link with the default route in next_hop
2792066af02d4144547e3531c16a5ede1340b97938bGuillaume Valadon        if "link" in next_hop:
2802066af02d4144547e3531c16a5ede1340b97938bGuillaume Valadon            next_hop = "::"
281706c5f38284d9d57cc8f7a957b8577513b97bc16Phil
2822066af02d4144547e3531c16a5ede1340b97938bGuillaume Valadon        # Default prefix length
2832066af02d4144547e3531c16a5ede1340b97938bGuillaume Valadon        destination_plen = 128
284706c5f38284d9d57cc8f7a957b8577513b97bc16Phil
2852066af02d4144547e3531c16a5ede1340b97938bGuillaume Valadon        # Extract network interface from the zone id
2862066af02d4144547e3531c16a5ede1340b97938bGuillaume Valadon        if '%' in destination:
2872066af02d4144547e3531c16a5ede1340b97938bGuillaume Valadon            destination, dev = destination.split('%')
2882066af02d4144547e3531c16a5ede1340b97938bGuillaume Valadon            if '/' in dev:
2892066af02d4144547e3531c16a5ede1340b97938bGuillaume Valadon                # Example: fe80::%lo0/64 ; dev = "lo0/64"
2902066af02d4144547e3531c16a5ede1340b97938bGuillaume Valadon                dev, destination_plen = dev.split('/')
2912066af02d4144547e3531c16a5ede1340b97938bGuillaume Valadon        if '%' in next_hop:
2922066af02d4144547e3531c16a5ede1340b97938bGuillaume Valadon            next_hop, dev = next_hop.split('%')
293706c5f38284d9d57cc8f7a957b8577513b97bc16Phil
2942066af02d4144547e3531c16a5ede1340b97938bGuillaume Valadon        # Ensure that the next hop is a valid IPv6 address
2952066af02d4144547e3531c16a5ede1340b97938bGuillaume Valadon        if not in6_isvalid(next_hop):
2962066af02d4144547e3531c16a5ede1340b97938bGuillaume Valadon            # Note: the 'Gateway' column might contain a MAC address
2972066af02d4144547e3531c16a5ede1340b97938bGuillaume Valadon            next_hop = "::"
2981ad4bbf39716a7c25161cb118f02344291e0c102Phil
2992066af02d4144547e3531c16a5ede1340b97938bGuillaume Valadon        # Modify parsed routing entries
3002066af02d4144547e3531c16a5ede1340b97938bGuillaume Valadon        # Note: these rules are OS specific and may evolve over time
3012066af02d4144547e3531c16a5ede1340b97938bGuillaume Valadon        if destination == "default":
3022066af02d4144547e3531c16a5ede1340b97938bGuillaume Valadon            destination, destination_plen = "::", 0
3032066af02d4144547e3531c16a5ede1340b97938bGuillaume Valadon        elif '/' in destination:
3042066af02d4144547e3531c16a5ede1340b97938bGuillaume Valadon            # Example: fe80::/10
3052066af02d4144547e3531c16a5ede1340b97938bGuillaume Valadon            destination, destination_plen = destination.split('/')
3062066af02d4144547e3531c16a5ede1340b97938bGuillaume Valadon        if '/' in dev:
3072066af02d4144547e3531c16a5ede1340b97938bGuillaume Valadon            # Example: ff02::%lo0/32 ; dev = "lo0/32"
3082066af02d4144547e3531c16a5ede1340b97938bGuillaume Valadon            dev, destination_plen = dev.split('/')
3091ad4bbf39716a7c25161cb118f02344291e0c102Phil
3102066af02d4144547e3531c16a5ede1340b97938bGuillaume Valadon        # Check route entries parameters consistency
3112066af02d4144547e3531c16a5ede1340b97938bGuillaume Valadon        if not in6_isvalid(destination):
3122066af02d4144547e3531c16a5ede1340b97938bGuillaume Valadon            warning("Invalid destination IPv6 address in route entry !")
3132066af02d4144547e3531c16a5ede1340b97938bGuillaume Valadon            continue
3142066af02d4144547e3531c16a5ede1340b97938bGuillaume Valadon        try:
3152066af02d4144547e3531c16a5ede1340b97938bGuillaume Valadon            destination_plen = int(destination_plen)
3162066af02d4144547e3531c16a5ede1340b97938bGuillaume Valadon        except:
3172066af02d4144547e3531c16a5ede1340b97938bGuillaume Valadon            warning("Invalid IPv6 prefix length in route entry !")
3182066af02d4144547e3531c16a5ede1340b97938bGuillaume Valadon            continue
3192066af02d4144547e3531c16a5ede1340b97938bGuillaume Valadon        if in6_ismlladdr(destination) or in6_ismnladdr(destination):
3202066af02d4144547e3531c16a5ede1340b97938bGuillaume Valadon            # Note: multicast routing is handled in Route6.route()
3212066af02d4144547e3531c16a5ede1340b97938bGuillaume Valadon            continue
3221ad4bbf39716a7c25161cb118f02344291e0c102Phil
32335f97efc53839f6366918c251dba5df2ac553c8dGuillaume Valadon        if LOOPBACK_NAME in dev:
3242066af02d4144547e3531c16a5ede1340b97938bGuillaume Valadon            # Handle ::1 separately
3252066af02d4144547e3531c16a5ede1340b97938bGuillaume Valadon            cset = ["::1"]
3262066af02d4144547e3531c16a5ede1340b97938bGuillaume Valadon            next_hop = "::"
3272066af02d4144547e3531c16a5ede1340b97938bGuillaume Valadon        else:
3282066af02d4144547e3531c16a5ede1340b97938bGuillaume Valadon            # Get possible IPv6 source addresses
32952165d0dd55a1071b935d002a41a95210c97f8e7Guillaume Valadon            devaddrs = (x for x in lifaddr if x[2] == dev)
330653d114f04a98dd7cc38c4f57a7cad78f28e50dcgpotter            cset = construct_source_candidate_set(destination, destination_plen, devaddrs)
3311ad4bbf39716a7c25161cb118f02344291e0c102Phil
3322066af02d4144547e3531c16a5ede1340b97938bGuillaume Valadon        if len(cset):
3333fc82d65918606609ab12a449982e37891e1ade3gpotter            routes.append((destination, destination_plen, next_hop, dev, cset, metric))
3342066af02d4144547e3531c16a5ede1340b97938bGuillaume Valadon
3352066af02d4144547e3531c16a5ede1340b97938bGuillaume Valadon    fd_netstat.close()
3362066af02d4144547e3531c16a5ede1340b97938bGuillaume Valadon    return routes
337