130 lines
2.7 KiB
Go
130 lines
2.7 KiB
Go
// Copyright (c) 2026 Karl Gaissmaier
|
|
// SPDX-License-Identifier: MIT
|
|
|
|
package random
|
|
|
|
import (
|
|
"fmt"
|
|
"math/rand/v2"
|
|
"net/netip"
|
|
)
|
|
|
|
// mpp is a abbreviation and panics on non masked prefixes.
|
|
//
|
|
//nolint:gochecknoglobals
|
|
var mpp = func(s string) netip.Prefix {
|
|
pfx := netip.MustParsePrefix(s)
|
|
if pfx == pfx.Masked() {
|
|
return pfx
|
|
}
|
|
panic(fmt.Sprintf("%s is not canonicalized as %s", s, pfx.Masked()))
|
|
}
|
|
|
|
// Prefix returns a randomly generated prefix
|
|
func Prefix(prng *rand.Rand) netip.Prefix {
|
|
if prng.IntN(2) == 1 {
|
|
return Prefix4(prng)
|
|
}
|
|
return Prefix6(prng)
|
|
}
|
|
|
|
func Prefix4(prng *rand.Rand) netip.Prefix {
|
|
bits := prng.IntN(33)
|
|
return netip.PrefixFrom(IP4(prng), bits).Masked()
|
|
}
|
|
|
|
func Prefix6(prng *rand.Rand) netip.Prefix {
|
|
bits := prng.IntN(129)
|
|
return netip.PrefixFrom(IP6(prng), bits).Masked()
|
|
}
|
|
|
|
func IP4(prng *rand.Rand) netip.Addr {
|
|
var b [4]byte
|
|
for i := range b {
|
|
//nolint:gosec // G115: integer overflow conversion uint -> byte
|
|
b[i] = byte(prng.UintN(256))
|
|
}
|
|
return netip.AddrFrom4(b)
|
|
}
|
|
|
|
func IP6(prng *rand.Rand) netip.Addr {
|
|
var b [16]byte
|
|
for i := range b {
|
|
//nolint:gosec // G115: integer overflow conversion uint -> byte
|
|
b[i] = byte(prng.UintN(256))
|
|
}
|
|
return netip.AddrFrom16(b)
|
|
}
|
|
|
|
func IP(prng *rand.Rand) netip.Addr {
|
|
if prng.IntN(2) == 1 {
|
|
return IP4(prng)
|
|
}
|
|
return IP6(prng)
|
|
}
|
|
|
|
func RealWorldPrefixes4(prng *rand.Rand, n int) []netip.Prefix {
|
|
set := make(map[netip.Prefix]struct{})
|
|
pfxs := make([]netip.Prefix, 0, n)
|
|
|
|
for len(set) < n {
|
|
pfx := Prefix4(prng)
|
|
|
|
// skip too small or too big masks
|
|
if pfx.Bits() < 8 || pfx.Bits() > 28 {
|
|
continue
|
|
}
|
|
|
|
// skip reserved/experimental ranges (e.g., 240.0.0.0/8)
|
|
if pfx.Overlaps(mpp("240.0.0.0/8")) {
|
|
continue
|
|
}
|
|
|
|
if _, ok := set[pfx]; !ok {
|
|
set[pfx] = struct{}{}
|
|
pfxs = append(pfxs, pfx)
|
|
}
|
|
}
|
|
return pfxs
|
|
}
|
|
|
|
func RealWorldPrefixes6(prng *rand.Rand, n int) []netip.Prefix {
|
|
set := make(map[netip.Prefix]struct{})
|
|
pfxs := make([]netip.Prefix, 0, n)
|
|
|
|
for len(set) < n {
|
|
pfx := Prefix6(prng)
|
|
|
|
// skip too small or too big masks
|
|
if pfx.Bits() < 16 || pfx.Bits() > 56 {
|
|
continue
|
|
}
|
|
|
|
// skip non global routes seen in the real world
|
|
if !pfx.Overlaps(mpp("2000::/3")) {
|
|
continue
|
|
}
|
|
if pfx.Addr().Compare(mpp("2c0f::/16").Addr()) == 1 {
|
|
continue
|
|
}
|
|
|
|
if _, ok := set[pfx]; !ok {
|
|
set[pfx] = struct{}{}
|
|
pfxs = append(pfxs, pfx)
|
|
}
|
|
}
|
|
return pfxs
|
|
}
|
|
|
|
func RealWorldPrefixes(prng *rand.Rand, n int) []netip.Prefix {
|
|
pfxs := make([]netip.Prefix, 0, n)
|
|
pfxs = append(pfxs, RealWorldPrefixes4(prng, n/2)...)
|
|
pfxs = append(pfxs, RealWorldPrefixes6(prng, n-len(pfxs))...)
|
|
|
|
prng.Shuffle(len(pfxs), func(i, j int) {
|
|
pfxs[i], pfxs[j] = pfxs[j], pfxs[i]
|
|
})
|
|
|
|
return pfxs
|
|
}
|