Skip to content

Commit

Permalink
Add nftables backend to portmap
Browse files Browse the repository at this point in the history
  • Loading branch information
danwinship committed Jul 31, 2023
1 parent 1533a6e commit 60eb4e3
Show file tree
Hide file tree
Showing 5 changed files with 645 additions and 28 deletions.
103 changes: 93 additions & 10 deletions plugins/meta/portmap/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ import (
"github.com/containernetworking/cni/pkg/types"
current "github.com/containernetworking/cni/pkg/types/100"
"github.com/containernetworking/cni/pkg/version"
"github.com/containernetworking/plugins/pkg/utils"
bv "github.com/containernetworking/plugins/pkg/utils/buildversion"
)

Expand All @@ -46,6 +47,12 @@ type PortMapper interface {
unforwardPorts(config *PortMapConf) error
}

// These are vars rather than consts so we can "&" them
var (
iptablesBackend = "iptables"
nftablesBackend = "nftables"
)

// PortMapEntry corresponds to a single entry in the port_mappings argument,
// see CONVENTIONS.md
type PortMapEntry struct {
Expand All @@ -61,6 +68,7 @@ type PortMapConf struct {
mapper PortMapper

// Generic config
Backend *string `json:"backend,omitempty"`
SNAT *bool `json:"snat,omitempty"`
ConditionsV4 *[]string `json:"conditionsV4"`
ConditionsV6 *[]string `json:"conditionsV6"`
Expand Down Expand Up @@ -219,19 +227,36 @@ func parseConfig(stdin []byte, ifName string) (*PortMapConf, *current.Result, er
conf.SNAT = &tvar
}

conf.mapper = &portMapperIPTables{}

if conf.MarkMasqBit != nil && conf.ExternalSetMarkChain != nil {
return nil, nil, fmt.Errorf("Cannot specify externalSetMarkChain and markMasqBit")
err := validateBackend(&conf)
if err != nil {
return nil, nil, err
}
switch *conf.Backend {
case iptablesBackend:
conf.mapper = &portMapperIPTables{}

if conf.MarkMasqBit == nil {
bvar := DefaultMarkBit // go constants are "special"
conf.MarkMasqBit = &bvar
}
if conf.MarkMasqBit != nil && conf.ExternalSetMarkChain != nil {
return nil, nil, fmt.Errorf("Cannot specify externalSetMarkChain and markMasqBit")
}

if conf.MarkMasqBit == nil {
bvar := DefaultMarkBit // go constants are "special"
conf.MarkMasqBit = &bvar
}

if *conf.MarkMasqBit < 0 || *conf.MarkMasqBit > 31 {
return nil, nil, fmt.Errorf("MasqMarkBit must be between 0 and 31")
if *conf.MarkMasqBit < 0 || *conf.MarkMasqBit > 31 {
return nil, nil, fmt.Errorf("MasqMarkBit must be between 0 and 31")
}

case nftablesBackend:
conf.mapper = &portMapperNFTables{}

if conf.MarkMasqBit != nil || conf.ExternalSetMarkChain != nil {
return nil, nil, fmt.Errorf("externalSetMarkChain and markMasqBit can only be specified when using iptables backend")
}

default:
return nil, nil, fmt.Errorf("unrecognized backend %q", *conf.Backend)
}

// Reject invalid port numbers
Expand Down Expand Up @@ -273,3 +298,61 @@ func parseConfig(stdin []byte, ifName string) (*PortMapConf, *current.Result, er

return &conf, result, nil
}

// validateBackend validates and/or sets conf.Backend
func validateBackend(conf *PortMapConf) error {
backendConfig := make(map[string][]string)

if conf.MarkMasqBit != nil {
backendConfig[iptablesBackend] = append(backendConfig[iptablesBackend], "markMasqBit")
}
if conf.ExternalSetMarkChain != nil {
backendConfig[iptablesBackend] = append(backendConfig[iptablesBackend], "externalSetMarkChain")
}
if conditionsBackend := detectBackendOfConditions(conf.ConditionsV4); conditionsBackend != "" {
backendConfig[conditionsBackend] = append(backendConfig[conditionsBackend], "conditionsV4")
}
if conditionsBackend := detectBackendOfConditions(conf.ConditionsV6); conditionsBackend != "" {
backendConfig[conditionsBackend] = append(backendConfig[conditionsBackend], "conditionsV6")
}

// If backend wasn't requested explicitly, default to iptables, unless it is not
// available (and nftables is). FIXME: flip this default at some point.
if conf.Backend == nil {
if !utils.SupportsIPTables() && utils.SupportsNFTables() {
conf.Backend = &nftablesBackend
} else {
conf.Backend = &iptablesBackend
}
}

// Make sure we dont have config for the wrong backend
var wrongBackend string
if *conf.Backend == iptablesBackend {
wrongBackend = nftablesBackend
} else {
wrongBackend = iptablesBackend
}
if len(backendConfig[wrongBackend]) > 0 {
return fmt.Errorf("%s backend was requested but configuration contains %s-specific options %v", *conf.Backend, wrongBackend, backendConfig[wrongBackend])
}

// OK
return nil
}

// detectBackendOfConditions returns "iptables" if conditions contains iptables
// conditions, "nftables" if it contains nftables conditions, and "" if it is empty.
func detectBackendOfConditions(conditions *[]string) string {
if conditions == nil || len(*conditions) == 0 || (*conditions)[0] == "" {
return ""
}

// The first token of any iptables condition would start with a hyphen (e.g. "-d",
// "--sport", "-m"). No nftables condition would start that way. (An nftables
// condition might include a negative number, but not as the first token.)
if (*conditions)[0][0] == '-' {
return iptablesBackend
}
return nftablesBackend
}
14 changes: 7 additions & 7 deletions plugins/meta/portmap/portmap_iptables_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ import (
"github.com/containernetworking/cni/pkg/types"
)

var _ = Describe("portmapping configuration", func() {
var _ = Describe("portmapping configuration (iptables)", func() {
netName := "testNetName"
containerID := "icee6giejonei6sohng6ahngee7laquohquee9shiGo7fohferakah3Feiyoolu2pei7ciPhoh7shaoX6vai3vuf0ahfaeng8yohb9ceu0daez5hashee8ooYai5wa3y"

Expand Down Expand Up @@ -59,8 +59,8 @@ var _ = Describe("portmapping configuration", func() {
]
},
"snat": true,
"conditionsV4": ["a", "b"],
"conditionsV6": ["c", "d"]
"conditionsV4": ["-a", "b"],
"conditionsV6": ["-c", "d"]
}`, ver))

conf, _, err := parseConfig(configBytes, "foo")
Expand All @@ -85,15 +85,15 @@ var _ = Describe("portmapping configuration", func() {
"-m", "multiport",
"-p", "tcp",
"--destination-ports", "8080,8081,8083,8084,8085,8086",
"a", "b",
"-a", "b",
},
{
"-m", "comment", "--comment",
fmt.Sprintf("dnat name: \"test\" id: \"%s\"", containerID),
"-m", "multiport",
"-p", "udp",
"--destination-ports", "8080,8082",
"a", "b",
"-a", "b",
},
}))

Expand Down Expand Up @@ -185,8 +185,8 @@ var _ = Describe("portmapping configuration", func() {
]
},
"externalSetMarkChain": "PLZ-SET-MARK",
"conditionsV4": ["a", "b"],
"conditionsV6": ["c", "d"]
"conditionsV4": ["-a", "b"],
"conditionsV6": ["-c", "d"]
}`, ver))

conf, _, err := parseConfig(configBytes, "foo")
Expand Down
Loading

0 comments on commit 60eb4e3

Please sign in to comment.