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