iptables.go 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141
  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. )
  20. type Chain struct {
  21. Name string
  22. Bridge string
  23. }
  24. func NewChain(name, bridge string) (*Chain, error) {
  25. if output, err := Raw("-t", "nat", "-N", name); err != nil {
  26. return nil, err
  27. } else if len(output) != 0 {
  28. return nil, fmt.Errorf("Error creating new iptables chain: %s", output)
  29. }
  30. chain := &Chain{
  31. Name: name,
  32. Bridge: bridge,
  33. }
  34. if err := chain.Prerouting(Add, "-m", "addrtype", "--dst-type", "LOCAL"); err != nil {
  35. return nil, fmt.Errorf("Failed to inject docker in PREROUTING chain: %s", err)
  36. }
  37. if err := chain.Output(Add, "-m", "addrtype", "--dst-type", "LOCAL", "!", "--dst", "127.0.0.0/8"); err != nil {
  38. return nil, fmt.Errorf("Failed to inject docker in OUTPUT chain: %s", err)
  39. }
  40. return chain, nil
  41. }
  42. func RemoveExistingChain(name string) error {
  43. chain := &Chain{
  44. Name: name,
  45. }
  46. return chain.Remove()
  47. }
  48. func (c *Chain) Forward(action Action, ip net.IP, port int, proto, dest_addr string, dest_port int) error {
  49. daddr := ip.String()
  50. if ip.IsUnspecified() {
  51. // iptables interprets "0.0.0.0" as "0.0.0.0/32", whereas we
  52. // want "0.0.0.0/0". "0/0" is correctly interpreted as "any
  53. // value" by both iptables and ip6tables.
  54. daddr = "0/0"
  55. }
  56. if output, err := Raw("-t", "nat", fmt.Sprint(action), c.Name,
  57. "-p", proto,
  58. "-d", daddr,
  59. "--dport", strconv.Itoa(port),
  60. "!", "-i", c.Bridge,
  61. "-j", "DNAT",
  62. "--to-destination", net.JoinHostPort(dest_addr, strconv.Itoa(dest_port))); err != nil {
  63. return err
  64. } else if len(output) != 0 {
  65. return fmt.Errorf("Error iptables forward: %s", output)
  66. }
  67. return nil
  68. }
  69. func (c *Chain) Prerouting(action Action, args ...string) error {
  70. a := append(nat, fmt.Sprint(action), "PREROUTING")
  71. if len(args) > 0 {
  72. a = append(a, args...)
  73. }
  74. if output, err := Raw(append(a, "-j", c.Name)...); err != nil {
  75. return err
  76. } else if len(output) != 0 {
  77. return fmt.Errorf("Error iptables prerouting: %s", output)
  78. }
  79. return nil
  80. }
  81. func (c *Chain) Output(action Action, args ...string) error {
  82. a := append(nat, fmt.Sprint(action), "OUTPUT")
  83. if len(args) > 0 {
  84. a = append(a, args...)
  85. }
  86. if output, err := Raw(append(a, "-j", c.Name)...); err != nil {
  87. return err
  88. } else if len(output) != 0 {
  89. return fmt.Errorf("Error iptables output: %s", output)
  90. }
  91. return nil
  92. }
  93. func (c *Chain) Remove() error {
  94. // Ignore errors - This could mean the chains were never set up
  95. c.Prerouting(Delete, "-m", "addrtype", "--dst-type", "LOCAL")
  96. c.Output(Delete, "-m", "addrtype", "--dst-type", "LOCAL", "!", "--dst", "127.0.0.0/8")
  97. c.Output(Delete, "-m", "addrtype", "--dst-type", "LOCAL") // Created in versions <= 0.1.6
  98. c.Prerouting(Delete)
  99. c.Output(Delete)
  100. Raw("-t", "nat", "-F", c.Name)
  101. Raw("-t", "nat", "-X", c.Name)
  102. return nil
  103. }
  104. // Check if an existing rule exists
  105. func Exists(args ...string) bool {
  106. if _, err := Raw(append([]string{"-C"}, args...)...); err != nil {
  107. return false
  108. }
  109. return true
  110. }
  111. func Raw(args ...string) ([]byte, error) {
  112. path, err := exec.LookPath("iptables")
  113. if err != nil {
  114. return nil, ErrIptablesNotFound
  115. }
  116. if os.Getenv("DEBUG") != "" {
  117. fmt.Printf("[DEBUG] [iptables]: %s, %v\n", path, args)
  118. }
  119. output, err := exec.Command(path, args...).CombinedOutput()
  120. if err != nil {
  121. return nil, fmt.Errorf("iptables failed: iptables %v: %s (%s)", strings.Join(args, " "), output, err)
  122. }
  123. return output, err
  124. }