Skip to content

Commit

Permalink
windows: add support for port mapping in vpc-bridge plugin
Browse files Browse the repository at this point in the history
Presently, the vpc-bridge plugin silently ignores any port mapping between container to host for the configured endpoint.

As per the current CNI convention, plugins can request that the runtime insert this dynamic configuration by explicitly listing their capabilities in the network configuration. Dynamic information (i.e. data that a runtime fills out) should be placed in a runtimeConfig section. Reference: https://www.cni.dev/docs/conventions/#dynamic-plugin-specific-fields-capabilities--runtime-configuration

`portMappings` is one such capability. This change adds the support for creating NAT port mappings between container and host ports as configured by the runtime.
  • Loading branch information
rawahars committed Jun 2, 2023
1 parent 921bef2 commit a26d849
Show file tree
Hide file tree
Showing 7 changed files with 234 additions and 92 deletions.
7 changes: 7 additions & 0 deletions network/vpc/port.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,13 @@ import (
"strings"
)

// PortMapping contains the container port to host port mapping information.
type PortMapping struct {
Protocol string `json:"protocol"`
HostPort int `json:"hostPort"`
ContainerPort int `json:"containerPort"`
}

// ValidatePort checks whether the port only has digits and is within valid port range.
func ValidatePort(p string) error {
port := strings.TrimSpace(p)
Expand Down
43 changes: 43 additions & 0 deletions network/vpc/protocol.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License"). You may
// not use this file except in compliance with the License. A copy of the
// License is located at
//
// http://aws.amazon.com/apache2.0/
//
// or in the "license" file accompanying this file. This file is distributed
// on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
// express or implied. See the License for the specific language governing
// permissions and limitations under the License.

package vpc

import (
"fmt"
"strings"
)

const (
// protocolTCP indicates TCP protocol number for port mapping.
protocolTCP uint32 = 6
// protocolUDP indicates UDP protocol number for port mapping.
protocolUDP uint32 = 17
)

// ProtocolToNumber converts the protocol to it's assigned number.
// Reference: https://www.iana.org/assignments/protocol-numbers/protocol-numbers.xhtml
func ProtocolToNumber(protocol string) (uint32, error) {
var protocolNumber uint32
switch strings.ToUpper(protocol) {
case "TCP":
protocolNumber = protocolTCP
case "UDP":
protocolNumber = protocolUDP
default:
// Protocol number 256 is invalid and therefore, returned with the error.
return 256, fmt.Errorf("unsupported protocol for portmapping: %s", protocol)
}

return protocolNumber, nil
}
53 changes: 53 additions & 0 deletions network/vpc/protocol_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License"). You may
// not use this file except in compliance with the License. A copy of the
// License is located at
//
// http://aws.amazon.com/apache2.0/
//
// or in the "license" file accompanying this file. This file is distributed
// on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
// express or implied. See the License for the specific language governing
// permissions and limitations under the License.

//go:build unit_test

package vpc

import (
"testing"

"github.com/stretchr/testify/assert"
)

// TestProtocolToNumberTCP tests the conversion of TCP protocol to number
// when the protocol is either in upper or lower case.
func TestProtocolToNumberTCP(t *testing.T) {
protocolToTest := []string{"TCP", "tcp"}

for _, testProtocol := range protocolToTest {
actualProtocolNumber, err := ProtocolToNumber(testProtocol)
assert.Equal(t, protocolTCP, actualProtocolNumber)
assert.NoError(t, err)
}
}

// TestProtocolToNumberUDP tests the conversion of UDP protocol to number
// when the protocol is either in upper or lower case.
func TestProtocolToNumberUDP(t *testing.T) {
protocolToTest := []string{"UDP", "udp"}

for _, testProtocol := range protocolToTest {
actualProtocolNumber, err := ProtocolToNumber(testProtocol)
assert.Equal(t, protocolUDP, actualProtocolNumber)
assert.NoError(t, err)
}
}

// TestProtocolToNumberFailure tests the failure case with invalid protocol.
func TestProtocolToNumberFailure(t *testing.T) {
actualProtocolNumber, err := ProtocolToNumber("ICMP")
assert.Equal(t, uint32(256), actualProtocolNumber)
assert.Error(t, err)
}
12 changes: 12 additions & 0 deletions plugins/vpc-bridge/config/netconfig.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ type NetConfig struct {
BridgeNetNSPath string
IPAddresses []net.IPNet
GatewayIPAddress net.IP
PortMappings []vpc.PortMapping
InterfaceType string
TapUserID int
Kubernetes *KubernetesConfig
Expand All @@ -46,6 +47,9 @@ type NetConfig struct {
// netConfigJSON defines the network configuration JSON file format for the vpc-bridge plugin.
type netConfigJSON struct {
cniTypes.NetConf
// Options to be passed in by the runtime
RuntimeConfig RuntimeConfig `json:"runtimeConfig"`
// Other explicit options
ENIName string `json:"eniName"`
ENIMACAddress string `json:"eniMACAddress"`
ENIIPAddresses []string `json:"eniIPAddresses"`
Expand All @@ -59,6 +63,13 @@ type netConfigJSON struct {
ServiceCIDR string `json:"serviceCIDR"`
}

// RuntimeConfig are the runtime options which will be populated dynamically by the runtime
// based on the requested capability.
// https://www.cni.dev/docs/conventions/#dynamic-plugin-specific-fields-capabilities--runtime-configuration
type RuntimeConfig struct {
PortMappings []vpc.PortMapping `json:"portMappings,omitempty"`
}

const (
// Bridge network namespace defaults to the host network namespace (empty string),
// or more precisely, whichever namespace the CNI plugin is running in.
Expand Down Expand Up @@ -107,6 +118,7 @@ func New(args *cniSkel.CmdArgs, isAddCmd bool) (*NetConfig, error) {
BridgeType: config.BridgeType,
BridgeNetNSPath: config.BridgeNetNSPath,
InterfaceType: config.InterfaceType,
PortMappings: config.RuntimeConfig.PortMappings,
}

// Parse the ENI MAC address.
Expand Down
Loading

0 comments on commit a26d849

Please sign in to comment.