-
Notifications
You must be signed in to change notification settings - Fork 23
/
net4.go
341 lines (297 loc) · 9.76 KB
/
net4.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
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
package iplib
import (
"crypto/rand"
"math"
"math/big"
"net"
"sync"
)
// Net4 is an implementation of Net intended for IPv4 netblocks. It has
// functions to return the broadcast address and wildcard mask not present in
// the IPv6 implementation
type Net4 struct {
net.IPNet
is4in6 bool
}
// NewNet4 returns an initialized Net4 object at the specified masklen. If
// mask is greater than 32, or if a v6 address is supplied, an empty Net4
// will be returned
func NewNet4(ip net.IP, masklen int) Net4 {
var maskMax = 32
if masklen > maskMax {
return Net4{IPNet: net.IPNet{}}
}
mask := net.CIDRMask(masklen, maskMax)
n := net.IPNet{IP: ForceIP4(ip).Mask(mask), Mask: mask}
return Net4{IPNet: n, is4in6: Is4in6(ip)}
}
// Net4FromStr takes a string which should be a v4 address in CIDR notation
// and returns an initialized Net4. If the string isn't parseable an empty
// Net4 will be returned
func Net4FromStr(s string) Net4 {
_, n, err := ParseCIDR(s)
if err != nil {
return Net4{}
}
if n4, ok := n.(Net4); ok {
return n4
}
return Net4{}
}
// BroadcastAddress returns the broadcast address for the represented network.
// In the context of IPv6 broadcast is meaningless and the value will be
// equivalent to LastAddress().
func (n Net4) BroadcastAddress() net.IP {
xip, _ := n.finalAddress()
return xip
}
// Contains returns true if ip is contained in the represented netblock
func (n Net4) Contains(ip net.IP) bool {
return n.IPNet.Contains(ip)
}
// ContainsNet returns true if the given Net is contained within the
// represented block
func (n Net4) ContainsNet(network Net) bool {
l1, _ := n.Mask().Size()
l2, _ := network.Mask().Size()
return l1 <= l2 && n.Contains(network.IP())
}
// Count returns the total number of usable IP addresses in the represented
// network..
func (n Net4) Count() uint32 {
ones, all := n.Mask().Size()
exp := all - ones
if exp == 1 {
return uint32(2) // special handling for RFC3021 /31
}
if exp == 0 {
return uint32(1) // special handling for /32
}
return uint32(math.Pow(2, float64(exp))) - 2
}
// Enumerate generates an array of all usable addresses in Net up to the
// given size starting at the given offset. If size=0 the entire block is
// enumerated.
//
// NOTE: RFC3021 defines a use case for netblocks of /31 for use in point-to-
// point links. For this reason enumerating networks at these lengths will
// return a 2-element array even though it would naturally return none.
//
// For consistency, enumerating a /32 will return the IP in a 1 element array
func (n Net4) Enumerate(size, offset int) []net.IP {
if n.IP() == nil {
return nil
}
count := int(n.Count())
// offset exceeds total, return an empty array
if offset > count {
return []net.IP{}
}
// size is greater than the number of addresses that can be returned,
// adjust the size of the slice but keep going
if size > (count-offset) || size == 0 {
size = count - offset
}
// Handle edge-case mask sizes
if count == 1 { // Count() returns 1 if host-bits == 0
return []net.IP{CopyIP(n.IPNet.IP)}
}
addrs := make([]net.IP, size)
netu := IP4ToUint32(n.FirstAddress())
netu += uint32(offset)
fip := Uint32ToIP4(netu)
limit := 65535
pos := 0
wg := sync.WaitGroup{}
for pos < size {
incr := limit
if limit > (size - pos) {
incr = size - pos
}
wg.Add(1)
go func(fip net.IP, pos, count int) {
defer wg.Done()
addrs[pos] = IncrementIP4By(fip, uint32(pos))
for i := 1; i < count; i++ {
pos++
addrs[pos] = NextIP(addrs[pos-1])
}
}(fip, pos, incr)
pos = pos + incr
}
wg.Wait()
return addrs
}
// FirstAddress returns the first usable address for the represented network
func (n Net4) FirstAddress() net.IP {
ones, _ := n.Mask().Size()
// if it's either a single IP or RFC 3021, return the network address
if ones >= 31 {
return n.IPNet.IP
}
return NextIP(n.IP())
}
// Is4in6 will return true if this Net4 object or any of its parents were
// explicitly initialized with a 4in6 address (::ffff:xxxx.xxx)
func (n Net4) Is4in6() bool {
return n.is4in6
}
// LastAddress returns the last usable address for the represented network
func (n Net4) LastAddress() net.IP {
xip, ones := n.finalAddress()
// if it's either a single IP or RFC 3021, return the last address
if ones >= 31 {
return xip
}
return PreviousIP(xip)
}
// Mask returns the netmask of the netblock
func (n Net4) Mask() net.IPMask {
return n.IPNet.Mask
}
// IP returns the network address for the represented network, e.g.
// the lowest IP address in the given block
func (n Net4) IP() net.IP {
return n.IPNet.IP
}
// NetworkAddress returns the network address for the represented network, e.g.
// the lowest IP address in the given block
func (n Net4) NetworkAddress() net.IP {
return n.IPNet.IP
}
// NextIP takes a net.IP as an argument and attempts to increment it by one.
// If the resulting address is outside of the range of the represented network
// it will return an empty net.IP and an ErrAddressOutOfRange. If the result
// is the broadcast address, the address _will_ be returned, but so will an
// ErrBroadcastAddress, to indicate that the address is technically
// outside the usable scope
func (n Net4) NextIP(ip net.IP) (net.IP, error) {
if !n.Contains(ip) {
return net.IP{}, ErrAddressOutOfRange
}
xip := NextIP(ip)
if !n.Contains(xip) {
return net.IP{}, ErrAddressOutOfRange
}
// if this is the broadcast address, return it but warn the caller via error
if n.BroadcastAddress().Equal(xip) {
return xip, ErrBroadcastAddress
}
return xip, nil
}
// NextNet takes a CIDR mask-size as an argument and attempts to create a new
// Net object just after the current Net, at the requested mask length
func (n Net4) NextNet(masklen int) Net4 {
l, _ := n.Mask().Size()
nextIP := NextIP(n.BroadcastAddress())
if masklen < l {
nextIP = IncrementIP4By(nextIP, uint32(math.Pow(2, 32-float64(masklen)))-2)
}
return NewNet4(nextIP, masklen)
}
// PreviousIP takes a net.IP as an argument and attempts to decrement it by
// one. If the resulting address is outside of the range of the represented
// network it will return an empty net.IP and an ErrAddressOutOfRange. If the
// result is the network address, the address _will_ be returned, but so will
// an ErrNetworkAddress, to indicate that the address is technically outside
// the usable scope
func (n Net4) PreviousIP(ip net.IP) (net.IP, error) {
if !n.Contains(ip) {
return net.IP{}, ErrAddressOutOfRange
}
xip := PreviousIP(ip)
if !n.Contains(xip) {
return net.IP{}, ErrAddressOutOfRange
}
// if this is the network address, return it but warn the caller via error
if n.IP().Equal(xip) {
return xip, ErrNetworkAddress
}
return xip, nil
}
// PreviousNet takes a CIDR mask-size as an argument and creates a new Net
// object just before the current one, at the requested mask length. If the
// specified mask is for a larger network than the current one then the new
// network may encompass the current one, e.g.:
//
// iplib.Net{192.168.4.0/22}.Subnet(21) -> 192.168.0.0/21
//
// In the above case 192.168.4.0/22 is part of 192.168.0.0/21
func (n Net4) PreviousNet(masklen int) Net4 {
return NewNet4(PreviousIP(n.IP()), masklen)
}
// RandomIP returns a random address from this Net4
func (n Net4) RandomIP() net.IP {
z, _ := rand.Int(rand.Reader, big.NewInt(int64(n.Count())))
return IncrementIP4By(n.IP(), uint32(z.Uint64()))
}
// String returns the CIDR notation of the enclosed network e.g. 192.168.0.1/24
func (n Net4) String() string {
return n.IPNet.String()
}
// Subnet takes a CIDR mask-size as an argument and carves the current Net
// object into subnets of that size, returning them as a []Net. The mask
// provided must be a larger-integer than the current mask. If set to 0 Subnet
// will carve the network in half
func (n Net4) Subnet(masklen int) ([]Net4, error) {
ones, all := n.Mask().Size()
if masklen == 0 {
masklen = ones + 1
}
if ones > masklen || masklen > all {
return nil, ErrBadMaskLength
}
mask := net.CIDRMask(masklen, all)
netlist := []Net4{{IPNet: net.IPNet{IP: n.IP(), Mask: mask}, is4in6: n.is4in6}}
for CompareIPs(netlist[len(netlist)-1].BroadcastAddress(), n.BroadcastAddress()) == -1 {
ng := net.IPNet{IP: NextIP(netlist[len(netlist)-1].BroadcastAddress()), Mask: mask}
netlist = append(netlist, Net4{ng, n.is4in6})
}
return netlist, nil
}
// Supernet takes a CIDR mask-size as an argument and returns a Net object
// containing the supernet of the current Net at the requested mask length.
// The mask provided must be a smaller-integer than the current mask. If set
// to 0 Supernet will return the next-largest network
//
// Examples:
// Net{192.168.1.0/24}.Supernet(0) -> Net{192.168.0.0/23}
// Net{192.168.1.0/24}.Supernet(22) -> Net{Net{192.168.0.0/22}
func (n Net4) Supernet(masklen int) (Net4, error) {
ones, all := n.Mask().Size()
if ones < masklen {
return Net4{}, ErrBadMaskLength
}
if masklen == 0 {
masklen = ones - 1
}
mask := net.CIDRMask(masklen, all)
ng := net.IPNet{IP: n.IP().Mask(mask), Mask: mask}
return Net4{ng, n.is4in6}, nil
}
// Version returns the version of IP for the enclosed netblock, 4 in this case
func (n Net4) Version() int {
return IP4Version
}
// Wildcard will return the wildcard mask for a given netmask
func (n Net4) Wildcard() net.IPMask {
wc := make([]byte, len(n.Mask()))
for pos, b := range n.Mask() {
wc[pos] = 0xff - b
}
return wc
}
// finalAddress returns the last address in the network. It is private
// because both LastAddress() and BroadcastAddress() rely on it, and both use
// it differently. It returns the last address in the block as well as the
// number of masked bits as an int.
func (n Net4) finalAddress() (net.IP, int) {
xip := make([]byte, len(n.IP()))
ones, _ := n.Mask().Size()
// apply wildcard to network, byte by byte
wc := n.Wildcard()
for pos := range n.IP() {
xip[pos] = n.IP()[pos] + wc[pos]
}
return xip, ones
}