Add acl.get_network_list() + tests; Update docs to reference the ability to use prefixlist as a source/destination

This commit is contained in:
Pim van Pelt
2023-01-16 12:03:34 +00:00
parent a274fdc2af
commit 4e2354c3d8
3 changed files with 91 additions and 2 deletions

View File

@ -359,6 +359,39 @@ interfaces:
exact-match: False exact-match: False
``` ```
### Prefix Lists
This construct allows to enumerate a list of IPv4 or IPv6 host addresses and/or networks. Each
prefixlist has a name which consists of anywhere between 1 and 56 characters, and it must start
with a letter. The syntax is straight forward:
* ***description***: A string, no longer than 64 characters, and excluding the single quote '
and double quote ". This string is currently not used anywhere, and serves for enduser
documentation purposes.
* ***members***: A list of zero or more entries which can take the form:
* ***IPv4 Host***: an IPv4 address, eg. `192.0.2.1`
* ***IPv4 Prefix***: an IPv6 prefix, eg. `192.0.2.0/24`
* ***IPv6 Host***: an IPv4 address, eg. `2001:db8::1`
* ***IPv6 Prefix***: an IPv6 prefix, eg. `2001:db8::0/64`
***NOTE***: It is valid to have host addresses with prefixlen, for example `192.168.1.1/24`
in other words, the prefix can be either a network or a host.
A few examples:
```
prefixlists:
example:
description: "An example prefixlist with hosts and prefixes"
members:
- 192.0.2.1
- 192.0.2.0/24
- 2001:db8::1
- 2001:db8::/64
empty:
description: "An empty prefixlist"
members: []
```
### Access Control Lists ### Access Control Lists
In VPP, a common firewall function is provided by the `acl-plugin`. The anatomy of this plugin In VPP, a common firewall function is provided by the `acl-plugin`. The anatomy of this plugin
@ -377,8 +410,10 @@ packets then either perform an action of `permit` or `deny` (for stateless) or `
* ***family***: Which IP address family to match, can be either `ipv4`, or `ipv6` or `any`, * ***family***: Which IP address family to match, can be either `ipv4`, or `ipv6` or `any`,
which is the default. If `any` is used, this term will also operate on any source and which is the default. If `any` is used, this term will also operate on any source and
destination addresses, and it will emit two ACEs, one for each address family. destination addresses, and it will emit two ACEs, one for each address family.
* ***source***: The IPv4 or IPv6 source prefix, eg. `192.0.2.0/24` or `2001:db8::/64`. If * ***source***: Either an IPv4 or IPv6 host (without prefixlen, eg. `192.0.2.1` or
left empty, this means any (ie. `0.0.0.0/0` or `::/0`). `2001:db8::1`), an IPv4 or IPv6 prefix (with prefixlen, eg. `192.0.2.0/24` or
`2001:db8::/64`), or a reference to the name of an existing _prefixlist_ (eg. `trusted`).
If left empty, this means all IPv4 and IPv6 (ie. `[ 0.0.0.0/0, ::/0 ]`).
* ***destination***: Similar to `source`, but for the destination field of the packets. * ***destination***: Similar to `source`, but for the destination field of the packets.
* ***protocol***: The L4 protocol, can be either a numeric value (eg. `6`), or a symbolic * ***protocol***: The L4 protocol, can be either a numeric value (eg. `6`), or a symbolic
string value from `/etc/protocols` (eg. `tcp`). If omitted, only L3 matches are performed. string value from `/etc/protocols` (eg. `tcp`). If omitted, only L3 matches are performed.

View File

@ -15,6 +15,7 @@
import logging import logging
import socket import socket
import ipaddress import ipaddress
from . import prefixlist
def get_acls(yaml): def get_acls(yaml):
@ -151,6 +152,32 @@ def get_port_low_high(portstring):
return None, None return None, None
def is_ip(ip_string):
"""Return True if the given ip_string is either an IPv4/IPv6 address or prefix."""
if not isinstance(ip_string, str):
return False
try:
ipn = ipaddress.ip_network(ip_string, strict=False)
return True
except:
pass
return False
def get_network_list(yaml, network_string):
"""Return the full list of source or destination address(es). This function resolves the
'source' or 'destination' field, which can either be an IP address, a Prefix, or the name
of a Prefix List. It returns a list of ip_network() objects, including prefix. IP addresses
will receive prefixlen /32 or /128."""
if is_ip(network_string):
ipn = ipaddress.ip_network(network_string, strict=False)
return [ipn]
return prefixlist.get_network_list(yaml, network_string)
def get_protocol(protostring): def get_protocol(protostring):
"""For a given protocol string, which can be either an integer or a symbolic port """For a given protocol string, which can be either an integer or a symbolic port
name in /etc/protocols, return the protocol number as integer, or None if it cannot name in /etc/protocols, return the protocol number as integer, or None if it cannot

View File

@ -113,3 +113,30 @@ class TestACLMethods(unittest.TestCase):
lo, hi = acl.get_icmp_low_high("10-20") lo, hi = acl.get_icmp_low_high("10-20")
self.assertEqual(10, lo) self.assertEqual(10, lo)
self.assertEqual(20, hi) self.assertEqual(20, hi)
def test_is_ip(self):
self.assertTrue(acl.is_ip("192.0.2.1"))
self.assertTrue(acl.is_ip("192.0.2.1/24"))
self.assertTrue(acl.is_ip("192.0.2.0/24"))
self.assertTrue(acl.is_ip("2001:db8::1"))
self.assertTrue(acl.is_ip("2001:db8::1/64"))
self.assertTrue(acl.is_ip("2001:db8::/64"))
self.assertFalse(acl.is_ip(True))
self.assertFalse(acl.is_ip("String"))
self.assertFalse(acl.is_ip([]))
self.assertFalse(acl.is_ip({}))
def test_get_network_list(self):
for s in ["192.0.2.1", "192.0.2.1/24", "2001:db8::1", "2001:db8::1/64"]:
l = acl.get_network_list(self.cfg, s)
self.assertIsInstance(l, list)
self.assertEquals(1, len(l))
n = l[0]
l = acl.get_network_list(self.cfg, "trusted")
self.assertIsInstance(l, list)
self.assertEquals(4, len(l))
l = acl.get_network_list(self.cfg, "pl-notexist")
self.assertIsInstance(l, list)
self.assertEquals(0, len(l))