├── LICENSE
├── README.md
├── get-rib.sh
├── ip2asn.py
├── rib-trie
└── trie_data.json.gz
├── setup.sh
└── static
├── background.png
└── ip2asn.png
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2023 Devansh Batham
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 | ip2asn
3 |
4 |
5 |
6 | A utility to quickly map IP addresses to their respective ASN
7 |
8 |
9 | 🏗️ Installation
10 |
11 | ⛏️ Usage
12 |
13 | 🔄 Updating RIB Data
14 |
15 | 📖 How It Works
16 |
17 |
18 |
19 |
20 | 
21 |
22 | # Installation
23 |
24 | ```sh
25 | git clone https://github.com/devanshbatham/ip2asn
26 | cd ip2asn
27 | sudo chmod +x setup.sh
28 | ./setup.sh
29 | ```
30 |
31 |
32 |
33 | *Note:* The utility comes pre-packed with trie data, so if you don't wish to update the RIB data, it should work well right off the bat.
34 |
35 | # Usage
36 |
37 |
38 | ```sh
39 | (~)>> cat input_ips.txt | ip2asn
40 |
41 | 74.122.191.97 [AS15211]
42 | 74.122.191.99 [AS15211]
43 | 151.101.65.49 [AS54113]
44 | 162.159.136.66 [AS13335]
45 | 75.2.48.152 [AS16509]
46 | 76.223.91.57 [AS16509]
47 | 80.239.194.78 [AS1299]
48 | 99.86.20.101 [AS16509]
49 | 99.86.20.118 [AS16509]
50 | ```
51 |
52 | - With `--json` for JSON output:
53 |
54 | ```sh
55 | (~)>> cat input_ips.txt | ip2asn --json
56 |
57 | {
58 | "AS1299": [
59 | "62.115.144.49",
60 | "62.115.37.34",
61 | "62.115.57.70",
62 | "80.239.194.78"
63 | ],
64 | "AS11404": [
65 | "65.50.201.2"
66 | ],
67 | "AS11696": [
68 | "69.60.198.202"
69 | ]
70 | }
71 | ```
72 |
73 |
74 |
75 | # Updating RIB Data
76 |
77 | If you wish to build the trie yourself or want to update the trie data:
78 |
79 | 1. By default, `trie_data.json.gz` is saved in the `~/.ip2asn/` folder. So, firstly, delete the `~/.ip2asn` folder.
80 | 2. Run the `get-rib.sh` script.
81 | 3. Build the trie using the `--rib-path` or `--rp` option with the updated RIB data.
82 |
83 | ```sh
84 | echo "8.8.8.8" | ip2asn --rib-path /path/to/processed_rib.txt
85 | ```
86 |
87 |
88 |
89 | # How It Works
90 |
91 | The `ip2asn` utility employs a trie data structure to map IP address prefixes to their corresponding Autonomous System Numbers (ASN). Each key in the trie represents an IP address prefix, while the corresponding value denotes the AS number that announces these prefixes. The utility checks this trie to find the longest matching prefix for each IP and retrieves its associated ASN.
92 |
--------------------------------------------------------------------------------
/get-rib.sh:
--------------------------------------------------------------------------------
1 |
2 | #!/bin/bash
3 |
4 | # Base URL for RouteViews archive
5 | BASE_URL="http://archive.routeviews.org/bgpdata"
6 |
7 | # Current working directory
8 | RIB_DIR=$(pwd)
9 |
10 | # Get yesterday's date in the format YYYY.MM and YYYYMMDD
11 | YEAR_MONTH=$(date -d "yesterday" "+%Y.%m")
12 | DAY=$(date -d "yesterday" "+%Y%m%d")
13 |
14 | # Construct the URL for the previous day at 10:00
15 | RIB_URL="$BASE_URL/$YEAR_MONTH/RIBS/rib.$DAY.1000.bz2"
16 |
17 | # Hardcoded fallback URL
18 | HARDCODED_URL="http://archive.routeviews.org/bgpdata/2023.08/RIBS/rib.20230807.1000.bz2"
19 |
20 | # Filename to save the downloaded RIB
21 | DOWNLOAD_PATH="$RIB_DIR/rib.$DAY.1000.bz2"
22 |
23 | # Extracted RIB filename
24 | EXTRACTED_RIB_FILE="$RIB_DIR/rib.$DAY.1000"
25 |
26 | # Processed RIB filename
27 | PROCESSED_RIB="$RIB_DIR/processed_rib.txt"
28 |
29 | # Download the RIB file
30 | echo "Downloading RIB data from $RIB_URL..."
31 | curl -o $DOWNLOAD_PATH $RIB_URL
32 |
33 | # Check if download was successful
34 | if [ $? -ne 0 ]; then
35 | echo "Failed to download RIB data from the generated URL."
36 | echo "Falling back to hardcoded URL: $HARDCODED_URL..."
37 | curl -o $DOWNLOAD_PATH $HARDCODED_URL
38 | if [ $? -ne 0 ]; then
39 | echo "Failed to download RIB data from hardcoded URL as well."
40 | exit 1
41 | fi
42 | fi
43 |
44 | echo "Extracting RIB data from $DOWNLOAD_PATH..."
45 | # Decompress the RIB data
46 | bunzip2 $DOWNLOAD_PATH
47 |
48 | # Check if decompression was successful
49 | if [ $? -ne 0 ]; then
50 | echo "Failed to extract RIB data."
51 | exit 1
52 | fi
53 |
54 | # Process the RIB data
55 | echo "Processing RIB data with bgpdump..."
56 | bgpdump $EXTRACTED_RIB_FILE > $PROCESSED_RIB
57 |
58 | # Check if processing was successful
59 | if [ $? -ne 0 ]; then
60 | echo "Failed to process RIB data with bgpdump."
61 | exit 1
62 | fi
63 |
64 | rm rib*
65 |
66 | echo "RIB data processed and saved to $PROCESSED_RIB"
67 |
68 |
69 | exit 0
--------------------------------------------------------------------------------
/ip2asn.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 |
3 | import pytricia
4 | import sys
5 | import json
6 | import gzip
7 | from tqdm import tqdm
8 | import os
9 | from termcolor import colored
10 | import argparse
11 | from collections import defaultdict
12 | import logging
13 | import ipaddress
14 | from pathlib import Path
15 |
16 |
17 |
18 | # Constants
19 | HOME_DIR = str(Path.home())
20 | TRIE_DIR = os.path.join(HOME_DIR, ".ip2asn")
21 | os.makedirs(TRIE_DIR, exist_ok=True)
22 | TRIE_SAVE_PATH = os.path.join(TRIE_DIR, "trie_data.json.gz")
23 |
24 | # Initialize logging
25 | logging.basicConfig(level=logging.INFO, format='%(message)s')
26 | logger = logging.getLogger()
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 | def is_private_ip(ip):
36 | """
37 | Check if the given IP is a private IP.
38 |
39 | Args:
40 | - ip (str): IP address to check.
41 |
42 | Returns:
43 | - bool: True if the IP is private, False otherwise.
44 | """
45 | try:
46 | return ipaddress.ip_address(ip).is_private
47 | except ValueError:
48 | # Handle the error (you can raise another exception, print an error message, etc.)
49 | pass
50 | return False
51 |
52 |
53 |
54 |
55 | def build_trie_from_rib(rib_file_path):
56 | """
57 | Build a Trie structure from the given RIB (Routing Information Base) file.
58 |
59 | Args:
60 | - rib_file_path (str): Path to the RIB file.
61 |
62 | Returns:
63 | - pytricia.PyTricia: Trie structure mapping IP prefixes to AS numbers.
64 | """
65 | trie = pytricia.PyTricia()
66 |
67 | try:
68 | with open(rib_file_path, "r") as f:
69 | total_lines = sum(1 for _ in f)
70 | f.seek(0)
71 | current_prefix = None
72 | for line in tqdm(f, total=total_lines, desc="Building Trie"):
73 | if line.startswith("PREFIX:"):
74 | current_prefix = line.split()[-1]
75 | elif line.startswith("ASPATH:") and current_prefix:
76 | asn = line.split()[-1]
77 | trie[current_prefix] = asn
78 | current_prefix = None
79 | except FileNotFoundError:
80 | logger.error(f"RIB file '{rib_file_path}' not found. Please ensure the path is correct or run get_rib.sh to generate the file.")
81 | sys.exit(1)
82 |
83 | with gzip.open(TRIE_SAVE_PATH, "wt") as f:
84 | json.dump({key: trie[key] for key in trie}, f)
85 |
86 | return trie
87 |
88 |
89 | def load_trie_from_file(file_path):
90 | """
91 | Load a Trie from a saved file.
92 |
93 | Args:
94 | - file_path (str): Path to the saved Trie file.
95 |
96 | Returns:
97 | - pytricia.PyTricia: Loaded Trie structure.
98 | """
99 | with gzip.open(file_path, "rt") as f:
100 | data = json.load(f)
101 | trie = pytricia.PyTricia()
102 | for key, value in data.items():
103 | trie[key] = value
104 | return trie
105 |
106 | def find_asn_for_ips(ips, trie):
107 | """
108 | Find AS numbers for given IPs using the Trie structure.
109 |
110 | Args:
111 | - ips (list of str): List of IP addresses to search for.
112 | - trie (pytricia.PyTricia): Trie structure to use for lookup.
113 |
114 | Returns:
115 | - dict: Mapping of IP addresses to their corresponding AS numbers.
116 | """
117 | asn_map = {}
118 | for ip in ips:
119 | try:
120 | asn_map[ip] = trie.get(ip)
121 | except ValueError:
122 | pass
123 | return asn_map
124 |
125 | parser = argparse.ArgumentParser(description="Find ASN for given IPs.")
126 | parser.add_argument("-j", "--json", action='store_true', help='Output results in JSON format.')
127 | parser.add_argument("-rp", "--rib-path", default="processed_rib.txt", help="Path to the RIB file. Defaults to 'processed_rib.txt'.")
128 | args = parser.parse_args()
129 |
130 |
131 | # Main execution starts here
132 |
133 |
134 | if os.path.exists(TRIE_SAVE_PATH):
135 | trie = load_trie_from_file(TRIE_SAVE_PATH)
136 | else:
137 | logger.info("Serialized trie not found. Building trie from RIB dump...")
138 | logger.info("This may take some time..")
139 | trie = build_trie_from_rib(args.rib_path)
140 |
141 | ips_to_search = [line.strip() for line in sys.stdin]
142 | filtered_ips_to_search = [ip for ip in ips_to_search if not is_private_ip(ip)]
143 | asns = find_asn_for_ips(filtered_ips_to_search, trie)
144 |
145 | if args.json:
146 | asn_to_ips = defaultdict(list)
147 | for ip, asn in asns.items():
148 | if asn:
149 | asn_to_ips[f"AS{asn}"].append(ip)
150 | else:
151 | asn_to_ips["NOT_FOUND"].append(ip)
152 | print(json.dumps(asn_to_ips, indent=4))
153 | else:
154 | for ip, asn in asns.items():
155 | if asn:
156 | print(f"{colored(ip, 'yellow')} {colored(f'[AS{asn}]', 'green')}")
157 | else:
158 | print(f"{colored(ip, 'yellow')} {colored('NOT_FOUND', 'red')}")
159 |
--------------------------------------------------------------------------------
/rib-trie/trie_data.json.gz:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/devanshbatham/ip2asn/86dee9befa96eb46b24caae9e3e8f4e26f8863b2/rib-trie/trie_data.json.gz
--------------------------------------------------------------------------------
/setup.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | # Rename the ip2asn.py file to ip2asn
4 | mv ip2asn.py ip2asn
5 |
6 | # Move the ip2asn file to /usr/local/bin
7 | sudo mv ip2asn /usr/local/bin/
8 |
9 | # Make the ip2asn file executable
10 | sudo chmod +x /usr/local/bin/ip2asn
11 |
12 | echo ""
13 | echo "ip2asn has been installed successfully! Usage 'cat ips.txt | ip2asn'"
14 |
15 | mkdir $HOME/.ip2asn
16 | mv rib-trie/trie_data.json.gz $HOME/.ip2asn/trie_data.json.gz
--------------------------------------------------------------------------------
/static/background.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/devanshbatham/ip2asn/86dee9befa96eb46b24caae9e3e8f4e26f8863b2/static/background.png
--------------------------------------------------------------------------------
/static/ip2asn.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/devanshbatham/ip2asn/86dee9befa96eb46b24caae9e3e8f4e26f8863b2/static/ip2asn.png
--------------------------------------------------------------------------------