Howdy, Stranger!

It looks like you're new here. If you want to get involved, click one of these buttons!


Shells Virtual Desktop
BMail.ag - Secure Email Service
Server.net
CPLicense.net
VPS Server
Buy VPN
Vultr
VMs for AI
HostDare
HostDare
ReliableSite White-Label Dedicated Hosting for Resellers
25% Recurring Discount on NVMe VPS
InterServer VPS
BMail.ag - Secure Email Service
Best VPN
High-Performance Bare Metal Server Solutions
Karvl.com
Server Mania Cloud Hosting
DataWagon Hosting
AlphaVPS Hosting
Evoxt.com
Clouvider
VPS Hosting with NVMe
Residential IPs in the US & 4G Mobile Proxies in EU & US with Unlimited Bandwidth
ReliableSite White-Label Dedicated Hosting for Resellers
Rabisu - Hosting Solutions
CloudLinux
Shells Virtual Desktop
New on LowEndTalk? Please Register and read our Community Rules.

All new Registrations are manually reviewed and approved, so a short delay after registration may occur before your account becomes active.

What firewall do you use?

FubukiFubuki Member

been using ufw for all of my firewall for my servers but iv been thinking of switching to nftables since it looks more advanced and stable

the quiz is upside down
  1. What type do you use?51 votes
    1. ufw
      60.78%
    2. iptables
      11.76%
    3. nftables
      17.65%
    4. other
        7.84%
    5. Cloudflare (and CF tunnels)
        1.96%

