diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..22d0d82f8095e9c0ed572776afb47f9ca293ce00 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +vendor diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml new file mode 100644 index 0000000000000000000000000000000000000000..dfab88961c90fd41f90c5ab42b03a552ca342c2d --- /dev/null +++ b/.gitlab-ci.yml @@ -0,0 +1,35 @@ +stages: + - build + - test + +variables: + BUILD_DIR: "/tmp/$CI_CONCURRENT_PROJECT_ID" + +before_script: + - mkdir -p $BUILD_DIR/src + - cd $BUILD_DIR/src + - if [ -d $CI_PROJECT_DIR ] + - then + - echo "soft link $CI_PROJECT_DIR exists" + - else + - echo "creating soft link $CI_PROJECT_DIR" + - ln -s $CI_PROJECT_DIR + - fi + - cd $CI_PROJECT_DIR + +build: + stage: build + tags: + - testing + script: + - echo $CI_JOB_STAGE + - go build + +test: + stage: test + tags: + - testing + script: + - echo $CI_JOB_STAGE + - go test -cover + coverage: '/coverage: \d+.\d+% of statements/' diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000000000000000000000000000000000000..23775ec6e5ed1e5575784be1df10f7790fc62301 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,31 @@ +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 diff --git a/Gopkg.lock b/Gopkg.lock new file mode 100644 index 0000000000000000000000000000000000000000..507a4972b1344f7f3001410486c7800f782380c5 --- /dev/null +++ b/Gopkg.lock @@ -0,0 +1,33 @@ +# This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'. + + +[[projects]] + digest = "1:a2c1d0e43bd3baaa071d1b9ed72c27d78169b2b269f71c105ac4ba34b1be4a39" + name = "github.com/davecgh/go-spew" + packages = ["spew"] + pruneopts = "UT" + revision = "346938d642f2ec3594ed81d874461961cd0faa76" + version = "v1.1.0" + +[[projects]] + digest = "1:0028cb19b2e4c3112225cd871870f2d9cf49b9b4276531f03438a88e94be86fe" + name = "github.com/pmezard/go-difflib" + packages = ["difflib"] + pruneopts = "UT" + revision = "792786c7400a136282c1664665ae0a8db921c6c2" + version = "v1.0.0" + +[[projects]] + digest = "1:f85e109eda8f6080877185d1c39e98dd8795e1780c08beca28304b87fd855a1c" + name = "github.com/stretchr/testify" + packages = ["assert"] + pruneopts = "UT" + revision = "12b6f73e6084dad08a7c6e575284b177ecafbc71" + version = "v1.2.1" + +[solve-meta] + analyzer-name = "dep" + analyzer-version = 1 + input-imports = ["github.com/stretchr/testify/assert"] + solver-name = "gps-cdcl" + solver-version = 1 diff --git a/Gopkg.toml b/Gopkg.toml new file mode 100644 index 0000000000000000000000000000000000000000..55dbd3b239d4874d0228208a6bef6606020126a0 --- /dev/null +++ b/Gopkg.toml @@ -0,0 +1,34 @@ +# Gopkg.toml example +# +# Refer to https://github.com/golang/dep/blob/master/docs/Gopkg.toml.md +# for detailed Gopkg.toml documentation. +# +# required = ["github.com/user/thing/cmd/thing"] +# ignored = ["github.com/user/project/pkgX", "bitbucket.org/user/project/pkgA/pkgY"] +# +# [[constraint]] +# name = "github.com/user/project" +# version = "1.0.0" +# +# [[constraint]] +# name = "github.com/user/project2" +# branch = "dev" +# source = "github.com/myfork/project2" +# +# [[override]] +# name = "github.com/x/y" +# version = "2.4.0" +# +# [prune] +# non-go = false +# go-tests = true +# unused-packages = true + + +[[constraint]] + name = "github.com/stretchr/testify" + version = "1.2.1" + +[prune] + go-tests = true + unused-packages = true diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000000000000000000000000000000000000..c41c62239374c93cd74b35213c7d0da42a7c2647 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2017 Yulin + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md index 82e4c192f78341424ec442bec21f2f04b8576471..7050a5547218588c765166de3e8c73c2ec223e2b 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,110 @@ -# go-cidranger +# cidranger -dms3 p2p go-cidranger \ No newline at end of file +Fast IP to CIDR block(s) lookup using trie in Golang, inspired by [IPv4 route lookup linux](https://vincent.bernat.im/en/blog/2017-ipv4-route-lookup-linux). Possible use cases include detecting if a IP address is from published cloud provider CIDR blocks (e.g. 52.95.110.1 is contained in published AWS Route53 CIDR 52.95.110.0/24), IP routing rules, etc. + +Forked from https://github.com/yl2chen/cidranger due to upstream inactivity. + +[![GoDoc Reference](https://img.shields.io/badge/godoc-reference-5272B4.svg?style=flat-square)](https://godoc.org/github.com/libp2p/go-cidranger) +[![Build Status](https://img.shields.io/travis/libp2p/go-cidranger.svg?branch=master&style=flat-square)](https://travis-ci.org/libp2p/go-cidranger) +[![Coverage Status](https://img.shields.io/coveralls/libp2p/go-cidranger.svg?branch=master&style=flat-square)](https://coveralls.io/github/libp2p/go-cidranger?branch=master) +[![Go Report Card](https://goreportcard.com/badge/github.com/libp2p/go-cidranger?&style=flat-square)](https://goreportcard.com/report/github.com/libp2p/go-cidranger) + +This is visualization of a trie storing CIDR blocks `128.0.0.0/2` `192.0.0.0/2` `200.0.0.0/5` without path compression, the 0/1 number on the path indicates the bit value of the IP address at specified bit position, hence the path from root node to a child node represents a CIDR block that contains all IP ranges of its children, and children's children. +

+ +Visualization of trie storing same CIDR blocks with path compression, improving both lookup speed and memory footprint. +

