Skip to content

Commit f6f7c32

Browse files
committed
fixes for opnsense ip-ranges and routes edge-case
1 parent 21f1b68 commit f6f7c32

File tree

3 files changed

+81
-17
lines changed

3 files changed

+81
-17
lines changed

src/firewall_test/plugins/translate/opnsense/interfaces.py

100644100755
Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,13 @@ def _parse_ni(raw: dict) -> dict:
3535
ips.extend(raw.get('ipv6', []))
3636
for ip_cnf in ips:
3737
ip_cidr = ip_cnf['ipaddr']
38-
ip, cidr = ip_cidr.split('/', 1)
38+
if ip_cidr.find('/') == -1:
39+
ip = ip_cidr
40+
cidr = str(ip_cnf.get('subnetbits', '32'))
41+
42+
else:
43+
ip, cidr = ip_cidr.split('/', 1)
44+
3945
ip = ip_address(ip)
4046
if cidr in ['32', '128']:
4147
net = None

src/firewall_test/plugins/translate/opnsense/routes.py

100644100755
Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,13 @@ def _parse_routes(raw: dict) -> list[dict]:
3333
ni = raw['identifier']
3434
gws = raw.get('gateways', [])
3535
routes = []
36-
src_pref_ip4, src_pref_ip6 = raw['config'].get('ipaddr', None), raw['config'].get('ipaddrv6', None)
36+
src_pref_ip4, src_pref_ip6 = None, None
37+
if 'config' in raw:
38+
src_pref_ip4 = raw['config'].get('ipaddr', None)
39+
src_pref_ip6 = raw['config'].get('ipaddrv6', None)
40+
if src_pref_ip6 is not None and src_pref_ip6.find(':') == -1:
41+
src_pref_ip6 = None # track6 ?!
42+
3743
gw_ip4, gw_ip6 = None, None
3844
default_ip4 = False
3945

src/firewall_test/plugins/translate/opnsense/ruleset.py

100644100755
Lines changed: 67 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
from threading import Lock
2-
from ipaddress import ip_network
2+
from ipaddress import ip_network, summarize_address_range, ip_address
33
import xml.etree.ElementTree as ET
44
from xml.etree.ElementTree import Element
55

@@ -74,13 +74,22 @@ def _validate_hooks(self):
7474

7575

7676
def split_csv(value: str) -> list:
77+
if value is None:
78+
return []
79+
7780
return [v for v in value.split(',') if v.strip() != '']
7881

7982

83+
def split_nlsv(value: list) -> list:
84+
out = []
85+
for v in value:
86+
out.extend([v2.strip() for v2 in v.split('\n')])
87+
88+
return out
89+
90+
8091
def xml_to_dict(element: Element) -> dict:
81-
out = {}
82-
if hasattr(element, 'uuid'):
83-
out['uuid'] = element.uuid
92+
out = {'uuid': element.get('uuid', None)}
8493

8594
for c in element:
8695
if len(list(c)) > 0:
@@ -160,10 +169,18 @@ def _parse_rule_address(self, value: str) -> (list, None):
160169

161170
for v in values:
162171
if v in self.aliases:
172+
if self.aliases[v] is None:
173+
log_warn('Firewall Plugin', f'Unable to parse rule-address: "{value}"')
174+
return None
175+
163176
out.extend(self.aliases[v])
164177
continue
165178

166-
if f'{v}ip' in self.nis_ips:
179+
if v in self.nis_ips:
180+
out.extend(self.nis_ips[v])
181+
continue
182+
183+
if v in self.nis_nets:
167184
out.extend(self.nis_nets[v])
168185
continue
169186

@@ -182,9 +199,17 @@ def _parse_rule_network(self, value: str) -> (list, None):
182199

183200
for v in values:
184201
if v in self.aliases:
202+
if self.aliases[v] is None:
203+
log_warn('Firewall Plugin', f'Unable to parse rule-network: "{value}"')
204+
return None
205+
185206
out.extend(self.aliases[v])
186207
continue
187208

209+
if v in self.nis_ips:
210+
out.extend(self.nis_ips[v])
211+
continue
212+
188213
if v in self.nis_nets:
189214
out.extend(self.nis_nets[v])
190215
continue
@@ -258,8 +283,10 @@ def _parse_rules_old(self):
258283
else:
259284
chain = self.chain_ni
260285

