| // Copyright 2023 The Fuchsia Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| library fuchsia.net.filter; |
| |
| using fuchsia.hardware.network; |
| using fuchsia.net; |
| using fuchsia.net.interfaces; |
| |
| const MAX_NAME_LEN uint8 = 255; |
| |
| /// A unique identifier for a [`Namespace`]. |
| alias NamespaceId = string:MAX_NAME_LEN; |
| |
| /// A namespace. |
| /// |
| /// A namespace is a scoped collection of filtering state, specifically |
| /// [`Routine`]s. It is analogous to a table in Netfilter. |
| type Namespace = table { |
| /// The identifier of the namespace. |
| /// |
| /// Must be unique within the scope of the controller in which the namespace |
| /// is created. |
| 1: id NamespaceId; |
| /// The domain (or domains) in which the namespace operates. |
| 2: domain @generated_name("Domain") flexible enum { |
| IPV4 = 1; |
| IPV6 = 2; |
| ALL_IP = 3; |
| }; |
| }; |
| |
| /// A unique identifier for a [`Routine`]. |
| type RoutineId = struct { |
| /// The namespace in which the routine is installed. |
| namespace NamespaceId; |
| /// The name of the routine. |
| /// |
| /// Must be unique within the namespace in which the routine is created. |
| name string:MAX_NAME_LEN; |
| }; |
| |
| type Empty = struct {}; |
| |
| /// The priority of the routine relative to other routines installed on the same |
| /// hook. For a given packet traversing a given hook, all installed routines are |
| /// executed in order of priority (stopping early only if a terminal action is |
| /// hit). |
| /// |
| /// If two routines are installed with the same priority on the same hook, the |
| /// routine that was installed earlier will be evaluated first. |
| alias Priority = int32; |
| |
| /// A routine. |
| /// |
| /// A routine is a sequence of [`Rule`]s. It is analogous to a chain in |
| /// Netfilter. |
| type Routine = table { |
| /// The ID of the routine. |
| /// |
| /// Must be unique within the scope of the namespace in which the routine is |
| /// created. |
| 1: id RoutineId; |
| /// The type of the routine. |
| /// |
| /// IP routines can only include rules with ordinary filter actions, whereas |
| /// NAT routines can also include rules with NAT actions. |
| /// |
| /// Note that NAT routines are only executed *once* for a given connection, |
| /// for the first packet in the flow. |
| 2: type @generated_name("RoutineType") flexible union { |
| 1: ip @generated_name("IpRoutine") table { |
| /// Installed routines are evaluated iff a packet hits the hook on |
| /// which it is installed. |
| /// |
| /// Uninstalled routines are useful for organizational purposes and |
| /// are only traversed when jumped to from another routine. |
| /// |
| /// If left unset, will be an uninstalled routine. |
| 1: installation @generated_name("InstalledIpRoutine") table { |
| /// The hook on which the routine is installed. |
| /// * The `INGRESS` hook occurs for incoming traffic before a |
| /// routing decision has been made. |
| /// * The `LOCAL_INGRESS` hook occurs for incoming traffic that |
| /// is destined for the local host. |
| /// * The `FORWARDING` hook occurs for incoming traffic that is |
| /// destined for another node. |
| /// * The `LOCAL_EGRESS` hook occurs for locally-generated |
| /// traffic before a final routing decision has been made. |
| /// * The `EGRESS` hook occurs for all outgoing traffic after a |
| /// routing decision has been made. |
| /// |
| /// Required. |
| 1: hook @generated_name("IpInstallationHook") flexible enum { |
| INGRESS = 1; |
| LOCAL_INGRESS = 2; |
| FORWARDING = 3; |
| LOCAL_EGRESS = 4; |
| EGRESS = 5; |
| }; |
| /// The priority of the routine relative to other routines |
| /// installed on the same hook. |
| /// |
| /// Interpreted as `DEFAULT_ROUTINE_PRIORITY` if unset. |
| 2: priority Priority; |
| }; |
| }; |
| 2: nat @generated_name("NatRoutine") table { |
| /// Installed routines are evaluated whenever a packet hits the hook |
| /// on which it is installed. |
| /// |
| /// Uninstalled routines are useful for organizational purposes and |
| /// are only traversed when jumped to from another routine. |
| /// |
| /// If left unset, will be an uninstalled routine. |
| 1: installation @generated_name("InstalledNatRoutine") table { |
| /// The optional hook in which to install the routine. If a |
| /// routine is not installed on a particular hook, it can only |
| /// be reached from other routines. |
| /// * The `INGRESS` hook occurs for incoming traffic before a |
| /// routing decision has been made. |
| /// * The `LOCAL_INGRESS` hook occurs for incoming traffic that |
| /// is destined for the local host. |
| /// * The `LOCAL_EGRESS` hook occurs for locally-generated |
| /// traffic before a routing decision has been made. |
| /// * The `EGRESS` hook occurs for all outgoing traffic after a |
| /// routing decision has been made. |
| /// |
| /// Required. |
| 1: hook @generated_name("NatInstallationHook") flexible enum { |
| INGRESS = 1; |
| LOCAL_INGRESS = 2; |
| LOCAL_EGRESS = 3; |
| EGRESS = 4; |
| }; |
| /// The priority of the routine relative to other routines |
| /// installed on the same hook. |
| /// |
| /// Interpreted as `DEFAULT_ROUTINE_PRIORITY` if unset. |
| 2: priority Priority; |
| }; |
| }; |
| }; |
| }; |
| |
| /// A unique identifier for a [`Rule`]. |
| type RuleId = struct { |
| /// The routine to which the rule is added. |
| routine RoutineId; |
| /// The index of the rule. |
| /// |
| /// Must be unique within the routine to which the rule is added. Within a |
| /// given routine, rules will be executed in order of `index`. Note that |
| /// indices in a routine can be sparse; this allows removal and insertion of |
| /// rules while maintaining stable indices for rules that were unchanged. |
| index uint32; |
| }; |
| |
| /// A matcher for network interfaces. |
| type InterfaceMatcher = flexible union { |
| /// The ID of the interface as assigned by the netstack. |
| 1: id fuchsia.net.InterfaceId; |
| /// The name of the interface. |
| 2: name fuchsia.net.interfaces.Name; |
| /// The device class of the interface. |
| 3: device_class flexible union { |
| /// The loopback interface. |
| 1: loopback Empty; |
| /// The interface's network device class. |
| 2: device fuchsia.hardware.network.DeviceClass; |
| }; |
| }; |
| |
| /// A matcher for IP addresses. |
| type AddressMatcher = struct { |
| matcher @generated_name("AddressMatcherType") flexible union { |
| /// The subnet that must contain the IP address in the packet header in |
| /// order for it to match. |
| 1: subnet fuchsia.net.Subnet; |
| /// The range of addresses that must include the IP address in the |
| /// packet header in order for it to match. |
| /// |
| /// The endpoints of the range must be in the same address family, and |
| /// `start` must <= `end`. (Comparisons are performed on the numerical |
| /// big-endian representation of the IP address.) |
| 2: range @generated_name("AddressRange") struct { |
| /// The inclusive start of the address range. |
| start fuchsia.net.IpAddress; |
| /// The inclusive end of the address range. |
| end fuchsia.net.IpAddress; |
| }; |
| }; |
| /// Whether to check for an "inverse" or "negative" match (in which case, |
| /// if the matcher criteria do *not* apply, it *is* considered a match, and |
| /// vice versa). |
| invert bool; |
| }; |
| |
| /// A matcher for transport-layer port numbers. |
| /// |
| /// `start` must <= `end`. |
| type PortMatcher = struct { |
| /// The inclusive start of the port range. |
| start uint16; |
| /// The inclusive end of the port range. |
| end uint16; |
| /// Whether to check for an "inverse" or "negative" match. |
| invert bool; |
| }; |
| |
| /// The criteria that a packet must match for a rule to be applied. |
| /// |
| /// Each field is optional, and will only be checked if provided. An unset |
| /// field will be considered to match any packet. (An entirely empty table |
| /// would match every packet.) Another way to think of the matching criteria |
| /// for a given rule is as an AND of every provided matcher. |
| /// |
| /// Some matchers are only available in certain contexts. For example, the |
| /// `in_interface` is not available in the `EGRESS` hook. If a matcher is |
| /// provided that is not available in the context in which the rule is |
| /// installed, the installation will fail with an error. |
| type Matchers = table { |
| /// The interface on which the packet entered the stack. |
| /// |
| /// Only available in `INGRESS`, `LOCAL_INGRESS`, and `FORWARDING`. |
| 1: in_interface InterfaceMatcher; |
| /// The interface through which the packet exits the stack. |
| /// |
| /// Only available in `FORWARDING`, `LOCAL_EGRESS`, and `EGRESS`. |
| 2: out_interface InterfaceMatcher; |
| /// Matcher for the source IP address. |
| 3: src_addr AddressMatcher; |
| /// Matcher for the destination IP address. |
| 4: dst_addr AddressMatcher; |
| /// Matchers for the transport layer protocol. |
| /// |
| /// Note that the variants of the `TransportProtocol` union allow matching |
| /// on the transport layer protocol itself; to match on specific properties |
| /// at the transport layer (such as TCP or UDP ports), clients should use |
| /// the fields of a protocol-specific matcher. |
| 5: transport_protocol @generated_name("TransportProtocol") flexible union { |
| 1: tcp @generated_name("TcpMatcher") table { |
| /// Matcher for the TCP source port. |
| 1: src_port PortMatcher; |
| /// Matcher for the TCP destination port. |
| 2: dst_port PortMatcher; |
| }; |
| 2: udp @generated_name("UdpMatcher") table { |
| /// Matcher for the UDP source port. |
| 1: src_port PortMatcher; |
| /// Matcher for the UDP destination port. |
| 2: dst_port PortMatcher; |
| }; |
| 3: icmp @generated_name("IcmpMatcher") table {}; |
| 4: icmpv6 @generated_name("Icmpv6Matcher") table {}; |
| }; |
| }; |
| |
| /// The action to take on a packet. |
| type Action = flexible union { |
| /// Accept the packet. |
| /// |
| /// This is a terminal action for the current *installed* routine, i.e. no |
| /// further rules will be evaluated for this packet in the installed routine |
| /// (or any subroutines) in which this rule is installed. Subsequent |
| /// routines installed on the same hook will still be evaluated. |
| 1: accept Empty; |
| /// Drop the packet. |
| /// |
| /// This is a terminal action, i.e. no further rules will be evaluated for |
| /// this packet, even in other routines on the same hook. |
| 2: drop Empty; |
| /// Jump from the current routine to the routine identified by the provided |
| /// name. |
| /// |
| /// The target routine must be in the same namespace as the calling routine, |
| /// and it cannot be installed on a hook; it must be an uninstalled routine. |
| 3: jump string:MAX_NAME_LEN; |
| /// Stop evaluation of the current routine and return to the calling routine |
| /// (the routine from which the current routine was jumped), continuing |
| /// evaluation at the next rule. |
| /// |
| /// If invoked in an installed routine, equivalent to `accept`, given packets |
| /// are accepted by default in the absence of any matching rules. |
| 4: return Empty; |
| /// Redirect the packet to a local socket without changing the packet header |
| /// in any way. |
| /// |
| /// This is a terminal action for the current hook, i.e. no further rules |
| /// will be evaluated for this packet, even in other routines on the same |
| /// hook. However, note that this does not preclude actions on *other* hooks |
| /// from having an effect on this packet; for example, a packet that hits |
| /// TransparentProxy in INGRESS could still be dropped in LOCAL_INGRESS. |
| /// |
| /// This action is only valid in the INGRESS hook. This action is also only |
| /// valid in a rule that ensures the presence of a TCP or UDP header by |
| /// matching on the transport protocol, so that the packet can be properly |
| /// dispatched. |
| /// |
| /// Also note that transparently proxied packets will only be delivered to |
| /// sockets with the transparent socket option enabled. If no such socket |
| /// exists, the packet will be dropped. |
| /// |
| /// This is analogous to the `tproxy` statement in Netfilter. |
| 5: transparent_proxy @generated_name("TransparentProxy") flexible union { |
| /// The bound address of the local socket to redirect the packet to. |
| /// |
| /// The destination port of the packet is used for local delivery. |
| 1: local_addr fuchsia.net.IpAddress; |
| /// The bound port of the local socket to redirect the packet to. It |
| /// must be nonzero. |
| /// |
| /// The destination IP address of the packet is used for local delivery. |
| 2: local_port uint16; |
| /// The bound address and port of the local socket to redirect the |
| /// packet to. The port must be nonzero. |
| 3: local_addr_and_port @generated_name("SocketAddr") struct { |
| addr fuchsia.net.IpAddress; |
| port uint16; |
| }; |
| }; |
| /// A special case of destination NAT (DNAT) that redirects the packet to |
| /// the local host. |
| /// |
| /// This is a terminal action for all NAT routines on the current hook. The |
| /// packet is redirected by rewriting the destination IP address to one |
| /// owned by the ingress interface (if operating on incoming traffic in |
| /// INGRESS) or the loopback address (if operating on locally-generated |
| /// traffic in LOCAL_EGRESS). |
| /// |
| /// As with all DNAT actions, this action is only valid in the INGRESS and |
| /// LOCAL_EGRESS hooks. If a destination port is specified, this action is |
| /// only valid in a rule that ensures the presence of a TCP or UDP header by |
| /// matching on the transport protocol, so that the destination port can be |
| /// rewritten. |
| /// |
| /// This is analogous to the `redirect` statement in Netfilter. |
| 6: redirect @generated_name("Redirect") table { |
| /// The optional range of destination ports used to rewrite the packet. |
| /// |
| /// If specified, the destination port of the packet will be rewritten |
| /// to some randomly chosen port in the range. If absent, the |
| /// destination port of the packet will not be rewritten. |
| 1: dst_port @generated_name("PortRange") struct { |
| /// The inclusive start of the port range. |
| start uint16; |
| /// The inclusive end of the port range. |
| end uint16; |
| }; |
| }; |
| }; |
| |
| /// A rule is a set of criteria (matchers) and a resultant action. |
| type Rule = struct { |
| /// The ID of the rule. |
| /// |
| /// Must be unique within the scope of the routine to which the rule is |
| /// added. |
| id RuleId; |
| /// The criteria that a packet must match for the action to be executed. |
| matchers Matchers; |
| /// The action to take on a matching packet. |
| action Action; |
| }; |