iptables_test.go 44 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187
  1. // +build linux
  2. /*
  3. Copyright 2014 The Kubernetes Authors.
  4. Licensed under the Apache License, Version 2.0 (the "License");
  5. you may not use this file except in compliance with the License.
  6. You may obtain a copy of the License at
  7. http://www.apache.org/licenses/LICENSE-2.0
  8. Unless required by applicable law or agreed to in writing, software
  9. distributed under the License is distributed on an "AS IS" BASIS,
  10. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  11. See the License for the specific language governing permissions and
  12. limitations under the License.
  13. */
  14. package iptables
  15. import (
  16. "bytes"
  17. "fmt"
  18. "net"
  19. "os"
  20. "reflect"
  21. "strings"
  22. "testing"
  23. "k8s.io/apimachinery/pkg/util/sets"
  24. utilversion "k8s.io/apimachinery/pkg/util/version"
  25. "k8s.io/utils/exec"
  26. fakeexec "k8s.io/utils/exec/testing"
  27. )
  28. const TestLockfilePath = "xtables.lock"
  29. func protocolStr(protocol Protocol) string {
  30. if protocol == ProtocolIpv4 {
  31. return "IPv4"
  32. }
  33. return "IPv6"
  34. }
  35. func testIPTablesVersionCmds(t *testing.T, protocol Protocol) {
  36. version := " v1.4.22"
  37. iptablesCmd := iptablesCommand(protocol)
  38. iptablesRestoreCmd := iptablesRestoreCommand(protocol)
  39. protoStr := protocolStr(protocol)
  40. fcmd := fakeexec.FakeCmd{
  41. CombinedOutputScript: []fakeexec.FakeAction{
  42. // iptables version response (for runner instantiation)
  43. func() ([]byte, []byte, error) { return []byte(iptablesCmd + version), nil, nil },
  44. // iptables-restore version response (for runner instantiation)
  45. func() ([]byte, []byte, error) { return []byte(iptablesRestoreCmd + version), nil, nil },
  46. },
  47. }
  48. fexec := fakeexec.FakeExec{
  49. CommandScript: []fakeexec.FakeCommandAction{
  50. func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) },
  51. func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) },
  52. },
  53. }
  54. _ = New(&fexec, protocol)
  55. // Check that proper iptables version command was used during runner instantiation
  56. if !sets.NewString(fcmd.CombinedOutputLog[0]...).HasAll(iptablesCmd, "--version") {
  57. t.Errorf("%s runner instantiate: Expected cmd '%s --version', Got '%s'", protoStr, iptablesCmd, fcmd.CombinedOutputLog[0])
  58. }
  59. // Check that proper iptables restore version command was used during runner instantiation
  60. if !sets.NewString(fcmd.CombinedOutputLog[1]...).HasAll(iptablesRestoreCmd, "--version") {
  61. t.Errorf("%s runner instantiate: Expected cmd '%s --version', Got '%s'", protoStr, iptablesRestoreCmd, fcmd.CombinedOutputLog[1])
  62. }
  63. }
  64. func TestIPTablesVersionCmdsIPv4(t *testing.T) {
  65. testIPTablesVersionCmds(t, ProtocolIpv4)
  66. }
  67. func TestIPTablesVersionCmdsIPv6(t *testing.T) {
  68. testIPTablesVersionCmds(t, ProtocolIpv6)
  69. }
  70. func testEnsureChain(t *testing.T, protocol Protocol) {
  71. protoStr := protocolStr(protocol)
  72. fcmd := fakeexec.FakeCmd{
  73. CombinedOutputScript: []fakeexec.FakeAction{
  74. // iptables version check
  75. func() ([]byte, []byte, error) { return []byte("iptables v1.9.22"), nil, nil },
  76. // Success.
  77. func() ([]byte, []byte, error) { return []byte{}, nil, nil },
  78. // Exists.
  79. func() ([]byte, []byte, error) { return nil, nil, &fakeexec.FakeExitError{Status: 1} },
  80. // Failure.
  81. func() ([]byte, []byte, error) { return nil, nil, &fakeexec.FakeExitError{Status: 2} },
  82. },
  83. }
  84. fexec := fakeexec.FakeExec{
  85. CommandScript: []fakeexec.FakeCommandAction{
  86. func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) },
  87. func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) },
  88. func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) },
  89. func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) },
  90. },
  91. }
  92. runner := New(&fexec, protocol)
  93. // Success.
  94. exists, err := runner.EnsureChain(TableNAT, Chain("FOOBAR"))
  95. if err != nil {
  96. t.Errorf("%s new chain: Expected success, got %v", protoStr, err)
  97. }
  98. if exists {
  99. t.Errorf("%s new chain: Expected exists = false", protoStr)
  100. }
  101. if fcmd.CombinedOutputCalls != 2 {
  102. t.Errorf("%s new chain: Expected 2 CombinedOutput() calls, got %d", protoStr, fcmd.CombinedOutputCalls)
  103. }
  104. cmd := iptablesCommand(protocol)
  105. if !sets.NewString(fcmd.CombinedOutputLog[1]...).HasAll(cmd, "-t", "nat", "-N", "FOOBAR") {
  106. t.Errorf("%s new chain: Expected cmd containing '%s -t nat -N FOOBAR', got %s", protoStr, cmd, fcmd.CombinedOutputLog[2])
  107. }
  108. // Exists.
  109. exists, err = runner.EnsureChain(TableNAT, Chain("FOOBAR"))
  110. if err != nil {
  111. t.Errorf("%s existing chain: Expected success, got %v", protoStr, err)
  112. }
  113. if !exists {
  114. t.Errorf("%s existing chain: Expected exists = true", protoStr)
  115. }
  116. // Simulate failure.
  117. _, err = runner.EnsureChain(TableNAT, Chain("FOOBAR"))
  118. if err == nil {
  119. t.Errorf("%s: Expected failure", protoStr)
  120. }
  121. }
  122. func TestEnsureChainIpv4(t *testing.T) {
  123. testEnsureChain(t, ProtocolIpv4)
  124. }
  125. func TestEnsureChainIpv6(t *testing.T) {
  126. testEnsureChain(t, ProtocolIpv6)
  127. }
  128. func TestFlushChain(t *testing.T) {
  129. fcmd := fakeexec.FakeCmd{
  130. CombinedOutputScript: []fakeexec.FakeAction{
  131. // iptables version check
  132. func() ([]byte, []byte, error) { return []byte("iptables v1.9.22"), nil, nil },
  133. // Success.
  134. func() ([]byte, []byte, error) { return []byte{}, nil, nil },
  135. // Failure.
  136. func() ([]byte, []byte, error) { return nil, nil, &fakeexec.FakeExitError{Status: 1} },
  137. },
  138. }
  139. fexec := fakeexec.FakeExec{
  140. CommandScript: []fakeexec.FakeCommandAction{
  141. func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) },
  142. func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) },
  143. func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) },
  144. },
  145. }
  146. runner := New(&fexec, ProtocolIpv4)
  147. // Success.
  148. err := runner.FlushChain(TableNAT, Chain("FOOBAR"))
  149. if err != nil {
  150. t.Errorf("expected success, got %v", err)
  151. }
  152. if fcmd.CombinedOutputCalls != 2 {
  153. t.Errorf("expected 2 CombinedOutput() calls, got %d", fcmd.CombinedOutputCalls)
  154. }
  155. if !sets.NewString(fcmd.CombinedOutputLog[1]...).HasAll("iptables", "-t", "nat", "-F", "FOOBAR") {
  156. t.Errorf("wrong CombinedOutput() log, got %s", fcmd.CombinedOutputLog[2])
  157. }
  158. // Failure.
  159. err = runner.FlushChain(TableNAT, Chain("FOOBAR"))
  160. if err == nil {
  161. t.Errorf("expected failure")
  162. }
  163. }
  164. func TestDeleteChain(t *testing.T) {
  165. fcmd := fakeexec.FakeCmd{
  166. CombinedOutputScript: []fakeexec.FakeAction{
  167. // iptables version check
  168. func() ([]byte, []byte, error) { return []byte("iptables v1.9.22"), nil, nil },
  169. // Success.
  170. func() ([]byte, []byte, error) { return []byte{}, nil, nil },
  171. // Failure.
  172. func() ([]byte, []byte, error) { return nil, nil, &fakeexec.FakeExitError{Status: 1} },
  173. },
  174. }
  175. fexec := fakeexec.FakeExec{
  176. CommandScript: []fakeexec.FakeCommandAction{
  177. func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) },
  178. func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) },
  179. func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) },
  180. },
  181. }
  182. runner := New(&fexec, ProtocolIpv4)
  183. // Success.
  184. err := runner.DeleteChain(TableNAT, Chain("FOOBAR"))
  185. if err != nil {
  186. t.Errorf("expected success, got %v", err)
  187. }
  188. if fcmd.CombinedOutputCalls != 2 {
  189. t.Errorf("expected 2 CombinedOutput() calls, got %d", fcmd.CombinedOutputCalls)
  190. }
  191. if !sets.NewString(fcmd.CombinedOutputLog[1]...).HasAll("iptables", "-t", "nat", "-X", "FOOBAR") {
  192. t.Errorf("wrong CombinedOutput() log, got %s", fcmd.CombinedOutputLog[2])
  193. }
  194. // Failure.
  195. err = runner.DeleteChain(TableNAT, Chain("FOOBAR"))
  196. if err == nil {
  197. t.Errorf("expected failure")
  198. }
  199. }
  200. func TestEnsureRuleAlreadyExists(t *testing.T) {
  201. fcmd := fakeexec.FakeCmd{
  202. CombinedOutputScript: []fakeexec.FakeAction{
  203. // iptables version check
  204. func() ([]byte, []byte, error) { return []byte("iptables v1.9.22"), nil, nil },
  205. // Success.
  206. func() ([]byte, []byte, error) { return []byte{}, nil, nil },
  207. },
  208. }
  209. fexec := fakeexec.FakeExec{
  210. CommandScript: []fakeexec.FakeCommandAction{
  211. // iptables version check
  212. func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) },
  213. // The second Command() call is checking the rule. Success of that exec means "done".
  214. func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) },
  215. },
  216. }
  217. runner := New(&fexec, ProtocolIpv4)
  218. exists, err := runner.EnsureRule(Append, TableNAT, ChainOutput, "abc", "123")
  219. if err != nil {
  220. t.Errorf("expected success, got %v", err)
  221. }
  222. if !exists {
  223. t.Errorf("expected exists = true")
  224. }
  225. if fcmd.CombinedOutputCalls != 2 {
  226. t.Errorf("expected 2 CombinedOutput() calls, got %d", fcmd.CombinedOutputCalls)
  227. }
  228. if !sets.NewString(fcmd.CombinedOutputLog[1]...).HasAll("iptables", "-t", "nat", "-C", "OUTPUT", "abc", "123") {
  229. t.Errorf("wrong CombinedOutput() log, got %s", fcmd.CombinedOutputLog[2])
  230. }
  231. }
  232. func TestEnsureRuleNew(t *testing.T) {
  233. fcmd := fakeexec.FakeCmd{
  234. CombinedOutputScript: []fakeexec.FakeAction{
  235. // iptables version check
  236. func() ([]byte, []byte, error) { return []byte("iptables v1.9.22"), nil, nil },
  237. // Status 1 on the first call.
  238. func() ([]byte, []byte, error) { return nil, nil, &fakeexec.FakeExitError{Status: 1} },
  239. // Success on the second call.
  240. func() ([]byte, []byte, error) { return []byte{}, nil, nil },
  241. },
  242. }
  243. fexec := fakeexec.FakeExec{
  244. CommandScript: []fakeexec.FakeCommandAction{
  245. // iptables version check
  246. func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) },
  247. // The second Command() call is checking the rule. Failure of that means create it.
  248. func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) },
  249. func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) },
  250. },
  251. }
  252. runner := New(&fexec, ProtocolIpv4)
  253. exists, err := runner.EnsureRule(Append, TableNAT, ChainOutput, "abc", "123")
  254. if err != nil {
  255. t.Errorf("expected success, got %v", err)
  256. }
  257. if exists {
  258. t.Errorf("expected exists = false")
  259. }
  260. if fcmd.CombinedOutputCalls != 3 {
  261. t.Errorf("expected 3 CombinedOutput() calls, got %d", fcmd.CombinedOutputCalls)
  262. }
  263. if !sets.NewString(fcmd.CombinedOutputLog[2]...).HasAll("iptables", "-t", "nat", "-A", "OUTPUT", "abc", "123") {
  264. t.Errorf("wrong CombinedOutput() log, got %s", fcmd.CombinedOutputLog[3])
  265. }
  266. }
  267. func TestEnsureRuleErrorChecking(t *testing.T) {
  268. fcmd := fakeexec.FakeCmd{
  269. CombinedOutputScript: []fakeexec.FakeAction{
  270. // iptables version check
  271. func() ([]byte, []byte, error) { return []byte("iptables v1.9.22"), nil, nil },
  272. // Status 2 on the first call.
  273. func() ([]byte, []byte, error) { return nil, nil, &fakeexec.FakeExitError{Status: 2} },
  274. },
  275. }
  276. fexec := fakeexec.FakeExec{
  277. CommandScript: []fakeexec.FakeCommandAction{
  278. // iptables version check
  279. func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) },
  280. // The second Command() call is checking the rule. Failure of that means create it.
  281. func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) },
  282. },
  283. }
  284. runner := New(&fexec, ProtocolIpv4)
  285. _, err := runner.EnsureRule(Append, TableNAT, ChainOutput, "abc", "123")
  286. if err == nil {
  287. t.Errorf("expected failure")
  288. }
  289. if fcmd.CombinedOutputCalls != 2 {
  290. t.Errorf("expected 2 CombinedOutput() calls, got %d", fcmd.CombinedOutputCalls)
  291. }
  292. }
  293. func TestEnsureRuleErrorCreating(t *testing.T) {
  294. fcmd := fakeexec.FakeCmd{
  295. CombinedOutputScript: []fakeexec.FakeAction{
  296. // iptables version check
  297. func() ([]byte, []byte, error) { return []byte("iptables v1.9.22"), nil, nil },
  298. // Status 1 on the first call.
  299. func() ([]byte, []byte, error) { return nil, nil, &fakeexec.FakeExitError{Status: 1} },
  300. // Status 1 on the second call.
  301. func() ([]byte, []byte, error) { return nil, nil, &fakeexec.FakeExitError{Status: 1} },
  302. },
  303. }
  304. fexec := fakeexec.FakeExec{
  305. CommandScript: []fakeexec.FakeCommandAction{
  306. // iptables version check
  307. func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) },
  308. // The second Command() call is checking the rule. Failure of that means create it.
  309. func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) },
  310. func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) },
  311. },
  312. }
  313. runner := New(&fexec, ProtocolIpv4)
  314. _, err := runner.EnsureRule(Append, TableNAT, ChainOutput, "abc", "123")
  315. if err == nil {
  316. t.Errorf("expected failure")
  317. }
  318. if fcmd.CombinedOutputCalls != 3 {
  319. t.Errorf("expected 3 CombinedOutput() calls, got %d", fcmd.CombinedOutputCalls)
  320. }
  321. }
  322. func TestDeleteRuleDoesNotExist(t *testing.T) {
  323. fcmd := fakeexec.FakeCmd{
  324. CombinedOutputScript: []fakeexec.FakeAction{
  325. // iptables version check
  326. func() ([]byte, []byte, error) { return []byte("iptables v1.9.22"), nil, nil },
  327. // Status 1 on the first call.
  328. func() ([]byte, []byte, error) { return nil, nil, &fakeexec.FakeExitError{Status: 1} },
  329. },
  330. }
  331. fexec := fakeexec.FakeExec{
  332. CommandScript: []fakeexec.FakeCommandAction{
  333. // iptables version check
  334. func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) },
  335. // The second Command() call is checking the rule. Failure of that exec means "does not exist".
  336. func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) },
  337. },
  338. }
  339. runner := New(&fexec, ProtocolIpv4)
  340. err := runner.DeleteRule(TableNAT, ChainOutput, "abc", "123")
  341. if err != nil {
  342. t.Errorf("expected success, got %v", err)
  343. }
  344. if fcmd.CombinedOutputCalls != 2 {
  345. t.Errorf("expected 2 CombinedOutput() calls, got %d", fcmd.CombinedOutputCalls)
  346. }
  347. if !sets.NewString(fcmd.CombinedOutputLog[1]...).HasAll("iptables", "-t", "nat", "-C", "OUTPUT", "abc", "123") {
  348. t.Errorf("wrong CombinedOutput() log, got %s", fcmd.CombinedOutputLog[2])
  349. }
  350. }
  351. func TestDeleteRuleExists(t *testing.T) {
  352. fcmd := fakeexec.FakeCmd{
  353. CombinedOutputScript: []fakeexec.FakeAction{
  354. // iptables version check
  355. func() ([]byte, []byte, error) { return []byte("iptables v1.9.22"), nil, nil },
  356. // Success on the first call.
  357. func() ([]byte, []byte, error) { return []byte{}, nil, nil },
  358. // Success on the second call.
  359. func() ([]byte, []byte, error) { return []byte{}, nil, nil },
  360. },
  361. }
  362. fexec := fakeexec.FakeExec{
  363. CommandScript: []fakeexec.FakeCommandAction{
  364. // iptables version check
  365. func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) },
  366. // The second Command() call is checking the rule. Success of that means delete it.
  367. func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) },
  368. func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) },
  369. },
  370. }
  371. runner := New(&fexec, ProtocolIpv4)
  372. err := runner.DeleteRule(TableNAT, ChainOutput, "abc", "123")
  373. if err != nil {
  374. t.Errorf("expected success, got %v", err)
  375. }
  376. if fcmd.CombinedOutputCalls != 3 {
  377. t.Errorf("expected 3 CombinedOutput() calls, got %d", fcmd.CombinedOutputCalls)
  378. }
  379. if !sets.NewString(fcmd.CombinedOutputLog[2]...).HasAll("iptables", "-t", "nat", "-D", "OUTPUT", "abc", "123") {
  380. t.Errorf("wrong CombinedOutput() log, got %s", fcmd.CombinedOutputLog[3])
  381. }
  382. }
  383. func TestDeleteRuleErrorChecking(t *testing.T) {
  384. fcmd := fakeexec.FakeCmd{
  385. CombinedOutputScript: []fakeexec.FakeAction{
  386. // iptables version check
  387. func() ([]byte, []byte, error) { return []byte("iptables v1.9.22"), nil, nil },
  388. // Status 2 on the first call.
  389. func() ([]byte, []byte, error) { return nil, nil, &fakeexec.FakeExitError{Status: 2} },
  390. },
  391. }
  392. fexec := fakeexec.FakeExec{
  393. CommandScript: []fakeexec.FakeCommandAction{
  394. // iptables version check
  395. func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) },
  396. // The second Command() call is checking the rule. Failure of that means create it.
  397. func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) },
  398. },
  399. }
  400. runner := New(&fexec, ProtocolIpv4)
  401. err := runner.DeleteRule(TableNAT, ChainOutput, "abc", "123")
  402. if err == nil {
  403. t.Errorf("expected failure")
  404. }
  405. if fcmd.CombinedOutputCalls != 2 {
  406. t.Errorf("expected 2 CombinedOutput() calls, got %d", fcmd.CombinedOutputCalls)
  407. }
  408. }
  409. func TestDeleteRuleErrorDeleting(t *testing.T) {
  410. fcmd := fakeexec.FakeCmd{
  411. CombinedOutputScript: []fakeexec.FakeAction{
  412. // iptables version check
  413. func() ([]byte, []byte, error) { return []byte("iptables v1.9.22"), nil, nil },
  414. // Success on the first call.
  415. func() ([]byte, []byte, error) { return []byte{}, nil, nil },
  416. // Status 1 on the second call.
  417. func() ([]byte, []byte, error) { return nil, nil, &fakeexec.FakeExitError{Status: 1} },
  418. },
  419. }
  420. fexec := fakeexec.FakeExec{
  421. CommandScript: []fakeexec.FakeCommandAction{
  422. // iptables version check
  423. func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) },
  424. // The second Command() call is checking the rule. Success of that means delete it.
  425. func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) },
  426. func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) },
  427. },
  428. }
  429. runner := New(&fexec, ProtocolIpv4)
  430. err := runner.DeleteRule(TableNAT, ChainOutput, "abc", "123")
  431. if err == nil {
  432. t.Errorf("expected failure")
  433. }
  434. if fcmd.CombinedOutputCalls != 3 {
  435. t.Errorf("expected 3 CombinedOutput() calls, got %d", fcmd.CombinedOutputCalls)
  436. }
  437. }
  438. func TestGetIPTablesHasCheckCommand(t *testing.T) {
  439. testCases := []struct {
  440. Version string
  441. Expected bool
  442. }{
  443. {"iptables v1.4.7", false},
  444. {"iptables v1.4.11", true},
  445. {"iptables v1.4.19.1", true},
  446. {"iptables v2.0.0", true},
  447. {"total junk", true},
  448. }
  449. for _, testCase := range testCases {
  450. fcmd := fakeexec.FakeCmd{
  451. CombinedOutputScript: []fakeexec.FakeAction{
  452. func() ([]byte, []byte, error) { return []byte(testCase.Version), nil, nil },
  453. func() ([]byte, []byte, error) { return []byte(testCase.Version), nil, nil },
  454. },
  455. }
  456. fexec := fakeexec.FakeExec{
  457. CommandScript: []fakeexec.FakeCommandAction{
  458. func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) },
  459. func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) },
  460. },
  461. }
  462. ipt := New(&fexec, ProtocolIpv4)
  463. runner := ipt.(*runner)
  464. if testCase.Expected != runner.hasCheck {
  465. t.Errorf("Expected result: %v, Got result: %v", testCase.Expected, runner.hasCheck)
  466. }
  467. }
  468. }
  469. func TestIPTablesCommands(t *testing.T) {
  470. testCases := []struct {
  471. funcName string
  472. protocol Protocol
  473. expectedCmd string
  474. }{
  475. {"iptablesCommand", ProtocolIpv4, cmdIPTables},
  476. {"iptablesCommand", ProtocolIpv6, cmdIP6Tables},
  477. {"iptablesSaveCommand", ProtocolIpv4, cmdIPTablesSave},
  478. {"iptablesSaveCommand", ProtocolIpv6, cmdIP6TablesSave},
  479. {"iptablesRestoreCommand", ProtocolIpv4, cmdIPTablesRestore},
  480. {"iptablesRestoreCommand", ProtocolIpv6, cmdIP6TablesRestore},
  481. }
  482. for _, testCase := range testCases {
  483. var cmd string
  484. switch testCase.funcName {
  485. case "iptablesCommand":
  486. cmd = iptablesCommand(testCase.protocol)
  487. case "iptablesSaveCommand":
  488. cmd = iptablesSaveCommand(testCase.protocol)
  489. case "iptablesRestoreCommand":
  490. cmd = iptablesRestoreCommand(testCase.protocol)
  491. }
  492. if cmd != testCase.expectedCmd {
  493. t.Errorf("Function: %s, Expected result: %s, Actual result: %s", testCase.funcName, testCase.expectedCmd, cmd)
  494. }
  495. }
  496. }
  497. func TestCheckRuleWithoutCheckPresent(t *testing.T) {
  498. iptablesSaveOutput := `# Generated by iptables-save v1.4.7 on Wed Oct 29 14:56:01 2014
  499. *nat
  500. :PREROUTING ACCEPT [2136997:197881818]
  501. :POSTROUTING ACCEPT [4284525:258542680]
  502. :OUTPUT ACCEPT [5901660:357267963]
  503. -A PREROUTING -m addrtype --dst-type LOCAL -m mark --mark 0x00004000/0x00004000 -j DOCKER
  504. COMMIT
  505. # Completed on Wed Oct 29 14:56:01 2014`
  506. fcmd := fakeexec.FakeCmd{
  507. CombinedOutputScript: []fakeexec.FakeAction{
  508. // Success.
  509. func() ([]byte, []byte, error) { return []byte(iptablesSaveOutput), nil, nil },
  510. },
  511. }
  512. fexec := fakeexec.FakeExec{
  513. CommandScript: []fakeexec.FakeCommandAction{
  514. // The first Command() call is checking the rule. Success of that exec means "done".
  515. func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) },
  516. },
  517. }
  518. runner := &runner{exec: &fexec}
  519. exists, err := runner.checkRuleWithoutCheck(
  520. TableNAT, ChainPrerouting,
  521. "-m", "addrtype",
  522. "-m", "mark", "--mark", "0x4000/0x4000",
  523. "-j", "DOCKER",
  524. "--dst-type", "LOCAL")
  525. if err != nil {
  526. t.Errorf("expected success, got %v", err)
  527. }
  528. if !exists {
  529. t.Errorf("expected exists = true")
  530. }
  531. if fcmd.CombinedOutputCalls != 1 {
  532. t.Errorf("expected 1 CombinedOutput() call, got %d", fcmd.CombinedOutputCalls)
  533. }
  534. if !sets.NewString(fcmd.CombinedOutputLog[0]...).HasAll("iptables-save", "-t", "nat") {
  535. t.Errorf("wrong CombinedOutput() log, got %s", fcmd.CombinedOutputLog[0])
  536. }
  537. }
  538. func TestCheckRuleWithoutCheckAbsent(t *testing.T) {
  539. iptablesSaveOutput := `# Generated by iptables-save v1.4.7 on Wed Oct 29 14:56:01 2014
  540. *nat
  541. :PREROUTING ACCEPT [2136997:197881818]
  542. :POSTROUTING ACCEPT [4284525:258542680]
  543. :OUTPUT ACCEPT [5901660:357267963]
  544. -A PREROUTING -m addrtype --dst-type LOCAL -j DOCKER
  545. COMMIT
  546. # Completed on Wed Oct 29 14:56:01 2014`
  547. fcmd := fakeexec.FakeCmd{
  548. CombinedOutputScript: []fakeexec.FakeAction{
  549. // Success.
  550. func() ([]byte, []byte, error) { return []byte(iptablesSaveOutput), nil, nil },
  551. },
  552. }
  553. fexec := fakeexec.FakeExec{
  554. CommandScript: []fakeexec.FakeCommandAction{
  555. // The first Command() call is checking the rule. Success of that exec means "done".
  556. func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) },
  557. },
  558. }
  559. runner := &runner{exec: &fexec}
  560. exists, err := runner.checkRuleWithoutCheck(TableNAT, ChainPrerouting, "-m", "addrtype", "-j", "DOCKER")
  561. if err != nil {
  562. t.Errorf("expected success, got %v", err)
  563. }
  564. if exists {
  565. t.Errorf("expected exists = false")
  566. }
  567. if fcmd.CombinedOutputCalls != 1 {
  568. t.Errorf("expected 1 CombinedOutput() call, got %d", fcmd.CombinedOutputCalls)
  569. }
  570. if !sets.NewString(fcmd.CombinedOutputLog[0]...).HasAll("iptables-save", "-t", "nat") {
  571. t.Errorf("wrong CombinedOutput() log, got %s", fcmd.CombinedOutputLog[0])
  572. }
  573. }
  574. func TestIPTablesWaitFlag(t *testing.T) {
  575. testCases := []struct {
  576. Version string
  577. Result []string
  578. }{
  579. {"0.55.55", nil},
  580. {"1.0.55", nil},
  581. {"1.4.19", nil},
  582. {"1.4.20", []string{WaitString}},
  583. {"1.4.21", []string{WaitString}},
  584. {"1.4.22", []string{WaitString, WaitSecondsValue}},
  585. {"1.5.0", []string{WaitString, WaitSecondsValue}},
  586. {"2.0.0", []string{WaitString, WaitSecondsValue, WaitIntervalString, WaitIntervalUsecondsValue}},
  587. }
  588. for _, testCase := range testCases {
  589. result := getIPTablesWaitFlag(utilversion.MustParseGeneric(testCase.Version))
  590. if !reflect.DeepEqual(result, testCase.Result) {
  591. t.Errorf("For %s expected %v got %v", testCase.Version, testCase.Result, result)
  592. }
  593. }
  594. }
  595. func TestWaitFlagUnavailable(t *testing.T) {
  596. fcmd := fakeexec.FakeCmd{
  597. CombinedOutputScript: []fakeexec.FakeAction{
  598. // iptables version check
  599. func() ([]byte, []byte, error) { return []byte("iptables v1.4.19"), nil, nil },
  600. // iptables-restore version check
  601. func() ([]byte, []byte, error) { return []byte{}, nil, nil },
  602. // Success.
  603. func() ([]byte, []byte, error) { return []byte{}, nil, nil },
  604. },
  605. }
  606. fexec := fakeexec.FakeExec{
  607. CommandScript: []fakeexec.FakeCommandAction{
  608. // iptables version check
  609. func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) },
  610. // iptables-restore version check
  611. func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) },
  612. func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) },
  613. },
  614. }
  615. runner := New(&fexec, ProtocolIpv4)
  616. err := runner.DeleteChain(TableNAT, Chain("FOOBAR"))
  617. if err != nil {
  618. t.Errorf("expected success, got %v", err)
  619. }
  620. if fcmd.CombinedOutputCalls != 3 {
  621. t.Errorf("expected 3 CombinedOutput() calls, got %d", fcmd.CombinedOutputCalls)
  622. }
  623. if sets.NewString(fcmd.CombinedOutputLog[2]...).Has(WaitString) {
  624. t.Errorf("wrong CombinedOutput() log, got %s", fcmd.CombinedOutputLog[2])
  625. }
  626. }
  627. func TestWaitFlagOld(t *testing.T) {
  628. fcmd := fakeexec.FakeCmd{
  629. CombinedOutputScript: []fakeexec.FakeAction{
  630. // iptables version check
  631. func() ([]byte, []byte, error) { return []byte("iptables v1.4.20"), nil, nil },
  632. // iptables-restore version check
  633. func() ([]byte, []byte, error) { return []byte{}, nil, nil },
  634. // Success.
  635. func() ([]byte, []byte, error) { return []byte{}, nil, nil },
  636. },
  637. }
  638. fexec := fakeexec.FakeExec{
  639. CommandScript: []fakeexec.FakeCommandAction{
  640. func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) },
  641. func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) },
  642. func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) },
  643. },
  644. }
  645. runner := New(&fexec, ProtocolIpv4)
  646. err := runner.DeleteChain(TableNAT, Chain("FOOBAR"))
  647. if err != nil {
  648. t.Errorf("expected success, got %v", err)
  649. }
  650. if fcmd.CombinedOutputCalls != 3 {
  651. t.Errorf("expected 3 CombinedOutput() calls, got %d", fcmd.CombinedOutputCalls)
  652. }
  653. if !sets.NewString(fcmd.CombinedOutputLog[2]...).HasAll("iptables", WaitString) {
  654. t.Errorf("wrong CombinedOutput() log, got %s", fcmd.CombinedOutputLog[2])
  655. }
  656. if sets.NewString(fcmd.CombinedOutputLog[2]...).Has(WaitSecondsValue) {
  657. t.Errorf("wrong CombinedOutput() log, got %s", fcmd.CombinedOutputLog[2])
  658. }
  659. }
  660. func TestWaitFlagNew(t *testing.T) {
  661. fcmd := fakeexec.FakeCmd{
  662. CombinedOutputScript: []fakeexec.FakeAction{
  663. // iptables version check
  664. func() ([]byte, []byte, error) { return []byte("iptables v1.4.22"), nil, nil },
  665. // iptables-restore version check
  666. func() ([]byte, []byte, error) { return []byte{}, nil, nil },
  667. // Success.
  668. func() ([]byte, []byte, error) { return []byte{}, nil, nil },
  669. },
  670. }
  671. fexec := fakeexec.FakeExec{
  672. CommandScript: []fakeexec.FakeCommandAction{
  673. func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) },
  674. func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) },
  675. func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) },
  676. },
  677. }
  678. runner := New(&fexec, ProtocolIpv4)
  679. err := runner.DeleteChain(TableNAT, Chain("FOOBAR"))
  680. if err != nil {
  681. t.Errorf("expected success, got %v", err)
  682. }
  683. if fcmd.CombinedOutputCalls != 3 {
  684. t.Errorf("expected 3 CombinedOutput() calls, got %d", fcmd.CombinedOutputCalls)
  685. }
  686. if !sets.NewString(fcmd.CombinedOutputLog[2]...).HasAll("iptables", WaitString, WaitSecondsValue) {
  687. t.Errorf("wrong CombinedOutput() log, got %s", fcmd.CombinedOutputLog[2])
  688. }
  689. }
  690. func TestWaitIntervalFlagNew(t *testing.T) {
  691. fcmd := fakeexec.FakeCmd{
  692. CombinedOutputScript: []fakeexec.FakeAction{
  693. // iptables version check
  694. func() ([]byte, []byte, error) { return []byte("iptables v1.6.1"), nil, nil },
  695. // iptables-restore version check
  696. func() ([]byte, []byte, error) { return []byte{}, nil, nil },
  697. // Success.
  698. func() ([]byte, []byte, error) { return []byte{}, nil, nil },
  699. },
  700. }
  701. fexec := fakeexec.FakeExec{
  702. CommandScript: []fakeexec.FakeCommandAction{
  703. func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) },
  704. func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) },
  705. func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) },
  706. },
  707. }
  708. runner := New(&fexec, ProtocolIpv4)
  709. err := runner.DeleteChain(TableNAT, Chain("FOOBAR"))
  710. if err != nil {
  711. t.Errorf("expected success, got %v", err)
  712. }
  713. if fcmd.CombinedOutputCalls != 3 {
  714. t.Errorf("expected 3 CombinedOutput() calls, got %d", fcmd.CombinedOutputCalls)
  715. }
  716. if !sets.NewString(fcmd.CombinedOutputLog[2]...).HasAll("iptables", WaitString, WaitSecondsValue, WaitIntervalString, WaitIntervalUsecondsValue) {
  717. t.Errorf("wrong CombinedOutput() log, got %s", fcmd.CombinedOutputLog[2])
  718. }
  719. }
  720. func testSaveInto(t *testing.T, protocol Protocol) {
  721. version := " v1.9.22"
  722. iptablesCmd := iptablesCommand(protocol)
  723. iptablesSaveCmd := iptablesSaveCommand(protocol)
  724. protoStr := protocolStr(protocol)
  725. output := fmt.Sprintf(`# Generated by %s on Thu Jan 19 11:38:09 2017
  726. *filter
  727. :INPUT ACCEPT [15079:38410730]
  728. :FORWARD ACCEPT [0:0]
  729. :OUTPUT ACCEPT [11045:521562]
  730. COMMIT
  731. # Completed on Thu Jan 19 11:38:09 2017`, iptablesSaveCmd+version)
  732. stderrOutput := "#STDERR OUTPUT" // SaveInto() should should NOT capture stderr into the buffer
  733. fcmd := fakeexec.FakeCmd{
  734. CombinedOutputScript: []fakeexec.FakeAction{
  735. // iptables version check
  736. func() ([]byte, []byte, error) { return []byte(iptablesCmd + version), nil, nil },
  737. },
  738. RunScript: []fakeexec.FakeAction{
  739. func() ([]byte, []byte, error) { return []byte(output), []byte(stderrOutput), nil },
  740. func() ([]byte, []byte, error) { return nil, []byte(stderrOutput), &fakeexec.FakeExitError{Status: 1} },
  741. },
  742. }
  743. fexec := fakeexec.FakeExec{
  744. CommandScript: []fakeexec.FakeCommandAction{
  745. func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) },
  746. func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) },
  747. func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) },
  748. },
  749. }
  750. runner := New(&fexec, protocol)
  751. buffer := bytes.NewBuffer(nil)
  752. // Success.
  753. err := runner.SaveInto(TableNAT, buffer)
  754. if err != nil {
  755. t.Fatalf("%s: Expected success, got %v", protoStr, err)
  756. }
  757. if string(buffer.Bytes()) != output {
  758. t.Errorf("%s: Expected output '%s', got '%v'", protoStr, output, string(buffer.Bytes()))
  759. }
  760. if fcmd.CombinedOutputCalls != 1 {
  761. t.Errorf("%s: Expected 1 CombinedOutput() calls, got %d", protoStr, fcmd.CombinedOutputCalls)
  762. }
  763. if fcmd.RunCalls != 1 {
  764. t.Errorf("%s: Expected 1 Run() call, got %d", protoStr, fcmd.RunCalls)
  765. }
  766. if !sets.NewString(fcmd.RunLog[0]...).HasAll(iptablesSaveCmd, "-t", "nat") {
  767. t.Errorf("%s: Expected cmd containing '%s -t nat', got '%s'", protoStr, iptablesSaveCmd, fcmd.RunLog[0])
  768. }
  769. // Failure.
  770. buffer.Reset()
  771. err = runner.SaveInto(TableNAT, buffer)
  772. if err == nil {
  773. t.Errorf("%s: Expected failure", protoStr)
  774. }
  775. if string(buffer.Bytes()) != stderrOutput {
  776. t.Errorf("%s: Expected output '%s', got '%v'", protoStr, stderrOutput, string(buffer.Bytes()))
  777. }
  778. }
  779. func TestSaveIntoIPv4(t *testing.T) {
  780. testSaveInto(t, ProtocolIpv4)
  781. }
  782. func TestSaveIntoIPv6(t *testing.T) {
  783. testSaveInto(t, ProtocolIpv6)
  784. }
  785. func testRestore(t *testing.T, protocol Protocol) {
  786. version := " v1.9.22"
  787. iptablesCmd := iptablesCommand(protocol)
  788. iptablesRestoreCmd := iptablesRestoreCommand(protocol)
  789. protoStr := protocolStr(protocol)
  790. fcmd := fakeexec.FakeCmd{
  791. CombinedOutputScript: []fakeexec.FakeAction{
  792. // iptables version check
  793. func() ([]byte, []byte, error) { return []byte(iptablesCmd + version), nil, nil },
  794. func() ([]byte, []byte, error) { return []byte{}, nil, nil },
  795. func() ([]byte, []byte, error) { return []byte{}, nil, nil },
  796. func() ([]byte, []byte, error) { return []byte{}, nil, nil },
  797. func() ([]byte, []byte, error) { return []byte{}, nil, nil },
  798. func() ([]byte, []byte, error) { return nil, nil, &fakeexec.FakeExitError{Status: 1} },
  799. },
  800. }
  801. fexec := fakeexec.FakeExec{
  802. CommandScript: []fakeexec.FakeCommandAction{
  803. func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) },
  804. func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) },
  805. func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) },
  806. func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) },
  807. func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) },
  808. func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) },
  809. },
  810. }
  811. runner := New(&fexec, protocol)
  812. // both flags true
  813. err := runner.Restore(TableNAT, []byte{}, FlushTables, RestoreCounters)
  814. if err != nil {
  815. t.Errorf("%s flush,restore: Expected success, got %v", protoStr, err)
  816. }
  817. commandSet := sets.NewString(fcmd.CombinedOutputLog[1]...)
  818. if !commandSet.HasAll(iptablesRestoreCmd, "-T", string(TableNAT), "--counters") || commandSet.HasAny("--noflush") {
  819. t.Errorf("%s flush, restore: Expected cmd containing '%s -T %s --counters', got '%s'", protoStr, iptablesRestoreCmd, string(TableNAT), fcmd.CombinedOutputLog[1])
  820. }
  821. // FlushTables, NoRestoreCounters
  822. err = runner.Restore(TableNAT, []byte{}, FlushTables, NoRestoreCounters)
  823. if err != nil {
  824. t.Errorf("%s flush, no restore: Expected success, got %v", protoStr, err)
  825. }
  826. commandSet = sets.NewString(fcmd.CombinedOutputLog[2]...)
  827. if !commandSet.HasAll(iptablesRestoreCmd, "-T", string(TableNAT)) || commandSet.HasAny("--noflush", "--counters") {
  828. t.Errorf("%s flush, no restore: Expected cmd containing '--noflush' or '--counters', got '%s'", protoStr, fcmd.CombinedOutputLog[2])
  829. }
  830. // NoFlushTables, RestoreCounters
  831. err = runner.Restore(TableNAT, []byte{}, NoFlushTables, RestoreCounters)
  832. if err != nil {
  833. t.Errorf("%s no flush, restore: Expected success, got %v", protoStr, err)
  834. }
  835. commandSet = sets.NewString(fcmd.CombinedOutputLog[3]...)
  836. if !commandSet.HasAll(iptablesRestoreCmd, "-T", string(TableNAT), "--noflush", "--counters") {
  837. t.Errorf("%s no flush, restore: Expected cmd containing '--noflush' and '--counters', got '%s'", protoStr, fcmd.CombinedOutputLog[3])
  838. }
  839. // NoFlushTables, NoRestoreCounters
  840. err = runner.Restore(TableNAT, []byte{}, NoFlushTables, NoRestoreCounters)
  841. if err != nil {
  842. t.Errorf("%s no flush, no restore: Expected success, got %v", protoStr, err)
  843. }
  844. commandSet = sets.NewString(fcmd.CombinedOutputLog[4]...)
  845. if !commandSet.HasAll(iptablesRestoreCmd, "-T", string(TableNAT), "--noflush") || commandSet.HasAny("--counters") {
  846. t.Errorf("%s no flush, no restore: Expected cmd containing '%s -T %s --noflush', got '%s'", protoStr, iptablesRestoreCmd, string(TableNAT), fcmd.CombinedOutputLog[4])
  847. }
  848. if fcmd.CombinedOutputCalls != 5 {
  849. t.Errorf("%s: Expected 5 total CombinedOutput() calls, got %d", protoStr, fcmd.CombinedOutputCalls)
  850. }
  851. // Failure.
  852. err = runner.Restore(TableNAT, []byte{}, FlushTables, RestoreCounters)
  853. if err == nil {
  854. t.Errorf("%s Expected a failure", protoStr)
  855. }
  856. }
  857. func TestRestoreIPv4(t *testing.T) {
  858. testRestore(t, ProtocolIpv4)
  859. }
  860. func TestRestoreIPv6(t *testing.T) {
  861. testRestore(t, ProtocolIpv6)
  862. }
  863. // TestRestoreAll tests only the simplest use case, as flag handling code is already tested in TestRestore
  864. func TestRestoreAll(t *testing.T) {
  865. fcmd := fakeexec.FakeCmd{
  866. CombinedOutputScript: []fakeexec.FakeAction{
  867. // iptables version check
  868. func() ([]byte, []byte, error) { return []byte("iptables v1.9.22"), nil, nil },
  869. func() ([]byte, []byte, error) { return []byte{}, nil, nil },
  870. func() ([]byte, []byte, error) { return nil, nil, &fakeexec.FakeExitError{Status: 1} },
  871. },
  872. }
  873. fexec := fakeexec.FakeExec{
  874. CommandScript: []fakeexec.FakeCommandAction{
  875. func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) },
  876. func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) },
  877. func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) },
  878. },
  879. }
  880. runner := newInternal(&fexec, ProtocolIpv4, TestLockfilePath)
  881. defer os.Remove(TestLockfilePath)
  882. err := runner.RestoreAll([]byte{}, NoFlushTables, RestoreCounters)
  883. if err != nil {
  884. t.Fatalf("expected success, got %v", err)
  885. }
  886. commandSet := sets.NewString(fcmd.CombinedOutputLog[1]...)
  887. if !commandSet.HasAll("iptables-restore", "--counters", "--noflush") {
  888. t.Errorf("wrong CombinedOutput() log, got %s", fcmd.CombinedOutputLog[2])
  889. }
  890. if fcmd.CombinedOutputCalls != 2 {
  891. t.Errorf("expected 2 CombinedOutput() calls, got %d", fcmd.CombinedOutputCalls)
  892. }
  893. // Failure.
  894. err = runner.Restore(TableNAT, []byte{}, FlushTables, RestoreCounters)
  895. if err == nil {
  896. t.Errorf("expected failure")
  897. }
  898. }
  899. // TestRestoreAllWait tests that the "wait" flag is passed to a compatible iptables-restore
  900. func TestRestoreAllWait(t *testing.T) {
  901. fcmd := fakeexec.FakeCmd{
  902. CombinedOutputScript: []fakeexec.FakeAction{
  903. // iptables version check
  904. func() ([]byte, []byte, error) { return []byte("iptables v1.9.22"), nil, nil },
  905. func() ([]byte, []byte, error) { return []byte{}, nil, nil },
  906. func() ([]byte, []byte, error) { return nil, nil, &fakeexec.FakeExitError{Status: 1} },
  907. },
  908. }
  909. fexec := fakeexec.FakeExec{
  910. CommandScript: []fakeexec.FakeCommandAction{
  911. func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) },
  912. func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) },
  913. func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) },
  914. },
  915. }
  916. runner := newInternal(&fexec, ProtocolIpv4, TestLockfilePath)
  917. defer os.Remove(TestLockfilePath)
  918. err := runner.RestoreAll([]byte{}, NoFlushTables, RestoreCounters)
  919. if err != nil {
  920. t.Fatalf("expected success, got %v", err)
  921. }
  922. commandSet := sets.NewString(fcmd.CombinedOutputLog[1]...)
  923. if !commandSet.HasAll("iptables-restore", WaitString, WaitSecondsValue, WaitIntervalString, WaitIntervalUsecondsValue, "--counters", "--noflush") {
  924. t.Errorf("wrong CombinedOutput() log, got %s", fcmd.CombinedOutputLog[1])
  925. }
  926. if fcmd.CombinedOutputCalls != 2 {
  927. t.Errorf("expected 2 CombinedOutput() calls, got %d", fcmd.CombinedOutputCalls)
  928. }
  929. // Failure.
  930. err = runner.Restore(TableNAT, []byte{}, FlushTables, RestoreCounters)
  931. if err == nil {
  932. t.Errorf("expected failure")
  933. }
  934. }
  935. // TestRestoreAllWaitOldIptablesRestore tests that the "wait" flag is not passed
  936. // to an old iptables-restore
  937. func TestRestoreAllWaitOldIptablesRestore(t *testing.T) {
  938. fcmd := fakeexec.FakeCmd{
  939. CombinedOutputScript: []fakeexec.FakeAction{
  940. // iptables version check
  941. func() ([]byte, []byte, error) { return []byte("iptables v1.4.22"), nil, nil },
  942. // iptables-restore version check
  943. func() ([]byte, []byte, error) { return []byte{}, nil, nil },
  944. func() ([]byte, []byte, error) { return []byte{}, nil, nil },
  945. func() ([]byte, []byte, error) { return nil, nil, &fakeexec.FakeExitError{Status: 1} },
  946. },
  947. }
  948. fexec := fakeexec.FakeExec{
  949. CommandScript: []fakeexec.FakeCommandAction{
  950. func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) },
  951. func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) },
  952. func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) },
  953. func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) },
  954. },
  955. }
  956. runner := newInternal(&fexec, ProtocolIpv4, TestLockfilePath)
  957. defer os.Remove(TestLockfilePath)
  958. err := runner.RestoreAll([]byte{}, NoFlushTables, RestoreCounters)
  959. if err != nil {
  960. t.Fatalf("expected success, got %v", err)
  961. }
  962. commandSet := sets.NewString(fcmd.CombinedOutputLog[2]...)
  963. if !commandSet.HasAll("iptables-restore", "--counters", "--noflush") {
  964. t.Errorf("wrong CombinedOutput() log, got %s", fcmd.CombinedOutputLog[2])
  965. }
  966. if commandSet.HasAll(WaitString) {
  967. t.Errorf("wrong CombinedOutput() log (unexpected %s option), got %s", WaitString, fcmd.CombinedOutputLog[1])
  968. }
  969. if fcmd.CombinedOutputCalls != 3 {
  970. t.Errorf("expected 3 CombinedOutput() calls, got %d", fcmd.CombinedOutputCalls)
  971. }
  972. // Failure.
  973. err = runner.Restore(TableNAT, []byte{}, FlushTables, RestoreCounters)
  974. if err == nil {
  975. t.Errorf("expected failure")
  976. }
  977. }
  978. // TestRestoreAllGrabNewLock tests that the iptables code will grab the
  979. // iptables /run lock when using an iptables-restore version that does not
  980. // support the --wait argument
  981. func TestRestoreAllGrabNewLock(t *testing.T) {
  982. fcmd := fakeexec.FakeCmd{
  983. CombinedOutputScript: []fakeexec.FakeAction{
  984. // iptables version check
  985. func() ([]byte, []byte, error) { return []byte("iptables v1.4.22"), nil, nil },
  986. // iptables-restore version check
  987. func() ([]byte, []byte, error) { return []byte{}, nil, nil },
  988. },
  989. }
  990. fexec := fakeexec.FakeExec{
  991. CommandScript: []fakeexec.FakeCommandAction{
  992. func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) },
  993. func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) },
  994. },
  995. }
  996. runner := newInternal(&fexec, ProtocolIpv4, TestLockfilePath)
  997. defer os.Remove(TestLockfilePath)
  998. // Grab the /run lock and ensure the RestoreAll fails
  999. runLock, err := os.OpenFile(TestLockfilePath, os.O_CREATE, 0600)
  1000. if err != nil {
  1001. t.Fatalf("expected to open %s, got %v", TestLockfilePath, err)
  1002. }
  1003. defer runLock.Close()
  1004. if err := grabIptablesFileLock(runLock); err != nil {
  1005. t.Errorf("expected to lock %s, got %v", TestLockfilePath, err)
  1006. }
  1007. err = runner.RestoreAll([]byte{}, NoFlushTables, RestoreCounters)
  1008. if err == nil {
  1009. t.Errorf("expected failure, got success instead")
  1010. }
  1011. if !strings.Contains(err.Error(), "failed to acquire new iptables lock: timed out waiting for the condition") {
  1012. t.Errorf("expected timeout error, got %v", err)
  1013. }
  1014. }
  1015. // TestRestoreAllGrabOldLock tests that the iptables code will grab the
  1016. // iptables @xtables abstract unix socket lock when using an iptables-restore
  1017. // version that does not support the --wait argument
  1018. func TestRestoreAllGrabOldLock(t *testing.T) {
  1019. fcmd := fakeexec.FakeCmd{
  1020. CombinedOutputScript: []fakeexec.FakeAction{
  1021. // iptables version check
  1022. func() ([]byte, []byte, error) { return []byte("iptables v1.4.22"), nil, nil },
  1023. // iptables-restore version check
  1024. func() ([]byte, []byte, error) { return []byte{}, nil, nil },
  1025. },
  1026. }
  1027. fexec := fakeexec.FakeExec{
  1028. CommandScript: []fakeexec.FakeCommandAction{
  1029. func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) },
  1030. func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) },
  1031. },
  1032. }
  1033. runner := newInternal(&fexec, ProtocolIpv4, TestLockfilePath)
  1034. defer os.Remove(TestLockfilePath)
  1035. // Grab the abstract @xtables socket
  1036. runLock, err := net.ListenUnix("unix", &net.UnixAddr{Name: "@xtables", Net: "unix"})
  1037. if err != nil {
  1038. t.Fatalf("expected to lock @xtables, got %v", err)
  1039. }
  1040. defer runLock.Close()
  1041. err = runner.RestoreAll([]byte{}, NoFlushTables, RestoreCounters)
  1042. if err == nil {
  1043. t.Errorf("expected failure, got success instead")
  1044. }
  1045. if !strings.Contains(err.Error(), "failed to acquire old iptables lock: timed out waiting for the condition") {
  1046. t.Errorf("expected timeout error, got %v", err)
  1047. }
  1048. }
  1049. // TestRestoreAllWaitBackportedIptablesRestore tests that the "wait" flag is passed
  1050. // to a seemingly-old-but-actually-new iptables-restore
  1051. func TestRestoreAllWaitBackportedIptablesRestore(t *testing.T) {
  1052. fcmd := fakeexec.FakeCmd{
  1053. CombinedOutputScript: []fakeexec.FakeAction{
  1054. // iptables version check
  1055. func() ([]byte, []byte, error) { return []byte("iptables v1.4.22"), nil, nil },
  1056. // iptables-restore version check
  1057. func() ([]byte, []byte, error) { return []byte("iptables v1.4.22"), nil, nil },
  1058. func() ([]byte, []byte, error) { return []byte{}, nil, nil },
  1059. func() ([]byte, []byte, error) { return nil, nil, &fakeexec.FakeExitError{Status: 1} },
  1060. },
  1061. }
  1062. fexec := fakeexec.FakeExec{
  1063. CommandScript: []fakeexec.FakeCommandAction{
  1064. func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) },
  1065. func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) },
  1066. func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) },
  1067. func(cmd string, args ...string) exec.Cmd { return fakeexec.InitFakeCmd(&fcmd, cmd, args...) },
  1068. },
  1069. }
  1070. runner := newInternal(&fexec, ProtocolIpv4, TestLockfilePath)
  1071. defer os.Remove(TestLockfilePath)
  1072. err := runner.RestoreAll([]byte{}, NoFlushTables, RestoreCounters)
  1073. if err != nil {
  1074. t.Fatalf("expected success, got %v", err)
  1075. }
  1076. commandSet := sets.NewString(fcmd.CombinedOutputLog[2]...)
  1077. if !commandSet.HasAll("iptables-restore", "--counters", "--noflush") {
  1078. t.Errorf("wrong CombinedOutput() log, got %s", fcmd.CombinedOutputLog[2])
  1079. }
  1080. if !commandSet.HasAll(WaitString) {
  1081. t.Errorf("wrong CombinedOutput() log (expected %s option), got %s", WaitString, fcmd.CombinedOutputLog[1])
  1082. }
  1083. if fcmd.CombinedOutputCalls != 3 {
  1084. t.Errorf("expected 3 CombinedOutput() calls, got %d", fcmd.CombinedOutputCalls)
  1085. }
  1086. // Failure.
  1087. err = runner.Restore(TableNAT, []byte{}, FlushTables, RestoreCounters)
  1088. if err == nil {
  1089. t.Errorf("expected failure")
  1090. }
  1091. }