Skip to content

Commit

Permalink
Merge pull request #2646 from fenxiong/dev
Browse files Browse the repository at this point in the history
Merge branch 'ipv6' into dev
  • Loading branch information
fenxiong authored Sep 22, 2020
2 parents abe5e5c + 29f69eb commit c501145
Show file tree
Hide file tree
Showing 25 changed files with 389 additions and 187 deletions.
1 change: 1 addition & 0 deletions agent/acs/handler/acs_handler_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@ const (
"ipv6Addresses": [{
"address": "ipv6"
}],
"subnetGatewayIpv4Address": "ipv4/20",
"macAddress": "mac"
}],
"roleCredentials": {
Expand Down
10 changes: 6 additions & 4 deletions agent/acs/handler/payload_handler_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -724,7 +724,8 @@ func TestPayloadHandlerAddedENIToTask(t *testing.T) {
Address: aws.String("ipv6"),
},
},
MacAddress: aws.String("mac"),
SubnetGatewayIpv4Address: aws.String("ipv4/20"),
MacAddress: aws.String("mac"),
},
},
},
Expand All @@ -733,7 +734,7 @@ func TestPayloadHandlerAddedENIToTask(t *testing.T) {
}

err := tester.payloadHandler.handleSingleMessage(payloadMessage)
assert.NoError(t, err)
require.NoError(t, err)

