iptables.go 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192
  1. package iptables
  2. import (
  3. "errors"
  4. "fmt"
  5. "net"
  6. "os/exec"
  7. "regexp"
  8. "strconv"
  9. "strings"
  10. "github.com/docker/docker/pkg/log"
  11. )
  12. type Action string
  13. const (
  14. Add Action = "-A"
  15. Delete Action = "-D"
  16. )
  17. var (
  18. ErrIptablesNotFound = errors.New("Iptables not found")
  19. nat = []string{"-t", "nat"}
  20. supportsXlock = false
  21. )
  22. type Chain struct {
  23. Name string
  24. Bridge string
  25. }
  26. func init() {
  27. supportsXlock = exec.Command("iptables", "--wait", "-L", "-n").Run() == nil
  28. }
  29. func NewChain(name, bridge string) (*Chain, error) {
  30. if output, err := Raw("-t", "nat", "-N", name); err != nil {
  31. return nil, err
  32. } else if len(output) != 0 {
  33. return nil, fmt.Errorf("Error creating new iptables chain: %s", output)
  34. }
  35. chain := &Chain{
  36. Name: name,
  37. Bridge: bridge,
  38. }
  39. if err := chain.Prerouting(Add, "-m", "addrtype", "--dst-type", "LOCAL"); err != nil {
  40. return nil, fmt.Errorf("Failed to inject docker in PREROUTING chain: %s", err)
  41. }
  42. if err := chain.Output(Add, "-m", "addrtype", "--dst-type", "LOCAL", "!", "--dst", "127.0.0.0/8"); err != nil {
  43. return nil, fmt.Errorf("Failed to inject docker in OUTPUT chain: %s", err)
  44. }
  45. return chain, nil
  46. }
  47. func RemoveExistingChain(name string) error {
  48. chain := &Chain{
  49. Name: name,
  50. }
  51. return chain.Remove()
  52. }
  53. func (c *Chain) Forward(action Action, ip net.IP, port int, proto, dest_addr string, dest_port int) error {
  54. daddr := ip.String()
  55. if ip.IsUnspecified() {
  56. // iptables interprets "0.0.0.0" as "0.0.0.0/32", whereas we
  57. // want "0.0.0.0/0". "0/0" is correctly interpreted as "any
  58. // value" by both iptables and ip6tables.
  59. daddr = "0/0"
  60. }
  61. if output, err := Raw("-t", "nat", fmt.Sprint(action), c.Name,
  62. "-p", proto,
  63. "-d", daddr,
  64. "--dport", strconv.Itoa(port),
  65. "!", "-i", c.Bridge,
  66. "-j", "DNAT",
  67. "--to-destination", net.JoinHostPort(dest_addr, strconv.Itoa(dest_port))); err != nil {
  68. return err
  69. } else if len(output) != 0 {
  70. return fmt.Errorf("Error iptables forward: %s", output)
  71. }
  72. fAction := action
  73. if fAction == Add {
  74. fAction = "-I"
  75. }
  76. if output, err := Raw(string(fAction), "FORWARD",
  77. "!", "-i", c.Bridge,
  78. "-o", c.Bridge,
  79. "-p", proto,
  80. "-d", dest_addr,
  81. "--dport", strconv.Itoa(dest_port),
  82. "-j", "ACCEPT"); err != nil {
  83. return err
  84. } else if len(output) != 0 {
  85. return fmt.Errorf("Error iptables forward: %s", output)
  86. }
  87. return nil
  88. }
  89. func (c *Chain) Prerouting(action Action, args ...string) error {
  90. a := append(nat, fmt.Sprint(action), "PREROUTING")
  91. if len(args) > 0 {
  92. a = append(a, args...)
  93. }
  94. if output, err := Raw(append(a, "-j", c.Name)...); err != nil {
  95. return err
  96. } else if len(output) != 0 {
  97. return fmt.Errorf("Error iptables prerouting: %s", output)
  98. }
  99. return nil
  100. }
  101. func (c *Chain) Output(action Action, args ...string) error {
  102. a := append(nat, fmt.Sprint(action), "OUTPUT")
  103. if len(args) > 0 {
  104. a = append(a, args...)
  105. }
  106. if output, err := Raw(append(a, "-j", c.Name)...); err != nil {
  107. return err
  108. } else if len(output) != 0 {
  109. return fmt.Errorf("Error iptables output: %s", output)
  110. }
  111. return nil
  112. }
  113. func (c *Chain) Remove() error {
  114. // Ignore errors - This could mean the chains were never set up
  115. c.Prerouting(Delete, "-m", "addrtype", "--dst-type", "LOCAL")
  116. c.Output(Delete, "-m", "addrtype", "--dst-type", "LOCAL", "!", "--dst", "127.0.0.0/8")
  117. c.Output(Delete, "-m", "addrtype", "--dst-type", "LOCAL") // Created in versions <= 0.1.6
  118. c.Prerouting(Delete)
  119. c.Output(Delete)
  120. Raw("-t", "nat", "-F", c.Name)
  121. Raw("-t", "nat", "-X", c.Name)
  122. return nil
  123. }
  124. // Check if an existing rule exists
  125. func Exists(args ...string) bool {
  126. // iptables -C, --check option was added in v.1.4.11
  127. // http://ftp.netfilter.org/pub/iptables/changes-iptables-1.4.11.txt
  128. // try -C
  129. // if exit status is 0 then return true, the rule exists
  130. if _, err := Raw(append([]string{"-C"}, args...)...); err == nil {
  131. return true
  132. }
  133. // parse iptables-save for the rule
  134. rule := strings.Replace(strings.Join(args, " "), "-t nat ", "", -1)
  135. existingRules, _ := exec.Command("iptables-save").Output()
  136. // regex to replace ips in rule
  137. // because MASQUERADE rule will not be exactly what was passed
  138. re := regexp.MustCompile(`[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\/[0-9]{1,2}`)
  139. return strings.Contains(
  140. re.ReplaceAllString(string(existingRules), "?"),
  141. re.ReplaceAllString(rule, "?"),
  142. )
  143. }
  144. func Raw(args ...string) ([]byte, error) {
  145. path, err := exec.LookPath("iptables")
  146. if err != nil {
  147. return nil, ErrIptablesNotFound
  148. }
  149. if supportsXlock {
  150. args = append([]string{"--wait"}, args...)
  151. }
  152. log.Debugf("%s, %v", path, args)
  153. output, err := exec.Command(path, args...).CombinedOutput()
  154. if err != nil {
  155. return nil, fmt.Errorf("iptables failed: iptables %v: %s (%s)", strings.Join(args, " "), output, err)
  156. }
  157. // ignore iptables' message about xtables lock
  158. if strings.Contains(string(output), "waiting for it to exit") {
  159. output = []byte("")
  160. }
  161. return output, err
  162. }