Unverified Commit dfb290b3 authored by Adin Schmahmann's avatar Adin Schmahmann Committed by GitHub

Merge pull request #1 from libp2p/feat/init

Library for IP -> ASN mapping
parents 373dd8a5 85005c6c
os:
- linux
language: go
go:
- 1.14.x
- 1.15.x
env:
global:
- GOTFLAGS="-race"
matrix:
- BUILD_DEPTYPE=gomod
# disable travis install
install:
- true
script:
- bash <(curl -s https://raw.githubusercontent.com/ipfs/ci-helpers/master/travis-ci/run-standard-tests.sh)
cache:
directories:
- $GOPATH/pkg/mod
- $HOME/.cache/go-build
notifications:
email: false
# go-libp2p-asn-util
===
[![](https://img.shields.io/badge/made%20by-Protocol%20Labs-blue.svg?style=flat-square)](http://protocol.ai)
[![](https://img.shields.io/badge/project-libp2p-yellow.svg?style=flat-square)](http://github.com/libp2p/libp2p)
A library to lookup the ASN(Autonomous System Number) for an IP address. It uses the IPv6 to ASN database downloaded from https://iptoasn.com/.
Supports ONLY IPv6 addresses for now.
## Table of Contents
- [Install](#install)
- [Usage](#usage)
- [Documentation](#documentation)
- [Contribute](#contribute)
- [License](#license)
## Install
```
go get github.com/libp2p/go-libp2p-asn-util
```
## Usage
```go
import (
asn "github.com/libp2p/go-libp2p-asn-util"
)
func main() {
store, err := asn.NewAsnStore()
asNumber,err := store.AsnForIP(net.ParseIP("2a03:2880:f003:c07:face:b00c::2"))
}
```
## Contribute
Feel free to join in. All welcome. Open an [issue](https://github.com/libp2p/go-libp2p-asn/issues)!
This repository falls under the IPFS [Code of Conduct](https://github.com/ipfs/community/blob/master/code-of-conduct.md).
## License
MIT
---
\ No newline at end of file
package asnutil
import (
"errors"
"fmt"
"net"
"github.com/libp2p/go-cidranger"
)
var Store *indirectAsnStore
func init() {
Store = newIndirectAsnStore()
}
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()
for k, v := range ipv6CidrToAsnMap {
_, nn, err := net.ParseCIDR(k)
if err != nil {
return nil, fmt.Errorf("failed to parse CIDR %s: %w", k, err)
}
if err := cr.Insert(&networkWithAsn{*nn, v}); err != nil {
return nil, fmt.Errorf("failed to insert CIDR %s in Trie store: %w", k, err)
}
}
return &asnStore{cr}, nil
}
type indirectAsnStore struct {
store *asnStore
doneLoading chan struct{}
}
// 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 *indirectAsnStore) AsnForIPv6(ip net.IP) (string, error) {
<-a.doneLoading
return a.store.AsnForIPv6(ip)
}
func newIndirectAsnStore() *indirectAsnStore {
a := &indirectAsnStore{
doneLoading: make(chan struct{}),
}
go func() {
defer close(a.doneLoading)
store, err := newAsnStore()
if err != nil {
panic(err)
}
a.store = store
}()
return a
}
package asnutil
import (
"net"
"testing"
"github.com/stretchr/testify/require"
)
func TestAsnIpv6(t *testing.T) {
tcs := map[string]struct {
ip net.IP
expectedASN string
}{
"google": {
ip: net.ParseIP("2001:4860:4860::8888"),
expectedASN: "15169",
},
"facebook": {
ip: net.ParseIP("2a03:2880:f003:c07:face:b00c::2"),
expectedASN: "32934",
},
"comcast": {
ip: net.ParseIP("2601::"),
expectedASN: "7922",
},
"does not exist": {
ip: net.ParseIP("::"),
expectedASN: "",
},
}
for name, tc := range tcs {
require.NotEmpty(t, tc.ip, name)
n, err := Store.AsnForIPv6(tc.ip)
require.NoError(t, err)
require.Equal(t, tc.expectedASN, n, name)
}
}
This diff is collapsed.
package main
import (
"encoding/csv"
"errors"
"fmt"
"io"
"math/bits"
"net"
"os"
u "github.com/ipfs/go-ipfs-util"
)
const (
pkgName = "asnutil"
ipv6OutputFile = "ipv6_asn_map.go"
ipv6MapName = "ipv6CidrToAsnMap"
)
func main() {
// file with the ASN mappings for IPv6 CIDRs.
// See ipv6_asn.tsv
ipv6File := os.Getenv("ASN_IPV6_FILE")
if len(ipv6File) == 0 {
panic(errors.New("environment vars must be provided"))
}
ipv6CidrToAsnMap := readMappingFile(ipv6File)
f, err := os.Create(ipv6OutputFile)
if err != nil {
panic(err)
}
defer f.Close()
writeMappingToFile(f, ipv6CidrToAsnMap, ipv6MapName)
}
func writeMappingToFile(f *os.File, m map[string]string, mapName string) {
printf := func(s string, args ...interface{}) {
_, err := fmt.Fprintf(f, s, args...)
if err != nil {
panic(err)
}
}
printf("package %s\n\n", pkgName)
printf("// Code generated by generate/main.go DO NOT EDIT\n")
printf("var %s = map[string]string {", mapName)
for k, v := range m {
printf("\n\t \"%s\": \"%s\",", k, v)
}
printf("\n}")
}
func readMappingFile(path string) map[string]string {
m := make(map[string]string)
f, err := os.Open(path)
if err != nil {
panic(err)
}
defer f.Close()
r := csv.NewReader(f)
r.Comma = '\t'
for {
record, err := r.Read()
// Stop at EOF.
if err == io.EOF {
return m
}
startIP := record[0]
endIP := record[1]
asn := record[2]
if asn == "0" {
continue
}
s := net.ParseIP(startIP)
e := net.ParseIP(endIP)
if s.To16() == nil || e.To16() == nil {
panic(errors.New("IP should be v6"))
}
prefixLen := zeroPrefixLen(u.XOR(s.To16(), e.To16()))
cn := fmt.Sprintf("%s/%d", startIP, prefixLen)
m[cn] = asn
}
}
func zeroPrefixLen(id []byte) int {
for i, b := range id {
if b != 0 {
return i*8 + bits.LeadingZeros8(uint8(b))
}
}
return len(id) * 8
}
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/ipfs/go-detect-race v0.0.1 h1:qX/xay2W3E4Q1U7d9lNs1sU9nvguX0a7319XbyQ6cOk=
github.com/ipfs/go-detect-race v0.0.1/go.mod h1:8BNT7shDZPo99Q74BpGMK+4D8Mn4j46UU0LZ723meps=
github.com/ipfs/go-ipfs-util v0.0.2 h1:59Sswnk1MFaiq+VcaknX7aYEyGyGDAA73ilhEK2POp8=
github.com/ipfs/go-ipfs-util v0.0.2/go.mod h1:CbPtkWJzjLdEcezDns2XYaehFVNXG9zrdrtMecczcsQ=
github.com/libp2p/go-cidranger v1.1.0 h1:ewPN8EZ0dd1LSnrtuwd4709PXVcITVeuwbag38yPW7c=
github.com/libp2p/go-cidranger v1.1.0/go.mod h1:KWZTfSr+r9qEo9OkI9/SIEeAtw+NNoU0dXIXt15Okic=
github.com/minio/blake2b-simd v0.0.0-20160723061019-3f5f724cb5b1 h1:lYpkrQH5ajf0OXOcUbGjvZxxijuBwbbmlSxLiuofa+g=
github.com/minio/blake2b-simd v0.0.0-20160723061019-3f5f724cb5b1/go.mod h1:pD8RvIylQ358TN4wwqatJ8rNavkEINozVn9DtGI3dfQ=
github.com/minio/sha256-simd v0.1.1-0.20190913151208-6de447530771 h1:MHkK1uRtFbVqvAgvWxafZe54+5uBxLluGylDiKgdhwo=
github.com/minio/sha256-simd v0.1.1-0.20190913151208-6de447530771/go.mod h1:B5e1o+1/KgNmWrSQK08Y6Z1Vb5pwIktudl0J58iy0KM=
github.com/mr-tron/base58 v1.1.3 h1:v+sk57XuaCKGXpWtVBX8YJzO7hMGx4Aajh4TQbdEFdc=
github.com/mr-tron/base58 v1.1.3/go.mod h1:BinMc/sQntlIE1frQmRFPUoPA1Zkr8VRgBdjWI2mNwc=
github.com/multiformats/go-multihash v0.0.13 h1:06x+mk/zj1FoMsgNejLpy6QTvJqlSt/BhLEy87zidlc=
github.com/multiformats/go-multihash v0.0.13/go.mod h1:VdAWLKTwram9oKAatUcLxBNUjdtcVwxObEQBtRfuyjc=
github.com/multiformats/go-varint v0.0.5 h1:XVZwSo04Cs3j/jS0uAEPpT3JY6DzMcVLLoWOSnCxOjg=
github.com/multiformats/go-varint v0.0.5/go.mod h1:3Ls8CIEsrijN6+B7PbrXRPxHRPuXSrVKRY101jdMZYE=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/spaolacci/murmur3 v1.1.0 h1:7c1g84S4BPRrfL5Xrdp6fOJ206sU9y293DDHaoy0bLI=
github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8 h1:1wopBVtVdWnn03fZelqdXTqk7U7zPQCb+T4rbU9ZEoU=
golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d h1:+R4KGOnez64A81RvjARKc4UT5/tI9ujCIVX+P5KiHuI=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
This diff is collapsed.
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment