iptables_test.go 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318
  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. if err != nil {
  20. t.Fatal(err)
  21. }
  22. err = ProgramChain(natChain, bridgeName, false, true)
  23. if err != nil {
  24. t.Fatal(err)
  25. }
  26. filterChain, err = NewChain(chainName, Filter, false)
  27. if err != nil {
  28. t.Fatal(err)
  29. }
  30. err = ProgramChain(filterChain, bridgeName, false, true)
  31. if err != nil {
  32. t.Fatal(err)
  33. }
  34. }
  35. func TestForward(t *testing.T) {
  36. ip := net.ParseIP("192.168.1.1")
  37. port := 1234
  38. dstAddr := "172.17.0.1"
  39. dstPort := 4321
  40. proto := "tcp"
  41. bridgeName := "lo"
  42. err := natChain.Forward(Insert, ip, port, proto, dstAddr, dstPort, bridgeName)
  43. if err != nil {
  44. t.Fatal(err)
  45. }
  46. dnatRule := []string{
  47. "-d", ip.String(),
  48. "-p", proto,
  49. "--dport", strconv.Itoa(port),
  50. "-j", "DNAT",
  51. "--to-destination", dstAddr + ":" + strconv.Itoa(dstPort),
  52. "!", "-i", bridgeName,
  53. }
  54. if !Exists(natChain.Table, natChain.Name, dnatRule...) {
  55. t.Fatal("DNAT rule does not exist")
  56. }
  57. filterRule := []string{
  58. "!", "-i", bridgeName,
  59. "-o", bridgeName,
  60. "-d", dstAddr,
  61. "-p", proto,
  62. "--dport", strconv.Itoa(dstPort),
  63. "-j", "ACCEPT",
  64. }
  65. if !Exists(filterChain.Table, filterChain.Name, filterRule...) {
  66. t.Fatal("filter rule does not exist")
  67. }
  68. masqRule := []string{
  69. "-d", dstAddr,
  70. "-s", dstAddr,
  71. "-p", proto,
  72. "--dport", strconv.Itoa(dstPort),
  73. "-j", "MASQUERADE",
  74. }
  75. if !Exists(natChain.Table, "POSTROUTING", masqRule...) {
  76. t.Fatal("MASQUERADE rule does not exist")
  77. }
  78. }
  79. func TestLink(t *testing.T) {
  80. var err error
  81. bridgeName := "lo"
  82. ip1 := net.ParseIP("192.168.1.1")
  83. ip2 := net.ParseIP("192.168.1.2")
  84. port := 1234
  85. proto := "tcp"
  86. err = filterChain.Link(Append, ip1, ip2, port, proto, bridgeName)
  87. if err != nil {
  88. t.Fatal(err)
  89. }
  90. rule1 := []string{
  91. "-i", bridgeName,
  92. "-o", bridgeName,
  93. "-p", proto,
  94. "-s", ip1.String(),
  95. "-d", ip2.String(),
  96. "--dport", strconv.Itoa(port),
  97. "-j", "ACCEPT"}
  98. if !Exists(filterChain.Table, filterChain.Name, rule1...) {
  99. t.Fatal("rule1 does not exist")
  100. }
  101. rule2 := []string{
  102. "-i", bridgeName,
  103. "-o", bridgeName,
  104. "-p", proto,
  105. "-s", ip2.String(),
  106. "-d", ip1.String(),
  107. "--sport", strconv.Itoa(port),
  108. "-j", "ACCEPT"}
  109. if !Exists(filterChain.Table, filterChain.Name, rule2...) {
  110. t.Fatal("rule2 does not exist")
  111. }
  112. }
  113. func TestPrerouting(t *testing.T) {
  114. args := []string{
  115. "-i", "lo",
  116. "-d", "192.168.1.1"}
  117. err := natChain.Prerouting(Insert, args...)
  118. if err != nil {
  119. t.Fatal(err)
  120. }
  121. if !Exists(natChain.Table, "PREROUTING", args...) {
  122. t.Fatal("rule does not exist")
  123. }
  124. delRule := append([]string{"-D", "PREROUTING", "-t", string(Nat)}, args...)
  125. if _, err = Raw(delRule...); err != nil {
  126. t.Fatal(err)
  127. }
  128. }
  129. func TestOutput(t *testing.T) {
  130. args := []string{
  131. "-o", "lo",
  132. "-d", "192.168.1.1"}
  133. err := natChain.Output(Insert, args...)
  134. if err != nil {
  135. t.Fatal(err)
  136. }
  137. if !Exists(natChain.Table, "OUTPUT", args...) {
  138. t.Fatal("rule does not exist")
  139. }
  140. delRule := append([]string{"-D", "OUTPUT", "-t",
  141. string(natChain.Table)}, args...)
  142. if _, err = Raw(delRule...); err != nil {
  143. t.Fatal(err)
  144. }
  145. }
  146. func TestConcurrencyWithWait(t *testing.T) {
  147. RunConcurrencyTest(t, true)
  148. }
  149. func TestConcurrencyNoWait(t *testing.T) {
  150. RunConcurrencyTest(t, false)
  151. }
  152. // Runs 10 concurrent rule additions. This will fail if iptables
  153. // is actually invoked simultaneously without --wait.
  154. // Note that if iptables does not support the xtable lock on this
  155. // system, then allowXlock has no effect -- it will always be off.
  156. func RunConcurrencyTest(t *testing.T, allowXlock bool) {
  157. var wg sync.WaitGroup
  158. if !allowXlock && supportsXlock {
  159. supportsXlock = false
  160. defer func() { supportsXlock = true }()
  161. }
  162. ip := net.ParseIP("192.168.1.1")
  163. port := 1234
  164. dstAddr := "172.17.0.1"
  165. dstPort := 4321
  166. proto := "tcp"
  167. for i := 0; i < 10; i++ {
  168. wg.Add(1)
  169. go func() {
  170. defer wg.Done()
  171. err := natChain.Forward(Append, ip, port, proto, dstAddr, dstPort, "lo")
  172. if err != nil {
  173. t.Fatal(err)
  174. }
  175. }()
  176. }
  177. wg.Wait()
  178. }
  179. func TestCleanup(t *testing.T) {
  180. var err error
  181. var rules []byte
  182. // Cleanup filter/FORWARD first otherwise output of iptables-save is dirty
  183. link := []string{"-t", string(filterChain.Table),
  184. string(Delete), "FORWARD",
  185. "-o", bridgeName,
  186. "-j", filterChain.Name}
  187. if _, err = Raw(link...); err != nil {
  188. t.Fatal(err)
  189. }
  190. filterChain.Remove()
  191. err = RemoveExistingChain(chainName, Nat)
  192. if err != nil {
  193. t.Fatal(err)
  194. }
  195. rules, err = exec.Command("iptables-save").Output()
  196. if err != nil {
  197. t.Fatal(err)
  198. }
  199. if strings.Contains(string(rules), chainName) {
  200. t.Fatalf("Removing chain failed. %s found in iptables-save", chainName)
  201. }
  202. }
  203. func TestExistsRaw(t *testing.T) {
  204. testChain1 := "ABCD"
  205. testChain2 := "EFGH"
  206. _, err := NewChain(testChain1, Filter, false)
  207. if err != nil {
  208. t.Fatal(err)
  209. }
  210. defer func() {
  211. RemoveExistingChain(testChain1, Filter)
  212. }()
  213. _, err = NewChain(testChain2, Filter, false)
  214. if err != nil {
  215. t.Fatal(err)
  216. }
  217. defer func() {
  218. RemoveExistingChain(testChain2, Filter)
  219. }()
  220. // Test detection over full and truncated rule string
  221. input := []struct{ rule []string }{
  222. {[]string{"-s", "172.8.9.9/32", "-j", "ACCEPT"}},
  223. {[]string{"-d", "172.8.9.0/24", "-j", "DROP"}},
  224. {[]string{"-s", "172.0.3.0/24", "-d", "172.17.0.0/24", "-p", "tcp", "-m", "tcp", "--dport", "80", "-j", testChain2}},
  225. {[]string{"-j", "RETURN"}},
  226. }
  227. for i, r := range input {
  228. ruleAdd := append([]string{"-t", string(Filter), "-A", testChain1}, r.rule...)
  229. err = RawCombinedOutput(ruleAdd...)
  230. if err != nil {
  231. t.Fatalf("i=%d, err: %v", i, err)
  232. }
  233. if !existsRaw(Filter, testChain1, r.rule...) {
  234. t.Fatalf("Failed to detect rule. i=%d", i)
  235. }
  236. // Truncate the rule
  237. trg := r.rule[len(r.rule)-1]
  238. trg = trg[:len(trg)-2]
  239. r.rule[len(r.rule)-1] = trg
  240. if existsRaw(Filter, testChain1, r.rule...) {
  241. t.Fatalf("Invalid detection. i=%d", i)
  242. }
  243. }
  244. }
  245. func TestGetVersion(t *testing.T) {
  246. mj, mn, mc := parseVersionNumbers("iptables v1.4.19.1-alpha")
  247. if mj != 1 || mn != 4 || mc != 19 {
  248. t.Fatal("Failed to parse version numbers")
  249. }
  250. }
  251. func TestSupportsCOption(t *testing.T) {
  252. input := []struct {
  253. mj int
  254. mn int
  255. mc int
  256. ok bool
  257. }{
  258. {1, 4, 11, true},
  259. {1, 4, 12, true},
  260. {1, 5, 0, true},
  261. {0, 4, 11, false},
  262. {0, 5, 12, false},
  263. {1, 3, 12, false},
  264. {1, 4, 10, false},
  265. }
  266. for ind, inp := range input {
  267. if inp.ok != supportsCOption(inp.mj, inp.mn, inp.mc) {
  268. t.Fatalf("Incorrect check: %d", ind)
  269. }
  270. }
  271. }