Skip to content

Commit

Permalink
Get default network interfaces on host mode (#4342)
Browse files Browse the repository at this point in the history
* Get default network interfaces on host mode

* Add more logs when iterate over all routes
  • Loading branch information
xxx0624 authored Sep 23, 2024
1 parent 1528765 commit 5cc0fca
Show file tree
Hide file tree
Showing 5 changed files with 297 additions and 0 deletions.
61 changes: 61 additions & 0 deletions ecs-agent/tmds/utils/netconfig/netconfig_linux.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
//go:build linux
// +build linux

// 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 netconfig

import (
"github.com/aws/amazon-ecs-agent/ecs-agent/logger"
"github.com/aws/amazon-ecs-agent/ecs-agent/logger/field"
"github.com/aws/amazon-ecs-agent/ecs-agent/utils/netlinkwrapper"

"github.com/vishvananda/netlink"
)

// DefaultNetInterfaceName returns the device name of the first default network interface
// available on the instance. If none exist, an empty string and nil will be returned.
func DefaultNetInterfaceName(netlinkClient netlinkwrapper.NetLink) (string, error) {
routes, err := netlinkClient.RouteList(nil, netlink.FAMILY_ALL)
if err != nil {
return "", err
}

// Iterate over all routes
for _, route := range routes {
logger.Debug("Found route", logger.Fields{"Route": route})
if route.Gw == nil {
// A default route has a gateway. If it doesn't, skip it.
continue
}

if route.Dst == nil || route.Dst.String() == "0.0.0.0/0" || route.Dst.String() == "::/0" {
// Get the link (interface) associated with the default route
link, err := netlinkClient.LinkByIndex(route.LinkIndex)
if err != nil {
logger.Warn("Not able to get the associated network interface by the index", logger.Fields{
field.Error: err,
"LinkIndex": route.LinkIndex,
})
} else {
logger.Debug("Found the associated network interface by the index", logger.Fields{
"LinkName": link.Attrs().Name,
"LinkIndex": route.LinkIndex,
})
return link.Attrs().Name, nil
}
}
}
return "", nil
}
169 changes: 169 additions & 0 deletions ecs-agent/tmds/utils/netconfig/netconfig_linux_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,169 @@
//go:build linux && unit
// +build linux,unit

// 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 netconfig

import (
"net"
"testing"

mock_netlinkwrapper "github.com/aws/amazon-ecs-agent/ecs-agent/utils/netlinkwrapper/mocks"

"github.com/golang/mock/gomock"
"github.com/stretchr/testify/assert"
"github.com/vishvananda/netlink"
)

func TestDefaultNetInterfaceName(t *testing.T) {
ctrl := gomock.NewController(t)
defer ctrl.Finish()

_, allIpNet, err := net.ParseCIDR("0.0.0.0/0")
assert.NoError(t, err)
_, randomIpNet, err := net.ParseCIDR("192.168.1.0/24")
assert.NoError(t, err)

tcs := []struct {
name string
routes []netlink.Route
link netlink.Link
expectedDefaultNetInterfaceName string
expectedErrMsg string
}{
{
name: "no default route 1",
routes: []netlink.Route{
netlink.Route{
Gw: nil,
Dst: nil,
LinkIndex: 0,
},
},
link: &netlink.Device{
LinkAttrs: netlink.LinkAttrs{
Index: 0,
Name: "eni-0",
},
},
expectedDefaultNetInterfaceName: "",
expectedErrMsg: "",
},
{
name: "no default route 2",
routes: []netlink.Route{
netlink.Route{
Gw: net.ParseIP("10.194.20.1"),
Dst: randomIpNet,
LinkIndex: 0,
},
},
link: &netlink.Device{
LinkAttrs: netlink.LinkAttrs{
Index: 0,
Name: "eni-0",
},
},
expectedDefaultNetInterfaceName: "",
expectedErrMsg: "",
},
{
name: "one default route 1",
routes: []netlink.Route{
netlink.Route{
Gw: net.ParseIP("10.194.20.1"),
Dst: nil,
LinkIndex: 0,
},
},
link: &netlink.Device{
LinkAttrs: netlink.LinkAttrs{
Index: 0,
Name: "eni-0",
},
},
expectedDefaultNetInterfaceName: "eni-0",
expectedErrMsg: "",
},
{
name: "one default route 2",
routes: []netlink.Route{
netlink.Route{
Gw: net.ParseIP("10.194.20.1"),
Dst: allIpNet,
LinkIndex: 1,
},
},
link: &netlink.Device{
LinkAttrs: netlink.LinkAttrs{
Index: 1,
Name: "eni-1",
},
},
expectedDefaultNetInterfaceName: "eni-1",
expectedErrMsg: "",
},
{
name: "two default routes",
routes: []netlink.Route{
netlink.Route{
Gw: net.ParseIP("10.194.20.1"),
Dst: randomIpNet,
LinkIndex: 0,
},
netlink.Route{
Gw: net.ParseIP("10.194.20.1"),
Dst: allIpNet,
LinkIndex: 1,
},
netlink.Route{
Gw: net.ParseIP("10.194.20.1"),
Dst: nil,
LinkIndex: 2,
},
},
link: &netlink.Device{
LinkAttrs: netlink.LinkAttrs{
Index: 1,
Name: "eni-0",
},
},
expectedDefaultNetInterfaceName: "eni-0",
expectedErrMsg: "",
},
}

for _, tc := range tcs {
tc := tc
t.Run(tc.name, func(t *testing.T) {
t.Parallel()

netLink := mock_netlinkwrapper.NewMockNetLink(ctrl)
gomock.InOrder(
netLink.EXPECT().RouteList(nil, netlink.FAMILY_ALL).Return(tc.routes, nil).AnyTimes(),
netLink.EXPECT().LinkByIndex(tc.link.Attrs().Index).Return(tc.link, nil).AnyTimes(),
)

defaultNetInterfaceName, err := DefaultNetInterfaceName(netLink)
errMsg := ""
if err != nil {
errMsg = err.Error()
}

assert.Equal(t, tc.expectedErrMsg, errMsg)
assert.Equal(t, tc.expectedDefaultNetInterfaceName, defaultNetInterfaceName)
})
}
}
25 changes: 25 additions & 0 deletions ecs-agent/tmds/utils/netconfig/netconfig_windows.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
//go:build windows
// +build windows

// 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 netconfig

import "errors"

// DefaultNetInterfaceName returns the device name of the first default network interface
// available on the instance. This is not supported on windows as of now.
func DefaultNetInterfaceName() (string, error) {
return "", errors.New("Not supported on windows")
}
30 changes: 30 additions & 0 deletions ecs-agent/utils/netlinkwrapper/mocks/netlinkwrapper_mocks_linux.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

12 changes: 12 additions & 0 deletions ecs-agent/utils/netlinkwrapper/netlink_linux.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ import (
type NetLink interface {
LinkByName(name string) (netlink.Link, error)
LinkSetUp(link netlink.Link) error
RouteList(link netlink.Link, family int) ([]netlink.Route, error)
LinkByIndex(index int) (netlink.Link, error)
}

type netLink struct{}
Expand All @@ -38,3 +40,13 @@ func (nl *netLink) LinkByName(name string) (netlink.Link, error) {
func (nl *netLink) LinkSetUp(link netlink.Link) error {
return netlink.LinkSetUp(link)
}

// RouteList gets a list of routes in the system. Equivalent to: `ip route show`.
// The list can be filtered by link and ip family.
func (nl *netLink) RouteList(link netlink.Link, family int) ([]netlink.Route, error) {
return netlink.RouteList(link, family)
}

func (nl *netLink) LinkByIndex(index int) (netlink.Link, error) {
return netlink.LinkByIndex(index)
}

0 comments on commit 5cc0fca

Please sign in to comment.