Merge pull request #1037 from gpotter2/win-metrics-fix

[Windows] Guess metrics when InterfaceMetric is not available
This commit is contained in:
Pierre Lalet 2018-01-12 15:41:18 +01:00 committed by GitHub
commit 7ce1152b25
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 63 additions and 22 deletions

View File

@ -843,8 +843,29 @@ def read_routes():
warning("No default IPv4 routes found. Your Windows release may no be supported and you have to enter your routes manually", onlyOnce=True)
return routes
def _get_metrics(ipv6=False):
"""Returns a dict containing all IPv4 or IPv6 interfaces' metric,
ordered by their interface index.
"""
query_cmd = "netsh interface " + ("ipv6" if ipv6 else "ipv4") + " show interfaces level=verbose"
stdout = POWERSHELL_PROCESS.query([query_cmd])
res = {}
_buffer = []
_pattern = re.compile(".*:\s+(\d+)")
for _line in stdout:
if not _line.strip():
continue
_buffer.append(_line)
if len(_buffer) == 32: # An interface, with all its parameters, is 32 lines long
if_index = re.search(_pattern, _buffer[3]).group(1)
if_metric = int(re.search(_pattern, _buffer[5]).group(1))
res[if_index] = if_metric
_buffer = []
return res
def _read_routes_post2008():
routes = []
if4_metrics = None
# This works only starting from Windows 8/2012 and up. For older Windows another solution is needed
# Get-NetRoute -AddressFamily IPV4 | select ifIndex, DestinationPrefix, NextHop, RouteMetric, InterfaceMetric | fl
for line in exec_query(['Get-NetRoute', '-AddressFamily IPV4'], ['ifIndex', 'DestinationPrefix', 'NextHop', 'RouteMetric', 'InterfaceMetric']):
@ -861,8 +882,14 @@ def _read_routes_post2008():
# continue
dest, mask = line[1].split('/')
ip = "127.0.0.1" if line[0] == "1" else iface.ip # Force loopback on iface 1
if not line[4].strip(): # InterfaceMetric is not available. Load it from netsh
if not if4_metrics:
if4_metrics = _get_metrics()
metric = int(line[3]) + if4_metrics.get(iface.win_index, 0) # RouteMetric + InterfaceMetric
else:
metric = int(line[3]) + int(line[4]) # RouteMetric + InterfaceMetric
routes.append((atol(dest), itom(int(mask)),
line[2], iface, ip, int(line[3])+int(line[4])))
line[2], iface, ip, metric))
return routes
############
@ -918,33 +945,13 @@ def _read_routes6_post2008():
_append_route6(routes6, dpref, dp, nh, iface, lifaddr, metric)
return routes6
def _get_i6_metric():
"""Returns a dict containing all IPv6 interfaces' metric,
ordered by their interface index.
"""
query_cmd = "netsh interface ipv6 show interfaces level=verbose"
stdout = POWERSHELL_PROCESS.query([query_cmd])
res = {}
_buffer = []
_pattern = re.compile(".*:\s+(\d+)")
for _line in stdout:
if not _line.strip():
continue
_buffer.append(_line)
if len(_buffer) == 32: # An interface, with all its parameters, is 32 lines long
if_index = re.search(_pattern, _buffer[3]).group(1)
if_metric = int(re.search(_pattern, _buffer[5]).group(1))
res[if_index] = if_metric
_buffer = []
return res
def _read_routes6_7():
# Not supported in powershell, we have to use netsh
routes = []
query_cmd = "netsh interface ipv6 show route level=verbose"
stdout = POWERSHELL_PROCESS.query([query_cmd])
lifaddr = in6_getifaddr()
if6_metrics = _get_i6_metric()
if6_metrics = _get_metrics(ipv6=True)
# Define regexes
r_int = [".*:\s+(\d+)"]
r_all = ["(.*)"]

View File

@ -158,6 +158,40 @@ InterfaceMetric : 256
test_read_routes6_windows()
= Test _read_routes_post2008 with missing InterfaceMetric
from scapy.arch.windows import _read_routes_post2008
@mock.patch("scapy.arch.windows._get_metrics")
@mock.patch("scapy.arch.windows.POWERSHELL_PROCESS.query")
@mock.patch("scapy.arch.windows.get_if_list")
@mock.patch("scapy.arch.windows.dev_from_index")
def test_missing_ifacemetric(mock_dev_from_index, mock_winpcapylist, mock_exec_query, mock_get_metrics):
exc_query_output = """ifIndex : 3
DestinationPrefix : 255.255.255.255/0
NextHop : 192.168.103.1
RouteMetric : 10
InterfaceMetric : 256
ifIndex : 13
DestinationPrefix : 255.255.255.255/32
NextHop : 0.0.0.0
RouteMetric : 20
InterfaceMetric :
"""
mock_exec_query.side_effect = lambda *args, **kargs: exc_query_output.split("\n")
mock_winpcapylist.return_value = [u'\\Device\\NPF_{0EC72537-B662-4F5D-B34E-48BFAE799BBE}', u'\\Device\\NPF_{C56DFFB3-992C-4964-B000-3E7C0F76E8BA}']
mock_dev_from_index.side_effect = dev_from_index_custom
mock_get_metrics.side_effect = lambda: {'16': 0, '13': 123}
routes = _read_routes_post2008()
for r in routes:
print(r)
assert len(routes) == 2
# Test if metrics were correctly read/guessed
assert routes[0][5] == 266
assert routes[1][5] == 143
test_missing_ifacemetric()
############
############