github.com/Prakhar-Agarwal-byte/moby@v0.0.0-20231027092010-a14e3e8ab87e/libnetwork/drivers/bridge/setup_ip_tables_linux_test.go (about) 1 package bridge 2 3 import ( 4 "net" 5 "testing" 6 7 "github.com/Prakhar-Agarwal-byte/moby/internal/testutils/netnsutils" 8 "github.com/Prakhar-Agarwal-byte/moby/libnetwork/driverapi" 9 "github.com/Prakhar-Agarwal-byte/moby/libnetwork/iptables" 10 "github.com/Prakhar-Agarwal-byte/moby/libnetwork/netlabel" 11 "github.com/Prakhar-Agarwal-byte/moby/libnetwork/portmapper" 12 "github.com/vishvananda/netlink" 13 "gotest.tools/v3/assert" 14 ) 15 16 const ( 17 iptablesTestBridgeIP = "192.168.42.1" 18 ) 19 20 // A testRegisterer implements the driverapi.Registerer interface. 21 type testRegisterer struct { 22 t *testing.T 23 d *driver 24 } 25 26 func (r *testRegisterer) RegisterDriver(name string, di driverapi.Driver, _ driverapi.Capability) error { 27 if got, want := name, "bridge"; got != want { 28 r.t.Fatalf("got driver name %s, want %s", got, want) 29 } 30 d, ok := di.(*driver) 31 if !ok { 32 r.t.Fatalf("got driver type %T, want %T", di, &driver{}) 33 } 34 r.d = d 35 return nil 36 } 37 38 func TestProgramIPTable(t *testing.T) { 39 // Create a test bridge with a basic bridge configuration (name + IPv4). 40 defer netnsutils.SetupTestOSContext(t)() 41 42 nh, err := netlink.NewHandle() 43 if err != nil { 44 t.Fatal(err) 45 } 46 47 createTestBridge(getBasicTestConfig(), &bridgeInterface{nlh: nh}, t) 48 49 // Store various iptables chain rules we care for. 50 rules := []struct { 51 rule iptRule 52 descr string 53 }{ 54 {iptRule{ipv: iptables.IPv4, table: iptables.Filter, chain: "FORWARD", args: []string{"-d", "127.1.2.3", "-i", "lo", "-o", "lo", "-j", "DROP"}}, "Test Loopback"}, 55 {iptRule{ipv: iptables.IPv4, table: iptables.Nat, chain: "POSTROUTING", args: []string{"-s", iptablesTestBridgeIP, "!", "-o", DefaultBridgeName, "-j", "MASQUERADE"}}, "NAT Test"}, 56 {iptRule{ipv: iptables.IPv4, table: iptables.Filter, chain: "FORWARD", args: []string{"-o", DefaultBridgeName, "-m", "conntrack", "--ctstate", "RELATED,ESTABLISHED", "-j", "ACCEPT"}}, "Test ACCEPT INCOMING"}, 57 {iptRule{ipv: iptables.IPv4, table: iptables.Filter, chain: "FORWARD", args: []string{"-i", DefaultBridgeName, "!", "-o", DefaultBridgeName, "-j", "ACCEPT"}}, "Test ACCEPT NON_ICC OUTGOING"}, 58 {iptRule{ipv: iptables.IPv4, table: iptables.Filter, chain: "FORWARD", args: []string{"-i", DefaultBridgeName, "-o", DefaultBridgeName, "-j", "ACCEPT"}}, "Test enable ICC"}, 59 {iptRule{ipv: iptables.IPv4, table: iptables.Filter, chain: "FORWARD", args: []string{"-i", DefaultBridgeName, "-o", DefaultBridgeName, "-j", "DROP"}}, "Test disable ICC"}, 60 } 61 62 // Assert the chain rules' insertion and removal. 63 for _, c := range rules { 64 assertIPTableChainProgramming(c.rule, c.descr, t) 65 } 66 } 67 68 func TestSetupIPChains(t *testing.T) { 69 // Create a test bridge with a basic bridge configuration (name + IPv4). 70 defer netnsutils.SetupTestOSContext(t)() 71 72 nh, err := netlink.NewHandle() 73 if err != nil { 74 t.Fatal(err) 75 } 76 77 driverconfig := configuration{ 78 EnableIPTables: true, 79 } 80 d := &driver{ 81 config: driverconfig, 82 } 83 assertChainConfig(d, t) 84 85 config := getBasicTestConfig() 86 br := &bridgeInterface{nlh: nh} 87 createTestBridge(config, br, t) 88 89 assertBridgeConfig(config, br, d, t) 90 91 config.EnableIPMasquerade = true 92 assertBridgeConfig(config, br, d, t) 93 94 config.EnableICC = true 95 assertBridgeConfig(config, br, d, t) 96 97 config.EnableIPMasquerade = false 98 assertBridgeConfig(config, br, d, t) 99 } 100 101 func getBasicTestConfig() *networkConfiguration { 102 config := &networkConfiguration{ 103 BridgeName: DefaultBridgeName, 104 AddressIPv4: &net.IPNet{IP: net.ParseIP(iptablesTestBridgeIP), Mask: net.CIDRMask(16, 32)}, 105 } 106 return config 107 } 108 109 func createTestBridge(config *networkConfiguration, br *bridgeInterface, t *testing.T) { 110 if err := setupDevice(config, br); err != nil { 111 t.Fatalf("Failed to create the testing Bridge: %s", err.Error()) 112 } 113 if err := setupBridgeIPv4(config, br); err != nil { 114 t.Fatalf("Failed to bring up the testing Bridge: %s", err.Error()) 115 } 116 if config.EnableIPv6 { 117 if err := setupBridgeIPv6(config, br); err != nil { 118 t.Fatalf("Failed to bring up the testing Bridge: %s", err.Error()) 119 } 120 } 121 } 122 123 // Assert base function which pushes iptables chain rules on insertion and removal. 124 func assertIPTableChainProgramming(rule iptRule, descr string, t *testing.T) { 125 // Add 126 if err := programChainRule(rule, descr, true); err != nil { 127 t.Fatalf("Failed to program iptable rule %s: %s", descr, err.Error()) 128 } 129 130 if !rule.Exists() { 131 t.Fatalf("Failed to effectively program iptable rule: %s", descr) 132 } 133 134 // Remove 135 if err := programChainRule(rule, descr, false); err != nil { 136 t.Fatalf("Failed to remove iptable rule %s: %s", descr, err.Error()) 137 } 138 if rule.Exists() { 139 t.Fatalf("Failed to effectively remove iptable rule: %s", descr) 140 } 141 } 142 143 // Assert function which create chains. 144 func assertChainConfig(d *driver, t *testing.T) { 145 var err error 146 147 d.natChain, d.filterChain, d.isolationChain1, d.isolationChain2, err = setupIPChains(d.config, iptables.IPv4) 148 if err != nil { 149 t.Fatal(err) 150 } 151 if d.config.EnableIP6Tables { 152 d.natChainV6, d.filterChainV6, d.isolationChain1V6, d.isolationChain2V6, err = setupIPChains(d.config, iptables.IPv6) 153 if err != nil { 154 t.Fatal(err) 155 } 156 } 157 } 158 159 // Assert function which pushes chains based on bridge config parameters. 160 func assertBridgeConfig(config *networkConfiguration, br *bridgeInterface, d *driver, t *testing.T) { 161 nw := bridgeNetwork{ 162 portMapper: portmapper.New(), 163 portMapperV6: portmapper.New(), 164 config: config, 165 } 166 nw.driver = d 167 168 // Attempt programming of ip tables. 169 err := nw.setupIP4Tables(config, br) 170 if err != nil { 171 t.Fatalf("%v", err) 172 } 173 if d.config.EnableIP6Tables { 174 if err := nw.setupIP6Tables(config, br); err != nil { 175 t.Fatalf("%v", err) 176 } 177 } 178 } 179 180 // Regression test for https://github.com/moby/moby/issues/46445 181 func TestSetupIP6TablesWithHostIPv4(t *testing.T) { 182 defer netnsutils.SetupTestOSContext(t)() 183 d := newDriver() 184 dc := &configuration{ 185 EnableIPTables: true, 186 EnableIP6Tables: true, 187 } 188 if err := d.configure(map[string]interface{}{netlabel.GenericData: dc}); err != nil { 189 t.Fatal(err) 190 } 191 nc := &networkConfiguration{ 192 BridgeName: DefaultBridgeName, 193 AddressIPv4: &net.IPNet{IP: net.ParseIP(iptablesTestBridgeIP), Mask: net.CIDRMask(16, 32)}, 194 EnableIPMasquerade: true, 195 EnableIPv6: true, 196 AddressIPv6: &net.IPNet{IP: net.ParseIP("2001:db8::1"), Mask: net.CIDRMask(64, 128)}, 197 HostIPv4: net.ParseIP("192.0.2.2"), 198 } 199 nh, err := netlink.NewHandle() 200 if err != nil { 201 t.Fatal(err) 202 } 203 br := &bridgeInterface{nlh: nh} 204 createTestBridge(nc, br, t) 205 assertBridgeConfig(nc, br, d, t) 206 } 207 208 func TestOutgoingNATRules(t *testing.T) { 209 br := "br-nattest" 210 brIPv4 := &net.IPNet{IP: net.ParseIP(iptablesTestBridgeIP), Mask: net.CIDRMask(16, 32)} 211 brIPv6 := &net.IPNet{IP: net.ParseIP("2001:db8::1"), Mask: net.CIDRMask(64, 128)} 212 maskedBrIPv4 := &net.IPNet{IP: brIPv4.IP.Mask(brIPv4.Mask), Mask: brIPv4.Mask} 213 maskedBrIPv6 := &net.IPNet{IP: brIPv6.IP.Mask(brIPv6.Mask), Mask: brIPv6.Mask} 214 hostIPv4 := net.ParseIP("192.0.2.2") 215 for _, tc := range []struct { 216 desc string 217 enableIPTables bool 218 enableIP6Tables bool 219 enableIPv6 bool 220 enableIPMasquerade bool 221 hostIPv4 net.IP 222 // Hairpin NAT rules are not tested here because they are orthogonal to outgoing NAT. They 223 // exist to support the port forwarding DNAT rules: without any port forwarding there would be 224 // no need for any hairpin NAT rules, and when there is port forwarding then hairpin NAT rules 225 // are needed even if outgoing NAT is disabled. Hairpin NAT tests belong with the port 226 // forwarding DNAT tests. 227 wantIPv4Masq bool 228 wantIPv4Snat bool 229 wantIPv6Masq bool 230 }{ 231 { 232 desc: "everything disabled", 233 }, 234 { 235 desc: "iptables/ip6tables disabled", 236 enableIPv6: true, 237 enableIPMasquerade: true, 238 }, 239 { 240 desc: "host IP with iptables/ip6tables disabled", 241 enableIPv6: true, 242 enableIPMasquerade: true, 243 hostIPv4: hostIPv4, 244 }, 245 { 246 desc: "masquerade disabled, no host IP", 247 enableIPTables: true, 248 enableIP6Tables: true, 249 enableIPv6: true, 250 }, 251 { 252 desc: "masquerade disabled, with host IP", 253 enableIPTables: true, 254 enableIP6Tables: true, 255 enableIPv6: true, 256 hostIPv4: hostIPv4, 257 }, 258 { 259 desc: "IPv4 masquerade, IPv6 disabled", 260 enableIPTables: true, 261 enableIPMasquerade: true, 262 wantIPv4Masq: true, 263 }, 264 { 265 desc: "IPv4 SNAT, IPv6 disabled", 266 enableIPTables: true, 267 enableIPMasquerade: true, 268 hostIPv4: hostIPv4, 269 wantIPv4Snat: true, 270 }, 271 { 272 desc: "IPv4 masquerade, IPv6 masquerade", 273 enableIPTables: true, 274 enableIP6Tables: true, 275 enableIPv6: true, 276 enableIPMasquerade: true, 277 wantIPv4Masq: true, 278 wantIPv6Masq: true, 279 }, 280 { 281 desc: "IPv4 SNAT, IPv6 masquerade", 282 enableIPTables: true, 283 enableIP6Tables: true, 284 enableIPv6: true, 285 enableIPMasquerade: true, 286 hostIPv4: hostIPv4, 287 wantIPv4Snat: true, 288 wantIPv6Masq: true, 289 }, 290 } { 291 t.Run(tc.desc, func(t *testing.T) { 292 defer netnsutils.SetupTestOSContext(t)() 293 dc := &configuration{ 294 EnableIPTables: tc.enableIPTables, 295 EnableIP6Tables: tc.enableIP6Tables, 296 } 297 r := &testRegisterer{t: t} 298 if err := Register(r, map[string]interface{}{netlabel.GenericData: dc}); err != nil { 299 t.Fatal(err) 300 } 301 if r.d == nil { 302 t.Fatal("testRegisterer.RegisterDriver never called") 303 } 304 nc := &networkConfiguration{ 305 BridgeName: br, 306 AddressIPv4: brIPv4, 307 AddressIPv6: brIPv6, 308 EnableIPv6: tc.enableIPv6, 309 EnableIPMasquerade: tc.enableIPMasquerade, 310 HostIPv4: tc.hostIPv4, 311 } 312 ipv4Data := []driverapi.IPAMData{{Pool: maskedBrIPv4, Gateway: brIPv4}} 313 ipv6Data := []driverapi.IPAMData{{Pool: maskedBrIPv6, Gateway: brIPv6}} 314 if !nc.EnableIPv6 { 315 nc.AddressIPv6 = nil 316 ipv6Data = nil 317 } 318 if err := r.d.CreateNetwork("nattest", map[string]interface{}{netlabel.GenericData: nc}, nil, ipv4Data, ipv6Data); err != nil { 319 t.Fatal(err) 320 } 321 defer func() { 322 if err := r.d.DeleteNetwork("nattest"); err != nil { 323 t.Fatal(err) 324 } 325 }() 326 // Log the contents of all chains to aid troubleshooting. 327 for _, ipv := range []iptables.IPVersion{iptables.IPv4, iptables.IPv6} { 328 ipt := iptables.GetIptable(ipv) 329 for _, table := range []iptables.Table{iptables.Nat, iptables.Filter, iptables.Mangle} { 330 out, err := ipt.Raw("-t", string(table), "-S") 331 if err != nil { 332 t.Error(err) 333 } 334 t.Logf("%s: %s %s table rules:\n%s", tc.desc, ipv, table, string(out)) 335 } 336 } 337 for _, rc := range []struct { 338 want bool 339 rule iptRule 340 }{ 341 // Rule order doesn't matter: At most one of the following IPv4 rules will exist, and the 342 // same goes for the IPv6 rules. 343 {tc.wantIPv4Masq, iptRule{iptables.IPv4, iptables.Nat, "POSTROUTING", []string{"-s", maskedBrIPv4.String(), "!", "-o", br, "-j", "MASQUERADE"}}}, 344 {tc.wantIPv4Snat, iptRule{iptables.IPv4, iptables.Nat, "POSTROUTING", []string{"-s", maskedBrIPv4.String(), "!", "-o", br, "-j", "SNAT", "--to-source", hostIPv4.String()}}}, 345 {tc.wantIPv6Masq, iptRule{iptables.IPv6, iptables.Nat, "POSTROUTING", []string{"-s", maskedBrIPv6.String(), "!", "-o", br, "-j", "MASQUERADE"}}}, 346 } { 347 assert.Equal(t, rc.rule.Exists(), rc.want) 348 } 349 }) 350 } 351 }