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 }