golang快速IP到CIDR查找插件库cidranger的使用

golang快速IP到CIDR查找插件库cidranger的使用

cidranger是一个使用trie数据结构实现的快速IP到CIDR块查找的Golang库,灵感来自Linux IPv4路由查找。典型使用场景包括检测IP地址是否来自发布的云提供商CIDR块(例如52.95.110.1包含在AWS Route53发布的CIDR 52.95.110.0/24中)、IP路由规则等。

可视化示例

这是一个存储CIDR块128.0.0.0/2192.0.0.0/2200.0.0.0/5的trie可视化,路径上的0/1数字表示IP地址在特定位位置上的位值:

未压缩路径的trie可视化

这是相同CIDR块使用路径压缩后的trie可视化,提高了查找速度和内存效率:

路径压缩的trie可视化

快速开始

导入库

import (
  "net"

  "github.com/yl2chen/cidranger"
)

创建新的ranger

ranger := NewPCTrieRanger()  // 使用路径压缩前缀trie实现

插入CIDR块

_, network1, _ := net.ParseCIDR("192.168.1.0/24")
_, network2, _ := net.ParseCIDR("128.168.1.0/24")
ranger.Insert(NewBasicRangerEntry(*network1))
ranger.Insert(NewBasicRangerEntry(*network2))

自定义RangerEntry

要附加任何额外值到条目,只需创建一个存储所需值的自定义结构体并实现RangerEntry接口:

type RangerEntry interface {
	Network() net.IPNet
}

前缀trie可视化

构造的前缀trie可以可视化为:

0.0.0.0/0 (target_pos:31:has_entry:false)
| 1--> 128.0.0.0/1 (target_pos:30:has_entry:false)
| | 0--> 128.168.1.0/24 (target_pos:7:has_entry:true)
| | 1--> 192.168.1.0/24 (target_pos:7:has_entry:true)

测试IP是否包含在ranger中

contains, err = ranger.Contains(net.ParseIP("128.168.1.0")) // 返回true, nil
contains, err = ranger.Contains(net.ParseIP("192.168.2.0")) // 返回false, nil

获取IP包含的所有网络

containingNetworks, err = ranger.ContainingNetworks(net.ParseIP("128.168.1.0"))

获取ranger中的所有网络

entries, err := ranger.CoveredNetworks(*AllIPv4) // 获取IPv4网络
entries, err := ranger.CoveredNetworks(*AllIPv6) // 获取IPv6网络

完整示例代码

package main

import (
	"fmt"
	"net"

	"github.com/yl2chen/cidranger"
)

func main() {
	// 创建新的ranger
	ranger := cidranger.NewPCTrieRanger()

	// 插入CIDR块
	_, network1, _ := net.ParseCIDR("192.168.1.0/24")
	_, network2, _ := net.ParseCIDR("128.168.1.0/24")
	ranger.Insert(cidranger.NewBasicRangerEntry(*network1))
	ranger.Insert(cidranger.NewBasicRangerEntry(*network2))

	// 测试IP是否在CIDR块中
	ip := net.ParseIP("192.168.1.1")
	contains, err := ranger.Contains(ip)
	if err != nil {
		fmt.Printf("Error checking IP: %v\n", err)
		return
	}
	fmt.Printf("IP %s is in ranger: %t\n", ip, contains)

	// 获取包含IP的所有网络
	networks, err := ranger.ContainingNetworks(ip)
	if err != nil {
		fmt.Printf("Error getting containing networks: %v\n", err)
		return
	}
	for _, network := range networks {
		fmt.Printf("IP %s is in network: %s\n", ip, network.Network())
	}

	// 获取所有IPv4网络
	allIPv4 := net.IPNet{IP: net.IPv4zero, Mask: net.CIDRMask(0, 32)}
	entries, err := ranger.CoveredNetworks(allIPv4)
	if err != nil {
		fmt.Printf("Error getting all networks: %v\n", err)
		return
	}
	fmt.Println("All IPv4 networks in ranger:")
	for _, entry := range entries {
		fmt.Println(entry.Network())
	}
}

性能基准

比较使用PC trie与暴力实现在IPv4/IPv6上的命中/未命中情况,Ranger使用发布的AWS IP范围初始化(889个IPv4 CIDR块和360个IPv6):