+ +## Getting Started +Configure imports. +```go +import ( + "net" + + "github.com/libp2p/go-cidranger" +) +``` +Create a new ranger implemented using Path-Compressed prefix trie. +```go +ranger := NewPCTrieRanger() +``` +Inserts CIDR blocks. +```go +_, network1, _ := net.ParseCIDR("192.168.1.0/24") +_, network2, _ := net.ParseCIDR("128.168.1.0/24") +ranger.Insert(NewBasicRangerEntry(*network1)) +ranger.Insert(NewBasicRangerEntry(*network2)) +``` +To attach any additional value(s) to the entry, simply create custom struct +storing the desired value(s) that implements the RangerEntry interface: +```go +type RangerEntry interface { + Network() net.IPNet +} +``` +The prefix trie can be visualized as: +``` +0.0.0.0/0 (target_pos:31:has_entry:false) +| 1--> 128.0.0.0/1 (target_pos:30:has_entry:false) +| | 0--> 128.168.1.0/24 (target_pos:7:has_entry:true) +| | 1--> 192.168.1.0/24 (target_pos:7:has_entry:true) +``` +To test if given IP is contained in constructed ranger, +```go +contains, err = ranger.Contains(net.ParseIP("128.168.1.0")) // returns true, nil +contains, err = ranger.Contains(net.ParseIP("192.168.2.0")) // returns false, nil +``` +To get all the networks given is contained in, +```go +containingNetworks, err = ranger.ContainingNetworks(net.ParseIP("128.168.1.0")) +``` +To get all networks in ranger, +```go +entries, err := ranger.CoveredNetworks(*AllIPv4) // for IPv4 +entries, err := ranger.CoveredNetworks(*AllIPv6) // for IPv6 +``` + +## Benchmark +Compare hit/miss case for IPv4/IPv6 using PC trie vs brute force implementation, Ranger is initialized with published AWS ip ranges (889 IPv4 CIDR blocks and 360 IPv6) +```go +// Ipv4 lookup hit scenario +BenchmarkPCTrieHitIPv4UsingAWSRanges-4 5000000 353 ns/op +BenchmarkBruteRangerHitIPv4UsingAWSRanges-4 100000 13719 ns/op + +// Ipv6 lookup hit scenario, counter-intuitively faster then IPv4 due to less IPv6 CIDR +// blocks in the AWS dataset, hence the constructed trie has less path splits and depth. +BenchmarkPCTrieHitIPv6UsingAWSRanges-4 10000000 143 ns/op +BenchmarkBruteRangerHitIPv6UsingAWSRanges-4 300000 5178 ns/op + +// Ipv4 lookup miss scenario +BenchmarkPCTrieMissIPv4UsingAWSRanges-4 20000000 96.5 ns/op +BenchmarkBruteRangerMissIPv4UsingAWSRanges-4 50000 24781 ns/op + +// Ipv6 lookup miss scenario +BenchmarkPCTrieHMissIPv6UsingAWSRanges-4 10000000 115 ns/op +BenchmarkBruteRangerMissIPv6UsingAWSRanges-4 100000 10824 ns/op +``` + +## Example of IPv6 trie: +``` +::/0 (target_pos:127:has_entry:false) +| 0--> 2400::/14 (target_pos:113:has_entry:false) +| | 0--> 2400:6400::/22 (target_pos:105:has_entry:false) +| | | 0--> 2400:6500::/32 (target_pos:95:has_entry:false) +| | | | 0--> 2400:6500::/39 (target_pos:88:has_entry:false) +| | | | | 0--> 2400:6500:0:7000::/53 (target_pos:74:has_entry:false) +| | | | | | 0--> 2400:6500:0:7000::/54 (target_pos:73:has_entry:false) +| | | | | | | 0--> 2400:6500:0:7000::/55 (target_pos:72:has_entry:false) +| | | | | | | | 0--> 2400:6500:0:7000::/56 (target_pos:71:has_entry:true) +| | | | | | | | 1--> 2400:6500:0:7100::/56 (target_pos:71:has_entry:true) +| | | | | | | 1--> 2400:6500:0:7200::/56 (target_pos:71:has_entry:true) +| | | | | | 1--> 2400:6500:0:7400::/55 (target_pos:72:has_entry:false) +| | | | | | | 0--> 2400:6500:0:7400::/56 (target_pos:71:has_entry:true) +| | | | | | | 1--> 2400:6500:0:7500::/56 (target_pos:71:has_entry:true) +| | | | | 1--> 2400:6500:100:7000::/54 (target_pos:73:has_entry:false) +| | | | | | 0--> 2400:6500:100:7100::/56 (target_pos:71:has_entry:true) +| | | | | | 1--> 2400:6500:100:7200::/56 (target_pos:71:has_entry:true) +| | | | 1--> 2400:6500:ff00::/64 (target_pos:63:has_entry:true) +| | | 1--> 2400:6700:ff00::/64 (target_pos:63:has_entry:true) +| | 1--> 2403:b300:ff00::/64 (target_pos:63:has_entry:true) +``` diff --git a/brute.go b/brute.go new file mode 100644 index 0000000000000000000000000000000000000000..198ec3c34ce9ab6f0c7ce9107d12ea618275db47 --- /dev/null +++ b/brute.go @@ -0,0 +1,124 @@ +package cidranger + +import ( + "net" + + rnet "gitlab.dms3.io/p2p/go-cidranger/net" +) + +// bruteRanger is a brute force implementation of Ranger. Insertion and +// deletion of networks is performed on an internal storage in the form of +// map[string]net.IPNet (constant time operations). However, inclusion tests are +// always performed linearly at no guaranteed traversal order of recorded networks, +// so one can assume a worst case performance of O(N). The performance can be +// boosted many ways, e.g. changing usage of net.IPNet.Contains() to using masked +// bits equality checking, but the main purpose of this implementation is for +// testing because the correctness of this implementation can be easily guaranteed, +// and used as the ground truth when running a wider range of 'random' tests on +// other more sophisticated implementations. +type bruteRanger struct { + ipV4Entries map[string]RangerEntry + ipV6Entries map[string]RangerEntry +} + +// newBruteRanger returns a new Ranger. +func newBruteRanger() Ranger { + return &bruteRanger{ + ipV4Entries: make(map[string]RangerEntry), + ipV6Entries: make(map[string]RangerEntry), + } +} + +// Insert inserts a RangerEntry into ranger. +func (b *bruteRanger) Insert(entry RangerEntry) error { + network := entry.Network() + key := network.String() + if _, found := b.ipV4Entries[key]; !found { + entries, err := b.getEntriesByVersion(entry.Network().IP) + if err != nil { + return err + } + entries[key] = entry + } + return nil +} + +// Remove removes a RangerEntry identified by given network from ranger. +func (b *bruteRanger) Remove(network net.IPNet) (RangerEntry, error) { + networks, err := b.getEntriesByVersion(network.IP) + if err != nil { + return nil, err + } + key := network.String() + if networkToDelete, found := networks[key]; found { + delete(networks, key) + return networkToDelete, nil + } + return nil, nil +} + +// Contains returns bool indicating whether given ip is contained by any +// network in ranger. +func (b *bruteRanger) Contains(ip net.IP) (bool, error) { + entries, err := b.getEntriesByVersion(ip) + if err != nil { + return false, err + } + for _, entry := range entries { + network := entry.Network() + if network.Contains(ip) { + return true, nil + } + } + return false, nil +} + +// ContainingNetworks returns all RangerEntry(s) that given ip contained in. +func (b *bruteRanger) ContainingNetworks(ip net.IP) ([]RangerEntry, error) { + entries, err := b.getEntriesByVersion(ip) + if err != nil { + return nil, err + } + results := []RangerEntry{} + for _, entry := range entries { + network := entry.Network() + if network.Contains(ip) { + results = append(results, entry) + } + } + return results, nil +} + +// CoveredNetworks returns the list of RangerEntry(s) the given ipnet +// covers. That is, the networks that are completely subsumed by the +// specified network. +func (b *bruteRanger) CoveredNetworks(network net.IPNet) ([]RangerEntry, error) { + entries, err := b.getEntriesByVersion(network.IP) + if err != nil { + return nil, err + } + var results []RangerEntry + testNetwork := rnet.NewNetwork(network) + for _, entry := range entries { + entryNetwork := rnet.NewNetwork(entry.Network()) + if testNetwork.Covers(entryNetwork) { + results = append(results, entry) + } + } + return results, nil +} + +// Len returns number of networks in ranger. +func (b *bruteRanger) Len() int { + return len(b.ipV4Entries) + len(b.ipV6Entries) +} + +func (b *bruteRanger) getEntriesByVersion(ip net.IP) (map[string]RangerEntry, error) { + if ip.To4() != nil { + return b.ipV4Entries, nil + } + if ip.To16() != nil { + return b.ipV6Entries, nil + } + return nil, ErrInvalidNetworkInput +} diff --git a/brute_test.go b/brute_test.go new file mode 100644 index 0000000000000000000000000000000000000000..71ee637a2894ec7f56f6f7b20de1f00372cfe184 --- /dev/null +++ b/brute_test.go @@ -0,0 +1,177 @@ +package cidranger + +import ( + "net" + "sort" + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestInsert(t *testing.T) { + ranger := newBruteRanger().(*bruteRanger) + _, networkIPv4, _ := net.ParseCIDR("0.0.1.0/24") + _, networkIPv6, _ := net.ParseCIDR("8000::/96") + entryIPv4 := NewBasicRangerEntry(*networkIPv4) + entryIPv6 := NewBasicRangerEntry(*networkIPv6) + + ranger.Insert(entryIPv4) + ranger.Insert(entryIPv6) + + assert.Equal(t, 1, len(ranger.ipV4Entries)) + assert.Equal(t, entryIPv4, ranger.ipV4Entries["0.0.1.0/24"]) + assert.Equal(t, 1, len(ranger.ipV6Entries)) + assert.Equal(t, entryIPv6, ranger.ipV6Entries["8000::/96"]) +} + +func TestInsertError(t *testing.T) { + bRanger := newBruteRanger().(*bruteRanger) + _, networkIPv4, _ := net.ParseCIDR("0.0.1.0/24") + networkIPv4.IP = append(networkIPv4.IP, byte(4)) + err := bRanger.Insert(NewBasicRangerEntry(*networkIPv4)) + assert.Equal(t, ErrInvalidNetworkInput, err) +} + +func TestRemove(t *testing.T) { + ranger := newBruteRanger().(*bruteRanger) + _, networkIPv4, _ := net.ParseCIDR("0.0.1.0/24") + _, networkIPv6, _ := net.ParseCIDR("8000::/96") + _, notInserted, _ := net.ParseCIDR("8000::/96") + + insertIPv4 := NewBasicRangerEntry(*networkIPv4) + insertIPv6 := NewBasicRangerEntry(*networkIPv6) + + ranger.Insert(insertIPv4) + deletedIPv4, err := ranger.Remove(*networkIPv4) + assert.NoError(t, err) + + ranger.Insert(insertIPv6) + deletedIPv6, err := ranger.Remove(*networkIPv6) + assert.NoError(t, err) + + entry, err := ranger.Remove(*notInserted) + assert.NoError(t, err) + assert.Nil(t, entry) + + assert.Equal(t, insertIPv4, deletedIPv4) + assert.Equal(t, 0, len(ranger.ipV4Entries)) + assert.Equal(t, insertIPv6, deletedIPv6) + assert.Equal(t, 0, len(ranger.ipV6Entries)) +} + +func TestRemoveError(t *testing.T) { + r := newBruteRanger().(*bruteRanger) + _, invalidNetwork, _ := net.ParseCIDR("0.0.1.0/24") + invalidNetwork.IP = append(invalidNetwork.IP, byte(4)) + + _, err := r.Remove(*invalidNetwork) + assert.Equal(t, ErrInvalidNetworkInput, err) +} + +func TestContains(t *testing.T) { + r := newBruteRanger().(*bruteRanger) + _, network, _ := net.ParseCIDR("0.0.1.0/24") + _, network1, _ := net.ParseCIDR("8000::/112") + r.Insert(NewBasicRangerEntry(*network)) + r.Insert(NewBasicRangerEntry(*network1)) + + cases := []struct { + ip net.IP + contains bool + err error + name string + }{ + {net.ParseIP("0.0.1.255"), true, nil, "IPv4 should contain"}, + {net.ParseIP("0.0.0.255"), false, nil, "IPv4 houldn't contain"}, + {net.ParseIP("8000::ffff"), true, nil, "IPv6 shouldn't contain"}, + {net.ParseIP("8000::1:ffff"), false, nil, "IPv6 shouldn't contain"}, + {append(net.ParseIP("8000::1:ffff"), byte(0)), false, ErrInvalidNetworkInput, "Invalid IP"}, + } + + for _, tc := range cases { + t.Run(tc.name, func(t *testing.T) { + contains, err := r.Contains(tc.ip) + if tc.err != nil { + assert.Equal(t, tc.err, err) + } else { + assert.NoError(t, err) + assert.Equal(t, tc.contains, contains) + } + }) + } +} + +func TestContainingNetworks(t *testing.T) { + r := newBruteRanger().(*bruteRanger) + _, network1, _ := net.ParseCIDR("0.0.1.0/24") + _, network2, _ := net.ParseCIDR("0.0.1.0/25") + _, network3, _ := net.ParseCIDR("8000::/112") + _, network4, _ := net.ParseCIDR("8000::/113") + entry1 := NewBasicRangerEntry(*network1) + entry2 := NewBasicRangerEntry(*network2) + entry3 := NewBasicRangerEntry(*network3) + entry4 := NewBasicRangerEntry(*network4) + r.Insert(entry1) + r.Insert(entry2) + r.Insert(entry3) + r.Insert(entry4) + cases := []struct { + ip net.IP + containingNetworks []RangerEntry + err error + name string + }{ + {net.ParseIP("0.0.1.255"), []RangerEntry{entry1}, nil, "IPv4 should contain"}, + {net.ParseIP("0.0.1.127"), []RangerEntry{entry1, entry2}, nil, "IPv4 should contain both"}, + {net.ParseIP("0.0.0.127"), []RangerEntry{}, nil, "IPv4 should contain none"}, + {net.ParseIP("8000::ffff"), []RangerEntry{entry3}, nil, "IPv6 should constain"}, + {net.ParseIP("8000::7fff"), []RangerEntry{entry3, entry4}, nil, "IPv6 should contain both"}, + {net.ParseIP("8000::1:7fff"), []RangerEntry{}, nil, "IPv6 should contain none"}, + {append(net.ParseIP("8000::1:7fff"), byte(0)), nil, ErrInvalidNetworkInput, "Invalid IP"}, + } + + for _, tc := range cases { + t.Run(tc.name, func(t *testing.T) { + networks, err := r.ContainingNetworks(tc.ip) + if tc.err != nil { + assert.Equal(t, tc.err, err) + } else { + assert.NoError(t, err) + assert.Equal(t, len(tc.containingNetworks), len(networks)) + for _, network := range tc.containingNetworks { + assert.Contains(t, networks, network) + } + } + }) + } +} + +func TestCoveredNetworks(t *testing.T) { + for _, tc := range coveredNetworkTests { + t.Run(tc.name, func(t *testing.T) { + ranger := newBruteRanger() + for _, insert := range tc.inserts { + _, network, _ := net.ParseCIDR(insert) + err := ranger.Insert(NewBasicRangerEntry(*network)) + assert.NoError(t, err) + } + var expectedEntries []string + for _, network := range tc.networks { + expectedEntries = append(expectedEntries, network) + } + sort.Strings(expectedEntries) + _, snet, _ := net.ParseCIDR(tc.search) + networks, err := ranger.CoveredNetworks(*snet) + assert.NoError(t, err) + + var results []string + for _, result := range networks { + net := result.Network() + results = append(results, net.String()) + } + sort.Strings(results) + + assert.Equal(t, expectedEntries, results) + }) + } +} diff --git a/cidranger.go b/cidranger.go new file mode 100644 index 0000000000000000000000000000000000000000..f638eedf322ef499ed9241fe188373285bcc5456 --- /dev/null +++ b/cidranger.go @@ -0,0 +1,99 @@ +/* +Package cidranger provides utility to store CIDR blocks and perform ip +inclusion tests against it. + +To create a new instance of the path-compressed trie: + + ranger := NewPCTrieRanger() + +To insert or remove an entry (any object that satisfies the RangerEntry +interface): + + _, network, _ := net.ParseCIDR("192.168.0.0/24") + ranger.Insert(NewBasicRangerEntry(*network)) + ranger.Remove(network) + +If you desire for any value to be attached to the entry, simply +create custom struct that satisfies the RangerEntry interface: + + type RangerEntry interface { + Network() net.IPNet + } + +To test whether an IP is contained in the constructed networks ranger: + + // returns bool, error + containsBool, err := ranger.Contains(net.ParseIP("192.168.0.1")) + +To get a list of CIDR blocks in constructed ranger that contains IP: + + // returns []RangerEntry, error + entries, err := ranger.ContainingNetworks(net.ParseIP("192.168.0.1")) + +To get a list of all IPv4/IPv6 rangers respectively: + + // returns []RangerEntry, error + entries, err := ranger.CoveredNetworks(*AllIPv4) + entries, err := ranger.CoveredNetworks(*AllIPv6) + +*/ +package cidranger + +import ( + "fmt" + "net" +) + +// ErrInvalidNetworkInput is returned upon invalid network input. +var ErrInvalidNetworkInput = fmt.Errorf("Invalid network input") + +// ErrInvalidNetworkNumberInput is returned upon invalid network input. +var ErrInvalidNetworkNumberInput = fmt.Errorf("Invalid network number input") + +// AllIPv4 is a IPv4 CIDR that contains all networks +var AllIPv4 = parseCIDRUnsafe("0.0.0.0/0") + +// AllIPv6 is a IPv6 CIDR that contains all networks +var AllIPv6 = parseCIDRUnsafe("0::0/0") + +func parseCIDRUnsafe(s string) *net.IPNet { + _, cidr, _ := net.ParseCIDR(s) + return cidr +} + +// RangerEntry is an interface for insertable entry into a Ranger. +type RangerEntry interface { + Network() net.IPNet +} + +type basicRangerEntry struct { + ipNet net.IPNet +} + +func (b *basicRangerEntry) Network() net.IPNet { + return b.ipNet +} + +// NewBasicRangerEntry returns a basic RangerEntry that only stores the network +// itself. +func NewBasicRangerEntry(ipNet net.IPNet) RangerEntry { + return &basicRangerEntry{ + ipNet: ipNet, + } +} + +// Ranger is an interface for cidr block containment lookups. +type Ranger interface { + Insert(entry RangerEntry) error + Remove(network net.IPNet) (RangerEntry, error) + Contains(ip net.IP) (bool, error) + ContainingNetworks(ip net.IP) ([]RangerEntry, error) + CoveredNetworks(network net.IPNet) ([]RangerEntry, error) + Len() int +} + +// NewPCTrieRanger returns a versionedRanger that supports both IPv4 and IPv6 +// using the path compressed trie implemention. +func NewPCTrieRanger() Ranger { + return newVersionedRanger(newRanger) +} diff --git a/cidranger_test.go b/cidranger_test.go new file mode 100644 index 0000000000000000000000000000000000000000..f55c6b1b73e61d2f6557bf5997a1bf855c53055d --- /dev/null +++ b/cidranger_test.go @@ -0,0 +1,292 @@ +package cidranger + +import ( + "encoding/json" + "io/ioutil" + "math/rand" + "net" + "testing" + "time" + + "github.com/stretchr/testify/assert" + rnet "gitlab.dms3.io/p2p/go-cidranger/net" +) + +/* + ****************************************************************** + Test Contains/ContainingNetworks against basic brute force ranger. + ****************************************************************** +*/ + +func TestContainsAgainstBaseIPv4(t *testing.T) { + testContainsAgainstBase(t, 100000, randIPv4Gen) +} + +func TestContainingNetworksAgaistBaseIPv4(t *testing.T) { + testContainingNetworksAgainstBase(t, 100000, randIPv4Gen) +} + +func TestCoveredNetworksAgainstBaseIPv4(t *testing.T) { + testCoversNetworksAgainstBase(t, 100000, randomIPNetGenFactory(ipV4AWSRangesIPNets)) +} + +// IPv6 spans an extremely large address space (2^128), randomly generated IPs +// will often fall outside of the test ranges (AWS public CIDR blocks), so it +// it more meaningful for testing to run from a curated list of IPv6 IPs. +func TestContainsAgaistBaseIPv6(t *testing.T) { + testContainsAgainstBase(t, 100000, curatedAWSIPv6Gen) +} + +func TestContainingNetworksAgaistBaseIPv6(t *testing.T) { + testContainingNetworksAgainstBase(t, 100000, curatedAWSIPv6Gen) +} + +func TestCoveredNetworksAgainstBaseIPv6(t *testing.T) { + testCoversNetworksAgainstBase(t, 100000, randomIPNetGenFactory(ipV6AWSRangesIPNets)) +} + +func testContainsAgainstBase(t *testing.T, iterations int, ipGen ipGenerator) { + if testing.Short() { + t.Skip("Skipping memory test in `-short` mode") + } + rangers := []Ranger{NewPCTrieRanger()} + baseRanger := newBruteRanger() + for _, ranger := range rangers { + configureRangerWithAWSRanges(t, ranger) + } + configureRangerWithAWSRanges(t, baseRanger) + + for i := 0; i < iterations; i++ { + nn := ipGen() + expected, err := baseRanger.Contains(nn.ToIP()) + assert.NoError(t, err) + for _, ranger := range rangers { + actual, err := ranger.Contains(nn.ToIP()) + assert.NoError(t, err) + assert.Equal(t, expected, actual) + } + } +} + +func testContainingNetworksAgainstBase(t *testing.T, iterations int, ipGen ipGenerator) { + if testing.Short() { + t.Skip("Skipping memory test in `-short` mode") + } + rangers := []Ranger{NewPCTrieRanger()} + baseRanger := newBruteRanger() + for _, ranger := range rangers { + configureRangerWithAWSRanges(t, ranger) + } + configureRangerWithAWSRanges(t, baseRanger) + + for i := 0; i < iterations; i++ { + nn := ipGen() + expected, err := baseRanger.ContainingNetworks(nn.ToIP()) + assert.NoError(t, err) + for _, ranger := range rangers { + actual, err := ranger.ContainingNetworks(nn.ToIP()) + assert.NoError(t, err) + assert.Equal(t, len(expected), len(actual)) + for _, network := range actual { + assert.Contains(t, expected, network) + } + } + } +} + +func testCoversNetworksAgainstBase(t *testing.T, iterations int, netGen networkGenerator) { + if testing.Short() { + t.Skip("Skipping memory test in `-short` mode") + } + rangers := []Ranger{NewPCTrieRanger()} + baseRanger := newBruteRanger() + for _, ranger := range rangers { + configureRangerWithAWSRanges(t, ranger) + } + configureRangerWithAWSRanges(t, baseRanger) + + for i := 0; i < iterations; i++ { + network := netGen() + expected, err := baseRanger.CoveredNetworks(network.IPNet()) + assert.NoError(t, err) + for _, ranger := range rangers { + actual, err := ranger.CoveredNetworks(network.IPNet()) + assert.NoError(t, err) + assert.Equal(t, len(expected), len(actual)) + for _, network := range actual { + assert.Contains(t, expected, network) + } + } + } +} + +/* + ****************************************************************** + Benchmarks. + ****************************************************************** +*/ + +func BenchmarkPCTrieHitIPv4UsingAWSRanges(b *testing.B) { + benchmarkContainsUsingAWSRanges(b, net.ParseIP("52.95.110.1"), NewPCTrieRanger()) +} +func BenchmarkBruteRangerHitIPv4UsingAWSRanges(b *testing.B) { + benchmarkContainsUsingAWSRanges(b, net.ParseIP("52.95.110.1"), newBruteRanger()) +} + +func BenchmarkPCTrieHitIPv6UsingAWSRanges(b *testing.B) { + benchmarkContainsUsingAWSRanges(b, net.ParseIP("2620:107:300f::36b7:ff81"), NewPCTrieRanger()) +} +func BenchmarkBruteRangerHitIPv6UsingAWSRanges(b *testing.B) { + benchmarkContainsUsingAWSRanges(b, net.ParseIP("2620:107:300f::36b7:ff81"), newBruteRanger()) +} + +func BenchmarkPCTrieMissIPv4UsingAWSRanges(b *testing.B) { + benchmarkContainsUsingAWSRanges(b, net.ParseIP("123.123.123.123"), NewPCTrieRanger()) +} +func BenchmarkBruteRangerMissIPv4UsingAWSRanges(b *testing.B) { + benchmarkContainsUsingAWSRanges(b, net.ParseIP("123.123.123.123"), newBruteRanger()) +} + +func BenchmarkPCTrieHMissIPv6UsingAWSRanges(b *testing.B) { + benchmarkContainsUsingAWSRanges(b, net.ParseIP("2620::ffff"), NewPCTrieRanger()) +} +func BenchmarkBruteRangerMissIPv6UsingAWSRanges(b *testing.B) { + benchmarkContainsUsingAWSRanges(b, net.ParseIP("2620::ffff"), newBruteRanger()) +} + +func BenchmarkPCTrieHitContainingNetworksIPv4UsingAWSRanges(b *testing.B) { + benchmarkContainingNetworksUsingAWSRanges(b, net.ParseIP("52.95.110.1"), NewPCTrieRanger()) +} +func BenchmarkBruteRangerHitContainingNetworksIPv4UsingAWSRanges(b *testing.B) { + benchmarkContainingNetworksUsingAWSRanges(b, net.ParseIP("52.95.110.1"), newBruteRanger()) +} + +func BenchmarkPCTrieHitContainingNetworksIPv6UsingAWSRanges(b *testing.B) { + benchmarkContainingNetworksUsingAWSRanges(b, net.ParseIP("2620:107:300f::36b7:ff81"), NewPCTrieRanger()) +} +func BenchmarkBruteRangerHitContainingNetworksIPv6UsingAWSRanges(b *testing.B) { + benchmarkContainingNetworksUsingAWSRanges(b, net.ParseIP("2620:107:300f::36b7:ff81"), newBruteRanger()) +} + +func BenchmarkPCTrieMissContainingNetworksIPv4UsingAWSRanges(b *testing.B) { + benchmarkContainingNetworksUsingAWSRanges(b, net.ParseIP("123.123.123.123"), NewPCTrieRanger()) +} +func BenchmarkBruteRangerMissContainingNetworksIPv4UsingAWSRanges(b *testing.B) { + benchmarkContainingNetworksUsingAWSRanges(b, net.ParseIP("123.123.123.123"), newBruteRanger()) +} + +func BenchmarkPCTrieHMissContainingNetworksIPv6UsingAWSRanges(b *testing.B) { + benchmarkContainingNetworksUsingAWSRanges(b, net.ParseIP("2620::ffff"), NewPCTrieRanger()) +} +func BenchmarkBruteRangerMissContainingNetworksIPv6UsingAWSRanges(b *testing.B) { + benchmarkContainingNetworksUsingAWSRanges(b, net.ParseIP("2620::ffff"), newBruteRanger()) +} + +func benchmarkContainsUsingAWSRanges(tb testing.TB, nn net.IP, ranger Ranger) { + configureRangerWithAWSRanges(tb, ranger) + for n := 0; n < tb.(*testing.B).N; n++ { + ranger.Contains(nn) + } +} + +func benchmarkContainingNetworksUsingAWSRanges(tb testing.TB, nn net.IP, ranger Ranger) { + configureRangerWithAWSRanges(tb, ranger) + for n := 0; n < tb.(*testing.B).N; n++ { + ranger.ContainingNetworks(nn) + } +} + +/* + ****************************************************************** + Helper methods and initialization. + ****************************************************************** +*/ + +type ipGenerator func() rnet.NetworkNumber + +func randIPv4Gen() rnet.NetworkNumber { + return rnet.NetworkNumber{rand.Uint32()} +} +func randIPv6Gen() rnet.NetworkNumber { + return rnet.NetworkNumber{rand.Uint32(), rand.Uint32(), rand.Uint32(), rand.Uint32()} +} +func curatedAWSIPv6Gen() rnet.NetworkNumber { + randIdx := rand.Intn(len(ipV6AWSRangesIPNets)) + + // Randomly generate an IP somewhat near the range. + network := ipV6AWSRangesIPNets[randIdx] + nn := rnet.NewNetworkNumber(network.IP) + ones, bits := network.Mask.Size() + zeros := bits - ones + nnPartIdx := zeros / rnet.BitsPerUint32 + nn[nnPartIdx] = rand.Uint32() + return nn +} + +type networkGenerator func() rnet.Network + +func randomIPNetGenFactory(pool []*net.IPNet) networkGenerator { + return func() rnet.Network { + return rnet.NewNetwork(*pool[rand.Intn(len(pool))]) + } +} + +type AWSRanges struct { + Prefixes []Prefix `json:"prefixes"` + IPv6Prefixes []IPv6Prefix `json:"ipv6_prefixes"` +} + +type Prefix struct { + IPPrefix string `json:"ip_prefix"` + Region string `json:"region"` + Service string `json:"service"` +} + +type IPv6Prefix struct { + IPPrefix string `json:"ipv6_prefix"` + Region string `json:"region"` + Service string `json:"service"` +} + +var awsRanges *AWSRanges +var ipV4AWSRangesIPNets []*net.IPNet +var ipV6AWSRangesIPNets []*net.IPNet + +func loadAWSRanges() *AWSRanges { + file, err := ioutil.ReadFile("./testdata/aws_ip_ranges.json") + if err != nil { + panic(err) + } + var ranges AWSRanges + err = json.Unmarshal(file, &ranges) + if err != nil { + panic(err) + } + return &ranges +} + +func configureRangerWithAWSRanges(tb testing.TB, ranger Ranger) { + for _, prefix := range awsRanges.Prefixes { + _, network, err := net.ParseCIDR(prefix.IPPrefix) + assert.NoError(tb, err) + ranger.Insert(NewBasicRangerEntry(*network)) + } + for _, prefix := range awsRanges.IPv6Prefixes { + _, network, err := net.ParseCIDR(prefix.IPPrefix) + assert.NoError(tb, err) + ranger.Insert(NewBasicRangerEntry(*network)) + } +} + +func init() { + awsRanges = loadAWSRanges() + for _, prefix := range awsRanges.IPv6Prefixes { + _, network, _ := net.ParseCIDR(prefix.IPPrefix) + ipV6AWSRangesIPNets = append(ipV6AWSRangesIPNets, network) + } + for _, prefix := range awsRanges.Prefixes { + _, network, _ := net.ParseCIDR(prefix.IPPrefix) + ipV4AWSRangesIPNets = append(ipV4AWSRangesIPNets, network) + } + rand.Seed(time.Now().Unix()) +} diff --git a/go.mod b/go.mod new file mode 100644 index 0000000000000000000000000000000000000000..b9505587f118ce71bdf80dffa3ff4a29f4d0e64d --- /dev/null +++ b/go.mod @@ -0,0 +1,8 @@ +module gitlab.dms3.io/p2p/go-cidranger + +go 1.15 + +require ( + github.com/stretchr/testify v1.4.0 + gitlab.dms3.io/dms3/public/go-detect-race v0.0.1 +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000000000000000000000000000000000000..001794a49ce8752752d9bbde63a12bf427081093 --- /dev/null +++ b/go.sum @@ -0,0 +1,13 @@ +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/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +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= +gitlab.dms3.io/dms3/public/go-detect-race v0.0.1 h1:njXKVicy8gRJxyKtG8znbzxpqzxDQBSlzlLTXOna2dU= +gitlab.dms3.io/dms3/public/go-detect-race v0.0.1/go.mod h1:NcJv8mYRyNhzWxY9ry4uRt0iD+0f7AvunqCKLgqJpwI= +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= diff --git a/net/ip.go b/net/ip.go new file mode 100644 index 0000000000000000000000000000000000000000..f00ec208fddc01c8b86ef59ae39da4687d068fc1 --- /dev/null +++ b/net/ip.go @@ -0,0 +1,300 @@ +/* +Package net provides utility functions for working with IPs (net.IP). +*/ +package net + +import ( + "encoding/binary" + "fmt" + "math" + "net" +) + +// IPVersion is version of IP address. +type IPVersion string + +// Helper constants. +const ( + IPv4Uint32Count = 1 + IPv6Uint32Count = 4 + + BitsPerUint32 = 32 + BytePerUint32 = 4 + + IPv4 IPVersion = "IPv4" + IPv6 IPVersion = "IPv6" +) + +// ErrInvalidBitPosition is returned when bits requested is not valid. +var ErrInvalidBitPosition = fmt.Errorf("bit position not valid") + +// ErrVersionMismatch is returned upon mismatch in network input versions. +var ErrVersionMismatch = fmt.Errorf("Network input version mismatch") + +// ErrNoGreatestCommonBit is an error returned when no greatest common bit +// exists for the cidr ranges. +var ErrNoGreatestCommonBit = fmt.Errorf("No greatest common bit") + +// NetworkNumber represents an IP address using uint32 as internal storage. +// IPv4 usings 1 uint32, while IPv6 uses 4 uint32. +type NetworkNumber []uint32 + +// NewNetworkNumber returns a equivalent NetworkNumber to given IP address, +// return nil if ip is neither IPv4 nor IPv6. +func NewNetworkNumber(ip net.IP) NetworkNumber { + if ip == nil { + return nil + } + coercedIP := ip.To4() + parts := 1 + if coercedIP == nil { + coercedIP = ip.To16() + parts = 4 + } + if coercedIP == nil { + return nil + } + nn := make(NetworkNumber, parts) + for i := 0; i < parts; i++ { + idx := i * net.IPv4len + nn[i] = binary.BigEndian.Uint32(coercedIP[idx : idx+net.IPv4len]) + } + return nn +} + +// ToV4 returns ip address if ip is IPv4, returns nil otherwise. +func (n NetworkNumber) ToV4() NetworkNumber { + if len(n) != IPv4Uint32Count { + return nil + } + return n +} + +// ToV6 returns ip address if ip is IPv6, returns nil otherwise. +func (n NetworkNumber) ToV6() NetworkNumber { + if len(n) != IPv6Uint32Count { + return nil + } + return n +} + +// ToIP returns equivalent net.IP. +func (n NetworkNumber) ToIP() net.IP { + ip := make(net.IP, len(n)*BytePerUint32) + for i := 0; i < len(n); i++ { + idx := i * net.IPv4len + binary.BigEndian.PutUint32(ip[idx:idx+net.IPv4len], n[i]) + } + if len(ip) == net.IPv4len { + ip = net.IPv4(ip[0], ip[1], ip[2], ip[3]) + } + return ip +} + +// Equal is the equality test for 2 network numbers. +func (n NetworkNumber) Equal(n1 NetworkNumber) bool { + if len(n) != len(n1) { + return false + } + if n[0] != n1[0] { + return false + } + if len(n) == IPv6Uint32Count { + return n[1] == n1[1] && n[2] == n1[2] && n[3] == n1[3] + } + return true +} + +// Next returns the next logical network number. +func (n NetworkNumber) Next() NetworkNumber { + newIP := make(NetworkNumber, len(n)) + copy(newIP, n) + for i := len(newIP) - 1; i >= 0; i-- { + newIP[i]++ + if newIP[i] > 0 { + break + } + } + return newIP +} + +// Previous returns the previous logical network number. +func (n NetworkNumber) Previous() NetworkNumber { + newIP := make(NetworkNumber, len(n)) + copy(newIP, n) + for i := len(newIP) - 1; i >= 0; i-- { + newIP[i]-- + if newIP[i] < math.MaxUint32 { + break + } + } + return newIP +} + +// Bit returns uint32 representing the bit value at given position, e.g., +// "128.0.0.0" has bit value of 1 at position 31, and 0 for positions 30 to 0. +func (n NetworkNumber) Bit(position uint) (uint32, error) { + if int(position) > len(n)*BitsPerUint32-1 { + return 0, ErrInvalidBitPosition + } + idx := len(n) - 1 - int(position/BitsPerUint32) + // Mod 31 to get array index. + rShift := position & (BitsPerUint32 - 1) + return (n[idx] >> rShift) & 1, nil +} + +// LeastCommonBitPosition returns the smallest position of the preceding common +// bits of the 2 network numbers, and returns an error ErrNoGreatestCommonBit +// if the two network number diverges from the first bit. +// e.g., if the network number diverges after the 1st bit, it returns 131 for +// IPv6 and 31 for IPv4 . +func (n NetworkNumber) LeastCommonBitPosition(n1 NetworkNumber) (uint, error) { + if len(n) != len(n1) { + return 0, ErrVersionMismatch + } + for i := 0; i < len(n); i++ { + mask := uint32(1) << 31 + pos := uint(31) + for ; mask > 0; mask >>= 1 { + if n[i]&mask != n1[i]&mask { + if i == 0 && pos == 31 { + return 0, ErrNoGreatestCommonBit + } + return (pos + 1) + uint(BitsPerUint32)*uint(len(n)-i-1), nil + } + pos-- + } + } + return 0, nil +} + +// Network represents a block of network numbers, also known as CIDR. +type Network struct { + Number NetworkNumber + Mask NetworkNumberMask +} + +// NewNetwork returns Network built using given net.IPNet. +func NewNetwork(ipNet net.IPNet) Network { + ones, _ := ipNet.Mask.Size() + return Network{ + Number: NewNetworkNumber(ipNet.IP), + Mask: NetworkNumberMask(ones), + } +} + +// Masked returns a new network conforming to new mask. +func (n Network) Masked(ones int) Network { + mask := NetworkNumberMask(ones) + return Network{ + Number: mask.Mask(n.Number), + Mask: mask, + } +} + +func sub(a, b uint8) uint8 { + res := a - b + if res > a { + res = 0 + } + return res +} + +func mask(m NetworkNumberMask) (mask1, mask2, mask3, mask4 uint32) { + // We're relying on overflow here. + const ones uint32 = 0xFFFFFFFF + mask1 = ones << sub(1*32, uint8(m)) + mask2 = ones << sub(2*32, uint8(m)) + mask3 = ones << sub(3*32, uint8(m)) + mask4 = ones << sub(4*32, uint8(m)) + return +} + +// Contains returns true if NetworkNumber is in range of Network, false +// otherwise. +func (n Network) Contains(nn NetworkNumber) bool { + if len(n.Number) != len(nn) { + return false + } + const ones uint32 = 0xFFFFFFFF + + mask1, mask2, mask3, mask4 := mask(n.Mask) + switch len(n.Number) { + case IPv4Uint32Count: + return nn[0]&mask1 == n.Number[0] + case IPv6Uint32Count: + return nn[0]&mask1 == n.Number[0] && + nn[1]&mask2 == n.Number[1] && + nn[2]&mask3 == n.Number[2] && + nn[3]&mask4 == n.Number[3] + default: + return false + } +} + +// Contains returns true if Network covers o, false otherwise +func (n Network) Covers(o Network) bool { + return n.Contains(o.Number) && n.Mask <= o.Mask +} + +// LeastCommonBitPosition returns the smallest position of the preceding common +// bits of the 2 networks, and returns an error ErrNoGreatestCommonBit +// if the two network number diverges from the first bit. +func (n Network) LeastCommonBitPosition(n1 Network) (uint, error) { + maskSize := n.Mask + if n1.Mask < n.Mask { + maskSize = n1.Mask + } + maskPosition := len(n1.Number)*BitsPerUint32 - int(maskSize) + lcb, err := n.Number.LeastCommonBitPosition(n1.Number) + if err != nil { + return 0, err + } + return uint(math.Max(float64(maskPosition), float64(lcb))), nil +} + +// Equal is the equality test for 2 networks. +func (n Network) Equal(n1 Network) bool { + return n.Number.Equal(n1.Number) && n.Mask == n1.Mask +} + +func (n Network) String() string { + return fmt.Sprintf("%s/%d", n.Number.ToIP(), n.Mask) +} + +func (n Network) IPNet() net.IPNet { + return net.IPNet{ + IP: n.Number.ToIP(), + Mask: net.CIDRMask(int(n.Mask), len(n.Number)*32), + } +} + +// NetworkNumberMask is an IP address. +type NetworkNumberMask int + +// Mask returns a new masked NetworkNumber from given NetworkNumber. +func (m NetworkNumberMask) Mask(n NetworkNumber) NetworkNumber { + mask1, mask2, mask3, mask4 := mask(m) + + result := make(NetworkNumber, len(n)) + switch len(n) { + case IPv4Uint32Count: + result[0] = n[0] & mask1 + case IPv6Uint32Count: + result[0] = n[0] & mask1 + result[1] = n[1] & mask2 + result[2] = n[2] & mask3 + result[3] = n[3] & mask4 + } + return result +} + +// NextIP returns the next sequential ip. +func NextIP(ip net.IP) net.IP { + return NewNetworkNumber(ip).Next().ToIP() +} + +// PreviousIP returns the previous sequential ip. +func PreviousIP(ip net.IP) net.IP { + return NewNetworkNumber(ip).Previous().ToIP() +} diff --git a/net/ip_test.go b/net/ip_test.go new file mode 100644 index 0000000000000000000000000000000000000000..8bc40ab24f646d16757b1d03e9d8303cc4076c9b --- /dev/null +++ b/net/ip_test.go @@ -0,0 +1,491 @@ +package net + +import ( + "math" + "net" + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestNewNetworkNumber(t *testing.T) { + cases := []struct { + ip net.IP + nn NetworkNumber + name string + }{ + {nil, nil, "nil input"}, + {net.IP([]byte{1, 1, 1, 1, 1}), nil, "bad input"}, + {net.ParseIP("128.0.0.0"), NetworkNumber([]uint32{2147483648}), "IPv4"}, + { + net.ParseIP("2001:0db8::ff00:0042:8329"), + NetworkNumber([]uint32{536939960, 0, 65280, 4358953}), + "IPv6", + }, + } + for _, tc := range cases { + t.Run(tc.name, func(t *testing.T) { + assert.Equal(t, tc.nn, NewNetworkNumber(tc.ip)) + }) + } +} + +func TestNetworkNumberAssertion(t *testing.T) { + cases := []struct { + ip NetworkNumber + to4 NetworkNumber + to6 NetworkNumber + name string + }{ + {NetworkNumber([]uint32{1}), NetworkNumber([]uint32{1}), nil, "is IPv4"}, + {NetworkNumber([]uint32{1, 1, 1, 1}), nil, NetworkNumber([]uint32{1, 1, 1, 1}), "is IPv6"}, + {NetworkNumber([]uint32{1, 1}), nil, nil, "is invalid"}, + } + for _, tc := range cases { + t.Run(tc.name, func(t *testing.T) { + assert.Equal(t, tc.to4, tc.ip.ToV4()) + assert.Equal(t, tc.to6, tc.ip.ToV6()) + }) + } +} + +func TestNetworkNumberBit(t *testing.T) { + cases := []struct { + ip NetworkNumber + ones map[uint]bool + name string + }{ + {NewNetworkNumber(net.ParseIP("128.0.0.0")), map[uint]bool{31: true}, "128.0.0.0"}, + {NewNetworkNumber(net.ParseIP("1.1.1.1")), map[uint]bool{0: true, 8: true, 16: true, 24: true}, "1.1.1.1"}, + {NewNetworkNumber(net.ParseIP("8000::")), map[uint]bool{127: true}, "8000::"}, + {NewNetworkNumber(net.ParseIP("8000::8000")), map[uint]bool{127: true, 15: true}, "8000::8000"}, + } + for _, tc := range cases { + t.Run(tc.name, func(t *testing.T) { + for i := uint(0); i < uint(len(tc.ip)*BitsPerUint32); i++ { + bit, err := tc.ip.Bit(i) + assert.NoError(t, err) + if _, isOne := tc.ones[i]; isOne { + assert.Equal(t, uint32(1), bit) + } else { + assert.Equal(t, uint32(0), bit) + } + } + }) + } +} + +func TestNetworkNumberBitError(t *testing.T) { + cases := []struct { + ip NetworkNumber + position uint + err error + name string + }{ + {NewNetworkNumber(net.ParseIP("128.0.0.0")), 0, nil, "IPv4 index in bound"}, + {NewNetworkNumber(net.ParseIP("128.0.0.0")), 31, nil, "IPv4 index in bound"}, + {NewNetworkNumber(net.ParseIP("128.0.0.0")), 32, ErrInvalidBitPosition, "IPv4 index out of bounds"}, + {NewNetworkNumber(net.ParseIP("8000::")), 0, nil, "IPv6 index in bound"}, + {NewNetworkNumber(net.ParseIP("8000::")), 127, nil, "IPv6 index in bound"}, + {NewNetworkNumber(net.ParseIP("8000::")), 128, ErrInvalidBitPosition, "IPv6 index out of bounds"}, + } + for _, tc := range cases { + t.Run(tc.name, func(t *testing.T) { + _, err := tc.ip.Bit(tc.position) + assert.Equal(t, tc.err, err) + }) + } +} + +func TestNetworkNumberEqual(t *testing.T) { + cases := []struct { + n1 NetworkNumber + n2 NetworkNumber + equals bool + name string + }{ + {NetworkNumber{math.MaxUint32}, NetworkNumber{math.MaxUint32}, true, "IPv4 equals"}, + {NetworkNumber{math.MaxUint32}, NetworkNumber{math.MaxUint32 - 1}, false, "IPv4 does not equal"}, + {NetworkNumber{1, 1, 1, 1}, NetworkNumber{1, 1, 1, 1}, true, "IPv6 equals"}, + {NetworkNumber{1, 1, 1, 1}, NetworkNumber{1, 1, 1, 2}, false, "IPv6 does not equal"}, + {NetworkNumber{1}, NetworkNumber{1, 2, 3, 4}, false, "Version mismatch"}, + } + for _, tc := range cases { + t.Run(tc.name, func(t *testing.T) { + assert.Equal(t, tc.equals, tc.n1.Equal(tc.n2)) + }) + } +} + +func TestNetworkNumberNext(t *testing.T) { + cases := []struct { + ip string + next string + name string + }{ + {"0.0.0.0", "0.0.0.1", "IPv4 basic"}, + {"0.0.0.255", "0.0.1.0", "IPv4 rollover"}, + {"0.255.255.255", "1.0.0.0", "IPv4 consecutive rollover"}, + {"8000::0", "8000::1", "IPv6 basic"}, + {"0::ffff", "0::1:0", "IPv6 rollover"}, + {"0:ffff:ffff:ffff:ffff:ffff:ffff:ffff", "1::", "IPv6 consecutive rollover"}, + } + + for _, tc := range cases { + t.Run(tc.name, func(t *testing.T) { + ip := NewNetworkNumber(net.ParseIP(tc.ip)) + expected := NewNetworkNumber(net.ParseIP(tc.next)) + assert.Equal(t, expected, ip.Next()) + }) + } +} + +func TestNeworkNumberPrevious(t *testing.T) { + cases := []struct { + ip string + previous string + name string + }{ + {"0.0.0.1", "0.0.0.0", "IPv4 basic"}, + {"0.0.1.0", "0.0.0.255", "IPv4 rollover"}, + {"1.0.0.0", "0.255.255.255", "IPv4 consecutive rollover"}, + {"8000::1", "8000::0", "IPv6 basic"}, + {"0::1:0", "0::ffff", "IPv6 rollover"}, + {"1::0", "0:ffff:ffff:ffff:ffff:ffff:ffff:ffff", "IPv6 consecutive rollover"}, + } + + for _, tc := range cases { + t.Run(tc.name, func(t *testing.T) { + ip := NewNetworkNumber(net.ParseIP(tc.ip)) + expected := NewNetworkNumber(net.ParseIP(tc.previous)) + assert.Equal(t, expected, ip.Previous()) + }) + } +} + +func TestLeastCommonBitPositionForNetworks(t *testing.T) { + cases := []struct { + ip1 NetworkNumber + ip2 NetworkNumber + position uint + err error + name string + }{ + { + NetworkNumber([]uint32{2147483648}), + NetworkNumber([]uint32{3221225472, 0, 0, 0}), + 0, ErrVersionMismatch, "Version mismatch", + }, + { + NetworkNumber([]uint32{2147483648}), + NetworkNumber([]uint32{3221225472}), + 31, nil, "IPv4 31st position", + }, + { + NetworkNumber([]uint32{2147483648}), + NetworkNumber([]uint32{2147483648}), + 0, nil, "IPv4 0th position", + }, + { + NetworkNumber([]uint32{2147483648}), + NetworkNumber([]uint32{1}), + 0, ErrNoGreatestCommonBit, "IPv4 diverge at first bit", + }, + { + NetworkNumber([]uint32{2147483648, 0, 0, 0}), + NetworkNumber([]uint32{3221225472, 0, 0, 0}), + 127, nil, "IPv6 127th position", + }, + { + NetworkNumber([]uint32{2147483648, 1, 1, 1}), + NetworkNumber([]uint32{2147483648, 1, 1, 1}), + 0, nil, "IPv6 0th position", + }, + { + NetworkNumber([]uint32{2147483648, 0, 0, 0}), + NetworkNumber([]uint32{0, 0, 0, 1}), + 0, ErrNoGreatestCommonBit, "IPv6 diverge at first bit", + }, + } + for _, tc := range cases { + t.Run(tc.name, func(t *testing.T) { + pos, err := tc.ip1.LeastCommonBitPosition(tc.ip2) + assert.Equal(t, tc.err, err) + assert.Equal(t, tc.position, pos) + }) + } +} + +func TestNewNetwork(t *testing.T) { + _, ipNet, _ := net.ParseCIDR("192.128.0.0/24") + n := NewNetwork(*ipNet) + + newIPNet := n.IPNet() + assert.True(t, ipNet.IP.Equal(newIPNet.IP)) + assert.Equal(t, ipNet.Mask, newIPNet.Mask) + assert.Equal(t, NetworkNumber{3229614080}, n.Number) + assert.Equal(t, NetworkNumberMask(24), n.Mask) +} + +func TestNetworkMasked(t *testing.T) { + cases := []struct { + network string + mask int + maskedNetwork string + }{ + {"192.168.0.0/16", 16, "192.168.0.0/16"}, + {"192.168.0.0/16", 14, "192.168.0.0/14"}, + {"192.168.0.0/16", 18, "192.168.0.0/18"}, + {"192.168.0.0/16", 8, "192.0.0.0/8"}, + {"8000::/128", 96, "8000::/96"}, + {"8000::/128", 128, "8000::/128"}, + {"8000::/96", 112, "8000::/112"}, + {"8000:ffff::/96", 16, "8000::/16"}, + } + for _, testcase := range cases { + _, network, _ := net.ParseCIDR(testcase.network) + _, expected, _ := net.ParseCIDR(testcase.maskedNetwork) + n1 := NewNetwork(*network) + e1 := NewNetwork(*expected) + assert.Equal(t, e1.String(), n1.Masked(testcase.mask).String()) + } +} + +func TestNetworkEqual(t *testing.T) { + cases := []struct { + n1 string + n2 string + equal bool + name string + }{ + {"192.128.0.0/24", "192.128.0.0/24", true, "IPv4 equals"}, + {"192.128.0.0/24", "192.128.0.0/23", false, "IPv4 not equals"}, + {"8000::/24", "8000::/24", true, "IPv6 equals"}, + {"8000::/24", "8000::/23", false, "IPv6 not equals"}, + } + for _, tc := range cases { + t.Run(tc.name, func(t *testing.T) { + _, ipNet1, _ := net.ParseCIDR(tc.n1) + _, ipNet2, _ := net.ParseCIDR(tc.n2) + assert.Equal(t, tc.equal, NewNetwork(*ipNet1).Equal(NewNetwork(*ipNet2))) + }) + } +} + +func TestNetworkContains(t *testing.T) { + cases := []struct { + network string + firstIP string + lastIP string + name string + }{ + {"192.168.0.0/24", "192.168.0.0", "192.168.0.255", "192.168.0.0/24 contains"}, + {"8000::0/120", "8000::0", "8000::ff", "8000::0/120 contains"}, + } + for _, tc := range cases { + t.Run(tc.name, func(t *testing.T) { + _, net1, _ := net.ParseCIDR(tc.network) + network := NewNetwork(*net1) + ip := NewNetworkNumber(net.ParseIP(tc.firstIP)) + lastIP := NewNetworkNumber(net.ParseIP(tc.lastIP)) + assert.False(t, network.Contains(ip.Previous())) + assert.False(t, network.Contains(lastIP.Next())) + for ; !ip.Equal(lastIP.Next()); ip = ip.Next() { + assert.True(t, network.Contains(ip)) + } + }) + } +} + +func TestNetworkContainsVersionMismatch(t *testing.T) { + cases := []struct { + network string + ip string + name string + }{ + {"192.168.0.0/24", "8000::0", "IPv6 in IPv4 network"}, + {"8000::0/120", "192.168.0.0", "IPv4 in IPv6 network"}, + } + for _, tc := range cases { + t.Run(tc.name, func(t *testing.T) { + _, net1, _ := net.ParseCIDR(tc.network) + network := NewNetwork(*net1) + assert.False(t, network.Contains(NewNetworkNumber(net.ParseIP(tc.ip)))) + }) + } +} + +func TestNetworkCovers(t *testing.T) { + cases := []struct { + network string + covers string + result bool + name string + }{ + {"10.0.0.0/24", "10.0.0.1/25", true, "contains"}, + {"10.0.0.0/24", "11.0.0.1/25", false, "not contains"}, + {"10.0.0.0/16", "10.0.0.0/15", false, "prefix false"}, + {"10.0.0.0/15", "10.0.0.0/16", true, "prefix true"}, + {"10.0.0.0/15", "10.0.0.0/15", true, "same"}, + {"10::0/15", "10.0.0.0/15", false, "ip version mismatch"}, + {"10::0/15", "10::0/16", true, "ipv6"}, + } + + for _, tc := range cases { + t.Run(tc.name, func(t *testing.T) { + _, n, _ := net.ParseCIDR(tc.network) + network := NewNetwork(*n) + _, n, _ = net.ParseCIDR(tc.covers) + covers := NewNetwork(*n) + assert.Equal(t, tc.result, network.Covers(covers)) + }) + } +} + +func TestNetworkLeastCommonBitPosition(t *testing.T) { + cases := []struct { + cidr1 string + cidr2 string + expectedPos uint + expectedErr error + name string + }{ + {"0.0.1.0/24", "0.0.0.0/24", uint(9), nil, "IPv4 diverge before mask pos"}, + {"0.0.0.0/24", "0.0.0.0/24", uint(8), nil, "IPv4 diverge after mask pos"}, + {"0.0.0.128/24", "0.0.0.0/16", uint(16), nil, "IPv4 different mask pos"}, + {"128.0.0.0/24", "0.0.0.0/24", 0, ErrNoGreatestCommonBit, "IPv4 diverge at 1st pos"}, + {"8000::/96", "8000::1:0:0/96", uint(33), nil, "IPv6 diverge before mask pos"}, + {"8000::/96", "8000::8:0/96", uint(32), nil, "IPv6 diverge after mask pos"}, + {"8000::/96", "8000::/95", uint(33), nil, "IPv6 different mask pos"}, + {"ffff::0/24", "0::1/24", 0, ErrNoGreatestCommonBit, "IPv6 diverge at 1st pos"}, + } + for _, c := range cases { + _, cidr1, err := net.ParseCIDR(c.cidr1) + assert.NoError(t, err) + _, cidr2, err := net.ParseCIDR(c.cidr2) + assert.NoError(t, err) + n1 := NewNetwork(*cidr1) + pos, err := n1.LeastCommonBitPosition(NewNetwork(*cidr2)) + if c.expectedErr != nil { + assert.Equal(t, c.expectedErr, err) + } else { + assert.Equal(t, c.expectedPos, pos) + } + } +} + +func TestMask(t *testing.T) { + cases := []struct { + mask NetworkNumberMask + ip NetworkNumber + masked NetworkNumber + name string + }{ + {32, NetworkNumber{math.MaxUint32}, NetworkNumber{math.MaxUint32}, "nop IPv4 mask"}, + {16, NetworkNumber{math.MaxUint16 + 1}, NetworkNumber{math.MaxUint16 + 1}, "nop IPv4 mask"}, + {16, NetworkNumber{math.MaxUint32}, NetworkNumber{math.MaxUint32 - math.MaxUint16}, "IPv4 masked"}, + {96, NetworkNumber{math.MaxUint32, 0, 0, 0}, NetworkNumber{math.MaxUint32, 0, 0, 0}, "nop IPv6 mask"}, + {16, NetworkNumber{math.MaxUint16 + 1, 0, 0, 0}, NetworkNumber{math.MaxUint16 + 1, 0, 0, 0}, "nop IPv6 mask"}, + {16, NetworkNumber{math.MaxUint32, 0, 0, 0}, NetworkNumber{math.MaxUint32 - math.MaxUint16, 0, 0, 0}, "IPv6 masked"}, + } + for _, tc := range cases { + t.Run(tc.name, func(t *testing.T) { + masked := tc.mask.Mask(tc.ip) + assert.Equal(t, tc.masked, masked, tc.name) + }) + } +} + +func TestNextIP(t *testing.T) { + cases := []struct { + ip string + next string + name string + }{ + {"0.0.0.0", "0.0.0.1", "IPv4 basic"}, + {"0.0.0.255", "0.0.1.0", "IPv4 rollover"}, + {"0.255.255.255", "1.0.0.0", "IPv4 consecutive rollover"}, + {"8000::0", "8000::1", "IPv6 basic"}, + {"0::ffff", "0::1:0", "IPv6 rollover"}, + {"0:ffff:ffff:ffff:ffff:ffff:ffff:ffff", "1::", "IPv6 consecutive rollover"}, + } + + for _, tc := range cases { + t.Run(tc.name, func(t *testing.T) { + assert.Equal(t, net.ParseIP(tc.next), NextIP(net.ParseIP(tc.ip))) + }) + } +} + +func TestPreviousIP(t *testing.T) { + cases := []struct { + ip string + next string + name string + }{ + {"0.0.0.1", "0.0.0.0", "IPv4 basic"}, + {"0.0.1.0", "0.0.0.255", "IPv4 rollover"}, + {"1.0.0.0", "0.255.255.255", "IPv4 consecutive rollover"}, + {"8000::1", "8000::0", "IPv6 basic"}, + {"0::1:0", "0::ffff", "IPv6 rollover"}, + {"1::0", "0:ffff:ffff:ffff:ffff:ffff:ffff:ffff", "IPv6 consecutive rollover"}, + } + + for _, tc := range cases { + t.Run(tc.name, func(t *testing.T) { + assert.Equal(t, net.ParseIP(tc.next), PreviousIP(net.ParseIP(tc.ip))) + }) + } +} + +/* + ********************************* + Benchmarking ip manipulations. + ********************************* +*/ +func BenchmarkNetworkNumberBitIPv4(b *testing.B) { + benchmarkNetworkNumberBit(b, "52.95.110.1", 6) +} +func BenchmarkNetworkNumberBitIPv6(b *testing.B) { + benchmarkNetworkNumberBit(b, "2600:1ffe:e000::", 44) +} + +func BenchmarkNetworkNumberEqualIPv4(b *testing.B) { + benchmarkNetworkNumberEqual(b, "52.95.110.1", "52.95.110.1") +} + +func BenchmarkNetworkNumberEqualIPv6(b *testing.B) { + benchmarkNetworkNumberEqual(b, "2600:1ffe:e000::", "2600:1ffe:e000::") +} + +func BenchmarkNetworkContainsIPv4(b *testing.B) { + benchmarkNetworkContains(b, "52.95.110.0/24", "52.95.110.1") +} + +func BenchmarkNetworkContainsIPv6(b *testing.B) { + benchmarkNetworkContains(b, "2600:1ffe:e000::/40", "2600:1ffe:f000::") +} + +func benchmarkNetworkNumberBit(b *testing.B, ip string, pos uint) { + nn := NewNetworkNumber(net.ParseIP(ip)) + for n := 0; n < b.N; n++ { + nn.Bit(pos) + } +} + +func benchmarkNetworkNumberEqual(b *testing.B, ip1 string, ip2 string) { + nn1 := NewNetworkNumber(net.ParseIP(ip1)) + nn2 := NewNetworkNumber(net.ParseIP(ip2)) + for n := 0; n < b.N; n++ { + nn1.Equal(nn2) + } +} + +func benchmarkNetworkContains(b *testing.B, cidr string, ip string) { + nn := NewNetworkNumber(net.ParseIP(ip)) + _, ipNet, _ := net.ParseCIDR(cidr) + network := NewNetwork(*ipNet) + for n := 0; n < b.N; n++ { + network.Contains(nn) + } +} diff --git a/testdata/aws_ip_ranges.json b/testdata/aws_ip_ranges.json new file mode 100644 index 0000000000000000000000000000000000000000..f6c08898af059b425a1121e6fa044ec401adfb18 --- /dev/null +++ b/testdata/aws_ip_ranges.json @@ -0,0 +1,6253 @@ +{ + "syncToken": "1502143334", + "createDate": "2017-08-07-22-02-14", + "prefixes": [ + { + "ip_prefix": "13.32.0.0/15", + "region": "GLOBAL", + "service": "AMAZON" + }, + { + "ip_prefix": "13.54.0.0/15", + "region": "ap-southeast-2", + "service": "AMAZON" + }, + { + "ip_prefix": "13.56.0.0/16", + "region": "us-west-1", + "service": "AMAZON" + }, + { + "ip_prefix": "13.57.0.0/16", + "region": "us-west-1", + "service": "AMAZON" + }, + { + "ip_prefix": "13.58.0.0/15", + "region": "us-east-2", + "service": "AMAZON" + }, + { + "ip_prefix": "13.112.0.0/14", + "region": "ap-northeast-1", + "service": "AMAZON" + }, + { + "ip_prefix": "13.124.0.0/16", + "region": "ap-northeast-2", + "service": "AMAZON" + }, + { + "ip_prefix": "13.125.0.0/16", + "region": "ap-northeast-2", + "service": "AMAZON" + }, + { + "ip_prefix": "13.126.0.0/15", + "region": "ap-south-1", + "service": "AMAZON" + }, + { + "ip_prefix": "13.209.0.0/16", + "region": "ap-northeast-2", + "service": "AMAZON" + }, + { + "ip_prefix": "13.210.0.0/15", + "region": "ap-southeast-2", + "service": "AMAZON" + }, + { + "ip_prefix": "13.228.0.0/15", + "region": "ap-southeast-1", + "service": "AMAZON" + }, + { + "ip_prefix": "13.230.0.0/15", + "region": "ap-northeast-1", + "service": "AMAZON" + }, + { + "ip_prefix": "13.232.0.0/14", + "region": "ap-south-1", + "service": "AMAZON" + }, + { + "ip_prefix": "13.236.0.0/14", + "region": "ap-southeast-2", + "service": "AMAZON" + }, + { + "ip_prefix": "13.250.0.0/15", + "region": "ap-southeast-1", + "service": "AMAZON" + }, + { + "ip_prefix": "18.194.0.0/15", + "region": "eu-central-1", + "service": "AMAZON" + }, + { + "ip_prefix": "18.196.0.0/15", + "region": "eu-central-1", + "service": "AMAZON" + }, + { + "ip_prefix": "18.216.0.0/14", + "region": "us-east-2", + "service": "AMAZON" + }, + { + "ip_prefix": "18.220.0.0/14", + "region": "us-east-2", + "service": "AMAZON" + }, + { + "ip_prefix": "18.231.0.0/16", + "region": "sa-east-1", + "service": "AMAZON" + }, + { + "ip_prefix": "23.20.0.0/14", + "region": "us-east-1", + "service": "AMAZON" + }, + { + "ip_prefix": "27.0.0.0/22", + "region": "ap-northeast-1", + "service": "AMAZON" + }, + { + "ip_prefix": "34.192.0.0/12", + "region": "us-east-1", + "service": "AMAZON" + }, + { + "ip_prefix": "34.208.0.0/12", + "region": "us-west-2", + "service": "AMAZON" + }, + { + "ip_prefix": "34.224.0.0/12", + "region": "us-east-1", + "service": "AMAZON" + }, + { + "ip_prefix": "34.240.0.0/13", + "region": "eu-west-1", + "service": "AMAZON" + }, + { + "ip_prefix": "34.248.0.0/13", + "region": "eu-west-1", + "service": "AMAZON" + }, + { + "ip_prefix": "35.154.0.0/16", + "region": "ap-south-1", + "service": "AMAZON" + }, + { + "ip_prefix": "35.155.0.0/16", + "region": "us-west-2", + "service": "AMAZON" + }, + { + "ip_prefix": "35.156.0.0/14", + "region": "eu-central-1", + "service": "AMAZON" + }, + { + "ip_prefix": "35.160.0.0/13", + "region": "us-west-2", + "service": "AMAZON" + }, + { + "ip_prefix": "35.168.0.0/13", + "region": "us-east-1", + "service": "AMAZON" + }, + { + "ip_prefix": "35.176.0.0/15", + "region": "eu-west-2", + "service": "AMAZON" + }, + { + "ip_prefix": "35.178.0.0/15", + "region": "eu-west-2", + "service": "AMAZON" + }, + { + "ip_prefix": "35.180.0.0/15", + "region": "ca-central-1", + "service": "AMAZON" + }, + { + "ip_prefix": "35.182.0.0/15", + "region": "ca-central-1", + "service": "AMAZON" + }, + { + "ip_prefix": "43.250.192.0/24", + "region": "ap-southeast-1", + "service": "AMAZON" + }, + { + "ip_prefix": "43.250.193.0/24", + "region": "ap-southeast-1", + "service": "AMAZON" + }, + { + "ip_prefix": "46.51.128.0/18", + "region": "eu-west-1", + "service": "AMAZON" + }, + { + "ip_prefix": "46.51.192.0/20", + "region": "eu-west-1", + "service": "AMAZON" + }, + { + "ip_prefix": "46.51.216.0/21", + "region": "ap-southeast-1", + "service": "AMAZON" + }, + { + "ip_prefix": "46.51.224.0/19", + "region": "ap-northeast-1", + "service": "AMAZON" + }, + { + "ip_prefix": "46.137.0.0/17", + "region": "eu-west-1", + "service": "AMAZON" + }, + { + "ip_prefix": "46.137.128.0/18", + "region": "eu-west-1", + "service": "AMAZON" + }, + { + "ip_prefix": "46.137.192.0/19", + "region": "ap-southeast-1", + "service": "AMAZON" + }, + { + "ip_prefix": "46.137.224.0/19", + "region": "ap-southeast-1", + "service": "AMAZON" + }, + { + "ip_prefix": "50.16.0.0/15", + "region": "us-east-1", + "service": "AMAZON" + }, + { + "ip_prefix": "50.18.0.0/16", + "region": "us-west-1", + "service": "AMAZON" + }, + { + "ip_prefix": "50.19.0.0/16", + "region": "us-east-1", + "service": "AMAZON" + }, + { + "ip_prefix": "50.112.0.0/16", + "region": "us-west-2", + "service": "AMAZON" + }, + { + "ip_prefix": "52.0.0.0/15", + "region": "us-east-1", + "service": "AMAZON" + }, + { + "ip_prefix": "52.2.0.0/15", + "region": "us-east-1", + "service": "AMAZON" + }, + { + "ip_prefix": "52.4.0.0/14", + "region": "us-east-1", + "service": "AMAZON" + }, + { + "ip_prefix": "52.8.0.0/16", + "region": "us-west-1", + "service": "AMAZON" + }, + { + "ip_prefix": "52.9.0.0/16", + "region": "us-west-1", + "service": "AMAZON" + }, + { + "ip_prefix": "52.10.0.0/15", + "region": "us-west-2", + "service": "AMAZON" + }, + { + "ip_prefix": "52.12.0.0/15", + "region": "us-west-2", + "service": "AMAZON" + }, + { + "ip_prefix": "52.14.0.0/16", + "region": "us-east-2", + "service": "AMAZON" + }, + { + "ip_prefix": "52.15.0.0/16", + "region": "us-east-2", + "service": "AMAZON" + }, + { + "ip_prefix": "52.16.0.0/15", + "region": "eu-west-1", + "service": "AMAZON" + }, + { + "ip_prefix": "52.18.0.0/15", + "region": "eu-west-1", + "service": "AMAZON" + }, + { + "ip_prefix": "52.20.0.0/14", + "region": "us-east-1", + "service": "AMAZON" + }, + { + "ip_prefix": "52.24.0.0/14", + "region": "us-west-2", + "service": "AMAZON" + }, + { + "ip_prefix": "52.28.0.0/16", + "region": "eu-central-1", + "service": "AMAZON" + }, + { + "ip_prefix": "52.29.0.0/16", + "region": "eu-central-1", + "service": "AMAZON" + }, + { + "ip_prefix": "52.30.0.0/15", + "region": "eu-west-1", + "service": "AMAZON" + }, + { + "ip_prefix": "52.32.0.0/14", + "region": "us-west-2", + "service": "AMAZON" + }, + { + "ip_prefix": "52.36.0.0/14", + "region": "us-west-2", + "service": "AMAZON" + }, + { + "ip_prefix": "52.40.0.0/14", + "region": "us-west-2", + "service": "AMAZON" + }, + { + "ip_prefix": "52.44.0.0/15", + "region": "us-east-1", + "service": "AMAZON" + }, + { + "ip_prefix": "52.46.0.0/18", + "region": "GLOBAL", + "service": "AMAZON" + }, + { + "ip_prefix": "52.46.64.0/20", + "region": "eu-west-3", + "service": "AMAZON" + }, + { + "ip_prefix": "52.46.80.0/21", + "region": "eu-west-3", + "service": "AMAZON" + }, + { + "ip_prefix": "52.46.88.0/22", + "region": "eu-west-3", + "service": "AMAZON" + }, + { + "ip_prefix": "52.46.92.0/22", + "region": "eu-west-3", + "service": "AMAZON" + }, + { + "ip_prefix": "52.47.0.0/16", + "region": "eu-west-3", + "service": "AMAZON" + }, + { + "ip_prefix": "52.48.0.0/14", + "region": "eu-west-1", + "service": "AMAZON" + }, + { + "ip_prefix": "52.52.0.0/15", + "region": "us-west-1", + "service": "AMAZON" + }, + { + "ip_prefix": "52.54.0.0/15", + "region": "us-east-1", + "service": "AMAZON" + }, + { + "ip_prefix": "52.56.0.0/16", + "region": "eu-west-2", + "service": "AMAZON" + }, + { + "ip_prefix": "52.57.0.0/16", + "region": "eu-central-1", + "service": "AMAZON" + }, + { + "ip_prefix": "52.58.0.0/15", + "region": "eu-central-1", + "service": "AMAZON" + }, + { + "ip_prefix": "52.60.0.0/16", + "region": "ca-central-1", + "service": "AMAZON" + }, + { + "ip_prefix": "52.61.0.0/16", + "region": "us-gov-west-1", + "service": "AMAZON" + }, + { + "ip_prefix": "52.62.0.0/15", + "region": "ap-southeast-2", + "service": "AMAZON" + }, + { + "ip_prefix": "52.64.0.0/17", + "region": "ap-southeast-2", + "service": "AMAZON" + }, + { + "ip_prefix": "52.64.128.0/17", + "region": "ap-southeast-2", + "service": "AMAZON" + }, + { + "ip_prefix": "52.65.0.0/16", + "region": "ap-southeast-2", + "service": "AMAZON" + }, + { + "ip_prefix": "52.66.0.0/16", + "region": "ap-south-1", + "service": "AMAZON" + }, + { + "ip_prefix": "52.67.0.0/16", + "region": "sa-east-1", + "service": "AMAZON" + }, + { + "ip_prefix": "52.68.0.0/15", + "region": "ap-northeast-1", + "service": "AMAZON" + }, + { + "ip_prefix": "52.70.0.0/15", + "region": "us-east-1", + "service": "AMAZON" + }, + { + "ip_prefix": "52.72.0.0/15", + "region": "us-east-1", + "service": "AMAZON" + }, + { + "ip_prefix": "52.74.0.0/16", + "region": "ap-southeast-1", + "service": "AMAZON" + }, + { + "ip_prefix": "52.76.0.0/17", + "region": "ap-southeast-1", + "service": "AMAZON" + }, + { + "ip_prefix": "52.76.128.0/17", + "region": "ap-southeast-1", + "service": "AMAZON" + }, + { + "ip_prefix": "52.77.0.0/16", + "region": "ap-southeast-1", + "service": "AMAZON" + }, + { + "ip_prefix": "52.78.0.0/16", + "region": "ap-northeast-2", + "service": "AMAZON" + }, + { + "ip_prefix": "52.79.0.0/16", + "region": "ap-northeast-2", + "service": "AMAZON" + }, + { + "ip_prefix": "52.80.0.0/16", + "region": "cn-north-1", + "service": "AMAZON" + }, + { + "ip_prefix": "52.82.187.0/24", + "region": "cn-northwest-1", + "service": "AMAZON" + }, + { + "ip_prefix": "52.82.188.0/22", + "region": "cn-northwest-1", + "service": "AMAZON" + }, + { + "ip_prefix": "52.82.196.0/22", + "region": "cn-northwest-1", + "service": "AMAZON" + }, + { + "ip_prefix": "52.82.200.0/25", + "region": "cn-northwest-1", + "service": "AMAZON" + }, + { + "ip_prefix": "52.82.204.0/23", + "region": "cn-northwest-1", + "service": "AMAZON" + }, + { + "ip_prefix": "52.82.208.0/20", + "region": "cn-northwest-1", + "service": "AMAZON" + }, + { + "ip_prefix": "52.83.0.0/16", + "region": "cn-northwest-1", + "service": "AMAZON" + }, + { + "ip_prefix": "52.84.0.0/15", + "region": "GLOBAL", + "service": "AMAZON" + }, + { + "ip_prefix": "52.86.0.0/15", + "region": "us-east-1", + "service": "AMAZON" + }, + { + "ip_prefix": "52.88.0.0/15", + "region": "us-west-2", + "service": "AMAZON" + }, + { + "ip_prefix": "52.90.0.0/15", + "region": "us-east-1", + "service": "AMAZON" + }, + { + "ip_prefix": "52.92.0.0/20", + "region": "ap-northeast-2", + "service": "AMAZON" + }, + { + "ip_prefix": "52.92.16.0/20", + "region": "us-east-1", + "service": "AMAZON" + }, + { + "ip_prefix": "52.92.32.0/22", + "region": "us-west-2", + "service": "AMAZON" + }, + { + "ip_prefix": "52.92.39.0/24", + "region": "sa-east-1", + "service": "AMAZON" + }, + { + "ip_prefix": "52.92.40.0/21", + "region": "eu-west-1", + "service": "AMAZON" + }, + { + "ip_prefix": "52.92.48.0/22", + "region": "us-west-1", + "service": "AMAZON" + }, + { + "ip_prefix": "52.92.52.0/22", + "region": "ap-southeast-2", + "service": "AMAZON" + }, + { + "ip_prefix": "52.92.56.0/22", + "region": "ap-southeast-1", + "service": "AMAZON" + }, + { + "ip_prefix": "52.92.60.0/22", + "region": "ap-northeast-1", + "service": "AMAZON" + }, + { + "ip_prefix": "52.92.64.0/22", + "region": "sa-east-1", + "service": "AMAZON" + }, + { + "ip_prefix": "52.92.68.0/22", + "region": "eu-central-1", + "service": "AMAZON" + }, + { + "ip_prefix": "52.92.72.0/22", + "region": "sa-east-1", + "service": "AMAZON" + }, + { + "ip_prefix": "52.92.76.0/22", + "region": "us-east-2", + "service": "AMAZON" + }, + { + "ip_prefix": "52.92.80.0/22", + "region": "ap-northeast-1", + "service": "AMAZON" + }, + { + "ip_prefix": "52.92.84.0/22", + "region": "ca-central-1", + "service": "AMAZON" + }, + { + "ip_prefix": "52.92.88.0/22", + "region": "eu-west-2", + "service": "AMAZON" + }, + { + "ip_prefix": "52.92.248.0/22", + "region": "ap-south-1", + "service": "AMAZON" + }, + { + "ip_prefix": "52.92.252.0/22", + "region": "us-gov-west-1", + "service": "AMAZON" + }, + { + "ip_prefix": "52.93.0.0/24", + "region": "eu-west-1", + "service": "AMAZON" + }, + { + "ip_prefix": "52.93.1.0/24", + "region": "us-east-1", + "service": "AMAZON" + }, + { + "ip_prefix": "52.93.2.0/24", + "region": "eu-west-1", + "service": "AMAZON" + }, + { + "ip_prefix": "52.93.3.0/24", + "region": "us-east-1", + "service": "AMAZON" + }, + { + "ip_prefix": "52.93.4.0/24", + "region": "us-east-1", + "service": "AMAZON" + }, + { + "ip_prefix": "52.93.5.0/24", + "region": "ca-central-1", + "service": "AMAZON" + }, + { + "ip_prefix": "52.93.8.0/22", + "region": "ap-southeast-1", + "service": "AMAZON" + }, + { + "ip_prefix": "52.93.16.0/24", + "region": "eu-west-1", + "service": "AMAZON" + }, + { + "ip_prefix": "52.94.0.0/22", + "region": "us-east-1", + "service": "AMAZON" + }, + { + "ip_prefix": "52.94.4.0/24", + "region": "us-east-2", + "service": "AMAZON" + }, + { + "ip_prefix": "52.94.5.0/24", + "region": "eu-west-1", + "service": "AMAZON" + }, + { + "ip_prefix": "52.94.6.0/24", + "region": "ap-northeast-2", + "service": "AMAZON" + }, + { + "ip_prefix": "52.94.7.0/24", + "region": "sa-east-1", + "service": "AMAZON" + }, + { + "ip_prefix": "52.94.8.0/24", + "region": "ap-northeast-1", + "service": "AMAZON" + }, + { + "ip_prefix": "52.94.9.0/24", + "region": "us-gov-west-1", + "service": "AMAZON" + }, + { + "ip_prefix": "52.94.10.0/24", + "region": "us-west-2", + "service": "AMAZON" + }, + { + "ip_prefix": "52.94.11.0/24", + "region": "ap-southeast-1", + "service": "AMAZON" + }, + { + "ip_prefix": "52.94.12.0/24", + "region": "us-west-1", + "service": "AMAZON" + }, + { + "ip_prefix": "52.94.13.0/24", + "region": "ap-southeast-2", + "service": "AMAZON" + }, + { + "ip_prefix": "52.94.14.0/24", + "region": "ca-central-1", + "service": "AMAZON" + }, + { + "ip_prefix": "52.94.15.0/24", + "region": "eu-west-2", + "service": "AMAZON" + }, + { + "ip_prefix": "52.94.16.0/24", + "region": "eu-west-3", + "service": "AMAZON" + }, + { + "ip_prefix": "52.94.17.0/24", + "region": "eu-central-1", + "service": "AMAZON" + }, + { + "ip_prefix": "52.94.20.0/24", + "region": "ap-south-1", + "service": "AMAZON" + }, + { + "ip_prefix": "52.94.24.0/23", + "region": "eu-west-1", + "service": "AMAZON" + }, + { + "ip_prefix": "52.94.26.0/23", + "region": "eu-west-1", + "service": "AMAZON" + }, + { + "ip_prefix": "52.94.28.0/23", + "region": "us-west-2", + "service": "AMAZON" + }, + { + "ip_prefix": "52.94.30.0/23", + "region": "us-west-2", + "service": "AMAZON" + }, + { + "ip_prefix": "52.94.32.0/20", + "region": "eu-west-2", + "service": "AMAZON" + }, + { + "ip_prefix": "52.94.48.0/20", + "region": "eu-west-2", + "service": "AMAZON" + }, + { + "ip_prefix": "52.94.64.0/22", + "region": "ca-central-1", + "service": "AMAZON" + }, + { + "ip_prefix": "52.94.72.0/22", + "region": "us-west-1", + "service": "AMAZON" + }, + { + "ip_prefix": "52.94.80.0/20", + "region": "ca-central-1", + "service": "AMAZON" + }, + { + "ip_prefix": "52.94.96.0/20", + "region": "ca-central-1", + "service": "AMAZON" + }, + { + "ip_prefix": "52.94.112.0/22", + "region": "eu-west-2", + "service": "AMAZON" + }, + { + "ip_prefix": "52.94.192.0/22", + "region": "us-east-1", + "service": "AMAZON" + }, + { + "ip_prefix": "52.94.196.0/24", + "region": "eu-west-1", + "service": "AMAZON" + }, + { + "ip_prefix": "52.94.197.0/24", + "region": "us-west-2", + "service": "AMAZON" + }, + { + "ip_prefix": "52.94.198.0/28", + "region": "us-west-1", + "service": "AMAZON" + }, + { + "ip_prefix": "52.94.198.16/28", + "region": "sa-east-1", + "service": "AMAZON" + }, + { + "ip_prefix": "52.94.198.32/28", + "region": "us-gov-west-1", + "service": "AMAZON" + }, + { + "ip_prefix": "52.94.198.48/28", + "region": "eu-central-1", + "service": "AMAZON" + }, + { + "ip_prefix": "52.94.198.64/28", + "region": "ap-northeast-2", + "service": "AMAZON" + }, + { + "ip_prefix": "52.94.198.80/28", + "region": "ap-south-1", + "service": "AMAZON" + }, + { + "ip_prefix": "52.94.198.96/28", + "region": "ap-southeast-1", + "service": "AMAZON" + }, + { + "ip_prefix": "52.94.198.112/28", + "region": "ap-southeast-2", + "service": "AMAZON" + }, + { + "ip_prefix": "52.94.198.128/28", + "region": "ca-central-1", + "service": "AMAZON" + }, + { + "ip_prefix": "52.94.198.144/28", + "region": "eu-west-2", + "service": "AMAZON" + }, + { + "ip_prefix": "52.94.199.0/24", + "region": "us-east-2", + "service": "AMAZON" + }, + { + "ip_prefix": "52.94.200.0/24", + "region": "ap-northeast-1", + "service": "AMAZON" + }, + { + "ip_prefix": "52.94.204.0/23", + "region": "eu-central-1", + "service": "AMAZON" + }, + { + "ip_prefix": "52.94.206.0/23", + "region": "sa-east-1", + "service": "AMAZON" + }, + { + "ip_prefix": "52.94.208.0/21", + "region": "us-west-2", + "service": "AMAZON" + }, + { + "ip_prefix": "52.94.216.0/21", + "region": "eu-west-1", + "service": "AMAZON" + }, + { + "ip_prefix": "52.94.224.0/20", + "region": "us-east-1", + "service": "AMAZON" + }, + { + "ip_prefix": "52.94.240.0/22", + "region": "us-east-1", + "service": "AMAZON" + }, + { + "ip_prefix": "52.94.244.0/22", + "region": "us-east-1", + "service": "AMAZON" + }, + { + "ip_prefix": "52.94.248.0/28", + "region": "us-east-1", + "service": "AMAZON" + }, + { + "ip_prefix": "52.94.248.16/28", + "region": "eu-west-1", + "service": "AMAZON" + }, + { + "ip_prefix": "52.94.248.32/28", + "region": "ap-southeast-1", + "service": "AMAZON" + }, + { + "ip_prefix": "52.94.248.48/28", + "region": "sa-east-1", + "service": "AMAZON" + }, + { + "ip_prefix": "52.94.248.64/28", + "region": "ap-southeast-2", + "service": "AMAZON" + }, + { + "ip_prefix": "52.94.248.80/28", + "region": "ap-northeast-1", + "service": "AMAZON" + }, + { + "ip_prefix": "52.94.248.96/28", + "region": "us-west-2", + "service": "AMAZON" + }, + { + "ip_prefix": "52.94.248.112/28", + "region": "eu-central-1", + "service": "AMAZON" + }, + { + "ip_prefix": "52.94.248.128/28", + "region": "us-west-1", + "service": "AMAZON" + }, + { + "ip_prefix": "52.94.248.144/28", + "region": "ap-south-1", + "service": "AMAZON" + }, + { + "ip_prefix": "52.94.248.160/28", + "region": "us-east-2", + "service": "AMAZON" + }, + { + "ip_prefix": "52.94.248.176/28", + "region": "ap-northeast-2", + "service": "AMAZON" + }, + { + "ip_prefix": "52.94.248.192/28", + "region": "eu-west-2", + "service": "AMAZON" + }, + { + "ip_prefix": "52.94.248.208/28", + "region": "ca-central-1", + "service": "AMAZON" + }, + { + "ip_prefix": "52.94.248.224/28", + "region": "us-gov-west-1", + "service": "AMAZON" + }, + { + "ip_prefix": "52.94.249.0/28", + "region": "cn-north-1", + "service": "AMAZON" + }, + { + "ip_prefix": "52.94.249.16/28", + "region": "cn-northwest-1", + "service": "AMAZON" + }, + { + "ip_prefix": "52.94.249.32/28", + "region": "eu-west-3", + "service": "AMAZON" + }, + { + "ip_prefix": "52.94.249.64/28", + "region": "us-west-2", + "service": "AMAZON" + }, + { + "ip_prefix": "52.94.249.80/28", + "region": "us-west-1", + "service": "AMAZON" + }, + { + "ip_prefix": "52.94.252.0/23", + "region": "us-east-1", + "service": "AMAZON" + }, + { + "ip_prefix": "52.94.254.0/23", + "region": "us-east-1", + "service": "AMAZON" + }, + { + "ip_prefix": "52.95.0.0/20", + "region": "us-east-2", + "service": "AMAZON" + }, + { + "ip_prefix": "52.95.16.0/21", + "region": "us-east-2", + "service": "AMAZON" + }, + { + "ip_prefix": "52.95.24.0/22", + "region": "us-east-2", + "service": "AMAZON" + }, + { + "ip_prefix": "52.95.28.0/24", + "region": "us-east-2", + "service": "AMAZON" + }, + { + "ip_prefix": "52.95.30.0/23", + "region": "ap-northeast-1", + "service": "AMAZON" + }, + { + "ip_prefix": "52.95.34.0/24", + "region": "ap-northeast-1", + "service": "AMAZON" + }, + { + "ip_prefix": "52.95.35.0/24", + "region": "ap-southeast-1", + "service": "AMAZON" + }, + { + "ip_prefix": "52.95.36.0/22", + "region": "ap-southeast-2", + "service": "AMAZON" + }, + { + "ip_prefix": "52.95.40.0/24", + "region": "us-west-2", + "service": "AMAZON" + }, + { + "ip_prefix": "52.95.48.0/22", + "region": "us-east-1", + "service": "AMAZON" + }, + { + "ip_prefix": "52.95.56.0/22", + "region": "ap-northeast-1", + "service": "AMAZON" + }, + { + "ip_prefix": "52.95.60.0/24", + "region": "eu-west-1", + "service": "AMAZON" + }, + { + "ip_prefix": "52.95.61.0/24", + "region": "eu-west-1", + "service": "AMAZON" + }, + { + "ip_prefix": "52.95.62.0/24", + "region": "us-east-1", + "service": "AMAZON" + }, + { + "ip_prefix": "52.95.63.0/24", + "region": "us-east-1", + "service": "AMAZON" + }, + { + "ip_prefix": "52.95.64.0/20", + "region": "ap-south-1", + "service": "AMAZON" + }, + { + "ip_prefix": "52.95.80.0/20", + "region": "ap-south-1", + "service": "AMAZON" + }, + { + "ip_prefix": "52.95.96.0/22", + "region": "ap-south-1", + "service": "AMAZON" + }, + { + "ip_prefix": "52.95.100.0/22", + "region": "us-gov-west-1", + "service": "AMAZON" + }, + { + "ip_prefix": "52.95.104.0/22", + "region": "eu-west-1", + "service": "AMAZON" + }, + { + "ip_prefix": "52.95.108.0/23", + "region": "us-east-1", + "service": "AMAZON" + }, + { + "ip_prefix": "52.95.110.0/24", + "region": "GLOBAL", + "service": "AMAZON" + }, + { + "ip_prefix": "52.95.111.0/24", + "region": "ap-northeast-2", + "service": "AMAZON" + }, + { + "ip_prefix": "52.95.112.0/20", + "region": "eu-west-1", + "service": "AMAZON" + }, + { + "ip_prefix": "52.95.128.0/21", + "region": "ap-southeast-2", + "service": "AMAZON" + }, + { + "ip_prefix": "52.95.136.0/23", + "region": "sa-east-1", + "service": "AMAZON" + }, + { + "ip_prefix": "52.95.138.0/24", + "region": "sa-east-1", + "service": "AMAZON" + }, + { + "ip_prefix": "52.95.142.0/23", + "region": "us-gov-west-1", + "service": "AMAZON" + }, + { + "ip_prefix": "52.95.144.0/24", + "region": "us-gov-west-1", + "service": "AMAZON" + }, + { + "ip_prefix": "52.95.145.0/24", + "region": "ca-central-1", + "service": "AMAZON" + }, + { + "ip_prefix": "52.95.146.0/23", + "region": "ca-central-1", + "service": "AMAZON" + }, + { + "ip_prefix": "52.95.148.0/23", + "region": "eu-west-2", + "service": "AMAZON" + }, + { + "ip_prefix": "52.95.150.0/24", + "region": "eu-west-2", + "service": "AMAZON" + }, + { + "ip_prefix": "52.95.154.0/23", + "region": "eu-west-3", + "service": "AMAZON" + }, + { + "ip_prefix": "52.95.156.0/24", + "region": "eu-west-3", + "service": "AMAZON" + }, + { + "ip_prefix": "52.95.192.0/20", + "region": "ap-northeast-2", + "service": "AMAZON" + }, + { + "ip_prefix": "52.95.212.0/22", + "region": "ap-southeast-1", + "service": "AMAZON" + }, + { + "ip_prefix": "52.95.240.0/24", + "region": "sa-east-1", + "service": "AMAZON" + }, + { + "ip_prefix": "52.95.241.0/24", + "region": "ap-southeast-2", + "service": "AMAZON" + }, + { + "ip_prefix": "52.95.242.0/24", + "region": "ap-southeast-1", + "service": "AMAZON" + }, + { + "ip_prefix": "52.95.243.0/24", + "region": "ap-northeast-1", + "service": "AMAZON" + }, + { + "ip_prefix": "52.95.244.0/24", + "region": "eu-west-1", + "service": "AMAZON" + }, + { + "ip_prefix": "52.95.245.0/24", + "region": "us-east-1", + "service": "AMAZON" + }, + { + "ip_prefix": "52.95.246.0/24", + "region": "us-west-1", + "service": "AMAZON" + }, + { + "ip_prefix": "52.95.247.0/24", + "region": "us-west-2", + "service": "AMAZON" + }, + { + "ip_prefix": "52.95.248.0/24", + "region": "eu-central-1", + "service": "AMAZON" + }, + { + "ip_prefix": "52.95.249.0/24", + "region": "ap-south-1", + "service": "AMAZON" + }, + { + "ip_prefix": "52.95.250.0/24", + "region": "ca-central-1", + "service": "AMAZON" + }, + { + "ip_prefix": "52.95.251.0/24", + "region": "us-east-2", + "service": "AMAZON" + }, + { + "ip_prefix": "52.95.252.0/24", + "region": "ap-northeast-2", + "service": "AMAZON" + }, + { + "ip_prefix": "52.95.253.0/24", + "region": "eu-west-2", + "service": "AMAZON" + }, + { + "ip_prefix": "52.95.254.0/24", + "region": "eu-west-3", + "service": "AMAZON" + }, + { + "ip_prefix": "52.95.255.0/28", + "region": "sa-east-1", + "service": "AMAZON" + }, + { + "ip_prefix": "52.95.255.16/28", + "region": "ap-southeast-2", + "service": "AMAZON" + }, + { + "ip_prefix": "52.95.255.32/28", + "region": "ap-southeast-1", + "service": "AMAZON" + }, + { + "ip_prefix": "52.95.255.48/28", + "region": "ap-northeast-1", + "service": "AMAZON" + }, + { + "ip_prefix": "52.95.255.64/28", + "region": "eu-west-1", + "service": "AMAZON" + }, + { + "ip_prefix": "52.95.255.80/28", + "region": "us-east-1", + "service": "AMAZON" + }, + { + "ip_prefix": "52.95.255.96/28", + "region": "us-west-1", + "service": "AMAZON" + }, + { + "ip_prefix": "52.95.255.112/28", + "region": "us-west-2", + "service": "AMAZON" + }, + { + "ip_prefix": "52.95.255.128/28", + "region": "eu-central-1", + "service": "AMAZON" + }, + { + "ip_prefix": "52.95.255.144/28", + "region": "cn-north-1", + "service": "AMAZON" + }, + { + "ip_prefix": "52.119.160.0/20", + "region": "us-west-2", + "service": "AMAZON" + }, + { + "ip_prefix": "52.119.176.0/21", + "region": "us-west-1", + "service": "AMAZON" + }, + { + "ip_prefix": "52.119.184.0/22", + "region": "ap-southeast-1", + "service": "AMAZON" + }, + { + "ip_prefix": "52.119.188.0/22", + "region": "eu-central-1", + "service": "AMAZON" + }, + { + "ip_prefix": "52.119.192.0/22", + "region": "eu-west-1", + "service": "AMAZON" + }, + { + "ip_prefix": "52.119.196.0/22", + "region": "us-east-1", + "service": "AMAZON" + }, + { + "ip_prefix": "52.119.206.0/23", + "region": "us-east-1", + "service": "AMAZON" + }, + { + "ip_prefix": "52.119.208.0/23", + "region": "us-gov-west-1", + "service": "AMAZON" + }, + { + "ip_prefix": "52.119.212.0/23", + "region": "us-east-1", + "service": "AMAZON" + }, + { + "ip_prefix": "52.119.214.0/23", + "region": "us-east-1", + "service": "AMAZON" + }, + { + "ip_prefix": "52.119.216.0/21", + "region": "ap-northeast-1", + "service": "AMAZON" + }, + { + "ip_prefix": "52.119.224.0/21", + "region": "us-east-1", + "service": "AMAZON" + }, + { + "ip_prefix": "52.119.232.0/21", + "region": "us-east-1", + "service": "AMAZON" + }, + { + "ip_prefix": "52.192.0.0/15", + "region": "ap-northeast-1", + "service": "AMAZON" + }, + { + "ip_prefix": "52.196.0.0/14", + "region": "ap-northeast-1", + "service": "AMAZON" + }, + { + "ip_prefix": "52.200.0.0/13", + "region": "us-east-1", + "service": "AMAZON" + }, + { + "ip_prefix": "52.208.0.0/13", + "region": "eu-west-1", + "service": "AMAZON" + }, + { + "ip_prefix": "52.216.0.0/15", + "region": "us-east-1", + "service": "AMAZON" + }, + { + "ip_prefix": "52.218.0.0/17", + "region": "eu-west-1", + "service": "AMAZON" + }, + { + "ip_prefix": "52.218.128.0/17", + "region": "us-west-2", + "service": "AMAZON" + }, + { + "ip_prefix": "52.219.0.0/20", + "region": "ap-northeast-1", + "service": "AMAZON" + }, + { + "ip_prefix": "52.219.16.0/22", + "region": "ap-northeast-1", + "service": "AMAZON" + }, + { + "ip_prefix": "52.219.20.0/22", + "region": "us-west-1", + "service": "AMAZON" + }, + { + "ip_prefix": "52.219.24.0/21", + "region": "us-west-1", + "service": "AMAZON" + }, + { + "ip_prefix": "52.219.32.0/21", + "region": "ap-southeast-1", + "service": "AMAZON" + }, + { + "ip_prefix": "52.219.40.0/22", + "region": "ap-southeast-1", + "service": "AMAZON" + }, + { + "ip_prefix": "52.219.44.0/22", + "region": "eu-central-1", + "service": "AMAZON" + }, + { + "ip_prefix": "52.219.56.0/22", + "region": "ap-northeast-2", + "service": "AMAZON" + }, + { + "ip_prefix": "52.219.60.0/23", + "region": "ap-northeast-2", + "service": "AMAZON" + }, + { + "ip_prefix": "52.219.62.0/23", + "region": "ap-south-1", + "service": "AMAZON" + }, + { + "ip_prefix": "52.219.64.0/22", + "region": "ap-south-1", + "service": "AMAZON" + }, + { + "ip_prefix": "52.219.68.0/22", + "region": "ap-northeast-1", + "service": "AMAZON" + }, + { + "ip_prefix": "52.219.72.0/22", + "region": "eu-central-1", + "service": "AMAZON" + }, + { + "ip_prefix": "52.219.76.0/22", + "region": "ap-southeast-1", + "service": "AMAZON" + }, + { + "ip_prefix": "52.219.80.0/20", + "region": "us-east-2", + "service": "AMAZON" + }, + { + "ip_prefix": "52.220.0.0/15", + "region": "ap-southeast-1", + "service": "AMAZON" + }, + { + "ip_prefix": "52.222.0.0/17", + "region": "us-gov-west-1", + "service": "AMAZON" + }, + { + "ip_prefix": "52.222.128.0/17", + "region": "GLOBAL", + "service": "AMAZON" + }, + { + "ip_prefix": "54.64.0.0/15", + "region": "ap-northeast-1", + "service": "AMAZON" + }, + { + "ip_prefix": "54.66.0.0/16", + "region": "ap-southeast-2", + "service": "AMAZON" + }, + { + "ip_prefix": "54.67.0.0/16", + "region": "us-west-1", + "service": "AMAZON" + }, + { + "ip_prefix": "54.68.0.0/14", + "region": "us-west-2", + "service": "AMAZON" + }, + { + "ip_prefix": "54.72.0.0/15", + "region": "eu-west-1", + "service": "AMAZON" + }, + { + "ip_prefix": "54.74.0.0/15", + "region": "eu-west-1", + "service": "AMAZON" + }, + { + "ip_prefix": "54.76.0.0/15", + "region": "eu-west-1", + "service": "AMAZON" + }, + { + "ip_prefix": "54.78.0.0/16", + "region": "eu-west-1", + "service": "AMAZON" + }, + { + "ip_prefix": "54.79.0.0/16", + "region": "ap-southeast-2", + "service": "AMAZON" + }, + { + "ip_prefix": "54.80.0.0/13", + "region": "us-east-1", + "service": "AMAZON" + }, + { + "ip_prefix": "54.88.0.0/14", + "region": "us-east-1", + "service": "AMAZON" + }, + { + "ip_prefix": "54.92.0.0/17", + "region": "ap-northeast-1", + "service": "AMAZON" + }, + { + "ip_prefix": "54.92.128.0/17", + "region": "us-east-1", + "service": "AMAZON" + }, + { + "ip_prefix": "54.93.0.0/16", + "region": "eu-central-1", + "service": "AMAZON" + }, + { + "ip_prefix": "54.94.0.0/16", + "region": "sa-east-1", + "service": "AMAZON" + }, + { + "ip_prefix": "54.95.0.0/16", + "region": "ap-northeast-1", + "service": "AMAZON" + }, + { + "ip_prefix": "54.144.0.0/14", + "region": "us-east-1", + "service": "AMAZON" + }, + { + "ip_prefix": "54.148.0.0/15", + "region": "us-west-2", + "service": "AMAZON" + }, + { + "ip_prefix": "54.150.0.0/16", + "region": "ap-northeast-1", + "service": "AMAZON" + }, + { + "ip_prefix": "54.151.0.0/17", + "region": "us-west-1", + "service": "AMAZON" + }, + { + "ip_prefix": "54.151.128.0/17", + "region": "ap-southeast-1", + "service": "AMAZON" + }, + { + "ip_prefix": "54.152.0.0/16", + "region": "us-east-1", + "service": "AMAZON" + }, + { + "ip_prefix": "54.153.0.0/17", + "region": "us-west-1", + "service": "AMAZON" + }, + { + "ip_prefix": "54.153.128.0/17", + "region": "ap-southeast-2", + "service": "AMAZON" + }, + { + "ip_prefix": "54.154.0.0/16", + "region": "eu-west-1", + "service": "AMAZON" + }, + { + "ip_prefix": "54.155.0.0/16", + "region": "eu-west-1", + "service": "AMAZON" + }, + { + "ip_prefix": "54.156.0.0/14", + "region": "us-east-1", + "service": "AMAZON" + }, + { + "ip_prefix": "54.160.0.0/13", + "region": "us-east-1", + "service": "AMAZON" + }, + { + "ip_prefix": "54.168.0.0/16", + "region": "ap-northeast-1", + "service": "AMAZON" + }, + { + "ip_prefix": "54.169.0.0/16", + "region": "ap-southeast-1", + "service": "AMAZON" + }, + { + "ip_prefix": "54.170.0.0/15", + "region": "eu-west-1", + "service": "AMAZON" + }, + { + "ip_prefix": "54.172.0.0/15", + "region": "us-east-1", + "service": "AMAZON" + }, + { + "ip_prefix": "54.174.0.0/15", + "region": "us-east-1", + "service": "AMAZON" + }, + { + "ip_prefix": "54.176.0.0/15", + "region": "us-west-1", + "service": "AMAZON" + }, + { + "ip_prefix": "54.178.0.0/16", + "region": "ap-northeast-1", + "service": "AMAZON" + }, + { + "ip_prefix": "54.179.0.0/16", + "region": "ap-southeast-1", + "service": "AMAZON" + }, + { + "ip_prefix": "54.182.0.0/16", + "region": "GLOBAL", + "service": "AMAZON" + }, + { + "ip_prefix": "54.183.0.0/16", + "region": "us-west-1", + "service": "AMAZON" + }, + { + "ip_prefix": "54.184.0.0/13", + "region": "us-west-2", + "service": "AMAZON" + }, + { + "ip_prefix": "54.192.0.0/16", + "region": "GLOBAL", + "service": "AMAZON" + }, + { + "ip_prefix": "54.193.0.0/16", + "region": "us-west-1", + "service": "AMAZON" + }, + { + "ip_prefix": "54.194.0.0/15", + "region": "eu-west-1", + "service": "AMAZON" + }, + { + "ip_prefix": "54.196.0.0/15", + "region": "us-east-1", + "service": "AMAZON" + }, + { + "ip_prefix": "54.198.0.0/16", + "region": "us-east-1", + "service": "AMAZON" + }, + { + "ip_prefix": "54.199.0.0/16", + "region": "ap-northeast-1", + "service": "AMAZON" + }, + { + "ip_prefix": "54.200.0.0/15", + "region": "us-west-2", + "service": "AMAZON" + }, + { + "ip_prefix": "54.202.0.0/15", + "region": "us-west-2", + "service": "AMAZON" + }, + { + "ip_prefix": "54.204.0.0/15", + "region": "us-east-1", + "service": "AMAZON" + }, + { + "ip_prefix": "54.206.0.0/16", + "region": "ap-southeast-2", + "service": "AMAZON" + }, + { + "ip_prefix": "54.207.0.0/16", + "region": "sa-east-1", + "service": "AMAZON" + }, + { + "ip_prefix": "54.208.0.0/15", + "region": "us-east-1", + "service": "AMAZON" + }, + { + "ip_prefix": "54.210.0.0/15", + "region": "us-east-1", + "service": "AMAZON" + }, + { + "ip_prefix": "54.212.0.0/15", + "region": "us-west-2", + "service": "AMAZON" + }, + { + "ip_prefix": "54.214.0.0/16", + "region": "us-west-2", + "service": "AMAZON" + }, + { + "ip_prefix": "54.215.0.0/16", + "region": "us-west-1", + "service": "AMAZON" + }, + { + "ip_prefix": "54.216.0.0/15", + "region": "eu-west-1", + "service": "AMAZON" + }, + { + "ip_prefix": "54.218.0.0/16", + "region": "us-west-2", + "service": "AMAZON" + }, + { + "ip_prefix": "54.219.0.0/16", + "region": "us-west-1", + "service": "AMAZON" + }, + { + "ip_prefix": "54.220.0.0/16", + "region": "eu-west-1", + "service": "AMAZON" + }, + { + "ip_prefix": "54.221.0.0/16", + "region": "us-east-1", + "service": "AMAZON" + }, + { + "ip_prefix": "54.222.0.0/19", + "region": "cn-north-1", + "service": "AMAZON" + }, + { + "ip_prefix": "54.222.48.0/22", + "region": "cn-north-1", + "service": "AMAZON" + }, + { + "ip_prefix": "54.222.57.0/24", + "region": "cn-north-1", + "service": "AMAZON" + }, + { + "ip_prefix": "54.222.58.0/28", + "region": "cn-north-1", + "service": "AMAZON" + }, + { + "ip_prefix": "54.222.128.0/17", + "region": "cn-north-1", + "service": "AMAZON" + }, + { + "ip_prefix": "54.223.0.0/16", + "region": "cn-north-1", + "service": "AMAZON" + }, + { + "ip_prefix": "54.224.0.0/15", + "region": "us-east-1", + "service": "AMAZON" + }, + { + "ip_prefix": "54.226.0.0/15", + "region": "us-east-1", + "service": "AMAZON" + }, + { + "ip_prefix": "54.228.0.0/16", + "region": "eu-west-1", + "service": "AMAZON" + }, + { + "ip_prefix": "54.229.0.0/16", + "region": "eu-west-1", + "service": "AMAZON" + }, + { + "ip_prefix": "54.230.0.0/16", + "region": "GLOBAL", + "service": "AMAZON" + }, + { + "ip_prefix": "54.231.0.0/17", + "region": "us-east-1", + "service": "AMAZON" + }, + { + "ip_prefix": "54.231.128.0/19", + "region": "eu-west-1", + "service": "AMAZON" + }, + { + "ip_prefix": "54.231.160.0/19", + "region": "us-west-2", + "service": "AMAZON" + }, + { + "ip_prefix": "54.231.192.0/20", + "region": "eu-central-1", + "service": "AMAZON" + }, + { + "ip_prefix": "54.231.224.0/21", + "region": "ap-northeast-1", + "service": "AMAZON" + }, + { + "ip_prefix": "54.231.232.0/21", + "region": "us-west-1", + "service": "AMAZON" + }, + { + "ip_prefix": "54.231.240.0/22", + "region": "ap-southeast-1", + "service": "AMAZON" + }, + { + "ip_prefix": "54.231.244.0/22", + "region": "us-east-1", + "service": "AMAZON" + }, + { + "ip_prefix": "54.231.248.0/22", + "region": "ap-southeast-2", + "service": "AMAZON" + }, + { + "ip_prefix": "54.231.252.0/24", + "region": "ap-southeast-2", + "service": "AMAZON" + }, + { + "ip_prefix": "54.231.253.0/24", + "region": "sa-east-1", + "service": "AMAZON" + }, + { + "ip_prefix": "54.232.0.0/16", + "region": "sa-east-1", + "service": "AMAZON" + }, + { + "ip_prefix": "54.233.0.0/18", + "region": "sa-east-1", + "service": "AMAZON" + }, + { + "ip_prefix": "54.233.64.0/18", + "region": "sa-east-1", + "service": "AMAZON" + }, + { + "ip_prefix": "54.233.128.0/17", + "region": "sa-east-1", + "service": "AMAZON" + }, + { + "ip_prefix": "54.234.0.0/15", + "region": "us-east-1", + "service": "AMAZON" + }, + { + "ip_prefix": "54.236.0.0/15", + "region": "us-east-1", + "service": "AMAZON" + }, + { + "ip_prefix": "54.238.0.0/16", + "region": "ap-northeast-1", + "service": "AMAZON" + }, + { + "ip_prefix": "54.239.0.0/28", + "region": "us-east-1", + "service": "AMAZON" + }, + { + "ip_prefix": "54.239.0.16/28", + "region": "us-west-1", + "service": "AMAZON" + }, + { + "ip_prefix": "54.239.0.32/28", + "region": "us-west-2", + "service": "AMAZON" + }, + { + "ip_prefix": "54.239.0.48/28", + "region": "eu-west-1", + "service": "AMAZON" + }, + { + "ip_prefix": "54.239.0.64/28", + "region": "sa-east-1", + "service": "AMAZON" + }, + { + "ip_prefix": "54.239.0.80/28", + "region": "ap-northeast-1", + "service": "AMAZON" + }, + { + "ip_prefix": "54.239.0.96/28", + "region": "ap-southeast-1", + "service": "AMAZON" + }, + { + "ip_prefix": "54.239.0.112/28", + "region": "ap-southeast-2", + "service": "AMAZON" + }, + { + "ip_prefix": "54.239.0.128/28", + "region": "us-gov-west-1", + "service": "AMAZON" + }, + { + "ip_prefix": "54.239.0.144/28", + "region": "cn-north-1", + "service": "AMAZON" + }, + { + "ip_prefix": "54.239.0.160/28", + "region": "eu-central-1", + "service": "AMAZON" + }, + { + "ip_prefix": "54.239.0.176/28", + "region": "cn-northwest-1", + "service": "AMAZON" + }, + { + "ip_prefix": "54.239.0.192/28", + "region": "ap-northeast-2", + "service": "AMAZON" + }, + { + "ip_prefix": "54.239.0.208/28", + "region": "ap-south-1", + "service": "AMAZON" + }, + { + "ip_prefix": "54.239.0.224/28", + "region": "us-east-2", + "service": "AMAZON" + }, + { + "ip_prefix": "54.239.0.240/28", + "region": "eu-west-2", + "service": "AMAZON" + }, + { + "ip_prefix": "54.239.1.0/28", + "region": "ca-central-1", + "service": "AMAZON" + }, + { + "ip_prefix": "54.239.1.16/28", + "region": "eu-west-3", + "service": "AMAZON" + }, + { + "ip_prefix": "54.239.2.0/23", + "region": "us-west-2", + "service": "AMAZON" + }, + { + "ip_prefix": "54.239.4.0/22", + "region": "eu-central-1", + "service": "AMAZON" + }, + { + "ip_prefix": "54.239.8.0/21", + "region": "us-east-1", + "service": "AMAZON" + }, + { + "ip_prefix": "54.239.16.0/20", + "region": "us-east-1", + "service": "AMAZON" + }, + { + "ip_prefix": "54.239.32.0/21", + "region": "eu-west-1", + "service": "AMAZON" + }, + { + "ip_prefix": "54.239.48.0/22", + "region": "us-west-2", + "service": "AMAZON" + }, + { + "ip_prefix": "54.239.52.0/23", + "region": "ap-northeast-1", + "service": "AMAZON" + }, + { + "ip_prefix": "54.239.54.0/23", + "region": "eu-central-1", + "service": "AMAZON" + }, + { + "ip_prefix": "54.239.56.0/21", + "region": "eu-central-1", + "service": "AMAZON" + }, + { + "ip_prefix": "54.239.96.0/24", + "region": "ap-northeast-1", + "service": "AMAZON" + }, + { + "ip_prefix": "54.239.98.0/24", + "region": "us-east-1", + "service": "AMAZON" + }, + { + "ip_prefix": "54.239.99.0/24", + "region": "eu-west-1", + "service": "AMAZON" + }, + { + "ip_prefix": "54.239.100.0/23", + "region": "eu-west-1", + "service": "AMAZON" + }, + { + "ip_prefix": "54.239.104.0/23", + "region": "us-east-1", + "service": "AMAZON" + }, + { + "ip_prefix": "54.239.108.0/22", + "region": "us-east-1", + "service": "AMAZON" + }, + { + "ip_prefix": "54.239.116.0/22", + "region": "ap-northeast-2", + "service": "AMAZON" + }, + { + "ip_prefix": "54.239.120.0/21", + "region": "ap-northeast-2", + "service": "AMAZON" + }, + { + "ip_prefix": "54.239.128.0/18", + "region": "GLOBAL", + "service": "AMAZON" + }, + { + "ip_prefix": "54.239.192.0/19", + "region": "GLOBAL", + "service": "AMAZON" + }, + { + "ip_prefix": "54.240.128.0/18", + "region": "GLOBAL", + "service": "AMAZON" + }, + { + "ip_prefix": "54.240.192.0/22", + "region": "ap-southeast-2", + "service": "AMAZON" + }, + { + "ip_prefix": "54.240.196.0/24", + "region": "us-east-1", + "service": "AMAZON" + }, + { + "ip_prefix": "54.240.197.0/24", + "region": "eu-west-1", + "service": "AMAZON" + }, + { + "ip_prefix": "54.240.198.0/24", + "region": "us-west-1", + "service": "AMAZON" + }, + { + "ip_prefix": "54.240.199.0/24", + "region": "ap-southeast-1", + "service": "AMAZON" + }, + { + "ip_prefix": "54.240.200.0/24", + "region": "ap-northeast-1", + "service": "AMAZON" + }, + { + "ip_prefix": "54.240.202.0/24", + "region": "us-east-1", + "service": "AMAZON" + }, + { + "ip_prefix": "54.240.203.0/24", + "region": "ap-southeast-2", + "service": "AMAZON" + }, + { + "ip_prefix": "54.240.204.0/22", + "region": "ap-southeast-2", + "service": "AMAZON" + }, + { + "ip_prefix": "54.240.208.0/22", + "region": "us-east-1", + "service": "AMAZON" + }, + { + "ip_prefix": "54.240.212.0/22", + "region": "us-west-1", + "service": "AMAZON" + }, + { + "ip_prefix": "54.240.216.0/22", + "region": "us-east-1", + "service": "AMAZON" + }, + { + "ip_prefix": "54.240.220.0/22", + "region": "eu-west-1", + "service": "AMAZON" + }, + { + "ip_prefix": "54.240.225.0/24", + "region": "ap-northeast-1", + "service": "AMAZON" + }, + { + "ip_prefix": "54.240.226.0/24", + "region": "ap-southeast-1", + "service": "AMAZON" + }, + { + "ip_prefix": "54.240.227.0/24", + "region": "ap-southeast-1", + "service": "AMAZON" + }, + { + "ip_prefix": "54.240.228.0/23", + "region": "us-east-1", + "service": "AMAZON" + }, + { + "ip_prefix": "54.240.230.0/23", + "region": "us-west-2", + "service": "AMAZON" + }, + { + "ip_prefix": "54.240.232.0/22", + "region": "us-east-1", + "service": "AMAZON" + }, + { + "ip_prefix": "54.240.244.0/22", + "region": "sa-east-1", + "service": "AMAZON" + }, + { + "ip_prefix": "54.240.248.0/21", + "region": "us-west-2", + "service": "AMAZON" + }, + { + "ip_prefix": "54.241.0.0/16", + "region": "us-west-1", + "service": "AMAZON" + }, + { + "ip_prefix": "54.242.0.0/15", + "region": "us-east-1", + "service": "AMAZON" + }, + { + "ip_prefix": "54.244.0.0/16", + "region": "us-west-2", + "service": "AMAZON" + }, + { + "ip_prefix": "54.245.0.0/16", + "region": "us-west-2", + "service": "AMAZON" + }, + { + "ip_prefix": "54.246.0.0/16", + "region": "eu-west-1", + "service": "AMAZON" + }, + { + "ip_prefix": "54.247.0.0/16", + "region": "eu-west-1", + "service": "AMAZON" + }, + { + "ip_prefix": "54.248.0.0/15", + "region": "ap-northeast-1", + "service": "AMAZON" + }, + { + "ip_prefix": "54.250.0.0/16", + "region": "ap-northeast-1", + "service": "AMAZON" + }, + { + "ip_prefix": "54.251.0.0/16", + "region": "ap-southeast-1", + "service": "AMAZON" + }, + { + "ip_prefix": "54.252.0.0/16", + "region": "ap-southeast-2", + "service": "AMAZON" + }, + { + "ip_prefix": "54.253.0.0/16", + "region": "ap-southeast-2", + "service": "AMAZON" + }, + { + "ip_prefix": "54.254.0.0/16", + "region": "ap-southeast-1", + "service": "AMAZON" + }, + { + "ip_prefix": "54.255.0.0/16", + "region": "ap-southeast-1", + "service": "AMAZON" + }, + { + "ip_prefix": "67.202.0.0/18", + "region": "us-east-1", + "service": "AMAZON" + }, + { + "ip_prefix": "72.21.192.0/19", + "region": "us-east-1", + "service": "AMAZON" + }, + { + "ip_prefix": "72.44.32.0/19", + "region": "us-east-1", + "service": "AMAZON" + }, + { + "ip_prefix": "75.101.128.0/17", + "region": "us-east-1", + "service": "AMAZON" + }, + { + "ip_prefix": "79.125.0.0/17", + "region": "eu-west-1", + "service": "AMAZON" + }, + { + "ip_prefix": "87.238.80.0/21", + "region": "eu-west-1", + "service": "AMAZON" + }, + { + "ip_prefix": "96.127.0.0/17", + "region": "us-gov-west-1", + "service": "AMAZON" + }, + { + "ip_prefix": "103.4.8.0/22", + "region": "ap-northeast-1", + "service": "AMAZON" + }, + { + "ip_prefix": "103.4.12.0/22", + "region": "ap-northeast-1", + "service": "AMAZON" + }, + { + "ip_prefix": "103.8.172.0/22", + "region": "ap-southeast-2", + "service": "AMAZON" + }, + { + "ip_prefix": "103.246.148.0/23", + "region": "ap-southeast-1", + "service": "AMAZON" + }, + { + "ip_prefix": "103.246.150.0/23", + "region": "ap-northeast-1", + "service": "AMAZON" + }, + { + "ip_prefix": "107.20.0.0/14", + "region": "us-east-1", + "service": "AMAZON" + }, + { + "ip_prefix": "122.248.192.0/18", + "region": "ap-southeast-1", + "service": "AMAZON" + }, + { + "ip_prefix": "172.96.97.0/24", + "region": "us-east-1", + "service": "AMAZON" + }, + { + "ip_prefix": "172.96.98.0/24", + "region": "eu-west-1", + "service": "AMAZON" + }, + { + "ip_prefix": "174.129.0.0/16", + "region": "us-east-1", + "service": "AMAZON" + }, + { + "ip_prefix": "175.41.128.0/18", + "region": "ap-southeast-1", + "service": "AMAZON" + }, + { + "ip_prefix": "175.41.192.0/18", + "region": "ap-northeast-1", + "service": "AMAZON" + }, + { + "ip_prefix": "176.32.64.0/19", + "region": "ap-northeast-1", + "service": "AMAZON" + }, + { + "ip_prefix": "176.32.96.0/21", + "region": "us-east-1", + "service": "AMAZON" + }, + { + "ip_prefix": "176.32.104.0/21", + "region": "eu-west-1", + "service": "AMAZON" + }, + { + "ip_prefix": "176.32.112.0/21", + "region": "us-west-1", + "service": "AMAZON" + }, + { + "ip_prefix": "176.32.120.0/22", + "region": "us-east-1", + "service": "AMAZON" + }, + { + "ip_prefix": "176.32.125.0/25", + "region": "us-west-2", + "service": "AMAZON" + }, + { + "ip_prefix": "176.34.0.0/19", + "region": "ap-northeast-1", + "service": "AMAZON" + }, + { + "ip_prefix": "176.34.32.0/19", + "region": "ap-northeast-1", + "service": "AMAZON" + }, + { + "ip_prefix": "176.34.64.0/18", + "region": "eu-west-1", + "service": "AMAZON" + }, + { + "ip_prefix": "176.34.128.0/17", + "region": "eu-west-1", + "service": "AMAZON" + }, + { + "ip_prefix": "177.71.128.0/17", + "region": "sa-east-1", + "service": "AMAZON" + }, + { + "ip_prefix": "177.72.240.0/21", + "region": "sa-east-1", + "service": "AMAZON" + }, + { + "ip_prefix": "178.236.0.0/20", + "region": "eu-west-1", + "service": "AMAZON" + }, + { + "ip_prefix": "184.72.0.0/18", + "region": "us-west-1", + "service": "AMAZON" + }, + { + "ip_prefix": "184.72.64.0/18", + "region": "us-east-1", + "service": "AMAZON" + }, + { + "ip_prefix": "184.72.128.0/17", + "region": "us-east-1", + "service": "AMAZON" + }, + { + "ip_prefix": "184.73.0.0/16", + "region": "us-east-1", + "service": "AMAZON" + }, + { + "ip_prefix": "184.169.128.0/17", + "region": "us-west-1", + "service": "AMAZON" + }, + { + "ip_prefix": "185.48.120.0/22", + "region": "eu-west-1", + "service": "AMAZON" + }, + { + "ip_prefix": "185.143.16.0/24", + "region": "eu-west-1", + "service": "AMAZON" + }, + { + "ip_prefix": "203.83.220.0/22", + "region": "ap-southeast-1", + "service": "AMAZON" + }, + { + "ip_prefix": "204.236.128.0/18", + "region": "us-west-1", + "service": "AMAZON" + }, + { + "ip_prefix": "204.236.192.0/18", + "region": "us-east-1", + "service": "AMAZON" + }, + { + "ip_prefix": "204.246.160.0/22", + "region": "us-west-1", + "service": "AMAZON" + }, + { + "ip_prefix": "204.246.164.0/22", + "region": "GLOBAL", + "service": "AMAZON" + }, + { + "ip_prefix": "204.246.168.0/22", + "region": "GLOBAL", + "service": "AMAZON" + }, + { + "ip_prefix": "204.246.174.0/23", + "region": "GLOBAL", + "service": "AMAZON" + }, + { + "ip_prefix": "204.246.176.0/20", + "region": "GLOBAL", + "service": "AMAZON" + }, + { + "ip_prefix": "205.251.192.0/19", + "region": "GLOBAL", + "service": "AMAZON" + }, + { + "ip_prefix": "205.251.224.0/22", + "region": "us-east-1", + "service": "AMAZON" + }, + { + "ip_prefix": "205.251.228.0/22", + "region": "us-west-1", + "service": "AMAZON" + }, + { + "ip_prefix": "205.251.232.0/22", + "region": "us-west-2", + "service": "AMAZON" + }, + { + "ip_prefix": "205.251.236.0/22", + "region": "us-gov-west-1", + "service": "AMAZON" + }, + { + "ip_prefix": "205.251.240.0/22", + "region": "us-east-1", + "service": "AMAZON" + }, + { + "ip_prefix": "205.251.244.0/23", + "region": "us-east-1", + "service": "AMAZON" + }, + { + "ip_prefix": "205.251.247.0/24", + "region": "us-east-1", + "service": "AMAZON" + }, + { + "ip_prefix": "205.251.248.0/24", + "region": "us-east-1", + "service": "AMAZON" + }, + { + "ip_prefix": "205.251.249.0/24", + "region": "GLOBAL", + "service": "AMAZON" + }, + { + "ip_prefix": "205.251.250.0/23", + "region": "GLOBAL", + "service": "AMAZON" + }, + { + "ip_prefix": "205.251.252.0/23", + "region": "GLOBAL", + "service": "AMAZON" + }, + { + "ip_prefix": "205.251.254.0/24", + "region": "GLOBAL", + "service": "AMAZON" + }, + { + "ip_prefix": "207.171.160.0/20", + "region": "us-east-1", + "service": "AMAZON" + }, + { + "ip_prefix": "207.171.176.0/20", + "region": "us-east-1", + "service": "AMAZON" + }, + { + "ip_prefix": "216.137.32.0/19", + "region": "GLOBAL", + "service": "AMAZON" + }, + { + "ip_prefix": "216.182.224.0/20", + "region": "us-east-1", + "service": "AMAZON" + }, + { + "ip_prefix": "54.183.255.128/26", + "region": "us-west-1", + "service": "ROUTE53_HEALTHCHECKS" + }, + { + "ip_prefix": "54.228.16.0/26", + "region": "eu-west-1", + "service": "ROUTE53_HEALTHCHECKS" + }, + { + "ip_prefix": "54.232.40.64/26", + "region": "sa-east-1", + "service": "ROUTE53_HEALTHCHECKS" + }, + { + "ip_prefix": "54.241.32.64/26", + "region": "us-west-1", + "service": "ROUTE53_HEALTHCHECKS" + }, + { + "ip_prefix": "54.243.31.192/26", + "region": "us-east-1", + "service": "ROUTE53_HEALTHCHECKS" + }, + { + "ip_prefix": "54.244.52.192/26", + "region": "us-west-2", + "service": "ROUTE53_HEALTHCHECKS" + }, + { + "ip_prefix": "54.245.168.0/26", + "region": "us-west-2", + "service": "ROUTE53_HEALTHCHECKS" + }, + { + "ip_prefix": "54.248.220.0/26", + "region": "ap-northeast-1", + "service": "ROUTE53_HEALTHCHECKS" + }, + { + "ip_prefix": "54.250.253.192/26", + "region": "ap-northeast-1", + "service": "ROUTE53_HEALTHCHECKS" + }, + { + "ip_prefix": "54.251.31.128/26", + "region": "ap-southeast-1", + "service": "ROUTE53_HEALTHCHECKS" + }, + { + "ip_prefix": "54.252.79.128/26", + "region": "ap-southeast-2", + "service": "ROUTE53_HEALTHCHECKS" + }, + { + "ip_prefix": "54.252.254.192/26", + "region": "ap-southeast-2", + "service": "ROUTE53_HEALTHCHECKS" + }, + { + "ip_prefix": "54.255.254.192/26", + "region": "ap-southeast-1", + "service": "ROUTE53_HEALTHCHECKS" + }, + { + "ip_prefix": "107.23.255.0/26", + "region": "us-east-1", + "service": "ROUTE53_HEALTHCHECKS" + }, + { + "ip_prefix": "176.34.159.192/26", + "region": "eu-west-1", + "service": "ROUTE53_HEALTHCHECKS" + }, + { + "ip_prefix": "177.71.207.128/26", + "region": "sa-east-1", + "service": "ROUTE53_HEALTHCHECKS" + }, + { + "ip_prefix": "52.82.188.0/22", + "region": "cn-northwest-1", + "service": "S3" + }, + { + "ip_prefix": "52.92.0.0/20", + "region": "ap-northeast-2", + "service": "S3" + }, + { + "ip_prefix": "52.92.16.0/20", + "region": "us-east-1", + "service": "S3" + }, + { + "ip_prefix": "52.92.32.0/22", + "region": "us-west-2", + "service": "S3" + }, + { + "ip_prefix": "52.92.39.0/24", + "region": "sa-east-1", + "service": "S3" + }, + { + "ip_prefix": "52.92.40.0/21", + "region": "eu-west-1", + "service": "S3" + }, + { + "ip_prefix": "52.92.48.0/22", + "region": "us-west-1", + "service": "S3" + }, + { + "ip_prefix": "52.92.52.0/22", + "region": "ap-southeast-2", + "service": "S3" + }, + { + "ip_prefix": "52.92.56.0/22", + "region": "ap-southeast-1", + "service": "S3" + }, + { + "ip_prefix": "52.92.60.0/22", + "region": "ap-northeast-1", + "service": "S3" + }, + { + "ip_prefix": "52.92.64.0/22", + "region": "sa-east-1", + "service": "S3" + }, + { + "ip_prefix": "52.92.68.0/22", + "region": "eu-central-1", + "service": "S3" + }, + { + "ip_prefix": "52.92.72.0/22", + "region": "sa-east-1", + "service": "S3" + }, + { + "ip_prefix": "52.92.76.0/22", + "region": "us-east-2", + "service": "S3" + }, + { + "ip_prefix": "52.92.80.0/22", + "region": "ap-northeast-1", + "service": "S3" + }, + { + "ip_prefix": "52.92.84.0/22", + "region": "ca-central-1", + "service": "S3" + }, + { + "ip_prefix": "52.92.88.0/22", + "region": "eu-west-2", + "service": "S3" + }, + { + "ip_prefix": "52.92.248.0/22", + "region": "ap-south-1", + "service": "S3" + }, + { + "ip_prefix": "52.92.252.0/22", + "region": "us-gov-west-1", + "service": "S3" + }, + { + "ip_prefix": "52.95.128.0/21", + "region": "ap-southeast-2", + "service": "S3" + }, + { + "ip_prefix": "52.95.136.0/23", + "region": "sa-east-1", + "service": "S3" + }, + { + "ip_prefix": "52.95.138.0/24", + "region": "sa-east-1", + "service": "S3" + }, + { + "ip_prefix": "52.95.142.0/23", + "region": "us-gov-west-1", + "service": "S3" + }, + { + "ip_prefix": "52.95.144.0/24", + "region": "us-gov-west-1", + "service": "S3" + }, + { + "ip_prefix": "52.95.145.0/24", + "region": "ca-central-1", + "service": "S3" + }, + { + "ip_prefix": "52.95.146.0/23", + "region": "ca-central-1", + "service": "S3" + }, + { + "ip_prefix": "52.95.148.0/23", + "region": "eu-west-2", + "service": "S3" + }, + { + "ip_prefix": "52.95.150.0/24", + "region": "eu-west-2", + "service": "S3" + }, + { + "ip_prefix": "52.95.154.0/23", + "region": "eu-west-3", + "service": "S3" + }, + { + "ip_prefix": "52.95.156.0/24", + "region": "eu-west-3", + "service": "S3" + }, + { + "ip_prefix": "52.216.0.0/15", + "region": "us-east-1", + "service": "S3" + }, + { + "ip_prefix": "52.218.0.0/17", + "region": "eu-west-1", + "service": "S3" + }, + { + "ip_prefix": "52.218.128.0/17", + "region": "us-west-2", + "service": "S3" + }, + { + "ip_prefix": "52.219.0.0/20", + "region": "ap-northeast-1", + "service": "S3" + }, + { + "ip_prefix": "52.219.16.0/22", + "region": "ap-northeast-1", + "service": "S3" + }, + { + "ip_prefix": "52.219.20.0/22", + "region": "us-west-1", + "service": "S3" + }, + { + "ip_prefix": "52.219.24.0/21", + "region": "us-west-1", + "service": "S3" + }, + { + "ip_prefix": "52.219.32.0/21", + "region": "ap-southeast-1", + "service": "S3" + }, + { + "ip_prefix": "52.219.40.0/22", + "region": "ap-southeast-1", + "service": "S3" + }, + { + "ip_prefix": "52.219.44.0/22", + "region": "eu-central-1", + "service": "S3" + }, + { + "ip_prefix": "52.219.56.0/22", + "region": "ap-northeast-2", + "service": "S3" + }, + { + "ip_prefix": "52.219.60.0/23", + "region": "ap-northeast-2", + "service": "S3" + }, + { + "ip_prefix": "52.219.62.0/23", + "region": "ap-south-1", + "service": "S3" + }, + { + "ip_prefix": "52.219.64.0/22", + "region": "ap-south-1", + "service": "S3" + }, + { + "ip_prefix": "52.219.68.0/22", + "region": "ap-northeast-1", + "service": "S3" + }, + { + "ip_prefix": "52.219.72.0/22", + "region": "eu-central-1", + "service": "S3" + }, + { + "ip_prefix": "52.219.76.0/22", + "region": "ap-southeast-1", + "service": "S3" + }, + { + "ip_prefix": "52.219.80.0/20", + "region": "us-east-2", + "service": "S3" + }, + { + "ip_prefix": "54.222.20.0/22", + "region": "cn-north-1", + "service": "S3" + }, + { + "ip_prefix": "54.222.48.0/22", + "region": "cn-north-1", + "service": "S3" + }, + { + "ip_prefix": "54.231.0.0/17", + "region": "us-east-1", + "service": "S3" + }, + { + "ip_prefix": "54.231.128.0/19", + "region": "eu-west-1", + "service": "S3" + }, + { + "ip_prefix": "54.231.160.0/19", + "region": "us-west-2", + "service": "S3" + }, + { + "ip_prefix": "54.231.192.0/20", + "region": "eu-central-1", + "service": "S3" + }, + { + "ip_prefix": "54.231.224.0/21", + "region": "ap-northeast-1", + "service": "S3" + }, + { + "ip_prefix": "54.231.232.0/21", + "region": "us-west-1", + "service": "S3" + }, + { + "ip_prefix": "54.231.240.0/22", + "region": "ap-southeast-1", + "service": "S3" + }, + { + "ip_prefix": "54.231.248.0/22", + "region": "ap-southeast-2", + "service": "S3" + }, + { + "ip_prefix": "54.231.252.0/24", + "region": "ap-southeast-2", + "service": "S3" + }, + { + "ip_prefix": "54.231.253.0/24", + "region": "sa-east-1", + "service": "S3" + }, + { + "ip_prefix": "13.54.0.0/15", + "region": "ap-southeast-2", + "service": "EC2" + }, + { + "ip_prefix": "13.56.0.0/16", + "region": "us-west-1", + "service": "EC2" + }, + { + "ip_prefix": "13.57.0.0/16", + "region": "us-west-1", + "service": "EC2" + }, + { + "ip_prefix": "13.58.0.0/15", + "region": "us-east-2", + "service": "EC2" + }, + { + "ip_prefix": "13.112.0.0/14", + "region": "ap-northeast-1", + "service": "EC2" + }, + { + "ip_prefix": "13.124.0.0/16", + "region": "ap-northeast-2", + "service": "EC2" + }, + { + "ip_prefix": "13.125.0.0/16", + "region": "ap-northeast-2", + "service": "EC2" + }, + { + "ip_prefix": "13.126.0.0/15", + "region": "ap-south-1", + "service": "EC2" + }, + { + "ip_prefix": "13.209.0.0/16", + "region": "ap-northeast-2", + "service": "EC2" + }, + { + "ip_prefix": "13.210.0.0/15", + "region": "ap-southeast-2", + "service": "EC2" + }, + { + "ip_prefix": "13.228.0.0/15", + "region": "ap-southeast-1", + "service": "EC2" + }, + { + "ip_prefix": "13.230.0.0/15", + "region": "ap-northeast-1", + "service": "EC2" + }, + { + "ip_prefix": "13.232.0.0/14", + "region": "ap-south-1", + "service": "EC2" + }, + { + "ip_prefix": "13.236.0.0/14", + "region": "ap-southeast-2", + "service": "EC2" + }, + { + "ip_prefix": "13.250.0.0/15", + "region": "ap-southeast-1", + "service": "EC2" + }, + { + "ip_prefix": "18.194.0.0/15", + "region": "eu-central-1", + "service": "EC2" + }, + { + "ip_prefix": "18.196.0.0/15", + "region": "eu-central-1", + "service": "EC2" + }, + { + "ip_prefix": "18.216.0.0/14", + "region": "us-east-2", + "service": "EC2" + }, + { + "ip_prefix": "18.220.0.0/14", + "region": "us-east-2", + "service": "EC2" + }, + { + "ip_prefix": "18.231.0.0/16", + "region": "sa-east-1", + "service": "EC2" + }, + { + "ip_prefix": "23.20.0.0/14", + "region": "us-east-1", + "service": "EC2" + }, + { + "ip_prefix": "34.192.0.0/12", + "region": "us-east-1", + "service": "EC2" + }, + { + "ip_prefix": "34.208.0.0/12", + "region": "us-west-2", + "service": "EC2" + }, + { + "ip_prefix": "34.224.0.0/12", + "region": "us-east-1", + "service": "EC2" + }, + { + "ip_prefix": "34.240.0.0/13", + "region": "eu-west-1", + "service": "EC2" + }, + { + "ip_prefix": "34.248.0.0/13", + "region": "eu-west-1", + "service": "EC2" + }, + { + "ip_prefix": "35.154.0.0/16", + "region": "ap-south-1", + "service": "EC2" + }, + { + "ip_prefix": "35.155.0.0/16", + "region": "us-west-2", + "service": "EC2" + }, + { + "ip_prefix": "35.156.0.0/14", + "region": "eu-central-1", + "service": "EC2" + }, + { + "ip_prefix": "35.160.0.0/13", + "region": "us-west-2", + "service": "EC2" + }, + { + "ip_prefix": "35.168.0.0/13", + "region": "us-east-1", + "service": "EC2" + }, + { + "ip_prefix": "35.176.0.0/15", + "region": "eu-west-2", + "service": "EC2" + }, + { + "ip_prefix": "35.178.0.0/15", + "region": "eu-west-2", + "service": "EC2" + }, + { + "ip_prefix": "35.180.0.0/15", + "region": "ca-central-1", + "service": "EC2" + }, + { + "ip_prefix": "35.182.0.0/15", + "region": "ca-central-1", + "service": "EC2" + }, + { + "ip_prefix": "46.51.128.0/18", + "region": "eu-west-1", + "service": "EC2" + }, + { + "ip_prefix": "46.51.192.0/20", + "region": "eu-west-1", + "service": "EC2" + }, + { + "ip_prefix": "46.51.216.0/21", + "region": "ap-southeast-1", + "service": "EC2" + }, + { + "ip_prefix": "46.51.224.0/19", + "region": "ap-northeast-1", + "service": "EC2" + }, + { + "ip_prefix": "46.137.0.0/17", + "region": "eu-west-1", + "service": "EC2" + }, + { + "ip_prefix": "46.137.128.0/18", + "region": "eu-west-1", + "service": "EC2" + }, + { + "ip_prefix": "46.137.192.0/19", + "region": "ap-southeast-1", + "service": "EC2" + }, + { + "ip_prefix": "46.137.224.0/19", + "region": "ap-southeast-1", + "service": "EC2" + }, + { + "ip_prefix": "50.16.0.0/15", + "region": "us-east-1", + "service": "EC2" + }, + { + "ip_prefix": "50.18.0.0/16", + "region": "us-west-1", + "service": "EC2" + }, + { + "ip_prefix": "50.19.0.0/16", + "region": "us-east-1", + "service": "EC2" + }, + { + "ip_prefix": "50.112.0.0/16", + "region": "us-west-2", + "service": "EC2" + }, + { + "ip_prefix": "52.0.0.0/15", + "region": "us-east-1", + "service": "EC2" + }, + { + "ip_prefix": "52.2.0.0/15", + "region": "us-east-1", + "service": "EC2" + }, + { + "ip_prefix": "52.4.0.0/14", + "region": "us-east-1", + "service": "EC2" + }, + { + "ip_prefix": "52.8.0.0/16", + "region": "us-west-1", + "service": "EC2" + }, + { + "ip_prefix": "52.9.0.0/16", + "region": "us-west-1", + "service": "EC2" + }, + { + "ip_prefix": "52.10.0.0/15", + "region": "us-west-2", + "service": "EC2" + }, + { + "ip_prefix": "52.12.0.0/15", + "region": "us-west-2", + "service": "EC2" + }, + { + "ip_prefix": "52.14.0.0/16", + "region": "us-east-2", + "service": "EC2" + }, + { + "ip_prefix": "52.15.0.0/16", + "region": "us-east-2", + "service": "EC2" + }, + { + "ip_prefix": "52.16.0.0/15", + "region": "eu-west-1", + "service": "EC2" + }, + { + "ip_prefix": "52.18.0.0/15", + "region": "eu-west-1", + "service": "EC2" + }, + { + "ip_prefix": "52.20.0.0/14", + "region": "us-east-1", + "service": "EC2" + }, + { + "ip_prefix": "52.24.0.0/14", + "region": "us-west-2", + "service": "EC2" + }, + { + "ip_prefix": "52.28.0.0/16", + "region": "eu-central-1", + "service": "EC2" + }, + { + "ip_prefix": "52.29.0.0/16", + "region": "eu-central-1", + "service": "EC2" + }, + { + "ip_prefix": "52.30.0.0/15", + "region": "eu-west-1", + "service": "EC2" + }, + { + "ip_prefix": "52.32.0.0/14", + "region": "us-west-2", + "service": "EC2" + }, + { + "ip_prefix": "52.36.0.0/14", + "region": "us-west-2", + "service": "EC2" + }, + { + "ip_prefix": "52.40.0.0/14", + "region": "us-west-2", + "service": "EC2" + }, + { + "ip_prefix": "52.44.0.0/15", + "region": "us-east-1", + "service": "EC2" + }, + { + "ip_prefix": "52.47.0.0/16", + "region": "eu-west-3", + "service": "EC2" + }, + { + "ip_prefix": "52.48.0.0/14", + "region": "eu-west-1", + "service": "EC2" + }, + { + "ip_prefix": "52.52.0.0/15", + "region": "us-west-1", + "service": "EC2" + }, + { + "ip_prefix": "52.54.0.0/15", + "region": "us-east-1", + "service": "EC2" + }, + { + "ip_prefix": "52.56.0.0/16", + "region": "eu-west-2", + "service": "EC2" + }, + { + "ip_prefix": "52.57.0.0/16", + "region": "eu-central-1", + "service": "EC2" + }, + { + "ip_prefix": "52.58.0.0/15", + "region": "eu-central-1", + "service": "EC2" + }, + { + "ip_prefix": "52.60.0.0/16", + "region": "ca-central-1", + "service": "EC2" + }, + { + "ip_prefix": "52.61.0.0/16", + "region": "us-gov-west-1", + "service": "EC2" + }, + { + "ip_prefix": "52.62.0.0/15", + "region": "ap-southeast-2", + "service": "EC2" + }, + { + "ip_prefix": "52.64.0.0/17", + "region": "ap-southeast-2", + "service": "EC2" + }, + { + "ip_prefix": "52.64.128.0/17", + "region": "ap-southeast-2", + "service": "EC2" + }, + { + "ip_prefix": "52.65.0.0/16", + "region": "ap-southeast-2", + "service": "EC2" + }, + { + "ip_prefix": "52.66.0.0/16", + "region": "ap-south-1", + "service": "EC2" + }, + { + "ip_prefix": "52.67.0.0/16", + "region": "sa-east-1", + "service": "EC2" + }, + { + "ip_prefix": "52.68.0.0/15", + "region": "ap-northeast-1", + "service": "EC2" + }, + { + "ip_prefix": "52.70.0.0/15", + "region": "us-east-1", + "service": "EC2" + }, + { + "ip_prefix": "52.72.0.0/15", + "region": "us-east-1", + "service": "EC2" + }, + { + "ip_prefix": "52.74.0.0/16", + "region": "ap-southeast-1", + "service": "EC2" + }, + { + "ip_prefix": "52.76.0.0/17", + "region": "ap-southeast-1", + "service": "EC2" + }, + { + "ip_prefix": "52.76.128.0/17", + "region": "ap-southeast-1", + "service": "EC2" + }, + { + "ip_prefix": "52.77.0.0/16", + "region": "ap-southeast-1", + "service": "EC2" + }, + { + "ip_prefix": "52.78.0.0/16", + "region": "ap-northeast-2", + "service": "EC2" + }, + { + "ip_prefix": "52.79.0.0/16", + "region": "ap-northeast-2", + "service": "EC2" + }, + { + "ip_prefix": "52.80.0.0/16", + "region": "cn-north-1", + "service": "EC2" + }, + { + "ip_prefix": "52.83.0.0/16", + "region": "cn-northwest-1", + "service": "EC2" + }, + { + "ip_prefix": "52.86.0.0/15", + "region": "us-east-1", + "service": "EC2" + }, + { + "ip_prefix": "52.88.0.0/15", + "region": "us-west-2", + "service": "EC2" + }, + { + "ip_prefix": "52.90.0.0/15", + "region": "us-east-1", + "service": "EC2" + }, + { + "ip_prefix": "52.95.240.0/24", + "region": "sa-east-1", + "service": "EC2" + }, + { + "ip_prefix": "52.95.241.0/24", + "region": "ap-southeast-2", + "service": "EC2" + }, + { + "ip_prefix": "52.95.242.0/24", + "region": "ap-southeast-1", + "service": "EC2" + }, + { + "ip_prefix": "52.95.243.0/24", + "region": "ap-northeast-1", + "service": "EC2" + }, + { + "ip_prefix": "52.95.244.0/24", + "region": "eu-west-1", + "service": "EC2" + }, + { + "ip_prefix": "52.95.245.0/24", + "region": "us-east-1", + "service": "EC2" + }, + { + "ip_prefix": "52.95.246.0/24", + "region": "us-west-1", + "service": "EC2" + }, + { + "ip_prefix": "52.95.247.0/24", + "region": "us-west-2", + "service": "EC2" + }, + { + "ip_prefix": "52.95.248.0/24", + "region": "eu-central-1", + "service": "EC2" + }, + { + "ip_prefix": "52.95.249.0/24", + "region": "ap-south-1", + "service": "EC2" + }, + { + "ip_prefix": "52.95.250.0/24", + "region": "ca-central-1", + "service": "EC2" + }, + { + "ip_prefix": "52.95.251.0/24", + "region": "us-east-2", + "service": "EC2" + }, + { + "ip_prefix": "52.95.252.0/24", + "region": "ap-northeast-2", + "service": "EC2" + }, + { + "ip_prefix": "52.95.253.0/24", + "region": "eu-west-2", + "service": "EC2" + }, + { + "ip_prefix": "52.95.254.0/24", + "region": "eu-west-3", + "service": "EC2" + }, + { + "ip_prefix": "52.95.255.0/28", + "region": "sa-east-1", + "service": "EC2" + }, + { + "ip_prefix": "52.95.255.16/28", + "region": "ap-southeast-2", + "service": "EC2" + }, + { + "ip_prefix": "52.95.255.32/28", + "region": "ap-southeast-1", + "service": "EC2" + }, + { + "ip_prefix": "52.95.255.48/28", + "region": "ap-northeast-1", + "service": "EC2" + }, + { + "ip_prefix": "52.95.255.64/28", + "region": "eu-west-1", + "service": "EC2" + }, + { + "ip_prefix": "52.95.255.80/28", + "region": "us-east-1", + "service": "EC2" + }, + { + "ip_prefix": "52.95.255.96/28", + "region": "us-west-1", + "service": "EC2" + }, + { + "ip_prefix": "52.95.255.112/28", + "region": "us-west-2", + "service": "EC2" + }, + { + "ip_prefix": "52.95.255.128/28", + "region": "eu-central-1", + "service": "EC2" + }, + { + "ip_prefix": "52.95.255.144/28", + "region": "cn-north-1", + "service": "EC2" + }, + { + "ip_prefix": "52.192.0.0/15", + "region": "ap-northeast-1", + "service": "EC2" + }, + { + "ip_prefix": "52.196.0.0/14", + "region": "ap-northeast-1", + "service": "EC2" + }, + { + "ip_prefix": "52.200.0.0/13", + "region": "us-east-1", + "service": "EC2" + }, + { + "ip_prefix": "52.208.0.0/13", + "region": "eu-west-1", + "service": "EC2" + }, + { + "ip_prefix": "52.220.0.0/15", + "region": "ap-southeast-1", + "service": "EC2" + }, + { + "ip_prefix": "52.222.0.0/17", + "region": "us-gov-west-1", + "service": "EC2" + }, + { + "ip_prefix": "54.64.0.0/15", + "region": "ap-northeast-1", + "service": "EC2" + }, + { + "ip_prefix": "54.66.0.0/16", + "region": "ap-southeast-2", + "service": "EC2" + }, + { + "ip_prefix": "54.67.0.0/16", + "region": "us-west-1", + "service": "EC2" + }, + { + "ip_prefix": "54.68.0.0/14", + "region": "us-west-2", + "service": "EC2" + }, + { + "ip_prefix": "54.72.0.0/15", + "region": "eu-west-1", + "service": "EC2" + }, + { + "ip_prefix": "54.74.0.0/15", + "region": "eu-west-1", + "service": "EC2" + }, + { + "ip_prefix": "54.76.0.0/15", + "region": "eu-west-1", + "service": "EC2" + }, + { + "ip_prefix": "54.78.0.0/16", + "region": "eu-west-1", + "service": "EC2" + }, + { + "ip_prefix": "54.79.0.0/16", + "region": "ap-southeast-2", + "service": "EC2" + }, + { + "ip_prefix": "54.80.0.0/13", + "region": "us-east-1", + "service": "EC2" + }, + { + "ip_prefix": "54.88.0.0/14", + "region": "us-east-1", + "service": "EC2" + }, + { + "ip_prefix": "54.92.0.0/17", + "region": "ap-northeast-1", + "service": "EC2" + }, + { + "ip_prefix": "54.92.128.0/17", + "region": "us-east-1", + "service": "EC2" + }, + { + "ip_prefix": "54.93.0.0/16", + "region": "eu-central-1", + "service": "EC2" + }, + { + "ip_prefix": "54.94.0.0/16", + "region": "sa-east-1", + "service": "EC2" + }, + { + "ip_prefix": "54.95.0.0/16", + "region": "ap-northeast-1", + "service": "EC2" + }, + { + "ip_prefix": "54.144.0.0/14", + "region": "us-east-1", + "service": "EC2" + }, + { + "ip_prefix": "54.148.0.0/15", + "region": "us-west-2", + "service": "EC2" + }, + { + "ip_prefix": "54.150.0.0/16", + "region": "ap-northeast-1", + "service": "EC2" + }, + { + "ip_prefix": "54.151.0.0/17", + "region": "us-west-1", + "service": "EC2" + }, + { + "ip_prefix": "54.151.128.0/17", + "region": "ap-southeast-1", + "service": "EC2" + }, + { + "ip_prefix": "54.152.0.0/16", + "region": "us-east-1", + "service": "EC2" + }, + { + "ip_prefix": "54.153.0.0/17", + "region": "us-west-1", + "service": "EC2" + }, + { + "ip_prefix": "54.153.128.0/17", + "region": "ap-southeast-2", + "service": "EC2" + }, + { + "ip_prefix": "54.154.0.0/16", + "region": "eu-west-1", + "service": "EC2" + }, + { + "ip_prefix": "54.155.0.0/16", + "region": "eu-west-1", + "service": "EC2" + }, + { + "ip_prefix": "54.156.0.0/14", + "region": "us-east-1", + "service": "EC2" + }, + { + "ip_prefix": "54.160.0.0/13", + "region": "us-east-1", + "service": "EC2" + }, + { + "ip_prefix": "54.168.0.0/16", + "region": "ap-northeast-1", + "service": "EC2" + }, + { + "ip_prefix": "54.169.0.0/16", + "region": "ap-southeast-1", + "service": "EC2" + }, + { + "ip_prefix": "54.170.0.0/15", + "region": "eu-west-1", + "service": "EC2" + }, + { + "ip_prefix": "54.172.0.0/15", + "region": "us-east-1", + "service": "EC2" + }, + { + "ip_prefix": "54.174.0.0/15", + "region": "us-east-1", + "service": "EC2" + }, + { + "ip_prefix": "54.176.0.0/15", + "region": "us-west-1", + "service": "EC2" + }, + { + "ip_prefix": "54.178.0.0/16", + "region": "ap-northeast-1", + "service": "EC2" + }, + { + "ip_prefix": "54.179.0.0/16", + "region": "ap-southeast-1", + "service": "EC2" + }, + { + "ip_prefix": "54.183.0.0/16", + "region": "us-west-1", + "service": "EC2" + }, + { + "ip_prefix": "54.184.0.0/13", + "region": "us-west-2", + "service": "EC2" + }, + { + "ip_prefix": "54.193.0.0/16", + "region": "us-west-1", + "service": "EC2" + }, + { + "ip_prefix": "54.194.0.0/15", + "region": "eu-west-1", + "service": "EC2" + }, + { + "ip_prefix": "54.196.0.0/15", + "region": "us-east-1", + "service": "EC2" + }, + { + "ip_prefix": "54.198.0.0/16", + "region": "us-east-1", + "service": "EC2" + }, + { + "ip_prefix": "54.199.0.0/16", + "region": "ap-northeast-1", + "service": "EC2" + }, + { + "ip_prefix": "54.200.0.0/15", + "region": "us-west-2", + "service": "EC2" + }, + { + "ip_prefix": "54.202.0.0/15", + "region": "us-west-2", + "service": "EC2" + }, + { + "ip_prefix": "54.204.0.0/15", + "region": "us-east-1", + "service": "EC2" + }, + { + "ip_prefix": "54.206.0.0/16", + "region": "ap-southeast-2", + "service": "EC2" + }, + { + "ip_prefix": "54.207.0.0/16", + "region": "sa-east-1", + "service": "EC2" + }, + { + "ip_prefix": "54.208.0.0/15", + "region": "us-east-1", + "service": "EC2" + }, + { + "ip_prefix": "54.210.0.0/15", + "region": "us-east-1", + "service": "EC2" + }, + { + "ip_prefix": "54.212.0.0/15", + "region": "us-west-2", + "service": "EC2" + }, + { + "ip_prefix": "54.214.0.0/16", + "region": "us-west-2", + "service": "EC2" + }, + { + "ip_prefix": "54.215.0.0/16", + "region": "us-west-1", + "service": "EC2" + }, + { + "ip_prefix": "54.216.0.0/15", + "region": "eu-west-1", + "service": "EC2" + }, + { + "ip_prefix": "54.218.0.0/16", + "region": "us-west-2", + "service": "EC2" + }, + { + "ip_prefix": "54.219.0.0/16", + "region": "us-west-1", + "service": "EC2" + }, + { + "ip_prefix": "54.220.0.0/16", + "region": "eu-west-1", + "service": "EC2" + }, + { + "ip_prefix": "54.221.0.0/16", + "region": "us-east-1", + "service": "EC2" + }, + { + "ip_prefix": "54.222.128.0/17", + "region": "cn-north-1", + "service": "EC2" + }, + { + "ip_prefix": "54.223.0.0/16", + "region": "cn-north-1", + "service": "EC2" + }, + { + "ip_prefix": "54.224.0.0/15", + "region": "us-east-1", + "service": "EC2" + }, + { + "ip_prefix": "54.226.0.0/15", + "region": "us-east-1", + "service": "EC2" + }, + { + "ip_prefix": "54.228.0.0/16", + "region": "eu-west-1", + "service": "EC2" + }, + { + "ip_prefix": "54.229.0.0/16", + "region": "eu-west-1", + "service": "EC2" + }, + { + "ip_prefix": "54.232.0.0/16", + "region": "sa-east-1", + "service": "EC2" + }, + { + "ip_prefix": "54.233.0.0/18", + "region": "sa-east-1", + "service": "EC2" + }, + { + "ip_prefix": "54.233.64.0/18", + "region": "sa-east-1", + "service": "EC2" + }, + { + "ip_prefix": "54.233.128.0/17", + "region": "sa-east-1", + "service": "EC2" + }, + { + "ip_prefix": "54.234.0.0/15", + "region": "us-east-1", + "service": "EC2" + }, + { + "ip_prefix": "54.236.0.0/15", + "region": "us-east-1", + "service": "EC2" + }, + { + "ip_prefix": "54.238.0.0/16", + "region": "ap-northeast-1", + "service": "EC2" + }, + { + "ip_prefix": "54.241.0.0/16", + "region": "us-west-1", + "service": "EC2" + }, + { + "ip_prefix": "54.242.0.0/15", + "region": "us-east-1", + "service": "EC2" + }, + { + "ip_prefix": "54.244.0.0/16", + "region": "us-west-2", + "service": "EC2" + }, + { + "ip_prefix": "54.245.0.0/16", + "region": "us-west-2", + "service": "EC2" + }, + { + "ip_prefix": "54.246.0.0/16", + "region": "eu-west-1", + "service": "EC2" + }, + { + "ip_prefix": "54.247.0.0/16", + "region": "eu-west-1", + "service": "EC2" + }, + { + "ip_prefix": "54.248.0.0/15", + "region": "ap-northeast-1", + "service": "EC2" + }, + { + "ip_prefix": "54.250.0.0/16", + "region": "ap-northeast-1", + "service": "EC2" + }, + { + "ip_prefix": "54.251.0.0/16", + "region": "ap-southeast-1", + "service": "EC2" + }, + { + "ip_prefix": "54.252.0.0/16", + "region": "ap-southeast-2", + "service": "EC2" + }, + { + "ip_prefix": "54.253.0.0/16", + "region": "ap-southeast-2", + "service": "EC2" + }, + { + "ip_prefix": "54.254.0.0/16", + "region": "ap-southeast-1", + "service": "EC2" + }, + { + "ip_prefix": "54.255.0.0/16", + "region": "ap-southeast-1", + "service": "EC2" + }, + { + "ip_prefix": "67.202.0.0/18", + "region": "us-east-1", + "service": "EC2" + }, + { + "ip_prefix": "72.44.32.0/19", + "region": "us-east-1", + "service": "EC2" + }, + { + "ip_prefix": "75.101.128.0/17", + "region": "us-east-1", + "service": "EC2" + }, + { + "ip_prefix": "79.125.0.0/17", + "region": "eu-west-1", + "service": "EC2" + }, + { + "ip_prefix": "96.127.0.0/17", + "region": "us-gov-west-1", + "service": "EC2" + }, + { + "ip_prefix": "103.4.8.0/22", + "region": "ap-northeast-1", + "service": "EC2" + }, + { + "ip_prefix": "103.4.12.0/22", + "region": "ap-northeast-1", + "service": "EC2" + }, + { + "ip_prefix": "107.20.0.0/14", + "region": "us-east-1", + "service": "EC2" + }, + { + "ip_prefix": "122.248.192.0/18", + "region": "ap-southeast-1", + "service": "EC2" + }, + { + "ip_prefix": "174.129.0.0/16", + "region": "us-east-1", + "service": "EC2" + }, + { + "ip_prefix": "175.41.128.0/18", + "region": "ap-southeast-1", + "service": "EC2" + }, + { + "ip_prefix": "175.41.192.0/18", + "region": "ap-northeast-1", + "service": "EC2" + }, + { + "ip_prefix": "176.32.64.0/19", + "region": "ap-northeast-1", + "service": "EC2" + }, + { + "ip_prefix": "176.34.0.0/19", + "region": "ap-northeast-1", + "service": "EC2" + }, + { + "ip_prefix": "176.34.32.0/19", + "region": "ap-northeast-1", + "service": "EC2" + }, + { + "ip_prefix": "176.34.64.0/18", + "region": "eu-west-1", + "service": "EC2" + }, + { + "ip_prefix": "176.34.128.0/17", + "region": "eu-west-1", + "service": "EC2" + }, + { + "ip_prefix": "177.71.128.0/17", + "region": "sa-east-1", + "service": "EC2" + }, + { + "ip_prefix": "184.72.0.0/18", + "region": "us-west-1", + "service": "EC2" + }, + { + "ip_prefix": "184.72.64.0/18", + "region": "us-east-1", + "service": "EC2" + }, + { + "ip_prefix": "184.72.128.0/17", + "region": "us-east-1", + "service": "EC2" + }, + { + "ip_prefix": "184.73.0.0/16", + "region": "us-east-1", + "service": "EC2" + }, + { + "ip_prefix": "184.169.128.0/17", + "region": "us-west-1", + "service": "EC2" + }, + { + "ip_prefix": "185.48.120.0/22", + "region": "eu-west-1", + "service": "EC2" + }, + { + "ip_prefix": "204.236.128.0/18", + "region": "us-west-1", + "service": "EC2" + }, + { + "ip_prefix": "204.236.192.0/18", + "region": "us-east-1", + "service": "EC2" + }, + { + "ip_prefix": "216.182.224.0/20", + "region": "us-east-1", + "service": "EC2" + }, + { + "ip_prefix": "52.95.110.0/24", + "region": "GLOBAL", + "service": "ROUTE53" + }, + { + "ip_prefix": "205.251.192.0/21", + "region": "GLOBAL", + "service": "ROUTE53" + }, + { + "ip_prefix": "13.32.0.0/15", + "region": "GLOBAL", + "service": "CLOUDFRONT" + }, + { + "ip_prefix": "13.54.63.128/26", + "region": "ap-southeast-2", + "service": "CLOUDFRONT" + }, + { + "ip_prefix": "13.59.250.0/26", + "region": "us-east-2", + "service": "CLOUDFRONT" + }, + { + "ip_prefix": "13.113.203.0/24", + "region": "ap-northeast-1", + "service": "CLOUDFRONT" + }, + { + "ip_prefix": "13.228.69.0/24", + "region": "ap-southeast-1", + "service": "CLOUDFRONT" + }, + { + "ip_prefix": "34.195.252.0/24", + "region": "us-east-1", + "service": "CLOUDFRONT" + }, + { + "ip_prefix": "34.226.14.0/24", + "region": "us-east-1", + "service": "CLOUDFRONT" + }, + { + "ip_prefix": "35.158.136.0/24", + "region": "eu-central-1", + "service": "CLOUDFRONT" + }, + { + "ip_prefix": "35.162.63.192/26", + "region": "us-west-2", + "service": "CLOUDFRONT" + }, + { + "ip_prefix": "35.167.191.128/26", + "region": "us-west-2", + "service": "CLOUDFRONT" + }, + { + "ip_prefix": "52.15.127.128/26", + "region": "us-east-2", + "service": "CLOUDFRONT" + }, + { + "ip_prefix": "52.46.0.0/18", + "region": "GLOBAL", + "service": "CLOUDFRONT" + }, + { + "ip_prefix": "52.52.191.128/26", + "region": "us-west-1", + "service": "CLOUDFRONT" + }, + { + "ip_prefix": "52.56.127.0/25", + "region": "eu-west-2", + "service": "CLOUDFRONT" + }, + { + "ip_prefix": "52.57.254.0/24", + "region": "eu-central-1", + "service": "CLOUDFRONT" + }, + { + "ip_prefix": "52.66.194.128/26", + "region": "ap-south-1", + "service": "CLOUDFRONT" + }, + { + "ip_prefix": "52.78.247.128/26", + "region": "ap-northeast-2", + "service": "CLOUDFRONT" + }, + { + "ip_prefix": "52.84.0.0/15", + "region": "GLOBAL", + "service": "CLOUDFRONT" + }, + { + "ip_prefix": "52.199.127.192/26", + "region": "ap-northeast-1", + "service": "CLOUDFRONT" + }, + { + "ip_prefix": "52.212.248.0/26", + "region": "eu-west-1", + "service": "CLOUDFRONT" + }, + { + "ip_prefix": "52.220.191.0/26", + "region": "ap-southeast-1", + "service": "CLOUDFRONT" + }, + { + "ip_prefix": "52.222.128.0/17", + "region": "GLOBAL", + "service": "CLOUDFRONT" + }, + { + "ip_prefix": "54.182.0.0/16", + "region": "GLOBAL", + "service": "CLOUDFRONT" + }, + { + "ip_prefix": "54.192.0.0/16", + "region": "GLOBAL", + "service": "CLOUDFRONT" + }, + { + "ip_prefix": "54.230.0.0/16", + "region": "GLOBAL", + "service": "CLOUDFRONT" + }, + { + "ip_prefix": "54.233.255.128/26", + "region": "sa-east-1", + "service": "CLOUDFRONT" + }, + { + "ip_prefix": "54.239.128.0/18", + "region": "GLOBAL", + "service": "CLOUDFRONT" + }, + { + "ip_prefix": "54.239.192.0/19", + "region": "GLOBAL", + "service": "CLOUDFRONT" + }, + { + "ip_prefix": "54.240.128.0/18", + "region": "GLOBAL", + "service": "CLOUDFRONT" + }, + { + "ip_prefix": "204.246.164.0/22", + "region": "GLOBAL", + "service": "CLOUDFRONT" + }, + { + "ip_prefix": "204.246.168.0/22", + "region": "GLOBAL", + "service": "CLOUDFRONT" + }, + { + "ip_prefix": "204.246.174.0/23", + "region": "GLOBAL", + "service": "CLOUDFRONT" + }, + { + "ip_prefix": "204.246.176.0/20", + "region": "GLOBAL", + "service": "CLOUDFRONT" + }, + { + "ip_prefix": "205.251.192.0/19", + "region": "GLOBAL", + "service": "CLOUDFRONT" + }, + { + "ip_prefix": "205.251.249.0/24", + "region": "GLOBAL", + "service": "CLOUDFRONT" + }, + { + "ip_prefix": "205.251.250.0/23", + "region": "GLOBAL", + "service": "CLOUDFRONT" + }, + { + "ip_prefix": "205.251.252.0/23", + "region": "GLOBAL", + "service": "CLOUDFRONT" + }, + { + "ip_prefix": "205.251.254.0/24", + "region": "GLOBAL", + "service": "CLOUDFRONT" + }, + { + "ip_prefix": "216.137.32.0/19", + "region": "GLOBAL", + "service": "CLOUDFRONT" + } + ], + "ipv6_prefixes": [ + { + "ipv6_prefix": "2400:6500:0:7000::/56", + "region": "ap-southeast-1", + "service": "AMAZON" + }, + { + "ipv6_prefix": "2400:6500:0:7100::/56", + "region": "ap-northeast-1", + "service": "AMAZON" + }, + { + "ipv6_prefix": "2400:6500:0:7200::/56", + "region": "ap-southeast-2", + "service": "AMAZON" + }, + { + "ipv6_prefix": "2400:6500:0:7400::/56", + "region": "ap-northeast-2", + "service": "AMAZON" + }, + { + "ipv6_prefix": "2400:6500:0:7500::/56", + "region": "ap-south-1", + "service": "AMAZON" + }, + { + "ipv6_prefix": "2400:6500:100:7100::/56", + "region": "cn-north-1", + "service": "AMAZON" + }, + { + "ipv6_prefix": "2400:6500:100:7200::/56", + "region": "cn-northwest-1", + "service": "AMAZON" + }, + { + "ipv6_prefix": "2400:6500:ff00::/64", + "region": "ap-southeast-1", + "service": "AMAZON" + }, + { + "ipv6_prefix": "2400:6700:ff00::/64", + "region": "ap-northeast-1", + "service": "AMAZON" + }, + { + "ipv6_prefix": "2403:b300:ff00::/64", + "region": "ap-southeast-2", + "service": "AMAZON" + }, + { + "ipv6_prefix": "2406:da00:2000::/40", + "region": "ap-northeast-2", + "service": "AMAZON" + }, + { + "ipv6_prefix": "2406:da00:4000::/40", + "region": "ap-northeast-1", + "service": "AMAZON" + }, + { + "ipv6_prefix": "2406:da00:8000::/40", + "region": "ap-southeast-1", + "service": "AMAZON" + }, + { + "ipv6_prefix": "2406:da00:a000::/40", + "region": "ap-south-1", + "service": "AMAZON" + }, + { + "ipv6_prefix": "2406:da00:c000::/40", + "region": "ap-southeast-2", + "service": "AMAZON" + }, + { + "ipv6_prefix": "2406:da00:ff00::/64", + "region": "us-east-1", + "service": "AMAZON" + }, + { + "ipv6_prefix": "2406:da12::/36", + "region": "ap-northeast-2", + "service": "AMAZON" + }, + { + "ipv6_prefix": "2406:da14::/36", + "region": "ap-northeast-1", + "service": "AMAZON" + }, + { + "ipv6_prefix": "2406:da18::/36", + "region": "ap-southeast-1", + "service": "AMAZON" + }, + { + "ipv6_prefix": "2406:da1a::/36", + "region": "ap-south-1", + "service": "AMAZON" + }, + { + "ipv6_prefix": "2406:da1c::/36", + "region": "ap-southeast-2", + "service": "AMAZON" + }, + { + "ipv6_prefix": "2406:daa0:2000::/40", + "region": "ap-northeast-2", + "service": "AMAZON" + }, + { + "ipv6_prefix": "2406:daa0:4000::/40", + "region": "ap-northeast-1", + "service": "AMAZON" + }, + { + "ipv6_prefix": "2406:daa0:8000::/40", + "region": "ap-southeast-1", + "service": "AMAZON" + }, + { + "ipv6_prefix": "2406:daa0:a000::/40", + "region": "ap-south-1", + "service": "AMAZON" + }, + { + "ipv6_prefix": "2406:daa0:c000::/40", + "region": "ap-southeast-2", + "service": "AMAZON" + }, + { + "ipv6_prefix": "2406:daf8:2000::/40", + "region": "ap-northeast-2", + "service": "AMAZON" + }, + { + "ipv6_prefix": "2406:daf8:4000::/40", + "region": "ap-northeast-1", + "service": "AMAZON" + }, + { + "ipv6_prefix": "2406:daf8:8000::/40", + "region": "ap-southeast-1", + "service": "AMAZON" + }, + { + "ipv6_prefix": "2406:daf8:a000::/40", + "region": "ap-south-1", + "service": "AMAZON" + }, + { + "ipv6_prefix": "2406:daf8:c000::/40", + "region": "ap-southeast-2", + "service": "AMAZON" + }, + { + "ipv6_prefix": "2406:daf9:2000::/40", + "region": "ap-northeast-2", + "service": "AMAZON" + }, + { + "ipv6_prefix": "2406:daf9:4000::/40", + "region": "ap-northeast-1", + "service": "AMAZON" + }, + { + "ipv6_prefix": "2406:daf9:8000::/40", + "region": "ap-southeast-1", + "service": "AMAZON" + }, + { + "ipv6_prefix": "2406:daf9:a000::/40", + "region": "ap-south-1", + "service": "AMAZON" + }, + { + "ipv6_prefix": "2406:daf9:c000::/40", + "region": "ap-southeast-2", + "service": "AMAZON" + }, + { + "ipv6_prefix": "2406:dafa:2000::/40", + "region": "ap-northeast-2", + "service": "AMAZON" + }, + { + "ipv6_prefix": "2406:dafa:4000::/40", + "region": "ap-northeast-1", + "service": "AMAZON" + }, + { + "ipv6_prefix": "2406:dafa:8000::/40", + "region": "ap-southeast-1", + "service": "AMAZON" + }, + { + "ipv6_prefix": "2406:dafa:a000::/40", + "region": "ap-south-1", + "service": "AMAZON" + }, + { + "ipv6_prefix": "2406:dafa:c000::/40", + "region": "ap-southeast-2", + "service": "AMAZON" + }, + { + "ipv6_prefix": "2406:dafc:2000::/40", + "region": "ap-northeast-2", + "service": "AMAZON" + }, + { + "ipv6_prefix": "2406:dafc:4000::/40", + "region": "ap-northeast-1", + "service": "AMAZON" + }, + { + "ipv6_prefix": "2406:dafc:8000::/40", + "region": "ap-southeast-1", + "service": "AMAZON" + }, + { + "ipv6_prefix": "2406:dafc:a000::/40", + "region": "ap-south-1", + "service": "AMAZON" + }, + { + "ipv6_prefix": "2406:dafc:c000::/40", + "region": "ap-southeast-2", + "service": "AMAZON" + }, + { + "ipv6_prefix": "2406:dafe:2000::/40", + "region": "ap-northeast-2", + "service": "AMAZON" + }, + { + "ipv6_prefix": "2406:dafe:4000::/40", + "region": "ap-northeast-1", + "service": "AMAZON" + }, + { + "ipv6_prefix": "2406:dafe:8000::/40", + "region": "ap-southeast-1", + "service": "AMAZON" + }, + { + "ipv6_prefix": "2406:dafe:a000::/40", + "region": "ap-south-1", + "service": "AMAZON" + }, + { + "ipv6_prefix": "2406:dafe:c000::/40", + "region": "ap-southeast-2", + "service": "AMAZON" + }, + { + "ipv6_prefix": "2406:daff:2000::/40", + "region": "ap-northeast-2", + "service": "AMAZON" + }, + { + "ipv6_prefix": "2406:daff:4000::/40", + "region": "ap-northeast-1", + "service": "AMAZON" + }, + { + "ipv6_prefix": "2406:daff:8000::/40", + "region": "ap-southeast-1", + "service": "AMAZON" + }, + { + "ipv6_prefix": "2406:daff:a000::/40", + "region": "ap-south-1", + "service": "AMAZON" + }, + { + "ipv6_prefix": "2406:daff:c000::/40", + "region": "ap-southeast-2", + "service": "AMAZON" + }, + { + "ipv6_prefix": "240f:8000:4000::/40", + "region": "cn-northwest-1", + "service": "AMAZON" + }, + { + "ipv6_prefix": "240f:8000:8000::/40", + "region": "cn-north-1", + "service": "AMAZON" + }, + { + "ipv6_prefix": "240f:8014::/36", + "region": "cn-northwest-1", + "service": "AMAZON" + }, + { + "ipv6_prefix": "240f:8018::/36", + "region": "cn-north-1", + "service": "AMAZON" + }, + { + "ipv6_prefix": "240f:80a0:4000::/40", + "region": "cn-northwest-1", + "service": "AMAZON" + }, + { + "ipv6_prefix": "240f:80a0:8000::/40", + "region": "cn-north-1", + "service": "AMAZON" + }, + { + "ipv6_prefix": "240f:80f8:4000::/40", + "region": "cn-northwest-1", + "service": "AMAZON" + }, + { + "ipv6_prefix": "240f:80f8:8000::/40", + "region": "cn-north-1", + "service": "AMAZON" + }, + { + "ipv6_prefix": "240f:80f9:4000::/40", + "region": "cn-northwest-1", + "service": "AMAZON" + }, + { + "ipv6_prefix": "240f:80f9:8000::/40", + "region": "cn-north-1", + "service": "AMAZON" + }, + { + "ipv6_prefix": "240f:80fa:4000::/40", + "region": "cn-northwest-1", + "service": "AMAZON" + }, + { + "ipv6_prefix": "240f:80fa:8000::/40", + "region": "cn-north-1", + "service": "AMAZON" + }, + { + "ipv6_prefix": "240f:80fc:4000::/40", + "region": "cn-northwest-1", + "service": "AMAZON" + }, + { + "ipv6_prefix": "240f:80fc:8000::/40", + "region": "cn-north-1", + "service": "AMAZON" + }, + { + "ipv6_prefix": "240f:80fe:4000::/40", + "region": "cn-northwest-1", + "service": "AMAZON" + }, + { + "ipv6_prefix": "240f:80fe:8000::/40", + "region": "cn-north-1", + "service": "AMAZON" + }, + { + "ipv6_prefix": "240f:80ff:4000::/40", + "region": "cn-northwest-1", + "service": "AMAZON" + }, + { + "ipv6_prefix": "240f:80ff:8000::/40", + "region": "cn-north-1", + "service": "AMAZON" + }, + { + "ipv6_prefix": "2600:1f00:1000::/40", + "region": "ca-central-1", + "service": "AMAZON" + }, + { + "ipv6_prefix": "2600:1f00:2000::/40", + "region": "us-gov-west-1", + "service": "AMAZON" + }, + { + "ipv6_prefix": "2600:1f00:4000::/40", + "region": "us-west-2", + "service": "AMAZON" + }, + { + "ipv6_prefix": "2600:1f00:6000::/40", + "region": "us-east-2", + "service": "AMAZON" + }, + { + "ipv6_prefix": "2600:1f00:8000::/40", + "region": "us-east-1", + "service": "AMAZON" + }, + { + "ipv6_prefix": "2600:1f00:c000::/40", + "region": "us-west-1", + "service": "AMAZON" + }, + { + "ipv6_prefix": "2600:1f00:e000::/40", + "region": "sa-east-1", + "service": "AMAZON" + }, + { + "ipv6_prefix": "2600:1f11::/36", + "region": "ca-central-1", + "service": "AMAZON" + }, + { + "ipv6_prefix": "2600:1f12::/36", + "region": "us-gov-west-1", + "service": "AMAZON" + }, + { + "ipv6_prefix": "2600:1f14::/35", + "region": "us-west-2", + "service": "AMAZON" + }, + { + "ipv6_prefix": "2600:1f16::/36", + "region": "us-east-2", + "service": "AMAZON" + }, + { + "ipv6_prefix": "2600:1f18::/33", + "region": "us-east-1", + "service": "AMAZON" + }, + { + "ipv6_prefix": "2600:1f1c::/36", + "region": "us-west-1", + "service": "AMAZON" + }, + { + "ipv6_prefix": "2600:1f1e::/36", + "region": "sa-east-1", + "service": "AMAZON" + }, + { + "ipv6_prefix": "2600:1fa0:1000::/40", + "region": "ca-central-1", + "service": "AMAZON" + }, + { + "ipv6_prefix": "2600:1fa0:2000::/40", + "region": "us-gov-west-1", + "service": "AMAZON" + }, + { + "ipv6_prefix": "2600:1fa0:4000::/40", + "region": "us-west-2", + "service": "AMAZON" + }, + { + "ipv6_prefix": "2600:1fa0:6000::/40", + "region": "us-east-2", + "service": "AMAZON" + }, + { + "ipv6_prefix": "2600:1fa0:8000::/40", + "region": "us-east-1", + "service": "AMAZON" + }, + { + "ipv6_prefix": "2600:1fa0:c000::/40", + "region": "us-west-1", + "service": "AMAZON" + }, + { + "ipv6_prefix": "2600:1fa0:e000::/40", + "region": "sa-east-1", + "service": "AMAZON" + }, + { + "ipv6_prefix": "2600:1ff8:1000::/40", + "region": "ca-central-1", + "service": "AMAZON" + }, + { + "ipv6_prefix": "2600:1ff8:2000::/40", + "region": "us-gov-west-1", + "service": "AMAZON" + }, + { + "ipv6_prefix": "2600:1ff8:4000::/40", + "region": "us-west-2", + "service": "AMAZON" + }, + { + "ipv6_prefix": "2600:1ff8:6000::/40", + "region": "us-east-2", + "service": "AMAZON" + }, + { + "ipv6_prefix": "2600:1ff8:8000::/40", + "region": "us-east-1", + "service": "AMAZON" + }, + { + "ipv6_prefix": "2600:1ff8:c000::/40", + "region": "us-west-1", + "service": "AMAZON" + }, + { + "ipv6_prefix": "2600:1ff8:e000::/40", + "region": "sa-east-1", + "service": "AMAZON" + }, + { + "ipv6_prefix": "2600:1ff9:1000::/40", + "region": "ca-central-1", + "service": "AMAZON" + }, + { + "ipv6_prefix": "2600:1ff9:2000::/40", + "region": "us-gov-west-1", + "service": "AMAZON" + }, + { + "ipv6_prefix": "2600:1ff9:4000::/40", + "region": "us-west-2", + "service": "AMAZON" + }, + { + "ipv6_prefix": "2600:1ff9:6000::/40", + "region": "us-east-2", + "service": "AMAZON" + }, + { + "ipv6_prefix": "2600:1ff9:8000::/40", + "region": "us-east-1", + "service": "AMAZON" + }, + { + "ipv6_prefix": "2600:1ff9:c000::/40", + "region": "us-west-1", + "service": "AMAZON" + }, + { + "ipv6_prefix": "2600:1ff9:e000::/40", + "region": "sa-east-1", + "service": "AMAZON" + }, + { + "ipv6_prefix": "2600:1ffa:1000::/40", + "region": "ca-central-1", + "service": "AMAZON" + }, + { + "ipv6_prefix": "2600:1ffa:2000::/40", + "region": "us-gov-west-1", + "service": "AMAZON" + }, + { + "ipv6_prefix": "2600:1ffa:4000::/40", + "region": "us-west-2", + "service": "AMAZON" + }, + { + "ipv6_prefix": "2600:1ffa:6000::/40", + "region": "us-east-2", + "service": "AMAZON" + }, + { + "ipv6_prefix": "2600:1ffa:8000::/40", + "region": "us-east-1", + "service": "AMAZON" + }, + { + "ipv6_prefix": "2600:1ffa:c000::/40", + "region": "us-west-1", + "service": "AMAZON" + }, + { + "ipv6_prefix": "2600:1ffa:e000::/40", + "region": "sa-east-1", + "service": "AMAZON" + }, + { + "ipv6_prefix": "2600:1ffc:1000::/40", + "region": "ca-central-1", + "service": "AMAZON" + }, + { + "ipv6_prefix": "2600:1ffc:2000::/40", + "region": "us-gov-west-1", + "service": "AMAZON" + }, + { + "ipv6_prefix": "2600:1ffc:4000::/40", + "region": "us-west-2", + "service": "AMAZON" + }, + { + "ipv6_prefix": "2600:1ffc:6000::/40", + "region": "us-east-2", + "service": "AMAZON" + }, + { + "ipv6_prefix": "2600:1ffc:8000::/40", + "region": "us-east-1", + "service": "AMAZON" + }, + { + "ipv6_prefix": "2600:1ffc:c000::/40", + "region": "us-west-1", + "service": "AMAZON" + }, + { + "ipv6_prefix": "2600:1ffc:e000::/40", + "region": "sa-east-1", + "service": "AMAZON" + }, + { + "ipv6_prefix": "2600:1ffe:1000::/40", + "region": "ca-central-1", + "service": "AMAZON" + }, + { + "ipv6_prefix": "2600:1ffe:2000::/40", + "region": "us-gov-west-1", + "service": "AMAZON" + }, + { + "ipv6_prefix": "2600:1ffe:4000::/40", + "region": "us-west-2", + "service": "AMAZON" + }, + { + "ipv6_prefix": "2600:1ffe:6000::/40", + "region": "us-east-2", + "service": "AMAZON" + }, + { + "ipv6_prefix": "2600:1ffe:8000::/40", + "region": "us-east-1", + "service": "AMAZON" + }, + { + "ipv6_prefix": "2600:1ffe:c000::/40", + "region": "us-west-1", + "service": "AMAZON" + }, + { + "ipv6_prefix": "2600:1ffe:e000::/40", + "region": "sa-east-1", + "service": "AMAZON" + }, + { + "ipv6_prefix": "2600:1fff:1000::/40", + "region": "ca-central-1", + "service": "AMAZON" + }, + { + "ipv6_prefix": "2600:1fff:2000::/40", + "region": "us-gov-west-1", + "service": "AMAZON" + }, + { + "ipv6_prefix": "2600:1fff:4000::/40", + "region": "us-west-2", + "service": "AMAZON" + }, + { + "ipv6_prefix": "2600:1fff:6000::/40", + "region": "us-east-2", + "service": "AMAZON" + }, + { + "ipv6_prefix": "2600:1fff:8000::/40", + "region": "us-east-1", + "service": "AMAZON" + }, + { + "ipv6_prefix": "2600:1fff:c000::/40", + "region": "us-west-1", + "service": "AMAZON" + }, + { + "ipv6_prefix": "2600:1fff:e000::/40", + "region": "sa-east-1", + "service": "AMAZON" + }, + { + "ipv6_prefix": "2600:9000::/28", + "region": "GLOBAL", + "service": "AMAZON" + }, + { + "ipv6_prefix": "2620:107:300f::/64", + "region": "us-west-1", + "service": "AMAZON" + }, + { + "ipv6_prefix": "2620:107:4000:5::/64", + "region": "us-gov-west-1", + "service": "AMAZON" + }, + { + "ipv6_prefix": "2620:107:4000:7000::/56", + "region": "us-east-1", + "service": "AMAZON" + }, + { + "ipv6_prefix": "2620:107:4000:7100::/56", + "region": "us-west-1", + "service": "AMAZON" + }, + { + "ipv6_prefix": "2620:107:4000:7200::/56", + "region": "us-west-2", + "service": "AMAZON" + }, + { + "ipv6_prefix": "2620:107:4000:7400::/56", + "region": "us-gov-west-1", + "service": "AMAZON" + }, + { + "ipv6_prefix": "2620:107:4000:7700::/56", + "region": "us-east-2", + "service": "AMAZON" + }, + { + "ipv6_prefix": "2620:107:4000:7800::/56", + "region": "ca-central-1", + "service": "AMAZON" + }, + { + "ipv6_prefix": "2620:108:7000::/44", + "region": "us-west-2", + "service": "AMAZON" + }, + { + "ipv6_prefix": "2620:108:d000::/44", + "region": "us-gov-west-1", + "service": "AMAZON" + }, + { + "ipv6_prefix": "2804:800:0:7000::/56", + "region": "sa-east-1", + "service": "AMAZON" + }, + { + "ipv6_prefix": "2804:800:ff00::/64", + "region": "sa-east-1", + "service": "AMAZON" + }, + { + "ipv6_prefix": "2a01:578:0:7000::/56", + "region": "eu-west-1", + "service": "AMAZON" + }, + { + "ipv6_prefix": "2a01:578:0:7100::/56", + "region": "eu-central-1", + "service": "AMAZON" + }, + { + "ipv6_prefix": "2a01:578:0:7200::/56", + "region": "eu-west-2", + "service": "AMAZON" + }, + { + "ipv6_prefix": "2a01:578:3::/64", + "region": "eu-west-1", + "service": "AMAZON" + }, + { + "ipv6_prefix": "2a01:578:13::/64", + "region": "eu-central-1", + "service": "AMAZON" + }, + { + "ipv6_prefix": "2a05:d000:2000::/40", + "region": "eu-west-3", + "service": "AMAZON" + }, + { + "ipv6_prefix": "2a05:d000:4000::/40", + "region": "eu-central-1", + "service": "AMAZON" + }, + { + "ipv6_prefix": "2a05:d000:8000::/40", + "region": "eu-west-1", + "service": "AMAZON" + }, + { + "ipv6_prefix": "2a05:d000:c000::/40", + "region": "eu-west-2", + "service": "AMAZON" + }, + { + "ipv6_prefix": "2a05:d012::/36", + "region": "eu-west-3", + "service": "AMAZON" + }, + { + "ipv6_prefix": "2a05:d014::/36", + "region": "eu-central-1", + "service": "AMAZON" + }, + { + "ipv6_prefix": "2a05:d018::/36", + "region": "eu-west-1", + "service": "AMAZON" + }, + { + "ipv6_prefix": "2a05:d01c::/36", + "region": "eu-west-2", + "service": "AMAZON" + }, + { + "ipv6_prefix": "2a05:d050:2000::/40", + "region": "eu-west-3", + "service": "AMAZON" + }, + { + "ipv6_prefix": "2a05:d050:4000::/40", + "region": "eu-central-1", + "service": "AMAZON" + }, + { + "ipv6_prefix": "2a05:d050:8000::/40", + "region": "eu-west-1", + "service": "AMAZON" + }, + { + "ipv6_prefix": "2a05:d050:c000::/40", + "region": "eu-west-2", + "service": "AMAZON" + }, + { + "ipv6_prefix": "2a05:d078:2000::/40", + "region": "eu-west-3", + "service": "AMAZON" + }, + { + "ipv6_prefix": "2a05:d078:4000::/40", + "region": "eu-central-1", + "service": "AMAZON" + }, + { + "ipv6_prefix": "2a05:d078:8000::/40", + "region": "eu-west-1", + "service": "AMAZON" + }, + { + "ipv6_prefix": "2a05:d078:c000::/40", + "region": "eu-west-2", + "service": "AMAZON" + }, + { + "ipv6_prefix": "2a05:d079:2000::/40", + "region": "eu-west-3", + "service": "AMAZON" + }, + { + "ipv6_prefix": "2a05:d079:4000::/40", + "region": "eu-central-1", + "service": "AMAZON" + }, + { + "ipv6_prefix": "2a05:d079:8000::/40", + "region": "eu-west-1", + "service": "AMAZON" + }, + { + "ipv6_prefix": "2a05:d079:c000::/40", + "region": "eu-west-2", + "service": "AMAZON" + }, + { + "ipv6_prefix": "2a05:d07a:2000::/40", + "region": "eu-west-3", + "service": "AMAZON" + }, + { + "ipv6_prefix": "2a05:d07a:4000::/40", + "region": "eu-central-1", + "service": "AMAZON" + }, + { + "ipv6_prefix": "2a05:d07a:8000::/40", + "region": "eu-west-1", + "service": "AMAZON" + }, + { + "ipv6_prefix": "2a05:d07a:c000::/40", + "region": "eu-west-2", + "service": "AMAZON" + }, + { + "ipv6_prefix": "2a05:d07c:2000::/40", + "region": "eu-west-3", + "service": "AMAZON" + }, + { + "ipv6_prefix": "2a05:d07c:4000::/40", + "region": "eu-central-1", + "service": "AMAZON" + }, + { + "ipv6_prefix": "2a05:d07c:8000::/40", + "region": "eu-west-1", + "service": "AMAZON" + }, + { + "ipv6_prefix": "2a05:d07c:c000::/40", + "region": "eu-west-2", + "service": "AMAZON" + }, + { + "ipv6_prefix": "2a05:d07e:2000::/40", + "region": "eu-west-3", + "service": "AMAZON" + }, + { + "ipv6_prefix": "2a05:d07e:4000::/40", + "region": "eu-central-1", + "service": "AMAZON" + }, + { + "ipv6_prefix": "2a05:d07e:8000::/40", + "region": "eu-west-1", + "service": "AMAZON" + }, + { + "ipv6_prefix": "2a05:d07e:c000::/40", + "region": "eu-west-2", + "service": "AMAZON" + }, + { + "ipv6_prefix": "2a05:d07f:2000::/40", + "region": "eu-west-3", + "service": "AMAZON" + }, + { + "ipv6_prefix": "2a05:d07f:4000::/40", + "region": "eu-central-1", + "service": "AMAZON" + }, + { + "ipv6_prefix": "2a05:d07f:8000::/40", + "region": "eu-west-1", + "service": "AMAZON" + }, + { + "ipv6_prefix": "2a05:d07f:c000::/40", + "region": "eu-west-2", + "service": "AMAZON" + }, + { + "ipv6_prefix": "2400:6500:ff00::36fb:1f80/122", + "region": "ap-southeast-1", + "service": "ROUTE53_HEALTHCHECKS" + }, + { + "ipv6_prefix": "2400:6500:ff00::36ff:fec0/122", + "region": "ap-southeast-1", + "service": "ROUTE53_HEALTHCHECKS" + }, + { + "ipv6_prefix": "2400:6700:ff00::36f8:dc00/122", + "region": "ap-northeast-1", + "service": "ROUTE53_HEALTHCHECKS" + }, + { + "ipv6_prefix": "2400:6700:ff00::36fa:fdc0/122", + "region": "ap-northeast-1", + "service": "ROUTE53_HEALTHCHECKS" + }, + { + "ipv6_prefix": "2403:b300:ff00::36fc:4f80/122", + "region": "ap-southeast-2", + "service": "ROUTE53_HEALTHCHECKS" + }, + { + "ipv6_prefix": "2403:b300:ff00::36fc:fec0/122", + "region": "ap-southeast-2", + "service": "ROUTE53_HEALTHCHECKS" + }, + { + "ipv6_prefix": "2406:da00:ff00::36f3:1fc0/122", + "region": "us-east-1", + "service": "ROUTE53_HEALTHCHECKS" + }, + { + "ipv6_prefix": "2406:da00:ff00::6b17:ff00/122", + "region": "us-east-1", + "service": "ROUTE53_HEALTHCHECKS" + }, + { + "ipv6_prefix": "2406:da14:7ff:f800::/53", + "region": "ap-northeast-1", + "service": "ROUTE53_HEALTHCHECKS" + }, + { + "ipv6_prefix": "2406:da14:fff:f800::/53", + "region": "ap-northeast-1", + "service": "ROUTE53_HEALTHCHECKS" + }, + { + "ipv6_prefix": "2406:da18:7ff:f800::/53", + "region": "ap-southeast-1", + "service": "ROUTE53_HEALTHCHECKS" + }, + { + "ipv6_prefix": "2406:da18:fff:f800::/53", + "region": "ap-southeast-1", + "service": "ROUTE53_HEALTHCHECKS" + }, + { + "ipv6_prefix": "2406:da1c:7ff:f800::/53", + "region": "ap-southeast-2", + "service": "ROUTE53_HEALTHCHECKS" + }, + { + "ipv6_prefix": "2406:da1c:fff:f800::/53", + "region": "ap-southeast-2", + "service": "ROUTE53_HEALTHCHECKS" + }, + { + "ipv6_prefix": "2600:1f14:7ff:f800::/53", + "region": "us-west-2", + "service": "ROUTE53_HEALTHCHECKS" + }, + { + "ipv6_prefix": "2600:1f14:fff:f800::/53", + "region": "us-west-2", + "service": "ROUTE53_HEALTHCHECKS" + }, + { + "ipv6_prefix": "2600:1f18:3fff:f800::/53", + "region": "us-east-1", + "service": "ROUTE53_HEALTHCHECKS" + }, + { + "ipv6_prefix": "2600:1f18:7fff:f800::/53", + "region": "us-east-1", + "service": "ROUTE53_HEALTHCHECKS" + }, + { + "ipv6_prefix": "2600:1f1c:7ff:f800::/53", + "region": "us-west-1", + "service": "ROUTE53_HEALTHCHECKS" + }, + { + "ipv6_prefix": "2600:1f1c:fff:f800::/53", + "region": "us-west-1", + "service": "ROUTE53_HEALTHCHECKS" + }, + { + "ipv6_prefix": "2600:1f1e:7ff:f800::/53", + "region": "sa-east-1", + "service": "ROUTE53_HEALTHCHECKS" + }, + { + "ipv6_prefix": "2600:1f1e:fff:f800::/53", + "region": "sa-east-1", + "service": "ROUTE53_HEALTHCHECKS" + }, + { + "ipv6_prefix": "2620:107:300f::36b7:ff80/122", + "region": "us-west-1", + "service": "ROUTE53_HEALTHCHECKS" + }, + { + "ipv6_prefix": "2620:107:300f::36f1:2040/122", + "region": "us-west-1", + "service": "ROUTE53_HEALTHCHECKS" + }, + { + "ipv6_prefix": "2620:108:700f::36f4:34c0/122", + "region": "us-west-2", + "service": "ROUTE53_HEALTHCHECKS" + }, + { + "ipv6_prefix": "2620:108:700f::36f5:a800/122", + "region": "us-west-2", + "service": "ROUTE53_HEALTHCHECKS" + }, + { + "ipv6_prefix": "2804:800:ff00::36e8:2840/122", + "region": "sa-east-1", + "service": "ROUTE53_HEALTHCHECKS" + }, + { + "ipv6_prefix": "2804:800:ff00::b147:cf80/122", + "region": "sa-east-1", + "service": "ROUTE53_HEALTHCHECKS" + }, + { + "ipv6_prefix": "2a01:578:3::36e4:1000/122", + "region": "eu-west-1", + "service": "ROUTE53_HEALTHCHECKS" + }, + { + "ipv6_prefix": "2a01:578:3::b022:9fc0/122", + "region": "eu-west-1", + "service": "ROUTE53_HEALTHCHECKS" + }, + { + "ipv6_prefix": "2a05:d018:7ff:f800::/53", + "region": "eu-west-1", + "service": "ROUTE53_HEALTHCHECKS" + }, + { + "ipv6_prefix": "2a05:d018:fff:f800::/53", + "region": "eu-west-1", + "service": "ROUTE53_HEALTHCHECKS" + }, + { + "ipv6_prefix": "2406:daa0:2000::/40", + "region": "ap-northeast-2", + "service": "S3" + }, + { + "ipv6_prefix": "2406:daa0:4000::/40", + "region": "ap-northeast-1", + "service": "S3" + }, + { + "ipv6_prefix": "2406:daa0:8000::/40", + "region": "ap-southeast-1", + "service": "S3" + }, + { + "ipv6_prefix": "2406:daa0:a000::/40", + "region": "ap-south-1", + "service": "S3" + }, + { + "ipv6_prefix": "2406:daa0:c000::/40", + "region": "ap-southeast-2", + "service": "S3" + }, + { + "ipv6_prefix": "2406:daf8:2000::/40", + "region": "ap-northeast-2", + "service": "S3" + }, + { + "ipv6_prefix": "2406:daf8:4000::/40", + "region": "ap-northeast-1", + "service": "S3" + }, + { + "ipv6_prefix": "2406:daf8:8000::/40", + "region": "ap-southeast-1", + "service": "S3" + }, + { + "ipv6_prefix": "2406:daf8:a000::/40", + "region": "ap-south-1", + "service": "S3" + }, + { + "ipv6_prefix": "2406:daf8:c000::/40", + "region": "ap-southeast-2", + "service": "S3" + }, + { + "ipv6_prefix": "2406:daf9:2000::/40", + "region": "ap-northeast-2", + "service": "S3" + }, + { + "ipv6_prefix": "2406:daf9:4000::/40", + "region": "ap-northeast-1", + "service": "S3" + }, + { + "ipv6_prefix": "2406:daf9:8000::/40", + "region": "ap-southeast-1", + "service": "S3" + }, + { + "ipv6_prefix": "2406:daf9:a000::/40", + "region": "ap-south-1", + "service": "S3" + }, + { + "ipv6_prefix": "2406:daf9:c000::/40", + "region": "ap-southeast-2", + "service": "S3" + }, + { + "ipv6_prefix": "2406:dafa:2000::/40", + "region": "ap-northeast-2", + "service": "S3" + }, + { + "ipv6_prefix": "2406:dafa:4000::/40", + "region": "ap-northeast-1", + "service": "S3" + }, + { + "ipv6_prefix": "2406:dafa:8000::/40", + "region": "ap-southeast-1", + "service": "S3" + }, + { + "ipv6_prefix": "2406:dafa:a000::/40", + "region": "ap-south-1", + "service": "S3" + }, + { + "ipv6_prefix": "2406:dafa:c000::/40", + "region": "ap-southeast-2", + "service": "S3" + }, + { + "ipv6_prefix": "240f:80a0:4000::/40", + "region": "cn-northwest-1", + "service": "S3" + }, + { + "ipv6_prefix": "240f:80a0:8000::/40", + "region": "cn-north-1", + "service": "S3" + }, + { + "ipv6_prefix": "240f:80f8:4000::/40", + "region": "cn-northwest-1", + "service": "S3" + }, + { + "ipv6_prefix": "240f:80f8:8000::/40", + "region": "cn-north-1", + "service": "S3" + }, + { + "ipv6_prefix": "240f:80f9:4000::/40", + "region": "cn-northwest-1", + "service": "S3" + }, + { + "ipv6_prefix": "240f:80f9:8000::/40", + "region": "cn-north-1", + "service": "S3" + }, + { + "ipv6_prefix": "240f:80fa:4000::/40", + "region": "cn-northwest-1", + "service": "S3" + }, + { + "ipv6_prefix": "240f:80fa:8000::/40", + "region": "cn-north-1", + "service": "S3" + }, + { + "ipv6_prefix": "2600:1fa0:1000::/40", + "region": "ca-central-1", + "service": "S3" + }, + { + "ipv6_prefix": "2600:1fa0:2000::/40", + "region": "us-gov-west-1", + "service": "S3" + }, + { + "ipv6_prefix": "2600:1fa0:4000::/40", + "region": "us-west-2", + "service": "S3" + }, + { + "ipv6_prefix": "2600:1fa0:6000::/40", + "region": "us-east-2", + "service": "S3" + }, + { + "ipv6_prefix": "2600:1fa0:8000::/40", + "region": "us-east-1", + "service": "S3" + }, + { + "ipv6_prefix": "2600:1fa0:c000::/40", + "region": "us-west-1", + "service": "S3" + }, + { + "ipv6_prefix": "2600:1fa0:e000::/40", + "region": "sa-east-1", + "service": "S3" + }, + { + "ipv6_prefix": "2600:1ff8:1000::/40", + "region": "ca-central-1", + "service": "S3" + }, + { + "ipv6_prefix": "2600:1ff8:2000::/40", + "region": "us-gov-west-1", + "service": "S3" + }, + { + "ipv6_prefix": "2600:1ff8:4000::/40", + "region": "us-west-2", + "service": "S3" + }, + { + "ipv6_prefix": "2600:1ff8:6000::/40", + "region": "us-east-2", + "service": "S3" + }, + { + "ipv6_prefix": "2600:1ff8:8000::/40", + "region": "us-east-1", + "service": "S3" + }, + { + "ipv6_prefix": "2600:1ff8:c000::/40", + "region": "us-west-1", + "service": "S3" + }, + { + "ipv6_prefix": "2600:1ff8:e000::/40", + "region": "sa-east-1", + "service": "S3" + }, + { + "ipv6_prefix": "2600:1ff9:1000::/40", + "region": "ca-central-1", + "service": "S3" + }, + { + "ipv6_prefix": "2600:1ff9:2000::/40", + "region": "us-gov-west-1", + "service": "S3" + }, + { + "ipv6_prefix": "2600:1ff9:4000::/40", + "region": "us-west-2", + "service": "S3" + }, + { + "ipv6_prefix": "2600:1ff9:6000::/40", + "region": "us-east-2", + "service": "S3" + }, + { + "ipv6_prefix": "2600:1ff9:8000::/40", + "region": "us-east-1", + "service": "S3" + }, + { + "ipv6_prefix": "2600:1ff9:c000::/40", + "region": "us-west-1", + "service": "S3" + }, + { + "ipv6_prefix": "2600:1ff9:e000::/40", + "region": "sa-east-1", + "service": "S3" + }, + { + "ipv6_prefix": "2600:1ffa:1000::/40", + "region": "ca-central-1", + "service": "S3" + }, + { + "ipv6_prefix": "2600:1ffa:2000::/40", + "region": "us-gov-west-1", + "service": "S3" + }, + { + "ipv6_prefix": "2600:1ffa:4000::/40", + "region": "us-west-2", + "service": "S3" + }, + { + "ipv6_prefix": "2600:1ffa:6000::/40", + "region": "us-east-2", + "service": "S3" + }, + { + "ipv6_prefix": "2600:1ffa:8000::/40", + "region": "us-east-1", + "service": "S3" + }, + { + "ipv6_prefix": "2600:1ffa:c000::/40", + "region": "us-west-1", + "service": "S3" + }, + { + "ipv6_prefix": "2600:1ffa:e000::/40", + "region": "sa-east-1", + "service": "S3" + }, + { + "ipv6_prefix": "2a05:d050:2000::/40", + "region": "eu-west-3", + "service": "S3" + }, + { + "ipv6_prefix": "2a05:d050:4000::/40", + "region": "eu-central-1", + "service": "S3" + }, + { + "ipv6_prefix": "2a05:d050:8000::/40", + "region": "eu-west-1", + "service": "S3" + }, + { + "ipv6_prefix": "2a05:d050:c000::/40", + "region": "eu-west-2", + "service": "S3" + }, + { + "ipv6_prefix": "2a05:d078:2000::/40", + "region": "eu-west-3", + "service": "S3" + }, + { + "ipv6_prefix": "2a05:d078:4000::/40", + "region": "eu-central-1", + "service": "S3" + }, + { + "ipv6_prefix": "2a05:d078:8000::/40", + "region": "eu-west-1", + "service": "S3" + }, + { + "ipv6_prefix": "2a05:d078:c000::/40", + "region": "eu-west-2", + "service": "S3" + }, + { + "ipv6_prefix": "2a05:d079:2000::/40", + "region": "eu-west-3", + "service": "S3" + }, + { + "ipv6_prefix": "2a05:d079:4000::/40", + "region": "eu-central-1", + "service": "S3" + }, + { + "ipv6_prefix": "2a05:d079:8000::/40", + "region": "eu-west-1", + "service": "S3" + }, + { + "ipv6_prefix": "2a05:d079:c000::/40", + "region": "eu-west-2", + "service": "S3" + }, + { + "ipv6_prefix": "2a05:d07a:2000::/40", + "region": "eu-west-3", + "service": "S3" + }, + { + "ipv6_prefix": "2a05:d07a:4000::/40", + "region": "eu-central-1", + "service": "S3" + }, + { + "ipv6_prefix": "2a05:d07a:8000::/40", + "region": "eu-west-1", + "service": "S3" + }, + { + "ipv6_prefix": "2a05:d07a:c000::/40", + "region": "eu-west-2", + "service": "S3" + }, + { + "ipv6_prefix": "2400:6500:ff00::/64", + "region": "ap-southeast-1", + "service": "EC2" + }, + { + "ipv6_prefix": "2400:6700:ff00::/64", + "region": "ap-northeast-1", + "service": "EC2" + }, + { + "ipv6_prefix": "2403:b300:ff00::/64", + "region": "ap-southeast-2", + "service": "EC2" + }, + { + "ipv6_prefix": "2406:da00:2000::/40", + "region": "ap-northeast-2", + "service": "EC2" + }, + { + "ipv6_prefix": "2406:da00:4000::/40", + "region": "ap-northeast-1", + "service": "EC2" + }, + { + "ipv6_prefix": "2406:da00:8000::/40", + "region": "ap-southeast-1", + "service": "EC2" + }, + { + "ipv6_prefix": "2406:da00:a000::/40", + "region": "ap-south-1", + "service": "EC2" + }, + { + "ipv6_prefix": "2406:da00:c000::/40", + "region": "ap-southeast-2", + "service": "EC2" + }, + { + "ipv6_prefix": "2406:da00:ff00::/64", + "region": "us-east-1", + "service": "EC2" + }, + { + "ipv6_prefix": "2406:da12::/36", + "region": "ap-northeast-2", + "service": "EC2" + }, + { + "ipv6_prefix": "2406:da14::/36", + "region": "ap-northeast-1", + "service": "EC2" + }, + { + "ipv6_prefix": "2406:da18::/36", + "region": "ap-southeast-1", + "service": "EC2" + }, + { + "ipv6_prefix": "2406:da1a::/36", + "region": "ap-south-1", + "service": "EC2" + }, + { + "ipv6_prefix": "2406:da1c::/36", + "region": "ap-southeast-2", + "service": "EC2" + }, + { + "ipv6_prefix": "2406:daff:2000::/40", + "region": "ap-northeast-2", + "service": "EC2" + }, + { + "ipv6_prefix": "2406:daff:4000::/40", + "region": "ap-northeast-1", + "service": "EC2" + }, + { + "ipv6_prefix": "2406:daff:8000::/40", + "region": "ap-southeast-1", + "service": "EC2" + }, + { + "ipv6_prefix": "2406:daff:a000::/40", + "region": "ap-south-1", + "service": "EC2" + }, + { + "ipv6_prefix": "2406:daff:c000::/40", + "region": "ap-southeast-2", + "service": "EC2" + }, + { + "ipv6_prefix": "240f:8000:4000::/40", + "region": "cn-northwest-1", + "service": "EC2" + }, + { + "ipv6_prefix": "240f:8000:8000::/40", + "region": "cn-north-1", + "service": "EC2" + }, + { + "ipv6_prefix": "240f:8014::/36", + "region": "cn-northwest-1", + "service": "EC2" + }, + { + "ipv6_prefix": "240f:8018::/36", + "region": "cn-north-1", + "service": "EC2" + }, + { + "ipv6_prefix": "240f:80ff:4000::/40", + "region": "cn-northwest-1", + "service": "EC2" + }, + { + "ipv6_prefix": "240f:80ff:8000::/40", + "region": "cn-north-1", + "service": "EC2" + }, + { + "ipv6_prefix": "2600:1f00:1000::/40", + "region": "ca-central-1", + "service": "EC2" + }, + { + "ipv6_prefix": "2600:1f00:2000::/40", + "region": "us-gov-west-1", + "service": "EC2" + }, + { + "ipv6_prefix": "2600:1f00:4000::/40", + "region": "us-west-2", + "service": "EC2" + }, + { + "ipv6_prefix": "2600:1f00:6000::/40", + "region": "us-east-2", + "service": "EC2" + }, + { + "ipv6_prefix": "2600:1f00:8000::/40", + "region": "us-east-1", + "service": "EC2" + }, + { + "ipv6_prefix": "2600:1f00:c000::/40", + "region": "us-west-1", + "service": "EC2" + }, + { + "ipv6_prefix": "2600:1f00:e000::/40", + "region": "sa-east-1", + "service": "EC2" + }, + { + "ipv6_prefix": "2600:1f11::/36", + "region": "ca-central-1", + "service": "EC2" + }, + { + "ipv6_prefix": "2600:1f12::/36", + "region": "us-gov-west-1", + "service": "EC2" + }, + { + "ipv6_prefix": "2600:1f14::/35", + "region": "us-west-2", + "service": "EC2" + }, + { + "ipv6_prefix": "2600:1f16::/36", + "region": "us-east-2", + "service": "EC2" + }, + { + "ipv6_prefix": "2600:1f18::/33", + "region": "us-east-1", + "service": "EC2" + }, + { + "ipv6_prefix": "2600:1f1c::/36", + "region": "us-west-1", + "service": "EC2" + }, + { + "ipv6_prefix": "2600:1f1e::/36", + "region": "sa-east-1", + "service": "EC2" + }, + { + "ipv6_prefix": "2600:1fff:1000::/40", + "region": "ca-central-1", + "service": "EC2" + }, + { + "ipv6_prefix": "2600:1fff:2000::/40", + "region": "us-gov-west-1", + "service": "EC2" + }, + { + "ipv6_prefix": "2600:1fff:4000::/40", + "region": "us-west-2", + "service": "EC2" + }, + { + "ipv6_prefix": "2600:1fff:6000::/40", + "region": "us-east-2", + "service": "EC2" + }, + { + "ipv6_prefix": "2600:1fff:8000::/40", + "region": "us-east-1", + "service": "EC2" + }, + { + "ipv6_prefix": "2600:1fff:c000::/40", + "region": "us-west-1", + "service": "EC2" + }, + { + "ipv6_prefix": "2600:1fff:e000::/40", + "region": "sa-east-1", + "service": "EC2" + }, + { + "ipv6_prefix": "2620:107:300f::/64", + "region": "us-west-1", + "service": "EC2" + }, + { + "ipv6_prefix": "2620:108:700f::/64", + "region": "us-west-2", + "service": "EC2" + }, + { + "ipv6_prefix": "2620:108:d00f::/64", + "region": "us-gov-west-1", + "service": "EC2" + }, + { + "ipv6_prefix": "2804:800:ff00::/64", + "region": "sa-east-1", + "service": "EC2" + }, + { + "ipv6_prefix": "2a01:578:3::/64", + "region": "eu-west-1", + "service": "EC2" + }, + { + "ipv6_prefix": "2a01:578:13::/64", + "region": "eu-central-1", + "service": "EC2" + }, + { + "ipv6_prefix": "2a05:d000:2000::/40", + "region": "eu-west-3", + "service": "EC2" + }, + { + "ipv6_prefix": "2a05:d000:4000::/40", + "region": "eu-central-1", + "service": "EC2" + }, + { + "ipv6_prefix": "2a05:d000:8000::/40", + "region": "eu-west-1", + "service": "EC2" + }, + { + "ipv6_prefix": "2a05:d000:c000::/40", + "region": "eu-west-2", + "service": "EC2" + }, + { + "ipv6_prefix": "2a05:d012::/36", + "region": "eu-west-3", + "service": "EC2" + }, + { + "ipv6_prefix": "2a05:d014::/36", + "region": "eu-central-1", + "service": "EC2" + }, + { + "ipv6_prefix": "2a05:d018::/36", + "region": "eu-west-1", + "service": "EC2" + }, + { + "ipv6_prefix": "2a05:d01c::/36", + "region": "eu-west-2", + "service": "EC2" + }, + { + "ipv6_prefix": "2a05:d07f:2000::/40", + "region": "eu-west-3", + "service": "EC2" + }, + { + "ipv6_prefix": "2a05:d07f:4000::/40", + "region": "eu-central-1", + "service": "EC2" + }, + { + "ipv6_prefix": "2a05:d07f:8000::/40", + "region": "eu-west-1", + "service": "EC2" + }, + { + "ipv6_prefix": "2a05:d07f:c000::/40", + "region": "eu-west-2", + "service": "EC2" + }, + { + "ipv6_prefix": "2600:9000::/28", + "region": "GLOBAL", + "service": "CLOUDFRONT" + } + ] +} diff --git a/trie.go b/trie.go new file mode 100644 index 0000000000000000000000000000000000000000..679adc0fc4b6722477c5327ad7842a79676807f2 --- /dev/null +++ b/trie.go @@ -0,0 +1,404 @@ +package cidranger + +import ( + "fmt" + "net" + "strings" + + rnet "gitlab.dms3.io/p2p/go-cidranger/net" +) + +// prefixTrie is a path-compressed (PC) trie implementation of the +// ranger interface inspired by this blog post: +// https://vincent.bernat.im/en/blog/2017-ipv4-route-lookup-linux +// +// CIDR blocks are stored using a prefix tree structure where each node has its +// parent as prefix, and the path from the root node represents current CIDR +// block. +// +// For IPv4, the trie structure guarantees max depth of 32 as IPv4 addresses are +// 32 bits long and each bit represents a prefix tree starting at that bit. This +// property also guarantees constant lookup time in Big-O notation. +// +// Path compression compresses a string of node with only 1 child into a single +// node, decrease the amount of lookups necessary during containment tests. +// +// Level compression dictates the amount of direct children of a node by +// allowing it to handle multiple bits in the path. The heuristic (based on +// children population) to decide when the compression and decompression happens +// is outlined in the prior linked blog, and will be experimented with in more +// depth in this project in the future. +// +// Note: Can not insert both IPv4 and IPv6 network addresses into the same +// prefix trie, use versionedRanger wrapper instead. +// +// TODO: Implement level-compressed component of the LPC trie. +type prefixTrie struct { + parent *prefixTrie + children [2]*prefixTrie + + numBitsSkipped uint + numBitsHandled uint + + network rnet.Network + entry RangerEntry + + size int // This is only maintained in the root trie. +} + +var ip4ZeroCIDR, ip6ZeroCIDR net.IPNet + +func init() { + _, v4, _ := net.ParseCIDR("0.0.0.0/0") + _, v6, _ := net.ParseCIDR("0::0/0") + ip4ZeroCIDR = *v4 + ip6ZeroCIDR = *v6 +} + +func newRanger(version rnet.IPVersion) Ranger { + return newPrefixTree(version) +} + +// newPrefixTree creates a new prefixTrie. +func newPrefixTree(version rnet.IPVersion) *prefixTrie { + rootNet := ip4ZeroCIDR + if version == rnet.IPv6 { + rootNet = ip6ZeroCIDR + } + return &prefixTrie{ + numBitsSkipped: 0, + numBitsHandled: 1, + network: rnet.NewNetwork(rootNet), + } +} + +func newPathprefixTrie(network rnet.Network, numBitsSkipped uint) *prefixTrie { + version := rnet.IPv4 + if len(network.Number) == rnet.IPv6Uint32Count { + version = rnet.IPv6 + } + path := newPrefixTree(version) + path.numBitsSkipped = numBitsSkipped + path.network = network.Masked(int(numBitsSkipped)) + return path +} + +func newEntryTrie(network rnet.Network, entry RangerEntry) *prefixTrie { + leaf := newPathprefixTrie(network, uint(network.Mask)) + leaf.entry = entry + return leaf +} + +// Insert inserts a RangerEntry into prefix trie. +func (p *prefixTrie) Insert(entry RangerEntry) error { + network := entry.Network() + sizeIncreased, err := p.insert(rnet.NewNetwork(network), entry) + if sizeIncreased { + p.size++ + } + return err +} + +// Remove removes RangerEntry identified by given network from trie. +func (p *prefixTrie) Remove(network net.IPNet) (RangerEntry, error) { + entry, err := p.remove(rnet.NewNetwork(network)) + if entry != nil { + p.size-- + } + return entry, err +} + +// Contains returns boolean indicating whether given ip is contained in any +// of the inserted networks. +func (p *prefixTrie) Contains(ip net.IP) (bool, error) { + nn := rnet.NewNetworkNumber(ip) + if nn == nil { + return false, ErrInvalidNetworkNumberInput + } + return p.contains(nn) +} + +// ContainingNetworks returns the list of RangerEntry(s) the given ip is +// contained in in ascending prefix order. +func (p *prefixTrie) ContainingNetworks(ip net.IP) ([]RangerEntry, error) { + nn := rnet.NewNetworkNumber(ip) + if nn == nil { + return nil, ErrInvalidNetworkNumberInput + } + return p.containingNetworks(nn) +} + +// CoveredNetworks returns the list of RangerEntry(s) the given ipnet +// covers. That is, the networks that are completely subsumed by the +// specified network. +func (p *prefixTrie) CoveredNetworks(network net.IPNet) ([]RangerEntry, error) { + net := rnet.NewNetwork(network) + return p.coveredNetworks(net) +} + +// Len returns number of networks in ranger. +func (p *prefixTrie) Len() int { + return p.size +} + +// String returns string representation of trie, mainly for visualization and +// debugging. +func (p *prefixTrie) String() string { + children := []string{} + padding := strings.Repeat("| ", p.level()+1) + for bits, child := range p.children { + if child == nil { + continue + } + childStr := fmt.Sprintf("\n%s%d--> %s", padding, bits, child.String()) + children = append(children, childStr) + } + return fmt.Sprintf("%s (target_pos:%d:has_entry:%t)%s", p.network, + p.targetBitPosition(), p.hasEntry(), strings.Join(children, "")) +} + +func (p *prefixTrie) contains(number rnet.NetworkNumber) (bool, error) { + if !p.network.Contains(number) { + return false, nil + } + if p.hasEntry() { + return true, nil + } + if p.targetBitPosition() < 0 { + return false, nil + } + bit, err := p.targetBitFromIP(number) + if err != nil { + return false, err + } + child := p.children[bit] + if child != nil { + return child.contains(number) + } + return false, nil +} + +func (p *prefixTrie) containingNetworks(number rnet.NetworkNumber) ([]RangerEntry, error) { + results := []RangerEntry{} + if !p.network.Contains(number) { + return results, nil + } + if p.hasEntry() { + results = []RangerEntry{p.entry} + } + if p.targetBitPosition() < 0 { + return results, nil + } + bit, err := p.targetBitFromIP(number) + if err != nil { + return nil, err + } + child := p.children[bit] + if child != nil { + ranges, err := child.containingNetworks(number) + if err != nil { + return nil, err + } + if len(ranges) > 0 { + if len(results) > 0 { + results = append(results, ranges...) + } else { + results = ranges + } + } + } + return results, nil +} + +func (p *prefixTrie) coveredNetworks(network rnet.Network) ([]RangerEntry, error) { + var results []RangerEntry + if network.Covers(p.network) { + for entry := range p.walkDepth() { + results = append(results, entry) + } + } else if p.targetBitPosition() >= 0 { + bit, err := p.targetBitFromIP(network.Number) + if err != nil { + return results, err + } + child := p.children[bit] + if child != nil { + return child.coveredNetworks(network) + } + } + return results, nil +} + +func (p *prefixTrie) insert(network rnet.Network, entry RangerEntry) (bool, error) { + if p.network.Equal(network) { + sizeIncreased := p.entry == nil + p.entry = entry + return sizeIncreased, nil + } + + bit, err := p.targetBitFromIP(network.Number) + if err != nil { + return false, err + } + existingChild := p.children[bit] + + // No existing child, insert new leaf trie. + if existingChild == nil { + p.appendTrie(bit, newEntryTrie(network, entry)) + return true, nil + } + + // Check whether it is necessary to insert additional path prefix between current trie and existing child, + // in the case that inserted network diverges on its path to existing child. + lcb, err := network.LeastCommonBitPosition(existingChild.network) + divergingBitPos := int(lcb) - 1 + if divergingBitPos > existingChild.targetBitPosition() { + pathPrefix := newPathprefixTrie(network, p.totalNumberOfBits()-lcb) + err := p.insertPrefix(bit, pathPrefix, existingChild) + if err != nil { + return false, err + } + // Update new child + existingChild = pathPrefix + } + return existingChild.insert(network, entry) +} + +func (p *prefixTrie) appendTrie(bit uint32, prefix *prefixTrie) { + p.children[bit] = prefix + prefix.parent = p +} + +func (p *prefixTrie) insertPrefix(bit uint32, pathPrefix, child *prefixTrie) error { + // Set parent/child relationship between current trie and inserted pathPrefix + p.children[bit] = pathPrefix + pathPrefix.parent = p + + // Set parent/child relationship between inserted pathPrefix and original child + pathPrefixBit, err := pathPrefix.targetBitFromIP(child.network.Number) + if err != nil { + return err + } + pathPrefix.children[pathPrefixBit] = child + child.parent = pathPrefix + return nil +} + +func (p *prefixTrie) remove(network rnet.Network) (RangerEntry, error) { + if p.hasEntry() && p.network.Equal(network) { + entry := p.entry + p.entry = nil + + err := p.compressPathIfPossible() + if err != nil { + return nil, err + } + return entry, nil + } + bit, err := p.targetBitFromIP(network.Number) + if err != nil { + return nil, err + } + child := p.children[bit] + if child != nil { + return child.remove(network) + } + return nil, nil +} + +func (p *prefixTrie) qualifiesForPathCompression() bool { + // Current prefix trie can be path compressed if it meets all following. + // 1. records no CIDR entry + // 2. has single or no child + // 3. is not root trie + return !p.hasEntry() && p.childrenCount() <= 1 && p.parent != nil +} + +func (p *prefixTrie) compressPathIfPossible() error { + if !p.qualifiesForPathCompression() { + // Does not qualify to be compressed + return nil + } + + // Find lone child. + var loneChild *prefixTrie + for _, child := range p.children { + if child != nil { + loneChild = child + break + } + } + + // Find root of currnt single child lineage. + parent := p.parent + for ; parent.qualifiesForPathCompression(); parent = parent.parent { + } + parentBit, err := parent.targetBitFromIP(p.network.Number) + if err != nil { + return err + } + parent.children[parentBit] = loneChild + + // Attempts to furthur apply path compression at current lineage parent, in case current lineage + // compressed into parent. + return parent.compressPathIfPossible() +} + +func (p *prefixTrie) childrenCount() int { + count := 0 + for _, child := range p.children { + if child != nil { + count++ + } + } + return count +} + +func (p *prefixTrie) totalNumberOfBits() uint { + return rnet.BitsPerUint32 * uint(len(p.network.Number)) +} + +func (p *prefixTrie) targetBitPosition() int { + return int(p.totalNumberOfBits()-p.numBitsSkipped) - 1 +} + +func (p *prefixTrie) targetBitFromIP(n rnet.NetworkNumber) (uint32, error) { + // This is a safe uint boxing of int since we should never attempt to get + // target bit at a negative position. + return n.Bit(uint(p.targetBitPosition())) +} + +func (p *prefixTrie) hasEntry() bool { + return p.entry != nil +} + +func (p *prefixTrie) level() int { + if p.parent == nil { + return 0 + } + return p.parent.level() + 1 +} + +// walkDepth walks the trie in depth order, for unit testing. +func (p *prefixTrie) walkDepth() <-chan RangerEntry { + entries := make(chan RangerEntry) + go func() { + if p.hasEntry() { + entries <- p.entry + } + childEntriesList := []<-chan RangerEntry{} + for _, trie := range p.children { + if trie == nil { + continue + } + childEntriesList = append(childEntriesList, trie.walkDepth()) + } + for _, childEntries := range childEntriesList { + for entry := range childEntries { + entries <- entry + } + } + close(entries) + }() + return entries +} diff --git a/trie_test.go b/trie_test.go new file mode 100644 index 0000000000000000000000000000000000000000..b4d12fe878e7e921d60f960c1e27311c87b491fd --- /dev/null +++ b/trie_test.go @@ -0,0 +1,569 @@ +package cidranger + +import ( + "encoding/binary" + "math/rand" + "net" + "runtime" + "testing" + "time" + + detectrace "gitlab.dms3.io/dms3/public/go-detect-race" + + "github.com/stretchr/testify/assert" + rnet "gitlab.dms3.io/p2p/go-cidranger/net" +) + +func getAllByVersion(version rnet.IPVersion) *net.IPNet { + if version == rnet.IPv6 { + return AllIPv6 + } + return AllIPv4 +} + +func TestPrefixTrieInsert(t *testing.T) { + cases := []struct { + version rnet.IPVersion + inserts []string + expectedNetworksInDepthOrder []string + name string + }{ + {rnet.IPv4, []string{"192.168.0.1/24"}, []string{"192.168.0.1/24"}, "basic insert"}, + { + rnet.IPv4, + []string{"1.2.3.4/32", "1.2.3.5/32"}, + []string{"1.2.3.4/32", "1.2.3.5/32"}, + "single ip IPv4 network insert", + }, + { + rnet.IPv6, + []string{"0::1/128", "0::2/128"}, + []string{"0::1/128", "0::2/128"}, + "single ip IPv6 network insert", + }, + { + rnet.IPv4, + []string{"192.168.0.1/16", "192.168.0.1/24"}, + []string{"192.168.0.1/16", "192.168.0.1/24"}, + "in order insert", + }, + { + rnet.IPv4, + []string{"192.168.0.1/32", "192.168.0.1/32"}, + []string{"192.168.0.1/32"}, + "duplicate network insert", + }, + { + rnet.IPv4, + []string{"192.168.0.1/24", "192.168.0.1/16"}, + []string{"192.168.0.1/16", "192.168.0.1/24"}, + "reverse insert", + }, + { + rnet.IPv4, + []string{"192.168.0.1/24", "192.168.1.1/24"}, + []string{"192.168.0.1/24", "192.168.1.1/24"}, + "branch insert", + }, + { + rnet.IPv4, + []string{"192.168.0.1/24", "192.168.1.1/24", "192.168.1.1/30"}, + []string{"192.168.0.1/24", "192.168.1.1/24", "192.168.1.1/30"}, + "branch inserts", + }, + } + for _, tc := range cases { + t.Run(tc.name, func(t *testing.T) { + trie := newPrefixTree(tc.version) + for _, insert := range tc.inserts { + _, network, _ := net.ParseCIDR(insert) + err := trie.Insert(NewBasicRangerEntry(*network)) + assert.NoError(t, err) + } + + assert.Equal(t, len(tc.expectedNetworksInDepthOrder), trie.Len(), "trie size should match") + + allNetworks, err := trie.CoveredNetworks(*getAllByVersion(tc.version)) + assert.Nil(t, err) + assert.Equal(t, len(allNetworks), trie.Len(), "trie size should match") + + walk := trie.walkDepth() + for _, network := range tc.expectedNetworksInDepthOrder { + _, ipnet, _ := net.ParseCIDR(network) + expected := NewBasicRangerEntry(*ipnet) + actual := <-walk + assert.Equal(t, expected, actual) + } + + // Ensure no unexpected elements in trie. + for network := range walk { + assert.Nil(t, network) + } + }) + } +} + +func TestPrefixTrieString(t *testing.T) { + inserts := []string{"192.168.0.1/24", "192.168.1.1/24", "192.168.1.1/30"} + trie := newPrefixTree(rnet.IPv4) + for _, insert := range inserts { + _, network, _ := net.ParseCIDR(insert) + trie.Insert(NewBasicRangerEntry(*network)) + } + expected := `0.0.0.0/0 (target_pos:31:has_entry:false) +| 1--> 192.168.0.0/23 (target_pos:8:has_entry:false) +| | 0--> 192.168.0.0/24 (target_pos:7:has_entry:true) +| | 1--> 192.168.1.0/24 (target_pos:7:has_entry:true) +| | | 0--> 192.168.1.0/30 (target_pos:1:has_entry:true)` + assert.Equal(t, expected, trie.String()) +} + +func TestPrefixTrieRemove(t *testing.T) { + cases := []struct { + version rnet.IPVersion + inserts []string + removes []string + expectedRemoves []string + expectedNetworksInDepthOrder []string + expectedTrieString string + name string + }{ + { + rnet.IPv4, + []string{"192.168.0.1/24"}, + []string{"192.168.0.1/24"}, + []string{"192.168.0.1/24"}, + []string{}, + "0.0.0.0/0 (target_pos:31:has_entry:false)", + "basic remove", + }, + { + rnet.IPv4, + []string{"1.2.3.4/32", "1.2.3.5/32"}, + []string{"1.2.3.5/32"}, + []string{"1.2.3.5/32"}, + []string{"1.2.3.4/32"}, + `0.0.0.0/0 (target_pos:31:has_entry:false) +| 0--> 1.2.3.4/32 (target_pos:-1:has_entry:true)`, + "single ip IPv4 network remove", + }, + { + rnet.IPv4, + []string{"0::1/128", "0::2/128"}, + []string{"0::2/128"}, + []string{"0::2/128"}, + []string{"0::1/128"}, + `0.0.0.0/0 (target_pos:31:has_entry:false) +| 0--> ::1/128 (target_pos:-1:has_entry:true)`, + "single ip IPv6 network remove", + }, + { + rnet.IPv4, + []string{"192.168.0.1/24", "192.168.0.1/25", "192.168.0.1/26"}, + []string{"192.168.0.1/25"}, + []string{"192.168.0.1/25"}, + []string{"192.168.0.1/24", "192.168.0.1/26"}, + `0.0.0.0/0 (target_pos:31:has_entry:false) +| 1--> 192.168.0.0/24 (target_pos:7:has_entry:true) +| | 0--> 192.168.0.0/26 (target_pos:5:has_entry:true)`, + "remove path prefix", + }, + { + rnet.IPv4, + []string{"192.168.0.1/24", "192.168.0.1/25", "192.168.0.64/26", "192.168.0.1/26"}, + []string{"192.168.0.1/25"}, + []string{"192.168.0.1/25"}, + []string{"192.168.0.1/24", "192.168.0.1/26", "192.168.0.64/26"}, + `0.0.0.0/0 (target_pos:31:has_entry:false) +| 1--> 192.168.0.0/24 (target_pos:7:has_entry:true) +| | 0--> 192.168.0.0/25 (target_pos:6:has_entry:false) +| | | 0--> 192.168.0.0/26 (target_pos:5:has_entry:true) +| | | 1--> 192.168.0.64/26 (target_pos:5:has_entry:true)`, + "remove path prefix with more than 1 children", + }, + { + rnet.IPv4, + []string{"192.168.0.1/24", "192.168.0.1/25"}, + []string{"192.168.0.1/26"}, + []string{""}, + []string{"192.168.0.1/24", "192.168.0.1/25"}, + `0.0.0.0/0 (target_pos:31:has_entry:false) +| 1--> 192.168.0.0/24 (target_pos:7:has_entry:true) +| | 0--> 192.168.0.0/25 (target_pos:6:has_entry:true)`, + "remove non existent", + }, + } + + for _, tc := range cases { + t.Run(tc.name, func(t *testing.T) { + trie := newPrefixTree(tc.version) + for _, insert := range tc.inserts { + _, network, _ := net.ParseCIDR(insert) + err := trie.Insert(NewBasicRangerEntry(*network)) + assert.NoError(t, err) + } + for i, remove := range tc.removes { + _, network, _ := net.ParseCIDR(remove) + removed, err := trie.Remove(*network) + assert.NoError(t, err) + if str := tc.expectedRemoves[i]; str != "" { + _, ipnet, _ := net.ParseCIDR(str) + expected := NewBasicRangerEntry(*ipnet) + assert.Equal(t, expected, removed) + } else { + assert.Nil(t, removed) + } + } + + assert.Equal(t, len(tc.expectedNetworksInDepthOrder), trie.Len(), "trie size should match after revmoval") + + allNetworks, err := trie.CoveredNetworks(*getAllByVersion(tc.version)) + assert.Nil(t, err) + assert.Equal(t, len(allNetworks), trie.Len(), "trie size should match") + + walk := trie.walkDepth() + for _, network := range tc.expectedNetworksInDepthOrder { + _, ipnet, _ := net.ParseCIDR(network) + expected := NewBasicRangerEntry(*ipnet) + actual := <-walk + assert.Equal(t, expected, actual) + } + + // Ensure no unexpected elements in trie. + for network := range walk { + assert.Nil(t, network) + } + + assert.Equal(t, tc.expectedTrieString, trie.String()) + }) + } +} + +func TestToReplicateIssue(t *testing.T) { + cases := []struct { + version rnet.IPVersion + inserts []string + ip net.IP + networks []string + name string + }{ + { + rnet.IPv4, + []string{"192.168.0.1/32"}, + net.ParseIP("192.168.0.1"), + []string{"192.168.0.1/32"}, + "basic containing network for /32 mask", + }, + { + rnet.IPv6, + []string{"a::1/128"}, + net.ParseIP("a::1"), + []string{"a::1/128"}, + "basic containing network for /128 mask", + }, + } + for _, tc := range cases { + t.Run(tc.name, func(t *testing.T) { + trie := newPrefixTree(tc.version) + for _, insert := range tc.inserts { + _, network, _ := net.ParseCIDR(insert) + err := trie.Insert(NewBasicRangerEntry(*network)) + assert.NoError(t, err) + } + expectedEntries := []RangerEntry{} + for _, network := range tc.networks { + _, net, _ := net.ParseCIDR(network) + expectedEntries = append(expectedEntries, NewBasicRangerEntry(*net)) + } + contains, err := trie.Contains(tc.ip) + assert.NoError(t, err) + assert.True(t, contains) + networks, err := trie.ContainingNetworks(tc.ip) + assert.NoError(t, err) + assert.Equal(t, expectedEntries, networks) + }) + } +} + +type expectedIPRange struct { + start net.IP + end net.IP +} + +func TestPrefixTrieContains(t *testing.T) { + cases := []struct { + version rnet.IPVersion + inserts []string + expectedIPs []expectedIPRange + name string + }{ + { + rnet.IPv4, + []string{"192.168.0.0/24"}, + []expectedIPRange{ + {net.ParseIP("192.168.0.0"), net.ParseIP("192.168.1.0")}, + }, + "basic contains", + }, + { + rnet.IPv4, + []string{"192.168.0.0/24", "128.168.0.0/24"}, + []expectedIPRange{ + {net.ParseIP("192.168.0.0"), net.ParseIP("192.168.1.0")}, + {net.ParseIP("128.168.0.0"), net.ParseIP("128.168.1.0")}, + }, + "multiple ranges contains", + }, + } + + for _, tc := range cases { + t.Run(tc.name, func(t *testing.T) { + trie := newPrefixTree(tc.version) + for _, insert := range tc.inserts { + _, network, _ := net.ParseCIDR(insert) + err := trie.Insert(NewBasicRangerEntry(*network)) + assert.NoError(t, err) + } + for _, expectedIPRange := range tc.expectedIPs { + var contains bool + var err error + start := expectedIPRange.start + for ; !expectedIPRange.end.Equal(start); start = rnet.NextIP(start) { + contains, err = trie.Contains(start) + assert.NoError(t, err) + assert.True(t, contains) + } + + // Check out of bounds ips on both ends + contains, err = trie.Contains(rnet.PreviousIP(expectedIPRange.start)) + assert.NoError(t, err) + assert.False(t, contains) + contains, err = trie.Contains(rnet.NextIP(expectedIPRange.end)) + assert.NoError(t, err) + assert.False(t, contains) + } + }) + } +} + +func TestPrefixTrieContainingNetworks(t *testing.T) { + cases := []struct { + version rnet.IPVersion + inserts []string + ip net.IP + networks []string + name string + }{ + { + rnet.IPv4, + []string{"192.168.0.0/24"}, + net.ParseIP("192.168.0.1"), + []string{"192.168.0.0/24"}, + "basic containing networks", + }, + { + rnet.IPv4, + []string{"192.168.0.0/24", "192.168.0.0/25"}, + net.ParseIP("192.168.0.1"), + []string{"192.168.0.0/24", "192.168.0.0/25"}, + "inclusive networks", + }, + } + for _, tc := range cases { + t.Run(tc.name, func(t *testing.T) { + trie := newPrefixTree(tc.version) + for _, insert := range tc.inserts { + _, network, _ := net.ParseCIDR(insert) + err := trie.Insert(NewBasicRangerEntry(*network)) + assert.NoError(t, err) + } + expectedEntries := []RangerEntry{} + for _, network := range tc.networks { + _, net, _ := net.ParseCIDR(network) + expectedEntries = append(expectedEntries, NewBasicRangerEntry(*net)) + } + networks, err := trie.ContainingNetworks(tc.ip) + assert.NoError(t, err) + assert.Equal(t, expectedEntries, networks) + }) + } +} + +type coveredNetworkTest struct { + version rnet.IPVersion + inserts []string + search string + networks []string + name string +} + +var coveredNetworkTests = []coveredNetworkTest{ + { + rnet.IPv4, + []string{"192.168.0.0/24"}, + "192.168.0.0/16", + []string{"192.168.0.0/24"}, + "basic covered networks", + }, + { + rnet.IPv4, + []string{"192.168.0.0/24"}, + "10.1.0.0/16", + nil, + "nothing", + }, + { + rnet.IPv4, + []string{"192.168.0.0/24", "192.168.0.0/25"}, + "192.168.0.0/16", + []string{"192.168.0.0/24", "192.168.0.0/25"}, + "multiple networks", + }, + { + rnet.IPv4, + []string{"192.168.0.0/24", "192.168.0.0/25", "192.168.0.1/32"}, + "192.168.0.0/16", + []string{"192.168.0.0/24", "192.168.0.0/25", "192.168.0.1/32"}, + "multiple networks 2", + }, + { + rnet.IPv4, + []string{"192.168.1.1/32"}, + "192.168.0.0/16", + []string{"192.168.1.1/32"}, + "leaf", + }, + { + rnet.IPv4, + []string{"0.0.0.0/0", "192.168.1.1/32"}, + "192.168.0.0/16", + []string{"192.168.1.1/32"}, + "leaf with root", + }, + { + rnet.IPv4, + []string{ + "0.0.0.0/0", "192.168.0.0/24", "192.168.1.1/32", + "10.1.0.0/16", "10.1.1.0/24", + }, + "192.168.0.0/16", + []string{"192.168.0.0/24", "192.168.1.1/32"}, + "path not taken", + }, + { + rnet.IPv4, + []string{ + "192.168.0.0/15", + }, + "192.168.0.0/16", + nil, + "only masks different", + }, +} + +func TestPrefixTrieCoveredNetworks(t *testing.T) { + for _, tc := range coveredNetworkTests { + t.Run(tc.name, func(t *testing.T) { + trie := newPrefixTree(tc.version) + for _, insert := range tc.inserts { + _, network, _ := net.ParseCIDR(insert) + err := trie.Insert(NewBasicRangerEntry(*network)) + assert.NoError(t, err) + } + var expectedEntries []RangerEntry + for _, network := range tc.networks { + _, net, _ := net.ParseCIDR(network) + expectedEntries = append(expectedEntries, + NewBasicRangerEntry(*net)) + } + _, snet, _ := net.ParseCIDR(tc.search) + networks, err := trie.CoveredNetworks(*snet) + assert.NoError(t, err) + assert.Equal(t, expectedEntries, networks) + }) + } +} + +func TestTrieMemUsage(t *testing.T) { + if testing.Short() { + t.Skip("Skipping memory test in `-short` mode") + } + + if detectrace.WithRace() { + t.Skip("Skipping memory test in `-race` mode") + } + + numIPs := 100000 + runs := 10 + + // Avg heap allocation over all runs should not be more than the heap allocation of first run multiplied + // by threshold, picking 1% as sane number for detecting memory leak. + thresh := 1.01 + + trie := newPrefixTree(rnet.IPv4) + + var baseLineHeap, totalHeapAllocOverRuns uint64 + for i := 0; i < runs; i++ { + t.Logf("Executing Run %d of %d", i+1, runs) + + // Insert networks. + for n := 0; n < numIPs; n++ { + trie.Insert(NewBasicRangerEntry(GenLeafIPNet(GenIPV4()))) + } + t.Logf("Inserted All (%d networks)", trie.Len()) + assert.Less(t, 0, trie.Len(), "Len should > 0") + assert.LessOrEqualf(t, trie.Len(), numIPs, "Len should <= %d", numIPs) + + allNetworks, err := trie.CoveredNetworks(*getAllByVersion(rnet.IPv4)) + assert.Nil(t, err) + assert.Equal(t, len(allNetworks), trie.Len(), "trie size should match") + + // Remove networks. + _, all, _ := net.ParseCIDR("0.0.0.0/0") + ll, _ := trie.CoveredNetworks(*all) + for i := 0; i < len(ll); i++ { + trie.Remove(ll[i].Network()) + } + t.Logf("Removed All (%d networks)", len(ll)) + assert.Equal(t, 0, trie.Len(), "Len after removal should == 0") + + // Perform GC + runtime.GC() + + // Get HeapAlloc stats. + heapAlloc := GetHeapAllocation() + totalHeapAllocOverRuns += heapAlloc + if i == 0 { + baseLineHeap = heapAlloc + } + } + + // Assert that heap allocation from first loop is within set threshold of avg over all runs. + assert.Less(t, uint64(0), baseLineHeap) + assert.LessOrEqual(t, float64(baseLineHeap), float64(totalHeapAllocOverRuns/uint64(runs))*thresh) +} + +func GenLeafIPNet(ip net.IP) net.IPNet { + return net.IPNet{ + IP: ip, + Mask: net.CIDRMask(32, 32), + } +} + +// GenIPV4 generates an IPV4 address +func GenIPV4() net.IP { + rand.Seed(time.Now().UnixNano()) + var min, max int + min = 1 + max = 4294967295 + nn := rand.Intn(max-min) + min + ip := make(net.IP, 4) + binary.BigEndian.PutUint32(ip, uint32(nn)) + return ip +} + +func GetHeapAllocation() uint64 { + var m runtime.MemStats + runtime.ReadMemStats(&m) + return m.HeapAlloc +} diff --git a/version.go b/version.go new file mode 100644 index 0000000000000000000000000000000000000000..dddf6d5b302aa3788bde5776b8139532565bff70 --- /dev/null +++ b/version.go @@ -0,0 +1,77 @@ +package cidranger + +import ( + "net" + + rnet "gitlab.dms3.io/p2p/go-cidranger/net" +) + +type rangerFactory func(rnet.IPVersion) Ranger + +type versionedRanger struct { + ipV4Ranger Ranger + ipV6Ranger Ranger +} + +func newVersionedRanger(factory rangerFactory) Ranger { + return &versionedRanger{ + ipV4Ranger: factory(rnet.IPv4), + ipV6Ranger: factory(rnet.IPv6), + } +} + +func (v *versionedRanger) Insert(entry RangerEntry) error { + network := entry.Network() + ranger, err := v.getRangerForIP(network.IP) + if err != nil { + return err + } + return ranger.Insert(entry) +} + +func (v *versionedRanger) Remove(network net.IPNet) (RangerEntry, error) { + ranger, err := v.getRangerForIP(network.IP) + if err != nil { + return nil, err + } + return ranger.Remove(network) +} + +func (v *versionedRanger) Contains(ip net.IP) (bool, error) { + ranger, err := v.getRangerForIP(ip) + if err != nil { + return false, err + } + return ranger.Contains(ip) +} + +func (v *versionedRanger) ContainingNetworks(ip net.IP) ([]RangerEntry, error) { + ranger, err := v.getRangerForIP(ip) + if err != nil { + return nil, err + } + return ranger.ContainingNetworks(ip) +} + +func (v *versionedRanger) CoveredNetworks(network net.IPNet) ([]RangerEntry, error) { + ranger, err := v.getRangerForIP(network.IP) + if err != nil { + return nil, err + } + return ranger.CoveredNetworks(network) +} + +// Len returns number of networks in ranger. +func (v *versionedRanger) Len() int { + return v.ipV4Ranger.Len() + v.ipV6Ranger.Len() +} + +func (v *versionedRanger) getRangerForIP(ip net.IP) (Ranger, error) { + if ip.To4() != nil { + return v.ipV4Ranger, nil + } + if ip.To16() != nil { + return v.ipV6Ranger, nil + } + return nil, ErrInvalidNetworkNumberInput +}