cachediptoasn.go 2.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384
  1. package thoth
  2. import (
  3. "context"
  4. "errors"
  5. "fmt"
  6. "log/slog"
  7. "net/netip"
  8. iptoasnv1 "github.com/TecharoHQ/thoth-proto/gen/techaro/thoth/iptoasn/v1"
  9. "github.com/gaissmai/bart"
  10. "google.golang.org/grpc"
  11. )
  12. type IPToASNWithCache struct {
  13. next iptoasnv1.IpToASNServiceClient
  14. table *bart.Table[*iptoasnv1.LookupResponse]
  15. }
  16. func NewIpToASNWithCache(next iptoasnv1.IpToASNServiceClient) *IPToASNWithCache {
  17. result := &IPToASNWithCache{
  18. next: next,
  19. table: &bart.Table[*iptoasnv1.LookupResponse]{},
  20. }
  21. for _, pfx := range []netip.Prefix{
  22. netip.MustParsePrefix("10.0.0.0/8"), // RFC 1918
  23. netip.MustParsePrefix("172.16.0.0/12"), // RFC 1918
  24. netip.MustParsePrefix("192.168.0.0/16"), // RFC 1918
  25. netip.MustParsePrefix("127.0.0.0/8"), // Loopback
  26. netip.MustParsePrefix("169.254.0.0/16"), // Link-local
  27. netip.MustParsePrefix("100.64.0.0/10"), // CGNAT
  28. netip.MustParsePrefix("192.0.0.0/24"), // Protocol assignments
  29. netip.MustParsePrefix("192.0.2.0/24"), // TEST-NET-1
  30. netip.MustParsePrefix("198.18.0.0/15"), // Benchmarking
  31. netip.MustParsePrefix("198.51.100.0/24"), // TEST-NET-2
  32. netip.MustParsePrefix("203.0.113.0/24"), // TEST-NET-3
  33. netip.MustParsePrefix("240.0.0.0/4"), // Reserved
  34. netip.MustParsePrefix("255.255.255.255/32"), // Broadcast
  35. netip.MustParsePrefix("fc00::/7"), // Unique local address
  36. netip.MustParsePrefix("fe80::/10"), // Link-local
  37. netip.MustParsePrefix("::1/128"), // Loopback
  38. netip.MustParsePrefix("::/128"), // Unspecified
  39. netip.MustParsePrefix("100::/64"), // Discard-only
  40. netip.MustParsePrefix("2001:db8::/32"), // Documentation
  41. } {
  42. result.table.Insert(pfx, &iptoasnv1.LookupResponse{Announced: false})
  43. }
  44. return result
  45. }
  46. func (ip2asn *IPToASNWithCache) Lookup(ctx context.Context, lr *iptoasnv1.LookupRequest, opts ...grpc.CallOption) (*iptoasnv1.LookupResponse, error) {
  47. addr, err := netip.ParseAddr(lr.GetIpAddress())
  48. if err != nil {
  49. return nil, fmt.Errorf("input is not an IP address: %w", err)
  50. }
  51. cachedResponse, ok := ip2asn.table.Lookup(addr)
  52. if ok {
  53. return cachedResponse, nil
  54. }
  55. resp, err := ip2asn.next.Lookup(ctx, lr, opts...)
  56. if err != nil {
  57. return nil, err
  58. }
  59. var errs []error
  60. for _, cidr := range resp.GetCidr() {
  61. pfx, err := netip.ParsePrefix(cidr)
  62. if err != nil {
  63. errs = append(errs, err)
  64. continue
  65. }
  66. ip2asn.table.Insert(pfx, resp)
  67. }
  68. if len(errs) != 0 {
  69. slog.Error("errors parsing IP prefixes", "err", errors.Join(errs...))
  70. }
  71. return resp, nil
  72. }