Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Traffic ipv6 translation support #7981

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 35 additions & 11 deletions src/opnsense/scripts/interfaces/traffic_top.py
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,14 @@ def local_addresses():
result.append(ip)
return result

def ipv6_gateways():
result = {}
sp = subprocess.run(['/usr/local/opnsense/scripts/routes/ipv6_gateways.php'], capture_output=True, text=True)
for line in sp.stdout.strip().split('\n'):
subnet, name = line.split('\t')
result[subnet] = name
return result

def from_bformat(value):
value = value.lower()
if value.endswith('kb'):
Expand All @@ -85,6 +93,7 @@ def to_bformat(value):
class AsyncLookup:
def __init__(self):
self._results = {}
self.all_ipv6_gateways = ipv6_gateways()

async def request_ittr(self, addresses):
self._results = {}
Expand All @@ -94,33 +103,48 @@ async def request_ittr(self, addresses):
return
dnsResolver.timeout = 2
tasks = []
address_mapping = {}

for address in addresses:
try:
if ":" in address:
ipv6_obj = ipaddress.IPv6Address(address)
reverse_ip = '.'.join(reversed(ipv6_obj.exploded.replace(":", ""))) + ".ip6.arpa"
matched_gateway = self.get_gateway_for_ipv6(ipv6_obj)
if matched_gateway:
last_part = address.split(":")[-1]
self._results[address] = f"{last_part}@{matched_gateway}"
else:
reverse_ip = '.'.join(reversed(ipv6_obj.exploded.replace(":", ""))) + ".ip6.arpa"
tasks.append(dnsResolver.resolve(reverse_ip, "PTR"))
address_mapping[reverse_ip] = address
else:
reverse_ip = '.'.join(reversed(address.split("."))) + ".in-addr.arpa"
tasks.append(dnsResolver.resolve(reverse_ip, "PTR"))
address_mapping[reverse_ip] = address

tasks.append(dnsResolver.resolve(reverse_ip, "PTR"))
except Exception as e:
return
except Exception:
continue

responses = await asyncio.gather(*tasks, return_exceptions=True)

for response, address in zip(responses, addresses):
for response, reverse_ip in zip(responses, address_mapping.keys()):
original_address = address_mapping[reverse_ip]
if isinstance(response, dns.resolver.Answer):
if ":" in address:
addr = str(ipaddress.IPv6Address(address))
else:
addr = ".".join(reversed(response.canonical_name.to_text().replace('.in-addr.arpa.', '').split('.')))

for item in response.response.answer:
if isinstance(item, dns.rrset.RRset) and len(item.items) > 0:
self._results[addr] = str(list(item.items)[0])
self._results[original_address] = str(list(item.items)[0])

return self._results

def get_gateway_for_ipv6(self, ipv6_address):
for subnet, gateway in self.all_ipv6_gateways.items():
network = ipaddress.IPv6Network(subnet)
if network.prefixlen == 128:
continue
if network.supernet_of(ipaddress.IPv6Network(ipv6_address.exploded + "/128")):
return gateway
return None

def collect(self, addresses):
loop = asyncio.new_event_loop()
asyncio.set_event_loop(loop)
Expand Down
21 changes: 21 additions & 0 deletions src/opnsense/scripts/routes/ipv6_gateways.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
#!/usr/local/bin/php
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we would rather fold this special case return into an existing script. duplicating functionality for a single IP family is not ideal and we can easily build a plugin-based wrapper that can be called with pluginctl.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please point me in the right direction, where you want it, I'm not familiar with this.
I was trying to fetch the info on gateways directly from system, but gateway names are not there. So they are extracted from possibly non-applied config instead.

<?php

/*
* Returns static IPv6 routes with gateway names in TSV format.
* It is used for approximate IPv6 translation of dynamic addresses in traffic view (traffic_top.py).
*/
require_once 'config.inc';
require_once 'util.inc';

$allRoutes = get_staticroutes();
$allSubnets = [];
foreach ($allRoutes as $route) {
if (str_contains($route['network'], ':') && is_subnet($route['network'])) {
$allSubnets[$route['network']] = $route['gateway'];
}
}

foreach ($allSubnets as $network => $gateway) {
echo $network . "\t" . $gateway . "\n";
}