A custom iptables chain groups related firewall rules behind a named jump target. Use one when a built-in chain such as INPUT is becoming hard to read, or when several rules should be managed as one packet-handling block.

The default filter table contains the built-in INPUT, FORWARD, and OUTPUT chains. A user-defined chain created with --new-chain is empty and inactive until a rule in a built-in chain jumps to it. The example below creates SG_LOCAL_PING and sends loopback ICMP echo requests from INPUT into that chain.

Rule order still decides whether packets reach the custom chain. Insert the jump before a broader DROP or REJECT rule, keep the custom chain in the same table as its caller, and save the finished rules only after counters prove the expected traffic reaches the new chain.

Steps to create and connect a custom iptables chain:

  1. Check the current INPUT rule order before adding the jump.
    $ sudo iptables --list INPUT --line-numbers --verbose --numeric --exact
    Chain INPUT (policy ACCEPT 0 packets, 0 bytes)
    num      pkts      bytes target     prot opt in     out     source               destination
  2. Create the custom chain in the default filter table.
    $ sudo iptables --new-chain SG_LOCAL_PING

    No output means the chain was created. Use the same --table value on the custom chain and on the parent-chain jump when working outside the default filter table.

  3. Add the rule that belongs inside the custom chain.
    $ sudo iptables --append SG_LOCAL_PING --protocol icmp --icmp-type echo-request --jump ACCEPT
  4. Insert a jump from INPUT to the custom chain at line 1.
    $ sudo iptables --insert INPUT 1 --in-interface lo --protocol icmp --icmp-type echo-request --jump SG_LOCAL_PING

    Place the jump before any broader rule that would accept, drop, or reject the same packets first.

  5. Confirm the custom chain contains the expected rule.
    $ sudo iptables --list-rules SG_LOCAL_PING
    -N SG_LOCAL_PING
    -A SG_LOCAL_PING -p icmp -m icmp --icmp-type 8 -j ACCEPT
  6. Confirm the parent chain jumps to the custom chain.
    $ sudo iptables --list-rules INPUT
    -P INPUT ACCEPT
    -A INPUT -i lo -p icmp -m icmp --icmp-type 8 -j SG_LOCAL_PING
  7. Send one loopback ICMP packet that should match the jump.
    $ ping -c 1 127.0.0.1
    PING 127.0.0.1 (127.0.0.1) 56(84) bytes of data.
    64 bytes from 127.0.0.1: icmp_seq=1 ttl=64 time=0.057 ms
    
    --- 127.0.0.1 ping statistics ---
    1 packets transmitted, 1 received, 0% packet loss, time 0ms
    rtt min/avg/max/mdev = 0.057/0.057/0.057/0.000 ms
  8. Check the custom-chain counters after the test packet.
    $ sudo iptables --list SG_LOCAL_PING --verbose --numeric --exact
    Chain SG_LOCAL_PING (1 references)
        pkts      bytes target     prot opt in     out     source               destination
           1       84 ACCEPT     icmp --  *      *       0.0.0.0/0            0.0.0.0/0            icmptype 8

    The nonzero pkts and bytes values confirm that the parent jump reached the custom chain.

  9. Save the rules with your distribution's persistence method if the chain should remain after reboot.