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