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 }