123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121 |
- package addrgen
- import (
- "errors"
- "fmt"
- "math/big"
- "math/rand"
- "net/netip"
- "strings"
- "github.com/SenseUnit/dtlspipe/randpool"
- )
- type AddrRange struct {
- base *big.Int
- size *big.Int
- v6 bool
- }
- var _ AddrGen = &AddrRange{}
- func NewAddrRange(start, end netip.Addr) (*AddrRange, error) {
- if start.BitLen() != end.BitLen() {
- return nil, errors.New("addr bit length mismatch - one of them is IPv4, another is IPv6")
- }
- if end.Less(start) {
- return NewAddrRange(end, start)
- }
- base := new(big.Int)
- base.SetBytes(start.AsSlice())
- upper := new(big.Int)
- upper.SetBytes(end.AsSlice())
- size := new(big.Int)
- size.Sub(upper, base)
- size.Add(size, big.NewInt(1))
- return &AddrRange{
- base: base,
- size: size,
- v6: start.BitLen() == 128,
- }, nil
- }
- func NewAddrRangeFromPrefix(pfx netip.Prefix) (*AddrRange, error) {
- if !pfx.IsValid() {
- return nil, errors.New("invalid prefix")
- }
- pfx = pfx.Masked()
- addr := pfx.Addr()
- base := new(big.Int)
- base.SetBytes(addr.AsSlice())
- pfxPower := addr.BitLen() - pfx.Bits()
- size := big.NewInt(1)
- size.Lsh(size, uint(pfxPower))
- return &AddrRange{
- base: base,
- size: size,
- v6: addr.BitLen() == 128,
- }, nil
- }
- func (ar *AddrRange) Addr() string {
- res := new(big.Int)
- randpool.Borrow(func(r *rand.Rand) {
- res.Rand(r, ar.size)
- })
- res.Add(ar.base, res)
- var resArr [16]byte
- resSlice := resArr[:]
- if !ar.v6 {
- resSlice = resSlice[:4]
- }
- res.FillBytes(resSlice)
- resAddr, ok := netip.AddrFromSlice(resSlice[:])
- if !ok {
- panic("can't parse address from slice")
- }
- return resAddr.String()
- }
- func (ar *AddrRange) Power() *big.Int {
- res := new(big.Int)
- res.Set(ar.size)
- return res
- }
- func ParseAddrRangeSpec(spec string) (AddrGen, error) {
- switch {
- case strings.Contains(spec, "/"):
- pfx, err := netip.ParsePrefix(spec)
- if err != nil {
- return nil, fmt.Errorf("unable to parse prefix %q: %w", spec, err)
- }
- if pfx.IsSingleIP() {
- return SingleAddr(pfx.Addr().String()), nil
- }
- r, err := NewAddrRangeFromPrefix(pfx)
- if err != nil {
- return nil, fmt.Errorf("unable to parse range spec %q: %w", spec, err)
- }
- return r, nil
- case strings.Contains(spec, ".."):
- parts := strings.SplitN(spec, "..", 2)
- start, err := netip.ParseAddr(parts[0])
- if err != nil {
- return nil, fmt.Errorf("unable to parse addr %q: %w", parts[0], err)
- }
- end, err := netip.ParseAddr(parts[1])
- if err != nil {
- return nil, fmt.Errorf("unable to parse addr %q: %w", parts[1], err)
- }
- r, err := NewAddrRange(start, end)
- if err != nil {
- return nil, fmt.Errorf("invalid range spec %q: %w", spec, err)
- }
- return r, nil
- }
- return SingleAddr(spec), nil
- }
|