github.com/containerd/nerdctl/v2@v2.0.0-beta.5.0.20240520001846-b5758f54fa28/pkg/netutil/subnet/subnet.go (about)

     1  /*
     2     Copyright The containerd Authors.
     3  
     4     Licensed under the Apache License, Version 2.0 (the "License");
     5     you may not use this file except in compliance with the License.
     6     You may obtain a copy of the License at
     7  
     8         http://www.apache.org/licenses/LICENSE-2.0
     9  
    10     Unless required by applicable law or agreed to in writing, software
    11     distributed under the License is distributed on an "AS IS" BASIS,
    12     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13     See the License for the specific language governing permissions and
    14     limitations under the License.
    15  */
    16  
    17  package subnet
    18  
    19  import (
    20  	"fmt"
    21  	"net"
    22  
    23  	"github.com/containerd/nerdctl/v2/pkg/rootlessutil"
    24  )
    25  
    26  func GetLiveNetworkSubnets() ([]*net.IPNet, error) {
    27  	var addrs []net.Addr
    28  	if err := rootlessutil.WithDetachedNetNSIfAny(func() error {
    29  		var err2 error
    30  		addrs, err2 = net.InterfaceAddrs()
    31  		return err2
    32  	}); err != nil {
    33  		return nil, err
    34  	}
    35  	nets := make([]*net.IPNet, 0, len(addrs))
    36  	for _, address := range addrs {
    37  		_, n, err := net.ParseCIDR(address.String())
    38  		if err != nil {
    39  			return nil, err
    40  		}
    41  		nets = append(nets, n)
    42  	}
    43  	return nets, nil
    44  }
    45  
    46  // GetFreeSubnet try to find a free subnet in the given network
    47  func GetFreeSubnet(n *net.IPNet, usedNetworks []*net.IPNet) (*net.IPNet, error) {
    48  	for {
    49  		if !IntersectsWithNetworks(n, usedNetworks) {
    50  			return n, nil
    51  		}
    52  		next, err := nextSubnet(n)
    53  		if err != nil {
    54  			break
    55  		}
    56  		n = next
    57  	}
    58  	return nil, fmt.Errorf("could not find free subnet")
    59  }
    60  
    61  func nextSubnet(subnet *net.IPNet) (*net.IPNet, error) {
    62  	newSubnet := &net.IPNet{
    63  		IP:   subnet.IP,
    64  		Mask: subnet.Mask,
    65  	}
    66  	ones, bits := newSubnet.Mask.Size()
    67  	if ones == 0 {
    68  		return nil, fmt.Errorf("%s has only one subnet", subnet.String())
    69  	}
    70  	zeroes := uint(bits - ones)
    71  	shift := zeroes % 8
    72  	idx := (ones - 1) / 8
    73  	if err := incByte(newSubnet, idx, shift); err != nil {
    74  		return nil, err
    75  	}
    76  	return newSubnet, nil
    77  }
    78  
    79  func incByte(subnet *net.IPNet, idx int, shift uint) error {
    80  	if idx < 0 {
    81  		return fmt.Errorf("no more subnets left")
    82  	}
    83  
    84  	var val byte = 1 << shift
    85  	// if overflow we have to inc the previous byte
    86  	if uint(subnet.IP[idx])+uint(val) > 255 {
    87  		if err := incByte(subnet, idx-1, 0); err != nil {
    88  			return err
    89  		}
    90  	}
    91  	subnet.IP[idx] += val
    92  	return nil
    93  }
    94  
    95  func IntersectsWithNetworks(n *net.IPNet, networklist []*net.IPNet) bool {
    96  	for _, nw := range networklist {
    97  		if n.Contains(nw.IP) || nw.Contains(n.IP) {
    98  			return true
    99  		}
   100  	}
   101  	return false
   102  }
   103  
   104  // lastIPInSubnet gets the last IP in a subnet
   105  // https://github.com/containers/podman/blob/v4.0.0-rc1/libpod/network/util/ip.go#L18
   106  func LastIPInSubnet(addr *net.IPNet) (net.IP, error) {
   107  	// re-parse to ensure clean network address
   108  	_, cidr, err := net.ParseCIDR(addr.String())
   109  	if err != nil {
   110  		return nil, err
   111  	}
   112  	ones, bits := cidr.Mask.Size()
   113  	if ones == bits {
   114  		return cidr.IP, err
   115  	}
   116  	for i := range cidr.IP {
   117  		cidr.IP[i] = cidr.IP[i] | ^cidr.Mask[i]
   118  	}
   119  	return cidr.IP, err
   120  }
   121  
   122  // firstIPInSubnet gets the first IP in a subnet
   123  // https://github.com/containers/podman/blob/v4.0.0-rc1/libpod/network/util/ip.go#L36
   124  func FirstIPInSubnet(addr *net.IPNet) (net.IP, error) {
   125  	// re-parse to ensure clean network address
   126  	_, cidr, err := net.ParseCIDR(addr.String())
   127  	if err != nil {
   128  		return nil, err
   129  	}
   130  	ones, bits := cidr.Mask.Size()
   131  	if ones == bits {
   132  		return cidr.IP, err
   133  	}
   134  	cidr.IP[len(cidr.IP)-1]++
   135  	return cidr.IP, err
   136  }