Add nftables implementation of ipmasq
Signed-off-by: Dan Winship <danwinship@redhat.com>
This commit is contained in:
committed by
Casey Callendrello
parent
729dd23c40
commit
61d078645a
@@ -35,7 +35,6 @@ import (
|
||||
"github.com/containernetworking/plugins/pkg/ipam"
|
||||
"github.com/containernetworking/plugins/pkg/link"
|
||||
"github.com/containernetworking/plugins/pkg/ns"
|
||||
"github.com/containernetworking/plugins/pkg/utils"
|
||||
bv "github.com/containernetworking/plugins/pkg/utils/buildversion"
|
||||
"github.com/containernetworking/plugins/pkg/utils/sysctl"
|
||||
)
|
||||
@@ -52,6 +51,7 @@ type NetConf struct {
|
||||
IsDefaultGW bool `json:"isDefaultGateway"`
|
||||
ForceAddress bool `json:"forceAddress"`
|
||||
IPMasq bool `json:"ipMasq"`
|
||||
IPMasqBackend *string `json:"ipMasqBackend,omitempty"`
|
||||
MTU int `json:"mtu"`
|
||||
HairpinMode bool `json:"hairpinMode"`
|
||||
PromiscMode bool `json:"promiscMode"`
|
||||
@@ -673,10 +673,8 @@ func cmdAdd(args *skel.CmdArgs) error {
|
||||
}
|
||||
|
||||
if n.IPMasq {
|
||||
chain := utils.FormatChainName(n.Name, args.ContainerID)
|
||||
comment := utils.FormatComment(n.Name, args.ContainerID)
|
||||
for _, ipc := range result.IPs {
|
||||
if err = ip.SetupIPMasq(&ipc.Address, chain, comment); err != nil {
|
||||
if err = ip.SetupIPMasqForNetwork(n.IPMasqBackend, &ipc.Address, n.Name, args.IfName, args.ContainerID); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
@@ -814,10 +812,8 @@ func cmdDel(args *skel.CmdArgs) error {
|
||||
}
|
||||
|
||||
if isLayer3 && n.IPMasq {
|
||||
chain := utils.FormatChainName(n.Name, args.ContainerID)
|
||||
comment := utils.FormatComment(n.Name, args.ContainerID)
|
||||
for _, ipn := range ipnets {
|
||||
if err := ip.TeardownIPMasq(ipn, chain, comment); err != nil {
|
||||
if err := ip.TeardownIPMasqForNetwork(ipn, n.Name, args.IfName, args.ContainerID); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net"
|
||||
@@ -27,6 +28,7 @@ import (
|
||||
. "github.com/onsi/gomega"
|
||||
"github.com/vishvananda/netlink"
|
||||
"github.com/vishvananda/netlink/nl"
|
||||
"sigs.k8s.io/knftables"
|
||||
|
||||
"github.com/containernetworking/cni/pkg/skel"
|
||||
"github.com/containernetworking/cni/pkg/types"
|
||||
@@ -77,6 +79,7 @@ type testCase struct {
|
||||
vlanTrunk []*VlanTrunk
|
||||
removeDefaultVlan bool
|
||||
ipMasq bool
|
||||
ipMasqBackend string
|
||||
macspoofchk bool
|
||||
disableContIface bool
|
||||
|
||||
@@ -172,6 +175,9 @@ const (
|
||||
ipMasqConfStr = `,
|
||||
"ipMasq": %t`
|
||||
|
||||
ipMasqBackendConfStr = `,
|
||||
"ipMasqBackend": "%s"`
|
||||
|
||||
// Single subnet configuration (legacy)
|
||||
subnetConfStr = `,
|
||||
"subnet": "%s"`
|
||||
@@ -243,6 +249,9 @@ func (tc testCase) netConfJSON(dataDir string) string {
|
||||
if tc.ipMasq {
|
||||
conf += tc.ipMasqConfig()
|
||||
}
|
||||
if tc.ipMasqBackend != "" {
|
||||
conf += tc.ipMasqBackendConfig()
|
||||
}
|
||||
if tc.args.cni.mac != "" {
|
||||
conf += fmt.Sprintf(argsFormat, tc.args.cni.mac)
|
||||
}
|
||||
@@ -295,6 +304,11 @@ func (tc testCase) ipMasqConfig() string {
|
||||
return conf
|
||||
}
|
||||
|
||||
func (tc testCase) ipMasqBackendConfig() string {
|
||||
conf := fmt.Sprintf(ipMasqBackendConfStr, tc.ipMasqBackend)
|
||||
return conf
|
||||
}
|
||||
|
||||
func (tc testCase) rangesConfig() string {
|
||||
conf := rangesStartStr
|
||||
for i, tcRange := range tc.ranges {
|
||||
@@ -2390,41 +2404,82 @@ var _ = Describe("bridge Operations", func() {
|
||||
})
|
||||
|
||||
if testutils.SpecVersionHasChaining(ver) {
|
||||
It(fmt.Sprintf("[%s] configures a bridge and ipMasq rules", ver), func() {
|
||||
err := originalNS.Do(func(ns.NetNS) error {
|
||||
defer GinkgoRecover()
|
||||
tc := testCase{
|
||||
ranges: []rangeInfo{{
|
||||
subnet: "10.1.2.0/24",
|
||||
}},
|
||||
ipMasq: true,
|
||||
cniVersion: ver,
|
||||
}
|
||||
for _, tc := range []testCase{
|
||||
{
|
||||
ranges: []rangeInfo{{
|
||||
subnet: "10.1.2.0/24",
|
||||
}},
|
||||
ipMasq: true,
|
||||
cniVersion: ver,
|
||||
},
|
||||
{
|
||||
ranges: []rangeInfo{{
|
||||
subnet: "10.1.2.0/24",
|
||||
}},
|
||||
ipMasq: true,
|
||||
ipMasqBackend: "iptables",
|
||||
cniVersion: ver,
|
||||
},
|
||||
{
|
||||
ranges: []rangeInfo{{
|
||||
subnet: "10.1.2.0/24",
|
||||
}},
|
||||
ipMasq: true,
|
||||
ipMasqBackend: "nftables",
|
||||
cniVersion: ver,
|
||||
},
|
||||
} {
|
||||
tc := tc
|
||||
It(fmt.Sprintf("[%s] configures a bridge and ipMasq rules with ipMasqBackend %q", ver, tc.ipMasqBackend), func() {
|
||||
err := originalNS.Do(func(ns.NetNS) error {
|
||||
defer GinkgoRecover()
|
||||
|
||||
args := tc.createCmdArgs(originalNS, dataDir)
|
||||
r, _, err := testutils.CmdAddWithArgs(args, func() error {
|
||||
return cmdAdd(args)
|
||||
args := tc.createCmdArgs(originalNS, dataDir)
|
||||
r, _, err := testutils.CmdAddWithArgs(args, func() error {
|
||||
return cmdAdd(args)
|
||||
})
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
result, err := types100.GetResult(r)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(result.IPs).Should(HaveLen(1))
|
||||
|
||||
ip := result.IPs[0].Address.IP.String()
|
||||
|
||||
// Update this if the default ipmasq backend changes
|
||||
switch tc.ipMasqBackend {
|
||||
case "iptables", "":
|
||||
ipt, err := iptables.NewWithProtocol(iptables.ProtocolIPv4)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
rules, err := ipt.List("nat", "POSTROUTING")
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(rules).Should(ContainElement(ContainSubstring(ip)))
|
||||
case "nftables":
|
||||
nft, err := knftables.New(knftables.InetFamily, "cni_plugins_masquerade")
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
rules, err := nft.ListRules(context.TODO(), "masq_checks")
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
// FIXME: ListRules() doesn't return the actual rule strings,
|
||||
// and we can't easily compute the ipmasq plugin's comment.
|
||||
comments := 0
|
||||
for _, r := range rules {
|
||||
if r.Comment != nil {
|
||||
comments++
|
||||
break
|
||||
}
|
||||
}
|
||||
Expect(comments).To(Equal(1), "expected to find exactly one Rule with a comment")
|
||||
}
|
||||
|
||||
err = testutils.CmdDelWithArgs(args, func() error {
|
||||
return cmdDel(args)
|
||||
})
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
return nil
|
||||
})
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
result, err := types100.GetResult(r)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(result.IPs).Should(HaveLen(1))
|
||||
|
||||
ipt, err := iptables.NewWithProtocol(iptables.ProtocolIPv4)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
rules, err := ipt.List("nat", "POSTROUTING")
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(rules).Should(ContainElement(ContainSubstring(result.IPs[0].Address.IP.String())))
|
||||
|
||||
err = testutils.CmdDelWithArgs(args, func() error {
|
||||
return cmdDel(args)
|
||||
})
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
return nil
|
||||
})
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
})
|
||||
}
|
||||
|
||||
for i, tc := range []testCase{
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user