Skip to content

Commit

Permalink
Workaround to ignore interfaces name truncated by netstat
Browse files Browse the repository at this point in the history
When interface names are longer than 7 characters, netstat will truncate
them to the first 7 chars which leads to looking up an invalid interface
name.
We attempt to guess the name of the interface based on the output of
ifconfig -l. If there is only one candidate we consider it, otherwise we
ignore it.
  • Loading branch information
guedou authored and p-l- committed Aug 12, 2016
1 parent 004b9f8 commit 02ae08b
Show file tree
Hide file tree
Showing 2 changed files with 89 additions and 2 deletions.
35 changes: 33 additions & 2 deletions scapy/arch/unix.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
Common customizations for all Unix-like operating systems other than Linux
"""

from __future__ import with_statement

import sys,os,struct,socket,time
from fcntl import ioctl
import socket
Expand All @@ -24,6 +26,21 @@
## Routes stuff ##
##################

def _guess_iface_name(netif):
"""
We attempt to guess the name of interfaces that are truncated from the
output of ifconfig -l.
If there is only one possible candidate matching the interface name then we
return it.
If there are none or more, then we return None.
"""
with os.popen('%s -l' % conf.prog.ifconfig) as fdesc:
ifaces = fdesc.readline().strip().split(' ')
matches = [iface for iface in ifaces if iface.startswith(netif)]
if len(matches) == 1:
return matches[0]
return None


def read_routes():
if scapy.arch.SOLARIS:
Expand Down Expand Up @@ -81,8 +98,22 @@ def read_routes():
if not "G" in flg:
gw = '0.0.0.0'
if netif is not None:
ifaddr = scapy.arch.get_if_addr(netif)
routes.append((dest,netmask,gw,netif,ifaddr))
try:
ifaddr = scapy.arch.get_if_addr(netif)
routes.append((dest,netmask,gw,netif,ifaddr))
except OSError as exc:
if exc.message == 'Device not configured':
# This means the interface name is probably truncated by
# netstat -nr. We attempt to guess it's name and if not we
# ignore it.
guessed_netif = _guess_iface_name(netif)
if guessed_netif is not None:
ifaddr = scapy.arch.get_if_addr(guessed_netif)
routes.append((dest, netmask, gw, guessed_netif, ifaddr))
else:
warning("Could not guess partial interface name: %s" % netif)
else:
raise
else:
pending_if.append((dest,netmask,gw))
f.close()
Expand Down
56 changes: 56 additions & 0 deletions test/regression.uts
Original file line number Diff line number Diff line change
Expand Up @@ -4554,6 +4554,62 @@ assert isinstance(pkt, UDP) and pkt.dport == 5353
pkt = pkt.payload
assert isinstance(pkt, DNS) and isinstance(pkt.payload, NoPayload)

+ Mocked read_routes() calls

= Truncated netstat -rn output on OS X

import mock
import StringIO

@mock.patch("scapy.arch.get_if_addr")
@mock.patch("scapy.arch.unix.os")
def test_osx_netstat_truncated(mock_os, mock_get_if_addr):
"""Test read_routes() on OS X 10.? with a long interface name"""
# netstat & ifconfig outputs from https://github.com/secdev/scapy/pull/119
netstat_output = """
Routing tables

Internet:
Destination Gateway Flags Refs Use Netif Expire
default 192.168.1.1 UGSc 460 0 en1
default link#11 UCSI 1 0 bridge1
127 127.0.0.1 UCS 1 0 lo0
127.0.0.1 127.0.0.1 UH 10 2012351 lo0
"""
ifconfig_output = "lo0 en1 bridge10\n"
# Mocked file descriptors
def se_popen(command):
"""Perform specific side effects"""
if command == "netstat -rn":
return StringIO.StringIO(netstat_output)
elif command == "ifconfig -l":
ret = StringIO.StringIO(ifconfig_output)
def unit():
return ret
ret.__call__ = unit
ret.__enter__ = unit
ret.__exit__ = lambda x,y,z: None
return ret
raise Exception("Command not mocked: %s" % command)
mock_os.popen.side_effect = se_popen
# Mocked get_if_addr() behavior
def se_get_if_addr(iface):
"""Perform specific side effects"""
if iface == "bridge1":
oserror_exc = OSError()
oserror_exc.message = "Device not configured"
raise oserror_exc
return "1.2.3.4"
mock_get_if_addr.side_effect = se_get_if_addr
# Test the function
from scapy.arch.unix import read_routes
routes = read_routes()
assert(len(routes) == 4)
assert([r for r in routes if r[3] == "bridge10"])


test_osx_netstat_truncated()

+ Mocked read_routes6() calls

= Preliminary definitions
Expand Down

0 comments on commit 02ae08b

Please sign in to comment.