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 Mar 8, 2022
1 parent 8a75f58 commit c69d2f2
Show file tree
Hide file tree
Showing 2 changed files with 235 additions and 0 deletions.
83 changes: 83 additions & 0 deletions libpod/networking_linux.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ import (
"github.com/containers/podman/v3/pkg/rootless"
"github.com/containers/podman/v3/pkg/rootlessport"
"github.com/cri-o/ocicni/pkg/ocicni"
spec "github.com/opencontainers/runtime-spec/specs-go"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
"github.com/vishvananda/netlink"
Expand Down Expand Up @@ -984,6 +985,18 @@ func (c *Container) getContainerNetworkInfo() (*define.InspectNetworkSettings, e

// We can't do more if the network is down.
if c.state.NetNS == nil {
if networkNSPath := c.joinedNetworkNSPath(); networkNSPath != "" {
if result, err := c.inspectJoinedNetworkNS(networkNSPath); err == nil {
if basicConfig, err := resultToBasicNetworkConfig(&result); err == nil {
settings.InspectBasicNetworkConfig = basicConfig
return settings, nil
}
}
// do not propagate error inspecting a joined network ns
logrus.Errorf("Error inspecting network namespace: %s of container %s: %v", networkNSPath, c.ID(), err)
}
// We can't do more if the network is down.

// We still want to make dummy configurations for each CNI net
// the container joined.
if len(networks) > 0 {
Expand Down Expand Up @@ -1059,6 +1072,76 @@ func (c *Container) getContainerNetworkInfo() (*define.InspectNetworkSettings, e
return settings, nil
}

func (c *Container) joinedNetworkNSPath() string {
for _, namespace := range c.config.Spec.Linux.Namespaces {
if namespace.Type == spec.NetworkNamespace {
return namespace.Path
}
}
return ""
}

func (c *Container) inspectJoinedNetworkNS(networkns string) (q cnitypes.Result, retErr error) {
var result cnitypes.Result
result.CNIVersion = "none"
err := ns.WithNetNSPath(networkns, func(_ ns.NetNS) error {
ifaces, err := net.Interfaces()
if err != nil {
return err
}
routes, err := netlink.RouteList(nil, netlink.FAMILY_ALL)
if err != nil {
return err
}
var gateway net.IP
for _, route := range routes {
// default gateway
if route.Dst == nil {
gateway = route.Gw
}
}
for _, iface := range ifaces {
if iface.Flags&net.FlagLoopback != 0 {
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 {
if ipnet.IP.IsLinkLocalMulticast() || ipnet.IP.IsLinkLocalUnicast() {
continue
}
version := "6"
if ipnet.IP.To4() != nil {
version = "4"
}
subnet := cnitypes.IPConfig{
Version: version,
Address: *ipnet,
Interface: &interfaceIndex,
}
if ipnet.Contains(gateway) {
subnet.Gateway = gateway
}
result.IPs = append(result.IPs, &subnet)
}
}
}
return nil
})
return result, err
}

// setupNetworkDescriptions adds networks and eth values to the container's
// network descriptions
func (c *Container) setupNetworkDescriptions(networks []string) error {
Expand Down
152 changes: 152 additions & 0 deletions test/e2e/run_networking_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,18 @@ package integration

import (
"fmt"
"net"
"os"
"strings"
"syscall"

"github.com/containernetworking/plugins/pkg/ns"
. "github.com/containers/podman/v3/test/utils"
"github.com/containers/storage/pkg/stringid"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
"github.com/uber/jaeger-client-go/utils"
"github.com/vishvananda/netlink"
)

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

addAddr := func(cidr string, containerInterface netlink.Link) error {
_, ipnet, err := net.ParseCIDR(cidr)
Expect(err).To(BeNil())
addr := &netlink.Addr{IPNet: ipnet, Label: ""}
if err := netlink.AddrAdd(containerInterface, addr); err != nil && err != syscall.EEXIST {
return err
}
return nil
}

loopbackup := func() {
lo, err := netlink.LinkByName("lo")
Expect(err).To(BeNil())
err = netlink.LinkSetUp(lo)
Expect(err).To(BeNil())
}

linkup := func(name string, mac string, addresses []string) {
linkAttr := netlink.NewLinkAttrs()
linkAttr.Name = name
m, err := net.ParseMAC(mac)
Expect(err).To(BeNil())
linkAttr.HardwareAddr = net.HardwareAddr(m)
eth := &netlink.Dummy{LinkAttrs: linkAttr}
err = netlink.LinkAdd(eth)
Expect(err).To(BeNil())
err = netlink.LinkSetUp(eth)
Expect(err).To(BeNil())
for _, address := range addresses {
err := addAddr(address, eth)
Expect(err).To(BeNil())
}
}

routeAdd := func(gateway string) {
gw := net.ParseIP(gateway)
route := &netlink.Route{Dst: nil, Gw: gw}
netlink.RouteAdd(route)
}

setupNetworkNs := func(networkNSName string) {
ns.WithNetNSPath("/run/netns/"+networkNSName, func(_ ns.NetNS) error {
loopbackup()
linkup("eth0", "46:7f:45:6e:4f:c8", []string{"10.25.40.0/24", "fd04:3e42:4a4e:3381::/64"})
linkup("eth1", "56:6e:35:5d:3e:a8", []string{"10.88.0.0/16"})

routeAdd("10.25.40.0")
return nil
})
}

checkNetworkNsInspect := func(name string) {
inspectOut := podmanTest.InspectContainer(name)
Expect(inspectOut[0].NetworkSettings.IPAddress).To(Equal("10.25.40.0"))
Expect(inspectOut[0].NetworkSettings.IPPrefixLen).To(Equal(24))
Expect(len(inspectOut[0].NetworkSettings.SecondaryIPAddresses)).To(Equal(1))
Expect(inspectOut[0].NetworkSettings.SecondaryIPAddresses[0]).To(Equal("10.88.0.0/16"))
Expect(len(inspectOut[0].NetworkSettings.SecondaryIPv6Addresses)).To(Equal(1))
Expect(inspectOut[0].NetworkSettings.SecondaryIPv6Addresses[0]).To(Equal("fd04:3e42:4a4e:3381::/64"))
Expect(inspectOut[0].NetworkSettings.MacAddress).To(Equal("46:7f:45:6e:4f:c8"))
Expect(len(inspectOut[0].NetworkSettings.AdditionalMacAddresses)).To(Equal(1))
Expect(inspectOut[0].NetworkSettings.AdditionalMacAddresses[0]).To(Equal("56:6e:35:5d:3e:a8"))
Expect(inspectOut[0].NetworkSettings.Gateway).To(Equal("10.25.40.0"))

}

It("podman run newtork inspect fails gracefully on non-reachable network ns", func() {
SkipIfRootless("ip netns is not supported for rootless users")

networkNSName := RandomString(12)
addNamedNetwork := SystemExec("ip", []string{"netns", "add", networkNSName})
Expect(addNamedNetwork.ExitCode()).To(Equal(0))

setupNetworkNs(networkNSName)

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

// delete the named network ns before inspect
delNetworkNamespace := SystemExec("ip", []string{"netns", "delete", networkNSName})
Expect(delNetworkNamespace.ExitCode()).To(Equal(0))

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

It("podman inspect can handle joined network ns with multiple interfaces", func() {
SkipIfRootless("ip netns is not supported for rootless users")

networkNSName := RandomString(12)
addNamedNetwork := SystemExec("ip", []string{"netns", "add", networkNSName})
Expect(addNamedNetwork.ExitCode()).To(Equal(0))
defer func() {
delNetworkNamespace := SystemExec("ip", []string{"netns", "delete", networkNSName})
Expect(delNetworkNamespace.ExitCode()).To(Equal(0))
}()
setupNetworkNs(networkNSName)

name := RandomString(12)
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()

checkNetworkNsInspect(name)
})

It("podman do not tamper with joined network ns interfaces", func() {
SkipIfRootless("ip netns is not supported for rootless users")

networkNSName := RandomString(12)
addNamedNetwork := SystemExec("ip", []string{"netns", "add", networkNSName})
Expect(addNamedNetwork.ExitCode()).To(Equal(0))
defer func() {
delNetworkNamespace := SystemExec("ip", []string{"netns", "delete", networkNSName})
Expect(delNetworkNamespace.ExitCode()).To(Equal(0))
}()

setupNetworkNs(networkNSName)

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

checkNetworkNsInspect(name)

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

checkNetworkNsInspect(name)

// delete container, the network inspect should not change
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()

checkNetworkNsInspect(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 c69d2f2

Please sign in to comment.