unix.py revision 291400c1b6f65363e33cc982aaf0d43d31cc424e
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)
971ad4bbf39716a7c25161cb118f02344291e0c102Phil        if not "G" in flg:
981ad4bbf39716a7c25161cb118f02344291e0c102Phil            gw = '0.0.0.0'
9935125c23da6e8137a570583e754da9b918c8a63bPhil        if netif is not None:
10002ae08b4ad973184a167ffcfb10043dfeda1244dGuillaume Valadon            try:
10135f97efc53839f6366918c251dba5df2ac553c8dGuillaume Valadon                ifaddr = get_if_addr(netif)
10202ae08b4ad973184a167ffcfb10043dfeda1244dGuillaume Valadon                routes.append((dest,netmask,gw,netif,ifaddr))
10302ae08b4ad973184a167ffcfb10043dfeda1244dGuillaume Valadon            except OSError as exc:
10402ae08b4ad973184a167ffcfb10043dfeda1244dGuillaume Valadon                if exc.message == 'Device not configured':
10502ae08b4ad973184a167ffcfb10043dfeda1244dGuillaume Valadon                    # This means the interface name is probably truncated by
10602ae08b4ad973184a167ffcfb10043dfeda1244dGuillaume Valadon                    # netstat -nr. We attempt to guess it's name and if not we
10702ae08b4ad973184a167ffcfb10043dfeda1244dGuillaume Valadon                    # ignore it.
10802ae08b4ad973184a167ffcfb10043dfeda1244dGuillaume Valadon                    guessed_netif = _guess_iface_name(netif)
10902ae08b4ad973184a167ffcfb10043dfeda1244dGuillaume Valadon                    if guessed_netif is not None:
11035f97efc53839f6366918c251dba5df2ac553c8dGuillaume Valadon                        ifaddr = get_if_addr(guessed_netif)
11102ae08b4ad973184a167ffcfb10043dfeda1244dGuillaume Valadon                        routes.append((dest, netmask, gw, guessed_netif, ifaddr))
11202ae08b4ad973184a167ffcfb10043dfeda1244dGuillaume Valadon                    else:
11302ae08b4ad973184a167ffcfb10043dfeda1244dGuillaume Valadon                        warning("Could not guess partial interface name: %s" % netif)
11402ae08b4ad973184a167ffcfb10043dfeda1244dGuillaume Valadon                else:
11502ae08b4ad973184a167ffcfb10043dfeda1244dGuillaume Valadon                    raise
11635125c23da6e8137a570583e754da9b918c8a63bPhil        else:
11735125c23da6e8137a570583e754da9b918c8a63bPhil            pending_if.append((dest,netmask,gw))
1181ad4bbf39716a7c25161cb118f02344291e0c102Phil    f.close()
11935125c23da6e8137a570583e754da9b918c8a63bPhil
12035125c23da6e8137a570583e754da9b918c8a63bPhil    # On Solaris, netstat does not provide output interfaces for some routes
12135125c23da6e8137a570583e754da9b918c8a63bPhil    # We need to parse completely the routing table to route their gw and
12235125c23da6e8137a570583e754da9b918c8a63bPhil    # know their output interface
12335125c23da6e8137a570583e754da9b918c8a63bPhil    for dest,netmask,gw in pending_if:
12435125c23da6e8137a570583e754da9b918c8a63bPhil        gw_l = scapy.utils.atol(gw)
12535125c23da6e8137a570583e754da9b918c8a63bPhil        max_rtmask,gw_if,gw_if_addr, = 0,None,None
12635125c23da6e8137a570583e754da9b918c8a63bPhil        for rtdst,rtmask,_,rtif,rtaddr in routes[:]:
12735125c23da6e8137a570583e754da9b918c8a63bPhil            if gw_l & rtmask == rtdst:
12835125c23da6e8137a570583e754da9b918c8a63bPhil                if rtmask >= max_rtmask:
12935125c23da6e8137a570583e754da9b918c8a63bPhil                    max_rtmask = rtmask
13035125c23da6e8137a570583e754da9b918c8a63bPhil                    gw_if = rtif
13135125c23da6e8137a570583e754da9b918c8a63bPhil                    gw_if_addr = rtaddr
13235125c23da6e8137a570583e754da9b918c8a63bPhil        if gw_if:
13335125c23da6e8137a570583e754da9b918c8a63bPhil            routes.append((dest,netmask,gw,gw_if,gw_if_addr))
13435125c23da6e8137a570583e754da9b918c8a63bPhil        else:
13535125c23da6e8137a570583e754da9b918c8a63bPhil            warning("Did not find output interface to reach gateway %s" % gw)
1360d92fc9fbb58df9cc4bbeb007bf65020fe1aa092Victor Pfautz
1371ad4bbf39716a7c25161cb118f02344291e0c102Phil    return routes
1381ad4bbf39716a7c25161cb118f02344291e0c102Phil
139706c5f38284d9d57cc8f7a957b8577513b97bc16Phil############
140706c5f38284d9d57cc8f7a957b8577513b97bc16Phil### IPv6 ###
141706c5f38284d9d57cc8f7a957b8577513b97bc16Phil############
142706c5f38284d9d57cc8f7a957b8577513b97bc16Phil
143cb32e5344e075f97776c8043ffd312c2d3ce3477Guillaume Valadondef _in6_getifaddr(ifname):
144cb32e5344e075f97776c8043ffd312c2d3ce3477Guillaume Valadon    """
145cb32e5344e075f97776c8043ffd312c2d3ce3477Guillaume Valadon    Returns a list of IPv6 addresses configured on the interface ifname.
146cb32e5344e075f97776c8043ffd312c2d3ce3477Guillaume Valadon    """
147cb32e5344e075f97776c8043ffd312c2d3ce3477Guillaume Valadon
148cb32e5344e075f97776c8043ffd312c2d3ce3477Guillaume Valadon    # Get the output of ifconfig
149cb32e5344e075f97776c8043ffd312c2d3ce3477Guillaume Valadon    try:
150cb32e5344e075f97776c8043ffd312c2d3ce3477Guillaume Valadon        f = os.popen("%s %s" % (conf.prog.ifconfig, ifname))
151cb32e5344e075f97776c8043ffd312c2d3ce3477Guillaume Valadon    except OSError,msg:
152cb32e5344e075f97776c8043ffd312c2d3ce3477Guillaume Valadon        log_interactive.warning("Failed to execute ifconfig.")
153cb32e5344e075f97776c8043ffd312c2d3ce3477Guillaume Valadon        return []
154cb32e5344e075f97776c8043ffd312c2d3ce3477Guillaume Valadon
155cb32e5344e075f97776c8043ffd312c2d3ce3477Guillaume Valadon    # Iterate over lines and extract IPv6 addresses
156af70bfcbd63dca4b743b3b2b76210dc33d8679c1Guillaume Valadon    ret = []
15799e2f18c2b0187dcd9d21e13df44f981de4925f2Guillaume Valadon    for line in f:
158af70bfcbd63dca4b743b3b2b76210dc33d8679c1Guillaume Valadon        if "inet6" in line:
15999e2f18c2b0187dcd9d21e13df44f981de4925f2Guillaume Valadon            addr = line.rstrip().split(None, 2)[1] # The second element is the IPv6 address
160cb32e5344e075f97776c8043ffd312c2d3ce3477Guillaume Valadon        else:
161cb32e5344e075f97776c8043ffd312c2d3ce3477Guillaume Valadon            continue
162af70bfcbd63dca4b743b3b2b76210dc33d8679c1Guillaume Valadon        if '%' in line: # Remove the interface identifier if present
16399e2f18c2b0187dcd9d21e13df44f981de4925f2Guillaume Valadon            addr = addr.split("%", 1)[0]
164cb32e5344e075f97776c8043ffd312c2d3ce3477Guillaume Valadon
165cb32e5344e075f97776c8043ffd312c2d3ce3477Guillaume Valadon        # Check if it is a valid IPv6 address
166cb32e5344e075f97776c8043ffd312c2d3ce3477Guillaume Valadon        try:
167cb32e5344e075f97776c8043ffd312c2d3ce3477Guillaume Valadon            socket.inet_pton(socket.AF_INET6, addr)
168cb32e5344e075f97776c8043ffd312c2d3ce3477Guillaume Valadon        except:
169cb32e5344e075f97776c8043ffd312c2d3ce3477Guillaume Valadon            continue
170cb32e5344e075f97776c8043ffd312c2d3ce3477Guillaume Valadon
171cb32e5344e075f97776c8043ffd312c2d3ce3477Guillaume Valadon        # Get the scope and keep the address
1722066af02d4144547e3531c16a5ede1340b97938bGuillaume Valadon        scope = in6_getscope(addr)
173cb32e5344e075f97776c8043ffd312c2d3ce3477Guillaume Valadon        ret.append((addr, scope, ifname))
174cb32e5344e075f97776c8043ffd312c2d3ce3477Guillaume Valadon
175cb32e5344e075f97776c8043ffd312c2d3ce3477Guillaume Valadon    return ret
176cb32e5344e075f97776c8043ffd312c2d3ce3477Guillaume Valadon
1770d92fc9fbb58df9cc4bbeb007bf65020fe1aa092Victor Pfautzdef in6_getifaddr():
178706c5f38284d9d57cc8f7a957b8577513b97bc16Phil    """
179706c5f38284d9d57cc8f7a957b8577513b97bc16Phil    Returns a list of 3-tuples of the form (addr, scope, iface) where
180706c5f38284d9d57cc8f7a957b8577513b97bc16Phil    'addr' is the address of scope 'scope' associated to the interface
181cb32e5344e075f97776c8043ffd312c2d3ce3477Guillaume Valadon    'iface'.
182706c5f38284d9d57cc8f7a957b8577513b97bc16Phil
183706c5f38284d9d57cc8f7a957b8577513b97bc16Phil    This is the list of all addresses of all interfaces available on
184706c5f38284d9d57cc8f7a957b8577513b97bc16Phil    the system.
185706c5f38284d9d57cc8f7a957b8577513b97bc16Phil    """
186706c5f38284d9d57cc8f7a957b8577513b97bc16Phil
187cb32e5344e075f97776c8043ffd312c2d3ce3477Guillaume Valadon    # List all network interfaces
18835f97efc53839f6366918c251dba5df2ac553c8dGuillaume Valadon    if OPENBSD:
189a7d199a93e4b8deeefaf4e85fd553ec0a1c002daDaniel Jakots        try:
190a7d199a93e4b8deeefaf4e85fd553ec0a1c002daDaniel Jakots            f = os.popen("%s" % conf.prog.ifconfig)
191a7d199a93e4b8deeefaf4e85fd553ec0a1c002daDaniel Jakots        except OSError,msg:
1920d92fc9fbb58df9cc4bbeb007bf65020fe1aa092Victor Pfautz            log_interactive.warning("Failed to execute ifconfig.")
1930d92fc9fbb58df9cc4bbeb007bf65020fe1aa092Victor Pfautz            return []
194a7d199a93e4b8deeefaf4e85fd553ec0a1c002daDaniel Jakots
195a7d199a93e4b8deeefaf4e85fd553ec0a1c002daDaniel Jakots        # Get the list of network interfaces
196a7d199a93e4b8deeefaf4e85fd553ec0a1c002daDaniel Jakots        splitted_line = []
197a7d199a93e4b8deeefaf4e85fd553ec0a1c002daDaniel Jakots        for l in f:
198a7d199a93e4b8deeefaf4e85fd553ec0a1c002daDaniel Jakots            if "flags" in l:
199a7d199a93e4b8deeefaf4e85fd553ec0a1c002daDaniel Jakots                iface = l.split()[0].rstrip(':')
200a7d199a93e4b8deeefaf4e85fd553ec0a1c002daDaniel Jakots                splitted_line.append(iface)
201a7d199a93e4b8deeefaf4e85fd553ec0a1c002daDaniel Jakots
202a7d199a93e4b8deeefaf4e85fd553ec0a1c002daDaniel Jakots    else: # FreeBSD, NetBSD or Darwin
203a7d199a93e4b8deeefaf4e85fd553ec0a1c002daDaniel Jakots        try:
2040d92fc9fbb58df9cc4bbeb007bf65020fe1aa092Victor Pfautz            f = os.popen("%s -l" % conf.prog.ifconfig)
205a7d199a93e4b8deeefaf4e85fd553ec0a1c002daDaniel Jakots        except OSError,msg:
2060d92fc9fbb58df9cc4bbeb007bf65020fe1aa092Victor Pfautz            log_interactive.warning("Failed to execute ifconfig.")
2070d92fc9fbb58df9cc4bbeb007bf65020fe1aa092Victor Pfautz            return []
208a7d199a93e4b8deeefaf4e85fd553ec0a1c002daDaniel Jakots
209a7d199a93e4b8deeefaf4e85fd553ec0a1c002daDaniel Jakots        # Get the list of network interfaces
210a7d199a93e4b8deeefaf4e85fd553ec0a1c002daDaniel Jakots        splitted_line = f.readline().rstrip().split()
211cb32e5344e075f97776c8043ffd312c2d3ce3477Guillaume Valadon
212706c5f38284d9d57cc8f7a957b8577513b97bc16Phil    ret = []
213af70bfcbd63dca4b743b3b2b76210dc33d8679c1Guillaume Valadon    for i in splitted_line:
2140d92fc9fbb58df9cc4bbeb007bf65020fe1aa092Victor Pfautz        ret += _in6_getifaddr(i)
2150d92fc9fbb58df9cc4bbeb007bf65020fe1aa092Victor Pfautz    return ret
216706c5f38284d9d57cc8f7a957b8577513b97bc16Phil
2172066af02d4144547e3531c16a5ede1340b97938bGuillaume Valadon
218706c5f38284d9d57cc8f7a957b8577513b97bc16Phildef read_routes6():
2192066af02d4144547e3531c16a5ede1340b97938bGuillaume Valadon    """Return a list of IPv6 routes than can be used by Scapy."""
2202066af02d4144547e3531c16a5ede1340b97938bGuillaume Valadon
2212066af02d4144547e3531c16a5ede1340b97938bGuillaume Valadon    # Call netstat to retrieve IPv6 routes
2222066af02d4144547e3531c16a5ede1340b97938bGuillaume Valadon    fd_netstat = os.popen("netstat -rn -f inet6")
2232066af02d4144547e3531c16a5ede1340b97938bGuillaume Valadon
2240d92fc9fbb58df9cc4bbeb007bf65020fe1aa092Victor Pfautz    # List interfaces IPv6 addresses
2252066af02d4144547e3531c16a5ede1340b97938bGuillaume Valadon    lifaddr = in6_getifaddr()
2262066af02d4144547e3531c16a5ede1340b97938bGuillaume Valadon    if not lifaddr:
2272066af02d4144547e3531c16a5ede1340b97938bGuillaume Valadon        return []
2282066af02d4144547e3531c16a5ede1340b97938bGuillaume Valadon
2292066af02d4144547e3531c16a5ede1340b97938bGuillaume Valadon    # Routes header information
2302066af02d4144547e3531c16a5ede1340b97938bGuillaume Valadon    got_header = False
231963c58e7bf45216aa225f56b721aa9261fd9fe5aPhil    mtu_present = False
232963c58e7bf45216aa225f56b721aa9261fd9fe5aPhil    prio_present = False
2332066af02d4144547e3531c16a5ede1340b97938bGuillaume Valadon
2342066af02d4144547e3531c16a5ede1340b97938bGuillaume Valadon    # Parse the routes
235706c5f38284d9d57cc8f7a957b8577513b97bc16Phil    routes = []
2362066af02d4144547e3531c16a5ede1340b97938bGuillaume Valadon    for line in fd_netstat.readlines():
2372066af02d4144547e3531c16a5ede1340b97938bGuillaume Valadon
2382066af02d4144547e3531c16a5ede1340b97938bGuillaume Valadon        # Parse the routes header and try to identify extra columns
2392066af02d4144547e3531c16a5ede1340b97938bGuillaume Valadon        if not got_header:
2402066af02d4144547e3531c16a5ede1340b97938bGuillaume Valadon            if "Destination" == line[:11]:
2412066af02d4144547e3531c16a5ede1340b97938bGuillaume Valadon                got_header = True
2422066af02d4144547e3531c16a5ede1340b97938bGuillaume Valadon                mtu_present = "Mtu" in line
2432066af02d4144547e3531c16a5ede1340b97938bGuillaume Valadon                prio_present = "Prio" in line
244706c5f38284d9d57cc8f7a957b8577513b97bc16Phil            continue
2452066af02d4144547e3531c16a5ede1340b97938bGuillaume Valadon
2462066af02d4144547e3531c16a5ede1340b97938bGuillaume Valadon        # Parse a route entry according to the operating system
2472066af02d4144547e3531c16a5ede1340b97938bGuillaume Valadon        splitted_line = line.split()
24835f97efc53839f6366918c251dba5df2ac553c8dGuillaume Valadon        if OPENBSD or NETBSD:
2492066af02d4144547e3531c16a5ede1340b97938bGuillaume Valadon            index = 5 + mtu_present + prio_present
2502066af02d4144547e3531c16a5ede1340b97938bGuillaume Valadon            if len(splitted_line) < index:
2512066af02d4144547e3531c16a5ede1340b97938bGuillaume Valadon                warning("Not enough columns in route entry !")
2522066af02d4144547e3531c16a5ede1340b97938bGuillaume Valadon                continue
2532066af02d4144547e3531c16a5ede1340b97938bGuillaume Valadon            destination, next_hop, flags = splitted_line[:3]
2542066af02d4144547e3531c16a5ede1340b97938bGuillaume Valadon            dev = splitted_line[index]
2552066af02d4144547e3531c16a5ede1340b97938bGuillaume Valadon        else:
2560d92fc9fbb58df9cc4bbeb007bf65020fe1aa092Victor Pfautz            # FREEBSD or DARWIN
2572066af02d4144547e3531c16a5ede1340b97938bGuillaume Valadon            if len(splitted_line) < 4:
2582066af02d4144547e3531c16a5ede1340b97938bGuillaume Valadon                warning("Not enough columns in route entry !")
2592066af02d4144547e3531c16a5ede1340b97938bGuillaume Valadon                continue
2602066af02d4144547e3531c16a5ede1340b97938bGuillaume Valadon            destination, next_hop, flags, dev = splitted_line[:4]
2612066af02d4144547e3531c16a5ede1340b97938bGuillaume Valadon
2622066af02d4144547e3531c16a5ede1340b97938bGuillaume Valadon        # Check flags
2632066af02d4144547e3531c16a5ede1340b97938bGuillaume Valadon        if not "U" in flags:  # usable route
2642066af02d4144547e3531c16a5ede1340b97938bGuillaume Valadon            continue
265ed95a6a3cea25583d339e564b7c93bdbcbbf9483Guillaume Valadon        if "R" in flags:  # Host or net unreachable
266706c5f38284d9d57cc8f7a957b8577513b97bc16Phil            continue
2672066af02d4144547e3531c16a5ede1340b97938bGuillaume Valadon        if "m" in flags:  # multicast address
2682066af02d4144547e3531c16a5ede1340b97938bGuillaume Valadon            # Note: multicast routing is handled in Route6.route()
269706c5f38284d9d57cc8f7a957b8577513b97bc16Phil            continue
270706c5f38284d9d57cc8f7a957b8577513b97bc16Phil
2712066af02d4144547e3531c16a5ede1340b97938bGuillaume Valadon        # Replace link with the default route in next_hop
2722066af02d4144547e3531c16a5ede1340b97938bGuillaume Valadon        if "link" in next_hop:
2732066af02d4144547e3531c16a5ede1340b97938bGuillaume Valadon            next_hop = "::"
274706c5f38284d9d57cc8f7a957b8577513b97bc16Phil
2752066af02d4144547e3531c16a5ede1340b97938bGuillaume Valadon        # Default prefix length
2762066af02d4144547e3531c16a5ede1340b97938bGuillaume Valadon        destination_plen = 128
277706c5f38284d9d57cc8f7a957b8577513b97bc16Phil
2782066af02d4144547e3531c16a5ede1340b97938bGuillaume Valadon        # Extract network interface from the zone id
2792066af02d4144547e3531c16a5ede1340b97938bGuillaume Valadon        if '%' in destination:
2802066af02d4144547e3531c16a5ede1340b97938bGuillaume Valadon            destination, dev = destination.split('%')
2812066af02d4144547e3531c16a5ede1340b97938bGuillaume Valadon            if '/' in dev:
2822066af02d4144547e3531c16a5ede1340b97938bGuillaume Valadon                # Example: fe80::%lo0/64 ; dev = "lo0/64"
2832066af02d4144547e3531c16a5ede1340b97938bGuillaume Valadon                dev, destination_plen = dev.split('/')
2842066af02d4144547e3531c16a5ede1340b97938bGuillaume Valadon        if '%' in next_hop:
2852066af02d4144547e3531c16a5ede1340b97938bGuillaume Valadon            next_hop, dev = next_hop.split('%')
286706c5f38284d9d57cc8f7a957b8577513b97bc16Phil
2872066af02d4144547e3531c16a5ede1340b97938bGuillaume Valadon        # Ensure that the next hop is a valid IPv6 address
2882066af02d4144547e3531c16a5ede1340b97938bGuillaume Valadon        if not in6_isvalid(next_hop):
2892066af02d4144547e3531c16a5ede1340b97938bGuillaume Valadon            # Note: the 'Gateway' column might contain a MAC address
2902066af02d4144547e3531c16a5ede1340b97938bGuillaume Valadon            next_hop = "::"
2911ad4bbf39716a7c25161cb118f02344291e0c102Phil
2922066af02d4144547e3531c16a5ede1340b97938bGuillaume Valadon        # Modify parsed routing entries
2932066af02d4144547e3531c16a5ede1340b97938bGuillaume Valadon        # Note: these rules are OS specific and may evolve over time
2942066af02d4144547e3531c16a5ede1340b97938bGuillaume Valadon        if destination == "default":
2952066af02d4144547e3531c16a5ede1340b97938bGuillaume Valadon            destination, destination_plen = "::", 0
2962066af02d4144547e3531c16a5ede1340b97938bGuillaume Valadon        elif '/' in destination:
2972066af02d4144547e3531c16a5ede1340b97938bGuillaume Valadon            # Example: fe80::/10
2982066af02d4144547e3531c16a5ede1340b97938bGuillaume Valadon            destination, destination_plen = destination.split('/')
2992066af02d4144547e3531c16a5ede1340b97938bGuillaume Valadon        if '/' in dev:
3002066af02d4144547e3531c16a5ede1340b97938bGuillaume Valadon            # Example: ff02::%lo0/32 ; dev = "lo0/32"
3012066af02d4144547e3531c16a5ede1340b97938bGuillaume Valadon            dev, destination_plen = dev.split('/')
3021ad4bbf39716a7c25161cb118f02344291e0c102Phil
3032066af02d4144547e3531c16a5ede1340b97938bGuillaume Valadon        # Check route entries parameters consistency
3042066af02d4144547e3531c16a5ede1340b97938bGuillaume Valadon        if not in6_isvalid(destination):
3052066af02d4144547e3531c16a5ede1340b97938bGuillaume Valadon            warning("Invalid destination IPv6 address in route entry !")
3062066af02d4144547e3531c16a5ede1340b97938bGuillaume Valadon            continue
3072066af02d4144547e3531c16a5ede1340b97938bGuillaume Valadon        try:
3082066af02d4144547e3531c16a5ede1340b97938bGuillaume Valadon            destination_plen = int(destination_plen)
3092066af02d4144547e3531c16a5ede1340b97938bGuillaume Valadon        except:
3102066af02d4144547e3531c16a5ede1340b97938bGuillaume Valadon            warning("Invalid IPv6 prefix length in route entry !")
3112066af02d4144547e3531c16a5ede1340b97938bGuillaume Valadon            continue
3122066af02d4144547e3531c16a5ede1340b97938bGuillaume Valadon        if in6_ismlladdr(destination) or in6_ismnladdr(destination):
3132066af02d4144547e3531c16a5ede1340b97938bGuillaume Valadon            # Note: multicast routing is handled in Route6.route()
3142066af02d4144547e3531c16a5ede1340b97938bGuillaume Valadon            continue
3151ad4bbf39716a7c25161cb118f02344291e0c102Phil
31635f97efc53839f6366918c251dba5df2ac553c8dGuillaume Valadon        if LOOPBACK_NAME in dev:
3172066af02d4144547e3531c16a5ede1340b97938bGuillaume Valadon            # Handle ::1 separately
3182066af02d4144547e3531c16a5ede1340b97938bGuillaume Valadon            cset = ["::1"]
3192066af02d4144547e3531c16a5ede1340b97938bGuillaume Valadon            next_hop = "::"
3202066af02d4144547e3531c16a5ede1340b97938bGuillaume Valadon        else:
3212066af02d4144547e3531c16a5ede1340b97938bGuillaume Valadon            # Get possible IPv6 source addresses
3222066af02d4144547e3531c16a5ede1340b97938bGuillaume Valadon            devaddrs = filter(lambda x: x[2] == dev, lifaddr)
32335f97efc53839f6366918c251dba5df2ac553c8dGuillaume Valadon            cset = construct_source_candidate_set(destination, destination_plen, devaddrs, LOOPBACK_NAME)
3241ad4bbf39716a7c25161cb118f02344291e0c102Phil
3252066af02d4144547e3531c16a5ede1340b97938bGuillaume Valadon        if len(cset):
3262066af02d4144547e3531c16a5ede1340b97938bGuillaume Valadon            routes.append((destination, destination_plen, next_hop, dev, cset))
3272066af02d4144547e3531c16a5ede1340b97938bGuillaume Valadon
3282066af02d4144547e3531c16a5ede1340b97938bGuillaume Valadon    fd_netstat.close()
3292066af02d4144547e3531c16a5ede1340b97938bGuillaume Valadon    return routes
330