// Validate the added task has the eni information as expected
expectedENI := payloadMessage.Tasks[0].ElasticNetworkInterfaces[0]
Expand Down Expand Up @@ -849,7 +850,8 @@ func TestPayloadHandlerAddedENITrunkToTask(t *testing.T) {
Address: aws.String("ipv6"),
},
},
MacAddress: aws.String("mac"),
SubnetGatewayIpv4Address: aws.String("ipv4/20"),
MacAddress: aws.String("mac"),
InterfaceVlanProperties: &ecsacs.NetworkInterfaceVlanProperties{
VlanId: aws.String("12345"),
TrunkInterfaceMacAddress: aws.String("mac"),
Expand All @@ -862,7 +864,7 @@ func TestPayloadHandlerAddedENITrunkToTask(t *testing.T) {
}

err := tester.payloadHandler.handleSingleMessage(payloadMessage)
assert.NoError(t, err)
require.NoError(t, err)

taskeni := addedTask.GetPrimaryENI()

Expand Down
127 changes: 106 additions & 21 deletions agent/api/eni/eni.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ package eni

import (
"fmt"
"net"
"strings"

"github.com/aws/amazon-ecs-agent/agent/acs/model/ecsacs"
Expand All @@ -26,26 +27,34 @@ import (
type ENI struct {
// ID is the id of eni
ID string `json:"ec2Id"`
// InterfaceAssociationProtocol is the type of ENI, valid value: "default", "vlan"
InterfaceAssociationProtocol string `json:",omitempty"`
// MacAddress is the mac address of the eni
MacAddress string
// IPV4Addresses is the ipv4 address associated with the eni
IPV4Addresses []*ENIIPV4Address
// IPV6Addresses is the ipv6 address associated with the eni
IPV6Addresses []*ENIIPV6Address
// MacAddress is the mac address of the eni
MacAddress string
// SubnetGatewayIPV4Address is the IPv4 address of the subnet gateway of the ENI
SubnetGatewayIPV4Address string `json:",omitempty"`
// DomainNameServers specifies the nameserver IP addresses for the eni
DomainNameServers []string `json:",omitempty"`
// DomainNameSearchList specifies the search list for the domain
// name lookup, for the eni
DomainNameSearchList []string `json:",omitempty"`
// PrivateDNSName is the dns name assigned by the vpc to this eni
PrivateDNSName string `json:",omitempty"`
// InterfaceAssociationProtocol is the type of ENI, valid value: "default", "vlan"
InterfaceAssociationProtocol string `json:",omitempty"`
// InterfaceVlanProperties contains information for an interface
// that is supposed to be used as a VLAN device
InterfaceVlanProperties *InterfaceVlanProperties `json:",omitempty"`
// PrivateDNSName is the dns name assigned by the vpc to this eni
PrivateDNSName string `json:",omitempty"`
// SubnetGatewayIPV4Address is the address to the subnet gateway for the eni
SubnetGatewayIPV4Address string `json:",omitempty"`

// Due to historical reasons, the IPv4 subnet prefix length is sent with IPv4 subnet gateway
// address instead of the ENI's IP addresses. However, CNI plugins and many OS APIs expect it
// the other way around. Instead of doing this conversion all the time for each CNI and TMDS
// request, compute it once on demand and cache it here.
ipv4SubnetPrefixLength string
ipv4SubnetCIDRBlock string
ipv6SubnetCIDRBlock string
}

// InterfaceVlanProperties contains information for an interface that
Expand All @@ -61,9 +70,16 @@ const (

// VLANInterfaceAssociationProtocol represents the ENI with trunking enabled.
VLANInterfaceAssociationProtocol = "vlan"

// IPv6SubnetPrefixLength is the IPv6 global unicast address prefix length, consisting of
// global routing prefix and subnet ID lengths as specified in IPv6 addressing architecture
// (RFC 4291 section 2.5.4) and IPv6 Global Unicast Address Format (RFC 3587).
// The ACS ENI payload structure does not contain an IPv6 subnet prefix length because "/64" is
// the only allowed length per RFCs above, and the only one that VPC supports.
IPv6SubnetPrefixLength = "64"
)

// GetIPV4Addresses returns a list of ipv4 addresses allocated to the ENI
// GetIPV4Addresses returns the list of IPv4 addresses assigned to the ENI.
func (eni *ENI) GetIPV4Addresses() []string {
var addresses []string
for _, addr := range eni.IPV4Addresses {
Expand All @@ -73,7 +89,17 @@ func (eni *ENI) GetIPV4Addresses() []string {
return addresses
}

// GetPrimaryIPv4Address returns the primary IPv4 address associated with the ENI.
// GetIPV6Addresses returns the list of IPv6 addresses assigned to the ENI.
func (eni *ENI) GetIPV6Addresses() []string {
var addresses []string
for _, addr := range eni.IPV6Addresses {
addresses = append(addresses, addr.Address)
}

return addresses
}

// GetPrimaryIPv4Address returns the primary IPv4 address assigned to the ENI.
func (eni *ENI) GetPrimaryIPv4Address() string {
var primaryAddr string
for _, addr := range eni.IPV4Addresses {
Expand All @@ -86,27 +112,75 @@ func (eni *ENI) GetPrimaryIPv4Address() string {
return primaryAddr
}

// GetIPV6Addresses returns a list of ipv6 addresses allocated to the ENI
func (eni *ENI) GetIPV6Addresses() []string {
// GetPrimaryIPv4AddressWithPrefixLength returns the primary IPv4 address assigned to the ENI with
// its subnet prefix length.
func (eni *ENI) GetPrimaryIPv4AddressWithPrefixLength() string {
return eni.GetPrimaryIPv4Address() + "/" + eni.GetIPv4SubnetPrefixLength()
}

// GetIPAddressesWithPrefixLength returns the list of all IP addresses assigned to the ENI with
// their subnet prefix length.
func (eni *ENI) GetIPAddressesWithPrefixLength() []string {
var addresses []string
for _, addr := range eni.IPV4Addresses {
addresses = append(addresses, addr.Address+"/"+eni.GetIPv4SubnetPrefixLength())
}
for _, addr := range eni.IPV6Addresses {
addresses = append(addresses, addr.Address)
addresses = append(addresses, addr.Address+"/"+IPv6SubnetPrefixLength)
}

return addresses
}

// GetIPv4SubnetPrefixLength returns the IPv4 prefix length of the ENI's subnet.
func (eni *ENI) GetIPv4SubnetPrefixLength() string {
if eni.ipv4SubnetPrefixLength == "" && eni.SubnetGatewayIPV4Address != "" {
eni.ipv4SubnetPrefixLength = strings.Split(eni.SubnetGatewayIPV4Address, "/")[1]
}

return eni.ipv4SubnetPrefixLength
}

// GetIPv4SubnetCIDRBlock returns the IPv4 CIDR block, if any, of the ENI's subnet.
func (eni *ENI) GetIPv4SubnetCIDRBlock() string {
if eni.ipv4SubnetCIDRBlock == "" && eni.SubnetGatewayIPV4Address != "" {
_, ipv4Net, err := net.ParseCIDR(eni.SubnetGatewayIPV4Address)
if err == nil {
eni.ipv4SubnetCIDRBlock = ipv4Net.String()
}
}

return eni.ipv4SubnetCIDRBlock
}

// GetIPv6SubnetCIDRBlock returns the IPv6 CIDR block, if any, of the ENI's subnet.
func (eni *ENI) GetIPv6SubnetCIDRBlock() string {
if eni.ipv6SubnetCIDRBlock == "" && len(eni.IPV6Addresses) > 0 {
ipv6Addr := eni.IPV6Addresses[0].Address + "/" + IPv6SubnetPrefixLength
_, ipv6Net, err := net.ParseCIDR(ipv6Addr)
if err == nil {
eni.ipv6SubnetCIDRBlock = ipv6Net.String()
}
}

return eni.ipv6SubnetCIDRBlock
}

// GetSubnetGatewayIPv4Address returns the subnet gateway IPv4 address for the ENI.
func (eni *ENI) GetSubnetGatewayIPv4Address() string {
var gwAddr string
if eni.SubnetGatewayIPV4Address != "" {
gwAddr = strings.Split(eni.SubnetGatewayIPV4Address, "/")[0]
}

return gwAddr
}

// GetHostname returns the hostname assigned to the ENI
func (eni *ENI) GetHostname() string {
return eni.PrivateDNSName
}

// GetSubnetGatewayIPV4Address returns the subnet IPv4 gateway address assigned
// to the ENI
func (eni *ENI) GetSubnetGatewayIPV4Address() string {
return eni.SubnetGatewayIPV4Address
}

// IsStandardENI returns true if the ENI is a standard/regular ENI. That is, if it
// has its association protocol as standard. To be backwards compatible, if the
// association protocol is not set for an ENI, it's considered a standard ENI as well.
Expand Down Expand Up @@ -189,6 +263,7 @@ func ENIFromACS(acsENI *ecsacs.ElasticNetworkInterface) (*ENI, error) {
})
}

// Read ENI association properties.
var interfaceVlanProperties InterfaceVlanProperties

if aws.StringValue(acsENI.InterfaceAssociationProtocol) == VLANInterfaceAssociationProtocol {
Expand All @@ -198,11 +273,11 @@ func ENIFromACS(acsENI *ecsacs.ElasticNetworkInterface) (*ENI, error) {

eni := &ENI{
ID: aws.StringValue(acsENI.Ec2Id),
MacAddress: aws.StringValue(acsENI.MacAddress),
IPV4Addresses: ipv4Addrs,
IPV6Addresses: ipv6Addrs,
MacAddress: aws.StringValue(acsENI.MacAddress),
PrivateDNSName: aws.StringValue(acsENI.PrivateDnsName),
SubnetGatewayIPV4Address: aws.StringValue(acsENI.SubnetGatewayIpv4Address),
PrivateDNSName: aws.StringValue(acsENI.PrivateDnsName),
InterfaceAssociationProtocol: aws.StringValue(acsENI.InterfaceAssociationProtocol),
InterfaceVlanProperties: &interfaceVlanProperties,
}
Expand All @@ -224,6 +299,16 @@ func ValidateTaskENI(acsENI *ecsacs.ElasticNetworkInterface) error {
return errors.Errorf("eni message validation: no ipv4 addresses in the message")
}

if acsENI.SubnetGatewayIpv4Address == nil {
return errors.Errorf("eni message validation: no subnet gateway ipv4 address in the message")
}
gwIPv4Addr := aws.StringValue(acsENI.SubnetGatewayIpv4Address)
s := strings.Split(gwIPv4Addr, "/")
if len(s) != 2 {
return errors.Errorf(
"eni message validation: invalid subnet gateway ipv4 address %s", gwIPv4Addr)
}

if acsENI.MacAddress == nil {
return errors.Errorf("eni message validation: empty eni mac address in the message")
}
Expand Down
Loading

0 comments on commit c501145

Please sign in to comment.