forked from oauth2-proxy/oauth2-proxy
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathnet_set.go
112 lines (96 loc) · 2.83 KB
/
net_set.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
package ip
import (
"fmt"
"net"
)
// Fast lookup table for intersection of a single IP address within a collection of CIDR networks.
//
// Supports 4-byte (IPv4) and 16-byte (IPv6) networks.
//
// Provides O(1) best-case, O(log(n)) worst-case performance.
// In practice netmasks included will generally only be of standard lengths:
// - /8, /16, /24, and /32 for IPv4
// - /64 and /128 for IPv6.
// As a result, typical lookup times will lean closer to best-case rather than worst-case even when most of the internet
// is included.
type NetSet struct {
ip4NetMaps []ipNetMap
ip6NetMaps []ipNetMap
}
// Create a new NetSet with all of the provided networks.
func NewNetSet() *NetSet {
return &NetSet{
ip4NetMaps: make([]ipNetMap, 0),
ip6NetMaps: make([]ipNetMap, 0),
}
}
// Check if `ip` is in the set, true if within the set otherwise false.
func (w *NetSet) Has(ip net.IP) bool {
netMaps := w.getNetMaps(ip)
// Check all ipNetMaps for intersection with `ip`.
for _, netMap := range *netMaps {
if netMap.has(ip) {
return true
}
}
return false
}
// Add an CIDR network to the set.
func (w *NetSet) AddIPNet(ipNet net.IPNet) {
netMaps := w.getNetMaps(ipNet.IP)
// Determine the size / number of ones in the CIDR network mask.
ones, _ := ipNet.Mask.Size()
var netMap *ipNetMap
// Search for the ipNetMap containing networks with the same number of ones.
for i := 0; len(*netMaps) > i; i++ {
if netMapOnes, _ := (*netMaps)[i].mask.Size(); netMapOnes == ones {
netMap = &(*netMaps)[i]
break
}
}
// Create a new ipNetMap if none with this number of ones have been created yet.
if netMap == nil {
netMap = &ipNetMap{
mask: ipNet.Mask,
ips: make(map[string]bool),
}
*netMaps = append(*netMaps, *netMap)
// Recurse once now that there exists an netMap.
w.AddIPNet(ipNet)
return
}
// Add the IP to the ipNetMap.
netMap.ips[ipNet.IP.String()] = true
}
// Get the appropriate array of networks for the given IP version.
func (w *NetSet) getNetMaps(ip net.IP) (netMaps *[]ipNetMap) {
switch {
case ip.To4() != nil:
netMaps = &w.ip4NetMaps
case ip.To16() != nil:
netMaps = &w.ip6NetMaps
default:
panic(fmt.Sprintf("IP (%s) is neither 4-byte nor 16-byte?", ip.String()))
}
return netMaps
}
// Hash-set of CIDR networks with the same mask size.
type ipNetMap struct {
mask net.IPMask
ips map[string]bool
}
// Check if the IP is in any of the CIDR networks contained in this map.
func (m ipNetMap) has(ip net.IP) bool {
// Apply the mask to the IP to remove any irrelevant bits in the IP.
ipMasked := ip.Mask(m.mask)
if ipMasked == nil {
panic(fmt.Sprintf(
"Mismatch in net.IPMask and net.IP protocol version, cannot apply mask %s to %s",
m.mask.String(), ip.String()))
}
// Check if the masked IP is the same as any of the networks.
if _, ok := m.ips[ipMasked.String()]; ok {
return true
}
return false
}