iptables_test.go 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236
  1. package iptables
  2. import (
  3. "net"
  4. "os/exec"
  5. "strconv"
  6. "strings"
  7. "sync"
  8. "testing"
  9. _ "github.com/docker/libnetwork/testutils"
  10. )
  11. const chainName = "DOCKEREST"
  12. var natChain *ChainInfo
  13. var filterChain *ChainInfo
  14. var bridgeName string
  15. func TestNewChain(t *testing.T) {
  16. var err error
  17. bridgeName = "lo"
  18. natChain, err = NewChain(chainName, Nat, false)
  19. err = ProgramChain(natChain, bridgeName, false, true)
  20. if err != nil {
  21. t.Fatal(err)
  22. }
  23. filterChain, err = NewChain(chainName, Filter, false)
  24. err = ProgramChain(filterChain, bridgeName, false, true)
  25. if err != nil {
  26. t.Fatal(err)
  27. }
  28. }
  29. func TestForward(t *testing.T) {
  30. ip := net.ParseIP("192.168.1.1")
  31. port := 1234
  32. dstAddr := "172.17.0.1"
  33. dstPort := 4321
  34. proto := "tcp"
  35. bridgeName := "lo"
  36. err := natChain.Forward(Insert, ip, port, proto, dstAddr, dstPort, bridgeName)
  37. if err != nil {
  38. t.Fatal(err)
  39. }
  40. dnatRule := []string{
  41. "-d", ip.String(),
  42. "-p", proto,
  43. "--dport", strconv.Itoa(port),
  44. "-j", "DNAT",
  45. "--to-destination", dstAddr + ":" + strconv.Itoa(dstPort),
  46. "!", "-i", bridgeName,
  47. }
  48. if !Exists(natChain.Table, natChain.Name, dnatRule...) {
  49. t.Fatalf("DNAT rule does not exist")
  50. }
  51. filterRule := []string{
  52. "!", "-i", bridgeName,
  53. "-o", bridgeName,
  54. "-d", dstAddr,
  55. "-p", proto,
  56. "--dport", strconv.Itoa(dstPort),
  57. "-j", "ACCEPT",
  58. }
  59. if !Exists(filterChain.Table, filterChain.Name, filterRule...) {
  60. t.Fatalf("filter rule does not exist")
  61. }
  62. masqRule := []string{
  63. "-d", dstAddr,
  64. "-s", dstAddr,
  65. "-p", proto,
  66. "--dport", strconv.Itoa(dstPort),
  67. "-j", "MASQUERADE",
  68. }
  69. if !Exists(natChain.Table, "POSTROUTING", masqRule...) {
  70. t.Fatalf("MASQUERADE rule does not exist")
  71. }
  72. }
  73. func TestLink(t *testing.T) {
  74. var err error
  75. bridgeName := "lo"
  76. ip1 := net.ParseIP("192.168.1.1")
  77. ip2 := net.ParseIP("192.168.1.2")
  78. port := 1234
  79. proto := "tcp"
  80. err = filterChain.Link(Append, ip1, ip2, port, proto, bridgeName)
  81. if err != nil {
  82. t.Fatal(err)
  83. }
  84. rule1 := []string{
  85. "-i", bridgeName,
  86. "-o", bridgeName,
  87. "-p", proto,
  88. "-s", ip1.String(),
  89. "-d", ip2.String(),
  90. "--dport", strconv.Itoa(port),
  91. "-j", "ACCEPT"}
  92. if !Exists(filterChain.Table, filterChain.Name, rule1...) {
  93. t.Fatalf("rule1 does not exist")
  94. }
  95. rule2 := []string{
  96. "-i", bridgeName,
  97. "-o", bridgeName,
  98. "-p", proto,
  99. "-s", ip2.String(),
  100. "-d", ip1.String(),
  101. "--sport", strconv.Itoa(port),
  102. "-j", "ACCEPT"}
  103. if !Exists(filterChain.Table, filterChain.Name, rule2...) {
  104. t.Fatalf("rule2 does not exist")
  105. }
  106. }
  107. func TestPrerouting(t *testing.T) {
  108. args := []string{
  109. "-i", "lo",
  110. "-d", "192.168.1.1"}
  111. err := natChain.Prerouting(Insert, args...)
  112. if err != nil {
  113. t.Fatal(err)
  114. }
  115. if !Exists(natChain.Table, "PREROUTING", args...) {
  116. t.Fatalf("rule does not exist")
  117. }
  118. delRule := append([]string{"-D", "PREROUTING", "-t", string(Nat)}, args...)
  119. if _, err = Raw(delRule...); err != nil {
  120. t.Fatal(err)
  121. }
  122. }
  123. func TestOutput(t *testing.T) {
  124. args := []string{
  125. "-o", "lo",
  126. "-d", "192.168.1.1"}
  127. err := natChain.Output(Insert, args...)
  128. if err != nil {
  129. t.Fatal(err)
  130. }
  131. if !Exists(natChain.Table, "OUTPUT", args...) {
  132. t.Fatalf("rule does not exist")
  133. }
  134. delRule := append([]string{"-D", "OUTPUT", "-t",
  135. string(natChain.Table)}, args...)
  136. if _, err = Raw(delRule...); err != nil {
  137. t.Fatal(err)
  138. }
  139. }
  140. func TestConcurrencyWithWait(t *testing.T) {
  141. RunConcurrencyTest(t, true)
  142. }
  143. func TestConcurrencyNoWait(t *testing.T) {
  144. RunConcurrencyTest(t, false)
  145. }
  146. // Runs 10 concurrent rule additions. This will fail if iptables
  147. // is actually invoked simultaneously without --wait.
  148. // Note that if iptables does not support the xtable lock on this
  149. // system, then allowXlock has no effect -- it will always be off.
  150. func RunConcurrencyTest(t *testing.T, allowXlock bool) {
  151. var wg sync.WaitGroup
  152. if !allowXlock && supportsXlock {
  153. supportsXlock = false
  154. defer func() { supportsXlock = true }()
  155. }
  156. ip := net.ParseIP("192.168.1.1")
  157. port := 1234
  158. dstAddr := "172.17.0.1"
  159. dstPort := 4321
  160. proto := "tcp"
  161. for i := 0; i < 10; i++ {
  162. wg.Add(1)
  163. go func() {
  164. defer wg.Done()
  165. err := natChain.Forward(Append, ip, port, proto, dstAddr, dstPort, "lo")
  166. if err != nil {
  167. t.Fatal(err)
  168. }
  169. }()
  170. }
  171. wg.Wait()
  172. }
  173. func TestCleanup(t *testing.T) {
  174. var err error
  175. var rules []byte
  176. // Cleanup filter/FORWARD first otherwise output of iptables-save is dirty
  177. link := []string{"-t", string(filterChain.Table),
  178. string(Delete), "FORWARD",
  179. "-o", bridgeName,
  180. "-j", filterChain.Name}
  181. if _, err = Raw(link...); err != nil {
  182. t.Fatal(err)
  183. }
  184. filterChain.Remove()
  185. err = RemoveExistingChain(chainName, Nat)
  186. if err != nil {
  187. t.Fatal(err)
  188. }
  189. rules, err = exec.Command("iptables-save").Output()
  190. if err != nil {
  191. t.Fatal(err)
  192. }
  193. if strings.Contains(string(rules), chainName) {
  194. t.Fatalf("Removing chain failed. %s found in iptables-save", chainName)
  195. }
  196. }