// IPv4查找命中场景
BenchmarkPCTrieHitIPv4UsingAWSRanges-4         	 5000000	       353   ns/op
BenchmarkBruteRangerHitIPv4UsingAWSRanges-4    	  100000	     13719   ns/op

// IPv6查找命中场景,由于AWS数据集中IPv6 CIDR块较少,构造的trie路径分割和深度更小,因此比IPv4更快
BenchmarkPCTrieHitIPv6UsingAWSRanges-4         	10000000	       143   ns/op
BenchmarkBruteRangerHitIPv6UsingAWSRanges-4    	  300000	      5178   ns/op

// IPv4查找未命中场景
BenchmarkPCTrieMissIPv4UsingAWSRanges-4        	20000000	        96.5 ns/op
BenchmarkBruteRangerMissIPv4UsingAWSRanges-4   	   50000	     24781   ns/op

// IPv6查找未命中场景
BenchmarkPCTrieHMissIPv6UsingAWSRanges-4       	10000000	       115   ns/op
BenchmarkBruteRangerMissIPv6UsingAWSRanges-4   	  100000	     10824   ns/op

IPv6 trie示例

::/0 (target_pos:127:has_entry:false)
| 0--> 2400::/14 (target_pos:113:has_entry:false)
| | 0--> 2400:6400::/22 (target_pos:105:has_entry:false)
| | | 0--> 2400:6500::/32 (target_pos:95:has_entry:false)
| | | | 0--> 2400:6500::/39 (target_pos:88:has_entry:false)
| | | | | 0--> 2400:6500:0:7000::/53 (target_pos:74:has_entry:false)
| | | | | | 0--> 2400:6500:0:7000::/54 (target_pos:73:has_entry:false)
| | | | | | | 0--> 2400:6500:0:7000::/55 (target_pos:72:has_entry:false)
| | | | | | | | 0--> 2400:6500:0:7000::/56 (target_pos:71:has_entry:true)
| | | | | | | | 1--> 2400:6500:0:7100::/56 (target_pos:71:has_entry:true)
| | | | | | | 1--> 2400:6500:0:7200::/56 (target_pos:71:has_entry:true)
| | | | | | 1--> 2400:6500:0:7400::/55 (target_pos:72:has_entry:false)
| | | | | | | 0--> 2400:6500:0:7400::/56 (target_pos:71:has_entry:true)
| | | | | | | 1--> 2400:6500:0:7500::/56 (target_pos:71:has_entry:true)
| | | | | 1--> 2400:6500:100:7000::/54 (target_pos:73:has_entry:false)
| | | | | | 0--> 2400:6500:100:7100::/56 (target_pos:71:has_entry:true)
| | | | | | 1--> 2400:6500:100:7200::/56 (target_pos:71:has_entry:true)
| | | | 1--> 2400:6500:ff00::/64 (target_pos:63:has_entry:true)
| | | 1--> 2400:6700:ff00::/64 (target_pos:63:has_entry:true)
| | 1--> 2403:b300:ff00::/64 (target_pos:63:has_entry:true)

更多关于golang快速IP到CIDR查找插件库cidranger的使用的实战教程也可以访问 https://www.itying.com/category-94-b0.html

1 回复

更多关于golang快速IP到CIDR查找插件库cidranger的使用的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


Golang快速IP到CIDR查找插件库cidranger使用指南

cidranger是一个高效的Golang库,用于快速进行IP地址到CIDR块的查找操作。它特别适合需要处理大量CIDR范围并进行快速IP匹配的场景,如防火墙规则检查、地理位置查找等。

安装

go get github.com/yl2chen/cidranger

基本使用

1. 创建Ranger实例

package main

import (
	"fmt"
	"net"
	
	"github.com/yl2chen/cidranger"
)

func main() {
	// 创建一个新的PrefixTrie ranger (支持IPv4和IPv6)
	ranger := cidranger.NewPCTrieRanger()
	
	// 或者只支持IPv4
	// ranger := cidranger.NewPCTrieRanger(cidranger.IPv4)
	
	// 或者只支持IPv6
	// ranger := cidranger.NewPCTrieRanger(cidranger.IPv6)
}

2. 插入CIDR网络

_, network1, _ := net.ParseCIDR("192.168.1.0/24")
_, network2, _ := net.ParseCIDR("10.0.0.0/8")
_, network3, _ := net.ParseCIDR("2001:db8::/32")

