/
cidr.go
99 lines (85 loc) · 2.31 KB
/
cidr.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
package cloudconnect
import (
"errors"
"fmt"
"net"
"github.com/apparentlymart/go-cidr/cidr"
"gopkg.in/yaml.v3"
)
// CIDR is a wrapper around net.IPNet that supports YAML (un)marshalling.
type CIDR struct {
net.IPNet
}
// String for CIDR...
func (c *CIDR) String() string {
return c.IPNet.String()
}
// MarshalYAML for CIDR...
func (c CIDR) MarshalYAML() (interface{}, error) {
return c.String(), nil
}
// UnmarshalYAML for CIDR...
func (c *CIDR) UnmarshalYAML(value *yaml.Node) error {
_, parsed, err := net.ParseCIDR(value.Value)
if err != nil {
return fmt.Errorf("parse cidr: %s", err)
}
c.IPNet = *parsed
return nil
}
// Includes checks whether the CIDR includes the given subnet.
func (c *CIDR) Includes(subnet *CIDR) bool {
first, last := cidr.AddressRange(&subnet.IPNet)
if c.IPNet.Contains(first) && c.IPNet.Contains(last) {
return true
}
return false
}
// Subnet finds the next available subnet with the desired prefix within the CIDR.
func (c *CIDR) Subnet(prefix int, reserved []*CIDR) (*CIDR, error) {
supernetPrefix, _ := c.IPNet.Mask.Size()
newBits := prefix - supernetPrefix
if newBits < 0 {
return nil, errors.New("desired prefix exceeds the supernet")
}
next, err := cidr.Subnet(&c.IPNet, newBits, 0)
if err != nil {
return nil, fmt.Errorf("new subnet: %s", err)
}
Loop:
for {
for _, r := range reserved {
if err := VerifyNoOverlap([]*CIDR{{IPNet: *next}, r}); err != nil {
next, _ = cidr.NextSubnet(&r.IPNet, prefix)
continue Loop
}
}
break Loop
}
cidr := &CIDR{IPNet: *next}
if !c.Includes(cidr) {
return nil, errors.New("no space left in supernet")
}
return cidr, nil
}
// AddressCount returns the number of IP addresses in the CIDR block.
func (c *CIDR) AddressCount() int {
count := cidr.AddressCount(&c.IPNet)
return int(count)
}
// VerifyNoOverlap takes a list of subnets and verifies that none of them are overlapping.
// Adapted from: https://github.com/apparentlymart/go-cidr/blob/master/cidr/cidr.go#L126
func VerifyNoOverlap(subnets []*CIDR) error {
for i, s := range subnets {
first, last := cidr.AddressRange(&s.IPNet)
for j := 0; j < len(subnets); j++ {
if i == j {
continue
}
if subnets[j].Contains(first) || subnets[j].Contains(last) {
return fmt.Errorf("%s overlaps with %s", s.String(), subnets[j].String())
}
}
}
return nil
}