Skip to content

Commit

Permalink
podman support for IPv6 networks
Browse files Browse the repository at this point in the history
podman containers using IPv6 were missing the default route, breaking
deployments trying to use them.

The problem is that the default route was hardcoded to IPv4, this
takes into consideration the podman subnet IP family to generate
the corresponding default route.

Signed-off-by: Antonio Ojea <aojea@redhat.com>
  • Loading branch information
Antonio Ojea committed Aug 15, 2020
1 parent d777a7b commit 07e3f1b
Show file tree
Hide file tree
Showing 5 changed files with 97 additions and 4 deletions.
2 changes: 1 addition & 1 deletion pkg/domain/infra/abi/network.go
Original file line number Diff line number Diff line change
Expand Up @@ -191,7 +191,7 @@ func createBridge(r *libpod.Runtime, name string, options entities.NetworkCreate
var plugins []network.CNIPlugins
var routes []network.IPAMRoute

defaultRoute, err := network.NewIPAMDefaultRoute()
defaultRoute, err := network.NewIPAMDefaultRoute(network.IsIPv6(subnet.IP))
if err != nil {
return "", err
}
Expand Down
5 changes: 5 additions & 0 deletions pkg/network/ip.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,8 @@ func CalcGatewayIP(ipn *net.IPNet) net.IP {
nid := ipn.IP.Mask(ipn.Mask)
return ip.NextIP(nid)
}

// IsIPv6 returns if netIP is IPv6.
func IsIPv6(netIP net.IP) bool {
return netIP != nil && netIP.To4() == nil
}
15 changes: 12 additions & 3 deletions pkg/network/netconflist.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,11 @@ import (
"path/filepath"
)

const (
defaultIPv4Route = "0.0.0.0/0"
defaultIPv6Route = "::/0"
)

// NcList describes a generic map
type NcList map[string]interface{}

Expand Down Expand Up @@ -86,9 +91,13 @@ func NewIPAMRoute(r *net.IPNet) IPAMRoute { //nolint:interfacer
}

// NewIPAMDefaultRoute creates a new IPAMDefault route of
// 0.0.0.0/0
func NewIPAMDefaultRoute() (IPAMRoute, error) {
_, n, err := net.ParseCIDR("0.0.0.0/0")
// 0.0.0.0/0 for IPv4 or ::/0 for IPv6
func NewIPAMDefaultRoute(isIPv6 bool) (IPAMRoute, error) {
route := defaultIPv4Route
if isIPv6 {
route = defaultIPv6Route
}
_, n, err := net.ParseCIDR(route)
if err != nil {
return IPAMRoute{}, err
}
Expand Down
38 changes: 38 additions & 0 deletions pkg/network/netconflist_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package network

import (
"reflect"
"testing"
)

func TestNewIPAMDefaultRoute(t *testing.T) {

tests := []struct {
name string
isIPv6 bool
want IPAMRoute
}{
{
name: "IPv4 default route",
isIPv6: false,
want: IPAMRoute{defaultIPv4Route},
},
{
name: "IPv6 default route",
isIPv6: true,
want: IPAMRoute{defaultIPv6Route},
},
}
for _, tt := range tests {
tt := tt
t.Run(tt.name, func(t *testing.T) {
got, err := NewIPAMDefaultRoute(tt.isIPv6)
if err != nil {
t.Errorf("no errorr expected: %v", err)
}
if !reflect.DeepEqual(got, tt.want) {
t.Errorf("NewIPAMDefaultRoute() = %v, want %v", got, tt.want)
}
})
}
}
41 changes: 41 additions & 0 deletions test/e2e/network_create_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,47 @@ var _ = Describe("Podman network create", func() {
Expect(subnet.Contains(containerIP)).To(BeTrue())
})

It("podman network create with name and IPv6 subnet", func() {
SkipIfRemote()
var (
results []network.NcList
)
nc := podmanTest.Podman([]string{"network", "create", "--subnet", "fd00:1:2:3:4::/64", "newIPv6network"})
nc.WaitWithDefaultTimeout()
Expect(nc.ExitCode()).To(BeZero())

defer podmanTest.removeCNINetwork("newIPv6network")

// Inspect the network configuration
inspect := podmanTest.Podman([]string{"network", "inspect", "newIPv6network"})
inspect.WaitWithDefaultTimeout()

// JSON the network configuration into something usable
err := json.Unmarshal([]byte(inspect.OutputToString()), &results)
Expect(err).To(BeNil())
result := results[0]
Expect(result["name"]).To(Equal("newIPv6network"))

// JSON the bridge info
bridgePlugin, err := genericPluginsToBridge(result["plugins"], "bridge")
Expect(err).To(BeNil())
Expect(bridgePlugin.IPAM.Routes[0].Dest).To(Equal("::/0"))

// Once a container executes a new network, the nic will be created. We should clean those up
// best we can
defer removeNetworkDevice(bridgePlugin.BrName)

try := podmanTest.Podman([]string{"run", "-it", "--rm", "--network", "newIPv6network", ALPINE, "sh", "-c", "ip addr show eth0 | grep global | awk ' /inet6 / {print $2}'"})
try.WaitWithDefaultTimeout()

_, subnet, err := net.ParseCIDR("fd00:1:2:3:4::/64")
Expect(err).To(BeNil())
containerIP, _, err := net.ParseCIDR(try.OutputToString())
Expect(err).To(BeNil())
// Ensure that the IP the container got is within the subnet the user asked for
Expect(subnet.Contains(containerIP)).To(BeTrue())
})

It("podman network create with invalid subnet", func() {
nc := podmanTest.Podman([]string{"network", "create", "--subnet", "10.11.12.0/17000", "fail"})
nc.WaitWithDefaultTimeout()
Expand Down

0 comments on commit 07e3f1b

Please sign in to comment.