From 633a168ac81259a7c079f86e7622d6d9f774bd76 Mon Sep 17 00:00:00 2001 From: Quantum Date: Sun, 16 Jun 2024 01:13:52 -0400 Subject: [PATCH] Add basic AS200351 aut-num and routing policy --- .gitignore | 1 + AS200351.rpsl | 80 ++++++++++++++++++++++++ AS200351@AS-UPSTREAMS.rpsl | 37 +++++++++++ update-irr.py | 124 +++++++++++++++++++++++++++++++++++++ 4 files changed, 242 insertions(+) create mode 100644 .gitignore create mode 100644 AS200351.rpsl create mode 100644 AS200351@AS-UPSTREAMS.rpsl create mode 100755 update-irr.py diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..1c6915e --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +.api-key diff --git a/AS200351.rpsl b/AS200351.rpsl new file mode 100644 index 0000000..3871944 --- /dev/null +++ b/AS200351.rpsl @@ -0,0 +1,80 @@ +aut-num: AS200351 +as-name: DQN-AS-ANYCAST +descr: Dynamic Quantum Networks +descr: https://as200351.net +descr: https://quantum5.ca +remarks: +-------------------------------------------------------------+ +remarks: | ____ _ | +remarks: | / __ \__ ______ ____ _____ ___ (_)____ | +remarks: | / / / / / / / __ \/ __ `/ __ `__ \/ / ___/ | +remarks: | / /_/ / /_/ / / / / /_/ / / / / / / / /__ | +remarks: | /_____/\__, /_/ /_/\__,_/_/ /_/ /_/_/\___/ | +remarks: | /____/ ____ __ | +remarks: | / __ \__ ______ _____ / /___ ______ ___ | +remarks: | / / / / / / / __ `/ __ \/ __/ / / / __ `__ \ | +remarks: | / /_/ / /_/ / /_/ / / / / /_/ /_/ / / / / / / | +remarks: | \___\_\__,_/\__,_/_/ /_/\__/\__,_/_/ /_/ /_/ | +remarks: | _ __ __ __ | +remarks: | / | / /__ / /__ ______ _____/ /_______ | +remarks: | / |/ / _ \/ __/ | /| / / __ \/ ___/ //_/ ___/ | +remarks: | / /| / __/ /_ | |/ |/ / /_/ / / / ,< (__ ) | +remarks: | /_/ |_/\___/\__/ |__/|__/\____/_/ /_/|_/____/ | +remarks: | | +remarks: +-------------------------------------------------------------+ +remarks: +remarks: ===================== ROUTING INFORMATION ===================== +remarks: ====== upstreams ====== +import: from AS200351:as-upstreams accept ANY +mp-import: afi any.unicast from AS200351:as-upstreams accept ANY +export: to AS200351:as-upstreams announce AS200351:as-all +mp-export: afi any.unicast to AS200351:as-upstreams announce AS200351:as-all +remarks: +remarks: ====== IXP: ONIX ====== +remarks: * IPv4: 149.112.50.51 +remarks: * IPv6: 2001:504:125:e1::51 +import: from AS57369 accept AS57369:AS-ONIX +mp-import: afi any.unicast from AS57369 accept AS57369:AS-ONIX +export: to AS57369 announce AS200351:as-all +mp-export: afi any.unicast to AS57369 announce AS200351:as-all +remarks: +remarks: ===== IXP: FogIXP ===== +remarks: * IPv4: 185.1.147.210 +remarks: * IPv6: 2001:7f8:ca:1:0:20:0351:1 +import: from AS47498 accept AS-FOGIXP +mp-import: afi any.unicast from AS47498 accept AS-FOGIXP +export: to AS47498 announce AS200351:as-all +mp-export: afi any.unicast to AS47498 announce AS200351:as-all +remarks: +remarks: ===== IXP: AccurIX ==== +remarks: * IPv4: 149.112.74.8 +remarks: * IPv6: 2001:504:132::8 +import: from AS57194 accept AS57194:as-accurix-members +mp-import: afi any.unicast from AS57194 accept AS57194:as-accurix-members +export: to AS57194 announce AS200351:as-all +mp-export: afi any.unicast to AS57194 announce AS200351:as-all +remarks: +remarks: ===== IXP: FrysIX ===== +remarks: * IPv4: 185.1.203.229 +remarks: * IPv6: 2001:7f8:10f::3:e9f:229 +import: from AS56393 accept AS-FRYS-IX-CONNECTED +mp-import: afi any.unicast from AS56393 accept AS-FRYS-IX-CONNECTED +export: to AS56393 announce AS200351:as-all +mp-export: afi any.unicast to AS56393 announce AS200351:as-all +remarks: +remarks: ====== IXP: KCIX ====== +remarks: * IPv4: 206.51.7.216 +remarks: * IPv6: 2001:504:1b:1::216 +remarks: +remarks: ===== IXP: STLIX ====== +remarks: * IPv4: 206.83.12.70 +remarks: * IPv6: 2001:504:98::70 +remarks: +remarks: ===== IXP: HOUIX ====== +remarks: * IPv4: 206.83.136.41 +remarks: * IPv6: 2001:504:9E::41 +remarks: +remarks: ==================== OWNERSHIP INFORMATION ==================== +admin-c: DQNA-ARIN +tech-c: DQNOC-ARIN +mnt-by: MNT-GC-1348 +source: ARIN diff --git a/AS200351@AS-UPSTREAMS.rpsl b/AS200351@AS-UPSTREAMS.rpsl new file mode 100644 index 0000000..09fad80 --- /dev/null +++ b/AS200351@AS-UPSTREAMS.rpsl @@ -0,0 +1,37 @@ +as-set: AS200351:AS-UPSTREAMS +descr: AS200351's Upstreams +remarks: ==== Frantech Solutions ==== +members: AS53667 +remarks: ===== Constant / Vultr ===== +members: AS20473 +remarks: ===== Cloudie Networks ===== +members: AS924 +remarks: === Accuris Technologies === +members: AS52210 +remarks: ==== Hurricane Electric ==== +members: AS6939 +remarks: ======== iFog GmbH ========= +members: AS34927, AS209533 +remarks: ======= FreeTransit ======== +members: AS41051 +remarks: === F4 Networks / Rozint === +members: AS21738, AS25759 +remarks: ========== Suble =========== +members: AS199545 +remarks: ======== Tschajera ========= +members: AS209022 +remarks: ========= HyeHost ========== +members: AS47272 +remarks: ========= Diederik ========= +members: AS50917 +remarks: ==== Snaju / DartNode ====== +members: AS399646 +remarks: ===== Jon Arve Vanvik ====== +members: AS210475 +remarks: ========= FlowVPS ========== +members: AS37988 +remarks: ============================ +admin-c: DQNA-ARIN +tech-c: DQNOC-ARIN +mnt-by: MNT-GC-1348 +source: ARIN diff --git a/update-irr.py b/update-irr.py new file mode 100755 index 0000000..963d19e --- /dev/null +++ b/update-irr.py @@ -0,0 +1,124 @@ +#!/usr/bin/env python3 + +import argparse +import getpass +import os +import re +import sys +from abc import ABC, abstractmethod +from pathlib import Path + +import requests + +REGISTRY = 'https://reg.arin.net/rest/irr' + + +class BaseResource(ABC): + type: str + + def __init__(self, name, rpsl): + self.name = name + self.rpsl = rpsl + + @abstractmethod + def validate(self): ... + + @abstractmethod + def get_create_url(self): ... + + @abstractmethod + def get_update_url(self): ... + + +class AsSetResource(BaseResource): + def __init__(self, name, rpsl): + self.type = 'as-set' + super().__init__(name, rpsl) + + def validate(self): + if not re.match(f'^as-set:\s+{re.escape(self.name)}$', self.rpsl, re.M): + raise ValueError(f'Expected aut-num: {self.name} in RPSL') + + def get_create_url(self): + return f'{REGISTRY}/as-set' + + def get_update_url(self): + return f'{REGISTRY}/as-set/{self.name}' + + +class AutNumResource(BaseResource): + def __init__(self, name, rpsl): + self.type = 'aut-num' + super().__init__(name, rpsl) + + def validate(self): + if not re.match(f'^aut-num:\s+{re.escape(self.name)}$', self.rpsl, re.M): + raise ValueError(f'Expected aut-num: {self.name} in RPSL') + + def get_create_url(self): + return f'{REGISTRY}/aut-num/{self.name}' + + def get_update_url(self): + return f'{REGISTRY}/aut-num/{self.name}' + + +def get_resource(path, rpsl): + if (m := re.match('^(AS\d+)\.rpsl$', path)): + return AutNumResource(m.group(1), rpsl) + elif (m := re.match('^((?:AS\d+@)?(AS-[A-Z0-9-]+@)*AS-[A-Z0-9-]+)\.rpsl$', path)): + return AsSetResource(m.group(1).replace('@', ':'), rpsl) + + +def get_api_key(): + key_file = Path(__file__).parent / '.api-key' + if key_file.exists(): + with open(key_file) as f: + return f.read().strip() + + return getpass.getpass('ARIN api key: ') + + +def main(): + parser = argparse.ArgumentParser( + prog=sys.argv[0], + description='Updates the ARIN IRR database with RPSL files' + ) + parser.add_argument('filename', type=argparse.FileType('r')) + parser.add_argument('-c', '--create', action='store_true') + parser.add_argument( + '-o', '--org', + help='Organization handle (e.g. when creating an as-set)' + ) + + args = parser.parse_args() + + with args.filename as f: + rpsl = f.read() + + resource = get_resource(args.filename.name, rpsl) + if not resource: + print(f"Can't identify resource type from {args.filename.name}") + raise SystemExit(1) + + resource.validate() + + params = {'apikey': get_api_key()} + if args.org: + params['orgHandle'] = args.org + + if args.create: + verb = 'POST' + url = resource.get_create_url() + else: + verb = 'PUT' + url = resource.get_update_url() + + r = requests.request(verb, url=url, params=params, data=resource.rpsl, headers={ + 'Accept': 'application/rpsl', + 'Content-Type': 'application/rpsl', + }) + + print(r.content.decode('utf-8', 'replace')) + +if __name__ == '__main__': + main()