summaryrefslogtreecommitdiff
path: root/libpod
diff options
context:
space:
mode:
authorPaul Holzinger <pholzing@redhat.com>2021-08-26 12:10:08 +0200
committerPaul Holzinger <pholzing@redhat.com>2021-09-15 18:05:26 +0200
commitaa7bc4e37168e4cdb4469ba8b728d7f5157e46b5 (patch)
tree44ec15dfff52a1b04134b24f597e418b9b87d96c /libpod
parentc0cde378298bd8d5183155c8f62aae574be069eb (diff)
downloadpodman-aa7bc4e37168e4cdb4469ba8b728d7f5157e46b5.tar.gz
podman-aa7bc4e37168e4cdb4469ba8b728d7f5157e46b5.tar.bz2
podman-aa7bc4e37168e4cdb4469ba8b728d7f5157e46b5.zip
network create: validate the input subnet
Check that the given subnet does not conflict with existing ones (other configs or host interfaces). Signed-off-by: Paul Holzinger <pholzing@redhat.com>
Diffstat (limited to 'libpod')
-rw-r--r--libpod/network/cni/config.go29
-rw-r--r--libpod/network/cni/config_test.go27
-rw-r--r--libpod/network/cni/network.go24
3 files changed, 55 insertions, 25 deletions
diff --git a/libpod/network/cni/config.go b/libpod/network/cni/config.go
index ee203f80d..44842c664 100644
--- a/libpod/network/cni/config.go
+++ b/libpod/network/cni/config.go
@@ -73,13 +73,18 @@ func (n *cniNetwork) networkCreate(net types.Network, writeToDisk bool) (*networ
net.Name = name
}
+ usedNetworks, err := n.getUsedSubnets()
+ if err != nil {
+ return nil, err
+ }
+
switch net.Driver {
case types.BridgeNetworkDriver:
// if the name was created with getFreeDeviceName set the interface to it as well
if name != "" && net.NetworkInterface == "" {
net.NetworkInterface = name
}
- err = n.createBridge(&net)
+ err = n.createBridge(&net, usedNetworks)
if err != nil {
return nil, err
}
@@ -93,7 +98,7 @@ func (n *cniNetwork) networkCreate(net types.Network, writeToDisk bool) (*networ
}
for i := range net.Subnets {
- err := validateSubnet(&net.Subnets[i], !net.Internal)
+ err := validateSubnet(&net.Subnets[i], !net.Internal, usedNetworks)
if err != nil {
return nil, err
}
@@ -218,7 +223,7 @@ func createMacVLAN(network *types.Network) error {
return nil
}
-func (n *cniNetwork) createBridge(network *types.Network) error {
+func (n *cniNetwork) createBridge(network *types.Network, usedNetworks []*net.IPNet) error {
if network.NetworkInterface != "" {
bridges := n.getBridgeInterfaceNames()
if pkgutil.StringInSlice(network.NetworkInterface, bridges) {
@@ -236,7 +241,7 @@ func (n *cniNetwork) createBridge(network *types.Network) error {
}
if len(network.Subnets) == 0 {
- freeSubnet, err := n.getFreeIPv4NetworkSubnet()
+ freeSubnet, err := n.getFreeIPv4NetworkSubnet(usedNetworks)
if err != nil {
return err
}
@@ -256,14 +261,14 @@ func (n *cniNetwork) createBridge(network *types.Network) error {
}
}
if !ipv4 {
- freeSubnet, err := n.getFreeIPv4NetworkSubnet()
+ freeSubnet, err := n.getFreeIPv4NetworkSubnet(usedNetworks)
if err != nil {
return err
}
network.Subnets = append(network.Subnets, *freeSubnet)
}
if !ipv6 {
- freeSubnet, err := n.getFreeIPv6NetworkSubnet()
+ freeSubnet, err := n.getFreeIPv6NetworkSubnet(usedNetworks)
if err != nil {
return err
}
@@ -278,10 +283,14 @@ func (n *cniNetwork) createBridge(network *types.Network) error {
// given gateway and lease range are part of this subnet. If the
// gateway is empty and addGateway is true it will get the first
// available ip in the subnet assigned.
-func validateSubnet(s *types.Subnet, addGateway bool) error {
+func validateSubnet(s *types.Subnet, addGateway bool, usedNetworks []*net.IPNet) error {
if s == nil {
return errors.New("subnet is nil")
}
+ if s.Subnet.IP == nil {
+ return errors.New("subnet ip is nil")
+ }
+
// Reparse to ensure subnet is valid.
// Do not use types.ParseCIDR() because we want the ip to be
// the network address and not a random ip in the subnet.
@@ -289,6 +298,12 @@ func validateSubnet(s *types.Subnet, addGateway bool) error {
if err != nil {
return errors.Wrap(err, "subnet invalid")
}
+
+ // check that the new subnet does not conflict with existing ones
+ if util.NetworkIntersectsWithNetworks(net, usedNetworks) {
+ return errors.Errorf("subnet %s is already used on the host or by another config", net.String())
+ }
+
s.Subnet = types.IPNet{IPNet: *net}
if s.Gateway != nil {
if !s.Subnet.Contains(s.Gateway) {
diff --git a/libpod/network/cni/config_test.go b/libpod/network/cni/config_test.go
index f67402657..2465f1d1c 100644
--- a/libpod/network/cni/config_test.go
+++ b/libpod/network/cni/config_test.go
@@ -508,6 +508,9 @@ var _ = Describe("Config", func() {
Expect(network1.Subnets[0].Gateway.String()).To(Equal("10.0.0.1"))
Expect(network1.Subnets[0].LeaseRange.StartIP.String()).To(Equal(startIP))
+ err = libpodNet.NetworkRemove(network1.Name)
+ Expect(err).To(BeNil())
+
endIP := "10.0.0.10"
network = types.Network{
Driver: "bridge",
@@ -529,6 +532,9 @@ var _ = Describe("Config", func() {
Expect(network1.Subnets[0].Gateway.String()).To(Equal("10.0.0.1"))
Expect(network1.Subnets[0].LeaseRange.EndIP.String()).To(Equal(endIP))
+ err = libpodNet.NetworkRemove(network1.Name)
+ Expect(err).To(BeNil())
+
network = types.Network{
Driver: "bridge",
Subnets: []types.Subnet{
@@ -590,7 +596,7 @@ var _ = Describe("Config", func() {
}
_, err := libpodNet.NetworkCreate(network)
Expect(err).To(HaveOccurred())
- Expect(err.Error()).To(ContainSubstring("subnet invalid"))
+ Expect(err.Error()).To(ContainSubstring("subnet ip is nil"))
})
It("create network with name", func() {
@@ -886,6 +892,25 @@ var _ = Describe("Config", func() {
Expect(err.Error()).To(Equal("default network podman cannot be removed"))
})
+ It("network create with same subnet", func() {
+ subnet := "10.0.0.0/24"
+ n, _ := types.ParseCIDR(subnet)
+ subnet2 := "10.10.0.0/24"
+ n2, _ := types.ParseCIDR(subnet2)
+ network := types.Network{Subnets: []types.Subnet{{Subnet: n}, {Subnet: n2}}}
+ network1, err := libpodNet.NetworkCreate(network)
+ Expect(err).To(BeNil())
+ Expect(network1.Subnets).To(HaveLen(2))
+ network = types.Network{Subnets: []types.Subnet{{Subnet: n}}}
+ _, err = libpodNet.NetworkCreate(network)
+ Expect(err).To(HaveOccurred())
+ Expect(err.Error()).To(ContainSubstring("subnet 10.0.0.0/24 is already used on the host or by another config"))
+ network = types.Network{Subnets: []types.Subnet{{Subnet: n2}}}
+ _, err = libpodNet.NetworkCreate(network)
+ Expect(err).To(HaveOccurred())
+ Expect(err.Error()).To(ContainSubstring("subnet 10.10.0.0/24 is already used on the host or by another config"))
+ })
+
})
Context("network load valid existing ones", func() {
diff --git a/libpod/network/cni/network.go b/libpod/network/cni/network.go
index fde08a0c6..479f9e0eb 100644
--- a/libpod/network/cni/network.go
+++ b/libpod/network/cni/network.go
@@ -221,12 +221,7 @@ func getNetworkIDFromName(name string) string {
}
// getFreeIPv6NetworkSubnet returns a unused ipv4 subnet
-func (n *cniNetwork) getFreeIPv4NetworkSubnet() (*types.Subnet, error) {
- networks, err := n.getUsedSubnets()
- if err != nil {
- return nil, err
- }
-
+func (n *cniNetwork) getFreeIPv4NetworkSubnet(usedNetworks []*net.IPNet) (*types.Subnet, error) {
// the default podman network is 10.88.0.0/16
// start locking for free /24 networks
network := &net.IPNet{
@@ -236,12 +231,13 @@ func (n *cniNetwork) getFreeIPv4NetworkSubnet() (*types.Subnet, error) {
// TODO: make sure to not use public subnets
for {
- if intersectsConfig := util.NetworkIntersectsWithNetworks(network, networks); !intersectsConfig {
+ if intersectsConfig := util.NetworkIntersectsWithNetworks(network, usedNetworks); !intersectsConfig {
logrus.Debugf("found free ipv4 network subnet %s", network.String())
return &types.Subnet{
Subnet: types.IPNet{IPNet: *network},
}, nil
}
+ var err error
network, err = util.NextSubnet(network)
if err != nil {
return nil, err
@@ -250,12 +246,7 @@ func (n *cniNetwork) getFreeIPv4NetworkSubnet() (*types.Subnet, error) {
}
// getFreeIPv6NetworkSubnet returns a unused ipv6 subnet
-func (n *cniNetwork) getFreeIPv6NetworkSubnet() (*types.Subnet, error) {
- networks, err := n.getUsedSubnets()
- if err != nil {
- return nil, err
- }
-
+func (n *cniNetwork) getFreeIPv6NetworkSubnet(usedNetworks []*net.IPNet) (*types.Subnet, error) {
// FIXME: Is 10000 fine as limit? We should prevent an endless loop.
for i := 0; i < 10000; i++ {
// RFC4193: Choose the ipv6 subnet random and NOT sequentially.
@@ -263,7 +254,7 @@ func (n *cniNetwork) getFreeIPv6NetworkSubnet() (*types.Subnet, error) {
if err != nil {
return nil, err
}
- if intersectsConfig := util.NetworkIntersectsWithNetworks(&network, networks); !intersectsConfig {
+ if intersectsConfig := util.NetworkIntersectsWithNetworks(&network, usedNetworks); !intersectsConfig {
logrus.Debugf("found free ipv6 network subnet %s", network.String())
return &types.Subnet{
Subnet: types.IPNet{IPNet: network},
@@ -279,9 +270,8 @@ func (n *cniNetwork) getUsedSubnets() ([]*net.IPNet, error) {
// first, load all used subnets from network configs
subnets := make([]*net.IPNet, 0, len(n.networks))
for _, val := range n.networks {
- for _, subnet := range val.libpodNet.Subnets {
- // nolint:exportloopref
- subnets = append(subnets, &subnet.Subnet.IPNet)
+ for i := range val.libpodNet.Subnets {
+ subnets = append(subnets, &val.libpodNet.Subnets[i].Subnet.IPNet)
}
}
// second, load networks from the current system