2021-08-23 13:14:53 +00:00
//go:build linux
2021-05-28 18:53:49 +00:00
// +build linux
2015-05-06 23:47:41 +00:00
package iptables
import (
2023-06-23 00:33:17 +00:00
"context"
2015-05-06 23:47:41 +00:00
"errors"
"fmt"
"net"
"os/exec"
"strconv"
"strings"
2015-05-13 03:27:22 +00:00
"sync"
2018-04-11 15:46:18 +00:00
"time"
2015-05-06 23:47:41 +00:00
2023-06-23 00:33:17 +00:00
"github.com/containerd/containerd/log"
2023-01-03 12:08:40 +00:00
"github.com/docker/docker/pkg/rootless"
2015-05-06 23:47:41 +00:00
)
2015-05-18 23:49:12 +00:00
// Action signifies the iptable action.
2015-05-06 23:47:41 +00:00
type Action string
2016-10-28 20:54:52 +00:00
// Policy is the default iptable policies
type Policy string
2015-05-18 23:49:12 +00:00
// Table refers to Nat, Filter or Mangle.
2015-05-06 23:47:41 +00:00
type Table string
2017-11-28 21:15:55 +00:00
// IPVersion refers to IP version, v4 or v6
type IPVersion string
2015-05-06 23:47:41 +00:00
const (
2015-05-18 23:49:12 +00:00
// Append appends the rule at the end of the chain.
2015-05-06 23:47:41 +00:00
Append Action = "-A"
2015-05-18 23:49:12 +00:00
// Delete deletes the rule from the chain.
2015-05-06 23:47:41 +00:00
Delete Action = "-D"
2015-05-18 23:49:12 +00:00
// Insert inserts the rule at the top of the chain.
2015-05-06 23:47:41 +00:00
Insert Action = "-I"
2015-05-18 23:49:12 +00:00
// Nat table is used for nat translation rules.
2015-05-06 23:47:41 +00:00
Nat Table = "nat"
2015-05-18 23:49:12 +00:00
// Filter table is used for filter rules.
2015-05-06 23:47:41 +00:00
Filter Table = "filter"
2015-05-18 23:49:12 +00:00
// Mangle table is used for mangling the packet.
2015-05-06 23:47:41 +00:00
Mangle Table = "mangle"
2016-10-28 20:54:52 +00:00
// Drop is the default iptables DROP policy
Drop Policy = "DROP"
// Accept is the default iptables ACCEPT policy
Accept Policy = "ACCEPT"
2017-11-28 21:15:55 +00:00
// IPv4 is version 4
IPv4 IPVersion = "IPV4"
// IPv6 is version 6
IPv6 IPVersion = "IPV6"
2015-05-06 23:47:41 +00:00
)
var (
iptablesPath string
2017-11-28 21:15:55 +00:00
ip6tablesPath string
2015-05-06 23:47:41 +00:00
supportsXlock = false
2018-04-11 15:46:18 +00:00
xLockWaitMsg = "Another app is currently holding the xtables lock"
2015-05-13 03:27:22 +00:00
// used to lock iptables commands if xtables lock is not supported
bestEffortLock sync . Mutex
2023-07-05 14:51:01 +00:00
initOnce sync . Once
2015-05-06 23:47:41 +00:00
)
2020-07-22 13:08:22 +00:00
// IPTable defines struct with IPVersion
2017-11-28 21:15:55 +00:00
type IPTable struct {
Version IPVersion
}
2015-06-12 01:12:00 +00:00
// ChainInfo defines the iptables chain.
type ChainInfo struct {
2015-06-11 23:53:20 +00:00
Name string
Table Table
HairpinMode bool
2023-07-05 13:41:16 +00:00
IPVersion IPVersion
2015-05-06 23:47:41 +00:00
}
2015-05-18 23:49:12 +00:00
// ChainError is returned to represent errors during ip table operation.
2015-05-06 23:47:41 +00:00
type ChainError struct {
Chain string
Output [ ] byte
}
func ( e ChainError ) Error ( ) string {
2023-07-05 14:53:19 +00:00
return fmt . Sprintf ( "error iptables %s: %s" , e . Chain , string ( e . Output ) )
2015-05-06 23:47:41 +00:00
}
2017-03-08 18:37:13 +00:00
func detectIptables ( ) {
2019-03-26 19:55:49 +00:00
path , err := exec . LookPath ( "iptables" )
if err != nil {
2023-06-23 00:33:17 +00:00
log . G ( context . TODO ( ) ) . WithError ( err ) . Warnf ( "failed to find iptables" )
2019-03-26 19:55:49 +00:00
return
2016-03-21 20:40:43 +00:00
}
2017-03-08 18:37:13 +00:00
iptablesPath = path
2021-12-06 00:03:14 +00:00
2021-12-06 01:23:42 +00:00
// The --wait flag was added in iptables v1.6.0.
// TODO remove this check once we drop support for CentOS/RHEL 7, which uses an older version of iptables
2021-12-06 00:32:42 +00:00
if out , err := exec . Command ( path , "--wait" , "-L" , "-n" ) . CombinedOutput ( ) ; err != nil {
2023-06-23 00:33:17 +00:00
log . G ( context . TODO ( ) ) . WithError ( err ) . Infof ( "unable to detect if iptables supports xlock: 'iptables --wait -L -n': `%s`" , strings . TrimSpace ( string ( out ) ) )
2021-12-06 00:32:42 +00:00
} else {
supportsXlock = true
2016-03-21 20:40:43 +00:00
}
2021-12-06 00:32:42 +00:00
2021-12-06 00:03:14 +00:00
path , err = exec . LookPath ( "ip6tables" )
2017-11-28 21:15:55 +00:00
if err != nil {
2023-06-23 00:33:17 +00:00
log . G ( context . TODO ( ) ) . WithError ( err ) . Warnf ( "unable to find ip6tables" )
2021-12-06 00:03:14 +00:00
} else {
ip6tablesPath = path
2017-11-28 21:15:55 +00:00
}
2016-03-21 20:40:43 +00:00
}
func initFirewalld ( ) {
2022-07-11 00:48:18 +00:00
// When running with RootlessKit, firewalld is running as the root outside our network namespace
// https://github.com/moby/moby/issues/43781
if rootless . RunningWithRootlessKit ( ) {
2023-06-23 00:33:17 +00:00
log . G ( context . TODO ( ) ) . Info ( "skipping firewalld management for rootless mode" )
2022-07-11 00:48:18 +00:00
return
}
2016-03-21 20:40:43 +00:00
if err := FirewalldInit ( ) ; err != nil {
2023-06-23 00:33:17 +00:00
log . G ( context . TODO ( ) ) . WithError ( err ) . Debugf ( "unable to initialize firewalld; using raw iptables instead" )
2017-11-28 21:15:55 +00:00
}
2017-03-08 18:37:13 +00:00
}
2017-04-06 19:26:08 +00:00
func initDependencies ( ) {
2017-03-08 18:37:13 +00:00
initFirewalld ( )
detectIptables ( )
}
2015-05-06 23:47:41 +00:00
func initCheck ( ) error {
2017-04-06 19:26:08 +00:00
initOnce . Do ( initDependencies )
2017-03-08 18:37:13 +00:00
2015-05-06 23:47:41 +00:00
if iptablesPath == "" {
2023-07-05 14:51:01 +00:00
return errors . New ( "iptables not found" )
2015-05-06 23:47:41 +00:00
}
return nil
}
2017-11-28 21:15:55 +00:00
// GetIptable returns an instance of IPTable with specified version
func GetIptable ( version IPVersion ) * IPTable {
return & IPTable { Version : version }
}
2015-05-18 23:49:12 +00:00
// NewChain adds a new chain to ip table.
2017-11-28 21:15:55 +00:00
func ( iptable IPTable ) NewChain ( name string , table Table , hairpinMode bool ) ( * ChainInfo , error ) {
2023-07-05 13:18:01 +00:00
if table == "" {
table = Filter
2015-05-06 23:47:41 +00:00
}
// Add chain if it doesn't exist
2023-07-05 13:18:01 +00:00
if _ , err := iptable . Raw ( "-t" , string ( table ) , "-n" , "-L" , name ) ; err != nil {
if output , err := iptable . Raw ( "-t" , string ( table ) , "-N" , name ) ; err != nil {
2015-05-06 23:47:41 +00:00
return nil , err
} else if len ( output ) != 0 {
2023-07-05 13:18:01 +00:00
return nil , fmt . Errorf ( "could not create %s/%s chain: %s" , table , name , output )
2015-05-06 23:47:41 +00:00
}
}
2023-07-05 13:18:01 +00:00
return & ChainInfo {
Name : name ,
Table : table ,
HairpinMode : hairpinMode ,
2023-07-05 13:41:16 +00:00
IPVersion : iptable . Version ,
2023-07-05 13:18:01 +00:00
} , nil
2015-06-12 01:12:00 +00:00
}
2017-11-28 21:15:55 +00:00
// LoopbackByVersion returns loopback address by version
func ( iptable IPTable ) LoopbackByVersion ( ) string {
if iptable . Version == IPv6 {
return "::1/128"
}
return "127.0.0.0/8"
}
2015-06-12 01:12:00 +00:00
// ProgramChain is used to add rules to a chain
2017-11-28 21:15:55 +00:00
func ( iptable IPTable ) ProgramChain ( c * ChainInfo , bridgeName string , hairpinMode , enable bool ) error {
2015-06-12 01:12:00 +00:00
if c . Name == "" {
2023-07-05 14:53:19 +00:00
return errors . New ( "could not program chain, missing chain name" )
2015-06-12 01:12:00 +00:00
}
2015-05-06 23:47:41 +00:00
2020-05-04 20:51:42 +00:00
// Either add or remove the interface from the firewalld zone
if firewalldRunning {
if enable {
if err := AddInterfaceFirewalld ( bridgeName ) ; err != nil {
return err
}
} else {
if err := DelInterfaceFirewalld ( bridgeName ) ; err != nil {
return err
}
}
}
2015-06-12 01:12:00 +00:00
switch c . Table {
2015-05-06 23:47:41 +00:00
case Nat :
preroute := [ ] string {
"-m" , "addrtype" ,
2015-06-16 06:55:29 +00:00
"--dst-type" , "LOCAL" ,
2022-01-20 13:05:08 +00:00
"-j" , c . Name ,
}
2017-11-28 21:15:55 +00:00
if ! iptable . Exists ( Nat , "PREROUTING" , preroute ... ) && enable {
2015-05-06 23:47:41 +00:00
if err := c . Prerouting ( Append , preroute ... ) ; err != nil {
2023-07-05 14:53:19 +00:00
return fmt . Errorf ( "failed to inject %s in PREROUTING chain: %s" , c . Name , err )
2015-05-06 23:47:41 +00:00
}
2017-11-28 21:15:55 +00:00
} else if iptable . Exists ( Nat , "PREROUTING" , preroute ... ) && ! enable {
2015-10-05 05:53:45 +00:00
if err := c . Prerouting ( Delete , preroute ... ) ; err != nil {
2023-07-05 14:53:19 +00:00
return fmt . Errorf ( "failed to remove %s in PREROUTING chain: %s" , c . Name , err )
2015-10-05 05:53:45 +00:00
}
2015-05-06 23:47:41 +00:00
}
output := [ ] string {
"-m" , "addrtype" ,
2015-06-16 06:55:29 +00:00
"--dst-type" , "LOCAL" ,
2022-01-20 13:05:08 +00:00
"-j" , c . Name ,
}
2015-05-18 23:49:12 +00:00
if ! hairpinMode {
2017-11-28 21:15:55 +00:00
output = append ( output , "!" , "--dst" , iptable . LoopbackByVersion ( ) )
2015-05-18 23:49:12 +00:00
}
2017-11-28 21:15:55 +00:00
if ! iptable . Exists ( Nat , "OUTPUT" , output ... ) && enable {
2015-05-06 23:47:41 +00:00
if err := c . Output ( Append , output ... ) ; err != nil {
2023-07-05 14:53:19 +00:00
return fmt . Errorf ( "failed to inject %s in OUTPUT chain: %s" , c . Name , err )
2015-05-06 23:47:41 +00:00
}
2017-11-28 21:15:55 +00:00
} else if iptable . Exists ( Nat , "OUTPUT" , output ... ) && ! enable {
2015-10-05 05:53:45 +00:00
if err := c . Output ( Delete , output ... ) ; err != nil {
2023-07-05 14:53:19 +00:00
return fmt . Errorf ( "failed to inject %s in OUTPUT chain: %s" , c . Name , err )
2015-10-05 05:53:45 +00:00
}
2015-05-06 23:47:41 +00:00
}
case Filter :
2015-06-12 01:12:00 +00:00
if bridgeName == "" {
2023-07-05 14:53:19 +00:00
return fmt . Errorf ( "could not program chain %s/%s, missing bridge name" , c . Table , c . Name )
2015-06-12 01:12:00 +00:00
}
2015-05-06 23:47:41 +00:00
link := [ ] string {
2015-06-12 01:12:00 +00:00
"-o" , bridgeName ,
2022-01-20 13:05:08 +00:00
"-j" , c . Name ,
}
2017-11-28 21:15:55 +00:00
if ! iptable . Exists ( Filter , "FORWARD" , link ... ) && enable {
2015-05-06 23:47:41 +00:00
insert := append ( [ ] string { string ( Insert ) , "FORWARD" } , link ... )
2017-11-28 21:15:55 +00:00
if output , err := iptable . Raw ( insert ... ) ; err != nil {
2015-06-12 01:12:00 +00:00
return err
2015-05-06 23:47:41 +00:00
} else if len ( output ) != 0 {
2023-07-05 14:53:19 +00:00
return fmt . Errorf ( "could not create linking rule to %s/%s: %s" , c . Table , c . Name , output )
2015-05-06 23:47:41 +00:00
}
2017-11-28 21:15:55 +00:00
} else if iptable . Exists ( Filter , "FORWARD" , link ... ) && ! enable {
2015-10-05 05:53:45 +00:00
del := append ( [ ] string { string ( Delete ) , "FORWARD" } , link ... )
2017-11-28 21:15:55 +00:00
if output , err := iptable . Raw ( del ... ) ; err != nil {
2015-10-05 05:53:45 +00:00
return err
} else if len ( output ) != 0 {
2023-07-05 14:53:19 +00:00
return fmt . Errorf ( "could not delete linking rule from %s/%s: %s" , c . Table , c . Name , output )
2015-10-05 05:53:45 +00:00
}
2015-05-06 23:47:41 +00:00
}
2016-02-24 12:43:32 +00:00
establish := [ ] string {
"-o" , bridgeName ,
"-m" , "conntrack" ,
"--ctstate" , "RELATED,ESTABLISHED" ,
2022-01-20 13:05:08 +00:00
"-j" , "ACCEPT" ,
}
2017-11-28 21:15:55 +00:00
if ! iptable . Exists ( Filter , "FORWARD" , establish ... ) && enable {
2016-02-24 12:43:32 +00:00
insert := append ( [ ] string { string ( Insert ) , "FORWARD" } , establish ... )
2017-11-28 21:15:55 +00:00
if output , err := iptable . Raw ( insert ... ) ; err != nil {
2016-02-24 12:43:32 +00:00
return err
} else if len ( output ) != 0 {
2023-07-05 14:53:19 +00:00
return fmt . Errorf ( "could not create establish rule to %s: %s" , c . Table , output )
2016-02-24 12:43:32 +00:00
}
2017-11-28 21:15:55 +00:00
} else if iptable . Exists ( Filter , "FORWARD" , establish ... ) && ! enable {
2016-02-24 12:43:32 +00:00
del := append ( [ ] string { string ( Delete ) , "FORWARD" } , establish ... )
2017-11-28 21:15:55 +00:00
if output , err := iptable . Raw ( del ... ) ; err != nil {
2016-02-24 12:43:32 +00:00
return err
} else if len ( output ) != 0 {
2023-07-05 14:53:19 +00:00
return fmt . Errorf ( "could not delete establish rule from %s: %s" , c . Table , output )
2016-02-24 12:43:32 +00:00
}
}
2015-05-06 23:47:41 +00:00
}
2015-06-12 01:12:00 +00:00
return nil
2015-05-06 23:47:41 +00:00
}
2015-05-18 23:49:12 +00:00
// RemoveExistingChain removes existing chain from the table.
2017-11-28 21:15:55 +00:00
func ( iptable IPTable ) RemoveExistingChain ( name string , table Table ) error {
2023-07-05 13:38:57 +00:00
if table == "" {
table = Filter
}
2015-06-12 01:12:00 +00:00
c := & ChainInfo {
2023-07-05 13:41:16 +00:00
Name : name ,
Table : table ,
IPVersion : iptable . Version ,
2015-05-06 23:47:41 +00:00
}
return c . Remove ( )
}
2015-05-18 23:49:12 +00:00
// Forward adds forwarding rule to 'filter' table and corresponding nat rule to 'nat' table.
2015-06-12 01:12:00 +00:00
func ( c * ChainInfo ) Forward ( action Action , ip net . IP , port int , proto , destAddr string , destPort int , bridgeName string ) error {
2023-07-05 13:41:16 +00:00
iptable := GetIptable ( c . IPVersion )
2015-05-06 23:47:41 +00:00
daddr := ip . String ( )
if ip . IsUnspecified ( ) {
// iptables interprets "0.0.0.0" as "0.0.0.0/32", whereas we
// want "0.0.0.0/0". "0/0" is correctly interpreted as "any
// value" by both iptables and ip6tables.
daddr = "0/0"
}
2016-09-30 18:08:38 +00:00
args := [ ] string {
2015-05-06 23:47:41 +00:00
"-p" , proto ,
"-d" , daddr ,
"--dport" , strconv . Itoa ( port ) ,
"-j" , "DNAT" ,
2022-01-20 13:05:08 +00:00
"--to-destination" , net . JoinHostPort ( destAddr , strconv . Itoa ( destPort ) ) ,
}
2017-11-28 21:15:55 +00:00
2015-06-11 23:53:20 +00:00
if ! c . HairpinMode {
2015-06-12 01:12:00 +00:00
args = append ( args , "!" , "-i" , bridgeName )
2015-06-11 23:53:20 +00:00
}
2017-11-28 21:15:55 +00:00
if err := iptable . ProgramRule ( Nat , c . Name , action , args ) ; err != nil {
2015-05-06 23:47:41 +00:00
return err
}
2016-09-30 18:08:38 +00:00
args = [ ] string {
2015-06-12 01:12:00 +00:00
"!" , "-i" , bridgeName ,
"-o" , bridgeName ,
2015-05-06 23:47:41 +00:00
"-p" , proto ,
"-d" , destAddr ,
"--dport" , strconv . Itoa ( destPort ) ,
2016-09-30 18:08:38 +00:00
"-j" , "ACCEPT" ,
}
2017-11-28 21:15:55 +00:00
if err := iptable . ProgramRule ( Filter , c . Name , action , args ) ; err != nil {
2015-05-06 23:47:41 +00:00
return err
}
2016-09-30 18:08:38 +00:00
args = [ ] string {
2015-05-06 23:47:41 +00:00
"-p" , proto ,
"-s" , destAddr ,
"-d" , destAddr ,
"--dport" , strconv . Itoa ( destPort ) ,
2016-09-30 18:08:38 +00:00
"-j" , "MASQUERADE" ,
}
2017-06-13 05:29:56 +00:00
2017-11-28 21:15:55 +00:00
if err := iptable . ProgramRule ( Nat , "POSTROUTING" , action , args ) ; err != nil {
2017-06-13 05:29:56 +00:00
return err
}
if proto == "sctp" {
// Linux kernel v4.9 and below enables NETIF_F_SCTP_CRC for veth by
// the following commit.
// This introduces a problem when conbined with a physical NIC without
// NETIF_F_SCTP_CRC. As for a workaround, here we add an iptables entry
// to fill the checksum.
//
// https://github.com/torvalds/linux/commit/c80fafbbb59ef9924962f83aac85531039395b18
args = [ ] string {
"-p" , proto ,
"--sport" , strconv . Itoa ( destPort ) ,
"-j" , "CHECKSUM" ,
"--checksum-fill" ,
}
2017-11-28 21:15:55 +00:00
if err := iptable . ProgramRule ( Mangle , "POSTROUTING" , action , args ) ; err != nil {
2017-06-13 05:29:56 +00:00
return err
}
}
return nil
2015-05-06 23:47:41 +00:00
}
2015-05-18 23:49:12 +00:00
// Link adds reciprocal ACCEPT rule for two supplied IP addresses.
2015-05-06 23:47:41 +00:00
// Traffic is allowed from ip1 to ip2 and vice-versa
2015-06-12 01:12:00 +00:00
func ( c * ChainInfo ) Link ( action Action , ip1 , ip2 net . IP , port int , proto string , bridgeName string ) error {
2023-07-05 13:41:16 +00:00
iptable := GetIptable ( c . IPVersion )
2016-09-30 18:08:38 +00:00
// forward
args := [ ] string {
2015-06-12 01:12:00 +00:00
"-i" , bridgeName , "-o" , bridgeName ,
2015-05-06 23:47:41 +00:00
"-p" , proto ,
"-s" , ip1 . String ( ) ,
"-d" , ip2 . String ( ) ,
"--dport" , strconv . Itoa ( port ) ,
2016-09-30 18:08:38 +00:00
"-j" , "ACCEPT" ,
}
2017-11-28 21:15:55 +00:00
if err := iptable . ProgramRule ( Filter , c . Name , action , args ) ; err != nil {
2015-05-06 23:47:41 +00:00
return err
}
2016-09-30 18:08:38 +00:00
// reverse
args [ 7 ] , args [ 9 ] = args [ 9 ] , args [ 7 ]
args [ 10 ] = "--sport"
2017-11-28 21:15:55 +00:00
return iptable . ProgramRule ( Filter , c . Name , action , args )
2015-05-06 23:47:41 +00:00
}
2016-09-30 18:08:38 +00:00
// ProgramRule adds the rule specified by args only if the
// rule is not already present in the chain. Reciprocally,
// it removes the rule only if present.
2017-11-28 21:15:55 +00:00
func ( iptable IPTable ) ProgramRule ( table Table , chain string , action Action , args [ ] string ) error {
if iptable . Exists ( table , chain , args ... ) != ( action == Delete ) {
2016-09-30 18:08:38 +00:00
return nil
}
2017-11-28 21:15:55 +00:00
return iptable . RawCombinedOutput ( append ( [ ] string { "-t" , string ( table ) , string ( action ) , chain } , args ... ) ... )
2016-09-30 18:08:38 +00:00
}
2015-05-18 23:49:12 +00:00
// Prerouting adds linking rule to nat/PREROUTING chain.
2015-06-12 01:12:00 +00:00
func ( c * ChainInfo ) Prerouting ( action Action , args ... string ) error {
2023-07-05 13:41:16 +00:00
iptable := GetIptable ( c . IPVersion )
2015-05-06 23:47:41 +00:00
a := [ ] string { "-t" , string ( Nat ) , string ( action ) , "PREROUTING" }
if len ( args ) > 0 {
a = append ( a , args ... )
}
2017-11-28 21:15:55 +00:00
if output , err := iptable . Raw ( a ... ) ; err != nil {
2015-05-06 23:47:41 +00:00
return err
} else if len ( output ) != 0 {
return ChainError { Chain : "PREROUTING" , Output : output }
}
return nil
}
2015-05-18 23:49:12 +00:00
// Output adds linking rule to an OUTPUT chain.
2015-06-12 01:12:00 +00:00
func ( c * ChainInfo ) Output ( action Action , args ... string ) error {
2015-05-06 23:47:41 +00:00
a := [ ] string { "-t" , string ( c . Table ) , string ( action ) , "OUTPUT" }
if len ( args ) > 0 {
a = append ( a , args ... )
}
2023-07-05 13:41:16 +00:00
if output , err := GetIptable ( c . IPVersion ) . Raw ( a ... ) ; err != nil {
2015-05-06 23:47:41 +00:00
return err
} else if len ( output ) != 0 {
return ChainError { Chain : "OUTPUT" , Output : output }
}
return nil
}
2015-05-18 23:49:12 +00:00
// Remove removes the chain.
2015-06-12 01:12:00 +00:00
func ( c * ChainInfo ) Remove ( ) error {
2023-07-05 13:41:16 +00:00
iptable := GetIptable ( c . IPVersion )
2015-05-06 23:47:41 +00:00
// Ignore errors - This could mean the chains were never set up
if c . Table == Nat {
2023-07-05 13:27:53 +00:00
_ = c . Prerouting ( Delete , "-m" , "addrtype" , "--dst-type" , "LOCAL" , "-j" , c . Name )
_ = c . Output ( Delete , "-m" , "addrtype" , "--dst-type" , "LOCAL" , "!" , "--dst" , iptable . LoopbackByVersion ( ) , "-j" , c . Name )
_ = c . Output ( Delete , "-m" , "addrtype" , "--dst-type" , "LOCAL" , "-j" , c . Name ) // Created in versions <= 0.1.6
_ = c . Prerouting ( Delete )
_ = c . Output ( Delete )
2015-05-06 23:47:41 +00:00
}
2023-07-05 13:27:53 +00:00
_ , _ = iptable . Raw ( "-t" , string ( c . Table ) , "-F" , c . Name )
_ , _ = iptable . Raw ( "-t" , string ( c . Table ) , "-X" , c . Name )
2015-05-06 23:47:41 +00:00
return nil
}
2015-05-18 23:49:12 +00:00
// Exists checks if a rule exists
2017-11-28 21:15:55 +00:00
func ( iptable IPTable ) Exists ( table Table , chain string , rule ... string ) bool {
return iptable . exists ( false , table , chain , rule ... )
2016-10-05 07:04:05 +00:00
}
// ExistsNative behaves as Exists with the difference it
// will always invoke `iptables` binary.
2017-11-28 21:15:55 +00:00
func ( iptable IPTable ) ExistsNative ( table Table , chain string , rule ... string ) bool {
return iptable . exists ( true , table , chain , rule ... )
2016-10-05 07:04:05 +00:00
}
2017-11-28 21:15:55 +00:00
func ( iptable IPTable ) exists ( native bool , table Table , chain string , rule ... string ) bool {
2023-07-05 13:26:23 +00:00
if err := initCheck ( ) ; err != nil {
// The exists() signature does not allow us to return an error, but at least
// we can skip the (likely invalid) exec invocation.
return false
}
2017-11-28 21:15:55 +00:00
f := iptable . Raw
2016-10-05 07:04:05 +00:00
if native {
2017-11-28 21:15:55 +00:00
f = iptable . raw
2016-10-05 07:04:05 +00:00
}
2023-07-05 13:26:23 +00:00
if table == "" {
2015-05-06 23:47:41 +00:00
table = Filter
}
2021-12-06 01:23:42 +00:00
// if exit status is 0 then return true, the rule exists
_ , err := f ( append ( [ ] string { "-t" , string ( table ) , "-C" , chain } , rule ... ) ... )
return err == nil
2015-05-06 23:47:41 +00:00
}
2018-04-11 15:46:18 +00:00
// Maximum duration that an iptables operation can take
// before flagging a warning.
const opWarnTime = 2 * time . Second
func filterOutput ( start time . Time , output [ ] byte , args ... string ) [ ] byte {
// Flag operations that have taken a long time to complete
2018-04-26 14:19:26 +00:00
opTime := time . Since ( start )
if opTime > opWarnTime {
2023-06-23 00:33:17 +00:00
log . G ( context . TODO ( ) ) . Warnf ( "xtables contention detected while running [%s]: Waited for %.2f seconds and received %q" , strings . Join ( args , " " ) , float64 ( opTime ) / float64 ( time . Second ) , string ( output ) )
2018-04-11 15:46:18 +00:00
}
// ignore iptables' message about xtables lock:
// it is a warning, not an error.
if strings . Contains ( string ( output ) , xLockWaitMsg ) {
output = [ ] byte ( "" )
}
// Put further filters here if desired
return output
}
2015-05-18 23:49:12 +00:00
// Raw calls 'iptables' system command, passing supplied arguments.
2017-11-28 21:15:55 +00:00
func ( iptable IPTable ) Raw ( args ... string ) ( [ ] byte , error ) {
2015-05-06 23:47:41 +00:00
if firewalldRunning {
2021-01-07 16:46:32 +00:00
// select correct IP version for firewalld
ipv := Iptables
if iptable . Version == IPv6 {
ipv = IP6Tables
}
2018-04-11 15:46:18 +00:00
startTime := time . Now ( )
2021-01-07 16:46:32 +00:00
output , err := Passthrough ( ipv , args ... )
2015-05-06 23:47:41 +00:00
if err == nil || ! strings . Contains ( err . Error ( ) , "was not provided by any .service files" ) {
2018-04-11 15:46:18 +00:00
return filterOutput ( startTime , output , args ... ) , err
2015-05-06 23:47:41 +00:00
}
}
2017-11-28 21:15:55 +00:00
return iptable . raw ( args ... )
2016-02-05 18:34:48 +00:00
}
2015-05-06 23:47:41 +00:00
2017-11-28 21:15:55 +00:00
func ( iptable IPTable ) raw ( args ... string ) ( [ ] byte , error ) {
2015-05-06 23:47:41 +00:00
if err := initCheck ( ) ; err != nil {
return nil , err
}
if supportsXlock {
args = append ( [ ] string { "--wait" } , args ... )
2015-05-13 03:27:22 +00:00
} else {
bestEffortLock . Lock ( )
defer bestEffortLock . Unlock ( )
2015-05-06 23:47:41 +00:00
}
2017-11-28 21:15:55 +00:00
path := iptablesPath
2020-12-11 10:10:55 +00:00
commandName := "iptables"
2017-11-28 21:15:55 +00:00
if iptable . Version == IPv6 {
2021-12-06 00:32:42 +00:00
if ip6tablesPath == "" {
return nil , fmt . Errorf ( "ip6tables is missing" )
}
2017-11-28 21:15:55 +00:00
path = ip6tablesPath
2020-12-11 10:10:55 +00:00
commandName = "ip6tables"
2017-11-28 21:15:55 +00:00
}
2023-06-23 00:33:17 +00:00
log . G ( context . TODO ( ) ) . Debugf ( "%s, %v" , path , args )
2015-05-06 23:47:41 +00:00
2018-04-11 15:46:18 +00:00
startTime := time . Now ( )
2017-11-28 21:15:55 +00:00
output , err := exec . Command ( path , args ... ) . CombinedOutput ( )
2015-05-06 23:47:41 +00:00
if err != nil {
2020-12-11 10:10:55 +00:00
return nil , fmt . Errorf ( "iptables failed: %s %v: %s (%s)" , commandName , strings . Join ( args , " " ) , output , err )
2015-05-06 23:47:41 +00:00
}
2018-04-11 15:46:18 +00:00
return filterOutput ( startTime , output , args ... ) , err
2015-05-06 23:47:41 +00:00
}
2015-12-15 05:57:56 +00:00
2017-05-22 02:25:52 +00:00
// RawCombinedOutput internally calls the Raw function and returns a non nil
2015-12-15 05:57:56 +00:00
// error if Raw returned a non nil error or a non empty output
2017-11-28 21:15:55 +00:00
func ( iptable IPTable ) RawCombinedOutput ( args ... string ) error {
if output , err := iptable . Raw ( args ... ) ; err != nil || len ( output ) != 0 {
2015-12-15 05:57:56 +00:00
return fmt . Errorf ( "%s (%v)" , string ( output ) , err )
}
return nil
}
2015-09-15 08:30:10 +00:00
2016-02-05 18:34:48 +00:00
// RawCombinedOutputNative behave as RawCombinedOutput with the difference it
// will always invoke `iptables` binary
2017-11-28 21:15:55 +00:00
func ( iptable IPTable ) RawCombinedOutputNative ( args ... string ) error {
if output , err := iptable . raw ( args ... ) ; err != nil || len ( output ) != 0 {
2016-02-05 18:34:48 +00:00
return fmt . Errorf ( "%s (%v)" , string ( output ) , err )
}
return nil
}
2015-09-15 08:30:10 +00:00
// ExistChain checks if a chain exists
2017-11-28 21:15:55 +00:00
func ( iptable IPTable ) ExistChain ( chain string , table Table ) bool {
2023-07-07 07:29:59 +00:00
_ , err := iptable . Raw ( "-t" , string ( table ) , "-nL" , chain )
return err == nil
2015-09-15 08:30:10 +00:00
}
2016-02-02 20:52:34 +00:00
2016-10-28 20:54:52 +00:00
// SetDefaultPolicy sets the passed default policy for the table/chain
2017-11-28 21:15:55 +00:00
func ( iptable IPTable ) SetDefaultPolicy ( table Table , chain string , policy Policy ) error {
if err := iptable . RawCombinedOutput ( "-t" , string ( table ) , "-P" , chain , string ( policy ) ) ; err != nil {
2016-10-28 20:54:52 +00:00
return fmt . Errorf ( "setting default policy to %v in %v chain failed: %v" , policy , chain , err )
}
return nil
}
2017-03-13 07:46:46 +00:00
// AddReturnRule adds a return rule for the chain in the filter table
2017-11-28 21:15:55 +00:00
func ( iptable IPTable ) AddReturnRule ( chain string ) error {
2023-07-05 13:00:08 +00:00
if iptable . Exists ( Filter , chain , "-j" , "RETURN" ) {
2017-03-13 07:46:46 +00:00
return nil
}
2023-07-05 13:00:08 +00:00
err := iptable . RawCombinedOutput ( "-A" , chain , "-j" , "RETURN" )
2017-03-13 07:46:46 +00:00
if err != nil {
2023-07-05 13:20:13 +00:00
return fmt . Errorf ( "unable to add return rule in %s chain: %v" , chain , err )
2017-03-13 07:46:46 +00:00
}
return nil
}
// EnsureJumpRule ensures the jump rule is on top
2017-11-28 21:15:55 +00:00
func ( iptable IPTable ) EnsureJumpRule ( fromChain , toChain string ) error {
2023-07-05 13:00:08 +00:00
if iptable . Exists ( Filter , fromChain , "-j" , toChain ) {
err := iptable . RawCombinedOutput ( "-D" , fromChain , "-j" , toChain )
2017-03-13 07:46:46 +00:00
if err != nil {
2023-07-05 13:20:13 +00:00
return fmt . Errorf ( "unable to remove jump to %s rule in %s chain: %v" , toChain , fromChain , err )
2017-03-13 07:46:46 +00:00
}
}
2023-07-05 13:00:08 +00:00
err := iptable . RawCombinedOutput ( "-I" , fromChain , "-j" , toChain )
2017-03-13 07:46:46 +00:00
if err != nil {
2023-07-05 13:20:13 +00:00
return fmt . Errorf ( "unable to insert jump to %s rule in %s chain: %v" , toChain , fromChain , err )
2017-03-13 07:46:46 +00:00
}
return nil
}