55 "fmt"
66 "net"
77 "os/exec"
8+ "regexp"
89 "strconv"
910 "strings"
1011 "sync"
@@ -36,6 +37,7 @@ const (
3637var (
3738 iptablesPath string
3839 supportsXlock = false
40+ supportsCOpt = false
3941 // used to lock iptables commands if xtables lock is not supported
4042 bestEffortLock sync.Mutex
4143 // ErrIptablesNotFound is returned when the rule is not found.
@@ -60,14 +62,19 @@ func (e ChainError) Error() string {
6062}
6163
6264func initCheck () error {
63-
6465 if iptablesPath == "" {
6566 path , err := exec .LookPath ("iptables" )
6667 if err != nil {
6768 return ErrIptablesNotFound
6869 }
6970 iptablesPath = path
7071 supportsXlock = exec .Command (iptablesPath , "--wait" , "-L" , "-n" ).Run () == nil
72+ mj , mn , mc , err := GetVersion ()
73+ if err != nil {
74+ logrus .Warnf ("Failed to read iptables version: %v" , err )
75+ return nil
76+ }
77+ supportsCOpt = supportsCOption (mj , mn , mc )
7178 }
7279 return nil
7380}
@@ -299,20 +306,19 @@ func Exists(table Table, chain string, rule ...string) bool {
299306 table = Filter
300307 }
301308
302- // iptables -C, --check option was added in v.1.4.11
303- // http://ftp.netfilter.org/pub/iptables/changes-iptables-1.4.11.txt
304-
305- // try -C
306- // if exit status is 0 then return true, the rule exists
307- if _ , err := Raw (append ([]string {
308- "-t" , string (table ), "-C" , chain }, rule ... )... ); err == nil {
309- return true
309+ if supportsCOpt {
310+ // if exit status is 0 then return true, the rule exists
311+ _ , err := Raw (append ([]string {"-t" , string (table ), "-C" , chain }, rule ... )... )
312+ return err == nil
310313 }
311314
312- // parse "iptables -S" for the rule (this checks rules in a specific chain
313- // in a specific table)
314- ruleString := strings .Join (rule , " " )
315- ruleString = chain + " " + ruleString
315+ // parse "iptables -S" for the rule (it checks rules in a specific chain
316+ // in a specific table and it is very unreliable)
317+ return existsRaw (table , chain , rule ... )
318+ }
319+
320+ func existsRaw (table Table , chain string , rule ... string ) bool {
321+ ruleString := fmt .Sprintf ("%s %s\n " , chain , strings .Join (rule , " " ))
316322 existingRules , _ := exec .Command (iptablesPath , "-t" , string (table ), "-S" , chain ).Output ()
317323
318324 return strings .Contains (string (existingRules ), ruleString )
@@ -380,3 +386,25 @@ func ExistChain(chain string, table Table) bool {
380386 }
381387 return false
382388}
389+
390+ // GetVersion reads the iptables version numbers
391+ func GetVersion () (major , minor , micro int , err error ) {
392+ out , err := Raw ("--version" )
393+ if err == nil {
394+ major , minor , micro = parseVersionNumbers (string (out ))
395+ }
396+ return
397+ }
398+
399+ func parseVersionNumbers (input string ) (major , minor , micro int ) {
400+ re := regexp .MustCompile (`v\d*.\d*.\d*` )
401+ line := re .FindString (input )
402+ fmt .Sscanf (line , "v%d.%d.%d" , & major , & minor , & micro )
403+ return
404+ }
405+
406+ // iptables -C, --check option was added in v.1.4.11
407+ // http://ftp.netfilter.org/pub/iptables/changes-iptables-1.4.11.txt
408+ func supportsCOption (mj , mn , mc int ) bool {
409+ return mj > 1 || (mj == 1 && (mn > 4 || (mn == 4 && mc >= 11 )))
410+ }
0 commit comments