-
Notifications
You must be signed in to change notification settings - Fork 0
/
extnetip.go
145 lines (120 loc) · 3.6 KB
/
extnetip.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
// Package extnetip is an extension to net/netip.
//
// No additional types are defined, only required auxiliary
// functions for some existing net/netip types are provided.
//
// With these small extensions, third-party IP range libraries
// based on stdlib net/netip are now possible without frequent
// conversion to/from bytes.
package extnetip
import "net/netip"
// Range returns the inclusive range of IP addresses that p covers.
//
// If p is invalid, Range returns the zero values.
func Range(p netip.Prefix) (first, last netip.Addr) {
if !p.IsValid() {
return
}
// peek the internals, do math in uint128
pa := peek(p.Addr())
z := pa.z
bits := p.Bits()
if z == z4 {
bits += 96
}
mask := mask6(bits)
first128 := pa.ip.and(mask)
last128 := first128.or(mask.not())
// convert back to netip.Addr
first = back(addr{first128, z})
last = back(addr{last128, z})
return
}
// Prefix returns the netip.Prefix from first to last and ok=true,
// if it can be presented exactly as such.
//
// If first or last are not valid, in the wrong order or not exactly
// equal to one prefix, ok is false.
func Prefix(first, last netip.Addr) (prefix netip.Prefix, ok bool) {
if !(first.IsValid() && last.IsValid()) {
return
}
if last.Less(first) {
return
}
// peek the internals, do math in uint128
pFirst := peek(first)
pLast := peek(last)
// IP versions differ?
if pFirst.z != pLast.z {
return
}
// do math in uint128
bits, ok := pFirst.ip.prefixOK(pLast.ip)
if !ok {
return
}
if pFirst.z == z4 {
bits -= 96
}
// make prefix
return netip.PrefixFrom(first, bits), ok
}
// Prefixes returns the set of netip.Prefix entries that covers the
// IP range from first to last.
//
// If first or last are invalid, in the wrong order, or if they're of different
// address families, then Prefixes returns nil.
//
// Prefixes necessarily allocates. See PrefixesAppend for a version that
// uses memory you provide.
func Prefixes(first, last netip.Addr) []netip.Prefix {
return PrefixesAppend(nil, first, last)
}
// PrefixesAppend is an append version of Prefixes. It appends
// the netip.Prefix entries to dst that covers the IP range from first to last.
func PrefixesAppend(dst []netip.Prefix, first, last netip.Addr) []netip.Prefix {
if !(first.IsValid() && last.IsValid()) {
return nil
}
if last.Less(first) {
return nil
}
// peek the internals, do math in uint128
pFirst := peek(first)
pLast := peek(last)
// different IP versions
if pFirst.z != pLast.z {
return nil
}
return prefixesAppendRec(dst, pFirst, pLast)
}
// append prefix if (first, last) represents a whole CIDR, like 10.0.0.0/8
// (first being 10.0.0.0 and last being 10.255.255.255)
//
// Otherwise recursively do both halves.
//
// Recursion is here faster than an iterative algo, no bounds checking and no heap escape and
// btw. the recursion level is max. 254 deep, for IP range: ::1-ffff:ffff:ffff:ffff:ffff:ffff:ffff:fffe
func prefixesAppendRec(dst []netip.Prefix, first, last addr) []netip.Prefix {
// are first-last already representing a prefix?
bits, ok := first.ip.prefixOK(last.ip)
if ok {
if first.z == z4 {
bits -= 96
}
// convert back to netip
pfx := netip.PrefixFrom(back(first), bits)
return append(dst, pfx)
}
// otherwise split the range, make two halves and do both halves recursively
mask := mask6(bits + 1)
// make middle last, set hostbits
midOne := addr{first.ip.or(mask.not()), first.z}
// make middle next, clear hostbits
midTwo := addr{last.ip.and(mask), first.z}
// ... do both halves recursively
dst = prefixesAppendRec(dst, first, midOne)
dst = prefixesAppendRec(dst, midTwo, last)
return dst
}