兼容ipv4和ipv6的方案,将ip范围转换为CIDR表示。
在线运行: <https://go.dev/play/p/Ynx1liLAGs2>
```go
package main
import (
"errors"
"fmt"
"math/big"
"net/netip"
"strconv"
"strings"
)
// Verify the result in the following way
// https://www.ipaddressguide.com/cidr
// https://www.ipaddressguide.com/ipv6-cidr
func IpRangeToCIDR(cidr []string, start, end string) ([]string, error) {
ips, err := netip.ParseAddr(start)
if err != nil {
return nil, err
}
ipe, err := netip.ParseAddr(end)
if err != nil {
return nil, err
}
isV4 := ips.Is4()
if isV4 != ipe.Is4() {
return nil, errors.New("start and end types are different")
}
if ips.Compare(ipe) > 0 {
return nil, errors.New("start > end")
}
var (
ipsInt = new(big.Int).SetBytes(ips.AsSlice())
ipeInt = new(big.Int).SetBytes(ipe.AsSlice())
tmpInt = new(big.Int)
mask = new(big.Int)
one = big.NewInt(1)
buf []byte
bits, maxBit uint
)
if isV4 {
maxBit = 32
buf = make([]byte, 4)
} else {
maxBit = 128
buf = make([]byte, 16)
}
for {
bits = 1
mask.SetUint64(1)
for bits < maxBit {
if (tmpInt.Or(ipsInt, mask).Cmp(ipeInt) > 0) ||
(tmpInt.Lsh(tmpInt.Rsh(ipsInt, bits), bits).Cmp(ipsInt) != 0) {
bits--
mask.Rsh(mask, 1)
break
}
bits++
mask.Add(mask.Lsh(mask, 1), one)
}
addr, _ := netip.AddrFromSlice(ipsInt.FillBytes(buf))
cidr = append(cidr, addr.String()+"/"+strconv.FormatUint(uint64(maxBit-bits), 10))
if tmpInt.Or(ipsInt, mask); tmpInt.Cmp(ipeInt) >= 0 {
break
}
ipsInt.Add(tmpInt, one)
}
return cidr, nil
}
func main() {
start, end := "10.5.6.0", "10.23.25.255"
cidr, err := IpRangeToCIDR(nil, start, end)
if err != nil {
panic(err)
}
fmt.Println(strings.Join(cidr, "\n"))
start = "2001:4860:4860:0000:0000:0000:0000:8888"
end = "2001:4860:4860:0000:0000:0000:4567:1234"
cidr, err = IpRangeToCIDR(cidr[:0], start, end)
if err != nil {
panic(err)
}
fmt.Println(strings.Join(cidr, "\n"))
}
```
有疑问加站长微信联系(非本文作者)