iptables.go 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165
  1. package ip
  2. import (
  3. "bytes"
  4. "fmt"
  5. log "github.com/coreos/flannel/Godeps/_workspace/src/github.com/golang/glog"
  6. "os/exec"
  7. "regexp"
  8. "strconv"
  9. "strings"
  10. "syscall"
  11. )
  12. type IPTables struct {
  13. path string
  14. }
  15. func NewIPTables() (*IPTables, error) {
  16. path, err := exec.LookPath("iptables")
  17. if err != nil {
  18. return nil, err
  19. }
  20. return &IPTables{path}, nil
  21. }
  22. func (ipt *IPTables) Exists(table string, args ...string) (bool, error) {
  23. checkPresent, err := getIptablesHasCheckCommand()
  24. if err != nil {
  25. log.Warningf("Error checking iptables version, assuming version at least 1.4.11: %v\n", err)
  26. checkPresent = true
  27. }
  28. if !checkPresent {
  29. cmd := append([]string{"-A"}, args...)
  30. return existsForOldIpTables(table, strings.Join(cmd, " "))
  31. } else {
  32. cmd := append([]string{"-t", table, "-C"}, args...)
  33. err = exec.Command(ipt.path, cmd...).Run()
  34. }
  35. switch {
  36. case err == nil:
  37. return true, nil
  38. case err.(*exec.ExitError).Sys().(syscall.WaitStatus).ExitStatus() == 1:
  39. return false, nil
  40. default:
  41. return false, err
  42. }
  43. }
  44. func (ipt *IPTables) Append(table string, args ...string) error {
  45. cmd := append([]string{"-t", table, "-A"}, args...)
  46. return exec.Command(ipt.path, cmd...).Run()
  47. }
  48. // AppendUnique acts like Append except that it won't add a duplicate
  49. func (ipt *IPTables) AppendUnique(table string, args ...string) error {
  50. exists, err := ipt.Exists(table, args...)
  51. if err != nil {
  52. return err
  53. }
  54. if !exists {
  55. return ipt.Append(table, args...)
  56. }
  57. return nil
  58. }
  59. func (ipt *IPTables) ClearChain(table, chain string) error {
  60. cmd := append([]string{"-t", table, "-N", chain})
  61. err := exec.Command(ipt.path, cmd...).Run()
  62. switch {
  63. case err == nil:
  64. return nil
  65. case err.(*exec.ExitError).Sys().(syscall.WaitStatus).ExitStatus() == 1:
  66. // chain already exists. Flush (clear) it.
  67. cmd := append([]string{"-t", table, "-F", chain})
  68. return exec.Command(ipt.path, cmd...).Run()
  69. default:
  70. return err
  71. }
  72. }
  73. // Checks if iptables has the "-C" flag
  74. func getIptablesHasCheckCommand() (bool, error) {
  75. vstring, err := getIptablesVersionString()
  76. if err != nil {
  77. return false, err
  78. }
  79. v1, v2, v3, err := extractIptablesVersion(vstring)
  80. if err != nil {
  81. return false, err
  82. }
  83. return iptablesHasCheckCommand(v1, v2, v3), nil
  84. }
  85. // getIptablesVersion returns the first three components of the iptables version.
  86. // e.g. "iptables v1.3.66" would return (1, 3, 66, nil)
  87. func extractIptablesVersion(str string) (int, int, int, error) {
  88. versionMatcher := regexp.MustCompile("v([0-9]+)\\.([0-9]+)\\.([0-9]+)")
  89. result := versionMatcher.FindStringSubmatch(str)
  90. if result == nil {
  91. return 0, 0, 0, fmt.Errorf("no iptables version found in string: %s", str)
  92. }
  93. v1, err := strconv.Atoi(result[1])
  94. if err != nil {
  95. return 0, 0, 0, err
  96. }
  97. v2, err := strconv.Atoi(result[2])
  98. if err != nil {
  99. return 0, 0, 0, err
  100. }
  101. v3, err := strconv.Atoi(result[3])
  102. if err != nil {
  103. return 0, 0, 0, err
  104. }
  105. return v1, v2, v3, nil
  106. }
  107. // Runs "iptables --version" to get the version string
  108. func getIptablesVersionString() (string, error) {
  109. cmd := exec.Command("iptables", "--version")
  110. var out bytes.Buffer
  111. cmd.Stdout = &out
  112. err := cmd.Run()
  113. if err != nil {
  114. return "", err
  115. }
  116. return out.String(), nil
  117. }
  118. // Checks if an iptables version is after 1.4.11, when --check was added
  119. func iptablesHasCheckCommand(v1 int, v2 int, v3 int) bool {
  120. if v1 > 1 {
  121. return true
  122. }
  123. if v1 == 1 && v2 > 4 {
  124. return true
  125. }
  126. if v1 == 1 && v2 == 4 && v3 >= 11 {
  127. return true
  128. }
  129. return false
  130. }
  131. // Checks if a rule specification exists for a table
  132. func existsForOldIpTables(table string, ruleSpec string) (bool, error) {
  133. cmd := exec.Command("iptables", "-t", table, "-S")
  134. var out bytes.Buffer
  135. cmd.Stdout = &out
  136. err := cmd.Run()
  137. if err != nil {
  138. return false, err
  139. }
  140. rules := out.String()
  141. return strings.Contains(rules, ruleSpec), nil
  142. }