portmap: increase test coverage to 1.0.0 and older spec versions

Signed-off-by: Dan Williams <dcbw@redhat.com>
This commit is contained in:
Dan Williams 2021-02-15 10:08:24 -06:00
parent 8f7fe6d8e8
commit d2e5b5decb
2 changed files with 641 additions and 628 deletions

View File

@ -25,7 +25,7 @@ import (
"path/filepath" "path/filepath"
"github.com/containernetworking/cni/libcni" "github.com/containernetworking/cni/libcni"
current "github.com/containernetworking/cni/pkg/types/100" "github.com/containernetworking/cni/pkg/types/100"
"github.com/containernetworking/plugins/pkg/ns" "github.com/containernetworking/plugins/pkg/ns"
"github.com/containernetworking/plugins/pkg/testutils" "github.com/containernetworking/plugins/pkg/testutils"
"github.com/coreos/go-iptables/iptables" "github.com/coreos/go-iptables/iptables"
@ -37,9 +37,36 @@ import (
const TIMEOUT = 90 const TIMEOUT = 90
func makeConfig(ver string) *libcni.NetworkConfigList {
configList, err := libcni.ConfListFromBytes([]byte(fmt.Sprintf(`{
"cniVersion": "%s",
"name": "cni-portmap-unit-test",
"plugins": [
{
"type": "ptp",
"ipMasq": true,
"ipam": {
"type": "host-local",
"subnet": "172.16.31.0/24",
"routes": [
{"dst": "0.0.0.0/0"}
]
}
},
{
"type": "portmap",
"capabilities": {
"portMappings": true
}
}
]
}`, ver)))
Expect(err).NotTo(HaveOccurred())
return configList
}
var _ = Describe("portmap integration tests", func() { var _ = Describe("portmap integration tests", func() {
var ( var (
configList *libcni.NetworkConfigList
cniConf *libcni.CNIConfig cniConf *libcni.CNIConfig
targetNS ns.NetNS targetNS ns.NetNS
containerPort int containerPort int
@ -47,38 +74,11 @@ var _ = Describe("portmap integration tests", func() {
) )
BeforeEach(func() { BeforeEach(func() {
var err error
rawConfig := `{
"cniVersion": "0.3.0",
"name": "cni-portmap-unit-test",
"plugins": [
{
"type": "ptp",
"ipMasq": true,
"ipam": {
"type": "host-local",
"subnet": "172.16.31.0/24",
"routes": [
{"dst": "0.0.0.0/0"}
]
}
},
{
"type": "portmap",
"capabilities": {
"portMappings": true
}
}
]
}`
configList, err = libcni.ConfListFromBytes([]byte(rawConfig))
Expect(err).NotTo(HaveOccurred())
// turn PATH in to CNI_PATH // turn PATH in to CNI_PATH
dirs := filepath.SplitList(os.Getenv("PATH")) dirs := filepath.SplitList(os.Getenv("PATH"))
cniConf = &libcni.CNIConfig{Path: dirs} cniConf = &libcni.CNIConfig{Path: dirs}
var err error
targetNS, err = testutils.NewNS() targetNS, err = testutils.NewNS()
Expect(err).NotTo(HaveOccurred()) Expect(err).NotTo(HaveOccurred())
fmt.Fprintln(GinkgoWriter, "namespace:", targetNS.Path()) fmt.Fprintln(GinkgoWriter, "namespace:", targetNS.Path())
@ -90,333 +90,340 @@ var _ = Describe("portmap integration tests", func() {
AfterEach(func() { AfterEach(func() {
session.Terminate().Wait() session.Terminate().Wait()
if targetNS != nil { targetNS.Close()
targetNS.Close() testutils.UnmountNS(targetNS)
}
}) })
Describe("Creating an interface in a namespace with the ptp plugin", func() { for _, ver := range []string{"0.3.0", "0.3.1", "0.4.0", "1.0.0"} {
// This needs to be done using Ginkgo's asynchronous testing mode. // Redefine ver inside for scope so real value is picked up by each dynamically defined It()
It("forwards a TCP port on ipv4", func(done Done) { // See Gingkgo's "Patterns for dynamically generating tests" documentation.
var err error ver := ver
hostPort := rand.Intn(10000) + 1025
runtimeConfig := libcni.RuntimeConf{ Describe("Creating an interface in a namespace with the ptp plugin", func() {
ContainerID: fmt.Sprintf("unit-test-%d", hostPort), // This needs to be done using Ginkgo's asynchronous testing mode.
NetNS: targetNS.Path(), It(fmt.Sprintf("[%s] forwards a TCP port on ipv4", ver), func(done Done) {
IfName: "eth0", var err error
CapabilityArgs: map[string]interface{}{ hostPort := rand.Intn(10000) + 1025
"portMappings": []map[string]interface{}{ runtimeConfig := libcni.RuntimeConf{
{ ContainerID: fmt.Sprintf("unit-test-%d", hostPort),
"hostPort": hostPort, NetNS: targetNS.Path(),
"containerPort": containerPort, IfName: "eth0",
"protocol": "tcp", CapabilityArgs: map[string]interface{}{
"portMappings": []map[string]interface{}{
{
"hostPort": hostPort,
"containerPort": containerPort,
"protocol": "tcp",
},
}, },
}, },
},
}
// Make delete idempotent, so we can clean up on failure
netDeleted := false
deleteNetwork := func() error {
if netDeleted {
return nil
} }
netDeleted = true configList := makeConfig(ver)
return cniConf.DelNetworkList(context.TODO(), configList, &runtimeConfig)
}
// we'll also manually check the iptables chains // Make delete idempotent, so we can clean up on failure
ipt, err := iptables.NewWithProtocol(iptables.ProtocolIPv4) netDeleted := false
Expect(err).NotTo(HaveOccurred()) deleteNetwork := func() error {
dnatChainName := genDnatChain("cni-portmap-unit-test", runtimeConfig.ContainerID).name if netDeleted {
return nil
// Create the network }
resI, err := cniConf.AddNetworkList(context.TODO(), configList, &runtimeConfig) netDeleted = true
Expect(err).NotTo(HaveOccurred()) return cniConf.DelNetworkList(context.TODO(), configList, &runtimeConfig)
defer deleteNetwork()
// Undo Docker's forwarding policy
cmd := exec.Command("iptables", "-t", "filter",
"-P", "FORWARD", "ACCEPT")
cmd.Stderr = GinkgoWriter
err = cmd.Run()
Expect(err).NotTo(HaveOccurred())
// Check the chain exists
_, err = ipt.List("nat", dnatChainName)
Expect(err).NotTo(HaveOccurred())
result, err := current.GetResult(resI)
Expect(err).NotTo(HaveOccurred())
var contIP net.IP
for _, ip := range result.IPs {
intfIndex := *ip.Interface
if result.Interfaces[intfIndex].Sandbox == "" {
continue
} }
contIP = ip.Address.IP
}
if contIP == nil {
Fail("could not determine container IP")
}
hostIP := getLocalIP() // we'll also manually check the iptables chains
fmt.Fprintf(GinkgoWriter, "hostIP: %s:%d, contIP: %s:%d\n", ipt, err := iptables.NewWithProtocol(iptables.ProtocolIPv4)
hostIP, hostPort, contIP, containerPort) Expect(err).NotTo(HaveOccurred())
dnatChainName := genDnatChain("cni-portmap-unit-test", runtimeConfig.ContainerID).name
// dump iptables-save output for debugging // Create the network
cmd = exec.Command("iptables-save") resI, err := cniConf.AddNetworkList(context.TODO(), configList, &runtimeConfig)
cmd.Stderr = GinkgoWriter Expect(err).NotTo(HaveOccurred())
cmd.Stdout = GinkgoWriter defer deleteNetwork()
Expect(cmd.Run()).To(Succeed())
// dump ip routes output for debugging // Undo Docker's forwarding policy
cmd = exec.Command("ip", "route") cmd := exec.Command("iptables", "-t", "filter",
cmd.Stderr = GinkgoWriter "-P", "FORWARD", "ACCEPT")
cmd.Stdout = GinkgoWriter cmd.Stderr = GinkgoWriter
Expect(cmd.Run()).To(Succeed()) err = cmd.Run()
Expect(err).NotTo(HaveOccurred())
// dump ip addresses output for debugging // Check the chain exists
cmd = exec.Command("ip", "addr") _, err = ipt.List("nat", dnatChainName)
cmd.Stderr = GinkgoWriter Expect(err).NotTo(HaveOccurred())
cmd.Stdout = GinkgoWriter
Expect(cmd.Run()).To(Succeed())
// Sanity check: verify that the container is reachable directly result, err := types100.GetResult(resI)
contOK := testEchoServer(contIP.String(), "tcp", containerPort, "") Expect(err).NotTo(HaveOccurred())
var contIP net.IP
// Verify that a connection to the forwarded port works for _, ip := range result.IPs {
dnatOK := testEchoServer(hostIP, "tcp", hostPort, "") intfIndex := *ip.Interface
if result.Interfaces[intfIndex].Sandbox == "" {
continue
}
contIP = ip.Address.IP
}
if contIP == nil {
Fail("could not determine container IP")
}
// Verify that a connection to localhost works hostIP := getLocalIP()
snatOK := testEchoServer("127.0.0.1", "tcp", hostPort, "") fmt.Fprintf(GinkgoWriter, "hostIP: %s:%d, contIP: %s:%d\n",
hostIP, hostPort, contIP, containerPort)
// verify that hairpin works // dump iptables-save output for debugging
hairpinOK := testEchoServer(hostIP, "tcp", hostPort, targetNS.Path()) cmd = exec.Command("iptables-save")
cmd.Stderr = GinkgoWriter
cmd.Stdout = GinkgoWriter
Expect(cmd.Run()).To(Succeed())
// Cleanup // dump ip routes output for debugging
session.Terminate() cmd = exec.Command("ip", "route")
err = deleteNetwork() cmd.Stderr = GinkgoWriter
Expect(err).NotTo(HaveOccurred()) cmd.Stdout = GinkgoWriter
Expect(cmd.Run()).To(Succeed())
// Verify iptables rules are gone // dump ip addresses output for debugging
_, err = ipt.List("nat", dnatChainName) cmd = exec.Command("ip", "addr")
Expect(err).To(MatchError(ContainSubstring("iptables: No chain/target/match by that name."))) cmd.Stderr = GinkgoWriter
cmd.Stdout = GinkgoWriter
Expect(cmd.Run()).To(Succeed())
// Check that everything succeeded *after* we clean up the network // Sanity check: verify that the container is reachable directly
if !contOK { contOK := testEchoServer(contIP.String(), "tcp", containerPort, "")
Fail("connection direct to " + contIP.String() + " failed")
}
if !dnatOK {
Fail("Connection to " + hostIP + " was not forwarded")
}
if !snatOK {
Fail("connection to 127.0.0.1 was not forwarded")
}
if !hairpinOK {
Fail("Hairpin connection failed")
}
close(done) // Verify that a connection to the forwarded port works
}, TIMEOUT*9) dnatOK := testEchoServer(hostIP, "tcp", hostPort, "")
It("forwards a UDP port on ipv4 and keep working after creating a second container with the same HostPort", func(done Done) { // Verify that a connection to localhost works
var err error snatOK := testEchoServer("127.0.0.1", "tcp", hostPort, "")
hostPort := rand.Intn(10000) + 1025
runtimeConfig := libcni.RuntimeConf{ // verify that hairpin works
ContainerID: fmt.Sprintf("unit-test-%d", hostPort), hairpinOK := testEchoServer(hostIP, "tcp", hostPort, targetNS.Path())
NetNS: targetNS.Path(),
IfName: "eth0", // Cleanup
CapabilityArgs: map[string]interface{}{ session.Terminate()
"portMappings": []map[string]interface{}{ err = deleteNetwork()
{ Expect(err).NotTo(HaveOccurred())
"hostPort": hostPort,
"containerPort": containerPort, // Verify iptables rules are gone
"protocol": "udp", _, err = ipt.List("nat", dnatChainName)
Expect(err).To(MatchError(ContainSubstring("iptables: No chain/target/match by that name.")))
// Check that everything succeeded *after* we clean up the network
if !contOK {
Fail("connection direct to " + contIP.String() + " failed")
}
if !dnatOK {
Fail("Connection to " + hostIP + " was not forwarded")
}
if !snatOK {
Fail("connection to 127.0.0.1 was not forwarded")
}
if !hairpinOK {
Fail("Hairpin connection failed")
}
close(done)
}, TIMEOUT*9)
It(fmt.Sprintf("[%s] forwards a UDP port on ipv4 and keep working after creating a second container with the same HostPort", ver), func(done Done) {
var err error
hostPort := rand.Intn(10000) + 1025
runtimeConfig := libcni.RuntimeConf{
ContainerID: fmt.Sprintf("unit-test-%d", hostPort),
NetNS: targetNS.Path(),
IfName: "eth0",
CapabilityArgs: map[string]interface{}{
"portMappings": []map[string]interface{}{
{
"hostPort": hostPort,
"containerPort": containerPort,
"protocol": "udp",
},
}, },
}, },
},
}
// Make delete idempotent, so we can clean up on failure
netDeleted := false
deleteNetwork := func() error {
if netDeleted {
return nil
} }
netDeleted = true configList := makeConfig(ver)
return cniConf.DelNetworkList(context.TODO(), configList, &runtimeConfig)
}
// Create the network // Make delete idempotent, so we can clean up on failure
resI, err := cniConf.AddNetworkList(context.TODO(), configList, &runtimeConfig) netDeleted := false
Expect(err).NotTo(HaveOccurred()) deleteNetwork := func() error {
defer deleteNetwork() if netDeleted {
return nil
// Undo Docker's forwarding policy }
cmd := exec.Command("iptables", "-t", "filter", netDeleted = true
"-P", "FORWARD", "ACCEPT") return cniConf.DelNetworkList(context.TODO(), configList, &runtimeConfig)
cmd.Stderr = GinkgoWriter
err = cmd.Run()
Expect(err).NotTo(HaveOccurred())
result, err := current.GetResult(resI)
Expect(err).NotTo(HaveOccurred())
var contIP net.IP
for _, ip := range result.IPs {
intfIndex := *ip.Interface
if result.Interfaces[intfIndex].Sandbox == "" {
continue
} }
contIP = ip.Address.IP
}
if contIP == nil {
Fail("could not determine container IP")
}
hostIP := getLocalIP() // Create the network
fmt.Fprintf(GinkgoWriter, "First container hostIP: %s:%d, contIP: %s:%d\n", resI, err := cniConf.AddNetworkList(context.TODO(), configList, &runtimeConfig)
hostIP, hostPort, contIP, containerPort) Expect(err).NotTo(HaveOccurred())
defer deleteNetwork()
// dump iptables-save output for debugging // Undo Docker's forwarding policy
cmd = exec.Command("iptables-save") cmd := exec.Command("iptables", "-t", "filter",
cmd.Stderr = GinkgoWriter "-P", "FORWARD", "ACCEPT")
cmd.Stdout = GinkgoWriter cmd.Stderr = GinkgoWriter
Expect(cmd.Run()).To(Succeed()) err = cmd.Run()
Expect(err).NotTo(HaveOccurred())
// dump ip routes output for debugging result, err := types100.GetResult(resI)
cmd = exec.Command("ip", "route") Expect(err).NotTo(HaveOccurred())
cmd.Stderr = GinkgoWriter var contIP net.IP
cmd.Stdout = GinkgoWriter
Expect(cmd.Run()).To(Succeed())
// dump ip addresses output for debugging for _, ip := range result.IPs {
cmd = exec.Command("ip", "addr") intfIndex := *ip.Interface
cmd.Stderr = GinkgoWriter if result.Interfaces[intfIndex].Sandbox == "" {
cmd.Stdout = GinkgoWriter continue
Expect(cmd.Run()).To(Succeed()) }
contIP = ip.Address.IP
}
if contIP == nil {
Fail("could not determine container IP")
}
// Sanity check: verify that the container is reachable directly hostIP := getLocalIP()
fmt.Fprintln(GinkgoWriter, "Connect to container:", contIP.String(), containerPort) fmt.Fprintf(GinkgoWriter, "First container hostIP: %s:%d, contIP: %s:%d\n",
contOK := testEchoServer(contIP.String(), "udp", containerPort, "") hostIP, hostPort, contIP, containerPort)
// Verify that a connection to the forwarded port works // dump iptables-save output for debugging
fmt.Fprintln(GinkgoWriter, "Connect to host:", hostIP, hostPort) cmd = exec.Command("iptables-save")
dnatOK := testEchoServer(hostIP, "udp", hostPort, "") cmd.Stderr = GinkgoWriter
cmd.Stdout = GinkgoWriter
Expect(cmd.Run()).To(Succeed())
// Cleanup // dump ip routes output for debugging
session.Terminate() cmd = exec.Command("ip", "route")
err = deleteNetwork() cmd.Stderr = GinkgoWriter
Expect(err).NotTo(HaveOccurred()) cmd.Stdout = GinkgoWriter
Expect(cmd.Run()).To(Succeed())
// Check that everything succeeded *after* we clean up the network // dump ip addresses output for debugging
if !contOK { cmd = exec.Command("ip", "addr")
Fail("connection direct to " + contIP.String() + " failed") cmd.Stderr = GinkgoWriter
} cmd.Stdout = GinkgoWriter
if !dnatOK { Expect(cmd.Run()).To(Succeed())
Fail("Connection to " + hostIP + " was not forwarded")
}
// Create a second container
targetNS2, err := testutils.NewNS()
Expect(err).NotTo(HaveOccurred())
fmt.Fprintln(GinkgoWriter, "namespace:", targetNS2.Path())
// Start an echo server and get the port // Sanity check: verify that the container is reachable directly
containerPort, session2, err := StartEchoServerInNamespace(targetNS2) fmt.Fprintln(GinkgoWriter, "Connect to container:", contIP.String(), containerPort)
Expect(err).NotTo(HaveOccurred()) contOK := testEchoServer(contIP.String(), "udp", containerPort, "")
runtimeConfig2 := libcni.RuntimeConf{ // Verify that a connection to the forwarded port works
ContainerID: fmt.Sprintf("unit-test2-%d", hostPort), fmt.Fprintln(GinkgoWriter, "Connect to host:", hostIP, hostPort)
NetNS: targetNS2.Path(), dnatOK := testEchoServer(hostIP, "udp", hostPort, "")
IfName: "eth0",
CapabilityArgs: map[string]interface{}{ // Cleanup
"portMappings": []map[string]interface{}{ session.Terminate()
{ err = deleteNetwork()
"hostPort": hostPort, Expect(err).NotTo(HaveOccurred())
"containerPort": containerPort,
"protocol": "udp", // Check that everything succeeded *after* we clean up the network
if !contOK {
Fail("connection direct to " + contIP.String() + " failed")
}
if !dnatOK {
Fail("Connection to " + hostIP + " was not forwarded")
}
// Create a second container
targetNS2, err := testutils.NewNS()
Expect(err).NotTo(HaveOccurred())
fmt.Fprintln(GinkgoWriter, "namespace:", targetNS2.Path())
// Start an echo server and get the port
containerPort, session2, err := StartEchoServerInNamespace(targetNS2)
Expect(err).NotTo(HaveOccurred())
runtimeConfig2 := libcni.RuntimeConf{
ContainerID: fmt.Sprintf("unit-test2-%d", hostPort),
NetNS: targetNS2.Path(),
IfName: "eth0",
CapabilityArgs: map[string]interface{}{
"portMappings": []map[string]interface{}{
{
"hostPort": hostPort,
"containerPort": containerPort,
"protocol": "udp",
},
}, },
}, },
},
}
// Make delete idempotent, so we can clean up on failure
net2Deleted := false
deleteNetwork2 := func() error {
if net2Deleted {
return nil
} }
net2Deleted = true
return cniConf.DelNetworkList(context.TODO(), configList, &runtimeConfig2)
}
// Create the network // Make delete idempotent, so we can clean up on failure
resI2, err := cniConf.AddNetworkList(context.TODO(), configList, &runtimeConfig2) net2Deleted := false
Expect(err).NotTo(HaveOccurred()) deleteNetwork2 := func() error {
defer deleteNetwork2() if net2Deleted {
return nil
result2, err := current.GetResult(resI2) }
Expect(err).NotTo(HaveOccurred()) net2Deleted = true
var contIP2 net.IP return cniConf.DelNetworkList(context.TODO(), configList, &runtimeConfig2)
for _, ip := range result2.IPs {
intfIndex := *ip.Interface
if result2.Interfaces[intfIndex].Sandbox == "" {
continue
} }
contIP2 = ip.Address.IP
}
if contIP2 == nil {
Fail("could not determine container IP")
}
fmt.Fprintf(GinkgoWriter, "Second container: hostIP: %s:%d, contIP: %s:%d\n", // Create the network
hostIP, hostPort, contIP2, containerPort) resI2, err := cniConf.AddNetworkList(context.TODO(), configList, &runtimeConfig2)
Expect(err).NotTo(HaveOccurred())
defer deleteNetwork2()
// dump iptables-save output for debugging result2, err := types100.GetResult(resI2)
cmd = exec.Command("iptables-save") Expect(err).NotTo(HaveOccurred())
cmd.Stderr = GinkgoWriter var contIP2 net.IP
cmd.Stdout = GinkgoWriter
Expect(cmd.Run()).To(Succeed())
// dump ip routes output for debugging for _, ip := range result2.IPs {
cmd = exec.Command("ip", "route") intfIndex := *ip.Interface
cmd.Stderr = GinkgoWriter if result2.Interfaces[intfIndex].Sandbox == "" {
cmd.Stdout = GinkgoWriter continue
Expect(cmd.Run()).To(Succeed()) }
contIP2 = ip.Address.IP
}
if contIP2 == nil {
Fail("could not determine container IP")
}
// dump ip addresses output for debugging fmt.Fprintf(GinkgoWriter, "Second container: hostIP: %s:%d, contIP: %s:%d\n",
cmd = exec.Command("ip", "addr") hostIP, hostPort, contIP2, containerPort)
cmd.Stderr = GinkgoWriter
cmd.Stdout = GinkgoWriter
Expect(cmd.Run()).To(Succeed())
// Sanity check: verify that the container is reachable directly // dump iptables-save output for debugging
fmt.Fprintln(GinkgoWriter, "Connect to container:", contIP2.String(), containerPort) cmd = exec.Command("iptables-save")
cont2OK := testEchoServer(contIP2.String(), "udp", containerPort, "") cmd.Stderr = GinkgoWriter
cmd.Stdout = GinkgoWriter
Expect(cmd.Run()).To(Succeed())
// Verify that a connection to the forwarded port works // dump ip routes output for debugging
fmt.Fprintln(GinkgoWriter, "Connect to host:", hostIP, hostPort) cmd = exec.Command("ip", "route")
dnat2OK := testEchoServer(hostIP, "udp", hostPort, "") cmd.Stderr = GinkgoWriter
cmd.Stdout = GinkgoWriter
Expect(cmd.Run()).To(Succeed())
// Cleanup // dump ip addresses output for debugging
session2.Terminate() cmd = exec.Command("ip", "addr")
err = deleteNetwork2() cmd.Stderr = GinkgoWriter
Expect(err).NotTo(HaveOccurred()) cmd.Stdout = GinkgoWriter
Expect(cmd.Run()).To(Succeed())
// Check that everything succeeded *after* we clean up the network // Sanity check: verify that the container is reachable directly
if !cont2OK { fmt.Fprintln(GinkgoWriter, "Connect to container:", contIP2.String(), containerPort)
Fail("connection direct to " + contIP2.String() + " failed") cont2OK := testEchoServer(contIP2.String(), "udp", containerPort, "")
}
if !dnat2OK {
Fail("Connection to " + hostIP + " was not forwarded")
}
close(done) // Verify that a connection to the forwarded port works
}, TIMEOUT*9) fmt.Fprintln(GinkgoWriter, "Connect to host:", hostIP, hostPort)
}) dnat2OK := testEchoServer(hostIP, "udp", hostPort, "")
// Cleanup
session2.Terminate()
err = deleteNetwork2()
Expect(err).NotTo(HaveOccurred())
// Check that everything succeeded *after* we clean up the network
if !cont2OK {
Fail("connection direct to " + contIP2.String() + " failed")
}
if !dnat2OK {
Fail("Connection to " + hostIP + " was not forwarded")
}
close(done)
}, TIMEOUT*9)
})
}
}) })
// testEchoServer returns true if we found an echo server on the port // testEchoServer returns true if we found an echo server on the port

View File

@ -27,336 +27,342 @@ var _ = Describe("portmapping configuration", func() {
netName := "testNetName" netName := "testNetName"
containerID := "icee6giejonei6sohng6ahngee7laquohquee9shiGo7fohferakah3Feiyoolu2pei7ciPhoh7shaoX6vai3vuf0ahfaeng8yohb9ceu0daez5hashee8ooYai5wa3y" containerID := "icee6giejonei6sohng6ahngee7laquohquee9shiGo7fohferakah3Feiyoolu2pei7ciPhoh7shaoX6vai3vuf0ahfaeng8yohb9ceu0daez5hashee8ooYai5wa3y"
Context("config parsing", func() { for _, ver := range []string{"0.3.0", "0.3.1", "0.4.0", "1.0.0"} {
It("Correctly parses an ADD config", func() { // Redefine ver inside for scope so real value is picked up by each dynamically defined It()
configBytes := []byte(`{ // See Gingkgo's "Patterns for dynamically generating tests" documentation.
"name": "test", ver := ver
"type": "portmap",
"cniVersion": "0.3.1",
"runtimeConfig": {
"portMappings": [
{ "hostPort": 8080, "containerPort": 80, "protocol": "tcp"},
{ "hostPort": 8081, "containerPort": 81, "protocol": "udp"}
]
},
"snat": false,
"conditionsV4": ["a", "b"],
"conditionsV6": ["c", "d"],
"prevResult": {
"interfaces": [
{"name": "host"},
{"name": "container", "sandbox":"netns"}
],
"ips": [
{
"version": "4",
"address": "10.0.0.1/24",
"gateway": "10.0.0.1",
"interface": 0
},
{
"version": "6",
"address": "2001:db8:1::2/64",
"gateway": "2001:db8:1::1",
"interface": 1
},
{
"version": "4",
"address": "10.0.0.2/24",
"gateway": "10.0.0.1",
"interface": 1
}
]
}
}`)
c, _, err := parseConfig(configBytes, "container")
Expect(err).NotTo(HaveOccurred())
Expect(c.CNIVersion).To(Equal("0.3.1"))
Expect(c.ConditionsV4).To(Equal(&[]string{"a", "b"}))
Expect(c.ConditionsV6).To(Equal(&[]string{"c", "d"}))
fvar := false
Expect(c.SNAT).To(Equal(&fvar))
Expect(c.Name).To(Equal("test"))
n, err := types.ParseCIDR("10.0.0.2/24") Context("config parsing", func() {
Expect(c.ContIPv4).To(Equal(*n)) It(fmt.Sprintf("[%s] correctly parses an ADD config", ver), func() {
n, err = types.ParseCIDR("2001:db8:1::2/64") configBytes := []byte(fmt.Sprintf(`{
Expect(c.ContIPv6).To(Equal(*n)) "name": "test",
}) "type": "portmap",
"cniVersion": "%s",
It("Correctly parses a DEL config", func() { "runtimeConfig": {
// When called with DEL, neither runtimeConfig nor prevResult may be specified "portMappings": [
configBytes := []byte(`{ { "hostPort": 8080, "containerPort": 80, "protocol": "tcp"},
"name": "test", { "hostPort": 8081, "containerPort": 81, "protocol": "udp"}
"type": "portmap", ]
"cniVersion": "0.3.1", },
"snat": false, "snat": false,
"conditionsV4": ["a", "b"], "conditionsV4": ["a", "b"],
"conditionsV6": ["c", "d"] "conditionsV6": ["c", "d"],
}`) "prevResult": {
c, _, err := parseConfig(configBytes, "container") "interfaces": [
Expect(err).NotTo(HaveOccurred()) {"name": "host"},
Expect(c.CNIVersion).To(Equal("0.3.1")) {"name": "container", "sandbox":"netns"}
Expect(c.ConditionsV4).To(Equal(&[]string{"a", "b"})) ],
Expect(c.ConditionsV6).To(Equal(&[]string{"c", "d"})) "ips": [
fvar := false {
Expect(c.SNAT).To(Equal(&fvar)) "version": "4",
Expect(c.Name).To(Equal("test")) "address": "10.0.0.1/24",
}) "gateway": "10.0.0.1",
"interface": 0
It("fails with invalid mappings", func() { },
configBytes := []byte(`{ {
"name": "test", "version": "6",
"type": "portmap", "address": "2001:db8:1::2/64",
"cniVersion": "0.3.1", "gateway": "2001:db8:1::1",
"snat": false, "interface": 1
"conditionsV4": ["a", "b"], },
"conditionsV6": ["c", "d"], {
"runtimeConfig": { "version": "4",
"portMappings": [ "address": "10.0.0.2/24",
{ "hostPort": 0, "containerPort": 80, "protocol": "tcp"} "gateway": "10.0.0.1",
] "interface": 1
} }
}`) ]
_, _, err := parseConfig(configBytes, "container") }
Expect(err).To(MatchError("Invalid host port number: 0")) }`, ver))
}) c, _, err := parseConfig(configBytes, "container")
It("Does not fail on missing prevResult interface index", func() {
configBytes := []byte(`{
"name": "test",
"type": "portmap",
"cniVersion": "0.3.1",
"runtimeConfig": {
"portMappings": [
{ "hostPort": 8080, "containerPort": 80, "protocol": "tcp"}
]
},
"conditionsV4": ["a", "b"],
"prevResult": {
"interfaces": [
{"name": "host"}
],
"ips": [
{
"version": "4",
"address": "10.0.0.1/24",
"gateway": "10.0.0.1"
}
]
}
}`)
_, _, err := parseConfig(configBytes, "container")
Expect(err).NotTo(HaveOccurred())
})
})
Describe("Generating chains", func() {
Context("for DNAT", func() {
It("generates a correct standard container chain", func() {
ch := genDnatChain(netName, containerID)
Expect(ch).To(Equal(chain{
table: "nat",
name: "CNI-DN-bfd599665540dd91d5d28",
entryChains: []string{TopLevelDNATChainName},
}))
configBytes := []byte(`{
"name": "test",
"type": "portmap",
"cniVersion": "0.3.1",
"runtimeConfig": {
"portMappings": [
{ "hostPort": 8080, "containerPort": 80, "protocol": "tcp"},
{ "hostPort": 8081, "containerPort": 80, "protocol": "tcp"},
{ "hostPort": 8080, "containerPort": 81, "protocol": "udp"},
{ "hostPort": 8082, "containerPort": 82, "protocol": "udp"},
{ "hostPort": 8083, "containerPort": 83, "protocol": "tcp", "hostIP": "192.168.0.2"},
{ "hostPort": 8084, "containerPort": 84, "protocol": "tcp", "hostIP": "0.0.0.0"},
{ "hostPort": 8085, "containerPort": 85, "protocol": "tcp", "hostIP": "2001:db8:a::1"},
{ "hostPort": 8086, "containerPort": 86, "protocol": "tcp", "hostIP": "::"}
]
},
"snat": true,
"conditionsV4": ["a", "b"],
"conditionsV6": ["c", "d"]
}`)
conf, _, err := parseConfig(configBytes, "foo")
Expect(err).NotTo(HaveOccurred()) Expect(err).NotTo(HaveOccurred())
conf.ContainerID = containerID Expect(c.CNIVersion).To(Equal(ver))
Expect(c.ConditionsV4).To(Equal(&[]string{"a", "b"}))
ch = genDnatChain(conf.Name, containerID) Expect(c.ConditionsV6).To(Equal(&[]string{"c", "d"}))
Expect(ch).To(Equal(chain{
table: "nat",
name: "CNI-DN-67e92b96e692a494b6b85",
entryChains: []string{"CNI-HOSTPORT-DNAT"},
}))
n, err := types.ParseCIDR("10.0.0.2/24")
fillDnatRules(&ch, conf, *n)
Expect(ch.entryRules).To(Equal([][]string{
{"-m", "comment", "--comment",
fmt.Sprintf("dnat name: \"test\" id: \"%s\"", containerID),
"-m", "multiport",
"-p", "tcp",
"--destination-ports", "8080,8081,8083,8084,8085,8086",
"a", "b"},
{"-m", "comment", "--comment",
fmt.Sprintf("dnat name: \"test\" id: \"%s\"", containerID),
"-m", "multiport",
"-p", "udp",
"--destination-ports", "8080,8082",
"a", "b"},
}))
Expect(ch.rules).To(Equal([][]string{
// tcp rules and not hostIP
{"-p", "tcp", "--dport", "8080", "-s", "10.0.0.2/24", "-j", "CNI-HOSTPORT-SETMARK"},
{"-p", "tcp", "--dport", "8080", "-s", "127.0.0.1", "-j", "CNI-HOSTPORT-SETMARK"},
{"-p", "tcp", "--dport", "8080", "-j", "DNAT", "--to-destination", "10.0.0.2:80"},
{"-p", "tcp", "--dport", "8081", "-s", "10.0.0.2/24", "-j", "CNI-HOSTPORT-SETMARK"},
{"-p", "tcp", "--dport", "8081", "-s", "127.0.0.1", "-j", "CNI-HOSTPORT-SETMARK"},
{"-p", "tcp", "--dport", "8081", "-j", "DNAT", "--to-destination", "10.0.0.2:80"},
// udp rules and not hostIP
{"-p", "udp", "--dport", "8080", "-s", "10.0.0.2/24", "-j", "CNI-HOSTPORT-SETMARK"},
{"-p", "udp", "--dport", "8080", "-s", "127.0.0.1", "-j", "CNI-HOSTPORT-SETMARK"},
{"-p", "udp", "--dport", "8080", "-j", "DNAT", "--to-destination", "10.0.0.2:81"},
{"-p", "udp", "--dport", "8082", "-s", "10.0.0.2/24", "-j", "CNI-HOSTPORT-SETMARK"},
{"-p", "udp", "--dport", "8082", "-s", "127.0.0.1", "-j", "CNI-HOSTPORT-SETMARK"},
{"-p", "udp", "--dport", "8082", "-j", "DNAT", "--to-destination", "10.0.0.2:82"},
// tcp rules and hostIP
{"-p", "tcp", "--dport", "8083", "-d", "192.168.0.2", "-s", "10.0.0.2/24", "-j", "CNI-HOSTPORT-SETMARK"},
{"-p", "tcp", "--dport", "8083", "-d", "192.168.0.2", "-s", "127.0.0.1", "-j", "CNI-HOSTPORT-SETMARK"},
{"-p", "tcp", "--dport", "8083", "-d", "192.168.0.2", "-j", "DNAT", "--to-destination", "10.0.0.2:83"},
// tcp rules and hostIP = "0.0.0.0"
{"-p", "tcp", "--dport", "8084", "-s", "10.0.0.2/24", "-j", "CNI-HOSTPORT-SETMARK"},
{"-p", "tcp", "--dport", "8084", "-s", "127.0.0.1", "-j", "CNI-HOSTPORT-SETMARK"},
{"-p", "tcp", "--dport", "8084", "-j", "DNAT", "--to-destination", "10.0.0.2:84"},
}))
ch.rules = nil
ch.entryRules = nil
n, err = types.ParseCIDR("2001:db8::2/64")
fillDnatRules(&ch, conf, *n)
Expect(ch.rules).To(Equal([][]string{
// tcp rules and not hostIP
{"-p", "tcp", "--dport", "8080", "-s", "2001:db8::2/64", "-j", "CNI-HOSTPORT-SETMARK"},
{"-p", "tcp", "--dport", "8080", "-j", "DNAT", "--to-destination", "[2001:db8::2]:80"},
{"-p", "tcp", "--dport", "8081", "-s", "2001:db8::2/64", "-j", "CNI-HOSTPORT-SETMARK"},
{"-p", "tcp", "--dport", "8081", "-j", "DNAT", "--to-destination", "[2001:db8::2]:80"},
// udp rules and not hostIP
{"-p", "udp", "--dport", "8080", "-s", "2001:db8::2/64", "-j", "CNI-HOSTPORT-SETMARK"},
{"-p", "udp", "--dport", "8080", "-j", "DNAT", "--to-destination", "[2001:db8::2]:81"},
{"-p", "udp", "--dport", "8082", "-s", "2001:db8::2/64", "-j", "CNI-HOSTPORT-SETMARK"},
{"-p", "udp", "--dport", "8082", "-j", "DNAT", "--to-destination", "[2001:db8::2]:82"},
// tcp rules and hostIP
{"-p", "tcp", "--dport", "8085", "-d", "2001:db8:a::1", "-s", "2001:db8::2/64", "-j", "CNI-HOSTPORT-SETMARK"},
{"-p", "tcp", "--dport", "8085", "-d", "2001:db8:a::1", "-j", "DNAT", "--to-destination", "[2001:db8::2]:85"},
// tcp rules and hostIP = "::"
{"-p", "tcp", "--dport", "8086", "-s", "2001:db8::2/64", "-j", "CNI-HOSTPORT-SETMARK"},
{"-p", "tcp", "--dport", "8086", "-j", "DNAT", "--to-destination", "[2001:db8::2]:86"},
}))
// Disable snat, generate rules
ch.rules = nil
ch.entryRules = nil
fvar := false fvar := false
conf.SNAT = &fvar Expect(c.SNAT).To(Equal(&fvar))
Expect(c.Name).To(Equal("test"))
n, err = types.ParseCIDR("10.0.0.2/24")
fillDnatRules(&ch, conf, *n)
Expect(ch.rules).To(Equal([][]string{
{"-p", "tcp", "--dport", "8080", "-j", "DNAT", "--to-destination", "10.0.0.2:80"},
{"-p", "tcp", "--dport", "8081", "-j", "DNAT", "--to-destination", "10.0.0.2:80"},
{"-p", "udp", "--dport", "8080", "-j", "DNAT", "--to-destination", "10.0.0.2:81"},
{"-p", "udp", "--dport", "8082", "-j", "DNAT", "--to-destination", "10.0.0.2:82"},
{"-p", "tcp", "--dport", "8083", "-d", "192.168.0.2", "-j", "DNAT", "--to-destination", "10.0.0.2:83"},
{"-p", "tcp", "--dport", "8084", "-j", "DNAT", "--to-destination", "10.0.0.2:84"},
}))
})
It("generates a correct chain with external mark", func() {
ch := genDnatChain(netName, containerID)
Expect(ch).To(Equal(chain{
table: "nat",
name: "CNI-DN-bfd599665540dd91d5d28",
entryChains: []string{TopLevelDNATChainName},
}))
configBytes := []byte(`{
"name": "test",
"type": "portmap",
"cniVersion": "0.3.1",
"runtimeConfig": {
"portMappings": [
{ "hostPort": 8080, "containerPort": 80, "protocol": "tcp"}
]
},
"externalSetMarkChain": "PLZ-SET-MARK",
"conditionsV4": ["a", "b"],
"conditionsV6": ["c", "d"]
}`)
conf, _, err := parseConfig(configBytes, "foo")
Expect(err).NotTo(HaveOccurred())
conf.ContainerID = containerID
ch = genDnatChain(conf.Name, containerID)
n, err := types.ParseCIDR("10.0.0.2/24") n, err := types.ParseCIDR("10.0.0.2/24")
fillDnatRules(&ch, conf, *n) Expect(c.ContIPv4).To(Equal(*n))
Expect(ch.rules).To(Equal([][]string{ n, err = types.ParseCIDR("2001:db8:1::2/64")
{"-p", "tcp", "--dport", "8080", "-s", "10.0.0.2/24", "-j", "PLZ-SET-MARK"}, Expect(c.ContIPv6).To(Equal(*n))
{"-p", "tcp", "--dport", "8080", "-s", "127.0.0.1", "-j", "PLZ-SET-MARK"},
{"-p", "tcp", "--dport", "8080", "-j", "DNAT", "--to-destination", "10.0.0.2:80"},
}))
}) })
It("generates a correct top-level chain", func() { It(fmt.Sprintf("[%s] correctly parses a DEL config", ver), func() {
ch := genToplevelDnatChain() // When called with DEL, neither runtimeConfig nor prevResult may be specified
configBytes := []byte(fmt.Sprintf(`{
Expect(ch).To(Equal(chain{ "name": "test",
table: "nat", "type": "portmap",
name: "CNI-HOSTPORT-DNAT", "cniVersion": "%s",
entryChains: []string{"PREROUTING", "OUTPUT"}, "snat": false,
entryRules: [][]string{{"-m", "addrtype", "--dst-type", "LOCAL"}}, "conditionsV4": ["a", "b"],
})) "conditionsV6": ["c", "d"]
}`, ver))
c, _, err := parseConfig(configBytes, "container")
Expect(err).NotTo(HaveOccurred())
Expect(c.CNIVersion).To(Equal(ver))
Expect(c.ConditionsV4).To(Equal(&[]string{"a", "b"}))
Expect(c.ConditionsV6).To(Equal(&[]string{"c", "d"}))
fvar := false
Expect(c.SNAT).To(Equal(&fvar))
Expect(c.Name).To(Equal("test"))
}) })
It("generates the correct mark chains", func() { It(fmt.Sprintf("[%s] fails with invalid mappings", ver), func() {
masqBit := 5 configBytes := []byte(fmt.Sprintf(`{
ch := genSetMarkChain(masqBit) "name": "test",
Expect(ch).To(Equal(chain{ "type": "portmap",
table: "nat", "cniVersion": "%s",
name: "CNI-HOSTPORT-SETMARK", "snat": false,
rules: [][]string{{ "conditionsV4": ["a", "b"],
"-m", "comment", "conditionsV6": ["c", "d"],
"--comment", "CNI portfwd masquerade mark", "runtimeConfig": {
"-j", "MARK", "portMappings": [
"--set-xmark", "0x20/0x20", { "hostPort": 0, "containerPort": 80, "protocol": "tcp"}
}}, ]
})) }
}`, ver))
_, _, err := parseConfig(configBytes, "container")
Expect(err).To(MatchError("Invalid host port number: 0"))
})
ch = genMarkMasqChain(masqBit) It(fmt.Sprintf("[%s] does not fail on missing prevResult interface index", ver), func() {
Expect(ch).To(Equal(chain{ configBytes := []byte(fmt.Sprintf(`{
table: "nat", "name": "test",
name: "CNI-HOSTPORT-MASQ", "type": "portmap",
entryChains: []string{"POSTROUTING"}, "cniVersion": "%s",
entryRules: [][]string{{ "runtimeConfig": {
"-m", "comment", "portMappings": [
"--comment", "CNI portfwd requiring masquerade", { "hostPort": 8080, "containerPort": 80, "protocol": "tcp"}
}}, ]
rules: [][]string{{ },
"-m", "mark", "conditionsV4": ["a", "b"],
"--mark", "0x20/0x20", "prevResult": {
"-j", "MASQUERADE", "interfaces": [
}}, {"name": "host"}
prependEntry: true, ],
})) "ips": [
{
"version": "4",
"address": "10.0.0.1/24",
"gateway": "10.0.0.1"
}
]
}
}`, ver))
_, _, err := parseConfig(configBytes, "container")
Expect(err).NotTo(HaveOccurred())
}) })
}) })
})
Describe("Generating chains", func() {
Context("for DNAT", func() {
It(fmt.Sprintf("[%s] generates a correct standard container chain", ver), func() {
ch := genDnatChain(netName, containerID)
Expect(ch).To(Equal(chain{
table: "nat",
name: "CNI-DN-bfd599665540dd91d5d28",
entryChains: []string{TopLevelDNATChainName},
}))
configBytes := []byte(fmt.Sprintf(`{
"name": "test",
"type": "portmap",
"cniVersion": "%s",
"runtimeConfig": {
"portMappings": [
{ "hostPort": 8080, "containerPort": 80, "protocol": "tcp"},
{ "hostPort": 8081, "containerPort": 80, "protocol": "tcp"},
{ "hostPort": 8080, "containerPort": 81, "protocol": "udp"},
{ "hostPort": 8082, "containerPort": 82, "protocol": "udp"},
{ "hostPort": 8083, "containerPort": 83, "protocol": "tcp", "hostIP": "192.168.0.2"},
{ "hostPort": 8084, "containerPort": 84, "protocol": "tcp", "hostIP": "0.0.0.0"},
{ "hostPort": 8085, "containerPort": 85, "protocol": "tcp", "hostIP": "2001:db8:a::1"},
{ "hostPort": 8086, "containerPort": 86, "protocol": "tcp", "hostIP": "::"}
]
},
"snat": true,
"conditionsV4": ["a", "b"],
"conditionsV6": ["c", "d"]
}`, ver))
conf, _, err := parseConfig(configBytes, "foo")
Expect(err).NotTo(HaveOccurred())
conf.ContainerID = containerID
ch = genDnatChain(conf.Name, containerID)
Expect(ch).To(Equal(chain{
table: "nat",
name: "CNI-DN-67e92b96e692a494b6b85",
entryChains: []string{"CNI-HOSTPORT-DNAT"},
}))
n, err := types.ParseCIDR("10.0.0.2/24")
fillDnatRules(&ch, conf, *n)
Expect(ch.entryRules).To(Equal([][]string{
{"-m", "comment", "--comment",
fmt.Sprintf("dnat name: \"test\" id: \"%s\"", containerID),
"-m", "multiport",
"-p", "tcp",
"--destination-ports", "8080,8081,8083,8084,8085,8086",
"a", "b"},
{"-m", "comment", "--comment",
fmt.Sprintf("dnat name: \"test\" id: \"%s\"", containerID),
"-m", "multiport",
"-p", "udp",
"--destination-ports", "8080,8082",
"a", "b"},
}))
Expect(ch.rules).To(Equal([][]string{
// tcp rules and not hostIP
{"-p", "tcp", "--dport", "8080", "-s", "10.0.0.2/24", "-j", "CNI-HOSTPORT-SETMARK"},
{"-p", "tcp", "--dport", "8080", "-s", "127.0.0.1", "-j", "CNI-HOSTPORT-SETMARK"},
{"-p", "tcp", "--dport", "8080", "-j", "DNAT", "--to-destination", "10.0.0.2:80"},
{"-p", "tcp", "--dport", "8081", "-s", "10.0.0.2/24", "-j", "CNI-HOSTPORT-SETMARK"},
{"-p", "tcp", "--dport", "8081", "-s", "127.0.0.1", "-j", "CNI-HOSTPORT-SETMARK"},
{"-p", "tcp", "--dport", "8081", "-j", "DNAT", "--to-destination", "10.0.0.2:80"},
// udp rules and not hostIP
{"-p", "udp", "--dport", "8080", "-s", "10.0.0.2/24", "-j", "CNI-HOSTPORT-SETMARK"},
{"-p", "udp", "--dport", "8080", "-s", "127.0.0.1", "-j", "CNI-HOSTPORT-SETMARK"},
{"-p", "udp", "--dport", "8080", "-j", "DNAT", "--to-destination", "10.0.0.2:81"},
{"-p", "udp", "--dport", "8082", "-s", "10.0.0.2/24", "-j", "CNI-HOSTPORT-SETMARK"},
{"-p", "udp", "--dport", "8082", "-s", "127.0.0.1", "-j", "CNI-HOSTPORT-SETMARK"},
{"-p", "udp", "--dport", "8082", "-j", "DNAT", "--to-destination", "10.0.0.2:82"},
// tcp rules and hostIP
{"-p", "tcp", "--dport", "8083", "-d", "192.168.0.2", "-s", "10.0.0.2/24", "-j", "CNI-HOSTPORT-SETMARK"},
{"-p", "tcp", "--dport", "8083", "-d", "192.168.0.2", "-s", "127.0.0.1", "-j", "CNI-HOSTPORT-SETMARK"},
{"-p", "tcp", "--dport", "8083", "-d", "192.168.0.2", "-j", "DNAT", "--to-destination", "10.0.0.2:83"},
// tcp rules and hostIP = "0.0.0.0"
{"-p", "tcp", "--dport", "8084", "-s", "10.0.0.2/24", "-j", "CNI-HOSTPORT-SETMARK"},
{"-p", "tcp", "--dport", "8084", "-s", "127.0.0.1", "-j", "CNI-HOSTPORT-SETMARK"},
{"-p", "tcp", "--dport", "8084", "-j", "DNAT", "--to-destination", "10.0.0.2:84"},
}))
ch.rules = nil
ch.entryRules = nil
n, err = types.ParseCIDR("2001:db8::2/64")
fillDnatRules(&ch, conf, *n)
Expect(ch.rules).To(Equal([][]string{
// tcp rules and not hostIP
{"-p", "tcp", "--dport", "8080", "-s", "2001:db8::2/64", "-j", "CNI-HOSTPORT-SETMARK"},
{"-p", "tcp", "--dport", "8080", "-j", "DNAT", "--to-destination", "[2001:db8::2]:80"},
{"-p", "tcp", "--dport", "8081", "-s", "2001:db8::2/64", "-j", "CNI-HOSTPORT-SETMARK"},
{"-p", "tcp", "--dport", "8081", "-j", "DNAT", "--to-destination", "[2001:db8::2]:80"},
// udp rules and not hostIP
{"-p", "udp", "--dport", "8080", "-s", "2001:db8::2/64", "-j", "CNI-HOSTPORT-SETMARK"},
{"-p", "udp", "--dport", "8080", "-j", "DNAT", "--to-destination", "[2001:db8::2]:81"},
{"-p", "udp", "--dport", "8082", "-s", "2001:db8::2/64", "-j", "CNI-HOSTPORT-SETMARK"},
{"-p", "udp", "--dport", "8082", "-j", "DNAT", "--to-destination", "[2001:db8::2]:82"},
// tcp rules and hostIP
{"-p", "tcp", "--dport", "8085", "-d", "2001:db8:a::1", "-s", "2001:db8::2/64", "-j", "CNI-HOSTPORT-SETMARK"},
{"-p", "tcp", "--dport", "8085", "-d", "2001:db8:a::1", "-j", "DNAT", "--to-destination", "[2001:db8::2]:85"},
// tcp rules and hostIP = "::"
{"-p", "tcp", "--dport", "8086", "-s", "2001:db8::2/64", "-j", "CNI-HOSTPORT-SETMARK"},
{"-p", "tcp", "--dport", "8086", "-j", "DNAT", "--to-destination", "[2001:db8::2]:86"},
}))
// Disable snat, generate rules
ch.rules = nil
ch.entryRules = nil
fvar := false
conf.SNAT = &fvar
n, err = types.ParseCIDR("10.0.0.2/24")
fillDnatRules(&ch, conf, *n)
Expect(ch.rules).To(Equal([][]string{
{"-p", "tcp", "--dport", "8080", "-j", "DNAT", "--to-destination", "10.0.0.2:80"},
{"-p", "tcp", "--dport", "8081", "-j", "DNAT", "--to-destination", "10.0.0.2:80"},
{"-p", "udp", "--dport", "8080", "-j", "DNAT", "--to-destination", "10.0.0.2:81"},
{"-p", "udp", "--dport", "8082", "-j", "DNAT", "--to-destination", "10.0.0.2:82"},
{"-p", "tcp", "--dport", "8083", "-d", "192.168.0.2", "-j", "DNAT", "--to-destination", "10.0.0.2:83"},
{"-p", "tcp", "--dport", "8084", "-j", "DNAT", "--to-destination", "10.0.0.2:84"},
}))
})
It(fmt.Sprintf("[%s] generates a correct chain with external mark", ver), func() {
ch := genDnatChain(netName, containerID)
Expect(ch).To(Equal(chain{
table: "nat",
name: "CNI-DN-bfd599665540dd91d5d28",
entryChains: []string{TopLevelDNATChainName},
}))
configBytes := []byte(fmt.Sprintf(`{
"name": "test",
"type": "portmap",
"cniVersion": "%s",
"runtimeConfig": {
"portMappings": [
{ "hostPort": 8080, "containerPort": 80, "protocol": "tcp"}
]
},
"externalSetMarkChain": "PLZ-SET-MARK",
"conditionsV4": ["a", "b"],
"conditionsV6": ["c", "d"]
}`, ver))
conf, _, err := parseConfig(configBytes, "foo")
Expect(err).NotTo(HaveOccurred())
conf.ContainerID = containerID
ch = genDnatChain(conf.Name, containerID)
n, err := types.ParseCIDR("10.0.0.2/24")
fillDnatRules(&ch, conf, *n)
Expect(ch.rules).To(Equal([][]string{
{"-p", "tcp", "--dport", "8080", "-s", "10.0.0.2/24", "-j", "PLZ-SET-MARK"},
{"-p", "tcp", "--dport", "8080", "-s", "127.0.0.1", "-j", "PLZ-SET-MARK"},
{"-p", "tcp", "--dport", "8080", "-j", "DNAT", "--to-destination", "10.0.0.2:80"},
}))
})
It(fmt.Sprintf("[%s] generates a correct top-level chain", ver), func() {
ch := genToplevelDnatChain()
Expect(ch).To(Equal(chain{
table: "nat",
name: "CNI-HOSTPORT-DNAT",
entryChains: []string{"PREROUTING", "OUTPUT"},
entryRules: [][]string{{"-m", "addrtype", "--dst-type", "LOCAL"}},
}))
})
It(fmt.Sprintf("[%s] generates the correct mark chains", ver), func() {
masqBit := 5
ch := genSetMarkChain(masqBit)
Expect(ch).To(Equal(chain{
table: "nat",
name: "CNI-HOSTPORT-SETMARK",
rules: [][]string{{
"-m", "comment",
"--comment", "CNI portfwd masquerade mark",
"-j", "MARK",
"--set-xmark", "0x20/0x20",
}},
}))
ch = genMarkMasqChain(masqBit)
Expect(ch).To(Equal(chain{
table: "nat",
name: "CNI-HOSTPORT-MASQ",
entryChains: []string{"POSTROUTING"},
entryRules: [][]string{{
"-m", "comment",
"--comment", "CNI portfwd requiring masquerade",
}},
rules: [][]string{{
"-m", "mark",
"--mark", "0x20/0x20",
"-j", "MASQUERADE",
}},
prependEntry: true,
}))
})
})
})
}
}) })