backtrace/back/generate.go

81 lines
2.8 KiB
Go

package backtrace
import (
"errors"
"fmt"
"net"
)
const defaultPrefixListLimit = 1 << 16
var (
ErrInvalidPrefix = errors.New("invalid IPv4 prefix")
ErrIPv6PrefixUnsupported = errors.New("IPv6 prefix generation is unsupported")
ErrPrefixListTooLarge = errors.New("prefix list exceeds limit")
)
// GeneratePrefixList generates the legacy list of /24 address prefixes. The
// old API cannot return an error, so invalid, IPv6, and over-sized inputs
// return nil. Callers that need to distinguish these cases should use
// GeneratePrefixListWithLimit.
func GeneratePrefixList(prefix string) []string {
result, err := GeneratePrefixListWithLimit(prefix, defaultPrefixListLimit)
if err != nil {
return nil
}
return result
}
// GeneratePrefixListWithLimit generates one entry for every /24-sized block
// covered by an IPv4 CIDR. Prefixes narrower than /24 are rejected once the
// number of blocks exceeds maxEntries; this keeps /0 and similarly broad
// input from allocating or iterating unbounded data. A prefix longer than
// /24 still produces the containing block, preserving the legacy behaviour.
func GeneratePrefixListWithLimit(prefix string, maxEntries int) ([]string, error) {
if maxEntries <= 0 {
maxEntries = defaultPrefixListLimit
}
_, ipNet, err := net.ParseCIDR(prefix)
if err != nil {
return nil, fmt.Errorf("%w: %q", ErrInvalidPrefix, prefix)
}
maskSize, bits := ipNet.Mask.Size()
if bits == 128 {
return nil, fmt.Errorf("%w: %q", ErrIPv6PrefixUnsupported, prefix)
}
if bits != 32 || maskSize < 0 || maskSize > 32 || ipNet.IP.To4() == nil {
return nil, fmt.Errorf("%w: %q", ErrInvalidPrefix, prefix)
}
// A /24 block is the output unit used by the original implementation. A
// /25-/32 prefix therefore has one containing block, while a /0 would need
// 2^24 entries and is rejected before allocation.
blocks := uint64(1)
if maskSize < 24 {
blocks = uint64(1) << uint(24-maskSize)
}
if blocks > uint64(maxEntries) {
return nil, fmt.Errorf("%w: %d entries (limit %d)", ErrPrefixListTooLarge, blocks, maxEntries)
}
start := binaryIPToInt(ipNet.IP.To4()) &^ uint32(255)
result := make([]string, 0, int(blocks))
for index := uint64(0); index < blocks; index++ {
// The arithmetic is bounded by 2^24 blocks, so this multiplication and
// addition cannot overflow uint32 for a valid IPv4 network.
value := start + uint32(index*256)
result = append(result, fmt.Sprintf("%d.%d.%d", byte(value>>24), byte(value>>16), byte(value>>8)))
}
return result, nil
}
// 将IP地址转换为32位整数
func binaryIPToInt(ip net.IP) uint32 {
return (uint32(ip[0]) << 24) | (uint32(ip[1]) << 16) | (uint32(ip[2]) << 8) | uint32(ip[3])
}
// 将32位整数转换为IP地址
func intToBinaryIP(i uint32) net.IP {
return net.IPv4(byte(i>>24), byte(i>>16), byte(i>>8), byte(i))
}