Skip to content

Commit

Permalink
Inspect network info of a joined network namespace
Browse files Browse the repository at this point in the history
Closes: containers#13150
Signed-off-by: 馃槑 Mostafa Emami <mustafaemami@gmail.com>
  • Loading branch information
idleroamer committed Feb 27, 2022
1 parent f1d510b commit 2e06bf6
Show file tree
Hide file tree
Showing 3 changed files with 234 additions and 0 deletions.
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ require (
github.com/uber/jaeger-client-go v2.29.1+incompatible
github.com/vbauerster/mpb/v6 v6.0.4
github.com/vishvananda/netlink v1.1.1-0.20201029203352-d40f9887b852
github.com/vishvananda/netns v0.0.0-20200728191858-db3c7e526aae
go.etcd.io/bbolt v1.3.6
golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c
Expand Down
99 changes: 99 additions & 0 deletions libpod/networking_linux.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import (
"sort"
"strconv"
"strings"
"sync"
"syscall"
"time"

Expand All @@ -30,6 +31,7 @@ import (
"github.com/containers/podman/v3/pkg/util"
"github.com/containers/storage/pkg/lockfile"
"github.com/cri-o/ocicni/pkg/ocicni"
spec "github.com/opencontainers/runtime-spec/specs-go"
"github.com/opencontainers/selinux/go-selinux/label"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
Expand Down Expand Up @@ -1028,6 +1030,25 @@ func (c *Container) getContainerNetworkInfo() (*define.InspectNetworkSettings, e
return nil, err
}

for _, namespace := range c.config.Spec.Linux.Namespaces {
if namespace.Type == spec.NetworkNamespace {
if namespace.Path != "" {
result, err := c.inspectJoinedNetworkNS(namespace.Path)
// do not propagate the error inspecting a joined network ns
if err != nil {
logrus.Errorf("Error inspecting network namespace: %s of container %s: %v", namespace.Path, c.ID(), err)
return settings, nil
}
basicConfig, err := resultToBasicNetworkConfig(&result)
if err != nil {
return nil, err
}
settings.InspectBasicNetworkConfig = basicConfig
return settings, nil
}
}
}

// We can't do more if the network is down.
if c.state.NetNS == nil {
// We still want to make dummy configurations for each CNI net
Expand Down Expand Up @@ -1105,6 +1126,84 @@ func (c *Container) getContainerNetworkInfo() (*define.InspectNetworkSettings, e
return settings, nil
}

func (c *Container) inspectJoinedNetworkNS(networkns string) (q cnitypes.Result, retErr error) {
var (
wg sync.WaitGroup
)
var result cnitypes.Result
result.CNIVersion = "none"
var err error
wg.Add(1)
go func() {
defer wg.Done()
var f *os.File
f, err = os.OpenFile(networkns, os.O_RDONLY, 0755)
if err != nil {
return
}
if err = unix.Setns(int(f.Fd()), unix.CLONE_NEWNET); err != nil {
return
}
var ifaces []net.Interface
ifaces, err = net.Interfaces()
if err != nil {
return
}
var routes []netlink.Route
routes, err = netlink.RouteList(nil, netlink.FAMILY_ALL)
if err != nil {
return
}
var gateway net.IP
for _, route := range routes {
// default gateway
if route.Dst == nil {
gateway = route.Gw
}
}

for _, iface := range ifaces {
if strings.Contains(iface.Flags.String(), "loopback") {
continue
}
addrs, err := iface.Addrs()
if err != nil {
continue
}
if len(addrs) == 0 {
continue
}
result.Interfaces = append(result.Interfaces, &cnitypes.Interface{
Name: iface.Name,
Mac: iface.HardwareAddr.String(),
})
interfaceIndex := len(result.Interfaces) - 1
for _, address := range addrs {
if ipnet, ok := address.(*net.IPNet); ok {
version := "4"
if ipnet.IP.To4() == nil {
version = "6"
}
// due to discrepency between SecondaryIPAddresses of libpod
// and docker API collecting more IPAddresses will break inspect
// as one exports list of strings and other import list of Addresses
// it is already fixed in podman 4
if len(result.IPs) == 0 {
result.IPs = append(result.IPs, &cnitypes.IPConfig{
Version: version,
Address: *ipnet,
Gateway: gateway,
Interface: &interfaceIndex,
})
}
}
}
}
}()
wg.Wait()
return result, err
}

// setupNetworkDescriptions adds networks and eth values to the container's
// network descriptions
func (c *Container) setupNetworkDescriptions(networks []string) error {
Expand Down
134 changes: 134 additions & 0 deletions test/e2e/run_networking_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package integration
import (
"fmt"
"os"
"runtime"
"strings"

. "github.com/containers/podman/v3/test/utils"
Expand All @@ -11,6 +12,7 @@ import (
. "github.com/onsi/gomega"
. "github.com/onsi/gomega/gexec"
"github.com/uber/jaeger-client-go/utils"
"github.com/vishvananda/netns"
)

var _ = Describe("Podman run networking", func() {
Expand Down Expand Up @@ -641,6 +643,138 @@ var _ = Describe("Podman run networking", func() {
Expect(session.OutputToString()).To(ContainSubstring("11.11.11.11"))
})

setup_network_ns := func(networkNSName string, newns netns.NsHandle) {
runtime.LockOSThread()
defer runtime.UnlockOSThread()

// Save the current network namespace
origns, _ := netns.Get()
defer origns.Close()

err = netns.Set(newns)
Expect(err).To(BeNil())

setupNetworkNS := SystemExec("ip", []string{"link", "add", "enp2s0", "type", "veth", "peer", "name", "eth0"})
Expect(setupNetworkNS).Should(Exit(0))
setupNetworkNS = SystemExec("ip", []string{"addr", "add", "10.0.0.1/24", "dev", "eth0"})
Expect(setupNetworkNS).Should(Exit(0))
setupNetworkNS = SystemExec("ifconfig", []string{"eth0", "hw", "ether", "46:7f:45:6e:4f:c8"})
Expect(setupNetworkNS).Should(Exit(0))
setupNetworkNS = SystemExec("ip", []string{"-6", "addr", "add", "2A00:0C98:2060:A000:0001:0000:1d1e:ca75/64", "dev", "eth0"})
Expect(setupNetworkNS).Should(Exit(0))
setupNetworkNS = SystemExec("ip", []string{"link", "set", "eth0", "up"})
Expect(setupNetworkNS).Should(Exit(0))

setupNetworkNS = SystemExec("ip", []string{"link", "add", "enp2s", "type", "veth", "peer", "name", "eth1"})
Expect(setupNetworkNS).Should(Exit(0))
setupNetworkNS = SystemExec("ip", []string{"addr", "add", "10.10.10.0/20", "dev", "eth1"})
Expect(setupNetworkNS).Should(Exit(0))
setupNetworkNS = SystemExec("ip", []string{"addr", "add", "10.20.20.0/16", "dev", "eth1"})
Expect(setupNetworkNS).Should(Exit(0))
setupNetworkNS = SystemExec("ifconfig", []string{"eth1", "hw", "ether", "56:6e:35:5d:3e:a8"})
Expect(setupNetworkNS).Should(Exit(0))
setupNetworkNS = SystemExec("ip", []string{"link", "set", "eth1", "up"})
Expect(setupNetworkNS).Should(Exit(0))

setupNetworkNS = SystemExec("ip", []string{"route", "add", "default", "via", "10.10.10.0", "dev", "eth1"})
Expect(setupNetworkNS).Should(Exit(0))

// Switch back to the original namespace
netns.Set(origns)
}

check_network_ns_inspect := func(name string) {
inspectOut := podmanTest.InspectContainer(name)
Expect(inspectOut[0].NetworkSettings.IPAddress).To(Equal("10.0.0.1"))
Expect(inspectOut[0].NetworkSettings.IPPrefixLen).To(Equal(24))
Expect(inspectOut[0].NetworkSettings.Gateway).To(Equal("10.10.10.0"))
Expect(inspectOut[0].NetworkSettings.MacAddress).To(Equal("46:7f:45:6e:4f:c8"))
}

FIt("podman run newtork inspect fails gracefully on non-reachable network ns", func() {
SkipIfRootless("ip netns is not supported for rootless users")
if Containerized() {
Skip("Cannot be run within a container.")
}
networkNSName := "xxx3"
newns, _ := netns.NewNamed(networkNSName)

setup_network_ns(networkNSName, newns)

name := "xxx3Container"
session := podmanTest.Podman([]string{"run", "-d", "--name", name, "--net", "ns:/run/netns/" + networkNSName, ALPINE, "top"})
session.WaitWithDefaultTimeout()

// delete the named network ns before inspect
newns.Close()
netns.DeleteNamed(networkNSName)

inspectOut := podmanTest.InspectContainer(name)
Expect(inspectOut[0].NetworkSettings.IPAddress).To(Equal(""))
Expect(len(inspectOut[0].NetworkSettings.Networks)).To(Equal(0))
})

FIt("podman inspect can handle joined network ns with multiple interfaces", func() {
SkipIfRootless("ip netns is not supported for rootless users")
if Containerized() {
Skip("Cannot be run within a container.")
}

networkNSName := "xxx3"
newns, _ := netns.NewNamed(networkNSName)
defer newns.Close()
defer netns.DeleteNamed(networkNSName)

setup_network_ns(networkNSName, newns)

name := "xxx3Container"
session := podmanTest.Podman([]string{"run", "--name", name, "--net", "ns:/run/netns/" + networkNSName, ALPINE})
session.WaitWithDefaultTimeout()

session = podmanTest.Podman([]string{"container", "rm", name})
session.WaitWithDefaultTimeout()

// no network teardown should touch joined network ns interfaces
session = podmanTest.Podman([]string{"run", "-d", "--replace", "--name", name, "--net", "ns:/run/netns/" + networkNSName, ALPINE, "top"})
session.WaitWithDefaultTimeout()

check_network_ns_inspect(name)
})

FIt("podman do not tamper with joined network ns interfaces", func() {
SkipIfRootless("ip netns is not supported for rootless users")
if Containerized() {
Skip("Cannot be run within a container.")
}

networkNSName := "xxx3"
newns, _ := netns.NewNamed(networkNSName)
defer newns.Close()
defer netns.DeleteNamed(networkNSName)

setup_network_ns(networkNSName, newns)

name := "xxx3Container"
session := podmanTest.Podman([]string{"run", "--name", name, "--net", "ns:/run/netns/" + networkNSName, ALPINE})
session.WaitWithDefaultTimeout()

check_network_ns_inspect(name)

name = "xxx4Container"
session = podmanTest.Podman([]string{"run", "--name", name, "--net", "ns:/run/netns/" + networkNSName, ALPINE})
session.WaitWithDefaultTimeout()

check_network_ns_inspect(name)

session = podmanTest.Podman([]string{"container", "rm", name})
session.WaitWithDefaultTimeout()

session = podmanTest.Podman([]string{"run", "-d", "--replace", "--name", name, "--net", "ns:/run/netns/" + networkNSName, ALPINE, "top"})
session.WaitWithDefaultTimeout()

check_network_ns_inspect(name)
})

It("podman run network in bogus user created network namespace", func() {
session := podmanTest.Podman([]string{"run", "-dt", "--net", "ns:/run/netns/xxy", ALPINE, "wget", "www.podman.io"})
session.Wait(90)
Expand Down

0 comments on commit 2e06bf6

Please sign in to comment.