diff --git a/docs/config-guide.md b/docs/config-guide.md index f4838b9..947fad4 100644 --- a/docs/config-guide.md +++ b/docs/config-guide.md @@ -358,3 +358,79 @@ interfaces: dot1q: 200 exact-match: False ``` + +### Access Control Lists + +In VPP, a common firewall function is provided by the `acl-plugin`. The anatomy of this plugin +is as follows. First, an ACL consists of one or more Access Control Elements or `ACE`s. These +can match on IPv4 or IPv6 source/destination, an IP protocol, and then for TCP/UDP a range +of source- and destination ports, and for ICMP a range of ICMP type and codes. Any matching +packets then either perform an action of `permit` or `deny` (for stateless) or `permit+reflect` +(stateful). The full syntax is as follows: + +* ***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. +* ***terms***: A list of Access Control Elements: + * ***action***: What to do upon match, can be either `permit`, `deny` or `permit+reflect`. + This is the only required field. + * ***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 + 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 + left empty, this means any (ie. `0.0.0.0/0` or `::/0`). + * ***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 + string value from `/etc/protocols` (eg. `tcp`). If omitted, only L3 matches are performed. + * ***source-port***: When `TCP` or `UDP` are specified, this field specified which source + port(s) are matched. It can be either a numeric value (eg. `80`), a symbolic string value + from `/etc/services` (eg. `www`), a numeric range with start and/or end ranges (eg. `-1024` + for all ports from 0-1024 inclusive; or `1024-` for all ports from 1024-65535 inclusive, + or an actual range `49152-65535`). The default keyword `any` is also permitted, which results + in range `0-65535`, and is the default if the field is not specified. + * ***destination-port***: Similar to `source-port` but for the destination port field in the + `TCP` or `UDP` header. + * ***icmp-type***: It can be either a numeric value (eg. `3`), a numeric range with start + and/or end ranges (eg. `-10` for all types from 0-10 inclusive; or `10-` for all types from + 10-255 inclusive, or an actual range `10-15`). The default keyword `any` is also permitted, + which results in range `0-255`, and is the default if the field is not specified. This field + can only be specified if the `protocol` field is `icmp` (or `1`). + * ***icmp-code***: Similar to `icmp-type` but for the ICMP code field. This field can only be + specified if the `protocol` field is `icmp` (or `1`). + +An example ACL with three ACE terms: +``` +acls: + acl01: + description: "Test ACL" + terms: + - description: "Allow a specific IPv6 TCP flow" + action: permit + source: 2001:db8::/64 + destination: 2001:db8:1::/64 + protocol: tcp + destination-port: www + source-port: "1024-65535" + - description: "Allow IPv4 ICMP Destination Unreachable, any code" + family: ipv4 + action: permit + protocol: icmp + icmp-type: 3 + icmp-code: any + - description: "Deny any IPv4 or IPv6" + action: deny +``` + +One or more of these ACLs are then applied to an interface in either the `input` or the `output` +direction: + +``` +interfaces: + GigabitEthernet3/0/0: + acls: + input: acl01 + output: [ acl02, acl03 ] +``` +The configuration here is tolerant of either a singleton (a literal string referring to the one +ACL that must be applied), or a _list_ of strings to more than one ACL, in which case they will +be tested in order (with a first-match return value). diff --git a/vppcfg/example.yaml b/vppcfg/example.yaml index 85578b8..a7700f5 100644 --- a/vppcfg/example.yaml +++ b/vppcfg/example.yaml @@ -117,3 +117,23 @@ taps: name: vpp-tap101 mtu: 1500 bridge: br1 + +acls: + acl01: + description: "Test ACL" + terms: + - description: "Allow a specific IPv6 TCP flow" + action: permit + source: 2001:db8::/64 + destination: 2001:db8:1::/64 + protocol: tcp + destination-port: www + source-port: "1024-65535" + - description: "Allow IPv4 ICMP Destination Unreachable, any code" + family: ipv4 + action: permit + protocol: icmp + icmp-type: 3 + icmp-code: any + - description: "Deny any IPv4 or IPv6" + action: deny diff --git a/vppcfg/schema.yaml b/vppcfg/schema.yaml index 2baadb6..2c5e739 100644 --- a/vppcfg/schema.yaml +++ b/vppcfg/schema.yaml @@ -4,6 +4,7 @@ loopbacks: map(include('loopback'),key=str(matches='loop[0-9]+'),required=False) bridgedomains: map(include('bridgedomain'),key=str(matches='bd[0-9]+'),required=False) vxlan_tunnels: map(include('vxlan'),key=str(matches='vxlan_tunnel[0-9]+'),required=False) taps: map(include('tap'),key=str(matches='tap[0-9]+'),required=False) +acls: map(include('acl'),key=str(matches='[a-z][a-z0-9\-]+'),required=False) --- vxlan: description: str(exclude='\'"',len=64,required=False) @@ -79,3 +80,24 @@ tap: namespace-create: bool(required=False) rx-ring-size: int(min=8,max=32768,required=False) tx-ring-size: int(min=8,max=32768,required=False) +--- +# Valid: 80 "www" "-1024" "1024-" "1024-65535", and "any" +acl-term-port-int-range-symbolic: any(int(min=1,max=65535),str(equals="any"),regex('^([1-9][0-9]*-|-[1-9][0-9]*|[1-9][0-9]*-[1-9][0-9]*)$'),regex('^[a-z][a-z0-9-]*$')) +# Valid: 80 "-245" "10-" "10-245", and "any" +acl-term-icmp-int-range: any(int(min=0,max=255),str(equals="any"),regex('^([0-9]+-|-[1-9][0-9]*|[0-9]*-[1-9][0-9]*)$')) +--- +acl-term: + description: str(exclude='\'"',len=64,required=False) + action: enum('permit','deny','permit+reflect') + family: enum('ipv4','ipv6','any',required=False) + source: ip_interface(required=False) + destination: ip_interface(required=False) + protocol: any(int(min=1,max=255),regex('^[a-z][a-z0-9-]*$'),required=False) + source-port: include('acl-term-port-int-range-symbolic', required=False) + destination-port: include('acl-term-port-int-range-symbolic', required=False) + icmp-type: include('acl-term-icmp-int-range',required=False) + icmp-code: include('acl-term-icmp-int-range',required=False) +--- +acl: + description: str(exclude='\'"',len=64,required=False) + terms: list(include('acl-term'), min=1, max=100, required=True)