2022-04-01 16:16:46 +00:00
|
|
|
package asnutil
|
|
|
|
|
|
|
|
import (
|
|
|
|
"errors"
|
|
|
|
"fmt"
|
|
|
|
"net"
|
2023-05-19 20:23:55 +00:00
|
|
|
"sync"
|
2022-04-01 16:16:46 +00:00
|
|
|
|
|
|
|
"github.com/libp2p/go-cidranger"
|
|
|
|
)
|
|
|
|
|
2023-05-19 20:23:55 +00:00
|
|
|
var Store *lazyAsnStore
|
2022-04-01 16:16:46 +00:00
|
|
|
|
|
|
|
func init() {
|
2023-05-19 20:23:55 +00:00
|
|
|
Store = &lazyAsnStore{}
|
2022-04-01 16:16:46 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
type networkWithAsn struct {
|
|
|
|
nn net.IPNet
|
|
|
|
asn string
|
|
|
|
}
|
|
|
|
|
|
|
|
func (e *networkWithAsn) Network() net.IPNet {
|
|
|
|
return e.nn
|
|
|
|
}
|
|
|
|
|
|
|
|
type asnStore struct {
|
|
|
|
cr cidranger.Ranger
|
|
|
|
}
|
|
|
|
|
|
|
|
// AsnForIPv6 returns the AS number for the given IPv6 address.
|
|
|
|
// If no mapping exists for the given IP, this function will
|
|
|
|
// return an empty ASN and a nil error.
|
|
|
|
func (a *asnStore) AsnForIPv6(ip net.IP) (string, error) {
|
|
|
|
if ip.To16() == nil {
|
|
|
|
return "", errors.New("ONLY IPv6 addresses supported for now")
|
|
|
|
}
|
|
|
|
|
|
|
|
ns, err := a.cr.ContainingNetworks(ip)
|
|
|
|
if err != nil {
|
|
|
|
return "", fmt.Errorf("failed to find matching networks for the given ip: %w", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(ns) == 0 {
|
|
|
|
return "", nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// longest prefix match
|
|
|
|
n := ns[len(ns)-1].(*networkWithAsn)
|
|
|
|
return n.asn, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func newAsnStore() (*asnStore, error) {
|
|
|
|
cr := cidranger.NewPCTrieRanger()
|
|
|
|
|
2022-08-19 16:34:07 +00:00
|
|
|
for _, v := range ipv6CidrToAsnPairList {
|
|
|
|
_, nn, err := net.ParseCIDR(v.cidr)
|
2022-04-01 16:16:46 +00:00
|
|
|
if err != nil {
|
2022-08-19 16:34:07 +00:00
|
|
|
return nil, fmt.Errorf("failed to parse CIDR %s: %w", v.cidr, err)
|
2022-04-01 16:16:46 +00:00
|
|
|
}
|
|
|
|
|
2022-08-19 16:34:07 +00:00
|
|
|
if err := cr.Insert(&networkWithAsn{*nn, v.asn}); err != nil {
|
|
|
|
return nil, fmt.Errorf("failed to insert CIDR %s in Trie store: %w", v.cidr, err)
|
2022-04-01 16:16:46 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return &asnStore{cr}, nil
|
|
|
|
}
|
|
|
|
|
2023-05-19 20:23:55 +00:00
|
|
|
// lazyAsnStore builds the underlying trie on first call to AsnForIPv6.
|
|
|
|
// Alternatively, Init can be called to manually trigger initialization.
|
|
|
|
type lazyAsnStore struct {
|
|
|
|
store *asnStore
|
|
|
|
once sync.Once
|
2022-04-01 16:16:46 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// AsnForIPv6 returns the AS number for the given IPv6 address.
|
|
|
|
// If no mapping exists for the given IP, this function will
|
|
|
|
// return an empty ASN and a nil error.
|
2023-05-19 20:23:55 +00:00
|
|
|
func (a *lazyAsnStore) AsnForIPv6(ip net.IP) (string, error) {
|
|
|
|
a.once.Do(a.init)
|
2022-04-01 16:16:46 +00:00
|
|
|
return a.store.AsnForIPv6(ip)
|
|
|
|
}
|
|
|
|
|
2023-05-19 20:23:55 +00:00
|
|
|
func (a *lazyAsnStore) Init() {
|
|
|
|
a.once.Do(a.init)
|
|
|
|
}
|
2022-04-01 16:16:46 +00:00
|
|
|
|
2023-05-19 20:23:55 +00:00
|
|
|
func (a *lazyAsnStore) init() {
|
|
|
|
store, err := newAsnStore()
|
|
|
|
if err != nil {
|
|
|
|
panic(err)
|
|
|
|
}
|
|
|
|
a.store = store
|
2022-04-01 16:16:46 +00:00
|
|
|
}
|