// 插入CIDR网络,可以附加任意数据
ranger.Insert(cidranger.NewBasicRangerEntry(*network1))
ranger.Insert(cidranger.NewBasicRangerEntry(*network2))
ranger.Insert(cidranger.NewBasicRangerEntry(*network3))

3. 查找IP是否在CIDR范围内

ip := net.ParseIP("192.168.1.10")

// 检查IP是否在任何CIDR范围内
contains, err := ranger.Contains(ip)
if err != nil {
	panic(err)
}
fmt.Printf("Contains IP: %v\n", contains) // true

// 获取包含该IP的所有CIDR条目
entries, err := ranger.ContainingNetworks(ip)
if err != nil {
	panic(err)
}
for _, entry := range entries {
	fmt.Printf("IP is contained in network: %s\n", entry.Network())
}

4. 获取覆盖某个IP的所有CIDR网络

ip := net.ParseIP("192.168.1.15")
networks, err := ranger.ContainingNetworks(ip)
if err != nil {
	panic(err)
}

for _, network := range networks {
	fmt.Printf("IP is in network: %s\n", network.Network())
}

高级功能

1. 自定义条目类型

可以创建自定义类型来存储额外的CIDR信息:

type customRangerEntry struct {
	net.IPNet
	Data string
}

func (e *customRangerEntry) Network() net.IPNet {
	return e.IPNet
}

// 使用自定义条目
_, network, _ := net.ParseCIDR("172.16.0.0/16")
customEntry := &customRangerEntry{
	IPNet: *network,
	Data:  "This is a private network",
}
ranger.Insert(customEntry)

// 查找时获取自定义数据
ip := net.ParseIP("172.16.1.1")
entries, _ := ranger.ContainingNetworks(ip)
for _, entry := range entries {
	if custom, ok := entry.(*customRangerEntry); ok {
		fmt.Printf("Network: %s, Data: %s\n", custom.Network(), custom.Data)
	}
}

2. 删除CIDR网络

_, network, _ := net.ParseCIDR("192.168.1.0/24")
entry := cidranger.NewBasicRangerEntry(*network)
ranger.Insert(entry)

// 删除网络
deleted, err := ranger.Remove(*network)
if err != nil {
	panic(err)
}
fmt.Printf("Deleted: %v\n", deleted) // true

3. 获取所有CIDR网络

entries, err := ranger.CoveredNetworks(*net.IPv4zero, 0)
if err != nil {
	panic(err)
}

for _, entry := range entries {
	fmt.Println(entry.Network())
}

性能考虑

cidranger使用前缀压缩的Trie数据结构(PCTrie),具有以下特点:

  1. 查找时间复杂度为O(k),其中k是IP地址的位数(IPv4为32,IPv6为128)
  2. 内存效率高,共享相同前缀的CIDR块会共享存储
  3. 支持快速插入和删除操作

对于大多数应用场景,cidranger提供了足够高的性能,可以处理数十万甚至数百万个CIDR块的快速查找。

完整示例

package main

import (
	"fmt"
	"log"
	"net"
	
	"github.com/yl2chen/cidranger"
)

func main() {
	// 创建ranger
	ranger := cidranger.NewPCTrieRanger()
	
	// 插入一些CIDR网络
	networks := []string{
		"10.0.0.0/8",
		"192.168.1.0/24",
		"172.16.0.0/12",
		"2001:db8::/32",
	}
	
	for _, n := range networks {
		_, network, err := net.ParseCIDR(n)
		if err != nil {
			log.Fatal(err)
		}
		ranger.Insert(cidranger.NewBasicRangerEntry(*network))
	}
	
	// 测试IP
	ips := []string{
		"10.1.2.3",
		"192.168.1.15",
		"172.16.100.200",
		"2001:db8::1",
		"8.8.8.8",
	}
	
	for _, ipStr := range ips {
		ip := net.ParseIP(ipStr)
		contains, err := ranger.Contains(ip)
		if err != nil {
			log.Fatal(err)
		}
		
		fmt.Printf("IP %s is in any network: %v\n", ipStr, contains)
		
		if contains {
			entries, err := ranger.ContainingNetworks(ip)
			if err != nil {
				log.Fatal(err)
			}
			for _, entry := range entries {
				fmt.Printf("  - Contained in: %s\n", entry.Network())
			}
		}
	}
}

cidranger是一个简单而强大的库,可以轻松集成到需要IP-CIDR查找功能的Golang应用中。

回到顶部