【问题标题】:Given a set of IP address ranges, which all the ranges which contain the given IP address给定一组 IP 地址范围,其中包含给定 IP 地址的所有范围
【发布时间】:2015-04-08 20:38:58
【问题描述】:

我有一组 IP 地址范围,它们对应一些条件。

给定一个输入 IP 地址,我想知道包含该输入 IP 地址的所有 IP 范围集。

到目前为止,段树似乎是合适的 https://github.com/toberndo/go-stree 但它只需要整数,而我需要 IPv4 和 IPv6 范围。

对适合此类问题的算法有什么建议吗?可能是Go/GoLang check IP address in range的扩展

【问题讨论】:

  • IP 范围是如何提供的?作为网络 IP + 掩码?
  • 现在我在想起始 IP 地址:结束 IP 地址
  • 您可以使用转换为数字的有序 IP,看看这个问题stackoverflow.com/questions/26296761/…
  • 链接指定它只是查找数字是否存在于范围内。我想要所有有这个数字的范围。段树似乎是正确的,但它只处理整数,而 IPv6 比 int 大得多
  • 嗯,我明白了,无论如何,对范围进行排序会加快搜索速度,这或多或少是 stree 所做的。也许您可以调整这个 stree 库以使用 bigint golang.org/pkg/math/big 或字节数组。无论如何,是的,你是对的,stree 看起来是这样做的方式。

标签: go


【解决方案1】:

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]

【讨论】:

    猜你喜欢
    • 2018-10-08
    • 2011-08-21
    • 2016-04-09
    • 2020-12-21
    • 2012-05-12
    • 2011-12-01
    • 2017-05-28
    • 2010-09-10
    • 2022-07-22
    相关资源
    最近更新 更多