base-golang/pkg/cidr/ip.go
2024-07-23 10:23:43 +08:00

195 lines
4.2 KiB
Go

package cidr
import (
"encoding/hex"
"fmt"
"math"
"math/big"
"net"
)
// 裂解子网的方式
const (
MethodSubnetNum = 0 // 基于子网数量
MethodHostNum = 1 // 基于主机数量
)
var _ CIDR = (*cidr)(nil)
type CIDR interface {
CIDR() string
IP() string
Network() string
Broadcast() string
Mask() string
MaskSize() (int, int)
IPRange() (string, string)
IPCount() *big.Int
IsIPv4() bool
IsIPv6() bool
Equal(string) bool
Contains(string) bool
ForEachIP(func(string) error) error
ForEachIPBeginWith(string, func(string) error) error
SubNetting(method, num int) ([]*cidr, error)
}
type cidr struct {
ip net.IP
ipNet *net.IPNet
}
// ParseCIDR 解析CIDR网段
func ParseCIDR(s string) (*cidr, error) {
i, n, err := net.ParseCIDR(s)
if err != nil {
return nil, err
}
return &cidr{ip: i, ipNet: n}, nil
}
// Equal 判断网段是否相等
func (c *cidr) Equal(ns string) bool {
c2, err := ParseCIDR(ns)
if err != nil {
return false
}
return c.ipNet.IP.Equal(c2.ipNet.IP) /* && c.ipNet.IP.Equal(c2.ip) */
}
// IsIPv4 判断是否IPv4
func (c *cidr) IsIPv4() bool {
_, bits := c.ipNet.Mask.Size()
return bits/8 == net.IPv4len
}
// IsIPv6 判断是否IPv6
func (c *cidr) IsIPv6() bool {
_, bits := c.ipNet.Mask.Size()
return bits/8 == net.IPv6len
}
// Contains 判断IP是否包含在网段中
func (c *cidr) Contains(ip string) bool {
return c.ipNet.Contains(net.ParseIP(ip))
}
// CIDR 根据子网掩码长度校准后的CIDR
func (c *cidr) CIDR() string {
return c.ipNet.String()
}
// IP CIDR字符串中的IP部分
func (c *cidr) IP() string {
return c.ip.String()
}
// Network 网络号
func (c *cidr) Network() string {
return c.ipNet.IP.String()
}
// MaskSize 子网掩码位数
func (c *cidr) MaskSize() (ones, bits int) {
ones, bits = c.ipNet.Mask.Size()
return
}
// Mask 子网掩码
func (c *cidr) Mask() string {
mask, _ := hex.DecodeString(c.ipNet.Mask.String())
return net.IP([]byte(mask)).String()
}
// Broadcast 广播地址(网段最后一个IP)
func (c *cidr) Broadcast() string {
mask := c.ipNet.Mask
bcst := make(net.IP, len(c.ipNet.IP))
copy(bcst, c.ipNet.IP)
for i := 0; i < len(mask); i++ {
ipIdx := len(bcst) - i - 1
bcst[ipIdx] = c.ipNet.IP[ipIdx] | ^mask[len(mask)-i-1]
}
return bcst.String()
}
// IPRange 起始IP、结束IP
func (c *cidr) IPRange() (start, end string) {
return c.Network(), c.Broadcast()
}
// IPCount IP数量
func (c *cidr) IPCount() *big.Int {
ones, bits := c.ipNet.Mask.Size()
return big.NewInt(0).Lsh(big.NewInt(1), uint(bits-ones))
}
// ForEachIP 遍历网段下所有IP
func (c *cidr) ForEachIP(iterator func(ip string) error) error {
next := make(net.IP, len(c.ipNet.IP))
copy(next, c.ipNet.IP)
for c.ipNet.Contains(next) {
if err := iterator(next.String()); err != nil {
return err
}
IncrIP(next)
}
return nil
}
// ForEachIPBeginWith 从指定IP开始遍历网段下后续的IP
func (c *cidr) ForEachIPBeginWith(beginIP string, iterator func(ip string) error) error {
next := net.ParseIP(beginIP)
for c.ipNet.Contains(next) {
if err := iterator(next.String()); err != nil {
return err
}
IncrIP(next)
}
return nil
}
// SubNetting 裂解网段
func (c *cidr) SubNetting(method, num int) ([]*cidr, error) {
if num < 1 || (num&(num-1)) != 0 {
return nil, fmt.Errorf("裂解数量必须是2的次方")
}
newOnes := int(math.Log2(float64(num)))
ones, bits := c.MaskSize()
switch method {
default:
return nil, fmt.Errorf("不支持的裂解方式")
case MethodSubnetNum:
newOnes = ones + newOnes
// 如果子网的掩码长度大于父网段的长度,则无法裂解
if newOnes > bits {
return nil, nil
}
case MethodHostNum:
newOnes = bits - newOnes
// 如果子网的掩码长度小于等于父网段的掩码长度,则无法裂解
if newOnes <= ones {
return nil, nil
}
// 主机数量转换为子网数量
num = int(math.Pow(float64(2), float64(newOnes-ones)))
}
var cidrs []*cidr
network := make(net.IP, len(c.ipNet.IP))
copy(network, c.ipNet.IP)
for i := 0; i < num; i++ {
cidr, _ := ParseCIDR(fmt.Sprintf("%v/%v", network.String(), newOnes))
cidrs = append(cidrs, cidr)
// 广播地址的下一个IP即为下一段的网络号
network = net.ParseIP(cidr.Broadcast())
IncrIP(network)
}
return cidrs, nil
}