-
Notifications
You must be signed in to change notification settings - Fork 8
/
ipam.go
206 lines (183 loc) · 6.44 KB
/
ipam.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
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
package sabakan
import (
"errors"
"net"
"github.com/cybozu-go/netutil"
)
// IPAMConfig is a set of IPAM configurations.
type IPAMConfig struct {
MaxNodesInRack uint `json:"max-nodes-in-rack"`
NodeIPv4Pool string `json:"node-ipv4-pool"`
NodeIPv4Offset string `json:"node-ipv4-offset,omitempty"`
NodeRangeSize uint `json:"node-ipv4-range-size"`
NodeRangeMask uint `json:"node-ipv4-range-mask"`
NodeIPPerNode uint `json:"node-ip-per-node"`
NodeIndexOffset uint `json:"node-index-offset"`
NodeGatewayOffset uint `json:"node-gateway-offset"`
BMCIPv4Pool string `json:"bmc-ipv4-pool"`
BMCIPv4Offset string `json:"bmc-ipv4-offset,omitempty"`
BMCRangeSize uint `json:"bmc-ipv4-range-size"`
BMCRangeMask uint `json:"bmc-ipv4-range-mask"`
BMCGatewayOffset uint `json:"bmc-ipv4-gateway-offset"`
}
// Validate validates configurations
func (c *IPAMConfig) Validate() error {
if c.MaxNodesInRack == 0 {
return errors.New("max-nodes-in-rack must not be zero")
}
ip, ipNet, err := net.ParseCIDR(c.NodeIPv4Pool)
if err != nil {
return errors.New("invalid node-ipv4-pool")
}
if !ip.Equal(ipNet.IP) {
return errors.New("host part of node-ipv4-pool must be cleared")
}
if len(c.NodeIPv4Offset) > 0 && net.ParseIP(c.NodeIPv4Offset) == nil {
return errors.New("invalid node-ipv4-offset")
}
if c.NodeRangeSize == 0 {
return errors.New("node-ipv4-range-size must not be zero")
}
if c.NodeRangeMask < 8 || 32 < c.NodeRangeMask {
return errors.New("invalid node-ipv4-range-mask")
}
if c.NodeIPPerNode == 0 {
return errors.New("node-ip-per-node must not be zero")
}
if c.NodeIndexOffset == 0 {
return errors.New("node-index-offset must not be zero")
}
if c.NodeGatewayOffset == 0 {
return errors.New("node-gateway-offset must not be zero")
}
ip, ipNet, err = net.ParseCIDR(c.BMCIPv4Pool)
if err != nil {
return errors.New("invalid bmc-ipv4-pool")
}
if !ip.Equal(ipNet.IP) {
return errors.New("host part of bmc-ipv4-pool must be cleared")
}
if len(c.BMCIPv4Offset) > 0 && net.ParseIP(c.BMCIPv4Offset) == nil {
return errors.New("invalid bmc-ipv4-offset")
}
if c.BMCRangeSize == 0 {
return errors.New("bmc-ipv4-range-size must not be zero")
}
if c.BMCRangeMask < 8 || 32 < c.BMCRangeMask {
return errors.New("invalid bmc-ipv4-range-mask")
}
if c.BMCGatewayOffset == 0 {
return errors.New("bmc-ipv4-gateway-offset must not be zero")
}
return nil
}
// GatewayAddress returns a gateway address for the given node address
func (c *IPAMConfig) GatewayAddress(addr *net.IPNet) *net.IPNet {
return &net.IPNet{
IP: netutil.IPAdd(addr.IP.Mask(addr.Mask), int64(c.NodeGatewayOffset)),
Mask: addr.Mask,
}
}
// GenerateIP generates IP addresses for a machine.
// Generated IP addresses are stored in mc.
func (c *IPAMConfig) GenerateIP(mc *Machine) {
// IP addresses are calculated as follows (LRN=Logical Rack Number):
// node0: INET_NTOA(INET_ATON(NodeIPv4Pool) + INET_ATON(NodeIPv4Offset) + (2^NodeRangeSize * NodeIPPerNode * LRN) + index-in-rack)
// node1: INET_NTOA(INET_ATON(NodeIPv4Pool) + INET_ATON(NodeIPv4Offset) + (2^NodeRangeSize * NodeIPPerNode * LRN) + index-in-rack + 2^NodeRangeSize)
// node2: INET_NTOA(INET_ATON(NodeIPv4Pool) + INET_ATON(NodeIPv4Offset) + (2^NodeRangeSize * NodeIPPerNode * LRN) + index-in-rack + 2^NodeRangeSize * 2)
// BMC: INET_NTOA(INET_ATON(BMCIPv4Pool) + INET_ATON(BMCIPv4Offset) + (2^BMCRangeSize * LRN) + index-in-rack)
calc := func(pool, offset string, shift, numip, lrn, idx uint) []net.IP {
result := make([]net.IP, numip)
poolIP, _, _ := net.ParseCIDR(pool)
var noffset int64
if len(offset) > 0 {
for _, b := range []byte(net.ParseIP(offset).To4()) {
noffset <<= 8
noffset |= int64(b)
}
}
a := netutil.IPAdd(poolIP, noffset)
su := uint(1) << shift
for i := uint(0); i < numip; i++ {
result[i] = netutil.IPAdd(a, int64(su*numip*lrn+idx+i*su))
}
return result
}
lrn := mc.Spec.Rack
idx := mc.Spec.IndexInRack
ips := calc(c.NodeIPv4Pool, c.NodeIPv4Offset, c.NodeRangeSize, c.NodeIPPerNode, lrn, idx)
strIPs := make([]string, len(ips))
nics := make([]NICConfig, len(ips))
mask := net.CIDRMask(int(c.NodeRangeMask), 32)
strMask := net.IP(mask).String()
for i, p := range ips {
strP := p.String()
strIPs[i] = strP
nics[i].Address = strP
nics[i].Netmask = strMask
nics[i].MaskBits = int(c.NodeRangeMask)
gw := c.GatewayAddress(&net.IPNet{IP: p, Mask: mask})
nics[i].Gateway = gw.IP.String()
}
mc.Spec.IPv4 = strIPs
mc.Spec.IPv6 = nil
mc.Info.Network.IPv4 = nics
bmcIPs := calc(c.BMCIPv4Pool, c.BMCIPv4Offset, c.BMCRangeSize, 1, lrn, idx)
mc.Spec.BMC.IPv4 = bmcIPs[0].String()
mc.Spec.BMC.IPv6 = ""
bmcMask := net.CIDRMask(int(c.BMCRangeMask), 32)
mc.Info.BMC.IPv4.Address = mc.Spec.BMC.IPv4
mc.Info.BMC.IPv4.Netmask = net.IP(bmcMask).String()
mc.Info.BMC.IPv4.MaskBits = int(c.BMCRangeMask)
bmcGW := netutil.IPAdd(bmcIPs[0].Mask(bmcMask), int64(c.BMCGatewayOffset))
mc.Info.BMC.IPv4.Gateway = bmcGW.String()
}
// LeaseRange is a range of IP addresses for DHCP lease.
type LeaseRange struct {
BeginAddress net.IP
Count int
key string
}
// IP returns n-th IP address in the range.
func (l *LeaseRange) IP(n int) net.IP {
return netutil.IPAdd(l.BeginAddress, int64(n))
}
// Key return key string.
func (l *LeaseRange) Key() string {
if len(l.key) == 0 {
l.key = l.BeginAddress.String()
}
return l.key
}
// LeaseRange returns a LeaseRange for the interface that receives DHCP requests.
// If no range can be assigned, this returns nil.
func (c *IPAMConfig) LeaseRange(ifaddr net.IP) *LeaseRange {
ip1, _, _ := net.ParseCIDR(c.NodeIPv4Pool)
var noffset1 int64
if len(c.NodeIPv4Offset) > 0 {
for _, b := range []byte(net.ParseIP(c.NodeIPv4Offset).To4()) {
noffset1 <<= 8
noffset1 |= int64(b)
}
}
diff := netutil.IPDiff(netutil.IPAdd(ip1, noffset1), ifaddr)
if diff <= 0 {
return nil
}
// Given these configurations,
// MaxNodesInRack = 28
// NodeRangeSize = 6
// NodeIndexOffset = 3
//
// The lease range will start at offset 32, and ends at 62 (64 - 1 - 1).
// Therefore the available lease IP address count is 31.
rangeSize := uint32(1 << c.NodeRangeSize)
offset := uint32(c.NodeIndexOffset + c.MaxNodesInRack + 1)
ranges := diff / int64(rangeSize)
startIP := netutil.IPAdd(ip1, int64(int64(rangeSize)*ranges+int64(c.NodeIndexOffset+c.MaxNodesInRack+1)+noffset1))
count := (rangeSize - 2) - offset + 1
return &LeaseRange{
BeginAddress: startIP,
Count: int(count),
}
}