iptables.go 4.2 KB

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