IPAddress Go library 可以通过多种不同的方式使用多态代码来实现这一点,同时使用 IPv4 和 IPv6 地址。 Repository here。免责声明:我是项目经理。
我将展示两种方法,使用示例 IPv4 地址。无论哪种方式都没有太多代码。
这里有 28 个随机样本 IPv4 范围和一个地址,用于检查它所在的范围:
ipRangeStrs := [][2]string{
{"26.154.36.255", "82.200.127.52"},
{"26.154.36.255", "192.250.200.250"},
{"37.99.32.76", "62.192.232.40"},
{"37.99.32.76", "141.101.231.182"},
{"37.99.32.144", "185.148.82.122"},
{"37.99.32.144", "194.59.250.237"},
{"46.56.236.163", "186.169.171.32"},
{"46.56.236.163", "195.231.4.217"},
{"62.109.3.240", "91.87.64.89"},
{"62.109.11.226", "62.192.232.40"},
{"82.200.127.52", "103.192.63.244"},
{"88.250.248.235", "188.18.253.203"},
{"88.250.248.235", "212.74.202.30"},
{"91.87.64.89", "209.97.191.87"},
{"103.192.63.244", "212.253.90.11"},
{"109.197.13.33", "109.197.13.149"},
{"109.197.13.33", "141.101.231.203"},
{"109.197.13.33", "195.231.4.217"},
{"109.197.13.33", "212.253.90.11"},
{"109.197.13.149", "212.253.90.11"},
{"122.114.118.63", "186.169.171.32"},
{"122.114.118.63", "188.18.253.203"},
{"141.101.231.182", "185.148.82.122"},
{"141.101.231.203", "212.74.202.30"},
{"192.250.200.250", "212.253.90.111"},
{"194.59.250.237", "212.253.90.11"},
{"195.231.4.132", "209.97.191.87"},
{"195.231.4.132", "212.253.90.111"},
}
// candidate to check
candidate := "88.255.1.1"
一种方法是使用顺序范围,遍历每个范围。这是 O(n)。
var ipRanges []*ipaddr.IPAddressSeqRange
for _, strs := range ipRangeStrs {
addr1, addr2 := ipaddr.NewIPAddressString(strs[0]).GetAddress(),
ipaddr.NewIPAddressString(strs[1]).GetAddress()
rng := addr1.SpanWithRange(addr2)
ipRanges = append(ipRanges, rng)
}
addrToCheck := ipaddr.NewIPAddressString(candidate).GetAddress()
counter := 0
for _, rng := range ipRanges {
if rng.Contains(addrToCheck) {
counter++
fmt.Printf("range %d %v contains %v\n", counter, rng, addrToCheck)
}
}
输出:
range 1 26.154.36.255 -> 192.250.200.250 contains 88.255.1.1
range 2 37.99.32.76 -> 141.101.231.182 contains 88.255.1.1
range 3 37.99.32.144 -> 185.148.82.122 contains 88.255.1.1
range 4 37.99.32.144 -> 194.59.250.237 contains 88.255.1.1
range 5 46.56.236.163 -> 186.169.171.32 contains 88.255.1.1
range 6 46.56.236.163 -> 195.231.4.217 contains 88.255.1.1
range 7 62.109.3.240 -> 91.87.64.89 contains 88.255.1.1
range 8 82.200.127.52 -> 103.192.63.244 contains 88.255.1.1
range 9 88.250.248.235 -> 188.18.253.203 contains 88.255.1.1
range 10 88.250.248.235 -> 212.74.202.30 contains 88.255.1.1
现在换一种方式。
无论使用多少范围,对于每个搜索候选者,以下算法都是恒定时间操作 O(1)。构建 trie 是 O(n)。因此,这最适合您可能进行多次搜索以查找包含范围且范围列表变化不大的情况。
将每个范围转换为可以添加到地址树的跨 CIDR 前缀块。
将每个 trie 条目映射到原始范围。
在 trie 中查找包含块。这是一个恒定时间的操作。无论使用多少范围,它最多需要 32 次比较,因为 IPv4 地址位大小为 32。
平均而言,它将进行 log(n) 比较,其中 n 是 trie 中的项目数。所以在这个例子中,平均是 9 次比较(trie 大小为 421),少于前面代码所需的 28 次。
我还要注意,这段代码是多态的,它也适用于 IPv6 地址。
ipRanges 切片和 addrToCheck 地址继承自上一个示例。
trie := ipaddr.AssociativeAddressTrie{}
for _, rng := range ipRanges {
blocks := rng.SpanWithPrefixBlocks()
for _, block := range blocks {
trie.Remap(block.ToAddressBase(),
func(existing ipaddr.NodeValue) ipaddr.NodeValue {
// make the node point to the original range(s)
if existing == nil {
return []*ipaddr.IPAddressSeqRange{rng}
} else {
return append(existing.([]*ipaddr.IPAddressSeqRange), rng)
}
})
}
}
fmt.Printf("The trie has %d blocks\n", trie.Size())
//print the trie with: fmt.Println(trie)
trieNode := trie.ElementsContaining(addrToCheck.ToAddressBase())
// ElementsContaining returns a linked list in trie form of containing blocks.
// The mapped values of each in the list are the containing ranges.
var result []*ipaddr.IPAddressSeqRange
for trieNode != nil {
ranges := trieNode.GetValue().([]*ipaddr.IPAddressSeqRange)
result = append(result, ranges...)
if trieNode.GetUpperSubNode() != nil {
trieNode = trieNode.GetUpperSubNode()
} else {
trieNode = trieNode.GetLowerSubNode()
}
}
fmt.Printf("The %d containing ranges are: %v", len(result), result)
输出:
The trie has 421 blocks
The 10 containing ranges are: [26.154.36.255 -> 192.250.200.250 37.99.32.76 -> 141.101.231.182 37.99.32.144 -> 185.148.82.122 37.99.32.144 -> 194.59.250.237 46.56.236.163 -> 186.169.171.32 46.56.236.163 -> 195.231.4.217 82.200.127.52 -> 103.192.63.244 62.109.3.240 -> 91.87.64.89 88.250.248.235 -> 188.18.253.203 88.250.248.235 -> 212.74.202.30]