blob: 14fdc8683f97fe0502d57a3163677350ed0508f5 [file] [log] [blame] [edit]
*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