本文提供一套Golang编写的IP工具库,包含:IP列表合并为网段列表、IPv6书写格式转化、IP排序、随机生成IP序列等功能。
1 IP工具库介绍
工具库目录如图:
本节将介绍该工具库的功能。
1.1 IP排序
将IP列表排序。示例:
func main() {
var ips []net.IP
for i := 0; i < 3; i++ {
ips = append(ips, ip_utils.RandomIPv4())
ips = append(ips, ip_utils.RandomIPv6())
}
fmt.Println(ips)
ip_utils.SortIP(ips)
fmt.Println(ips)
}
示例中随机生成了6个IPv4/IPv6,结果:
[53.77.76.9 c82b:6d6e:2139:c99d:c431:5bb5:9c36:62c3 151.170.54.188 57d8:f5e5:bdf7:f80d:755a:4952:2d87:92bd 157.133.15.97 582:23cd:cea9:6787:238b:3c4d:727:81a0]
[53.77.76.9 151.170.54.188 157.133.15.97 582:23cd:cea9:6787:238b:3c4d:727:81a0 57d8:f5e5:bdf7:f80d:755a:4952:2d87:92bd c82b:6d6e:2139:c99d:c431:5bb5:9c36:62c3]
1.2 合并网段
将IP列表合并为网段列表。其中IP列表为多段连续的IP,示例:
func main() {
var ips []net.IP
for i := 0; i < 2; i++ {
ips = append(ips, ip_utils.RandomIPv4Seq(rand.Intn(16)+1)...)
ips = append(ips, ip_utils.RandomIPv6Seq(rand.Intn(16)+1)...)
}
ip_utils.SortIP(ips)
fmt.Println(ips)
ipMerge := ip_utils.NewIPMerge(ips)
res, ok := ipMerge.Next()
for ok {
fmt.Println(res.Cidr, res.Count)
res, ok = ipMerge.Next()
}
}
示例中随机生成了IP列表(包含4段连续的IPv4/IPv6),然后使用IPMerge.Next()解析出网段。
结果:
[1.34.198.104 1.34.198.105 186.149.200.62 186.149.200.63 186.149.200.64 186.149.200.65 186.149.200.66 186.149.200.67 186.149.200.68 186.149.200.69 97dd:5e84:a852:310:b56c:57ac:4d30:2138 97dd:5e84:a852:310:b56c:57ac:4d30:2139 97dd:5e84:a852:310:b56c:57ac:4d30:213a 97dd:5e84:a852:310:b56c:57ac:4d30:213b 97dd:5e84:a852:310:b56c:57ac:4d30:213c 97dd:5e84:a852:310:b56c:57ac:4d30:213d 97dd:5e84:a852:310:b56c:57ac:4d30:213e 97dd:5e84:a852:310:b56c:57ac:4d30:213f 97dd:5e84:a852:310:b56c:57ac:4d30:2140 97dd:5e84:a852:310:b56c:57ac:4d30:2141 97dd:5e84:a852:310:b56c:57ac:4d30:2142 97dd:5e84:a852:310:b56c:57ac:4d30:2143 dab5:f60c:f254:7e7f:88bf:3d85:dde1:c898 dab5:f60c:f254:7e7f:88bf:3d85:dde1:c899 dab5:f60c:f254:7e7f:88bf:3d85:dde1:c89a dab5:f60c:f254:7e7f:88bf:3d85:dde1:c89b dab5:f60c:f254:7e7f:88bf:3d85:dde1:c89c dab5:f60c:f254:7e7f:88bf:3d85:dde1:c89d dab5:f60c:f254:7e7f:88bf:3d85:dde1:c89e dab5:f60c:f254:7e7f:88bf:3d85:dde1:c89f dab5:f60c:f254:7e7f:88bf:3d85:dde1:c8a0 dab5:f60c:f254:7e7f:88bf:3d85:dde1:c8a1 dab5:f60c:f254:7e7f:88bf:3d85:dde1:c8a2 dab5:f60c:f254:7e7f:88bf:3d85:dde1:c8a3 dab5:f60c:f254:7e7f:88bf:3d85:dde1:c8a4 dab5:f60c:f254:7e7f:88bf:3d85:dde1:c8a5 dab5:f60c:f254:7e7f:88bf:3d85:dde1:c8a6 dab5:f60c:f254:7e7f:88bf:3d85:dde1:c8a7]
1.34.198.104/31 2
186.149.200.62/31 2
186.149.200.64/30 4
186.149.200.68/31 2
97dd:5e84:a852:310:b56c:57ac:4d30:2138/125 8
97dd:5e84:a852:310:b56c:57ac:4d30:2140/126 4
dab5:f60c:f254:7e7f:88bf:3d85:dde1:c898/125 8
dab5:f60c:f254:7e7f:88bf:3d85:dde1:c8a0/125 8
1.3 IPv6书写格式转化
IPv6根据缩写规则可以有多种写法,如:全写、省略前导零、最简格式。示例:
func main() {
ips := []string{
"0000:ff06:0000:0000:0000:0000:0000:0000",
"0000:0000:0000:0000:0000:0000:0000:0000",
"ff02:0000:0000:0000:0000:0001:ff00:0001",
"fd82:139b:8752:0000:246e:0031:888c:36db",
"fd82:0000:8752:0000:0000:0031:888c:36db",
"fd82:0000:0000:8752:0000:0031:888c:36db",
}
for _, ip := range ips {
fmt.Printf("%39s %39s %39s\n", ip, ip_utils.FormatZero(ip), ip_utils.Format(ip))
}
}
结果:
0000:ff06:0000:0000:0000:0000:0000:0000 0:ff06:0:0:0:0:0:0 0:ff06::
0000:0000:0000:0000:0000:0000:0000:0000 0:0:0:0:0:0:0:0 ::
ff02:0000:0000:0000:0000:0001:ff00:0001 ff02:0:0:0:0:1:ff00:1 ff02::1:ff00:1
fd82:139b:8752:0000:246e:0031:888c:36db fd82:139b:8752:0:246e:31:888c:36db fd82:139b:8752:0:246e:31:888c:36db
fd82:0000:8752:0000:0000:0031:888c:36db fd82:0:8752:0:0:31:888c:36db fd82:0:8752::31:888c:36db
fd82:0000:0000:8752:0000:0031:888c:36db fd82:0:0:8752:0:31:888c:36db fd82::8752:0:31:888c:36db
2 源码
直接上代码。
ip_common.go
package ip_utils
import (
"encoding/hex"
"net"
"strconv"
)
func BitLen(ip net.IP) int {
if ip.To4() != nil {
return 32
}
return 128
}
func Copy(ip net.IP) net.IP {
dup := make(net.IP, len(ip))
copy(dup, ip)
return dup
}
func GenMask(ones int, bits int) string {
mask, _ := hex.DecodeString(net.CIDRMask(ones, bits).String())
return net.IP(mask).String()
}
func GenCidr(ip net.IP, ones int) string {
return ip.String() + "/" + strconv.Itoa(ones)
}
ip_format.go
package ip_utils
import (
"net"
)
func IsIPv4(ip string) bool {
if net.ParseIP(ip).To4() != nil {
return true
}
return false
}
// Format ipv6最简格式,示例:240e:f7:c000:103:13::f4
func Format(ip string) string {
netIp := net.ParseIP(ip)
if netIp == nil {
return ip
}
return netIp.String()
}
// FormatZero ipv6省略前导零格式,示例:240e:f7:c000:103:13:0:0:f4
func FormatZero(ip string) string {
p := net.ParseIP(ip)
if p == nil || p.To4() != nil || len(p) != net.IPv6len {
return ip
}
const maxLen = len("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff")
b := make([]byte, 0, maxLen)
for i := 0; i < net.IPv6len; i += 2 {
if i > 0 {
b = append(b, ':')
}
b = appendHex(b, (uint32(p[i])<<8)|uint32(p[i+1]))
}
return string(b)
}
const hexDigit = "0123456789abcdef"
// Convert i to a hexadecimal string. Leading zeros are not printed.
func appendHex(dst []byte, i uint32) []byte {
if i == 0 {
return append(dst, '0')
}
for j := 7; j >= 0; j-- {
v := i >> uint(j*4)
if v > 0 {
dst = append(dst, hexDigit[v&0xf])
}
}
return dst
}
ip_merge.go
package ip_utils
import (
"bytes"
"math"
"net"
"sort"
)
type IPMerge struct {
Index int
IPs []net.IP
}
type MergeResult struct {
Cidr string
Mask string
FirstAddress string
Count int
}
func NewIPMerge(ips []net.IP) *IPMerge {
sort.Slice(ips, func(i, j int) bool {
return bytes.Compare(ips[i].To16(), ips[j].To16()) < 0
})
return &IPMerge{IPs: ips}
}
// 计算下一个最大的子网段
func (m *IPMerge) Next() (res MergeResult, ok bool) {
if m.Index >= len(m.IPs) {
return
}
ip := m.IPs[m.Index]
bits := BitLen(ip)
n := 1 // 子网段的掩码0的位数
num := 2
for {
lastIndex := m.Index + int(math.Pow(2, float64(n))) - 1
if lastIndex >= len(m.IPs) {
break
}
_, ipNet, err := net.ParseCIDR(GenCidr(ip, bits-n))
if err != nil {
break
}
start, end := IpNetRange(ipNet)
sIP, eIP := net.ParseIP(start), net.ParseIP(end)
if !ip.Equal(sIP) || !m.IPs[lastIndex].Equal(eIP) {
break
}
n++
num *= 2
}
ones := bits - n + 1
ipCount := num / 2
m.Index += ipCount
res.Cidr = GenCidr(ip, ones)
res.Mask = GenMask(ones, bits)
res.FirstAddress = ip.String()
res.Count = ipCount
return res, true
}
ip_merge_test.go
package ip_utils
import (
"math/rand"
"net"
"testing"
)
func TestIPMerge_Next(t *testing.T) {
var ips []net.IP
for i := 0; i < 2; i++ {
ips = append(ips, RandomIPv6Seq(rand.Intn(32)+1)...)
ips = append(ips, RandomIPv4Seq(rand.Intn(32)+1)...)
}
t.Log(ips)
ipMerge := NewIPMerge(ips)
res, ok := ipMerge.Next()
for ok {
t.Log(res.Cidr, res.Count)
res, ok = ipMerge.Next()
}
}
ip_random.go
package ip_utils
import (
"encoding/binary"
"math/rand"
"net"
"time"
)
func RandomIPv4() net.IP {
rand32 := rand.New(rand.NewSource(time.Now().UnixNano())).Uint32()
ip := make(net.IP, net.IPv4len)
copy(ip, net.IPv4zero)
binary.BigEndian.PutUint32(ip.To4(), rand32)
return ip
}
func RandomIPv6() net.IP {
var rand128 [2]uint64
rand128[0] = rand.New(rand.NewSource(time.Now().UnixNano())).Uint64()
rand128[1] = rand.New(rand.NewSource(time.Now().UnixNano())).Uint64()
ip := make(net.IP, net.IPv6len)
copy(ip, net.IPv6zero)
binary.BigEndian.PutUint64(ip[:8], rand128[0])
binary.BigEndian.PutUint64(ip[8:16], rand128[1])
return ip
}
func RandomIPv4Seq(num int) []net.IP {
result := make([]net.IP, 0)
ip := RandomIPv4()
for i := 0; i < num; i++ {
result = append(result, Copy(ip))
IncreaseIP(ip)
}
return result
}
func RandomIPv6Seq(num int) []net.IP {
result := make([]net.IP, 0)
ip := RandomIPv6()
for i := 0; i < num; i++ {
result = append(result, Copy(ip))
IncreaseIP(ip)
}
return result
}
ip_range.go
package ip_utils
import "net"
// IpNetRange 返回网段的起始IP、结束IP
func IpNetRange(ipNet *net.IPNet) (start, end string) {
mask := ipNet.Mask
broadcast := Copy(ipNet.IP)
for i := 0; i < len(mask); i++ {
ipIdx := len(broadcast) - i - 1
broadcast[ipIdx] = ipNet.IP[ipIdx] | ^mask[len(mask)-i-1]
}
return ipNet.IP.String(), broadcast.String()
}
// IncreaseIP IP地址自增
func IncreaseIP(ip net.IP) {
for i := len(ip) - 1; i >= 0; i-- {
ip[i]++
if ip[i] > 0 {
break
}
}
}
// DecreaseIP IP地址自减
func DecreaseIP(ip net.IP) {
length := len(ip)
for i := length - 1; i >= 0; i-- {
ip[length-1]--
if ip[length-1] < 0xFF {
break
}
for j := 1; j < length; j++ {
ip[length-j-1]--
if ip[length-j-1] < 0xFF {
return
}
}
}
}
ip_sort.go
package ip_utils
import (
"bytes"
"net"
"sort"
)
// CompareLess return true if ip1 < ip2
func CompareLess(ipStr1, ipStr2 string) bool {
ip1 := net.ParseIP(ipStr1)
ip2 := net.ParseIP(ipStr2)
if ip1 == nil || ip2 == nil {
return ipStr1 < ipStr2
}
return IPCompareLess(ip1, ip2)
}
func IPCompareLess(ip1, ip2 net.IP) bool {
if bytes.Compare(ip1.To16(), ip2.To16()) < 0 {
return true
}
return false
}
// Sort ips从小到大排序
func Sort(ips []string) {
sort.Slice(ips, func(i, j int) bool {
return CompareLess(ips[i], ips[j])
})
}
// SortIP ips从小到大排序
func SortIP(ips []net.IP) {
sort.Slice(ips, func(i, j int) bool {
return IPCompareLess(ips[i], ips[j])
})
}