261-
build['uuid'] = rule.get('uuid', None)
262286
build['desc'] = rule.get('descr', None)
287+
build['uuid'] = rule.get('uuid', None)
288+
if build['uuid'] is None:
289+
continue
263290

264291
### SEQUENCE ###
265292
nr += 1
@@ -278,6 +305,7 @@ def _parse_rules_old(self):
278305

279306
### RESOLVE NETWORK-INTERFACE GROUPS ###
280307
build['nis'] = []
308+
build['ni_direction'] = rule.get('direction', 'any')
281309
for ni in nis:
282310
if ni in self.ni_grp:
283311
build['nis'].extend(self.ni_grp[ni])
@@ -376,18 +404,27 @@ def _parse_local_ips(self):
376404
ip = p.get(ipp, None)
377405
cidr = p.get(subnet, '32')
378406
if ip is not None and ip.strip() != '':
379-
nets.append(ip_network(f'{ip}/{cidr}', strict=False))
380-
ip = ip_network(ip)
381-
self.local_ips.append(ip)
382-
ips.append(ip)
407+
try:
408+
nets.append(ip_network(f'{ip}/{cidr}', strict=False))
409+
410+
except ValueError:
411+
log_warn('Firewall Plugin', f'Unable to parse IP: "{ip}/{cidr}"')
412+
413+
try:
414+
ip = ip_network(ip)
415+
self.local_ips.append(ip)
416+
ips.append(ip)
417+
418+
except ValueError:
419+
log_warn('Firewall Plugin', f'Unable to parse IP: "{ip}"')
383420

384-
self.nis_ips[ni.tag] = ips
421+
self.nis_ips[f'{ni.tag}ip'] = ips
385422
self.nis_nets[ni.tag] = nets
386423

387424
vips_raw = self.raw.getroot().find(XML_ELEMENT_VIPS)
388425
for vip in vips_raw:
389426
p = xml_to_dict(vip)
390-
self.local_ips.append(ip_network(f"{p['subnet']}/{p['subnet_bits']}"))
427+
self.local_ips.append(ip_network(p['subnet'], strict=False))
391428

392429
### ALIASES ###
393430

@@ -446,7 +483,7 @@ def _resolve_alias_dns(self, aliases: dict):
446483
parallel=DNS_RESOLVE_THREADS,
447484
)
448485

449-
def _parse_alias_iplist_plain(self, url: str) -> list[str]:
486+
def _parse_alias_iplist_plain(self, url: str) -> (list[str], None):
450487
content = []
451488
res = self._download_alias_iplist(url)
452489
if res is None:
@@ -479,6 +516,7 @@ def _parse_alias_iplist_plain(self, url: str) -> list[str]:
479516

480517
if len(content) == 0:
481518
log_warn('Firewall Plugin', f'Alias-type "urltable" resulted in empty list: "{url}"')
519+
return None
482520

483521
return content
484522

@@ -525,11 +563,16 @@ def _parse_aliases(self):
525563
self.aliases[name] = ports
526564

527565
elif t == 'network':
528-
self.aliases[name] = [ip_network(n) for n in p['content']]
566+
try:
567+
self.aliases[name] = [ip_network(n) for n in split_nlsv(p['content'])]
568+
569+
except ValueError:
570+
invalid = True
571+
possible_nested[name] = p['content']
529572

530573
elif t == 'host':
531574
content = []
532-
for c in p['content']:
575+
for c in split_nlsv(p['content']):
533576
if valid_domain(c):
534577
if c in self._dns_cache:
535578
content.extend(self._dns_cache[c])
@@ -541,6 +584,15 @@ def _parse_aliases(self):
541584
invalid = False
542585
for c in content:
543586
try:
587+
if c.find('-') != -1:
588+
ip_range1, ip_range2 = c.split('-', 1)
589+
range_nets = list(summarize_address_range(
590+
ip_address(ip_range1),
591+
ip_address(ip_range2),
592+
))
593+
nets.extend(range_nets)
594+
continue
595+
544596
nets.append(ip_network(c))
545597

546598
except ValueError:

0 commit comments

Comments
 (0)