Comments

  • forestforest Member
    edited 3:14AM

    All of these actually use nftables under the hood. UFW is an iptables frontend and modern iptables is usually iptables-nft, which just translate iptables commands into (sloppy) nftables rules. But writing directly in nftables is always best.

    I like to use nftables, but if bpfilter becomes stable and usable one day, I'll switch in a heartbeat because the performance improvement is absolutely unbeatable and its extensibility is limitless.

    The nft syntax can be a little confusing at first, but it's really not that bad. A basic "allow all out, allow SSH in from 203.0.113.69 only, allow web traffic in from any IP" ruleset would be:

    flush ruleset
    
    table inet filter {
            chain input {
                    type filter hook input priority filter; policy drop;
    
                    # allow loopback and incoming replies
                    iif lo accept
                    ct state { established, related } accept
    
                    # restrict ssh to our home ip but allow anyone to access our web server
                    ct state new ip saddr 203.0.113.69 tcp dport ssh accept
                    ct state new tcp dport { http, https } accept
    
                    # allow necessary and useful icmp types
                    icmp type echo-request limit rate 25/second accept
                    icmpv6 type { echo-request, nd-neighbor-solicit, nd-router-advert, nd-neighbor-advert } limit rate 25/second accept
            }
    
            chain output {
                    type filter hook output priority filter; policy accept;
            }
    
            chain forward {
                    type filter hook forward priority filter; policy drop;
            }
    }
    

    Btw, you can also use it for more complex things like port knocking (in a bit of a hacky way), DNAT, forcing certain applications to use certain source IPs, and even loopback and output filtering for server hardening:

    flush ruleset
    
    define primary_ipv4 = 23.137.105.248
    define secondary_ipv4 = [REDACTED]
    define primary_ipv6 = 2602:fb54:1400::1d
    
    define knock_port_1 = [REDACTED]
    define knock_port_2 = [REDACTED]
    define knock_port_3 = [REDACTED]
    define ssh_nat_port = [REDACTED]
    
    define tor_orport = 9001
    define i2p_port = 27016
    define freenet_opennet = 61601
    define freenet_darknet = 54696
    define monero_p2p_port = 18080
    define monero_rpc_port = 18089
    define syncthing_relay_port = 22067
    define syncthing_status_port = 22070
    define snowflake_port_min = 41000
    define snowflake_port_max = 41999
    
    define beszel_addr = [REDACTED]
    define beszel_port = [REDACTED]
    
    table inet filter {
            set knock_stage_1 {
                    type ipv4_addr
                    size 8192
                    flags timeout
                    timeout 5s
            }
    
            set knock_stage_2 {
                    type ipv4_addr
                    size 256
                    flags timeout
                    timeout 5s
            }
    
            set knock_success {
                    type ipv4_addr
                    size 256
                    flags timeout
                    timeout 5s
            }
    
            chain prerouting {
                    type filter hook prerouting priority raw; policy accept;
                    tcp flags syn jump knock_check
            }
    
            chain prerouting_nat {
                    type nat hook prerouting priority dstnat; policy accept;
                    tcp dport $ssh_nat_port ip saddr @knock_success redirect to :ssh
            }
    
            chain knock_check {
                    # Knock sequence
                    ip saddr @knock_stage_2 tcp dport $knock_port_3 log prefix "Port knock: " add @knock_success { ip saddr } goto knock_reset
                    ip saddr @knock_stage_1 tcp dport $knock_port_2 delete @knock_stage_1 { ip saddr } add @knock_stage_2 { ip saddr } return
                    tcp dport $knock_port_1 goto knock_start
    
                    # Wrong port knock_success, reset
                    ip saddr @knock_stage_2 goto knock_reset
                    ip saddr @knock_stage_1 goto knock_reset
            }
    
            chain knock_reset {
                    delete @knock_stage_1 { ip saddr }
                    delete @knock_stage_2 { ip saddr }
            }
    
            chain knock_start {
                    jump knock_reset
                    add @knock_stage_1 { ip saddr }
            }
    
            chain input {
                    type filter hook input priority filter; policy drop;
    
                    # Allow local traffic and replies
                    iif lo accept
                    ct state { established, related } accept
                    ct state invalid drop
    
                    # Received on primary IPs
                    ct state new ip daddr $primary_ipv4 jump {
                            tcp dport { $tor_orport, $monero_p2p_port, $monero_rpc_port, $syncthing_relay_port, $syncthing_status_port } accept
                            udp dport { $freenet_darknet, $freenet_opennet } accept
                            tcp dport $beszel_port ip saddr $beszel_addr accept
                            tcp dport ssh ip saddr @knock_success ct status dnat ct count 1 accept
                    }
    
                    ct state new ip6 daddr $primary_ipv6 jump {
                            tcp dport { $tor_orport, $i2p_port } accept
                            udp dport { $freenet_darknet, $freenet_opennet, $i2p_port } accept
                    }
    
                    # Can't run i2pd on the exit's IP: https://github.com/i2p/i2p.i2p/blob/master/installer/resources/blocklist-tor.txt
                    ct state new ip daddr $secondary_ipv4 jump {
                            tcp dport $i2p_port accept
                            udp dport $i2p_port accept
                            udp dport $snowflake_port_min - $snowflake_port_max accept
                    }
    
                    # Rate-limit ICMP
                    icmp type echo-request limit rate 25/second accept
                    icmpv6 type { echo-request, nd-neighbor-solicit, nd-router-advert, nd-neighbor-advert } limit rate 25/second accept
            }
    
            chain output {
                    type filter hook output priority filter; policy accept;
    
                    ct state { established, related } accept
                    ct state invalid drop
    
                    # Block Beszel agent from initiating its own connections, even remote ones
                    skuid beszel counter reject
    
                    # Allow Unbound to access DNS and nothing else
                    # Note that root.zone is updated from the root nameservers over the default IP, not the secondary IP
                    skuid unbound jump {
                            ip protocol { tcp, udp } th dport 53 accept
                            ip6 nexthdr { tcp, udp } th dport 53 accept
                            counter reject
                    }
    
                    # Allow non-local connections and local connections to DNS
                    fib daddr type != local accept
                    ip protocol { tcp, udp } th dport 53 ip daddr 127.0.0.1 accept
    
                    # Allow Java service wrapper
                    skuid freenet tcp dport 32000 ip daddr 127.0.0.1 accept
    
                    # Allow Tor to connect to local ports set in HiddenServicePort
                    skuid debian-tor jump {
                            tcp dport { ssh, 7070, 8888 } ip daddr 127.0.0.1 accept
                            counter reject
                    }
    
                    # Block any remaining packets initiated by these users
                    skuid { _chrony, globalping, syncthing-relaysrv, i2pd, freenet, monero, snowflake-proxy } counter reject
            }
    
            chain forward {
                    type filter hook forward priority filter; policy drop;
            }
    }
    

    The only issue I have with nftables is that there's no xtables integration so there's no xt_bpf, which means I can't directly integrate BPF programs with nftables. But that's not an issue for 99.9% of users who don't need to do DPI.

  • JohnnySacJohnnySac Member

    ufw is nice and easy to use for simple firewalls but I think it's best to learn nftables because it gives you total control and access to more features than ufw (which really just wraps nftables). It has a bit of a learning curve but it's really not bad. Once you start needing more complex firewalls ufw won't cut it.

    Thanked by 1mandala
  • Nftables + RHEL ftw

  • nghialelenghialele Member

    @forest said:
    All of these actually use nftables under the hood. UFW is an iptables frontend and modern iptables is usually iptables-nft, which just translate iptables commands into (sloppy) nftables rules. But writing directly in nftables is always best.

    I like to use nftables, but if bpfilter becomes stable and usable one day, I'll switch in a heartbeat because the performance improvement is absolutely unbeatable and its extensibility is limitless.

    The nft syntax can be a little confusing at first, but it's really not that bad. A basic "allow all out, allow SSH in from 203.0.113.69 only, allow web traffic in from any IP" ruleset would be:

    flush ruleset
    
    table inet filter {
            chain input {
                    type filter hook input priority filter; policy drop;
    
                    # allow loopback and incoming replies
                    iif lo accept
                    ct state { established, related } accept
    
                    # restrict ssh to our home ip but allow anyone to access our web server
                    ct state new ip saddr 203.0.113.69 tcp dport ssh accept
                    ct state new tcp dport { http, https } accept
    
                    # allow necessary and useful icmp types
                    icmp type echo-request limit rate 25/second accept
                    icmpv6 type { echo-request, nd-neighbor-solicit, nd-router-advert, nd-neighbor-advert } limit rate 25/second accept
            }
    
            chain output {
                    type filter hook output priority filter; policy accept;
            }
    
            chain forward {
                    type filter hook forward priority filter; policy drop;
            }
    }
    

    Btw, you can also use it for more complex things like port knocking (in a bit of a hacky way), DNAT, forcing certain applications to use certain source IPs, and even loopback and output filtering for server hardening:

    flush ruleset
    
    define primary_ipv4 = 23.137.105.248
    define secondary_ipv4 = [REDACTED]
    define primary_ipv6 = 2602:fb54:1400::1d
    
    define knock_port_1 = [REDACTED]
    define knock_port_2 = [REDACTED]
    define knock_port_3 = [REDACTED]
    define ssh_nat_port = [REDACTED]
    
    define tor_orport = 9001
    define i2p_port = 27016
    define freenet_opennet = 61601
    define freenet_darknet = 54696
    define monero_p2p_port = 18080
    define monero_rpc_port = 18089
    define syncthing_relay_port = 22067
    define syncthing_status_port = 22070
    define snowflake_port_min = 41000
    define snowflake_port_max = 41999
    
    define beszel_addr = [REDACTED]
    define beszel_port = [REDACTED]
    
    table inet filter {
            set knock_stage_1 {
                    type ipv4_addr
                    size 8192
                    flags timeout
                    timeout 5s
            }
    
            set knock_stage_2 {
                    type ipv4_addr
                    size 256
                    flags timeout
                    timeout 5s
            }
    
            set knock_success {
                    type ipv4_addr
                    size 256
                    flags timeout
                    timeout 5s
            }
    
            chain prerouting {
                    type filter hook prerouting priority raw; policy accept;
                    tcp flags syn jump knock_check
            }
    
            chain prerouting_nat {
                    type nat hook prerouting priority dstnat; policy accept;
                    tcp dport $ssh_nat_port ip saddr @knock_success redirect to :ssh
            }
    
            chain knock_check {
                    # Knock sequence
                    ip saddr @knock_stage_2 tcp dport $knock_port_3 log prefix "Port knock: " add @knock_success { ip saddr } goto knock_reset
                    ip saddr @knock_stage_1 tcp dport $knock_port_2 delete @knock_stage_1 { ip saddr } add @knock_stage_2 { ip saddr } return
                    tcp dport $knock_port_1 goto knock_start
    
                    # Wrong port knock_success, reset
                    ip saddr @knock_stage_2 goto knock_reset
                    ip saddr @knock_stage_1 goto knock_reset
            }
    
            chain knock_reset {
                    delete @knock_stage_1 { ip saddr }
                    delete @knock_stage_2 { ip saddr }
            }
    
            chain knock_start {
                    jump knock_reset
                    add @knock_stage_1 { ip saddr }
            }
    
            chain input {
                    type filter hook input priority filter; policy drop;
    
                    # Allow local traffic and replies
                    iif lo accept
                    ct state { established, related } accept
                    ct state invalid drop
    
                    # Received on primary IPs
                    ct state new ip daddr $primary_ipv4 jump {
                            tcp dport { $tor_orport, $monero_p2p_port, $monero_rpc_port, $syncthing_relay_port, $syncthing_status_port } accept
                            udp dport { $freenet_darknet, $freenet_opennet } accept
                            tcp dport $beszel_port ip saddr $beszel_addr accept
                            tcp dport ssh ip saddr @knock_success ct status dnat ct count 1 accept
                    }
    
                    ct state new ip6 daddr $primary_ipv6 jump {
                            tcp dport { $tor_orport, $i2p_port } accept
                            udp dport { $freenet_darknet, $freenet_opennet, $i2p_port } accept
                    }
    
                    # Can't run i2pd on the exit's IP: https://github.com/i2p/i2p.i2p/blob/master/installer/resources/blocklist-tor.txt
                    ct state new ip daddr $secondary_ipv4 jump {
                            tcp dport $i2p_port accept
                            udp dport $i2p_port accept
                            udp dport $snowflake_port_min - $snowflake_port_max accept
                    }
    
                    # Rate-limit ICMP
                    icmp type echo-request limit rate 25/second accept
                    icmpv6 type { echo-request, nd-neighbor-solicit, nd-router-advert, nd-neighbor-advert } limit rate 25/second accept
            }
    
            chain output {
                    type filter hook output priority filter; policy accept;
    
                    ct state { established, related } accept
                    ct state invalid drop
    
                    # Block Beszel agent from initiating its own connections, even remote ones
                    skuid beszel counter reject
    
                    # Allow Unbound to access DNS and nothing else
                    # Note that root.zone is updated from the root nameservers over the default IP, not the secondary IP
                    skuid unbound jump {
                            ip protocol { tcp, udp } th dport 53 accept
                            ip6 nexthdr { tcp, udp } th dport 53 accept
                            counter reject
                    }
    
                    # Allow non-local connections and local connections to DNS
                    fib daddr type != local accept
                    ip protocol { tcp, udp } th dport 53 ip daddr 127.0.0.1 accept
    
                    # Allow Java service wrapper
                    skuid freenet tcp dport 32000 ip daddr 127.0.0.1 accept
    
                    # Allow Tor to connect to local ports set in HiddenServicePort
                    skuid debian-tor jump {
                            tcp dport { ssh, 7070, 8888 } ip daddr 127.0.0.1 accept
                            counter reject
                    }
    
                    # Block any remaining packets initiated by these users
                    skuid { _chrony, globalping, syncthing-relaysrv, i2pd, freenet, monero, snowflake-proxy } counter reject
            }
    
            chain forward {
                    type filter hook forward priority filter; policy drop;
            }
    }
    

    The only issue I have with nftables is that there's no xtables integration so there's no xt_bpf, which means I can't directly integrate BPF programs with nftables. But that's not an issue for 99.9% of users who don't need to do DPI.

    Thanks man! I need more digging on this.

    Thanked by 1forest
  • forestforest Member
    edited 3:45AM

    @nghialele said:

    @forest said:
    All of these actually use nftables under the hood. UFW is an iptables frontend and modern iptables is usually iptables-nft, which just translate iptables commands into (sloppy) nftables rules. But writing directly in nftables is always best.

    I like to use nftables, but if bpfilter becomes stable and usable one day, I'll switch in a heartbeat because the performance improvement is absolutely unbeatable and its extensibility is limitless.

    The nft syntax can be a little confusing at first, but it's really not that bad. A basic "allow all out, allow SSH in from 203.0.113.69 only, allow web traffic in from any IP" ruleset would be:

    flush ruleset
    
    table inet filter {
            chain input {
                    type filter hook input priority filter; policy drop;
    
                    # allow loopback and incoming replies
                    iif lo accept
                    ct state { established, related } accept
    
                    # restrict ssh to our home ip but allow anyone to access our web server
                    ct state new ip saddr 203.0.113.69 tcp dport ssh accept
                    ct state new tcp dport { http, https } accept
    
                    # allow necessary and useful icmp types
                    icmp type echo-request limit rate 25/second accept
                    icmpv6 type { echo-request, nd-neighbor-solicit, nd-router-advert, nd-neighbor-advert } limit rate 25/second accept
            }
    
            chain output {
                    type filter hook output priority filter; policy accept;
            }
    
            chain forward {
                    type filter hook forward priority filter; policy drop;
            }
    }
    

    Btw, you can also use it for more complex things like port knocking (in a bit of a hacky way), DNAT, forcing certain applications to use certain source IPs, and even loopback and output filtering for server hardening:

    flush ruleset
    
    define primary_ipv4 = 23.137.105.248
    define secondary_ipv4 = [REDACTED]
    define primary_ipv6 = 2602:fb54:1400::1d
    
    define knock_port_1 = [REDACTED]
    define knock_port_2 = [REDACTED]
    define knock_port_3 = [REDACTED]
    define ssh_nat_port = [REDACTED]
    
    define tor_orport = 9001
    define i2p_port = 27016
    define freenet_opennet = 61601
    define freenet_darknet = 54696
    define monero_p2p_port = 18080
    define monero_rpc_port = 18089
    define syncthing_relay_port = 22067
    define syncthing_status_port = 22070
    define snowflake_port_min = 41000
    define snowflake_port_max = 41999
    
    define beszel_addr = [REDACTED]
    define beszel_port = [REDACTED]
    
    table inet filter {
            set knock_stage_1 {
                    type ipv4_addr
                    size 8192
                    flags timeout
                    timeout 5s
            }
    
            set knock_stage_2 {
                    type ipv4_addr
                    size 256
                    flags timeout
                    timeout 5s
            }
    
            set knock_success {
                    type ipv4_addr
                    size 256
                    flags timeout
                    timeout 5s
            }
    
            chain prerouting {
                    type filter hook prerouting priority raw; policy accept;
                    tcp flags syn jump knock_check
            }
    
            chain prerouting_nat {
                    type nat hook prerouting priority dstnat; policy accept;
                    tcp dport $ssh_nat_port ip saddr @knock_success redirect to :ssh
            }
    
            chain knock_check {
                    # Knock sequence
                    ip saddr @knock_stage_2 tcp dport $knock_port_3 log prefix "Port knock: " add @knock_success { ip saddr } goto knock_reset
                    ip saddr @knock_stage_1 tcp dport $knock_port_2 delete @knock_stage_1 { ip saddr } add @knock_stage_2 { ip saddr } return
                    tcp dport $knock_port_1 goto knock_start
    
                    # Wrong port knock_success, reset
                    ip saddr @knock_stage_2 goto knock_reset
                    ip saddr @knock_stage_1 goto knock_reset
            }
    
            chain knock_reset {
                    delete @knock_stage_1 { ip saddr }
                    delete @knock_stage_2 { ip saddr }
            }
    
            chain knock_start {
                    jump knock_reset
                    add @knock_stage_1 { ip saddr }
            }
    
            chain input {
                    type filter hook input priority filter; policy drop;
    
                    # Allow local traffic and replies
                    iif lo accept
                    ct state { established, related } accept
                    ct state invalid drop
    
                    # Received on primary IPs
                    ct state new ip daddr $primary_ipv4 jump {
                            tcp dport { $tor_orport, $monero_p2p_port, $monero_rpc_port, $syncthing_relay_port, $syncthing_status_port } accept
                            udp dport { $freenet_darknet, $freenet_opennet } accept
                            tcp dport $beszel_port ip saddr $beszel_addr accept
                            tcp dport ssh ip saddr @knock_success ct status dnat ct count 1 accept
                    }
    
                    ct state new ip6 daddr $primary_ipv6 jump {
                            tcp dport { $tor_orport, $i2p_port } accept
                            udp dport { $freenet_darknet, $freenet_opennet, $i2p_port } accept
                    }
    
                    # Can't run i2pd on the exit's IP: https://github.com/i2p/i2p.i2p/blob/master/installer/resources/blocklist-tor.txt
                    ct state new ip daddr $secondary_ipv4 jump {
                            tcp dport $i2p_port accept
                            udp dport $i2p_port accept
                            udp dport $snowflake_port_min - $snowflake_port_max accept
                    }
    
                    # Rate-limit ICMP
                    icmp type echo-request limit rate 25/second accept
                    icmpv6 type { echo-request, nd-neighbor-solicit, nd-router-advert, nd-neighbor-advert } limit rate 25/second accept
            }
    
            chain output {
                    type filter hook output priority filter; policy accept;
    
                    ct state { established, related } accept
                    ct state invalid drop
    
                    # Block Beszel agent from initiating its own connections, even remote ones
                    skuid beszel counter reject
    
                    # Allow Unbound to access DNS and nothing else
                    # Note that root.zone is updated from the root nameservers over the default IP, not the secondary IP
                    skuid unbound jump {
                            ip protocol { tcp, udp } th dport 53 accept
                            ip6 nexthdr { tcp, udp } th dport 53 accept
                            counter reject
                    }
    
                    # Allow non-local connections and local connections to DNS
                    fib daddr type != local accept
                    ip protocol { tcp, udp } th dport 53 ip daddr 127.0.0.1 accept
    
                    # Allow Java service wrapper
                    skuid freenet tcp dport 32000 ip daddr 127.0.0.1 accept
    
                    # Allow Tor to connect to local ports set in HiddenServicePort
                    skuid debian-tor jump {
                            tcp dport { ssh, 7070, 8888 } ip daddr 127.0.0.1 accept
                            counter reject
                    }
    
                    # Block any remaining packets initiated by these users
                    skuid { _chrony, globalping, syncthing-relaysrv, i2pd, freenet, monero, snowflake-proxy } counter reject
            }
    
            chain forward {
                    type filter hook forward priority filter; policy drop;
            }
    }
    

    The only issue I have with nftables is that there's no xtables integration so there's no xt_bpf, which means I can't directly integrate BPF programs with nftables. But that's not an issue for 99.9% of users who don't need to do DPI.

    Thanks man! I need more digging on this.

    If you already know iptables, you can use iptables-translate(8) to convert from iptables to nftables. It's not an optimized translation by any means, but it gives you a good idea about how changes to iptables rules result in changes to the nft rules.

    Some usage examples are here:

    % iptables-translate -A INPUT -p tcp --dport 22 -m conntrack --ctstate NEW -j ACCEPT
    nft add rule ip filter INPUT tcp dport 22 ct state new counter accept
    
    Thanked by 2nghialele david
  • forestforest Member
    edited 3:49AM

    @Fubuki You should have added pf to the list for the BSD folks, an option for "none" for naïve people, and an option for "my hypervisor's firewall" for people who configure the firewall using their VPS' control panel.

  • @forest said:
    @Fubuki You should have added pf to the list for the BSD folks, an option for "none" for naïve people, and an option for "my hypervisor's firewall" for people who configure the firewall using their VPS' control panel.

    And npf so i can throw up a little ;)

    Thanked by 1gatewaysentryllc
  • gatewaysentryllcgatewaysentryllc Member, Patron Provider

    use XDP, even if its XDP Generic it still greatly offers you higher capabilities than iptables.

    Thanked by 1forest
  • forestforest Member
    edited 4:44AM

    @gatewaysentryllc said:
    use XDP, even if its XDP Generic it still greatly offers you higher capabilities than iptables.

    Yep, that's uses eBPF like bpfilter. It's amazingly powerful! Each rule is JIT compiled into native machine code.

    Back in the day before XDP was stable, I would just use xt_bpf in the raw iptables chain, which gave similar performance (but still not as good).

    Thanked by 1gatewaysentryllc
  • edited 5:03AM

    @gatewaysentryllc said:
    use XDP, even if its XDP Generic it still greatly offers you higher capabilities than iptables.

    Kinda depends on who's being addressed i guess. I mean, there's a reason UFW is so far ahead. The average user usually isn't looking for something fancy but just wants to block a bunch of ports. Once something goes beyond that most people check out pretty quickly and even if not the general solutions already do way more stuff than the average person would use. I don't think this can be generalized.

  • forestforest Member
    edited 5:09AM

    @totally_not_banned said: Kinda depends on who's being addressed i guess. I mean, there's a reason UFW is so far ahead. The average user usually isn't looking for something fancy but just wants to block a bunch of ports

    XDP is generally used for earlier filtering, so it actually augments UFW (and iptables and the like) rather than replacing it. It runs extremely early in the network stack which makes it good for DDoS protection so that packets can be discarded or redirected before they hit the much heavier (and more easily overwhelmed) NetFilter code.

    Thanked by 1gatewaysentryllc
  • edited 5:17AM

    @forest said:

    @totally_not_banned said: Kinda depends on who's being addressed i guess. I mean, there's a reason UFW is so far ahead. The average user usually isn't looking for something fancy but just wants to block a bunch of ports

    XDP is generally used for earlier filtering, so it actually augments UFW (and iptables and the like) rather than replacing it. It runs extremely early in the network stack which makes it good for DDoS protection so that packets can be discarded or redirected before it hits the much heavier (but more abstract and "useful") NetFilter code.

    Sure, i didn't say it was bad or would necessarily do anything at all but just that it goes beyond what most users care about. People stick to UFW because they don't feel the need to go beyond that. I mean if there was a lot of stuff people were missing, there would be more people looking into at least iptables or nfttables, right?

    Thanked by 1gatewaysentryllc
  • forestforest Member

    @totally_not_banned said:

    @forest said:

    @totally_not_banned said: Kinda depends on who's being addressed i guess. I mean, there's a reason UFW is so far ahead. The average user usually isn't looking for something fancy but just wants to block a bunch of ports

    XDP is generally used for earlier filtering, so it actually augments UFW (and iptables and the like) rather than replacing it. It runs extremely early in the network stack which makes it good for DDoS protection so that packets can be discarded or redirected before it hits the much heavier (but more abstract and "useful") NetFilter code.

    Sure, i didn't say it was bad or necessarily do anything at all but just that it goes beyond what most users care about. People stick to UFW because they don't feel the need to go beyond that. I mean if there was a lot of stuff people were missing, there would be more people looking into iptables or nfttables, right?

    True, and unless you're handling many millions or billions of PPS, the equivalent ruleset for ufw, iptables, or nftables would be more than enough and XDP would be a premature micro-optimization.

  • edited 5:34AM

    @forest said:

    @totally_not_banned said:

    @forest said:

    @totally_not_banned said: Kinda depends on who's being addressed i guess. I mean, there's a reason UFW is so far ahead. The average user usually isn't looking for something fancy but just wants to block a bunch of ports

    XDP is generally used for earlier filtering, so it actually augments UFW (and iptables and the like) rather than replacing it. It runs extremely early in the network stack which makes it good for DDoS protection so that packets can be discarded or redirected before it hits the much heavier (but more abstract and "useful") NetFilter code.

    Sure, i didn't say it was bad or necessarily do anything at all but just that it goes beyond what most users care about. People stick to UFW because they don't feel the need to go beyond that. I mean if there was a lot of stuff people were missing, there would be more people looking into iptables or nfttables, right?

    True, and unless you're handling many millions or billions of PPS, the equivalent ruleset for ufw, iptables, or nftables would be more than enough and XDP would be a premature micro-optimization.

    Excactly. I mean, it's a good pointer, no question about that but i just wanted to relativize the imperative form of "use" a little. People getting into writing their own rule sets will regularly already be a little intimidated as is. In this case it's probably not overly helpful to feel pushed into adding yet another layer of specialized tooling making the whole thing become even more abstract than it already is.

    Thanked by 1forest
  • gatewaysentryllcgatewaysentryllc Member, Patron Provider

    @forest said:

    @totally_not_banned said:

    @forest said:

    @totally_not_banned said: Kinda depends on who's being addressed i guess. I mean, there's a reason UFW is so far ahead. The average user usually isn't looking for something fancy but just wants to block a bunch of ports

    XDP is generally used for earlier filtering, so it actually augments UFW (and iptables and the like) rather than replacing it. It runs extremely early in the network stack which makes it good for DDoS protection so that packets can be discarded or redirected before it hits the much heavier (but more abstract and "useful") NetFilter code.

    Sure, i didn't say it was bad or necessarily do anything at all but just that it goes beyond what most users care about. People stick to UFW because they don't feel the need to go beyond that. I mean if there was a lot of stuff people were missing, there would be more people looking into iptables or nfttables, right?

    True, and unless you're handling many millions or billions of PPS, the equivalent ruleset for ufw, iptables, or nftables would be more than enough and XDP would be a premature micro-optimization.

    We did a benchmark using internal testing VM to VM,

    Ryzen 9950x
    32GB Ram
    MLX5 Interface 2x 100GbE (PCIe Gen 3, so roughly 120Gbps +-)
    iptables = 15Mil PPS
    xdp native = 94Mil PPS

    Currently we are using 9654, 7742, 9960x, PCIE Gen 4 NIC

    2x 100GbE CX6
    180M PPS Each

    With ZERO additive latency (under 1MS so its unreadable for us)

    While iptables added soo much latency, roughly 20MS +- (during load)

    Thanked by 1nghialele
  • rpqurpqu Member

    @gatewaysentryllc said:

    @forest said:

    @totally_not_banned said:

    @forest said:

    @totally_not_banned said: Kinda depends on who's being addressed i guess. I mean, there's a reason UFW is so far ahead. The average user usually isn't looking for something fancy but just wants to block a bunch of ports

    XDP is generally used for earlier filtering, so it actually augments UFW (and iptables and the like) rather than replacing it. It runs extremely early in the network stack which makes it good for DDoS protection so that packets can be discarded or redirected before it hits the much heavier (but more abstract and "useful") NetFilter code.

    Sure, i didn't say it was bad or necessarily do anything at all but just that it goes beyond what most users care about. People stick to UFW because they don't feel the need to go beyond that. I mean if there was a lot of stuff people were missing, there would be more people looking into iptables or nfttables, right?

    True, and unless you're handling many millions or billions of PPS, the equivalent ruleset for ufw, iptables, or nftables would be more than enough and XDP would be a premature micro-optimization.

    We did a benchmark using internal testing VM to VM,

    Ryzen 9950x
    32GB Ram
    MLX5 Interface 2x 100GbE (PCIe Gen 3, so roughly 120Gbps +-)
    iptables = 15Mil PPS
    xdp native = 94Mil PPS

    Currently we are using 9654, 7742, 9960x, PCIE Gen 4 NIC

    2x 100GbE CX6
    180M PPS Each

    With ZERO additive latency (under 1MS so its unreadable for us)

    While iptables added soo much latency, roughly 20MS +- (during load)

    Thanks for the data. I guess I'll have claude optimized my filter

    @AlteredParadox, you still got quota to burn?

  • gatewaysentryllcgatewaysentryllc Member, Patron Provider

    @rpqu said:

    @gatewaysentryllc said:

    @forest said:

    @totally_not_banned said:

    @forest said:

    @totally_not_banned said: Kinda depends on who's being addressed i guess. I mean, there's a reason UFW is so far ahead. The average user usually isn't looking for something fancy but just wants to block a bunch of ports

    XDP is generally used for earlier filtering, so it actually augments UFW (and iptables and the like) rather than replacing it. It runs extremely early in the network stack which makes it good for DDoS protection so that packets can be discarded or redirected before it hits the much heavier (but more abstract and "useful") NetFilter code.

    Sure, i didn't say it was bad or necessarily do anything at all but just that it goes beyond what most users care about. People stick to UFW because they don't feel the need to go beyond that. I mean if there was a lot of stuff people were missing, there would be more people looking into iptables or nfttables, right?

    True, and unless you're handling many millions or billions of PPS, the equivalent ruleset for ufw, iptables, or nftables would be more than enough and XDP would be a premature micro-optimization.

    We did a benchmark using internal testing VM to VM,

    Ryzen 9950x
    32GB Ram
    MLX5 Interface 2x 100GbE (PCIe Gen 3, so roughly 120Gbps +-)
    iptables = 15Mil PPS
    xdp native = 94Mil PPS

    Currently we are using 9654, 7742, 9960x, PCIE Gen 4 NIC

    2x 100GbE CX6
    180M PPS Each

    With ZERO additive latency (under 1MS so its unreadable for us)

    While iptables added soo much latency, roughly 20MS +- (during load)

    Thanks for the data. I guess I'll have claude optimized my filter

    @AlteredParadox, you still got quota to burn?

    What are you currently using? I don't mind giving some assistance and advice!

    Thanked by 1forest
  • forestforest Member

    @rpqu said: Thanks for the data. I guess I'll have claude optimized my filter

    LLMs are actually pretty bad at optimizing this kind of thing.

  • FubukiFubuki Member

    @gatewaysentryllc said:
    use XDP, even if its XDP Generic it still greatly offers you higher capabilities than iptables.

    never used XDP before, how does it work like port blocking n such like if i were to deploy it on a VPS, how can it detect before the kernel responds?

    @forest said:
    @Fubuki You should have added pf to the list for the BSD folks, an option for "none" for naïve people, and an option for "my hypervisor's firewall" for people who configure the firewall using their VPS' control panel.

    ill keep a note on that next time

  • forestforest Member
    edited 6:38AM

    @Fubuki said: never used XDP before, how does it work like port blocking n such like if i were to deploy it on a VPS, how can it detect before the kernel responds?

    It's not that it blocks something before the kernel responds, but it is run very early in the networking stack, so any packets that are part of a DDoS waste as few CPU cycles as possible before being discarded. It runs before any other firewall decisions or even routing decisions are run. It's not much more than "put packet in memory map, execute tiny function in pure machine code with access to memory map, drop or don't drop based on function's return value".

    I wrote a (I think) very understandable answer here explaining the basics of BPF filtering, which is what XDP uses internally: https://unix.stackexchange.com/a/699504/164143. In that particular example, it's explaining tcpdump, but same idea.

    It executes machine code on each packet that returns a true or false (typically). The code is in a virtual assembly language called BPF (or more commonly, eBPF which is the extended variant). The code only has access to the packet itself. So for example, if it tries to load address [12], it will load a byte at offset 12 in the packet. Although no real CPU directly executes BPF instructions, it does get compiled by the kernel into native machine code which gives it insane efficiency.

    Here's an example from my answer containing annotated BPF bytecode. This a filter that matches only ECN packets on the 192.168.1.0/24 subnet, excluding 192.168.1.100, with a size larger than 1000 bytes going to port 80 or 443:

    ; Check EtherType
    (000) ldh      [12]                              ; Load 2 byte EtherType from offset 12
    (001) jeq      #0x800           jt 2    jf 28    ; Is it IPv4? If yes, jump to next instruction, else jump to 28 (drop)
    
    ; Check IP protocol
    (002) ldb      [23]                              ; Load a single byte from IP header offset 9 (protocol field)
    (003) jeq      #0x6             jt 4    jf 28    ; Is it TCP (6)? If yes, jump to next instruction, else jump to 28 (drop)
    
    ; Skip fragmented IP datagrams
    (004) ldh      [20]                              ; Load 2 bytes from IP header offset 0 (flags and frag offset)
    (005) jset     #0x1fff          jt 28   jf 6     ; Are fragmentation flags set? If yes, the packet is incomplete; jump to 28 (drop), else jump to next instruction
    
    ; Calculate TCP header offset
    (006) ldxb     4*([14]&0xf)                      ; Load IP header length from offset 14, multiply by 4 and save result (TCP header start offset) in X
    
    ; Check source and destination port
    (007) ldh      [x + 14]                          ; Load TCP source port (14 bytes into the TCP header)
    (008) jeq      #0x50            jt 13   jf 9     ; Is it 80 (0x50)? If yes, jump past dest port checks and source port check for 443, else jump to next instruction
    (009) jeq      #0x1bb           jt 13   jf 10    ; Is it 443 (0x1bb)? If yes, jump past dest port checks, else jump to next instruction
    (010) ldh      [x + 16]                          ; Load TCP dest port (16 bytes into the TCP header)
    (011) jeq      #0x50            jt 13   jf 12    ; Is it 80? If yes, jump past the check for port 443, else jump to next instruction
    (012) jeq      #0x1bb           jt 13   jf 28    ; Is it 443? If yes, jump to next instruction, else jump to 28 (drop)
    
    ; Check source and destination address
    (013) ld       [26]                              ; Load 4 byte source IP from offset 26
    (014) and      #0xffffff00                       ; Mask last byte to isolate /24 subnet
    (015) jeq      #0xc0a80100      jt 19   jf 16    ; Is it in 192.168.1.0/24? If yes, jump past source/dest IP check, else jump to next instruction
    (016) ld       [30]                              ; Load 4 byte dest IP from offset 30
    (017) and      #0xffffff00                       ; Mask last byte to isolate /24 subnet
    (018) jeq      #0xc0a80100      jt 19   jf 28    ; Is it in 192.168.1.0/24? If yes, jump to next instruction, else jump to 28 (drop)
    
    ; Exclude host 192.168.1.100
    (019) ld       [26]                              ; Load source IP again
    (020) jeq      #0xc0a80164      jt 28   jf 21    ; Is it 192.168.1.100? If yes, jump to 28 (drop), else jump to next instruction
    (021) ld       [30]                              ; Load dest IP again
    (022) jeq      #0xc0a80164      jt 28   jf 23    ; Is it 192.168.1.100? If yes, jump to 28 (drop), else jump to next instruction
    
    ; Check ECN flags (ECE or CWR)
    (023) ldb      [x + 27]                          ; Load 1 byte TCP flags (27 bytes into TCP header)
    (024) jset     #0xc0            jt 25   jf 28    ; Is the ECE (0x40) or CWR (0x80) flag set? If yes, jump to next instruction, else jump to 28 (drop)
    
    ; Check packet length
    (025) ld       #pktlen                           ; Load 4 byte packet length
    (026) jgt      #0x3e8           jt 27   jf 28    ; Is it >1000 (0x3e8)? If yes, jump to next instruction (match), else jump to 28 (drop)
    
    ; Final decision
    (027) ret      #262144                           ; Return 262144 (match)
    (028) ret      #0                                ; Return 0 (drop)
    

    As you can see, it's not x86 assembly, it's BPF assembly. But it gets compiled into x86 (on x86 devices) when it's loaded so it runs at full speed. So even a very complex pass/drop decision being made on a packet can be done in literally a few dozen instructions. Something more abstract like NetFilter probably runs thousands of instructions for even the simplest decisions.

  • AlteredParadoxAlteredParadox Member, Megathread Squad

    @rpqu said:
    @AlteredParadox, you still got quota to burn?

    NOPE. lol

  • rpqurpqu Member

    @gatewaysentryllc said:

    @rpqu said:

    @gatewaysentryllc said:

    @forest said:

    @totally_not_banned said:

    @forest said:

    @totally_not_banned said: Kinda depends on who's being addressed i guess. I mean, there's a reason UFW is so far ahead. The average user usually isn't looking for something fancy but just wants to block a bunch of ports

    XDP is generally used for earlier filtering, so it actually augments UFW (and iptables and the like) rather than replacing it. It runs extremely early in the network stack which makes it good for DDoS protection so that packets can be discarded or redirected before it hits the much heavier (but more abstract and "useful") NetFilter code.

    Sure, i didn't say it was bad or necessarily do anything at all but just that it goes beyond what most users care about. People stick to UFW because they don't feel the need to go beyond that. I mean if there was a lot of stuff people were missing, there would be more people looking into iptables or nfttables, right?

    True, and unless you're handling many millions or billions of PPS, the equivalent ruleset for ufw, iptables, or nftables would be more than enough and XDP would be a premature micro-optimization.

    We did a benchmark using internal testing VM to VM,

    Ryzen 9950x
    32GB Ram
    MLX5 Interface 2x 100GbE (PCIe Gen 3, so roughly 120Gbps +-)
    iptables = 15Mil PPS
    xdp native = 94Mil PPS

    Currently we are using 9654, 7742, 9960x, PCIE Gen 4 NIC

    2x 100GbE CX6
    180M PPS Each

    With ZERO additive latency (under 1MS so its unreadable for us)

    While iptables added soo much latency, roughly 20MS +- (during load)

    Thanks for the data. I guess I'll have claude optimized my filter

    @AlteredParadox, you still got quota to burn?

    What are you currently using? I don't mind giving some assistance and advice!

    It's ufw with a little bit complexity around docker and allow/deny rules for ports.. The rules is generated using script + /etc\/hosts + local config file

    @forest said:

    @rpqu said: Thanks for the data. I guess I'll have claude optimized my filter

    LLMs are actually pretty bad at optimizing this kind of thing.

    :(

    @AlteredParadox said:

    @rpqu said:
    @AlteredParadox, you still got quota to burn?

    NOPE. lol

    Too bad. I got only two 5h limit left.

Sign In or Register to comment.