mirror of
https://github.com/containers/podman.git
synced 2025-06-27 13:38:49 +08:00
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>
This commit is contained in:
@ -73,13 +73,18 @@ func (n *cniNetwork) networkCreate(net types.Network, writeToDisk bool) (*networ
|
|||||||
net.Name = name
|
net.Name = name
|
||||||
}
|
}
|
||||||
|
|
||||||
|
usedNetworks, err := n.getUsedSubnets()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
switch net.Driver {
|
switch net.Driver {
|
||||||
case types.BridgeNetworkDriver:
|
case types.BridgeNetworkDriver:
|
||||||
// if the name was created with getFreeDeviceName set the interface to it as well
|
// if the name was created with getFreeDeviceName set the interface to it as well
|
||||||
if name != "" && net.NetworkInterface == "" {
|
if name != "" && net.NetworkInterface == "" {
|
||||||
net.NetworkInterface = name
|
net.NetworkInterface = name
|
||||||
}
|
}
|
||||||
err = n.createBridge(&net)
|
err = n.createBridge(&net, usedNetworks)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -93,7 +98,7 @@ func (n *cniNetwork) networkCreate(net types.Network, writeToDisk bool) (*networ
|
|||||||
}
|
}
|
||||||
|
|
||||||
for i := range net.Subnets {
|
for i := range net.Subnets {
|
||||||
err := validateSubnet(&net.Subnets[i], !net.Internal)
|
err := validateSubnet(&net.Subnets[i], !net.Internal, usedNetworks)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -218,7 +223,7 @@ func createMacVLAN(network *types.Network) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (n *cniNetwork) createBridge(network *types.Network) error {
|
func (n *cniNetwork) createBridge(network *types.Network, usedNetworks []*net.IPNet) error {
|
||||||
if network.NetworkInterface != "" {
|
if network.NetworkInterface != "" {
|
||||||
bridges := n.getBridgeInterfaceNames()
|
bridges := n.getBridgeInterfaceNames()
|
||||||
if pkgutil.StringInSlice(network.NetworkInterface, bridges) {
|
if pkgutil.StringInSlice(network.NetworkInterface, bridges) {
|
||||||
@ -236,7 +241,7 @@ func (n *cniNetwork) createBridge(network *types.Network) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if len(network.Subnets) == 0 {
|
if len(network.Subnets) == 0 {
|
||||||
freeSubnet, err := n.getFreeIPv4NetworkSubnet()
|
freeSubnet, err := n.getFreeIPv4NetworkSubnet(usedNetworks)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -256,14 +261,14 @@ func (n *cniNetwork) createBridge(network *types.Network) error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if !ipv4 {
|
if !ipv4 {
|
||||||
freeSubnet, err := n.getFreeIPv4NetworkSubnet()
|
freeSubnet, err := n.getFreeIPv4NetworkSubnet(usedNetworks)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
network.Subnets = append(network.Subnets, *freeSubnet)
|
network.Subnets = append(network.Subnets, *freeSubnet)
|
||||||
}
|
}
|
||||||
if !ipv6 {
|
if !ipv6 {
|
||||||
freeSubnet, err := n.getFreeIPv6NetworkSubnet()
|
freeSubnet, err := n.getFreeIPv6NetworkSubnet(usedNetworks)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
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
|
// given gateway and lease range are part of this subnet. If the
|
||||||
// gateway is empty and addGateway is true it will get the first
|
// gateway is empty and addGateway is true it will get the first
|
||||||
// available ip in the subnet assigned.
|
// 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 {
|
if s == nil {
|
||||||
return errors.New("subnet is 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.
|
// Reparse to ensure subnet is valid.
|
||||||
// Do not use types.ParseCIDR() because we want the ip to be
|
// Do not use types.ParseCIDR() because we want the ip to be
|
||||||
// the network address and not a random ip in the subnet.
|
// 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 {
|
if err != nil {
|
||||||
return errors.Wrap(err, "subnet invalid")
|
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}
|
s.Subnet = types.IPNet{IPNet: *net}
|
||||||
if s.Gateway != nil {
|
if s.Gateway != nil {
|
||||||
if !s.Subnet.Contains(s.Gateway) {
|
if !s.Subnet.Contains(s.Gateway) {
|
||||||
|
@ -508,6 +508,9 @@ var _ = Describe("Config", func() {
|
|||||||
Expect(network1.Subnets[0].Gateway.String()).To(Equal("10.0.0.1"))
|
Expect(network1.Subnets[0].Gateway.String()).To(Equal("10.0.0.1"))
|
||||||
Expect(network1.Subnets[0].LeaseRange.StartIP.String()).To(Equal(startIP))
|
Expect(network1.Subnets[0].LeaseRange.StartIP.String()).To(Equal(startIP))
|
||||||
|
|
||||||
|
err = libpodNet.NetworkRemove(network1.Name)
|
||||||
|
Expect(err).To(BeNil())
|
||||||
|
|
||||||
endIP := "10.0.0.10"
|
endIP := "10.0.0.10"
|
||||||
network = types.Network{
|
network = types.Network{
|
||||||
Driver: "bridge",
|
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].Gateway.String()).To(Equal("10.0.0.1"))
|
||||||
Expect(network1.Subnets[0].LeaseRange.EndIP.String()).To(Equal(endIP))
|
Expect(network1.Subnets[0].LeaseRange.EndIP.String()).To(Equal(endIP))
|
||||||
|
|
||||||
|
err = libpodNet.NetworkRemove(network1.Name)
|
||||||
|
Expect(err).To(BeNil())
|
||||||
|
|
||||||
network = types.Network{
|
network = types.Network{
|
||||||
Driver: "bridge",
|
Driver: "bridge",
|
||||||
Subnets: []types.Subnet{
|
Subnets: []types.Subnet{
|
||||||
@ -590,7 +596,7 @@ var _ = Describe("Config", func() {
|
|||||||
}
|
}
|
||||||
_, err := libpodNet.NetworkCreate(network)
|
_, err := libpodNet.NetworkCreate(network)
|
||||||
Expect(err).To(HaveOccurred())
|
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() {
|
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"))
|
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() {
|
Context("network load valid existing ones", func() {
|
||||||
|
@ -221,12 +221,7 @@ func getNetworkIDFromName(name string) string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// getFreeIPv6NetworkSubnet returns a unused ipv4 subnet
|
// getFreeIPv6NetworkSubnet returns a unused ipv4 subnet
|
||||||
func (n *cniNetwork) getFreeIPv4NetworkSubnet() (*types.Subnet, error) {
|
func (n *cniNetwork) getFreeIPv4NetworkSubnet(usedNetworks []*net.IPNet) (*types.Subnet, error) {
|
||||||
networks, err := n.getUsedSubnets()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// the default podman network is 10.88.0.0/16
|
// the default podman network is 10.88.0.0/16
|
||||||
// start locking for free /24 networks
|
// start locking for free /24 networks
|
||||||
network := &net.IPNet{
|
network := &net.IPNet{
|
||||||
@ -236,12 +231,13 @@ func (n *cniNetwork) getFreeIPv4NetworkSubnet() (*types.Subnet, error) {
|
|||||||
|
|
||||||
// TODO: make sure to not use public subnets
|
// TODO: make sure to not use public subnets
|
||||||
for {
|
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())
|
logrus.Debugf("found free ipv4 network subnet %s", network.String())
|
||||||
return &types.Subnet{
|
return &types.Subnet{
|
||||||
Subnet: types.IPNet{IPNet: *network},
|
Subnet: types.IPNet{IPNet: *network},
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
var err error
|
||||||
network, err = util.NextSubnet(network)
|
network, err = util.NextSubnet(network)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@ -250,12 +246,7 @@ func (n *cniNetwork) getFreeIPv4NetworkSubnet() (*types.Subnet, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// getFreeIPv6NetworkSubnet returns a unused ipv6 subnet
|
// getFreeIPv6NetworkSubnet returns a unused ipv6 subnet
|
||||||
func (n *cniNetwork) getFreeIPv6NetworkSubnet() (*types.Subnet, error) {
|
func (n *cniNetwork) getFreeIPv6NetworkSubnet(usedNetworks []*net.IPNet) (*types.Subnet, error) {
|
||||||
networks, err := n.getUsedSubnets()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// FIXME: Is 10000 fine as limit? We should prevent an endless loop.
|
// FIXME: Is 10000 fine as limit? We should prevent an endless loop.
|
||||||
for i := 0; i < 10000; i++ {
|
for i := 0; i < 10000; i++ {
|
||||||
// RFC4193: Choose the ipv6 subnet random and NOT sequentially.
|
// RFC4193: Choose the ipv6 subnet random and NOT sequentially.
|
||||||
@ -263,7 +254,7 @@ func (n *cniNetwork) getFreeIPv6NetworkSubnet() (*types.Subnet, error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
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())
|
logrus.Debugf("found free ipv6 network subnet %s", network.String())
|
||||||
return &types.Subnet{
|
return &types.Subnet{
|
||||||
Subnet: types.IPNet{IPNet: network},
|
Subnet: types.IPNet{IPNet: network},
|
||||||
@ -279,9 +270,8 @@ func (n *cniNetwork) getUsedSubnets() ([]*net.IPNet, error) {
|
|||||||
// first, load all used subnets from network configs
|
// first, load all used subnets from network configs
|
||||||
subnets := make([]*net.IPNet, 0, len(n.networks))
|
subnets := make([]*net.IPNet, 0, len(n.networks))
|
||||||
for _, val := range n.networks {
|
for _, val := range n.networks {
|
||||||
for _, subnet := range val.libpodNet.Subnets {
|
for i := range val.libpodNet.Subnets {
|
||||||
// nolint:exportloopref
|
subnets = append(subnets, &val.libpodNet.Subnets[i].Subnet.IPNet)
|
||||||
subnets = append(subnets, &subnet.Subnet.IPNet)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// second, load networks from the current system
|
// second, load networks from the current system
|
||||||
|
Reference in New Issue
Block a user