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 }