| *mangle |
| :PREROUTING ACCEPT [0:0] |
| :INPUT ACCEPT [0:0] |
| :FORWARD ACCEPT [0:0] |
| :OUTPUT ACCEPT [0:0] |
| :POSTROUTING ACCEPT [0:0] |
| :apply_local_source_mark - [0:0] |
| :skip_apply_vpn_mark - [0:0] |
| :apply_vpn_mark - [0:0] |
| :qos_detect_static - [0:0] |
| :qos_detect - [0:0] |
| :qos_detect_doh - [0:0] |
| :qos_detect_borealis - [0:0] |
| :qos_apply_dscp - [0:0] |
| |
| -A PREROUTING -j qos_detect_static |
| -A OUTPUT -j apply_local_source_mark |
| -A OUTPUT -j qos_detect_static |
| {#- Applies the routing tag saved in conntrack for any established connection #} |
| {#- for sockets created in the host network namespace. Do not overwrite the #} |
| {#- routing tag if the fwmark of the packet already has it. This can happen if #} |
| {#- the socket fwmark is set with the TagSocket API. #} |
| -A OUTPUT -m mark --mark 0x0/0xffff0000 -j CONNMARK --restore-mark --nfmask 0xffff0000 --ctmask 0xffff0000 |
| {#- All local outgoing DNS traffic eligible to VPN routing should skip the VPN #} |
| {#- routing chain and instead go through DNS proxy. #} |
| -A OUTPUT -j skip_apply_vpn_mark |
| {#- All local outgoing traffic eligible to VPN routing should traverse the VPN #} |
| {#- marking chain. #} |
| -A OUTPUT -m mark --mark 0x8000/0xc000 -j apply_vpn_mark |
| |
| {#- Add a rule for skipping apply_local_source_mark if the packet already has a #} |
| {#- source mark (e.g., packets from a wireguard socket in the kernel). #} |
| -A apply_local_source_mark -m mark ! --mark 0x0/0x3f00 -j RETURN |
| {#- Create rules for tagging local sources with the source tag. #} |
| -A apply_local_source_mark -m owner --uid-owner 1000 -j MARK --set-xmark 0x0100/0x3f00 |
| -A apply_local_source_mark -m owner --uid-owner 216 -j MARK --set-xmark 0x0200/0x3f00 |
| -A apply_local_source_mark -m owner --uid-owner 277 -j MARK --set-xmark 0x0200/0x3f00 |
| -A apply_local_source_mark -m owner --uid-owner 269 -j MARK --set-xmark 0x0200/0x3f00 |
| -A apply_local_source_mark -m owner --uid-owner 20131 -j MARK --set-xmark 0x0400/0x3f00 |
| -A apply_local_source_mark -m owner --uid-owner 20138 -j MARK --set-xmark 0x0400/0x3f00 |
| -A apply_local_source_mark -m owner --uid-owner 234 -j MARK --set-xmark 0x0400/0x3f00 |
| -A apply_local_source_mark -m owner --uid-owner 20128 -j MARK --set-xmark 0x0200/0x3f00 |
| -A apply_local_source_mark -m owner --uid-owner 307 -j MARK --set-xmark 0x0400/0x3f00 |
| -A apply_local_source_mark -m mark --mark 0x0/0xc000 -m mark ! --mark 0x0/0x3f00 -j MARK --set-mark 0x8000/0xc000 |
| -A apply_local_source_mark -m cgroup --cgroup 65537 -j MARK --set-xmark 0x300/0x3f00 |
| {#- Finally add a catch-all rule for tagging any remaining local sources with the SYSTEM source tag. #} |
| -A apply_local_source_mark -m mark --mark 0x0/0x3f00 -j MARK --set-xmark 0x400/0x3f00 |
| {#- From QoS categories to DSCP values. See go/cros-qos-dscp-classes-1p for the mapping. #} |
| {#- RealTimeInteractive: 0x20 #} |
| {#- MultimediaConferencing: 0x22 #} |
| {#- NetworkControl: 0x30 #} |
| {#- WebRTC: 0x22 #} |
| -A qos_apply_dscp -m mark --mark 0x20/0xe0 -j DSCP --set-dscp 0x20 |
| -A qos_apply_dscp -m mark --mark 0x40/0xe0 -j DSCP --set-dscp 0x22 |
| -A qos_apply_dscp -m mark --mark 0x60/0xe0 -j DSCP --set-dscp 0x30 |
| -A qos_apply_dscp -m mark --mark 0x80/0xe0 -j DSCP --set-dscp 0x22 |
| {#- Reset the QoS-related bits in fwmark. Some sockets will set their own #} |
| {#- fwmarks when sending packets, while this is not compatible with the rules #} |
| {#- here. See b/303216552 for an example. Note that the matcher part in this #} |
| {#- rule (`--mark 0x0/0xe0`) is not a must, just for checking how many packets #} |
| {#- have their own fwmarks. #} |
| -A qos_detect -m mark ! --mark 0x0/0xe0 -j MARK --set-xmark 0x0/0xe0 |
| {#- Skip QoS detection if DSCP value is already set. #} |
| -A qos_detect -m dscp ! --dscp 0x00 -j RETURN |
| {#- Restore the QoS bits from the conntrack mark to the fwmark of a packet. #} |
| {#- This is used by connections detected by ARC++ socket monitor and WebRTC #} |
| {#- detector. This will override the original fwmark on the packet (if the #} |
| {#- sender sets it) by intention. #} |
| -A qos_detect -j CONNMARK --restore-mark --nfmask 0xe0 --ctmask 0xe0 |
| {#- Add a jump rule to the Borealis detection chain. Rules in this chain will #} |
| {#- be installed dynamically in Datapath::{Add,Remove}BorealisQoSRule. #} |
| -A qos_detect -j qos_detect_borealis |
| {#- If the mark is not 0, skip the following detection. #} |
| -A qos_detect -m mark ! --mark 0x0/0xe0 -j RETURN |
| {#- Marking the first packet in the TCP handshake (SYN bit set and the ACK,RST #} |
| {#- and FIN bits cleared). We only care about the TCP connection initiated from #} |
| {#- the device now. #} |
| -A qos_detect -p tcp -m tcp --tcp-flags FIN,SYN,RST,ACK SYN -j MARK --set-xmark 0x60/0xe0 |
| {#- Marking ICMP packets. #} |
| {% if ipv4 -%} |
| -A qos_detect -p icmp -j MARK --set-xmark 0x60/0xe0 |
| {% elif ipv6 -%} |
| -A qos_detect -p ipv6-icmp -j MARK --set-xmark 0x60/0xe0 |
| {%- endif -%} |
| {#- Marking DNS packets. 853 for DoT for Android is ignored here since it won't #} |
| {#- happen when dns-proxy is on. #} |
| -A qos_detect -p udp -m udp --dport 53 -j MARK --set-xmark 0x60/0xe0 |
| -A qos_detect -p tcp -m tcp --dport 53 -j MARK --set-xmark 0x60/0xe0 |
| {#- Add a jump rule to the DoH detection chain. Rules in this chain will be #} |
| {#- installed dynamically in UpdateDoHProvidersForQoS(). #} |
| -A qos_detect -j qos_detect_doh |
| {#- Rules for WebRTC detection depends on kernel CAP_BPF support thus will be #} |
| {#- installed dynamically. #} |
| COMMIT |
| |
| *nat |
| :PREROUTING ACCEPT [0:0] |
| :INPUT ACCEPT [0:0] |
| :OUTPUT ACCEPT [0:0] |
| :POSTROUTING ACCEPT [0:0] |
| :redirect_default_dns - [0:0] |
| :redirect_user_dns - [0:0] |
| {% if ipv6 -%} |
| :snat_user_dns - [0:0] |
| {% elif ipv4 -%} |
| :redirect_dns - [0:0] |
| :ingress_port_forwarding - [0:0] |
| :apply_auto_dnat_to_arc - [0:0] |
| :apply_auto_dnat_to_crostini - [0:0] |
| :apply_auto_dnat_to_parallels - [0:0] |
| {% endif -%} |
| |
| -A PREROUTING -j redirect_default_dns |
| {#- "ingress_port_forwarding" must be traversed before the default #} |
| {#- "apply_auto_dnat_to_*" autoforwarding chains. #} |
| {% if ipv4 -%} |
| -A PREROUTING -j ingress_port_forwarding |
| {#- ARC default ingress forwarding is always first, Crostini second, and Parallels VM last. #} |
| -A PREROUTING -j apply_auto_dnat_to_arc |
| -A PREROUTING -j apply_auto_dnat_to_crostini |
| -A PREROUTING -j apply_auto_dnat_to_parallels |
| {% endif -%} |
| -A OUTPUT -m mark --mark 0x8000/0xc000 -j redirect_user_dns |
| {#- Set static SNAT rules for any traffic originated from a guest (ARC, #} |
| {#- Crostini, ...) or a connected namespace. #} |
| {#- For IPv6, the SNAT rule is expected to only be triggered when static IPv6 #} |
| {#- is used (without SLAAC). See AddDownstreamInterfaceRules for the method #} |
| {#- that sets up the SNAT mark. #} |
| -A POSTROUTING -m mark --mark 0x1/0x1 -j MASQUERADE |
| {% if ipv6 -%} |
| -A POSTROUTING -m mark --mark 0x8000/0xc000 -j snat_user_dns |
| {% endif -%} |
| COMMIT |
| |
| *filter |
| :INPUT DROP [0:0] |
| :FORWARD DROP [0:0] |
| :OUTPUT DROP [0:0] |
| :vpn_egress_filters - [0:0] |
| :vpn_accept - [0:0] |
| :vpn_lockdown - [0:0] |
| {% if ipv4 -%} |
| :drop_guest_ipv4_prefix - [0:0] |
| :drop_guest_invalid_ipv4 - [0:0] |
| {% elif ipv6 -%} |
| :enforce_ipv6_src_prefix - [0:0] |
| {% endif -%} |
| :ingress_port_firewall - [0:0] |
| :egress_port_firewall - [0:0] |
| :ingress_downstream_network - [0:0] |
| :forward_tethering - [0:0] |
| :egress_tethering - [0:0] |
| :ingress_tethering - [0:0] |
| :forward_localonly - [0:0] |
| :ingress_localonly - [0:0] |
| :egress_localonly - [0:0] |
| :drop_output_to_bruschetta - [0:0] |
| :drop_forward_to_bruschetta - [0:0] |
| :accept_egress_to_dns_proxy - [0:0] |
| |
| -A INPUT -m state --state RELATED,ESTABLISHED -j ACCEPT |
| -A INPUT -i lo -j ACCEPT |
| -A INPUT -p tcp -m tcp --dport 22 -j ACCEPT |
| {% if ipv4 -%} |
| -A INPUT -p icmp -j ACCEPT |
| {% elif ipv6 -%} |
| -A INPUT -p ipv6-icmp -j ACCEPT |
| {% endif -%} |
| -A INPUT -j ingress_port_firewall |
| -A INPUT -j ingress_downstream_network |
| {% if ipv4 -%} |
| -A INPUT -d 224.0.0.251/32 -p udp -m udp --dport 5353 -j ACCEPT |
| -A INPUT -d 239.255.255.250/32 -p udp -m udp --dport 1900 -j ACCEPT |
| {% elif ipv6 -%} |
| -A INPUT -d ff02::fb/128 -p udp -m udp --dport 5353 -j ACCEPT |
| {% endif -%} |
| {%- if ipv4 -%} |
| {#- b/196898241: To ensure that the drop chains drop_guest_ipv4_prefix and #} |
| {#- drop_guest_invalid_ipv4 chain are traversed before vpn_accept and #} |
| {#- vpn_lockdown, they are inserted last in front of the OUTPUT chain and #} |
| {#- FORWARD chains respectively. #} |
| -A FORWARD -j drop_guest_invalid_ipv4 |
| {%- endif -%} |
| {#- Always ACCEPT traffic to DNS proxy's addresses. The rule is inserted #} |
| {#- before VPN filters and after drop guest IPv4 prefix rules. #} |
| -A FORWARD -j accept_egress_to_dns_proxy |
| {#- When VPN lockdown is enabled, a REJECT rule must stop #} |
| {#- any egress traffic tagged with the |kFwmarkRouteOnVpn| intent mark. #} |
| {#- This REJECT rule is added to |kVpnLockdownChain|. In addition, when VPN #} |
| {#- lockdown is enabled and a VPN is connected, an ACCEPT rule protects the #} |
| {#- traffic tagged with the VPN routing mark from being reject by the VPN #} |
| {#- lockdown rule. This ACCEPT rule is added to |kVpnAcceptChain|. #} |
| {#- Therefore, egress traffic must: #} |
| {#- - traverse kVpnAcceptChain before kVpnLockdownChain, #} |
| {#- - traverse kVpnLockdownChain before other ACCEPT rules in OUTPUT and #} |
| {#- FORWARD. #} |
| {#- Finally, egress VPN filter rules must be inserted in front of the #} |
| {#- OUTPUT chain to override basic rules set outside patchpanel. #} |
| -A FORWARD -j vpn_egress_filters |
| -A FORWARD -m state --state RELATED,ESTABLISHED -j ACCEPT |
| {#- Jump to the chain accepting tethering traffic between the upstream and #} |
| {#- downstream network interfaces and reject any other forwarded traffic between #} |
| {#- the downstream and another network interface. Jump rules for the OUTPUT and #} |
| {#- INPUT filter chains are created dynamically. #} |
| -A FORWARD -j forward_tethering |
| {#- Jump to the chain checking localonly traffic and block any traffic between the #} |
| {#- downstrean network interface and any other network, physical, VPN, or guest #} |
| {#- virtual network. #} |
| -A FORWARD -j forward_localonly |
| -A FORWARD -j drop_forward_to_bruschetta |
| {% if ipv6 -%} |
| -A FORWARD -p ipv6-icmp -j ACCEPT |
| {% endif -%} |
| {% if ipv4 -%} |
| {#- b/196898241: To ensure that the drop chains drop_guest_ipv4_prefix and #} |
| {#- drop_guest_invalid_ipv4 chain are traversed before vpn_accept and #} |
| {#- vpn_lockdown, they are inserted last in front of the OUTPUT chain and #} |
| {#- FORWARD chains respectively. #} |
| -A OUTPUT -j drop_guest_ipv4_prefix |
| {#- Always ACCEPT traffic to DNS proxy's addresses. The rule is inserted #} |
| {#- before VPN filters and after drop guest IPv4 prefix rules. #} |
| {% endif -%} |
| -A OUTPUT -j accept_egress_to_dns_proxy |
| -A OUTPUT -j vpn_egress_filters |
| -A OUTPUT -j drop_output_to_bruschetta |
| -A OUTPUT -m state --state NEW,RELATED,ESTABLISHED -j ACCEPT |
| -A OUTPUT -o lo -j ACCEPT |
| {% if ipv6 -%} |
| {#- Do not send out Destination Unreachable packets. #} |
| -A OUTPUT -p ipv6-icmp -m icmp6 --icmpv6-type 1 -j DROP |
| -A OUTPUT -p ipv6-icmp -j ACCEPT |
| {% endif -%} |
| -A OUTPUT -j egress_port_firewall |
| |
| {% if ipv4 -%} |
| {#- b/196899048: IPv4 TCP packets with TCP flags FIN,PSH coming from downstream #} |
| {#- guests need to be dropped explicitly because SNAT will not apply to them #} |
| {#- but the --state INVALID rule above will also not match for these packets. #} |
| {#- crbug/1241756: Make sure that only egress FINPSH packets are dropped. #} |
| {% for ifname_prefix in cellular_ifname_prefixes -%} |
| -A drop_guest_invalid_ipv4 -s 100.115.92.0/23 -o {{ifname_prefix}}+ -p tcp -m tcp --tcp-flags FIN,PSH FIN,PSH -j DROP |
| {% endfor %} |
| {#- chromium:1050579: INVALID packets cannot be tracked by conntrack therefore #} |
| {#- need to be explicitly dropped as SNAT cannot be applied to them. #} |
| {#- b/196898241: To ensure that the INVALID DROP rule is traversed before #} |
| {#- vpn_accept and vpn_lockdown, insert it in front of the FORWARD chain last. -#} |
| -A drop_guest_invalid_ipv4 -m mark --mark 0x1/0x1 -m state --state INVALID -j DROP |
| {#- chromium:898210: Drop any locally originated traffic that would exit a #} |
| {#- physical interface with a source IPv4 address from the subnet of IPs used #} |
| {#- for VMs, containers, and connected namespaces. This is needed to prevent #} |
| {#- packets leaking with an incorrect src IP when a local process binds to the #} |
| {#- wrong interface. #} |
| {% for ifname_prefix in physical_ifname_prefixes -%} |
| -A drop_guest_ipv4_prefix -s 100.115.92.0/23 -o {{ifname_prefix}}+ -j DROP |
| {% endfor %} |
| {% elif ipv6 -%} |
| -A enforce_ipv6_src_prefix -s 2000::/3 -j DROP |
| -A enforce_ipv6_src_prefix -s fc00::/7 -j DROP |
| {% endif -%} |
| |
| {% if ipv4 -%} |
| -A egress_tethering -p icmp -j ACCEPT |
| -A egress_tethering -p udp -m udp --dport 68 -j ACCEPT |
| {% elif ipv6 -%} |
| -A egress_tethering -p ipv6-icmp -j ACCEPT |
| {% endif -%} |
| -A egress_tethering -j DROP |
| {% if ipv4 -%} |
| -A ingress_tethering -p udp -m udp --dport 67 -j ACCEPT |
| {% endif -%} |
| -A ingress_tethering -j DROP |
| |
| -A egress_localonly -m state --state NEW,RELATED,ESTABLISHED -j ACCEPT |
| {% if ipv4 -%} |
| -A egress_localonly -p icmp -j ACCEPT |
| -A egress_localonly -p udp -m udp --dport 68 -j ACCEPT |
| {% elif ipv6 -%} |
| -A egress_localonly -p ipv6-icmp -j ACCEPT |
| {% endif -%} |
| {% if ipv4 -%} |
| -A ingress_localonly -p udp -m udp --dport 67 -j ACCEPT |
| {% endif -%} |
| -A ingress_localonly -j DROP |
| |
| -A vpn_egress_filters -j vpn_accept |
| -A vpn_egress_filters -j vpn_lockdown |
| COMMIT |
| |