-
Notifications
You must be signed in to change notification settings - Fork 0
/
extnetip.go
151 lines (123 loc) · 3.65 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
146
147
148
149
150
151
// extnetip is an extension to net/netip.
//
// Some missing math functions are added to the closed
// private internals of netip using unsafe.
//
// No further types are defined, only helper functions on
// existing net/netip types.
//
// With these tiny extensions, third party IP-Range libraries,
// based on the stdlib net/netip, are now possible without
// further bytes/bits fumbling.
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
exhib := peek(p.Addr())
z := exhib.z
bits := p.Bits()
if z == z4 {
bits += 96
}
mask := mask6(bits)
base128 := exhib.addr.and(mask)
last128 := base128.or(mask.not())
// convert back to netip.Addr
first = back(exhibType{base128, z})
last = back(exhibType{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
exhibBase := peek(first)
exhibLast := peek(last)
// IP versions differ?
if exhibBase.z != exhibLast.z {
return
}
bits, ok := exhibBase.addr.prefixOK(exhibLast.addr)
if !ok {
return
}
if exhibBase.z == z4 {
bits -= 96
}
// convert back to netip
return netip.PrefixFrom(back(exhibBase), 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 AppendPrefixes for a version that
// uses memory you provide.
func Prefixes(first, last netip.Addr) []netip.Prefix {
return AppendPrefixes(nil, first, last)
}
// AppendPrefixes is an append version of Prefixes. It appends
// the netip.Prefix entries to dst that covers the IP range from first to last.
func AppendPrefixes(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
exhibFirst := peek(first)
exhibLast := peek(last)
// different IP versions
if exhibFirst.z != exhibLast.z {
return nil
}
// no recursion, use an iterative algo with stack
var stack []exhibType
// push, params are the starting point
stack = append(stack, exhibFirst, exhibLast)
for len(stack) > 0 {
// pop two addresses
exhibLast := stack[len(stack)-1]
exhibFirst := stack[len(stack)-2]
stack = stack[:len(stack)-2]
// are base-last already representing a prefix?
bits, ok := exhibFirst.addr.prefixOK(exhibLast.addr)
if ok {
if exhibFirst.z == z4 {
bits -= 96
}
// convert back to netip
pfx := netip.PrefixFrom(back(exhibFirst), bits)
dst = append(dst, pfx)
continue
}
// Otherwise split the range, make two halves and push it on the stack
mask := mask6(bits + 1)
// make middle last, set hostbits
exhibMidOne := exhibType{exhibFirst.addr.or(mask.not()), exhibFirst.z}
// make middle base, clear hostbits
exhibMidTwo := exhibType{exhibLast.addr.and(mask), exhibFirst.z}
// push both halves (in reverse order, prefixes are then sorted)
stack = append(stack, exhibMidTwo, exhibLast)
stack = append(stack, exhibFirst, exhibMidOne)
}
return dst
}