github.com/containerd/nerdctl@v1.7.7/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  
    24  func GetLiveNetworkSubnets() ([]*net.IPNet, error) {
    25  	addrs, err := net.InterfaceAddrs()
    26  	if err != nil {
    27  		return nil, err
    28  	}
    29  	nets := make([]*net.IPNet, 0, len(addrs))
    30  	for _, address := range addrs {
    31  		_, n, err := net.ParseCIDR(address.String())
    32  		if err != nil {
    33  			return nil, err
    34  		}
    35  		nets = append(nets, n)
    36  	}
    37  	return nets, nil
    38  }
    39  
    40  // GetFreeSubnet try to find a free subnet in the given network
    41  func GetFreeSubnet(n *net.IPNet, usedNetworks []*net.IPNet) (*net.IPNet, error) {
    42  	for {
    43  		if !IntersectsWithNetworks(n, usedNetworks) {
    44  			return n, nil
    45  		}
    46  		next, err := nextSubnet(n)
    47  		if err != nil {
    48  			break
    49  		}
    50  		n = next
    51  	}
    52  	return nil, fmt.Errorf("could not find free subnet")
    53  }
    54  
    55  func nextSubnet(subnet *net.IPNet) (*net.IPNet, error) {
    56  	newSubnet := &net.IPNet{
    57  		IP:   subnet.IP,
    58  		Mask: subnet.Mask,
    59  	}
    60  	ones, bits := newSubnet.Mask.Size()
    61  	if ones == 0 {
    62  		return nil, fmt.Errorf("%s has only one subnet", subnet.String())
    63  	}
    64  	zeroes := uint(bits - ones)
    65  	shift := zeroes % 8
    66  	idx := (ones - 1) / 8
    67  	if err := incByte(newSubnet, idx, shift); err != nil {
    68  		return nil, err
    69  	}
    70  	return newSubnet, nil
    71  }
    72  
    73  func incByte(subnet *net.IPNet, idx int, shift uint) error {
    74  	if idx < 0 {
    75  		return fmt.Errorf("no more subnets left")
    76  	}
    77  
    78  	var val byte = 1 << shift
    79  	// if overflow we have to inc the previous byte
    80  	if uint(subnet.IP[idx])+uint(val) > 255 {
    81  		if err := incByte(subnet, idx-1, 0); err != nil {
    82  			return err
    83  		}
    84  	}
    85  	subnet.IP[idx] += val
    86  	return nil
    87  }
    88  
    89  func IntersectsWithNetworks(n *net.IPNet, networklist []*net.IPNet) bool {
    90  	for _, nw := range networklist {
    91  		if n.Contains(nw.IP) || nw.Contains(n.IP) {
    92  			return true
    93  		}
    94  	}
    95  	return false
    96  }
    97  
    98  // lastIPInSubnet gets the last IP in a subnet
    99  // https://github.com/containers/podman/blob/v4.0.0-rc1/libpod/network/util/ip.go#L18
   100  func LastIPInSubnet(addr *net.IPNet) (net.IP, error) {
   101  	// re-parse to ensure clean network address
   102  	_, cidr, err := net.ParseCIDR(addr.String())
   103  	if err != nil {
   104  		return nil, err
   105  	}
   106  	ones, bits := cidr.Mask.Size()
   107  	if ones == bits {
   108  		return cidr.IP, err
   109  	}
   110  	for i := range cidr.IP {
   111  		cidr.IP[i] = cidr.IP[i] | ^cidr.Mask[i]
   112  	}
   113  	return cidr.IP, err
   114  }
   115  
   116  // firstIPInSubnet gets the first IP in a subnet
   117  // https://github.com/containers/podman/blob/v4.0.0-rc1/libpod/network/util/ip.go#L36
   118  func FirstIPInSubnet(addr *net.IPNet) (net.IP, error) {
   119  	// re-parse to ensure clean network address
   120  	_, cidr, err := net.ParseCIDR(addr.String())
   121  	if err != nil {
   122  		return nil, err
   123  	}
   124  	ones, bits := cidr.Mask.Size()
   125  	if ones == bits {
   126  		return cidr.IP, err
   127  	}
   128  	cidr.IP[len(cidr.IP)-1]++
   129  	return cidr.IP, err
   130  }