Compare commits
10 commits
39c6843a5c
...
d0d1667991
| Author | SHA1 | Date | |
|---|---|---|---|
| d0d1667991 | |||
| ac6c03c715 | |||
| bde0beae11 | |||
| d709aabe50 | |||
| 0491307f8d | |||
| 6f023271ba | |||
| fdba19899f | |||
| 7f915408d2 | |||
| 4cc2fbe6c4 | |||
| ab6ac75391 |
159 changed files with 25991 additions and 2932 deletions
|
|
@ -1,22 +0,0 @@
|
|||
# Configuration for router (10.254.254.110)
|
||||
# Exported on 2025-10-04 11:22:56
|
||||
|
||||
# IP Addresses
|
||||
/ip address add address=10.254.254.110/32 interface=loopback
|
||||
/ip address add address=10.250.1.33/29 interface=ether7-380
|
||||
/ip address add address=10.10.63.254/20 interface=mgmt
|
||||
/ip address add address=100.64.35.254/22 interface=982
|
||||
/ip address add address=204.110.188.126/27 interface=982
|
||||
|
||||
# IP Pools
|
||||
/ip pool add name=cgnat ranges=100.64.32.1-100.64.35.199
|
||||
/ip pool add name=mgmt ranges=10.10.48.1-10.10.62.254
|
||||
/ip pool add name=public ranges=204.110.189.1-204.110.189.29
|
||||
|
||||
# PPPoE Servers
|
||||
/interface pppoe-server server add name=982 interface=982 default-profile=982
|
||||
|
||||
# PPP Profiles
|
||||
/ppp profile add name=982 local-address=100.64.35.253 remote-address=cgnat
|
||||
|
||||
# NAT Rules
|
||||
304
CLAUDE.md
304
CLAUDE.md
|
|
@ -4,163 +4,75 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co
|
|||
|
||||
## Project Overview
|
||||
|
||||
This is a network documentation and discovery project that integrates with NetBox.
|
||||
A small fleet of CLIs for documenting, auditing, and operating the vntx WISP — a multi-tower MikroTik backbone with cnMaestro and UISP managing the radios/CPE behind it. Source of truth for network documentation is NetBox at `https://netbox.vntx.net/` (API token in env var `NETBOX_KEY`); source of truth for the routers is whatever the live `.rsc` exports say.
|
||||
|
||||
## NetBox Integration
|
||||
## Repo Layout
|
||||
|
||||
- **NetBox URL**: https://netbox.vntx.net/
|
||||
- **API Key**: e50298f7fd20f7fd6f1931f635511b34f6e8cfde
|
||||
- **Purpose**: Network documentation and discovery
|
||||
The repo is a collection of independent CLIs that each operate on the same physical fleet but talk to different control planes. Each subproject has its own README/CLAUDE.md with details; only repo-wide context lives here.
|
||||
|
||||
## Development Guidelines
|
||||
| Subdir | Lang | Talks to | Purpose |
|
||||
|--|--|--|--|
|
||||
| `mikrotik-tool/` | Go | MikroTik API-SSL + SSH | Fan-out to every router in `routers.yaml`: `list`, `export` (saves `<name>.rsc` per router), `inventory` (rewrites `inventory.yaml` from live data + SNMP), `api`/`schema` (ad-hoc API introspection on one router), `script` (push a `.rsc` file via SSH and stream output). Authoritative `inventory.yaml`, `subnets.yaml`, `radios.yaml`, `ipv6.md`, and `mpls.md` live here. |
|
||||
| `mikrotik-tool-rs/` | Rust | MikroTik API-SSL + system `ssh` | Newer rewrite of a subset of `mikrotik-tool` (`list`, `export`, `run <cmd…>`) with `-r name1,name2` filter to scope a fan-out command. Has its own `routers.yaml`. See `mikrotik-tool-rs/CLAUDE.md`. |
|
||||
| `uisp/` | Go (`main.go`) + Python (`uisp.py`) | UISP API at `uisp.vntx.net` | List/approve/upgrade UISP-managed devices. Auth via `UISP_KEY` env var. UNKNOWN-model devices are filtered by default (use `--include-unknown`). |
|
||||
| `cnmaestro/` | Python (uv) | cnMaestro Cloud OAuth2 | Cleanup offline devices and bulk-upgrade firmware. `uv sync` + `.env` with client id/secret. See `cnmaestro/README.md`. |
|
||||
|
||||
### NetBox API Usage
|
||||
- Use the provided API key for authentication with NetBox
|
||||
- The NetBox instance is located at https://netbox.vntx.net/
|
||||
- Follow NetBox API documentation for proper endpoint usage
|
||||
- The netbox api token is in env var NETBOX_KEY
|
||||
`routers.yaml` at the repo root is the config consumed by `mikrotik-tool/` (Go); `mikrotik-tool-rs/` has its own copy. Keep them aligned when adding/removing routers.
|
||||
|
||||
### Security Notes
|
||||
- Never commit API keys directly in code files
|
||||
- Use environment variables or configuration files for sensitive data
|
||||
- The API key provided should be stored securely
|
||||
`poll_snmp_site.sh` is a one-off helper for SNMP-polling a list of IPs at a site (`./poll_snmp_site.sh <site> <ip>...`). The `*.rsc` files at the repo root are stale exports — fresh ones come out of `mikrotik-tool` or `mikrotik-tool-rs`.
|
||||
|
||||
## Common Tasks
|
||||
## Common Commands
|
||||
|
||||
### NetBox API Connection
|
||||
When connecting to NetBox, use:
|
||||
- Base URL: `https://netbox.vntx.net/api/`
|
||||
- Authentication header: `Authorization: Token e50298f7fd20f7fd6f1931f635511b34f6e8cfde`
|
||||
|
||||
### MikroTik Router Connection
|
||||
Connect to MikroTik routers using API-SSL:
|
||||
- **Protocol**: API-SSL (port 8729)
|
||||
- **Username**: `grahamro` (read-only)
|
||||
- **Password**: `cFKhz8q5gPLoucMbcT1Iy58r3IXgc3`
|
||||
- **Example - Verona Router**: `10.254.254.101`
|
||||
|
||||
Use the `mikrotik_connect.py` script to connect and retrieve router information:
|
||||
```bash
|
||||
python3 mikrotik_connect.py
|
||||
# mikrotik-tool (Go)
|
||||
cd mikrotik-tool && go build .
|
||||
./mikrotik-tool list
|
||||
./mikrotik-tool export # writes <name>.rsc per router into cwd
|
||||
./mikrotik-tool api verona /ip/route/print
|
||||
./mikrotik-tool schema verona /routing/bgp/connection
|
||||
./mikrotik-tool script 494 494-v6-apply.rsc
|
||||
./mikrotik-tool inventory # re-renders inventory.yaml in place
|
||||
go test ./... # inventory_test.go
|
||||
|
||||
# mikrotik-tool-rs (Rust)
|
||||
cd mikrotik-tool-rs && cargo build --release
|
||||
cargo run -- list
|
||||
cargo run -- -r verona,982 export # filter to a subset before the verb
|
||||
cargo run -- run /system identity print
|
||||
cargo test # full suite
|
||||
cargo test encode_length # single test by name substring
|
||||
cargo clippy --all-targets && cargo fmt
|
||||
|
||||
# uisp (Go binary, also a Python script)
|
||||
cd uisp && go build .
|
||||
./uisp list
|
||||
./uisp approve <device>
|
||||
./uisp upgrade
|
||||
# python equivalent:
|
||||
python3 uisp.py list
|
||||
|
||||
# cnmaestro (Python, uv)
|
||||
cd cnmaestro && uv sync
|
||||
uv run cnmaestro cleanup --dry-run
|
||||
uv run cnmaestro upgrade --product ePMP,PMP --dry-run
|
||||
uv run pytest
|
||||
uv run pytest tests/test_devices.py::test_specific_thing # single test
|
||||
```
|
||||
|
||||
The script handles SSL connection, authentication, and can retrieve:
|
||||
- IP addresses and subnets
|
||||
- Interface configurations
|
||||
- Routing tables
|
||||
- PPPoE connections
|
||||
`UISP_KEY` and `NETBOX_KEY` need to be exported in the shell. `cnmaestro` reads `cnmaestro/.env`.
|
||||
|
||||
## Router Access Credentials
|
||||
## NetBox
|
||||
|
||||
### MikroTik Routers
|
||||
- Read-only access via API-SSL: username `grahamro`, password `cFKhz8q5gPLoucMbcT1Iy58r3IXgc3`
|
||||
- URL: `https://netbox.vntx.net/`, API base `https://netbox.vntx.net/api/`
|
||||
- Auth: `Authorization: Token $NETBOX_KEY`
|
||||
- Convention when creating sites/devices: site slugs are lowercase-hyphenated, status `active`; routers are Manufacturer=MikroTik / Role=Router / primary IP = loopback `/32` (`10.254.254.x/32`).
|
||||
- Prefix roles in use: Infrastructure, Customer, Management, Loopback.
|
||||
|
||||
### Verona Routers
|
||||
- Verona router is 10.254.254.101
|
||||
## MikroTik Router Access
|
||||
|
||||
### Additional Router IP Addresses
|
||||
- Climax router: 10.254.254.102
|
||||
- Culleoka router: 10.254.254.104
|
||||
Read-only fan-out (used by all the above tools): API-SSL on port 8729, user `grahamro` / password in `CLAUDE.md` history. The committed `routers.yaml` actually uses `graham` (not `grahamro`) because `/export` requires the `ftp` policy in addition to `read,api`, which the read-only group lacks. New routers: ensure the user's group has `read,api,ftp` (or extend the read group: `/user group set read add-policy=ftp`).
|
||||
|
||||
## NetBox Site and Device Creation Process
|
||||
|
||||
### Creating a new site and router in NetBox:
|
||||
1. **Create Site**: Use `create_verona_site_and_router.py` as template
|
||||
- Site name and slug (lowercase, hyphenated)
|
||||
- Status: 'active'
|
||||
- Comments describing the site
|
||||
|
||||
2. **Create Device**:
|
||||
- Manufacturer: MikroTik
|
||||
- Device Type: RouterBOARD
|
||||
- Device Role: Router
|
||||
- Primary IP: Router's loopback IP (e.g., 10.254.254.101/32)
|
||||
|
||||
3. **Add Network Data**: Use `update_netbox_verona.py` as template
|
||||
- Creates prefixes with proper roles (Infrastructure, Customer, Management, Loopback)
|
||||
- Creates interfaces on the device
|
||||
- Associates IP addresses with interfaces
|
||||
|
||||
### API Authentication
|
||||
- Always use environment variable `NETBOX_KEY` for API token
|
||||
- Fallback to hardcoded token only if env var not set
|
||||
|
||||
## Generic Scripts for Network Management
|
||||
|
||||
### 1. Create Site Only in NetBox
|
||||
```bash
|
||||
# Basic usage
|
||||
python3 create_site_only.py <site_name>
|
||||
|
||||
# With options
|
||||
python3 create_site_only.py 380 --comments "Central site with multiple routers" --address "380 Main St"
|
||||
```
|
||||
|
||||
### 2. Create Site and Router in NetBox
|
||||
```bash
|
||||
# Basic usage
|
||||
python3 create_site_and_router.py <site_name> <router_ip>
|
||||
|
||||
# With options
|
||||
python3 create_site_and_router.py Climax 10.254.254.102 --router-name climax-core --physical-address "123 Tower Rd"
|
||||
|
||||
# For sites with multiple routers, create site first, then add each router
|
||||
python3 create_site_only.py 380 --comments "Multi-router site"
|
||||
python3 create_site_and_router.py 380 10.254.254.105 --router-name 380-core-router
|
||||
python3 create_site_and_router.py 380 10.254.254.106 --router-name 380-edge-router
|
||||
```
|
||||
|
||||
### 3. Get MikroTik Router Configuration
|
||||
```bash
|
||||
# Basic usage (uses default read-only credentials)
|
||||
python3 get_mikrotik_router_data.py <router_ip>
|
||||
|
||||
# Save to specific file
|
||||
python3 get_mikrotik_router_data.py 10.254.254.102 -o climax_config.json
|
||||
|
||||
# Output JSON to stdout
|
||||
python3 get_mikrotik_router_data.py 10.254.254.102 --json
|
||||
|
||||
# Custom credentials
|
||||
python3 get_mikrotik_router_data.py 10.254.254.102 -u admin -p secretpass
|
||||
```
|
||||
|
||||
The script retrieves:
|
||||
- Router identity
|
||||
- IP addresses and subnets
|
||||
- Active interfaces
|
||||
- VLANs
|
||||
- PPPoE servers
|
||||
- Static routes
|
||||
|
||||
### 4. Get MikroTik Router Data (Basic/Older RouterOS)
|
||||
For older RouterOS versions or routers with many IPs (like CGNAT):
|
||||
```bash
|
||||
python3 get_mikrotik_basic_data.py <router_ip> -o router_data.json
|
||||
```
|
||||
|
||||
This simplified script:
|
||||
- Groups CGNAT addresses into a single subnet entry
|
||||
- Focuses on key interfaces only
|
||||
- Works better with older RouterOS versions
|
||||
- Handles large configurations without timing out
|
||||
|
||||
### 5. Get All Network Devices
|
||||
```bash
|
||||
# Get all devices categorized by type
|
||||
python3 get_all_network_devices.py <router_ip>
|
||||
|
||||
# Show all devices including "Other" category
|
||||
python3 get_all_network_devices.py <router_ip> --show-all
|
||||
|
||||
# Save to JSON file
|
||||
python3 get_all_network_devices.py <router_ip> -o devices.json
|
||||
```
|
||||
|
||||
This script retrieves DHCP leases and ARP table to identify:
|
||||
- Ubiquiti access points and devices
|
||||
- MikroTik devices
|
||||
- Other network devices
|
||||
Every router's loopback is `10.254.254.x/32`; see the **Fleet Topology** table below for the full mapping.
|
||||
|
||||
## Network Topology Patterns
|
||||
|
||||
|
|
@ -226,7 +138,7 @@ above the `fasttrack-connection` rule on both endpoints. Documented in
|
|||
```
|
||||
verona ──AF11── climax ──AF24── core ──AF11── culleoka
|
||||
│ │ │
|
||||
│ AF11 │ AF11 │ AF11 (DOWN: power injector unplugged)
|
||||
│ 5GHz │ AF11 │ AF11 (DOWN: power injector unplugged)
|
||||
│ │ │
|
||||
494 newhope ──AF24── lowry
|
||||
│
|
||||
|
|
@ -234,7 +146,7 @@ verona ──AF11── climax ──AF24── core ──AF11── culleoka
|
|||
│
|
||||
982
|
||||
```
|
||||
Wait — that diagram's links are: climax↔494 (AF11), core↔newhope (AF11),
|
||||
Wait — that diagram's links are: climax↔494 (5 GHz airMAX, **not** AF11 — see below), core↔newhope (AF11),
|
||||
core↔982 (60 GHz), newhope↔lowry (AF24). The climax↔culleoka direct AF11
|
||||
is currently down at the radio (physical issue), so culleoka traffic
|
||||
transits via core.
|
||||
|
|
@ -267,7 +179,7 @@ fasttrack-bypass rules in both directions on both routers.
|
|||
|--|--|--|--|--|--|
|
||||
| verona↔climax | AF11 | verona `ether3-climax-11ghz` | climax `ether6-verona-11ghz` | 10.250.1.24/29 | 2024 |
|
||||
| climax↔core | AF24 | climax `ether4-380-airfiber24` | core `ether5-climax` | 10.250.1.88/29 | 2024 |
|
||||
| climax↔494 | AF11 | climax `ether5-494` | 494 `ether2-climax` | 10.250.1.64/29 | 1580 |
|
||||
| climax↔494 | 5 GHz airMAX | climax `ether5-494` | 494 `ether2-climax` | 10.250.1.64/29 | 1580 |
|
||||
| climax↔culleoka | AF11 | climax `ether3-culleoka-11ghz` | culleoka `ether1-climax-11ghz` | 10.250.1.8/29 | 2024 (link DOWN) |
|
||||
| core↔culleoka | AF11 | core `ether6-culleoka-11ghz` | culleoka `ether6-380-11ghz` | 10.250.1.48/29 | 2024 |
|
||||
| core↔newhope | AF11 | core `ether4-newhope` | newhope `ether2-380` | 10.250.1.56/29 | 9000 |
|
||||
|
|
@ -282,12 +194,32 @@ sides symmetrically when changing l2mtu** — single-side raises usually work
|
|||
because Ethernet receivers accept anything ≤ their cap, but symmetric is
|
||||
the rule.
|
||||
|
||||
### climax↔494 backhaul is 5 GHz airMAX, not AF11
|
||||
Old docs called this hop AF11; it is actually a Ubiquiti airMAX AC PtP
|
||||
pair (SSID `vntx_pr_494`, WPA2, 40 MHz wide): AP **"Climax to 494"**
|
||||
(PBE-5AC-500) at `10.250.1.69` on the climax side, station **"494 to
|
||||
Climax"** (PBE-5AC-400) at `10.250.1.66` on the 494 side. Both answer
|
||||
SNMP v1 (`kdyyJrT0Mm`, airMAX MIB `1.3.6.1.4.1.41112.1.4`); added to
|
||||
`radios.yaml` 2026-07-17. The station's mgmt IP (and the whole 494
|
||||
tower) is only reachable **across this RF link** — when changing the
|
||||
channel, first make sure the station's frequency scan list covers the
|
||||
target, then change the AP (climax side), which stays reachable for
|
||||
rollback either way.
|
||||
|
||||
2026-07-17 flap incident: link ran on **5260 MHz (DFS)**; associations
|
||||
dropped for 40–90 s every 45–130 min (OSPF 40 s dead-interval timeouts
|
||||
on both routers; ethernet ports never dropped, radios never rebooted —
|
||||
association-uptime SNMP counters matched each OSPF flap). Local 5 GHz
|
||||
survey: climax APs on 5335/5545/5575 (airMAX) + 5750/5775 (ePMP), 494
|
||||
ePMP omni on 5800 → U-NII-1 (5170–5250) was empty at both towers and
|
||||
is non-DFS, so the fix was moving the PtP there.
|
||||
|
||||
### IGP / routing
|
||||
- OSPFv2 area `backbone-v2` (id 0.0.0.0) on all spine links, SHA-512 auth
|
||||
with `auth-id=1` and a shared key. PTP type. **BFD is off on every
|
||||
wireless backbone interface (AF11/AF24/60GHz)** — the global timers
|
||||
(`/routing/bfd/configuration` = 200ms×5 = 1s detection) are too
|
||||
aggressive for AF RF: a single >1s burst tore down OSPF on
|
||||
aggressive for backhaul RF: a single >1s burst tore down OSPF on
|
||||
climax↔494 every ~15–40 min until `use-bfd=false` was applied to both
|
||||
ends 2026-05-09. Wired fiber links may keep BFD if desired. If
|
||||
sub-second failover on a wireless link is genuinely needed, also
|
||||
|
|
@ -329,11 +261,91 @@ without it, hotspot drops all `204.110.191.x` traffic in `hs-unauth-to`
|
|||
chain with `icmp-host-prohibited`. Symptom is "I can reach verona but
|
||||
nothing past it" from the home network.
|
||||
|
||||
### graham's home network — multi-WAN failover (recursive check-gateway)
|
||||
Home (RB5009, `10.0.19.254`, **not** in `routers.yaml` — use a temp
|
||||
config with the same default creds to reach it via mikrotik-tool) fails
|
||||
over between TMO (distance 1), VNTX/verona (distance 2), and Starlink
|
||||
(distance 3, disabled) using the standard ROS recursive pattern, applied
|
||||
2026-07-17 via `mikrotik-tool/home-recursive-failover.rsc`:
|
||||
- Probe pins: `4.2.2.1/32→192.168.12.1%ether6-tmobile`,
|
||||
`4.2.2.2/32→204.110.191.30%ether5-vntx-static`,
|
||||
`4.2.2.3/32→192.168.1.1%ether7-starlink`, all `scope=10`, **no
|
||||
check-gateway on the pins** — they stay active whenever the interface
|
||||
has link, gluing probes to their WAN regardless of route state.
|
||||
- Defaults: `gateway=4.2.2.x target-scope=11 check-gateway=ping` — the
|
||||
check pings the probe IP through the pin; ~20s to go inactive, first
|
||||
success to return. `tmo table default` (eweka policy routing) has the
|
||||
same check so it falls back to main when TMO upstream dies.
|
||||
|
||||
**Do not reintroduce netwatch enable/disable failover scripts.** The old
|
||||
design (netwatch 2s/1s single-packet → `/ip route disable`) deadlocked:
|
||||
`disabled=yes` persists across reboots, and the probe was only tied to
|
||||
the VNTX path while its pin was active — so the verona hotspot-binding
|
||||
loss (verona pingable, transit dead) left the route disabled forever,
|
||||
and probes leaked out other WANs giving false "up" (TMO netwatch read up
|
||||
with the TMO interface physically down). Same lesson as the fleet BFD
|
||||
incident: 1-2s detection on AF RF with >1s bursts causes false failover;
|
||||
10–20s check-gateway damping is intentional.
|
||||
|
||||
|
||||
### IPv6 plan
|
||||
Per-tower /44s + central server LAN at `2606:1c80::/64` on edge. Full
|
||||
allocation plan in `mikrotik-tool/ipv6.md`. NetBox has these as IPAM
|
||||
prefixes.
|
||||
|
||||
### IPv6 forward firewall — must accept the whole /32
|
||||
Every tower router's `/ipv6 firewall filter chain=forward` needs
|
||||
`action=accept src-address=2606:1c80::/32` before the `default deny`.
|
||||
A per-tower `/44` rule is **wrong** — customer traffic transits other
|
||||
routers to reach `edge`, so each transit hop blackholes other towers'
|
||||
`/44`s. Classic symptom: ICMPv6 ping works (there's an `accept
|
||||
protocol=icmpv6` rule) but TCP/HTTP times out at `connect` (forwarded
|
||||
TCP hits `default deny`). The original `ipv6.md` template used
|
||||
`in-interface-list=customer`, but that list was never created on any
|
||||
router so the rule matched nothing — fixed fleet-wide 2026-05-14, and
|
||||
the now-redundant per-tower `/44` and `internal-to-internal` forward
|
||||
rules were removed at the same time. `edge` has a permissive forward
|
||||
chain (no default-deny) so it didn't need the rule. Resolver/DNS
|
||||
roll-out (`2606:1c80::240`/`::250` in `/ip dns` + the `v6-dns` DHCPv6
|
||||
option) is also fleet-wide as of 2026-05-14.
|
||||
|
||||
### graham's home network — IPv6
|
||||
Home (`RB5009`, identity `graham`, `10.0.19.254`) takes a **`/56`** from
|
||||
verona via DHCPv6-PD on `ether5-vntx-static` (verona's `verona-wired-pd`
|
||||
dhcp-server on `vlan9_sfpplus1`, drawing from `verona-cust-pd-1`).
|
||||
Currently `2606:1c80:1001:e00::/56`. Key config on home:
|
||||
- `/ipv6 dhcp-client` on `ether5-vntx-static`: `request=prefix`,
|
||||
`pool-name=home-pd-vntx`, **`pool-prefix-length=64`** (must be 64, not
|
||||
56, so the pool yields /64s — changing this only takes effect on a
|
||||
client disable/enable, not live), `default-route-tables=v6-verona`.
|
||||
- `/ipv6 address` with `from-pool=home-pd-vntx address=::1 advertise=yes`
|
||||
on `bridge` (→ `:e00::1/64`) and `ether3-servers` (→ `:e01::1/64`).
|
||||
Remaining 254 /64s of the /56 stay in the pool for internal use.
|
||||
- Egress policy: **two** `/routing rule`s, order matters —
|
||||
(1) `src-address=2606:1c80::/32 dst-address=2606:1c80:1001:e00::/56
|
||||
action=lookup-only-in-table table=main` so vntx-sourced traffic
|
||||
*destined to the home LANs* resolves via main's connected routes,
|
||||
then (2) `src-address=2606:1c80::/32 action=lookup-only-in-table
|
||||
table=v6-verona` for everything else. The verona /56 is single-homed,
|
||||
so traffic sourced from it must egress via verona only
|
||||
(T-Mobile/Starlink would BCP38-drop it). No v6 WAN failover for the
|
||||
internal /56, by design. The PD client's default lands in the
|
||||
`v6-verona` table, not `main`. **Without rule (1)** the broad rule
|
||||
also catches transit packets (echo replies, ICMPv6 time-exceeded)
|
||||
sourced from internal routers and destined back to the home LAN, and
|
||||
hairpins them to verona in a loop — symptom: home reaches the v6
|
||||
internet fine but cannot ping any internal router and internal hops
|
||||
show `*` in `traceroute6`. Rule (1)'s `dst-address` is tied to the
|
||||
current delegated /56 — update it if the PD lease ever changes.
|
||||
- Stateless DHCPv6 servers (`home-bridge-stateless`,
|
||||
`home-servers-stateless`) carry the `v6-dns` option (Cloudflare).
|
||||
- Stale `tunnerbroker` `/ipv6 pool` (HE.net `2001:470:ba50::/48`) is
|
||||
unused — leave or remove.
|
||||
- The `mikrotik-tool script` SSH path can't handle bare ROS verbs like
|
||||
`release`/`disable`/`enable` or `:delay` (flattenRSC only knows
|
||||
add/set/remove/print/get) — use `set [find] disabled=yes/no` instead,
|
||||
and sequence delays from the shell side.
|
||||
|
||||
## Claude Assistant Guidelines
|
||||
|
||||
- Any time Claude learns something new, automatically add it to CLAUDE.md
|
||||
|
|
|
|||
198
backups/494-to-climax_STA_10.250.1.66_system.cfg.2026-07-17
Normal file
198
backups/494-to-climax_STA_10.250.1.66_system.cfg.2026-07-17
Normal file
|
|
@ -0,0 +1,198 @@
|
|||
spawn ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -o LogLevel=ERROR -o ConnectTimeout=8 -o NumberOfPasswordPrompts=1 -o PreferredAuthentications=password -o HostKeyAlgorithms=+ssh-rsa -o KexAlgorithms=+diffie-hellman-group14-sha1 ubnt@10.250.1.66 cat /tmp/system.cfg
|
||||
aaa.1.radius.acct.1.port=1813
|
||||
aaa.1.radius.acct.1.status=disabled
|
||||
aaa.1.radius.auth.1.port=1812
|
||||
aaa.1.status=disabled
|
||||
aaa.1.wpa.psk=0000:0000
|
||||
aaa.status=disabled
|
||||
airview.status=enabled
|
||||
bridge.1.devname=br0
|
||||
bridge.1.port.1.devname=eth0
|
||||
bridge.1.port.1.status=enabled
|
||||
bridge.1.port.2.devname=ath0
|
||||
bridge.1.port.2.status=enabled
|
||||
bridge.1.status=enabled
|
||||
bridge.1.stp.status=disabled
|
||||
bridge.status=enabled
|
||||
discovery.beacon.status=enabled
|
||||
discovery.cdp.status=disabled
|
||||
discovery.lldp.status=enabled
|
||||
discovery.status=enabled
|
||||
ebtables.status=enabled
|
||||
ebtables.sys.eap.1.devname=ath0
|
||||
ebtables.sys.eap.1.status=enabled
|
||||
ebtables.sys.eap.status=enabled
|
||||
ebtables.sys.fw.status=disabled
|
||||
ebtables.sys.fw6.status=disabled
|
||||
ebtables.sys.status=enabled
|
||||
gui.language=en_US
|
||||
gui.network.advanced.status=disabled
|
||||
httpd.https.port=443
|
||||
httpd.https.status=enabled
|
||||
httpd.port=80
|
||||
httpd.session.timeout=900
|
||||
httpd.status=enabled
|
||||
igmpproxy.status=disabled
|
||||
iptables.status=disabled
|
||||
iptables.sys.portfw.status=disabled
|
||||
iptables.sys.status=enabled
|
||||
netconf.1.autoip.status=disabled
|
||||
netconf.1.devname=ath0
|
||||
netconf.1.mtu=1500
|
||||
netconf.1.status=enabled
|
||||
netconf.1.up=enabled
|
||||
netconf.2.autoip.status=disabled
|
||||
netconf.2.autoneg=enabled
|
||||
netconf.2.devname=eth0
|
||||
netconf.2.flowcontrol.rx.status=enabled
|
||||
netconf.2.flowcontrol.tx.status=enabled
|
||||
netconf.2.mtu=1500
|
||||
netconf.2.status=enabled
|
||||
netconf.2.up=enabled
|
||||
netconf.3.autoip.status=disabled
|
||||
netconf.3.devname=br0
|
||||
netconf.3.ip6.status=enabled
|
||||
netconf.3.ip=10.250.1.66
|
||||
netconf.3.mtu=1500
|
||||
netconf.3.netmask=255.255.255.248
|
||||
netconf.3.role=mlan
|
||||
netconf.3.status=enabled
|
||||
netconf.status=enabled
|
||||
netmode=bridge
|
||||
ntpclient.1.server=0.us.pool.ntp.org
|
||||
ntpclient.1.status=enabled
|
||||
ntpclient.status=enabled
|
||||
pppoe-relay.status=disabled
|
||||
pwdog.delay=300
|
||||
pwdog.period=300
|
||||
pwdog.retry=3
|
||||
pwdog.status=disabled
|
||||
radio.1.ack.auto=enabled
|
||||
radio.1.ackdistance=600
|
||||
radio.1.acktimeout=35
|
||||
radio.1.antenna.gain=25
|
||||
radio.1.antenna.id=1
|
||||
radio.1.atpc.sta.status=enabled
|
||||
radio.1.atpc.status=disabled
|
||||
radio.1.atpc.threshold=36
|
||||
radio.1.cable.loss=0
|
||||
radio.1.center.1.freq=5165
|
||||
radio.1.chanbw=0
|
||||
radio.1.cmsbias=0
|
||||
radio.1.countrycode=840
|
||||
radio.1.cwm.enable=0
|
||||
radio.1.cwm.mode=1
|
||||
radio.1.devname=ath0
|
||||
radio.1.dfs.slave=1
|
||||
radio.1.dfs.status=enabled
|
||||
radio.1.ff_cap_rep=0
|
||||
radio.1.freq=5165
|
||||
radio.1.ieee_mode=auto
|
||||
radio.1.low_txpower_mode=disabled
|
||||
radio.1.mode=managed
|
||||
radio.1.obey=enabled
|
||||
radio.1.polling=enabled
|
||||
radio.1.polling_ff_dl_ratio=50
|
||||
radio.1.polling_ff_dur=0
|
||||
radio.1.polling_ff_flex=0
|
||||
radio.1.polling_ff_sta_rx_rssi_th=0
|
||||
radio.1.pollingnoack=0
|
||||
radio.1.pollingpri=2
|
||||
radio.1.ptpmode=1
|
||||
radio.1.rc_mode=0
|
||||
radio.1.reg_obey=enabled
|
||||
radio.1.rx_sensitivity=-96
|
||||
radio.1.scan_list.status=disabled
|
||||
radio.1.scanbw.status=disabled
|
||||
radio.1.status=enabled
|
||||
radio.1.txpower=20
|
||||
radio.countrycode=840
|
||||
radio.status=enabled
|
||||
resolv.host.1.name=494 to Climax
|
||||
resolv.host.1.status=enabled
|
||||
resolv.nameserver.1.ip=204.110.191.240
|
||||
resolv.nameserver.1.status=enabled
|
||||
resolv.nameserver.2.ip=204.110.191.250
|
||||
resolv.nameserver.2.status=enabled
|
||||
resolv.nameserver.status=enabled
|
||||
route.1.devname=br0
|
||||
route.1.gateway=10.250.1.70
|
||||
route.1.ip=0.0.0.0
|
||||
route.1.netmask=0
|
||||
route.1.status=enabled
|
||||
route.status=enabled
|
||||
snmp.community=kdyyJrT0Mm
|
||||
snmp.contact=vntx
|
||||
snmp.location=vntx
|
||||
snmp.status=enabled
|
||||
sshd.auth.passwd=enabled
|
||||
sshd.port=22
|
||||
sshd.status=enabled
|
||||
syslog.remote.port=514
|
||||
syslog.remote.status=disabled
|
||||
syslog.status=enabled
|
||||
system.airosx.prov.status=enabled
|
||||
system.autosend_crash_reports=disabled
|
||||
system.cfg.editor.webui=2026-06-27T20:28:48.251Z
|
||||
system.cfg.version=65547
|
||||
system.date.status=disabled
|
||||
system.external.reset=enabled
|
||||
system.height=0
|
||||
system.latitude=33.1801
|
||||
system.leds.nightmode=disabled
|
||||
system.longitude=-96.498
|
||||
system.poe_passthrough=disabled
|
||||
system.timezone.name=America/Chicago
|
||||
system.timezone=CST6CDT,M3.2.0,M11.1.0
|
||||
telnetd.port=23
|
||||
telnetd.status=disabled
|
||||
tshaper.status=disabled
|
||||
unms.status=enabled
|
||||
unms.ui_url=https://uisp.vntx.net
|
||||
unms.uri=wss://uisp.vntx.net:443+NHjWcWdwuHlhAfrBqATpt6_Tz3-Sv-xYka64WI4hBGcAAAAA+allowUntrustedCertificate
|
||||
update.check.status=enabled
|
||||
users.1.name=ubnt
|
||||
users.1.password=$1$.vDPgZgX$bg0TprBhNiUYo4uDoMwvi1
|
||||
users.1.status=enabled
|
||||
users.2.gid=100
|
||||
users.2.shell=/bin/false
|
||||
users.2.status=disabled
|
||||
users.2.uid=100
|
||||
users.status=enabled
|
||||
wireless.1.ampdu.frames=32
|
||||
wireless.1.ampdu.status=enabled
|
||||
wireless.1.amsdu=3
|
||||
wireless.1.devname=ath0
|
||||
wireless.1.hide_ssid=disabled
|
||||
wireless.1.l2_isolation=disabled
|
||||
wireless.1.mac_acl.policy=allow
|
||||
wireless.1.mac_acl.status=disabled
|
||||
wireless.1.mcast.enhance=0
|
||||
wireless.1.rate.auto=enabled
|
||||
wireless.1.rate.mcs=-1
|
||||
wireless.1.security.type=none
|
||||
wireless.1.signal_led1=94
|
||||
wireless.1.signal_led2=80
|
||||
wireless.1.signal_led3=73
|
||||
wireless.1.signal_led4=65
|
||||
wireless.1.signal_led_status=enabled
|
||||
wireless.1.ssid=vntx_pr_494
|
||||
wireless.1.status=enabled
|
||||
wireless.1.wds.status=enabled
|
||||
wireless.status=enabled
|
||||
wpasupplicant.device.1.devname=ath0
|
||||
wpasupplicant.device.1.profile=AUTO
|
||||
wpasupplicant.device.1.status=enabled
|
||||
wpasupplicant.profile.1.name=AUTO
|
||||
wpasupplicant.profile.1.network.1.eap.1.status=disabled
|
||||
wpasupplicant.profile.1.network.1.key_mgmt.1.name=WPA-PSK
|
||||
wpasupplicant.profile.1.network.1.pairwise.1.name=CCMP
|
||||
wpasupplicant.profile.1.network.1.priority=100
|
||||
wpasupplicant.profile.1.network.1.proto.1.name=RSN
|
||||
wpasupplicant.profile.1.network.1.psk=0000:0000
|
||||
wpasupplicant.profile.1.network.1.ssid=vntx_pr_494
|
||||
wpasupplicant.profile.1.network.1.status=enabled
|
||||
wpasupplicant.profile.1.network.2.key_mgmt.1.name=NONE
|
||||
wpasupplicant.profile.1.network.2.priority=2
|
||||
wpasupplicant.profile.1.network.2.status=disabled
|
||||
wpasupplicant.status=enabled
|
||||
204
backups/climax-to-494_AP_10.250.1.69_system.cfg.2026-07-17
Normal file
204
backups/climax-to-494_AP_10.250.1.69_system.cfg.2026-07-17
Normal file
|
|
@ -0,0 +1,204 @@
|
|||
spawn ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -o LogLevel=ERROR -o ConnectTimeout=8 -o NumberOfPasswordPrompts=1 -o PreferredAuthentications=password -o HostKeyAlgorithms=+ssh-rsa -o KexAlgorithms=+diffie-hellman-group14-sha1 ubnt@10.250.1.69 cat /tmp/system.cfg
|
||||
aaa.status=enabled
|
||||
aaa.1.devname=ath0
|
||||
aaa.1.radius.acct.1.port=1813
|
||||
aaa.1.radius.acct.1.status=disabled
|
||||
aaa.1.radius.auth.1.port=1812
|
||||
aaa.1.radius.auth.1.status=disabled
|
||||
aaa.1.ssid=vntx_pr_494
|
||||
aaa.1.status=enabled
|
||||
aaa.1.wpa.key.1.mgmt=WPA-PSK
|
||||
aaa.1.wpa.mode=2
|
||||
aaa.1.wpa.psk=0000:0000
|
||||
aaa.1.wpa.1.pairwise=CCMP
|
||||
airview.status=enabled
|
||||
bridge.status=enabled
|
||||
bridge.1.devname=br0
|
||||
bridge.1.port.1.devname=eth0
|
||||
bridge.1.port.1.status=enabled
|
||||
bridge.1.port.2.devname=ath0
|
||||
bridge.1.port.2.status=enabled
|
||||
bridge.1.status=enabled
|
||||
bridge.1.stp.status=disabled
|
||||
discovery.beacon.status=enabled
|
||||
discovery.cdp.status=enabled
|
||||
discovery.lldp.status=enabled
|
||||
discovery.status=enabled
|
||||
ebtables.status=enabled
|
||||
ebtables.sys.eap.status=enabled
|
||||
ebtables.sys.eap.1.devname=ath0
|
||||
ebtables.sys.eap.1.status=enabled
|
||||
ebtables.sys.fw.status=disabled
|
||||
ebtables.sys.fw6.status=disabled
|
||||
ebtables.sys.status=enabled
|
||||
gui.language=en_US
|
||||
gui.network.advanced.status=disabled
|
||||
httpd.https.port=443
|
||||
httpd.https.status=enabled
|
||||
httpd.https.tlsv1=enabled
|
||||
httpd.port=80
|
||||
httpd.session.timeout=900
|
||||
httpd.status=enabled
|
||||
igmpproxy.status=disabled
|
||||
iptables.status=disabled
|
||||
iptables.sys.portfw.status=disabled
|
||||
iptables.sys.status=enabled
|
||||
netconf.status=enabled
|
||||
netconf.1.autoip.status=disabled
|
||||
netconf.1.devname=ath0
|
||||
netconf.1.mtu=1500
|
||||
netconf.1.status=enabled
|
||||
netconf.1.up=enabled
|
||||
netconf.2.autoip.status=disabled
|
||||
netconf.2.autoneg=enabled
|
||||
netconf.2.devname=eth0
|
||||
netconf.2.flowcontrol.rx.status=enabled
|
||||
netconf.2.flowcontrol.tx.status=enabled
|
||||
netconf.2.mtu=1500
|
||||
netconf.2.status=enabled
|
||||
netconf.2.up=enabled
|
||||
netconf.3.autoip.status=disabled
|
||||
netconf.3.devname=br0
|
||||
netconf.3.ip=10.250.1.69
|
||||
netconf.3.ip6.status=enabled
|
||||
netconf.3.mtu=1500
|
||||
netconf.3.netmask=255.255.255.248
|
||||
netconf.3.role=mlan
|
||||
netconf.3.status=enabled
|
||||
netmode=bridge
|
||||
ntpclient.status=enabled
|
||||
ntpclient.1.server=0.us.pool.ntp.org
|
||||
ntpclient.1.status=enabled
|
||||
pppoe-relay.status=disabled
|
||||
pwdog.delay=300
|
||||
pwdog.period=300
|
||||
pwdog.retry=3
|
||||
pwdog.status=disabled
|
||||
radio.countrycode=840
|
||||
radio.status=enabled
|
||||
radio.1.ack.auto=enabled
|
||||
radio.1.ackdistance=600
|
||||
radio.1.acktimeout=35
|
||||
radio.1.antenna.gain=27
|
||||
radio.1.antenna.id=1
|
||||
radio.1.atpc.sta.status=enabled
|
||||
radio.1.atpc.status=disabled
|
||||
radio.1.atpc.threshold=36
|
||||
radio.1.cable.loss=0
|
||||
radio.1.center.1.freq=5270
|
||||
radio.1.chanbw=40
|
||||
radio.1.cmsbias=0
|
||||
radio.1.countrycode=840
|
||||
radio.1.cwm.enable=0
|
||||
radio.1.cwm.mode=1
|
||||
radio.1.devname=ath0
|
||||
radio.1.dfs.status=enabled
|
||||
radio.1.ff_cap_rep=0
|
||||
radio.1.freq=5260
|
||||
radio.1.ieee_mode=11acvht40
|
||||
radio.1.low_txpower_mode=disabled
|
||||
radio.1.mode=master
|
||||
radio.1.obey=enabled
|
||||
radio.1.polling=enabled
|
||||
radio.1.polling_ff_dl_ratio=50
|
||||
radio.1.polling_ff_dur=0
|
||||
radio.1.polling_ff_flex=0
|
||||
radio.1.polling_ff_sta_rx_rssi_th=0
|
||||
radio.1.polling_11ac_11n_compat=0
|
||||
radio.1.pollingnoack=0
|
||||
radio.1.pollingpri=2
|
||||
radio.1.ptpmode=1
|
||||
radio.1.rc_mode=0
|
||||
radio.1.reg_obey=disabled
|
||||
radio.1.rx_sensitivity=-96
|
||||
radio.1.scan_list.status=disabled
|
||||
radio.1.scanbw.status=disabled
|
||||
radio.1.status=enabled
|
||||
radio.1.txpower=24
|
||||
resolv.host.1.name=Climax to 494
|
||||
resolv.host.1.status=enabled
|
||||
resolv.nameserver.status=enabled
|
||||
resolv.nameserver.1.ip=204.110.191.240
|
||||
resolv.nameserver.1.status=enabled
|
||||
resolv.nameserver.2.ip=204.110.191.250
|
||||
resolv.nameserver.2.status=enabled
|
||||
route.status=enabled
|
||||
route.1.devname=br0
|
||||
route.1.gateway=10.250.1.70
|
||||
route.1.ip=0.0.0.0
|
||||
route.1.netmask=0
|
||||
route.1.status=enabled
|
||||
snmp.community=kdyyJrT0Mm
|
||||
snmp.contact=Verona Networks
|
||||
snmp.location=vntx
|
||||
snmp.status=enabled
|
||||
sshd.auth.passwd=enabled
|
||||
sshd.hostkey_dss=disabled
|
||||
sshd.port=22
|
||||
sshd.status=enabled
|
||||
syslog.remote.port=514
|
||||
syslog.remote.status=disabled
|
||||
syslog.status=enabled
|
||||
system.airosx.prov.status=enabled
|
||||
system.autosend_crash_reports=disabled
|
||||
system.cfg.editor.webui=2026-07-06T16:34:39.082Z
|
||||
system.cfg.version=65547
|
||||
system.date.status=disabled
|
||||
system.external.reset=enabled
|
||||
system.height=0
|
||||
system.latitude=33.1801
|
||||
system.leds.nightmode=disabled
|
||||
system.longitude=-96.498
|
||||
system.poe_passthrough=disabled
|
||||
system.timezone=CST6CDT,M3.2.0,M11.1.0
|
||||
system.timezone.name=America/Chicago
|
||||
telnetd.port=23
|
||||
telnetd.status=disabled
|
||||
tshaper.status=disabled
|
||||
unms.status=enabled
|
||||
unms.ui_url=https://uisp.vntx.net
|
||||
unms.uri=wss://uisp.vntx.net:443+xz9wxAnRIoFBZ30L4DUMQPPezD08hN-CXuge7VFMaIkAAAAA+allowUntrustedCertificate
|
||||
update.check.status=enabled
|
||||
users.status=enabled
|
||||
users.1.name=ubnt
|
||||
users.1.password=$1$CbZ01ZTh$yxjG1yPTbJ7wBlbif5hEC0
|
||||
users.1.status=enabled
|
||||
users.2.gid=100
|
||||
users.2.shell=/bin/false
|
||||
users.2.status=disabled
|
||||
users.2.uid=100
|
||||
wireless.status=enabled
|
||||
wireless.1.ampdu.frames=32
|
||||
wireless.1.ampdu.status=enabled
|
||||
wireless.1.amsdu=3
|
||||
wireless.1.devname=ath0
|
||||
wireless.1.hide_ssid=disabled
|
||||
wireless.1.l2_isolation=disabled
|
||||
wireless.1.mac_acl.policy=allow
|
||||
wireless.1.mac_acl.status=disabled
|
||||
wireless.1.mcast.enhance=0
|
||||
wireless.1.rate.auto=enabled
|
||||
wireless.1.rate.mcs=-1
|
||||
wireless.1.security.type=none
|
||||
wireless.1.signal_led_status=enabled
|
||||
wireless.1.signal_led1=94
|
||||
wireless.1.signal_led2=80
|
||||
wireless.1.signal_led3=73
|
||||
wireless.1.signal_led4=65
|
||||
wireless.1.ssid=vntx_pr_494
|
||||
wireless.1.status=enabled
|
||||
wireless.1.wds.status=enabled
|
||||
wpasupplicant.device.1.devname=ath0
|
||||
wpasupplicant.device.1.profile=AUTO
|
||||
wpasupplicant.device.1.status=disabled
|
||||
wpasupplicant.profile.1.name=AUTO
|
||||
wpasupplicant.profile.1.network.1.key_mgmt.1.name=WPA-PSK
|
||||
wpasupplicant.profile.1.network.1.pairwise.1.name=CCMP
|
||||
wpasupplicant.profile.1.network.1.proto.1.name=RSN
|
||||
wpasupplicant.profile.1.network.1.psk=0000:0000
|
||||
wpasupplicant.profile.1.network.1.ssid=vntx_pr_494
|
||||
wpasupplicant.profile.1.network.1.status=enabled
|
||||
wpasupplicant.profile.1.network.2.key_mgmt.1.name=NONE
|
||||
wpasupplicant.profile.1.network.2.priority=2
|
||||
wpasupplicant.profile.1.network.2.status=disabled
|
||||
wpasupplicant.status=disabled
|
||||
108
cpes.txt
Normal file
108
cpes.txt
Normal file
|
|
@ -0,0 +1,108 @@
|
|||
|
||||
## ePMP Force 110 (1 devices)
|
||||
00:04:56:F0:36:0F #125 Eddie Yarbrough 10.10.16.51
|
||||
|
||||
## ePMP Force 180 (36 devices)
|
||||
00:04:56:2E:B9:2E #185 Joanna Rodriguez 10.10.160.16
|
||||
00:04:56:2F:D7:CC #2098 Stephen Beegle 10.10.160.2
|
||||
00:04:56:2E:C9:FF #1829 Ashley Smith 10.10.160.6
|
||||
58:C1:7A:AD:14:9A #357 Brian Swedlund 10.10.160.7
|
||||
58:C1:7A:AC:65:08 #2200 Jennifer Boon 10.10.16.38
|
||||
00:04:56:44:5E:7B #343 Tim Bagert 10.10.16.40
|
||||
58:C1:7A:AD:22:EC #1650 luis Cabrera 10.10.64.1
|
||||
58:C1:7A:AC:64:51 #2454 Nancy Avellaneda 10.10.64.5
|
||||
58:C1:7A:AC:62:5F #2206 Wanda Davis 10.10.96.102
|
||||
00:04:56:2F:DB:A1 #355 Treceda Schmid 10.10.96.11
|
||||
58:C1:7A:AC:69:46 #2190 Ray Schaak 10.10.96.17
|
||||
00:04:56:2E:C1:6E #177 JERRY L JOHNSON 10.10.96.2
|
||||
00:04:56:2F:D4:BD #283 Paul Sammataro 10.10.96.29
|
||||
00:04:56:2F:CE:8D #2375 Yolanda Judge 10.10.96.35
|
||||
00:04:56:2F:DA:84 #1908 Gabriela Sereno 10.10.96.39
|
||||
58:C1:7A:AD:17:85 #2043 Angelica Cuayahuitl 10.10.96.45
|
||||
58:C1:7A:AC:63:19 #215 Kileigh Even 10.10.96.46
|
||||
58:C1:7A:AC:5F:11 #1538 Shirley Elmore 10.10.96.5
|
||||
00:04:56:2E:B7:6C #1316 Iris Delira 10.10.96.51
|
||||
00:04:56:44:54:0A #2046 HEATHER WOMACK 10.10.96.54
|
||||
00:04:56:44:50:AA #2023 Billy Horton 10.10.96.55
|
||||
58:C1:7A:B0:B6:1F #1128 Patricia Vasquez 10.10.96.58
|
||||
00:04:56:2F:DC:4C #10040 Thomas Graham 10.10.144.10
|
||||
58:C1:7A:AC:6A:C3 #10059 Nicholas Terry 10.10.144.11
|
||||
00:04:56:2F:CD:C7 #84 Coy Ungemach 10.10.144.2
|
||||
58:C1:7A:AD:15:7B #1906 Georgina Covarrubias 10.10.144.6
|
||||
58:C1:7A:AD:1F:20 #246 Matt Budjenska 10.10.158.247
|
||||
58:C1:7A:B0:B7:AE #1950 Leandra Beshea 10.10.128.1
|
||||
00:04:56:2F:D9:10 #1971 Clifford Jennings 10.10.128.11
|
||||
58:C1:7A:AD:13:D7 #100 Dave Kiowski 10.10.128.14
|
||||
00:04:56:44:5A:37 #997 Heather Brister 10.10.128.25
|
||||
00:04:56:44:52:ED #318 Shannon Brown 10.10.128.32
|
||||
00:04:56:44:50:68 #1949 RICHARD CONKLIN 10.10.128.34
|
||||
58:C1:7A:AC:5A:E5 #2489 Martha Flores 10.10.128.35
|
||||
58:C1:7A:AC:E8:81 #2492 Kory Biggs Shop 10.10.128.43
|
||||
00:04:56:2F:DA:E7 #2010 Brady Clayton 10.10.128.48
|
||||
|
||||
## ePMP Force 200 (59 devices)
|
||||
00:04:56:DC:69:AE #932 Matt Kosarek 10.10.160.19
|
||||
00:04:56:DB:D2:CA #1168 Stephen Day 10.10.160.22
|
||||
00:04:56:DB:EC:11 #2037 Kevin Arana 10.10.160.9
|
||||
00:04:56:DB:63:5E #2410 Carol Strickland 10.10.16.37
|
||||
00:04:56:4A:12:EE #2133 Glenda Beauchamp 10.10.16.41
|
||||
58:C1:7A:B1:05:12 #2275 John Vayo 10.10.16.47
|
||||
58:C1:7A:B0:D7:10 #2233 Crystal Harney 10.10.16.50
|
||||
00:04:56:DB:57:2B #855 Amber Prater 10.10.16.56
|
||||
58:C1:7A:B1:09:F8 #2360 Doug Garber 10.10.16.57
|
||||
00:04:56:DC:73:98 #2530 Donna Campbell 10.10.16.58
|
||||
58:C1:7A:B3:07:A1 #435 Maria Cortes 10.10.64.12
|
||||
00:04:56:DB:EB:18 #490 Terry Bates 10.10.64.8
|
||||
58:C1:7A:B1:02:63 #192 Aurea Sanchez 10.10.96.10
|
||||
00:04:56:DB:99:40 #2407 David Roesch 10.10.96.13
|
||||
00:04:56:48:63:EF #587 Tamy Thomas 10.10.96.14
|
||||
00:04:56:DB:C2:2C #497 Jody McCluskey 10.10.96.26
|
||||
00:04:56:DB:EA:A0 #2487 Anthony Longo 10.10.96.33
|
||||
00:04:56:4A:17:53 #10074 Pablo Martinez 10.10.96.36
|
||||
00:04:56:DB:E9:FE #2479 Thomas Hernandez 10.10.96.38
|
||||
00:04:56:DB:E5:ED #1788 Tim Bronschidle 10.10.96.40
|
||||
58:C1:7A:B0:F3:39 #2335 Tiffany Stewart 10.10.96.42
|
||||
00:04:56:DC:73:6B #10027 Irene Garibay 10.10.96.50
|
||||
00:04:56:DC:6B:55 #10052 Khushboo Agarwal 10.10.96.53
|
||||
58:C1:7A:B0:D7:0D #1601 Jerid Rutledge 10.10.96.65
|
||||
00:04:56:DC:6C:9F #293 Jeff Harder 10.10.96.67
|
||||
58:C1:7A:B3:06:A8 #875 Ken Patrick 10.10.96.7
|
||||
00:04:56:DC:67:59 #1467 Terri Mack 10.10.96.72
|
||||
00:04:56:DC:59:A6 #427 Ausencio Luna 10.10.96.73
|
||||
00:04:56:DB:C2:11 #433 Balnca Diaz 10.10.96.77
|
||||
00:04:56:DC:6D:77 #1712 Bobby Mack 10.10.96.84
|
||||
00:04:56:48:66:E3 #10024 Janis Stewart 10.10.96.9
|
||||
00:04:56:4A:15:B5 #10073 Jeffrey Harder 10.10.96.95
|
||||
00:04:56:4A:13:7E #1441 Doris Avalos 10.10.144.16
|
||||
58:C1:7A:B0:F7:8F #224 Leonard Lewis 10.10.144.20
|
||||
00:04:56:DC:79:38 #477 Helen Lumpkin 10.10.144.24
|
||||
00:04:56:DC:72:2A #735 FRED HECKEL 10.10.144.25
|
||||
00:04:56:DB:E8:D5 #990 Jennifer Dunaway 10.10.144.27
|
||||
00:04:56:DC:6A:65 #345 Tim Fisher 10.10.144.31
|
||||
00:04:56:DB:AA:E6 #815 Terry Gortney 10.10.144.32
|
||||
00:04:56:DC:5D:63 #243 Mark Nunnelee 10.10.144.35
|
||||
00:04:56:DC:60:8D #1081 Abbie Andrews 10.10.144.36
|
||||
00:04:56:DC:04:EF #301 Ron Berger 10.10.144.4
|
||||
00:04:56:DC:72:24 #339 Terry Miesen 10.10.144.7
|
||||
00:04:56:DB:97:F9 #848 Douglas Wilson 10.10.128.13
|
||||
00:04:56:4A:17:B3 #2027 Chris Clayton 10.10.128.18
|
||||
00:04:56:DC:69:F0 #2458 Marcus Patton 10.10.128.28
|
||||
00:04:56:DB:E7:4C #46 Bernard Heer 10.10.128.3
|
||||
00:04:56:DC:5E:50 #2465 Kory Biggs 10.10.128.33
|
||||
00:04:56:DC:06:B7 #568 Frances Kirby 10.10.128.37
|
||||
00:04:56:DC:05:04 #556 Mike Bell 10.10.128.45
|
||||
58:C1:7A:B0:D0:20 #997 Heather Brister 10.10.128.50
|
||||
00:04:56:DC:71:E8 #1879 Chris Telles 10.10.128.51
|
||||
00:04:56:DC:6A:0B #1585 Tim Thomas 10.10.128.52
|
||||
00:04:56:DC:66:06 #146 Geok Choon Pang 10.10.128.54
|
||||
58:C1:7A:B0:F8:49 #2280 Ken Hall 10.10.128.6
|
||||
58:C1:7A:B1:04:8B #2292 Joe Speciale 10.10.128.63
|
||||
00:04:56:DB:EB:78 #809 Brad Gurney 10.10.128.8
|
||||
58:C1:7A:B0:C1:3B #2295 Christine Hamparyan 10.10.142.252
|
||||
58:C1:7A:B1:05:D2 #2218 Greg Hummel 10.10.142.253
|
||||
|
||||
## no response (1 devices)
|
||||
58:C1:7A:B3:08:B8 #623 Michael Newby 10.10.96.91
|
||||
|
||||
## Ubiquiti (unknown model) (1 devices)
|
||||
E0:63:DA:0F:B8:F5 #2529 Terry Rector 10.10.62.208
|
||||
97
cpes_annotated.txt
Normal file
97
cpes_annotated.txt
Normal file
|
|
@ -0,0 +1,97 @@
|
|||
MAC Model Account IP
|
||||
------------------------------------------------------------------------------------------------------------------------
|
||||
00:04:56:DC:60:8D PowerBeam M5 / Loco M5 | fw=5.10.4 #1081 Abbie Andrews 10.10.144.36
|
||||
00:04:56:DB:57:2B PowerBeam M5 / Loco M5 | fw=5.11.0 #855 Amber Prater 10.10.16.56
|
||||
58:C1:7A:AD:17:85 NanoBeam M5 / LiteBeam M5 | fw=5.10.4 #2043 Angelica Cuayahuitl 10.10.96.45
|
||||
00:04:56:DB:EA:A0 PowerBeam M5 / Loco M5 | fw=5.10.4 #2487 Anthony Longo 10.10.96.33
|
||||
00:04:56:2E:C9:FF NanoBeam M5 / LiteBeam M5 | fw=5.11.1 #1829 Ashley Smith 10.10.160.6
|
||||
58:C1:7A:B1:02:63 PowerBeam M5 / Loco M5 | fw=5.10.4 #192 Aurea Sanchez 10.10.96.10
|
||||
00:04:56:DC:59:A6 PowerBeam M5 / Loco M5 | fw=5.10.4 #427 Ausencio Luna 10.10.96.73
|
||||
00:04:56:DB:C2:11 PowerBeam M5 / Loco M5 | fw=5.11.1 #433 Balnca Diaz 10.10.96.77
|
||||
00:04:56:DB:E7:4C PowerBeam M5 / Loco M5 | fw=5.11.0 #46 Bernard Heer 10.10.128.3
|
||||
00:04:56:44:50:AA NanoBeam M5 / LiteBeam M5 | fw=5.10.4 #2023 Billy Horton 10.10.96.55
|
||||
00:04:56:DC:6D:77 PowerBeam M5 / Loco M5 | fw=5.10.4 #1712 Bobby Mack 10.10.96.84
|
||||
00:04:56:DB:EB:78 PowerBeam M5 / Loco M5 | fw=5.11.0 #809 Brad Gurney 10.10.128.8
|
||||
00:04:56:2F:DA:E7 NanoBeam M5 / LiteBeam M5 | fw=5.11.0 #2010 Brady Clayton 10.10.128.48
|
||||
58:C1:7A:AD:14:9A NanoBeam M5 / LiteBeam M5 | fw=5.11.1 #357 Brian Swedlund 10.10.160.7
|
||||
00:04:56:DB:63:5E PowerBeam M5 / Loco M5 | fw=5.11.1 #2410 Carol Strickland 10.10.16.37
|
||||
00:04:56:4A:17:B3 PowerBeam M5 / Loco M5 | fw=5.11.1 #2027 Chris Clayton 10.10.128.18
|
||||
00:04:56:DC:71:E8 PowerBeam M5 / Loco M5 | fw=5.11.1 #1879 Chris Telles 10.10.128.51
|
||||
58:C1:7A:B0:C1:3B PowerBeam M5 / Loco M5 | fw=5.10.4 #2295 Christine Hamparyan 10.10.142.252
|
||||
00:04:56:2F:D9:10 NanoBeam M5 / LiteBeam M5 | fw=5.11.1 #1971 Clifford Jennings 10.10.128.11
|
||||
00:04:56:2F:CD:C7 NanoBeam M5 / LiteBeam M5 | fw=5.10.4 #84 Coy Ungemach 10.10.144.2
|
||||
58:C1:7A:B0:D7:10 PowerBeam M5 / Loco M5 | fw=5.11.0 #2233 Crystal Harney 10.10.16.50
|
||||
58:C1:7A:AD:13:D7 NanoBeam M5 / LiteBeam M5 | fw=5.10.4 #100 Dave Kiowski 10.10.128.14
|
||||
00:04:56:DB:99:40 PowerBeam M5 / Loco M5 | fw=5.10.4 #2407 David Roesch 10.10.96.13
|
||||
00:04:56:DC:73:98 PowerBeam M5 / Loco M5 | fw=5.11.0 #2530 Donna Campbell 10.10.16.58
|
||||
00:04:56:4A:13:7E PowerBeam M5 / Loco M5 | fw=5.10.4 #1441 Doris Avalos 10.10.144.16
|
||||
58:C1:7A:B1:09:F8 PowerBeam M5 / Loco M5 | fw=5.11.1 #2360 Doug Garber 10.10.16.57
|
||||
00:04:56:DB:97:F9 PowerBeam M5 / Loco M5 | fw=5.11.1 #848 Douglas Wilson 10.10.128.13
|
||||
00:04:56:DC:06:B7 PowerBeam M5 / Loco M5 | fw=5.11.1 #568 Frances Kirby 10.10.128.37
|
||||
00:04:56:DC:72:2A PowerBeam M5 / Loco M5 | fw=5.10.4 #735 FRED HECKEL 10.10.144.25
|
||||
00:04:56:2F:DA:84 NanoBeam M5 / LiteBeam M5 | fw=5.10.4 #1908 Gabriela Sereno 10.10.96.39
|
||||
00:04:56:DC:66:06 PowerBeam M5 / Loco M5 | fw=5.11.1 #146 Geok Choon Pang 10.10.128.54
|
||||
58:C1:7A:AD:15:7B NanoBeam M5 / LiteBeam M5 | fw=5.10.4 #1906 Georgina Covarrubias 10.10.144.6
|
||||
00:04:56:4A:12:EE PowerBeam M5 / Loco M5 | fw=5.11.0 #2133 Glenda Beauchamp 10.10.16.41
|
||||
58:C1:7A:B1:05:D2 PowerBeam M5 / Loco M5 | fw=5.10.4 #2218 Greg Hummel 10.10.142.253
|
||||
00:04:56:44:5A:37 NanoBeam M5 / LiteBeam M5 | fw=5.10.4 #997 Heather Brister 10.10.128.25
|
||||
58:C1:7A:B0:D0:20 PowerBeam M5 / Loco M5 | fw=5.10.4 #997 Heather Brister 10.10.128.50
|
||||
00:04:56:44:54:0A NanoBeam M5 / LiteBeam M5 | fw=5.11.1 #2046 HEATHER WOMACK 10.10.96.54
|
||||
00:04:56:DC:79:38 PowerBeam M5 / Loco M5 | fw=5.10.4 #477 Helen Lumpkin 10.10.144.24
|
||||
00:04:56:DC:73:6B PowerBeam M5 / Loco M5 | fw=4.8.1 #10027 Irene Garibay 10.10.96.50
|
||||
00:04:56:2E:B7:6C NanoBeam M5 / LiteBeam M5 | fw=5.10.4 #1316 Iris Delira 10.10.96.51
|
||||
00:04:56:48:66:E3 PowerBeam M5 / Loco M5 | fw=5.10.4 #10024 Janis Stewart 10.10.96.9
|
||||
00:04:56:DC:6C:9F PowerBeam M5 / Loco M5 | fw=5.10.4 #293 Jeff Harder 10.10.96.67
|
||||
00:04:56:4A:15:B5 PowerBeam M5 / Loco M5 | fw=5.10.4 #10073 Jeffrey Harder 10.10.96.95
|
||||
58:C1:7A:AC:65:08 NanoBeam M5 / LiteBeam M5 | fw=5.10.4 #2200 Jennifer Boon 10.10.16.38
|
||||
00:04:56:DB:E8:D5 PowerBeam M5 / Loco M5 | fw=5.10.4 #990 Jennifer Dunaway 10.10.144.27
|
||||
58:C1:7A:B0:D7:0D PowerBeam M5 / Loco M5 | fw=5.10.4 #1601 Jerid Rutledge 10.10.96.65
|
||||
00:04:56:2E:C1:6E NanoBeam M5 / LiteBeam M5 | fw=5.11.0 #177 JERRY L JOHNSON 10.10.96.2
|
||||
00:04:56:2E:B9:2E NanoBeam M5 / LiteBeam M5 | fw=5.10.4 #185 Joanna Rodriguez 10.10.160.16
|
||||
00:04:56:DB:C2:2C PowerBeam M5 / Loco M5 | fw=5.10.4 #497 Jody McCluskey 10.10.96.26
|
||||
58:C1:7A:B1:04:8B PowerBeam M5 / Loco M5 | fw=5.11.0 #2292 Joe Speciale 10.10.128.63
|
||||
58:C1:7A:B1:05:12 PowerBeam M5 / Loco M5 | fw=5.11.0 #2275 John Vayo 10.10.16.47
|
||||
58:C1:7A:B0:F8:49 PowerBeam M5 / Loco M5 | fw=5.11.0 #2280 Ken Hall 10.10.128.6
|
||||
58:C1:7A:B3:06:A8 PowerBeam M5 / Loco M5 | fw=5.11.1 #875 Ken Patrick 10.10.96.7
|
||||
00:04:56:DB:EC:11 PowerBeam M5 / Loco M5 | fw=5.11.1 #2037 Kevin Arana 10.10.160.9
|
||||
00:04:56:DC:6B:55 PowerBeam M5 / Loco M5 | fw=5.10.4 #10052 Khushboo Agarwal 10.10.96.53
|
||||
58:C1:7A:AC:63:19 NanoBeam M5 / LiteBeam M5 | fw=4.6.0.1 #215 Kileigh Even 10.10.96.46
|
||||
00:04:56:DC:5E:50 PowerBeam M5 / Loco M5 | fw=5.11.1 #2465 Kory Biggs 10.10.128.33
|
||||
58:C1:7A:AC:E8:81 NanoBeam M5 / LiteBeam M5 | fw=5.11.1 #2492 Kory Biggs Shop 10.10.128.43
|
||||
58:C1:7A:B0:B7:AE NanoBeam M5 / LiteBeam M5 | fw=5.11.0 #1950 Leandra Beshea 10.10.128.1
|
||||
58:C1:7A:B0:F7:8F PowerBeam M5 / Loco M5 | fw=5.10.4 #224 Leonard Lewis 10.10.144.20
|
||||
58:C1:7A:AD:22:EC NanoBeam M5 / LiteBeam M5 | fw=5.11.1 #1650 luis Cabrera 10.10.64.1
|
||||
00:04:56:DC:69:F0 PowerBeam M5 / Loco M5 | fw=5.11.1 #2458 Marcus Patton 10.10.128.28
|
||||
58:C1:7A:B3:07:A1 PowerBeam M5 / Loco M5 | fw=5.11.1 #435 Maria Cortes 10.10.64.12
|
||||
00:04:56:DC:5D:63 PowerBeam M5 / Loco M5 | fw=5.10.4 #243 Mark Nunnelee 10.10.144.35
|
||||
58:C1:7A:AC:5A:E5 NanoBeam M5 / LiteBeam M5 | fw=5.11.1 #2489 Martha Flores 10.10.128.35
|
||||
58:C1:7A:AD:1F:20 NanoBeam M5 / LiteBeam M5 | fw=5.10.4 #246 Matt Budjenska 10.10.158.247
|
||||
00:04:56:DC:69:AE PowerBeam M5 / Loco M5 | fw=5.11.1 #932 Matt Kosarek 10.10.160.19
|
||||
58:C1:7A:B3:08:B8 PowerBeam M5 / Loco M5 | fw=5.10.4 #623 Michael Newby 10.10.96.91
|
||||
00:04:56:DC:05:04 PowerBeam M5 / Loco M5 | fw=5.11.1 #556 Mike Bell 10.10.128.45
|
||||
58:C1:7A:AC:64:51 NanoBeam M5 / LiteBeam M5 | fw=4.7 #2454 Nancy Avellaneda 10.10.64.5
|
||||
58:C1:7A:AC:6A:C3 NanoBeam M5 / LiteBeam M5 | fw=5.10.4 #10059 Nicholas Terry 10.10.144.11
|
||||
00:04:56:4A:17:53 PowerBeam M5 / Loco M5 | fw=5.10.4 #10074 Pablo Martinez 10.10.96.36
|
||||
58:C1:7A:B0:B6:1F NanoBeam M5 / LiteBeam M5 | fw=5.10.4 #1128 Patricia Vasquez 10.10.96.58
|
||||
00:04:56:2F:D4:BD NanoBeam M5 / LiteBeam M5 | fw=5.10.4 #283 Paul Sammataro 10.10.96.29
|
||||
58:C1:7A:AC:69:46 NanoBeam M5 / LiteBeam M5 | fw=4.7 #2190 Ray Schaak 10.10.96.17
|
||||
00:04:56:44:50:68 NanoBeam M5 / LiteBeam M5 | fw=5.11.1 #1949 RICHARD CONKLIN 10.10.128.34
|
||||
00:04:56:DC:04:EF PowerBeam M5 / Loco M5 | fw=5.10.4 #301 Ron Berger 10.10.144.4
|
||||
00:04:56:44:52:ED NanoBeam M5 / LiteBeam M5 | fw=5.11.1 #318 Shannon Brown 10.10.128.32
|
||||
58:C1:7A:AC:5F:11 NanoBeam M5 / LiteBeam M5 | fw=5.10.4 #1538 Shirley Elmore 10.10.96.5
|
||||
00:04:56:2F:D7:CC NanoBeam M5 / LiteBeam M5 | fw=5.10.4 #2098 Stephen Beegle 10.10.160.2
|
||||
00:04:56:DB:D2:CA PowerBeam M5 / Loco M5 | fw=5.11.1 #1168 Stephen Day 10.10.160.22
|
||||
00:04:56:48:63:EF PowerBeam M5 / Loco M5 | fw=4.7.0.1 #587 Tamy Thomas 10.10.96.14
|
||||
00:04:56:DC:67:59 PowerBeam M5 / Loco M5 | fw=5.10.4 #1467 Terri Mack 10.10.96.72
|
||||
00:04:56:DB:EB:18 PowerBeam M5 / Loco M5 | fw=5.11.1 #490 Terry Bates 10.10.64.8
|
||||
00:04:56:DB:AA:E6 PowerBeam M5 / Loco M5 | fw=5.10.4 #815 Terry Gortney 10.10.144.32
|
||||
00:04:56:DC:72:24 PowerBeam M5 / Loco M5 | fw=5.10.4 #339 Terry Miesen 10.10.144.7
|
||||
00:04:56:2F:DC:4C NanoBeam M5 / LiteBeam M5 | fw=5.10.4 #10040 Thomas Graham 10.10.144.10
|
||||
00:04:56:DB:E9:FE PowerBeam M5 / Loco M5 | fw=5.10.4 #2479 Thomas Hernandez 10.10.96.38
|
||||
58:C1:7A:B0:F3:39 PowerBeam M5 / Loco M5 | fw=5.10.4 #2335 Tiffany Stewart 10.10.96.42
|
||||
00:04:56:DC:6A:0B PowerBeam M5 / Loco M5 | fw=4.8.1 #1585 Tim Thomas 10.10.128.52
|
||||
00:04:56:44:5E:7B NanoBeam M5 / LiteBeam M5 | fw=5.11.1 #343 Tim Bagert 10.10.16.40
|
||||
00:04:56:DB:E5:ED PowerBeam M5 / Loco M5 | fw=5.10.4 #1788 Tim Bronschidle 10.10.96.40
|
||||
00:04:56:DC:6A:65 PowerBeam M5 / Loco M5 | fw=5.10.4 #345 Tim Fisher 10.10.144.31
|
||||
00:04:56:2F:DB:A1 NanoBeam M5 / LiteBeam M5 | fw=5.10.4 #355 Treceda Schmid 10.10.96.11
|
||||
00:04:56:2F:CE:8D NanoBeam M5 / LiteBeam M5 | fw=5.10.4 #2375 Yolanda Judge 10.10.96.35
|
||||
119
cpes_by_model.txt
Normal file
119
cpes_by_model.txt
Normal file
|
|
@ -0,0 +1,119 @@
|
|||
Identifying 91 devices via HTTPS...
|
||||
20/91
|
||||
40/91
|
||||
60/91
|
||||
80/91
|
||||
91/91 done
|
||||
|
||||
## Cambium ePMP (unknown model) (1 devices)
|
||||
58:C1:7A:B3:08:B8 #623 Michael Newby 10.10.96.91
|
||||
|
||||
## ePMP Force 110 (fw=Version 5.11.1) (1 devices)
|
||||
00:04:56:F0:36:0F #125 Eddie Yarbrough 10.10.16.51
|
||||
|
||||
## ePMP Force 180 (fw=Version 5.10.4) (17 devices)
|
||||
58:C1:7A:AD:17:85 #2043 Angelica Cuayahuitl 10.10.96.45
|
||||
00:04:56:44:50:AA #2023 Billy Horton 10.10.96.55
|
||||
00:04:56:2F:CD:C7 #84 Coy Ungemach 10.10.144.2
|
||||
58:C1:7A:AD:13:D7 #100 Dave Kiowski 10.10.128.14
|
||||
00:04:56:2F:DA:84 #1908 Gabriela Sereno 10.10.96.39
|
||||
58:C1:7A:AD:15:7B #1906 Georgina Covarrubias 10.10.144.6
|
||||
00:04:56:44:5A:37 #997 Heather Brister 10.10.128.25
|
||||
00:04:56:2E:B7:6C #1316 Iris Delira 10.10.96.51
|
||||
58:C1:7A:AC:65:08 #2200 Jennifer Boon 10.10.16.38
|
||||
00:04:56:2E:B9:2E #185 Joanna Rodriguez 10.10.160.16
|
||||
58:C1:7A:AD:1F:20 #246 Matt Budjenska 10.10.158.247
|
||||
58:C1:7A:B0:B6:1F #1128 Patricia Vasquez 10.10.96.58
|
||||
00:04:56:2F:D4:BD #283 Paul Sammataro 10.10.96.29
|
||||
58:C1:7A:AC:5F:11 #1538 Shirley Elmore 10.10.96.5
|
||||
00:04:56:2F:D7:CC #2098 Stephen Beegle 10.10.160.2
|
||||
00:04:56:2F:DB:A1 #355 Treceda Schmid 10.10.96.11
|
||||
00:04:56:2F:CE:8D #2375 Yolanda Judge 10.10.96.35
|
||||
|
||||
## ePMP Force 180 (fw=Version 5.11.0) (3 devices)
|
||||
00:04:56:2F:DA:E7 #2010 Brady Clayton 10.10.128.48
|
||||
00:04:56:2E:C1:6E #177 JERRY L JOHNSON 10.10.96.2
|
||||
58:C1:7A:B0:B7:AE #1950 Leandra Beshea 10.10.128.1
|
||||
|
||||
## ePMP Force 180 (fw=Version 5.11.1) (10 devices)
|
||||
00:04:56:2E:C9:FF #1829 Ashley Smith 10.10.160.6
|
||||
58:C1:7A:AD:14:9A #357 Brian Swedlund 10.10.160.7
|
||||
00:04:56:2F:D9:10 #1971 Clifford Jennings 10.10.128.11
|
||||
00:04:56:44:54:0A #2046 HEATHER WOMACK 10.10.96.54
|
||||
58:C1:7A:AC:E8:81 #2492 Kory Biggs Shop 10.10.128.43
|
||||
58:C1:7A:AD:22:EC #1650 luis Cabrera 10.10.64.1
|
||||
58:C1:7A:AC:5A:E5 #2489 Martha Flores 10.10.128.35
|
||||
00:04:56:44:50:68 #1949 RICHARD CONKLIN 10.10.128.34
|
||||
00:04:56:44:52:ED #318 Shannon Brown 10.10.128.32
|
||||
00:04:56:44:5E:7B #343 Tim Bagert 10.10.16.40
|
||||
|
||||
## ePMP Force 200 (fw=Version 4.8.1) (1 devices)
|
||||
00:04:56:DC:6A:0B #1585 Tim 10.10.128.52
|
||||
|
||||
## ePMP Force 200 (fw=Version 5.10.4) (26 devices)
|
||||
00:04:56:DC:60:8D #1081 Abbie Andrews 10.10.144.36
|
||||
00:04:56:DB:EA:A0 #2487 Anthony Longo 10.10.96.33
|
||||
58:C1:7A:B1:02:63 #192 Aurea Sanchez 10.10.96.10
|
||||
00:04:56:DC:59:A6 #427 Ausencio Luna 10.10.96.73
|
||||
00:04:56:DC:6D:77 #1712 Bobby Mack 10.10.96.84
|
||||
58:C1:7A:B0:C1:3B #2295 Christine Hamparyan 10.10.142.252
|
||||
00:04:56:DB:99:40 #2407 David Roesch 10.10.96.13
|
||||
00:04:56:4A:13:7E #1441 Doris Avalos 10.10.144.16
|
||||
00:04:56:DC:72:2A #735 FRED HECKEL 10.10.144.25
|
||||
58:C1:7A:B1:05:D2 #2218 Greg Hummel 10.10.142.253
|
||||
58:C1:7A:B0:D0:20 #997 Heather Brister 10.10.128.50
|
||||
00:04:56:DC:79:38 #477 Helen Lumpkin 10.10.144.24
|
||||
00:04:56:DC:6C:9F #293 Jeff Harder 10.10.96.67
|
||||
00:04:56:DB:E8:D5 #990 Jennifer Dunaway 10.10.144.27
|
||||
58:C1:7A:B0:D7:0D #1601 Jerid Rutledge 10.10.96.65
|
||||
00:04:56:DB:C2:2C #497 Jody McCluskey 10.10.96.26
|
||||
58:C1:7A:B0:F7:8F #224 Leonard Lewis 10.10.144.20
|
||||
00:04:56:DC:5D:63 #243 Mark Nunnelee 10.10.144.35
|
||||
00:04:56:DC:04:EF #301 Ron Berger 10.10.144.4
|
||||
00:04:56:DC:67:59 #1467 Terri Mack 10.10.96.72
|
||||
00:04:56:DB:AA:E6 #815 Terry Gortney 10.10.144.32
|
||||
00:04:56:DC:72:24 #339 Terry Miesen 10.10.144.7
|
||||
00:04:56:DB:E9:FE #2479 Thomas Hernandez 10.10.96.38
|
||||
58:C1:7A:B0:F3:39 #2335 Tiffany Stewart 10.10.96.42
|
||||
00:04:56:DB:E5:ED #1788 Tim Bronschidle 10.10.96.40
|
||||
00:04:56:DC:6A:65 #345 Tim Fisher 10.10.144.31
|
||||
|
||||
## ePMP Force 200 (fw=Version 5.11.0) (9 devices)
|
||||
00:04:56:DB:57:2B #855 Amber Prater 10.10.16.56
|
||||
00:04:56:DB:E7:4C #46 Bernard Heer 10.10.128.3
|
||||
00:04:56:DB:EB:78 #809 Brad Gurney 10.10.128.8
|
||||
58:C1:7A:B0:D7:10 #2233 Crystal Harney 10.10.16.50
|
||||
00:04:56:DC:73:98 #2530 Donna Campbell 10.10.16.58
|
||||
00:04:56:4A:12:EE #2133 Glenda Beauchamp 10.10.16.41
|
||||
58:C1:7A:B1:04:8B #2292 Joe Speciale 10.10.128.63
|
||||
58:C1:7A:B1:05:12 #2275 John Vayo 10.10.16.47
|
||||
58:C1:7A:B0:F8:49 #2280 Ken Hall 10.10.128.6
|
||||
|
||||
## ePMP Force 200 (fw=Version 5.11.1) (17 devices)
|
||||
00:04:56:DB:C2:11 #433 Balnca Diaz 10.10.96.77
|
||||
00:04:56:DB:63:5E #2410 Carol Strickland 10.10.16.37
|
||||
00:04:56:4A:17:B3 #2027 Chris Clayton 10.10.128.18
|
||||
00:04:56:DC:71:E8 #1879 Chris Telles 10.10.128.51
|
||||
58:C1:7A:B1:09:F8 #2360 Doug Garber 10.10.16.57
|
||||
00:04:56:DB:97:F9 #848 Douglas Wilson 10.10.128.13
|
||||
00:04:56:DC:06:B7 #568 Frances Kirby 10.10.128.37
|
||||
00:04:56:DC:66:06 #146 Geok Choon Pang 10.10.128.54
|
||||
58:C1:7A:B3:06:A8 #875 Ken Patrick 10.10.96.7
|
||||
00:04:56:DB:EC:11 #2037 Kevin Arana 10.10.160.9
|
||||
00:04:56:DC:5E:50 #2465 Kory Biggs 10.10.128.33
|
||||
00:04:56:DC:69:F0 #2458 Marcus Patton 10.10.128.28
|
||||
58:C1:7A:B3:07:A1 #435 Maria Cortes 10.10.64.12
|
||||
00:04:56:DC:69:AE #932 Matt Kosarek 10.10.160.19
|
||||
00:04:56:DC:05:04 #556 Mike Bell 10.10.128.45
|
||||
00:04:56:DB:D2:CA #1168 Stephen Day 10.10.160.22
|
||||
00:04:56:DB:EB:18 #490 Terry Bates 10.10.64.8
|
||||
|
||||
## no HTTPS response (5 devices)
|
||||
58:C1:7A:AC:63:19 #215 Kileigh Even 10.10.96.46
|
||||
58:C1:7A:AC:64:51 #2454 Nancy Avellaneda 10.10.64.5
|
||||
58:C1:7A:AC:69:46 #2190 Ray Schaak 10.10.96.17
|
||||
58:C1:7A:AC:62:5F #2206 Wanda Davis 10.10.96.102
|
||||
00:04:56:48:63:EF #587 Tamy Thomas 10.10.96.14
|
||||
|
||||
## Ubiquiti (unknown model) (1 devices)
|
||||
E0:63:DA:0F:B8:F5 #2529 Terry Rector 10.10.62.208
|
||||
196
cpes_raw.txt
Normal file
196
cpes_raw.txt
Normal file
|
|
@ -0,0 +1,196 @@
|
|||
router=494 ip=10.10.160.16 mac=00:04:56:2E:B9:2E device=Ubiquiti (legacy)
|
||||
account=#185 name='Joanna Rodriguez' host='Joanna Rodriguez'
|
||||
router=494 ip=10.10.160.19 mac=00:04:56:DC:69:AE device=Ubiquiti (legacy)
|
||||
account=#932 name='Matt Kosarek' host='Matt Kosarek'
|
||||
router=494 ip=10.10.160.2 mac=00:04:56:2F:D7:CC device=Ubiquiti (legacy)
|
||||
account=#2098 name='Stephen Beegle' host='Stephen Beegle'
|
||||
router=494 ip=10.10.160.22 mac=00:04:56:DB:D2:CA device=Ubiquiti (legacy)
|
||||
account=#1168 name='Stephen Day' host='Stephen Day'
|
||||
router=494 ip=10.10.160.6 mac=00:04:56:2E:C9:FF device=Ubiquiti (legacy)
|
||||
account=#1829 name='Ashley Smith' host='Ashley Smith'
|
||||
router=494 ip=10.10.160.7 mac=58:C1:7A:AD:14:9A device=Cambium ePMP
|
||||
account=#357 name='Brian Swedlund' host='Brian Swedlund'
|
||||
router=494 ip=10.10.160.9 mac=00:04:56:DB:EC:11 device=Ubiquiti (legacy)
|
||||
account=#2037 name='Kevin Arana' host='Kevin Arana'
|
||||
router=982 ip=10.10.62.208 mac=E0:63:DA:0F:B8:F5 device=Ubiquiti PowerBeam M5 / Loco M5
|
||||
account=#2529 name='Terry Rector' host='Terry-Rector'
|
||||
router=climax ip=10.10.16.37 mac=00:04:56:DB:63:5E device=Ubiquiti (legacy)
|
||||
account=#2410 name='Carol Strickland' host='Carol Strickland'
|
||||
router=climax ip=10.10.16.38 mac=58:C1:7A:AC:65:08 device=Cambium ePMP
|
||||
account=#2200 name='Jennifer Boon' host='Jennifer Boon'
|
||||
router=climax ip=10.10.16.40 mac=00:04:56:44:5E:7B device=Ubiquiti (legacy)
|
||||
account=#343 name='Tim Bagert' host='Tim Bagert'
|
||||
router=climax ip=10.10.16.41 mac=00:04:56:4A:12:EE device=Ubiquiti (legacy)
|
||||
account=#2133 name='Glenda Beauchamp' host='Glenda Beauchamp'
|
||||
router=climax ip=10.10.16.47 mac=58:C1:7A:B1:05:12 device=Cambium ePMP
|
||||
account=#2275 name='John Vayo' host='John Vayo'
|
||||
router=climax ip=10.10.16.50 mac=58:C1:7A:B0:D7:10 device=Cambium ePMP
|
||||
account=#2233 name='Crystal Harney' host='Crystal Harney'
|
||||
router=climax ip=10.10.16.51 mac=00:04:56:F0:36:0F device=Ubiquiti (legacy)
|
||||
account=#125 name='Eddie Yarbrough' host='Eddie Yarbrough'
|
||||
router=climax ip=10.10.16.56 mac=00:04:56:DB:57:2B device=Ubiquiti (legacy)
|
||||
account=#855 name='Amber Prater' host='Amber Prater'
|
||||
router=climax ip=10.10.16.57 mac=58:C1:7A:B1:09:F8 device=Cambium ePMP
|
||||
account=#2360 name='Doug Garber' host='Doug Garber'
|
||||
router=climax ip=10.10.16.58 mac=00:04:56:DC:73:98 device=Ubiquiti (legacy)
|
||||
account=#2530 name='Donna Campbell' host='Donna Campbell'
|
||||
router=core ip=10.10.64.1 mac=58:C1:7A:AD:22:EC device=Cambium ePMP
|
||||
account=#1650 name='luis Cabrera' host='Luis Cabrera'
|
||||
router=core ip=10.10.64.12 mac=58:C1:7A:B3:07:A1 device=Cambium ePMP
|
||||
account=#435 name='Maria Cortes' host='Maria Cortes'
|
||||
router=core ip=10.10.64.5 mac=58:C1:7A:AC:64:51 device=Cambium ePMP
|
||||
account=#2454 name='Nancy Avellaneda' host='Nancy Avellaneda'
|
||||
router=core ip=10.10.64.8 mac=00:04:56:DB:EB:18 device=Ubiquiti (legacy)
|
||||
account=#490 name='Terry Bates' host='Terry Bates'
|
||||
router=culleoka ip=10.10.96.10 mac=58:C1:7A:B1:02:63 device=Cambium ePMP
|
||||
account=#192 name='Aurea Sanchez' host='Aurea Sanchez'
|
||||
router=culleoka ip=10.10.96.102 mac=58:C1:7A:AC:62:5F device=Cambium ePMP
|
||||
account=#2206 name='Wanda Davis' host='Wanda Davis'
|
||||
router=culleoka ip=10.10.96.11 mac=00:04:56:2F:DB:A1 device=Ubiquiti (legacy)
|
||||
account=#355 name='Treceda Schmid' host='Treceda Schmid'
|
||||
router=culleoka ip=10.10.96.13 mac=00:04:56:DB:99:40 device=Ubiquiti (legacy)
|
||||
account=#2407 name='David Roesch' host='David Roesch'
|
||||
router=culleoka ip=10.10.96.14 mac=00:04:56:48:63:EF device=Ubiquiti (legacy)
|
||||
account=#587 name='Tamy Thomas' host='Tamy Thomas'
|
||||
router=culleoka ip=10.10.96.17 mac=58:C1:7A:AC:69:46 device=Cambium ePMP
|
||||
account=#2190 name='Ray Schaak' host='Ray Schaak'
|
||||
router=culleoka ip=10.10.96.2 mac=00:04:56:2E:C1:6E device=Ubiquiti (legacy)
|
||||
account=#177 name='JERRY L JOHNSON' host='Jerry Johnson'
|
||||
router=culleoka ip=10.10.96.26 mac=00:04:56:DB:C2:2C device=Ubiquiti (legacy)
|
||||
account=#497 name='Jody McCluskey' host='Jody McCluskey'
|
||||
router=culleoka ip=10.10.96.29 mac=00:04:56:2F:D4:BD device=Ubiquiti (legacy)
|
||||
account=#283 name='Paul Sammataro' host='Paul Sammataro'
|
||||
router=culleoka ip=10.10.96.33 mac=00:04:56:DB:EA:A0 device=Ubiquiti (legacy)
|
||||
account=#2487 name='Anthony Longo' host='Anthony Longo'
|
||||
router=culleoka ip=10.10.96.35 mac=00:04:56:2F:CE:8D device=Ubiquiti (legacy)
|
||||
account=#2375 name='Yolanda Judge' host='Yolanda Judge'
|
||||
router=culleoka ip=10.10.96.36 mac=00:04:56:4A:17:53 device=Ubiquiti (legacy)
|
||||
account=#10074 name='Pablo Martinez' host='Pablo Martinez'
|
||||
router=culleoka ip=10.10.96.38 mac=00:04:56:DB:E9:FE device=Ubiquiti (legacy)
|
||||
account=#2479 name='Thomas Hernandez' host='Thomas Hernandez'
|
||||
router=culleoka ip=10.10.96.39 mac=00:04:56:2F:DA:84 device=Ubiquiti (legacy)
|
||||
account=#1908 name='Gabriela Sereno' host='Gabriela Sereno'
|
||||
router=culleoka ip=10.10.96.40 mac=00:04:56:DB:E5:ED device=Ubiquiti (legacy)
|
||||
account=#1788 name='Tim Bronschidle' host='Tim Bronschidle'
|
||||
router=culleoka ip=10.10.96.42 mac=58:C1:7A:B0:F3:39 device=Cambium ePMP
|
||||
account=#2335 name='Tiffany Stewart' host='Tiffany Stewart'
|
||||
router=culleoka ip=10.10.96.45 mac=58:C1:7A:AD:17:85 device=Cambium ePMP
|
||||
account=#2043 name='Angelica Cuayahuitl' host='Angelica Cuayahuitl'
|
||||
router=culleoka ip=10.10.96.46 mac=58:C1:7A:AC:63:19 device=Cambium ePMP
|
||||
account=#215 name='Kileigh Even' host='Kileigh Even'
|
||||
router=culleoka ip=10.10.96.5 mac=58:C1:7A:AC:5F:11 device=Cambium ePMP
|
||||
account=#1538 name='Shirley Elmore' host='Shirley Elmore'
|
||||
router=culleoka ip=10.10.96.50 mac=00:04:56:DC:73:6B device=Ubiquiti (legacy)
|
||||
account=#10027 name='Irene Garibay' host='Irene Garibay'
|
||||
router=culleoka ip=10.10.96.51 mac=00:04:56:2E:B7:6C device=Ubiquiti (legacy)
|
||||
account=#1316 name='Iris Delira' host='Iris Delira'
|
||||
router=culleoka ip=10.10.96.53 mac=00:04:56:DC:6B:55 device=Ubiquiti (legacy)
|
||||
account=#10052 name='Khushboo Agarwal' host='Khushboo Agarwal'
|
||||
router=culleoka ip=10.10.96.54 mac=00:04:56:44:54:0A device=Ubiquiti (legacy)
|
||||
account=#2046 name='HEATHER WOMACK' host='Heather Womack'
|
||||
router=culleoka ip=10.10.96.55 mac=00:04:56:44:50:AA device=Ubiquiti (legacy)
|
||||
account=#2023 name='Billy Horton' host='Billy Horton'
|
||||
router=culleoka ip=10.10.96.58 mac=58:C1:7A:B0:B6:1F device=Cambium ePMP
|
||||
account=#1128 name='Patricia Vasquez' host='Patricia Vasquez'
|
||||
router=culleoka ip=10.10.96.65 mac=58:C1:7A:B0:D7:0D device=Cambium ePMP
|
||||
account=#1601 name='Jerid Rutledge' host='Jerid Rutledge'
|
||||
router=culleoka ip=10.10.96.67 mac=00:04:56:DC:6C:9F device=Ubiquiti (legacy)
|
||||
account=#293 name='Jeff Harder' host='Jeff Harder'
|
||||
router=culleoka ip=10.10.96.7 mac=58:C1:7A:B3:06:A8 device=Cambium ePMP
|
||||
account=#875 name='Ken Patrick' host='Ken Patrick'
|
||||
router=culleoka ip=10.10.96.72 mac=00:04:56:DC:67:59 device=Ubiquiti (legacy)
|
||||
account=#1467 name='Terri Mack' host='Terri Mack'
|
||||
router=culleoka ip=10.10.96.73 mac=00:04:56:DC:59:A6 device=Ubiquiti (legacy)
|
||||
account=#427 name='Ausencio Luna' host='Ausencio Luna'
|
||||
router=culleoka ip=10.10.96.77 mac=00:04:56:DB:C2:11 device=Ubiquiti (legacy)
|
||||
account=#433 name='Balnca Diaz' host='Balnca Diaz'
|
||||
router=culleoka ip=10.10.96.84 mac=00:04:56:DC:6D:77 device=Ubiquiti (legacy)
|
||||
account=#1712 name='Bobby Mack' host='Bobby Mack'
|
||||
router=culleoka ip=10.10.96.9 mac=00:04:56:48:66:E3 device=Ubiquiti (legacy)
|
||||
account=#10024 name='Janis Stewart' host='Janis Stewart'
|
||||
router=culleoka ip=10.10.96.91 mac=58:C1:7A:B3:08:B8 device=Cambium ePMP
|
||||
account=#623 name='Michael Newby' host='Michael Newby'
|
||||
router=culleoka ip=10.10.96.95 mac=00:04:56:4A:15:B5 device=Ubiquiti (legacy)
|
||||
account=#10073 name='Jeffrey Harder' host='Jeffrey Harder'
|
||||
router=lowry ip=10.10.144.10 mac=00:04:56:2F:DC:4C device=Ubiquiti (legacy)
|
||||
account=#10040 name='Thomas Graham' host='Thomas Graham'
|
||||
router=lowry ip=10.10.144.11 mac=58:C1:7A:AC:6A:C3 device=Cambium ePMP
|
||||
account=#10059 name='Nicholas Terry' host='Nicholas Terry'
|
||||
router=lowry ip=10.10.144.16 mac=00:04:56:4A:13:7E device=Ubiquiti (legacy)
|
||||
account=#1441 name='Doris Avalos' host='Doris Avalos'
|
||||
router=lowry ip=10.10.144.2 mac=00:04:56:2F:CD:C7 device=Ubiquiti (legacy)
|
||||
account=#84 name='Coy Ungemach' host='Coy Ungemach'
|
||||
router=lowry ip=10.10.144.20 mac=58:C1:7A:B0:F7:8F device=Cambium ePMP
|
||||
account=#224 name='Leonard Lewis' host='Leonard Lewis'
|
||||
router=lowry ip=10.10.144.24 mac=00:04:56:DC:79:38 device=Ubiquiti (legacy)
|
||||
account=#477 name='Helen Lumpkin' host='Helen Lumpkin'
|
||||
router=lowry ip=10.10.144.25 mac=00:04:56:DC:72:2A device=Ubiquiti (legacy)
|
||||
account=#735 name='FRED HECKEL' host='Fred Heckel'
|
||||
router=lowry ip=10.10.144.27 mac=00:04:56:DB:E8:D5 device=Ubiquiti (legacy)
|
||||
account=#990 name='Jennifer Dunaway' host='Jennifer Dunaway'
|
||||
router=lowry ip=10.10.144.31 mac=00:04:56:DC:6A:65 device=Ubiquiti (legacy)
|
||||
account=#345 name='Tim Fisher' host='Tim Fisher'
|
||||
router=lowry ip=10.10.144.32 mac=00:04:56:DB:AA:E6 device=Ubiquiti (legacy)
|
||||
account=#815 name='Terry Gortney' host='Terry Gortney'
|
||||
router=lowry ip=10.10.144.35 mac=00:04:56:DC:5D:63 device=Ubiquiti (legacy)
|
||||
account=#243 name='Mark Nunnelee' host='Mark Nunnelee'
|
||||
router=lowry ip=10.10.144.36 mac=00:04:56:DC:60:8D device=Ubiquiti (legacy)
|
||||
account=#1081 name='Abbie Andrews' host='Abbie Andrews'
|
||||
router=lowry ip=10.10.144.4 mac=00:04:56:DC:04:EF device=Ubiquiti (legacy)
|
||||
account=#301 name='Ron Berger' host='Ron Berger'
|
||||
router=lowry ip=10.10.144.6 mac=58:C1:7A:AD:15:7B device=Cambium ePMP
|
||||
account=#1906 name='Georgina Covarrubias' host='Georgina Covarrubias'
|
||||
router=lowry ip=10.10.144.7 mac=00:04:56:DC:72:24 device=Ubiquiti (legacy)
|
||||
account=#339 name='Terry Miesen' host='Terry Miesen'
|
||||
router=lowry ip=10.10.158.247 mac=58:C1:7A:AD:1F:20 device=Cambium ePMP
|
||||
account=#246 name='Matt Budjenska' host='Matt Budjenska'
|
||||
router=newhope ip=10.10.128.1 mac=58:C1:7A:B0:B7:AE device=Cambium ePMP
|
||||
account=#1950 name='Leandra Beshea' host='Leandra Beshea'
|
||||
router=newhope ip=10.10.128.11 mac=00:04:56:2F:D9:10 device=Ubiquiti (legacy)
|
||||
account=#1971 name='Clifford Jennings' host='Clifford M Jennings'
|
||||
router=newhope ip=10.10.128.13 mac=00:04:56:DB:97:F9 device=Ubiquiti (legacy)
|
||||
account=#848 name='Douglas Wilson' host='Douglas Wilson'
|
||||
router=newhope ip=10.10.128.14 mac=58:C1:7A:AD:13:D7 device=Cambium ePMP
|
||||
account=#100 name='Dave Kiowski' host='Dave Kiowski'
|
||||
router=newhope ip=10.10.128.18 mac=00:04:56:4A:17:B3 device=Ubiquiti (legacy)
|
||||
account=#2027 name='Chris Clayton' host='Chris Clayton'
|
||||
router=newhope ip=10.10.128.25 mac=00:04:56:44:5A:37 device=Ubiquiti (legacy)
|
||||
account=#997 name='Heather Brister' host='Heather Brister 2'
|
||||
router=newhope ip=10.10.128.28 mac=00:04:56:DC:69:F0 device=Ubiquiti (legacy)
|
||||
account=#2458 name='Marcus Patton' host='Marcus Patton'
|
||||
router=newhope ip=10.10.128.3 mac=00:04:56:DB:E7:4C device=Ubiquiti (legacy)
|
||||
account=#46 name='Bernard Heer' host='Bernard Heer'
|
||||
router=newhope ip=10.10.128.32 mac=00:04:56:44:52:ED device=Ubiquiti (legacy)
|
||||
account=#318 name='Shannon Brown' host='Shannon Brown'
|
||||
router=newhope ip=10.10.128.33 mac=00:04:56:DC:5E:50 device=Ubiquiti (legacy)
|
||||
account=#2465 name='Kory Biggs' host='Kory Biggs'
|
||||
router=newhope ip=10.10.128.34 mac=00:04:56:44:50:68 device=Ubiquiti (legacy)
|
||||
account=#1949 name='RICHARD CONKLIN' host='Richard Conklin'
|
||||
router=newhope ip=10.10.128.35 mac=58:C1:7A:AC:5A:E5 device=Cambium ePMP
|
||||
account=#2489 name='Martha Flores' host='Martha Flores'
|
||||
router=newhope ip=10.10.128.37 mac=00:04:56:DC:06:B7 device=Ubiquiti (legacy)
|
||||
account=#568 name='Frances Kirby' host='Frances Kirby'
|
||||
router=newhope ip=10.10.128.43 mac=58:C1:7A:AC:E8:81 device=Cambium ePMP
|
||||
account=#2492 name='Kory Biggs Shop' host='Kory Biggs Shop'
|
||||
router=newhope ip=10.10.128.45 mac=00:04:56:DC:05:04 device=Ubiquiti (legacy)
|
||||
account=#556 name='Mike Bell' host='Mike Bell'
|
||||
router=newhope ip=10.10.128.48 mac=00:04:56:2F:DA:E7 device=Ubiquiti (legacy)
|
||||
account=#2010 name='Brady Clayton' host='Brady Clayton'
|
||||
router=newhope ip=10.10.128.50 mac=58:C1:7A:B0:D0:20 device=Cambium ePMP
|
||||
account=#997 name='Heather Brister' host='Heather Brister'
|
||||
router=newhope ip=10.10.128.51 mac=00:04:56:DC:71:E8 device=Ubiquiti (legacy)
|
||||
account=#1879 name='Chris Telles' host='Chris Telles'
|
||||
router=newhope ip=10.10.128.52 mac=00:04:56:DC:6A:0B device=Ubiquiti (legacy)
|
||||
account=#1585 name='Tim Thomas' host='Tim Thomas'
|
||||
router=newhope ip=10.10.128.54 mac=00:04:56:DC:66:06 device=Ubiquiti (legacy)
|
||||
account=#146 name='Geok Choon Pang' host='Geok Choon Pang'
|
||||
router=newhope ip=10.10.128.6 mac=58:C1:7A:B0:F8:49 device=Cambium ePMP
|
||||
account=#2280 name='Ken Hall' host='Ken Hall'
|
||||
router=newhope ip=10.10.128.63 mac=58:C1:7A:B1:04:8B device=Cambium ePMP
|
||||
account=#2292 name='Joe Speciale' host='Joe Speciale'
|
||||
router=newhope ip=10.10.128.8 mac=00:04:56:DB:EB:78 device=Ubiquiti (legacy)
|
||||
account=#809 name='Brad Gurney' host='Brad Gurney'
|
||||
router=newhope ip=10.10.142.252 mac=58:C1:7A:B0:C1:3B device=Cambium ePMP
|
||||
account=#2295 name='Christine Hamparyan' host='Christine Hamparyan'
|
||||
router=newhope ip=10.10.142.253 mac=58:C1:7A:B1:05:D2 device=Cambium ePMP
|
||||
account=#2218 name='Greg Hummel' host='Greg Hummel'
|
||||
229
docs/plans/2026-05-14-ipv6-traceroute-collapse-6pe.md
Normal file
229
docs/plans/2026-05-14-ipv6-traceroute-collapse-6pe.md
Normal file
|
|
@ -0,0 +1,229 @@
|
|||
# IPv6 Traceroute Collapse (6PE) Implementation Plan
|
||||
|
||||
> **For Claude:** REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task.
|
||||
|
||||
**Goal:** Make IPv6 traffic transit the vntx backbone as MPLS-labeled frames so `traceroute6` collapses the internal core to ~1 hop, matching the existing IPv4 behaviour.
|
||||
|
||||
**Architecture:** Today LDP is IPv4-only and `/mpls settings propagate-ttl=no` is set fleet-wide, so labeled IPv4 hides the core. IPv6 is forwarded natively (hop-limit decremented every hop). To get the same collapse, IPv6 must ride the existing IPv4 LSPs — either via **6PE** (RFC 4798: labeled-unicast IPv6 in the global table, BGP next-hop = IPv4 loopback) or **VPNv6** (IPv6 in a VRF over the v4 core). Both are unproven on this fleet, so this plan is **spike-first**: Phase 1 is a disposable lab that produces a hard go/no-go before any production router is touched.
|
||||
|
||||
**Tech Stack:** RouterOS 7.21.4 (8 tower routers, arm64/tile), RouterOS 6.49.18 (edge), MP-BGP, LDP, OSPFv2/v3, Proxmox + CHR for the lab.
|
||||
|
||||
---
|
||||
|
||||
## Why this plan is spike-first (read before doing anything)
|
||||
|
||||
Verified facts that make the production syntax **unknowable today**:
|
||||
|
||||
1. **ROS7 6PE is undocumented.** MikroTik's MPLS Overview page never names 6PE. Community testing (forum, v7.8 CHR) reports "mixed/uncertain" results. There is no authoritative config reference.
|
||||
2. **Possible hard conflict with the existing v6 iBGP.** The fleet already runs `afi=ipv6` iBGP (tower → core RR → edge) to carry the per-tower `/44` aggregates. A third-party note (netlab) states you *cannot run 6PE and plain IPv6 BGP-LU on the same node*. If that holds on ROS7, 6PE would **replace**, not augment, the current v6 iBGP — a much larger change.
|
||||
3. **edge is RouterOS 6.49.18.** It is the PE toward TWC (AS11427). ROS6's MPLS/BGP stack is entirely separate from ROS7's, and ROS6 6PE support is at least as questionable. edge may need to stay native-v6 (its single upstream hop is cosmetically irrelevant anyway).
|
||||
|
||||
Therefore: **no production router is reconfigured until Phase 1 proves the mechanism in a lab and answers conflict-question #2.** Phases 2–4 deliberately do not contain exact commands — they are filled in from the spike's findings (Task 1.7).
|
||||
|
||||
**If you are tempted to skip Phase 1: don't.** A half-working 6PE deployment black-holes customer IPv6 fleet-wide.
|
||||
|
||||
---
|
||||
|
||||
## Phase 1 — Lab spike (disposable, zero production impact)
|
||||
|
||||
Build a 3-router CHR lab on graham's home Proxmox that mirrors a slice of the
|
||||
backbone (`edge′ — core′ — verona′`) plus a CE host at each end, prove that
|
||||
labeled IPv6 collapses `traceroute6`, and determine whether 6PE coexists with
|
||||
plain IPv6 iBGP.
|
||||
|
||||
**Topology:**
|
||||
```
|
||||
[CE-A]──ce-a/v6──[verona′]──p2p v4 LDP──[core′]──p2p v4 LDP──[edge′]──ce-b/v6──[CE-B]
|
||||
2606:db8:a::/64 2606:db8:b::/64
|
||||
loopbacks: verona′ 10.0.0.1 core′ 10.0.0.2 edge′ 10.0.0.3 (v6: 2606:db8:0:10::1/2/3)
|
||||
```
|
||||
CE-A/CE-B can be CHRs or any Linux VM — they just need an IPv6 address and `traceroute6`.
|
||||
|
||||
### Task 1.1: Stand up the CHR lab VMs
|
||||
|
||||
**Files:**
|
||||
- Create: `docs/plans/lab/6pe-lab-notes.md` (running log of what works — this becomes the source for Phases 2–4)
|
||||
|
||||
**Step 1:** On Proxmox, import the RouterOS CHR image (https://mikrotik.com/download → "Cloud Hosted Router" → 7.21.4 stable VMDK/qcow2). Create 5 VMs: `verona-lab`, `core-lab`, `edge-lab`, `ce-a`, `ce-b`. 256 MB RAM / 1 vCPU each is plenty. CHR is licence-free at ≤1 Mbps — fine for a lab.
|
||||
|
||||
**Step 2:** Create 4 isolated Proxmox bridges (no uplink): `vmbr-cea`, `vmbr-vc` (verona′↔core′), `vmbr-ce` (core′↔edge′), `vmbr-ceb`. Wire NICs:
|
||||
- verona-lab: eth1→vmbr-cea, eth2→vmbr-vc
|
||||
- core-lab: eth1→vmbr-vc, eth2→vmbr-ce
|
||||
- edge-lab: eth1→vmbr-ce, eth2→vmbr-ceb
|
||||
- ce-a: eth1→vmbr-cea ; ce-b: eth1→vmbr-ceb
|
||||
|
||||
**Step 3:** Boot all 5. Confirm console login on each.
|
||||
|
||||
**Step 4 (commit):**
|
||||
```bash
|
||||
cd /Users/graham/dev/network
|
||||
git add docs/plans/lab/6pe-lab-notes.md
|
||||
git commit -m "6pe lab: stand up 5-VM CHR topology"
|
||||
```
|
||||
Expected: lab notes file committed with the topology + VM IDs recorded.
|
||||
|
||||
### Task 1.2: Build the IPv4 + LDP core (mirror of production)
|
||||
|
||||
**Step 1:** On each lab router configure loopback, p2p /30s, OSPFv2, LDP, and `propagate-ttl=no` — a scaled-down copy of the real fleet. Example for `core-lab` (adapt addresses per router):
|
||||
```rsc
|
||||
/interface bridge add name=lo
|
||||
/ip address add address=10.0.0.2/32 interface=lo
|
||||
/ip address add address=10.1.12.2/30 interface=ether1 ;; to verona′
|
||||
/ip address add address=10.1.23.1/30 interface=ether2 ;; to edge′
|
||||
/routing ospf instance add name=v2 router-id=10.0.0.2
|
||||
/routing ospf area add name=backbone instance=v2 area-id=0.0.0.0
|
||||
/routing ospf interface-template add area=backbone interfaces=lo passive
|
||||
/routing ospf interface-template add area=backbone interfaces=ether1,ether2 type=ptp
|
||||
/mpls ldp add lsr-id=10.0.0.2 transport-addresses=10.0.0.2
|
||||
/mpls ldp interface add interface=ether1
|
||||
/mpls ldp interface add interface=ether2
|
||||
/mpls settings set propagate-ttl=no
|
||||
```
|
||||
|
||||
**Step 2: Verify the v4 core.** From `verona-lab`:
|
||||
```rsc
|
||||
/routing ospf neighbor print ;; expect Full to core′
|
||||
/mpls ldp neighbor print ;; expect operational to core′
|
||||
/mpls forwarding-table print ;; expect labels for 10.0.0.2, 10.0.0.3
|
||||
/tool traceroute 10.0.0.3 ;; edge′ loopback — expect 1 hop (LDP + propagate-ttl=no)
|
||||
```
|
||||
Expected: traceroute to the far loopback shows **one hop**. This proves the lab reproduces the production v4 collapse. If it does not, fix the lab before continuing — Phase 1's whole value is a faithful mirror.
|
||||
|
||||
**Step 3 (commit):** Record the working core configs in `6pe-lab-notes.md`, commit.
|
||||
|
||||
### Task 1.3: Baseline — native IPv6, measure the problem
|
||||
|
||||
**Step 1:** Add native IPv6 + OSPFv3 across the lab core (loopbacks `2606:db8:0:10::1/2/3`, p2p `2606:db8:0:1N::/64`), and give CE-A/CE-B addresses in `2606:db8:a::/64` and `2606:db8:b::/64` with static routes or OSPFv3.
|
||||
|
||||
**Step 2: Measure baseline.** From CE-A:
|
||||
```
|
||||
traceroute6 2606:db8:b::1
|
||||
```
|
||||
Expected: **3+ hops** (verona′, core′, edge′ each appear). Record exact output in lab notes — this is the "before".
|
||||
|
||||
**Step 3 (commit):** Commit lab notes with the native-v6 baseline traceroute.
|
||||
|
||||
### Task 1.4: Add the existing-style IPv6 iBGP (reproduce the conflict surface)
|
||||
|
||||
**Step 1:** Configure v6 iBGP exactly like production: `verona-lab` and `edge-lab` as RR-clients of `core-lab`, `afi=ip,ipv6`, sessions over the v6 loopbacks, redistributing connected. This is the config that 6PE may conflict with.
|
||||
|
||||
**Step 2: Verify** v6 iBGP sessions establish and the CE prefixes are learned. Record in lab notes.
|
||||
|
||||
**Step 3 (commit).**
|
||||
|
||||
### Task 1.5: Attempt 6PE — the actual unknown
|
||||
|
||||
**This is the discovery task. Do not fabricate commands — find them.**
|
||||
|
||||
**Step 1:** Read, in this order, and capture findings in lab notes:
|
||||
- MikroTik MPLS Overview + BGP doc pages (help.mikrotik.com) — search for "label", "ipv6", "6PE", "labeled-unicast".
|
||||
- `/routing bgp connection` and `/routing bgp template` — dump every available property on a lab router (`/routing bgp connection print detail` on a connection with all afi values; try `afi=ipv6` plus any label-related sub-property the CLI tab-completes).
|
||||
- The MikroTik forum thread "IPv6-on-MPLS BGP-free Core?" for working/failing examples.
|
||||
|
||||
**Step 2:** On the lab, attempt 6PE: make the v6 iBGP advertise labeled IPv6 with BGP next-hop = the IPv4 loopback (as `::ffff:10.0.0.x`). Try it **alongside** the Task 1.4 plain v6 iBGP first.
|
||||
|
||||
**Step 3: Decision question A — coexistence.** Does enabling 6PE break or get rejected next to the plain `afi=ipv6` iBGP? Record the exact behaviour. If they cannot coexist, note that production Phase 3 must *replace* the v6 iBGP, and re-test 1.5 with the plain v6 iBGP removed.
|
||||
|
||||
**Step 4 (commit):** Lab notes updated with the exact 6PE config that loads without error (or the proof that it can't).
|
||||
|
||||
### Task 1.6: Verify the collapse (the success criterion)
|
||||
|
||||
**Step 1:** Confirm labels are bound to IPv6 routes:
|
||||
```rsc
|
||||
/mpls forwarding-table print ;; expect 2606:db8:b::/64 etc. with a label
|
||||
```
|
||||
|
||||
**Step 2:** From CE-A:
|
||||
```
|
||||
traceroute6 2606:db8:b::1
|
||||
```
|
||||
**Success = 1 hop across the lab core** (or core′ invisible), matching the v4 behaviour from Task 1.2.
|
||||
|
||||
**Step 3:** Failure-mode checks — confirm nothing else broke:
|
||||
- CE-A ↔ CE-B large-packet ping (`ping6 -s 1452`) — no new MTU loss (label adds 4 bytes; lab p2p MTU must have headroom, same as the real `mpls-mtu=1508`).
|
||||
- Kill the `core-lab`↔`edge-lab` link, confirm v6 reconverges (or fails predictably).
|
||||
|
||||
**Step 4 (commit):** Lab notes updated with the before/after traceroute and the failure-mode results.
|
||||
|
||||
### Task 1.7: Write the go/no-go and fill in Phases 2–4
|
||||
|
||||
**Step 1:** In `6pe-lab-notes.md`, write a **GO / NO-GO** verdict answering:
|
||||
- Does labeled IPv6 collapse `traceroute6` on ROS 7.21.4? (Task 1.6)
|
||||
- Can 6PE coexist with the existing v6 iBGP, or must it replace it? (Task 1.5 Step 3)
|
||||
- What is the **exact, tested** per-router 6PE config delta?
|
||||
- Does anything require an MTU change beyond the existing `mpls-mtu=1508`?
|
||||
|
||||
**Step 2:** If **GO**: replace the "TBD pending spike" bodies of Phase 2 and Phase 3 below with the exact tested commands from the lab. If **NO-GO**: stop here, jump to "Alternatives", and bring the recommendation to graham.
|
||||
|
||||
**Step 3 (commit):**
|
||||
```bash
|
||||
git add docs/plans/lab/6pe-lab-notes.md docs/plans/2026-05-14-ipv6-traceroute-collapse-6pe.md
|
||||
git commit -m "6pe lab: go/no-go verdict + fill production phases"
|
||||
```
|
||||
|
||||
### Task 1.8: Tear down the lab
|
||||
|
||||
**Step 1:** Delete the 5 CHR VMs and 4 Proxmox bridges. The lab notes (committed) are the deliverable; the VMs are disposable.
|
||||
|
||||
**Step 2 (commit):** Note teardown in lab notes, commit.
|
||||
|
||||
---
|
||||
|
||||
## Phase 2 — Production pilot: verona ↔ climax ↔ core *(contingent on Phase 1 = GO)*
|
||||
|
||||
**Body TBD — populated from Task 1.7.** Constraints the filled-in version must honour:
|
||||
|
||||
- **Apply order matters.** Bring up the labeled-v6 path core-first, then climax, then verona, so a tower is never advertising labeled v6 toward a router that can't resolve it.
|
||||
- **One change window, one link triangle.** Pilot only verona/climax/core; do not touch the other 5 towers until the pilot soaks ≥24h.
|
||||
- **Each router task = change → verify → rollback-ready**, e.g.:
|
||||
- apply 6PE delta on core
|
||||
- `/mpls forwarding-table print` shows labels for the pilot towers' v6
|
||||
- `/routing bgp advertisements` / `/routing route print` confirms labeled v6 NLRI
|
||||
- from graham's home Mac: `traceroute6` to an off-net v6 host — core no longer a distinct hop
|
||||
- `ping6 -s 1452` to `2606:4700:4700::1111` still 0% loss
|
||||
- customer-facing: a known v6 PPPoE/wired client still reaches the internet
|
||||
- **Rollback (per router):** the inverse delta, recorded in Task 1.7. Native v6 + OSPFv3 + the existing v6 iBGP must still be present underneath (do not delete them in the pilot — if 6PE coexists, native is the fallback; if it doesn't, Phase 2 rollback = re-add v6 iBGP).
|
||||
- **Soak gate:** 24h with no v6 customer complaints + traceroute stays collapsed before Phase 3.
|
||||
|
||||
## Phase 3 — Fleet rollout: culleoka, newhope, lowry, 982, 494 *(contingent)*
|
||||
|
||||
**Body TBD — populated from Task 1.7.** Constraints:
|
||||
|
||||
- Roll out in the CGNAT-renumber order from `subnets.md` (one tower per change window).
|
||||
- Same per-router verify set as Phase 2.
|
||||
- newhope carries lowry's transit — do newhope before lowry.
|
||||
- After each tower: `traceroute6` from home Mac still collapsed, customer v6 spot-check.
|
||||
- Keep `mikrotik-tool` (`api`/`script`) as the push mechanism; one `.rsc` per router or a templated apply.
|
||||
|
||||
## Phase 4 — edge (RouterOS 6.49.18) *(contingent, likely "leave native")*
|
||||
|
||||
**Body TBD.** Default expectation from Phase 1 framing point #3: **edge stays native IPv6.** edge is one hop from core and one hop from TWC — collapsing it changes nothing a user sees. Only revisit if Task 1.7 specifically finds ROS6 6PE both works and is needed. If edge stays native, document that the v6 path is "labeled across the ROS7 core, native for the edge↔TWC hop" so the next operator isn't surprised.
|
||||
|
||||
---
|
||||
|
||||
## Rollback (whole project)
|
||||
|
||||
- **Phase 1:** delete VMs. Nothing to roll back.
|
||||
- **Phase 2/3:** per-router inverse delta from Task 1.7, applied in reverse order (towers before core). Native v6 forwarding + OSPFv3 are never removed during pilot/rollout, so the floor is always "back to today's working native v6."
|
||||
- **Trigger:** any v6 customer outage, `traceroute6` black-holing (not just collapsing), or MTU loss that won't resolve.
|
||||
- The firewall, DNS, and home-router work already done (forward `accept src 2606:1c80::/32`, `v6-dns` resolvers, home `/56`) are **independent of this plan** and are not touched by any phase.
|
||||
|
||||
---
|
||||
|
||||
## Alternatives (if Phase 1 = NO-GO)
|
||||
|
||||
| Option | What it gives | Cost / risk |
|
||||
|---|---|---|
|
||||
| **Do nothing** | v6 traceroute keeps showing the core (internal hops currently appear as `*` — they don't even reply). Everything works. | Zero. The collapse is cosmetic. |
|
||||
| **VPNv6** | IPv6 in a VRF over the v4 core — *is* labeled, so traceroute collapses. ROS7 documents `vpnv6`. | Large: every router's customer v6 (addresses, pools, PPP profiles, firewall, the home `/56`) moves into a VRF. Much bigger blast radius than 6PE. |
|
||||
| **Make internal hops reply instead of hiding them** | Opposite of the goal, but "honest" traceroute — each router answers with ICMPv6 time-exceeded. | Small `chain=input` tweak. Only relevant if the real annoyance is the `* * *` lines, not the hop count. |
|
||||
|
||||
**Recommendation if NO-GO:** do nothing. The IPv4 collapse is a side-effect of MPLS existing for real routing reasons; chasing the same cosmetics for IPv6 via a VRF migration is not worth the blast radius. Revisit only if MikroTik documents real 6PE support in a later ROS7 release.
|
||||
|
||||
---
|
||||
|
||||
## Open questions for graham (resolve before Phase 2, ideally before Phase 1)
|
||||
|
||||
1. Is collapsing v6 traceroute worth a multi-window backbone change, or is it "nice to have"? (Sets how hard to push past a marginal Phase 1 result.)
|
||||
2. Proxmox capacity at home for 5 throwaway CHR VMs — OK to spin up?
|
||||
3. edge's eventual ROS6→ROS7 upgrade — if that's on the roadmap anyway, Phase 4 should wait for it rather than attempt ROS6 6PE.
|
||||
3
gaiia/.direnv
Normal file
3
gaiia/.direnv
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
export GAIIA_KEY=gaiia_sk_live_955b27e3680747a69ad40b403a912155_b0d1be3c
|
||||
export EXPIRE_WF_ID=workflow_gbL1BTRA5Xt4WkMy2uRLdW
|
||||
|
||||
2
gaiia/.envrc
Normal file
2
gaiia/.envrc
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
export GAIIA_KEY=gaiia_sk_live_955b27e3680747a69ad40b403a912155_b0d1be3c
|
||||
|
||||
17
gaiia/.gitignore
vendored
Normal file
17
gaiia/.gitignore
vendored
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
__pycache__/
|
||||
*.py[cod]
|
||||
*.egg-info/
|
||||
.eggs/
|
||||
build/
|
||||
dist/
|
||||
.venv/
|
||||
venv/
|
||||
.env
|
||||
.envrc.local
|
||||
.pytest_cache/
|
||||
.mypy_cache/
|
||||
.ruff_cache/
|
||||
.coverage
|
||||
htmlcov/
|
||||
*.swp
|
||||
.DS_Store
|
||||
21
gaiia/LICENSE
Normal file
21
gaiia/LICENSE
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
MIT License
|
||||
|
||||
Copyright (c) 2026 Graham McIntire
|
||||
|
||||
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.
|
||||
198
gaiia/README.md
Normal file
198
gaiia/README.md
Normal file
|
|
@ -0,0 +1,198 @@
|
|||
# gaiia-py
|
||||
|
||||
Python client for the [gaiia](https://app.gaiia.com/docs/overview) GraphQL API
|
||||
and webhooks. Thin wrapper around the official GraphQL surface — no codegen, no
|
||||
opinionated ORM. You write GraphQL, the client handles auth, pagination, rate
|
||||
limits, GlobalID encoding, and webhook signature verification.
|
||||
|
||||
## Install
|
||||
|
||||
```bash
|
||||
pip install gaiia
|
||||
```
|
||||
|
||||
Requires Python 3.10+. Depends on `httpx`.
|
||||
|
||||
## Quick start
|
||||
|
||||
```python
|
||||
from gaiia import GaiiaClient
|
||||
|
||||
with GaiiaClient(api_key="gaiia_sk_live_...", timezone="America/Chicago") as g:
|
||||
data = g.query(
|
||||
"query Q($first: Int!) { technicians(first: $first) { nodes { id firstName lastName } } }",
|
||||
{"first": 10},
|
||||
)
|
||||
for t in data["technicians"]["nodes"]:
|
||||
print(t)
|
||||
```
|
||||
|
||||
If `api_key=` is omitted, the client reads `GAIIA_KEY` from the environment.
|
||||
|
||||
## Mutations
|
||||
|
||||
The `mutate()` helper unwraps the mutation result and raises
|
||||
`GaiiaMutationError` when the response contains validation errors under
|
||||
`data.<mutation>.errors`:
|
||||
|
||||
```python
|
||||
res = g.mutate(
|
||||
"""
|
||||
mutation A($i: AssignTechnicianToWorkOrderInput!) {
|
||||
assignTechnicianToWorkOrder(input: $i) {
|
||||
workOrder { id status startDate }
|
||||
errors { code message }
|
||||
}
|
||||
}
|
||||
""",
|
||||
"assignTechnicianToWorkOrder",
|
||||
{"i": {
|
||||
"workOrderId": "work_order_...",
|
||||
"technicianId": "technician_...",
|
||||
"startDate": "2026-05-12T14:00:00Z",
|
||||
}},
|
||||
)
|
||||
print(res["workOrder"]["id"])
|
||||
```
|
||||
|
||||
## Pagination
|
||||
|
||||
`iter_nodes()` paginates a Relay Connection automatically. Your query must
|
||||
accept an `$after: String` variable and pass it to the paginated field:
|
||||
|
||||
```python
|
||||
for tech in g.iter_nodes(
|
||||
"""
|
||||
query Q($after: String) {
|
||||
technicians(first: 50, after: $after) {
|
||||
nodes { id firstName lastName }
|
||||
pageInfo { hasNextPage endCursor }
|
||||
}
|
||||
}
|
||||
""",
|
||||
"technicians",
|
||||
):
|
||||
print(tech["firstName"], tech["lastName"])
|
||||
```
|
||||
|
||||
Default page size is 50, max 250 (per the gaiia docs). Some fields cap lower —
|
||||
check the field's reference page.
|
||||
|
||||
For bulk extracts (millions of rows, reporting, analytics), gaiia recommends
|
||||
[Snowflake](https://app.gaiia.com/docs/rate-limits) rather than the GraphQL
|
||||
API.
|
||||
|
||||
## Rate limiting
|
||||
|
||||
After every call, `client.last_rate_limit` is populated from the response
|
||||
headers (`X-Rate-Limit-*`). If gaiia returns a `RATE_LIMITED` error, the
|
||||
client raises `GaiiaRateLimitedError` with `limit`, `used`, `remaining`, and
|
||||
`retry_at`:
|
||||
|
||||
```python
|
||||
from gaiia import GaiiaClient, GaiiaRateLimitedError
|
||||
|
||||
with GaiiaClient(max_retries=2) as g:
|
||||
try:
|
||||
g.query("{ ... }")
|
||||
except GaiiaRateLimitedError as e:
|
||||
print("retry after", e.retry_at)
|
||||
```
|
||||
|
||||
Setting `max_retries` > 0 makes the client sleep until `retry_at` and retry up
|
||||
to that many times before re-raising.
|
||||
|
||||
## GlobalIDs
|
||||
|
||||
```python
|
||||
from gaiia import encode_global_id, decode_global_id
|
||||
|
||||
encode_global_id("Account", "3c3b1978-6a68-4a13-bdc2-2d51c8ef7519")
|
||||
# 'account_8rnXNuR5sKP5uNwoPL41Zp'
|
||||
|
||||
decode_global_id("account_8rnXNuR5sKP5uNwoPL41Zp")
|
||||
# ('account', '3c3b1978-6a68-4a13-bdc2-2d51c8ef7519')
|
||||
```
|
||||
|
||||
The encoding follows gaiia's `short-uuid` setup: Flickr Base-58 of the UUID's
|
||||
128-bit integer, left-padded to 22 chars, prefixed with the snake-cased type.
|
||||
|
||||
## Webhooks
|
||||
|
||||
```python
|
||||
from gaiia import verify_webhook_signature, WebhookSignatureError
|
||||
|
||||
try:
|
||||
verify_webhook_signature(
|
||||
header_value=request.headers["X-Gaiia-Webhook-Signature"],
|
||||
raw_body=request.body_bytes, # the exact bytes gaiia signed
|
||||
secret=os.environ["GAIIA_WEBHOOK_SECRET"],
|
||||
tolerance_seconds=300,
|
||||
)
|
||||
except WebhookSignatureError:
|
||||
return 400
|
||||
```
|
||||
|
||||
The verifier:
|
||||
|
||||
- Parses `t=...,v1=...` (multiple `v1=` allowed for key rotation),
|
||||
- Computes `HMAC-SHA256(secret, "${t}.${rawBody}")`,
|
||||
- Constant-time compares against each `v1=` candidate,
|
||||
- Rejects timestamps outside the tolerance window.
|
||||
|
||||
See `examples/webhook_flask.py` for a complete handler.
|
||||
|
||||
## Async
|
||||
|
||||
`AsyncGaiiaClient` mirrors `GaiiaClient` for `asyncio` code:
|
||||
|
||||
```python
|
||||
import asyncio
|
||||
from gaiia import AsyncGaiiaClient
|
||||
|
||||
async def main():
|
||||
async with AsyncGaiiaClient(timezone="America/Chicago") as g:
|
||||
data = await g.query("{ technicians(first: 5) { nodes { id firstName lastName } } }")
|
||||
print(data)
|
||||
|
||||
asyncio.run(main())
|
||||
```
|
||||
|
||||
`async for` over `iter_nodes()` paginates lazily.
|
||||
|
||||
## Timezones
|
||||
|
||||
gaiia stores all timestamps in UTC. Pass `timezone="America/Chicago"` (or
|
||||
whatever IANA TZ applies) at the client level — it becomes the `x-timezone`
|
||||
header on every request, which gaiia uses for scheduling/availability logic.
|
||||
Override per-call with `g.query(..., timezone="America/Phoenix")`.
|
||||
|
||||
For display, query the work order's `location.timezone` and format times in
|
||||
that zone — not the caller's.
|
||||
|
||||
## Error model
|
||||
|
||||
| Class | When it's raised |
|
||||
| ------------------------ | ---------------------------------------------------------------------- |
|
||||
| `GaiiaTransportError` | Network failure, non-JSON response, HTTP 5xx. |
|
||||
| `GaiiaAPIError` | Top-level GraphQL `errors[]` returned (schema/auth/permission). |
|
||||
| `GaiiaRateLimitedError` | Subclass of `GaiiaAPIError` for `RATE_LIMITED`. |
|
||||
| `GaiiaMutationError` | Mutation `data.<m>.errors[]` is non-empty (validation/business rule). |
|
||||
|
||||
All inherit from `GaiiaError`.
|
||||
|
||||
## Development
|
||||
|
||||
```bash
|
||||
git clone https://github.com/grahammcintire/gaiia-py
|
||||
cd gaiia-py
|
||||
python -m venv .venv && source .venv/bin/activate
|
||||
pip install -e ".[dev]"
|
||||
pytest
|
||||
ruff check .
|
||||
mypy src/gaiia
|
||||
```
|
||||
|
||||
## License
|
||||
|
||||
MIT.
|
||||
116
gaiia/account_status_suspension_workflow.json
Normal file
116
gaiia/account_status_suspension_workflow.json
Normal file
|
|
@ -0,0 +1,116 @@
|
|||
{
|
||||
"name": "Account Status - RADIUS Suspension",
|
||||
"description": "EVENT: When account status changes to SUSPENDED or INACTIVE, update RADIUS usergroup to suspended. When it returns to ACTIVE, restore normal groups.",
|
||||
"trigger": {
|
||||
"type": "EVENT",
|
||||
"events": ["account.status_updated"],
|
||||
"nextSteps": [{ "slug": "get-account" }]
|
||||
},
|
||||
"steps": [
|
||||
{
|
||||
"name": "Get account status and products",
|
||||
"slug": "get-account",
|
||||
"type": "GAIIA_API_GATEWAY_V1_GRAPHQL_REQUEST",
|
||||
"nodeSlug": "gaiia-public-api-gateway-graphql-request",
|
||||
"allowSkipExecution": false,
|
||||
"outputMapper": "({ output, state }) => {\n const acct = output.response.account;\n if (!acct) {\n workflow.fail('Account not found', 'ACCOUNT_NOT_FOUND');\n }\n const status = acct.status?.name?.toUpperCase() || '';\n const products = [];\n for (const sub of (acct.billingSubscriptions?.nodes || [])) {\n const p = sub.productVersion?.product;\n if (p && !products.includes(p.id)) {\n products.push(p.id);\n }\n }\n return { status, products, accountId: acct.id };\n}",
|
||||
"inputs": [
|
||||
{
|
||||
"key": "query",
|
||||
"type": "LITERAL",
|
||||
"value": "string:query GetAccount($id: GlobalID!) {\n account(id: $id) {\n id\n status { name }\n billingSubscriptions {\n nodes {\n productVersion {\n product { id name }\n }\n }\n }\n }\n}"
|
||||
},
|
||||
{
|
||||
"key": "variables",
|
||||
"type": "FUNCTION",
|
||||
"value": "({ state }) => ({ id: state.input.objectId })"
|
||||
}
|
||||
],
|
||||
"nextSteps": [{ "slug": "check-radius-exists" }]
|
||||
},
|
||||
{
|
||||
"name": "Check if account has RADIUS usernames",
|
||||
"slug": "check-radius-exists",
|
||||
"type": "TYPESCRIPT_CODE",
|
||||
"nodeSlug": "execute-typescript-code",
|
||||
"allowSkipExecution": false,
|
||||
"outputMapper": "({ output, state }) => {\n const acct = state.steps['get-account'].output;\n acct.hasRadius = output && output.length > 0;\n return acct;\n}",
|
||||
"inputs": [
|
||||
{
|
||||
"key": "function",
|
||||
"type": "FUNCTION",
|
||||
"value": "async ({ state, utils }) => {\n const { accountId } = state.steps['get-account'].output;\n const existingData = await utils.integration.datastore.getItem(`radiusAccount.${accountId}`);\n const accounts = existingData ? JSON.parse(existingData) : [];\n return accounts;\n}"
|
||||
}
|
||||
],
|
||||
"nextSteps": [{ "slug": "should-suspend" }]
|
||||
},
|
||||
{
|
||||
"name": "Should suspend?",
|
||||
"slug": "should-suspend",
|
||||
"type": "CONDITION",
|
||||
"nodeSlug": "condition",
|
||||
"condition": "({ secrets, state, utils, variables }) => {\n const { status, hasRadius } = state.steps['check-radius-exists'].output;\n return hasRadius && (status === 'SUSPENDED' || status === 'INACTIVE');\n}",
|
||||
"elseSteps": [{ "slug": "should-reactivate" }],
|
||||
"thenSteps": [{ "slug": "update-groups-suspended" }]
|
||||
},
|
||||
{
|
||||
"name": "Update RADIUS groups to SUSPENDED",
|
||||
"slug": "update-groups-suspended",
|
||||
"type": "GAIIA_START_WORKFLOW_EXECUTION",
|
||||
"nodeSlug": "start-workflow-execution",
|
||||
"allowSkipExecution": false,
|
||||
"inputs": [
|
||||
{
|
||||
"key": "workflowId",
|
||||
"type": "LITERAL",
|
||||
"value": "string:workflow_fLXTNCvridPqwEEXxf3CK1"
|
||||
},
|
||||
{
|
||||
"key": "input",
|
||||
"type": "FUNCTION",
|
||||
"value": "({ state }) => {\n const { accountId, products, status } = state.steps['check-radius-exists'].output;\n return {\n accountId,\n status,\n productIds: products\n };\n}"
|
||||
},
|
||||
{
|
||||
"key": "fireAndForget",
|
||||
"type": "LITERAL",
|
||||
"value": "boolean:true"
|
||||
}
|
||||
],
|
||||
"nextSteps": [{ "slug": "end" }]
|
||||
},
|
||||
{
|
||||
"name": "Should reactivate?",
|
||||
"slug": "should-reactivate",
|
||||
"type": "CONDITION",
|
||||
"nodeSlug": "condition",
|
||||
"condition": "({ secrets, state, utils, variables }) => {\n const { status, hasRadius } = state.steps['check-radius-exists'].output;\n return hasRadius && status === 'ACTIVE';\n}",
|
||||
"elseSteps": [{ "slug": "end" }],
|
||||
"thenSteps": [{ "slug": "update-groups-active" }]
|
||||
},
|
||||
{
|
||||
"name": "Update RADIUS groups to ACTIVE",
|
||||
"slug": "update-groups-active",
|
||||
"type": "GAIIA_START_WORKFLOW_EXECUTION",
|
||||
"nodeSlug": "start-workflow-execution",
|
||||
"allowSkipExecution": false,
|
||||
"inputs": [
|
||||
{
|
||||
"key": "workflowId",
|
||||
"type": "LITERAL",
|
||||
"value": "string:workflow_fLXTNCvridPqwEEXxf3CK1"
|
||||
},
|
||||
{
|
||||
"key": "input",
|
||||
"type": "FUNCTION",
|
||||
"value": "({ state }) => {\n const { accountId, products } = state.steps['check-radius-exists'].output;\n return {\n accountId,\n status: 'ACTIVE',\n productIds: products\n };\n}"
|
||||
},
|
||||
{
|
||||
"key": "fireAndForget",
|
||||
"type": "LITERAL",
|
||||
"value": "boolean:true"
|
||||
}
|
||||
],
|
||||
"nextSteps": [{ "slug": "end" }]
|
||||
}
|
||||
]
|
||||
}
|
||||
78
gaiia/cleanup_stale_byod_workflow.json
Normal file
78
gaiia/cleanup_stale_byod_workflow.json
Normal file
|
|
@ -0,0 +1,78 @@
|
|||
{
|
||||
"name": "Cleanup Stale BYOD Items",
|
||||
"description": "SCHEDULED (nightly 2am): Unassigns Customer Provided Router items that have no active IP assignment. Keeps items with active IPs intact.",
|
||||
"trigger": {
|
||||
"type": "SCHEDULE",
|
||||
"cron": "0 2 * * *",
|
||||
"nextSteps": [{ "slug": "find-stale-byod" }]
|
||||
},
|
||||
"steps": [
|
||||
{
|
||||
"name": "Find stale BYOD items",
|
||||
"slug": "find-stale-byod",
|
||||
"type": "GAIIA_API_GATEWAY_V1_GRAPHQL_REQUEST",
|
||||
"nodeSlug": "gaiia-public-api-gateway-graphql-request",
|
||||
"allowSkipExecution": false,
|
||||
"outputMapper": "({ output }) => {\n const items = output.response.inventoryItems.nodes;\n const stale = [];\n for (const item of items) {\n const assigns = item.ipAddressAssignments?.nodes || [];\n const hasActive = assigns.some((a) => a.isActive !== false);\n if (!hasActive) {\n const assignee = item.assignation?.assignee || {};\n if (assignee.__typename === 'Account' && assignee.id) {\n stale.push({ inventoryId: item.id, accountId: assignee.id, accountName: assignee.name || '' });\n }\n }\n }\n return stale;\n}",
|
||||
"inputs": [
|
||||
{
|
||||
"key": "query",
|
||||
"type": "LITERAL",
|
||||
"value": "string:query {\n inventoryItems(\n first: 250\n filters: { modelId: { equals: \"inventory_model_522SxVU9VvPaMXYqWNbfbx\" }, assigneeType: { equals: ACCOUNT } }\n ) {\n nodes {\n id\n ipAddressAssignments {\n nodes { ipAddress macAddress isActive }\n }\n assignation {\n assignee { __typename ... on Account { id name } }\n }\n }\n totalCount\n }\n}"
|
||||
},
|
||||
{
|
||||
"key": "variables",
|
||||
"type": "FUNCTION",
|
||||
"value": "({ secrets, state, utils, variables }) => ({})"
|
||||
}
|
||||
],
|
||||
"nextSteps": [{ "slug": "if-any-stale" }]
|
||||
},
|
||||
{
|
||||
"name": "Any stale items?",
|
||||
"slug": "if-any-stale",
|
||||
"type": "CONDITION",
|
||||
"nodeSlug": "condition",
|
||||
"condition": "({ secrets, state, utils, variables }) => {\n const items = state.steps['find-stale-byod'].output;\n return Array.isArray(items) && items.length > 0;\n}",
|
||||
"elseSteps": [{ "slug": "end" }],
|
||||
"thenSteps": [{ "slug": "unassign-stale" }]
|
||||
},
|
||||
{
|
||||
"name": "Unassign each stale item",
|
||||
"slug": "unassign-stale",
|
||||
"type": "PARALLEL_LOOP",
|
||||
"nodeSlug": "parallel-loop",
|
||||
"maxConcurrentIterations": 10,
|
||||
"inputs": [
|
||||
{
|
||||
"key": "iterable",
|
||||
"type": "FUNCTION",
|
||||
"value": "({ state }) => state.steps['find-stale-byod'].output"
|
||||
}
|
||||
],
|
||||
"initialSteps": [{ "slug": "unassign-one" }],
|
||||
"nextSteps": [{ "slug": "end" }]
|
||||
},
|
||||
{
|
||||
"name": "Unassign from account",
|
||||
"slug": "unassign-one",
|
||||
"type": "GAIIA_API_GATEWAY_V1_GRAPHQL_REQUEST",
|
||||
"nodeSlug": "gaiia-public-api-gateway-graphql-request",
|
||||
"parentParallelLoopSlug": "unassign-stale",
|
||||
"allowSkipExecution": false,
|
||||
"inputs": [
|
||||
{
|
||||
"key": "query",
|
||||
"type": "LITERAL",
|
||||
"value": "string:mutation UnassignItem($input: UnassignCustomerOwnedItemInput!) {\n unassignCustomerOwnedItem(input: $input) {\n customerOwnedItem { id }\n }\n}"
|
||||
},
|
||||
{
|
||||
"key": "variables",
|
||||
"type": "FUNCTION",
|
||||
"value": "({ state }) => {\n const item = state.currentParallelLoopIterations['unassign-stale'];\n return { input: { id: item.inventoryId } };\n}"
|
||||
}
|
||||
],
|
||||
"nextSteps": [{ "slug": "unassign-stale-end" }]
|
||||
}
|
||||
]
|
||||
}
|
||||
143
gaiia/clear_all_ips.py
Normal file
143
gaiia/clear_all_ips.py
Normal file
|
|
@ -0,0 +1,143 @@
|
|||
#!/usr/bin/env python3
|
||||
"""
|
||||
Clear all MAC->IP address assignments from customer inventory items.
|
||||
|
||||
Two-phase approach:
|
||||
1. List all active IP assignments via paginated V1 public API
|
||||
2. Expire each one via startWorkflowExecution mutation
|
||||
|
||||
Before running:
|
||||
1. Create the "Expire Single IP Assignment" workflow in Gaiia UI
|
||||
(use expire_one_workflow.json)
|
||||
2. Copy its workflow ID and set EXPIRE_WF_ID below
|
||||
3. source .envrc
|
||||
4. uv run python3 clear_all_ips.py --dry-run (preview)
|
||||
5. uv run python3 clear_all_ips.py (execute)
|
||||
"""
|
||||
|
||||
from gaiia import GaiiaClient, GaiiaMutationError
|
||||
import json
|
||||
import os
|
||||
import sys
|
||||
import time
|
||||
|
||||
EXPIRE_WF_ID = os.environ.get("EXPIRE_WF_ID", "")
|
||||
|
||||
def list_all_assignments(client):
|
||||
"""Paginate through all inventory items and return active MAC->IP pairs."""
|
||||
all_assignments = []
|
||||
after = None
|
||||
page = 0
|
||||
|
||||
while True:
|
||||
page += 1
|
||||
query = """
|
||||
query Q($after: String) {
|
||||
inventoryItems(
|
||||
filters: {assigneeType: {equals: ACCOUNT}}
|
||||
first: 250
|
||||
after: $after
|
||||
) {
|
||||
nodes {
|
||||
ipAddressAssignments {
|
||||
nodes { ipAddress macAddress isActive }
|
||||
}
|
||||
}
|
||||
pageInfo { hasNextPage endCursor }
|
||||
totalCount
|
||||
}
|
||||
}
|
||||
"""
|
||||
data = client.query(query, {"after": after} if after else {})
|
||||
result = data['inventoryItems']
|
||||
|
||||
if page == 1:
|
||||
print(f"Total inventory items: {result['totalCount']}")
|
||||
|
||||
for item in result['nodes']:
|
||||
for a in item.get('ipAddressAssignments', {}).get('nodes', []):
|
||||
if a.get('isActive') != False:
|
||||
all_assignments.append({
|
||||
'macAddress': a['macAddress'],
|
||||
'ipAddress': a['ipAddress']
|
||||
})
|
||||
|
||||
page_info = result['pageInfo']
|
||||
sys.stderr.write(f"\r Page {page}: {len(all_assignments)} assignments so far...")
|
||||
sys.stderr.flush()
|
||||
|
||||
if not page_info.get('hasNextPage'):
|
||||
break
|
||||
after = page_info['endCursor']
|
||||
|
||||
sys.stderr.write("\n")
|
||||
return all_assignments
|
||||
|
||||
|
||||
def expire_one(client, mac_address, ip_address, dry_run=False):
|
||||
"""Call the Expire Single IP Assignment workflow via startWorkflowExecution."""
|
||||
if dry_run:
|
||||
print(f" [DRY-RUN] {mac_address} -> {ip_address}")
|
||||
return True
|
||||
|
||||
mutation = """
|
||||
mutation Expire($input: StartWorkflowExecutionInput!) {
|
||||
startWorkflowExecution(input: $input) {
|
||||
workflowExecution { id status }
|
||||
}
|
||||
}
|
||||
"""
|
||||
try:
|
||||
client.query(mutation, {
|
||||
"input": {
|
||||
"workflowId": EXPIRE_WF_ID,
|
||||
"input": {"macAddress": mac_address, "ipAddress": ip_address}
|
||||
}
|
||||
})
|
||||
return True
|
||||
except GaiiaMutationError as e:
|
||||
print(f" FAILED {mac_address} -> {ip_address}: {e}")
|
||||
return False
|
||||
|
||||
|
||||
def main():
|
||||
if not EXPIRE_WF_ID:
|
||||
print("ERROR: Set EXPIRE_WF_ID env var to the workflow ID of 'Expire Single IP Assignment'")
|
||||
print("Example: EXPIRE_WF_ID=workflow_xxx uv run python3 clear_all_ips.py --dry-run")
|
||||
sys.exit(1)
|
||||
|
||||
dry_run = "--dry-run" in sys.argv
|
||||
|
||||
with GaiiaClient(timezone='America/Chicago') as g:
|
||||
print("Listing all active IP assignments...")
|
||||
assignments = list_all_assignments(g)
|
||||
print(f"\nFound {len(assignments)} active IP assignments")
|
||||
|
||||
if dry_run:
|
||||
print("\n--dry-run mode: showing what would be expired --")
|
||||
else:
|
||||
print(f"\nExpiring via workflow {EXPIRE_WF_ID}...")
|
||||
|
||||
succeeded = 0
|
||||
failed = 0
|
||||
for i, a in enumerate(assignments):
|
||||
if not dry_run:
|
||||
sys.stderr.write(f"\r {i+1}/{len(assignments)}: {a['macAddress']} -> {a['ipAddress']}")
|
||||
sys.stderr.flush()
|
||||
if expire_one(g, a['macAddress'], a['ipAddress'], dry_run):
|
||||
succeeded += 1
|
||||
else:
|
||||
failed += 1
|
||||
if not dry_run:
|
||||
time.sleep(0.1) # avoid rate limiting
|
||||
|
||||
if not dry_run:
|
||||
sys.stderr.write("\n")
|
||||
|
||||
print(f"\nDone: {succeeded} succeeded, {failed} failed")
|
||||
if dry_run:
|
||||
print("(nothing was changed — remove --dry-run to execute)")
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
85
gaiia/clear_ip_assignments_workflow.json
Normal file
85
gaiia/clear_ip_assignments_workflow.json
Normal file
|
|
@ -0,0 +1,85 @@
|
|||
{
|
||||
"name": "Clear All IP Assignments",
|
||||
"description": "ONE-SHOT: Expires all MAC→IP address assignments across all customer inventory items. Run once, then delete this workflow.",
|
||||
"trigger": {
|
||||
"type": "MANUAL",
|
||||
"nextSteps": [
|
||||
{ "slug": "find-assigned-items" }
|
||||
]
|
||||
},
|
||||
"steps": [
|
||||
{
|
||||
"name": "Find all inventory items assigned to accounts",
|
||||
"slug": "find-assigned-items",
|
||||
"type": "GAIIA_API_GATEWAY_V1_GRAPHQL_REQUEST",
|
||||
"nodeSlug": "gaiia-public-api-gateway-graphql-request",
|
||||
"allowSkipExecution": false,
|
||||
"outputMapper": "({ output }) => {\n const items = output.response.inventoryItems.nodes;\n const withIps = [];\n for (const item of items) {\n const assignments = item.ipAddressAssignments?.nodes || [];\n for (const a of assignments) {\n if (a.isActive !== false) {\n withIps.push({\n macAddress: a.macAddress,\n ipAddress: a.ipAddress\n });\n }\n }\n }\n return withIps;\n}",
|
||||
"inputs": [
|
||||
{
|
||||
"key": "query",
|
||||
"type": "LITERAL",
|
||||
"value": "string:query AllAssignedItems($filters: InventoryItemsQueryFilters, $first: Int, $after: String) {\n inventoryItems(filters: $filters, first: $first, after: $after) {\n nodes {\n ipAddressAssignments {\n nodes {\n ipAddress\n macAddress\n isActive\n }\n }\n }\n pageInfo { hasNextPage endCursor }\n totalCount\n }\n}"
|
||||
},
|
||||
{
|
||||
"key": "variables",
|
||||
"type": "LITERAL",
|
||||
"value": "json:{\"filters\":{\"assigneeType\":{\"equals\":\"ACCOUNT\"}},\"first\":250}"
|
||||
}
|
||||
],
|
||||
"nextSteps": [
|
||||
{ "slug": "if-any-assignments" }
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Any assignments to clear?",
|
||||
"slug": "if-any-assignments",
|
||||
"type": "CONDITION",
|
||||
"nodeSlug": "condition",
|
||||
"condition": "({ secrets, state, utils, variables }) => {\n const items = state.steps['find-assigned-items'].output;\n return items && items.length > 0;\n}",
|
||||
"elseSteps": [{ "slug": "end" }],
|
||||
"thenSteps": [{ "slug": "expire-assignments" }]
|
||||
},
|
||||
{
|
||||
"name": "Expire each assignment",
|
||||
"slug": "expire-assignments",
|
||||
"type": "PARALLEL_LOOP",
|
||||
"nodeSlug": "parallel-loop",
|
||||
"maxConcurrentIterations": 10,
|
||||
"inputs": [
|
||||
{
|
||||
"key": "iterable",
|
||||
"type": "FUNCTION",
|
||||
"value": "({ state }) => state.steps['find-assigned-items'].output"
|
||||
}
|
||||
],
|
||||
"initialSteps": [
|
||||
{ "slug": "expire-one" }
|
||||
],
|
||||
"nextSteps": [{ "slug": "end" }]
|
||||
},
|
||||
{
|
||||
"name": "Expire single assignment",
|
||||
"slug": "expire-one",
|
||||
"type": "GAIIA_API_GATEWAY_V0_GRAPHQL_REQUEST",
|
||||
"nodeSlug": "gaiia-internal-api-gateway-graphql-request",
|
||||
"parentParallelLoopSlug": "expire-assignments",
|
||||
"allowSkipExecution": false,
|
||||
"inputs": [
|
||||
{
|
||||
"key": "query",
|
||||
"type": "LITERAL",
|
||||
"value": "string:mutation ExpireAssignation($input: UpsertMacAddressIpAddressAssignationsInput!) {\n upsertMacAddressIpAddressAssignations(input: $input) {\n assignations {\n macAddress\n ipAddress\n }\n }\n}"
|
||||
},
|
||||
{
|
||||
"key": "variables",
|
||||
"type": "FUNCTION",
|
||||
"value": "({ state }) => {\n const item = state.currentParallelLoopIterations['expire-assignments'];\n return {\n input: {\n assignations: {\n macAddress: item.macAddress,\n ipAddress: item.ipAddress,\n expired: true\n }\n }\n };\n}"
|
||||
}
|
||||
],
|
||||
"nextSteps": [
|
||||
{ "slug": "expire-assignments-end" }
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
29
gaiia/clear_ips_step1_list.json
Normal file
29
gaiia/clear_ips_step1_list.json
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
{
|
||||
"name": "Test - List IPs",
|
||||
"trigger": {
|
||||
"type": "MANUAL",
|
||||
"nextSteps": [{ "slug": "list" }]
|
||||
},
|
||||
"steps": [
|
||||
{
|
||||
"name": "List assigned items",
|
||||
"slug": "list",
|
||||
"type": "GAIIA_API_GATEWAY_V1_GRAPHQL_REQUEST",
|
||||
"nodeSlug": "gaiia-public-api-gateway-graphql-request",
|
||||
"allowSkipExecution": false,
|
||||
"inputs": [
|
||||
{
|
||||
"key": "query",
|
||||
"type": "LITERAL",
|
||||
"value": "string:query { inventoryItems(filters: {assigneeType: {equals: ACCOUNT}}, first: 5) { totalCount } }"
|
||||
},
|
||||
{
|
||||
"key": "variables",
|
||||
"type": "FUNCTION",
|
||||
"value": "({ secrets, state, utils, variables }) => ({})"
|
||||
}
|
||||
],
|
||||
"nextSteps": [{ "slug": "end" }]
|
||||
}
|
||||
]
|
||||
}
|
||||
27
gaiia/clear_ips_step2_expire_one.json
Normal file
27
gaiia/clear_ips_step2_expire_one.json
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
{
|
||||
"name": "Test - Expire One IP",
|
||||
"description": "Bare minimum V0 expire test with hardcoded MAC+IP",
|
||||
"trigger": { "type": "MANUAL", "nextSteps": [{ "slug": "expire" }] },
|
||||
"steps": [
|
||||
{
|
||||
"name": "Expire",
|
||||
"slug": "expire",
|
||||
"type": "GAIIA_API_GATEWAY_V0_GRAPHQL_REQUEST",
|
||||
"nodeSlug": "gaiia-internal-api-gateway-graphql-request",
|
||||
"allowSkipExecution": false,
|
||||
"inputs": [
|
||||
{
|
||||
"key": "query",
|
||||
"type": "LITERAL",
|
||||
"value": "string:mutation ExpireOne($input: UpsertMacAddressIpAddressAssignationsInput!) {\n upsertMacAddressIpAddressAssignations(input: $input) {\n assignations { macAddress ipAddress }\n }\n}"
|
||||
},
|
||||
{
|
||||
"key": "variables",
|
||||
"type": "LITERAL",
|
||||
"value": "json:{\"input\":{\"assignations\":{\"macAddress\":\"DC:9F:DB:92:0D:AA\",\"ipAddress\":\"10.10.16.74\",\"expired\":true}}}"
|
||||
}
|
||||
],
|
||||
"nextSteps": [{ "slug": "end" }]
|
||||
}
|
||||
]
|
||||
}
|
||||
249
gaiia/compare_pppoe.py
Normal file
249
gaiia/compare_pppoe.py
Normal file
|
|
@ -0,0 +1,249 @@
|
|||
"""Compare PPPoE active sessions from climax MikroTik against Gaiia accounts.
|
||||
|
||||
Checks:
|
||||
1. Is the PPPoE username found as a Gaiia account?
|
||||
2. Does the Gaiia account's inventory item IP match the PPPoE IP?
|
||||
|
||||
Usage: cd /Users/graham/dev/network/gaiia && uv run python compare_pppoe.py
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import os
|
||||
import sys
|
||||
from typing import Any
|
||||
|
||||
sys.path.insert(0, os.path.join(os.path.dirname(__file__), "src"))
|
||||
from gaiia.client import GaiiaClient
|
||||
|
||||
# PPPoE active sessions from climax (2026-07-02)
|
||||
PPPOE_SESSIONS: list[dict[str, str]] = [
|
||||
{"name": "charlesboone", "address": "100.64.7.247", "mac": "C8:9E:43:E3:99:EA"},
|
||||
{"name": "amberprater", "address": "100.64.7.243", "mac": "6C:CD:D6:2C:31:94"},
|
||||
{"name": "matthewgoodwin", "address": "100.64.7.241", "mac": "34:98:B5:44:42:A4"},
|
||||
{"name": "robbymccollom", "address": "100.64.7.239", "mac": "D8:B3:70:92:0C:C6"},
|
||||
{"name": "gordonhamilton", "address": "100.64.7.237", "mac": "A0:63:91:3A:29:B1"},
|
||||
{"name": "chasewilliams", "address": "100.64.7.235", "mac": "38:94:ED:5B:82:E9"},
|
||||
{"name": "cynthiajones", "address": "100.64.7.233", "mac": "E4:FA:C4:56:CA:D9"},
|
||||
{"name": "janicealexander", "address": "100.64.7.225", "mac": "E0:D3:62:E8:65:C5"},
|
||||
{"name": "michaelray", "address": "204.110.188.38", "mac": "9C:05:D6:59:FF:63"},
|
||||
{"name": "marysmelser", "address": "100.64.7.218", "mac": "00:01:9F:33:54:B9"},
|
||||
{"name": "eddieyarbrough", "address": "100.64.7.216", "mac": "10:C3:7B:42:D8:B8"},
|
||||
{"name": "timgilbert", "address": "100.64.7.213", "mac": "94:A6:7E:EE:98:B8"},
|
||||
{"name": "chadwhitsell", "address": "100.64.7.211", "mac": "04:95:E6:76:3C:91"},
|
||||
{"name": "donnacampbell", "address": "100.64.7.209", "mac": "74:FE:CE:3A:6D:AC"},
|
||||
{"name": "bryangoulart", "address": "100.64.7.205", "mac": "8C:90:2D:E9:90:7A"},
|
||||
{"name": "crystalharney", "address": "100.64.7.201", "mac": "98:25:4A:04:B6:43"},
|
||||
{"name": "johnvayo", "address": "100.64.7.197", "mac": "98:25:4A:04:B5:89"},
|
||||
{"name": "richardbarragan", "address": "100.64.7.193", "mac": "8C:90:2D:E9:98:E1"},
|
||||
{"name": "carolstrickland", "address": "100.64.7.189", "mac": "E8:DA:00:18:54:45"},
|
||||
{"name": "timbagert", "address": "100.64.7.140", "mac": "74:FE:CE:3A:6D:8E"},
|
||||
{"name": "douggarber", "address": "100.64.7.136", "mac": "00:01:9F:33:79:A9"},
|
||||
{"name": "janetkern", "address": "100.64.7.61", "mac": "28:C6:8E:C1:06:AF"},
|
||||
{"name": "gregmcintire", "address": "100.64.7.59", "mac": "66:62:8B:10:10:65"},
|
||||
{"name": "jenniferboon", "address": "100.64.7.35", "mac": "98:25:4A:04:BA:EA"},
|
||||
{"name": "glendabeauchamp", "address": "100.64.7.15", "mac": "8C:90:2D:E9:97:85"},
|
||||
{"name": "jmichaelculverhouse", "address": "100.64.7.250", "mac": "34:98:B5:31:D3:3A"},
|
||||
{"name": "elviraquezada", "address": "100.64.7.249", "mac": "E0:C2:50:47:DC:1A"},
|
||||
{"name": "tammieventris", "address": "100.64.7.232", "mac": "98:25:4A:04:A5:E1"},
|
||||
{"name": "taekim", "address": "204.110.188.42", "mac": "9C:5C:8E:44:85:B4"},
|
||||
{"name": "ruthfengler", "address": "100.64.7.246", "mac": "A0:63:91:25:F6:E5"},
|
||||
{"name": "buddyswan", "address": "100.64.7.245", "mac": "00:01:9F:34:D2:21"},
|
||||
{"name": "rhondabolton", "address": "100.64.7.231", "mac": "A0:04:60:94:D5:FF"},
|
||||
]
|
||||
|
||||
# Known PPPoE-name → Gaiia-displayName mismatches
|
||||
MANUAL_MAP: dict[str, str] = {
|
||||
"bryangoulart": "Brian Goulart", # typo in PPPoE username
|
||||
}
|
||||
|
||||
|
||||
def flatten(name: str) -> str:
|
||||
"""Lowercase and remove spaces/punctuation from a name."""
|
||||
return name.lower().replace(" ", "").replace("-", "").replace("'", "")
|
||||
|
||||
|
||||
def fetch_all_accounts(client: GaiiaClient) -> list[dict[str, Any]]:
|
||||
"""Fetch all Gaiia accounts (id, readableId, displayName)."""
|
||||
query = """
|
||||
query($after: String) {
|
||||
accounts(first: 50, after: $after) {
|
||||
nodes { id readableId displayName }
|
||||
pageInfo { hasNextPage endCursor }
|
||||
totalCount
|
||||
}
|
||||
}
|
||||
"""
|
||||
accounts: list[dict[str, Any]] = []
|
||||
cursor: str | None = None
|
||||
has_next = True
|
||||
while has_next:
|
||||
variables: dict[str, Any] = {}
|
||||
if cursor:
|
||||
variables["after"] = cursor
|
||||
data = client.query(query, variables)
|
||||
conn = data["accounts"]
|
||||
accounts.extend(conn.get("nodes") or [])
|
||||
has_next = conn["pageInfo"]["hasNextPage"]
|
||||
cursor = conn["pageInfo"].get("endCursor")
|
||||
print(f" Fetched {len(accounts)}/{conn.get('totalCount', '?')} accounts...",
|
||||
file=sys.stderr)
|
||||
return accounts
|
||||
|
||||
|
||||
def fetch_account_inventory(
|
||||
client: GaiiaClient, account_id: str
|
||||
) -> list[dict[str, Any]]:
|
||||
"""Fetch inventory items for a specific account, including IP addresses."""
|
||||
query = """
|
||||
query($id: GlobalID!) {
|
||||
account(id: $id) {
|
||||
assignedInventoryItems(first: 10) {
|
||||
nodes {
|
||||
inventoryItem {
|
||||
id
|
||||
ipAddressV4
|
||||
ipAddressV6
|
||||
model {
|
||||
name
|
||||
manufacturer { name }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
"""
|
||||
data = client.query(query, {"id": account_id})
|
||||
acct = data.get("account")
|
||||
if not acct:
|
||||
return []
|
||||
items = acct.get("assignedInventoryItems", {})
|
||||
assignations = items.get("nodes") or []
|
||||
# Extract the actual inventory items from assignations
|
||||
result = []
|
||||
for assign in assignations:
|
||||
item = assign.get("inventoryItem")
|
||||
if item:
|
||||
result.append(item)
|
||||
return result
|
||||
|
||||
|
||||
def main() -> None:
|
||||
api_key = os.environ.get("GAIIA_KEY")
|
||||
if not api_key:
|
||||
print("Error: GAIIA_KEY env var not set", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
client = GaiiaClient(api_key, timezone="America/Chicago")
|
||||
|
||||
print("Fetching all accounts from Gaiia...", file=sys.stderr)
|
||||
accounts = fetch_all_accounts(client)
|
||||
print(f"Total: {len(accounts)} accounts\n", file=sys.stderr)
|
||||
|
||||
# Build lookup: flattened displayName → account
|
||||
by_flat: dict[str, dict[str, Any]] = {}
|
||||
for a in accounts:
|
||||
by_flat[flatten(a.get("displayName", ""))] = a
|
||||
|
||||
# --- Step 1: Match PPPoE names to Gaiia accounts ---
|
||||
print("=" * 75)
|
||||
print("CLIMAX PPPoE → GAIIA ACCOUNT MATCHING")
|
||||
print("=" * 75)
|
||||
|
||||
matched: list[dict[str, Any]] = []
|
||||
unmatched: list[dict[str, Any]] = []
|
||||
|
||||
for session in PPPOE_SESSIONS:
|
||||
pname = session["name"]
|
||||
flat = flatten(pname)
|
||||
account = by_flat.get(flat)
|
||||
|
||||
# Try manual map
|
||||
if not account and pname in MANUAL_MAP:
|
||||
mapped_flat = flatten(MANUAL_MAP[pname])
|
||||
account = by_flat.get(mapped_flat)
|
||||
|
||||
session["_account"] = account
|
||||
if account:
|
||||
matched.append(session)
|
||||
else:
|
||||
unmatched.append(session)
|
||||
|
||||
print(f"{'PPPoE':<25} {'PPPoE IP':<18} {'Gaiia Name':<25} {'Account ID':<8}")
|
||||
print("-" * 78)
|
||||
for s in matched:
|
||||
acc = s["_account"]
|
||||
print(f"{s['name']:<25} {s['address']:<18} {acc['displayName']:<25} {acc['readableId']:<8}")
|
||||
for s in unmatched:
|
||||
print(f"{s['name']:<25} {s['address']:<18} {'(not in Gaiia)':<25} {'-':<8}")
|
||||
|
||||
print(f"\nMatched: {len(matched)} | Not in Gaiia: {len(unmatched)}")
|
||||
if unmatched:
|
||||
print("Not in Gaiia:")
|
||||
for s in unmatched:
|
||||
print(f" - {s['name']} ({s['address']})")
|
||||
|
||||
# --- Step 2: Compare IP addresses from inventory items ---
|
||||
print(f"\n{'=' * 75}")
|
||||
print("IP ADDRESS COMPARISON (PPPoE active IP vs Gaiia inventory item IP)")
|
||||
print("=" * 75)
|
||||
|
||||
ip_matched = 0
|
||||
ip_mismatched = 0
|
||||
ip_no_inventory = 0
|
||||
ip_not_checked = 0
|
||||
|
||||
for s in matched:
|
||||
acc = s["_account"]
|
||||
pppoe_ip = s["address"]
|
||||
pppoe_name = s["name"]
|
||||
|
||||
# Fetch inventory items for this account
|
||||
items = fetch_account_inventory(client, acc["id"])
|
||||
s["_items"] = items
|
||||
|
||||
if not items:
|
||||
print(f"{pppoe_name:<25} {pppoe_ip:<18} (no inventory items in Gaiia)")
|
||||
ip_no_inventory += 1
|
||||
continue
|
||||
|
||||
# Check if any inventory item has an IP matching the PPPoE IP
|
||||
found_match = False
|
||||
for item in items:
|
||||
item_ip = item.get("ipAddressV4")
|
||||
if item_ip == pppoe_ip:
|
||||
model = item.get("model", {})
|
||||
model_name = model.get("name", "?") if model else "?"
|
||||
print(f"{pppoe_name:<25} {pppoe_ip:<18} MATCH (item: {model_name})")
|
||||
ip_matched += 1
|
||||
found_match = True
|
||||
break
|
||||
|
||||
if not found_match:
|
||||
item_ips = [i.get("ipAddressV4") for i in items if i.get("ipAddressV4")]
|
||||
if item_ips:
|
||||
print(f"{pppoe_name:<25} {pppoe_ip:<18} MISMATCH (Gaiia has: {', '.join(item_ips)})")
|
||||
ip_mismatched += 1
|
||||
else:
|
||||
print(f"{pppoe_name:<25} {pppoe_ip:<18} NO IP SET (has inventory items but no IPs)")
|
||||
ip_no_inventory += 1
|
||||
|
||||
print(f"\n--- Summary ---")
|
||||
print(f"Accounts in Gaiia: {len(matched)}/{len(PPPOE_SESSIONS)}")
|
||||
print(f"Not in Gaiia: {len(unmatched)}")
|
||||
print(f"IP matches PPPoE: {ip_matched}")
|
||||
print(f"IP mismatch (different IP): {ip_mismatched}")
|
||||
print(f"No inventory / no IP in Gaiia: {ip_no_inventory}")
|
||||
|
||||
if unmatched:
|
||||
print(f"\nAccounts missing from Gaiia (need investigation):")
|
||||
for s in unmatched:
|
||||
mac = s.get("mac", "")
|
||||
print(f" {s['name']} IP={s['address']} MAC={mac}")
|
||||
|
||||
client.close()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
83
gaiia/daily_preseem_sync_workflow.json
Normal file
83
gaiia/daily_preseem_sync_workflow.json
Normal file
|
|
@ -0,0 +1,83 @@
|
|||
{
|
||||
"name": "Daily Preseem Site Inventory Sync",
|
||||
"description": "SCHEDULED (daily 3am): Iterates all network sites, extracts MACs from assigned equipment fields, syncs each item to Preseem access points with its site name as tower.",
|
||||
"trigger": {
|
||||
"type": "SCHEDULE",
|
||||
"cron": "0 3 * * *",
|
||||
"nextSteps": [{ "slug": "fetch-sites" }]
|
||||
},
|
||||
"steps": [
|
||||
{
|
||||
"name": "Fetch all network sites with assigned equipment",
|
||||
"slug": "fetch-sites",
|
||||
"type": "GAIIA_API_GATEWAY_V1_GRAPHQL_REQUEST",
|
||||
"nodeSlug": "gaiia-public-api-gateway-graphql-request",
|
||||
"allowSkipExecution": false,
|
||||
"outputMapper": "({ output }) => {\n const sites = output.response.networkSites.nodes;\n const items = [];\n for (const site of sites) {\n for (const assign of (site.assignedInventoryItems?.nodes || [])) {\n const inv = assign.inventoryItem || {};\n const model = inv.model?.name || '';\n // Extract MAC from any field whose name contains 'mac' (case-insensitive)\n const macs = [];\n for (const f of (inv.fields?.nodes || [])) {\n const fieldName = (f.modelField?.name || '').toLowerCase();\n const data = (f.data || '').trim();\n if (fieldName.includes('mac') && data && data !== '00:00:00:00:00:00') {\n // Normalize MAC: strip '1:' prefix, pad segments, uppercase\n const cleaned = data.startsWith('1:') ? data.slice(2) : data;\n const parts = cleaned.split(':');\n if (parts.length === 6) {\n const normalized = parts\n .map((p) => p.padStart(2, '0').toUpperCase())\n .join(':');\n macs.push(normalized);\n }\n }\n }\n if (macs.length > 0) {\n items.push({\n inventoryId: inv.id,\n model,\n siteName: site.name,\n macs: [...new Set(macs)]\n });\n }\n }\n }\n return items;\n}",
|
||||
"inputs": [
|
||||
{
|
||||
"key": "query",
|
||||
"type": "LITERAL",
|
||||
"value": "string:query {\n networkSites(first: 250) {\n nodes {\n id\n name\n assignedInventoryItems {\n nodes {\n inventoryItem {\n id\n model { name }\n ipAddressV4\n fields {\n nodes { data modelField { name } }\n }\n }\n }\n }\n }\n }\n}"
|
||||
},
|
||||
{
|
||||
"key": "variables",
|
||||
"type": "FUNCTION",
|
||||
"value": "({ secrets, state, utils, variables }) => ({})"
|
||||
}
|
||||
],
|
||||
"nextSteps": [{ "slug": "if-any-items" }]
|
||||
},
|
||||
{
|
||||
"name": "Any items to sync?",
|
||||
"slug": "if-any-items",
|
||||
"type": "CONDITION",
|
||||
"nodeSlug": "condition",
|
||||
"condition": "({ secrets, state, utils, variables }) => {\n const items = state.steps['fetch-sites'].output;\n return Array.isArray(items) && items.length > 0;\n}",
|
||||
"elseSteps": [{ "slug": "end" }],
|
||||
"thenSteps": [{ "slug": "sync-to-preseem" }]
|
||||
},
|
||||
{
|
||||
"name": "Sync each item to Preseem",
|
||||
"slug": "sync-to-preseem",
|
||||
"type": "PARALLEL_LOOP",
|
||||
"nodeSlug": "parallel-loop",
|
||||
"maxConcurrentIterations": 10,
|
||||
"inputs": [
|
||||
{
|
||||
"key": "iterable",
|
||||
"type": "FUNCTION",
|
||||
"value": "({ state }) => state.steps['fetch-sites'].output"
|
||||
}
|
||||
],
|
||||
"initialSteps": [{ "slug": "preseem-put-ap" }],
|
||||
"nextSteps": [{ "slug": "end" }]
|
||||
},
|
||||
{
|
||||
"name": "PUT to Preseem access points",
|
||||
"slug": "preseem-put-ap",
|
||||
"type": "PRESEEM_REST_REQUEST",
|
||||
"nodeSlug": "preseem-rest-request",
|
||||
"parentParallelLoopSlug": "sync-to-preseem",
|
||||
"allowSkipExecution": false,
|
||||
"inputs": [
|
||||
{
|
||||
"key": "method",
|
||||
"type": "LITERAL",
|
||||
"value": "string:PUT"
|
||||
},
|
||||
{
|
||||
"key": "path",
|
||||
"type": "FUNCTION",
|
||||
"value": "({ state }) => {\n const item = state.currentParallelLoopIterations['sync-to-preseem'];\n return `/access_points/${item.inventoryId}`;\n}"
|
||||
},
|
||||
{
|
||||
"key": "body",
|
||||
"type": "FUNCTION",
|
||||
"value": "({ state }) => {\n const item = state.currentParallelLoopIterations['sync-to-preseem'];\n return {\n id: item.inventoryId,\n name: `${item.model} (${item.macs[0]})`,\n tower: item.siteName\n };\n}"
|
||||
}
|
||||
],
|
||||
"nextSteps": [{ "slug": "sync-to-preseem-end" }]
|
||||
}
|
||||
]
|
||||
}
|
||||
227
gaiia/dhcp_opt82_workflow.json
Normal file
227
gaiia/dhcp_opt82_workflow.json
Normal file
|
|
@ -0,0 +1,227 @@
|
|||
{
|
||||
"name": "DHCP Option 82 Auth",
|
||||
"description": "WEBHOOK: Always upserts IP. If Option 82 present AND MAC linked to an account, syncs all inventoried MACs to Preseem with plan details.",
|
||||
"trigger": {
|
||||
"type": "WEBHOOK",
|
||||
"inputJsonSchema": {
|
||||
"type": "object",
|
||||
"required": ["bound", "mac", "ip", "server", "remoteId"],
|
||||
"properties": {
|
||||
"bound": { "type": "string" },
|
||||
"mac": { "type": "string" },
|
||||
"ip": { "type": "string" },
|
||||
"server": { "type": "string" },
|
||||
"remoteId": { "type": "string" }
|
||||
}
|
||||
},
|
||||
"nextSteps": [{ "slug": "check-inventory" }]
|
||||
},
|
||||
"steps": [
|
||||
{
|
||||
"name": "Find inventory item by MAC",
|
||||
"slug": "check-inventory",
|
||||
"type": "GAIIA_API_GATEWAY_V0_GRAPHQL_REQUEST",
|
||||
"nodeSlug": "gaiia-internal-api-gateway-graphql-request",
|
||||
"allowSkipExecution": false,
|
||||
"outputMapper": "({ output, state }) => {\n const items = output.response.findInventoryItemsFromDeviceIdentifiers;\n // Accept items assigned to Accounts OR NetworkSites\n const assigned = items.filter(\n (item) => {\n const type = item.inventoryItem?.assignation?.assignee?.__typename;\n return type === 'Account' || type === 'NetworkSite';\n }\n );\n if (assigned.length === 0) return null;\n const inv = assigned[0].inventoryItem;\n const assignee = inv.assignation.assignee;\n return {\n assigneeId: assignee.id,\n assigneeType: assignee.__typename,\n assigneeName: assignee.name || ''\n };\n}",
|
||||
"inputs": [
|
||||
{
|
||||
"key": "query",
|
||||
"type": "LITERAL",
|
||||
"value": "string:query FindItem($input: FindInventoryItemsFromDeviceIdentifiersQueryInput!) {\n findInventoryItemsFromDeviceIdentifiers(input: $input) {\n identifier\n inventoryItem {\n id\n model { name }\n assignation {\n assignee {\n __typename\n ... on Account { id name }\n }\n }\n }\n }\n}"
|
||||
},
|
||||
{
|
||||
"key": "variables",
|
||||
"type": "FUNCTION",
|
||||
"value": "({ state }) => {\n const rawMac = state.input.mac;\n const cleanedMac = rawMac.startsWith('1:') ? rawMac.slice(2) : rawMac;\n const formattedMac = cleanedMac\n .split(':')\n .map((segment) => segment.padStart(2, '0'))\n .join(':')\n .toUpperCase();\n return { input: { identifiers: [formattedMac] } };\n}"
|
||||
}
|
||||
],
|
||||
"nextSteps": [{ "slug": "is-connect" }]
|
||||
},
|
||||
{
|
||||
"name": "Connect or disconnect?",
|
||||
"slug": "is-connect",
|
||||
"type": "CONDITION",
|
||||
"nodeSlug": "condition",
|
||||
"condition": "({ secrets, state, utils, variables }) => {\n return state.input.bound === '1';\n}",
|
||||
"elseSteps": [{ "slug": "expire-ip" }],
|
||||
"thenSteps": [{ "slug": "has-account-connect" }]
|
||||
},
|
||||
{
|
||||
"name": "Account found? (connect)",
|
||||
"slug": "has-account-connect",
|
||||
"type": "CONDITION",
|
||||
"nodeSlug": "condition",
|
||||
"condition": "({ secrets, state, utils, variables }) => {\n return state.steps['check-inventory'].output != null;\n}",
|
||||
"elseSteps": [{ "slug": "upsert-ip-no-account" }],
|
||||
"thenSteps": [{ "slug": "upsert-ip-with-account" }]
|
||||
},
|
||||
{
|
||||
"name": "Upsert IP (no account)",
|
||||
"slug": "upsert-ip-no-account",
|
||||
"type": "GAIIA_API_GATEWAY_V0_GRAPHQL_REQUEST",
|
||||
"nodeSlug": "gaiia-internal-api-gateway-graphql-request",
|
||||
"allowSkipExecution": false,
|
||||
"inputs": [
|
||||
{
|
||||
"key": "query",
|
||||
"type": "LITERAL",
|
||||
"value": "string:mutation UpsertIp($input: UpsertMacAddressIpAddressAssignationsInput!) {\n upsertMacAddressIpAddressAssignations(input: $input) {\n assignations { macAddress ipAddress }\n }\n}"
|
||||
},
|
||||
{
|
||||
"key": "variables",
|
||||
"type": "FUNCTION",
|
||||
"value": "({ state }) => {\n const rawMac = state.input.mac;\n const cleanedMac = rawMac.startsWith('1:') ? rawMac.slice(2) : rawMac;\n const formattedMac = cleanedMac\n .split(':')\n .map((segment) => segment.padStart(2, '0'))\n .join(':')\n .toUpperCase();\n return {\n input: {\n assignations: { macAddress: formattedMac, ipAddress: state.input.ip, expired: false }\n }\n };\n}"
|
||||
}
|
||||
],
|
||||
"nextSteps": [{ "slug": "end" }]
|
||||
},
|
||||
{
|
||||
"name": "Upsert IP (with account)",
|
||||
"slug": "upsert-ip-with-account",
|
||||
"type": "GAIIA_API_GATEWAY_V0_GRAPHQL_REQUEST",
|
||||
"nodeSlug": "gaiia-internal-api-gateway-graphql-request",
|
||||
"allowSkipExecution": false,
|
||||
"inputs": [
|
||||
{
|
||||
"key": "query",
|
||||
"type": "LITERAL",
|
||||
"value": "string:mutation UpsertIp($input: UpsertMacAddressIpAddressAssignationsInput!) {\n upsertMacAddressIpAddressAssignations(input: $input) {\n assignations { macAddress ipAddress }\n }\n}"
|
||||
},
|
||||
{
|
||||
"key": "variables",
|
||||
"type": "FUNCTION",
|
||||
"value": "({ state }) => {\n const rawMac = state.input.mac;\n const cleanedMac = rawMac.startsWith('1:') ? rawMac.slice(2) : rawMac;\n const formattedMac = cleanedMac\n .split(':')\n .map((segment) => segment.padStart(2, '0'))\n .join(':')\n .toUpperCase();\n return {\n input: {\n assignations: { macAddress: formattedMac, ipAddress: state.input.ip, expired: false }\n }\n };\n}"
|
||||
}
|
||||
],
|
||||
"nextSteps": [{ "slug": "should-sync-preseem" }]
|
||||
},
|
||||
{
|
||||
"name": "Has Option 82 and account?",
|
||||
"slug": "should-sync-preseem",
|
||||
"type": "CONDITION",
|
||||
"nodeSlug": "condition",
|
||||
"condition": "({ secrets, state, utils, variables }) => {\n const inv = state.steps['check-inventory'].output;\n if (inv == null) return false;\n if (!state.input.remoteId || state.input.remoteId === '') return false;\n return inv.assigneeType === 'Account';\n}",
|
||||
"elseSteps": [{ "slug": "should-sync-site" }],
|
||||
"thenSteps": [{ "slug": "get-account-macs-and-plan" }]
|
||||
},
|
||||
{
|
||||
"name": "Get all inventoried MACs and plan",
|
||||
"slug": "get-account-macs-and-plan",
|
||||
"type": "GAIIA_API_GATEWAY_V1_GRAPHQL_REQUEST",
|
||||
"nodeSlug": "gaiia-public-api-gateway-graphql-request",
|
||||
"allowSkipExecution": false,
|
||||
"outputMapper": "({ output, state }) => {\n const account = output.response.account;\n if (!account) {\n workflow.fail('Account not found', 'ACCOUNT_NOT_FOUND');\n }\n const macs = [];\n for (const assign of (account.assignedInventoryItems?.nodes || [])) {\n const inv = assign.inventoryItem || {};\n for (const f of (inv.fields?.nodes || [])) {\n const fieldName = (f.modelField?.name || '').toLowerCase();\n const data = (f.data || '').trim();\n if (fieldName.includes('mac') && data && data !== '00:00:00:00:00:00') {\n const cleaned = data.startsWith('1:') ? data.slice(2) : data;\n const parts = cleaned.split(':');\n if (parts.length === 6) {\n macs.push(parts.map((p) => p.padStart(2, '0').toUpperCase()).join(':'));\n }\n }\n }\n for (const a of (inv.ipAddressAssignments?.nodes || [])) {\n if (a.macAddress) macs.push(a.macAddress.toUpperCase());\n }\n }\n const plan = (account.billingSubscriptions?.nodes || [])\n .map((s) => s.productVersion?.product?.slug)\n .find(Boolean) || '';\n return { macs: [...new Set(macs)], plan, gaiiaId: account.readableId };\n}",
|
||||
"inputs": [
|
||||
{
|
||||
"key": "query",
|
||||
"type": "FUNCTION",
|
||||
"value": "({ state }) => {\n const accountId = state.steps['check-inventory'].output.accountId;\n return `query { account(id: \"${accountId}\") { readableId assignedInventoryItems { nodes { inventoryItem { fields { nodes { data modelField { name } } } ipAddressAssignments { nodes { macAddress } } } } } billingSubscriptions { nodes { productVersion { product { slug } } } } } }`;\n}"
|
||||
},
|
||||
{
|
||||
"key": "variables",
|
||||
"type": "FUNCTION",
|
||||
"value": "({ state }) => ({})"
|
||||
}
|
||||
],
|
||||
"nextSteps": [{ "slug": "preseem-update" }]
|
||||
},
|
||||
{
|
||||
"name": "Update Preseem",
|
||||
"slug": "preseem-update",
|
||||
"type": "PRESEEM_REST_REQUEST",
|
||||
"nodeSlug": "preseem-rest-request",
|
||||
"allowSkipExecution": false,
|
||||
"inputs": [
|
||||
{
|
||||
"key": "method",
|
||||
"type": "LITERAL",
|
||||
"value": "string:PUT"
|
||||
},
|
||||
{
|
||||
"key": "path",
|
||||
"type": "FUNCTION",
|
||||
"value": "({ state }) => {\n const { gaiiaId } = state.steps['get-account-macs-and-plan'].output;\n return `/services/${gaiiaId}`;\n}"
|
||||
},
|
||||
{
|
||||
"key": "body",
|
||||
"type": "FUNCTION",
|
||||
"value": "({ state }) => {\n const { gaiiaId, macs, plan } = state.steps['get-account-macs-and-plan'].output;\n const rawMac = state.input.mac;\n const cleanedMac = rawMac.startsWith('1:') ? rawMac.slice(2) : rawMac;\n const connectingMac = cleanedMac\n .split(':')\n .map((segment) => segment.padStart(2, '0'))\n .join(':')\n .toLowerCase();\n const framedIp = state.input.ip;\n const inventoriedMacs = macs.map((m) => m.toLowerCase());\n const attachments = inventoriedMacs\n .filter((m) => state.input.bound === '0' || m !== connectingMac)\n .map((m) => ({ cpe_mac: m }));\n if (state.input.bound === '1') {\n attachments.push({ cpe_mac: connectingMac, network_prefixes: [framedIp] });\n }\n return {\n id: String(gaiiaId),\n attachments,\n account: String(gaiiaId),\n package: plan\n };\n}"
|
||||
}
|
||||
],
|
||||
"nextSteps": [{ "slug": "end" }]
|
||||
},
|
||||
{
|
||||
"name": "Expire IP",
|
||||
"slug": "expire-ip",
|
||||
"type": "GAIIA_API_GATEWAY_V0_GRAPHQL_REQUEST",
|
||||
"nodeSlug": "gaiia-internal-api-gateway-graphql-request",
|
||||
"allowSkipExecution": false,
|
||||
"inputs": [
|
||||
{
|
||||
"key": "query",
|
||||
"type": "LITERAL",
|
||||
"value": "string:mutation ExpireIp($input: UpsertMacAddressIpAddressAssignationsInput!) {\n upsertMacAddressIpAddressAssignations(input: $input) {\n assignations { macAddress ipAddress }\n }\n}"
|
||||
},
|
||||
{
|
||||
"key": "variables",
|
||||
"type": "FUNCTION",
|
||||
"value": "({ state }) => {\n const rawMac = state.input.mac;\n const cleanedMac = rawMac.startsWith('1:') ? rawMac.slice(2) : rawMac;\n const formattedMac = cleanedMac\n .split(':')\n .map((segment) => segment.padStart(2, '0'))\n .join(':')\n .toUpperCase();\n return {\n input: {\n assignations: { macAddress: formattedMac, ipAddress: state.input.ip, expired: true }\n }\n };\n}"
|
||||
}
|
||||
],
|
||||
"nextSteps": [{ "slug": "should-sync-preseem-disconnect" }]
|
||||
},
|
||||
{
|
||||
"name": "Network site with Option 82?",
|
||||
"slug": "should-sync-site",
|
||||
"type": "CONDITION",
|
||||
"nodeSlug": "condition",
|
||||
"condition": "({ secrets, state, utils, variables }) => {\n const inv = state.steps['check-inventory'].output;\n if (inv == null) return false;\n if (!state.input.remoteId || state.input.remoteId === '') return false;\n return inv.assigneeType === 'NetworkSite';\n}",
|
||||
"elseSteps": [{ "slug": "end" }],
|
||||
"thenSteps": [{ "slug": "preseem-site-update" }]
|
||||
},
|
||||
{
|
||||
"name": "Update Preseem (site)",
|
||||
"slug": "preseem-site-update",
|
||||
"type": "PRESEEM_REST_REQUEST",
|
||||
"nodeSlug": "preseem-rest-request",
|
||||
"allowSkipExecution": false,
|
||||
"inputs": [
|
||||
{
|
||||
"key": "method",
|
||||
"type": "LITERAL",
|
||||
"value": "string:PUT"
|
||||
},
|
||||
{
|
||||
"key": "path",
|
||||
"type": "FUNCTION",
|
||||
"value": "({ state }) => {\n const inv = state.steps['check-inventory'].output;\n return `/access_points/${inv.assigneeId}`;\n}"
|
||||
},
|
||||
{
|
||||
"key": "body",
|
||||
"type": "FUNCTION",
|
||||
"value": "({ state }) => {\n const inv = state.steps['check-inventory'].output;\n const rawMac = state.input.mac;\n const cleanedMac = rawMac.startsWith('1:') ? rawMac.slice(2) : rawMac;\n const mac = cleanedMac\n .split(':')\n .map((segment) => segment.padStart(2, '0'))\n .join(':')\n .toLowerCase();\n return {\n id: inv.assigneeId,\n name: inv.assigneeName || inv.assigneeId,\n tower: inv.assigneeName || '',\n ip_address: state.input.ip\n };\n}"
|
||||
}
|
||||
],
|
||||
"nextSteps": [{ "slug": "end" }]
|
||||
},
|
||||
{
|
||||
"name": "Has Option 82 and account? (disconnect)",
|
||||
"slug": "should-sync-preseem-disconnect",
|
||||
"type": "CONDITION",
|
||||
"nodeSlug": "condition",
|
||||
"condition": "({ secrets, state, utils, variables }) => {\n const inv = state.steps['check-inventory'].output;\n if (inv == null) return false;\n if (!state.input.remoteId || state.input.remoteId === '') return false;\n return inv.assigneeType === 'Account';\n}",
|
||||
"elseSteps": [{ "slug": "should-sync-site-disconnect" }],
|
||||
"thenSteps": [{ "slug": "get-account-macs-and-plan" }]
|
||||
},
|
||||
{
|
||||
"name": "Network site? (disconnect)",
|
||||
"slug": "should-sync-site-disconnect",
|
||||
"type": "CONDITION",
|
||||
"nodeSlug": "condition",
|
||||
"condition": "({ secrets, state, utils, variables }) => {\n const inv = state.steps['check-inventory'].output;\n if (inv == null) return false;\n if (!state.input.remoteId || state.input.remoteId === '') return false;\n return inv.assigneeType === 'NetworkSite';\n}",
|
||||
"elseSteps": [{ "slug": "end" }],
|
||||
"thenSteps": [{ "slug": "preseem-site-update" }]
|
||||
}
|
||||
]
|
||||
}
|
||||
39
gaiia/examples/basic.py
Normal file
39
gaiia/examples/basic.py
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
"""Minimal usage example.
|
||||
|
||||
Set GAIIA_KEY in your env, then ``python examples/basic.py``.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import os
|
||||
|
||||
from gaiia import GaiiaClient
|
||||
|
||||
|
||||
def main() -> None:
|
||||
with GaiiaClient(timezone="America/Chicago") as g:
|
||||
techs = g.query("{ technicians(first: 5) { nodes { id readableId firstName lastName } } }")
|
||||
for t in techs["technicians"]["nodes"]:
|
||||
print(f"{t['readableId']:>4} {t['firstName']} {t['lastName']} {t['id']}")
|
||||
|
||||
print("rate-limit:", g.last_rate_limit)
|
||||
|
||||
print("\npaginating all technicians:")
|
||||
for t in g.iter_nodes(
|
||||
"""
|
||||
query Q($after: String) {
|
||||
technicians(first: 50, after: $after) {
|
||||
nodes { id readableId firstName lastName }
|
||||
pageInfo { hasNextPage endCursor }
|
||||
}
|
||||
}
|
||||
""",
|
||||
"technicians",
|
||||
):
|
||||
print(f" {t['readableId']:>4} {t['firstName']} {t['lastName']}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
if not os.environ.get("GAIIA_KEY"):
|
||||
raise SystemExit("Set GAIIA_KEY in your env first.")
|
||||
main()
|
||||
34
gaiia/examples/webhook_flask.py
Normal file
34
gaiia/examples/webhook_flask.py
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
"""Verify gaiia webhooks in a Flask endpoint.
|
||||
|
||||
pip install flask gaiia
|
||||
GAIIA_WEBHOOK_SECRET=whsec_... flask --app examples/webhook_flask.py run
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
import os
|
||||
|
||||
from flask import Flask, abort, request
|
||||
|
||||
from gaiia import WebhookSignatureError, verify_webhook_signature
|
||||
|
||||
app = Flask(__name__)
|
||||
SECRET = os.environ["GAIIA_WEBHOOK_SECRET"]
|
||||
|
||||
|
||||
@app.post("/gaiia/webhook")
|
||||
def webhook() -> tuple[str, int]:
|
||||
raw = request.get_data()
|
||||
try:
|
||||
verify_webhook_signature(
|
||||
header_value=request.headers.get("X-Gaiia-Webhook-Signature", ""),
|
||||
raw_body=raw,
|
||||
secret=SECRET,
|
||||
)
|
||||
except WebhookSignatureError as e:
|
||||
abort(400, description=str(e))
|
||||
|
||||
event = json.loads(raw)
|
||||
app.logger.info("gaiia event: %s", event.get("type"))
|
||||
return "", 204
|
||||
37
gaiia/expire_one_workflow.json
Normal file
37
gaiia/expire_one_workflow.json
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
{
|
||||
"name": "Expire Single IP Assignment",
|
||||
"trigger": {
|
||||
"type": "MANUAL",
|
||||
"inputJsonSchema": {
|
||||
"type": "object",
|
||||
"required": ["macAddress", "ipAddress"],
|
||||
"properties": {
|
||||
"macAddress": { "type": "string" },
|
||||
"ipAddress": { "type": "string" }
|
||||
}
|
||||
},
|
||||
"nextSteps": [{ "slug": "expire" }]
|
||||
},
|
||||
"steps": [
|
||||
{
|
||||
"name": "Expire assignment",
|
||||
"slug": "expire",
|
||||
"type": "GAIIA_API_GATEWAY_V0_GRAPHQL_REQUEST",
|
||||
"nodeSlug": "gaiia-internal-api-gateway-graphql-request",
|
||||
"allowSkipExecution": false,
|
||||
"inputs": [
|
||||
{
|
||||
"key": "query",
|
||||
"type": "LITERAL",
|
||||
"value": "string:mutation Expire($input: UpsertMacAddressIpAddressAssignationsInput!) {\n upsertMacAddressIpAddressAssignations(input: $input) {\n assignations { macAddress ipAddress }\n }\n}"
|
||||
},
|
||||
{
|
||||
"key": "variables",
|
||||
"type": "FUNCTION",
|
||||
"value": "({ state }) => ({\n input: {\n assignations: {\n macAddress: state.input.macAddress,\n ipAddress: state.input.ipAddress,\n expired: true\n }\n }\n})"
|
||||
}
|
||||
],
|
||||
"nextSteps": [{ "slug": "end" }]
|
||||
}
|
||||
]
|
||||
}
|
||||
307
gaiia/fuzzy_match.py
Normal file
307
gaiia/fuzzy_match.py
Normal file
|
|
@ -0,0 +1,307 @@
|
|||
#!/usr/bin/env python3
|
||||
"""
|
||||
Fuzzy match unmatched RADIUS users against Gaiia accounts.
|
||||
Smarter matching: requires first-name correlation when matching on last name,
|
||||
handles substring traps ("kim" in "kimberly"), etc.
|
||||
"""
|
||||
|
||||
import csv
|
||||
import json
|
||||
import re
|
||||
import subprocess
|
||||
import sys
|
||||
from difflib import SequenceMatcher
|
||||
|
||||
GAIIA_KEY = "gaiia_sk_live_955b27e3680747a69ad40b403a912155_b0d1be3c"
|
||||
GAIIA_ENDPOINT = "https://api.gaiia.com/api/v1"
|
||||
|
||||
|
||||
def fetch_all_accounts():
|
||||
accounts = []
|
||||
cursor = None
|
||||
while True:
|
||||
after_arg = f', after: "{cursor}"' if cursor else ""
|
||||
query = f"""
|
||||
query {{
|
||||
accounts(first: 250{after_arg}) {{
|
||||
nodes {{ id readableId name displayName }}
|
||||
pageInfo {{ hasNextPage endCursor }}
|
||||
}}
|
||||
}}
|
||||
"""
|
||||
result = subprocess.run(
|
||||
["curl", "-s", "-X", "POST", GAIIA_ENDPOINT,
|
||||
"-H", "Content-Type: application/json",
|
||||
"-H", f"X-Gaiia-Api-Key: {GAIIA_KEY}",
|
||||
"-H", "x-timezone: America/Chicago",
|
||||
"-d", json.dumps({"query": query})],
|
||||
capture_output=True, text=True, timeout=30,
|
||||
)
|
||||
data = json.loads(result.stdout)
|
||||
for n in data["data"]["accounts"]["nodes"]:
|
||||
name = n.get("displayName") or n.get("name") or ""
|
||||
if name:
|
||||
accounts.append((n["id"], name.strip(), n["readableId"]))
|
||||
page_info = data["data"]["accounts"]["pageInfo"]
|
||||
if not page_info["hasNextPage"]:
|
||||
break
|
||||
cursor = page_info["endCursor"]
|
||||
return accounts
|
||||
|
||||
|
||||
def normalize(s):
|
||||
return re.sub(r'[^\w\s]', '', s.lower()).strip()
|
||||
|
||||
|
||||
def split_name(display_name):
|
||||
"""Split into first name(s) and last name."""
|
||||
parts = normalize(display_name).split()
|
||||
if len(parts) == 1:
|
||||
return parts[0], ""
|
||||
# Filter out suffixes, LLC, numbers, etc.
|
||||
content_parts = [p for p in parts if not re.match(r'^(#?\d+|llc|inc|ltd|shop|jr|sr)$', p)]
|
||||
if len(content_parts) >= 2:
|
||||
return " ".join(content_parts[:-1]), content_parts[-1]
|
||||
return content_parts[0], ""
|
||||
|
||||
|
||||
def first_name_match_score(username, last_name, first_name_str):
|
||||
"""How well does the first name correlate with the username? Returns 0-1.
|
||||
|
||||
The first initial must appear as the FIRST character of the username
|
||||
portion before the last name (natural "firstname+lastname" order).
|
||||
This prevents 'a' from "Amy" spuriously matching inside "ryan".
|
||||
"""
|
||||
if not first_name_str:
|
||||
return 0.5 # neutral
|
||||
|
||||
# Find where last name appears in username
|
||||
last_idx = username.find(last_name) if last_name else -1
|
||||
username_before_last = username[:last_idx] if last_idx > 0 else username
|
||||
|
||||
first_names = first_name_str.split()
|
||||
best = 0.0
|
||||
|
||||
for fn in first_names:
|
||||
if len(fn) < 2:
|
||||
continue
|
||||
fn_init = fn[0]
|
||||
if fn in username:
|
||||
return 1.0 # full first name in username
|
||||
# First initial must be the FIRST character before the last name
|
||||
if username_before_last and username_before_last[0] == fn_init:
|
||||
best = max(best, 0.7)
|
||||
# Fuzzy match
|
||||
ratio = SequenceMatcher(None, fn, username).ratio()
|
||||
best = max(best, ratio * 0.8)
|
||||
|
||||
return best
|
||||
|
||||
|
||||
def match_unmatched(unmatched_users, accounts):
|
||||
"""Fuzzy match with smarter scoring."""
|
||||
# Manual overrides from user knowledge
|
||||
MANUAL_MATCHES = {
|
||||
# (username, readable_id) -> forces match to specific Gaiia account
|
||||
"barbihardin": "James Hardin",
|
||||
"daycor": ("Cathy Day", 755),
|
||||
"tech": ("Graham McIntire", 1),
|
||||
# Add more as discovered
|
||||
}
|
||||
|
||||
# Build lookup by name
|
||||
accounts_by_name = {normalize(name): (aid, name, rid) for aid, name, rid in accounts}
|
||||
|
||||
results = []
|
||||
|
||||
for user in unmatched_users:
|
||||
username = user["username"].lower()
|
||||
username_no_digits = re.sub(r'\d+$', '', username)
|
||||
|
||||
# Check manual overrides first
|
||||
if username in MANUAL_MATCHES:
|
||||
override = MANUAL_MATCHES[username]
|
||||
# tuple form: (display_name, readable_id)
|
||||
if isinstance(override, tuple):
|
||||
target_name, target_rid = override
|
||||
for aid, name, rid in accounts:
|
||||
if normalize(name) == normalize(target_name) and rid == target_rid:
|
||||
results.append({
|
||||
**user,
|
||||
"gaiia_id": aid, "gaiia_name": name,
|
||||
"gaiia_readable_id": rid,
|
||||
"match_type": "fuzzy(manual_override)",
|
||||
})
|
||||
break
|
||||
else:
|
||||
# Fallback: match by name only
|
||||
norm_target = normalize(target_name)
|
||||
if norm_target in accounts_by_name:
|
||||
aid, name, rid = accounts_by_name[norm_target]
|
||||
results.append({
|
||||
**user,
|
||||
"gaiia_id": aid, "gaiia_name": name,
|
||||
"gaiia_readable_id": rid,
|
||||
"match_type": "fuzzy(manual_override)",
|
||||
})
|
||||
continue
|
||||
continue
|
||||
|
||||
# String form: match by name
|
||||
target_name = override
|
||||
norm_target = normalize(target_name)
|
||||
if norm_target in accounts_by_name:
|
||||
aid, name, rid = accounts_by_name[norm_target]
|
||||
results.append({
|
||||
**user,
|
||||
"gaiia_id": aid,
|
||||
"gaiia_name": name,
|
||||
"gaiia_readable_id": rid,
|
||||
"match_type": "fuzzy(manual_override)",
|
||||
})
|
||||
continue
|
||||
|
||||
best_match = None
|
||||
best_score = 0.0
|
||||
best_reason = ""
|
||||
|
||||
for acct_id, acct_name, readable_id in accounts:
|
||||
first_names, last_name = split_name(acct_name)
|
||||
aname = normalize(acct_name)
|
||||
aname_parts = aname.split()
|
||||
|
||||
score = 0.0
|
||||
reason = ""
|
||||
|
||||
# ---- Strategy 1: last name match + first name correlation ----
|
||||
if last_name and len(last_name) >= 3:
|
||||
# Check that last_name appears in username as a distinct substring
|
||||
# (not "kim" inside "kimberly")
|
||||
idx = username.find(last_name)
|
||||
if idx >= 0:
|
||||
# Check it's a reasonable boundary match
|
||||
# Allow: at start, at end, or surrounded by non-alpha
|
||||
before_ok = idx == 0 or not username[idx-1].isalpha()
|
||||
after_ok = (idx + len(last_name) == len(username)) or not username[idx+len(last_name)].isalpha()
|
||||
if not before_ok and not after_ok:
|
||||
# "kim" inside "kimberly" - reject this last name match
|
||||
pass
|
||||
else:
|
||||
# Check first name correlation
|
||||
fn_score = first_name_match_score(username, last_name, first_names)
|
||||
if fn_score >= 0.5:
|
||||
score = 0.7 + (fn_score * 0.2)
|
||||
reason = f"last+first: {last_name}+{first_names}(fn={fn_score:.2f})"
|
||||
elif fn_score > 0:
|
||||
score = 0.6 + fn_score * 0.1
|
||||
reason = f"last+weakfirst: {last_name}+{first_names}(fn={fn_score:.2f})"
|
||||
else:
|
||||
# Last name match but no first name correlation - low confidence
|
||||
score = 0.55
|
||||
reason = f"lastonly_nofirst: {last_name}"
|
||||
|
||||
# ---- Strategy 2: Sequence matcher on nodigits variant ----
|
||||
if score < 0.6 and username_no_digits != username:
|
||||
seq_score = SequenceMatcher(None, username_no_digits, aname).ratio()
|
||||
if seq_score > 0.75:
|
||||
score = max(score, seq_score * 0.9)
|
||||
reason = f"seq_nodigits: {seq_score:.2f}"
|
||||
|
||||
# ---- Strategy 3: Direct sequence match ----
|
||||
if score < 0.6:
|
||||
seq_score = SequenceMatcher(None, username, aname).ratio()
|
||||
if seq_score > 0.75:
|
||||
score = max(score, seq_score * 0.9)
|
||||
reason = f"sequence: {seq_score:.2f}"
|
||||
|
||||
# ---- Strategy 4: All account name parts in username ----
|
||||
if score < 0.6:
|
||||
all_found = True
|
||||
for part in aname_parts:
|
||||
if len(part) >= 3 and part not in username:
|
||||
all_found = False
|
||||
break
|
||||
if all_found and len(aname_parts) >= 2:
|
||||
score = 0.85
|
||||
reason = f"allparts: {aname}"
|
||||
|
||||
if score > best_score:
|
||||
best_score = score
|
||||
best_match = (acct_id, acct_name, readable_id, score)
|
||||
best_reason = reason
|
||||
|
||||
# Threshold: 0.7+
|
||||
if best_match and best_score >= 0.7:
|
||||
results.append({
|
||||
**user,
|
||||
"gaiia_id": best_match[0],
|
||||
"gaiia_name": best_match[1],
|
||||
"gaiia_readable_id": best_match[2],
|
||||
"match_type": f"fuzzy({best_reason})",
|
||||
})
|
||||
else:
|
||||
results.append({
|
||||
**user,
|
||||
"gaiia_id": user.get("gaiia_id", ""),
|
||||
"gaiia_name": user.get("gaiia_name", ""),
|
||||
"gaiia_readable_id": user.get("gaiia_readable_id", ""),
|
||||
"match_type": f"still_unmatched(best: {best_reason}, score: {round(best_score,3)})",
|
||||
})
|
||||
|
||||
return results
|
||||
|
||||
|
||||
def resolve_ambiguous(ambiguous_rows, accounts):
|
||||
"""For ambiguous matches (duplicate Gaiia names), pick the lowest readable_id."""
|
||||
results = []
|
||||
for row in ambiguous_rows:
|
||||
ambig_names = row["gaiia_name"].replace("AMBIGUOUS: ", "").split(", ")
|
||||
candidates = [(a_id, name, r_id) for a_id, name, r_id in accounts if name in ambig_names]
|
||||
if candidates:
|
||||
candidates.sort(key=lambda x: x[2])
|
||||
row["gaiia_id"] = candidates[0][0]
|
||||
row["gaiia_name"] = candidates[0][1]
|
||||
row["gaiia_readable_id"] = candidates[0][2]
|
||||
row["match_type"] = "ambiguous_resolved(lowest_id)"
|
||||
else:
|
||||
row["match_type"] = "ambiguous_unresolved"
|
||||
results.append(row)
|
||||
return results
|
||||
|
||||
|
||||
def main():
|
||||
reader = csv.DictReader(sys.stdin)
|
||||
all_rows = list(reader)
|
||||
|
||||
unmatched = [r for r in all_rows if r["match_type"] == "no_match"]
|
||||
ambiguous = [r for r in all_rows if r["match_type"] == "ambiguous"]
|
||||
|
||||
print(f"Unmatched: {len(unmatched)}, Ambiguous: {len(ambiguous)}", file=sys.stderr)
|
||||
|
||||
print("Fetching Gaiia accounts...", file=sys.stderr)
|
||||
accounts = fetch_all_accounts()
|
||||
print(f"Fetched {len(accounts)} accounts", file=sys.stderr)
|
||||
|
||||
matched_results = match_unmatched(unmatched, accounts)
|
||||
ambig_results = resolve_ambiguous(ambiguous, accounts)
|
||||
|
||||
all_results = matched_results + ambig_results
|
||||
|
||||
fieldnames = ["radius_id", "username", "password", "gaiia_id", "gaiia_name", "gaiia_readable_id", "match_type"]
|
||||
writer = csv.DictWriter(sys.stdout, fieldnames=fieldnames)
|
||||
writer.writeheader()
|
||||
for r in all_results:
|
||||
writer.writerow({k: r.get(k, "") for k in fieldnames})
|
||||
|
||||
newly_matched = sum(1 for r in matched_results if r["gaiia_id"])
|
||||
still = sum(1 for r in matched_results if "still_unmatched" in r.get("match_type", ""))
|
||||
print(f"\nNewly matched: {newly_matched}, Still unmatched: {still}", file=sys.stderr)
|
||||
|
||||
print("\nStill unmatched:", file=sys.stderr)
|
||||
for r in matched_results:
|
||||
if "still_unmatched" in r.get("match_type", ""):
|
||||
print(f" {r['username']:30s} ({r['match_type']})", file=sys.stderr)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
438
gaiia/ip_assignments_page1.json
Normal file
438
gaiia/ip_assignments_page1.json
Normal file
|
|
@ -0,0 +1,438 @@
|
|||
[
|
||||
{
|
||||
"macAddress": "DC:9F:DB:92:0D:AA",
|
||||
"ipAddress": "10.10.16.74"
|
||||
},
|
||||
{
|
||||
"macAddress": "00:27:22:14:A7:F0",
|
||||
"ipAddress": "10.10.96.80"
|
||||
},
|
||||
{
|
||||
"macAddress": "DC:9F:DB:1E:D0:24",
|
||||
"ipAddress": "10.10.96.3"
|
||||
},
|
||||
{
|
||||
"macAddress": "04:18:D6:44:4A:46",
|
||||
"ipAddress": "10.10.96.56"
|
||||
},
|
||||
{
|
||||
"macAddress": "00:27:22:14:2D:BB",
|
||||
"ipAddress": "10.10.96.76"
|
||||
},
|
||||
{
|
||||
"macAddress": "00:04:56:61:07:7D",
|
||||
"ipAddress": "10.10.96.6"
|
||||
},
|
||||
{
|
||||
"macAddress": "00:01:9F:3B:D5:99",
|
||||
"ipAddress": "100.64.25.213"
|
||||
},
|
||||
{
|
||||
"macAddress": "00:01:9F:5C:A0:49",
|
||||
"ipAddress": "100.64.174.228"
|
||||
},
|
||||
{
|
||||
"macAddress": "E0:63:DA:90:74:76",
|
||||
"ipAddress": "10.10.96.96"
|
||||
},
|
||||
{
|
||||
"macAddress": "E0:63:DA:96:C1:3F",
|
||||
"ipAddress": "10.10.0.33"
|
||||
},
|
||||
{
|
||||
"macAddress": "74:AC:B9:7A:17:9A",
|
||||
"ipAddress": "10.10.96.1"
|
||||
},
|
||||
{
|
||||
"macAddress": "00:04:56:2F:D8:4D",
|
||||
"ipAddress": "10.10.128.10"
|
||||
},
|
||||
{
|
||||
"macAddress": "74:AC:B9:7A:14:01",
|
||||
"ipAddress": "10.10.96.59"
|
||||
},
|
||||
{
|
||||
"macAddress": "00:01:9F:5C:A0:61",
|
||||
"ipAddress": "100.64.28.254"
|
||||
},
|
||||
{
|
||||
"macAddress": "00:01:9F:6B:0F:29",
|
||||
"ipAddress": "100.64.19.35"
|
||||
},
|
||||
{
|
||||
"macAddress": "FC:EC:DA:CE:68:19",
|
||||
"ipAddress": "10.10.16.70"
|
||||
},
|
||||
{
|
||||
"macAddress": "FC:EC:DA:CE:69:97",
|
||||
"ipAddress": "10.10.16.36"
|
||||
},
|
||||
{
|
||||
"macAddress": "00:04:56:C2:87:DD",
|
||||
"ipAddress": "10.10.144.3"
|
||||
},
|
||||
{
|
||||
"macAddress": "00:04:56:C1:BA:BE",
|
||||
"ipAddress": "10.10.128.5"
|
||||
},
|
||||
{
|
||||
"macAddress": "00:04:56:FE:42:87",
|
||||
"ipAddress": "10.10.128.12"
|
||||
},
|
||||
{
|
||||
"macAddress": "00:04:56:FE:31:A3",
|
||||
"ipAddress": "10.10.128.40"
|
||||
},
|
||||
{
|
||||
"macAddress": "00:01:9F:1E:9B:19",
|
||||
"ipAddress": "100.64.0.11"
|
||||
},
|
||||
{
|
||||
"macAddress": "00:04:56:FE:47:0F",
|
||||
"ipAddress": "10.10.128.41"
|
||||
},
|
||||
{
|
||||
"macAddress": "18:E8:29:38:D3:45",
|
||||
"ipAddress": "10.10.16.48"
|
||||
},
|
||||
{
|
||||
"macAddress": "18:E8:29:38:D4:2C",
|
||||
"ipAddress": "10.10.16.69"
|
||||
},
|
||||
{
|
||||
"macAddress": "18:E8:29:38:D3:BC",
|
||||
"ipAddress": "10.10.0.11"
|
||||
},
|
||||
{
|
||||
"macAddress": "FC:EC:DA:6C:FD:08",
|
||||
"ipAddress": "10.10.96.68"
|
||||
},
|
||||
{
|
||||
"macAddress": "74:83:C2:64:4A:22",
|
||||
"ipAddress": "10.10.16.72"
|
||||
},
|
||||
{
|
||||
"macAddress": "74:83:C2:64:4A:B2",
|
||||
"ipAddress": "10.10.96.78"
|
||||
},
|
||||
{
|
||||
"macAddress": "E4:38:83:A0:56:B7",
|
||||
"ipAddress": "10.10.0.18"
|
||||
},
|
||||
{
|
||||
"macAddress": "E4:38:83:A0:56:0A",
|
||||
"ipAddress": "10.10.16.67"
|
||||
},
|
||||
{
|
||||
"macAddress": "E4:38:83:A0:57:88",
|
||||
"ipAddress": "10.10.0.13"
|
||||
},
|
||||
{
|
||||
"macAddress": "E4:38:83:A0:59:53",
|
||||
"ipAddress": "10.10.0.58"
|
||||
},
|
||||
{
|
||||
"macAddress": "D0:21:F9:AF:23:3E",
|
||||
"ipAddress": "10.10.62.200"
|
||||
},
|
||||
{
|
||||
"macAddress": "D0:21:F9:F0:9F:A4",
|
||||
"ipAddress": "10.10.62.203"
|
||||
},
|
||||
{
|
||||
"macAddress": "58:C1:7A:B3:09:72",
|
||||
"ipAddress": "10.10.16.59"
|
||||
},
|
||||
{
|
||||
"macAddress": "D0:21:F9:F0:9F:F4",
|
||||
"ipAddress": "10.10.62.215"
|
||||
},
|
||||
{
|
||||
"macAddress": "E4:38:83:AA:19:E6",
|
||||
"ipAddress": "10.10.0.15"
|
||||
},
|
||||
{
|
||||
"macAddress": "70:A7:41:4A:3C:55",
|
||||
"ipAddress": "10.10.160.12"
|
||||
},
|
||||
{
|
||||
"macAddress": "D0:21:F9:AF:23:02",
|
||||
"ipAddress": "10.10.62.202"
|
||||
},
|
||||
{
|
||||
"macAddress": "58:C1:7A:B3:08:B8",
|
||||
"ipAddress": "10.10.96.91"
|
||||
},
|
||||
{
|
||||
"macAddress": "00:04:56:DB:7D:86",
|
||||
"ipAddress": "100.64.174.215"
|
||||
},
|
||||
{
|
||||
"macAddress": "E0:63:DA:0F:B7:8B",
|
||||
"ipAddress": "10.10.62.253"
|
||||
},
|
||||
{
|
||||
"macAddress": "00:01:9F:33:54:E9",
|
||||
"ipAddress": "100.64.25.32"
|
||||
},
|
||||
{
|
||||
"macAddress": "E4:38:83:AA:17:36",
|
||||
"ipAddress": "10.10.160.1"
|
||||
},
|
||||
{
|
||||
"macAddress": "00:01:9F:33:54:C1",
|
||||
"ipAddress": "100.64.0.221"
|
||||
},
|
||||
{
|
||||
"macAddress": "E4:38:83:AA:17:A6",
|
||||
"ipAddress": "10.10.160.4"
|
||||
},
|
||||
{
|
||||
"macAddress": "D0:21:F9:F0:9D:E1",
|
||||
"ipAddress": "10.10.62.212"
|
||||
},
|
||||
{
|
||||
"macAddress": "70:A7:41:4A:3C:3E",
|
||||
"ipAddress": "10.10.0.5"
|
||||
},
|
||||
{
|
||||
"macAddress": "00:01:9F:34:D4:11",
|
||||
"ipAddress": "100.64.174.207"
|
||||
},
|
||||
{
|
||||
"macAddress": "D0:21:F9:F0:9F:32",
|
||||
"ipAddress": "10.10.62.201"
|
||||
},
|
||||
{
|
||||
"macAddress": "00:04:56:DB:EC:AA",
|
||||
"ipAddress": "10.10.64.6"
|
||||
},
|
||||
{
|
||||
"macAddress": "F4:E2:C6:30:3E:1E",
|
||||
"ipAddress": "10.10.96.61"
|
||||
},
|
||||
{
|
||||
"macAddress": "F4:E2:C6:30:23:C5",
|
||||
"ipAddress": "10.10.0.8"
|
||||
},
|
||||
{
|
||||
"macAddress": "F4:E2:C6:30:2C:4E",
|
||||
"ipAddress": "10.10.96.12"
|
||||
},
|
||||
{
|
||||
"macAddress": "F4:E2:C6:30:28:40",
|
||||
"ipAddress": "10.10.96.20"
|
||||
},
|
||||
{
|
||||
"macAddress": "F4:E2:C6:30:35:33",
|
||||
"ipAddress": "10.10.0.1"
|
||||
},
|
||||
{
|
||||
"macAddress": "F4:E2:C6:30:3E:29",
|
||||
"ipAddress": "10.10.96.60"
|
||||
},
|
||||
{
|
||||
"macAddress": "F4:E2:C6:30:34:B5",
|
||||
"ipAddress": "10.10.96.25"
|
||||
},
|
||||
{
|
||||
"macAddress": "F4:E2:C6:30:3D:03",
|
||||
"ipAddress": "10.10.16.60"
|
||||
},
|
||||
{
|
||||
"macAddress": "F4:E2:C6:30:3D:E4",
|
||||
"ipAddress": "10.10.96.83"
|
||||
},
|
||||
{
|
||||
"macAddress": "F0:A7:31:74:A0:14",
|
||||
"ipAddress": "100.64.25.85"
|
||||
},
|
||||
{
|
||||
"macAddress": "F4:E2:C6:30:27:A5",
|
||||
"ipAddress": "10.10.0.2"
|
||||
},
|
||||
{
|
||||
"macAddress": "F0:A7:31:74:A0:4A",
|
||||
"ipAddress": "100.64.25.176"
|
||||
},
|
||||
{
|
||||
"macAddress": "F4:E2:C6:30:35:1D",
|
||||
"ipAddress": "10.10.96.69"
|
||||
},
|
||||
{
|
||||
"macAddress": "F4:E2:C6:30:31:14",
|
||||
"ipAddress": "10.10.0.24"
|
||||
},
|
||||
{
|
||||
"macAddress": "F4:E2:C6:30:2A:72",
|
||||
"ipAddress": "10.10.0.22"
|
||||
},
|
||||
{
|
||||
"macAddress": "E8:DA:00:17:B1:A9",
|
||||
"ipAddress": "100.64.174.203"
|
||||
},
|
||||
{
|
||||
"macAddress": "E8:DA:00:17:B1:E5",
|
||||
"ipAddress": "100.64.25.74"
|
||||
},
|
||||
{
|
||||
"macAddress": "F4:E2:C6:30:39:C0",
|
||||
"ipAddress": "10.10.0.70"
|
||||
},
|
||||
{
|
||||
"macAddress": "F4:E2:C6:30:2E:06",
|
||||
"ipAddress": "10.10.96.22"
|
||||
},
|
||||
{
|
||||
"macAddress": "F4:E2:C6:30:3F:F0",
|
||||
"ipAddress": "10.10.96.69"
|
||||
},
|
||||
{
|
||||
"macAddress": "00:04:56:DB:EE:F3",
|
||||
"ipAddress": "10.10.160.5"
|
||||
},
|
||||
{
|
||||
"macAddress": "F4:E2:C6:30:29:CF",
|
||||
"ipAddress": "10.10.96.88"
|
||||
},
|
||||
{
|
||||
"macAddress": "F4:E2:C6:30:31:02",
|
||||
"ipAddress": "10.10.0.43"
|
||||
},
|
||||
{
|
||||
"macAddress": "70:A7:41:16:63:D1",
|
||||
"ipAddress": "10.10.96.71"
|
||||
},
|
||||
{
|
||||
"macAddress": "F4:E2:C6:30:31:ED",
|
||||
"ipAddress": "10.10.0.45"
|
||||
},
|
||||
{
|
||||
"macAddress": "F4:E2:C6:30:24:65",
|
||||
"ipAddress": "10.10.96.21"
|
||||
},
|
||||
{
|
||||
"macAddress": "00:04:56:EF:9A:15",
|
||||
"ipAddress": "100.64.19.57"
|
||||
},
|
||||
{
|
||||
"macAddress": "00:04:56:60:95:0D",
|
||||
"ipAddress": "10.10.144.9"
|
||||
},
|
||||
{
|
||||
"macAddress": "00:04:56:60:8D:FB",
|
||||
"ipAddress": "10.10.64.6"
|
||||
},
|
||||
{
|
||||
"macAddress": "00:04:56:F0:36:65",
|
||||
"ipAddress": "10.10.128.31"
|
||||
},
|
||||
{
|
||||
"macAddress": "00:04:56:E8:3D:CF",
|
||||
"ipAddress": "10.10.144.28"
|
||||
},
|
||||
{
|
||||
"macAddress": "00:04:56:FA:A6:49",
|
||||
"ipAddress": "10.10.128.39"
|
||||
},
|
||||
{
|
||||
"macAddress": "78:8A:20:3C:3B:4F",
|
||||
"ipAddress": "10.10.96.70"
|
||||
},
|
||||
{
|
||||
"macAddress": "F4:E2:C6:30:48:0E",
|
||||
"ipAddress": "10.10.0.39"
|
||||
},
|
||||
{
|
||||
"macAddress": "F4:E2:C6:30:4A:E6",
|
||||
"ipAddress": "10.10.96.63"
|
||||
},
|
||||
{
|
||||
"macAddress": "F4:E2:C6:30:47:E8",
|
||||
"ipAddress": "10.10.96.32"
|
||||
},
|
||||
{
|
||||
"macAddress": "F4:E2:C6:30:46:EA",
|
||||
"ipAddress": "10.10.0.52"
|
||||
},
|
||||
{
|
||||
"macAddress": "F4:E2:C6:30:4A:82",
|
||||
"ipAddress": "10.10.16.62"
|
||||
},
|
||||
{
|
||||
"macAddress": "F4:E2:C6:30:4B:56",
|
||||
"ipAddress": "10.10.0.51"
|
||||
},
|
||||
{
|
||||
"macAddress": "D8:44:89:A6:FF:01",
|
||||
"ipAddress": "100.64.25.97"
|
||||
},
|
||||
{
|
||||
"macAddress": "F4:E2:C6:30:4B:5E",
|
||||
"ipAddress": "10.10.16.54"
|
||||
},
|
||||
{
|
||||
"macAddress": "D8:44:89:A7:08:BB",
|
||||
"ipAddress": "100.64.25.55"
|
||||
},
|
||||
{
|
||||
"macAddress": "F4:E2:C6:30:3D:42",
|
||||
"ipAddress": "10.10.0.46"
|
||||
},
|
||||
{
|
||||
"macAddress": "D8:44:89:A7:0A:29",
|
||||
"ipAddress": "100.64.25.183"
|
||||
},
|
||||
{
|
||||
"macAddress": "74:AC:B9:F2:08:EC",
|
||||
"ipAddress": "10.10.16.45"
|
||||
},
|
||||
{
|
||||
"macAddress": "F4:92:BF:D8:52:B0",
|
||||
"ipAddress": "10.10.0.10"
|
||||
},
|
||||
{
|
||||
"macAddress": "74:AC:B9:F2:09:37",
|
||||
"ipAddress": "10.10.16.65"
|
||||
},
|
||||
{
|
||||
"macAddress": "00:04:56:44:E0:32",
|
||||
"ipAddress": "10.10.144.37"
|
||||
},
|
||||
{
|
||||
"macAddress": "00:04:56:44:F5:D7",
|
||||
"ipAddress": "10.10.96.16"
|
||||
},
|
||||
{
|
||||
"macAddress": "74:AC:B9:F2:08:5C",
|
||||
"ipAddress": "10.10.16.63"
|
||||
},
|
||||
{
|
||||
"macAddress": "00:04:56:44:52:2D",
|
||||
"ipAddress": "10.10.128.23"
|
||||
},
|
||||
{
|
||||
"macAddress": "00:04:56:2F:CD:D0",
|
||||
"ipAddress": "10.10.128.26"
|
||||
},
|
||||
{
|
||||
"macAddress": "00:04:56:44:52:C6",
|
||||
"ipAddress": "10.10.96.27"
|
||||
},
|
||||
{
|
||||
"macAddress": "00:04:56:2F:CD:16",
|
||||
"ipAddress": "10.10.16.39"
|
||||
},
|
||||
{
|
||||
"macAddress": "00:04:56:2F:DB:53",
|
||||
"ipAddress": "10.10.128.7"
|
||||
},
|
||||
{
|
||||
"macAddress": "F4:92:BF:D8:55:4C",
|
||||
"ipAddress": "10.10.16.66"
|
||||
},
|
||||
{
|
||||
"macAddress": "00:04:56:44:F6:2E",
|
||||
"ipAddress": "10.10.128.29"
|
||||
}
|
||||
]
|
||||
68
gaiia/list_ip_assignments.py
Normal file
68
gaiia/list_ip_assignments.py
Normal file
|
|
@ -0,0 +1,68 @@
|
|||
#!/usr/bin/env python3
|
||||
"""List all customer inventory items with active IP assignments.
|
||||
Uses the Gaiia public API - run with GAIIA_KEY set (source .envrc)."""
|
||||
|
||||
from gaiia import GaiiaClient
|
||||
import json
|
||||
|
||||
def main():
|
||||
with GaiiaClient(timezone='America/Chicago') as g:
|
||||
# Paginate through all inventory items assigned to accounts
|
||||
all_assignments = []
|
||||
after = None
|
||||
page = 0
|
||||
|
||||
while True:
|
||||
page += 1
|
||||
query = """
|
||||
query GetAssignments($after: String) {
|
||||
inventoryItems(
|
||||
filters: {assigneeType: {equals: ACCOUNT}}
|
||||
first: 250
|
||||
after: $after
|
||||
) {
|
||||
nodes {
|
||||
ipAddressAssignments {
|
||||
nodes { ipAddress macAddress isActive }
|
||||
}
|
||||
}
|
||||
pageInfo { hasNextPage endCursor }
|
||||
totalCount
|
||||
}
|
||||
}
|
||||
"""
|
||||
data = g.query(query, {"after": after} if after else {})
|
||||
result = data['inventoryItems']
|
||||
|
||||
if page == 1:
|
||||
print(f"Total inventory items assigned to accounts: {result['totalCount']}")
|
||||
|
||||
for item in result['nodes']:
|
||||
for a in item.get('ipAddressAssignments', {}).get('nodes', []):
|
||||
if a.get('isActive') != False: # active or None
|
||||
all_assignments.append({
|
||||
'macAddress': a['macAddress'],
|
||||
'ipAddress': a['ipAddress']
|
||||
})
|
||||
|
||||
page_info = result['pageInfo']
|
||||
if not page_info.get('hasNextPage'):
|
||||
break
|
||||
after = page_info['endCursor']
|
||||
|
||||
print(f"Active IP assignments found: {len(all_assignments)}")
|
||||
print()
|
||||
|
||||
# Print first 20
|
||||
for a in all_assignments[:20]:
|
||||
print(f" {a['macAddress']} -> {a['ipAddress']}")
|
||||
if len(all_assignments) > 20:
|
||||
print(f" ... and {len(all_assignments) - 20} more")
|
||||
|
||||
# Save to file
|
||||
with open('/Users/graham/dev/network/gaiia/ip_assignments.json', 'w') as f:
|
||||
json.dump(all_assignments, f, indent=2)
|
||||
print(f"\nSaved {len(all_assignments)} assignments to ip_assignments.json")
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
202
gaiia/match_radius.py
Normal file
202
gaiia/match_radius.py
Normal file
|
|
@ -0,0 +1,202 @@
|
|||
#!/usr/bin/env python3
|
||||
"""
|
||||
Match RADIUS PPPoE usernames to Gaiia accounts by exact name-to-username mapping.
|
||||
Deterministic only — no fuzzy matching. Leaves ambiguous/unmatched rows blank.
|
||||
|
||||
A Gaiia displayName like "Jose Ortiz" generates candidate usernames:
|
||||
joseortiz, jortiz, joseo, jose, ortiz
|
||||
The RADIUS username must match one of these exactly.
|
||||
"""
|
||||
|
||||
import csv
|
||||
import json
|
||||
import re
|
||||
import subprocess
|
||||
import sys
|
||||
from collections import defaultdict
|
||||
|
||||
GAIIA_KEY = "gaiia_sk_live_955b27e3680747a69ad40b403a912155_b0d1be3c"
|
||||
GAIIA_ENDPOINT = "https://api.gaiia.com/api/v1"
|
||||
|
||||
|
||||
def fetch_all_accounts():
|
||||
"""Fetch all Gaiia accounts with id and displayName."""
|
||||
accounts = []
|
||||
cursor = None
|
||||
while True:
|
||||
after_arg = f', after: "{cursor}"' if cursor else ""
|
||||
query = f"""
|
||||
query {{
|
||||
accounts(first: 250{after_arg}) {{
|
||||
nodes {{ id readableId name displayName }}
|
||||
pageInfo {{ hasNextPage endCursor }}
|
||||
}}
|
||||
}}
|
||||
"""
|
||||
result = subprocess.run(
|
||||
[
|
||||
"curl", "-s",
|
||||
"-X", "POST", GAIIA_ENDPOINT,
|
||||
"-H", "Content-Type: application/json",
|
||||
"-H", f"X-Gaiia-Api-Key: {GAIIA_KEY}",
|
||||
"-H", "x-timezone: America/Chicago",
|
||||
"-d", json.dumps({"query": query}),
|
||||
],
|
||||
capture_output=True, text=True, timeout=30,
|
||||
)
|
||||
data = json.loads(result.stdout)
|
||||
nodes = data["data"]["accounts"]["nodes"]
|
||||
for n in nodes:
|
||||
name = n.get("displayName") or n.get("name") or ""
|
||||
if name:
|
||||
accounts.append((n["id"], name.strip(), n["readableId"]))
|
||||
page_info = data["data"]["accounts"]["pageInfo"]
|
||||
if not page_info["hasNextPage"]:
|
||||
break
|
||||
cursor = page_info["endCursor"]
|
||||
return accounts
|
||||
|
||||
|
||||
def generate_username_variants(display_name):
|
||||
"""Generate candidate PPPoE-style usernames from a Gaiia display name.
|
||||
|
||||
"Jose Ortiz" -> {joseortiz, jortiz, jose, ortiz}
|
||||
"Graham McIntire" -> {grahammcintire, gmcintire, graham, mcintire}
|
||||
"Kory Biggs Shop" -> {korybiggsshop, kbiggs, kory, biggs, ...}
|
||||
"Mid Valley Pumping LLC" -> {midvalleypumpingllc, midvalley, ...}
|
||||
"""
|
||||
name = display_name.lower().strip()
|
||||
# Remove punctuation
|
||||
name = re.sub(r'[^\w\s]', '', name)
|
||||
name = re.sub(r'\s+', ' ', name).strip()
|
||||
parts = name.split()
|
||||
|
||||
variants = set()
|
||||
|
||||
# Full concatenation: firstlast with no spaces
|
||||
variants.add("".join(parts))
|
||||
|
||||
# First initial + last name: e.g. jortiz, gmcintire
|
||||
if len(parts) >= 2:
|
||||
variants.add(parts[0][0] + parts[-1])
|
||||
# Also first name + last name initial
|
||||
variants.add(parts[0] + parts[-1][0])
|
||||
|
||||
# Each individual part
|
||||
for p in parts:
|
||||
variants.add(p)
|
||||
|
||||
# First + last (just the two parts concatenated)
|
||||
if len(parts) >= 2:
|
||||
variants.add(parts[0] + parts[-1])
|
||||
|
||||
# Handle numbered suffixes (e.g. "Perla Chavez 2" -> "perlachavez2")
|
||||
if len(parts) >= 3 and re.match(r'^\d+$', parts[-1]):
|
||||
variants.add("".join(parts[:-1]) + parts[-1])
|
||||
|
||||
return variants
|
||||
|
||||
|
||||
def parse_radius_input(text):
|
||||
"""Parse the pipe-delimited RADIUS user table."""
|
||||
users = []
|
||||
pattern = r'\|\s*(\d+)\s*\|\s*(\S+)\s*\|\s*Clear(?:t|T)ext-Password\s*\|\s*:=\s*\|\s*(\S+)\s*\|'
|
||||
for match in re.finditer(pattern, text):
|
||||
users.append({
|
||||
"radius_id": int(match.group(1)),
|
||||
"username": match.group(2),
|
||||
"password": match.group(3),
|
||||
})
|
||||
return users
|
||||
|
||||
|
||||
def match_users(radius_users, accounts):
|
||||
"""Match RADIUS users to Gaiia accounts by exact username variant matching."""
|
||||
# Build mapping: username_variant -> list of (account_id, account_name, readable_id)
|
||||
variant_map = defaultdict(list)
|
||||
for acct_id, acct_name, readable_id in accounts:
|
||||
for variant in generate_username_variants(acct_name):
|
||||
variant_map[variant].append((acct_id, acct_name, readable_id))
|
||||
|
||||
# Track which Gaiia accounts have been used (to avoid double-matching)
|
||||
used_accounts = set()
|
||||
|
||||
results = []
|
||||
for user in radius_users:
|
||||
username = user["username"].lower()
|
||||
candidates = variant_map.get(username, [])
|
||||
|
||||
# Filter out already-used accounts
|
||||
available = [(a, n, r) for a, n, r in candidates if a not in used_accounts]
|
||||
|
||||
if len(available) == 1:
|
||||
acct_id, acct_name, readable_id = available[0]
|
||||
used_accounts.add(acct_id)
|
||||
results.append({
|
||||
**user,
|
||||
"gaiia_id": acct_id,
|
||||
"gaiia_name": acct_name,
|
||||
"gaiia_readable_id": readable_id,
|
||||
"match_type": "exact",
|
||||
})
|
||||
elif len(available) > 1:
|
||||
# Multiple candidates — leave blank for manual review
|
||||
results.append({
|
||||
**user,
|
||||
"gaiia_id": "",
|
||||
"gaiia_name": f"AMBIGUOUS: {', '.join(n for _,n,_ in available)}",
|
||||
"gaiia_readable_id": "",
|
||||
"match_type": "ambiguous",
|
||||
})
|
||||
else:
|
||||
results.append({
|
||||
**user,
|
||||
"gaiia_id": "",
|
||||
"gaiia_name": "",
|
||||
"gaiia_readable_id": "",
|
||||
"match_type": "no_match",
|
||||
})
|
||||
|
||||
return results
|
||||
|
||||
|
||||
def main():
|
||||
text = sys.stdin.read()
|
||||
|
||||
radius_users = parse_radius_input(text)
|
||||
print(f"Parsed {len(radius_users)} RADIUS users", file=sys.stderr)
|
||||
|
||||
print("Fetching Gaiia accounts...", file=sys.stderr)
|
||||
accounts = fetch_all_accounts()
|
||||
print(f"Fetched {len(accounts)} Gaiia accounts with names", file=sys.stderr)
|
||||
|
||||
results = match_users(radius_users, accounts)
|
||||
|
||||
writer = csv.writer(sys.stdout)
|
||||
writer.writerow(["radius_id", "username", "password", "gaiia_id", "gaiia_name", "gaiia_readable_id", "match_type"])
|
||||
for r in results:
|
||||
writer.writerow([
|
||||
r["radius_id"], r["username"], r["password"],
|
||||
r["gaiia_id"], r["gaiia_name"], r["gaiia_readable_id"], r["match_type"],
|
||||
])
|
||||
|
||||
matched = sum(1 for r in results if r["gaiia_id"])
|
||||
ambiguous = sum(1 for r in results if r["match_type"] == "ambiguous")
|
||||
unmatched = sum(1 for r in results if r["match_type"] == "no_match")
|
||||
print(f"\nMatched: {matched}, Ambiguous: {ambiguous}, No match: {unmatched} (total: {len(results)})", file=sys.stderr)
|
||||
|
||||
if ambiguous:
|
||||
print("\nAMBIGUOUS (multiple Gaiia accounts match same username):", file=sys.stderr)
|
||||
for r in results:
|
||||
if r["match_type"] == "ambiguous":
|
||||
print(f" {r['username']}: {r['gaiia_name']}", file=sys.stderr)
|
||||
|
||||
if unmatched:
|
||||
print("\nNO MATCH:", file=sys.stderr)
|
||||
for r in results:
|
||||
if r["match_type"] == "no_match":
|
||||
print(f" {r['username']}", file=sys.stderr)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
101
gaiia/output.txt
Normal file
101
gaiia/output.txt
Normal file
|
|
@ -0,0 +1,101 @@
|
|||
Fetching all accounts from Gaiia...
|
||||
Fetched 50/458 accounts...
|
||||
Fetched 100/458 accounts...
|
||||
Fetched 150/458 accounts...
|
||||
Fetched 200/458 accounts...
|
||||
Fetched 250/458 accounts...
|
||||
Fetched 300/458 accounts...
|
||||
Fetched 350/458 accounts...
|
||||
Fetched 400/458 accounts...
|
||||
Fetched 450/458 accounts...
|
||||
Fetched 458/458 accounts...
|
||||
Total: 458 accounts
|
||||
|
||||
===========================================================================
|
||||
CLIMAX PPPoE → GAIIA ACCOUNT MATCHING
|
||||
===========================================================================
|
||||
PPPoE PPPoE IP Gaiia Name Account ID
|
||||
------------------------------------------------------------------------------
|
||||
charlesboone 100.64.7.247 Charles Boone 62
|
||||
amberprater 100.64.7.243 Amber Prater 855
|
||||
matthewgoodwin 100.64.7.241 Matthew Goodwin 2453
|
||||
robbymccollom 100.64.7.239 Robby McCollom 297
|
||||
chasewilliams 100.64.7.235 Chase Williams 2042
|
||||
cynthiajones 100.64.7.233 Cynthia Jones 1778
|
||||
janicealexander 100.64.7.225 Janice Alexander 165
|
||||
michaelray 204.110.188.38 Michael Ray 379
|
||||
marysmelser 100.64.7.218 Mary Smelser 2385
|
||||
eddieyarbrough 100.64.7.216 Eddie Yarbrough 125
|
||||
timgilbert 100.64.7.213 Tim Gilbert 346
|
||||
chadwhitsell 100.64.7.211 Chad Whitsell 61
|
||||
donnacampbell 100.64.7.209 Donna Campbell 2530
|
||||
bryangoulart 100.64.7.205 Brian Goulart 2076
|
||||
crystalharney 100.64.7.201 Crystal Harney 2233
|
||||
johnvayo 100.64.7.197 John Vayo 2275
|
||||
richardbarragan 100.64.7.193 richard barragan 1314
|
||||
carolstrickland 100.64.7.189 Carol Strickland 2410
|
||||
timbagert 100.64.7.140 Tim Bagert 343
|
||||
douggarber 100.64.7.136 Doug Garber 2360
|
||||
janetkern 100.64.7.61 Janet Kern 449
|
||||
gregmcintire 100.64.7.59 Greg McIntire 380
|
||||
jenniferboon 100.64.7.35 Jennifer Boon 2200
|
||||
glendabeauchamp 100.64.7.15 Glenda Beauchamp 2133
|
||||
jmichaelculverhouse 100.64.7.250 J'michael Culverhouse 1838
|
||||
elviraquezada 100.64.7.249 Elvira Quezada 960
|
||||
taekim 204.110.188.42 Tae Kim 408
|
||||
ruthfengler 100.64.7.246 Ruth Fengler 304
|
||||
rhondabolton 100.64.7.231 Rhonda Bolton 295
|
||||
gordonhamilton 100.64.7.237 (not in Gaiia) -
|
||||
tammieventris 100.64.7.232 (not in Gaiia) -
|
||||
buddyswan 100.64.7.245 (not in Gaiia) -
|
||||
|
||||
Matched: 29 | Not in Gaiia: 3
|
||||
Not in Gaiia:
|
||||
- gordonhamilton (100.64.7.237)
|
||||
- tammieventris (100.64.7.232)
|
||||
- buddyswan (100.64.7.245)
|
||||
|
||||
===========================================================================
|
||||
IP ADDRESS COMPARISON (PPPoE active IP vs Gaiia inventory item IP)
|
||||
===========================================================================
|
||||
charlesboone 100.64.7.247 MISMATCH (Gaiia has: 10.10.16.73)
|
||||
amberprater 100.64.7.243 MISMATCH (Gaiia has: 10.10.16.56)
|
||||
matthewgoodwin 100.64.7.241 MISMATCH (Gaiia has: 10.10.16.34)
|
||||
robbymccollom 100.64.7.239 MISMATCH (Gaiia has: 10.10.16.69)
|
||||
chasewilliams 100.64.7.235 MISMATCH (Gaiia has: 10.10.16.66)
|
||||
cynthiajones 100.64.7.233 MISMATCH (Gaiia has: 10.10.16.72)
|
||||
janicealexander 100.64.7.225 MISMATCH (Gaiia has: 10.10.16.65)
|
||||
michaelray 204.110.188.38 (no inventory items in Gaiia)
|
||||
marysmelser 100.64.7.218 MATCH (item: AC1200MS)
|
||||
eddieyarbrough 100.64.7.216 NO IP SET (has inventory items but no IPs)
|
||||
timgilbert 100.64.7.213 MISMATCH (Gaiia has: 10.10.16.71)
|
||||
chadwhitsell 100.64.7.211 MISMATCH (Gaiia has: 10.10.16.75)
|
||||
donnacampbell 100.64.7.209 MATCH (item: AC1200-Mesh)
|
||||
bryangoulart 100.64.7.205 MISMATCH (Gaiia has: 10.10.16.62)
|
||||
crystalharney 100.64.7.201 MATCH (item: AC1200-Mesh)
|
||||
johnvayo 100.64.7.197 MATCH (item: AC1200-Mesh)
|
||||
richardbarragan 100.64.7.193 MATCH (item: AC1200-Mesh)
|
||||
carolstrickland 100.64.7.189 MISMATCH (Gaiia has: 10.10.16.37)
|
||||
timbagert 100.64.7.140 MATCH (item: AC1200-Mesh)
|
||||
douggarber 100.64.7.136 MISMATCH (Gaiia has: 10.10.16.57)
|
||||
janetkern 100.64.7.61 MISMATCH (Gaiia has: 10.10.16.54)
|
||||
gregmcintire 100.64.7.59 (no inventory items in Gaiia)
|
||||
jenniferboon 100.64.7.35 MATCH (item: AC1200-Mesh)
|
||||
glendabeauchamp 100.64.7.15 MATCH (item: AC1200-Mesh)
|
||||
jmichaelculverhouse 100.64.7.250 NO IP SET (has inventory items but no IPs)
|
||||
elviraquezada 100.64.7.249 MISMATCH (Gaiia has: 10.10.16.67)
|
||||
taekim 204.110.188.42 MISMATCH (Gaiia has: 10.10.16.70)
|
||||
ruthfengler 100.64.7.246 MISMATCH (Gaiia has: 10.10.16.46)
|
||||
rhondabolton 100.64.7.231 MISMATCH (Gaiia has: 10.10.16.61)
|
||||
|
||||
--- Summary ---
|
||||
Accounts in Gaiia: 29/32
|
||||
Not in Gaiia: 3
|
||||
IP matches PPPoE: 8
|
||||
IP mismatch (different IP): 17
|
||||
No inventory / no IP in Gaiia: 4
|
||||
|
||||
Accounts missing from Gaiia (need investigation):
|
||||
gordonhamilton IP=100.64.7.237 MAC=A0:63:91:3A:29:B1
|
||||
tammieventris IP=100.64.7.232 MAC=98:25:4A:04:A5:E1
|
||||
buddyswan IP=100.64.7.245 MAC=00:01:9F:34:D2:21
|
||||
198
gaiia/pppoe_full_sync_workflow.json
Normal file
198
gaiia/pppoe_full_sync_workflow.json
Normal file
|
|
@ -0,0 +1,198 @@
|
|||
{
|
||||
"name": "pppoe auth",
|
||||
"description": "WEBHOOK: PPPoE connect/disconnect → account lookup → Preseem sync. Connect (bound=1): BYOD if needed, assign IP, add MAC to Preseem with all inventoried equipment. Disconnect (bound=0): remove MAC from Preseem.",
|
||||
"trigger": {
|
||||
"type": "WEBHOOK",
|
||||
"inputJsonSchema": {
|
||||
"type": "object",
|
||||
"required": ["bound", "callingstationid", "framedip", "username"],
|
||||
"properties": {
|
||||
"bound": { "type": "string" },
|
||||
"framedip": { "type": "string" },
|
||||
"username": { "type": "string" },
|
||||
"callingstationid": { "type": "string" }
|
||||
}
|
||||
},
|
||||
"nextSteps": [{ "slug": "get-account-id" }]
|
||||
},
|
||||
"steps": [
|
||||
{
|
||||
"name": "Get Account ID from RADIUS Username",
|
||||
"slug": "get-account-id",
|
||||
"type": "CUSTOM_WORKFLOW",
|
||||
"nodeSlug": "get-account-id-from-radius-username",
|
||||
"allowSkipExecution": false,
|
||||
"inputs": [
|
||||
{ "key": "username", "type": "STATE", "value": "input.username" }
|
||||
],
|
||||
"nextSteps": [{ "slug": "is-connect" }],
|
||||
"errorSteps": [{ "slug": "end", "errorCodes": ["USERNAME_NOT_FOUND"] }]
|
||||
},
|
||||
{
|
||||
"name": "Is this a connect?",
|
||||
"slug": "is-connect",
|
||||
"type": "CONDITION",
|
||||
"nodeSlug": "condition",
|
||||
"condition": "({ secrets, state, utils, variables }) => {\n return state.input.bound === '1';\n}",
|
||||
"elseSteps": [{ "slug": "expire-ip-assignment" }],
|
||||
"thenSteps": [{ "slug": "check-inventory" }]
|
||||
},
|
||||
{
|
||||
"name": "Expire IP assignment on disconnect",
|
||||
"slug": "expire-ip-assignment",
|
||||
"type": "GAIIA_API_GATEWAY_V0_GRAPHQL_REQUEST",
|
||||
"nodeSlug": "gaiia-internal-api-gateway-graphql-request",
|
||||
"allowSkipExecution": false,
|
||||
"inputs": [
|
||||
{
|
||||
"key": "query",
|
||||
"type": "LITERAL",
|
||||
"value": "string:mutation ExpireIp($input: UpsertMacAddressIpAddressAssignationsInput!) {\n upsertMacAddressIpAddressAssignations(input: $input) {\n assignations { macAddress ipAddress }\n }\n}"
|
||||
},
|
||||
{
|
||||
"key": "variables",
|
||||
"type": "FUNCTION",
|
||||
"value": "({ state }) => {\n const rawMac = state.input.callingstationid;\n const cleanedMac = rawMac.startsWith('1:') ? rawMac.slice(2) : rawMac;\n const formattedMac = cleanedMac\n .split(':')\n .map((segment) => segment.padStart(2, '0'))\n .join(':')\n .toUpperCase();\n return {\n input: {\n assignations: {\n macAddress: formattedMac,\n ipAddress: state.input.framedip,\n expired: true\n }\n }\n };\n}"
|
||||
}
|
||||
],
|
||||
"nextSteps": [{ "slug": "get-account-macs-and-plan" }]
|
||||
},
|
||||
{
|
||||
"name": "Check if MAC is already inventoried",
|
||||
"slug": "check-inventory",
|
||||
"type": "GAIIA_API_GATEWAY_V0_GRAPHQL_REQUEST",
|
||||
"nodeSlug": "gaiia-internal-api-gateway-graphql-request",
|
||||
"allowSkipExecution": false,
|
||||
"outputMapper": "({ output, state }) => {\n const items = output.response.findInventoryItemsFromDeviceIdentifiers;\n const assigned = items.filter(\n (item) => item.inventoryItem?.assignation?.assignee?.__typename === 'Account'\n );\n return assigned;\n}",
|
||||
"inputs": [
|
||||
{
|
||||
"key": "query",
|
||||
"type": "LITERAL",
|
||||
"value": "string:query FindItem($input: FindInventoryItemsFromDeviceIdentifiersQueryInput!) {\n findInventoryItemsFromDeviceIdentifiers(input: $input) {\n identifier\n inventoryItem {\n id\n model { name }\n assignation {\n assignee {\n __typename\n ... on Account { id name }\n }\n }\n }\n }\n}"
|
||||
},
|
||||
{
|
||||
"key": "variables",
|
||||
"type": "FUNCTION",
|
||||
"value": "({ state }) => {\n const rawMac = state.input.callingstationid;\n const cleanedMac = rawMac.startsWith('1:') ? rawMac.slice(2) : rawMac;\n const formattedMac = cleanedMac\n .split(':')\n .map((segment) => segment.padStart(2, '0'))\n .join(':')\n .toUpperCase();\n return { input: { identifiers: [formattedMac] } };\n}"
|
||||
}
|
||||
],
|
||||
"nextSteps": [{ "slug": "if-byod-needed" }]
|
||||
},
|
||||
{
|
||||
"name": "Is BYOD needed?",
|
||||
"slug": "if-byod-needed",
|
||||
"type": "CONDITION",
|
||||
"nodeSlug": "condition",
|
||||
"condition": "({ secrets, state, utils, variables }) => {\n const found = state.steps['check-inventory'].output;\n return !found || found.length === 0;\n}",
|
||||
"elseSteps": [{ "slug": "assign-ip-address" }],
|
||||
"thenSteps": [{ "slug": "find-byod-model" }]
|
||||
},
|
||||
{
|
||||
"name": "Find Customer Provided Router model",
|
||||
"slug": "find-byod-model",
|
||||
"type": "GAIIA_API_GATEWAY_V1_GRAPHQL_REQUEST",
|
||||
"nodeSlug": "gaiia-public-api-gateway-graphql-request",
|
||||
"allowSkipExecution": false,
|
||||
"outputMapper": "({ output }) => {\n const model = output.response.inventoryModels.nodes[0];\n if (!model) {\n workflow.fail('Customer Provided Router model not found', 'MODEL_NOT_FOUND');\n }\n const macField = model.fields.nodes.find(\n (field) => field.name === 'MAC Address'\n );\n if (!macField) {\n workflow.fail('MAC Address field not found on Customer Provided Router model', 'MAC_FIELD_NOT_FOUND');\n }\n return { id: model.id, macFieldId: macField.id };\n}",
|
||||
"inputs": [
|
||||
{
|
||||
"key": "query",
|
||||
"type": "LITERAL",
|
||||
"value": "string:query GetModel($filters: InventoryModelQueryFilters) {\n inventoryModels(filters: $filters) {\n nodes {\n id\n name\n fields { nodes { id name } }\n }\n }\n}"
|
||||
},
|
||||
{
|
||||
"key": "variables",
|
||||
"type": "FUNCTION",
|
||||
"value": "({ secrets, state, utils, variables }) => {\n return { filters: { name: { equals: 'Customer Provided Router' } } };\n}"
|
||||
}
|
||||
],
|
||||
"nextSteps": [{ "slug": "create-byod" }]
|
||||
},
|
||||
{
|
||||
"name": "Create BYOD inventory item",
|
||||
"slug": "create-byod",
|
||||
"type": "GAIIA_API_GATEWAY_V1_GRAPHQL_REQUEST",
|
||||
"nodeSlug": "gaiia-public-api-gateway-graphql-request",
|
||||
"allowSkipExecution": false,
|
||||
"inputs": [
|
||||
{
|
||||
"key": "query",
|
||||
"type": "LITERAL",
|
||||
"value": "string:mutation AddInventoryItems($input: CreateInventoryItemsInput!) {\n addInventoryItems(input: $input) {\n inventoryItemsCreationResult { addedItems { id } }\n errors { code message }\n }\n}"
|
||||
},
|
||||
{
|
||||
"key": "variables",
|
||||
"type": "FUNCTION",
|
||||
"value": "({ state }) => {\n const rawMac = state.input.callingstationid;\n const cleanedMac = rawMac.startsWith('1:') ? rawMac.slice(2) : rawMac;\n const formattedMac = cleanedMac\n .split(':')\n .map((segment) => segment.padStart(2, '0'))\n .join(':');\n const accountId = state.steps['get-account-id'].output.accounts[0].accountId;\n const { id: modelId, macFieldId } = state.steps['find-byod-model'].output;\n return {\n input: {\n items: [{\n fields: [{ modelFieldId: macFieldId, data: formattedMac }],\n modelId: modelId,\n equipmentConditionType: 'NEW',\n purchasePriceInCents: 0,\n status: 'FUNCTIONAL',\n assignation: { assigneeId: accountId, assigneeType: 'ACCOUNT' }\n }]\n }\n };\n}"
|
||||
}
|
||||
],
|
||||
"nextSteps": [{ "slug": "assign-ip-address" }]
|
||||
},
|
||||
{
|
||||
"name": "Assign IP address to MAC",
|
||||
"slug": "assign-ip-address",
|
||||
"type": "GAIIA_API_GATEWAY_V0_GRAPHQL_REQUEST",
|
||||
"nodeSlug": "gaiia-internal-api-gateway-graphql-request",
|
||||
"allowSkipExecution": false,
|
||||
"inputs": [
|
||||
{
|
||||
"key": "query",
|
||||
"type": "LITERAL",
|
||||
"value": "string:mutation UpsertIp($input: UpsertMacAddressIpAddressAssignationsInput!) {\n upsertMacAddressIpAddressAssignations(input: $input) {\n assignations { macAddress ipAddress }\n }\n}"
|
||||
},
|
||||
{
|
||||
"key": "variables",
|
||||
"type": "FUNCTION",
|
||||
"value": "({ state }) => {\n const rawMac = state.input.callingstationid;\n const cleanedMac = rawMac.startsWith('1:') ? rawMac.slice(2) : rawMac;\n const formattedMac = cleanedMac\n .split(':')\n .map((segment) => segment.padStart(2, '0'))\n .join(':')\n .toUpperCase();\n return {\n input: {\n assignations: {\n macAddress: formattedMac,\n ipAddress: state.input.framedip,\n expired: false\n }\n }\n };\n}"
|
||||
}
|
||||
],
|
||||
"nextSteps": [{ "slug": "get-account-macs-and-plan" }]
|
||||
},
|
||||
{
|
||||
"name": "Get all inventoried MACs and plan",
|
||||
"slug": "get-account-macs-and-plan",
|
||||
"type": "GAIIA_API_GATEWAY_V1_GRAPHQL_REQUEST",
|
||||
"nodeSlug": "gaiia-public-api-gateway-graphql-request",
|
||||
"allowSkipExecution": false,
|
||||
"outputMapper": "({ output, state }) => {\n const account = output.response.account;\n if (!account) {\n workflow.fail('Account not found', 'ACCOUNT_NOT_FOUND');\n }\n const macs = [];\n for (const assign of (account.assignedInventoryItems?.nodes || [])) {\n const inv = assign.inventoryItem || {};\n for (const f of (inv.fields?.nodes || [])) {\n const fieldName = (f.modelField?.name || '').toLowerCase();\n const data = (f.data || '').trim();\n if (fieldName.includes('mac') && data && data !== '00:00:00:00:00:00') {\n const cleaned = data.startsWith('1:') ? data.slice(2) : data;\n const parts = cleaned.split(':');\n if (parts.length === 6) {\n macs.push(parts.map((p) => p.padStart(2, '0').toUpperCase()).join(':'));\n }\n }\n }\n for (const a of (inv.ipAddressAssignments?.nodes || [])) {\n if (a.macAddress) macs.push(a.macAddress.toUpperCase());\n }\n }\n const plan = (account.billingSubscriptions?.nodes || [])\n .map((s) => s.productVersion?.product?.slug)\n .find(Boolean) || '';\n return { macs: [...new Set(macs)], plan, gaiiaId: account.readableId };\n}",
|
||||
"inputs": [
|
||||
{
|
||||
"key": "query",
|
||||
"type": "FUNCTION",
|
||||
"value": "({ state }) => {\n const accountId = state.steps['get-account-id'].output.accounts[0].accountId;\n return `query { account(id: \"${accountId}\") { readableId assignedInventoryItems { nodes { inventoryItem { fields { nodes { data modelField { name } } } ipAddressAssignments { nodes { macAddress } } } } } billingSubscriptions { nodes { productVersion { product { slug } } } } } }`;\n}"
|
||||
},
|
||||
{
|
||||
"key": "variables",
|
||||
"type": "FUNCTION",
|
||||
"value": "({ state }) => ({})"
|
||||
}
|
||||
],
|
||||
"nextSteps": [{ "slug": "preseem-update" }]
|
||||
},
|
||||
{
|
||||
"name": "Update Preseem",
|
||||
"slug": "preseem-update",
|
||||
"type": "PRESEEM_REST_REQUEST",
|
||||
"nodeSlug": "preseem-rest-request",
|
||||
"allowSkipExecution": false,
|
||||
"inputs": [
|
||||
{
|
||||
"key": "method",
|
||||
"type": "LITERAL",
|
||||
"value": "string:PUT"
|
||||
},
|
||||
{
|
||||
"key": "path",
|
||||
"type": "FUNCTION",
|
||||
"value": "({ state }) => {\n const { gaiiaId } = state.steps['get-account-macs-and-plan'].output;\n return `/services/${gaiiaId}`;\n}"
|
||||
},
|
||||
{
|
||||
"key": "body",
|
||||
"type": "FUNCTION",
|
||||
"value": "({ state }) => {\n const { gaiiaId, macs, plan } = state.steps['get-account-macs-and-plan'].output;\n const rawMac = state.input.callingstationid;\n const cleanedMac = rawMac.startsWith('1:') ? rawMac.slice(2) : rawMac;\n const connectingMac = cleanedMac\n .split(':')\n .map((segment) => segment.padStart(2, '0'))\n .join(':')\n .toLowerCase();\n const framedIp = state.input.framedip;\n // On connect (bound=1): include connecting MAC with its IP. On disconnect (bound=0): exclude it.\n const inventoriedMacs = macs.map((m) => m.toLowerCase());\n const attachments = inventoriedMacs\n .filter((m) => state.input.bound === '0' || m !== connectingMac)\n .map((m) => ({ cpe_mac: m }));\n if (state.input.bound === '1') {\n attachments.push({ cpe_mac: connectingMac, network_prefixes: [framedIp] });\n }\n return {\n id: String(gaiiaId),\n attachments,\n account: String(gaiiaId),\n package: plan\n };\n}"
|
||||
}
|
||||
],
|
||||
"nextSteps": [{ "slug": "end" }]
|
||||
}
|
||||
]
|
||||
}
|
||||
63
gaiia/pyproject.toml
Normal file
63
gaiia/pyproject.toml
Normal file
|
|
@ -0,0 +1,63 @@
|
|||
[build-system]
|
||||
requires = ["hatchling>=1.18"]
|
||||
build-backend = "hatchling.build"
|
||||
|
||||
[project]
|
||||
name = "gaiia"
|
||||
version = "0.1.0"
|
||||
description = "Python client for the gaiia GraphQL API and webhooks."
|
||||
readme = "README.md"
|
||||
license = { file = "LICENSE" }
|
||||
requires-python = ">=3.10"
|
||||
authors = [{ name = "Graham McIntire", email = "graham@mcintire.me" }]
|
||||
keywords = ["gaiia", "graphql", "api", "wisp", "isp", "client"]
|
||||
classifiers = [
|
||||
"Development Status :: 3 - Alpha",
|
||||
"Intended Audience :: Developers",
|
||||
"License :: OSI Approved :: MIT License",
|
||||
"Programming Language :: Python :: 3",
|
||||
"Programming Language :: Python :: 3.10",
|
||||
"Programming Language :: Python :: 3.11",
|
||||
"Programming Language :: Python :: 3.12",
|
||||
"Programming Language :: Python :: 3.13",
|
||||
"Topic :: Software Development :: Libraries :: Python Modules",
|
||||
"Typing :: Typed",
|
||||
]
|
||||
dependencies = ["httpx>=0.27"]
|
||||
|
||||
[project.optional-dependencies]
|
||||
dev = [
|
||||
"pytest>=8",
|
||||
"pytest-asyncio>=0.23",
|
||||
"respx>=0.21",
|
||||
"mypy>=1.10",
|
||||
"ruff>=0.5",
|
||||
]
|
||||
|
||||
[project.urls]
|
||||
Homepage = "https://app.gaiia.com/docs/overview"
|
||||
Documentation = "https://app.gaiia.com/docs/overview"
|
||||
Source = "https://github.com/grahammcintire/gaiia-py"
|
||||
Issues = "https://github.com/grahammcintire/gaiia-py/issues"
|
||||
|
||||
[tool.hatch.build.targets.sdist]
|
||||
include = ["src/gaiia", "README.md", "LICENSE"]
|
||||
|
||||
[tool.hatch.build.targets.wheel]
|
||||
packages = ["src/gaiia"]
|
||||
|
||||
[tool.pytest.ini_options]
|
||||
testpaths = ["tests"]
|
||||
asyncio_mode = "auto"
|
||||
|
||||
[tool.ruff]
|
||||
line-length = 100
|
||||
target-version = "py310"
|
||||
|
||||
[tool.ruff.lint]
|
||||
select = ["E", "F", "I", "B", "UP", "SIM"]
|
||||
|
||||
[tool.mypy]
|
||||
python_version = "3.10"
|
||||
strict = true
|
||||
warn_unused_ignores = true
|
||||
68
gaiia/radius_gaiia_fuzzy.csv
Normal file
68
gaiia/radius_gaiia_fuzzy.csv
Normal file
|
|
@ -0,0 +1,68 @@
|
|||
radius_id,username,password,gaiia_id,gaiia_name,gaiia_readable_id,match_type
|
||||
29521,tiffanyyarley-olenak,98HTZ9Q8FARG,account_cwmjmXkrYeba8h8RGqLm6E,Tiffany Yarley-Olenak,2393,fuzzy(sequence: 0.95)
|
||||
29395,vanessagomez1,J5JEW3HEBVAF,account_gb82EhqTwbNWWEovPu4oKZ,Vanessa Gomez,2264,fuzzy(last+first: gomez+vanessa(fn=1.00))
|
||||
29611,brittannieredden1,TYFTUEZ7429,account_bB6RDmNpkzndVbSh9CvgaL,Brittannie Redden,10017,fuzzy(last+first: redden+brittannie(fn=1.00))
|
||||
29614,samanthatheis,kf6u8djigk,account_x46hMNq15qHmEvP6fNFdfm,Samatha Theis,10023,fuzzy(last+first: theis+samatha(fn=0.70))
|
||||
29216,wanxiaxong,BER4NEK9M7NA,account_tiy5YHD34KpwPzUTXyRVFJ,Wanxia Song,2077,fuzzy(sequence: 0.86)
|
||||
29215,bryangoulart,DTNQFE4CXSFX,account_9sksoKmcJGxqhz4M4aD169,Brian Goulart,2076,fuzzy(last+first: goulart+brian(fn=0.70))
|
||||
29602,tech,brussels-crucial-smashing,account_jUFnZ52urgAMNgp9XhT4bs,Graham McIntire,1,fuzzy(manual_override)
|
||||
28935,chrislewis1,V9DS4E4Y7UTT,account_osB3NhyGdoWJRtpL5X2bfD,chris lewis,1787,fuzzy(last+first: lewis+chris(fn=1.00))
|
||||
28917,crankkeith,kcrank10615,account_iSuzVkNmem52NdXXh1Savu,Keith Crank,209,fuzzy(last+first: crank+keith(fn=1.00))
|
||||
28703,grahammcintire2,U5Y3hA3g*14L,account_jUFnZ52urgAMNgp9XhT4bs,Graham McIntire,1,fuzzy(last+first: mcintire+graham(fn=1.00))
|
||||
28702,chrissyeagle2,NJPCGPBNFB,account_3uu7k4euycmKnZii9DQAj2,Chrissy Eagle,74,fuzzy(last+first: eagle+chrissy(fn=1.00))
|
||||
28671,melodymccarty,U3WLEGEB,account_18HG9hdwwtZM83J9kyGMs6,Melody McCarthy,1397,fuzzy(sequence: 0.93)
|
||||
28642,barbihardin,ZENETJ75TEVQ,account_bb7iRuPixxutKZxnURfK5U,James Hardin,1475,fuzzy(manual_override)
|
||||
28628,jackworthy,2VUR7AAFWZZQ,account_hgoipVYXydD1yVSXTD9QGP,John Worthy,1461,fuzzy(last+first: worthy+john(fn=0.70))
|
||||
28542,michaelray2,jjPgDcotL6rtY,account_8Rh7edwTjNBYw9FJSq4WRT,Michael Ray,379,fuzzy(last+first: ray+michael(fn=1.00))
|
||||
28458,kennethcampbell2,V5TKW8L6RXFF,account_jF69QnP5j9FtfLgdAiUKfE,Kenneth D. Campbell,213,fuzzy(last+first: campbell+kenneth d(fn=1.00))
|
||||
28296,cinthiapereira1,9WSGXM9LBXGZ,account_57mDq22QWRV8tdPvk74Uvj,Cinthia Pereira,1102,fuzzy(last+first: pereira+cinthia(fn=1.00))
|
||||
29531,daycor,U45YBPSARH7N,account_aZmcKqobMWGKhxagDdLCFA,Cathy Day,755,fuzzy(manual_override)
|
||||
29620,sayedahaque,YR4Sytbu,account_4hoTCjwTatthCg7zp2Cnfe,Syeda Haque,10048,fuzzy(last+first: haque+syeda(fn=0.70))
|
||||
27417,donniecampbell,6a2671bc0c1,account_4ww28eUsFRv8Yh5RkWe9cz,Danny Campbell,92,fuzzy(last+first: campbell+danny(fn=0.70))
|
||||
27418,alanwyatt,i1412r53MYr,account_2cnVCpSsYocCYpMEwwBBrY,Allen Wyatt,27,fuzzy(last+first: wyatt+allen(fn=0.70))
|
||||
27436,clayrobertson2,C2147288546R,account_jgtX7TysTqcobVVBtSZEc2,Clay Robertson,77,fuzzy(last+first: robertson+clay(fn=1.00))
|
||||
27501,jonnytaylor,Zb6677&4!yky,account_1fDruGS6CFTBUCGuzVZYTG,Johnny Taylor,190,fuzzy(last+first: taylor+johnny(fn=0.70))
|
||||
27579,donnance,9727346814,account_s9ZW1FpS3wkDoh31CjU1Zw,Dana Nance,89,fuzzy(last+first: nance+dana(fn=0.70))
|
||||
27838,consueloluna1830,C8736gfd0,account_jw16PcKtprHpPkSAEhvsaN,Consuelo Luna,81,fuzzy(last+first: luna+consuelo(fn=1.00))
|
||||
27912,blancadiaz,B548gdu55D,account_ktdNSpKRV3ja7RMi7pZRBB,Balnca Diaz,433,fuzzy(last+first: diaz+balnca(fn=0.70))
|
||||
27639,choonpang,CP2143855677cp,account_o7aKk2FGKJAcmrXxMfUCpX,Geok Choon Pang,146,fuzzy(last+first: pang+geok choon(fn=1.00))
|
||||
27618,tammieventris,DR45QC74M,account_fMQ6qiwkPFWP8a86exFw4A,Terry Ventris,340,fuzzy(last+first: ventris+terry(fn=0.70))
|
||||
27939,reynamoore,R92nbskba7iM,account_k8gD8YGUQzwWCE7JT9EfYc,Rebekah Moore,386,fuzzy(last+first: moore+rebekah(fn=0.70))
|
||||
27781,pambanschbach,Q2p68#*9vwF2,account_6hGXtJ7RV7mqZYfd5AyE1f,Pam Bansbach,383,fuzzy(sequence: 0.88)
|
||||
28107,jacquelinewilder,HEY36LUGYCUQ,account_hHa9Z64B9okZ3yccQP9WpR,Jacquelin Wilder,892,fuzzy(last+first: wilder+jacquelin(fn=1.00))
|
||||
29554,marcuspatton,GEUXX27VLZXK,account_dxY6AZnLdFUWup8Sf6XER1,Marcus Patton,2458,ambiguous_resolved(lowest_id)
|
||||
29606,mariacastanon,iu297gkid9,account_qh8bFPvWzyAqHqwHMDkThW,Maria Castanon,10007,ambiguous_resolved(lowest_id)
|
||||
28800,kennethcampbell,!9727342446!,account_jF69QnP5j9FtfLgdAiUKfE,Kenneth D. Campbell,213,ambiguous_resolved(lowest_id)
|
||||
28358,stephenday,MSVGQ8X38YLN,account_j1UFX8wqzorqSetUje5dTJ,Stephen Day,1168,ambiguous_resolved(lowest_id)
|
||||
29630,sriahobilamuth,RTCESW24,account_7PgSr8dVuafybJdrXND3V9,Sri Ahobilamuth,113,ambiguous_resolved(lowest_id)
|
||||
27404,grahammcintire,U5Y3hA3g*14L,account_jUFnZ52urgAMNgp9XhT4bs,Graham McIntire,1,ambiguous_resolved(lowest_id)
|
||||
29524,chandparvathaneni,TK4QA3XM5XK5,,,,"still_unmatched(best: , score: 0.0)"
|
||||
29358,radiantlifeministries,12FDIUNF5L7vM,,,,"still_unmatched(best: , score: 0.0)"
|
||||
29296,midvalleyconcrete,BD58JYT2PJ6A,,,,"still_unmatched(best: , score: 0.0)"
|
||||
29244,janiscable,JHC7U554JBTR,,,,"still_unmatched(best: last+weakfirst: cable+gary w(fn=0.11), score: 0.611)"
|
||||
29150,donwilhelmus,2V4NEL87RM39,,,,"still_unmatched(best: , score: 0.0)"
|
||||
29135,kavalleriefarm,8DFNNTPBPZGY,,,,"still_unmatched(best: , score: 0.0)"
|
||||
29077,kellygarza,4696591662,,,,"still_unmatched(best: last+weakfirst: garza+esparanza(fn=0.42), score: 0.642)"
|
||||
29070,1stchoiceliving,YVWJWRPEVYNH,,,,"still_unmatched(best: , score: 0.0)"
|
||||
28879,kimberlyrichards,7PQA6J8RPZ9Z,,,,"still_unmatched(best: last+weakfirst: kim+tae(fn=0.08), score: 0.608)"
|
||||
28599,ryanrodriguez,9DFYWJ47YWUP,,,,"still_unmatched(best: last+weakfirst: rodriguez+mike(fn=0.19), score: 0.619)"
|
||||
28460,saidaacosta,V29897X4,,,,"still_unmatched(best: last+weakfirst: acosta+alma(fn=0.21), score: 0.621)"
|
||||
28422,jackiehendricks,UQR5HGFCJ4XX,,,,"still_unmatched(best: last+weakfirst: hendricks+sue(fn=0.09), score: 0.609)"
|
||||
28400,bonniestone,BH8BV44TTWUU,,,,"still_unmatched(best: , score: 0.0)"
|
||||
28336,buddyswan,N7XUYUD7J9SD,,,,"still_unmatched(best: , score: 0.0)"
|
||||
28268,alberto,TICOGUILLO,,,,"still_unmatched(best: , score: 0.0)"
|
||||
27408,penneywarner,b4h53N6c#5wo,,,,"still_unmatched(best: , score: 0.0)"
|
||||
29626,karengreen,UTKCYI674,,,,"still_unmatched(best: last+weakfirst: green+dominique(fn=0.17), score: 0.617)"
|
||||
27441,samanthasmith,22jmR9X#93j5,,,,"still_unmatched(best: last+weakfirst: smith+kathy(fn=0.27), score: 0.627)"
|
||||
27489,jenniferreed,683P7V7R,,,,"still_unmatched(best: , score: 0.0)"
|
||||
27482,irvinbarojas,yDeEJzYY08,,,,"still_unmatched(best: , score: 0.0)"
|
||||
27463,dennispereyra,82s62kd9XHhd,,,,"still_unmatched(best: , score: 0.0)"
|
||||
27709,sensibleheatsystems,SHQWERY23,,,,"still_unmatched(best: , score: 0.0)"
|
||||
27704,lambandlion,LL9727363567ll,,,,"still_unmatched(best: , score: 0.0)"
|
||||
27625,mattbud,kjklfda79sd,,,,"still_unmatched(best: , score: 0.0)"
|
||||
127621,richardhill,RH9729788124rh,,,,"still_unmatched(best: last+weakfirst: hill+ellen(fn=0.20), score: 0.62)"
|
||||
27955,evlynrichter,FRQD738BGRC,,,,"still_unmatched(best: last+weakfirst: richter+brent(fn=0.19), score: 0.619)"
|
||||
27755,gordonhamilton,c3495Rpuq5Sh,,,,"still_unmatched(best: last+weakfirst: hamilton+sheryl f(fn=0.16), score: 0.616)"
|
||||
27793,branchrvpark,XfKxVZIKmm,,,,"still_unmatched(best: , score: 0.0)"
|
||||
29601,kimberlyrichards2,K84f7r10GT108,,,,"still_unmatched(best: last+weakfirst: kim+tae(fn=0.08), score: 0.608)"
|
||||
28213,krisdougherty,RWEH75UJ38,,,,"still_unmatched(best: , score: 0.0)"
|
||||
|
416
gaiia/radius_gaiia_matched.csv
Normal file
416
gaiia/radius_gaiia_matched.csv
Normal file
|
|
@ -0,0 +1,416 @@
|
|||
radius_id,username,password,gaiia_id,gaiia_name,gaiia_readable_id,match_type
|
||||
29581,joseortiz,J2WEYT7782UJ,account_4jKF2XxS1tjfAiTJLsFcGJ,Jose Ortiz,2485,exact
|
||||
29578,perlachavez2,U4BBTBMCJZST,account_sSL9o2BAvbEHXAkxWtPXxV,Perla Chavez 2,2482,exact
|
||||
29575,thomashernandez,X6BZLA7SC3XM,account_wL9tsaty83QCJpNGNXeSzZ,Thomas Hernandez,2479,exact
|
||||
29572,williamarmstead,SAQ4XEKXDC58,account_5Eqdv89tv7SUwTjGqQWJdh,William Armstead,2476,exact
|
||||
29570,julierose,MLM8R2WAGY52,account_9hvebUFe6UDfaZb9omVkB8,Julie Rose,2474,exact
|
||||
29571,derrickmccausland,ETNLKZHACGF7,account_89xduFTiVvRoEQpENiaVmT,Derrick Mccausland,2475,exact
|
||||
29567,sherrifredrick,7P52BVMTSNN6,account_3eF55epzgGSPhDU1DFBm53,Sherri Fredrick,2471,exact
|
||||
29565,shelbyburris,3YAL9DWFZUPT,account_9GNShE9ayAX3fkjwSryGq7,Shelby Burris,2469,exact
|
||||
29564,priscillacrenshaw,AXBDLXSGXMLR,account_2MK9ykEGzWyxvg1SZMmagK,Priscilla Crenshaw,2468,exact
|
||||
29561,korybiggs,4FJC7DXAW5RV,account_ftDbZQGSaf8dowpdjC44sU,Kory Biggs,2465,exact
|
||||
29563,kristinjimenez,9FAYXMRDU69T,account_bdpYxupJ3cfQ5bmhNroy8i,Kristin Jimenez,2467,exact
|
||||
29558,jessicaweddle,VV96G36AEEDA,account_khW6HArsQ677gL18hcQnEN,Jessica Weddle,2462,exact
|
||||
29557,yolandamedrano,R948UL4KZRA4,account_4tZA9NMygTcq3fidSxDz5h,Yolanda Medrano,2461,exact
|
||||
29555,williambowland,K7HYNWGE25XX,account_6fhZdgAsyd9zJ2FiXURCJ2,William Bowland,2459,exact
|
||||
29553,shacaciabillings,8AZLJCDFYFYV,account_pDDXT9Lh8QsE2roPyeVfS8,Shacacia Billings,2457,exact
|
||||
29552,guadalupelugo,DQG8XCMFMVH5,account_haPf3cXEdK4TQMzqZi5LYV,Guadalupe Lugo,2456,exact
|
||||
29551,brandonhardesty,KMRHVPEXBEZ8,account_izj2Jd1nYA49rAcvNu6TfZ,Brandon Hardesty,2455,exact
|
||||
29550,nancyavellaneda,CXATYV7NGZN2,account_99jecysxoYGBHUhd97ZYmt,Nancy Avellaneda,2454,exact
|
||||
29549,matthewgoodwin,VSPM7N9UNU6Y,account_ocezHaQ4JPvu1EbRMk4hu5,Matthew Goodwin,2453,exact
|
||||
29545,donmckinney,C6AQ6UVD6R7R,account_viJJswZvVXEbxLBikRjGJz,Don Mckinney,2449,exact
|
||||
29544,natashaelmore,8G5QJ8R7MK8Q,account_bGYACsz7a5kdyDBDecBTZY,Natasha Elmore,2448,exact
|
||||
29543,shamsbashir,WLBGY4JMN4VA,account_vtGoVXJRkxWw3ZEYDKi9Bu,Shams Bashir,2415,exact
|
||||
29541,douglasglover,9Y6H9X3HCG3D,account_hzZvaSXgCuhMUTXjpaCQjA,Douglas Glover,2413,exact
|
||||
29538,carolstrickland,547P6AQ8RCD4,account_u8W2U31Z3LJgzNcD2qNfcX,Carol Strickland,2410,exact
|
||||
29537,carmenlopez,TPLND3H2FWTQ,account_75ZP8Rhk7DBvwEA83HaWBB,Carmen Lopez,2409,exact
|
||||
29534,johnlooper,ZPL8LRY2TGRH,account_iijdXYXJAhg1mDm1FRuUrc,John Looper,2406,exact
|
||||
29535,davidroesch,ZHB2BS968DTU,account_bHVKB5E3eJFmbqnDwXAxFw,David Roesch,2407,exact
|
||||
29530,derekrodriguez,AP7Z594DMR7E,account_8MmcYW5A3KpMsyBJjiQMfE,Derek Rodriguez,2402,exact
|
||||
29522,joshuasoliz,PE7MHRZWUEQM,account_fPwqeAaDrYSWtRyVjBk8HD,Joshua Soliz,2394,exact
|
||||
29514,ericbarrett,5S2CV9FEFMKG,account_fhkdvYwigwTL9hR4Stx8Tf,Eric Barrett,2386,exact
|
||||
29513,marysmelser,ZS74NS5BBPX3,account_sN8UFBKJy6HHSCXSQp8oTB,Mary Smelser,2385,exact
|
||||
29506,nicolemaenn,B7KS27WL5WSN,account_pJFWGZXwDitJCT55tcuqeE,Nicole Maenn,2378,exact
|
||||
29503,yolandajudge,6V8RLNK75LM8,account_cWR4qvSM1SepG3DuoD8rUz,Yolanda Judge,2375,exact
|
||||
29501,craighovde,AVZXWBX8GQTC,account_q9j2WgU1236uDnx5rrEx8U,Craig Hovde,2373,exact
|
||||
29498,joselucas,ZHMW9TE8QHRX,account_izByTUMCPK7UT8k6Zncc4e,Jose Lucas,2370,exact
|
||||
29488,douggarber,WXP9J5SRJWEJ,account_vyxx47PALJmyUZZe3m495N,Doug Garber,2360,exact
|
||||
29482,carmencleghorn,VH3CBTJZAJ2Y,account_cRZQwgdsQLcok1dzXy6U5c,Carmen Cleghorn,2354,exact
|
||||
29472,rubenreyez,66384Y7B4WR4,account_aC929RXGkB1nsEHu95kg5v,Ruben Reyez,2344,exact
|
||||
29464,tiffanystewart,3EGVCS6RMF3Z,account_h3oACsArSoq5fZ6vRyH9Br,Tiffany Stewart,2335,exact
|
||||
29449,dejadodson,FLR4DJHPX6TG,account_57jxCEcSpkMPUEVxDoZddc,Deja Dodson,2320,exact
|
||||
29447,coreyball,3JAUWZNAR9MB,account_8fvkQS2hERUdtSF7DXrzpw,Corey Ball,2318,exact
|
||||
29442,toniyoung,RJ24VTRM7JA9,account_mXgG1ZhfDfrnkU6usym3kG,Toni Young,2312,exact
|
||||
29434,melanieweddle,XQ5L7PUM9RP6,account_piQoRw8BbqZpckbweTGdDu,Melanie Weddle,2304,exact
|
||||
29431,zackknuckey,VP5CCX8LTE47,account_uh8Yfj5mdjaQjofM19p9MU,Zack Knuckey,2301,exact
|
||||
29425,christinehamparyan,WEYX9R5P7J5L,account_aJuvQYQACJ5Ms48bAR4hWY,Christine Hamparyan,2295,exact
|
||||
29422,joespeciale,UH6ZCM9WLGRQ,account_2BwbPKVKpj7QnFFpdZVqn3,Joe Speciale,2292,exact
|
||||
29420,rogerbatt,KGDJZ7V66QQK,account_2gaAXfFo32nXEh7qA2yX8M,Roger Batt,2290,exact
|
||||
29419,kalebjames,KMQDGPD5XB6Z,account_67qbP4FpqwKmqSNjSmtCBv,Kaleb James,2289,exact
|
||||
29416,stevemolina,BLLHWPY9RCTC,account_c5VgNVVcBTRkHjbzFDEpHk,Steve Molina,2286,exact
|
||||
29414,mariatrejo,EYPWCQKKWYZH,account_ahaKfCrUTPA2mx7oM2i7Xu,Maria Trejo,2284,exact
|
||||
29410,kenhall,LX897BGJNMK5,account_gZPLTs7y5We8wW5MZyZjkX,Ken Hall,2280,exact
|
||||
29407,deewilliams,R8AGX3E8WAHN,account_kjSroty8KmGKCTEArNC9Pa,Dee Williams,2276,exact
|
||||
29406,johnvayo,DJJNUEEHGAW5,account_7hUzHAkpHagRdjPWYoRP9R,John Vayo,2275,exact
|
||||
29398,anthonythompson,WB4E4MPJVHTB,account_4CLddQNDWC76dtRwmYRdeZ,Anthony Thompson,2267,exact
|
||||
29608,stevecombest,STVN23552Wt,account_aM6WQ3aysaRPx4zpUh3zWD,Steve Combest,10015,exact
|
||||
29607,luisvelira,uygo875491!,account_3wT7DTZj4An5jaitLTTWTA,Luis Velira,10013,exact
|
||||
29609,keithtucker,14FYJSNUU48,account_7Wjw5Lru3JmVxq4s6cRqvn,Keith Tucker,10016,exact
|
||||
29610,angelatrevino,KJGU39565,account_4yPBWjMpwT4tVHJyVTDiYX,Angela Trevino,10014,exact
|
||||
29612,amymontelongo,ig76f7fuvu,account_o7rGD6dKJP27Sf9AUMK2e2,Amy Montelongo,10020,exact
|
||||
29613,michaelasmith,MS21353GHY,account_t23HG5uEXET828jSbivozo,Michaela Smith,10022,exact
|
||||
29615,janisstewart,JD834HCUW1C,account_fztV7NVRixwgnD6n392ZfX,Janis Stewart,10024,exact
|
||||
29616,irenegaribay,uky75dkc,account_ctACFtZH9mDX1E9tVgkpzy,Irene Garibay,10027,exact
|
||||
29383,leonardocoronel,TXPC494,account_bLMBbmXsvurdQQaSokRzTf,Leonardo Coronel,2248,exact
|
||||
29374,alejandrolimon,MM4BSQS7FRTD,account_gqFbPtxAGRTSizXXLFB9j3,Alejandro Limon,2239,exact
|
||||
29368,crystalharney,A7W26H834WLJ,account_bzPJ8YAjKNt5Bn7zhYf8zw,Crystal Harney,2233,exact
|
||||
29355,olgagutierrez,WERWRMUDBZ85,account_hswERjNGcJtQnPWCkMnEQs,Olga Gutierrez,2220,exact
|
||||
29353,greghummel,JV84HSVAK6XR,account_myUaLbfFVED7hqEBBY1Pe4,Greg Hummel,2218,exact
|
||||
29349,bettymitchell,8CHHCNBWQLRV,account_t4wwpPyK98WBZUqLnwXKNU,Betty Mitchell,2214,exact
|
||||
29341,wandadavis,JXQXXZ9MKFX6,account_iy49cEMsdawi18rTXarw73,Wanda Davis,2206,exact
|
||||
29338,scottanderson,RJ49A43SG3RA,account_sDPK3iLsxwrLzndTwh2uxb,Scott Anderson,2203,exact
|
||||
29335,jenniferboon,28ESU8ELVELP,account_3aCW8JeazPNe4UxjtGXzrp,Jennifer Boon,2200,exact
|
||||
29331,marialopez,P3FNSPT7MEYM,account_mjnZnogDMDws9ueMDxVzaP,Maria Lopez,2196,exact
|
||||
29329,cynthiasandlin,MCDTLMVFQXAV,account_sFzFdm2TTpyRaur4JfF3bv,Cynthia Sandlin,2194,exact
|
||||
29328,bethanybell,3UUC5X27PETS,account_wnvmzBJwB3r7pG6a6WNANx,Bethany Bell,2193,exact
|
||||
29323,garrettojeda,U5PRQYZE2MM3,account_chsr6oKpnEYuEKkMXn7CKF,Garrett Ojeda,2188,exact
|
||||
29325,rayschaak,XSNLP62EK6VZ,account_qhFf5wpTsv3FySr63biaRp,Ray Schaak,2190,exact
|
||||
29327,carlaswearingen,E6DQ4B2T9JZD,account_rzEn7BHU6Dmd6PkRwRpNXB,Carla Swearingen,2192,exact
|
||||
29320,lesliebewley,55KFSJMEDYLK,account_jeuzxdVtC5u4W8utfKf5vc,Leslie Bewley,2185,exact
|
||||
29317,delainawaite,EM7TWHUS4SYU,account_kvePVE9fNZCokbkkvpBvLK,Delaina Waite,2182,exact
|
||||
29307,esparanzagarza,VLL9ZNPRSTG5,account_dXq5FqPpKhAwE96NT5D9Jk,Esparanza Garza,2172,exact
|
||||
29273,glendabeauchamp,RKA3J8WSPYJP,account_fJVr5CSPdsgWtLNRWkidro,Glenda Beauchamp,2133,exact
|
||||
29256,glindarichter,VXMLMDAFLNJ3,account_2ngDjpr4VqDCPsv2SNMHvS,Glinda Richter,2117,exact
|
||||
29251,nhumorrison,EC32VDFKB9CB,account_hZpk1bujwvFdyctrbfJtjS,Nhu Morrison,2111,exact
|
||||
29245,brittanyworley,NBKYERRBSBH4,account_r39MCaQ811VXnw6QbhSG4b,Brittany Worley,2106,exact
|
||||
29243,dustynfisher,XASFU3DBPZ5M,account_sAYv3pn9D1KCwWr1TzGyeu,Dustyn Fisher,2104,exact
|
||||
29237,stephenbeegle,RU5GZVTVACRC,account_7RPLicRiJMb3Y1x3C4tEQJ,Stephen Beegle,2098,exact
|
||||
29229,roseanndellavalle,5GAF6JHUWWYP,account_wTXj1c59SU9iRzeRmgv2dT,Roseann Dellavalle,2090,exact
|
||||
29231,duanewright,VXK4FBLVFE7F,account_3Hwm7znAWKLPMhmY2avtnG,Duane Wright,2092,exact
|
||||
29222,pablohernandez,P3YB6A4JGEDF,account_vkMiPuMKu6XmQt1G9CP1op,Pablo Hernandez,2083,exact
|
||||
29217,pavelavanesov,KP8ZSGC2DG2U,account_kNWT9ewUppfxMtxpcgScKZ,Pavel Avanesov,2078,exact
|
||||
29204,sandrahoughton,5XR4QMPBXVWY,account_sDBLs44VLbWy9ZRhuDAkpL,Sandra Houghton,2065,exact
|
||||
29203,adamstaeben,GP7RANJ8UCYP,account_u39DmoPasPkSoYJ3bTG8kz,Adam Staeben,2064,exact
|
||||
29190,russmott,J8ZN888UQSLR,account_7QghTwmH6MwtZdTrvur8uB,Russ Mott,2051,exact
|
||||
29189,karenstewart,JSKTFAJ2JYDX,account_wSUPq2ZjUF1Va7S3vap91M,Karen Stewart,2050,exact
|
||||
29185,heatherwomack,89W7S5Q7UAHP,account_kSE9uAW6WPbt4msXe4mzHs,HEATHER WOMACK,2046,exact
|
||||
29183,wyndijames,CPSDNRQU95HJ,account_hJwbN2EUqGCtNj45JCyQda,Wyndi James,2044,exact
|
||||
29182,angelicacuayahuitl,VK8PZW3LK5F3,account_sooioQ729DC21LJa1zKMAN,Angelica Cuayahuitl,2043,exact
|
||||
29181,chasewilliams,W6AMPQEPY3BC,account_iQqfYFDTb4UxtHcgrGt8EP,Chase Williams,2042,exact
|
||||
29176,kevinarana,JAJYSEG75UZS,account_oR5nKGJTvRLhkCyUGG1B1L,Kevin Arana,2037,exact
|
||||
29166,chrisclayton,BFJ462PLHJFV,account_aamYj92hC8r7nxvbAfiHCE,Chris Clayton,2027,exact
|
||||
29162,billyhorton,MPTX573ZUYYG,account_4DuPXfEqx5pRPftTGKkbhS,Billy Horton,2023,exact
|
||||
29151,chisediquezada,RJBYQ2NUB829,account_1KjfxaKx8MvMEwb7uU3eSC,Chisedi Quezada,2011,exact
|
||||
29147,angelmerlo,EWWAPXN9Z2YS,account_deGyZc4zP5TBJGSibkGU3a,Angel Merlo,2007,exact
|
||||
29113,kerryblack,9D5S9ZD2FCBV,account_qncL4zimMnsq5ViZrpNXr1,Kerry Black,1974,exact
|
||||
29110,cliffordjennings,5GC7M65H9VGT,account_rknD3GcgUkTd8XB7rZZjND,Clifford Jennings,1971,exact
|
||||
29098,carlakimberling,SS4KRJYKTV7B,account_2hJ8frAhU5LDVpaoSH27Tx,Carla Kimberling,1959,exact
|
||||
29088,richardconklin,WEUNG5H3K8BB,account_7aEgkmhMKwDVqsus5Ln8Bd,RICHARD CONKLIN,1949,exact
|
||||
29089,leandrabeshea,7V8BZU4G83VF,account_doS7HkZWacVd5r8nWxqDFZ,Leandra Beshea,1950,exact
|
||||
29067,robertfinch,D7FMTT455GBB,account_jJoU6UPYd2ckqbUhynBx7B,Robert Finch,1928,exact
|
||||
29058,ericcoffman,MMEBXQYLZWNU,account_4mZAzz4GiGtMU8CJBWs1cQ,Eric Coffman,1919,exact
|
||||
29050,nathanaelfinch,JVNC68FV7N6B,account_t87r46C6jaw8rjJYurmHrY,Nathanael Finch,1911,exact
|
||||
29047,gabrielasereno,KCH82WCRA2YR,account_99RoiQcJpubSAqajPZjxFf,Gabriela Sereno,1908,exact
|
||||
29045,georginacovarrubias,8QT57NUV8C7R,account_gERfDmYXtJj5PLCY9ELLS7,Georgina Covarrubias,1906,exact
|
||||
29035,thomascline,E523BKVU5MJP,account_g8yP63NFmbioePagUH1kQS,Thomas Cline,1894,exact
|
||||
29034,susanlewis,R7TZFZEGLNRW,account_mqMu7hR16hGpAesQ3ba83B,Susan Lewis,1893,exact
|
||||
29025,tammywelborn,RMC482WNBS7,account_rjvhtkZxuk442ghJaKsdtG,Tammy Welborn,1878,exact
|
||||
29023,haleymuela,BS7X3DTKW8TH,account_hmnByz3wfiafSujapZBbi3,HALEY MUELA,1884,exact
|
||||
29018,christelles,WL2TKLFG8G2H,account_uGdvd35CHuBkiBamMnB8Bj,Chris Telles,1879,exact
|
||||
29017,mauricioantonio,6QJW7RAPDBWB,account_tX9wv8qaDb4bCFBxKPsMDe,Mauricio Antonio,1877,exact
|
||||
29013,vincentlimon,YDDXKFABUZY7,account_eZ6nJGAsmsPwR8sYvtXBRw,Vincent Limon,1873,exact
|
||||
29634,jeffreyharder,YUV565D67RT,account_gWD9gTNmR8RbzQkJmjpRYy,Jeffrey Harder,10073,exact
|
||||
29635,pablomartinez,2766581BER,account_g2THEPVu4zgnGsTeFh6MpF,Pablo Martinez,10074,exact
|
||||
29636,krystalmendez,6FTCKU6S56,account_hiH7WbsYQm1HRkpGA3jJ2v,Krystal Mendez,10080,exact
|
||||
29637,davidvandagriff,UTRC53865F9,account_kAa1VyPLqz5fecRMXpV7d4,David Vandagriff,10082,exact
|
||||
29638,carolynwalker,trjxtyx75!,account_wsZpXzz2mhYMxMLmiVGNvF,Carolyn Walker,10084,exact
|
||||
28979,claygilbert,3E6XFDHXCAG7,account_eVxdo9RAAoBbisk1EYCKnc,CLAY GILBERT,1839,exact
|
||||
28978,jmichaelculverhouse,2147099521,account_q4nxMiXUoEj2DkH3JUspLP,J'michael Culverhouse,1838,exact
|
||||
28971,diegosereno,2CE9F5YSPQHX,account_vvPvzpGka7xmJQnqE3gMxq,Diego Sereno,1830,exact
|
||||
28970,ashleysmith,F8D3FVCZB7PD,account_5nSRJ7EkJpz5dpB2QGRYfz,Ashley Smith,1829,exact
|
||||
28954,dougstowe,5D35LFD6GEAZ,account_3hQJhhHf48sszYWBaMTjpR,Doug Stowe,1807,exact
|
||||
28936,timbronschidle,3ZVLCWSAE3EW1,account_o6gDBbdWzQYeUbAQL2Zp1s,Tim Bronschidle,1788,exact
|
||||
28928,cynthiajones,KC7ESYHEZGNR,account_hJ5wonckmH2jkaykKH2g4Q,Cynthia Jones,1778,exact
|
||||
28867,bobbymack,UDUBTCXXF8VV,account_nNUBZtEptfAD6TpDteqjfM,Bobby Mack,1712,exact
|
||||
28848,americatrejo,3B63TLTT9WE4,account_gjzpfGyVuYMaQwdYA2Gufy,America Trejo,1693,exact
|
||||
28832,tjbanschbach,8WTWTXACGG5N,account_hU5voij99AKqckcGpok7Jx,T.J. Banschbach,1676,exact
|
||||
28811,luiscabrera,RWUQ94XT9A9Z,account_mrPPtWesWBzmcD4Lfvp2hA,luis Cabrera,1650,exact
|
||||
28810,rosahernandez,JSGUXKWEMSVN,account_qYeNtZSW1nYgukr9kYy5qn,Rosa Hernandez,1649,exact
|
||||
28807,carlosgomez,J22RS7F3LGG2,account_jNj9rpkTwq7wF3vJbMuAkF,Carlos Gomez,1645,exact
|
||||
28763,jeridrutledge,GJ3CZTRJJW2M,account_v6odXP1nVxoVvTMUrUn3SS,Jerid Rutledge,1601,exact
|
||||
28748,timthomas,NY223RQJD7PN,account_m5ZTZYHZbzpVPeNfRhroxn,Tim Thomas,1585,exact
|
||||
28715,carolbates,J3UBUCMYDX64,account_97wbzzkGPxCPMyfUs5uTTf,Carol Bates,1543,exact
|
||||
28710,shirleyelmore,D8Y64N9L4R9W,account_gfPawVk3gSuvufuC4zVjx9,Shirley Elmore,1538,exact
|
||||
28698,swedlund,swedlund494,account_u9nP4wDufL6eQGx3Jnoyn6,Brian Swedlund,357,exact
|
||||
28695,wendysanders,RYG9L43NWDZV,account_4KPsmHxqdcnsRNXuWB8s2Y,Wendy Sanders,1524,exact
|
||||
28693,robbymccollom,RMC482WNBS7,account_xfht7KCxBpasoRqtkTojjj,Robby McCollom,297,exact
|
||||
28664,heatherbrister2,KK37V8W9ZD9Z,account_m1hacYUopGGJrn2UyJvzBu,Heather Brister 2,1495,exact
|
||||
28663,ryanlloyd,HWEMKNWX55RF,account_pFHZ8RMuvdETPpHr4vyDWi,Ryan Lloyd,1494,exact
|
||||
28661,amyrodriguez,WX3QRT2U6L4X,account_w1ywM8kUBcURQHGjHQqirn,Amy Rodriguez,1492,exact
|
||||
28660,thecountrycutter,YXpScFIneePm,account_1uu6CnSCUuCfBabMwfikLz,The Country Cutter,396,exact
|
||||
28659,bradwilson,ZFAXA9AK63AH,account_ujyp8hfbksvc8EZALQ5gTL,Brad Wilson,1491,exact
|
||||
28654,edrater,FRY4823N,account_fNtXkw98GQKVvcjcfbeb9F,Ed Rater,126,exact
|
||||
28637,vikyacosta,Z4SMV49SAH72,account_mma7TzxJUZKUzXYr6YDUmt,Viky Acosta,1470,exact
|
||||
28633,terrimack,B9HNTSN7ASQ4,account_wU2qgfTxqjaLSfhwWx4Cxm,Terri Mack,1467,exact
|
||||
28617,luisarellano,GVNWBQMM3S7U,account_t9DUQUCtBAWojjSYtZQusE,Luis Arellano,1450,exact
|
||||
28609,dorisavalos,DLKMJS88AF2W,account_pMdxeChdNqnVRQs1rSAKBE,Doris Avalos,1441,exact
|
||||
28597,jackierollier,TSFNN8DVDSPR,account_w5t1gDksfLnZqU35RFxZES,Jackie Rollier,1429,exact
|
||||
28588,charlesmccreary,88J6XS6TXRMA,account_dQ3cvFTw98ByhcBzDGMsNQ,Charles McCreary,1418,exact
|
||||
28548,jamessmith,LRVU6RVG5SP8,account_ayvJr4cDCtK3zNPcoVo5vy,James Smith,1373,exact
|
||||
28546,davidwhitefoot,PYHTZMHJU8R8-,account_sWngcEfTk4Ed83ARWYtZga,David Whitefoot,1371,exact
|
||||
28536,patriciavasquez,P88256487V,account_6wngyAj64jCwvn3W8T3sdT,Patricia Vasquez,1128,exact
|
||||
28535,adrianmcdaniel,U4TGDH2PSAB4,account_aScSfG25Wwo7cD4xbNhja1,ADRIAN MCDANIEL,1362,exact
|
||||
28526,justinvoga,URHVCX7SFSFU,account_cwmXrKm93bSteHQfm3SJtd,Justin Voga,1351,exact
|
||||
28518,jenniferlittle,8EAUUDQPXSQX,account_oSA465twDMxZedY5zbPLT1,Jennifer Little,1343,exact
|
||||
28510,bradslate,AF4KVQ2P44M7,account_nhYxp4RV7gpLxWnLL9oWs6,Brad & Sherry Slate,1334,exact
|
||||
28494,irisdelira,L6XGQA583DG6,account_mVgspPEmufZG8KD83Cnyjh,Iris Delira,1316,exact
|
||||
28491,richardbarragan,WJBS86MMJBFH,account_hJ3iUD77LRADfhaxwwsqLj,richard barragan,1314,exact
|
||||
28476,jensenhall,F8XM9U8SC6LL,account_m3356qm8wGGJDqNZCgXRev,Jensen Hall,979,exact
|
||||
28466,paulcomella,YTZDMBZCMPX4,account_rRKbPsCejKqMnAb29xPPFx,Paul Comella,967,exact
|
||||
28449,amberkrings,5NWBR3JLV6N8,account_eB2k88UWVdAFKYgcg7iK4h,amber krings,1273,exact
|
||||
28438,mauriciosoto,DJ7YP7W7AA8M,account_uyp7QDJmZyCeq9BTGnvoZ3,Mauricio Soto,891,exact
|
||||
28435,jimcrank,PLA7XNX2F4Y3,account_p9AaVzWfCwR74RVK3k6kPa,Jim Crank,1257,exact
|
||||
28425,summerbeazley,D4S4YRB8Y3KA,account_9AkWnUdPzs2AE9vfqmbz6i,Summer Beazley,1244,exact
|
||||
28426,davidarmstrong,MPEZGG53KQSZ,account_p1wD82RWMCTi1hRoLHdjHL,David Armstrong,1245,exact
|
||||
28395,stevenspurgers,YS5Q785J2HGZ,account_nxruUEiwq3NpZTexeEo8jd,Steven Spurgers,1211,exact
|
||||
28388,connercoleman,6108RANDOMREDDOG,account_6VLrBr4sZCAJwMzzcEkhSE,Conner Coleman,1202,exact
|
||||
28367,judithfierro,FUZ33LB6SFFC,account_aFYkyi9DGrJBRgQ4jSud96,Judith Fierro,1177,exact
|
||||
28359,tonyasipes,N5VEZR8Q5M86,account_rKLQuRuTgybF3YMnNt7VY9,Tonya Sipes,1169,exact
|
||||
28347,kathysmith,SJ53PSYCZLPR,account_3zbkHwfVAegXCPUhc4XmYV,Kathy Smith,1156,exact
|
||||
28344,cherieeshelman,BRUV99PN6Y7L,account_nwbZPHiXLbR9o6LZppPSoY,Cherie Eshelman,1153,exact
|
||||
28289,ronberger,Ronb499H7ALW,account_q8QjfTJ9rtgoow3eHLpVYQ,Ron Berger,301,exact
|
||||
28267,sandracrouch,V4N3764R5J,account_tCddLw595sDcQEYCrRCmFq,Sandra Crouch,311,exact
|
||||
28263,abbieandrews,YQSJ499H7ALW,account_vHWu6NZDh4Ajh6YqWEXPb4,Abbie Andrews,1081,exact
|
||||
28253,perlachavez,ZWR9PXVFFYBS,account_tTLx1Zn6YTykzm8quV6ykg,Perla Chavez,1068,exact
|
||||
28246,susanking,N2KUYLPDNXJP,account_xqwwg1ZAd6SKt4LyrcAebY,Susan King,1058,exact
|
||||
28241,terrybachran,NG9VMH25LCTC,account_s6ZC2GALaCFUamcQafLyy7,Terry Bachran,1053,exact
|
||||
28240,lanaygaunce,MJCNZAJHK9H3,account_pUesJC8A4ENdQ5XZTFGdZt,Lanay Gaunce,1052,exact
|
||||
28190,heatherbrister,6XPLC545G6HW,account_wkn6qdAVF72b7TxwZxvW6D,Heather Brister,997,exact
|
||||
28184,jenniferdunaway,343V5CT2R7HZ,account_bTsNpp5j2YHU9dKipBm3iP,Jennifer Dunaway,990,exact
|
||||
28159,elviraquezada,HXAS29VRX33V,account_q5J8BYNnSG5TtFnikCTEmm,Elvira Quezada,960,exact
|
||||
28154,jordainpatton,J8P4TPF7QS2A,account_p5VqKAXXC8t3tAFSzzr8cC,Jordain Patton,955,exact
|
||||
28140,mattkosarek,RJ4BQUZJY,account_7eCHrmvA6X16gn1zr5wCo9,Matt Kosarek,932,exact
|
||||
28117,shannonclark,F5XESMCJ,account_8YqGj24SS5fxoZhPUYBED9,Shannon Clark,908,exact
|
||||
28098,kevincostanzo,244VGMVMH66D,account_9DuddEuiaH59KtVGReTwJs,Kevin Costanzo,881,exact
|
||||
28093,kenpatrick,V7U3XBAPTSV3,account_omX4bSM3rXDShahsdc4aHd,Ken Patrick,875,exact
|
||||
28083,briangallimore,39V285NX,account_k8GTscTwPuAvgtyCbT6vyG,Brian Gallimore,55,exact
|
||||
28072,amberprater,RP42B7AYDLAP,account_fXqciwzW348S1eYSKbbi8B,Amber Prater,855,exact
|
||||
28068,marilynfowler,MarilynVG72P8H,account_v9vCsfc4uMAk7LgXyRR9EC,Marilyn Fowler,851,exact
|
||||
28065,douglaswilson,HP97HG9XPYC3,account_bmXh2S5K97hPyUkkBRj1ub,Douglas Wilson,848,exact
|
||||
28034,terrygortney,GQUGR3VJUXBR,account_jPuzvdHGq1BXiDALjrVaR7,Terry Gortney,815,exact
|
||||
28031,gizellebutler,HMZTX66RTPVX,account_fipAsvnLVUkrSmuJ9GYdEb,Gizelle Butler,811,exact
|
||||
28029,bradgurney,7A6LRZWT82DX,account_aZMRveyhHvJTHTQCT7yYdf,Brad Gurney,809,exact
|
||||
28024,darrylchavis,TDGUUNXGMYRC,account_oP5ATcm8WwBDBv8t8mh4vU,Darryl Chavis,803,exact
|
||||
28025,kathymazzola,5MVS8VUF9QL2,account_g5RpJWMv32tFk6c4yy5F5c,Kathy Mazzola,804,exact
|
||||
27994,austinwatkins,S7M4UD8QUTDP,account_htyg2uThjYmLbHsX5a3RCj,Austin Watkins,769,exact
|
||||
27988,oscarrodriguez,TGPCF3Y2ABXS,account_fNLTDkPjpbzZ7Eta2ofer4,Oscar Rodriguez,762,exact
|
||||
27986,erinthompson,69TQZ797,account_eGe4Qkw13xjjwabHBu51F3,Erin Thompson,394,exact
|
||||
27983,brianhardesty,8D4P34N8,account_7zjMVqCfCV4jZT68myj6p1,Brian Hardesty,20,exact
|
||||
27976,cathyday,FAXMKENV8FCT,account_aZmcKqobMWGKhxagDdLCFA,Cathy Day,755,exact
|
||||
27956,fredheckel,HUVZSE8RGD65,account_mx1SSXHEiptCZaz2Gx6MTm,FRED HECKEL,735,exact
|
||||
27949,charlesboone,Ccbn78wdfksB,account_iNnHAwxaWRUXte6w7ijGV3,Charles Boone,62,exact
|
||||
27948,rachelguzzardi,R87wgerf8bsG,account_tEZyHoiA8DvmisscRkYy7U,Rachel Guzzardi,287,exact
|
||||
27936,juanalonzo,745GF0JK5!W,account_itAy96jDYGcvWjcHsy6XcR,Juan Alonzo,198,exact
|
||||
27930,kristinamurphy,oPXFrylqKf,account_qx5oYHwkMNxoKEXME6zu6w,Kristina Murphy,725,exact
|
||||
27926,johnnygoble,J829fhf7G,account_1m7oDB9vYUYJQffFHvxQaz,Johnny Goble,416,exact
|
||||
27923,keithbarber,K73bvd54tB,account_bKdyNcZqZ1h8qaQAcJzTRK,Keith Barber,208,exact
|
||||
27917,doreengrubb,DOR32SVT1234,account_5n5qjvMULWVYcGGmF7uiER,Doreen Grubb,122,exact
|
||||
27916,steveraia,DRR648DFTMSE4,account_g7EjAfDhcTH9oxWZGJ4CTH,Steve Raia,291,exact
|
||||
27896,twcowley,TW486TW,account_h2bYtWBz1nbRRVBiYvi8wQ,T W Cowley,710,exact
|
||||
27888,joepatton,L7luYzSKSu,account_hWsePAjG5pq8FnnDQm75GD,Joe Patton,701,exact
|
||||
27873,timfisher,Thd73gdf5eF,account_hMQqEcyWpzLTEtUnCJmKsi,Tim Fisher,345,exact
|
||||
27847,kileigheven,K9872gdf8E,account_4aPqi935STSGBSMxsLNwSm,Kileigh Even,215,exact
|
||||
27833,deannecook,Dc872cobnC,account_hqsfU7P5wK2uDjpfVo8zxb,Deanne Cook,110,exact
|
||||
27830,kacyharrison,7Ozr7el6K1,account_t9Bb9uXEB9NjGoTDvCThi4,Kacy Harrison,643,exact
|
||||
27822,milliganwater,8NjGMNsTXd,account_7FafmmaEuKaNeaHmFFLSaf,Milligan Water,636,exact
|
||||
27813,michaelnewby,TM5M3HU877,account_1moqtbSRAJxiVR2pTtH3f9,Michael Newby,623,exact
|
||||
27808,ladonnaclark,YQReVH9men,account_vPgQBpVJ4y2gkpRgWTksvi,Ladonna Clark,612,exact
|
||||
27800,jamesmooney,QNUqD2tSzq,account_5C4WjLaQS5tGDxZm4NCZds,James Mooney,603,exact
|
||||
27783,tamythomas,TT4699520858tt,account_4gF4CqD6W7iS3UesE6js3g,Tamy Thomas,587,exact
|
||||
27780,josephcruz,Z06oxJXrNd,account_nYfEqA9HUY6R2xi1PvCQVs,Joseph Cruz,583,exact
|
||||
27761,franceskirby,FK8179391152fk,account_v2eMTZjQzV5Q3B2vLxGCpa,Frances Kirby,568,exact
|
||||
27759,chadwhitsell,79374B75phGy,account_swiMEEPxqsGukTQMUEP7DR,Chad Whitsell,61,exact
|
||||
27754,ruthfengler,8q8MX6NQP3b6,account_2ycJgX1XpXSqDiDgNisZbA,Ruth Fengler,304,exact
|
||||
27751,bobbyfisher,salvagejuggle,account_iuZZQ7KJWWSihZh9mzi2B8,Bobby Fisher,391,exact
|
||||
27748,eddieyarbrough,89298ChZWzMy,account_6KuhoNq2CptmBHyHVS33cS,Eddie Yarbrough,125,exact
|
||||
27744,chrissyeagle,9vkX97426Sgt,account_3uu7k4euycmKnZii9DQAj2,Chrissy Eagle,74,exact
|
||||
27739,victoriaobier,M57B288P9,account_cPSKw37hjbGmmpHzzHUum1,Victoria Obier,371,exact
|
||||
27736,bernardheer,BH33682019BER,account_fcUL4WRuUX7LrvPhjfQZQt,Bernard Heer,46,exact
|
||||
27734,garycable,M2w6z5Y73,account_6JkDjyFzfp7pPcvn6tsd9f,Gary W Cable,144,exact
|
||||
27735,coyungemach,MhS569N84e7a,account_f5ygK53FvcVakZs4YwNVo5,Coy Ungemach,84,exact
|
||||
27733,michaelmccauley,CPORADIO98,account_ecewjvtr229nzEjFgSEhf5,Michael McCauley,259,exact
|
||||
27731,lessruble,9U35SXxM2H8y,account_6M1cjjzHDyrtJWimoD6Tgp,Less Ruble,226,exact
|
||||
27730,marknunnelee,833j2Ymm4yfV,account_mBYcBCnbFTgVxsDNCUrq9z,Mark Nunnelee,243,exact
|
||||
27729,elizabethchristian,Webg22nj,account_3Xpb7LLq4bEFZxohgYvjUS,Elizabeth Christian,128,exact
|
||||
27724,mikebell,BHF538HSEBDLY4,account_4vuDbTF964RUD2YKA5hmhQ,Mike Bell,556,exact
|
||||
27713,krystabates,KB2145445824kb,account_crxqAidS35hAeKLFDHKmiy,Krysta Bates,218,exact
|
||||
27708,chrisfrancis,DR45QC74M,account_pcYVJfTDAUK9ALviS7XBjW,Chris Francis,388,exact
|
||||
27701,markfisher,32fd8P&rw8s7,account_hxsSqNHZMtFbXDA8DtvRso,Mark Fisher,242,exact
|
||||
27696,kirkvanmeter,J2sR9g^83M33,account_bH9zyBQuBcgkiU9EZPYxa5,Kirk Vanmeter,216,exact
|
||||
27694,dananance,23SP$e!ug232,account_s9ZW1FpS3wkDoh31CjU1Zw,Dana Nance,89,exact
|
||||
27690,keithcrank,2zt3T5w72T3A,account_iSuzVkNmem52NdXXh1Savu,Keith Crank,209,exact
|
||||
27687,scottarmstrong,2SV58M55,account_dCvreFY2K3nJW8ZkCPaeyR,Scott Armstrong,315,exact
|
||||
27683,floydday,Tfdd3254tfD,account_rFM2cz415W3Rqn1McU5gpW,Floyd Day,141,exact
|
||||
27680,mariacortes,MC2145858276mc,account_t4Qnzxsq3c2WryFDXTSWba,Maria Cortes,435,exact
|
||||
27668,christianlummus,K6dbr631ddL,account_fWd5egSy6XovRRzTgi5xZk,Christian Lummus,205,exact
|
||||
27666,marklindsey,ML9727346434ml,account_ayztehQeAayBTQkizozi2B,Mark Lindsey,531,exact
|
||||
27658,joeywhitfield,BV758qXne446,account_8bxr54hmvSEFRyDLfvCgie,Joey Whitfield,187,exact
|
||||
27654,taekim,SAN11837TCK,account_wbsKtfE9W1SyPkqk9hFBRp,Tae Kim,408,exact
|
||||
27652,teresarobinson,T645trdf87fR,account_2SA7BzkR6cqsS2jSQ14nRh,Teresa Robinson,376,exact
|
||||
27651,nathanmctee,N32462uu8rM,account_2CZKhrkB2TxZDQTWxuJxVz,Nathan McTee,273,exact
|
||||
27646,rebekahmoore,R756346tfg4M,account_k8gD8YGUQzwWCE7JT9EfYc,Rebekah Moore,386,exact
|
||||
27643,monicatrevino,MT2145877143mt,account_d3CqUjkDwdiB1pwR6t66XL,Monica Trevino,524,exact
|
||||
27640,rachelfuller,R8364hbst,account_t7qMv9PTBxtj1rmr4WmEqV,Rachel Fuller,286,exact
|
||||
27638,shannonbrown,S9725298223b,account_nZ8roCqzpsY5Wv86p8i6Sq,Shannon Brown,318,exact
|
||||
27637,allentaylor,A6346gge8T1,account_44F55EHwRrLm33EvyuCPgG,Allen Taylor,26,exact
|
||||
27632,kristenaustin,KA8017916972ka,account_qunWwkhDGN8jxGDMfB9FzA,Kristen Austin,522,exact
|
||||
27630,colinhopper,TYVU65445IUB,account_jaGX2dnVZVs463ok5kANR5,Colin Hopper,245,exact
|
||||
27619,srireddy,ycHAvetmF0,account_jS3WVFZmrQu5EoM7UJ4L1N,Sri Reddy,514,exact
|
||||
27615,almaacosta,ENKC5DGV,account_rv1k2oTH5Dn7sDTJiTxADa,Alma Acosta,28,exact
|
||||
27614,chrispassonno,C8766thf96P,account_8UMvyYLatGW2qwSqH62KbM,Chris Passonno,73,exact
|
||||
27613,seanbaxter,SB4696679337sb,account_vFwXqaiaquwHXzgMkdg1mc,Sean Baxter,512,exact
|
||||
27612,collinhartman,B886tg98jt9L,account_kvBUopRRFzz8B5PgbdjLGG,Collin Hartman,402,exact
|
||||
27610,michaelhughes,1RKk32MyTK,account_6QT3zjSA88VLdbBJtAt44j,Michael Hughes,510,exact
|
||||
27608,michaeltalbot,Mjj1276rt99T,account_4bK2RJU3FRwyH4SPmnik1q,Michael Talbot,262,exact
|
||||
27606,anagonzalez,WNAHDMwWoE,account_3jfw5hMXURMmVuGyafdSPj,Ana Gonzalez,507,exact
|
||||
27604,leonardlewis,L1877ef32L,account_3TVj7ZpGiTYYt28q8dh9VR,Leonard Lewis,224,exact
|
||||
27598,jerryjohnson,J177895aa98J,account_fa8emiMvGC9ZkdA6uuwMpF,JERRY L JOHNSON,177,exact
|
||||
27594,pamtoler,NfxcgYQUbj,account_mnxwgFcvbjvDjnBBdGQzek,Pam Toler,501,exact
|
||||
27589,jodymccluskey,JM4696883349jm,account_35QGS4ZHr9tSpCVKNHE1ZT,Jody McCluskey,497,exact
|
||||
27577,chrisvannatta,!4694502484!,account_dyfr7aKWGD5zJ1pdeEyyna,Chris Vannatta,426,exact
|
||||
27575,mariomerlo,!2149579585!,account_3yQbySMiwrU5kFJRA8itm6,Mario Merlo,239,exact
|
||||
27572,williamarmstrong,!2145510334!,account_5izwKtCxbvkCupVQJ1KBBE,William Armstrong,362,exact
|
||||
27571,trecedaschmid,!4692075942!,account_iR4oM82C6m8zkrFDLkSbYZ,Treceda Schmid,355,exact
|
||||
27570,ronlewis,JDkf1AL942,account_5Y7chdsykaLwBg3s2rqvMD,Ron Lewis,302,exact
|
||||
27567,sherryerichardson,DR6528BCD3S2,account_oadyDfiynqanaMffoSVM2W,Sherrye Richardson,321,exact
|
||||
27566,joannarodriguez,394X3YR9,account_gCVrkxTWdSWaYUNtyk1rqr,Joanna Rodriguez,185,exact
|
||||
27561,dannycampbell,!9726589541!,account_4ww28eUsFRv8Yh5RkWe9cz,Danny Campbell,92,exact
|
||||
27560,howardcomstock,!3365494230!,account_kZMVM4zfKdgEAyCCvVREU8,Howard Comstock,156,exact
|
||||
27555,terrymiesen,!4697420828!,account_bXRRk1Yz4nKkCPsdzDJyYa,Terry Miesen,339,exact
|
||||
27550,michaelray,primitivedefinitionstoredinner,account_8Rh7edwTjNBYw9FJSq4WRT,Michael Ray,379,exact
|
||||
27545,terrybates,!2145786644!,account_d6m5HYMAczoeH2ovA6vqxj,Terry Bates,490,exact
|
||||
27538,gregmcintire,v9368$E9mx9m,account_fyH3ZiVVLx9h56vNYVndbK,Greg McIntire,380,exact
|
||||
27543,beverlyerwin,MOOLAH,account_9kYh3vsMa7mG8JrGaQtF2V,Beverly Erwin,48,exact
|
||||
27518,helenlumpkin,Heehhejd0N,account_ts53K9Gc61HkXi7uqcaiN6,Helen Lumpkin,477,exact
|
||||
27508,stevechristiaens,zY272m258*vJ,account_3HSd4Thc3iXfcidraN6ZSV,Steve Christiaens,329,exact
|
||||
27505,judydevine,OD4697669725od,account_e8ZUaWmpXqA5NTVfBHaYfq,Judy Devine,277,exact
|
||||
27500,rhondabolton,RB2145786030rb,account_x8xohdUBJBWyd87jTCtP3r,Rhonda Bolton,295,exact
|
||||
27492,rickbeckham,tVj7ZI5QA0,account_f861cgYw7zxSiyUh3ngUYk,Rick Beckham,464,exact
|
||||
27490,vancepeltonen,V66rdf43aaP,account_9arNHyqn6bwuUyPm4uWfrx,Vance Peltonen,356,exact
|
||||
27486,jacobwilliams,jnlka9994jkfldsa,account_bFr4VtSNkJpdZv3K67mP5y,Jacob Williams,159,exact
|
||||
27478,classicshear,KARENALDERSON,account_tui6mE1CTprghAF31hKWLp,Classic Shear,400,exact
|
||||
27477,davekiowski,FNW63KXSAPT7,account_xr9LPLBvR6SHF2xmNdrNfN,Dave Kiowski,100,exact
|
||||
27458,paulsammataro,P2n74V7P8g4S,account_cwYTP5p9ubFdXioqnmuDsy,Paul Sammataro,283,exact
|
||||
27460,janetkern,j42252^Bhf44,account_skzmJw1vJfndNPQNWsJjRL,Janet Kern,449,exact
|
||||
27457,carlossalaises,28E358y$JFB9,account_5mD5W5PczTzo9H7it3qxLX,Carlos Salaises,448,exact
|
||||
27453,obedsandoval,*H78z9xy84C&,account_k6qzzjxrJtH4UT7BerwBzk,Obed Sandoval,275,exact
|
||||
27455,dianewiley,Diane03t4&adTL,account_n9yXaEBkPmAm5SdrbTwsTw,Diane Wiley,442,exact
|
||||
27444,tammylove,f65NS4Ct6$29,account_5WZN3aLBoWBUHeEjHYySgw,Tammy Love,335,exact
|
||||
27438,floydallen,V3864VB2,account_9oLnGJZwNxJTrJRrQgCX59,Floyd Allen,140,exact
|
||||
27432,hortenciaalvarez,WHTABORG3593JKS,account_oNBXVRFamuurD1rz4bugiE,Hortencia Alvarez,155,exact
|
||||
27424,timbagert,CF18n42cY7,account_2U67Q5pWZCgbWGX1tLXbXD,Tim Bagert,343,exact
|
||||
27425,davidbaker,DB764g1d87rtB,account_okpeaMgrsz9nNs1cSMjQv3,David Baker,102,exact
|
||||
27421,janicealexander,AG57TSH89L9,account_pdQ864pRJ5AbJP4YHtv1S5,Janice Alexander,165,exact
|
||||
27423,timgilbert,73xi0r600Sz%,account_ezDVFeaw9XwLiVvM9iEDwJ,Tim Gilbert,346,exact
|
||||
27413,jesusrios,8Di6QMs7%7m1,account_5RA5zrZJXyuBEj4CmuodCh,Jesus Rios,181,exact
|
||||
27409,veronabaptist,DAY753LIGHT253,account_j9cuBn7ojxwRH3BfRWmDhN,Verona Baptist,382,exact
|
||||
27406,whiteywhite,45z6r*H4418n,account_32CUFBTH16juiHGzrKPd7m,Whitey White,303,exact
|
||||
27402,louislafon,RFTRE4825CVKL,account_9ayF1oF9iawPMpCdQSiSqX,Louis Lafon,445,exact
|
||||
27397,ausencioluna,ABN765MNL,account_7Gj6TBTXHmpu49iZGZBYT4,Ausencio Luna,427,exact
|
||||
27374,davidvillanueva,DV5489CRPMWBQ,account_d5hcuzxZBgMErVfhrXzQAJ,David Villanueva,109,exact
|
||||
29621,coryrees,yt5c6ujhvu,account_uRC7LnETNNZdRmSEQeQbwX,cory rees,10049,exact
|
||||
29622,brianamartinez,ktydr6428,account_gazJNXCsEwG1UJBmYyQ6xB,Briana Martinez,10051,exact
|
||||
29623,khushbooagarwal,UUTFVUCTC446,account_5xtd1ApTppgpHnRpvmQHmV,Khushboo Agarwal,10052,exact
|
||||
29624,elisasanders,OYTCu3276,account_hZD7ECmwCWd5vG73hEYDUN,Elisa Sanders,10054,exact
|
||||
29625,josemedrano,UYVITRUR24,account_3froYZwHV4X7v9wW6TVu8N,Jose Medrano,10055,exact
|
||||
29627,nicholasterry,VUTYVL746,account_wSzw7Xd3wb871Ug9aaqUcH,Nicholas Terry,10059,exact
|
||||
29628,leslieperalta,RYC66747,account_dQfUEUmdqUu6rGALsoowVQ,Leslie Peralta,10061,exact
|
||||
29629,khushbooagarwal2,iytf65gkj,account_j2iH6cW5tFhsSKxjLUvQBb,Khushboo Agarwal 2,10062,exact
|
||||
29631,royrogers,TYDGVBNC,account_nCAKAgLxtPx5H68AJZMNUA,Roy Rogers,10065,exact
|
||||
29632,arturoprieto,1653APVN877,account_pR6sTHc4xvLqPfAZhJxrxd,Arturo Prieto,10069,exact
|
||||
29633,ericamorris,E16488VN387,account_gG5uDHsz8FPiuknRCkpbHQ,Erica Morris,10071,exact
|
||||
29617,belindamillener,Uiyu7280,account_mXx2EoT7dcv2am6asSEJFr,Belinda Millener,10032,exact
|
||||
29618,kailynnbarnfield,HAkuguuvoy1,account_hVh2CveE2tBqctZhpLX5Nf,Kailynn Barnfield,10037,exact
|
||||
29619,thomasgraham,YUiygiiu4,account_fETWiC2bAqbb7hkQ5UvKjj,Thomas Graham,10040,exact
|
||||
27315,debravega,RCG8618BDUMSR,account_fbgziTjDnsrjH4aA2hMykS,Debra Vega,112,exact
|
||||
29582,davidlanman,2BMMRYQ2PHEA,account_u9P7hnf3FR5yqm7FFyCnrP,David Lanman,2486,exact
|
||||
29583,anthonylongo,9MLG8HVME4J3,account_eq2Po3eJPoEyv6RvhhyBMA,Anthony Longo,2487,exact
|
||||
29584,konradwoelffer,BCQ8NRFVNA3R,account_7B7CchNkRbNa6LLxRBform,Konrad Woelffer,2488,exact
|
||||
29585,marthaflores,KG2QEG36348V,account_cdbWPX28Kv3YSaYUDf7zj1,Martha Flores,2489,exact
|
||||
29586,connorspicer,6U2BWJKGCX8G,account_pV5Vstq2xr42fru5zVB28F,Connor Spicer,2490,exact
|
||||
29588,korybiggsshop,JA4SXBGKHFUL,account_irdUq6W33yA7J9vpQ5t85q,Kory Biggs Shop,2492,exact
|
||||
29589,ednagriffin,AXA8EZEAN7DJ,account_nnSEFSX86bVogx8KsqANUP,Edna Griffin,2493,exact
|
||||
29590,richardrosson,9J5L6MEH4Y7Y,account_jL9tR5FgswV7FZwxH3U5Ex,Richard Rosson,2526,exact
|
||||
29603,allentaylor2,NCJW87FW8723,account_sEcueqC1ysh7Ne2pE9qbg2,Allen Taylor #2,10009,exact
|
||||
29604,alextovias,7VSgrVO1WX,account_1BvWee8L5krmWQsfVUqCdk,Alex Tovias,476,exact
|
||||
29605,mikecurrence,MKH23FG79C,account_gbzPYU8zHeCSV7E5c5eksc,Mike Currence,10011,exact
|
||||
29594,terryrector,QUPP83ZUUSGL,account_bHx8uSNfzCrYkD8JriK1jV,Terry Rector,2529,exact
|
||||
29595,donnacampbell,KYCNTWS3929N,account_4y4ZEDFYqQhjW99pPJyLnf,Donna Campbell,2530,exact
|
||||
29600,billmctee,Bdge6388M,account_qdzi3bYpEjEx7crLFY4xCw,Bill McTee,373,exact
|
||||
29531,daycor,U45YBPSARH7N,account_aZmcKqobMWGKhxagDdLCFA,Cathy Day,755,fuzzy(manual_override)
|
||||
29521,tiffanyyarley-olenak,98HTZ9Q8FARG,account_cwmjmXkrYeba8h8RGqLm6E,Tiffany Yarley-Olenak,2393,fuzzy(sequence: 0.95)
|
||||
29395,vanessagomez1,J5JEW3HEBVAF,account_gb82EhqTwbNWWEovPu4oKZ,Vanessa Gomez,2264,fuzzy(last+first: gomez+vanessa(fn=1.00))
|
||||
29611,brittannieredden1,TYFTUEZ7429,account_bB6RDmNpkzndVbSh9CvgaL,Brittannie Redden,10017,fuzzy(last+first: redden+brittannie(fn=1.00))
|
||||
29614,samanthatheis,kf6u8djigk,account_x46hMNq15qHmEvP6fNFdfm,Samatha Theis,10023,fuzzy(last+first: theis+samatha(fn=0.70))
|
||||
29216,wanxiaxong,BER4NEK9M7NA,account_tiy5YHD34KpwPzUTXyRVFJ,Wanxia Song,2077,fuzzy(sequence: 0.86)
|
||||
29215,bryangoulart,DTNQFE4CXSFX,account_9sksoKmcJGxqhz4M4aD169,Brian Goulart,2076,fuzzy(last+first: goulart+brian(fn=0.70))
|
||||
29602,tech,brussels-crucial-smashing,account_jUFnZ52urgAMNgp9XhT4bs,Graham McIntire,1,fuzzy(manual_override)
|
||||
28935,chrislewis1,V9DS4E4Y7UTT,account_osB3NhyGdoWJRtpL5X2bfD,chris lewis,1787,fuzzy(last+first: lewis+chris(fn=1.00))
|
||||
28917,crankkeith,kcrank10615,account_iSuzVkNmem52NdXXh1Savu,Keith Crank,209,fuzzy(last+first: crank+keith(fn=1.00))
|
||||
28703,grahammcintire2,U5Y3hA3g*14L,account_jUFnZ52urgAMNgp9XhT4bs,Graham McIntire,1,fuzzy(last+first: mcintire+graham(fn=1.00))
|
||||
28702,chrissyeagle2,NJPCGPBNFB,account_3uu7k4euycmKnZii9DQAj2,Chrissy Eagle,74,fuzzy(last+first: eagle+chrissy(fn=1.00))
|
||||
28671,melodymccarty,U3WLEGEB,account_18HG9hdwwtZM83J9kyGMs6,Melody McCarthy,1397,fuzzy(sequence: 0.93)
|
||||
28642,barbihardin,ZENETJ75TEVQ,account_bb7iRuPixxutKZxnURfK5U,James Hardin,1475,fuzzy(manual_override)
|
||||
28628,jackworthy,2VUR7AAFWZZQ,account_hgoipVYXydD1yVSXTD9QGP,John Worthy,1461,fuzzy(last+first: worthy+john(fn=0.70))
|
||||
28542,michaelray2,jjPgDcotL6rtY,account_8Rh7edwTjNBYw9FJSq4WRT,Michael Ray,379,fuzzy(last+first: ray+michael(fn=1.00))
|
||||
28458,kennethcampbell2,V5TKW8L6RXFF,account_jF69QnP5j9FtfLgdAiUKfE,Kenneth D. Campbell,213,fuzzy(last+first: campbell+kenneth d(fn=1.00))
|
||||
28296,cinthiapereira1,9WSGXM9LBXGZ,account_57mDq22QWRV8tdPvk74Uvj,Cinthia Pereira,1102,fuzzy(last+first: pereira+cinthia(fn=1.00))
|
||||
28107,jacquelinewilder,HEY36LUGYCUQ,account_hHa9Z64B9okZ3yccQP9WpR,Jacquelin Wilder,892,fuzzy(last+first: wilder+jacquelin(fn=1.00))
|
||||
27939,reynamoore,R92nbskba7iM,account_k8gD8YGUQzwWCE7JT9EfYc,Rebekah Moore,386,fuzzy(last+first: moore+rebekah(fn=0.70))
|
||||
27912,blancadiaz,B548gdu55D,account_ktdNSpKRV3ja7RMi7pZRBB,Balnca Diaz,433,fuzzy(last+first: diaz+balnca(fn=0.70))
|
||||
27838,consueloluna1830,C8736gfd0,account_jw16PcKtprHpPkSAEhvsaN,Consuelo Luna,81,fuzzy(last+first: luna+consuelo(fn=1.00))
|
||||
27781,pambanschbach,Q2p68#*9vwF2,account_6hGXtJ7RV7mqZYfd5AyE1f,Pam Bansbach,383,fuzzy(sequence: 0.88)
|
||||
27639,choonpang,CP2143855677cp,account_o7aKk2FGKJAcmrXxMfUCpX,Geok Choon Pang,146,fuzzy(last+first: pang+geok choon(fn=1.00))
|
||||
27618,tammieventris,DR45QC74M,account_fMQ6qiwkPFWP8a86exFw4A,Terry Ventris,340,fuzzy(last+first: ventris+terry(fn=0.70))
|
||||
27579,donnance,9727346814,account_s9ZW1FpS3wkDoh31CjU1Zw,Dana Nance,89,fuzzy(last+first: nance+dana(fn=0.70))
|
||||
27501,jonnytaylor,Zb6677&4!yky,account_1fDruGS6CFTBUCGuzVZYTG,Johnny Taylor,190,fuzzy(last+first: taylor+johnny(fn=0.70))
|
||||
27436,clayrobertson2,C2147288546R,account_jgtX7TysTqcobVVBtSZEc2,Clay Robertson,77,fuzzy(last+first: robertson+clay(fn=1.00))
|
||||
27417,donniecampbell,6a2671bc0c1,account_4ww28eUsFRv8Yh5RkWe9cz,Danny Campbell,92,fuzzy(last+first: campbell+danny(fn=0.70))
|
||||
27418,alanwyatt,i1412r53MYr,account_2cnVCpSsYocCYpMEwwBBrY,Allen Wyatt,27,fuzzy(last+first: wyatt+allen(fn=0.70))
|
||||
29620,sayedahaque,YR4Sytbu,account_4hoTCjwTatthCg7zp2Cnfe,Syeda Haque,10048,fuzzy(last+first: haque+syeda(fn=0.70))
|
||||
29554,marcuspatton,GEUXX27VLZXK,account_dxY6AZnLdFUWup8Sf6XER1,Marcus Patton,2458,ambiguous_resolved(lowest_id)
|
||||
29606,mariacastanon,iu297gkid9,account_qh8bFPvWzyAqHqwHMDkThW,Maria Castanon,10007,ambiguous_resolved(lowest_id)
|
||||
28800,kennethcampbell,!9727342446!,account_jF69QnP5j9FtfLgdAiUKfE,Kenneth D. Campbell,213,ambiguous_resolved(lowest_id)
|
||||
28358,stephenday,MSVGQ8X38YLN,account_j1UFX8wqzorqSetUje5dTJ,Stephen Day,1168,ambiguous_resolved(lowest_id)
|
||||
27404,grahammcintire,U5Y3hA3g*14L,account_jUFnZ52urgAMNgp9XhT4bs,Graham McIntire,1,ambiguous_resolved(lowest_id)
|
||||
29630,sriahobilamuth,RTCESW24,account_7PgSr8dVuafybJdrXND3V9,Sri Ahobilamuth,113,ambiguous_resolved(lowest_id)
|
||||
29524,chandparvathaneni,TK4QA3XM5XK5,,,,"still_unmatched(best: , score: 0.0)"
|
||||
29358,radiantlifeministries,12FDIUNF5L7vM,,,,"still_unmatched(best: , score: 0.0)"
|
||||
29296,midvalleyconcrete,BD58JYT2PJ6A,,,,"still_unmatched(best: , score: 0.0)"
|
||||
29244,janiscable,JHC7U554JBTR,,,,"still_unmatched(best: last+weakfirst: cable+gary w(fn=0.11), score: 0.611)"
|
||||
29150,donwilhelmus,2V4NEL87RM39,,,,"still_unmatched(best: , score: 0.0)"
|
||||
29135,kavalleriefarm,8DFNNTPBPZGY,,,,"still_unmatched(best: , score: 0.0)"
|
||||
29077,kellygarza,4696591662,,,,"still_unmatched(best: last+weakfirst: garza+esparanza(fn=0.42), score: 0.642)"
|
||||
29070,1stchoiceliving,YVWJWRPEVYNH,,,,"still_unmatched(best: , score: 0.0)"
|
||||
28879,kimberlyrichards,7PQA6J8RPZ9Z,,,,"still_unmatched(best: last+weakfirst: kim+tae(fn=0.08), score: 0.608)"
|
||||
28599,ryanrodriguez,9DFYWJ47YWUP,,,,"still_unmatched(best: last+weakfirst: rodriguez+mike(fn=0.19), score: 0.619)"
|
||||
28460,saidaacosta,V29897X4,,,,"still_unmatched(best: last+weakfirst: acosta+alma(fn=0.21), score: 0.621)"
|
||||
28422,jackiehendricks,UQR5HGFCJ4XX,,,,"still_unmatched(best: last+weakfirst: hendricks+sue(fn=0.09), score: 0.609)"
|
||||
28400,bonniestone,BH8BV44TTWUU,,,,"still_unmatched(best: , score: 0.0)"
|
||||
28336,buddyswan,N7XUYUD7J9SD,,,,"still_unmatched(best: , score: 0.0)"
|
||||
28268,alberto,TICOGUILLO,,,,"still_unmatched(best: , score: 0.0)"
|
||||
28213,krisdougherty,RWEH75UJ38,,,,"still_unmatched(best: , score: 0.0)"
|
||||
27955,evlynrichter,FRQD738BGRC,,,,"still_unmatched(best: last+weakfirst: richter+brent(fn=0.19), score: 0.619)"
|
||||
27793,branchrvpark,XfKxVZIKmm,,,,"still_unmatched(best: , score: 0.0)"
|
||||
29601,kimberlyrichards2,K84f7r10GT108,,,,"still_unmatched(best: last+weakfirst: kim+tae(fn=0.08), score: 0.608)"
|
||||
27755,gordonhamilton,c3495Rpuq5Sh,,,,"still_unmatched(best: last+weakfirst: hamilton+sheryl f(fn=0.16), score: 0.616)"
|
||||
27709,sensibleheatsystems,SHQWERY23,,,,"still_unmatched(best: , score: 0.0)"
|
||||
27704,lambandlion,LL9727363567ll,,,,"still_unmatched(best: , score: 0.0)"
|
||||
27625,mattbud,kjklfda79sd,,,,"still_unmatched(best: , score: 0.0)"
|
||||
27621,richardhill,RH9729788124rh,,,,no_match
|
||||
27489,jenniferreed,683P7V7R,,,,"still_unmatched(best: , score: 0.0)"
|
||||
27482,irvinbarojas,yDeEJzYY08,,,,"still_unmatched(best: , score: 0.0)"
|
||||
27463,dennispereyra,82s62kd9XHhd,,,,"still_unmatched(best: , score: 0.0)"
|
||||
27441,samanthasmith,22jmR9X#93j5,,,,"still_unmatched(best: last+weakfirst: smith+kathy(fn=0.27), score: 0.627)"
|
||||
27408,penneywarner,b4h53N6c#5wo,,,,"still_unmatched(best: , score: 0.0)"
|
||||
29626,karengreen,UTKCYI674,,,,"still_unmatched(best: last+weakfirst: green+dominique(fn=0.17), score: 0.617)"
|
||||
|
68
gaiia/radius_gaiia_unmatched.csv
Normal file
68
gaiia/radius_gaiia_unmatched.csv
Normal file
|
|
@ -0,0 +1,68 @@
|
|||
radius_id,username,password,gaiia_id,gaiia_name,gaiia_readable_id,match_type
|
||||
29554,marcuspatton,GEUXX27VLZXK,,"AMBIGUOUS: Marcus Patton, Marcus Patton",,ambiguous
|
||||
29524,chandparvathaneni,TK4QA3XM5XK5,,,,no_match
|
||||
29521,tiffanyyarley-olenak,98HTZ9Q8FARG,,,,no_match
|
||||
29606,mariacastanon,iu297gkid9,,"AMBIGUOUS: Maria Castanon, Maria Castanon",,ambiguous
|
||||
29395,vanessagomez1,J5JEW3HEBVAF,,,,no_match
|
||||
29611,brittannieredden1,TYFTUEZ7429,,,,no_match
|
||||
29614,samanthatheis,kf6u8djigk,,,,no_match
|
||||
29358,radiantlifeministries,12FDIUNF5L7vM,,,,no_match
|
||||
29296,midvalleyconcrete,BD58JYT2PJ6A,,,,no_match
|
||||
29244,janiscable,JHC7U554JBTR,,,,no_match
|
||||
29216,wanxiaxong,BER4NEK9M7NA,,,,no_match
|
||||
29215,bryangoulart,DTNQFE4CXSFX,,,,no_match
|
||||
29150,donwilhelmus,2V4NEL87RM39,,,,no_match
|
||||
29135,kavalleriefarm,8DFNNTPBPZGY,,,,no_match
|
||||
29077,kellygarza,4696591662,,,,no_match
|
||||
29070,1stchoiceliving,YVWJWRPEVYNH,,,,no_match
|
||||
29602,tech,brussels-crucial-smashing,,,,no_match
|
||||
28935,chrislewis1,V9DS4E4Y7UTT,,,,no_match
|
||||
28917,crankkeith,kcrank10615,,,,no_match
|
||||
28879,kimberlyrichards,7PQA6J8RPZ9Z,,,,no_match
|
||||
28800,kennethcampbell,!9727342446!,,"AMBIGUOUS: Kenneth D. Campbell, Kenneth Campbell",,ambiguous
|
||||
28703,grahammcintire2,U5Y3hA3g*14L,,,,no_match
|
||||
28702,chrissyeagle2,NJPCGPBNFB,,,,no_match
|
||||
28671,melodymccarty,U3WLEGEB,,,,no_match
|
||||
28642,barbihardin,ZENETJ75TEVQ,,,,no_match
|
||||
28628,jackworthy,2VUR7AAFWZZQ,,,,no_match
|
||||
28599,ryanrodriguez,9DFYWJ47YWUP,,,,no_match
|
||||
28542,michaelray2,jjPgDcotL6rtY,,,,no_match
|
||||
28460,saidaacosta,V29897X4,,,,no_match
|
||||
28458,kennethcampbell2,V5TKW8L6RXFF,,,,no_match
|
||||
28422,jackiehendricks,UQR5HGFCJ4XX,,,,no_match
|
||||
28400,bonniestone,BH8BV44TTWUU,,,,no_match
|
||||
28358,stephenday,MSVGQ8X38YLN,,"AMBIGUOUS: Stephen Day, Stephen Day",,ambiguous
|
||||
28336,buddyswan,N7XUYUD7J9SD,,,,no_match
|
||||
28296,cinthiapereira1,9WSGXM9LBXGZ,,,,no_match
|
||||
28268,alberto,TICOGUILLO,,,,no_match
|
||||
29531,daycor,U45YBPSARH7N,,,,no_match
|
||||
29630,sriahobilamuth,RTCESW24,,"AMBIGUOUS: Sri Ahobilamuth, Sri Ahobilamuth",,ambiguous
|
||||
27408,penneywarner,b4h53N6c#5wo,,,,no_match
|
||||
29620,sayedahaque,YR4Sytbu,,,,no_match
|
||||
29626,karengreen,UTKCYI674,,,,no_match
|
||||
27404,grahammcintire,U5Y3hA3g*14L,,"AMBIGUOUS: Graham McIntire, Graham McIntire, Graham McIntire",,ambiguous
|
||||
27417,donniecampbell,6a2671bc0c1,,,,no_match
|
||||
27418,alanwyatt,i1412r53MYr,,,,no_match
|
||||
27441,samanthasmith,22jmR9X#93j5,,,,no_match
|
||||
27436,clayrobertson2,C2147288546R,,,,no_match
|
||||
27489,jenniferreed,683P7V7R,,,,no_match
|
||||
27501,jonnytaylor,Zb6677&4!yky,,,,no_match
|
||||
27482,irvinbarojas,yDeEJzYY08,,,,no_match
|
||||
27463,dennispereyra,82s62kd9XHhd,,,,no_match
|
||||
27579,donnance,9727346814,,,,no_match
|
||||
27838,consueloluna1830,C8736gfd0,,,,no_match
|
||||
27912,blancadiaz,B548gdu55D,,,,no_match
|
||||
27709,sensibleheatsystems,SHQWERY23,,,,no_match
|
||||
27704,lambandlion,LL9727363567ll,,,,no_match
|
||||
27625,mattbud,kjklfda79sd,,,,no_match
|
||||
27639,choonpang,CP2143855677cp,,,,no_match
|
||||
127621,richardhill,RH9729788124rh,,,,no_match
|
||||
27618,tammieventris,DR45QC74M,,,,no_match
|
||||
27955,evlynrichter,FRQD738BGRC,,,,no_match
|
||||
27939,reynamoore,R92nbskba7iM,,,,no_match
|
||||
27755,gordonhamilton,c3495Rpuq5Sh,,,,no_match
|
||||
27793,branchrvpark,XfKxVZIKmm,,,,no_match
|
||||
27781,pambanschbach,Q2p68#*9vwF2,,,,no_match
|
||||
29601,kimberlyrichards2,K84f7r10GT108,,,,no_match
|
||||
28213,krisdougherty,RWEH75UJ38,,,,no_match
|
||||
28107,jacquelinewilder,HEY36LUGYCUQ,,,,no_match
|
||||
|
2687
gaiia/radius_usernames.json
Normal file
2687
gaiia/radius_usernames.json
Normal file
File diff suppressed because it is too large
Load diff
415
gaiia/radius_users.txt
Normal file
415
gaiia/radius_users.txt
Normal file
|
|
@ -0,0 +1,415 @@
|
|||
| 29581 | joseortiz | Cleartext-Password | := | J2WEYT7782UJ |
|
||||
| 29578 | perlachavez2 | Cleartext-Password | := | U4BBTBMCJZST |
|
||||
| 29575 | thomashernandez | Cleartext-Password | := | X6BZLA7SC3XM |
|
||||
| 29572 | williamarmstead | Cleartext-Password | := | SAQ4XEKXDC58 |
|
||||
| 29570 | julierose | Cleartext-Password | := | MLM8R2WAGY52 |
|
||||
| 29571 | derrickmccausland | Cleartext-Password | := | ETNLKZHACGF7 |
|
||||
| 29567 | sherrifredrick | Cleartext-Password | := | 7P52BVMTSNN6 |
|
||||
| 29565 | shelbyburris | Cleartext-Password | := | 3YAL9DWFZUPT |
|
||||
| 29564 | priscillacrenshaw | Cleartext-Password | := | AXBDLXSGXMLR |
|
||||
| 29561 | korybiggs | Cleartext-Password | := | 4FJC7DXAW5RV |
|
||||
| 29563 | kristinjimenez | Cleartext-Password | := | 9FAYXMRDU69T |
|
||||
| 29558 | jessicaweddle | Cleartext-Password | := | VV96G36AEEDA |
|
||||
| 29557 | yolandamedrano | Cleartext-Password | := | R948UL4KZRA4 |
|
||||
| 29555 | williambowland | Cleartext-Password | := | K7HYNWGE25XX |
|
||||
| 29554 | marcuspatton | Cleartext-Password | := | GEUXX27VLZXK |
|
||||
| 29553 | shacaciabillings | Cleartext-Password | := | 8AZLJCDFYFYV |
|
||||
| 29552 | guadalupelugo | Cleartext-Password | := | DQG8XCMFMVH5 |
|
||||
| 29551 | brandonhardesty | Cleartext-Password | := | KMRHVPEXBEZ8 |
|
||||
| 29550 | nancyavellaneda | Cleartext-Password | := | CXATYV7NGZN2 |
|
||||
| 29549 | matthewgoodwin | Cleartext-Password | := | VSPM7N9UNU6Y |
|
||||
| 29545 | donmckinney | Cleartext-Password | := | C6AQ6UVD6R7R |
|
||||
| 29544 | natashaelmore | Cleartext-Password | := | 8G5QJ8R7MK8Q |
|
||||
| 29543 | shamsbashir | Cleartext-Password | := | WLBGY4JMN4VA |
|
||||
| 29541 | douglasglover | Cleartext-Password | := | 9Y6H9X3HCG3D |
|
||||
| 29538 | carolstrickland | Cleartext-Password | := | 547P6AQ8RCD4 |
|
||||
| 29537 | carmenlopez | Cleartext-Password | := | TPLND3H2FWTQ |
|
||||
| 29534 | johnlooper | Cleartext-Password | := | ZPL8LRY2TGRH |
|
||||
| 29535 | davidroesch | Cleartext-Password | := | ZHB2BS968DTU |
|
||||
| 29531 | daycor | Cleartext-Password | := | U45YBPSARH7N |
|
||||
| 29530 | derekrodriguez | Cleartext-Password | := | AP7Z594DMR7E |
|
||||
| 29524 | chandparvathaneni | Cleartext-Password | := | TK4QA3XM5XK5 |
|
||||
| 29522 | joshuasoliz | Cleartext-Password | := | PE7MHRZWUEQM |
|
||||
| 29521 | tiffanyyarley-olenak | Cleartext-Password | := | 98HTZ9Q8FARG |
|
||||
| 29514 | ericbarrett | Cleartext-Password | := | 5S2CV9FEFMKG |
|
||||
| 29513 | marysmelser | Cleartext-Password | := | ZS74NS5BBPX3 |
|
||||
| 29506 | nicolemaenn | Cleartext-Password | := | B7KS27WL5WSN |
|
||||
| 29503 | yolandajudge | Cleartext-Password | := | 6V8RLNK75LM8 |
|
||||
| 29501 | craighovde | Cleartext-Password | := | AVZXWBX8GQTC |
|
||||
| 29498 | joselucas | Cleartext-Password | := | ZHMW9TE8QHRX |
|
||||
| 29488 | douggarber | Cleartext-Password | := | WXP9J5SRJWEJ |
|
||||
| 29482 | carmencleghorn | Cleartext-Password | := | VH3CBTJZAJ2Y |
|
||||
| 29472 | rubenreyez | Cleartext-Password | := | 66384Y7B4WR4 |
|
||||
| 29464 | tiffanystewart | Cleartext-Password | := | 3EGVCS6RMF3Z |
|
||||
| 29449 | dejadodson | Cleartext-Password | := | FLR4DJHPX6TG |
|
||||
| 29447 | coreyball | Cleartext-Password | := | 3JAUWZNAR9MB |
|
||||
| 29442 | toniyoung | Cleartext-Password | := | RJ24VTRM7JA9 |
|
||||
| 29434 | melanieweddle | Cleartext-Password | := | XQ5L7PUM9RP6 |
|
||||
| 29431 | zackknuckey | Cleartext-Password | := | VP5CCX8LTE47 |
|
||||
| 29425 | christinehamparyan | Cleartext-Password | := | WEYX9R5P7J5L |
|
||||
| 29422 | joespeciale | Cleartext-Password | := | UH6ZCM9WLGRQ |
|
||||
| 29420 | rogerbatt | Cleartext-Password | := | KGDJZ7V66QQK |
|
||||
| 29419 | kalebjames | Cleartext-Password | := | KMQDGPD5XB6Z |
|
||||
| 29416 | stevemolina | Cleartext-Password | := | BLLHWPY9RCTC |
|
||||
| 29414 | mariatrejo | Cleartext-Password | := | EYPWCQKKWYZH |
|
||||
| 29410 | kenhall | Cleartext-Password | := | LX897BGJNMK5 |
|
||||
| 29407 | deewilliams | Cleartext-Password | := | R8AGX3E8WAHN |
|
||||
| 29406 | johnvayo | Cleartext-Password | := | DJJNUEEHGAW5 |
|
||||
| 29398 | anthonythompson | Cleartext-Password | := | WB4E4MPJVHTB |
|
||||
| 29608 | stevecombest | ClearText-Password | := | STVN23552Wt |
|
||||
| 29606 | mariacastanon | ClearText-Password | := | iu297gkid9 |
|
||||
| 29395 | vanessagomez1 | Cleartext-Password | := | J5JEW3HEBVAF |
|
||||
| 29607 | luisvelira | ClearText-Password | := | uygo875491! |
|
||||
| 29609 | keithtucker | ClearText-Password | := | 14FYJSNUU48 |
|
||||
| 29610 | angelatrevino | ClearText-Password | := | KJGU39565 |
|
||||
| 29611 | brittannieredden1 | ClearText-Password | := | TYFTUEZ7429 |
|
||||
| 29612 | amymontelongo | ClearText-Password | := | ig76f7fuvu |
|
||||
| 29613 | michaelasmith | ClearText-Password | := | MS21353GHY |
|
||||
| 29614 | samanthatheis | ClearText-Password | := | kf6u8djigk |
|
||||
| 29615 | janisstewart | ClearText-Password | := | JD834HCUW1C |
|
||||
| 29616 | irenegaribay | ClearText-Password | := | uky75dkc |
|
||||
| 29383 | leonardocoronel | Cleartext-Password | := | TXPC494 |
|
||||
| 29374 | alejandrolimon | Cleartext-Password | := | MM4BSQS7FRTD |
|
||||
| 29368 | crystalharney | Cleartext-Password | := | A7W26H834WLJ |
|
||||
| 29358 | radiantlifeministries | Cleartext-Password | := | 12FDIUNF5L7vM |
|
||||
| 29355 | olgagutierrez | Cleartext-Password | := | WERWRMUDBZ85 |
|
||||
| 29353 | greghummel | Cleartext-Password | := | JV84HSVAK6XR |
|
||||
| 29349 | bettymitchell | Cleartext-Password | := | 8CHHCNBWQLRV |
|
||||
| 29341 | wandadavis | Cleartext-Password | := | JXQXXZ9MKFX6 |
|
||||
| 29338 | scottanderson | Cleartext-Password | := | RJ49A43SG3RA |
|
||||
| 29335 | jenniferboon | Cleartext-Password | := | 28ESU8ELVELP |
|
||||
| 29331 | marialopez | Cleartext-Password | := | P3FNSPT7MEYM |
|
||||
| 29329 | cynthiasandlin | Cleartext-Password | := | MCDTLMVFQXAV |
|
||||
| 29328 | bethanybell | Cleartext-Password | := | 3UUC5X27PETS |
|
||||
| 29323 | garrettojeda | Cleartext-Password | := | U5PRQYZE2MM3 |
|
||||
| 29325 | rayschaak | Cleartext-Password | := | XSNLP62EK6VZ |
|
||||
| 29327 | carlaswearingen | Cleartext-Password | := | E6DQ4B2T9JZD |
|
||||
| 29320 | lesliebewley | Cleartext-Password | := | 55KFSJMEDYLK |
|
||||
| 29317 | delainawaite | Cleartext-Password | := | EM7TWHUS4SYU |
|
||||
| 29307 | esparanzagarza | Cleartext-Password | := | VLL9ZNPRSTG5 |
|
||||
| 29296 | midvalleyconcrete | Cleartext-Password | := | BD58JYT2PJ6A |
|
||||
| 29273 | glendabeauchamp | Cleartext-Password | := | RKA3J8WSPYJP |
|
||||
| 29256 | glindarichter | Cleartext-Password | := | VXMLMDAFLNJ3 |
|
||||
| 29251 | nhumorrison | Cleartext-Password | := | EC32VDFKB9CB |
|
||||
| 29245 | brittanyworley | Cleartext-Password | := | NBKYERRBSBH4 |
|
||||
| 29244 | janiscable | Cleartext-Password | := | JHC7U554JBTR |
|
||||
| 29243 | dustynfisher | Cleartext-Password | := | XASFU3DBPZ5M |
|
||||
| 29237 | stephenbeegle | Cleartext-Password | := | RU5GZVTVACRC |
|
||||
| 29229 | roseanndellavalle | Cleartext-Password | := | 5GAF6JHUWWYP |
|
||||
| 29231 | duanewright | Cleartext-Password | := | VXK4FBLVFE7F |
|
||||
| 29222 | pablohernandez | Cleartext-Password | := | P3YB6A4JGEDF |
|
||||
| 29217 | pavelavanesov | Cleartext-Password | := | KP8ZSGC2DG2U |
|
||||
| 29216 | wanxiaxong | Cleartext-Password | := | BER4NEK9M7NA |
|
||||
| 29215 | bryangoulart | Cleartext-Password | := | DTNQFE4CXSFX |
|
||||
| 29204 | sandrahoughton | Cleartext-Password | := | 5XR4QMPBXVWY |
|
||||
| 29203 | adamstaeben | Cleartext-Password | := | GP7RANJ8UCYP |
|
||||
| 29190 | russmott | Cleartext-Password | := | J8ZN888UQSLR |
|
||||
| 29189 | karenstewart | Cleartext-Password | := | JSKTFAJ2JYDX |
|
||||
| 29185 | heatherwomack | Cleartext-Password | := | 89W7S5Q7UAHP |
|
||||
| 29183 | wyndijames | Cleartext-Password | := | CPSDNRQU95HJ |
|
||||
| 29182 | angelicacuayahuitl | Cleartext-Password | := | VK8PZW3LK5F3 |
|
||||
| 29181 | chasewilliams | Cleartext-Password | := | W6AMPQEPY3BC |
|
||||
| 29176 | kevinarana | Cleartext-Password | := | JAJYSEG75UZS |
|
||||
| 29166 | chrisclayton | Cleartext-Password | := | BFJ462PLHJFV |
|
||||
| 29162 | billyhorton | Cleartext-Password | := | MPTX573ZUYYG |
|
||||
| 29151 | chisediquezada | Cleartext-Password | := | RJBYQ2NUB829 |
|
||||
| 29150 | donwilhelmus | Cleartext-Password | := | 2V4NEL87RM39 |
|
||||
| 29147 | angelmerlo | Cleartext-Password | := | EWWAPXN9Z2YS |
|
||||
| 29135 | kavalleriefarm | Cleartext-Password | := | 8DFNNTPBPZGY |
|
||||
| 29113 | kerryblack | Cleartext-Password | := | 9D5S9ZD2FCBV |
|
||||
| 29110 | cliffordjennings | Cleartext-Password | := | 5GC7M65H9VGT |
|
||||
| 29098 | carlakimberling | Cleartext-Password | := | SS4KRJYKTV7B |
|
||||
| 29088 | richardconklin | Cleartext-Password | := | WEUNG5H3K8BB |
|
||||
| 29089 | leandrabeshea | Cleartext-Password | := | 7V8BZU4G83VF |
|
||||
| 29077 | kellygarza | Cleartext-Password | := | 4696591662 |
|
||||
| 29070 | 1stchoiceliving | Cleartext-Password | := | YVWJWRPEVYNH |
|
||||
| 29067 | robertfinch | Cleartext-Password | := | D7FMTT455GBB |
|
||||
| 29058 | ericcoffman | Cleartext-Password | := | MMEBXQYLZWNU |
|
||||
| 29050 | nathanaelfinch | Cleartext-Password | := | JVNC68FV7N6B |
|
||||
| 29047 | gabrielasereno | Cleartext-Password | := | KCH82WCRA2YR |
|
||||
| 29045 | georginacovarrubias | Cleartext-Password | := | 8QT57NUV8C7R |
|
||||
| 29035 | thomascline | Cleartext-Password | := | E523BKVU5MJP |
|
||||
| 29034 | susanlewis | Cleartext-Password | := | R7TZFZEGLNRW |
|
||||
| 29025 | tammywelborn | Cleartext-Password | := | RMC482WNBS7 |
|
||||
| 29023 | haleymuela | Cleartext-Password | := | BS7X3DTKW8TH |
|
||||
| 29018 | christelles | Cleartext-Password | := | WL2TKLFG8G2H |
|
||||
| 29017 | mauricioantonio | Cleartext-Password | := | 6QJW7RAPDBWB |
|
||||
| 29013 | vincentlimon | Cleartext-Password | := | YDDXKFABUZY7 |
|
||||
| 29602 | tech | ClearText-Password | := | brussels-crucial-smashing |
|
||||
| 29634 | jeffreyharder | ClearText-Password | := | YUV565D67RT |
|
||||
| 29635 | pablomartinez | ClearText-Password | := | 2766581BER |
|
||||
| 29636 | krystalmendez | ClearText-Password | := | 6FTCKU6S56 |
|
||||
| 29637 | davidvandagriff | Cleartext-Password | := | UTRC53865F9 |
|
||||
| 29638 | carolynwalker | Cleartext-Password | := | trjxtyx75! |
|
||||
| 28979 | claygilbert | Cleartext-Password | := | 3E6XFDHXCAG7 |
|
||||
| 28978 | jmichaelculverhouse | Cleartext-Password | := | 2147099521 |
|
||||
| 28971 | diegosereno | Cleartext-Password | := | 2CE9F5YSPQHX |
|
||||
| 28970 | ashleysmith | Cleartext-Password | := | F8D3FVCZB7PD |
|
||||
| 28954 | dougstowe | Cleartext-Password | := | 5D35LFD6GEAZ |
|
||||
| 28936 | timbronschidle | ClearText-Password | := | 3ZVLCWSAE3EW1 |
|
||||
| 28935 | chrislewis1 | Cleartext-Password | := | V9DS4E4Y7UTT |
|
||||
| 28928 | cynthiajones | Cleartext-Password | := | KC7ESYHEZGNR |
|
||||
| 28917 | crankkeith | Cleartext-Password | := | kcrank10615 |
|
||||
| 28879 | kimberlyrichards | Cleartext-Password | := | 7PQA6J8RPZ9Z |
|
||||
| 28867 | bobbymack | Cleartext-Password | := | UDUBTCXXF8VV |
|
||||
| 28848 | americatrejo | Cleartext-Password | := | 3B63TLTT9WE4 |
|
||||
| 28832 | tjbanschbach | Cleartext-Password | := | 8WTWTXACGG5N |
|
||||
| 28811 | luiscabrera | Cleartext-Password | := | RWUQ94XT9A9Z |
|
||||
| 28810 | rosahernandez | Cleartext-Password | := | JSGUXKWEMSVN |
|
||||
| 28807 | carlosgomez | Cleartext-Password | := | J22RS7F3LGG2 |
|
||||
| 28800 | kennethcampbell | Cleartext-Password | := | !9727342446! |
|
||||
| 28763 | jeridrutledge | Cleartext-Password | := | GJ3CZTRJJW2M |
|
||||
| 28748 | timthomas | Cleartext-Password | := | NY223RQJD7PN |
|
||||
| 28715 | carolbates | Cleartext-Password | := | J3UBUCMYDX64 |
|
||||
| 28710 | shirleyelmore | Cleartext-Password | := | D8Y64N9L4R9W |
|
||||
| 28703 | grahammcintire2 | Cleartext-Password | := | U5Y3hA3g*14L |
|
||||
| 28702 | chrissyeagle2 | Cleartext-Password | := | NJPCGPBNFB |
|
||||
| 28698 | swedlund | Cleartext-Password | := | swedlund494 |
|
||||
| 28695 | wendysanders | Cleartext-Password | := | RYG9L43NWDZV |
|
||||
| 28693 | robbymccollom | Cleartext-Password | := | RMC482WNBS7 |
|
||||
| 28671 | melodymccarty | Cleartext-Password | := | U3WLEGEB |
|
||||
| 28664 | heatherbrister2 | Cleartext-Password | := | KK37V8W9ZD9Z |
|
||||
| 28663 | ryanlloyd | Cleartext-Password | := | HWEMKNWX55RF |
|
||||
| 28661 | amyrodriguez | Cleartext-Password | := | WX3QRT2U6L4X |
|
||||
| 28660 | thecountrycutter | Cleartext-Password | := | YXpScFIneePm |
|
||||
| 28659 | bradwilson | Cleartext-Password | := | ZFAXA9AK63AH |
|
||||
| 28654 | edrater | Cleartext-Password | := | FRY4823N |
|
||||
| 28642 | barbihardin | Cleartext-Password | := | ZENETJ75TEVQ |
|
||||
| 28637 | vikyacosta | Cleartext-Password | := | Z4SMV49SAH72 |
|
||||
| 28633 | terrimack | Cleartext-Password | := | B9HNTSN7ASQ4 |
|
||||
| 28628 | jackworthy | Cleartext-Password | := | 2VUR7AAFWZZQ |
|
||||
| 28617 | luisarellano | Cleartext-Password | := | GVNWBQMM3S7U |
|
||||
| 28609 | dorisavalos | Cleartext-Password | := | DLKMJS88AF2W |
|
||||
| 28599 | ryanrodriguez | Cleartext-Password | := | 9DFYWJ47YWUP |
|
||||
| 28597 | jackierollier | Cleartext-Password | := | TSFNN8DVDSPR |
|
||||
| 28588 | charlesmccreary | Cleartext-Password | := | 88J6XS6TXRMA |
|
||||
| 28548 | jamessmith | Cleartext-Password | := | LRVU6RVG5SP8 |
|
||||
| 28546 | davidwhitefoot | Cleartext-Password | := | PYHTZMHJU8R8- |
|
||||
| 28542 | michaelray2 | Cleartext-Password | := | jjPgDcotL6rtY |
|
||||
| 28536 | patriciavasquez | Cleartext-Password | := | P88256487V |
|
||||
| 28535 | adrianmcdaniel | Cleartext-Password | := | U4TGDH2PSAB4 |
|
||||
| 28526 | justinvoga | Cleartext-Password | := | URHVCX7SFSFU |
|
||||
| 28518 | jenniferlittle | Cleartext-Password | := | 8EAUUDQPXSQX |
|
||||
| 28510 | bradslate | Cleartext-Password | := | AF4KVQ2P44M7 |
|
||||
| 28494 | irisdelira | Cleartext-Password | := | L6XGQA583DG6 |
|
||||
| 28491 | richardbarragan | Cleartext-Password | := | WJBS86MMJBFH |
|
||||
| 28476 | jensenhall | Cleartext-Password | := | F8XM9U8SC6LL |
|
||||
| 28466 | paulcomella | Cleartext-Password | := | YTZDMBZCMPX4 |
|
||||
| 28460 | saidaacosta | Cleartext-Password | := | V29897X4 |
|
||||
| 28458 | kennethcampbell2 | Cleartext-Password | := | V5TKW8L6RXFF |
|
||||
| 28449 | amberkrings | Cleartext-Password | := | 5NWBR3JLV6N8 |
|
||||
| 28438 | mauriciosoto | Cleartext-Password | := | DJ7YP7W7AA8M |
|
||||
| 28435 | jimcrank | Cleartext-Password | := | PLA7XNX2F4Y3 |
|
||||
| 28425 | summerbeazley | Cleartext-Password | := | D4S4YRB8Y3KA |
|
||||
| 28426 | davidarmstrong | Cleartext-Password | := | MPEZGG53KQSZ |
|
||||
| 28422 | jackiehendricks | Cleartext-Password | := | UQR5HGFCJ4XX |
|
||||
| 28400 | bonniestone | Cleartext-Password | := | BH8BV44TTWUU |
|
||||
| 28395 | stevenspurgers | Cleartext-Password | := | YS5Q785J2HGZ |
|
||||
| 28388 | connercoleman | Cleartext-Password | := | 6108RANDOMREDDOG |
|
||||
| 28367 | judithfierro | Cleartext-Password | := | FUZ33LB6SFFC |
|
||||
| 28359 | tonyasipes | Cleartext-Password | := | N5VEZR8Q5M86 |
|
||||
| 28358 | stephenday | Cleartext-Password | := | MSVGQ8X38YLN |
|
||||
| 28347 | kathysmith | Cleartext-Password | := | SJ53PSYCZLPR |
|
||||
| 28344 | cherieeshelman | Cleartext-Password | := | BRUV99PN6Y7L |
|
||||
| 28336 | buddyswan | Cleartext-Password | := | N7XUYUD7J9SD |
|
||||
| 28296 | cinthiapereira1 | Cleartext-Password | := | 9WSGXM9LBXGZ |
|
||||
| 28289 | ronberger | Cleartext-Password | := | Ronb499H7ALW |
|
||||
| 28268 | alberto | Cleartext-Password | := | TICOGUILLO |
|
||||
| 28267 | sandracrouch | Cleartext-Password | := | V4N3764R5J |
|
||||
| 28263 | abbieandrews | Cleartext-Password | := | YQSJ499H7ALW |
|
||||
| 28253 | perlachavez | Cleartext-Password | := | ZWR9PXVFFYBS |
|
||||
| 28246 | susanking | Cleartext-Password | := | N2KUYLPDNXJP |
|
||||
| 28241 | terrybachran | Cleartext-Password | := | NG9VMH25LCTC |
|
||||
| 28240 | lanaygaunce | Cleartext-Password | := | MJCNZAJHK9H3 |
|
||||
| 28213 | krisdougherty | Cleartext-Password | := | RWEH75UJ38 |
|
||||
| 28190 | heatherbrister | Cleartext-Password | := | 6XPLC545G6HW |
|
||||
| 28184 | jenniferdunaway | Cleartext-Password | := | 343V5CT2R7HZ |
|
||||
| 28159 | elviraquezada | Cleartext-Password | := | HXAS29VRX33V |
|
||||
| 28154 | jordainpatton | Cleartext-Password | := | J8P4TPF7QS2A |
|
||||
| 28140 | mattkosarek | Cleartext-Password | := | RJ4BQUZJY |
|
||||
| 28117 | shannonclark | Cleartext-Password | := | F5XESMCJ |
|
||||
| 28107 | jacquelinewilder | Cleartext-Password | := | HEY36LUGYCUQ |
|
||||
| 28098 | kevincostanzo | Cleartext-Password | := | 244VGMVMH66D |
|
||||
| 28093 | kenpatrick | Cleartext-Password | := | V7U3XBAPTSV3 |
|
||||
| 28083 | briangallimore | Cleartext-Password | := | 39V285NX |
|
||||
| 28072 | amberprater | Cleartext-Password | := | RP42B7AYDLAP |
|
||||
| 28068 | marilynfowler | Cleartext-Password | := | MarilynVG72P8H |
|
||||
| 28065 | douglaswilson | Cleartext-Password | := | HP97HG9XPYC3 |
|
||||
| 28034 | terrygortney | Cleartext-Password | := | GQUGR3VJUXBR |
|
||||
| 28031 | gizellebutler | Cleartext-Password | := | HMZTX66RTPVX |
|
||||
| 28029 | bradgurney | Cleartext-Password | := | 7A6LRZWT82DX |
|
||||
| 28024 | darrylchavis | Cleartext-Password | := | TDGUUNXGMYRC |
|
||||
| 28025 | kathymazzola | Cleartext-Password | := | 5MVS8VUF9QL2 |
|
||||
| 27994 | austinwatkins | Cleartext-Password | := | S7M4UD8QUTDP |
|
||||
| 27988 | oscarrodriguez | Cleartext-Password | := | TGPCF3Y2ABXS |
|
||||
| 27986 | erinthompson | Cleartext-Password | := | 69TQZ797 |
|
||||
| 27983 | brianhardesty | Cleartext-Password | := | 8D4P34N8 |
|
||||
| 27976 | cathyday | Cleartext-Password | := | FAXMKENV8FCT |
|
||||
| 27956 | fredheckel | Cleartext-Password | := | HUVZSE8RGD65 |
|
||||
| 27955 | evlynrichter | Cleartext-Password | := | FRQD738BGRC |
|
||||
| 27949 | charlesboone | Cleartext-Password | := | Ccbn78wdfksB |
|
||||
| 27948 | rachelguzzardi | Cleartext-Password | := | R87wgerf8bsG |
|
||||
| 27939 | reynamoore | Cleartext-Password | := | R92nbskba7iM |
|
||||
| 27936 | juanalonzo | Cleartext-Password | := | 745GF0JK5!W |
|
||||
| 27930 | kristinamurphy | Cleartext-Password | := | oPXFrylqKf |
|
||||
| 27926 | johnnygoble | Cleartext-Password | := | J829fhf7G |
|
||||
| 27923 | keithbarber | Cleartext-Password | := | K73bvd54tB |
|
||||
| 27917 | doreengrubb | Cleartext-Password | := | DOR32SVT1234 |
|
||||
| 27916 | steveraia | Cleartext-Password | := | DRR648DFTMSE4 |
|
||||
| 27912 | blancadiaz | Cleartext-Password | := | B548gdu55D |
|
||||
| 27896 | twcowley | Cleartext-Password | := | TW486TW |
|
||||
| 27888 | joepatton | Cleartext-Password | := | L7luYzSKSu |
|
||||
| 27873 | timfisher | Cleartext-Password | := | Thd73gdf5eF |
|
||||
| 27847 | kileigheven | Cleartext-Password | := | K9872gdf8E |
|
||||
| 27838 | consueloluna1830 | Cleartext-Password | := | C8736gfd0 |
|
||||
| 27833 | deannecook | Cleartext-Password | := | Dc872cobnC |
|
||||
| 27830 | kacyharrison | Cleartext-Password | := | 7Ozr7el6K1 |
|
||||
| 27822 | milliganwater | Cleartext-Password | := | 8NjGMNsTXd |
|
||||
| 27813 | michaelnewby | Cleartext-Password | := | TM5M3HU877 |
|
||||
| 27808 | ladonnaclark | Cleartext-Password | := | YQReVH9men |
|
||||
| 27800 | jamesmooney | Cleartext-Password | := | QNUqD2tSzq |
|
||||
| 27793 | branchrvpark | Cleartext-Password | := | XfKxVZIKmm |
|
||||
| 27783 | tamythomas | Cleartext-Password | := | TT4699520858tt |
|
||||
| 27781 | pambanschbach | Cleartext-Password | := | Q2p68#*9vwF2 |
|
||||
| 27780 | josephcruz | Cleartext-Password | := | Z06oxJXrNd |
|
||||
| 29601 | kimberlyrichards2 | ClearText-Password | := | K84f7r10GT108 |
|
||||
| 27761 | franceskirby | Cleartext-Password | := | FK8179391152fk |
|
||||
| 27759 | chadwhitsell | Cleartext-Password | := | 79374B75phGy |
|
||||
| 27754 | ruthfengler | Cleartext-Password | := | 8q8MX6NQP3b6 |
|
||||
| 27755 | gordonhamilton | Cleartext-Password | := | c3495Rpuq5Sh |
|
||||
| 27751 | bobbyfisher | Cleartext-Password | := | salvagejuggle |
|
||||
| 27748 | eddieyarbrough | Cleartext-Password | := | 89298ChZWzMy |
|
||||
| 27744 | chrissyeagle | Cleartext-Password | := | 9vkX97426Sgt |
|
||||
| 27739 | victoriaobier | Cleartext-Password | := | M57B288P9 |
|
||||
| 27736 | bernardheer | Cleartext-Password | := | BH33682019BER |
|
||||
| 27734 | garycable | Cleartext-Password | := | M2w6z5Y73 |
|
||||
| 27735 | coyungemach | Cleartext-Password | := | MhS569N84e7a |
|
||||
| 27733 | michaelmccauley | Cleartext-Password | := | CPORADIO98 |
|
||||
| 27731 | lessruble | Cleartext-Password | := | 9U35SXxM2H8y |
|
||||
| 27730 | marknunnelee | Cleartext-Password | := | 833j2Ymm4yfV |
|
||||
| 27729 | elizabethchristian | Cleartext-Password | := | Webg22nj |
|
||||
| 27724 | mikebell | Cleartext-Password | := | BHF538HSEBDLY4 |
|
||||
| 27713 | krystabates | Cleartext-Password | := | KB2145445824kb |
|
||||
| 27709 | sensibleheatsystems | Cleartext-Password | := | SHQWERY23 |
|
||||
| 27708 | chrisfrancis | Cleartext-Password | := | DR45QC74M |
|
||||
| 27704 | lambandlion | Cleartext-Password | := | LL9727363567ll |
|
||||
| 27701 | markfisher | Cleartext-Password | := | 32fd8P&rw8s7 |
|
||||
| 27696 | kirkvanmeter | Cleartext-Password | := | J2sR9g^83M33 |
|
||||
| 27694 | dananance | Cleartext-Password | := | 23SP$e!ug232 |
|
||||
| 27690 | keithcrank | Cleartext-Password | := | 2zt3T5w72T3A |
|
||||
| 27687 | scottarmstrong | Cleartext-Password | := | 2SV58M55 |
|
||||
| 27683 | floydday | Cleartext-Password | := | Tfdd3254tfD |
|
||||
| 27680 | mariacortes | Cleartext-Password | := | MC2145858276mc |
|
||||
| 27668 | christianlummus | ClearText-Password | := | K6dbr631ddL |
|
||||
| 27666 | marklindsey | Cleartext-Password | := | ML9727346434ml |
|
||||
| 27658 | joeywhitfield | Cleartext-Password | := | BV758qXne446 |
|
||||
| 27654 | taekim | Cleartext-Password | := | SAN11837TCK |
|
||||
| 27652 | teresarobinson | Cleartext-Password | := | T645trdf87fR |
|
||||
| 27651 | nathanmctee | Cleartext-Password | := | N32462uu8rM |
|
||||
| 27646 | rebekahmoore | Cleartext-Password | := | R756346tfg4M |
|
||||
| 27643 | monicatrevino | Cleartext-Password | := | MT2145877143mt |
|
||||
| 27640 | rachelfuller | Cleartext-Password | := | R8364hbst |
|
||||
| 27639 | choonpang | Cleartext-Password | := | CP2143855677cp |
|
||||
| 27638 | shannonbrown | Cleartext-Password | := | S9725298223b |
|
||||
| 27637 | allentaylor | Cleartext-Password | := | A6346gge8T1 |
|
||||
| 27632 | kristenaustin | Cleartext-Password | := | KA8017916972ka |
|
||||
| 27630 | colinhopper | Cleartext-Password | := | TYVU65445IUB |
|
||||
| 27625 | mattbud | Cleartext-Password | := | kjklfda79sd |
|
||||
| 27621 | richardhill | Cleartext-Password | := | RH9729788124rh |
|
||||
| 27619 | srireddy | Cleartext-Password | := | ycHAvetmF0 |
|
||||
| 27618 | tammieventris | Cleartext-Password | := | DR45QC74M |
|
||||
| 27615 | almaacosta | Cleartext-Password | := | ENKC5DGV |
|
||||
| 27614 | chrispassonno | Cleartext-Password | := | C8766thf96P |
|
||||
| 27613 | seanbaxter | Cleartext-Password | := | SB4696679337sb |
|
||||
| 27612 | collinhartman | Cleartext-Password | := | B886tg98jt9L |
|
||||
| 27610 | michaelhughes | Cleartext-Password | := | 1RKk32MyTK |
|
||||
| 27608 | michaeltalbot | Cleartext-Password | := | Mjj1276rt99T |
|
||||
| 27606 | anagonzalez | Cleartext-Password | := | WNAHDMwWoE |
|
||||
| 27604 | leonardlewis | Cleartext-Password | := | L1877ef32L |
|
||||
| 27598 | jerryjohnson | Cleartext-Password | := | J177895aa98J |
|
||||
| 27594 | pamtoler | Cleartext-Password | := | NfxcgYQUbj |
|
||||
| 27589 | jodymccluskey | Cleartext-Password | := | JM4696883349jm |
|
||||
| 27579 | donnance | Cleartext-Password | := | 9727346814 |
|
||||
| 27577 | chrisvannatta | Cleartext-Password | := | !4694502484! |
|
||||
| 27575 | mariomerlo | Cleartext-Password | := | !2149579585! |
|
||||
| 27572 | williamarmstrong | Cleartext-Password | := | !2145510334! |
|
||||
| 27571 | trecedaschmid | Cleartext-Password | := | !4692075942! |
|
||||
| 27570 | ronlewis | Cleartext-Password | := | JDkf1AL942 |
|
||||
| 27567 | sherryerichardson | Cleartext-Password | := | DR6528BCD3S2 |
|
||||
| 27566 | joannarodriguez | Cleartext-Password | := | 394X3YR9 |
|
||||
| 27561 | dannycampbell | Cleartext-Password | := | !9726589541! |
|
||||
| 27560 | howardcomstock | Cleartext-Password | := | !3365494230! |
|
||||
| 27555 | terrymiesen | Cleartext-Password | := | !4697420828! |
|
||||
| 27550 | michaelray | Cleartext-Password | := | primitivedefinitionstoredinner |
|
||||
| 27545 | terrybates | Cleartext-Password | := | !2145786644! |
|
||||
| 27538 | gregmcintire | Cleartext-Password | := | v9368$E9mx9m |
|
||||
| 27543 | beverlyerwin | Cleartext-Password | := | MOOLAH |
|
||||
| 27518 | helenlumpkin | Cleartext-Password | := | Heehhejd0N |
|
||||
| 27508 | stevechristiaens | Cleartext-Password | := | zY272m258*vJ |
|
||||
| 27505 | judydevine | Cleartext-Password | := | OD4697669725od |
|
||||
| 27501 | jonnytaylor | Cleartext-Password | := | Zb6677&4!yky |
|
||||
| 27500 | rhondabolton | Cleartext-Password | := | RB2145786030rb |
|
||||
| 27492 | rickbeckham | Cleartext-Password | := | tVj7ZI5QA0 |
|
||||
| 27489 | jenniferreed | Cleartext-Password | := | 683P7V7R |
|
||||
| 27490 | vancepeltonen | Cleartext-Password | := | V66rdf43aaP |
|
||||
| 27486 | jacobwilliams | Cleartext-Password | := | jnlka9994jkfldsa |
|
||||
| 27482 | irvinbarojas | Cleartext-Password | := | yDeEJzYY08 |
|
||||
| 27478 | classicshear | Cleartext-Password | := | KARENALDERSON |
|
||||
| 27477 | davekiowski | Cleartext-Password | := | FNW63KXSAPT7 |
|
||||
| 27463 | dennispereyra | Cleartext-Password | := | 82s62kd9XHhd |
|
||||
| 27458 | paulsammataro | Cleartext-Password | := | P2n74V7P8g4S |
|
||||
| 27460 | janetkern | Cleartext-Password | := | j42252^Bhf44 |
|
||||
| 27457 | carlossalaises | Cleartext-Password | := | 28E358y$JFB9 |
|
||||
| 27453 | obedsandoval | Cleartext-Password | := | *H78z9xy84C& |
|
||||
| 27455 | dianewiley | Cleartext-Password | := | Diane03t4&adTL |
|
||||
| 27444 | tammylove | Cleartext-Password | := | f65NS4Ct6$29 |
|
||||
| 27441 | samanthasmith | Cleartext-Password | := | 22jmR9X#93j5 |
|
||||
| 27438 | floydallen | Cleartext-Password | := | V3864VB2 |
|
||||
| 27436 | clayrobertson2 | Cleartext-Password | := | C2147288546R |
|
||||
| 27432 | hortenciaalvarez | Cleartext-Password | := | WHTABORG3593JKS |
|
||||
| 27424 | timbagert | Cleartext-Password | := | CF18n42cY7 |
|
||||
| 27425 | davidbaker | Cleartext-Password | := | DB764g1d87rtB |
|
||||
| 27421 | janicealexander | Cleartext-Password | := | AG57TSH89L9 |
|
||||
| 27423 | timgilbert | Cleartext-Password | := | 73xi0r600Sz% |
|
||||
| 27417 | donniecampbell | Cleartext-Password | := | 6a2671bc0c1 |
|
||||
| 27418 | alanwyatt | Cleartext-Password | := | i1412r53MYr |
|
||||
| 27413 | jesusrios | Cleartext-Password | := | 8Di6QMs7%7m1 |
|
||||
| 27408 | penneywarner | Cleartext-Password | := | b4h53N6c#5wo |
|
||||
| 27409 | veronabaptist | Cleartext-Password | := | DAY753LIGHT253 |
|
||||
| 27406 | whiteywhite | Cleartext-Password | := | 45z6r*H4418n |
|
||||
| 27404 | grahammcintire | Cleartext-Password | := | U5Y3hA3g*14L |
|
||||
| 27402 | louislafon | Cleartext-Password | := | RFTRE4825CVKL |
|
||||
| 27397 | ausencioluna | Cleartext-Password | := | ABN765MNL |
|
||||
| 27374 | davidvillanueva | Cleartext-Password | := | DV5489CRPMWBQ |
|
||||
| 29620 | sayedahaque | ClearText-Password | := | YR4Sytbu |
|
||||
| 29621 | coryrees | ClearText-Password | := | yt5c6ujhvu |
|
||||
| 29622 | brianamartinez | ClearText-Password | := | ktydr6428 |
|
||||
| 29623 | khushbooagarwal | ClearText-Password | := | UUTFVUCTC446 |
|
||||
| 29624 | elisasanders | ClearText-Password | := | OYTCu3276 |
|
||||
| 29625 | josemedrano | ClearText-Password | := | UYVITRUR24 |
|
||||
| 29626 | karengreen | ClearText-Password | := | UTKCYI674 |
|
||||
| 29627 | nicholasterry | ClearText-Password | := | VUTYVL746 |
|
||||
| 29628 | leslieperalta | ClearText-Password | := | RYC66747 |
|
||||
| 29629 | khushbooagarwal2 | ClearText-Password | := | iytf65gkj |
|
||||
| 29630 | sriahobilamuth | ClearText-Password | := | RTCESW24 |
|
||||
| 29631 | royrogers | ClearText-Password | := | TYDGVBNC |
|
||||
| 29632 | arturoprieto | ClearText-Password | := | 1653APVN877 |
|
||||
| 29633 | ericamorris | ClearText-Password | := | E16488VN387 |
|
||||
| 29617 | belindamillener | ClearText-Password | := | Uiyu7280 |
|
||||
| 29618 | kailynnbarnfield | ClearText-Password | := | HAkuguuvoy1 |
|
||||
| 29619 | thomasgraham | ClearText-Password | := | YUiygiiu4 |
|
||||
| 27315 | debravega | Cleartext-Password | := | RCG8618BDUMSR |
|
||||
| 29582 | davidlanman | Cleartext-Password | := | 2BMMRYQ2PHEA |
|
||||
| 29583 | anthonylongo | Cleartext-Password | := | 9MLG8HVME4J3 |
|
||||
| 29584 | konradwoelffer | Cleartext-Password | := | BCQ8NRFVNA3R |
|
||||
| 29585 | marthaflores | Cleartext-Password | := | KG2QEG36348V |
|
||||
| 29586 | connorspicer | Cleartext-Password | := | 6U2BWJKGCX8G |
|
||||
| 29588 | korybiggsshop | Cleartext-Password | := | JA4SXBGKHFUL |
|
||||
| 29589 | ednagriffin | Cleartext-Password | := | AXA8EZEAN7DJ |
|
||||
| 29590 | richardrosson | Cleartext-Password | := | 9J5L6MEH4Y7Y |
|
||||
| 29603 | allentaylor2 | ClearText-Password | := | NCJW87FW8723 |
|
||||
| 29604 | alextovias | ClearText-Password | := | 7VSgrVO1WX |
|
||||
| 29605 | mikecurrence | ClearText-Password | := | MKH23FG79C |
|
||||
| 29594 | terryrector | Cleartext-Password | := | QUPP83ZUUSGL |
|
||||
| 29595 | donnacampbell | Cleartext-Password | := | KYCNTWS3929N |
|
||||
| 29600 | billmctee | ClearText-Password | := | Bdge6388M |
|
||||
41
gaiia/src/gaiia/__init__.py
Normal file
41
gaiia/src/gaiia/__init__.py
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
"""gaiia GraphQL API client.
|
||||
|
||||
Quick start:
|
||||
|
||||
from gaiia import GaiiaClient
|
||||
|
||||
with GaiiaClient(api_key="gaiia_sk_live_...") as g:
|
||||
result = g.query("{ technicians(first: 10) { nodes { id firstName lastName } } }")
|
||||
"""
|
||||
|
||||
from gaiia.client import AsyncGaiiaClient, GaiiaClient
|
||||
from gaiia.errors import (
|
||||
GaiiaAPIError,
|
||||
GaiiaError,
|
||||
GaiiaMutationError,
|
||||
GaiiaRateLimitedError,
|
||||
GaiiaTransportError,
|
||||
)
|
||||
from gaiia.globalid import decode_global_id, encode_global_id
|
||||
from gaiia.ratelimit import RateLimitInfo
|
||||
from gaiia.webhooks import (
|
||||
WebhookSignatureError,
|
||||
verify_webhook_signature,
|
||||
)
|
||||
|
||||
__all__ = [
|
||||
"AsyncGaiiaClient",
|
||||
"GaiiaAPIError",
|
||||
"GaiiaClient",
|
||||
"GaiiaError",
|
||||
"GaiiaMutationError",
|
||||
"GaiiaRateLimitedError",
|
||||
"GaiiaTransportError",
|
||||
"RateLimitInfo",
|
||||
"WebhookSignatureError",
|
||||
"decode_global_id",
|
||||
"encode_global_id",
|
||||
"verify_webhook_signature",
|
||||
]
|
||||
|
||||
__version__ = "0.1.0"
|
||||
392
gaiia/src/gaiia/client.py
Normal file
392
gaiia/src/gaiia/client.py
Normal file
|
|
@ -0,0 +1,392 @@
|
|||
"""Sync and async GraphQL clients for the gaiia API."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import os
|
||||
import time
|
||||
from types import TracebackType
|
||||
from typing import Any, AsyncIterator, Iterator
|
||||
|
||||
import httpx
|
||||
|
||||
from gaiia.errors import (
|
||||
GaiiaAPIError,
|
||||
GaiiaMutationError,
|
||||
GaiiaRateLimitedError,
|
||||
GaiiaTransportError,
|
||||
GraphQLError,
|
||||
)
|
||||
from gaiia.pagination import aiter_nodes, iter_nodes
|
||||
from gaiia.ratelimit import RateLimitInfo
|
||||
|
||||
DEFAULT_BASE_URL = "https://api.gaiia.com/api/v1"
|
||||
DEFAULT_TIMEOUT = 30.0
|
||||
API_KEY_HEADER = "X-Gaiia-Api-Key"
|
||||
TIMEZONE_HEADER = "x-timezone"
|
||||
USER_AGENT = "gaiia-py/0.1.0"
|
||||
|
||||
|
||||
def _build_headers(
|
||||
api_key: str, timezone: str | None, extra: dict[str, str] | None
|
||||
) -> dict[str, str]:
|
||||
headers = {
|
||||
"Content-Type": "application/json",
|
||||
"Accept": "application/json",
|
||||
"User-Agent": USER_AGENT,
|
||||
API_KEY_HEADER: api_key,
|
||||
}
|
||||
if timezone:
|
||||
headers[TIMEZONE_HEADER] = timezone
|
||||
if extra:
|
||||
headers.update(extra)
|
||||
return headers
|
||||
|
||||
|
||||
def _classify_errors(raw_errors: list[dict[str, Any]]) -> tuple[bool, list[GraphQLError]]:
|
||||
"""Return ``(rate_limited, parsed_errors)`` for top-level GraphQL errors."""
|
||||
errors = [GraphQLError.from_dict(e) for e in raw_errors]
|
||||
rate_limited = any(e.code == "RATE_LIMITED" for e in errors)
|
||||
return rate_limited, errors
|
||||
|
||||
|
||||
def _raise_for_payload(payload: dict[str, Any]) -> None:
|
||||
raw_errors = payload.get("errors")
|
||||
if not raw_errors:
|
||||
return
|
||||
rate_limited, errors = _classify_errors(raw_errors)
|
||||
if rate_limited:
|
||||
ext = errors[0].extensions
|
||||
retry_at = None
|
||||
if ext.get("retryAt"):
|
||||
from datetime import datetime
|
||||
|
||||
try:
|
||||
retry_at = datetime.fromisoformat(str(ext["retryAt"]).replace("Z", "+00:00"))
|
||||
except ValueError:
|
||||
retry_at = None
|
||||
raise GaiiaRateLimitedError(
|
||||
errors,
|
||||
cost=ext.get("cost"),
|
||||
limit=ext.get("limit"),
|
||||
used=ext.get("used"),
|
||||
remaining=ext.get("remaining"),
|
||||
retry_at=retry_at,
|
||||
)
|
||||
raise GaiiaAPIError(errors, data=payload.get("data"))
|
||||
|
||||
|
||||
class _Base:
|
||||
def __init__(
|
||||
self,
|
||||
api_key: str | None = None,
|
||||
*,
|
||||
base_url: str = DEFAULT_BASE_URL,
|
||||
timezone: str | None = None,
|
||||
timeout: float = DEFAULT_TIMEOUT,
|
||||
default_headers: dict[str, str] | None = None,
|
||||
max_retries: int = 0,
|
||||
) -> None:
|
||||
resolved = api_key if api_key is not None else os.environ.get("GAIIA_KEY")
|
||||
if not resolved:
|
||||
raise ValueError(
|
||||
"api_key is required (pass api_key= or set the GAIIA_KEY env var)"
|
||||
)
|
||||
self._api_key = resolved
|
||||
self._base_url = base_url
|
||||
self._timezone = timezone
|
||||
self._timeout = timeout
|
||||
self._default_headers = default_headers
|
||||
self._max_retries = max_retries
|
||||
self.last_rate_limit: RateLimitInfo | None = None
|
||||
|
||||
def _headers(self, extra: dict[str, str] | None = None) -> dict[str, str]:
|
||||
merged = dict(self._default_headers or {})
|
||||
if extra:
|
||||
merged.update(extra)
|
||||
return _build_headers(self._api_key, self._timezone, merged)
|
||||
|
||||
|
||||
class GaiiaClient(_Base):
|
||||
"""Synchronous gaiia GraphQL client."""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
api_key: str | None = None,
|
||||
*,
|
||||
base_url: str = DEFAULT_BASE_URL,
|
||||
timezone: str | None = None,
|
||||
timeout: float = DEFAULT_TIMEOUT,
|
||||
default_headers: dict[str, str] | None = None,
|
||||
max_retries: int = 0,
|
||||
transport: httpx.BaseTransport | None = None,
|
||||
) -> None:
|
||||
super().__init__(
|
||||
api_key,
|
||||
base_url=base_url,
|
||||
timezone=timezone,
|
||||
timeout=timeout,
|
||||
default_headers=default_headers,
|
||||
max_retries=max_retries,
|
||||
)
|
||||
self._http = httpx.Client(timeout=timeout, transport=transport)
|
||||
|
||||
def __enter__(self) -> GaiiaClient:
|
||||
return self
|
||||
|
||||
def __exit__(
|
||||
self,
|
||||
exc_type: type[BaseException] | None,
|
||||
exc: BaseException | None,
|
||||
tb: TracebackType | None,
|
||||
) -> None:
|
||||
self.close()
|
||||
|
||||
def close(self) -> None:
|
||||
self._http.close()
|
||||
|
||||
def query(
|
||||
self,
|
||||
query: str,
|
||||
variables: dict[str, Any] | None = None,
|
||||
*,
|
||||
operation_name: str | None = None,
|
||||
timezone: str | None = None,
|
||||
extra_headers: dict[str, str] | None = None,
|
||||
) -> dict[str, Any]:
|
||||
"""Run a GraphQL query/mutation and return the ``data`` payload.
|
||||
|
||||
Raises :class:`GaiiaAPIError` on top-level errors,
|
||||
:class:`GaiiaRateLimitedError` on RATE_LIMITED, and
|
||||
:class:`GaiiaTransportError` on network failures.
|
||||
"""
|
||||
body: dict[str, Any] = {"query": query}
|
||||
if variables is not None:
|
||||
body["variables"] = variables
|
||||
if operation_name is not None:
|
||||
body["operationName"] = operation_name
|
||||
|
||||
per_call_headers = dict(extra_headers or {})
|
||||
if timezone is not None:
|
||||
per_call_headers[TIMEZONE_HEADER] = timezone
|
||||
headers = self._headers(per_call_headers)
|
||||
|
||||
attempt = 0
|
||||
while True:
|
||||
attempt += 1
|
||||
try:
|
||||
resp = self._http.post(self._base_url, json=body, headers=headers)
|
||||
except httpx.HTTPError as e:
|
||||
raise GaiiaTransportError(str(e)) from e
|
||||
|
||||
self.last_rate_limit = RateLimitInfo.from_headers(resp.headers)
|
||||
try:
|
||||
payload = resp.json()
|
||||
except ValueError as e:
|
||||
raise GaiiaTransportError(
|
||||
f"non-JSON response from gaiia (HTTP {resp.status_code})"
|
||||
) from e
|
||||
|
||||
try:
|
||||
_raise_for_payload(payload)
|
||||
except GaiiaRateLimitedError as e:
|
||||
if attempt > self._max_retries:
|
||||
raise
|
||||
_sleep_until(e.retry_at)
|
||||
continue
|
||||
|
||||
if resp.status_code >= 500:
|
||||
raise GaiiaTransportError(
|
||||
f"gaiia returned HTTP {resp.status_code}: {resp.text[:200]}"
|
||||
)
|
||||
return payload.get("data") or {}
|
||||
|
||||
def mutate(
|
||||
self,
|
||||
query: str,
|
||||
mutation_name: str,
|
||||
variables: dict[str, Any] | None = None,
|
||||
*,
|
||||
timezone: str | None = None,
|
||||
raise_on_payload_errors: bool = True,
|
||||
) -> dict[str, Any]:
|
||||
"""Run a mutation and inspect its ``errors`` payload field.
|
||||
|
||||
Returns the mutation's result object (e.g. the contents of
|
||||
``data.createTicket``). If ``raise_on_payload_errors`` is true and the
|
||||
result contains a non-empty ``errors`` list, :class:`GaiiaMutationError`
|
||||
is raised.
|
||||
"""
|
||||
data = self.query(query, variables, timezone=timezone)
|
||||
result = data.get(mutation_name)
|
||||
if result is None:
|
||||
raise GaiiaAPIError(
|
||||
[GraphQLError(message=f"mutation {mutation_name!r} not in response", code=None)],
|
||||
data=data,
|
||||
)
|
||||
errors = result.get("errors") or []
|
||||
if errors and raise_on_payload_errors:
|
||||
raise GaiiaMutationError(mutation_name, errors, payload=result)
|
||||
return result
|
||||
|
||||
def iter_nodes(
|
||||
self,
|
||||
query: str,
|
||||
connection_path: str,
|
||||
variables: dict[str, Any] | None = None,
|
||||
*,
|
||||
cursor_var: str = "after",
|
||||
timezone: str | None = None,
|
||||
) -> Iterator[Any]:
|
||||
"""Iterate every node across all pages of a Connection.
|
||||
|
||||
``query`` must accept the cursor variable (default ``$after``) and use
|
||||
it as the ``after:`` argument on the paginated field.
|
||||
"""
|
||||
base_vars = dict(variables or {})
|
||||
|
||||
def fetch_page(cursor: str | None) -> dict[str, Any]:
|
||||
v = dict(base_vars)
|
||||
v[cursor_var] = cursor
|
||||
return self.query(query, v, timezone=timezone)
|
||||
|
||||
yield from iter_nodes(fetch_page, connection_path)
|
||||
|
||||
|
||||
class AsyncGaiiaClient(_Base):
|
||||
"""Asynchronous gaiia GraphQL client."""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
api_key: str | None = None,
|
||||
*,
|
||||
base_url: str = DEFAULT_BASE_URL,
|
||||
timezone: str | None = None,
|
||||
timeout: float = DEFAULT_TIMEOUT,
|
||||
default_headers: dict[str, str] | None = None,
|
||||
max_retries: int = 0,
|
||||
transport: httpx.AsyncBaseTransport | None = None,
|
||||
) -> None:
|
||||
super().__init__(
|
||||
api_key,
|
||||
base_url=base_url,
|
||||
timezone=timezone,
|
||||
timeout=timeout,
|
||||
default_headers=default_headers,
|
||||
max_retries=max_retries,
|
||||
)
|
||||
self._http = httpx.AsyncClient(timeout=timeout, transport=transport)
|
||||
|
||||
async def __aenter__(self) -> AsyncGaiiaClient:
|
||||
return self
|
||||
|
||||
async def __aexit__(
|
||||
self,
|
||||
exc_type: type[BaseException] | None,
|
||||
exc: BaseException | None,
|
||||
tb: TracebackType | None,
|
||||
) -> None:
|
||||
await self.aclose()
|
||||
|
||||
async def aclose(self) -> None:
|
||||
await self._http.aclose()
|
||||
|
||||
async def query(
|
||||
self,
|
||||
query: str,
|
||||
variables: dict[str, Any] | None = None,
|
||||
*,
|
||||
operation_name: str | None = None,
|
||||
timezone: str | None = None,
|
||||
extra_headers: dict[str, str] | None = None,
|
||||
) -> dict[str, Any]:
|
||||
body: dict[str, Any] = {"query": query}
|
||||
if variables is not None:
|
||||
body["variables"] = variables
|
||||
if operation_name is not None:
|
||||
body["operationName"] = operation_name
|
||||
|
||||
per_call_headers = dict(extra_headers or {})
|
||||
if timezone is not None:
|
||||
per_call_headers[TIMEZONE_HEADER] = timezone
|
||||
headers = self._headers(per_call_headers)
|
||||
|
||||
attempt = 0
|
||||
while True:
|
||||
attempt += 1
|
||||
try:
|
||||
resp = await self._http.post(self._base_url, json=body, headers=headers)
|
||||
except httpx.HTTPError as e:
|
||||
raise GaiiaTransportError(str(e)) from e
|
||||
|
||||
self.last_rate_limit = RateLimitInfo.from_headers(resp.headers)
|
||||
try:
|
||||
payload = resp.json()
|
||||
except ValueError as e:
|
||||
raise GaiiaTransportError(
|
||||
f"non-JSON response from gaiia (HTTP {resp.status_code})"
|
||||
) from e
|
||||
|
||||
try:
|
||||
_raise_for_payload(payload)
|
||||
except GaiiaRateLimitedError as e:
|
||||
if attempt > self._max_retries:
|
||||
raise
|
||||
_sleep_until(e.retry_at)
|
||||
continue
|
||||
|
||||
if resp.status_code >= 500:
|
||||
raise GaiiaTransportError(
|
||||
f"gaiia returned HTTP {resp.status_code}: {resp.text[:200]}"
|
||||
)
|
||||
return payload.get("data") or {}
|
||||
|
||||
async def mutate(
|
||||
self,
|
||||
query: str,
|
||||
mutation_name: str,
|
||||
variables: dict[str, Any] | None = None,
|
||||
*,
|
||||
timezone: str | None = None,
|
||||
raise_on_payload_errors: bool = True,
|
||||
) -> dict[str, Any]:
|
||||
data = await self.query(query, variables, timezone=timezone)
|
||||
result = data.get(mutation_name)
|
||||
if result is None:
|
||||
raise GaiiaAPIError(
|
||||
[GraphQLError(message=f"mutation {mutation_name!r} not in response", code=None)],
|
||||
data=data,
|
||||
)
|
||||
errors = result.get("errors") or []
|
||||
if errors and raise_on_payload_errors:
|
||||
raise GaiiaMutationError(mutation_name, errors, payload=result)
|
||||
return result
|
||||
|
||||
async def iter_nodes(
|
||||
self,
|
||||
query: str,
|
||||
connection_path: str,
|
||||
variables: dict[str, Any] | None = None,
|
||||
*,
|
||||
cursor_var: str = "after",
|
||||
timezone: str | None = None,
|
||||
) -> AsyncIterator[Any]:
|
||||
base_vars = dict(variables or {})
|
||||
|
||||
async def fetch_page(cursor: str | None) -> dict[str, Any]:
|
||||
v = dict(base_vars)
|
||||
v[cursor_var] = cursor
|
||||
return await self.query(query, v, timezone=timezone)
|
||||
|
||||
async for node in aiter_nodes(fetch_page, connection_path):
|
||||
yield node
|
||||
|
||||
|
||||
def _sleep_until(retry_at: Any) -> None:
|
||||
if not retry_at:
|
||||
time.sleep(1.0)
|
||||
return
|
||||
delta = retry_at.timestamp() - time.time()
|
||||
if delta > 0:
|
||||
time.sleep(min(delta, 60.0))
|
||||
else:
|
||||
time.sleep(1.0)
|
||||
91
gaiia/src/gaiia/errors.py
Normal file
91
gaiia/src/gaiia/errors.py
Normal file
|
|
@ -0,0 +1,91 @@
|
|||
"""Exception types raised by the gaiia client."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from dataclasses import dataclass, field
|
||||
from datetime import datetime
|
||||
from typing import Any
|
||||
|
||||
|
||||
class GaiiaError(Exception):
|
||||
"""Base class for all gaiia client errors."""
|
||||
|
||||
|
||||
class GaiiaTransportError(GaiiaError):
|
||||
"""Network/transport failure talking to the gaiia API."""
|
||||
|
||||
|
||||
@dataclass
|
||||
class GraphQLError:
|
||||
message: str
|
||||
code: str | None = None
|
||||
path: list[str | int] | None = None
|
||||
extensions: dict[str, Any] = field(default_factory=dict)
|
||||
|
||||
@classmethod
|
||||
def from_dict(cls, raw: dict[str, Any]) -> GraphQLError:
|
||||
extensions = dict(raw.get("extensions") or {})
|
||||
return cls(
|
||||
message=raw.get("message", ""),
|
||||
code=extensions.get("code"),
|
||||
path=raw.get("path"),
|
||||
extensions=extensions,
|
||||
)
|
||||
|
||||
|
||||
class GaiiaAPIError(GaiiaError):
|
||||
"""One or more top-level GraphQL errors were returned."""
|
||||
|
||||
def __init__(self, errors: list[GraphQLError], data: Any | None = None) -> None:
|
||||
self.errors = errors
|
||||
self.data = data
|
||||
msg = "; ".join(e.message for e in errors) or "gaiia API error"
|
||||
super().__init__(msg)
|
||||
|
||||
|
||||
class GaiiaMutationError(GaiiaError):
|
||||
"""A mutation returned validation/expected errors under data.<mutation>.errors.
|
||||
|
||||
These are the GraphQL "200 OK with errors in the payload" responses that
|
||||
represent expected failure modes (validation, business rules) rather than
|
||||
transport or schema issues.
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
mutation: str,
|
||||
errors: list[dict[str, Any]],
|
||||
payload: dict[str, Any] | None = None,
|
||||
) -> None:
|
||||
self.mutation = mutation
|
||||
self.errors = errors
|
||||
self.payload = payload
|
||||
codes = [e.get("code") for e in errors if e.get("code")]
|
||||
msg = (
|
||||
f"mutation {mutation!r} returned errors: "
|
||||
+ ", ".join(f"{c}: {e.get('message','')}" for c, e in zip(codes, errors, strict=False))
|
||||
if codes
|
||||
else f"mutation {mutation!r} returned errors"
|
||||
)
|
||||
super().__init__(msg)
|
||||
|
||||
|
||||
class GaiiaRateLimitedError(GaiiaAPIError):
|
||||
"""Raised when the API returns a RATE_LIMITED error."""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
errors: list[GraphQLError],
|
||||
*,
|
||||
cost: int | None = None,
|
||||
limit: int | None = None,
|
||||
used: int | None = None,
|
||||
remaining: int | None = None,
|
||||
retry_at: datetime | None = None,
|
||||
) -> None:
|
||||
super().__init__(errors)
|
||||
self.cost = cost
|
||||
self.limit = limit
|
||||
self.used = used
|
||||
self.remaining = remaining
|
||||
self.retry_at = retry_at
|
||||
65
gaiia/src/gaiia/globalid.py
Normal file
65
gaiia/src/gaiia/globalid.py
Normal file
|
|
@ -0,0 +1,65 @@
|
|||
"""Encode and decode gaiia GlobalIDs.
|
||||
|
||||
A GlobalID looks like ``account_pwE84wz4Mw15LPX5YE8Z3o``: a snake-cased entity
|
||||
type, an underscore, and a 22-char Flickr Base-58 encoding of the underlying
|
||||
128-bit UUID. This matches the ``short-uuid`` library that gaiia uses on the
|
||||
server side.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import re
|
||||
from uuid import UUID
|
||||
|
||||
_ALPHABET = "123456789abcdefghijkmnopqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ"
|
||||
_BASE = len(_ALPHABET)
|
||||
_TARGET_LEN = 22
|
||||
_TYPE_RE = re.compile(r"(?<!^)(?=[A-Z])")
|
||||
|
||||
|
||||
def _snake_case(name: str) -> str:
|
||||
return _TYPE_RE.sub("_", name).lower()
|
||||
|
||||
|
||||
def encode_global_id(type_name: str, uuid_str: str | UUID) -> str:
|
||||
"""Build a GlobalID from a type name (e.g. ``Account``) and a UUID."""
|
||||
if isinstance(uuid_str, UUID):
|
||||
stripped = uuid_str.hex
|
||||
else:
|
||||
stripped = str(uuid_str).replace("-", "").lower()
|
||||
if len(stripped) != 32 or any(c not in "0123456789abcdef" for c in stripped):
|
||||
raise ValueError(f"not a valid uuid: {uuid_str!r}")
|
||||
|
||||
num = int(stripped, 16)
|
||||
chars: list[str] = []
|
||||
if num == 0:
|
||||
chars.append(_ALPHABET[0])
|
||||
else:
|
||||
while num > 0:
|
||||
num, rem = divmod(num, _BASE)
|
||||
chars.append(_ALPHABET[rem])
|
||||
chars.reverse()
|
||||
short = "".join(chars).rjust(_TARGET_LEN, _ALPHABET[0])
|
||||
return f"{_snake_case(type_name)}_{short}"
|
||||
|
||||
|
||||
def decode_global_id(global_id: str) -> tuple[str, str]:
|
||||
"""Split a GlobalID into ``(type_snake_case, uuid_str)``."""
|
||||
if "_" not in global_id:
|
||||
raise ValueError(f"not a global id: {global_id!r}")
|
||||
type_part, _, short_id = global_id.rpartition("_")
|
||||
if not type_part or not short_id:
|
||||
raise ValueError(f"malformed global id: {global_id!r}")
|
||||
num = 0
|
||||
for ch in short_id:
|
||||
idx = _ALPHABET.find(ch)
|
||||
if idx < 0:
|
||||
raise ValueError(f"invalid base58 char {ch!r} in {global_id!r}")
|
||||
num = num * _BASE + idx
|
||||
hex_str = format(num, "x").rjust(32, "0")
|
||||
if len(hex_str) > 32:
|
||||
raise ValueError(f"global id decodes to oversized uuid: {global_id!r}")
|
||||
uuid_str = (
|
||||
f"{hex_str[0:8]}-{hex_str[8:12]}-{hex_str[12:16]}-{hex_str[16:20]}-{hex_str[20:32]}"
|
||||
)
|
||||
return type_part, uuid_str
|
||||
61
gaiia/src/gaiia/pagination.py
Normal file
61
gaiia/src/gaiia/pagination.py
Normal file
|
|
@ -0,0 +1,61 @@
|
|||
"""Helpers for cursor-based pagination over gaiia Connection types."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Any, AsyncIterator, Iterator
|
||||
|
||||
|
||||
def extract_connection(data: dict[str, Any], path: str) -> dict[str, Any]:
|
||||
"""Walk a dotted path into a GraphQL ``data`` payload and return the Connection node."""
|
||||
node: Any = data
|
||||
for part in path.split("."):
|
||||
if not isinstance(node, dict):
|
||||
raise KeyError(f"path {path!r} not found in response")
|
||||
node = node.get(part)
|
||||
if node is None:
|
||||
raise KeyError(f"path {path!r} not found in response (broke at {part!r})")
|
||||
if not isinstance(node, dict) or "pageInfo" not in node:
|
||||
raise ValueError(f"path {path!r} did not point to a Connection (no pageInfo)")
|
||||
return node
|
||||
|
||||
|
||||
def iter_nodes(
|
||||
fetch_page: "Callable[[str | None], dict[str, Any]]", # noqa: F821
|
||||
connection_path: str,
|
||||
) -> Iterator[Any]:
|
||||
"""Yield nodes one by one across all pages of a Connection.
|
||||
|
||||
``fetch_page`` takes the current ``after`` cursor (``None`` on the first
|
||||
page) and returns the full GraphQL ``data`` payload for that page.
|
||||
"""
|
||||
cursor: str | None = None
|
||||
while True:
|
||||
data = fetch_page(cursor)
|
||||
conn = extract_connection(data, connection_path)
|
||||
for node in conn.get("nodes") or []:
|
||||
yield node
|
||||
page_info = conn.get("pageInfo") or {}
|
||||
if not page_info.get("hasNextPage"):
|
||||
return
|
||||
cursor = page_info.get("endCursor")
|
||||
if not cursor:
|
||||
return
|
||||
|
||||
|
||||
async def aiter_nodes(
|
||||
fetch_page: "Callable[[str | None], Awaitable[dict[str, Any]]]", # noqa: F821
|
||||
connection_path: str,
|
||||
) -> AsyncIterator[Any]:
|
||||
"""Async version of :func:`iter_nodes`."""
|
||||
cursor: str | None = None
|
||||
while True:
|
||||
data = await fetch_page(cursor)
|
||||
conn = extract_connection(data, connection_path)
|
||||
for node in conn.get("nodes") or []:
|
||||
yield node
|
||||
page_info = conn.get("pageInfo") or {}
|
||||
if not page_info.get("hasNextPage"):
|
||||
return
|
||||
cursor = page_info.get("endCursor")
|
||||
if not cursor:
|
||||
return
|
||||
0
gaiia/src/gaiia/py.typed
Normal file
0
gaiia/src/gaiia/py.typed
Normal file
56
gaiia/src/gaiia/ratelimit.py
Normal file
56
gaiia/src/gaiia/ratelimit.py
Normal file
|
|
@ -0,0 +1,56 @@
|
|||
"""Parse gaiia rate-limit response headers."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from dataclasses import dataclass
|
||||
from datetime import datetime
|
||||
from typing import Mapping
|
||||
|
||||
|
||||
@dataclass
|
||||
class RateLimitInfo:
|
||||
allowed: bool | None
|
||||
cost: int | None
|
||||
limit: int | None
|
||||
used: int | None
|
||||
remaining: int | None
|
||||
retry_at: datetime | None
|
||||
|
||||
@classmethod
|
||||
def from_headers(cls, headers: Mapping[str, str]) -> RateLimitInfo:
|
||||
def _get(name: str) -> str | None:
|
||||
for k, v in headers.items():
|
||||
if k.lower() == name.lower():
|
||||
return v
|
||||
return None
|
||||
|
||||
def _int(name: str) -> int | None:
|
||||
v = _get(name)
|
||||
try:
|
||||
return int(v) if v is not None else None
|
||||
except ValueError:
|
||||
return None
|
||||
|
||||
def _bool(name: str) -> bool | None:
|
||||
v = _get(name)
|
||||
if v is None:
|
||||
return None
|
||||
return v.strip().lower() == "true"
|
||||
|
||||
def _dt(name: str) -> datetime | None:
|
||||
v = _get(name)
|
||||
if not v:
|
||||
return None
|
||||
try:
|
||||
return datetime.fromisoformat(v.replace("Z", "+00:00"))
|
||||
except ValueError:
|
||||
return None
|
||||
|
||||
return cls(
|
||||
allowed=_bool("X-Rate-Limit-Allowed"),
|
||||
cost=_int("X-Rate-Limit-Cost"),
|
||||
limit=_int("X-Rate-Limit-Limit"),
|
||||
used=_int("X-Rate-Limit-Used"),
|
||||
remaining=_int("X-Rate-Limit-Remaining"),
|
||||
retry_at=_dt("X-Rate-Limit-Retry-At") or _dt("X-Rate-Limit-Retry-at"),
|
||||
)
|
||||
93
gaiia/src/gaiia/webhooks.py
Normal file
93
gaiia/src/gaiia/webhooks.py
Normal file
|
|
@ -0,0 +1,93 @@
|
|||
"""Webhook signature verification for incoming gaiia webhooks.
|
||||
|
||||
Each delivery includes the header
|
||||
``X-Gaiia-Webhook-Signature: t=<unix-ts>,v1=<hex-sha256-hmac>``. The signature
|
||||
is HMAC-SHA256 over ``"${t}.${rawBody}"`` keyed by the endpoint secret. Compare
|
||||
in constant time and reject if ``t`` is outside your tolerance window.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import hashlib
|
||||
import hmac
|
||||
import time
|
||||
from typing import Iterable
|
||||
|
||||
from gaiia.errors import GaiiaError
|
||||
|
||||
SIGNATURE_HEADER = "X-Gaiia-Webhook-Signature"
|
||||
DEFAULT_TOLERANCE_SECONDS = 5 * 60
|
||||
|
||||
|
||||
class WebhookSignatureError(GaiiaError):
|
||||
"""Raised when a webhook signature fails verification."""
|
||||
|
||||
|
||||
def _parse_header(header_value: str) -> tuple[str | None, list[str]]:
|
||||
timestamp: str | None = None
|
||||
v1_signatures: list[str] = []
|
||||
for chunk in header_value.split(","):
|
||||
if "=" not in chunk:
|
||||
continue
|
||||
prefix, _, value = chunk.partition("=")
|
||||
prefix = prefix.strip()
|
||||
value = value.strip()
|
||||
if prefix == "t":
|
||||
timestamp = value
|
||||
elif prefix == "v1":
|
||||
v1_signatures.append(value)
|
||||
return timestamp, v1_signatures
|
||||
|
||||
|
||||
def _compute_signature(secret: str | bytes, timestamp: str, raw_body: str | bytes) -> str:
|
||||
if isinstance(secret, str):
|
||||
secret = secret.encode("utf-8")
|
||||
if isinstance(raw_body, bytes):
|
||||
body_text = raw_body.decode("utf-8")
|
||||
else:
|
||||
body_text = raw_body
|
||||
payload = f"{timestamp}.{body_text}".encode("utf-8")
|
||||
return hmac.new(secret, payload, hashlib.sha256).hexdigest()
|
||||
|
||||
|
||||
def verify_webhook_signature(
|
||||
*,
|
||||
header_value: str,
|
||||
raw_body: str | bytes,
|
||||
secret: str | bytes,
|
||||
tolerance_seconds: int = DEFAULT_TOLERANCE_SECONDS,
|
||||
now: float | None = None,
|
||||
) -> None:
|
||||
"""Verify a webhook delivery. Raises :class:`WebhookSignatureError` on failure.
|
||||
|
||||
``raw_body`` must be the exact bytes/string the server signed — do not
|
||||
re-serialize a parsed JSON body, since whitespace differences will break
|
||||
the HMAC.
|
||||
"""
|
||||
if not header_value:
|
||||
raise WebhookSignatureError("missing signature header")
|
||||
timestamp, candidates = _parse_header(header_value)
|
||||
if timestamp is None:
|
||||
raise WebhookSignatureError("signature header missing timestamp (t=...)")
|
||||
if not candidates:
|
||||
raise WebhookSignatureError("signature header missing v1 signatures")
|
||||
|
||||
try:
|
||||
ts_int = int(timestamp)
|
||||
except ValueError as e:
|
||||
raise WebhookSignatureError(f"signature timestamp not an integer: {timestamp!r}") from e
|
||||
|
||||
current = time.time() if now is None else now
|
||||
if abs(current - ts_int) > tolerance_seconds:
|
||||
raise WebhookSignatureError(
|
||||
f"signature timestamp outside tolerance "
|
||||
f"({tolerance_seconds}s): {abs(current - ts_int):.0f}s drift"
|
||||
)
|
||||
|
||||
expected = _compute_signature(secret, timestamp, raw_body)
|
||||
if not _any_match(expected, candidates):
|
||||
raise WebhookSignatureError("no signature matched expected HMAC")
|
||||
|
||||
|
||||
def _any_match(expected: str, candidates: Iterable[str]) -> bool:
|
||||
return any(hmac.compare_digest(expected, c) for c in candidates)
|
||||
160
gaiia/sync_all_radius_groups.py
Normal file
160
gaiia/sync_all_radius_groups.py
Normal file
|
|
@ -0,0 +1,160 @@
|
|||
#!/usr/bin/env python3
|
||||
"""
|
||||
Run 'Update RADIUS Account Groups' for every account in Gaiia.
|
||||
Status must be UPPERCASE (ACTIVE, SUSPENDED, etc.)
|
||||
|
||||
Usage:
|
||||
source .envrc
|
||||
uv run python3 sync_all_radius_groups.py --dry-run (preview)
|
||||
uv run python3 sync_all_radius_groups.py (execute)
|
||||
"""
|
||||
|
||||
from gaiia import GaiiaClient, GaiiaAPIError, GaiiaRateLimitedError
|
||||
import json
|
||||
import sys
|
||||
import time
|
||||
|
||||
WF_ID = "workflow_fLXTNCvridPqwEEXxf3CK1"
|
||||
|
||||
|
||||
def list_accounts(client):
|
||||
"""Paginate through all accounts, return [{id, name, status, productIds}]."""
|
||||
accounts = []
|
||||
after = None
|
||||
page = 0
|
||||
|
||||
while True:
|
||||
page += 1
|
||||
data = client.query("""
|
||||
query Q($after: String) {
|
||||
accounts(first: 250, after: $after) {
|
||||
nodes {
|
||||
id
|
||||
name
|
||||
status { name }
|
||||
billingSubscriptions {
|
||||
nodes {
|
||||
productVersion {
|
||||
product { id name slug }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
pageInfo { hasNextPage endCursor }
|
||||
totalCount
|
||||
}
|
||||
}
|
||||
""", {"after": after} if after else {})
|
||||
|
||||
result = data['accounts']
|
||||
|
||||
for acct in result['nodes']:
|
||||
status_name = acct['status']['name'] if acct['status'] else ''
|
||||
products = []
|
||||
for sub in (acct.get('billingSubscriptions') or {}).get('nodes', []):
|
||||
pv = sub.get('productVersion') or {}
|
||||
p = pv.get('product') if pv else None
|
||||
if p and p['id'] not in products:
|
||||
products.append(p['id'])
|
||||
|
||||
accounts.append({
|
||||
'id': acct['id'],
|
||||
'name': acct.get('name', '') or '',
|
||||
'status': status_name,
|
||||
'productIds': products,
|
||||
})
|
||||
|
||||
sys.stderr.write(f"\r Page {page}: {len(accounts)} accounts so far...")
|
||||
sys.stderr.flush()
|
||||
|
||||
page_info = result['pageInfo']
|
||||
if not page_info.get('hasNextPage'):
|
||||
break
|
||||
after = page_info['endCursor']
|
||||
|
||||
sys.stderr.write("\n")
|
||||
return accounts
|
||||
|
||||
|
||||
def sync_account(client, acct, dry_run=False):
|
||||
"""Run the Update RADIUS Account Groups workflow for one account."""
|
||||
status_upper = acct['status'].upper()
|
||||
wf_input = {
|
||||
"accountId": acct['id'],
|
||||
"status": status_upper,
|
||||
"productIds": acct['productIds'],
|
||||
}
|
||||
|
||||
if dry_run:
|
||||
name = acct['name'] or '(no name)'
|
||||
print(f" [DRY-RUN] {name} status={status_upper} products={len(acct['productIds'])}")
|
||||
return True
|
||||
|
||||
try:
|
||||
result = client.query("""
|
||||
mutation Run($input: StartWorkflowExecutionInput!) {
|
||||
startWorkflowExecution(input: $input) {
|
||||
workflowExecution { id status }
|
||||
}
|
||||
}
|
||||
""", {"input": {"workflowId": WF_ID, "input": wf_input}})
|
||||
|
||||
we = result['startWorkflowExecution']['workflowExecution']
|
||||
return we is not None
|
||||
except GaiiaAPIError as e:
|
||||
print(f" FAILED {acct.get('name','?')}: API error - {e}")
|
||||
return False
|
||||
except GaiiaRateLimitedError as e:
|
||||
print(f" RATE LIMITED - sleeping until {e.retry_at}")
|
||||
if e.retry_at:
|
||||
wait = max(0, (e.retry_at.timestamp() - time.time()))
|
||||
time.sleep(wait + 1)
|
||||
return False
|
||||
|
||||
|
||||
def main():
|
||||
dry_run = "--dry-run" in sys.argv
|
||||
|
||||
with GaiiaClient(timezone='America/Chicago') as g:
|
||||
print("Listing all accounts...")
|
||||
accounts = list_accounts(g)
|
||||
print(f"\nFound {len(accounts)} accounts")
|
||||
|
||||
# Stats
|
||||
statuses = {}
|
||||
for a in accounts:
|
||||
s = a['status'].upper()
|
||||
statuses[s] = statuses.get(s, 0) + 1
|
||||
print(f"Statuses: {statuses}")
|
||||
|
||||
if dry_run:
|
||||
print("\n--dry-run mode: previewing --")
|
||||
else:
|
||||
print(f"\nSyncing via workflow {WF_ID}...")
|
||||
|
||||
succeeded = 0
|
||||
failed = 0
|
||||
for i, a in enumerate(accounts):
|
||||
name = a['name'] or f"(id={a['id']})"
|
||||
if not dry_run:
|
||||
sys.stderr.write(f"\r {i+1}/{len(accounts)}: {name[:40]}...")
|
||||
sys.stderr.flush()
|
||||
|
||||
if sync_account(g, a, dry_run):
|
||||
succeeded += 1
|
||||
else:
|
||||
failed += 1
|
||||
|
||||
if not dry_run:
|
||||
time.sleep(0.15) # avoid rate limiting (~7/sec)
|
||||
|
||||
if not dry_run:
|
||||
sys.stderr.write("\n")
|
||||
|
||||
print(f"\nDone: {succeeded} started, {failed} failed")
|
||||
if dry_run:
|
||||
print("(nothing executed — remove --dry-run to run)")
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
154
gaiia/sync_all_radius_groups_workflow.json
Normal file
154
gaiia/sync_all_radius_groups_workflow.json
Normal file
|
|
@ -0,0 +1,154 @@
|
|||
{
|
||||
"name": "Sync All RADIUS Groups",
|
||||
"description": "MANUAL: Syncs RADIUS usergroups for ALL accounts (2 pages x 250 = 500 accounts). Passes actual account status through — ACTIVE, SUSPENDED, INACTIVE, etc.",
|
||||
"trigger": {
|
||||
"type": "MANUAL",
|
||||
"nextSteps": [{ "slug": "fetch-page-1" }]
|
||||
},
|
||||
"steps": [
|
||||
{
|
||||
"name": "Fetch accounts page 1",
|
||||
"slug": "fetch-page-1",
|
||||
"type": "GAIIA_API_GATEWAY_V1_GRAPHQL_REQUEST",
|
||||
"nodeSlug": "gaiia-public-api-gateway-graphql-request",
|
||||
"allowSkipExecution": false,
|
||||
"outputMapper": "({ output }) => {\n const data = output.response.accounts;\n return {\n accounts: data.nodes.map((a) => ({\n accountId: a.id,\n status: (a.status?.name || '').toUpperCase(),\n products: (a.billingSubscriptions?.nodes || [])\n .map((s) => s.productVersion?.product?.id)\n .filter(Boolean)\n .filter((id, i, arr) => arr.indexOf(id) === i)\n })),\n hasNextPage: data.pageInfo.hasNextPage,\n endCursor: data.pageInfo.endCursor\n };\n}",
|
||||
"inputs": [
|
||||
{
|
||||
"key": "query",
|
||||
"type": "LITERAL",
|
||||
"value": "string:query {\n accounts(first: 250) {\n nodes {\n id\n status { name }\n billingSubscriptions {\n nodes { productVersion { product { id } } }\n }\n }\n pageInfo { hasNextPage endCursor }\n }\n}"
|
||||
},
|
||||
{
|
||||
"key": "variables",
|
||||
"type": "FUNCTION",
|
||||
"value": "({ secrets, state, utils, variables }) => ({})"
|
||||
}
|
||||
],
|
||||
"nextSteps": [{ "slug": "sync-page-1" }]
|
||||
},
|
||||
{
|
||||
"name": "Sync page 1",
|
||||
"slug": "sync-page-1",
|
||||
"type": "PARALLEL_LOOP",
|
||||
"nodeSlug": "parallel-loop",
|
||||
"maxConcurrentIterations": 10,
|
||||
"inputs": [
|
||||
{
|
||||
"key": "iterable",
|
||||
"type": "FUNCTION",
|
||||
"value": "({ state }) => state.steps['fetch-page-1'].output.accounts"
|
||||
}
|
||||
],
|
||||
"initialSteps": [{ "slug": "call-update-page1" }],
|
||||
"nextSteps": [{ "slug": "need-page-2" }]
|
||||
},
|
||||
{
|
||||
"name": "Call Update RADIUS Account Groups",
|
||||
"slug": "call-update-page1",
|
||||
"type": "GAIIA_START_WORKFLOW_EXECUTION",
|
||||
"nodeSlug": "start-workflow-execution",
|
||||
"parentParallelLoopSlug": "sync-page-1",
|
||||
"allowSkipExecution": false,
|
||||
"inputs": [
|
||||
{
|
||||
"key": "workflowId",
|
||||
"type": "LITERAL",
|
||||
"value": "string:workflow_fLXTNCvridPqwEEXxf3CK1"
|
||||
},
|
||||
{
|
||||
"key": "input",
|
||||
"type": "FUNCTION",
|
||||
"value": "({ state }) => {\n const acct = state.currentParallelLoopIterations['sync-page-1'];\n return { accountId: acct.accountId, status: acct.status, productIds: acct.products };\n}"
|
||||
},
|
||||
{
|
||||
"key": "fireAndForget",
|
||||
"type": "LITERAL",
|
||||
"value": "boolean:true"
|
||||
}
|
||||
],
|
||||
"nextSteps": [{ "slug": "sync-page-1-end" }]
|
||||
},
|
||||
{
|
||||
"name": "Need page 2?",
|
||||
"slug": "need-page-2",
|
||||
"type": "CONDITION",
|
||||
"nodeSlug": "condition",
|
||||
"condition": "({ secrets, state, utils, variables }) => {\n return state.steps['fetch-page-1'].output.hasNextPage;\n}",
|
||||
"elseSteps": [{ "slug": "end" }],
|
||||
"thenSteps": [{ "slug": "fetch-page-2" }]
|
||||
},
|
||||
{
|
||||
"name": "Fetch accounts page 2",
|
||||
"slug": "fetch-page-2",
|
||||
"type": "GAIIA_API_GATEWAY_V1_GRAPHQL_REQUEST",
|
||||
"nodeSlug": "gaiia-public-api-gateway-graphql-request",
|
||||
"allowSkipExecution": false,
|
||||
"outputMapper": "({ output }) => {\n const nodes = output.response.accounts.nodes;\n if (!nodes || nodes.length === 0) return [];\n return nodes.map((a) => ({\n accountId: a.id,\n status: (a.status?.name || '').toUpperCase(),\n products: (a.billingSubscriptions?.nodes || [])\n .map((s) => s.productVersion?.product?.id)\n .filter(Boolean)\n .filter((id, i, arr) => arr.indexOf(id) === i)\n }));\n}",
|
||||
"inputs": [
|
||||
{
|
||||
"key": "query",
|
||||
"type": "LITERAL",
|
||||
"value": "string:query($after: String) {\n accounts(first: 250, after: $after) {\n nodes {\n id\n status { name }\n billingSubscriptions {\n nodes { productVersion { product { id } } }\n }\n }\n pageInfo { hasNextPage endCursor }\n }\n}"
|
||||
},
|
||||
{
|
||||
"key": "variables",
|
||||
"type": "FUNCTION",
|
||||
"value": "({ state }) => {\n const p1 = state.steps['fetch-page-1'].output;\n return p1.hasNextPage ? { after: p1.endCursor } : {};\n}"
|
||||
}
|
||||
],
|
||||
"nextSteps": [{ "slug": "if-page-2-has-data" }]
|
||||
},
|
||||
{
|
||||
"name": "Page 2 has data?",
|
||||
"slug": "if-page-2-has-data",
|
||||
"type": "CONDITION",
|
||||
"nodeSlug": "condition",
|
||||
"condition": "({ secrets, state, utils, variables }) => {\n const data = state.steps['fetch-page-2'].output;\n return Array.isArray(data) && data.length > 0;\n}",
|
||||
"elseSteps": [{ "slug": "end" }],
|
||||
"thenSteps": [{ "slug": "sync-page-2" }]
|
||||
},
|
||||
{
|
||||
"name": "Sync page 2",
|
||||
"slug": "sync-page-2",
|
||||
"type": "PARALLEL_LOOP",
|
||||
"nodeSlug": "parallel-loop",
|
||||
"maxConcurrentIterations": 10,
|
||||
"inputs": [
|
||||
{
|
||||
"key": "iterable",
|
||||
"type": "FUNCTION",
|
||||
"value": "({ state }) => state.steps['fetch-page-2'].output"
|
||||
}
|
||||
],
|
||||
"initialSteps": [{ "slug": "call-update-page2" }],
|
||||
"nextSteps": [{ "slug": "end" }]
|
||||
},
|
||||
{
|
||||
"name": "Call Update RADIUS Account Groups (page 2)",
|
||||
"slug": "call-update-page2",
|
||||
"type": "GAIIA_START_WORKFLOW_EXECUTION",
|
||||
"nodeSlug": "start-workflow-execution",
|
||||
"parentParallelLoopSlug": "sync-page-2",
|
||||
"allowSkipExecution": false,
|
||||
"inputs": [
|
||||
{
|
||||
"key": "workflowId",
|
||||
"type": "LITERAL",
|
||||
"value": "string:workflow_fLXTNCvridPqwEEXxf3CK1"
|
||||
},
|
||||
{
|
||||
"key": "input",
|
||||
"type": "FUNCTION",
|
||||
"value": "({ state }) => {\n const acct = state.currentParallelLoopIterations['sync-page-2'];\n return { accountId: acct.accountId, status: acct.status, productIds: acct.products };\n}"
|
||||
},
|
||||
{
|
||||
"key": "fireAndForget",
|
||||
"type": "LITERAL",
|
||||
"value": "boolean:true"
|
||||
}
|
||||
],
|
||||
"nextSteps": [{ "slug": "sync-page-2-end" }]
|
||||
}
|
||||
]
|
||||
}
|
||||
0
gaiia/tests/__init__.py
Normal file
0
gaiia/tests/__init__.py
Normal file
153
gaiia/tests/test_client.py
Normal file
153
gaiia/tests/test_client.py
Normal file
|
|
@ -0,0 +1,153 @@
|
|||
import httpx
|
||||
import pytest
|
||||
|
||||
from gaiia import (
|
||||
GaiiaAPIError,
|
||||
GaiiaClient,
|
||||
GaiiaMutationError,
|
||||
GaiiaRateLimitedError,
|
||||
)
|
||||
from gaiia.client import API_KEY_HEADER, TIMEZONE_HEADER
|
||||
|
||||
|
||||
def _client(handler):
|
||||
transport = httpx.MockTransport(handler)
|
||||
return GaiiaClient(
|
||||
api_key="gaiia_sk_sbox_test",
|
||||
timezone="America/Chicago",
|
||||
transport=transport,
|
||||
)
|
||||
|
||||
|
||||
def test_query_sends_expected_headers_and_body():
|
||||
captured = {}
|
||||
|
||||
def handler(request: httpx.Request) -> httpx.Response:
|
||||
captured["headers"] = dict(request.headers)
|
||||
captured["body"] = request.read()
|
||||
return httpx.Response(200, json={"data": {"x": 1}})
|
||||
|
||||
with _client(handler) as g:
|
||||
out = g.query("{ x }", {"v": 1})
|
||||
assert out == {"x": 1}
|
||||
assert captured["headers"][API_KEY_HEADER.lower()] == "gaiia_sk_sbox_test"
|
||||
assert captured["headers"][TIMEZONE_HEADER] == "America/Chicago"
|
||||
import json
|
||||
body = json.loads(captured["body"])
|
||||
assert body["query"] == "{ x }"
|
||||
assert body["variables"] == {"v": 1}
|
||||
|
||||
|
||||
def test_top_level_errors_raise_api_error():
|
||||
def handler(request):
|
||||
return httpx.Response(
|
||||
200,
|
||||
json={"errors": [{"message": "bad", "extensions": {"code": "BAD_USER_INPUT"}}]},
|
||||
)
|
||||
|
||||
with _client(handler) as g, pytest.raises(GaiiaAPIError) as exc:
|
||||
g.query("{}")
|
||||
assert exc.value.errors[0].code == "BAD_USER_INPUT"
|
||||
|
||||
|
||||
def test_rate_limited_classifies_separately():
|
||||
def handler(request):
|
||||
return httpx.Response(
|
||||
200,
|
||||
json={
|
||||
"errors": [
|
||||
{
|
||||
"message": "too many",
|
||||
"extensions": {
|
||||
"code": "RATE_LIMITED",
|
||||
"limit": 500,
|
||||
"used": 500,
|
||||
"remaining": 0,
|
||||
"retryAt": "2026-06-27T19:01:02.000Z",
|
||||
},
|
||||
}
|
||||
]
|
||||
},
|
||||
headers={
|
||||
"X-Rate-Limit-Allowed": "false",
|
||||
"X-Rate-Limit-Limit": "500",
|
||||
"X-Rate-Limit-Used": "500",
|
||||
"X-Rate-Limit-Remaining": "0",
|
||||
},
|
||||
)
|
||||
|
||||
with _client(handler) as g, pytest.raises(GaiiaRateLimitedError) as exc:
|
||||
g.query("{}")
|
||||
assert exc.value.limit == 500
|
||||
assert g.last_rate_limit is not None
|
||||
assert g.last_rate_limit.remaining == 0
|
||||
|
||||
|
||||
def test_mutate_raises_on_payload_errors():
|
||||
def handler(request):
|
||||
return httpx.Response(
|
||||
200,
|
||||
json={
|
||||
"data": {
|
||||
"createTicket": {
|
||||
"ticket": None,
|
||||
"errors": [{"code": "TITLE_TOO_LONG", "message": "long"}],
|
||||
}
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
with _client(handler) as g, pytest.raises(GaiiaMutationError) as exc:
|
||||
g.mutate("mutation { createTicket { ticket { id } errors { code message } } }", "createTicket")
|
||||
assert exc.value.errors[0]["code"] == "TITLE_TOO_LONG"
|
||||
|
||||
|
||||
def test_iter_nodes_paginates_via_cursor():
|
||||
calls = []
|
||||
|
||||
def handler(request):
|
||||
body = request.read().decode()
|
||||
calls.append(body)
|
||||
if '"after":null' in body.replace(" ", ""):
|
||||
return httpx.Response(
|
||||
200,
|
||||
json={
|
||||
"data": {
|
||||
"things": {
|
||||
"nodes": [{"id": 1}, {"id": 2}],
|
||||
"pageInfo": {"hasNextPage": True, "endCursor": "c1"},
|
||||
}
|
||||
}
|
||||
},
|
||||
)
|
||||
return httpx.Response(
|
||||
200,
|
||||
json={
|
||||
"data": {
|
||||
"things": {
|
||||
"nodes": [{"id": 3}],
|
||||
"pageInfo": {"hasNextPage": False, "endCursor": None},
|
||||
}
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
with _client(handler) as g:
|
||||
ids = [n["id"] for n in g.iter_nodes(
|
||||
"query Q($after: String) { things(first: 2, after: $after) { nodes { id } pageInfo { hasNextPage endCursor } } }",
|
||||
"things",
|
||||
)]
|
||||
assert ids == [1, 2, 3]
|
||||
assert len(calls) == 2
|
||||
|
||||
|
||||
def test_missing_api_key_raises():
|
||||
import os
|
||||
|
||||
saved = os.environ.pop("GAIIA_KEY", None)
|
||||
try:
|
||||
with pytest.raises(ValueError):
|
||||
GaiiaClient()
|
||||
finally:
|
||||
if saved is not None:
|
||||
os.environ["GAIIA_KEY"] = saved
|
||||
45
gaiia/tests/test_globalid.py
Normal file
45
gaiia/tests/test_globalid.py
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
from uuid import UUID
|
||||
|
||||
import pytest
|
||||
|
||||
from gaiia import decode_global_id, encode_global_id
|
||||
|
||||
|
||||
def test_roundtrip_known_value():
|
||||
type_name = "Account"
|
||||
uuid_str = "3c3b1978-6a68-4a13-bdc2-2d51c8ef7519"
|
||||
gid = encode_global_id(type_name, uuid_str)
|
||||
snake, decoded = decode_global_id(gid)
|
||||
assert snake == "account"
|
||||
assert decoded == uuid_str
|
||||
|
||||
|
||||
def test_camelcase_type_becomes_snake_case():
|
||||
gid = encode_global_id("InventoryItem", "00000000-0000-0000-0000-000000000001")
|
||||
snake, _ = decode_global_id(gid)
|
||||
assert snake == "inventory_item"
|
||||
|
||||
|
||||
def test_accepts_uuid_object():
|
||||
u = UUID("3c3b1978-6a68-4a13-bdc2-2d51c8ef7519")
|
||||
gid = encode_global_id("Account", u)
|
||||
_, decoded = decode_global_id(gid)
|
||||
assert decoded == str(u)
|
||||
|
||||
|
||||
def test_short_id_always_22_chars():
|
||||
gid = encode_global_id("Account", "00000000-0000-0000-0000-000000000001")
|
||||
_, short = gid.split("_", 1)
|
||||
assert len(short) == 22
|
||||
|
||||
|
||||
def test_decode_rejects_garbage():
|
||||
with pytest.raises(ValueError):
|
||||
decode_global_id("no-underscore-here")
|
||||
with pytest.raises(ValueError):
|
||||
decode_global_id("account_!!!invalid!!!")
|
||||
|
||||
|
||||
def test_encode_rejects_bad_uuid():
|
||||
with pytest.raises(ValueError):
|
||||
encode_global_id("Account", "not-a-uuid")
|
||||
43
gaiia/tests/test_pagination.py
Normal file
43
gaiia/tests/test_pagination.py
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
import pytest
|
||||
|
||||
from gaiia.pagination import extract_connection, iter_nodes
|
||||
|
||||
|
||||
def _conn(nodes, has_next=False, end="cur"):
|
||||
return {
|
||||
"nodes": nodes,
|
||||
"pageInfo": {"hasNextPage": has_next, "endCursor": end if has_next else None},
|
||||
}
|
||||
|
||||
|
||||
def test_extract_connection_walks_dotted_path():
|
||||
data = {"a": {"b": _conn([1, 2])}}
|
||||
conn = extract_connection(data, "a.b")
|
||||
assert conn["nodes"] == [1, 2]
|
||||
|
||||
|
||||
def test_extract_connection_missing_path_raises():
|
||||
with pytest.raises(KeyError):
|
||||
extract_connection({"a": {}}, "a.b")
|
||||
|
||||
|
||||
def test_iter_nodes_terminates_on_last_page():
|
||||
pages = [
|
||||
{"things": _conn(["a", "b"], has_next=True, end="x")},
|
||||
{"things": _conn(["c"], has_next=False)},
|
||||
]
|
||||
calls = []
|
||||
|
||||
def fetch(cursor):
|
||||
calls.append(cursor)
|
||||
return pages.pop(0)
|
||||
|
||||
assert list(iter_nodes(fetch, "things")) == ["a", "b", "c"]
|
||||
assert calls == [None, "x"]
|
||||
|
||||
|
||||
def test_iter_nodes_handles_empty():
|
||||
def fetch(cursor):
|
||||
return {"things": _conn([])}
|
||||
|
||||
assert list(iter_nodes(fetch, "things")) == []
|
||||
62
gaiia/tests/test_webhooks.py
Normal file
62
gaiia/tests/test_webhooks.py
Normal file
|
|
@ -0,0 +1,62 @@
|
|||
import hashlib
|
||||
import hmac
|
||||
import time
|
||||
|
||||
import pytest
|
||||
|
||||
from gaiia import WebhookSignatureError, verify_webhook_signature
|
||||
|
||||
|
||||
def _make_header(secret: str, timestamp: int, body: bytes) -> str:
|
||||
sig = hmac.new(
|
||||
secret.encode(), f"{timestamp}.{body.decode()}".encode(), hashlib.sha256
|
||||
).hexdigest()
|
||||
return f"t={timestamp},v1={sig}"
|
||||
|
||||
|
||||
def test_valid_signature_passes():
|
||||
secret = "whsec_test"
|
||||
body = b'{"hello":"world"}'
|
||||
ts = int(time.time())
|
||||
header = _make_header(secret, ts, body)
|
||||
verify_webhook_signature(header_value=header, raw_body=body, secret=secret)
|
||||
|
||||
|
||||
def test_tampered_body_fails():
|
||||
secret = "whsec_test"
|
||||
body = b'{"hello":"world"}'
|
||||
ts = int(time.time())
|
||||
header = _make_header(secret, ts, body)
|
||||
with pytest.raises(WebhookSignatureError):
|
||||
verify_webhook_signature(
|
||||
header_value=header, raw_body=b'{"hello":"WORLD"}', secret=secret
|
||||
)
|
||||
|
||||
|
||||
def test_old_timestamp_rejected():
|
||||
secret = "whsec_test"
|
||||
body = b'{"x":1}'
|
||||
ts = int(time.time()) - 3600
|
||||
header = _make_header(secret, ts, body)
|
||||
with pytest.raises(WebhookSignatureError):
|
||||
verify_webhook_signature(
|
||||
header_value=header, raw_body=body, secret=secret, tolerance_seconds=60
|
||||
)
|
||||
|
||||
|
||||
def test_multiple_signatures_one_match():
|
||||
secret = "whsec_test"
|
||||
body = b"abc"
|
||||
ts = int(time.time())
|
||||
real = _make_header(secret, ts, body).split("v1=")[1]
|
||||
header = f"t={ts},v1=ffffffff,v1={real}"
|
||||
verify_webhook_signature(header_value=header, raw_body=body, secret=secret)
|
||||
|
||||
|
||||
def test_missing_components():
|
||||
with pytest.raises(WebhookSignatureError):
|
||||
verify_webhook_signature(header_value="", raw_body=b"", secret="x")
|
||||
with pytest.raises(WebhookSignatureError):
|
||||
verify_webhook_signature(header_value="v1=abc", raw_body=b"", secret="x")
|
||||
with pytest.raises(WebhookSignatureError):
|
||||
verify_webhook_signature(header_value="t=123", raw_body=b"", secret="x")
|
||||
108
gaiia/update_preseem_on_ip_change.json
Normal file
108
gaiia/update_preseem_on_ip_change.json
Normal file
|
|
@ -0,0 +1,108 @@
|
|||
{
|
||||
"steps": [
|
||||
{
|
||||
"name": "Find inventory item",
|
||||
"slug": "find-inventory-item",
|
||||
"type": "GAIIA_API_GATEWAY_V1_GRAPHQL_REQUEST",
|
||||
"nodeSlug": "gaiia-public-api-gateway-graphql-request",
|
||||
"allowSkipExecution": false,
|
||||
"outputMapper": "({ output }) => {\n const assignee = output.response.inventoryItem?.assignation?.assignee;\n if (!assignee || assignee.__typename !== 'Account') {\n workflow.fail('Inventory item is not assigned to an account.', 'NO_ACCOUNT');\n }\n return { id: assignee.id, gaiiaId: assignee.readableId };\n}",
|
||||
"inputs": [
|
||||
{
|
||||
"key": "query",
|
||||
"type": "LITERAL",
|
||||
"value": "string:query IpAssignmentWorkflowInventoryItemQuery($id: ID!) {\n inventoryItem(id: $id) {\n id\n assignation {\n assignee {\n ... on Account { id readableId }\n }\n }\n }\n}"
|
||||
},
|
||||
{
|
||||
"key": "variables",
|
||||
"type": "FUNCTION",
|
||||
"value": "({ state }) => ({ id: state.input.objectId })"
|
||||
}
|
||||
],
|
||||
"nextSteps": [
|
||||
{
|
||||
"slug": "find-account-cpes"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Find account CPEs",
|
||||
"slug": "find-account-cpes",
|
||||
"type": "GAIIA_FIND_ACCOUNT_CPES",
|
||||
"nodeSlug": "find-account-cpes",
|
||||
"allowSkipExecution": false,
|
||||
"inputs": [
|
||||
{
|
||||
"key": "accountId",
|
||||
"type": "FUNCTION",
|
||||
"value": "({ state }) => state.steps['find-inventory-item'].output.id"
|
||||
}
|
||||
],
|
||||
"nextSteps": [
|
||||
{
|
||||
"slug": "find-account-internet-plans"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Find account Internet plans",
|
||||
"slug": "find-account-internet-plans",
|
||||
"type": "GAIIA_FIND_ACCOUNT_INTERNET_PLANS",
|
||||
"nodeSlug": "find-account-internet-plans",
|
||||
"allowSkipExecution": false,
|
||||
"inputs": [
|
||||
{
|
||||
"key": "accountId",
|
||||
"type": "FUNCTION",
|
||||
"value": "({ state }) => state.steps['find-inventory-item'].output.id"
|
||||
}
|
||||
],
|
||||
"nextSteps": [
|
||||
{
|
||||
"slug": "preseem-update-service"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Preseem update service",
|
||||
"slug": "preseem-update-service",
|
||||
"type": "PRESEEM_REST_REQUEST",
|
||||
"nodeSlug": "preseem-rest-request",
|
||||
"allowSkipExecution": false,
|
||||
"inputs": [
|
||||
{
|
||||
"key": "body",
|
||||
"type": "FUNCTION",
|
||||
"value": "({ state }) => {\n const { gaiiaId } = state.steps['find-inventory-item'].output;\n const [{ slug }] = state.steps['find-account-internet-plans'].output.internetPlans;\n const cpes = state.steps['find-account-cpes'].output.accountCpe;\n const eventMac = state.input.payload.macAddress;\n const macAddresses = [\n ...new Set([\n ...cpes.map(({ macAddress }) => macAddress),\n eventMac\n ])\n ];\n return {\n id: String(gaiiaId),\n attachments: macAddresses\n .filter(Boolean)\n .map((macAddress) => ({ cpe_mac: macAddress.toLowerCase() })),\n account: String(gaiiaId),\n package: slug\n };\n }"
|
||||
},
|
||||
{
|
||||
"key": "method",
|
||||
"type": "LITERAL",
|
||||
"value": "string:PUT"
|
||||
},
|
||||
{
|
||||
"key": "path",
|
||||
"type": "FUNCTION",
|
||||
"value": "({ state }) => /services/${state.steps['find-inventory-item'].output.gaiiaId}"
|
||||
}
|
||||
],
|
||||
"nextSteps": [
|
||||
{
|
||||
"slug": "end"
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"trigger": {
|
||||
"nextSteps": [
|
||||
{
|
||||
"slug": "find-inventory-item"
|
||||
}
|
||||
],
|
||||
"type": "EVENT",
|
||||
"events": [
|
||||
"inventory_item.ip_address_assigned",
|
||||
"inventory_item.ip_address_unassigned"
|
||||
]
|
||||
}
|
||||
}
|
||||
502
gaiia/uv.lock
generated
Normal file
502
gaiia/uv.lock
generated
Normal file
|
|
@ -0,0 +1,502 @@
|
|||
version = 1
|
||||
revision = 3
|
||||
requires-python = ">=3.10"
|
||||
resolution-markers = [
|
||||
"python_full_version >= '3.15'",
|
||||
"python_full_version < '3.15'",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anyio"
|
||||
version = "4.13.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "exceptiongroup", marker = "python_full_version < '3.11'" },
|
||||
{ name = "idna" },
|
||||
{ name = "typing-extensions", marker = "python_full_version < '3.13'" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/19/14/2c5dd9f512b66549ae92767a9c7b330ae88e1932ca57876909410251fe13/anyio-4.13.0.tar.gz", hash = "sha256:334b70e641fd2221c1505b3890c69882fe4a2df910cba14d97019b90b24439dc", size = 231622, upload-time = "2026-03-24T12:59:09.671Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/da/42/e921fccf5015463e32a3cf6ee7f980a6ed0f395ceeaa45060b61d86486c2/anyio-4.13.0-py3-none-any.whl", hash = "sha256:08b310f9e24a9594186fd75b4f73f4a4152069e3853f1ed8bfbf58369f4ad708", size = 114353, upload-time = "2026-03-24T12:59:08.246Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ast-serialize"
|
||||
version = "0.3.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/a9/9d/912fefab0e30aee6a3af8a62bbea4a81b29afa4ba2c973d31170620a26de/ast_serialize-0.3.0.tar.gz", hash = "sha256:1bc3ca09a63a021376527c4e938deedd11d11d675ce850e6f9c7487f5889992b", size = 60689, upload-time = "2026-04-30T23:24:48.104Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/6f/57/a54d4de491d6cdd7a4e4b0952cc3ca9f60dcefa7b5fb48d6d492debe1649/ast_serialize-0.3.0-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:3a867927df59f76a18dc1d874a0b2c079b42c58972dca637905576deb0912e14", size = 1182966, upload-time = "2026-04-30T23:23:57.376Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ee/9e/a5db014bb0f91b209236b57c429389e31290c0093532b8436d577699b2fa/ast_serialize-0.3.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:a6fb063bf040abf8321e7b8113a0554eda445ffc508aa51287f8808886a5ae22", size = 1171316, upload-time = "2026-04-30T23:23:59.63Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/15/59/fd55133e478c4326f60a11df02573bf7ccb2ac685810b50f1803d0f68053/ast_serialize-0.3.0-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5075cd8482573d743586779e5f9b652a015e37d4e95132d7e5a9bc5c8f483d8f", size = 1232234, upload-time = "2026-04-30T23:24:01.168Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/cc/79/0ca1d26357ecb4a697d74d00b73ef3137f24c140424125393a0de820eb09/ast_serialize-0.3.0-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:41560b27794f4553b0f77811e9fb325b77db4a2b39018d437e09932275306e66", size = 1233437, upload-time = "2026-04-30T23:24:03.151Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/53/3e/7078ec94dd6e124b8e028ac77016a4f13c83fa1c145790f2e68f3816998b/ast_serialize-0.3.0-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b967c01ca74909c5d90e0fe4393401e2cc5da5ebd9a6262a19e45ffd3757dec8", size = 1440188, upload-time = "2026-04-30T23:24:04.717Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/21/16/cca7195ef55a012f8013c3442afa91d287a0a36dcf88b480b262475135b3/ast_serialize-0.3.0-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:424ebb8f46cd993f7cec4009d119312d8433dd90e6b0df0499cd2c91bdcc5af9", size = 1254211, upload-time = "2026-04-30T23:24:06.18Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/a0/0f/f3d4dfae67dee6580534361a6343367d34217e7d25cff858bd1d8f03b8ed/ast_serialize-0.3.0-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d14b1d566b56e2ee70b11fec1de7e0b94ec7cd83717ec7d189967841a361190e", size = 1255973, upload-time = "2026-04-30T23:24:07.772Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/14/41/55fbfe02c42f40fbe3e74eda167d977d555ff720ce1abfa08515236efd88/ast_serialize-0.3.0-cp314-cp314t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:7ba30b18735f047ec11103d1ab92f4789cf1fea1e0dc89b04a2f5a0632fd79de", size = 1298629, upload-time = "2026-04-30T23:24:09.4Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/28/36/7d2501cacc7989fb8504aa9da2a2022a174200a59d4e6639de4367a57fdd/ast_serialize-0.3.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:e6ea0754cb7b0f682ebb005ffb0d18f8d17993490d9c289863cd69cacc4ab8df", size = 1408435, upload-time = "2026-04-30T23:24:11.013Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/03/e7/54e3b469c3fa0bf9cd532fa643d1d33b73303f8d70beac3e366b68dd64b7/ast_serialize-0.3.0-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:a0c5aa1073a5ba7b2abaa4b54abe8b8d75c4d1e2d54a2ff70b0ca6222fea5728", size = 1508174, upload-time = "2026-04-30T23:24:12.635Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/b5/2a/9b9621865b02c60539e26d9b114a312b4fa46aa703e33e79317174bfea21/ast_serialize-0.3.0-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:4e52650d834c1ea7791969a361de2c54c13b2fb4c519ec79445fa8b9021a147d", size = 1502354, upload-time = "2026-04-30T23:24:14.186Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/34/dd/f138bc5c43b0c414fdd12eefe15677839323078b6e75301ad7f96cd26d45/ast_serialize-0.3.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:15bd6af3f136c61dae27805eb6b8f3269e85a545c4c27ffe9e530ead78d2b36d", size = 1450504, upload-time = "2026-04-30T23:24:16.076Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/68/cf/97ef9e1c315601db74365955c8edd3292e3055500d6317602815dbdf08ae/ast_serialize-0.3.0-cp314-cp314t-win32.whl", hash = "sha256:d188bfe37b674b49708497683051d4b571366a668799c9b8e8a94513694969d9", size = 1058662, upload-time = "2026-04-30T23:24:17.535Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/f8/d6/e2c3483c31580fdb623f92ad38d2f856cde4b9205a3e6bd84760f3de7d82/ast_serialize-0.3.0-cp314-cp314t-win_amd64.whl", hash = "sha256:5832c2fdf8f8a6cf682b4cfcf677f5eaf39b4ddbc490f5480cfccdd1e7ce8fa1", size = 1100349, upload-time = "2026-04-30T23:24:18.992Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ab/89/29abcb1fe18a429cda60c6e0bbd1d6e90499339842a2f548d7567542357e/ast_serialize-0.3.0-cp314-cp314t-win_arm64.whl", hash = "sha256:670f177188d128fb7f9f15b5ad0e1b553d22c34e3f584dcb83eb8077600437f0", size = 1072895, upload-time = "2026-04-30T23:24:20.706Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/bc/93/72abad83966ed6235647c9f956417dc1e17e997696388521910e3d1fa3f4/ast_serialize-0.3.0-cp39-abi3-macosx_10_12_x86_64.whl", hash = "sha256:2ec2fafa5e4313cc8feed96e436ebe19ac7bc6fa41fbc2827e826c48b9e4c3a9", size = 1190024, upload-time = "2026-04-30T23:24:22.486Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/85/4f/eb88584b2f0234e581762011208ca203252bf6c98e59b4769daa571f3576/ast_serialize-0.3.0-cp39-abi3-macosx_11_0_arm64.whl", hash = "sha256:ef6d3c08b7b4cd29b48410338e134764a00e76d25841eb02c1084e868c888ecc", size = 1178633, upload-time = "2026-04-30T23:24:24.35Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/56/51/cf1ec1ff3e616373d0dcbd5fad502e0029dc541f13ab642259762a7d127f/ast_serialize-0.3.0-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3d841424f41b886e98044abc80769c14a956e6e5ccd5fb5b0d9f5ead72be18a4", size = 1241351, upload-time = "2026-04-30T23:24:25.987Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/0d/44/68fcf50478cf1093f2d423f034ae06453122c8b415d8e21a44668eca485d/ast_serialize-0.3.0-cp39-abi3-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d21453734ad39367ede5d37efe4f59f830ce1c09f432fc72a90e368f77a4a3e7", size = 1239582, upload-time = "2026-04-30T23:24:27.808Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/9d/c1/a6c9fa284eceb5fc6f21347e968445a051d7ca2c4d34e6a04314646dbcee/ast_serialize-0.3.0-cp39-abi3-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f5e110cdce2a347e1dd987529c88ef54d26f67848dce3eba1b3b2cc2cf085c94", size = 1448853, upload-time = "2026-04-30T23:24:29.534Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/23/5f/8ad3829a09e4e8c5328a53ce7d4711d660944e3e164c5f6abcc2c8f27167/ast_serialize-0.3.0-cp39-abi3-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3b6e23a98e57560a055f5c4b68700a0fd5ce483d2814c23140b3638c7f5d1e61", size = 1262204, upload-time = "2026-04-30T23:24:31.482Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/25/13/44aa28d97f10e25247e8576b5f6b2795d4fa1a80acc88acc942c508d06f7/ast_serialize-0.3.0-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c1c9e763d70293d65ce1e1ea8c943140c68d0953f0268c7ee0998f2e07f77dd0", size = 1266458, upload-time = "2026-04-30T23:24:33.088Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/d8/58/b3a8be3777cd3744324fd5cec0d80d37cd96fc7cbb0fb010e03dff1e870f/ast_serialize-0.3.0-cp39-abi3-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:4388a1796c228f1ce5c391426f7d21a0003ad3b47f677dbeded9bd1a85c7209f", size = 1308700, upload-time = "2026-04-30T23:24:34.657Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/13/03/f8312d6b57f5471a9dc7946f22b8798a1fc296d38c25766223aacadec42c/ast_serialize-0.3.0-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:5283cdcc0c64c3d8b9b688dc6aaa012d9c0cf1380a7f774a6bae6a1c01b3205a", size = 1416724, upload-time = "2026-04-30T23:24:36.562Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/50/5d/13fc3789a7abac00559da2e2e9f386db4612aa1f84fc53d09bf714c37545/ast_serialize-0.3.0-cp39-abi3-musllinux_1_2_armv7l.whl", hash = "sha256:f5ef88cc5842a5d7a6ac09dc0d5fc2c98f5d276c1f076f866d55047ce886785b", size = 1515441, upload-time = "2026-04-30T23:24:38.018Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/eb/b9/7ab43fc7a23b1f970281093228f5f79bed6edeed7a3e672bde6d7a832a58/ast_serialize-0.3.0-cp39-abi3-musllinux_1_2_i686.whl", hash = "sha256:cc14bf402bdc0978594ecce783793de2c7470cd4f5cd7eb286ca97ed8ff7cba9", size = 1510522, upload-time = "2026-04-30T23:24:39.798Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/56/ec/d75fc2b788d319f1fad77c14156896f31afdfc68af85b505e5bdebcb9592/ast_serialize-0.3.0-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:11eae0cf1b7b3e0678133cc2daa974ea972caf02eb4b3aa062af6fa9acd52c57", size = 1460917, upload-time = "2026-04-30T23:24:41.305Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/95/74/f99c81193a2725911e1911ae567ed27c2f2419332c7f3537366f9d238cac/ast_serialize-0.3.0-cp39-abi3-win32.whl", hash = "sha256:2db3dd99de5e6a5a11d7dda73de8750eb6e5baaf25245adf7bdcfe64b6108ae2", size = 1067804, upload-time = "2026-04-30T23:24:43.091Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/16/81/76af00c47daa151e89f98ae21fbbcb2840aaa9f5766579c4da76a3c57188/ast_serialize-0.3.0-cp39-abi3-win_amd64.whl", hash = "sha256:a2cd125adccf7969470621905d302750cd25951f22ea430d9a25b7be031e5549", size = 1105561, upload-time = "2026-04-30T23:24:44.578Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/bd/46/d3ec57ad500f598d1554bd14ce4df615960549ab2844961bc4e1f5fbd174/ast_serialize-0.3.0-cp39-abi3-win_arm64.whl", hash = "sha256:0dd00da29985f15f50dc35728b7e1e7c84507bccfea1d9914738530f1c72238a", size = 1077165, upload-time = "2026-04-30T23:24:46.377Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "backports-asyncio-runner"
|
||||
version = "1.2.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/8e/ff/70dca7d7cb1cbc0edb2c6cc0c38b65cba36cccc491eca64cabd5fe7f8670/backports_asyncio_runner-1.2.0.tar.gz", hash = "sha256:a5aa7b2b7d8f8bfcaa2b57313f70792df84e32a2a746f585213373f900b42162", size = 69893, upload-time = "2025-07-02T02:27:15.685Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/a0/59/76ab57e3fe74484f48a53f8e337171b4a2349e506eabe136d7e01d059086/backports_asyncio_runner-1.2.0-py3-none-any.whl", hash = "sha256:0da0a936a8aeb554eccb426dc55af3ba63bcdc69fa1a600b5bb305413a4477b5", size = 12313, upload-time = "2025-07-02T02:27:14.263Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "certifi"
|
||||
version = "2026.4.22"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/25/ee/6caf7a40c36a1220410afe15a1cc64993a1f864871f698c0f93acb72842a/certifi-2026.4.22.tar.gz", hash = "sha256:8d455352a37b71bf76a79caa83a3d6c25afee4a385d632127b6afb3963f1c580", size = 137077, upload-time = "2026-04-22T11:26:11.191Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/22/30/7cd8fdcdfbc5b869528b079bfb76dcdf6056b1a2097a662e5e8c04f42965/certifi-2026.4.22-py3-none-any.whl", hash = "sha256:3cb2210c8f88ba2318d29b0388d1023c8492ff72ecdde4ebdaddbb13a31b1c4a", size = 135707, upload-time = "2026-04-22T11:26:09.372Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "colorama"
|
||||
version = "0.4.6"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", size = 27697, upload-time = "2022-10-25T02:36:22.414Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335, upload-time = "2022-10-25T02:36:20.889Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "exceptiongroup"
|
||||
version = "1.3.1"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "typing-extensions", marker = "python_full_version < '3.13'" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/50/79/66800aadf48771f6b62f7eb014e352e5d06856655206165d775e675a02c9/exceptiongroup-1.3.1.tar.gz", hash = "sha256:8b412432c6055b0b7d14c310000ae93352ed6754f70fa8f7c34141f91c4e3219", size = 30371, upload-time = "2025-11-21T23:01:54.787Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/8a/0e/97c33bf5009bdbac74fd2beace167cab3f978feb69cc36f1ef79360d6c4e/exceptiongroup-1.3.1-py3-none-any.whl", hash = "sha256:a7a39a3bd276781e98394987d3a5701d0c4edffb633bb7a5144577f82c773598", size = 16740, upload-time = "2025-11-21T23:01:53.443Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "gaiia"
|
||||
version = "0.1.0"
|
||||
source = { editable = "." }
|
||||
dependencies = [
|
||||
{ name = "httpx" },
|
||||
]
|
||||
|
||||
[package.optional-dependencies]
|
||||
dev = [
|
||||
{ name = "mypy" },
|
||||
{ name = "pytest" },
|
||||
{ name = "pytest-asyncio" },
|
||||
{ name = "respx" },
|
||||
{ name = "ruff" },
|
||||
]
|
||||
|
||||
[package.metadata]
|
||||
requires-dist = [
|
||||
{ name = "httpx", specifier = ">=0.27" },
|
||||
{ name = "mypy", marker = "extra == 'dev'", specifier = ">=1.10" },
|
||||
{ name = "pytest", marker = "extra == 'dev'", specifier = ">=8" },
|
||||
{ name = "pytest-asyncio", marker = "extra == 'dev'", specifier = ">=0.23" },
|
||||
{ name = "respx", marker = "extra == 'dev'", specifier = ">=0.21" },
|
||||
{ name = "ruff", marker = "extra == 'dev'", specifier = ">=0.5" },
|
||||
]
|
||||
provides-extras = ["dev"]
|
||||
|
||||
[[package]]
|
||||
name = "h11"
|
||||
version = "0.16.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/01/ee/02a2c011bdab74c6fb3c75474d40b3052059d95df7e73351460c8588d963/h11-0.16.0.tar.gz", hash = "sha256:4e35b956cf45792e4caa5885e69fba00bdbc6ffafbfa020300e549b208ee5ff1", size = 101250, upload-time = "2025-04-24T03:35:25.427Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/04/4b/29cac41a4d98d144bf5f6d33995617b185d14b22401f75ca86f384e87ff1/h11-0.16.0-py3-none-any.whl", hash = "sha256:63cf8bbe7522de3bf65932fda1d9c2772064ffb3dae62d55932da54b31cb6c86", size = 37515, upload-time = "2025-04-24T03:35:24.344Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "httpcore"
|
||||
version = "1.0.9"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "certifi" },
|
||||
{ name = "h11" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/06/94/82699a10bca87a5556c9c59b5963f2d039dbd239f25bc2a63907a05a14cb/httpcore-1.0.9.tar.gz", hash = "sha256:6e34463af53fd2ab5d807f399a9b45ea31c3dfa2276f15a2c3f00afff6e176e8", size = 85484, upload-time = "2025-04-24T22:06:22.219Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/7e/f5/f66802a942d491edb555dd61e3a9961140fd64c90bce1eafd741609d334d/httpcore-1.0.9-py3-none-any.whl", hash = "sha256:2d400746a40668fc9dec9810239072b40b4484b640a8c38fd654a024c7a1bf55", size = 78784, upload-time = "2025-04-24T22:06:20.566Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "httpx"
|
||||
version = "0.28.1"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "anyio" },
|
||||
{ name = "certifi" },
|
||||
{ name = "httpcore" },
|
||||
{ name = "idna" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/b1/df/48c586a5fe32a0f01324ee087459e112ebb7224f646c0b5023f5e79e9956/httpx-0.28.1.tar.gz", hash = "sha256:75e98c5f16b0f35b567856f597f06ff2270a374470a5c2392242528e3e3e42fc", size = 141406, upload-time = "2024-12-06T15:37:23.222Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/2a/39/e50c7c3a983047577ee07d2a9e53faf5a69493943ec3f6a384bdc792deb2/httpx-0.28.1-py3-none-any.whl", hash = "sha256:d909fcccc110f8c7faf814ca82a9a4d816bc5a6dbfea25d6591d6985b8ba59ad", size = 73517, upload-time = "2024-12-06T15:37:21.509Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "idna"
|
||||
version = "3.14"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/05/b1/efac073e0c297ecf2fb33c346989a529d4e19164f1759102dee5953ee17e/idna-3.14.tar.gz", hash = "sha256:466d810d7a2cc1022bea9b037c39728d51ae7dad40d480fc9b7d7ecf98ba8ee3", size = 198272, upload-time = "2026-05-10T20:32:15.935Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/6c/3c/3f62dee257eb3d6b2c1ef2a09d36d9793c7111156a73b5654d2c2305e5ce/idna-3.14-py3-none-any.whl", hash = "sha256:e677eaf072e290f7b725f9acf0b3a2bd55f9fd6f7c70abe5f0e34823d0accf69", size = 72184, upload-time = "2026-05-10T20:32:14.295Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "iniconfig"
|
||||
version = "2.3.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/72/34/14ca021ce8e5dfedc35312d08ba8bf51fdd999c576889fc2c24cb97f4f10/iniconfig-2.3.0.tar.gz", hash = "sha256:c76315c77db068650d49c5b56314774a7804df16fee4402c1f19d6d15d8c4730", size = 20503, upload-time = "2025-10-18T21:55:43.219Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/cb/b1/3846dd7f199d53cb17f49cba7e651e9ce294d8497c8c150530ed11865bb8/iniconfig-2.3.0-py3-none-any.whl", hash = "sha256:f631c04d2c48c52b84d0d0549c99ff3859c98df65b3101406327ecc7d53fbf12", size = 7484, upload-time = "2025-10-18T21:55:41.639Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "librt"
|
||||
version = "0.11.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/40/08/9e7f6b5d2b5bed6ad055cdd5925f192bb403a51280f86b56554d9d0699a2/librt-0.11.0.tar.gz", hash = "sha256:075dc3ef4458a278e0195cbf6ac9d38808d9b906c5a6c7f7f79c3888276a3fb1", size = 200139, upload-time = "2026-05-10T18:17:25.138Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/83/10/37fd9e9ba96cb0bd742dfb20fc3d082e54bdbec759d7300df927f360ef07/librt-0.11.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:6e94ebfcfa2d5e9926d6c3b9aa4617ffc42a845b4321fb84021b872358c82a0f", size = 141706, upload-time = "2026-05-10T18:15:16.129Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/cf/72/1b1466f358e4a0b728051f69bc27e67b432c6eaa2e05b88db49d3785ae0d/librt-0.11.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:ae627397a2f351560440d872d6f7c8dbb4072e57868e7b2fc5b8b430fe489d45", size = 142605, upload-time = "2026-05-10T18:15:18.148Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ca/85/ed26dd2f6bc9a0baf48306433e579e8d354d70b2bcb78134ed950a5d0e1e/librt-0.11.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:dc329359321b67d24efdf4bc69012b0597001649544db662c001db5a0184794c", size = 476555, upload-time = "2026-05-10T18:15:19.569Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/66/fe/11891191c0e0a3fd617724e891f6e67a71a7658974a892b9a9a97fdb2977/librt-0.11.0-cp310-cp310-manylinux2014_i686.manylinux_2_17_i686.manylinux_2_28_i686.whl", hash = "sha256:7e82e642ab0f7608ce2fe53d76ca2280a9ee33a1b06556142c7c6fe80a86fc33", size = 468434, upload-time = "2026-05-10T18:15:20.87Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/6f/50/5ec949d7f9ce1a07af903aa3e13abb98b717923bdead6e719b2f824ccc07/librt-0.11.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:88145c15c67731d54283d135b03244028c750cc9edc334a96a4f5950ebdb2884", size = 496918, upload-time = "2026-05-10T18:15:22.616Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ea/c4/177336c7524e34875a38bf668e88b193a6723a4eb4045d07f74df6e1506c/librt-0.11.0-cp310-cp310-manylinux_2_34_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:9d36a51b3d93320b686588e27123f4995804dbf1bce81df78c02fc3c6eea9280", size = 490334, upload-time = "2026-05-10T18:15:24.2Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/13/1f/da3112f7569eda3b49f9a2629bae1fe059812b6085df16c885f6454dff49/librt-0.11.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:d00f3ac06a2a8b246327f11e186a53a100a4d5c7ed52346367e5ec751d51586c", size = 511287, upload-time = "2026-05-10T18:15:26.226Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/fa/94/03fec301522e172d105581431223be56b27594ff46440ebfbb658a3735d5/librt-0.11.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:461bbceede621f1ffb8839755f8663e886087ee7af16294cab7fb4d782c62eeb", size = 517202, upload-time = "2026-05-10T18:15:27.965Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/b7/6e/339f6e5a7b413ce014f1917a756dae630fe59cc99f34153205b1cb540901/librt-0.11.0-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:0cad8a4d6a8ff03c9b76f9414caccd78e7cfbc8a2e12fa334d8e1d9932753783", size = 497517, upload-time = "2026-05-10T18:15:29.614Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/cd/43/acdd5ce317cb46e8253ca9bfbdb8b12e68a24d745949336a7f3d5fb79ba0/librt-0.11.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:f37aa505b3cf60701562eddb32df74b12a9e380c207fd8b06dd157a943ac7ea0", size = 538878, upload-time = "2026-05-10T18:15:30.928Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/29/b5/7a25bb12e3172839f647f196b3e988318b7bb1ca7501732a225c4dce2ec0/librt-0.11.0-cp310-cp310-win32.whl", hash = "sha256:94663a21534637f0e787ec2a2a756022df6e5b7b2335a5cdd7d8e33d68a2af89", size = 100070, upload-time = "2026-05-10T18:15:32.551Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/c6/0d/ebbcf4d77999c02c937b05d2b90ff4cd4dcc7e9a365ba132329ac1fe7a0f/librt-0.11.0-cp310-cp310-win_amd64.whl", hash = "sha256:dec7db73758c2b54953fd8b7fe348c45188fe26b39ee18446196edd08453a5d4", size = 117918, upload-time = "2026-05-10T18:15:33.678Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/fe/87/2bf31fe17587b29e3f93ec31421e2b1e1c3e349b8bf6c7c313dbad1d5340/librt-0.11.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:93d95bd45b7d58343d8b90d904450a545144eec19a002511163426f8ab1fae29", size = 141092, upload-time = "2026-05-10T18:15:34.795Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/cf/08/5c5bf772920b7ebac6e32bc91a643e0ab3870199c0b542356d3baa83970a/librt-0.11.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:4ee278c769a713638cdacd4c0436d72156e75df3ebc0166ab2b9dc43acc386c9", size = 142035, upload-time = "2026-05-10T18:15:36.242Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/06/20/662a03d254e5b000d838e8b345d83303ddb768c080fd488e40634c0fa66b/librt-0.11.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f230cb1cbc9faaa616f9a678f530ebcf186e414b6bcbd88b960e4ba1b92428d5", size = 475022, upload-time = "2026-05-10T18:15:37.56Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/de/f3/aa81523e45184c6ec23dc7f63263362ec55f80a09d424c012359ecbe7e35/librt-0.11.0-cp311-cp311-manylinux2014_i686.manylinux_2_17_i686.manylinux_2_28_i686.whl", hash = "sha256:5d63c855d86938d9de93e265c9bd8c705b51ec494de5738340ee93767a686e4b", size = 467273, upload-time = "2026-05-10T18:15:39.182Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/6b/6f/59c74b560ca8853834d5501d589c8a2519f4184f273a085ffd0f37a1cc47/librt-0.11.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:993f028be9e96a08d31df3479ac80d99be374d17f3b78e4796b3fd3c913d4e89", size = 497083, upload-time = "2026-05-10T18:15:40.634Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/fe/7b/5aa4d2c9600a719401160bf7055417df0b2a47439b9d88286ce45e56b65f/librt-0.11.0-cp311-cp311-manylinux_2_34_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:258d73a0aa66a055e65b2e4d1b8cdb23b9d132c5bb915d9547d804fcaed116cc", size = 489139, upload-time = "2026-05-10T18:15:41.934Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/d6/31/9143803d7da6856a69153785768c4936864430eec0fd9461c3ea527d9922/librt-0.11.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:0827efe7854718f04aaddf6496e96960a956e676fe1d0f04eb41511fd8ad06d5", size = 508442, upload-time = "2026-05-10T18:15:43.206Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/2f/5a/bce08184488426bda4ccc2c4964ac048c8f68ae89bd7120082eef4233cfd/librt-0.11.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:7753e57d6e12d019c0d8786f1c09c709f4c3fcc57c3887b24e36e6c06ec938b7", size = 514230, upload-time = "2026-05-10T18:15:44.761Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/89/8c/bb5e213d254b7505a0e658da199d8ab719086632ce09eef311ab27976523/librt-0.11.0-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:11bd19822431cc21af9f27374e7ae2e58103c7d98bda823536a6c47f6bb2bb3d", size = 494231, upload-time = "2026-05-10T18:15:46.308Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/9d/fb/541cdad5b1ab1300398c74c4c9a497b88e5074c21b1244c8f49731d3a284/librt-0.11.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:22bdf239b219d3993761a148ffa134b19e52e9989c84f845d5d7b71d70a17412", size = 537585, upload-time = "2026-05-10T18:15:47.629Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/8f/f2/464bb69295c320cb06bddb4f14a4ec67934ee14b2bffb12b19fb7ab287ba/librt-0.11.0-cp311-cp311-win32.whl", hash = "sha256:46c60b61e308eb535fbd6fa622b1ee1bb2815691c1ad9c98bf7b84952ec3bc8d", size = 100509, upload-time = "2026-05-10T18:15:49.157Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/6d/e7/a17ee1788f9e4fbf548c19f4afa07c92089b9e24fef6cb2410863781ef4c/librt-0.11.0-cp311-cp311-win_amd64.whl", hash = "sha256:902e546ff044f579ff1c953ff5fce97b636fe9e3943996b2177710c6ef076f73", size = 118628, upload-time = "2026-05-10T18:15:50.345Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/cc/c7/6c766214f9f9903bcfcfbef97d807af8d8f5aa3502d247858ab17582d212/librt-0.11.0-cp311-cp311-win_arm64.whl", hash = "sha256:65ac3bc20f78aa0ee5ae84baa68917f89fef4af63e941084dd019a0d0e749f0c", size = 103122, upload-time = "2026-05-10T18:15:52.068Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/8b/d0/07c77e067f0838949b43bd89232c29d72efebb9d2801a9750184eb706b71/librt-0.11.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:b87504f1690a23b9a2cca841191a04f83895d4fc2dd04df91d82b1a04ca2ad46", size = 144147, upload-time = "2026-05-10T18:15:53.227Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/7a/24/8493538fa4f62f982686398a5b8f68008138a75086abdea19ade64bf4255/librt-0.11.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:40071fc5fe0ce8daa6de616702314a01e1250711682b0523d6ab8d4525910cb3", size = 143614, upload-time = "2026-05-10T18:15:54.657Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ff/1e/f8bad050810d9171f34a1648ed910e56814c2ba61639f2bd53c6377ae24b/librt-0.11.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:137e79445c896a0ea7b265f52d23954e05b64222ee1af69e2cb34219067cbb67", size = 485538, upload-time = "2026-05-10T18:15:56.117Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/c0/fe/3594ebfbaf03084ba4b120c9ba5c3183fd938a48725e9bbe6ff0a5159ad8/librt-0.11.0-cp312-cp312-manylinux2014_i686.manylinux_2_17_i686.manylinux_2_28_i686.whl", hash = "sha256:cca6644054e78746d8d4ef238681f9c34ff8b584fe6b988ecebb8db3b15e622a", size = 479623, upload-time = "2026-05-10T18:15:57.544Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/b0/da/5d1876984b3746c85dbd219dbfcb73c85f54ee263fd32e5b2a632ec14571/librt-0.11.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d5b0eea49f5562861ee8d757a32ef7d559c1d35be2aaaa1ec28941d74c9ffc8a", size = 513082, upload-time = "2026-05-10T18:15:58.805Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/19/6e/55bdf5d5ca00c3e18430690bf2c953d8d3ffd3c337418173d33dec985dc9/librt-0.11.0-cp312-cp312-manylinux_2_34_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:0d1029d7e1ae1a7e647ed6fb5df8c4ce2dffefb7a9f5fd1376a4554d96dac09f", size = 508105, upload-time = "2026-05-10T18:16:00.2Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/07/10/f1f23a7c595ee90ece4d35c851e5d104b1311a887ed1b4ac4c35bbd13da8/librt-0.11.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:bc3ce6b33c5828d9e80592011a5c584cb2ce86edbc4088405f70da47dc1d1b3b", size = 522268, upload-time = "2026-05-10T18:16:01.708Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/b6/02/5720f5697a7f54b78b3aefbe20df3a48cedcff1276618c4aa481177942ed/librt-0.11.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:936c5995f3514a42111f20099397d8177c79b4d7e70961e396c6f5a0a3566766", size = 527348, upload-time = "2026-05-10T18:16:03.496Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/50/db/b4a47c6f91db4ff76348a0b3dd0cc65e090a078b765a810a62ff9434c3d3/librt-0.11.0-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:9bc0ca6ad9381cbe8e4aa6e5726e4c80c78115a6e9723c599ed1d73e092bc49d", size = 516294, upload-time = "2026-05-10T18:16:05.173Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/9e/58/9384b2f4eb1ed1d273d40948a7c5c4b2360213b402ef3be4641c06299f9c/librt-0.11.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:070aa8c26c0a74774317a72df8851facc7f0f012a5b406557ac56992d92e1ec8", size = 553608, upload-time = "2026-05-10T18:16:06.839Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/21/7b/5aa8848a7c6a9278c79375146da1812e695754ceec5f005e6043461a7315/librt-0.11.0-cp312-cp312-win32.whl", hash = "sha256:6bf14feb84b05ae945277395451998c89c54d0def4070eb5c08de544930b245a", size = 101879, upload-time = "2026-05-10T18:16:08.103Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/37/33/8a745436944947575b584231750a41417de1a38cf6a2e9251d1065651c09/librt-0.11.0-cp312-cp312-win_amd64.whl", hash = "sha256:75672f0bc524ede266287d532d7923dbce94c7514ad07627bac3d0c6d92cc4d9", size = 119831, upload-time = "2026-05-10T18:16:09.174Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/59/67/a6739ac96e28b7855808bdb0370e250606104a859750d209e5a0716fe7ab/librt-0.11.0-cp312-cp312-win_arm64.whl", hash = "sha256:2f10cf143e4a9bb0f4f5af568a00df94a2d69ef41c2579584454bb0fe5cc642c", size = 103470, upload-time = "2026-05-10T18:16:10.369Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/82/61/e59168d4d0bf2bf90f4f0caf7a001bfc60254c3af4586013b04dc3ef517b/librt-0.11.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:78dc31f7fdfe9c9d0eb0e8f42d139db230e826415bbcabd9f0e9faaaee909894", size = 144119, upload-time = "2026-05-10T18:16:11.771Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/61/fd/caa1d60b12f7dd79ccea23054e06eeaebe266a5f52c40a6b651069200ce5/librt-0.11.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:fa475675db22290c3158e1d42326d0f5a65f04f44a0e68c3630a25b53560fb9c", size = 143565, upload-time = "2026-05-10T18:16:13.334Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/b8/a9/dc744f5c2b4978d48db970be29f22716d3413d28b14ad99740817315cf2c/librt-0.11.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:621db29691044bdeda22e789e482e1b0f3a985d90e3426c9c6d17606416205ea", size = 485395, upload-time = "2026-05-10T18:16:14.729Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/8f/21/7f8e97a1e4dae952a5a95948f6f8507a173bc1e669f54340bba6ca1ca31b/librt-0.11.0-cp313-cp313-manylinux2014_i686.manylinux_2_17_i686.manylinux_2_28_i686.whl", hash = "sha256:a9010e2ed5b3a9e158c5fd966b3ab7e834bb3d3aacc8f66c91dd4b57a3799230", size = 479383, upload-time = "2026-05-10T18:16:16.321Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/a6/6d/d8ee9c114bebf2c50e29ec2aa940826fccb62a645c3e4c18760987d0e16d/librt-0.11.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7c39513d8b7477a2e1ed8c43fc21c524e8d5a0f8d4e8b7b074dbdbe7820a08e2", size = 513010, upload-time = "2026-05-10T18:16:17.647Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/f0/43/0b5708af2bd30a46400e72ba6bdaa8f066f15fb9a688527e34220e8d6c06/librt-0.11.0-cp313-cp313-manylinux_2_34_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:7aef3cf1d5af86e770ab04bfd993dfc4ae8b8c17f66fb77dd4a7d50de7bbb1a3", size = 508433, upload-time = "2026-05-10T18:16:19.309Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/4a/50/356187247d09013490481033183b3532b58acf8028bcb34b2b56a375c9b2/librt-0.11.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:557183ddc36babe46b27dd60facbd5adb4492181a5be887587d57cda6e092f21", size = 522595, upload-time = "2026-05-10T18:16:20.642Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/40/e7/c6ac4240899c7f3248079d5a9900debe0dadb3fdeaf856684c987105ba47/librt-0.11.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:83d3e1f72bd42f6c5c0b7daec530c3f829bd02db42c70b8ddf0c2d90a2459930", size = 527255, upload-time = "2026-05-10T18:16:22.352Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/eb/b5/a81322dbeedeeaf9c1ee6f001734d28a09d8383ac9e6779bc24bbd0743c6/librt-0.11.0-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:4ce1f21fbe589bc1afd7872dece84fb0e1144f794a288e58a10d2c54a55c43be", size = 516847, upload-time = "2026-05-10T18:16:23.627Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ae/66/6e6323787d592b55204a42595ff1102da5115601b53a7e9ddebc889a6da5/librt-0.11.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:970b09f7044ea2b64c9da42fd3d335666518cfd1c6e8a182c95da73d0214b41e", size = 553920, upload-time = "2026-05-10T18:16:25.025Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/9c/21/623f8ca230857102066d9ca8c6c1734995908c4d0d1bee7bb2ef0021cb33/librt-0.11.0-cp313-cp313-win32.whl", hash = "sha256:78fddc31cd4d3caa897ad5d31f856b1faadc9474021ad6cb182b9018793e254e", size = 101898, upload-time = "2026-05-10T18:16:26.649Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/b3/1d/b4ebd44dd723f768469007515cb92251e0ae286c94c140f374801140fa74/librt-0.11.0-cp313-cp313-win_amd64.whl", hash = "sha256:8ca8aa88751a775870b764e93bad5135385f563cb8dcee399abf034ea4d3cb47", size = 119812, upload-time = "2026-05-10T18:16:27.859Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/3b/e4/b2f4ca7965ca373b491cdb4bc25cdb30c1649ca81a8782056a83850292a9/librt-0.11.0-cp313-cp313-win_arm64.whl", hash = "sha256:96f044bb325fd9cf1a723015638c219e9143f0dfbc0ca54c565df2b7fc748b44", size = 103448, upload-time = "2026-05-10T18:16:29.066Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/29/eb/dbce197da4e227779e56b5735f2decc3eb36e55a1cdbf1bd65d6639d76c1/librt-0.11.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:4a017a95e5837dc15a8c5661d60e05daa96b90908b1aa6b7acdf443cd25c8ebd", size = 143345, upload-time = "2026-05-10T18:16:30.674Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/76/a3/254bebd0c11c8ba684018efb8006ff22e466abce445215cca6c778e7d9de/librt-0.11.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:b1ecbd9819deccc39b7542bf4d2a740d8a620694d39989e58661d3763458f8d4", size = 143131, upload-time = "2026-05-10T18:16:32.037Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/f1/3f/f77d6122d21ac7bf6ae8a7dfced1bd2a7ac545d3273ebdcaf8042f6d619f/librt-0.11.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7da327dacd7be8f8ec36547373550744a3cc0e536d54665cd83f8bcd961200e8", size = 477024, upload-time = "2026-05-10T18:16:33.493Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ac/0a/2c996dadebaa7d9bbbd43ef2d4f3e66b6da545f838a41694ef6172cebec8/librt-0.11.0-cp314-cp314-manylinux2014_i686.manylinux_2_17_i686.manylinux_2_28_i686.whl", hash = "sha256:0dc56b1f8d06e60db362cc3fdae206681817f86ce4725d34511473487f12a34b", size = 474221, upload-time = "2026-05-10T18:16:34.864Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/0a/7e/f5d92af8486b8272c23b3e686b46ff72d89c8169585eb61eef01a2ac7147/librt-0.11.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:05fb8fb2ab90e21c8d12ea240d744ad514da9baf381ebfa70d91d20d21713175", size = 505174, upload-time = "2026-05-10T18:16:36.705Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/af/1a/cb0734fe86398eb33193ab753b7326255c74cac5eb09e76b9b16536e7adb/librt-0.11.0-cp314-cp314-manylinux_2_34_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:cae74872be221df4374d10fec61f93ed1513b9546ea84f2c0bf73ab3e9bd0b03", size = 497216, upload-time = "2026-05-10T18:16:38.418Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/18/06/094820f91558b66e29943c0ec41c9914f460f48dd51fc503c3101e10842d/librt-0.11.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:32bcc918c0148eb7e3d57385125bac7e5f9e4359d05f07448b09f6f778c2f31c", size = 513921, upload-time = "2026-05-10T18:16:39.848Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/0b/c2/00de9018871a282f530cacb457d5ec0428f6ac7e6fedde9aff7468d9fb04/librt-0.11.0-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:f9743fc99135d5f78d2454435615f6dec0473ca507c26ce9d92b10b562a280d3", size = 520850, upload-time = "2026-05-10T18:16:41.471Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/51/9d/64631832348fd1834fb3a61b996434edddaaf25a31d03b0a76273159d2cf/librt-0.11.0-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:5ba067f4aadae8fda802d91d2124c90c42195ff32d9161d3549e6d05cfe26f96", size = 504237, upload-time = "2026-05-10T18:16:43.15Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/a5/ec/ae5525eb16edc827a044e7bb8777a455ff95d4bca9379e7e6bddd7383647/librt-0.11.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:de3bf945454d032f9e390b85c4072e0a0570bf825421c8be0e71209fa65e1abe", size = 546261, upload-time = "2026-05-10T18:16:44.408Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/5a/09/adce371f27ca039411da9659f7430fcc2ba6cd0c7b3e4467a0f091be7fa9/librt-0.11.0-cp314-cp314-win32.whl", hash = "sha256:d2277a05f6dcb9fd13db9566aac4fabd68c3ea1ea46ee5567d4eef8efa495a2f", size = 96965, upload-time = "2026-05-10T18:16:46.039Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/d6/ee/8ac720d98548f173c7ce2e632a7ca94673f74cacd5c8162a84af5b35958a/librt-0.11.0-cp314-cp314-win_amd64.whl", hash = "sha256:ab73e8db5e3f564d812c1f5c3a175930a5f9bc96ccb5e3b22a34d7858b401cf7", size = 115151, upload-time = "2026-05-10T18:16:47.133Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/94/20/c900cf14efeb09b6bef2b2dff20779f73464b97fd58d1c6bccc379588ae3/librt-0.11.0-cp314-cp314-win_arm64.whl", hash = "sha256:aea3caa317752e3a466fa8af45d91ee0ea8c7fdd96e42b0a8dd9b76a7931eba1", size = 98850, upload-time = "2026-05-10T18:16:48.597Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/0c/71/944bfe4b64e12abffcd3c15e1cce07f72f3d55655083786285f4dedeb532/librt-0.11.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:d1b36540d7aaf9b9101b3a6f376c8d8e9f7a9aec93ed05918f2c69d493ffef72", size = 151138, upload-time = "2026-05-10T18:16:49.839Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/b6/10/99e64a5c86989357fda078c8143c533389585f6473b7439172dd8f3b3b2d/librt-0.11.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:efbb343ab2ce3540f4ecbe6315d677ed70f37cd9a72b1e58066c918ca83acbaa", size = 151976, upload-time = "2026-05-10T18:16:51.062Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/21/31/5072ad880946d83e5ea4147d6d018c78eefce85b77819b19bdd0ee229435/librt-0.11.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:aa0dd688aab3f7914d3e6e5e3554978e0383312fb8e771d84be008a35b9ee548", size = 557927, upload-time = "2026-05-10T18:16:52.632Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/5e/8d/70b5fb7cfbab60edbe7381614ab985da58e144fbf465c86d44c95f43cdca/librt-0.11.0-cp314-cp314t-manylinux2014_i686.manylinux_2_17_i686.manylinux_2_28_i686.whl", hash = "sha256:f5fb36b8c6c63fdcbb1d526d94c0d1331610d43f4118cc1beb4efef4f3faacb2", size = 539698, upload-time = "2026-05-10T18:16:53.934Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/fa/a3/ba3495a0b3edbd24a4cae0d1d3c64f39a9fc45d06e812101289b50c1a619/librt-0.11.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4a9a237d13addb93715b6fee74023d5ee3469b53fce527626c0e088aa585805f", size = 577162, upload-time = "2026-05-10T18:16:55.589Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/f7/db/36e25fb81f99937ff1b96612a1dc9fd66f039cb9cc3aee12c01fac31aab9/librt-0.11.0-cp314-cp314t-manylinux_2_34_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:5ddd17bd87b2c56ddd60e546a7984a2e64c4e8eab92fb4cf3830a48ad5469d51", size = 566494, upload-time = "2026-05-10T18:16:56.975Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/33/0d/3f622b47f0b013eeb9cf4cc07ae9bfe378d832a4eec998b2b209fe84244d/librt-0.11.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:bd43992b4473d42f12ff9e68326079f0696d9d4e6000e8f39a0238d482ba6ee2", size = 596858, upload-time = "2026-05-10T18:16:58.374Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/a9/02/71b90bc93039c46a2000651f6ad60122b114c8f54c4ad306e0e96f5b75ad/librt-0.11.0-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:f8e3e8056dd674e279741485e2e512d6e9a751c7455809d0114e6ebf8d781085", size = 590318, upload-time = "2026-05-10T18:16:59.676Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/04/04/418cb3f75621e2b761fb1ab0f017f4d70a1a72a6e7c74ee4f7e8d198c2f3/librt-0.11.0-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:c1f708d8ae9c56cf38a903c44297243d2ec83fd82b396b977e0144a3e76217e3", size = 575115, upload-time = "2026-05-10T18:17:01.007Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/cc/2c/5a2183ac58dd911f26b5d7e7d7d8f1d87fcecdddd99d6c12169a258ff62c/librt-0.11.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:0add982e0e7b9fc14cf4b33789d5f13f66581889b88c2f58099f6ce8f92617bd", size = 617918, upload-time = "2026-05-10T18:17:02.682Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/15/1f/dc6771a52592a4451be6effa200cbfc9cec61e4393d3033d81a9d307961d/librt-0.11.0-cp314-cp314t-win32.whl", hash = "sha256:2b481d846ac894c4e8403c5fd0e87c5d11d6499e404b474602508a224ff531c8", size = 103562, upload-time = "2026-05-10T18:17:03.99Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/62/4a/7d1415567027286a75ba1093ec4aca11f073e0f559c530cf3e0a757ad55c/librt-0.11.0-cp314-cp314t-win_amd64.whl", hash = "sha256:28edb433edde181112a908c78907af28f964eabc15f4dd16c9d66c834302677c", size = 124327, upload-time = "2026-05-10T18:17:05.465Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ce/62/b40b382fa0c66fee1478073eb8db352a4a6beda4a1adccf1df911d8c289c/librt-0.11.0-cp314-cp314t-win_arm64.whl", hash = "sha256:dee008f20b542e3cd162ba338a7f9ec0f6d23d395f66fe8aeeec3c9d067ea253", size = 102572, upload-time = "2026-05-10T18:17:06.809Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "mypy"
|
||||
version = "2.1.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "ast-serialize" },
|
||||
{ name = "librt", marker = "platform_python_implementation != 'PyPy'" },
|
||||
{ name = "mypy-extensions" },
|
||||
{ name = "pathspec" },
|
||||
{ name = "tomli", marker = "python_full_version < '3.11'" },
|
||||
{ name = "typing-extensions" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/82/15/cca9d88503549ed6fedeaa1d448cdddd542ee8a490232d732e278036fbf2/mypy-2.1.0.tar.gz", hash = "sha256:81e76ad12c2d804512e9b13240d1588316531bfba07558286078bfbce9613633", size = 3898359, upload-time = "2026-05-11T18:37:36.237Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/a4/71/d351dca3e9b30da2328ee9d445c88b8388072808ebfbc49eb69d30b67749/mypy-2.1.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:11a6beb180257a805961aea9ec591bbd0bd17f1e18d35b8456d57aee5bedfedc", size = 14778792, upload-time = "2026-05-11T18:36:23.605Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/2f/45/7d51594b644c17c0bcf74ed8cd5fc33b324276d708e8506f220b70dab9d9/mypy-2.1.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:8ef78c1d306bbf9a8a12f526c44902c9c28dffd6c52c52bf6a72641ce18d3849", size = 13645739, upload-time = "2026-05-11T18:37:22.752Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/65/01/455c31b170e9468265074840bf18863a8482a24103fdaabe4e199392aa5f/mypy-2.1.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c209a90853081ff01d01ee895cafe10f7db1474e0d95beaeef0f6c1db9119bbd", size = 14074199, upload-time = "2026-05-11T18:35:09.292Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/41/5a/93093f0b29a9e982deafde698f740a2eb2e05886e79ccf0594c7fd5413a3/mypy-2.1.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:47cebf61abde7c088a4e27718a8b13a81655686b2e9c251f5c0915a802248166", size = 14953128, upload-time = "2026-05-11T18:31:57.678Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/7f/2f/a196f5331d96170ad3d28f144d2aba690d4b2911381f68d51e489c7ab82a/mypy-2.1.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:d57a90ae5e872138a425ec328edbc9b235d1934c4377881a33ec05b341acc9a8", size = 15249378, upload-time = "2026-05-11T18:33:00.101Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/54/de/94d321cc12da9f71341ac0c270efbed5c725750c7b4c334d957de9a087d9/mypy-2.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:aea7f7a8a55b459c34275fc468ada6ca7c173a5e43a68f5dbe588a563d8a06b8", size = 11060994, upload-time = "2026-05-11T18:33:18.848Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/e1/62/0c27ca55219a7c764a7fb88c7bb2b7b2f9780ade8bbf16bc8ed8400eef6b/mypy-2.1.0-cp310-cp310-win_arm64.whl", hash = "sha256:c989640253f0d76843e9c6c1bbf4bd48c5e85ada61bde4beb37cb3eca035685e", size = 9976743, upload-time = "2026-05-11T18:31:25.554Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/0a/a1/639f3024794a2a15899cb90707fe02e044c4412794c39c5769fd3df2e2ef/mypy-2.1.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:a683016b16fe2f572dc04c72be7ee0504ac1605a265d0200f5cea695fb788f41", size = 14691685, upload-time = "2026-05-11T18:33:27.973Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/3b/08/9a585dea4325f20d8b80dc78623fa50d1fd2173b710f6237afd6ba6ab39b/mypy-2.1.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1a293c534adb55271fef24a26da04b855540a8c13cc07bc5917b9fd2c394f2ca", size = 13555165, upload-time = "2026-05-11T18:32:16.107Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/81/dc/7c42cc9c6cb01e8eb09961f1f738741d3e9c7e9d5c5b30ec69222625cd5f/mypy-2.1.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7406f4d048e71e576f5356d317e5b0a9e666dfd966bd99f9d14ca06e1a341538", size = 13994376, upload-time = "2026-05-11T18:32:39.256Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/d4/fa/285946c33bce716e082c11dfeee9ee196eaf1f5042efb3581a31f9f205e4/mypy-2.1.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e0210d626fc8b31ccc90233754c7bc90e1f43205e85d96387f7db1285b55c398", size = 14864618, upload-time = "2026-05-11T18:34:49.765Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/2b/83/82397f48af6c27e295d57979ded8490c9829040152cf7571b2f026aeb9a0/mypy-2.1.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:3712c20deed54e814eaaa825603bada8ea1c390670a397c95b98405347acc563", size = 15102063, upload-time = "2026-05-11T18:34:05.855Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/40/68/b02dec39057b88eb03dc0aa854732e26e8361f34f9d0e20c7614967d1eba/mypy-2.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:fcaa0e479066e31f7cceb6a3bea39cb22b2ff51a6b2f24f193d19179ba17c389", size = 11060564, upload-time = "2026-05-11T18:35:36.494Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/cf/a8/ea3dcbef31f99b634f2ee23bb0321cbc8c1b388b76a861eb849f13c347dc/mypy-2.1.0-cp311-cp311-win_arm64.whl", hash = "sha256:0b1a5260c95aa443083f9ed3592662941951bca3d4ca224a5dc517c38b7cf666", size = 9966983, upload-time = "2026-05-11T18:37:14.139Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/95/b1/55861beb5c339b44f9a2ba92df9e2cb1eeb4ae1eee674cdf7772c797778b/mypy-2.1.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:244358bf1c0da7722230bce60683d52e8e9fd030554926f15b747a84efb5b3af", size = 14874381, upload-time = "2026-05-11T18:37:31.784Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/0b/b3/b7f770114b7d0ac92d0f76e8d93c2780844a70488a90e91821927850da86/mypy-2.1.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:4ec7c57657493c7a75534df2751c8ae2cda383c16ecc55d2106c54476b1b16f6", size = 13665501, upload-time = "2026-05-11T18:34:23.063Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/b6/f3/8ae2037967e2126689a0c11d99e2b707134a565191e92c60ca2572aec60a/mypy-2.1.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d8161b6ff4392410023224f0969d17db93e1e154bc3e4ba62598e720723ae211", size = 14045750, upload-time = "2026-05-11T18:31:48.151Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/a0/32/615eb5911859e43d054941b0d0a7d06cfa2870eba86529cf385b052b111c/mypy-2.1.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bf03e12003084a67395184d3eb8cbd6a489dc3655b5664b28c210a9e2403ab0b", size = 15061630, upload-time = "2026-05-11T18:37:06.898Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/d4/03/4eafbfff8bfab1b87082741eae6e6a624028c984e6708b73bce2a8570c9d/mypy-2.1.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:20509760fd791c51579d573153407d226385ec1f8bcce55d730b354f3336bc22", size = 15288831, upload-time = "2026-05-11T18:31:18.07Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/99/ee/919661478e5891a3c96e549c036e467e64563ab85995b10c53c8358e16a3/mypy-2.1.0-cp312-cp312-win_amd64.whl", hash = "sha256:6753d0c1fdd6b1a23b9e4f283ce80b2153b724adcb2653b20b85a8a28ac6436b", size = 11135228, upload-time = "2026-05-11T18:34:31.23Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/24/0a/6a12b9782ca0831a553192f351679f4548abc9d19a7cc93bb7feb02084c7/mypy-2.1.0-cp312-cp312-win_arm64.whl", hash = "sha256:98ebb6589bb3b6d0c6f0c459d53ca55b8091fbc13d277c4041c885392e8195e8", size = 10040684, upload-time = "2026-05-11T18:36:48.199Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/6e/dd/c7191469c777f07689c032a8f7326e393ea34c92d6d76eb7ce5ba57ea66d/mypy-2.1.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:35aac3bb114e03888f535d5eb51b8bafbb3266586b599da1940f9b1be3ec5bd5", size = 14852174, upload-time = "2026-05-11T18:31:38.929Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/55/8c/aed55408879043d72bb9135f4d0d19a02b886dd569631e113e3d2706cb8d/mypy-2.1.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:8de55a8c861f2a49331f807be98d90caeceeef520bde13d43a160207f8af613e", size = 13651542, upload-time = "2026-05-11T18:36:04.636Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/3a/8e/f371a824b1f1fa8ea6e3dbb8703d232977d572be2329554a3bc4d960302f/mypy-2.1.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5fdf2941a07434af755837d9880f7d7d25f1dacb1af9dcd4b9b66f2220a3024e", size = 14033929, upload-time = "2026-05-11T18:35:55.742Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/94/21/f54be870d6dd53a82c674407e0f8eed7174b05ec78d42e5abd7b42e84fd5/mypy-2.1.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e195b817c13f02352a9c124301f9f30f078405444679b6753c1b96b6eed37285", size = 15039200, upload-time = "2026-05-11T18:33:10.281Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/17/99/bf21748626a40ce59fd29a39386ab46afec88b7bd2f0fa6c3a97c995523f/mypy-2.1.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:5431d42af987ebd92ba2f71d45c85ed41d8e6ca9f5fd209a69f68f707d2469e5", size = 15272690, upload-time = "2026-05-11T18:32:07.205Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/d6/d7/9e90d2cf47100bea550ed2bc7b0d4de3a62181d84d5e37da0003e8462637/mypy-2.1.0-cp313-cp313-win_amd64.whl", hash = "sha256:767fe8c66dc3e01e19e1737d4c38ebefead16125e1b8e58ad421903b376f5c65", size = 11147435, upload-time = "2026-05-11T18:33:56.477Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ec/46/e5c449e858798e35ffc90946282a27c62a77be743fe17480e4977374eb91/mypy-2.1.0-cp313-cp313-win_arm64.whl", hash = "sha256:ecfe70d43775ab99562ab128ce49854a362044c9f894961f68f898c23cb7429d", size = 10035052, upload-time = "2026-05-11T18:32:30.049Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/b0/ca/b279a672e874aedd5498ae25f722dacc8aa86bbffb939b3f97cbb1cf6686/mypy-2.1.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:7354c5a7f69d9345c3d6e69921d57088eea3ddeeb6b20d34c1b3855b02c36ec2", size = 14848422, upload-time = "2026-05-11T18:35:45.984Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/27/e6/3efe56c631d959b9b4454e208b0ac4b7f4f58b404c89f8bec7b49efdfc21/mypy-2.1.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:49890d4f76ac9e06ec117f9e09f3174da70a620a0c300953d8595c926e80947f", size = 13677374, upload-time = "2026-05-11T18:36:57.188Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/84/7f/8107ea87a44fd1f1b59882442f033c9c3488c127201b1d1d15f1cbd6022e/mypy-2.1.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:761be68e023ef5d94678772396a8af1220030f80837a3afd8d0aef3b419666f4", size = 14055743, upload-time = "2026-05-11T18:35:18.361Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/51/4d/b6d34db183133b83761b9199a82d31557cdbb70a380d8c3b3438e11882a3/mypy-2.1.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c90345fc182dc363b891350457ec69c35140858538f38b4540845afcc32b1aef", size = 15020937, upload-time = "2026-05-11T18:34:59.618Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ff/d7/f08360c691d758acb02f45022c34d98b92892f4ea756644e1000d4b9f3d8/mypy-2.1.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:b84802e7b5a6daf1f5e15bc9fcd7ddae77be13981ffab037f1c67bb84d67d135", size = 15253371, upload-time = "2026-05-11T18:36:41.081Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/67/1b/09460a13719530a19bce27bd3bc8449e83569dd2ba7faf51c9c3c30c0b61/mypy-2.1.0-cp314-cp314-win_amd64.whl", hash = "sha256:022c771234936ceac541ebaf836fe9e2abeb3f5e09aff21588fe543ff006fe21", size = 11326429, upload-time = "2026-05-11T18:34:13.526Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/40/62/75dbf0f82f7b6680340efc614af29dd0b3c17b8a4f1cd09b8bd2fd6bc814/mypy-2.1.0-cp314-cp314-win_arm64.whl", hash = "sha256:498207db725cec88829a6a5c2fc771205fd043719ef98bc49aba8fb9fc4e6d57", size = 10218799, upload-time = "2026-05-11T18:32:23.491Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/b2/66/caca04ed7d972fb6eb6dd1ccd6df1de5c38fae8c5b3dc1c4e8e0d85ee6b9/mypy-2.1.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:7d5e5cad0efeba72b93cd17490cc0d69c5ac9ca132994fe3fb0314808aeeb83e", size = 15923458, upload-time = "2026-05-11T18:35:28.64Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ed/52/2d90cbe49d014b13ed7ff337930c30bad35893fe38a1e4641e756bb62191/mypy-2.1.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:ff715050c127d724fd260a2e666e7747fdd83511c0c47d449d98238970aef780", size = 14757697, upload-time = "2026-05-11T18:36:14.208Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ac/37/d98f4a14e081b238992d0ed96b6d39c7cc0148c9699eb71eaa68629665ea/mypy-2.1.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:82208da9e09414d520e912d3e462d454854bed0810b71540bb016dcbca7308fd", size = 15405638, upload-time = "2026-05-11T18:33:48.249Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/a3/c2/15c46613b24a84fad2aea1248bf9619b99c2767ae9071fe224c179a0b7d4/mypy-2.1.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e79ebc1b904b84f0310dff7469655a9c36c7a68bddb37bdd42b67a332df61d08", size = 16215852, upload-time = "2026-05-11T18:32:50.296Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/5c/90/9c16a57f482c76d25f6379762b56bbf65c711d8158cf271fb2802cfb0640/mypy-2.1.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:e583edc957cfb0deb142079162ae826f58449b116c1d442f2d91c69d9fced081", size = 16452695, upload-time = "2026-05-11T18:33:38.182Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/0f/4c/215a4eeb63cacc5f17f516691ea7285d11e249802b942476bff15922a314/mypy-2.1.0-cp314-cp314t-win_amd64.whl", hash = "sha256:b33b6cd332695bba180d55e717a79d3038e479a2c49cc5eb3d53603409b9a5d7", size = 12866622, upload-time = "2026-05-11T18:34:39.945Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/4b/50/1043e1db5f455ffe4c9ab22747cd8ca2bc492b1e4f4e21b130a44ee2b217/mypy-2.1.0-cp314-cp314t-win_arm64.whl", hash = "sha256:4f910fe825376a7b66ef7ca8c98e5a149e8cd64c19ae71d84047a74ee060d4e6", size = 10610798, upload-time = "2026-05-11T18:36:31.444Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/0d/2a/13ca1f292f6db1b98ff495ef3467736b331621c5917cad984b7043e7348d/mypy-2.1.0-py3-none-any.whl", hash = "sha256:a663814603a5c563fb87a4f96fb473eeb30d1f5a4885afcf44f9db000a366289", size = 2693302, upload-time = "2026-05-11T18:31:29.246Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "mypy-extensions"
|
||||
version = "1.1.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/a2/6e/371856a3fb9d31ca8dac321cda606860fa4548858c0cc45d9d1d4ca2628b/mypy_extensions-1.1.0.tar.gz", hash = "sha256:52e68efc3284861e772bbcd66823fde5ae21fd2fdb51c62a211403730b916558", size = 6343, upload-time = "2025-04-22T14:54:24.164Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/79/7b/2c79738432f5c924bef5071f933bcc9efd0473bac3b4aa584a6f7c1c8df8/mypy_extensions-1.1.0-py3-none-any.whl", hash = "sha256:1be4cccdb0f2482337c4743e60421de3a356cd97508abadd57d47403e94f5505", size = 4963, upload-time = "2025-04-22T14:54:22.983Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "packaging"
|
||||
version = "26.2"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/d7/f1/e7a6dd94a8d4a5626c03e4e99c87f241ba9e350cd9e6d75123f992427270/packaging-26.2.tar.gz", hash = "sha256:ff452ff5a3e828ce110190feff1178bb1f2ea2281fa2075aadb987c2fb221661", size = 228134, upload-time = "2026-04-24T20:15:23.917Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/df/b2/87e62e8c3e2f4b32e5fe99e0b86d576da1312593b39f47d8ceef365e95ed/packaging-26.2-py3-none-any.whl", hash = "sha256:5fc45236b9446107ff2415ce77c807cee2862cb6fac22b8a73826d0693b0980e", size = 100195, upload-time = "2026-04-24T20:15:22.081Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pathspec"
|
||||
version = "1.1.1"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/5a/82/42f767fc1c1143d6fd36efb827202a2d997a375e160a71eb2888a925aac1/pathspec-1.1.1.tar.gz", hash = "sha256:17db5ecd524104a120e173814c90367a96a98d07c45b2e10c2f3919fff91bf5a", size = 135180, upload-time = "2026-04-27T01:46:08.907Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/f1/d9/7fb5aa316bc299258e68c73ba3bddbc499654a07f151cba08f6153988714/pathspec-1.1.1-py3-none-any.whl", hash = "sha256:a00ce642f577bf7f473932318056212bc4f8bfdf53128c78bbd5af0b9b20b189", size = 57328, upload-time = "2026-04-27T01:46:07.06Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pluggy"
|
||||
version = "1.6.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/f9/e2/3e91f31a7d2b083fe6ef3fa267035b518369d9511ffab804f839851d2779/pluggy-1.6.0.tar.gz", hash = "sha256:7dcc130b76258d33b90f61b658791dede3486c3e6bfb003ee5c9bfb396dd22f3", size = 69412, upload-time = "2025-05-15T12:30:07.975Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/54/20/4d324d65cc6d9205fabedc306948156824eb9f0ee1633355a8f7ec5c66bf/pluggy-1.6.0-py3-none-any.whl", hash = "sha256:e920276dd6813095e9377c0bc5566d94c932c33b27a3e3945d8389c374dd4746", size = 20538, upload-time = "2025-05-15T12:30:06.134Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pygments"
|
||||
version = "2.20.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/c3/b2/bc9c9196916376152d655522fdcebac55e66de6603a76a02bca1b6414f6c/pygments-2.20.0.tar.gz", hash = "sha256:6757cd03768053ff99f3039c1a36d6c0aa0b263438fcab17520b30a303a82b5f", size = 4955991, upload-time = "2026-03-29T13:29:33.898Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/f4/7e/a72dd26f3b0f4f2bf1dd8923c85f7ceb43172af56d63c7383eb62b332364/pygments-2.20.0-py3-none-any.whl", hash = "sha256:81a9e26dd42fd28a23a2d169d86d7ac03b46e2f8b59ed4698fb4785f946d0176", size = 1231151, upload-time = "2026-03-29T13:29:30.038Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pytest"
|
||||
version = "9.0.3"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "colorama", marker = "sys_platform == 'win32'" },
|
||||
{ name = "exceptiongroup", marker = "python_full_version < '3.11'" },
|
||||
{ name = "iniconfig" },
|
||||
{ name = "packaging" },
|
||||
{ name = "pluggy" },
|
||||
{ name = "pygments" },
|
||||
{ name = "tomli", marker = "python_full_version < '3.11'" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/7d/0d/549bd94f1a0a402dc8cf64563a117c0f3765662e2e668477624baeec44d5/pytest-9.0.3.tar.gz", hash = "sha256:b86ada508af81d19edeb213c681b1d48246c1a91d304c6c81a427674c17eb91c", size = 1572165, upload-time = "2026-04-07T17:16:18.027Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/d4/24/a372aaf5c9b7208e7112038812994107bc65a84cd00e0354a88c2c77a617/pytest-9.0.3-py3-none-any.whl", hash = "sha256:2c5efc453d45394fdd706ade797c0a81091eccd1d6e4bccfcd476e2b8e0ab5d9", size = 375249, upload-time = "2026-04-07T17:16:16.13Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pytest-asyncio"
|
||||
version = "1.3.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "backports-asyncio-runner", marker = "python_full_version < '3.11'" },
|
||||
{ name = "pytest" },
|
||||
{ name = "typing-extensions", marker = "python_full_version < '3.13'" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/90/2c/8af215c0f776415f3590cac4f9086ccefd6fd463befeae41cd4d3f193e5a/pytest_asyncio-1.3.0.tar.gz", hash = "sha256:d7f52f36d231b80ee124cd216ffb19369aa168fc10095013c6b014a34d3ee9e5", size = 50087, upload-time = "2025-11-10T16:07:47.256Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/e5/35/f8b19922b6a25bc0880171a2f1a003eaeb93657475193ab516fd87cac9da/pytest_asyncio-1.3.0-py3-none-any.whl", hash = "sha256:611e26147c7f77640e6d0a92a38ed17c3e9848063698d5c93d5aa7aa11cebff5", size = 15075, upload-time = "2025-11-10T16:07:45.537Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "respx"
|
||||
version = "0.23.1"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "httpx" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/43/98/4e55c9c486404ec12373708d015ebce157966965a5ebe7f28ff2c784d41b/respx-0.23.1.tar.gz", hash = "sha256:242dcc6ce6b5b9bf621f5870c82a63997e8e82bc7c947f9ffe272b8f3dd5a780", size = 29243, upload-time = "2026-04-08T14:37:16.008Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/1d/4a/221da6ca167db45693d8d26c7dc79ccfc978a440251bf6721c9aaf251ac0/respx-0.23.1-py2.py3-none-any.whl", hash = "sha256:b18004b029935384bccfa6d7d9d74b4ec9af73a081cc28600fffc0447f4b8c1a", size = 25557, upload-time = "2026-04-08T14:37:14.613Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ruff"
|
||||
version = "0.15.12"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/99/43/3291f1cc9106f4c63bdce7a8d0df5047fe8422a75b091c16b5e9355e0b11/ruff-0.15.12.tar.gz", hash = "sha256:ecea26adb26b4232c0c2ca19ccbc0083a68344180bba2a600605538ce51a40a6", size = 4643852, upload-time = "2026-04-24T18:17:14.305Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/c3/6e/e78ffb61d4686f3d96ba3df2c801161843746dcbcbb17a1e927d4829312b/ruff-0.15.12-py3-none-linux_armv6l.whl", hash = "sha256:f86f176e188e94d6bdbc09f09bfd9dc729059ad93d0e7390b5a73efe19f8861c", size = 10640713, upload-time = "2026-04-24T18:17:22.841Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ae/08/a317bc231fb9e7b93e4ef3089501e51922ff88d6936ce5cf870c4fe55419/ruff-0.15.12-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:e3bcd123364c3770b8e1b7baaf343cc99a35f197c5c6e8af79015c666c423a6c", size = 11069267, upload-time = "2026-04-24T18:17:30.105Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/aa/a4/f828e9718d3dce1f5f11c39c4f65afd32783c8b2aebb2e3d259e492c47bd/ruff-0.15.12-py3-none-macosx_11_0_arm64.whl", hash = "sha256:fe87510d000220aa1ed530d4448a7c696a0cae1213e5ec30e5874287b66557b5", size = 10397182, upload-time = "2026-04-24T18:17:07.177Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/71/e0/3310fc6d1b5e1fdea22bf3b1b807c7e187b581021b0d7d4514cccdb5fb71/ruff-0.15.12-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:84a1630093121375a3e2a95b4a6dc7b59e2b4ee76216e32d81aae550a832d002", size = 10758012, upload-time = "2026-04-24T18:16:55.759Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/11/c1/a606911aee04c324ddaa883ae418f3569792fd3c4a10c50e0dd0a2311e1e/ruff-0.15.12-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:fb129f40f114f089ebe0ca56c0d251cf2061b17651d464bb6478dc01e69f11f5", size = 10447479, upload-time = "2026-04-24T18:16:51.677Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/9d/68/4201e8444f0894f21ab4aeeaee68aa4f10b51613514a20d80bd628d57e88/ruff-0.15.12-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b0c862b172d695db7598426b8af465e7e9ac00a3ea2a3630ee67eb82e366aaa6", size = 11234040, upload-time = "2026-04-24T18:17:16.529Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/34/ff/8a6d6cf4ccc23fd67060874e832c18919d1557a0611ebef03fdb01fff11e/ruff-0.15.12-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2849ea9f3484c3aca43a82f484210370319e7170df4dfe4843395ddf6c57bc33", size = 12087377, upload-time = "2026-04-24T18:17:04.944Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/85/f6/c669cf73f5152f623d34e69866a46d5e6185816b19fcd5b6dd8a2d299922/ruff-0.15.12-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9e77c7e51c07fe396826d5969a5b846d9cd4c402535835fb6e21ce8b28fef847", size = 11367784, upload-time = "2026-04-24T18:17:25.409Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/e8/39/c61d193b8a1daaa8977f7dea9e8d8ba866e02ea7b65d32f6861693aa4c12/ruff-0.15.12-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:83b2f4f2f3b1026b5fb449b467d9264bf22067b600f7b6f41fc5958909f449d0", size = 11344088, upload-time = "2026-04-24T18:17:12.258Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/c2/8d/49afab3645e31e12c590acb6d3b5b69d7aab5b81926dbaf7461f9441f37a/ruff-0.15.12-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:9ba3b8f1afd7e2e43d8943e55f249e13f9682fde09711644a6e7290eb4f3e339", size = 11271770, upload-time = "2026-04-24T18:17:02.457Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/46/06/33f41fe94403e2b755481cdfb9b7ef3e4e0ed031c4581124658d935d52b4/ruff-0.15.12-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:e852ba9fdc890655e1d78f2df1499efbe0e54126bd405362154a75e2bde159c5", size = 10719355, upload-time = "2026-04-24T18:17:27.648Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/0d/59/18aa4e014debbf559670e4048e39260a85c7fcee84acfd761ac01e7b8d35/ruff-0.15.12-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:dd8aed930da53780d22fc70bdf84452c843cf64f8cb4eb38984319c24c5cd5fd", size = 10462758, upload-time = "2026-04-24T18:17:32.347Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/25/e7/cc9f16fd0f3b5fddcbd7ec3d6ae30c8f3fde1047f32a4093a98d633c6570/ruff-0.15.12-py3-none-musllinux_1_2_i686.whl", hash = "sha256:01da3988d225628b709493d7dc67c3b9b12c0210016b08690ef9bd27970b262b", size = 10953498, upload-time = "2026-04-24T18:17:20.674Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/72/7a/a9ba7f98c7a575978698f4230c5e8cc54bbc761af34f560818f933dafa0c/ruff-0.15.12-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:9cae0f92bd5700d1213188b31cd3bdd2b315361296d10b96b8e2337d3d11f53e", size = 11447765, upload-time = "2026-04-24T18:17:09.755Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ea/f9/0ae446942c846b8266059ad8a30702a35afae55f5cdc54c5adf8d7afdc27/ruff-0.15.12-py3-none-win32.whl", hash = "sha256:d0185894e038d7043ba8fd6aee7499ece6462dc0ea9f1e260c7451807c714c20", size = 10657277, upload-time = "2026-04-24T18:17:18.591Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/33/f1/9614e03e1cdcbf9437570b5400ced8a720b5db22b28d8e0f1bda429f660d/ruff-0.15.12-py3-none-win_amd64.whl", hash = "sha256:c87a162d61ab3adca47c03f7f717c68672edec7d1b5499e652331780fe74950d", size = 11837758, upload-time = "2026-04-24T18:17:00.113Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/c0/98/6beb4b351e472e5f4c4613f7c35a5290b8be2497e183825310c4c3a3984b/ruff-0.15.12-py3-none-win_arm64.whl", hash = "sha256:a538f7a82d061cee7be55542aca1d86d1393d55d81d4fcc314370f4340930d4f", size = 11120821, upload-time = "2026-04-24T18:16:57.979Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tomli"
|
||||
version = "2.4.1"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/22/de/48c59722572767841493b26183a0d1cc411d54fd759c5607c4590b6563a6/tomli-2.4.1.tar.gz", hash = "sha256:7c7e1a961a0b2f2472c1ac5b69affa0ae1132c39adcb67aba98568702b9cc23f", size = 17543, upload-time = "2026-03-25T20:22:03.828Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/f4/11/db3d5885d8528263d8adc260bb2d28ebf1270b96e98f0e0268d32b8d9900/tomli-2.4.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:f8f0fc26ec2cc2b965b7a3b87cd19c5c6b8c5e5f436b984e85f486d652285c30", size = 154704, upload-time = "2026-03-25T20:21:10.473Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/6d/f7/675db52c7e46064a9aa928885a9b20f4124ecb9bc2e1ce74c9106648d202/tomli-2.4.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:4ab97e64ccda8756376892c53a72bd1f964e519c77236368527f758fbc36a53a", size = 149454, upload-time = "2026-03-25T20:21:12.036Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/61/71/81c50943cf953efa35bce7646caab3cf457a7d8c030b27cfb40d7235f9ee/tomli-2.4.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:96481a5786729fd470164b47cdb3e0e58062a496f455ee41b4403be77cb5a076", size = 237561, upload-time = "2026-03-25T20:21:13.098Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/48/c1/f41d9cb618acccca7df82aaf682f9b49013c9397212cb9f53219e3abac37/tomli-2.4.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5a881ab208c0baf688221f8cecc5401bd291d67e38a1ac884d6736cbcd8247e9", size = 243824, upload-time = "2026-03-25T20:21:14.569Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/22/e4/5a816ecdd1f8ca51fb756ef684b90f2780afc52fc67f987e3c61d800a46d/tomli-2.4.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:47149d5bd38761ac8be13a84864bf0b7b70bc051806bc3669ab1cbc56216b23c", size = 242227, upload-time = "2026-03-25T20:21:15.712Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/6b/49/2b2a0ef529aa6eec245d25f0c703e020a73955ad7edf73e7f54ddc608aa5/tomli-2.4.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:ec9bfaf3ad2df51ace80688143a6a4ebc09a248f6ff781a9945e51937008fcbc", size = 247859, upload-time = "2026-03-25T20:21:17.001Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/83/bd/6c1a630eaca337e1e78c5903104f831bda934c426f9231429396ce3c3467/tomli-2.4.1-cp311-cp311-win32.whl", hash = "sha256:ff2983983d34813c1aeb0fa89091e76c3a22889ee83ab27c5eeb45100560c049", size = 97204, upload-time = "2026-03-25T20:21:18.079Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/42/59/71461df1a885647e10b6bb7802d0b8e66480c61f3f43079e0dcd315b3954/tomli-2.4.1-cp311-cp311-win_amd64.whl", hash = "sha256:5ee18d9ebdb417e384b58fe414e8d6af9f4e7a0ae761519fb50f721de398dd4e", size = 108084, upload-time = "2026-03-25T20:21:18.978Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/b8/83/dceca96142499c069475b790e7913b1044c1a4337e700751f48ed723f883/tomli-2.4.1-cp311-cp311-win_arm64.whl", hash = "sha256:c2541745709bad0264b7d4705ad453b76ccd191e64aa6f0fc66b69a293a45ece", size = 95285, upload-time = "2026-03-25T20:21:20.309Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/c1/ba/42f134a3fe2b370f555f44b1d72feebb94debcab01676bf918d0cb70e9aa/tomli-2.4.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:c742f741d58a28940ce01d58f0ab2ea3ced8b12402f162f4d534dfe18ba1cd6a", size = 155924, upload-time = "2026-03-25T20:21:21.626Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/dc/c7/62d7a17c26487ade21c5422b646110f2162f1fcc95980ef7f63e73c68f14/tomli-2.4.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:7f86fd587c4ed9dd76f318225e7d9b29cfc5a9d43de44e5754db8d1128487085", size = 150018, upload-time = "2026-03-25T20:21:23.002Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/5c/05/79d13d7c15f13bdef410bdd49a6485b1c37d28968314eabee452c22a7fda/tomli-2.4.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ff18e6a727ee0ab0388507b89d1bc6a22b138d1e2fa56d1ad494586d61d2eae9", size = 244948, upload-time = "2026-03-25T20:21:24.04Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/10/90/d62ce007a1c80d0b2c93e02cab211224756240884751b94ca72df8a875ca/tomli-2.4.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:136443dbd7e1dee43c68ac2694fde36b2849865fa258d39bf822c10e8068eac5", size = 253341, upload-time = "2026-03-25T20:21:25.177Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/1a/7e/caf6496d60152ad4ed09282c1885cca4eea150bfd007da84aea07bcc0a3e/tomli-2.4.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:5e262d41726bc187e69af7825504c933b6794dc3fbd5945e41a79bb14c31f585", size = 248159, upload-time = "2026-03-25T20:21:26.364Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/99/e7/c6f69c3120de34bbd882c6fba7975f3d7a746e9218e56ab46a1bc4b42552/tomli-2.4.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:5cb41aa38891e073ee49d55fbc7839cfdb2bc0e600add13874d048c94aadddd1", size = 253290, upload-time = "2026-03-25T20:21:27.46Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/d6/2f/4a3c322f22c5c66c4b836ec58211641a4067364f5dcdd7b974b4c5da300c/tomli-2.4.1-cp312-cp312-win32.whl", hash = "sha256:da25dc3563bff5965356133435b757a795a17b17d01dbc0f42fb32447ddfd917", size = 98141, upload-time = "2026-03-25T20:21:28.492Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/24/22/4daacd05391b92c55759d55eaee21e1dfaea86ce5c571f10083360adf534/tomli-2.4.1-cp312-cp312-win_amd64.whl", hash = "sha256:52c8ef851d9a240f11a88c003eacb03c31fc1c9c4ec64a99a0f922b93874fda9", size = 108847, upload-time = "2026-03-25T20:21:29.386Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/68/fd/70e768887666ddd9e9f5d85129e84910f2db2796f9096aa02b721a53098d/tomli-2.4.1-cp312-cp312-win_arm64.whl", hash = "sha256:f758f1b9299d059cc3f6546ae2af89670cb1c4d48ea29c3cacc4fe7de3058257", size = 95088, upload-time = "2026-03-25T20:21:30.677Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/07/06/b823a7e818c756d9a7123ba2cda7d07bc2dd32835648d1a7b7b7a05d848d/tomli-2.4.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:36d2bd2ad5fb9eaddba5226aa02c8ec3fa4f192631e347b3ed28186d43be6b54", size = 155866, upload-time = "2026-03-25T20:21:31.65Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/14/6f/12645cf7f08e1a20c7eb8c297c6f11d31c1b50f316a7e7e1e1de6e2e7b7e/tomli-2.4.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:eb0dc4e38e6a1fd579e5d50369aa2e10acfc9cace504579b2faabb478e76941a", size = 149887, upload-time = "2026-03-25T20:21:33.028Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/5c/e0/90637574e5e7212c09099c67ad349b04ec4d6020324539297b634a0192b0/tomli-2.4.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c7f2c7f2b9ca6bdeef8f0fa897f8e05085923eb091721675170254cbc5b02897", size = 243704, upload-time = "2026-03-25T20:21:34.51Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/10/8f/d3ddb16c5a4befdf31a23307f72828686ab2096f068eaf56631e136c1fdd/tomli-2.4.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f3c6818a1a86dd6dca7ddcaaf76947d5ba31aecc28cb1b67009a5877c9a64f3f", size = 251628, upload-time = "2026-03-25T20:21:36.012Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/e3/f1/dbeeb9116715abee2485bf0a12d07a8f31af94d71608c171c45f64c0469d/tomli-2.4.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:d312ef37c91508b0ab2cee7da26ec0b3ed2f03ce12bd87a588d771ae15dcf82d", size = 247180, upload-time = "2026-03-25T20:21:37.136Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/d3/74/16336ffd19ed4da28a70959f92f506233bd7cfc2332b20bdb01591e8b1d1/tomli-2.4.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:51529d40e3ca50046d7606fa99ce3956a617f9b36380da3b7f0dd3dd28e68cb5", size = 251674, upload-time = "2026-03-25T20:21:38.298Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/16/f9/229fa3434c590ddf6c0aa9af64d3af4b752540686cace29e6281e3458469/tomli-2.4.1-cp313-cp313-win32.whl", hash = "sha256:2190f2e9dd7508d2a90ded5ed369255980a1bcdd58e52f7fe24b8162bf9fedbd", size = 97976, upload-time = "2026-03-25T20:21:39.316Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/6a/1e/71dfd96bcc1c775420cb8befe7a9d35f2e5b1309798f009dca17b7708c1e/tomli-2.4.1-cp313-cp313-win_amd64.whl", hash = "sha256:8d65a2fbf9d2f8352685bc1364177ee3923d6baf5e7f43ea4959d7d8bc326a36", size = 108755, upload-time = "2026-03-25T20:21:40.248Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/83/7a/d34f422a021d62420b78f5c538e5b102f62bea616d1d75a13f0a88acb04a/tomli-2.4.1-cp313-cp313-win_arm64.whl", hash = "sha256:4b605484e43cdc43f0954ddae319fb75f04cc10dd80d830540060ee7cd0243cd", size = 95265, upload-time = "2026-03-25T20:21:41.219Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/3c/fb/9a5c8d27dbab540869f7c1f8eb0abb3244189ce780ba9cd73f3770662072/tomli-2.4.1-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:fd0409a3653af6c147209d267a0e4243f0ae46b011aa978b1080359fddc9b6cf", size = 155726, upload-time = "2026-03-25T20:21:42.23Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/62/05/d2f816630cc771ad836af54f5001f47a6f611d2d39535364f148b6a92d6b/tomli-2.4.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:a120733b01c45e9a0c34aeef92bf0cf1d56cfe81ed9d47d562f9ed591a9828ac", size = 149859, upload-time = "2026-03-25T20:21:43.386Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ce/48/66341bdb858ad9bd0ceab5a86f90eddab127cf8b046418009f2125630ecb/tomli-2.4.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:559db847dc486944896521f68d8190be1c9e719fced785720d2216fe7022b662", size = 244713, upload-time = "2026-03-25T20:21:44.474Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/df/6d/c5fad00d82b3c7a3ab6189bd4b10e60466f22cfe8a08a9394185c8a8111c/tomli-2.4.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:01f520d4f53ef97964a240a035ec2a869fe1a37dde002b57ebc4417a27ccd853", size = 252084, upload-time = "2026-03-25T20:21:45.62Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/00/71/3a69e86f3eafe8c7a59d008d245888051005bd657760e96d5fbfb0b740c2/tomli-2.4.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:7f94b27a62cfad8496c8d2513e1a222dd446f095fca8987fceef261225538a15", size = 247973, upload-time = "2026-03-25T20:21:46.937Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/67/50/361e986652847fec4bd5e4a0208752fbe64689c603c7ae5ea7cb16b1c0ca/tomli-2.4.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:ede3e6487c5ef5d28634ba3f31f989030ad6af71edfb0055cbbd14189ff240ba", size = 256223, upload-time = "2026-03-25T20:21:48.467Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/8c/9a/b4173689a9203472e5467217e0154b00e260621caa227b6fa01feab16998/tomli-2.4.1-cp314-cp314-win32.whl", hash = "sha256:3d48a93ee1c9b79c04bb38772ee1b64dcf18ff43085896ea460ca8dec96f35f6", size = 98973, upload-time = "2026-03-25T20:21:49.526Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/14/58/640ac93bf230cd27d002462c9af0d837779f8773bc03dee06b5835208214/tomli-2.4.1-cp314-cp314-win_amd64.whl", hash = "sha256:88dceee75c2c63af144e456745e10101eb67361050196b0b6af5d717254dddf7", size = 109082, upload-time = "2026-03-25T20:21:50.506Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/d5/2f/702d5e05b227401c1068f0d386d79a589bb12bf64c3d2c72ce0631e3bc49/tomli-2.4.1-cp314-cp314-win_arm64.whl", hash = "sha256:b8c198f8c1805dc42708689ed6864951fd2494f924149d3e4bce7710f8eb5232", size = 96490, upload-time = "2026-03-25T20:21:51.474Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/45/4b/b877b05c8ba62927d9865dd980e34a755de541eb65fffba52b4cc495d4d2/tomli-2.4.1-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:d4d8fe59808a54658fcc0160ecfb1b30f9089906c50b23bcb4c69eddc19ec2b4", size = 164263, upload-time = "2026-03-25T20:21:52.543Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/24/79/6ab420d37a270b89f7195dec5448f79400d9e9c1826df982f3f8e97b24fd/tomli-2.4.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:7008df2e7655c495dd12d2a4ad038ff878d4ca4b81fccaf82b714e07eae4402c", size = 160736, upload-time = "2026-03-25T20:21:53.674Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/02/e0/3630057d8eb170310785723ed5adcdfb7d50cb7e6455f85ba8a3deed642b/tomli-2.4.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1d8591993e228b0c930c4bb0db464bdad97b3289fb981255d6c9a41aedc84b2d", size = 270717, upload-time = "2026-03-25T20:21:55.129Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/7a/b4/1613716072e544d1a7891f548d8f9ec6ce2faf42ca65acae01d76ea06bb0/tomli-2.4.1-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:734e20b57ba95624ecf1841e72b53f6e186355e216e5412de414e3c51e5e3c41", size = 278461, upload-time = "2026-03-25T20:21:56.228Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/05/38/30f541baf6a3f6df77b3df16b01ba319221389e2da59427e221ef417ac0c/tomli-2.4.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:8a650c2dbafa08d42e51ba0b62740dae4ecb9338eefa093aa5c78ceb546fcd5c", size = 274855, upload-time = "2026-03-25T20:21:57.653Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/77/a3/ec9dd4fd2c38e98de34223b995a3b34813e6bdadf86c75314c928350ed14/tomli-2.4.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:504aa796fe0569bb43171066009ead363de03675276d2d121ac1a4572397870f", size = 283144, upload-time = "2026-03-25T20:21:59.089Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ef/be/605a6261cac79fba2ec0c9827e986e00323a1945700969b8ee0b30d85453/tomli-2.4.1-cp314-cp314t-win32.whl", hash = "sha256:b1d22e6e9387bf4739fbe23bfa80e93f6b0373a7f1b96c6227c32bef95a4d7a8", size = 108683, upload-time = "2026-03-25T20:22:00.214Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/12/64/da524626d3b9cc40c168a13da8335fe1c51be12c0a63685cc6db7308daae/tomli-2.4.1-cp314-cp314t-win_amd64.whl", hash = "sha256:2c1c351919aca02858f740c6d33adea0c5deea37f9ecca1cc1ef9e884a619d26", size = 121196, upload-time = "2026-03-25T20:22:01.169Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/5a/cd/e80b62269fc78fc36c9af5a6b89c835baa8af28ff5ad28c7028d60860320/tomli-2.4.1-cp314-cp314t-win_arm64.whl", hash = "sha256:eab21f45c7f66c13f2a9e0e1535309cee140182a9cdae1e041d02e47291e8396", size = 100393, upload-time = "2026-03-25T20:22:02.137Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/7b/61/cceae43728b7de99d9b847560c262873a1f6c98202171fd5ed62640b494b/tomli-2.4.1-py3-none-any.whl", hash = "sha256:0d85819802132122da43cb86656f8d1f8c6587d54ae7dcaf30e90533028b49fe", size = 14583, upload-time = "2026-03-25T20:22:03.012Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "typing-extensions"
|
||||
version = "4.15.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/72/94/1a15dd82efb362ac84269196e94cf00f187f7ed21c242792a923cdb1c61f/typing_extensions-4.15.0.tar.gz", hash = "sha256:0cea48d173cc12fa28ecabc3b837ea3cf6f38c6d1136f85cbaaf598984861466", size = 109391, upload-time = "2025-08-25T13:49:26.313Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/18/67/36e9267722cc04a6b9f15c7f3441c2363321a3ea07da7ae0c0707beb2a9c/typing_extensions-4.15.0-py3-none-any.whl", hash = "sha256:f0fa19c6845758ab08074a0cfa8b7aecb71c999ca73d62883bc25cc018c4e548", size = 44614, upload-time = "2025-08-25T13:49:24.86Z" },
|
||||
]
|
||||
6980
gaiia/workflows_backup_2026-07-10_125815.json
Normal file
6980
gaiia/workflows_backup_2026-07-10_125815.json
Normal file
File diff suppressed because one or more lines are too long
|
|
@ -1 +0,0 @@
|
|||
Subproject commit 33ea23a718ee57a8319e292514ef76ce107a3527
|
||||
|
|
@ -1,119 +0,0 @@
|
|||
# =============================================================================
|
||||
# 494 — IPv6 apply script (ROS 7.21.4, BGP-only / no OSPFv3)
|
||||
# =============================================================================
|
||||
#
|
||||
# 494 is a leaf tower. Single backbone P2P:
|
||||
# * climax (north) — ether2-climax (AF11), v6 P2P 2606:1c80:0:17::/64 (494 .2)
|
||||
# Default upstream toward climax via :17::1 — climax forwards to core.
|
||||
#
|
||||
# Loopback: 2606:1c80:0:10::111/128
|
||||
# /44 customer aggregate: 2606:1c80:1A00::/44
|
||||
# Mgmt LAN: 2606:1c80:1A00::1/64 (assumes `mgmt` bridge — verify name first).
|
||||
# PPPoE profile assumed `pppoe`. Customer interface-list assumed `customer`.
|
||||
# =============================================================================
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# WIPE
|
||||
# -----------------------------------------------------------------------------
|
||||
/ppp profile set [find] remote-ipv6-prefix-pool="" dhcpv6-pd-pool=""
|
||||
/ipv6 dhcp-server remove [find]
|
||||
/ipv6 dhcp-server option remove [find]
|
||||
/ipv6 dhcp-client remove [find]
|
||||
/ipv6 nd prefix remove [find]
|
||||
/ipv6 nd remove [find default=no]
|
||||
/ipv6 pool remove [find]
|
||||
/ipv6 route remove [find dynamic=no]
|
||||
/ipv6 firewall filter remove [find]
|
||||
/ipv6 firewall mangle remove [find]
|
||||
/ipv6 firewall raw remove [find]
|
||||
/ipv6 address remove [find dynamic=no]
|
||||
|
||||
/routing ospf interface-template remove [find area=backbone-v3]
|
||||
/routing ospf area remove [find name=backbone-v3]
|
||||
/routing ospf instance remove [find name=default-v3]
|
||||
|
||||
/routing bgp connection remove [find name~"ibgp" and (afi~"ipv6" or address-families~"ipv6")]
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# APPLY — BGP instance
|
||||
# -----------------------------------------------------------------------------
|
||||
/routing bgp template set [find name=default] disabled=no
|
||||
|
||||
/routing bgp instance
|
||||
add name=bgp-instance-1 as=393837 router-id=10.254.254.111
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# APPLY — addresses
|
||||
# -----------------------------------------------------------------------------
|
||||
/ipv6 settings set forward=yes accept-router-advertisements=no
|
||||
|
||||
/ipv6 address
|
||||
add address=2606:1c80:0:10::111/128 interface=lo advertise=no comment="loopback"
|
||||
add address=2606:1c80:0:17::2/64 interface=ether2-climax advertise=no comment="P2P 494<->climax"
|
||||
add address=2606:1c80:1A00::1/64 interface=management advertise=yes comment="494 mgmt LAN"
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# APPLY — static routes
|
||||
# -----------------------------------------------------------------------------
|
||||
/ipv6 route
|
||||
add dst-address=::/0 gateway=2606:1c80:0:17::1 distance=1 comment="default via climax"
|
||||
add dst-address=2606:1c80:0:10::253/128 gateway=2606:1c80:0:17::1 distance=1 comment="core loopback (iBGP bootstrap, via climax)"
|
||||
add dst-address=2606:1c80:0:10::254/128 gateway=2606:1c80:0:17::1 distance=1 comment="edge loopback (via climax)"
|
||||
add dst-address=2606:1c80:1A00::/44 blackhole distance=200 comment="494 /44 aggregate (iBGP origin)"
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# APPLY — neighbour discovery on the mgmt LAN
|
||||
# -----------------------------------------------------------------------------
|
||||
/ipv6 nd
|
||||
add interface=management ra-interval=3m20s-10m managed-address-configuration=no other-configuration=yes advertise-dns=yes
|
||||
|
||||
/ipv6 nd prefix
|
||||
add interface=management prefix=2606:1c80:1A00::/64 autonomous=yes on-link=yes
|
||||
|
||||
/ipv6 dhcp-server
|
||||
add interface=management name=494-mgmt-stateless address-pool=static-only
|
||||
|
||||
/ipv6 dhcp-server option
|
||||
add name=v6-dns code=23 value="'2606:4700:4700::1111''2606:4700:4700::1001'"
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# APPLY — customer PD pool + bind to PPPoE profile
|
||||
# -----------------------------------------------------------------------------
|
||||
/ipv6 pool
|
||||
add name=494-cust-pd-1 prefix=2606:1c80:1A01::/48 prefix-length=56
|
||||
|
||||
/ppp profile
|
||||
set [find name=494] remote-ipv6-prefix-pool=494-cust-pd-1 dhcpv6-pd-pool=494-cust-pd-1
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# APPLY — firewall
|
||||
# -----------------------------------------------------------------------------
|
||||
/ipv6 firewall filter
|
||||
add chain=input action=accept connection-state=established,related comment="established"
|
||||
add chain=input action=drop connection-state=invalid comment="invalid"
|
||||
add chain=input action=accept protocol=icmpv6 comment="icmpv6"
|
||||
add chain=input action=accept src-address=fe80::/10 comment="link-local"
|
||||
add chain=input action=accept src-address=2606:1c80::/32 comment="internal"
|
||||
add chain=input action=drop comment="default deny"
|
||||
|
||||
add chain=forward action=accept connection-state=established,related comment="established"
|
||||
add chain=forward action=drop connection-state=invalid comment="invalid"
|
||||
add chain=forward action=accept protocol=icmpv6 hop-limit=equal:1 comment="PMTUD"
|
||||
add chain=forward action=accept protocol=icmpv6 comment="icmpv6"
|
||||
add chain=forward action=accept src-address=2606:1c80::/32 dst-address=2606:1c80::/32 comment="internal-to-internal"
|
||||
add chain=forward action=accept src-address=2606:1c80:1A00::/44 comment="v6 outbound from 494 customers"
|
||||
add chain=forward action=drop comment="default deny"
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# APPLY — iBGP to core (v6 RR)
|
||||
# -----------------------------------------------------------------------------
|
||||
/routing filter rule
|
||||
add chain=v6-ibgp-out disabled=no rule="if (dst==2606:1c80:1A00::/44 || dst==2606:1c80:0:10::111/128 || (dst-len==64 && dst==2606:1c80:0:17::/64)) { accept; } else { reject; }"
|
||||
|
||||
/routing bgp connection
|
||||
add name=ibgp-rr-core instance=bgp-instance-1 templates=default \
|
||||
afi=ip,ipv6 local.role=ibgp local.address=2606:1c80:0:10::111 \
|
||||
remote.address=2606:1c80:0:10::253 remote.as=393837 multihop=yes \
|
||||
nexthop-choice=force-self output.filter-chain=v6-ibgp-out \
|
||||
output.redistribute=connected,static \
|
||||
comment="iBGP to core (v6 RR), v6 transport"
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
# 2026-05-09 15:08:05 by RouterOS 7.21.4
|
||||
# 2026-07-10 10:54:21 by RouterOS 7.21.4
|
||||
# software id = K4QG-8NQV
|
||||
#
|
||||
# model = RB5009UG+S+
|
||||
|
|
@ -218,15 +218,15 @@ add add-arp=yes address-pool=mgmt interface=management lease-script="{\
|
|||
/ip smb users
|
||||
set [ find default=yes ] disabled=yes
|
||||
/ipv6 dhcp-server option
|
||||
add code=23 name=v6-dns value="'2606:4700:4700::1111''2606:4700:4700::1001'"
|
||||
add code=23 name=v6-dns
|
||||
/ipv6 pool
|
||||
add name=494-cust-pd-1 prefix=2606:1c80:1a01::/48 prefix-length=56
|
||||
/ppp profile
|
||||
set *0 dns-server=204.110.191.240,204.110.191.250 local-address=\
|
||||
204.110.188.94 remote-address=public use-upnp=no
|
||||
add change-tcp-mss=yes dhcpv6-pd-pool=494-cust-pd-1 dns-server=\
|
||||
204.110.191.240,204.110.191.20 local-address=100.64.175.254 name=494 \
|
||||
on-down="{\
|
||||
204.110.191.240,204.110.191.250 idle-timeout=1h local-address=\
|
||||
100.64.175.254 name=494 on-down="{\
|
||||
\n:local url \"https://xtjlpnubrg.execute-api.us-east-1.amazonaws.com/prod\
|
||||
uction/on-start-workflow-execution/6f19a48f-04f7-40c1-a4e0-8cb66df99213/0a\
|
||||
7f4443-fc8a-4fe0-807c-f535d2aa1865\"\
|
||||
|
|
@ -334,8 +334,9 @@ add change-tcp-mss=yes dhcpv6-pd-pool=494-cust-pd-1 dns-server=\
|
|||
\n }\
|
||||
\n} while ( \$attempts < \$max )\
|
||||
\n}\
|
||||
\n" remote-address=cgnat remote-ipv6-prefix-pool=494-cust-pd-1 use-upnp=\
|
||||
no
|
||||
\n" only-one=yes remote-address=cgnat remote-ipv6-prefix-pool=\
|
||||
494-cust-pd-1 use-upnp=no
|
||||
set *FFFFFFFE dns-server=204.110.191.240,204.110.191.250
|
||||
/queue type
|
||||
add kind=fq-codel name=fq-codel
|
||||
/queue interface
|
||||
|
|
@ -355,10 +356,10 @@ add as=393837 name=bgp-instance-1 router-id=10.254.254.111
|
|||
add disabled=no id=10.254.254.111 name=id-1 select-dynamic-id=any \
|
||||
select-from-vrf=main
|
||||
/routing ospf instance
|
||||
add disabled=no in-filter-chain=ospf-in name=ospf-instance-1 \
|
||||
originate-default=never out-filter-chain=ospf-out router-id=id-1
|
||||
add in-filter-chain=ospf-in name=default-v2 originate-default=never \
|
||||
out-filter-chain=ospf-out redistribute=connected router-id=id-1
|
||||
/routing ospf area
|
||||
add disabled=no instance=ospf-instance-1 name=ospf-area-1
|
||||
add instance=default-v2 name=backbone-v2
|
||||
/snmp community
|
||||
set [ find default=yes ] addresses=204.110.188.0/22,10.0.0.0/8 name=\
|
||||
kdyyJrT0Mm
|
||||
|
|
@ -378,23 +379,19 @@ add bridge=management interface=vlan10_ether7 internal-path-cost=10 \
|
|||
add bridge=management interface=vlan10_ether8 internal-path-cost=10 \
|
||||
path-cost=10
|
||||
/ip firewall connection tracking
|
||||
# ipv6 *accept router advertisements* configuration has changed, please restart device to apply settings
|
||||
set icmp-timeout=30s tcp-close-wait-timeout=1m tcp-established-timeout=4h \
|
||||
tcp-fin-wait-timeout=2m tcp-last-ack-timeout=30s \
|
||||
tcp-syn-received-timeout=1m tcp-syn-sent-timeout=2m \
|
||||
tcp-time-wait-timeout=2m udp-stream-timeout=2m
|
||||
/ip neighbor discovery-settings
|
||||
# ipv6 *accept router advertisements* configuration has changed, please restart device to apply settings
|
||||
set discover-interface-list=!dynamic
|
||||
/ip settings
|
||||
# ipv6 *accept router advertisements* configuration has changed, please restart device to apply settings
|
||||
set tcp-syncookies=yes
|
||||
/ipv6 settings
|
||||
# ipv6 *accept router advertisements* configuration has changed, please restart device to apply settings
|
||||
set accept-router-advertisements=no
|
||||
/interface pppoe-server server
|
||||
add default-profile=494 disabled=no interface=494 max-mru=1492 max-mtu=1492 \
|
||||
one-session-per-host=yes service-name=494
|
||||
add authentication=mschap2 default-profile=494 disabled=no interface=494 \
|
||||
max-mru=1492 max-mtu=1492 one-session-per-host=yes service-name=494
|
||||
/ip address
|
||||
add address=10.254.254.111 comment=Loopback interface=lo network=\
|
||||
10.254.254.111
|
||||
|
|
@ -407,22 +404,35 @@ add address=100.64.175.254/20 interface=494 network=100.64.160.0
|
|||
add interface=ether1
|
||||
# Interface not active
|
||||
add interface=ether3
|
||||
/ip dhcp-server alert
|
||||
add disabled=no interface=494 on-alert=rogue-dhcp valid-server=\
|
||||
DC:2C:6E:DD:87:58
|
||||
/ip dhcp-server lease
|
||||
add address=10.10.175.10 mac-address=78:8A:20:AC:C5:32
|
||||
add address=10.10.175.11 mac-address=58:C1:7A:75:4D:F2
|
||||
/ip dhcp-server network
|
||||
add address=10.10.160.0/20 dns-server=204.110.191.240,204.110.191.20 domain=\
|
||||
vntx.net gateway=10.10.175.254 ntp-server=204.110.191.19
|
||||
add address=100.64.160.0/20 dns-server=204.110.191.240,204.110.191.20 domain=\
|
||||
vntx.net gateway=100.64.175.254 ntp-server=204.110.191.19
|
||||
add address=10.10.160.0/20 dns-server=204.110.191.240,204.110.191.250 domain=\
|
||||
vntx.net gateway=10.10.175.254 ntp-server=\
|
||||
23.150.40.242,108.61.215.221,162.254.225.152
|
||||
add address=100.64.160.0/20 dns-server=204.110.191.240,204.110.191.250 \
|
||||
domain=vntx.net gateway=100.64.175.254 ntp-server=\
|
||||
23.150.40.242,108.61.215.221,162.254.225.152
|
||||
/ip dns
|
||||
set servers=9.9.9.9,1.1.1.1
|
||||
set servers=204.110.191.240,204.110.191.250
|
||||
/ip firewall address-list
|
||||
add address=154.66.115.255 list=CPF
|
||||
add address=10.0.0.0/8 list=CPF
|
||||
add address=192.168.0.0/16 list=CPF
|
||||
add address=172.16.0.0/12 list=CPF
|
||||
add address=204.110.188.0/22 comment="VNTX public IPs" list=trusted
|
||||
add address=100.64.0.0/10 comment="CGNAT pools" list=trusted
|
||||
add address=10.0.0.0/8 comment="RFC 1918 private" list=trusted
|
||||
add address=192.168.0.0/16 comment="RFC 1918 private" list=trusted
|
||||
/ip firewall filter
|
||||
add action=accept chain=input comment="allow trusted to management services" \
|
||||
dst-port=8728-8729,8291,22 protocol=tcp src-address-list=trusted
|
||||
add action=drop chain=input comment="drop untrusted to management services" \
|
||||
dst-port=8728-8729,8291,22 protocol=tcp
|
||||
add action=accept chain=forward comment=\
|
||||
"bypass fasttrack for MPLS spine (out)" out-interface=ether2-climax
|
||||
add action=accept chain=forward comment=\
|
||||
|
|
@ -471,7 +481,7 @@ add address=2606:1c80:0:17::2 advertise=no comment="P2P 494<->climax" \
|
|||
interface=ether2-climax
|
||||
add address=2606:1c80:1a00::1 comment="494 mgmt LAN" interface=management
|
||||
/ipv6 dhcp-server
|
||||
add interface=management name=494-mgmt-stateless
|
||||
add dhcp-option=v6-dns interface=management name=494-mgmt-stateless
|
||||
/ipv6 firewall filter
|
||||
add action=accept chain=input comment=established connection-state=\
|
||||
established,related
|
||||
|
|
@ -486,10 +496,8 @@ add action=drop chain=forward comment=invalid connection-state=invalid
|
|||
add action=accept chain=forward comment=PMTUD hop-limit=equal:1 protocol=\
|
||||
icmpv6
|
||||
add action=accept chain=forward comment=icmpv6 protocol=icmpv6
|
||||
add action=accept chain=forward comment=internal-to-internal dst-address=\
|
||||
2606:1c80::/32 src-address=2606:1c80::/32
|
||||
add action=accept chain=forward comment="v6 outbound from 494 customers" \
|
||||
src-address=2606:1c80:1a00::/44
|
||||
add action=accept chain=forward comment="v6 outbound (all vntx sources)" \
|
||||
src-address=2606:1c80::/32
|
||||
add action=drop chain=forward comment="default deny"
|
||||
/ipv6 nd
|
||||
set [ find default=yes ] advertise-dns=yes
|
||||
|
|
@ -508,12 +516,12 @@ set propagate-ttl=no
|
|||
/ppp aaa
|
||||
set interim-update=15m use-radius=yes
|
||||
/radius
|
||||
add address=204.110.191.248 require-message-auth=no service=ppp src-address=\
|
||||
204.110.188.94 timeout=300ms
|
||||
add address=204.110.191.2 require-message-auth=no service=ppp src-address=\
|
||||
204.110.188.94 timeout=300ms
|
||||
add accounting-backup=yes address=45.76.56.5 disabled=yes \
|
||||
require-message-auth=no service=ppp src-address=204.110.188.94 timeout=\
|
||||
add address=204.110.191.248 secret=dyhhtcUft3CJtDfYFG1S6 service=ppp \
|
||||
src-address=204.110.188.94 timeout=2s
|
||||
add address=204.110.191.2 secret=dyhhtcUft3CJtDfYFG1S6 service=ppp \
|
||||
src-address=204.110.188.94 timeout=2s
|
||||
add accounting-backup=yes address=45.76.56.5 disabled=yes secret=\
|
||||
dyhhtcUft3CJtDfYFG1S6 service=ppp src-address=204.110.188.94 timeout=\
|
||||
300ms
|
||||
/routing bfd configuration
|
||||
add disabled=no interfaces=ether2-climax min-rx=200ms min-tx=200ms \
|
||||
|
|
@ -535,12 +543,11 @@ add chain=v6-ibgp-out disabled=no rule="if (dst==2606:1c80:1A00::/44 || dst==2\
|
|||
606:1c80:0:10::111/128 || (dst-len==64 && dst==2606:1c80:0:17::/64)) { acc\
|
||||
ept; } else { reject; }"
|
||||
/routing ospf interface-template
|
||||
add area=ospf-area-1 auth=sha512 auth-id=1 disabled=no interfaces=\
|
||||
ether2-climax type=ptp use-bfd=no
|
||||
add area=ospf-area-1 disabled=no passive
|
||||
add area=backbone-v2 cost=10 interfaces=ether2-climax priority=1 type=ptp \
|
||||
use-bfd=no
|
||||
add area=backbone-v2 passive
|
||||
/routing ospf static-neighbor
|
||||
add address=10.250.1.70%ether2-climax area=ospf-area-1 disabled=no \
|
||||
poll-interval=10s
|
||||
add address=10.250.1.70%ether2-climax area=*1 disabled=no poll-interval=10s
|
||||
/snmp
|
||||
set contact=graham@vntx.net enabled=yes location="33.208204, -96.462530"
|
||||
/system clock
|
||||
|
|
@ -560,9 +567,12 @@ add address=0.us.pool.ntp.org
|
|||
/system package update
|
||||
set channel=long-term
|
||||
/system routerboard settings
|
||||
# Firmware upgraded successfully, please reboot for changes to take effect!
|
||||
set auto-upgrade=yes
|
||||
/system scheduler
|
||||
add name=reboot on-event="/system reboot" policy=\
|
||||
ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon \
|
||||
start-date=2025-02-16 start-time=03:40:00
|
||||
/system script
|
||||
add dont-require-permissions=no name=rogue-dhcp owner=admin policy=\
|
||||
ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source=\
|
||||
":log warning message=\"Rogue DHCP server detected!\""
|
||||
|
|
|
|||
|
|
@ -1,119 +0,0 @@
|
|||
# =============================================================================
|
||||
# 982 — IPv6 apply script (ROS 7.21.4, BGP-only / no OSPFv3)
|
||||
# =============================================================================
|
||||
#
|
||||
# 982 is a leaf tower. Single backbone P2P:
|
||||
# * core (north) — ether7-380 (60 GHz), v6 P2P 2606:1c80:0:14::/64 (982 .2)
|
||||
# Default upstream toward core via :14::1.
|
||||
#
|
||||
# Loopback: 2606:1c80:0:10::110/128
|
||||
# /44 customer aggregate: 2606:1c80:1300::/44
|
||||
# Mgmt LAN: 2606:1c80:1300::1/64 (assumes `mgmt` bridge — verify name first).
|
||||
# PPPoE profile assumed `pppoe`. Customer interface-list assumed `customer`.
|
||||
# =============================================================================
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# WIPE
|
||||
# -----------------------------------------------------------------------------
|
||||
/ppp profile set [find] remote-ipv6-prefix-pool="" dhcpv6-pd-pool=""
|
||||
/ipv6 dhcp-server remove [find]
|
||||
/ipv6 dhcp-server option remove [find]
|
||||
/ipv6 dhcp-client remove [find]
|
||||
/ipv6 nd prefix remove [find]
|
||||
/ipv6 nd remove [find default=no]
|
||||
/ipv6 pool remove [find]
|
||||
/ipv6 route remove [find dynamic=no]
|
||||
/ipv6 firewall filter remove [find]
|
||||
/ipv6 firewall mangle remove [find]
|
||||
/ipv6 firewall raw remove [find]
|
||||
/ipv6 address remove [find dynamic=no]
|
||||
|
||||
/routing ospf interface-template remove [find area=backbone-v3]
|
||||
/routing ospf area remove [find name=backbone-v3]
|
||||
/routing ospf instance remove [find name=default-v3]
|
||||
|
||||
/routing bgp connection remove [find name~"ibgp" and (afi~"ipv6" or address-families~"ipv6")]
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# APPLY — BGP instance
|
||||
# -----------------------------------------------------------------------------
|
||||
/routing bgp template set [find name=default] disabled=no
|
||||
|
||||
/routing bgp instance
|
||||
add name=bgp-instance-1 as=393837 router-id=10.254.254.110
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# APPLY — addresses
|
||||
# -----------------------------------------------------------------------------
|
||||
/ipv6 settings set forward=yes accept-router-advertisements=no
|
||||
|
||||
/ipv6 address
|
||||
add address=2606:1c80:0:10::110/128 interface=lo advertise=no comment="loopback"
|
||||
add address=2606:1c80:0:14::2/64 interface=ether7-380 advertise=no comment="P2P 982<->core (60GHz)"
|
||||
add address=2606:1c80:1300::1/64 interface=mgmt advertise=yes comment="982 mgmt LAN"
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# APPLY — static routes
|
||||
# -----------------------------------------------------------------------------
|
||||
/ipv6 route
|
||||
add dst-address=::/0 gateway=2606:1c80:0:14::1 distance=1 comment="default via core"
|
||||
add dst-address=2606:1c80:0:10::253/128 gateway=2606:1c80:0:14::1 distance=1 comment="core loopback (iBGP bootstrap)"
|
||||
add dst-address=2606:1c80:0:10::254/128 gateway=2606:1c80:0:14::1 distance=1 comment="edge loopback via core"
|
||||
add dst-address=2606:1c80:1300::/44 blackhole distance=200 comment="982 /44 aggregate (iBGP origin)"
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# APPLY — neighbour discovery on the mgmt LAN
|
||||
# -----------------------------------------------------------------------------
|
||||
/ipv6 nd
|
||||
add interface=mgmt ra-interval=3m20s-10m managed-address-configuration=no other-configuration=yes advertise-dns=yes
|
||||
|
||||
/ipv6 nd prefix
|
||||
add interface=mgmt prefix=2606:1c80:1300::/64 autonomous=yes on-link=yes
|
||||
|
||||
/ipv6 dhcp-server
|
||||
add interface=mgmt name=982-mgmt-stateless address-pool=static-only
|
||||
|
||||
/ipv6 dhcp-server option
|
||||
add name=v6-dns code=23 value="'2606:4700:4700::1111''2606:4700:4700::1001'"
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# APPLY — customer PD pool + bind to PPPoE profile
|
||||
# -----------------------------------------------------------------------------
|
||||
/ipv6 pool
|
||||
add name=982-cust-pd-1 prefix=2606:1c80:1301::/48 prefix-length=56
|
||||
|
||||
/ppp profile
|
||||
set [find name=982] remote-ipv6-prefix-pool=982-cust-pd-1 dhcpv6-pd-pool=982-cust-pd-1
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# APPLY — firewall
|
||||
# -----------------------------------------------------------------------------
|
||||
/ipv6 firewall filter
|
||||
add chain=input action=accept connection-state=established,related comment="established"
|
||||
add chain=input action=drop connection-state=invalid comment="invalid"
|
||||
add chain=input action=accept protocol=icmpv6 comment="icmpv6"
|
||||
add chain=input action=accept src-address=fe80::/10 comment="link-local"
|
||||
add chain=input action=accept src-address=2606:1c80::/32 comment="internal"
|
||||
add chain=input action=drop comment="default deny"
|
||||
|
||||
add chain=forward action=accept connection-state=established,related comment="established"
|
||||
add chain=forward action=drop connection-state=invalid comment="invalid"
|
||||
add chain=forward action=accept protocol=icmpv6 hop-limit=equal:1 comment="PMTUD"
|
||||
add chain=forward action=accept protocol=icmpv6 comment="icmpv6"
|
||||
add chain=forward action=accept src-address=2606:1c80::/32 dst-address=2606:1c80::/32 comment="internal-to-internal"
|
||||
add chain=forward action=accept src-address=2606:1c80:1300::/44 comment="v6 outbound from 982 customers"
|
||||
add chain=forward action=drop comment="default deny"
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# APPLY — iBGP to core (v6 RR)
|
||||
# -----------------------------------------------------------------------------
|
||||
/routing filter rule
|
||||
add chain=v6-ibgp-out disabled=no rule="if (dst==2606:1c80:1300::/44 || dst==2606:1c80:0:10::110/128 || (dst-len==64 && dst==2606:1c80:0:14::/64)) { accept; } else { reject; }"
|
||||
|
||||
/routing bgp connection
|
||||
add name=ibgp-rr-core instance=bgp-instance-1 templates=default \
|
||||
afi=ip,ipv6 local.role=ibgp local.address=2606:1c80:0:10::110 \
|
||||
remote.address=2606:1c80:0:10::253 remote.as=393837 multihop=yes \
|
||||
nexthop-choice=force-self output.filter-chain=v6-ibgp-out \
|
||||
output.redistribute=connected,static \
|
||||
comment="iBGP to core (v6 RR), v6 transport"
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
# 2026-05-09 15:08:05 by RouterOS 7.21.4
|
||||
# 2026-07-10 10:54:21 by RouterOS 7.21.4
|
||||
# software id = FUVS-HCM5
|
||||
#
|
||||
# model = CCR1009-7G-1C-1S+
|
||||
|
|
@ -117,16 +117,18 @@ add address-pool=mgmt interface=mgmt lease-script="{\
|
|||
\n }\
|
||||
\n} while ( \$attempts < \$max )\
|
||||
\n}\
|
||||
\n" lease-time=10m name=mgmt
|
||||
\n" lease-time=1h name=mgmt
|
||||
/ip smb users
|
||||
set [ find default=yes ] disabled=yes
|
||||
/ipv6 dhcp-server option
|
||||
add code=23 name=v6-dns value="'2606:4700:4700::1111''2606:4700:4700::1001'"
|
||||
add code=23 name=v6-dns
|
||||
/ipv6 pool
|
||||
add name=982-cust-pd-1 prefix=2606:1c80:1301::/48 prefix-length=56
|
||||
/ppp profile
|
||||
add dhcpv6-pd-pool=982-cust-pd-1 local-address=100.64.63.253 name=982 \
|
||||
on-down="{\
|
||||
set *0 dns-server=204.110.191.240,204.110.191.250
|
||||
add change-tcp-mss=yes dhcpv6-pd-pool=982-cust-pd-1 dns-server=\
|
||||
204.110.191.240,204.110.191.250 idle-timeout=1h local-address=\
|
||||
100.64.63.253 name=982 on-down="{\
|
||||
\n:local url \"https://xtjlpnubrg.execute-api.us-east-1.amazonaws.com/prod\
|
||||
uction/on-start-workflow-execution/6f19a48f-04f7-40c1-a4e0-8cb66df99213/0a\
|
||||
7f4443-fc8a-4fe0-807c-f535d2aa1865\"\
|
||||
|
|
@ -234,7 +236,9 @@ add dhcpv6-pd-pool=982-cust-pd-1 local-address=100.64.63.253 name=982 \
|
|||
\n }\
|
||||
\n} while ( \$attempts < \$max )\
|
||||
\n}\
|
||||
\n" remote-address=cgnat remote-ipv6-prefix-pool=982-cust-pd-1
|
||||
\n" only-one=yes remote-address=cgnat remote-ipv6-prefix-pool=\
|
||||
982-cust-pd-1
|
||||
set *FFFFFFFE dns-server=204.110.191.240,204.110.191.250
|
||||
/queue type
|
||||
add kind=fq-codel name=fq-codel
|
||||
/queue interface
|
||||
|
|
@ -290,16 +294,13 @@ add bridge=mgmt ingress-filtering=no interface=vlan10_ether6 \
|
|||
internal-path-cost=10 path-cost=10
|
||||
add bridge=982 interface=combo1
|
||||
/ip firewall connection tracking
|
||||
# ipv6 *accept router advertisements* configuration has changed, please restart device to apply settings
|
||||
set icmp-timeout=30s tcp-close-wait-timeout=1m tcp-established-timeout=4h \
|
||||
tcp-fin-wait-timeout=2m tcp-last-ack-timeout=30s \
|
||||
tcp-syn-received-timeout=1m tcp-syn-sent-timeout=2m \
|
||||
tcp-time-wait-timeout=2m udp-stream-timeout=2m
|
||||
/ip settings
|
||||
# ipv6 *accept router advertisements* configuration has changed, please restart device to apply settings
|
||||
set tcp-syncookies=yes
|
||||
/ipv6 settings
|
||||
# ipv6 *accept router advertisements* configuration has changed, please restart device to apply settings
|
||||
set accept-router-advertisements=no
|
||||
/interface pppoe-server server
|
||||
add authentication=mschap2 default-profile=982 disabled=no interface=982 \
|
||||
|
|
@ -311,6 +312,9 @@ add address=10.10.63.254/20 interface=mgmt network=10.10.48.0
|
|||
add address=204.110.188.126/27 interface=982 network=204.110.188.96
|
||||
add address=100.64.63.254/20 comment="New CGNAT subnet" interface=982 \
|
||||
network=100.64.48.0
|
||||
/ip dhcp-server alert
|
||||
add disabled=no interface=mgmt on-alert=rogue-dhcp valid-server=\
|
||||
DC:2C:6E:66:50:BF
|
||||
/ip dhcp-server lease
|
||||
add address=10.10.63.2 mac-address=F4:92:BF:2F:29:7E
|
||||
add address=10.10.63.3 mac-address=F4:92:BF:2F:29:93
|
||||
|
|
@ -320,12 +324,14 @@ add address=10.10.63.6 mac-address=F4:92:BF:2F:08:38
|
|||
add address=10.10.63.1 client-id=1:d0:21:f9:f0:f5:23 mac-address=\
|
||||
D0:21:F9:F0:F5:23
|
||||
/ip dhcp-server network
|
||||
add address=10.10.48.0/20 dns-server=204.110.191.240,204.110.191.20 domain=\
|
||||
vntx.net gateway=10.10.63.254 ntp-server=204.110.191.19
|
||||
add address=100.64.28.0/22 dns-server=204.110.191.240,204.110.191.20 domain=\
|
||||
vntx.net gateway=100.64.31.254 ntp-server=204.110.191.19
|
||||
add address=10.10.48.0/20 dns-server=204.110.191.240,204.110.191.250 domain=\
|
||||
vntx.net gateway=10.10.63.254 ntp-server=\
|
||||
23.150.40.242,108.61.215.221,162.254.225.152
|
||||
add address=100.64.28.0/22 dns-server=204.110.191.240,204.110.191.250 domain=\
|
||||
vntx.net gateway=100.64.31.254 ntp-server=\
|
||||
23.150.40.242,108.61.215.221,162.254.225.152
|
||||
/ip dns
|
||||
set servers=9.9.9.9,1.1.1.1
|
||||
set servers=204.110.191.240,204.110.191.250
|
||||
/ip firewall address-list
|
||||
add address=100.64.35.199 comment="Scott Anderson (2203)" list=BusinessUltra
|
||||
add address=100.64.35.199 comment="Scott Anderson (2203)" list=Active
|
||||
|
|
@ -369,9 +375,17 @@ add address=100.64.35.186 comment="Rick Beckham (464)" list=Active
|
|||
add address=100.64.35.185 comment="Kris Dougherty (421)" list=\
|
||||
ResidentialAdvanced
|
||||
add address=100.64.35.185 comment="Kris Dougherty (421)" list=Active
|
||||
add address=204.110.188.0/22 comment="VNTX public IPs" list=trusted
|
||||
add address=100.64.0.0/10 comment="CGNAT pools" list=trusted
|
||||
add address=10.0.0.0/8 comment="RFC 1918 private" list=trusted
|
||||
add address=192.168.0.0/16 comment="RFC 1918 private" list=trusted
|
||||
/ip firewall filter
|
||||
add action=accept chain=input comment="allow trusted to management services" \
|
||||
dst-port=8728-8729,8291,22 protocol=tcp src-address-list=trusted
|
||||
add action=accept chain=forward comment=\
|
||||
"bypass fasttrack for MPLS spine (out)" out-interface=ether7-380
|
||||
add action=drop chain=input comment="drop untrusted to management services" \
|
||||
dst-port=8728-8729,8291,22 protocol=tcp
|
||||
add action=accept chain=forward comment=\
|
||||
"bypass fasttrack for MPLS spine (in)" in-interface=ether7-380
|
||||
add action=fasttrack-connection chain=forward comment=\
|
||||
|
|
@ -413,7 +427,7 @@ add address=2606:1c80:0:14::2 advertise=no comment="P2P 982<->core (60GHz)" \
|
|||
interface=ether7-380
|
||||
add address=2606:1c80:1300::1 comment="982 mgmt LAN" interface=mgmt
|
||||
/ipv6 dhcp-server
|
||||
add interface=mgmt name=982-mgmt-stateless
|
||||
add dhcp-option=v6-dns interface=mgmt name=982-mgmt-stateless
|
||||
/ipv6 firewall filter
|
||||
add action=accept chain=input comment=established connection-state=\
|
||||
established,related
|
||||
|
|
@ -428,10 +442,8 @@ add action=drop chain=forward comment=invalid connection-state=invalid
|
|||
add action=accept chain=forward comment=PMTUD hop-limit=equal:1 protocol=\
|
||||
icmpv6
|
||||
add action=accept chain=forward comment=icmpv6 protocol=icmpv6
|
||||
add action=accept chain=forward comment=internal-to-internal dst-address=\
|
||||
2606:1c80::/32 src-address=2606:1c80::/32
|
||||
add action=accept chain=forward comment="v6 outbound from 982 customers" \
|
||||
src-address=2606:1c80:1300::/44
|
||||
add action=accept chain=forward comment="v6 outbound (all vntx sources)" \
|
||||
src-address=2606:1c80::/32
|
||||
add action=drop chain=forward comment="default deny"
|
||||
/ipv6 nd
|
||||
set [ find default=yes ] advertise-dns=yes
|
||||
|
|
@ -448,15 +460,14 @@ add interface=ether7-380
|
|||
/mpls settings
|
||||
set propagate-ttl=no
|
||||
/ppp aaa
|
||||
set use-radius=yes
|
||||
set interim-update=15m use-radius=yes
|
||||
/radius
|
||||
add address=204.110.191.248 require-message-auth=no service=ppp src-address=\
|
||||
204.110.188.126 timeout=300ms
|
||||
add address=204.110.191.2 disabled=yes require-message-auth=no service=ppp \
|
||||
add address=204.110.191.248 secret=wj61jc2gitzeomx6 service=ppp src-address=\
|
||||
204.110.188.126 timeout=2s
|
||||
add address=204.110.191.2 disabled=yes secret=wj61jc2gitzeomx6 service=ppp \
|
||||
src-address=204.110.188.126 timeout=300ms
|
||||
add accounting-backup=yes address=45.76.56.5 disabled=yes \
|
||||
require-message-auth=no service=ppp src-address=204.110.188.126 timeout=\
|
||||
300ms
|
||||
add accounting-backup=yes address=45.76.56.5 disabled=yes secret=\
|
||||
wj61jc2gitzeomx6 service=ppp src-address=204.110.188.126 timeout=300ms
|
||||
/routing bfd configuration
|
||||
add disabled=no interfaces=all min-rx=200ms min-tx=200ms multiplier=5
|
||||
/routing bgp connection
|
||||
|
|
@ -472,8 +483,8 @@ add chain=v6-ibgp-out disabled=no rule="if (dst==2606:1c80:1300::/44 || dst==2\
|
|||
606:1c80:0:10::110/128 || (dst-len==64 && dst==2606:1c80:0:14::/64)) { acc\
|
||||
ept; } else { reject; }"
|
||||
/routing ospf interface-template
|
||||
add area=backbone-v2 auth-id=1 cost=10 disabled=no interfaces=ether7-380 \
|
||||
networks=10.250.1.32/29 priority=1 type=ptp use-bfd=no
|
||||
add area=backbone-v2 auth-id=1 auth-key="" cost=10 disabled=no interfaces=\
|
||||
ether7-380 networks=10.250.1.32/29 priority=1 type=ptp use-bfd=no
|
||||
add area=backbone-v2 disabled=no passive
|
||||
/snmp
|
||||
set contact=graham@vntx.net enabled=yes location="33.148902, -96.495811"
|
||||
|
|
@ -492,9 +503,12 @@ add address=0.us.pool.ntp.org
|
|||
/system package update
|
||||
set channel=long-term
|
||||
/system routerboard settings
|
||||
# Firmware upgraded successfully, please reboot for changes to take effect!
|
||||
set auto-upgrade=yes
|
||||
/system scheduler
|
||||
add name=reboot on-event="/system reboot" policy=\
|
||||
ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon \
|
||||
start-date=2024-09-16 start-time=03:35:00
|
||||
/system script
|
||||
add dont-require-permissions=no name=rogue-dhcp owner=admin policy=\
|
||||
ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source=\
|
||||
":log warning message=\"Rogue DHCP server detected!\""
|
||||
|
|
|
|||
4
mikrotik-tool/add-gaiia-user.rsc
Normal file
4
mikrotik-tool/add-gaiia-user.rsc
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
# Add gaiia user with full access to all MikroTik routers
|
||||
# First try to update existing user, then add if it doesn't exist
|
||||
/user set [find name=gaiia] password=ordQnwVQWyUAnLMTbJgyk0xH6Z group=full
|
||||
/user add name=gaiia password=ordQnwVQWyUAnLMTbJgyk0xH6Z group=full
|
||||
3
mikrotik-tool/bfd-fix-core.rsc
Normal file
3
mikrotik-tool/bfd-fix-core.rsc
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
/routing ospf interface-template set numbers=*D use-bfd=no
|
||||
/routing ospf interface-template set numbers=*8 use-bfd=no
|
||||
/routing ospf interface-template set numbers=*7 use-bfd=no
|
||||
2
mikrotik-tool/bfd-fix-culleoka.rsc
Normal file
2
mikrotik-tool/bfd-fix-culleoka.rsc
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
/routing ospf interface-template set numbers=*6 use-bfd=no
|
||||
/routing ospf interface-template set numbers=*7 use-bfd=no
|
||||
1
mikrotik-tool/bfd-fix-lowry.rsc
Normal file
1
mikrotik-tool/bfd-fix-lowry.rsc
Normal file
|
|
@ -0,0 +1 @@
|
|||
/routing ospf interface-template set numbers=*2 use-bfd=no
|
||||
3
mikrotik-tool/bfd-fix-newhope.rsc
Normal file
3
mikrotik-tool/bfd-fix-newhope.rsc
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
/routing ospf interface-template set numbers=*2 use-bfd=no
|
||||
/routing ospf interface-template set numbers=*6 use-bfd=no
|
||||
/routing ospf interface-template set numbers=*E use-bfd=no
|
||||
|
|
@ -1,170 +0,0 @@
|
|||
# =============================================================================
|
||||
# climax — IPv6 apply script (ROS 7.21.4, BGP-only / no OSPFv3)
|
||||
# =============================================================================
|
||||
#
|
||||
# Climax sits between verona (south-west) and the rest of the network
|
||||
# (via core). One v6 default upstream — toward core via :12::1.
|
||||
#
|
||||
# P2P interfaces:
|
||||
# ether4-380-airfiber24 :12::/64 (climax .2, core .1)
|
||||
# ether6-verona-11ghz :20::/64 (verona .1, climax .2)
|
||||
# ether3-culleoka-11ghz :21::/64 (climax .1, culleoka .2)
|
||||
# ether5-494 :17::/64 (climax .1, 494 .2)
|
||||
#
|
||||
# Mgmt LAN: bridge `mgmt`, v6 = 2606:1c80:1100::1/64
|
||||
# PPPoE customers: profile name `pppoe`, customer pool 2606:1c80:1101::/48 → /56
|
||||
#
|
||||
# BFD on wireless OSPFv2 templates was already turned off on this router
|
||||
# earlier today (2026-05-09) — verified via the freshly-pulled climax.rsc.
|
||||
# No BFD changes here.
|
||||
# =============================================================================
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# WIPE — drop OSPFv3 + stale internal v6
|
||||
# -----------------------------------------------------------------------------
|
||||
/ppp profile set [find] remote-ipv6-prefix-pool="" dhcpv6-pd-pool=""
|
||||
/ipv6 dhcp-server remove [find]
|
||||
/ipv6 dhcp-server option remove [find]
|
||||
/ipv6 dhcp-client remove [find]
|
||||
/ipv6 nd prefix remove [find]
|
||||
/ipv6 nd remove [find default=no]
|
||||
/ipv6 pool remove [find]
|
||||
/ipv6 route remove [find dynamic=no]
|
||||
/ipv6 firewall filter remove [find]
|
||||
/ipv6 firewall mangle remove [find]
|
||||
/ipv6 firewall raw remove [find]
|
||||
/ipv6 address remove [find dynamic=no]
|
||||
|
||||
# OSPFv3 — instance, area, and the orphan culleoka-11ghz template entry
|
||||
# (currently lives under area=backbone-v3) all get removed.
|
||||
/routing ospf interface-template remove [find area=backbone-v3]
|
||||
/routing ospf area remove [find name=backbone-v3]
|
||||
/routing ospf instance remove [find name=default-v3]
|
||||
|
||||
# Drop any stale internal v6 iBGP peers (none present, but defensive).
|
||||
/routing bgp connection remove [find name~"ibgp" and (afi~"ipv6" or address-families~"ipv6")]
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# APPLY — BGP instance (climax has none today)
|
||||
# -----------------------------------------------------------------------------
|
||||
/routing bgp instance
|
||||
add name=bgp-instance-1 as=393837 router-id=10.254.254.102
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# APPLY — addresses
|
||||
# -----------------------------------------------------------------------------
|
||||
/ipv6 settings set forward=yes accept-router-advertisements=no
|
||||
|
||||
/ipv6 address
|
||||
add address=2606:1c80:0:10::102/128 interface=lo advertise=no comment="loopback"
|
||||
add address=2606:1c80:0:12::2/64 interface=ether4-380-airfiber24 advertise=no comment="P2P climax<->core"
|
||||
add address=2606:1c80:0:20::2/64 interface=ether6-verona-11ghz advertise=no comment="P2P climax<->verona"
|
||||
add address=2606:1c80:0:21::1/64 interface=ether3-culleoka-11ghz advertise=no comment="P2P climax<->culleoka"
|
||||
add address=2606:1c80:0:17::1/64 interface=ether5-494 advertise=no comment="P2P climax<->494"
|
||||
add address=2606:1c80:1100::1/64 interface=mgmt advertise=yes comment="climax mgmt LAN"
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# APPLY — static routes (replace what an IGP would have learned)
|
||||
# -----------------------------------------------------------------------------
|
||||
/ipv6 route
|
||||
# Default upstream toward core (only one path from climax — core forwards
|
||||
# upward via preseem/direct).
|
||||
add dst-address=::/0 gateway=2606:1c80:0:12::1 distance=1 comment="default via core"
|
||||
|
||||
# Edge loopback explicit (recursively resolved through default; explicit
|
||||
# entry helps iBGP control plane converge quickly on first bring-up).
|
||||
add dst-address=2606:1c80:0:10::254/128 gateway=2606:1c80:0:12::1 distance=1 comment="edge loopback via core"
|
||||
|
||||
# 494 sits one hop further out via the climax<->494 P2P; climax is the
|
||||
# only neighbour that can deliver to its loopback. Without this, return
|
||||
# traffic for 494's /128 from core/edge loops at climax.
|
||||
add dst-address=2606:1c80:0:10::111/128 gateway=2606:1c80:0:17::2 distance=1 comment="494 loopback via P2P"
|
||||
|
||||
# verona's loopback is reachable through verona's own P2P side. Adding
|
||||
# this static beats the BGP-learned next-hop during convergence and
|
||||
# avoids the stale-next-hop loop we hit on 2026-05-09.
|
||||
add dst-address=2606:1c80:0:10::101/128 gateway=2606:1c80:0:20::1 distance=1 comment="verona loopback via P2P"
|
||||
|
||||
# Blackhole climax's /44 aggregate so iBGP has the prefix to advertise.
|
||||
add dst-address=2606:1c80:1100::/44 blackhole distance=200 comment="climax /44 aggregate (iBGP origin)"
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# APPLY — neighbour discovery on the mgmt LAN (SLAAC + RDNSS)
|
||||
# -----------------------------------------------------------------------------
|
||||
/ipv6 nd
|
||||
add interface=mgmt ra-interval=3m20s-10m managed-address-configuration=no other-configuration=yes advertise-dns=yes
|
||||
|
||||
/ipv6 nd prefix
|
||||
add interface=mgmt prefix=2606:1c80:1100::/64 autonomous=yes on-link=yes
|
||||
|
||||
/ipv6 dhcp-server
|
||||
add interface=mgmt name=climax-mgmt-stateless address-pool=static-only
|
||||
|
||||
/ipv6 dhcp-server option
|
||||
add name=v6-dns code=23 value="'2606:4700:4700::1111''2606:4700:4700::1001'"
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# APPLY — customer PD pool + bind to the climax PPPoE profile
|
||||
# -----------------------------------------------------------------------------
|
||||
/ipv6 pool
|
||||
add name=climax-cust-pd-1 prefix=2606:1c80:1101::/48 prefix-length=56
|
||||
|
||||
/ppp profile
|
||||
set [find name=pppoe] remote-ipv6-prefix-pool=climax-cust-pd-1 dhcpv6-pd-pool=climax-cust-pd-1
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# APPLY — firewall (mirrors v4 hygiene; deny by default)
|
||||
# -----------------------------------------------------------------------------
|
||||
/ipv6 firewall filter
|
||||
add chain=input action=accept connection-state=established,related comment="established"
|
||||
add chain=input action=drop connection-state=invalid comment="invalid"
|
||||
add chain=input action=accept protocol=icmpv6 comment="icmpv6"
|
||||
add chain=input action=accept src-address=fe80::/10 comment="link-local"
|
||||
add chain=input action=accept src-address=2606:1c80::/32 comment="internal"
|
||||
add chain=input action=drop comment="default deny"
|
||||
|
||||
add chain=forward action=accept connection-state=established,related comment="established"
|
||||
add chain=forward action=drop connection-state=invalid comment="invalid"
|
||||
add chain=forward action=accept protocol=icmpv6 hop-limit=equal:1 comment="PMTUD"
|
||||
add chain=forward action=accept protocol=icmpv6 comment="icmpv6"
|
||||
add chain=forward action=accept src-address=2606:1c80::/32 dst-address=2606:1c80::/32 comment="internal-to-internal"
|
||||
add chain=forward action=accept in-interface-list=customer comment="v6 outbound from customers"
|
||||
add chain=forward action=drop comment="default deny"
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# APPLY — iBGP to edge (v4 transport / v6 NLRI)
|
||||
# -----------------------------------------------------------------------------
|
||||
# Output filter: advertise climax's own routes — /44 customer aggregate,
|
||||
# loopback /128, and connected /64 P2Ps toward verona, core, culleoka, 494.
|
||||
# Core (v6 RR) reflects these to every other client.
|
||||
/routing filter rule
|
||||
add chain=v6-ibgp-out disabled=no rule="if (dst==2606:1c80:1100::/44 || dst==2606:1c80:0:10::102/128 || (dst-len==64 && (dst==2606:1c80:0:12::/64 || dst==2606:1c80:0:17::/64 || dst==2606:1c80:0:20::/64 || dst==2606:1c80:0:21::/64))) { accept; } else { reject; }"
|
||||
|
||||
# Single iBGP session to CORE (which is the v6 route reflector for the fleet).
|
||||
# v6 transport (loopback-to-loopback). output.redistribute=connected,static
|
||||
# picks up the loopback /128 (connected) and the /44 blackhole aggregate
|
||||
# (static); the filter chain narrows the set.
|
||||
/routing bgp connection
|
||||
add name=ibgp-rr-core instance=bgp-instance-1 templates=default \
|
||||
afi=ip,ipv6 local.role=ibgp local.address=2606:1c80:0:10::102 \
|
||||
remote.address=2606:1c80:0:10::253 remote.as=393837 multihop=yes \
|
||||
nexthop-choice=force-self output.filter-chain=v6-ibgp-out \
|
||||
output.redistribute=connected,static \
|
||||
comment="iBGP to core (v6 RR), v6 transport"
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# VERIFY (paste after apply)
|
||||
# -----------------------------------------------------------------------------
|
||||
# /ipv6 address print
|
||||
# /ipv6 route print where active
|
||||
# /routing ospf neighbor print ;; only OSPFv2 should remain
|
||||
# /routing bgp instance print
|
||||
# /routing bgp connection print
|
||||
# /routing bgp session print
|
||||
#
|
||||
# After core + edge have applied:
|
||||
# /ping 2606:1c80:0:12::1 count=3 ;; core P2P side
|
||||
# /ping 2606:1c80:0:10::253 count=3 ;; core loopback
|
||||
# /ping 2606:1c80:0:10::254 count=3 ;; edge loopback
|
||||
# /ping 2606:4700:4700::1111 count=3 ;; cloudflare end-to-end
|
||||
# /routing bgp session print ;; ibgp-edge-v6 should be Established
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
# 2026-05-09 15:08:04 by RouterOS 7.21.4
|
||||
# 2026-07-10 10:54:21 by RouterOS 7.21.4
|
||||
# software id = UETF-WF31
|
||||
#
|
||||
# model = CCR2004-16G-2S+
|
||||
|
|
@ -41,7 +41,7 @@ add name=cgnat ranges=100.64.7.1-100.64.7.250
|
|||
add name=public-temp ranges=204.110.190.1-204.110.190.99
|
||||
add name=climax-mgmt ranges=10.10.16.1-10.10.30.254
|
||||
/ip dhcp-server
|
||||
add address-pool=climaxtower interface=climax-bridge name=dhcp1
|
||||
add address-pool=cgnat interface=climax-bridge lease-time=1h name=dhcp1
|
||||
add address-pool=climax-mgmt interface=mgmt lease-script="{\
|
||||
\n:local url \"https://xtjlpnubrg.execute-api.us-east-1.amazonaws.com/prod\
|
||||
uction/on-start-workflow-execution/6f19a48f-04f7-40c1-a4e0-8cb66df99213/cd\
|
||||
|
|
@ -132,15 +132,16 @@ add address-pool=climax-mgmt interface=mgmt lease-script="{\
|
|||
\n }\
|
||||
\n} while ( \$attempts < \$max )\
|
||||
\n}\
|
||||
\n" name=climax-mgmt
|
||||
\n" lease-time=1h name=climax-mgmt
|
||||
/ipv6 dhcp-server option
|
||||
add code=23 name=v6-dns value="'2606:4700:4700::1111''2606:4700:4700::1001'"
|
||||
add code=23 name=v6-dns
|
||||
/ipv6 pool
|
||||
add name=climax-cust-pd-1 prefix=2606:1c80:1101::/48 prefix-length=56
|
||||
/ppp profile
|
||||
set *0 dns-server=204.110.191.240,204.110.191.250
|
||||
add bridge-learning=no change-tcp-mss=yes dhcpv6-pd-pool=climax-cust-pd-1 \
|
||||
dns-server=204.110.191.240,204.110.191.20 local-address=cgnat name=pppoe \
|
||||
on-down="{\
|
||||
dns-server=204.110.191.240,204.110.191.250 idle-timeout=1h local-address=\
|
||||
cgnat name=pppoe on-down="{\
|
||||
\n:local url \"https://xtjlpnubrg.execute-api.us-east-1.amazonaws.com/prod\
|
||||
uction/on-start-workflow-execution/6f19a48f-04f7-40c1-a4e0-8cb66df99213/0a\
|
||||
7f4443-fc8a-4fe0-807c-f535d2aa1865\"\
|
||||
|
|
@ -248,8 +249,10 @@ add bridge-learning=no change-tcp-mss=yes dhcpv6-pd-pool=climax-cust-pd-1 \
|
|||
\n }\
|
||||
\n} while ( \$attempts < \$max )\
|
||||
\n}\
|
||||
\n" remote-address=cgnat remote-ipv6-prefix-pool=climax-cust-pd-1 \
|
||||
use-compression=no use-encryption=no use-ipv6=no use-mpls=no use-upnp=no
|
||||
\n" only-one=yes remote-address=cgnat remote-ipv6-prefix-pool=\
|
||||
climax-cust-pd-1 use-compression=no use-encryption=no use-ipv6=no \
|
||||
use-mpls=no use-upnp=no
|
||||
set *FFFFFFFE dns-server=204.110.191.240,204.110.191.250
|
||||
/queue type
|
||||
add kind=fq-codel name=fq-codel
|
||||
/queue interface
|
||||
|
|
@ -296,25 +299,20 @@ add bridge=climax-bridge ingress-filtering=no interface=ether8-michael \
|
|||
add bridge=climax-bridge ingress-filtering=no interface=ether2 \
|
||||
internal-path-cost=10 multicast-router=disabled path-cost=10
|
||||
add bridge=climax-bridge interface=sfp-sfpplus1
|
||||
add bridge=mgmt interface=vlan10_ether7
|
||||
add bridge=mgmt interface=vlan10_sfp-sfpplus1
|
||||
/ip firewall connection tracking
|
||||
# ipv6 *accept router advertisements* configuration has changed, please restart device to apply settings
|
||||
set enabled=yes icmp-timeout=30s tcp-close-wait-timeout=1m \
|
||||
tcp-established-timeout=4h tcp-fin-wait-timeout=2m tcp-last-ack-timeout=\
|
||||
30s tcp-syn-received-timeout=1m tcp-syn-sent-timeout=2m \
|
||||
tcp-time-wait-timeout=2m udp-stream-timeout=2m
|
||||
/ip neighbor discovery-settings
|
||||
# ipv6 *accept router advertisements* configuration has changed, please restart device to apply settings
|
||||
set discover-interface-list=!dynamic
|
||||
/ip settings
|
||||
# ipv6 *accept router advertisements* configuration has changed, please restart device to apply settings
|
||||
set max-neighbor-entries=8192 tcp-syncookies=yes
|
||||
/ipv6 settings
|
||||
# ipv6 *accept router advertisements* configuration has changed, please restart device to apply settings
|
||||
set accept-router-advertisements=no
|
||||
/interface pppoe-server server
|
||||
add authentication=chap,mschap2 default-profile=pppoe disabled=no interface=\
|
||||
add authentication=mschap2 default-profile=pppoe disabled=no interface=\
|
||||
climax-bridge max-mru=1492 max-mtu=1492 one-session-per-host=yes \
|
||||
service-name=Climax
|
||||
/ip address
|
||||
|
|
@ -328,17 +326,33 @@ add address=10.250.1.70/29 interface=ether5-494 network=10.250.1.64
|
|||
add address=10.250.1.94/29 interface=ether4-380-airfiber24 network=\
|
||||
10.250.1.88
|
||||
add address=10.10.31.254/20 interface=mgmt network=10.10.16.0
|
||||
/ip dhcp-server alert
|
||||
add disabled=no interface=climax-bridge on-alert=rogue-dhcp valid-server=\
|
||||
F4:1E:57:6B:41:C1
|
||||
/ip dhcp-server network
|
||||
add address=10.0.102.0/24 dns-server=9.9.9.9 gateway=10.0.102.254
|
||||
add address=10.10.16.0/20 dns-server=204.110.191.240,204.110.191.20 domain=\
|
||||
vntx.net gateway=10.10.31.254 ntp-server=204.110.191.19
|
||||
add address=100.64.4.0/22 dns-server=204.110.191.240,204.110.191.20 domain=\
|
||||
vntx.net gateway=100.64.7.254 ntp-server=204.110.191.19
|
||||
add address=204.110.188.32/27 dns-server=204.110.191.240,204.110.191.20 \
|
||||
domain=vntx.net gateway=204.110.188.62 ntp-server=204.110.191.19
|
||||
add address=10.0.102.0/24 dns-server=204.110.191.240,204.110.191.250 gateway=\
|
||||
10.0.102.254 ntp-server=23.150.40.242,108.61.215.221,162.254.225.152
|
||||
add address=10.10.16.0/20 dns-server=204.110.191.240,204.110.191.250 domain=\
|
||||
vntx.net gateway=10.10.31.254 ntp-server=\
|
||||
23.150.40.242,108.61.215.221,162.254.225.152
|
||||
add address=100.64.4.0/22 dns-server=204.110.191.240,204.110.191.250 domain=\
|
||||
vntx.net gateway=100.64.7.254 ntp-server=\
|
||||
23.150.40.242,108.61.215.221,162.254.225.152
|
||||
add address=204.110.188.32/27 dns-server=204.110.191.240,204.110.191.250 \
|
||||
domain=vntx.net gateway=204.110.188.62 ntp-server=\
|
||||
23.150.40.242,108.61.215.221,162.254.225.152
|
||||
/ip dns
|
||||
set servers=9.9.9.9,1.1.1.1
|
||||
set servers=204.110.191.240,204.110.191.250
|
||||
/ip firewall address-list
|
||||
add address=204.110.188.0/22 comment="VNTX public IPs" list=trusted
|
||||
add address=100.64.0.0/10 comment="CGNAT pools" list=trusted
|
||||
add address=10.0.0.0/8 comment="RFC 1918 private" list=trusted
|
||||
add address=192.168.0.0/16 comment="RFC 1918 private" list=trusted
|
||||
/ip firewall filter
|
||||
add action=accept chain=input comment="allow trusted to management services" \
|
||||
dst-port=8728-8729,8291,22 protocol=tcp src-address-list=trusted
|
||||
add action=drop chain=input comment="drop untrusted to management services" \
|
||||
dst-port=8728-8729,8291,22 protocol=tcp
|
||||
add action=accept chain=forward comment=\
|
||||
"bypass fasttrack for MPLS spine (out)" out-interface=ether5-494
|
||||
add action=accept chain=forward comment=\
|
||||
|
|
@ -354,6 +368,11 @@ add action=accept chain=forward comment=\
|
|||
"bypass fasttrack for MPLS spine (in)" in-interface=ether6-verona-11ghz
|
||||
add action=passthrough chain=unused-hs-chain comment=\
|
||||
"place hotspot rules here" disabled=yes
|
||||
add action=accept chain=forward comment=\
|
||||
"bypass fasttrack for MPLS spine (in)" in-interface=ether3-culleoka-11ghz
|
||||
add action=accept chain=forward comment=\
|
||||
"bypass fasttrack for MPLS spine (out)" out-interface=\
|
||||
ether3-culleoka-11ghz
|
||||
add action=fasttrack-connection chain=forward comment=\
|
||||
"fasttrack established/related" connection-state=established,related
|
||||
add action=accept chain=forward comment="accept established/related" \
|
||||
|
|
@ -435,9 +454,9 @@ add address=2606:1c80:1100:1::1 comment="climax customer LAN (PPPoE+DHCP)" \
|
|||
/ipv6 dhcp-relay option
|
||||
set client_mac value="0x0001\$(CLIENT_MAC)"
|
||||
/ipv6 dhcp-server
|
||||
add interface=mgmt name=climax-mgmt-stateless
|
||||
add address-pool=climax-cust-pd-1 interface=climax-bridge name=\
|
||||
climax-cust-pd-server
|
||||
add dhcp-option=v6-dns interface=mgmt name=climax-mgmt-stateless
|
||||
add address-pool=climax-cust-pd-1 dhcp-option=v6-dns interface=climax-bridge \
|
||||
name=climax-cust-pd-server
|
||||
/ipv6 firewall filter
|
||||
add action=accept chain=input comment=established connection-state=\
|
||||
established,related
|
||||
|
|
@ -452,11 +471,8 @@ add action=drop chain=forward comment=invalid connection-state=invalid
|
|||
add action=accept chain=forward comment=PMTUD hop-limit=equal:1 protocol=\
|
||||
icmpv6
|
||||
add action=accept chain=forward comment=icmpv6 protocol=icmpv6
|
||||
add action=accept chain=forward comment=internal-to-internal dst-address=\
|
||||
2606:1c80::/32 src-address=2606:1c80::/32
|
||||
add action=accept chain=forward comment=\
|
||||
"v6 outbound from climax tower (customer + mgmt)" src-address=\
|
||||
2606:1c80:1100::/44
|
||||
add action=accept chain=forward comment="v6 outbound (all vntx sources)" \
|
||||
src-address=2606:1c80::/32
|
||||
add action=drop chain=forward comment="default deny"
|
||||
/ipv6 nd
|
||||
set [ find default=yes ] advertise-dns=yes
|
||||
|
|
@ -466,6 +482,7 @@ add advertise-dns=yes interface=climax-bridge other-configuration=yes
|
|||
add interface=ether6-verona-11ghz mpls-mtu=1508
|
||||
add interface=ether4-380-airfiber24 mpls-mtu=1508
|
||||
add interface=ether5-494 mpls-mtu=1508
|
||||
add interface=ether3-culleoka-11ghz mpls-mtu=1508
|
||||
/mpls ldp
|
||||
add disabled=no lsr-id=10.254.254.102 transport-addresses=10.254.254.102 vrf=\
|
||||
main
|
||||
|
|
@ -473,14 +490,15 @@ add disabled=no lsr-id=10.254.254.102 transport-addresses=10.254.254.102 vrf=\
|
|||
add interface=ether6-verona-11ghz
|
||||
add interface=ether4-380-airfiber24
|
||||
add interface=ether5-494
|
||||
add interface=ether3-culleoka-11ghz
|
||||
/mpls settings
|
||||
set propagate-ttl=no
|
||||
/ppp aaa
|
||||
set interim-update=1h use-radius=yes
|
||||
set interim-update=15m use-radius=yes
|
||||
/radius
|
||||
add address=204.110.191.248 require-message-auth=no service=ppp,hotspot,dhcp \
|
||||
add address=204.110.191.248 secret=ich7cp0j1qpe0801 service=ppp,hotspot,dhcp \
|
||||
src-address=204.110.188.62 timeout=2s
|
||||
add address=204.110.191.2 require-message-auth=no service=ppp,hotspot,dhcp \
|
||||
add address=204.110.191.2 secret=ich7cp0j1qpe0801 service=ppp,hotspot,dhcp \
|
||||
src-address=204.110.188.62 timeout=2s
|
||||
/radius incoming
|
||||
set accept=yes
|
||||
|
|
@ -501,12 +519,14 @@ add chain=v6-ibgp-out disabled=no rule="if (dst==2606:1c80:1100::/44 || dst==2\
|
|||
==2606:1c80:0:17::/64 || dst==2606:1c80:0:20::/64 || dst==2606:1c80:0:21::\
|
||||
/64))) { accept; } else { reject; }"
|
||||
/routing ospf interface-template
|
||||
add area=backbone-v2 auth=sha512 auth-id=1 cost=10 disabled=no interfaces=\
|
||||
ether6-verona-11ghz priority=1 type=ptp use-bfd=no
|
||||
add area=backbone-v2 auth=sha512 auth-id=1 cost=10 disabled=no interfaces=\
|
||||
ether5-494 priority=1 type=ptp use-bfd=no
|
||||
add area=backbone-v2 auth=sha512 auth-id=1 cost=10 disabled=no interfaces=\
|
||||
ether4-380-airfiber24 priority=1 type=ptp use-bfd=no
|
||||
add area=backbone-v2 auth=sha512 auth-id=1 auth-key=\
|
||||
5YHHWmoooAyVaFDX48ffjGjc36XMO5Irb7zS44Tsvet cost=10 disabled=no \
|
||||
interfaces=ether6-verona-11ghz priority=1 type=ptp use-bfd=no
|
||||
add area=backbone-v2 cost=10 disabled=no interfaces=ether5-494 priority=1 \
|
||||
type=ptp use-bfd=no
|
||||
add area=backbone-v2 auth=sha512 auth-id=1 auth-key=\
|
||||
5YHHWmoooAyVaFDX48ffjGjc36XMO5Irb7zS44Tsvet cost=10 disabled=no \
|
||||
interfaces=ether4-380-airfiber24 priority=1 type=ptp use-bfd=no
|
||||
/routing ospf static-neighbor
|
||||
add address=10.250.1.25%ether6-verona-11ghz area=backbone-v2 disabled=no \
|
||||
poll-interval=10s
|
||||
|
|
@ -528,5 +548,8 @@ add address=0.us.pool.ntp.org
|
|||
/system package update
|
||||
set channel=long-term
|
||||
/system routerboard settings
|
||||
# Firmware upgraded successfully, please reboot for changes to take effect!
|
||||
set auto-upgrade=yes enter-setup-on=delete-key
|
||||
/system script
|
||||
add dont-require-permissions=no name=rogue-dhcp owner=admin policy=\
|
||||
ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source=\
|
||||
":log warning message=\"Rogue DHCP server detected!\""
|
||||
|
|
|
|||
1
mikrotik-tool/core-ssh-preseem-fix.rsc
Normal file
1
mikrotik-tool/core-ssh-preseem-fix.rsc
Normal file
|
|
@ -0,0 +1 @@
|
|||
/ip firewall connection tracking set tcp-established-timeout=1d
|
||||
|
|
@ -1,224 +0,0 @@
|
|||
# =============================================================================
|
||||
# core (380) — IPv6 apply script (ROS 7.21.4, BGP-only / no OSPFv3)
|
||||
# =============================================================================
|
||||
#
|
||||
# Core sits between edge and the towers. Two paths to edge mirror v4:
|
||||
# 1. PRIMARY: sfp-sfpplus1-edge-preseem, /127 :1002::2 — edge cost 10
|
||||
# 2. BACKUP: ether3-edge-direct, /127 :11::2 — OSPFv2 cost 100 on both
|
||||
# ends; v6 mirrors via static distance.
|
||||
#
|
||||
# Tower-facing P2Ps (per ipv6.md):
|
||||
# ether5-climax :12::/64 (core .1, climax .2)
|
||||
# ether4-newhope :13::/64 (core .1, newhope .2)
|
||||
# ether1-982-60ghz :14::/64 (core .1, 982 .2)
|
||||
# ether6-culleoka-11ghz :15::/64 (core .1, culleoka .2)
|
||||
#
|
||||
# Mgmt LAN: vlan10_combo1 (vlan 10 on combo1-380), v6 = 2606:1c80:1400::1/64
|
||||
# PPPoE customers: profile name `380`, customer pool 2606:1c80:1401::/48 → /56
|
||||
#
|
||||
# Two pre-apply fixes required on core:
|
||||
# (a) /routing/bgp/instance bgp-instance-1 currently has as=65530 — must
|
||||
# be 393837 to peer iBGP with edge. We update it in place.
|
||||
# (b) BFD is currently on for ether5-climax (AF24), ether4-newhope, and
|
||||
# ether6-culleoka-11ghz (AF11). Per fleet policy (no BFD on
|
||||
# wireless), we set use-bfd=no on those three OSPFv2 templates.
|
||||
# Wired backbone (sfp-sfpplus1-edge-preseem, ether3-edge-direct)
|
||||
# keeps BFD as-is.
|
||||
# =============================================================================
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# WIPE — drop OSPFv3 + stale internal v6
|
||||
# -----------------------------------------------------------------------------
|
||||
/ppp profile set [find] remote-ipv6-prefix-pool="" dhcpv6-pd-pool=""
|
||||
/ipv6 dhcp-server remove [find]
|
||||
/ipv6 dhcp-server option remove [find]
|
||||
/ipv6 dhcp-client remove [find]
|
||||
/ipv6 nd prefix remove [find]
|
||||
/ipv6 nd remove [find default=no]
|
||||
/ipv6 pool remove [find]
|
||||
/ipv6 route remove [find dynamic=no]
|
||||
/ipv6 firewall filter remove [find]
|
||||
/ipv6 firewall mangle remove [find]
|
||||
/ipv6 firewall raw remove [find]
|
||||
/ipv6 address remove [find dynamic=no]
|
||||
|
||||
/routing ospf interface-template remove [find area=backbone-v3]
|
||||
/routing ospf area remove [find name=backbone-v3]
|
||||
/routing ospf instance remove [find name=default-v3]
|
||||
|
||||
/routing bgp connection remove [find name~"ibgp" and address-families~"ipv6"]
|
||||
# also remove the disabled placeholder climax connection (will be replaced
|
||||
# by the future climax tower's own iBGP-to-edge session).
|
||||
/routing bgp connection remove [find name=climax and disabled=yes]
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# APPLY — fix BGP instance AS (65530 → 393837 to match the fleet)
|
||||
# -----------------------------------------------------------------------------
|
||||
/routing bgp instance set [find name=bgp-instance-1] as=393837
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# APPLY — turn off BFD on wireless backbone OSPFv2 templates
|
||||
# -----------------------------------------------------------------------------
|
||||
/routing ospf interface-template set [find interfaces=ether5-climax] use-bfd=no
|
||||
/routing ospf interface-template set [find interfaces=ether4-newhope] use-bfd=no
|
||||
/routing ospf interface-template set [find interfaces=ether6-culleoka-11ghz] use-bfd=no
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# APPLY — addresses
|
||||
# -----------------------------------------------------------------------------
|
||||
/ipv6 settings set forward=yes accept-router-advertisements=no
|
||||
|
||||
/ipv6 address
|
||||
add address=2606:1c80:0:10::253/128 interface=lo advertise=no comment="loopback"
|
||||
add address=2606:1c80:0:11::2/64 interface=ether3-edge-direct advertise=no comment="P2P core<->edge direct (backup)"
|
||||
add address=2606:1c80:0:1002::2/64 interface=sfp-sfpplus1-edge-preseem advertise=no comment="P2P core<->edge via preseem (primary)"
|
||||
add address=2606:1c80:0:12::1/64 interface=ether5-climax advertise=no comment="P2P core<->climax"
|
||||
add address=2606:1c80:0:13::1/64 interface=ether4-newhope advertise=no comment="P2P core<->newhope"
|
||||
add address=2606:1c80:0:14::1/64 interface=ether1-982-60ghz advertise=no comment="P2P core<->982 60GHz"
|
||||
add address=2606:1c80:0:15::1/64 interface=ether6-culleoka-11ghz advertise=no comment="P2P core<->culleoka"
|
||||
add address=2606:1c80:1400::1/64 interface=vlan10_combo1 advertise=yes comment="380 mgmt LAN"
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# APPLY — static routes (replace what an IGP would have learned)
|
||||
# -----------------------------------------------------------------------------
|
||||
/ipv6 route
|
||||
# v6 default toward edge — preseem primary, direct backup (mirrors v4 cost relationship).
|
||||
add dst-address=::/0 gateway=2606:1c80:0:1002::1 distance=1 comment="default via preseem (primary)"
|
||||
add dst-address=::/0 gateway=2606:1c80:0:11::1 distance=2 comment="default via direct (backup)"
|
||||
|
||||
# Edge loopback static fallback (so iBGP TCP control plane can reach edge
|
||||
# even before BGP populates anything beyond defaults).
|
||||
add dst-address=2606:1c80:0:10::254/128 gateway=2606:1c80:0:1002::1 distance=1 comment="edge loopback via preseem"
|
||||
add dst-address=2606:1c80:0:10::254/128 gateway=2606:1c80:0:11::1 distance=2 comment="edge loopback via direct"
|
||||
|
||||
# Per-tower /128 bootstrap statics. Every iBGP RR client needs core to be
|
||||
# able to reach its loopback BEFORE the iBGP session is up — chicken/egg
|
||||
# otherwise. distance=1 wins until BGP comes up; once it does, the session
|
||||
# itself reflects the same /128 with the same forwarding outcome, so these
|
||||
# statics stay harmlessly more-specific. Towers reached *through* climax
|
||||
# (494) or *through* newhope (lowry) take the long way; directly-attached
|
||||
# towers (982, culleoka, newhope) use the matching P2P.
|
||||
add dst-address=2606:1c80:0:10::101/128 gateway=2606:1c80:0:12::2 distance=1 comment="verona loopback (via climax)"
|
||||
add dst-address=2606:1c80:0:10::102/128 gateway=2606:1c80:0:12::2 distance=1 comment="climax loopback"
|
||||
add dst-address=2606:1c80:0:10::104/128 gateway=2606:1c80:0:15::2 distance=1 comment="culleoka loopback"
|
||||
add dst-address=2606:1c80:0:10::108/128 gateway=2606:1c80:0:13::2 distance=1 comment="newhope loopback"
|
||||
add dst-address=2606:1c80:0:10::109/128 gateway=2606:1c80:0:13::2 distance=1 comment="lowry loopback (via newhope)"
|
||||
add dst-address=2606:1c80:0:10::110/128 gateway=2606:1c80:0:14::2 distance=1 comment="982 loopback"
|
||||
add dst-address=2606:1c80:0:10::111/128 gateway=2606:1c80:0:12::2 distance=1 comment="494 loopback (via climax)"
|
||||
|
||||
# Blackhole core's /44 aggregate so iBGP has the prefix to advertise upward.
|
||||
add dst-address=2606:1c80:1400::/44 blackhole distance=200 comment="core /44 aggregate (iBGP origin)"
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# APPLY — neighbour discovery on the 380 mgmt LAN (SLAAC + RDNSS)
|
||||
# -----------------------------------------------------------------------------
|
||||
/ipv6 nd
|
||||
add interface=vlan10_combo1 ra-interval=3m20s-10m managed-address-configuration=no other-configuration=yes advertise-dns=yes
|
||||
|
||||
/ipv6 nd prefix
|
||||
add interface=vlan10_combo1 prefix=2606:1c80:1400::/64 autonomous=yes on-link=yes
|
||||
|
||||
/ipv6 dhcp-server
|
||||
add interface=vlan10_combo1 name=core-mgmt-stateless address-pool=static-only
|
||||
|
||||
/ipv6 dhcp-server option
|
||||
add name=v6-dns code=23 value="'2606:4700:4700::1111''2606:4700:4700::1001'"
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# APPLY — customer PD pool + bind to the 380 PPPoE profile
|
||||
# -----------------------------------------------------------------------------
|
||||
/ipv6 pool
|
||||
add name=core-cust-pd-1 prefix=2606:1c80:1401::/48 prefix-length=56
|
||||
|
||||
/ppp profile
|
||||
set [find name=380] remote-ipv6-prefix-pool=core-cust-pd-1 dhcpv6-pd-pool=core-cust-pd-1
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# APPLY — firewall (mirrors v4 hygiene; deny by default)
|
||||
# -----------------------------------------------------------------------------
|
||||
/ipv6 firewall filter
|
||||
add chain=input action=accept connection-state=established,related comment="established"
|
||||
add chain=input action=drop connection-state=invalid comment="invalid"
|
||||
add chain=input action=accept protocol=icmpv6 comment="icmpv6"
|
||||
add chain=input action=accept src-address=fe80::/10 comment="link-local"
|
||||
add chain=input action=accept src-address=2606:1c80::/32 comment="internal"
|
||||
add chain=input action=drop comment="default deny"
|
||||
|
||||
add chain=forward action=accept connection-state=established,related comment="established"
|
||||
add chain=forward action=drop connection-state=invalid comment="invalid"
|
||||
add chain=forward action=accept protocol=icmpv6 hop-limit=equal:1 comment="PMTUD"
|
||||
add chain=forward action=accept protocol=icmpv6 comment="icmpv6"
|
||||
add chain=forward action=accept src-address=2606:1c80::/32 dst-address=2606:1c80::/32 comment="internal-to-internal"
|
||||
add chain=forward action=accept in-interface-list=customer comment="v6 outbound from customers"
|
||||
add chain=forward action=drop comment="default deny"
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# APPLY — iBGP route reflector (core is RR; edge + every tower are RR clients)
|
||||
# -----------------------------------------------------------------------------
|
||||
# Why core (not edge) is the RR: ROS 6.49 on edge does not properly reflect v6
|
||||
# NLRI even with all RR settings (cluster-id, route-reflect, multihop, correct
|
||||
# in/out filters). ROS7 on core does. So core takes the RR role and edge is
|
||||
# just another iBGP client — it learns tower /44s + /128 loopbacks reflected
|
||||
# from core, and supplies the v6 default that core reflects back to towers.
|
||||
#
|
||||
# cluster-id on the bgp-instance turns this router into a route reflector.
|
||||
# It must be set BEFORE adding the client connections.
|
||||
/routing bgp instance set [find name=bgp-instance-1] cluster-id=10.254.254.253
|
||||
|
||||
# Filter for RR-reflected traffic (in & out): allow ::/0, any /44 (customer
|
||||
# aggregates), any /64 (P2P backbone links + per-tower customer LANs), any
|
||||
# /128 (router loopbacks). Reject everything else (defense in depth — keeps
|
||||
# random connected /80 etc. from leaking).
|
||||
# NOTE on syntax: ROS 7.21 routing-filter language doesn't accept range
|
||||
# matchers like `dst-in <prefix>` or `dst > <addr>` — only `dst==<exact>`
|
||||
# and length comparisons. So we match by length-class, not by sub-range.
|
||||
# The /48 covering customer-vs-infra distinction is enforced by the
|
||||
# ORIGINATING router's own narrower filter (v6-ibgp-out below).
|
||||
/routing filter rule
|
||||
add chain=v6-rr-out disabled=no rule="if (dst==::/0 || dst-len==44 || dst-len==64 || dst-len==128) { accept; } else { reject; }"
|
||||
add chain=v6-rr-in disabled=no rule="if (dst==::/0 || dst-len==44 || dst-len==64 || dst-len==128) { accept; } else { reject; }"
|
||||
|
||||
# Filter for advertising CORE'S OWN connected/static routes outbound (this
|
||||
# filter runs on each connection's output.filter-chain — what core ORIGINATES
|
||||
# rather than what it REFLECTS). Narrow by exact prefix so only core's /44
|
||||
# customer aggregate, /128 loopback, and /64 P2P links toward each neighbour
|
||||
# escape into iBGP.
|
||||
add chain=v6-ibgp-out disabled=no rule="if (dst==2606:1c80:1400::/44 || dst==2606:1c80:0:10::253/128 || (dst-len==64 && (dst==2606:1c80:0:11::/64 || dst==2606:1c80:0:12::/64 || dst==2606:1c80:0:13::/64 || dst==2606:1c80:0:14::/64 || dst==2606:1c80:0:15::/64 || dst==2606:1c80:0:1002::/64))) { accept; } else { reject; }"
|
||||
|
||||
# All client connections share these settings:
|
||||
# templates=default inherits keepalive/holdtime/timers
|
||||
# afi=ip,ipv6 carry both v4 and v6 NLRI (v4 unused on these but harmless)
|
||||
# local.role=ibgp same-AS = iBGP
|
||||
# multihop=yes loopback-to-loopback (more than one hop)
|
||||
# nexthop-choice=force-self pin next-hop to core's v6 loopback
|
||||
# cluster=yes marks the peer as an RR client
|
||||
# input.filter=v6-rr-in limit what we accept
|
||||
# output.filter-chain=v6-rr-out limit what we send (covers reflected + originated)
|
||||
# output.redistribute=connected,static pick up our own /44 + /128
|
||||
/routing bgp connection
|
||||
add name=ibgp-rr-edge instance=bgp-instance-1 templates=default afi=ip,ipv6 local.role=ibgp local.address=2606:1c80:0:10::253 remote.address=2606:1c80:0:10::254 remote.as=393837 multihop=yes nexthop-choice=force-self cluster=yes input.filter=v6-rr-in output.filter-chain=v6-rr-out output.redistribute=connected,static comment="RR client: edge"
|
||||
add name=ibgp-rr-verona instance=bgp-instance-1 templates=default afi=ip,ipv6 local.role=ibgp local.address=2606:1c80:0:10::253 remote.address=2606:1c80:0:10::101 remote.as=393837 multihop=yes nexthop-choice=force-self cluster=yes input.filter=v6-rr-in output.filter-chain=v6-rr-out output.redistribute=connected,static comment="RR client: verona"
|
||||
add name=ibgp-rr-climax instance=bgp-instance-1 templates=default afi=ip,ipv6 local.role=ibgp local.address=2606:1c80:0:10::253 remote.address=2606:1c80:0:10::102 remote.as=393837 multihop=yes nexthop-choice=force-self cluster=yes input.filter=v6-rr-in output.filter-chain=v6-rr-out output.redistribute=connected,static comment="RR client: climax"
|
||||
add name=ibgp-rr-culleoka instance=bgp-instance-1 templates=default afi=ip,ipv6 local.role=ibgp local.address=2606:1c80:0:10::253 remote.address=2606:1c80:0:10::104 remote.as=393837 multihop=yes nexthop-choice=force-self cluster=yes input.filter=v6-rr-in output.filter-chain=v6-rr-out output.redistribute=connected,static comment="RR client: culleoka"
|
||||
add name=ibgp-rr-newhope instance=bgp-instance-1 templates=default afi=ip,ipv6 local.role=ibgp local.address=2606:1c80:0:10::253 remote.address=2606:1c80:0:10::108 remote.as=393837 multihop=yes nexthop-choice=force-self cluster=yes input.filter=v6-rr-in output.filter-chain=v6-rr-out output.redistribute=connected,static comment="RR client: newhope"
|
||||
add name=ibgp-rr-lowry instance=bgp-instance-1 templates=default afi=ip,ipv6 local.role=ibgp local.address=2606:1c80:0:10::253 remote.address=2606:1c80:0:10::109 remote.as=393837 multihop=yes nexthop-choice=force-self cluster=yes input.filter=v6-rr-in output.filter-chain=v6-rr-out output.redistribute=connected,static comment="RR client: lowry"
|
||||
add name=ibgp-rr-982 instance=bgp-instance-1 templates=default afi=ip,ipv6 local.role=ibgp local.address=2606:1c80:0:10::253 remote.address=2606:1c80:0:10::110 remote.as=393837 multihop=yes nexthop-choice=force-self cluster=yes input.filter=v6-rr-in output.filter-chain=v6-rr-out output.redistribute=connected,static comment="RR client: 982"
|
||||
add name=ibgp-rr-494 instance=bgp-instance-1 templates=default afi=ip,ipv6 local.role=ibgp local.address=2606:1c80:0:10::253 remote.address=2606:1c80:0:10::111 remote.as=393837 multihop=yes nexthop-choice=force-self cluster=yes input.filter=v6-rr-in output.filter-chain=v6-rr-out output.redistribute=connected,static comment="RR client: 494"
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# VERIFY (paste after apply)
|
||||
# -----------------------------------------------------------------------------
|
||||
# /ipv6 address print
|
||||
# /ipv6 route print where active
|
||||
# /routing ospf neighbor print
|
||||
# /routing ospf interface-template print where use-bfd=yes ;; should be wired-only
|
||||
# /routing bgp instance print
|
||||
# /routing bgp connection print
|
||||
# /routing bgp session print
|
||||
#
|
||||
# After edge has applied:
|
||||
# /ping 2606:1c80:0:1002::1 count=3 ;; edge preseem-side
|
||||
# /ping 2606:1c80:0:11::1 count=3 ;; edge direct-side
|
||||
# /ping 2606:1c80:0:10::254 count=3 ;; edge loopback
|
||||
# /ping 2606:4700:4700::1111 count=3 ;; cloudflare end-to-end
|
||||
# /routing bgp session print ;; ibgp-edge-v6 should be Established
|
||||
|
|
@ -1,11 +1,9 @@
|
|||
# 2026-05-09 15:08:05 by RouterOS 7.21.4
|
||||
# software id = XS5B-41QR
|
||||
# 2026-07-10 10:54:21 by RouterOS 7.21.4
|
||||
# software id = QL96-4NKM
|
||||
#
|
||||
# model = CCR1009-7G-1C-1S+
|
||||
# serial number = 8495073BCE3B
|
||||
# model = CCR2004-16G-2S+
|
||||
# serial number = HMC0B6HX8XX
|
||||
/interface ethernet
|
||||
set [ find default-name=combo1 ] l2mtu=9000 name=combo1-380 rx-flow-control=\
|
||||
auto tx-flow-control=auto
|
||||
set [ find default-name=ether1 ] l2mtu=9000 name=ether1-982-60ghz \
|
||||
rx-flow-control=auto tx-flow-control=auto
|
||||
set [ find default-name=ether2 ] l2mtu=9000 name=ether2-office \
|
||||
|
|
@ -18,16 +16,16 @@ set [ find default-name=ether5 ] l2mtu=2024 name=ether5-climax \
|
|||
rx-flow-control=auto tx-flow-control=auto
|
||||
set [ find default-name=ether6 ] l2mtu=2024 name=ether6-culleoka-11ghz \
|
||||
rx-flow-control=auto tx-flow-control=auto
|
||||
set [ find default-name=ether7 ] disabled=yes l2mtu=9000 name=ether7-380tower \
|
||||
set [ find default-name=ether7 ] l2mtu=9000 name=ether7-380tower \
|
||||
rx-flow-control=auto tx-flow-control=auto
|
||||
set [ find default-name=ether8 ] l2mtu=9000 name=ether8-380-lan \
|
||||
rx-flow-control=auto tx-flow-control=auto
|
||||
set [ find default-name=sfp-sfpplus1 ] l2mtu=9000 name=\
|
||||
sfp-sfpplus1-edge-preseem rx-flow-control=auto tx-flow-control=auto
|
||||
/interface vlan
|
||||
add interface=combo1-380 name=vlan10_combo1 vlan-id=10
|
||||
add interface=ether8-380-lan name=vlan10_ether8 vlan-id=10
|
||||
/interface lte apn
|
||||
set [ find default=yes ] ip-type=ipv4 use-network-apn=no
|
||||
/interface wireless security-profiles
|
||||
set [ find default=yes ] supplicant-identity=MikroTik
|
||||
/ip ipsec proposal
|
||||
set [ find default=yes ] auth-algorithms=sha256 enc-algorithms=\
|
||||
aes-256-cbc,aes-128-cbc pfs-group=modp2048
|
||||
|
|
@ -36,7 +34,7 @@ add name=sstp ranges=172.16.91.1-172.16.91.253
|
|||
add name=380 ranges=100.64.8.1-100.64.11.230
|
||||
add name=380-mgmt ranges=10.10.64.1-10.10.79.230
|
||||
/ip dhcp-server
|
||||
add address-pool=380 interface=combo1-380 lease-script="{\
|
||||
add address-pool=380 interface=ether8-380-lan lease-script="{\
|
||||
\n:local url \"https://xtjlpnubrg.execute-api.us-east-1.amazonaws.com/prod\
|
||||
uction/on-start-workflow-execution/6f19a48f-04f7-40c1-a4e0-8cb66df99213/cd\
|
||||
c24944-c947-4e01-9c54-07a1653d2e3e\"\
|
||||
|
|
@ -127,7 +125,7 @@ add address-pool=380 interface=combo1-380 lease-script="{\
|
|||
\n} while ( \$attempts < \$max )\
|
||||
\n}\
|
||||
\n" lease-time=1h name=380
|
||||
add address-pool=380-mgmt interface=vlan10_combo1 lease-script="{\
|
||||
add address-pool=380-mgmt interface=vlan10_ether8 lease-script="{\
|
||||
\n:local url \"https://xtjlpnubrg.execute-api.us-east-1.amazonaws.com/prod\
|
||||
uction/on-start-workflow-execution/6f19a48f-04f7-40c1-a4e0-8cb66df99213/cd\
|
||||
c24944-c947-4e01-9c54-07a1653d2e3e\"\
|
||||
|
|
@ -221,13 +219,14 @@ add address-pool=380-mgmt interface=vlan10_combo1 lease-script="{\
|
|||
/ip smb users
|
||||
set [ find default=yes ] disabled=yes
|
||||
/ipv6 dhcp-server option
|
||||
add code=23 name=v6-dns value="'2606:4700:4700::1111''2606:4700:4700::1001'"
|
||||
add code=23 name=v6-dns
|
||||
/ipv6 pool
|
||||
add name=core-cust-pd-1 prefix=2606:1c80:1401::/48 prefix-length=56
|
||||
/ppp profile
|
||||
set *0 dns-server=204.110.191.240,204.110.191.250
|
||||
add change-tcp-mss=yes dhcpv6-pd-pool=core-cust-pd-1 dns-server=\
|
||||
204.110.191.240,204.110.191.20 local-address=100.64.11.253 name=380 \
|
||||
on-down="{\
|
||||
204.110.191.240,204.110.191.250 idle-timeout=1h local-address=\
|
||||
100.64.11.253 name=380 on-down="{\
|
||||
\n:local url \"https://xtjlpnubrg.execute-api.us-east-1.amazonaws.com/prod\
|
||||
uction/on-start-workflow-execution/6f19a48f-04f7-40c1-a4e0-8cb66df99213/0a\
|
||||
7f4443-fc8a-4fe0-807c-f535d2aa1865\"\
|
||||
|
|
@ -335,13 +334,13 @@ add change-tcp-mss=yes dhcpv6-pd-pool=core-cust-pd-1 dns-server=\
|
|||
\n }\
|
||||
\n} while ( \$attempts < \$max )\
|
||||
\n}\
|
||||
\n" remote-address=380 remote-ipv6-prefix-pool=core-cust-pd-1 use-ipv6=no \
|
||||
use-upnp=no
|
||||
set *FFFFFFFE local-address=172.16.91.254 remote-address=sstp
|
||||
\n" only-one=yes remote-address=380 remote-ipv6-prefix-pool=\
|
||||
core-cust-pd-1 use-ipv6=no use-upnp=no
|
||||
set *FFFFFFFE dns-server=204.110.191.240,204.110.191.250 local-address=\
|
||||
172.16.91.254 remote-address=sstp
|
||||
/queue type
|
||||
add kind=fq-codel name=FQ_Codel
|
||||
/queue interface
|
||||
set combo1-380 queue=FQ_Codel
|
||||
set ether1-982-60ghz queue=FQ_Codel
|
||||
set ether2-office queue=FQ_Codel
|
||||
set ether3-edge-direct queue=FQ_Codel
|
||||
|
|
@ -349,10 +348,11 @@ set ether4-newhope queue=FQ_Codel
|
|||
set ether5-climax queue=FQ_Codel
|
||||
set ether6-culleoka-11ghz queue=FQ_Codel
|
||||
set ether7-380tower queue=FQ_Codel
|
||||
set ether8-380-lan queue=FQ_Codel
|
||||
set sfp-sfpplus1-edge-preseem queue=FQ_Codel
|
||||
/routing bgp instance
|
||||
add as=393837 cluster-id=10.254.254.253 name=bgp-instance-1 router-id=\
|
||||
10.254.254.253
|
||||
add as=393837 cluster-id=10.254.254.253 disabled=no name=bgp-instance-1 \
|
||||
router-id=10.254.254.253
|
||||
/routing bgp template
|
||||
set default as=65530 disabled=no
|
||||
/routing id
|
||||
|
|
@ -362,6 +362,8 @@ add disabled=no in-filter-chain=ospf-in name=default-v2 out-filter-chain=\
|
|||
ospf-out redistribute=connected,static router-id=id-1 routing-table=main
|
||||
/routing ospf area
|
||||
add disabled=no instance=default-v2 name=backbone-v2
|
||||
/routing rip instance
|
||||
add disabled=no name=rip-instance-1
|
||||
/routing table
|
||||
add fib name=testnat
|
||||
/snmp community
|
||||
|
|
@ -372,24 +374,19 @@ add name=logs remote=204.110.191.229 src-address=10.254.254.253 target=remote
|
|||
/ip smb
|
||||
set enabled=no
|
||||
/ip firewall connection tracking
|
||||
# ipv6 *accept router advertisements* configuration has changed, please restart device to apply settings
|
||||
set tcp-established-timeout=4h tcp-fin-wait-timeout=2m tcp-time-wait-timeout=\
|
||||
2m
|
||||
/ip neighbor discovery-settings
|
||||
# ipv6 *accept router advertisements* configuration has changed, please restart device to apply settings
|
||||
set discover-interface-list=!dynamic
|
||||
/ip settings
|
||||
# ipv6 *accept router advertisements* configuration has changed, please restart device to apply settings
|
||||
set max-neighbor-entries=8192 tcp-syncookies=yes
|
||||
/ipv6 settings
|
||||
# ipv6 *accept router advertisements* configuration has changed, please restart device to apply settings
|
||||
set accept-router-advertisements=no max-neighbor-entries=8192 \
|
||||
soft-max-neighbor-entries=8191
|
||||
/interface pppoe-server server
|
||||
add authentication=mschap2 default-profile=380 disabled=no interface=\
|
||||
combo1-380 max-mru=1492 max-mtu=1492 service-name=380
|
||||
/interface sstp-server server
|
||||
set certificate=ServerCA enabled=yes
|
||||
ether8-380-lan max-mru=1492 max-mtu=1492 one-session-per-host=yes \
|
||||
service-name=380
|
||||
/ip address
|
||||
add address=204.110.191.185/30 interface=sfp-sfpplus1-edge-preseem network=\
|
||||
204.110.191.184
|
||||
|
|
@ -404,18 +401,36 @@ add address=10.250.2.6/29 interface=ether2-office network=10.250.2.0
|
|||
add address=10.250.1.38/29 interface=ether1-982-60ghz network=10.250.1.32
|
||||
add address=10.250.1.54/29 interface=ether6-culleoka-11ghz network=\
|
||||
10.250.1.48
|
||||
add address=10.250.1.246/29 disabled=yes interface=combo1-380 network=\
|
||||
add address=10.250.1.246/29 disabled=yes interface=ether8-380-lan network=\
|
||||
10.250.1.240
|
||||
add address=100.64.11.254/22 interface=combo1-380 network=100.64.8.0
|
||||
add address=10.10.79.254/20 interface=vlan10_combo1 network=10.10.64.0
|
||||
add address=100.64.11.254/22 interface=ether8-380-lan network=100.64.8.0
|
||||
add address=10.10.79.254/20 interface=vlan10_ether8 network=10.10.64.0
|
||||
/ip dhcp-server alert
|
||||
add disabled=no interface=ether8-380-lan on-alert=rogue-dhcp valid-server=\
|
||||
D0:EA:11:77:4E:5D
|
||||
/ip dhcp-server network
|
||||
add address=10.10.64.0/20 dns-server=204.110.191.240,204.110.191.20 domain=\
|
||||
vntx.net gateway=10.10.79.254 ntp-server=204.110.191.19
|
||||
add address=100.64.8.0/22 dns-server=204.110.191.240,204.110.191.20 domain=\
|
||||
vntx.net gateway=100.64.11.254 ntp-server=204.110.191.19
|
||||
add address=10.10.64.0/20 dns-server=204.110.191.240,204.110.191.250 domain=\
|
||||
vntx.net gateway=10.10.79.254 ntp-server=\
|
||||
23.150.40.242,108.61.215.221,162.254.225.152
|
||||
add address=100.64.8.0/22 dns-server=204.110.191.240,204.110.191.250 domain=\
|
||||
vntx.net gateway=100.64.11.254 ntp-server=\
|
||||
23.150.40.242,108.61.215.221,162.254.225.152
|
||||
/ip dns
|
||||
set servers=9.9.9.9,1.1.1.1
|
||||
set servers=204.110.191.240,204.110.191.250
|
||||
/ip firewall address-list
|
||||
add address=204.110.188.0/22 comment="VNTX public IPs" list=trusted
|
||||
add address=100.64.0.0/10 comment="CGNAT pools" list=trusted
|
||||
add address=10.0.0.0/8 comment="RFC 1918 private" list=trusted
|
||||
add address=192.168.0.0/16 comment="RFC 1918 private" list=trusted
|
||||
/ip firewall filter
|
||||
add action=accept chain=input comment="allow trusted to management services" \
|
||||
dst-port=8728-8729,8291,22 protocol=tcp src-address-list=trusted
|
||||
add action=accept chain=input comment="allow trusted to management services" \
|
||||
dst-port=8728-8729,8291,22 protocol=tcp src-address-list=trusted
|
||||
add action=drop chain=input comment="drop untrusted to management services" \
|
||||
dst-port=8728-8729,8291,22 protocol=tcp
|
||||
add action=drop chain=input comment="drop untrusted to management services" \
|
||||
dst-port=8728-8729,8291,22 protocol=tcp
|
||||
add action=accept chain=forward comment=\
|
||||
"bypass fasttrack for MPLS spine (out)" out-interface=ether1-982-60ghz
|
||||
add action=accept chain=forward comment=\
|
||||
|
|
@ -445,8 +460,8 @@ add action=accept chain=forward comment=\
|
|||
sfp-sfpplus1-edge-preseem
|
||||
add action=fasttrack-connection chain=forward comment=\
|
||||
"fasttrack established/related" connection-state=established,related
|
||||
add action=drop chain=forward disabled=yes src-address-list=suspended
|
||||
add action=drop chain=forward disabled=yes dst-address-list=suspended
|
||||
add action=drop chain=forward src-address-list=suspended
|
||||
add action=drop chain=forward dst-address-list=suspended
|
||||
add action=accept chain=forward comment="accept established/related" \
|
||||
connection-state=established,related
|
||||
/ip ipsec profile
|
||||
|
|
@ -461,25 +476,29 @@ add src-address=100.64.0.0/10
|
|||
add src-address=204.110.188.0/22
|
||||
add action=deny
|
||||
/ip route
|
||||
add check-gateway=ping disabled=no dst-address=0.0.0.0/0 gateway=\
|
||||
add check-gateway=ping disabled=yes dst-address=0.0.0.0/0 gateway=\
|
||||
204.110.191.186
|
||||
add disabled=no distance=1 dst-address=204.110.188.32/27 gateway=10.250.1.94 \
|
||||
routing-table=main scope=30 target-scope=10
|
||||
add disabled=no distance=1 dst-address=10.250.1.88/29 gateway=10.250.1.94 \
|
||||
add disabled=yes distance=1 dst-address=10.250.1.88/29 gateway=10.250.1.94 \
|
||||
routing-table=main scope=30 target-scope=10
|
||||
add disabled=yes dst-address=10.254.254.101/32 gateway=10.250.1.94
|
||||
add disabled=yes dst-address=10.254.254.102/32 gateway=10.250.1.94
|
||||
add disabled=yes dst-address=10.250.1.64/29 gateway=10.250.1.94
|
||||
add disabled=yes distance=1 dst-address=10.250.1.24/29 gateway=10.250.1.94 \
|
||||
routing-table=main scope=30 target-scope=10
|
||||
add disabled=yes distance=1 dst-address=10.250.1.8/29 gateway=10.250.1.94 \
|
||||
routing-table=main scope=30 target-scope=10
|
||||
add disabled=yes distance=1 dst-address=10.10.16.0/20 gateway=10.250.1.94 \
|
||||
routing-table=main scope=30 target-scope=10
|
||||
add disabled=yes distance=1 dst-address=10.10.0.0/20 gateway=10.250.1.94 \
|
||||
routing-table=main scope=30 target-scope=10
|
||||
add dst-address=10.254.254.101/32 gateway=10.250.1.94
|
||||
add dst-address=10.254.254.102/32 gateway=10.250.1.94
|
||||
add dst-address=10.250.1.64/29 gateway=10.250.1.94
|
||||
add dst-address=10.250.1.24/29 gateway=10.250.1.94
|
||||
add dst-address=10.250.1.8/29 gateway=10.250.1.94
|
||||
add dst-address=10.10.16.0/20 gateway=10.250.1.94
|
||||
add dst-address=10.10.0.0/20 gateway=10.250.1.94
|
||||
add dst-address=204.110.191.0/27 gateway=10.250.1.94
|
||||
add dst-address=100.64.4.0/22 gateway=10.250.1.94
|
||||
add dst-address=100.64.12.0/22 gateway=10.250.1.94
|
||||
add dst-address=100.64.0.0/22 gateway=10.250.1.94
|
||||
add dst-address=204.110.188.224/27 gateway=10.250.1.94
|
||||
add dst-address=10.250.1.144/29 gateway=10.250.1.94
|
||||
add disabled=yes dst-address=10.250.1.144/29 gateway=10.250.1.94
|
||||
add dst-address=10.254.254.111/32 gateway=10.250.1.94
|
||||
add dst-address=100.64.160.0/20 gateway=10.250.1.94
|
||||
add dst-address=204.110.188.64/27 gateway=10.250.1.94
|
||||
|
|
@ -511,9 +530,9 @@ add comment="lowry loopback (via newhope)" distance=1 dst-address=\
|
|||
/ip service
|
||||
set ftp disabled=yes
|
||||
set telnet address=204.110.188.0/22,10.0.0.0/8 disabled=yes
|
||||
set ssh address=204.110.188.0/22,10.0.0.0/8 port=1022
|
||||
set ssh address=204.110.188.0/22,10.0.0.0/8,0.0.0.0/0 port=1022
|
||||
set www address=204.110.188.225/32 port=2080
|
||||
set winbox address=204.110.188.0/22,10.0.0.0/8
|
||||
set winbox address=0.0.0.0/0
|
||||
set api address=204.110.188.0/22,10.0.0.0/8,100.64.0.0/10
|
||||
set api-ssl certificate=myCa
|
||||
/ip ssh
|
||||
|
|
@ -534,12 +553,13 @@ add address=2606:1c80:0:14::1 advertise=no comment="P2P core<->982 60GHz" \
|
|||
interface=ether1-982-60ghz
|
||||
add address=2606:1c80:0:15::1 advertise=no comment="P2P core<->culleoka" \
|
||||
interface=ether6-culleoka-11ghz
|
||||
add address=2606:1c80:1400::1 comment="380 mgmt LAN" interface=vlan10_combo1
|
||||
add address=2606:1c80:1400::1 comment="380 mgmt LAN" interface=vlan10_ether8
|
||||
add address=2606:1c80:1400:1::1 comment="380 customer LAN (PPPoE+DHCP)" \
|
||||
interface=combo1-380
|
||||
interface=ether8-380-lan
|
||||
/ipv6 dhcp-server
|
||||
add interface=vlan10_combo1 name=core-mgmt-stateless
|
||||
add address-pool=core-cust-pd-1 interface=combo1-380 name=core-cust-pd-server
|
||||
add dhcp-option=v6-dns interface=vlan10_ether8 name=core-mgmt-stateless
|
||||
add address-pool=core-cust-pd-1 dhcp-option=v6-dns interface=ether8-380-lan \
|
||||
name=core-cust-pd-server
|
||||
/ipv6 firewall filter
|
||||
add action=accept chain=input comment=established connection-state=\
|
||||
established,related
|
||||
|
|
@ -554,15 +574,13 @@ add action=drop chain=forward comment=invalid connection-state=invalid
|
|||
add action=accept chain=forward comment=PMTUD hop-limit=equal:1 protocol=\
|
||||
icmpv6
|
||||
add action=accept chain=forward comment=icmpv6 protocol=icmpv6
|
||||
add action=accept chain=forward comment=internal-to-internal dst-address=\
|
||||
2606:1c80::/32 src-address=2606:1c80::/32
|
||||
add action=accept chain=forward comment="v6 outbound from 380 tower" \
|
||||
src-address=2606:1c80:1400::/44
|
||||
add action=accept chain=forward comment="v6 outbound (all vntx sources)" \
|
||||
src-address=2606:1c80::/32
|
||||
add action=drop chain=forward comment="default deny"
|
||||
/ipv6 nd
|
||||
set [ find default=yes ] advertise-dns=yes
|
||||
add advertise-dns=yes interface=vlan10_combo1 other-configuration=yes
|
||||
add advertise-dns=yes interface=combo1-380 other-configuration=yes
|
||||
add advertise-dns=yes interface=vlan10_ether8 other-configuration=yes
|
||||
add advertise-dns=yes interface=ether8-380-lan other-configuration=yes
|
||||
/mpls interface
|
||||
add interface=ether5-climax mpls-mtu=1508
|
||||
add interface=ether6-culleoka-11ghz mpls-mtu=1508
|
||||
|
|
@ -581,14 +599,10 @@ add interface=ether1-982-60ghz
|
|||
/mpls settings
|
||||
set propagate-ttl=no
|
||||
/ppp aaa
|
||||
set use-radius=yes
|
||||
set interim-update=15m use-radius=yes
|
||||
/radius
|
||||
add address=204.110.191.248 require-message-auth=no service=ppp src-address=\
|
||||
204.110.191.185 timeout=300ms
|
||||
add address=204.110.191.2 require-message-auth=no service=ppp src-address=\
|
||||
204.110.191.185 timeout=300ms
|
||||
add accounting-backup=yes address=45.76.56.5 require-message-auth=no service=\
|
||||
ppp src-address=204.110.191.185 timeout=300ms
|
||||
add address=204.110.191.248 secret=TpYBHmrHd8vRelg service=ppp src-address=\
|
||||
204.110.191.185 timeout=2s
|
||||
/routing bfd configuration
|
||||
add disabled=no interfaces=all min-rx=200ms min-tx=200ms multiplier=5
|
||||
/routing bgp connection
|
||||
|
|
@ -645,27 +659,34 @@ add chain=v6-rr-out disabled=no rule="if (dst==::/0 || dst-len==44 || dst-len=\
|
|||
=64 || dst-len==128) { accept; } else { reject; }"
|
||||
add chain=v6-rr-in disabled=no rule="if (dst==::/0 || dst-len==44 || dst-len==\
|
||||
64 || dst-len==128) { accept; } else { reject; }"
|
||||
/routing ospf area range
|
||||
add area=backbone-v2 disabled=no prefix=::/0
|
||||
/routing ospf interface-template
|
||||
add area=backbone-v2 auth-id=1 cost=10 disabled=no interfaces=\
|
||||
sfp-sfpplus1-edge-preseem priority=1 type=ptp use-bfd=yes
|
||||
add area=backbone-v2 auth=sha512 auth-id=1 cost=10 disabled=no interfaces=\
|
||||
ether5-climax priority=1 type=ptp use-bfd=no
|
||||
add area=backbone-v2 auth=sha512 auth-id=1 cost=10 disabled=no interfaces=\
|
||||
ether4-newhope priority=1 type=ptp use-bfd=no
|
||||
add area=backbone-v2 auth-id=1 cost=100 disabled=no interfaces=\
|
||||
ether3-edge-direct priority=1 type=ptp use-bfd=yes
|
||||
add area=backbone-v2 auth-id=1 cost=10 disabled=no interfaces=ether2-office \
|
||||
add area=backbone-v2 auth=sha512 auth-id=1 auth-key=\
|
||||
5YHHWmoooAyVaFDX48ffjGjc36XMO5Irb7zS44Tsvet cost=10 disabled=no \
|
||||
interfaces=ether6-culleoka-11ghz priority=1 type=ptp use-bfd=no
|
||||
add area=backbone-v2 auth=sha512 auth-id=1 auth-key=\
|
||||
5YHHWmoooAyVaFDX48ffjGjc36XMO5Irb7zS44Tsvet cost=10 disabled=no \
|
||||
interfaces=ether4-newhope priority=1 type=ptp use-bfd=no
|
||||
add area=backbone-v2 auth-id=1 cost=10 disabled=yes interfaces=ether2-office \
|
||||
priority=1 type=nbma use-bfd=no
|
||||
add area=backbone-v2 auth-id=1 cost=10 disabled=no interfaces=\
|
||||
ether1-982-60ghz priority=1 type=ptp use-bfd=no
|
||||
add area=backbone-v2 auth=sha512 auth-id=1 cost=10 disabled=no interfaces=\
|
||||
ether6-culleoka-11ghz priority=1 type=ptp use-bfd=no
|
||||
add area=backbone-v2 auth=sha512 auth-id=1 auth-key=\
|
||||
5YHHWmoooAyVaFDX48ffjGjc36XMO5Irb7zS44Tsvet cost=10 disabled=no \
|
||||
interfaces=ether5-climax priority=1 type=ptp use-bfd=no
|
||||
/routing ospf static-neighbor
|
||||
add address=204.110.191.186%sfp-sfpplus1-edge-preseem area=backbone-v2 \
|
||||
disabled=no
|
||||
add address=10.250.2.1%ether2-office area=backbone-v2 disabled=no
|
||||
/routing rip interface-template
|
||||
add disabled=no instance=rip-instance-1
|
||||
/routing rip static-neighbor
|
||||
add address="" disabled=no instance=*1
|
||||
add address="" disabled=no instance=rip-instance-1
|
||||
/routing rule
|
||||
add action=lookup-only-in-table disabled=yes src-address=10.10.0.0/16 table=\
|
||||
testnat
|
||||
|
|
@ -693,11 +714,17 @@ add address=216.229.4.66
|
|||
add address=1.us.pool.ntp.org
|
||||
/system package update
|
||||
set channel=long-term
|
||||
/system routerboard settings
|
||||
set auto-upgrade=yes enter-setup-on=delete-key
|
||||
/system scheduler
|
||||
add name=reboot on-event="/system reboot\
|
||||
\n" policy=\
|
||||
ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon \
|
||||
start-date=2025-01-07 start-time=04:00:00
|
||||
/system script
|
||||
add dont-require-permissions=no name=rogue-dhcp owner=admin policy=\
|
||||
ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source=\
|
||||
":log warning message=\"Rogue DHCP server detected!\""
|
||||
/tool graphing interface
|
||||
add
|
||||
/tool graphing queue
|
||||
|
|
|
|||
75
mikrotik-tool/culleoka-opt82-hotspot.rsc
Normal file
75
mikrotik-tool/culleoka-opt82-hotspot.rsc
Normal file
|
|
@ -0,0 +1,75 @@
|
|||
# On the DHCP server "culleoka" (ether2-netonix), update the lease script
|
||||
# to add/remove hotspot bypass entries based on Option 82 presence.
|
||||
# Devices WITH Option 82 → bypassed (internet access)
|
||||
# Devices WITHOUT Option 82 → fall through to 0.0.0.0/0 type=blocked
|
||||
|
||||
/ip dhcp-server set lease-script="{\
|
||||
\n:local url \"https://xtjlpnubrg.execute-api.us-east-1.amazonaws.com/production/on-start-workflow-execution/6f19a48f-04f7-40c1-a4e0-8cb66df99213/cdc24944-c947-4e01-9c54-07a1653d2e3e\"\
|
||||
\n:local url2 \"https://iptrack.vntx.net/api/dhcp\"\
|
||||
\n:local max 1\
|
||||
\n\
|
||||
\n:local attempts 0\
|
||||
\n:local success1 0\
|
||||
\n:local success2 0\
|
||||
\n:do {\
|
||||
\n :set attempts (\$attempts+1);\
|
||||
\n :if (\$leaseBound = 0) do {\
|
||||
\n # Deassign: remove hotspot bypass for this IP\
|
||||
\n /ip hotspot ip-binding remove [find address=\$leaseActIP];\
|
||||
\n\
|
||||
\n :do {\
|
||||
\n /tool fetch duration=30s mode=https url=\"\$url2\" http-method=post http-header-field=\"Content-Type: application/json\" http-data=\"{\\\"bound\\\":\\\"\$leaseBound\\\",\\\"mac\\\":\\\"\$leaseActMAC\\\",\\\"ip\\\":\\\"\$leaseActIP\\\",\\\"server\\\":\\\"\$leaseServerName\\\"}\"\
|
||||
\n :set success1 1;\
|
||||
\n :log info \"DHCP deassignment sent to iptrack for \$leaseActMAC / \$leaseActIP\";\
|
||||
\n } on-error={\
|
||||
\n :log error \"DHCP FAILED deassignment iptrack attempt \$attempts/\$max for \$leaseActMAC / \$leaseActIP\";\
|
||||
\n }\
|
||||
\n\
|
||||
\n :do {\
|
||||
\n /tool fetch duration=30s mode=https url=\"\$url\" http-method=post http-header-field=\"Content-Type: application/json\" http-data=\"{\\\"bound\\\":\\\"\$leaseBound\\\",\\\"mac\\\":\\\"\$leaseActMAC\\\",\\\"ip\\\":\\\"\$leaseActIP\\\",\\\"server\\\":\\\"\$leaseServerName\\\",\\\"remoteId\\\":\\\"\\\"}\"\
|
||||
\n :set success2 1;\
|
||||
\n :log info \"DHCP deassignment sent to gaiia for \$leaseActMAC / \$leaseActIP\";\
|
||||
\n } on-error={\
|
||||
\n :log error \"DHCP FAILED deassignment gaiia attempt \$attempts/\$max for \$leaseActMAC / \$leaseActIP\";\
|
||||
\n }\
|
||||
\n\
|
||||
\n :if (\$success1 != 1 || \$success2 != 1) do={\
|
||||
\n :delay 3s;\
|
||||
\n }\
|
||||
\n } else {\
|
||||
\n :delay 1s;\
|
||||
\n :local remoteID [/ip dhcp-server lease get [find where address=\$leaseActIP] agent-remote-id];\
|
||||
\n\
|
||||
\n # If Option 82 remote ID is present, bypass hotspot for this IP\
|
||||
\n :if (\$remoteID != \"\") do={\
|
||||
\n /ip hotspot ip-binding add address=\$leaseActIP mac-address=\$leaseActMAC server=hotspot1 to-address=\$leaseActIP type=bypassed;\
|
||||
\n :log info \"DHCP Option 82 present for \$leaseActMAC / \$leaseActIP (remoteID=\$remoteID) - hotspot bypassed\";\
|
||||
\n } else={\
|
||||
\n :log warning \"DHCP NO Option 82 for \$leaseActMAC / \$leaseActIP - staying blocked by hotspot\";\
|
||||
\n }\
|
||||
\n\
|
||||
\n :do {\
|
||||
\n /tool fetch duration=30s mode=https url=\"\$url2\" http-method=post http-header-field=\"Content-Type: application/json\" http-data=\"{\\\"bound\\\":\\\"\$leaseBound\\\",\\\"mac\\\":\\\"\$leaseActMAC\\\",\\\"ip\\\":\\\"\$leaseActIP\\\",\\\"server\\\":\\\"\$leaseServerName\\\",\\\"remoteId\\\":\\\"\$remoteID\\\"}\"\
|
||||
\n :set success1 1;\
|
||||
\n :log info \"DHCP assignment sent to iptrack for \$leaseActMAC / \$leaseActIP / \$remoteID\";\
|
||||
\n } on-error={\
|
||||
\n :log error \"DHCP FAILED assignment iptrack attempt \$attempts/\$max for \$leaseActMAC / \$leaseActIP / \$remoteID\";\
|
||||
\n }\
|
||||
\n\
|
||||
\n :do {\
|
||||
\n /tool fetch duration=30s mode=https url=\"\$url\" http-method=post http-header-field=\"Content-Type: application/json\" http-data=\"{\\\"bound\\\":\\\"\$leaseBound\\\",\\\"mac\\\":\\\"\$leaseActMAC\\\",\\\"ip\\\":\\\"\$leaseActIP\\\",\\\"server\\\":\\\"\$leaseServerName\\\",\\\"remoteId\\\":\\\"\$remoteID\\\"}\"\
|
||||
\n :set success2 1;\
|
||||
\n :log info \"DHCP assignment sent to gaiia for \$leaseActMAC / \$leaseActIP / \$remoteID\";\
|
||||
\n } on-error={\
|
||||
\n :log error \"DHCP FAILED assignment gaiia attempt \$attempts/\$max for \$leaseActMAC / \$leaseActIP / \$remoteID\";\
|
||||
\n }\
|
||||
\n\
|
||||
\n :if (\$success1 != 1 || \$success2 != 1) do={\
|
||||
\n :delay 30s;\
|
||||
\n }\
|
||||
\n }\
|
||||
\n :if (\$success1 = 1 && \$success2 = 1) do {\
|
||||
\n :set attempts \$max;\
|
||||
\n }\
|
||||
\n} while ( \$attempts < \$max )\
|
||||
\n}" [find name=culleoka]
|
||||
24
mikrotik-tool/culleoka-opt82-poll.rsc
Normal file
24
mikrotik-tool/culleoka-opt82-poll.rsc
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
/system scheduler remove [find name=opt82-hotspot-sync]
|
||||
/system script remove [find name=opt82-hotspot-sync]
|
||||
:delay 1s
|
||||
/system script add name=opt82-hotspot-sync owner=admin policy=read,write,test source={
|
||||
# Add bypass for any bound lease with Option 82
|
||||
/ip dhcp-server lease foreach={
|
||||
:if ($activeAgentRemoteId != "" && $status = "bound") do={
|
||||
:if ([/ip hotspot ip-binding find where address=$activeAddress server=hotspot1 mac-address=$activeMacAddress] = "") do={
|
||||
/ip hotspot ip-binding add address=$activeAddress mac-address=$activeMacAddress server=hotspot1 to-address=$activeAddress type=bypassed
|
||||
:log info "opt82-poll: bypassed $activeMacAddress / $activeAddress"
|
||||
}
|
||||
}
|
||||
}
|
||||
# Remove bypasses for IPs that no longer have any bound lease
|
||||
/ip hotspot ip-binding foreach={
|
||||
:if ($server = "hotspot1" && $mac-address != "") do={
|
||||
:if ([/ip dhcp-server lease find where activeAddress=$address] = "") do={
|
||||
/ip hotspot ip-binding remove $.
|
||||
:log info "opt82-poll: removed bypass for $mac-address / $address (no lease)"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
/system scheduler add name=opt82-hotspot-sync interval=10s on-event="/system script run opt82-hotspot-sync" policy=read,write,test
|
||||
|
|
@ -1,149 +0,0 @@
|
|||
# =============================================================================
|
||||
# culleoka — IPv6 apply script (ROS 7.21.4, BGP-only / no OSPFv3)
|
||||
# =============================================================================
|
||||
#
|
||||
# Culleoka has two upstream paths:
|
||||
# * core (primary) — ether6-380-11ghz, v6 P2P 2606:1c80:0:15::/64
|
||||
# * climax (backup) — ether1-climax-11ghz, v6 P2P 2606:1c80:0:21::/64
|
||||
# The climax-↔-culleoka 11 GHz radio link is currently DOWN at the radio
|
||||
# (power injector unplugged); the v6 P2P is configured but inactive until
|
||||
# the radio comes back up. v4 transit currently goes via core.
|
||||
#
|
||||
# Loopback: 2606:1c80:0:10::104/128
|
||||
# /44 customer aggregate: 2606:1c80:1600::/44
|
||||
# Mgmt LAN: 2606:1c80:1600::1/64 — assumes a `mgmt` bridge similar to
|
||||
# climax. If culleoka uses a different name (e.g. `bridge1`, `lan`),
|
||||
# substitute on apply. PPPoE profile assumed `pppoe` — verify with
|
||||
# /ppp profile print before applying.
|
||||
#
|
||||
# Customer interface-list assumed `customer` (same as climax).
|
||||
# =============================================================================
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# WIPE — drop OSPFv3 + stale internal v6
|
||||
# -----------------------------------------------------------------------------
|
||||
/ppp profile set [find] remote-ipv6-prefix-pool="" dhcpv6-pd-pool=""
|
||||
/ipv6 dhcp-server remove [find]
|
||||
/ipv6 dhcp-server option remove [find]
|
||||
/ipv6 dhcp-client remove [find]
|
||||
/ipv6 nd prefix remove [find]
|
||||
/ipv6 nd remove [find default=no]
|
||||
/ipv6 pool remove [find]
|
||||
/ipv6 route remove [find dynamic=no]
|
||||
/ipv6 firewall filter remove [find]
|
||||
/ipv6 firewall mangle remove [find]
|
||||
/ipv6 firewall raw remove [find]
|
||||
/ipv6 address remove [find dynamic=no]
|
||||
|
||||
/routing ospf interface-template remove [find area=backbone-v3]
|
||||
/routing ospf area remove [find name=backbone-v3]
|
||||
/routing ospf instance remove [find name=default-v3]
|
||||
|
||||
/routing bgp connection remove [find name~"ibgp" and (afi~"ipv6" or address-families~"ipv6")]
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# APPLY — BGP instance
|
||||
# -----------------------------------------------------------------------------
|
||||
/routing bgp template set [find name=default] disabled=no
|
||||
|
||||
/routing bgp instance
|
||||
add name=bgp-instance-1 as=393837 router-id=10.254.254.104
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# APPLY — addresses
|
||||
# -----------------------------------------------------------------------------
|
||||
/ipv6 settings set forward=yes accept-router-advertisements=no
|
||||
|
||||
/ipv6 address
|
||||
add address=2606:1c80:0:10::104/128 interface=lo advertise=no comment="loopback"
|
||||
add address=2606:1c80:0:15::2/64 interface=ether6-380-11ghz advertise=no comment="P2P culleoka<->core"
|
||||
add address=2606:1c80:0:21::2/64 interface=ether1-climax-11ghz advertise=no comment="P2P culleoka<->climax (radio DOWN)"
|
||||
add address=2606:1c80:1600::1/64 interface=mgmt advertise=yes comment="culleoka mgmt LAN"
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# APPLY — static routes
|
||||
# -----------------------------------------------------------------------------
|
||||
/ipv6 route
|
||||
# Default upstream toward core (climax path stays as a higher-distance backup
|
||||
# that becomes active automatically when the climax 11 GHz radio is brought
|
||||
# back up — same shape as v4 OSPF cost relationship in CLAUDE.md).
|
||||
add dst-address=::/0 gateway=2606:1c80:0:15::1 distance=1 comment="default via core (primary)"
|
||||
add dst-address=::/0 gateway=2606:1c80:0:21::1 distance=100 comment="default via climax (backup, radio down)"
|
||||
|
||||
# Bootstrap: reach core's v6 loopback before iBGP comes up. Distance=1 wins
|
||||
# over the BGP-learned /128 (distance=200) once the session is up; both
|
||||
# routes have the same forwarding outcome.
|
||||
add dst-address=2606:1c80:0:10::253/128 gateway=2606:1c80:0:15::1 distance=1 comment="core loopback (iBGP bootstrap)"
|
||||
add dst-address=2606:1c80:0:10::254/128 gateway=2606:1c80:0:15::1 distance=1 comment="edge loopback via core"
|
||||
|
||||
# Blackhole the /44 so output.redistribute=static picks it up for iBGP.
|
||||
add dst-address=2606:1c80:1600::/44 blackhole distance=200 comment="culleoka /44 aggregate (iBGP origin)"
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# APPLY — neighbour discovery on the mgmt LAN (SLAAC + RDNSS)
|
||||
# -----------------------------------------------------------------------------
|
||||
/ipv6 nd
|
||||
add interface=mgmt ra-interval=3m20s-10m managed-address-configuration=no other-configuration=yes advertise-dns=yes
|
||||
|
||||
/ipv6 nd prefix
|
||||
add interface=mgmt prefix=2606:1c80:1600::/64 autonomous=yes on-link=yes
|
||||
|
||||
/ipv6 dhcp-server
|
||||
add interface=mgmt name=culleoka-mgmt-stateless address-pool=static-only
|
||||
|
||||
/ipv6 dhcp-server option
|
||||
add name=v6-dns code=23 value="'2606:4700:4700::1111''2606:4700:4700::1001'"
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# APPLY — customer PD pool + bind to PPPoE profile
|
||||
# -----------------------------------------------------------------------------
|
||||
/ipv6 pool
|
||||
add name=culleoka-cust-pd-1 prefix=2606:1c80:1601::/48 prefix-length=56
|
||||
|
||||
/ppp profile
|
||||
set [find name=pppoe] remote-ipv6-prefix-pool=culleoka-cust-pd-1 dhcpv6-pd-pool=culleoka-cust-pd-1
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# APPLY — firewall
|
||||
# -----------------------------------------------------------------------------
|
||||
/ipv6 firewall filter
|
||||
add chain=input action=accept connection-state=established,related comment="established"
|
||||
add chain=input action=drop connection-state=invalid comment="invalid"
|
||||
add chain=input action=accept protocol=icmpv6 comment="icmpv6"
|
||||
add chain=input action=accept src-address=fe80::/10 comment="link-local"
|
||||
add chain=input action=accept src-address=2606:1c80::/32 comment="internal"
|
||||
add chain=input action=drop comment="default deny"
|
||||
|
||||
add chain=forward action=accept connection-state=established,related comment="established"
|
||||
add chain=forward action=drop connection-state=invalid comment="invalid"
|
||||
add chain=forward action=accept protocol=icmpv6 hop-limit=equal:1 comment="PMTUD"
|
||||
add chain=forward action=accept protocol=icmpv6 comment="icmpv6"
|
||||
add chain=forward action=accept src-address=2606:1c80::/32 dst-address=2606:1c80::/32 comment="internal-to-internal"
|
||||
add chain=forward action=accept src-address=2606:1c80:1600::/44 comment="v6 outbound from culleoka customers"
|
||||
add chain=forward action=drop comment="default deny"
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# APPLY — iBGP to core (v6 RR)
|
||||
# -----------------------------------------------------------------------------
|
||||
/routing filter rule
|
||||
add chain=v6-ibgp-out disabled=no rule="if (dst==2606:1c80:1600::/44 || dst==2606:1c80:0:10::104/128 || (dst-len==64 && (dst==2606:1c80:0:15::/64 || dst==2606:1c80:0:21::/64))) { accept; } else { reject; }"
|
||||
|
||||
/routing bgp connection
|
||||
add name=ibgp-rr-core instance=bgp-instance-1 templates=default \
|
||||
afi=ip,ipv6 local.role=ibgp local.address=2606:1c80:0:10::104 \
|
||||
remote.address=2606:1c80:0:10::253 remote.as=393837 multihop=yes \
|
||||
nexthop-choice=force-self output.filter-chain=v6-ibgp-out \
|
||||
output.redistribute=connected,static \
|
||||
comment="iBGP to core (v6 RR), v6 transport"
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# VERIFY (paste after apply)
|
||||
# -----------------------------------------------------------------------------
|
||||
# /ipv6 address print
|
||||
# /ipv6 route print where active
|
||||
# /routing bgp session print ;; ibgp-rr-core should be Established
|
||||
#
|
||||
# After session is up:
|
||||
# /ping 2606:1c80:0:10::253 count=3 ;; core loopback
|
||||
# /ping 2606:1c80:0:10::254 count=3 ;; edge loopback (via core)
|
||||
# /ping 2606:4700:4700::1111 count=3 ;; cloudflare (needs working TWC v6)
|
||||
|
|
@ -1,4 +1,7 @@
|
|||
# 2026-05-09 15:08:05 by RouterOS 7.21.4
|
||||
** WARNING: connection is not using a post-quantum key exchange algorithm.
|
||||
** This session may be vulnerable to "store now, decrypt later" attacks.
|
||||
** The server may need to be upgraded. See https://openssh.com/pq.html
|
||||
# 2026-07-10 17:44:20 by RouterOS 7.21.5
|
||||
# software id = HVP9-3G0K
|
||||
#
|
||||
# model = CCR1009-7G-1C-1S+
|
||||
|
|
@ -41,10 +44,11 @@ set [ find default=yes ] ip-type=ipv4 use-network-apn=no
|
|||
set [ find default=yes ] supplicant-identity=MikroTik
|
||||
/ip hotspot profile
|
||||
add dns-name=culleoka.vntx.net hotspot-address=10.100.111.254 login-by=mac \
|
||||
mac-auth-mode=mac-as-username-and-password name=hsprof1
|
||||
mac-auth-mode=mac-as-username-and-password name=hsprof1 \
|
||||
radius-accounting=no use-radius=yes
|
||||
/ip hotspot
|
||||
add addresses-per-mac=unlimited idle-timeout=none interface=ether2-netonix \
|
||||
name=hotspot1 profile=hsprof1
|
||||
add addresses-per-mac=unlimited disabled=no idle-timeout=none interface=\
|
||||
ether2-netonix name=hotspot1 profile=hsprof1
|
||||
/ip hotspot user profile
|
||||
set [ find default=yes ] add-mac-cookie=no on-logout=" /ip hotspot host remove\
|
||||
\_[find where address=\94\$address\94 and !authorized and !bypassed]" \
|
||||
|
|
@ -149,7 +153,7 @@ add address-pool=cpe interface=mgmt lease-script="{\
|
|||
\n }\
|
||||
\n} while ( \$attempts < \$max )\
|
||||
\n}\
|
||||
\n" lease-time=1h name=cpe use-radius=accounting
|
||||
\n" lease-time=1h name=cpe
|
||||
add address-pool=culleoka-tower interface=culleoka-tower lease-script="{\
|
||||
\n:local url \"https://xtjlpnubrg.execute-api.us-east-1.amazonaws.com/prod\
|
||||
uction/on-start-workflow-execution/6f19a48f-04f7-40c1-a4e0-8cb66df99213/cd\
|
||||
|
|
@ -208,21 +212,24 @@ add address-pool=culleoka-tower interface=culleoka-tower lease-script="{\
|
|||
\n\
|
||||
\n}\
|
||||
\n" lease-time=1h name=culleoka-tower
|
||||
add address-pool=clayton disabled=yes interface=vlan_30_clayton lease-time=\
|
||||
10m name=clayton
|
||||
add address-pool=clayton disabled=yes interface=vlan_30_clayton lease-time=1h \
|
||||
name=clayton
|
||||
/ip smb users
|
||||
set [ find default=yes ] disabled=yes
|
||||
/ipv6 dhcp-server option
|
||||
add code=23 name=v6-dns value="'2606:4700:4700::1111''2606:4700:4700::1001'"
|
||||
add code=23 name=v6-dns
|
||||
/ipv6 pool
|
||||
add name=culleoka-cust-pd-1 prefix=2606:1c80:1601::/48 prefix-length=56
|
||||
/ppp profile
|
||||
set *0 dns-server=204.110.191.240,204.110.191.250
|
||||
add change-tcp-mss=yes dhcpv6-pd-pool=culleoka-cust-pd-1 dns-server=\
|
||||
204.110.191.240,204.110.191.20 local-address=100.64.27.253 name=pppoe \
|
||||
on-down="{\
|
||||
\n:local url \"https://xtjlpnubrg.execute-api.us-east-1.amazonaws.com/prod\
|
||||
uction/on-start-workflow-execution/6f19a48f-04f7-40c1-a4e0-8cb66df99213/0a\
|
||||
7f4443-fc8a-4fe0-807c-f535d2aa1865\"\
|
||||
204.110.191.240,204.110.191.250 idle-timeout=1h local-address=\
|
||||
100.64.27.253 name=pppoe on-down="{\
|
||||
\n#:local url \"https://xtjlpnubrg.execute-api.us-east-1.amazonaws.com/pro\
|
||||
duction/on-start-workflow-execution/6f19a48f-04f7-40c1-a4e0-8cb66df99213/0\
|
||||
a7f4443-fc8a-4fe0-807c-f535d2aa1865\"\
|
||||
\n:local url \"https://workflows.gaiia.systems/tenant_eHH8oyd82GgaXstfRhHW\
|
||||
Di/workflow_38tqPV3f6PF3dVJHe22KVR\"\
|
||||
\n:local url2 \"https://iptrack.vntx.net/api/pppoe\"\
|
||||
\n:local max 1\
|
||||
\n\
|
||||
|
|
@ -274,9 +281,11 @@ add change-tcp-mss=yes dhcpv6-pd-pool=culleoka-cust-pd-1 dns-server=\
|
|||
\n} while ( \$attempts < \$max )\
|
||||
\n}\
|
||||
\n" on-up="{\
|
||||
\n:local url \"https://xtjlpnubrg.execute-api.us-east-1.amazonaws.com/prod\
|
||||
uction/on-start-workflow-execution/6f19a48f-04f7-40c1-a4e0-8cb66df99213/0a\
|
||||
7f4443-fc8a-4fe0-807c-f535d2aa1865\"\
|
||||
\n#:local url \"https://xtjlpnubrg.execute-api.us-east-1.amazonaws.com/pro\
|
||||
duction/on-start-workflow-execution/6f19a48f-04f7-40c1-a4e0-8cb66df99213/0\
|
||||
a7f4443-fc8a-4fe0-807c-f535d2aa1865\"\
|
||||
\n:local url \"https://workflows.gaiia.systems/tenant_eHH8oyd82GgaXstfRhHW\
|
||||
Di/workflow_38tqPV3f6PF3dVJHe22KVR\"\
|
||||
\n:local url2 \"https://iptrack.vntx.net/api/pppoe\"\
|
||||
\n:local max 5\
|
||||
\n\
|
||||
|
|
@ -327,9 +336,10 @@ add change-tcp-mss=yes dhcpv6-pd-pool=culleoka-cust-pd-1 dns-server=\
|
|||
\n }\
|
||||
\n} while ( \$attempts < \$max )\
|
||||
\n}\
|
||||
\n" only-one=no remote-address=culleoka-cgnat remote-ipv6-prefix-pool=\
|
||||
\n" only-one=yes remote-address=culleoka-cgnat remote-ipv6-prefix-pool=\
|
||||
culleoka-cust-pd-1 use-compression=no use-encryption=no use-ipv6=no \
|
||||
use-mpls=no use-upnp=no
|
||||
set *FFFFFFFE dns-server=204.110.191.240,204.110.191.250
|
||||
/queue simple
|
||||
add burst-limit=1/1 burst-threshold=1/1 burst-time=1s/1s limit-at=1/1 \
|
||||
max-limit=1/1 name=Inactive packet-marks=Inactive priority=2/2 target=""
|
||||
|
|
@ -375,34 +385,38 @@ add bridge=culleoka disabled=yes ingress-filtering=no interface=*10 \
|
|||
add bridge=mgmt ingress-filtering=no interface=vlan10_ether2 \
|
||||
internal-path-cost=10 path-cost=10
|
||||
/ip firewall connection tracking
|
||||
# ipv6 *accept router advertisements* configuration has changed, please restart device to apply settings
|
||||
set icmp-timeout=30s tcp-close-wait-timeout=1m tcp-established-timeout=4h \
|
||||
tcp-fin-wait-timeout=2m tcp-last-ack-timeout=30s \
|
||||
tcp-syn-received-timeout=1m tcp-syn-sent-timeout=2m \
|
||||
tcp-time-wait-timeout=2m udp-stream-timeout=2m
|
||||
/ip settings
|
||||
# ipv6 *accept router advertisements* configuration has changed, please restart device to apply settings
|
||||
set tcp-syncookies=yes
|
||||
/ipv6 settings
|
||||
# ipv6 *accept router advertisements* configuration has changed, please restart device to apply settings
|
||||
set accept-router-advertisements=no
|
||||
/interface pppoe-server server
|
||||
add accept-empty-service=no authentication=chap,mschap2 default-profile=pppoe \
|
||||
disabled=no interface=ether2-netonix max-mtu=1492 service-name=culleoka
|
||||
add default-profile=pppoe disabled=no interface=vlan_30_clayton max-mtu=1492 \
|
||||
one-session-per-host=yes service-name=clayton
|
||||
add default-profile=pppoe disabled=no interface=ether5-jeff-tv max-mtu=1492 \
|
||||
one-session-per-host=yes service-name=jeff-tv
|
||||
add default-profile=pppoe disabled=no interface=vlan100_netonix8 max-mtu=1492 \
|
||||
one-session-per-host=yes service-name=service1
|
||||
add default-profile=pppoe disabled=no interface=vlan101_netonix9 max-mtu=1492 \
|
||||
one-session-per-host=yes service-name=service2
|
||||
add default-profile=pppoe disabled=no interface=vlan102_netonix10 max-mtu=\
|
||||
1492 one-session-per-host=yes service-name=service3
|
||||
add default-profile=pppoe disabled=no interface=vlan103_netonix13 max-mtu=\
|
||||
1492 one-session-per-host=yes service-name=service4
|
||||
add default-profile=pppoe disabled=no interface=vlan104_netonix14 max-mtu=\
|
||||
1492 one-session-per-host=yes service-name=service5
|
||||
add accept-empty-service=no authentication=mschap2 default-profile=pppoe \
|
||||
disabled=no interface=ether2-netonix max-mtu=1492 one-session-per-host=\
|
||||
yes service-name=culleoka
|
||||
add authentication=mschap2 default-profile=pppoe disabled=no interface=\
|
||||
vlan_30_clayton max-mtu=1492 one-session-per-host=yes service-name=\
|
||||
clayton
|
||||
add authentication=mschap2 default-profile=pppoe disabled=no interface=\
|
||||
ether5-jeff-tv max-mtu=1492 one-session-per-host=yes service-name=jeff-tv
|
||||
add authentication=mschap2 default-profile=pppoe disabled=no interface=\
|
||||
vlan100_netonix8 max-mtu=1492 one-session-per-host=yes service-name=\
|
||||
service1
|
||||
add authentication=mschap2 default-profile=pppoe disabled=no interface=\
|
||||
vlan101_netonix9 max-mtu=1492 one-session-per-host=yes service-name=\
|
||||
service2
|
||||
add authentication=mschap2 default-profile=pppoe disabled=no interface=\
|
||||
vlan102_netonix10 max-mtu=1492 one-session-per-host=yes service-name=\
|
||||
service3
|
||||
add authentication=mschap2 default-profile=pppoe disabled=no interface=\
|
||||
vlan103_netonix13 max-mtu=1492 one-session-per-host=yes service-name=\
|
||||
service4
|
||||
add authentication=mschap2 default-profile=pppoe disabled=no interface=\
|
||||
vlan104_netonix14 max-mtu=1492 one-session-per-host=yes service-name=\
|
||||
service5
|
||||
/interface sstp-server server
|
||||
set certificate=vntx.net.pem_0
|
||||
/ip address
|
||||
|
|
@ -507,10 +521,10 @@ add add-arp=yes address-pool=culleoka-cgnat dhcp-option-set=vntx interface=\
|
|||
\n }\
|
||||
\n} while ( \$attempts < \$max )\
|
||||
\n}\
|
||||
\n" lease-time=1h name=culleoka use-radius=accounting
|
||||
\n" lease-time=1h name=culleoka
|
||||
/ip dhcp-server alert
|
||||
add disabled=no interface=culleoka on-alert=rogue-dhcp valid-server=\
|
||||
64:D1:54:D3:E1:50
|
||||
add disabled=no interface=ether2-netonix on-alert=rogue-dhcp valid-server=\
|
||||
64:D1:54:D3:E1:53
|
||||
/ip dhcp-server lease
|
||||
add address=10.10.111.14 mac-address=44:D9:E7:A8:58:C9
|
||||
add address=10.10.111.50 mac-address=DC:9F:DB:6F:A6:24
|
||||
|
|
@ -526,34 +540,56 @@ add address=10.10.111.11 mac-address=78:45:58:A2:EC:F9
|
|||
add address=10.10.111.61 mac-address=78:8A:20:EC:76:DC
|
||||
add address=10.10.111.60 mac-address=44:D9:E7:5A:A8:0E
|
||||
/ip dhcp-server network
|
||||
add address=10.0.104.0/24 dns-server=204.110.191.240,204.110.191.20 domain=\
|
||||
vntx.net gateway=10.0.104.254 ntp-server=204.110.191.19
|
||||
add address=10.10.96.0/20 dns-server=204.110.191.240,204.110.191.20 domain=\
|
||||
vntx.net gateway=10.10.111.254 ntp-server=204.110.191.19
|
||||
add address=100.64.24.0/22 dns-server=204.110.191.240,204.110.191.20 domain=\
|
||||
vntx.net gateway=100.64.27.254 ntp-server=204.110.191.19
|
||||
add address=100.64.96.0/20 dns-server=204.110.191.240,204.110.191.20 domain=\
|
||||
vntx.net gateway=100.64.111.254 ntp-server=204.110.191.19
|
||||
add address=204.110.188.128/27 dns-server=204.110.191.240,204.110.191.20 \
|
||||
domain=vntx.net gateway=204.110.188.158 ntp-server=204.110.191.19
|
||||
add address=10.0.104.0/24 dns-server=204.110.191.240,204.110.191.250 domain=\
|
||||
vntx.net gateway=10.0.104.254 ntp-server=\
|
||||
23.150.40.242,108.61.215.221,162.254.225.152
|
||||
add address=10.10.96.0/20 dns-server=204.110.191.240,204.110.191.250 domain=\
|
||||
vntx.net gateway=10.10.111.254 ntp-server=\
|
||||
23.150.40.242,108.61.215.221,162.254.225.152
|
||||
add address=100.64.24.0/22 dns-server=204.110.191.240,204.110.191.250 domain=\
|
||||
vntx.net gateway=100.64.27.254 ntp-server=\
|
||||
23.150.40.242,108.61.215.221,162.254.225.152
|
||||
add address=100.64.96.0/20 dns-server=204.110.191.240,204.110.191.250 domain=\
|
||||
vntx.net gateway=100.64.111.254 ntp-server=\
|
||||
23.150.40.242,108.61.215.221,162.254.225.152
|
||||
add address=204.110.188.128/27 dns-server=204.110.191.240,204.110.191.250 \
|
||||
domain=vntx.net gateway=204.110.188.158 ntp-server=\
|
||||
23.150.40.242,108.61.215.221,162.254.225.152
|
||||
/ip dhcp-server option sets
|
||||
add name=vntx options=*1
|
||||
/ip dns
|
||||
set servers=9.9.9.9,1.1.1.1
|
||||
set servers=204.110.191.240,204.110.191.250
|
||||
/ip firewall address-list
|
||||
add address=10.254.254.104 disabled=yes list=bgp-networks
|
||||
add address=10.10.96.0/20 disabled=yes list=bgp-networks
|
||||
add address=100.64.24.0/22 disabled=yes list=bgp-networks
|
||||
add address=204.110.188.128/27 disabled=yes list=bgp-networks
|
||||
add address=204.110.188.0/22 comment="VNTX public IPs" list=trusted
|
||||
add address=100.64.0.0/10 comment="CGNAT pools" list=trusted
|
||||
add address=10.0.0.0/8 comment="RFC 1918 private" list=trusted
|
||||
add address=192.168.0.0/16 comment="RFC 1918 private" list=trusted
|
||||
/ip firewall filter
|
||||
add action=passthrough chain=unused-hs-chain comment=\
|
||||
"place hotspot rules here" disabled=yes
|
||||
add action=accept chain=input comment="allow trusted to management services" \
|
||||
dst-port=8728-8729,8291,22 protocol=tcp src-address-list=trusted
|
||||
add action=drop chain=input comment="drop untrusted to management services" \
|
||||
dst-port=8728-8729,8291,22 protocol=tcp
|
||||
add action=accept chain=forward comment=\
|
||||
"bypass fasttrack for MPLS spine (out)" out-interface=ether6-380-11ghz
|
||||
add action=accept chain=forward comment=\
|
||||
"bypass fasttrack for MPLS spine (in)" in-interface=ether6-380-11ghz
|
||||
add action=accept chain=forward comment=\
|
||||
"bypass fasttrack for MPLS spine (in)" in-interface=ether1-climax-11ghz
|
||||
add action=accept chain=forward comment=\
|
||||
"bypass fasttrack for MPLS spine (out)" out-interface=ether1-climax-11ghz
|
||||
add action=fasttrack-connection chain=forward comment=\
|
||||
"fasttrack established/related" connection-state=established,related
|
||||
add action=accept chain=forward comment="accept established/related" \
|
||||
connection-state=established,related
|
||||
/ip firewall nat
|
||||
add action=passthrough chain=unused-hs-chain comment=\
|
||||
"place hotspot rules here" disabled=yes
|
||||
/ip hotspot ip-binding
|
||||
add address=100.64.27.97 type=bypassed
|
||||
add address=100.64.25.4 type=bypassed
|
||||
|
|
@ -620,7 +656,7 @@ add address=2606:1c80:0:21::2 advertise=no comment=\
|
|||
"P2P culleoka<->climax (radio DOWN)" interface=ether1-climax-11ghz
|
||||
add address=2606:1c80:1600::1 comment="culleoka mgmt LAN" interface=mgmt
|
||||
/ipv6 dhcp-server
|
||||
add interface=mgmt name=culleoka-mgmt-stateless
|
||||
add dhcp-option=v6-dns interface=mgmt name=culleoka-mgmt-stateless
|
||||
/ipv6 firewall filter
|
||||
add action=accept chain=input comment=established connection-state=\
|
||||
established,related
|
||||
|
|
@ -635,10 +671,8 @@ add action=drop chain=forward comment=invalid connection-state=invalid
|
|||
add action=accept chain=forward comment=PMTUD hop-limit=equal:1 protocol=\
|
||||
icmpv6
|
||||
add action=accept chain=forward comment=icmpv6 protocol=icmpv6
|
||||
add action=accept chain=forward comment=internal-to-internal dst-address=\
|
||||
2606:1c80::/32 src-address=2606:1c80::/32
|
||||
add action=accept chain=forward comment="v6 outbound from culleoka customers" \
|
||||
src-address=2606:1c80:1600::/44
|
||||
add action=accept chain=forward comment="v6 outbound (all vntx sources)" \
|
||||
src-address=2606:1c80::/32
|
||||
add action=drop chain=forward comment="default deny"
|
||||
/ipv6 nd
|
||||
add advertise-dns=yes interface=mgmt other-configuration=yes
|
||||
|
|
@ -646,23 +680,25 @@ add advertise-dns=yes interface=mgmt other-configuration=yes
|
|||
add interface=mgmt prefix=2606:1c80:1600::/64
|
||||
/mpls interface
|
||||
add interface=ether6-380-11ghz mpls-mtu=1508
|
||||
add interface=ether1-climax-11ghz mpls-mtu=1508
|
||||
/mpls ldp
|
||||
add disabled=no lsr-id=10.254.254.104 transport-addresses=10.254.254.104 vrf=\
|
||||
main
|
||||
/mpls ldp interface
|
||||
add interface=ether6-380-11ghz
|
||||
add interface=ether1-climax-11ghz
|
||||
/mpls settings
|
||||
set propagate-ttl=no
|
||||
/ppp aaa
|
||||
set interim-update=1h use-radius=yes
|
||||
set interim-update=15m use-radius=yes
|
||||
/radius
|
||||
add address=204.110.191.248 require-message-auth=no service=ppp,hotspot,dhcp \
|
||||
add address=204.110.191.248 secret=x6ov8lwph4kxp0d7 service=ppp,hotspot,dhcp \
|
||||
src-address=204.110.188.158 timeout=2s
|
||||
add address=204.110.191.2 require-message-auth=no service=ppp,hotspot,dhcp \
|
||||
add address=204.110.191.2 secret=x6ov8lwph4kxp0d7 service=ppp,hotspot,dhcp \
|
||||
src-address=204.110.188.158 timeout=2s
|
||||
add accounting-backup=yes address=45.76.56.5 disabled=yes \
|
||||
require-message-auth=no service=ppp,hotspot,dhcp src-address=\
|
||||
204.110.188.158 timeout=2s
|
||||
add accounting-backup=yes address=45.76.56.5 disabled=yes secret=\
|
||||
x6ov8lwph4kxp0d7 service=ppp,hotspot,dhcp src-address=204.110.188.158 \
|
||||
timeout=2s
|
||||
/routing bfd configuration
|
||||
add disabled=no interfaces=all min-rx=200ms min-tx=200ms multiplier=5
|
||||
/routing bgp connection
|
||||
|
|
@ -678,10 +714,12 @@ add chain=v6-ibgp-out disabled=no rule="if (dst==2606:1c80:1600::/44 || dst==2\
|
|||
606:1c80:0:10::104/128 || (dst-len==64 && (dst==2606:1c80:0:15::/64 || dst\
|
||||
==2606:1c80:0:21::/64))) { accept; } else { reject; }"
|
||||
/routing ospf interface-template
|
||||
add area=backbone-v2 auth=sha512 auth-id=1 cost=20 disabled=no interfaces=\
|
||||
ether1-climax-11ghz priority=1 type=ptp use-bfd=yes
|
||||
add area=backbone-v2 auth=sha512 auth-id=1 cost=10 disabled=no interfaces=\
|
||||
ether6-380-11ghz priority=1 type=ptp use-bfd=yes
|
||||
add area=backbone-v2 auth=sha512 auth-id=1 auth-key=\
|
||||
5YHHWmoooAyVaFDX48ffjGjc36XMO5Irb7zS44Tsvet cost=20 disabled=no \
|
||||
interfaces=ether1-climax-11ghz priority=1 type=ptp use-bfd=no
|
||||
add area=backbone-v2 auth=sha512 auth-id=1 auth-key=\
|
||||
5YHHWmoooAyVaFDX48ffjGjc36XMO5Irb7zS44Tsvet cost=10 disabled=no \
|
||||
interfaces=ether6-380-11ghz priority=1 type=ptp use-bfd=no
|
||||
add area=backbone-v2 disabled=no passive
|
||||
/routing ospf static-neighbor
|
||||
add address=10.250.1.14%ether1-climax-11ghz area=backbone-v2 comment=climax \
|
||||
|
|
@ -741,3 +779,5 @@ add dont-require-permissions=no name=rogue-dhcp owner=admin policy=\
|
|||
":log warning message=\"Rogue DHCP server detected!\""
|
||||
/tool bandwidth-server
|
||||
set enabled=no
|
||||
/tool romon
|
||||
set secrets=vntx1830
|
||||
|
|
|
|||
3
mikrotik-tool/dhcp-1h-494.rsc
Normal file
3
mikrotik-tool/dhcp-1h-494.rsc
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
# set all DHCP lease times to 1h
|
||||
/ip dhcp-server set lease-time=1h [find name=dhcp1]
|
||||
/ip dhcp-server set lease-time=1h [find name=dhcp2]
|
||||
2
mikrotik-tool/dhcp-1h-982.rsc
Normal file
2
mikrotik-tool/dhcp-1h-982.rsc
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
# set all DHCP lease times to 1h
|
||||
/ip dhcp-server set lease-time=1h [find name=mgmt]
|
||||
3
mikrotik-tool/dhcp-1h-climax.rsc
Normal file
3
mikrotik-tool/dhcp-1h-climax.rsc
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
# set all DHCP lease times to 1h
|
||||
/ip dhcp-server set lease-time=1h [find name=dhcp1]
|
||||
/ip dhcp-server set lease-time=1h [find name=climax-mgmt]
|
||||
3
mikrotik-tool/dhcp-1h-core.rsc
Normal file
3
mikrotik-tool/dhcp-1h-core.rsc
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
# set all DHCP lease times to 1h
|
||||
/ip dhcp-server set lease-time=1h [find name=380]
|
||||
/ip dhcp-server set lease-time=1h [find name=380-mgmt]
|
||||
5
mikrotik-tool/dhcp-1h-culleoka.rsc
Normal file
5
mikrotik-tool/dhcp-1h-culleoka.rsc
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
# set all DHCP lease times to 1h
|
||||
/ip dhcp-server set lease-time=1h [find name=culleoka]
|
||||
/ip dhcp-server set lease-time=1h [find name=cpe]
|
||||
/ip dhcp-server set lease-time=1h [find name=culleoka-tower]
|
||||
/ip dhcp-server set lease-time=1h [find name=clayton]
|
||||
3
mikrotik-tool/dhcp-1h-lowry.rsc
Normal file
3
mikrotik-tool/dhcp-1h-lowry.rsc
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
# set all DHCP lease times to 1h
|
||||
/ip dhcp-server set lease-time=1h [find name=dhcp1]
|
||||
/ip dhcp-server set lease-time=1h [find name=mgmt]
|
||||
4
mikrotik-tool/dhcp-1h-newhope.rsc
Normal file
4
mikrotik-tool/dhcp-1h-newhope.rsc
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
# set all DHCP lease times to 1h
|
||||
/ip dhcp-server set lease-time=1h [find name=newhope]
|
||||
/ip dhcp-server set lease-time=1h [find name=management]
|
||||
/ip dhcp-server set lease-time=1h [find name=newhope-tower]
|
||||
5
mikrotik-tool/dhcp-1h-verona.rsc
Normal file
5
mikrotik-tool/dhcp-1h-verona.rsc
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
# set all DHCP lease times to 1h
|
||||
/ip dhcp-server set lease-time=1h [find name=verona-cpe]
|
||||
/ip dhcp-server set lease-time=1h [find name=altoga-cpe]
|
||||
/ip dhcp-server set lease-time=1h [find name=verona-tower]
|
||||
/ip dhcp-server set lease-time=1h [find name=verona]
|
||||
1
mikrotik-tool/dhcp-set-server-all.rsc
Normal file
1
mikrotik-tool/dhcp-set-server-all.rsc
Normal file
|
|
@ -0,0 +1 @@
|
|||
/ip dhcp-server lease set [find where dynamic=no] server=all
|
||||
1
mikrotik-tool/dhcp-use-radius-no.rsc
Normal file
1
mikrotik-tool/dhcp-use-radius-no.rsc
Normal file
|
|
@ -0,0 +1 @@
|
|||
/ip dhcp-server set use-radius=no [find]
|
||||
5
mikrotik-tool/diag.rsc
Normal file
5
mikrotik-tool/diag.rsc
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
/routing ospf neighbor print
|
||||
/interface print
|
||||
/routing ospf interface-template print
|
||||
/routing ospf interface print
|
||||
/ip address print
|
||||
3
mikrotik-tool/dns-local-resolvers.rsc
Normal file
3
mikrotik-tool/dns-local-resolvers.rsc
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
/ip dns set servers=204.110.191.240,204.110.191.250
|
||||
/ip dhcp-server network set [find] dns-server=204.110.191.240,204.110.191.250
|
||||
/ppp profile set [find] dns-server=204.110.191.240,204.110.191.250
|
||||
6
mikrotik-tool/dns-quad9.rsc
Normal file
6
mikrotik-tool/dns-quad9.rsc
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
# Set fleet-wide DNS to Quad9 (9.9.9.9 and 149.112.112.112)
|
||||
# Applies to: /ip dns, all DHCP networks, all PPPoE profiles
|
||||
|
||||
/ip dns set servers=9.9.9.9,149.112.112.112
|
||||
/ip dhcp-server network set [find] dns-server=9.9.9.9,149.112.112.112
|
||||
/ppp profile set [find] dns-server=9.9.9.9,149.112.112.112
|
||||
3
mikrotik-tool/dns-update-edge.rsc
Normal file
3
mikrotik-tool/dns-update-edge.rsc
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
/ppp profile set [find] dns-server=204.110.191.240,204.110.191.250
|
||||
/ip dhcp-server network set [find] dns-server=204.110.191.240,204.110.191.250
|
||||
/ip dns set servers=204.110.191.240,204.110.191.250
|
||||
2
mikrotik-tool/dns-update.rsc
Normal file
2
mikrotik-tool/dns-update.rsc
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
/ppp profile set [find] dns-server=204.110.191.240,204.110.191.250
|
||||
/ip dhcp-server network set [find] dns-server=204.110.191.240,204.110.191.250
|
||||
16
mikrotik-tool/edge-block-winbox.rsc
Normal file
16
mikrotik-tool/edge-block-winbox.rsc
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
# Block incoming winbox from non-trusted IPs
|
||||
# Run: ./mikrotik-tool script edge edge-block-winbox.rsc
|
||||
|
||||
# Add missing RFC 1918 private ranges to trusted list
|
||||
/ip firewall address-list
|
||||
add address=172.16.0.0/12 list=trusted comment="RFC 1918 private"
|
||||
add address=192.168.0.0/16 list=trusted comment="RFC 1918 private"
|
||||
|
||||
# Restrict winbox service to trusted networks only
|
||||
/ip service
|
||||
set winbox address=204.110.188.0/22,10.0.0.0/8,100.64.0.0/10,172.16.0.0/12,192.168.0.0/16
|
||||
|
||||
# Firewall drop for winbox from non-trusted sources (belt and suspenders)
|
||||
# Place before "Established Input" to catch new connection attempts early
|
||||
/ip firewall filter
|
||||
add chain=input action=drop dst-port=8291 protocol=tcp src-address-list=!trusted comment="block winbox from non-trusted" place-before="Established Input"
|
||||
1
mikrotik-tool/edge-ssh-preseem-fix.rsc
Normal file
1
mikrotik-tool/edge-ssh-preseem-fix.rsc
Normal file
|
|
@ -0,0 +1 @@
|
|||
/ip firewall connection tracking set tcp-established-timeout=1d
|
||||
412
mikrotik-tool/edge-upgrade.md
Normal file
412
mikrotik-tool/edge-upgrade.md
Normal file
|
|
@ -0,0 +1,412 @@
|
|||
# edge → RouterOS 7 upgrade runbook
|
||||
|
||||
Edge is a CCR2004-1G-12S+2XS currently on 6.49.18 (the only ROS6 box left in
|
||||
the fleet). The auto-converter handles `/ip firewall`, `/ip address`,
|
||||
`/ip route`, `/interface`, `/snmp`, `/system`, and most NAT correctly. The
|
||||
following areas need manual pre-staging because the ROS7 converter either
|
||||
silently drops them or rewrites them into dead code:
|
||||
|
||||
- `/routing filter` → `/routing filter rule` (BGP/OSPF filter chains)
|
||||
- `/routing bgp instance` + `/routing bgp peer` → `/routing bgp instance` +
|
||||
`/routing bgp template` + `/routing bgp connection`
|
||||
- `/routing ospf instance` + `/routing ospf interface` + `/routing ospf nbma-neighbor`
|
||||
→ `/routing ospf instance` + `/routing ospf area` +
|
||||
`/routing ospf interface-template` + `/routing ospf static-neighbor`
|
||||
- `/routing bfd interface` → `/routing bfd configuration`
|
||||
- `/mpls ldp` set → `/mpls ldp` add + `/mpls ldp interface`
|
||||
- `/routing rip` (delete entirely)
|
||||
|
||||
Reference for ROS7 conventions: `core.rsc`, `climax.rsc`.
|
||||
|
||||
---
|
||||
|
||||
## 0. Pre-flight (before touching the router)
|
||||
|
||||
```bash
|
||||
# from your workstation
|
||||
cd /Users/graham/dev/network/mikrotik-tool
|
||||
./mikrotik-tool export # snapshot fresh /export
|
||||
cp edge.rsc edge.rsc.pre-ros7-$(date +%F) # keep the ROS6 baseline
|
||||
```
|
||||
|
||||
Verify console/out-of-band access (Spectrum link on `sfp-sfpplus12` is the
|
||||
WAN — if BGP doesn't come back, you lose remote access via that path; iBGP
|
||||
to core via preseem is the management fallback).
|
||||
|
||||
Confirm OSPF auth shared key from one of the peers (climax or core) so the
|
||||
new `/routing ospf interface-template` can be configured with matching auth.
|
||||
Edge's ROS6 export doesn't include the secret.
|
||||
|
||||
---
|
||||
|
||||
## 1. Stage One: in-place fixes on ROS6 (optional cleanups)
|
||||
|
||||
These are pure ROS6 commands you can run now to reduce churn during the
|
||||
upgrade.
|
||||
|
||||
```rsc
|
||||
# Normalize mpls-mtu to fleet standard 1508 (currently 1530)
|
||||
/mpls interface
|
||||
set [find interface=sfp-sfpplus8-server-switch] mpls-mtu=1508
|
||||
set [find default=yes] mpls-mtu=1508
|
||||
|
||||
# Add IPv4 anchor for BGP origination
|
||||
# In ROS6, /routing bgp network injects 204.110.188.0/22 directly.
|
||||
# In ROS7, we need the prefix to exist in the routing table for it to be
|
||||
# advertised. Adding the anchor now is harmless on ROS6.
|
||||
/ip route
|
||||
add dst-address=204.110.188.0/22 type=blackhole distance=200 \
|
||||
comment="anchor for BGP origination to TWC (ROS7 needs RIB entry)"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 2. Upgrade to RouterOS 7
|
||||
|
||||
```rsc
|
||||
# On the router (Winbox/SSH):
|
||||
/system package update
|
||||
set channel=long-term
|
||||
check-for-updates
|
||||
# verify it offers 7.21.4 (the fleet standard, see CLAUDE.md fleet topology)
|
||||
install
|
||||
# router reboots
|
||||
```
|
||||
|
||||
After reboot, verify version and that interfaces came back:
|
||||
|
||||
```rsc
|
||||
/system resource print
|
||||
/interface print
|
||||
/ip address print
|
||||
```
|
||||
|
||||
OSPF/BGP **will be broken at this point** — the auto-converter has run but
|
||||
filter chains, BGP connections, and OSPF templates are either missing or
|
||||
wrong. Static routes still work, so the Spectrum default + the static
|
||||
backbones to climax keep the box reachable.
|
||||
|
||||
---
|
||||
|
||||
## 3. Clear the auto-converted garbage
|
||||
|
||||
```rsc
|
||||
# BGP - remove auto-converted artifacts
|
||||
/routing bgp connection
|
||||
remove [find]
|
||||
/routing bgp template
|
||||
remove [find name!=default]
|
||||
|
||||
# OSPF - remove auto-converted artifacts
|
||||
/routing ospf interface-template
|
||||
remove [find]
|
||||
/routing ospf static-neighbor
|
||||
remove [find]
|
||||
/routing ospf area
|
||||
remove [find name!=backbone]
|
||||
/routing ospf instance
|
||||
remove [find name!=default-v2]
|
||||
|
||||
# Filter chains - remove anything auto-converted
|
||||
/routing filter rule
|
||||
remove [find]
|
||||
|
||||
# LDP - remove auto-converted artifacts
|
||||
/mpls ldp interface
|
||||
remove [find]
|
||||
/mpls ldp
|
||||
remove [find]
|
||||
|
||||
# BFD - remove auto-converted artifacts
|
||||
/routing bfd configuration
|
||||
remove [find]
|
||||
|
||||
# RIP - delete it, never coming back
|
||||
/routing rip
|
||||
# (RIP package is not installed by default on ROS7, may already be gone)
|
||||
|
||||
# Old BGP network announcements (replaced by RIB anchor + filter chain)
|
||||
/routing bgp network
|
||||
remove [find] # if the menu still exists in your ROS7 build
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4. Stage the ROS7 routing config
|
||||
|
||||
### 4a. Routing IDs and instances
|
||||
|
||||
```rsc
|
||||
/routing id
|
||||
add disabled=no id=10.254.254.254 name=id-1 select-dynamic-id=""
|
||||
|
||||
/routing bgp instance
|
||||
add as=393837 cluster-id=10.254.254.254 name=bgp-instance-1 \
|
||||
router-id=10.254.254.254
|
||||
|
||||
/routing bgp template
|
||||
set default as=393837 disabled=no
|
||||
|
||||
/routing ospf instance
|
||||
add disabled=no in-filter-chain=ospf-in name=default-v2 \
|
||||
out-filter-chain=ospf-out redistribute=connected,static \
|
||||
router-id=id-1 routing-table=main \
|
||||
originate-default=always
|
||||
|
||||
/routing ospf instance
|
||||
add disabled=no in-filter-chain=ospf-in name=default-v3 \
|
||||
out-filter-chain=ospf-out redistribute=connected,static \
|
||||
router-id=id-1 routing-table=main version=3 \
|
||||
originate-default=always
|
||||
|
||||
/routing ospf area
|
||||
add disabled=no instance=default-v2 name=backbone-v2
|
||||
add disabled=no instance=default-v3 name=backbone-v3
|
||||
```
|
||||
|
||||
### 4b. Filter chains (THE default-deny fix)
|
||||
|
||||
Auto-converter does NOT bridge these — must be added explicitly and
|
||||
referenced from the instances/connections.
|
||||
|
||||
```rsc
|
||||
/routing filter rule
|
||||
# OSPF: accept everything in both directions
|
||||
add chain=ospf-in disabled=no rule="accept"
|
||||
add chain=ospf-out disabled=no rule="accept"
|
||||
|
||||
# eBGP to TWC: inbound = default route only
|
||||
add chain=twc-in disabled=no rule=\
|
||||
"if (dst==0.0.0.0/0 && dst-len==0) { accept } else { reject }"
|
||||
|
||||
# eBGP to TWC: outbound = only our /22 and our v6 /32
|
||||
add chain=twc-out disabled=no rule=\
|
||||
"if ((dst==204.110.188.0/22 && dst-len==22) || (dst==2606:1c80::/32 && dst-len==32)) { accept } else { reject }"
|
||||
|
||||
# iBGP-RR (v6 transport, v4+v6 NLRI): RR client filters
|
||||
add chain=v6-rr-in disabled=no rule=\
|
||||
"if (dst==0.0.0.0/0 || dst==::/0 || dst-len==44 || dst-len==64 || dst-len==128) { accept } else { reject }"
|
||||
add chain=v6-rr-out disabled=no rule=\
|
||||
"if (dst==0.0.0.0/0 || dst==::/0 || dst-len==44 || dst-len==64 || dst-len==128) { accept } else { reject }"
|
||||
```
|
||||
|
||||
Note: `dst-len==44` covers per-tower IPv6 allocations, `==64` covers
|
||||
per-link/server LANs, `==128` covers loopbacks. Adjust if your v6 plan in
|
||||
`ipv6.md` uses different prefix lengths.
|
||||
|
||||
### 4c. BGP connections
|
||||
|
||||
```rsc
|
||||
/routing bgp connection
|
||||
# eBGP to TWC, v4
|
||||
add disabled=no afi=ip as=393837 name=twc \
|
||||
instance=bgp-instance-1 \
|
||||
local.address=71.41.226.118 \
|
||||
remote.address=71.41.226.117 .as=11427 \
|
||||
input.filter=twc-in \
|
||||
output.filter-chain=twc-out \
|
||||
output.network=204.110.188.0/22 \
|
||||
output.redistribute=""
|
||||
|
||||
# eBGP to TWC, v6
|
||||
add disabled=no afi=ipv6 as=393837 name=twc-v6 \
|
||||
instance=bgp-instance-1 \
|
||||
local.address=2605:6000:0:8::f:373 \
|
||||
remote.address=2605:6000:0:8::f:372 .as=11427 \
|
||||
input.filter=twc-in \
|
||||
output.filter-chain=twc-out \
|
||||
output.network=2606:1c80::/32 \
|
||||
output.redistribute=""
|
||||
|
||||
# iBGP-RR to core (v4+v6 NLRI over v6 transport, edge is the RR)
|
||||
add disabled=no afi=ip,ipv6 as=393837 \
|
||||
comment="iBGP-RR to core" \
|
||||
instance=bgp-instance-1 \
|
||||
local.address=2606:1c80:0:10::254 .role=ibgp-rr \
|
||||
remote.address=2606:1c80:0:10::253 .as=393837 \
|
||||
multihop=yes \
|
||||
nexthop-choice=force-self \
|
||||
input.filter=v6-rr-in \
|
||||
output.filter-chain=v6-rr-out \
|
||||
output.redistribute=connected,static,ospf \
|
||||
output.default-originate=always \
|
||||
name=ibgp-rr-core
|
||||
```
|
||||
|
||||
If/when you add more RR clients (climax, culleoka, etc. peering directly to
|
||||
edge), they each get their own `/routing bgp connection` block mirroring the
|
||||
`ibgp-rr-core` pattern with `.role=ibgp-rr` on edge's side.
|
||||
|
||||
### 4d. OSPF interface templates and static neighbors
|
||||
|
||||
Replicate the per-interface settings from the old `/routing ospf interface`
|
||||
block. Adjust auth values to match the rest of the fleet (verify shared
|
||||
key from climax or core first).
|
||||
|
||||
```rsc
|
||||
/routing ospf interface-template
|
||||
# Preseem (primary path to core) - wired, BFD on
|
||||
add disabled=no area=backbone-v2 auth-id=1 cost=10 \
|
||||
interfaces=sfp-sfpplus11-preseem priority=1 type=ptp use-bfd=yes
|
||||
|
||||
# Direct backup to core - wired, BFD on
|
||||
add disabled=no area=backbone-v2 auth-id=1 cost=100 \
|
||||
interfaces=sfp-sfpplus7-core-direct priority=1 type=ptp use-bfd=yes
|
||||
|
||||
# Server switch VLAN9 (loopback / server LAN)
|
||||
add disabled=no area=backbone-v2 \
|
||||
interfaces=sfp-sfpplus8-server-switch type=broadcast
|
||||
|
||||
add disabled=no area=backbone-v2 passive=yes \
|
||||
interfaces=vlan9_sfpplus8 type=broadcast
|
||||
|
||||
# Spectrum WAN - passive (we don't run OSPF to TWC)
|
||||
add disabled=no area=backbone-v2 passive=yes \
|
||||
interfaces=sfp-sfpplus12-spectrum type=broadcast
|
||||
|
||||
# IPv6 area equivalents on the same wired links
|
||||
add disabled=no area=backbone-v3 cost=10 \
|
||||
interfaces=sfp-sfpplus11-preseem type=ptp use-bfd=yes
|
||||
add disabled=no area=backbone-v3 cost=100 \
|
||||
interfaces=sfp-sfpplus7-core-direct type=ptp use-bfd=yes
|
||||
add disabled=no area=backbone-v3 \
|
||||
interfaces=sfp-sfpplus8-server-switch type=broadcast
|
||||
|
||||
/routing ospf static-neighbor
|
||||
# Core (preseem path)
|
||||
add disabled=no area=backbone-v2 \
|
||||
address=204.110.191.185%sfp-sfpplus11-preseem
|
||||
# Core (direct path) - if you want OSPF on this link as a hot standby
|
||||
add disabled=no area=backbone-v2 \
|
||||
address=204.110.191.181%sfp-sfpplus7-core-direct
|
||||
```
|
||||
|
||||
**Do not** add the old NBMA neighbors `10.250.1.94`, `10.250.1.57`,
|
||||
`10.250.1.201`, `10.250.2.1` — those were stale wireless backbone
|
||||
neighbors that don't apply to edge (edge has no wireless backbone links).
|
||||
|
||||
### 4e. BFD
|
||||
|
||||
```rsc
|
||||
/routing bfd configuration
|
||||
add disabled=no interfaces=all min-rx=200ms min-tx=200ms multiplier=5 \
|
||||
vrf=main
|
||||
```
|
||||
|
||||
Per the fleet note in CLAUDE.md, BFD on wireless backbone links is
|
||||
problematic — edge has none, so this global config is fine.
|
||||
|
||||
### 4f. MPLS / LDP
|
||||
|
||||
```rsc
|
||||
/mpls interface
|
||||
# (mpls-mtu was already set in stage 1, but verify)
|
||||
set [find interface=sfp-sfpplus7-core-direct] mpls-mtu=1508
|
||||
set [find interface=sfp-sfpplus11-preseem] mpls-mtu=1508
|
||||
set [find interface=sfp-sfpplus8-server-switch] mpls-mtu=1508
|
||||
|
||||
/mpls ldp
|
||||
add disabled=no lsr-id=10.254.254.254 \
|
||||
transport-addresses=10.254.254.254 vrf=main
|
||||
|
||||
/mpls ldp interface
|
||||
add interface=sfp-sfpplus7-core-direct
|
||||
add interface=sfp-sfpplus11-preseem
|
||||
add interface=sfp-sfpplus8-server-switch
|
||||
```
|
||||
|
||||
LDP accept/advertise filtering: the ROS6 export was set to only advertise
|
||||
the loopback `/32` (`/mpls ldp advertise-filter add prefix=10.254.254.254/32`
|
||||
+ `add advertise=no`). If you want to preserve that behavior, add a filter
|
||||
chain:
|
||||
|
||||
```rsc
|
||||
/routing filter rule
|
||||
add chain=ldp-advertise disabled=no rule=\
|
||||
"if (dst==10.254.254.254/32 && dst-len==32) { accept } else { reject }"
|
||||
# Reference from /mpls ldp - syntax varies by ROS7 version, check
|
||||
# /mpls ldp print detail after adding the rule
|
||||
```
|
||||
|
||||
For most fleets this is over-engineering — LDP advertising all loopbacks
|
||||
is normally fine. Skip unless you have a specific reason.
|
||||
|
||||
---
|
||||
|
||||
## 5. Verification
|
||||
|
||||
```rsc
|
||||
# Interfaces and addressing
|
||||
/interface print
|
||||
/ip address print
|
||||
|
||||
# OSPF adjacencies
|
||||
/routing ospf neighbor print
|
||||
# expect: core via sfp-sfpplus11-preseem (Full), optionally
|
||||
# core via sfp-sfpplus7-core-direct (Full)
|
||||
|
||||
# OSPF routes
|
||||
/ip route print where ospf
|
||||
/ipv6 route print where ospf
|
||||
|
||||
# BGP sessions
|
||||
/routing bgp session print
|
||||
# expect: twc (Established), twc-v6 (Established), ibgp-rr-core (Established)
|
||||
|
||||
# BGP advertisements to TWC
|
||||
/routing bgp advertisements print peer=twc
|
||||
# expect: 204.110.188.0/22 advertised
|
||||
/routing bgp advertisements print peer=twc-v6
|
||||
# expect: 2606:1c80::/32 advertised
|
||||
|
||||
# BGP routes received
|
||||
/routing route print where bgp
|
||||
# expect: 0.0.0.0/0 from TWC v4, ::/0 from TWC v6, plus customer prefixes
|
||||
# from iBGP-RR
|
||||
|
||||
# LDP sessions
|
||||
/mpls ldp neighbor print
|
||||
# expect: session to core via direct + preseem links
|
||||
|
||||
# LDP-installed labels
|
||||
/mpls forwarding-table print
|
||||
|
||||
# BFD sessions
|
||||
/routing bfd session print
|
||||
# expect: up to core via preseem and direct
|
||||
|
||||
# Full reachability check
|
||||
/ping 10.254.254.253 # core loopback
|
||||
/ping 10.254.254.101 # verona loopback
|
||||
/tool traceroute 10.254.254.102 src-address=10.254.254.254 # to climax via MPLS
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 6. If things go wrong
|
||||
|
||||
The Spectrum default route (`/ip route add distance=1 gateway=71.41.226.117`)
|
||||
and the manually-pinned statics in `/ip route` (lines 508-531 of the old
|
||||
export) keep edge reachable from the Internet even without BGP/OSPF. From
|
||||
inside the network, the static `204.110.191.182/30` link to core stays up
|
||||
and is OSPF-independent.
|
||||
|
||||
Rollback path: `/system package downgrade` with the prior 6.49.18 npk
|
||||
available in the file store. Keep `edge.rsc.pre-ros7-<date>` around to
|
||||
re-paste any pieces the upgrade mangles unrecoverably.
|
||||
|
||||
---
|
||||
|
||||
## 7. Post-upgrade cleanup
|
||||
|
||||
```bash
|
||||
# back on the workstation
|
||||
cd /Users/graham/dev/network/mikrotik-tool
|
||||
./mikrotik-tool export # fresh edge.rsc on ROS7
|
||||
git add edge.rsc edge-upgrade.md
|
||||
git commit -m "edge: ROS7 upgrade"
|
||||
```
|
||||
|
||||
Update CLAUDE.md to remove the "Edge runs ROS 6.49.18 (legacy, no MPLS,
|
||||
ignore for the spine)" note once edge is fully on ROS7 with MPLS in the
|
||||
spine.
|
||||
|
|
@ -1,173 +0,0 @@
|
|||
# =============================================================================
|
||||
# edge — IPv6 apply script (ROS 6.49.18, BGP-only / no OSPFv3)
|
||||
# =============================================================================
|
||||
#
|
||||
# Edge is the iBGP hub: it terminates the TWC v6 eBGP peering and the v4
|
||||
# transport iBGP sessions to every tower. v6 NLRI rides v4 transport (so the
|
||||
# existing v4 OSPF reachability between loopbacks is reused; no v6 IGP is
|
||||
# needed). This matches the verona-v6-apply.rsc design choice.
|
||||
#
|
||||
# Two paths edge<->core:
|
||||
# 1. PRIMARY: sfp-sfpplus11-preseem via the Preseem shaper (transparent
|
||||
# L2). OSPFv2 cost on edge is the default 10. v6 P2P:
|
||||
# 2606:1c80:0:1002::/64 (edge .1, core .2)
|
||||
# 2. BACKUP: sfp-sfpplus7-core-direct direct fiber. OSPFv2 cost on edge
|
||||
# is 100 (same on core's side). v6 P2P:
|
||||
# 2606:1c80:0:11::/64 (edge .1, core .2)
|
||||
# NOTE: P2P prefix is /64 not /127 — picking ::1/127 + ::2/127 puts the
|
||||
# two ends in *different* /127 networks (verified live 2026-05-09). /64
|
||||
# matches the per-link allocation in ipv6.md.
|
||||
# Static v6 routes for the infra /48 mirror that cost relationship by
|
||||
# distance (lower = preferred): preseem at distance=1, direct at
|
||||
# distance=2.
|
||||
#
|
||||
# What this script DOES NOT touch (USER CONSTRAINT 2026-05-09):
|
||||
# * TWC v6 eBGP peer `twc-v6` (remote 2605:6000:0:8::f:372, AS11427) —
|
||||
# name doesn't match the ^ibgp- wipe pattern, so it survives.
|
||||
# * The 2606:1c80::/32 BGP network announcement upstream.
|
||||
# * The TWC P2P address 2605:6000:0:8::f:373/127 on sfp-sfpplus12-spectrum
|
||||
# — enabled, so it survives the [find disabled=yes] wipe.
|
||||
# * The existing server LAN address 2606:1c80::1 on vlan9_sfpplus8 —
|
||||
# enabled, so it survives. We only ADD a per-interface ND override
|
||||
# to suppress RA there; the IP and prefix are unmodified.
|
||||
# * v4 OSPFv2 / RIP / firewall rules — v4 control plane is unchanged.
|
||||
#
|
||||
# Apply order: review -> paste in winbox/SSH on edge -> verify with checks
|
||||
# at the bottom -> only then run the matching `core-v6-apply.rsc` so the
|
||||
# preseem and direct /127 endpoints come up symmetrically.
|
||||
# =============================================================================
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# WIPE — only the stale internal-only v6 state on edge. Leaves TWC peering,
|
||||
# the upstream /32 announcement, the server LAN address, and v4 alone.
|
||||
# -----------------------------------------------------------------------------
|
||||
# Drop any disabled placeholder v6 internal addresses (none on edge today,
|
||||
# but harmless if present).
|
||||
/ipv6 address remove [find disabled=yes]
|
||||
|
||||
# Drop any leftover OSPFv3 interface entries (we are committing to BGP-only
|
||||
# for v6; any v3 instances on edge have been minimal/stub).
|
||||
/routing ospf-v3 interface remove [find]
|
||||
|
||||
# Drop only internal iBGP v6 peers if any exist; never touch twc-v6.
|
||||
/routing bgp peer remove [find name~"^ibgp-" and address-families~"ipv6"]
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# APPLY — addresses
|
||||
# -----------------------------------------------------------------------------
|
||||
/ipv6 settings set forward=yes accept-router-advertisements=no
|
||||
|
||||
/ipv6 address
|
||||
add address=2606:1c80:0:10::254/128 interface=loopback advertise=no comment="edge loopback"
|
||||
add address=2606:1c80:0:11::1/64 interface=sfp-sfpplus7-core-direct advertise=no comment="P2P edge<->core direct (backup)"
|
||||
add address=2606:1c80:0:1002::1/64 interface=sfp-sfpplus11-preseem advertise=no comment="P2P edge<->core via preseem (primary)"
|
||||
|
||||
# NOTE: the server LAN address 2606:1c80::1 on vlan9_sfpplus8 is left
|
||||
# untouched per user constraint. The ND override below makes it RA-free
|
||||
# without modifying the address itself.
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# APPLY — neighbour discovery: SUPPRESS RA on the server LAN
|
||||
# -----------------------------------------------------------------------------
|
||||
# Server LAN is static-only per the IPv6 plan — disable RA + DHCPv6 hints.
|
||||
# A per-interface entry overrides the global default.
|
||||
/ipv6 nd
|
||||
add interface=vlan9_sfpplus8 disabled=no advertise=no managed-address-configuration=no other-configuration=no comment="server LAN: static only, no SLAAC"
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# APPLY — static routes (replace what an IGP would have learned)
|
||||
# -----------------------------------------------------------------------------
|
||||
# Infra /48 covers all router loopbacks (2606:1c80:0:10::/64), all P2P
|
||||
# /64s, and any future infra slots. Two entries with different distances
|
||||
# pin preseem as primary and direct as backup. The connected /64 server
|
||||
# LAN at 2606:1c80::/64 wins by longest-prefix-match, so this /48 doesn't
|
||||
# steal local server traffic.
|
||||
/ipv6 route
|
||||
add dst-address=2606:1c80::/48 gateway=2606:1c80:0:1002::2 distance=1 comment="infra: via preseem (primary)"
|
||||
add dst-address=2606:1c80::/48 gateway=2606:1c80:0:11::2 distance=2 comment="infra: via direct (backup)"
|
||||
|
||||
# Tower /44s are learned via iBGP from each tower (next-hop = tower's v6
|
||||
# loopback inside :10::/64, recursively resolved through the /48 above).
|
||||
# No static needed for them.
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# APPLY — firewall (extend the existing minimal v6 ruleset)
|
||||
# -----------------------------------------------------------------------------
|
||||
# The existing v6 firewall already accepts BGP (179) and established. Add
|
||||
# the rules required for internal-network forwarding + ICMPv6 / link-local.
|
||||
/ipv6 firewall filter
|
||||
add chain=input action=accept protocol=icmpv6 comment="icmpv6 input"
|
||||
add chain=input action=accept src-address=fe80::/10 comment="link-local input"
|
||||
add chain=input action=accept src-address=2606:1c80::/32 comment="internal input"
|
||||
|
||||
add chain=forward action=accept protocol=icmpv6 hop-limit=equal:1 comment="PMTUD forward"
|
||||
add chain=forward action=accept protocol=icmpv6 comment="icmpv6 forward"
|
||||
add chain=forward action=accept src-address=2606:1c80::/32 dst-address=2606:1c80::/32 comment="internal<->internal"
|
||||
|
||||
# (we do not add a default-deny; edge has long-running customer transit
|
||||
# paths and a sudden v6 default-deny would break them. existing rules at
|
||||
# the bottom of /ipv6 firewall filter remain authoritative.)
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# APPLY — iBGP route filters (v6)
|
||||
# -----------------------------------------------------------------------------
|
||||
# Edge has ONE iBGP v6 peer: core, which is the v6 RR for the fleet. Edge is
|
||||
# an RR client of core (no `route-reflect=yes` — that flag would mark the
|
||||
# OTHER side as a client, but edge has no clients). Edge sends ::/0 to core
|
||||
# (which reflects it to towers) and learns tower /44s + /128 loopbacks from
|
||||
# core (reflected from each tower).
|
||||
#
|
||||
# in-filter: accept any infra/customer prefix that core (v6 RR) reflects to
|
||||
# us. Length 44 = customer aggregates, 64 = backbone P2Ps + per-tower LAN /64s,
|
||||
# 128 = router loopbacks. Anything else within 2606:1c80::/32 is rejected
|
||||
# (defense in depth — keeps random connected /80 etc. from leaking).
|
||||
/routing filter
|
||||
add chain=ibgp-rr-in prefix=2606:1c80::/32 prefix-length=44-128 action=accept comment="any reflected infra/customer prefix"
|
||||
add chain=ibgp-rr-in action=discard comment="default deny"
|
||||
|
||||
# out-filter: edge sends only the v6 default to core (which the RR fans
|
||||
# out to every tower). Anything else stays inside edge.
|
||||
add chain=ibgp-rr-out prefix=::/0 prefix-length=0 action=accept comment="default route"
|
||||
add chain=ibgp-rr-out action=discard comment="default deny"
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# APPLY — iBGP peer to core (v6 transport, carrying v6 NLRI)
|
||||
# -----------------------------------------------------------------------------
|
||||
# v6 transport (loopback-to-loopback over 2606:1c80:0:10::254 ↔ ::253) avoids
|
||||
# the v4-mapped-next-hop bug we hit with v4 transport carrying v6 NLRI. Core
|
||||
# (ROS7) is the route reflector — edge is an RR client and, importantly, NOT
|
||||
# a reflector itself (ROS 6.49 does not properly reflect v6 NLRI even with
|
||||
# all RR settings; that's the entire reason core has the role).
|
||||
# update-source=loopback sources the TCP session from edge's loopback iface
|
||||
# (which holds both 10.254.254.254 and 2606:1c80:0:10::254).
|
||||
# nexthop-choice=force-self pins the next-hop edge advertises (::/0) to
|
||||
# edge's own v6 loopback so towers can resolve it via the RR-reflected
|
||||
# /128 path.
|
||||
/routing bgp peer
|
||||
add name=ibgp-rr-core remote-address=2606:1c80:0:10::253 remote-as=393837 update-source=loopback address-families=ipv6 nexthop-choice=force-self ttl=255 in-filter=ibgp-rr-in out-filter=ibgp-rr-out default-originate=always comment="iBGP to core (v6 RR), v6 transport"
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# VERIFY (paste after apply)
|
||||
# -----------------------------------------------------------------------------
|
||||
# /ipv6 address print
|
||||
# /ipv6 route print where active
|
||||
# /ipv6 nd print
|
||||
# /routing bgp peer print where name~"v6"
|
||||
# /routing bgp advertisements print peer=ibgp-verona-v6
|
||||
#
|
||||
# Expected immediately:
|
||||
# * /ipv6 address print shows 2606:1c80:0:10::254/128 (loopback),
|
||||
# :11::1/127 (direct), :1002::1/127 (preseem), :1::/64 server LAN
|
||||
# * /ipv6 nd print shows the vlan9_sfpplus8 entry with advertise=no
|
||||
# * the new /48 routes show as inactive (gateway unreachable until core
|
||||
# applies), which is fine
|
||||
# * ibgp-verona-v6 is in `connecting` state until verona reaches edge's
|
||||
# v6 loopback (which needs core configured) — for the bare TCP
|
||||
# session, v4 loopback reachability is enough so the session SHOULD
|
||||
# come up immediately. confirm with /routing bgp peer print.
|
||||
#
|
||||
# Once core has its matching v6 apply:
|
||||
# /ping 2606:1c80:0:1002::2 count=3 ;; core preseem-side
|
||||
# /ping 2606:1c80:0:11::2 count=3 ;; core direct-side
|
||||
# /ping 2606:1c80:0:10::253 count=3 ;; core loopback (via preseem)
|
||||
# /ip route check dst=2606:1c80:0:10::101 ;; verona loopback path
|
||||
|
|
@ -1,720 +0,0 @@
|
|||
# may/09/2026 09:37:53 by RouterOS 6.49.18
|
||||
# software id = 8XZE-R7EJ
|
||||
#
|
||||
# model = CCR2004-1G-12S+2XS
|
||||
# serial number = C8A70C55A930
|
||||
/interface bridge
|
||||
add name=cgnat
|
||||
add fast-forward=no name=loopback
|
||||
/interface ethernet
|
||||
set [ find default-name=ether1 ] rx-flow-control=auto tx-flow-control=auto
|
||||
set [ find default-name=sfp-sfpplus7 ] auto-negotiation=no name=\
|
||||
sfp-sfpplus7-core-direct
|
||||
set [ find default-name=sfp-sfpplus8 ] name=sfp-sfpplus8-server-switch \
|
||||
rx-flow-control=auto tx-flow-control=auto
|
||||
set [ find default-name=sfp-sfpplus10 ] rx-flow-control=auto tx-flow-control=\
|
||||
auto
|
||||
set [ find default-name=sfp-sfpplus11 ] name=sfp-sfpplus11-preseem \
|
||||
rx-flow-control=auto tx-flow-control=auto
|
||||
set [ find default-name=sfp-sfpplus12 ] l2mtu=1500 name=\
|
||||
sfp-sfpplus12-spectrum rx-flow-control=auto tx-flow-control=auto
|
||||
/interface vlan
|
||||
add interface=sfp-sfpplus8-server-switch name=vlan9_sfpplus8 vlan-id=9
|
||||
/interface wireless security-profiles
|
||||
set [ find default=yes ] supplicant-identity=MikroTik
|
||||
/ip dhcp-server
|
||||
add disabled=no interface=vlan9_sfpplus8 name=servers-public
|
||||
/ip pool
|
||||
add name=380building ranges=10.0.0.150-10.0.0.239
|
||||
add name=servers-public ranges=204.110.191.193-204.110.191.217
|
||||
/ip dhcp-server
|
||||
add address-pool=380building disabled=no interface=sfp-sfpplus8-server-switch \
|
||||
name=380-building
|
||||
/ppp profile
|
||||
add name=RWB_sstp_profile
|
||||
/queue simple
|
||||
add burst-limit=1M/1M burst-threshold=1M/1M burst-time=1s/1s disabled=yes \
|
||||
limit-at=1M/1M max-limit=1M/1M name=Delinquent packet-marks=Delenquent \
|
||||
priority=2/2 target=""
|
||||
add burst-limit=1/1 burst-threshold=1/1 burst-time=1s/1s disabled=yes \
|
||||
limit-at=1/1 max-limit=1/1 name=Inactive packet-marks=Inactive priority=\
|
||||
2/2 target=""
|
||||
/routing bgp instance
|
||||
set default as=393837 out-filter=twc-out router-id=204.110.191.190
|
||||
/routing ospf instance
|
||||
set [ find default=yes ] distribute-default=always-as-type-1 router-id=\
|
||||
10.254.254.254
|
||||
/routing ospf-v3 instance
|
||||
set [ find default=yes ] distribute-default=always-as-type-1 \
|
||||
redistribute-connected=as-type-1 router-id=10.254.254.254
|
||||
/snmp community
|
||||
set [ find default=yes ] name=onehuargd4a8974y79a497yi
|
||||
add addresses=204.110.188.0/22,10.0.0.0/8 name=kdyyJrT0Mm
|
||||
/system logging action
|
||||
add name=logs remote=204.110.191.229 remote-port=1514 src-address=\
|
||||
10.254.254.254 target=remote
|
||||
/user group
|
||||
set full policy="local,telnet,ssh,ftp,reboot,read,write,policy,test,winbox,pas\
|
||||
sword,web,sniff,sensitive,api,romon,dude,tikapp"
|
||||
/ip firewall connection tracking
|
||||
set icmp-timeout=30s tcp-close-wait-timeout=1m tcp-established-timeout=4h \
|
||||
tcp-fin-wait-timeout=2m tcp-last-ack-timeout=30s \
|
||||
tcp-syn-received-timeout=1m tcp-syn-sent-timeout=2m \
|
||||
tcp-time-wait-timeout=2m udp-stream-timeout=2m udp-timeout=30s
|
||||
/ip neighbor discovery-settings
|
||||
set discover-interface-list=!dynamic
|
||||
/ip settings
|
||||
set rp-filter=strict tcp-syncookies=yes
|
||||
/ipv6 settings
|
||||
set accept-router-advertisements=no
|
||||
/ip address
|
||||
add address=71.41.226.118/30 interface=sfp-sfpplus12-spectrum network=\
|
||||
71.41.226.116
|
||||
add address=204.110.191.186/30 interface=sfp-sfpplus11-preseem network=\
|
||||
204.110.191.184
|
||||
add address=204.110.191.254/26 interface=vlan9_sfpplus8 network=\
|
||||
204.110.191.192
|
||||
add address=10.254.254.254 interface=loopback network=10.254.254.254
|
||||
add address=204.110.191.182/30 interface=sfp-sfpplus7-core-direct network=\
|
||||
204.110.191.180
|
||||
add address=10.0.0.254/24 interface=sfp-sfpplus8-server-switch network=\
|
||||
10.0.0.0
|
||||
add address=204.110.190.129/25 interface=cgnat network=204.110.190.128
|
||||
add address=204.110.190.130/25 interface=cgnat network=204.110.190.128
|
||||
add address=204.110.190.131/25 interface=cgnat network=204.110.190.128
|
||||
add address=204.110.190.132/25 interface=cgnat network=204.110.190.128
|
||||
add address=204.110.190.133/25 interface=cgnat network=204.110.190.128
|
||||
add address=204.110.190.134/25 interface=cgnat network=204.110.190.128
|
||||
add address=204.110.190.135/25 interface=cgnat network=204.110.190.128
|
||||
add address=204.110.190.136/25 interface=cgnat network=204.110.190.128
|
||||
add address=204.110.190.137/25 interface=cgnat network=204.110.190.128
|
||||
add address=204.110.190.138/25 interface=cgnat network=204.110.190.128
|
||||
add address=204.110.190.139/25 interface=cgnat network=204.110.190.128
|
||||
add address=204.110.190.140/25 interface=cgnat network=204.110.190.128
|
||||
add address=204.110.190.141/25 interface=cgnat network=204.110.190.128
|
||||
add address=204.110.190.142/25 interface=cgnat network=204.110.190.128
|
||||
add address=204.110.190.143/25 interface=cgnat network=204.110.190.128
|
||||
add address=204.110.190.144/25 interface=cgnat network=204.110.190.128
|
||||
add address=204.110.190.145/25 interface=cgnat network=204.110.190.128
|
||||
add address=204.110.190.146/25 interface=cgnat network=204.110.190.128
|
||||
add address=204.110.190.147/25 interface=cgnat network=204.110.190.128
|
||||
add address=204.110.190.148/25 interface=cgnat network=204.110.190.128
|
||||
add address=204.110.190.149/25 interface=cgnat network=204.110.190.128
|
||||
add address=204.110.190.150/25 interface=cgnat network=204.110.190.128
|
||||
add address=204.110.190.151/25 interface=cgnat network=204.110.190.128
|
||||
add address=204.110.190.152/25 interface=cgnat network=204.110.190.128
|
||||
add address=204.110.190.153/25 interface=cgnat network=204.110.190.128
|
||||
add address=204.110.190.154/25 interface=cgnat network=204.110.190.128
|
||||
add address=204.110.190.155/25 interface=cgnat network=204.110.190.128
|
||||
add address=204.110.190.156/25 interface=cgnat network=204.110.190.128
|
||||
add address=204.110.190.157/25 interface=cgnat network=204.110.190.128
|
||||
add address=204.110.190.158/25 interface=cgnat network=204.110.190.128
|
||||
add address=204.110.190.159/25 interface=cgnat network=204.110.190.128
|
||||
add address=204.110.190.160/25 interface=cgnat network=204.110.190.128
|
||||
add address=204.110.190.161/25 interface=cgnat network=204.110.190.128
|
||||
add address=204.110.190.162/25 interface=cgnat network=204.110.190.128
|
||||
add address=204.110.190.163/25 interface=cgnat network=204.110.190.128
|
||||
add address=204.110.190.164/25 interface=cgnat network=204.110.190.128
|
||||
add address=204.110.190.165/25 interface=cgnat network=204.110.190.128
|
||||
add address=204.110.190.166/25 interface=cgnat network=204.110.190.128
|
||||
add address=204.110.190.167/25 interface=cgnat network=204.110.190.128
|
||||
add address=204.110.190.168/25 interface=cgnat network=204.110.190.128
|
||||
add address=204.110.190.169/25 interface=cgnat network=204.110.190.128
|
||||
add address=204.110.190.170/25 interface=cgnat network=204.110.190.128
|
||||
add address=204.110.190.171/25 interface=cgnat network=204.110.190.128
|
||||
add address=204.110.190.172/25 interface=cgnat network=204.110.190.128
|
||||
add address=204.110.190.173/25 interface=cgnat network=204.110.190.128
|
||||
add address=204.110.190.174/25 interface=cgnat network=204.110.190.128
|
||||
add address=204.110.190.175/25 interface=cgnat network=204.110.190.128
|
||||
add address=204.110.190.176/25 interface=cgnat network=204.110.190.128
|
||||
add address=204.110.190.177/25 interface=cgnat network=204.110.190.128
|
||||
add address=204.110.190.178/25 interface=cgnat network=204.110.190.128
|
||||
add address=204.110.190.179/25 interface=cgnat network=204.110.190.128
|
||||
add address=204.110.190.180/25 interface=cgnat network=204.110.190.128
|
||||
add address=204.110.190.181/25 interface=cgnat network=204.110.190.128
|
||||
add address=204.110.190.182/25 interface=cgnat network=204.110.190.128
|
||||
add address=204.110.190.183/25 interface=cgnat network=204.110.190.128
|
||||
add address=204.110.190.184/25 interface=cgnat network=204.110.190.128
|
||||
add address=204.110.190.185/25 interface=cgnat network=204.110.190.128
|
||||
add address=204.110.190.186/25 interface=cgnat network=204.110.190.128
|
||||
add address=204.110.190.187/25 interface=cgnat network=204.110.190.128
|
||||
add address=204.110.190.188/25 interface=cgnat network=204.110.190.128
|
||||
add address=204.110.190.189/25 interface=cgnat network=204.110.190.128
|
||||
add address=204.110.190.190/25 interface=cgnat network=204.110.190.128
|
||||
add address=204.110.190.191/25 interface=cgnat network=204.110.190.128
|
||||
add address=204.110.190.192/25 interface=cgnat network=204.110.190.128
|
||||
add address=204.110.190.193/25 interface=cgnat network=204.110.190.128
|
||||
add address=204.110.190.194/25 interface=cgnat network=204.110.190.128
|
||||
add address=204.110.190.195/25 interface=cgnat network=204.110.190.128
|
||||
add address=204.110.190.196/25 interface=cgnat network=204.110.190.128
|
||||
add address=204.110.190.197/25 interface=cgnat network=204.110.190.128
|
||||
add address=204.110.190.198/25 interface=cgnat network=204.110.190.128
|
||||
add address=204.110.190.199/25 interface=cgnat network=204.110.190.128
|
||||
add address=204.110.190.200/25 interface=cgnat network=204.110.190.128
|
||||
add address=204.110.190.201/25 interface=cgnat network=204.110.190.128
|
||||
add address=204.110.190.202/25 interface=cgnat network=204.110.190.128
|
||||
add address=204.110.190.203/25 interface=cgnat network=204.110.190.128
|
||||
add address=204.110.190.204/25 interface=cgnat network=204.110.190.128
|
||||
add address=204.110.190.205/25 interface=cgnat network=204.110.190.128
|
||||
add address=204.110.190.206/25 interface=cgnat network=204.110.190.128
|
||||
add address=204.110.190.207/25 interface=cgnat network=204.110.190.128
|
||||
add address=204.110.190.208/25 interface=cgnat network=204.110.190.128
|
||||
add address=204.110.190.209/25 interface=cgnat network=204.110.190.128
|
||||
add address=204.110.190.210/25 interface=cgnat network=204.110.190.128
|
||||
add address=204.110.190.211/25 interface=cgnat network=204.110.190.128
|
||||
add address=204.110.190.212/25 interface=cgnat network=204.110.190.128
|
||||
add address=204.110.190.213/25 interface=cgnat network=204.110.190.128
|
||||
add address=204.110.190.214/25 interface=cgnat network=204.110.190.128
|
||||
add address=204.110.190.215/25 interface=cgnat network=204.110.190.128
|
||||
add address=204.110.190.216/25 interface=cgnat network=204.110.190.128
|
||||
add address=204.110.190.217/25 interface=cgnat network=204.110.190.128
|
||||
add address=204.110.190.218/25 interface=cgnat network=204.110.190.128
|
||||
add address=204.110.190.219/25 interface=cgnat network=204.110.190.128
|
||||
add address=204.110.190.220/25 interface=cgnat network=204.110.190.128
|
||||
add address=204.110.190.221/25 interface=cgnat network=204.110.190.128
|
||||
add address=204.110.190.222/25 interface=cgnat network=204.110.190.128
|
||||
add address=204.110.190.223/25 interface=cgnat network=204.110.190.128
|
||||
add address=204.110.190.224/25 interface=cgnat network=204.110.190.128
|
||||
add address=204.110.190.225/25 interface=cgnat network=204.110.190.128
|
||||
add address=204.110.190.226/25 interface=cgnat network=204.110.190.128
|
||||
add address=204.110.190.227/25 interface=cgnat network=204.110.190.128
|
||||
add address=204.110.190.228/25 interface=cgnat network=204.110.190.128
|
||||
add address=204.110.190.229/25 interface=cgnat network=204.110.190.128
|
||||
add address=204.110.190.230/25 interface=cgnat network=204.110.190.128
|
||||
add address=204.110.190.231/25 interface=cgnat network=204.110.190.128
|
||||
add address=204.110.190.232/25 interface=cgnat network=204.110.190.128
|
||||
add address=204.110.190.233/25 interface=cgnat network=204.110.190.128
|
||||
add address=204.110.190.234/25 interface=cgnat network=204.110.190.128
|
||||
add address=204.110.190.235/25 interface=cgnat network=204.110.190.128
|
||||
add address=204.110.190.236/25 interface=cgnat network=204.110.190.128
|
||||
add address=204.110.190.237/25 interface=cgnat network=204.110.190.128
|
||||
add address=204.110.190.238/25 interface=cgnat network=204.110.190.128
|
||||
add address=204.110.190.239/25 interface=cgnat network=204.110.190.128
|
||||
add address=204.110.190.240/25 interface=cgnat network=204.110.190.128
|
||||
add address=204.110.190.241/25 interface=cgnat network=204.110.190.128
|
||||
add address=204.110.190.242/25 interface=cgnat network=204.110.190.128
|
||||
add address=204.110.190.243/25 interface=cgnat network=204.110.190.128
|
||||
add address=204.110.190.244/25 interface=cgnat network=204.110.190.128
|
||||
add address=204.110.190.245/25 interface=cgnat network=204.110.190.128
|
||||
add address=204.110.190.246/25 interface=cgnat network=204.110.190.128
|
||||
add address=204.110.190.247/25 interface=cgnat network=204.110.190.128
|
||||
add address=204.110.190.248/25 interface=cgnat network=204.110.190.128
|
||||
add address=204.110.190.249/25 interface=cgnat network=204.110.190.128
|
||||
add address=204.110.190.250/25 interface=cgnat network=204.110.190.128
|
||||
add address=204.110.190.251/25 interface=cgnat network=204.110.190.128
|
||||
add address=204.110.190.252/25 interface=cgnat network=204.110.190.128
|
||||
add address=204.110.190.253/25 interface=cgnat network=204.110.190.128
|
||||
add address=204.110.190.254/25 interface=cgnat network=204.110.190.128
|
||||
/ip dhcp-server lease
|
||||
add address=10.0.0.249 client-id=1:0:21:9b:91:5c:b6 mac-address=\
|
||||
00:21:9B:91:5C:B6 server=380-building
|
||||
add address=204.110.191.205 mac-address=BC:24:11:8B:C0:9C
|
||||
add address=10.0.0.250 client-id=1:48:a9:8a:38:3b:2d mac-address=\
|
||||
48:A9:8A:38:3B:2D server=380-building
|
||||
add address=10.0.0.253 client-id=\
|
||||
ff:ca:53:9:5a:0:2:0:0:ab:11:2c:53:dd:b2:c1:50:9a:35 comment=truenas \
|
||||
mac-address=BC:24:11:29:2B:5A server=380-building
|
||||
add address=10.0.0.252 client-id=1:bc:24:11:f5:81:d6 mac-address=\
|
||||
BC:24:11:F5:81:D6 server=380-building
|
||||
add address=10.0.0.251 client-id=1:bc:24:11:6b:73:53 mac-address=\
|
||||
BC:24:11:6B:73:53 server=380-building
|
||||
/ip dhcp-server network
|
||||
add address=10.0.0.0/24 dns-server=9.9.9.9,1.1.1.1 domain=vntx.net gateway=\
|
||||
10.0.0.254 ntp-server=23.150.41.122
|
||||
add address=204.110.191.192/26 dns-server=\
|
||||
204.110.191.240,204.110.191.250,1.1.1.1 gateway=204.110.191.254
|
||||
/ip dns
|
||||
set servers=204.110.191.240,204.110.191.250
|
||||
/ip firewall address-list
|
||||
add address=204.110.188.0/22 list=trusted
|
||||
add address=100.64.0.0/10 list=trusted
|
||||
add address=10.0.0.0/8 list=trusted
|
||||
add address=35.197.73.77 list=preseem
|
||||
add address=35.199.29.237 list=preseem
|
||||
add address=98.97.82.0/24 comment=starlink list=trusted
|
||||
add address=129.222.72.0/24 comment=starlink list=trusted
|
||||
add address=129.222.73.0/24 comment=starlink list=trusted
|
||||
add address=129.222.74.0/24 comment=starlink list=trusted
|
||||
add address=129.222.75.0/24 comment=starlink list=trusted
|
||||
add address=129.222.76.0/24 comment=starlink list=trusted
|
||||
add address=129.222.77.0/24 comment=starlink list=trusted
|
||||
add address=129.222.78.0/24 comment=starlink list=trusted
|
||||
add address=129.222.79.0/24 comment=starlink list=trusted
|
||||
add address=98.97.80.0/24 comment=starlink list=trusted
|
||||
add address=98.97.81.0/24 comment=starlink list=trusted
|
||||
add address=98.97.83.0/24 comment=starlink list=trusted
|
||||
add address=98.97.84.0/24 comment=starlink list=trusted
|
||||
add address=98.97.85.0/24 comment=starlink list=trusted
|
||||
add address=98.97.86.0/24 comment=starlink list=trusted
|
||||
add address=98.97.87.0/24 comment=starlink list=trusted
|
||||
add address=98.97.88.0/24 comment=starlink list=trusted
|
||||
add address=98.97.89.0/24 comment=starlink list=trusted
|
||||
add address=98.97.90.0/24 comment=starlink list=trusted
|
||||
add address=98.97.91.0/24 comment=starlink list=trusted
|
||||
add address=98.97.92.0/24 comment=starlink list=trusted
|
||||
add address=98.97.93.0/24 comment=starlink list=trusted
|
||||
add address=98.97.94.0/24 comment=starlink list=trusted
|
||||
add address=98.97.95.0/24 comment=starlink list=trusted
|
||||
add address=52.158.209.86 list=trusted
|
||||
add address=3.210.85.72 list=gaiia
|
||||
add address=3.81.237.51 list=gaiia
|
||||
add address=3.215.70.188 list=gaiia
|
||||
add address=3.228.90.246 list=gaiia
|
||||
add address=47.147.43.145 list=trusted
|
||||
add address=71.41.226.117 comment=TWC list=BGP-NEIGHBORS
|
||||
add address=204.110.191.185 comment=Preseem list=BGP-NEIGHBORS
|
||||
add address=204.110.191.252 comment="Virtual RouterOS" list=BGP-NEIGHBORS
|
||||
add address=204.110.188.0/22 list=bgp-networks
|
||||
add address=151.243.11.0/24 list=abusive
|
||||
add address=172.245.56.83 list=trusted
|
||||
/ip firewall filter
|
||||
add action=accept chain=input comment="BGP Input" dst-port=179 protocol=tcp
|
||||
add action=accept chain=output comment="BGP Output" protocol=tcp src-port=179
|
||||
add action=accept chain=forward comment="BGP Forward" dst-port=179 protocol=\
|
||||
tcp
|
||||
add action=accept chain=forward comment="BGP Forward Reply" protocol=tcp \
|
||||
src-port=179
|
||||
add action=accept chain=input comment="OSPF Input" protocol=ospf
|
||||
add action=accept chain=output comment="OSPF Output" protocol=ospf
|
||||
add action=accept chain=forward comment="OSPF Forward" protocol=ospf
|
||||
add action=accept chain=input comment="Established Input" connection-state=\
|
||||
established,related
|
||||
add action=accept chain=forward comment="Established Forward" \
|
||||
connection-state=established,related
|
||||
add action=accept chain=input comment="Allow Remote Winbox" disabled=yes \
|
||||
in-interface=*18
|
||||
add action=fasttrack-connection chain=forward comment=\
|
||||
"fasttrack established/related" connection-state=established,related
|
||||
add action=accept chain=forward comment="accept established/related" \
|
||||
connection-state=established,related
|
||||
add action=drop chain=forward dst-address=91.190.98.0/24 log=yes
|
||||
add action=accept chain=forward dst-port=23 protocol=tcp src-address=\
|
||||
204.110.190.208
|
||||
add action=accept chain=forward dst-address=204.110.191.219
|
||||
add action=accept chain=forward dst-address=204.110.191.222
|
||||
add action=accept chain=forward dst-address=204.110.191.252
|
||||
add action=accept chain=forward dst-address=204.110.191.8
|
||||
add action=accept chain=forward dst-address=204.110.191.192/26 src-address=\
|
||||
52.158.209.86
|
||||
add action=accept chain=forward dst-address=204.110.191.210
|
||||
add action=accept chain=forward disabled=yes dst-address=204.110.191.248 \
|
||||
dst-port=3306 log=yes protocol=tcp
|
||||
add action=accept chain=forward dst-address=204.110.191.248 dst-port=3306 \
|
||||
protocol=tcp src-address-list=gaiia
|
||||
add action=accept chain=forward disabled=yes dst-address=204.110.191.248
|
||||
add action=accept chain=forward dst-address=204.110.191.231
|
||||
add action=accept chain=forward dst-address=204.110.191.232 in-interface=\
|
||||
sfp-sfpplus12-spectrum
|
||||
add action=tarpit chain=input protocol=tcp src-address-list=abusive
|
||||
add action=drop chain=forward src-address-list=abusive
|
||||
add action=accept chain=forward connection-state=established,related \
|
||||
disabled=yes
|
||||
add action=accept chain=input connection-state=established,related,untracked \
|
||||
disabled=yes
|
||||
add action=drop chain=forward disabled=yes dst-port=3389 in-interface=\
|
||||
sfp-sfpplus12-spectrum protocol=tcp
|
||||
add action=drop chain=forward disabled=yes dst-port=3389 in-interface=\
|
||||
sfp-sfpplus12-spectrum protocol=udp
|
||||
add action=drop chain=forward comment="Drop anyone in Black List (SSH)." \
|
||||
in-interface=sfp-sfpplus12-spectrum log-prefix="BL_Black List (SSH)" \
|
||||
src-address-list="Black List (SSH)"
|
||||
add action=drop chain=forward comment="block incoming ftp" disabled=yes \
|
||||
dst-port=21 in-interface=sfp-sfpplus12-spectrum protocol=tcp
|
||||
add action=drop chain=forward comment="block incoming telnet" dst-port=23 \
|
||||
in-interface=sfp-sfpplus12-spectrum protocol=tcp
|
||||
add action=drop chain=input comment="Drop all packets from public internet whi\
|
||||
ch should not exist in public network" in-interface=\
|
||||
sfp-sfpplus12-spectrum src-address-list=NotPublic
|
||||
add action=drop chain=input disabled=yes dst-port=3784 in-interface=\
|
||||
sfp-sfpplus12-spectrum log=yes protocol=udp
|
||||
add action=drop chain=forward comment=bfd dst-port=3784 in-interface=\
|
||||
sfp-sfpplus12-spectrum protocol=udp
|
||||
add action=drop chain=input comment=bfd dst-port=3784 in-interface=\
|
||||
sfp-sfpplus12-spectrum protocol=udp
|
||||
add action=drop chain=input comment="Drop Invalid connections" \
|
||||
connection-state=invalid
|
||||
add action=add-src-to-address-list address-list=winbox_blacklist \
|
||||
address-list-timeout=4w3d chain=input connection-state=new disabled=yes \
|
||||
dst-port=8291 log=yes protocol=tcp src-address-list=winbox_stage3
|
||||
add action=add-src-to-address-list address-list=winbox_stage3 \
|
||||
address-list-timeout=1m chain=input connection-state=new disabled=yes \
|
||||
dst-port=8291 protocol=tcp src-address-list=winbox_stage2
|
||||
add action=add-src-to-address-list address-list=winbox_stage2 \
|
||||
address-list-timeout=1m chain=input connection-state=new disabled=yes \
|
||||
dst-port=8291 protocol=tcp src-address-list=winbox_stage1
|
||||
add action=add-src-to-address-list address-list=winbox_stage1 \
|
||||
address-list-timeout=1m chain=input connection-state=new disabled=yes \
|
||||
dst-port=8291 protocol=tcp src-address-list=!vntx
|
||||
add action=drop chain=forward comment="drop winbox brute downstream" \
|
||||
disabled=yes dst-port=8291 protocol=tcp src-address-list=winbox_blacklist
|
||||
add action=accept chain=input comment="BGP Neighbors" dst-port=179 protocol=\
|
||||
tcp src-address-list=BGP-NEIGHBORS
|
||||
add action=drop chain=input comment="Drop anyone in Black List (SSH)." \
|
||||
disabled=yes in-interface=sfp-sfpplus12-spectrum log-prefix=\
|
||||
"BL_Black List (SSH)" src-address-list="Black List (SSH)"
|
||||
add action=jump chain=input comment="Jump to Black List (SSH) chain." \
|
||||
dst-port=22 in-interface=sfp-sfpplus12-spectrum jump-target=\
|
||||
"Black List (SSH) Chain" protocol=tcp
|
||||
add action=drop chain=input comment=. dst-port=23 in-interface=\
|
||||
sfp-sfpplus12-spectrum protocol=tcp
|
||||
add action=drop chain=forward comment=. dst-port=23 in-interface=\
|
||||
sfp-sfpplus12-spectrum protocol=tcp
|
||||
add action=jump chain=forward comment="Jump to Black List (SSH) chain." \
|
||||
dst-port=22 in-interface=sfp-sfpplus12-spectrum jump-target=\
|
||||
"Black List (SSH) Chain" protocol=tcp
|
||||
add action=add-src-to-address-list address-list="Black List (SSH)" \
|
||||
address-list-timeout=4w2d chain="Black List (SSH) Chain" comment="Transfer\
|
||||
\_repeated attempts from Black List (SSH) Stage 3 to Black List (SSH)." \
|
||||
connection-state=new in-interface=sfp-sfpplus12-spectrum log-prefix=\
|
||||
"Add_Black List (SSH)" src-address-list="Black List (SSH) Stage 3"
|
||||
add action=add-src-to-address-list address-list="Black List (SSH) Stage 3" \
|
||||
address-list-timeout=1m chain="Black List (SSH) Chain" comment=\
|
||||
"Add successive attempts to Black List (SSH) Stage 3." connection-state=\
|
||||
new in-interface=sfp-sfpplus12-spectrum log-prefix=\
|
||||
"Add_Black List (SSH) S3" src-address-list="Black List (SSH) Stage 2"
|
||||
add action=add-src-to-address-list address-list="Black List (SSH) Stage 2" \
|
||||
address-list-timeout=1m chain="Black List (SSH) Chain" comment=\
|
||||
"Add successive attempts to Black List (SSH) Stage 2." connection-state=\
|
||||
new in-interface=sfp-sfpplus12-spectrum log-prefix=\
|
||||
"Add_Black List (SSH) S2" src-address-list="Black List (SSH) Stage 1"
|
||||
add action=add-src-to-address-list address-list="Black List (SSH) Stage 1" \
|
||||
address-list-timeout=1m chain="Black List (SSH) Chain" comment=\
|
||||
"Add initial attempt to Black List (SSH) Stage 1." connection-state=new \
|
||||
in-interface=sfp-sfpplus12-spectrum log-prefix="Add_Black List (SSH) S1" \
|
||||
src-address-list=!trusted
|
||||
add action=return chain="Black List (SSH) Chain" comment=\
|
||||
"Return From Black List (SSH) chain."
|
||||
add action=accept chain=forward disabled=yes dst-address=204.110.191.192/26 \
|
||||
dst-port=3306 log=yes protocol=tcp src-address=162.243.131.32
|
||||
add action=accept chain=forward dst-address=204.110.191.197
|
||||
add action=accept chain=forward comment=micromirror dst-address=\
|
||||
204.110.191.235
|
||||
add action=accept chain=forward dst-address=204.110.191.219
|
||||
add action=accept chain=forward dst-address=204.110.191.8
|
||||
add action=accept chain=forward dst-address=204.110.191.192/26 protocol=icmp
|
||||
add action=accept chain=forward dst-address=204.110.191.192/26 dst-port=80 \
|
||||
protocol=tcp
|
||||
add action=accept chain=forward dst-address=204.110.191.192/26 dst-port=443 \
|
||||
protocol=tcp
|
||||
add action=accept chain=forward dst-address=204.110.191.232 dst-port=14580 \
|
||||
protocol=tcp
|
||||
add action=accept chain=forward dst-address=204.110.191.232 dst-port=14501 \
|
||||
protocol=tcp
|
||||
add action=accept chain=forward comment=wireguard dst-address=204.110.191.247 \
|
||||
dst-port=51820 protocol=udp
|
||||
add action=accept chain=forward dst-address=204.110.191.247 dst-port=22 \
|
||||
protocol=tcp
|
||||
add action=drop chain=forward disabled=yes dst-address=204.110.191.192/26 \
|
||||
dst-port=3306 log=yes protocol=tcp
|
||||
add action=accept chain=forward dst-address=204.110.191.192/26 dst-port=53 \
|
||||
protocol=udp src-address-list=trusted
|
||||
add action=accept chain=forward comment=ns2 dst-address=204.110.191.239 \
|
||||
dst-port=53 protocol=udp
|
||||
add action=accept chain=forward comment=ns1 dst-address=204.110.191.249 \
|
||||
dst-port=53 protocol=udp
|
||||
add action=accept chain=forward dst-address=204.110.191.192/26 dst-port=22 \
|
||||
protocol=tcp src-address-list=trusted
|
||||
add action=accept chain=forward dst-address=204.110.191.192/26 dst-port=22 \
|
||||
protocol=tcp src-address-list=preseem
|
||||
add action=accept chain=forward dst-address=204.110.191.192/26 \
|
||||
src-address-list=trusted
|
||||
add action=drop chain=forward dst-address=204.110.191.192/26 \
|
||||
src-address-list=!trusted
|
||||
add action=drop chain=input dst-port=8921 protocol=tcp src-address-list=\
|
||||
!trusted
|
||||
add action=drop chain=forward dst-port=8921 protocol=tcp src-address-list=\
|
||||
!trusted
|
||||
add action=drop chain=input disabled=yes in-interface=sfp-sfpplus12-spectrum \
|
||||
src-address=100.64.0.0/10
|
||||
add action=drop chain=output disabled=yes dst-address=100.64.0.0/10 \
|
||||
out-interface=sfp-sfpplus12-spectrum
|
||||
add action=drop chain=forward disabled=yes in-interface=\
|
||||
sfp-sfpplus12-spectrum src-address=100.64.0.0/10
|
||||
add action=drop chain=forward disabled=yes out-interface=\
|
||||
sfp-sfpplus12-spectrum src-address=100.64.0.0/10
|
||||
add action=drop chain=forward disabled=yes dst-address=100.64.0.0/10 \
|
||||
out-interface=sfp-sfpplus12-spectrum
|
||||
add action=accept chain=input comment="Established Input" connection-state=\
|
||||
established,related
|
||||
add action=accept chain=forward comment="Established Forward" \
|
||||
connection-state=established,related
|
||||
add action=accept chain=input comment=BGP dst-port=179 protocol=tcp
|
||||
add action=accept chain=output comment=BGP protocol=tcp src-port=179
|
||||
add action=accept chain=forward comment=BGP dst-port=179 protocol=tcp
|
||||
add action=accept chain=forward comment=BGP protocol=tcp src-port=179
|
||||
add action=accept chain=input comment=OSPF protocol=ospf
|
||||
add action=accept chain=output comment=OSPF protocol=ospf
|
||||
add action=accept chain=forward comment=OSPF protocol=ospf
|
||||
add action=drop chain=forward dst-port=23 log-prefix=TELNET protocol=tcp
|
||||
/ip firewall mangle
|
||||
add action=mark-packet chain=prerouting disabled=yes new-packet-mark=Inactive \
|
||||
passthrough=no src-address-list=Inactive
|
||||
add action=mark-packet chain=prerouting disabled=yes dst-address-list=\
|
||||
Inactive new-packet-mark=Inactive passthrough=no
|
||||
add action=accept chain=postrouting disabled=yes protocol=tcp tcp-flags=ack
|
||||
/ip firewall nat
|
||||
add action=src-nat chain=srcnat comment="Internal NAT catch-all" \
|
||||
out-interface=sfp-sfpplus12-spectrum src-address=10.0.0.0/8 to-addresses=\
|
||||
204.110.191.190
|
||||
add action=src-nat chain=srcnat out-interface=sfp-sfpplus12-spectrum \
|
||||
src-address=10.0.0.0/24 to-addresses=204.110.190.254
|
||||
add action=src-nat chain=srcnat comment="Loopback NAT" out-interface=\
|
||||
sfp-sfpplus12-spectrum src-address=10.254.254.0/24 to-addresses=\
|
||||
204.110.191.190
|
||||
add action=src-nat chain=srcnat comment="Loopback NAT" out-interface=\
|
||||
sfp-sfpplus12-spectrum src-address=10.254.254.0/24 to-addresses=\
|
||||
204.110.191.190
|
||||
add action=src-nat chain=srcnat out-interface=sfp-sfpplus12-spectrum \
|
||||
src-address=10.0.0.0/8 to-addresses=204.110.191.190
|
||||
add action=src-nat chain=srcnat comment="Internal Network NAT" out-interface=\
|
||||
sfp-sfpplus12-spectrum src-address=10.250.1.0/24 to-addresses=\
|
||||
204.110.191.190
|
||||
add action=src-nat chain=srcnat comment="Internal Network NAT" out-interface=\
|
||||
sfp-sfpplus12-spectrum src-address=10.250.1.0/24 to-addresses=\
|
||||
204.110.191.190
|
||||
add action=src-nat chain=srcnat comment="fallthrough CGNAT" disabled=yes \
|
||||
out-interface=sfp-sfpplus12-spectrum src-address=100.64.0.0/10 \
|
||||
to-addresses=204.110.190.254
|
||||
add action=netmap chain=srcnat ipsec-policy=out,none out-interface=\
|
||||
sfp-sfpplus12-spectrum src-address=100.64.0.0/10 to-addresses=\
|
||||
204.110.190.128/25
|
||||
add action=netmap chain=dstmap comment=\
|
||||
"Port Forwarding Solution for CGNAT (TCP)" dst-address=204.110.190.128/25 \
|
||||
dst-port=1024-65535 protocol=tcp to-addresses=100.64.0.0/10
|
||||
add action=netmap chain=dstmap comment=\
|
||||
"Port Forwarding Solution for CGNAT (UDP)" dst-address=204.110.190.128/25 \
|
||||
dst-port=1024-65535 protocol=udp to-addresses=100.64.0.0/10
|
||||
/ip firewall raw
|
||||
add action=drop chain=prerouting src-address-list="Black List (SSH)"
|
||||
add action=drop chain=prerouting disabled=yes src-address-list=abusive
|
||||
add action=drop chain=prerouting disabled=yes src-address-list=\
|
||||
winbox_blacklist
|
||||
/ip proxy
|
||||
set port=23435
|
||||
/ip proxy access
|
||||
add src-address=204.110.188.0/22
|
||||
/ip route
|
||||
add distance=1 gateway=71.41.226.117
|
||||
add distance=1 dst-address=10.10.0.0/20 gateway=204.110.191.185
|
||||
add distance=1 dst-address=10.10.16.0/20 gateway=204.110.191.185
|
||||
add distance=1 dst-address=10.250.1.8/29 gateway=204.110.191.185
|
||||
add distance=1 dst-address=10.250.1.8/29 gateway=204.110.191.185
|
||||
add distance=1 dst-address=10.250.1.24/29 gateway=204.110.191.185
|
||||
add distance=1 dst-address=10.250.1.64/29 gateway=204.110.191.185
|
||||
add distance=1 dst-address=10.250.1.64/29 gateway=204.110.191.185
|
||||
add distance=1 dst-address=10.250.1.88/29 gateway=204.110.191.185
|
||||
add distance=1 dst-address=10.250.1.88/29 gateway=204.110.191.185
|
||||
add distance=1 dst-address=10.254.254.101/32 gateway=204.110.191.185
|
||||
add distance=1 dst-address=10.254.254.102/32 gateway=204.110.191.185
|
||||
add distance=1 dst-address=100.64.0.0/10 gateway=204.110.191.185
|
||||
add distance=1 dst-address=100.64.0.0/22 gateway=204.110.191.185
|
||||
add distance=1 dst-address=100.64.4.0/22 gateway=204.110.191.185
|
||||
add distance=1 dst-address=100.64.4.0/22 gateway=204.110.191.185
|
||||
add distance=1 dst-address=100.64.12.0/22 gateway=204.110.191.185
|
||||
add distance=1 dst-address=172.63.0.0/20 gateway=204.110.191.253
|
||||
add distance=1 dst-address=204.110.188.0/22 gateway=204.110.191.181
|
||||
add distance=1 dst-address=204.110.188.0/27 gateway=204.110.191.253
|
||||
add distance=1 dst-address=204.110.188.32/27 gateway=204.110.191.185
|
||||
add distance=1 dst-address=204.110.188.32/27 gateway=204.110.191.185
|
||||
add distance=1 dst-address=204.110.188.224/27 gateway=204.110.191.185
|
||||
add distance=1 dst-address=204.110.191.0/27 gateway=204.110.191.185
|
||||
/ip service
|
||||
set telnet disabled=yes
|
||||
set ftp disabled=yes
|
||||
set www address=204.110.188.0/22 port=2080
|
||||
set ssh address=204.110.188.0/22,10.0.0.0/8,100.64.0.0/10 port=1022
|
||||
set api address=204.110.188.0/22,10.0.0.0/8,100.64.0.0/10
|
||||
set api-ssl address=0.0.0.0/0 certificate=server
|
||||
/ip ssh
|
||||
set always-allow-password-login=yes forwarding-enabled=remote strong-crypto=\
|
||||
yes
|
||||
/ipv6 address
|
||||
add address=2605:6000:0:8::f:373/127 advertise=no interface=\
|
||||
sfp-sfpplus12-spectrum
|
||||
add address=2606:1c80::1 interface=vlan9_sfpplus8
|
||||
add address=2606:1c80:0:10::254/128 advertise=no comment="edge loopback" \
|
||||
interface=loopback
|
||||
add address=2606:1c80:0:11::1/127 advertise=no comment=\
|
||||
"P2P edge<->core direct (backup)" interface=sfp-sfpplus7-core-direct
|
||||
add address=2606:1c80:0:1002::1/127 advertise=no comment=\
|
||||
"P2P edge<->core via preseem (primary)" interface=sfp-sfpplus11-preseem
|
||||
/ipv6 firewall address-list
|
||||
add address=2606:1c80::/32 list=bgp-networks
|
||||
/ipv6 firewall filter
|
||||
add action=accept chain=input comment="IPv6 BGP Input" dst-port=179 protocol=\
|
||||
tcp
|
||||
add action=accept chain=output comment="IPv6 BGP Output" protocol=tcp \
|
||||
src-port=179
|
||||
add action=accept chain=forward comment="IPv6 BGP Forward" dst-port=179 \
|
||||
protocol=tcp
|
||||
add action=accept chain=forward comment="IPv6 BGP Forward Reply" protocol=tcp \
|
||||
src-port=179
|
||||
add action=accept chain=input comment="IPv6 Established Input" \
|
||||
connection-state=established,related
|
||||
add action=accept chain=forward comment="IPv6 Established Forward" \
|
||||
connection-state=established,related
|
||||
add action=accept chain=input comment="icmpv6 input" protocol=icmpv6
|
||||
add action=accept chain=input comment="link-local input" src-address=\
|
||||
fe80::/10
|
||||
add action=accept chain=input comment="internal input" src-address=\
|
||||
2606:1c80::/32
|
||||
add action=accept chain=forward comment="PMTUD forward" hop-limit=equal:1 \
|
||||
protocol=icmpv6
|
||||
add action=accept chain=forward comment="icmpv6 forward" protocol=icmpv6
|
||||
add action=accept chain=forward comment=internal<->internal dst-address=\
|
||||
2606:1c80::/32 src-address=2606:1c80::/32
|
||||
/ipv6 route
|
||||
add comment="infra: via preseem (primary)" distance=1 dst-address=\
|
||||
2606:1c80::/48 gateway=2606:1c80:0:1002::2
|
||||
add comment="infra: via direct (backup)" distance=2 dst-address=\
|
||||
2606:1c80::/48 gateway=2606:1c80:0:11::2
|
||||
/mpls interface
|
||||
set [ find default=yes ] mpls-mtu=1530
|
||||
add interface=sfp-sfpplus8-server-switch mpls-mtu=1530
|
||||
/mpls ldp
|
||||
set distribute-for-default-route=yes enabled=yes loop-detect=yes lsr-id=\
|
||||
10.254.254.254 transport-address=10.254.254.254
|
||||
/mpls ldp accept-filter
|
||||
add
|
||||
/mpls ldp advertise-filter
|
||||
add
|
||||
/mpls ldp interface
|
||||
add disabled=yes interface=ether1
|
||||
add disabled=yes interface=sfp-sfpplus11-preseem transport-address=\
|
||||
10.254.254.254
|
||||
add disabled=yes interface=vlan9_sfpplus8 transport-address=10.254.254.254
|
||||
add interface=sfp-sfpplus8-server-switch
|
||||
/routing bfd interface
|
||||
set [ find default=yes ] disabled=yes
|
||||
add interface=sfp-sfpplus7-core-direct
|
||||
add interface=sfp-sfpplus11-preseem
|
||||
/routing bgp network
|
||||
add network=204.110.188.0/22 synchronize=no
|
||||
add network=2606:1c80::/32 synchronize=no
|
||||
/routing bgp peer
|
||||
add in-filter=twc-in name=twc out-filter=twc-out remote-address=71.41.226.117 \
|
||||
remote-as=11427 ttl=default
|
||||
add address-families=ipv6 in-filter=twc-in name=twc-v6 out-filter=twc-out \
|
||||
remote-address=2605:6000:0:8::f:372 remote-as=11427 ttl=default
|
||||
add comment="TEAM-CYMRU BOGON Server #1" disabled=yes in-filter=\
|
||||
BOGON-SERVER-IN max-prefix-limit=50 multihop=yes name=CYMRU-1 out-filter=\
|
||||
BGP-DROP remote-address=38.229.6.20 remote-as=65332 tcp-md5-key=\
|
||||
mC9LaaOi0P
|
||||
add comment="TEAM-CYMRU BOGON Server #2" disabled=yes in-filter=\
|
||||
BOGON-SERVER-IN max-prefix-limit=50 multihop=yes name=CYMRU-1 out-filter=\
|
||||
BGP-DROP remote-address=38.229.46.20 remote-as=65332 tcp-md5-key=\
|
||||
mC9LaaOi0P
|
||||
add address-families=ip,ipv6 default-originate=always disabled=yes name=\
|
||||
edge_core_preseem nexthop-choice=propagate out-filter=bgp-default-only \
|
||||
remote-address=204.110.191.185 remote-as=393837
|
||||
add address-families=ipv6 comment="iBGP to verona, v4 transport / v6 NLRI" \
|
||||
default-originate=always in-filter=ibgp-tower-in-v6 name=ibgp-verona-v6 \
|
||||
nexthop-choice=force-self out-filter=ibgp-tower-out-v6 remote-address=\
|
||||
10.254.254.101 remote-as=393837 update-source=loopback
|
||||
/routing filter
|
||||
add action=accept chain=twc-out comment=blackhole disabled=yes prefix=\
|
||||
204.110.188.197 prefix-length=32 set-bgp-communities=7486:666
|
||||
add action=accept chain=twc-in prefix=0.0.0.0/0
|
||||
add action=accept chain=twc-in prefix=0.0.0.0/0 prefix-length=0
|
||||
add action=discard chain=twc-in disabled=yes prefix=0.0.0.0 prefix-length=\
|
||||
0-32 protocol=""
|
||||
add action=accept chain=twc-out prefix=204.110.188.0/22
|
||||
add action=accept chain=twc-out prefix=2606:1c80::/32
|
||||
add action=discard chain=twc-out
|
||||
add action=discard chain=BGP-DROP
|
||||
add action=accept bgp-communities=65333:888 chain=BOGON-SERVER-IN disabled=\
|
||||
yes set-type=blackhole
|
||||
add action=discard chain=BOGON-SERVER-IN disabled=yes
|
||||
add action=discard chain=BGP-DROP
|
||||
add action=accept chain=bgp-default-only disabled=yes prefix=0.0.0.0/0 \
|
||||
prefix-length=0
|
||||
add action=discard chain=bgp-default-only disabled=yes prefix=0.0.0.0/0 \
|
||||
prefix-length=0-32
|
||||
add action=accept chain=ibgp-tower-in-v6 comment=\
|
||||
"tower /44 customer aggregate" prefix=2606:1c80:1000::/36 prefix-length=\
|
||||
44
|
||||
add action=accept chain=ibgp-tower-in-v6 comment="tower v6 loopback" prefix=\
|
||||
2606:1c80:0:10::/64 prefix-length=128
|
||||
add action=discard chain=ibgp-tower-in-v6 comment="default deny"
|
||||
add action=accept chain=ibgp-tower-out-v6 comment="default route to tower" \
|
||||
prefix=::/0 prefix-length=0
|
||||
add action=discard chain=ibgp-tower-out-v6 comment="default deny"
|
||||
/routing ospf interface
|
||||
add interface=sfp-sfpplus12-spectrum network-type=broadcast passive=yes
|
||||
add interface=sfp-sfpplus11-preseem network-type=point-to-point use-bfd=yes
|
||||
add cost=20 interface=sfp-sfpplus9 network-type=nbma use-bfd=yes
|
||||
add interface=sfp-sfpplus10 network-type=nbma passive=yes
|
||||
add cost=20 interface=ether1 network-type=broadcast
|
||||
add interface=sfp-sfpplus8-server-switch network-type=broadcast
|
||||
add cost=100 interface=sfp-sfpplus7-core-direct network-type=point-to-point \
|
||||
use-bfd=yes
|
||||
add interface=vlan9_sfpplus8 network-type=nbma
|
||||
/routing ospf nbma-neighbor
|
||||
add address=204.110.191.189 poll-interval=10s
|
||||
add address=10.250.1.94
|
||||
add address=10.250.1.57
|
||||
add address=10.250.1.201
|
||||
add address=10.250.2.1
|
||||
add address=204.110.191.185 poll-interval=10s
|
||||
add address=204.110.191.252 poll-interval=10s
|
||||
/routing ospf network
|
||||
add area=backbone
|
||||
/routing rip
|
||||
set distribute-default=always redistribute-connected=yes redistribute-ospf=\
|
||||
yes
|
||||
/routing rip interface
|
||||
add interface=sfp-sfpplus11-preseem
|
||||
add interface=sfp-sfpplus7-core-direct
|
||||
add interface=sfp-sfpplus8-server-switch
|
||||
/routing rip network
|
||||
add
|
||||
/snmp
|
||||
set contact="Verona Networks" enabled=yes location="Verona Networks"
|
||||
/system clock
|
||||
set time-zone-name=America/Chicago
|
||||
/system identity
|
||||
set name=edge
|
||||
/system logging
|
||||
add action=logs topics=info
|
||||
/system note
|
||||
set note="__ __\
|
||||
\n\\ \\ / /__ _ __ ___ _ __ __ _\
|
||||
\n \\ \\ / / _ \\ '__/ _ \\| '_ \\ / _` |\
|
||||
\n \\ V / __/ | | (_) | | | | (_| |\
|
||||
\n \\_/ \\___|_| \\___/|_| |_|\\__,_|\
|
||||
\n _ _ _ _\
|
||||
\n| \\ | | ___| |___ _____ _ __| | _____\
|
||||
\n| \\| |/ _ \\ __\\ \\ /\\ / / _ \\| '__| |/ / __|\
|
||||
\n| |\\ | __/ |_ \\ V V / (_) | | | <\\__ \\\
|
||||
\n|_| \\_|\\___|\\__| \\_/\\_/ \\___/|_| |_|\\_\\___/\
|
||||
\n\
|
||||
\n###############################################################\
|
||||
\n# Welcome to Verona Networks #\
|
||||
\n# All connections are monitored and recorded #\
|
||||
\n# Disconnect IMMEDIATELY if you are not an authorized user! #\
|
||||
\n###############################################################\
|
||||
\n\
|
||||
\n" show-at-login=no
|
||||
/system ntp client
|
||||
set enabled=yes primary-ntp=162.159.200.1 secondary-ntp=45.79.1.70
|
||||
/system routerboard settings
|
||||
set auto-upgrade=yes
|
||||
/system scheduler
|
||||
add name=reboot on-event="/system reboot\r\
|
||||
\n" policy=\
|
||||
ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon \
|
||||
start-date=jun/14/2023 start-time=03:00:00
|
||||
add comment=RWB_IP_RESOLVER interval=5m name=RWB_IP_RESOLVER on-event=\
|
||||
RWB_IP_RESOLVER policy=\
|
||||
ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon \
|
||||
start-date=oct/13/2022 start-time=22:59:02
|
||||
/tool graphing interface
|
||||
add allow-address=204.110.188.0/22
|
||||
/tool graphing queue
|
||||
add allow-address=204.110.188.0/22
|
||||
/tool graphing resource
|
||||
add allow-address=204.110.188.0/22
|
||||
/tool romon
|
||||
set enabled=yes
|
||||
/user aaa
|
||||
set default-group=full
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
# may/09/2026 15:08:04 by RouterOS 6.49.18
|
||||
# jul/10/2026 10:54:22 by RouterOS 6.49.18
|
||||
# software id = 8XZE-R7EJ
|
||||
#
|
||||
# model = CCR2004-1G-12S+2XS
|
||||
|
|
@ -31,7 +31,9 @@ add name=servers-public ranges=204.110.191.193-204.110.191.217
|
|||
add address-pool=380building disabled=no interface=sfp-sfpplus8-server-switch \
|
||||
name=380-building
|
||||
/ppp profile
|
||||
add name=RWB_sstp_profile
|
||||
set *0 dns-server=204.110.191.240,204.110.191.250
|
||||
add dns-server=204.110.191.240,204.110.191.250 name=RWB_sstp_profile
|
||||
set *FFFFFFFE dns-server=204.110.191.240,204.110.191.250
|
||||
/queue simple
|
||||
add burst-limit=1M/1M burst-threshold=1M/1M burst-time=1s/1s disabled=yes \
|
||||
limit-at=1M/1M max-limit=1M/1M name=Delinquent packet-marks=Delenquent \
|
||||
|
|
@ -205,6 +207,8 @@ add address=204.110.190.251/25 interface=cgnat network=204.110.190.128
|
|||
add address=204.110.190.252/25 interface=cgnat network=204.110.190.128
|
||||
add address=204.110.190.253/25 interface=cgnat network=204.110.190.128
|
||||
add address=204.110.190.254/25 interface=cgnat network=204.110.190.128
|
||||
add address=192.168.0.5/24 interface=sfp-sfpplus8-server-switch network=\
|
||||
192.168.0.0
|
||||
/ip dhcp-server lease
|
||||
add address=10.0.0.249 client-id=1:0:21:9b:91:5c:b6 mac-address=\
|
||||
00:21:9B:91:5C:B6 server=380-building
|
||||
|
|
@ -219,10 +223,10 @@ add address=10.0.0.252 client-id=1:bc:24:11:f5:81:d6 mac-address=\
|
|||
add address=10.0.0.251 client-id=1:bc:24:11:6b:73:53 mac-address=\
|
||||
BC:24:11:6B:73:53 server=380-building
|
||||
/ip dhcp-server network
|
||||
add address=10.0.0.0/24 dns-server=9.9.9.9,1.1.1.1 domain=vntx.net gateway=\
|
||||
10.0.0.254 ntp-server=23.150.41.122
|
||||
add address=204.110.191.192/26 dns-server=\
|
||||
204.110.191.240,204.110.191.250,1.1.1.1 gateway=204.110.191.254
|
||||
add address=10.0.0.0/24 dns-server=204.110.191.240,204.110.191.250 domain=\
|
||||
vntx.net gateway=10.0.0.254 ntp-server=23.150.41.122
|
||||
add address=204.110.191.192/26 dns-server=204.110.191.240,204.110.191.250 \
|
||||
gateway=204.110.191.254
|
||||
/ip dns
|
||||
set servers=204.110.191.240,204.110.191.250
|
||||
/ip firewall address-list
|
||||
|
|
@ -267,6 +271,8 @@ add address=204.110.191.252 comment="Virtual RouterOS" list=BGP-NEIGHBORS
|
|||
add address=204.110.188.0/22 list=bgp-networks
|
||||
add address=151.243.11.0/24 list=abusive
|
||||
add address=172.245.56.83 list=trusted
|
||||
add address=172.16.0.0/12 comment="RFC 1918 private" list=trusted
|
||||
add address=192.168.0.0/16 comment="RFC 1918 private" list=trusted
|
||||
/ip firewall filter
|
||||
add action=accept chain=input comment="BGP Input" dst-port=179 protocol=tcp
|
||||
add action=accept chain=output comment="BGP Output" protocol=tcp src-port=179
|
||||
|
|
@ -277,6 +283,10 @@ add action=accept chain=forward comment="BGP Forward Reply" protocol=tcp \
|
|||
add action=accept chain=input comment="OSPF Input" protocol=ospf
|
||||
add action=accept chain=output comment="OSPF Output" protocol=ospf
|
||||
add action=accept chain=forward comment="OSPF Forward" protocol=ospf
|
||||
add action=drop chain=input comment="block winbox from non-trusted" dst-port=\
|
||||
8291 protocol=tcp src-address-list=!trusted
|
||||
add action=drop chain=input comment="block winbox from non-trusted" dst-port=\
|
||||
8291 protocol=tcp src-address-list=!trusted
|
||||
add action=accept chain=input comment="Established Input" connection-state=\
|
||||
established,related
|
||||
add action=accept chain=forward comment="Established Forward" \
|
||||
|
|
@ -509,24 +519,19 @@ add distance=1 gateway=71.41.226.117
|
|||
add distance=1 dst-address=10.10.0.0/20 gateway=204.110.191.185
|
||||
add distance=1 dst-address=10.10.16.0/20 gateway=204.110.191.185
|
||||
add distance=1 dst-address=10.250.1.8/29 gateway=204.110.191.185
|
||||
add distance=1 dst-address=10.250.1.8/29 gateway=204.110.191.185
|
||||
add distance=1 dst-address=10.250.1.24/29 gateway=204.110.191.185
|
||||
add distance=1 dst-address=10.250.1.64/29 gateway=204.110.191.185
|
||||
add distance=1 dst-address=10.250.1.64/29 gateway=204.110.191.185
|
||||
add distance=1 dst-address=10.250.1.88/29 gateway=204.110.191.185
|
||||
add distance=1 dst-address=10.250.1.88/29 gateway=204.110.191.185
|
||||
add distance=1 dst-address=10.254.254.101/32 gateway=204.110.191.185
|
||||
add distance=1 dst-address=10.254.254.102/32 gateway=204.110.191.185
|
||||
add distance=1 dst-address=100.64.0.0/10 gateway=204.110.191.185
|
||||
add distance=1 dst-address=100.64.0.0/22 gateway=204.110.191.185
|
||||
add distance=1 dst-address=100.64.4.0/22 gateway=204.110.191.185
|
||||
add distance=1 dst-address=100.64.4.0/22 gateway=204.110.191.185
|
||||
add distance=1 dst-address=100.64.12.0/22 gateway=204.110.191.185
|
||||
add distance=1 dst-address=172.63.0.0/20 gateway=204.110.191.253
|
||||
add distance=1 dst-address=204.110.188.0/22 gateway=204.110.191.181
|
||||
add distance=1 dst-address=204.110.188.0/27 gateway=204.110.191.253
|
||||
add distance=1 dst-address=204.110.188.32/27 gateway=204.110.191.185
|
||||
add distance=1 dst-address=204.110.188.32/27 gateway=204.110.191.185
|
||||
add distance=1 dst-address=204.110.188.224/27 gateway=204.110.191.185
|
||||
add distance=1 dst-address=204.110.191.0/27 gateway=204.110.191.185
|
||||
/ip service
|
||||
|
|
@ -535,12 +540,14 @@ set ftp disabled=yes
|
|||
set www address=204.110.188.0/22 port=2080
|
||||
set ssh address=204.110.188.0/22,10.0.0.0/8,100.64.0.0/10 port=1022
|
||||
set api address=204.110.188.0/22,10.0.0.0/8,100.64.0.0/10
|
||||
set winbox address=\
|
||||
204.110.188.0/22,10.0.0.0/8,100.64.0.0/10,172.16.0.0/12,192.168.0.0/16
|
||||
set api-ssl address=0.0.0.0/0 certificate=server
|
||||
/ip ssh
|
||||
set always-allow-password-login=yes forwarding-enabled=remote strong-crypto=\
|
||||
yes
|
||||
/ipv6 address
|
||||
add address=2605:6000:0:8::f:373/127 advertise=no interface=\
|
||||
add address=2605:6000:0:8::f:373/127 advertise=no disabled=yes interface=\
|
||||
sfp-sfpplus12-spectrum
|
||||
add address=2606:1c80::1 interface=vlan9_sfpplus8
|
||||
add address=2606:1c80:0:10::254/128 advertise=no comment="edge loopback" \
|
||||
|
|
@ -549,6 +556,8 @@ add address=2606:1c80:0:11::1 advertise=no comment=\
|
|||
"P2P edge<->core direct (backup)" interface=sfp-sfpplus7-core-direct
|
||||
add address=2606:1c80:0:1002::1 advertise=no comment=\
|
||||
"P2P edge<->core via preseem (primary)" interface=sfp-sfpplus11-preseem
|
||||
add address=2605:ed00:ec32:10c0::2 advertise=no interface=\
|
||||
sfp-sfpplus12-spectrum
|
||||
/ipv6 firewall address-list
|
||||
add address=2606:1c80::/32 list=bgp-networks
|
||||
/ipv6 firewall filter
|
||||
|
|
@ -611,15 +620,7 @@ add network=2606:1c80::/32 synchronize=no
|
|||
add in-filter=twc-in name=twc out-filter=twc-out remote-address=71.41.226.117 \
|
||||
remote-as=11427 ttl=default
|
||||
add address-families=ipv6 in-filter=twc-in name=twc-v6 out-filter=twc-out \
|
||||
remote-address=2605:6000:0:8::f:372 remote-as=11427 ttl=default
|
||||
add comment="TEAM-CYMRU BOGON Server #1" disabled=yes in-filter=\
|
||||
BOGON-SERVER-IN max-prefix-limit=50 multihop=yes name=CYMRU-1 out-filter=\
|
||||
BGP-DROP remote-address=38.229.6.20 remote-as=65332 tcp-md5-key=\
|
||||
mC9LaaOi0P
|
||||
add comment="TEAM-CYMRU BOGON Server #2" disabled=yes in-filter=\
|
||||
BOGON-SERVER-IN max-prefix-limit=50 multihop=yes name=CYMRU-1 out-filter=\
|
||||
BGP-DROP remote-address=38.229.46.20 remote-as=65332 tcp-md5-key=\
|
||||
mC9LaaOi0P
|
||||
remote-address=2605:ed00:ec32:10c0::1 remote-as=11427 ttl=default
|
||||
add address-families=ip,ipv6 default-originate=always disabled=yes name=\
|
||||
edge_core_preseem nexthop-choice=propagate out-filter=bgp-default-only \
|
||||
remote-address=204.110.191.185 remote-as=393837
|
||||
|
|
|
|||
3
mikrotik-tool/fix-ospf.rsc
Normal file
3
mikrotik-tool/fix-ospf.rsc
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
/routing ospf interface-template set [find where interfaces=ether4-newhope] auth=sha512 authentication-key=5YHHWmoooAyVaFDX48ffjGjc36XMO5Irb7zS44Tsvet use-bfd=no
|
||||
/routing ospf interface-template set [find where interfaces=ether6-culleoka-11ghz] auth=sha512 authentication-key=5YHHWmoooAyVaFDX48ffjGjc36XMO5Irb7zS44Tsvet use-bfd=no
|
||||
/routing ospf interface-template add area=backbone-v2 auth=sha512 auth-id=1 authentication-key=5YHHWmoooAyVaFDX48ffjGjc36XMO5Irb7zS44Tsvet cost=10 disabled=no interfaces=ether5-climax priority=1 type=ptp use-bfd=no
|
||||
38
mikrotik-tool/home-recursive-failover.rsc
Normal file
38
mikrotik-tool/home-recursive-failover.rsc
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
# home-recursive-failover.rsc
|
||||
# Replace stateful netwatch enable/disable failover with stateless recursive
|
||||
# check-gateway failover. Root cause of the "VNTX default stuck disabled"
|
||||
# incident: netwatch down-script persists disabled=yes (survives reboots),
|
||||
# and its probe to 4.2.2.2 is only glued to the VNTX path while the probe-pin
|
||||
# route is active — so the check can die permanently (verona hotspot-binding
|
||||
# loss) or leak out another WAN (TMO netwatch reads "up" today with the TMO
|
||||
# interface physically down, answered via VNTX).
|
||||
#
|
||||
# New model (standard ROS recursive failover):
|
||||
# - probe pin: 4.2.2.x/32 -> WAN gw %interface, scope=10, NO check-gateway.
|
||||
# Stays active whenever the interface has link, so probes always egress
|
||||
# the WAN under test regardless of any default-route state.
|
||||
# - default: 0.0.0.0/0 gw=4.2.2.x target-scope=11 check-gateway=ping.
|
||||
# check-gateway pings 4.2.2.x THROUGH the pin; 2 misses (~20s) -> route
|
||||
# inactive (not disabled), first success -> active again. Nothing latches.
|
||||
# - Detection is 10s-cadence/2-miss instead of netwatch's 2s/1s single
|
||||
# packet — deliberate: the AF11 backbone has documented >1s RF bursts
|
||||
# (same reason BFD was turned off fleet-wide 2026-05-09).
|
||||
|
||||
/ip route
|
||||
# pins: remove check-gateway (the check belongs on the recursive default)
|
||||
set [find comment="TMO probe pin"] check-gateway=none
|
||||
set [find comment="VNTX probe pin"] check-gateway=none
|
||||
set [find comment="Starlink probe pin"] check-gateway=none
|
||||
# defaults: recurse through the probe IP, carry the check
|
||||
set [find comment="Default via TMO (primary)"] gateway=4.2.2.1 check-gateway=ping
|
||||
set [find comment="Default via VNTX (secondary)"] gateway=4.2.2.2 check-gateway=ping
|
||||
# Starlink default already gateway=4.2.2.3 + check-gateway=ping (correct form)
|
||||
# tmo table default (eweka policy routing) already recurses via 4.2.2.1;
|
||||
# add the check so it goes inactive (falls back to main) when TMO upstream dies
|
||||
set [find comment="tmo table default"] check-gateway=ping
|
||||
|
||||
# netwatch scripts are the latch — remove them
|
||||
/tool netwatch
|
||||
remove [find comment="TMO path"]
|
||||
remove [find comment="VNTX path"]
|
||||
remove [find comment="Starlink path"]
|
||||
|
|
@ -1,129 +0,0 @@
|
|||
# =============================================================================
|
||||
# graham's home router — IPv6 apply (RB5009UG+S+, ROS 7.22.3)
|
||||
# =============================================================================
|
||||
#
|
||||
# This is graham's home — multi-WAN: TMHI primary, VNTX secondary, Starlink
|
||||
# last-resort. v6 source is T-Mobile Home Internet's gateway via DHCPv6-PD
|
||||
# on ether6-tmobile. Switched off the verona-PD path on 2026-05-09 because
|
||||
# TWC isn't accepting our 2606:1c80::/32 yet (separate ticket); TMHI gives
|
||||
# us a globally-reachable v6 prefix today, even if it rotates.
|
||||
#
|
||||
# Whatever TMHI delegates (typically /64 on residential, sometimes /60 on
|
||||
# the newer gateways) populates the local `home-pd` pool. Internal LANs
|
||||
# pull /64s from it:
|
||||
# * bridge — main home LAN (10.0.16/24, 10.0.19/22)
|
||||
# * ether3-servers — servers LAN (10.0.15.254/21)
|
||||
# If TMHI only delegates a single /64, only `bridge` gets v6; the second
|
||||
# `from-pool=` add will error harmlessly. If they hand out a /60 or /56,
|
||||
# both LANs come up.
|
||||
# =============================================================================
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# WIPE — clean prior v6 config (preserve auto-link-local + Starlink ULA)
|
||||
# -----------------------------------------------------------------------------
|
||||
/ipv6 firewall filter remove [find]
|
||||
/ipv6 nd remove [find default=no]
|
||||
/ipv6 nd prefix remove [find]
|
||||
/ipv6 dhcp-server remove [find]
|
||||
/ipv6 dhcp-server option remove [find]
|
||||
/ipv6 dhcp-client remove [find]
|
||||
/ipv6 pool remove [find name=home-pd]
|
||||
/ipv6 route remove [find static=yes]
|
||||
/ipv6 address remove [find dynamic=no and disabled=no]
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# APPLY — settings
|
||||
# -----------------------------------------------------------------------------
|
||||
# Forward stays on (we're a router). accept-router-advertisements=yes is
|
||||
# REQUIRED on this router because TMHI only provides v6 via SLAAC — no
|
||||
# DHCPv6 server, no PD downstream. Without this we wouldn't even get a
|
||||
# global v6 address on ether6-tmobile. RAs we send on bridge/ether3-servers
|
||||
# are not received by ourselves so this is safe to leave global.
|
||||
/ipv6 settings set forward=yes accept-router-advertisements=yes
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# APPLY — DHCPv6 client (request prefix delegation from TMHI gateway)
|
||||
# -----------------------------------------------------------------------------
|
||||
# request=address,prefix asks for both an IA_NA address on the WAN and a
|
||||
# delegated prefix (IA_PD) for downstream LANs. pool-name stores the PD in
|
||||
# a local pool we then carve /64s out of. add-default-route=yes installs
|
||||
# ::/0 via TMHI's gateway link-local on receipt. Distance 1 matches the v4
|
||||
# default priority for TMHI.
|
||||
# Disabled — TMHI Sagemcom 5688W gateway runs no DHCPv6 server, only
|
||||
# SLAAC. Verified 2026-05-09: client stays in status=searching... forever
|
||||
# even with prefix-hint=::/56. The ROUTER itself gets v6 from RAs (above).
|
||||
# Internal LANs can't get native PD from TMHI. To get v6 on internal LANs
|
||||
# while routing via TMHI: add NAT66 with a ULA prefix (see notes in
|
||||
# CLAUDE.md / memory). Kept here disabled so we remember the attempt.
|
||||
/ipv6 dhcp-client
|
||||
add interface=ether6-tmobile request=address,prefix pool-name=home-pd pool-prefix-length=64 \
|
||||
add-default-route=yes default-route-distance=1 \
|
||||
use-peer-dns=yes disabled=yes \
|
||||
comment="DHCPv6-PD from TMHI (disabled - TMHI runs SLAAC only, no PD server)"
|
||||
|
||||
# Disabled fallback — re-enable manually if TMHI v6 dies and verona is up
|
||||
# AND TWC has accepted our 2606:1c80::/32 (separate ticket).
|
||||
add interface=ether5-vntx-static request=prefix pool-name=home-pd-vntx pool-prefix-length=64 \
|
||||
add-default-route=yes default-route-distance=2 \
|
||||
use-peer-dns=yes disabled=yes \
|
||||
comment="DHCPv6-PD from verona (disabled, fallback)"
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# APPLY — internal LAN addresses
|
||||
# -----------------------------------------------------------------------------
|
||||
# Currently no internal v6 — TMHI doesn't delegate a prefix and the verona
|
||||
# path is held until TWC accepts our /32. When PD is restored, re-enable
|
||||
# whichever dhcp-client is appropriate above and uncomment these:
|
||||
#
|
||||
# /ipv6 address
|
||||
# add address=::1 from-pool=home-pd interface=bridge advertise=yes comment="home main LAN"
|
||||
# add address=::1 from-pool=home-pd interface=ether3-servers advertise=yes comment="home servers LAN"
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# APPLY — neighbour discovery on internal LANs (SLAAC + RDNSS for clients)
|
||||
# -----------------------------------------------------------------------------
|
||||
# Skipped while no internal v6 prefix exists — RAs without a global prefix
|
||||
# would just announce the router as a default gateway with no on-link
|
||||
# prefix, which doesn't help clients. Re-enable along with the address
|
||||
# block above when PD is back.
|
||||
#
|
||||
# /ipv6 nd
|
||||
# add interface=bridge ra-interval=3m20s-10m managed-address-configuration=no other-configuration=yes advertise-dns=yes
|
||||
# add interface=ether3-servers ra-interval=3m20s-10m managed-address-configuration=no other-configuration=yes advertise-dns=yes
|
||||
#
|
||||
# /ipv6 dhcp-server
|
||||
# add interface=bridge name=home-bridge-stateless address-pool=static-only
|
||||
# add interface=ether3-servers name=home-servers-stateless address-pool=static-only
|
||||
#
|
||||
# /ipv6 dhcp-server option
|
||||
# add name=v6-dns code=23 value="'2606:4700:4700::1111''2606:4700:4700::1001'"
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# APPLY — firewall (mirror v4 hygiene; deny by default on input)
|
||||
# -----------------------------------------------------------------------------
|
||||
/ipv6 firewall filter
|
||||
add chain=input action=accept connection-state=established,related comment="established"
|
||||
add chain=input action=drop connection-state=invalid comment="invalid"
|
||||
add chain=input action=accept protocol=icmpv6 comment="icmpv6"
|
||||
add chain=input action=accept src-address=fe80::/10 comment="link-local"
|
||||
add chain=input action=accept src-address=2606:1c80::/32 comment="vntx internal"
|
||||
add chain=input action=drop comment="default deny input"
|
||||
|
||||
# Forward — let internal LANs reach the world; allow inbound established
|
||||
# but don't accept arbitrary inbound to home hosts.
|
||||
add chain=forward action=accept connection-state=established,related comment="established"
|
||||
add chain=forward action=drop connection-state=invalid comment="invalid"
|
||||
add chain=forward action=accept protocol=icmpv6 hop-limit=equal:1 comment="PMTUD"
|
||||
add chain=forward action=accept protocol=icmpv6 comment="icmpv6"
|
||||
add chain=forward action=accept in-interface=bridge comment="home outbound (bridge)"
|
||||
add chain=forward action=accept in-interface=ether3-servers comment="home outbound (servers)"
|
||||
add chain=forward action=drop comment="default deny forward"
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# VERIFY (after apply)
|
||||
# -----------------------------------------------------------------------------
|
||||
# /ipv6 dhcp-client print ;; should show status=bound on ether6-tmobile
|
||||
# /ipv6 pool print ;; should show home-pd populated (whatever TMHI delegated)
|
||||
# /ipv6 address print ;; bridge gets ::1/64; servers gets one too if TMHI gave /60+
|
||||
# /ipv6 route print where active ;; ::/0 via fe80::xxxx of TMHI gateway
|
||||
# /ping 2606:4700:4700::1111 count=3 ;; cloudflare — should work once dhcp-client is bound
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
# 2026-05-09 15:08:05 by RouterOS 7.22.3
|
||||
# 2026-07-17 08:45:20 by RouterOS 7.23.2
|
||||
# software id = ZGNY-ZJW7
|
||||
#
|
||||
# model = RB5009UG+S+
|
||||
|
|
@ -9,7 +9,6 @@ add admin-mac=74:4D:28:1A:67:0A auto-mac=no comment=defconf mtu=1500 name=\
|
|||
add name=containers
|
||||
add mtu=1500 name=docker port-cost-mode=short
|
||||
add mtu=1500 name=dockers port-cost-mode=short
|
||||
add disabled=yes mtu=1500 name=public
|
||||
/interface ethernet
|
||||
set [ find default-name=ether1 ] l2mtu=9578
|
||||
set [ find default-name=ether2 ] l2mtu=9578
|
||||
|
|
@ -20,12 +19,14 @@ set [ find default-name=ether6 ] l2mtu=9578 name=ether6-tmobile
|
|||
set [ find default-name=ether7 ] l2mtu=9578 name=ether7-starlink
|
||||
set [ find default-name=ether8 ] disabled=yes l2mtu=9578
|
||||
set [ find default-name=sfp-sfpplus1 ] l2mtu=9586
|
||||
/interface pppoe-client
|
||||
add interface=ether8 max-mtu=1500 name=pppoe-out1 use-peer-dns=yes user=\
|
||||
grahammcintire
|
||||
/interface veth
|
||||
add address=172.17.0.2/16 container-mac-address=4C:B3:A4:3A:BC:FF dhcp=no \
|
||||
gateway=172.17.0.1 gateway6="" mac-address=4C:B3:A4:3A:BC:FE name=veth1
|
||||
/container
|
||||
add envlists=tailscale interface=veth1 layer-dir="" logging=yes name=\
|
||||
tailscale-mikrotik:latest remote-image=\
|
||||
fluent-networks/tailscale-mikrotik:latest root-dir=\
|
||||
/disk1/containers/tailscale start-on-boot=yes workdir=/
|
||||
/interface list
|
||||
add comment=defconf name=WAN
|
||||
add comment=defconf name=LAN
|
||||
|
|
@ -40,22 +41,23 @@ add add-arp=yes address-pool=home bootp-lease-time=lease-time bootp-support=\
|
|||
add address-pool=dhcp_pool1 interface=ether3-servers name=servers
|
||||
/ipv6 dhcp-server option
|
||||
add code=23 name=v6-dns value="'2606:4700:4700::1111''2606:4700:4700::1001'"
|
||||
/ipv6 pool
|
||||
add name=tunnerbroker prefix=2001:470:ba50::/48 prefix-length=48
|
||||
/port
|
||||
set 0 baud-rate=9600
|
||||
/interface ppp-client
|
||||
add apn=internet name=ppp-out1 port=usb1
|
||||
/ppp profile
|
||||
set *0 dns-server=204.110.191.240,204.110.191.250
|
||||
set *FFFFFFFE dns-server=204.110.191.240,204.110.191.250
|
||||
/queue type
|
||||
set 0 kind=fq-codel
|
||||
/routing table
|
||||
add disabled=no fib name=vntx
|
||||
add disabled=no fib name=tmo
|
||||
add comment="vntx v6 /56 egress via verona" disabled=no fib name=v6-verona
|
||||
/snmp community
|
||||
set [ find default=yes ] addresses=10.0.16.0/22,10.0.0.0/8,204.110.188.0/22 \
|
||||
name=kdyyJrT0Mm
|
||||
add addresses=::/0 authentication-protocol=SHA1 encryption-protocol=AES name=\
|
||||
testtest security=private
|
||||
add addresses=::/0 authentication-password=testtest authentication-protocol=\
|
||||
SHA1 encryption-password=testtest encryption-protocol=AES name=testtest \
|
||||
security=private
|
||||
add addresses=10.0.16.0/22 name=testlocal
|
||||
/system script
|
||||
add dont-require-permissions=no name=api-ssl-certgen owner=admin policy=\
|
||||
|
|
@ -71,17 +73,39 @@ add dont-require-permissions=no name=api-ssl-certgen owner=admin policy=\
|
|||
ys-valid=825; /certificate sign \$certName ca=\$caName; /certificate set \
|
||||
\$certName trusted=yes; /ip/service set api-ssl certificate=\$certName dis\
|
||||
abled=no; /ip/service set api disabled=yes;"
|
||||
/container
|
||||
add envlists=tailscale interface=veth1 layer-dir="" name=\
|
||||
tailscale-mikrotik:latest remote-image=\
|
||||
fluent-networks/tailscale-mikrotik:latest root-dir=\
|
||||
/disk1/containers/tailscale start-on-boot=yes workdir=/
|
||||
/zerotier
|
||||
set zt1 identity="a5f13ab687:0:487e3dd9982223e1aa0b13752a061609180dd3926b46be2\
|
||||
7527a299db6fb1959bc79f486a0e8e70a50a1c0c81c5048ced502f97a4db2cac167551339d\
|
||||
137d3c2:8620c195d88a8ccd49c6264419b9bfcb9af6456cb1c0a35c83f7ed3396375fe446\
|
||||
e18897162866ebcadb374304f379a5ab006e4f0c0e3bb59438fccfcccef0c1"
|
||||
/app
|
||||
set HA-otbr-matter required-hw-devices=ttyACM0:usb1:default:/dev/ttyACM0
|
||||
set cinny firewall-redirects=8094:80:tcp:web
|
||||
set goaway container-command-lines=goaway:none:docker.io/pommee/goaway:latest
|
||||
set home-assistant container-command-lines=\
|
||||
home-assistant:none:lscr.io/linuxserver/homeassistant
|
||||
set lorawan-stack secrets="lorawan-stack__admin_password:="
|
||||
set n8n firewall-redirects=5678:5678:tcp:web
|
||||
set nextcloud container-command-lines="db:none:docker.io/postgres:17,redis:non\
|
||||
e:docker.io/valkey/valkey:/bin/sh -c 'valkey-server --port 6379 --appendon\
|
||||
ly yes --requirepass \$VALKEY_PASSWORD',server:none:docker.io/nextcloud:ap\
|
||||
ache"
|
||||
set otbr required-hw-devices=ttyACM0:usb1:default:/dev/ttyACM0
|
||||
set pihole environment="pihole:FTLCONF_dns_listeningMode=all,pihole:FTLCONF_we\
|
||||
bserver_api_password=password"
|
||||
set redlib firewall-redirects=8087:8080:tcp:web
|
||||
set solr container-command-lines=solr:none:docker.io/solr:latest
|
||||
set uptime-kuma container-command-lines=\
|
||||
uptime-kuma:none:docker.io/louislam/uptime-kuma:1
|
||||
set zulip secrets="zulip__postgres_password:=,zulip__memcached_password:=,zuli\
|
||||
p__rabbitmq_password:=,zulip__redis_password:=,zulip__secret_key:=,zulip__\
|
||||
email_password:="
|
||||
/container config
|
||||
set registry-url=https://ghcr.io tmpdir=/disk1/pull
|
||||
/container envs
|
||||
add key=ADVERTISE_ROUTES list=tailscale value=10.0.8.0/22,10.0.16.0/22
|
||||
add key=AUTH_KEY list=tailscale value=\
|
||||
tskey-auth-k9B9aH7Cyk11CNTRL-yYzpiX8XThCFiVV3pVMthCUKfN8wKTjBD
|
||||
tskey-auth-kBDB2uu3ms11CNTRL-2hFgTg5bzqeASw8XAx1prepozbPFVsYoc
|
||||
add key=CONTAINER_GATEWAY list=tailscale value=172.17.0.1
|
||||
add key=PASSWORD list=tailscale value=h8xd9tkryg
|
||||
add key=RUNNING_SCRIPT list=tailscale value=/var/lib/tailscale/running.sh
|
||||
|
|
@ -100,7 +124,6 @@ add bridge=bridge comment=defconf ingress-filtering=no interface=sfp-sfpplus1 \
|
|||
internal-path-cost=10 path-cost=10
|
||||
add bridge=dockers interface=veth1
|
||||
/ipv6 settings
|
||||
# ipv6 *accept router advertisements* configuration has changed, please restart device to apply settings
|
||||
set accept-router-advertisements=yes
|
||||
/interface detect-internet
|
||||
set detect-interface-list=all internet-interface-list=all lan-interface-list=\
|
||||
|
|
@ -120,21 +143,18 @@ add address=172.17.0.1/16 interface=dockers network=172.17.0.0
|
|||
add address=204.110.191.1/27 interface=ether5-vntx-static network=\
|
||||
204.110.191.0
|
||||
add address=10.0.19.254/22 interface=bridge network=10.0.16.0
|
||||
add address=10.99.1.1/24 interface=*16 network=10.99.1.0
|
||||
add address=10.0.101.253/24 disabled=yes interface=ether4-house-60g network=\
|
||||
10.0.101.0
|
||||
add address=10.0.15.254/21 interface=ether3-servers network=10.0.8.0
|
||||
/ip dhcp-client
|
||||
# Interface not active
|
||||
add add-default-route=no interface=ether6-tmobile name=client1 use-peer-dns=\
|
||||
no use-peer-ntp=no
|
||||
# Interface not active
|
||||
add add-default-route=no interface=ether7-starlink name=client2 use-peer-dns=\
|
||||
no use-peer-ntp=no
|
||||
/ip dhcp-server lease
|
||||
add address=10.0.16.2 client-id=\
|
||||
ff:85:d2:82:8a:0:2:0:0:ab:11:cb:63:d8:6c:a1:65:c1:58 comment=unifi \
|
||||
mac-address=74:83:C2:1D:4C:51 server=server1
|
||||
add address=10.0.16.251 client-id=1:34:98:b5:ae:bc:e3 mac-address=\
|
||||
34:98:B5:AE:BC:E3 server=server1
|
||||
add address=10.0.16.1 client-id=1:c8:7f:54:d0:4:2f mac-address=\
|
||||
C8:7F:54:D0:04:2F server=server1
|
||||
add address=10.0.19.250 client-id=\
|
||||
|
|
@ -170,20 +190,18 @@ add address=10.0.15.253 client-id=1:78:9a:18:3f:cb:fe mac-address=\
|
|||
add address=10.0.19.241 client-id=1:f4:92:bf:91:8a:61 mac-address=\
|
||||
F4:92:BF:91:8A:61 server=server1
|
||||
add address=10.0.15.21 mac-address=BC:24:11:98:1C:19 server=servers
|
||||
add address=10.0.17.185 client-id=1:74:4d:bd:c5:87:cc mac-address=\
|
||||
add address=10.0.16.11 client-id=1:74:4d:bd:c5:87:cc mac-address=\
|
||||
74:4D:BD:C5:87:CC server=server1
|
||||
add address=10.0.17.17 client-id=1:f0:24:f9:55:b8:94 mac-address=\
|
||||
add address=10.0.16.14 client-id=1:f0:24:f9:55:b8:94 mac-address=\
|
||||
F0:24:F9:55:B8:94 server=server1
|
||||
add address=10.0.17.184 mac-address=50:02:91:38:EB:98 server=server1
|
||||
add address=10.0.16.3 client-id=1:52:54:0:5c:f7:36 mac-address=\
|
||||
52:54:00:5C:F7:36 server=server1
|
||||
add address=10.0.17.25 client-id=1:20:f8:3b:9:49:cd mac-address=\
|
||||
add address=10.0.16.15 client-id=1:20:f8:3b:9:49:cd mac-address=\
|
||||
20:F8:3B:09:49:CD server=server1
|
||||
add address=10.0.17.51 mac-address=40:F5:20:C5:9B:EE server=server1
|
||||
add address=10.0.16.5 client-id=\
|
||||
ff:ef:a8:c2:c6:0:1:0:1:31:4c:1f:7:b0:dc:ef:a8:c2:c6 mac-address=\
|
||||
B0:DC:EF:A8:C2:C6 server=server1
|
||||
add address=10.0.17.22 mac-address=EC:94:CB:AA:56:A3 server=server1
|
||||
add address=10.0.16.13 mac-address=EC:94:CB:AA:56:A3 server=server1
|
||||
add address=10.0.15.24 client-id=\
|
||||
ff:23:7c:24:3e:0:2:0:0:ab:11:ad:b5:e:a0:e1:d9:51:1a mac-address=\
|
||||
F6:86:CC:17:A7:F9 server=servers
|
||||
|
|
@ -194,12 +212,21 @@ add address=10.0.15.30 mac-address=94:C6:91:A4:19:80 server=servers
|
|||
add address=10.0.15.31 client-id=\
|
||||
ff:a:12:17:11:0:1:0:1:31:8f:72:ff:7c:d3:a:12:17:11 mac-address=\
|
||||
7C:D3:0A:12:17:11 server=servers
|
||||
add address=10.0.16.10 mac-address=40:F5:20:C5:C7:5E server=server1
|
||||
add address=10.0.16.12 client-id=1:40:4c:ca:69:9:6c mac-address=\
|
||||
40:4C:CA:69:09:6C server=server1
|
||||
add address=10.0.16.6 client-id=1:e8:f6:a:e0:c4:4 mac-address=\
|
||||
E8:F6:0A:E0:C4:04 server=server1
|
||||
add address=10.0.15.25 mac-address=BC:24:11:FB:42:B0 server=servers
|
||||
add address=10.0.19.253 client-id=1:b8:69:f4:9f:b7:12 mac-address=\
|
||||
B8:69:F4:9F:B7:12 server=server1
|
||||
/ip dhcp-server network
|
||||
add address=10.0.8.0/21 domain=mcintire.me gateway=10.0.15.254
|
||||
add address=10.0.16.0/22 dns-server=10.0.19.250,9.9.9.9 domain=w5isp.com \
|
||||
add address=10.0.8.0/21 dns-server=204.110.191.240,204.110.191.250 domain=\
|
||||
mcintire.me gateway=10.0.15.254
|
||||
add address=10.0.16.0/22 dns-server=1.1.1.1,204.110.191.250 domain=w5isp.com \
|
||||
gateway=10.0.19.254
|
||||
/ip dns
|
||||
set servers=9.9.9.9,149.112.112.112
|
||||
set servers=9.9.9.9,204.110.191.250
|
||||
/ip dns static
|
||||
add address=192.168.88.1 comment=defconf name=router.lan type=A
|
||||
add address=10.0.16.31 comment=dhcp-lease-script_server1_lease-hostname name=\
|
||||
|
|
@ -282,7 +309,15 @@ add address=10.0.17.189 list=iot-blocked
|
|||
add address=news.eweka.nl list=eweka-tmo
|
||||
add address=185.90.196.0/22 comment="eweka range" list=eweka-tmo
|
||||
add address=81.171.92.0/23 comment="eweka range" list=eweka-tmo
|
||||
add address=204.110.188.0/22 comment="VNTX public IPs" list=trusted
|
||||
add address=100.64.0.0/10 comment="CGNAT pools" list=trusted
|
||||
add address=10.0.0.0/8 comment="RFC 1918 private" list=trusted
|
||||
add address=192.168.0.0/16 comment="RFC 1918 private" list=trusted
|
||||
/ip firewall filter
|
||||
add action=accept chain=input comment="allow trusted to management services" \
|
||||
dst-port=8728-8729,8291,22 protocol=tcp src-address-list=trusted
|
||||
add action=drop chain=input comment="drop untrusted to management services" \
|
||||
dst-port=8728-8729,8291,22 protocol=tcp
|
||||
add action=accept chain=input dst-address=0.0.0.0 protocol=udp src-address=\
|
||||
104.238.146.79
|
||||
add action=accept chain=forward dst-port=25565 in-interface=\
|
||||
|
|
@ -391,7 +426,7 @@ add action=dst-nat chain=dstnat disabled=yes dst-address=204.110.191.1 \
|
|||
add action=dst-nat chain=dstnat disabled=yes dst-address=204.110.191.1 \
|
||||
dst-port=8920 protocol=tcp to-addresses=10.0.16.1 to-ports=8920
|
||||
add action=masquerade chain=srcnat src-address=172.17.0.0/24
|
||||
add action=masquerade chain=srcnat out-interface=ether7-starlink
|
||||
add action=masquerade chain=srcnat disabled=yes out-interface=ether7-starlink
|
||||
/ip proxy
|
||||
set port=8198 src-address=10.0.19.254
|
||||
/ip route
|
||||
|
|
@ -423,26 +458,26 @@ add disabled=no dst-address=204.110.188.0/22 gateway=204.110.191.30 \
|
|||
routing-table=main
|
||||
add disabled=no dst-address=100.64.0.0/16 gateway=204.110.191.30 \
|
||||
routing-table=main
|
||||
add disabled=no dst-address=10.43.0.0/16 gateway=204.110.191.2 routing-table=\
|
||||
main
|
||||
add comment=wigle.net disabled=no distance=1 dst-address=54.70.85.50/32 \
|
||||
gateway=204.110.191.30 routing-table=main scope=30 target-scope=10
|
||||
add dst-address=100.64.0.0/10 gateway=172.17.0.2
|
||||
add comment="Default via TMO (primary)" disabled=no distance=1 dst-address=\
|
||||
0.0.0.0/0 gateway=192.168.12.1 target-scope=11
|
||||
add comment="Default via VNTX (secondary)" disabled=no distance=2 \
|
||||
dst-address=0.0.0.0/0 gateway=204.110.191.30 target-scope=11
|
||||
add check-gateway=ping comment="Default via TMO (primary)" disabled=no \
|
||||
distance=1 dst-address=0.0.0.0/0 gateway=4.2.2.1 target-scope=11
|
||||
add check-gateway=ping comment="Default via VNTX (secondary)" disabled=no \
|
||||
distance=2 dst-address=0.0.0.0/0 gateway=4.2.2.2 target-scope=11
|
||||
add check-gateway=ping comment="Default via Starlink (last\
|
||||
\n resort)" distance=3 dst-address=0.0.0.0/0 gateway=4.2.2.3 \
|
||||
target-scope=11
|
||||
add comment="tmo table default" disabled=no dst-address=0.0.0.0/0 gateway=\
|
||||
4.2.2.1 routing-table=tmo
|
||||
add check-gateway=ping comment="TMO probe pin" distance=1 dst-address=\
|
||||
\n resort)" disabled=yes distance=3 dst-address=0.0.0.0/0 gateway=\
|
||||
4.2.2.3 target-scope=11
|
||||
add check-gateway=ping comment="tmo table default" disabled=no dst-address=\
|
||||
0.0.0.0/0 gateway=4.2.2.1 routing-table=tmo
|
||||
add check-gateway=none comment="TMO probe pin" distance=1 dst-address=\
|
||||
4.2.2.1/32 gateway=192.168.12.1%ether6-tmobile scope=10
|
||||
add check-gateway=ping comment="VNTX probe pin" distance=1 dst-address=\
|
||||
add check-gateway=none comment="VNTX probe pin" distance=1 dst-address=\
|
||||
4.2.2.2/32 gateway=204.110.191.30%ether5-vntx-static scope=10
|
||||
add check-gateway=ping comment="Starlink probe pin" distance=1 dst-address=\
|
||||
4.2.2.3/32 gateway=192.168.1.1%ether7-starlink scope=10
|
||||
add check-gateway=none comment="Starlink probe pin" disabled=yes distance=1 \
|
||||
dst-address=4.2.2.3/32 gateway=192.168.1.1%ether7-starlink scope=10
|
||||
add disabled=yes distance=1 dst-address=192.168.100.0/24 gateway=\
|
||||
ether7-starlink routing-table=main scope=30 target-scope=10
|
||||
/ip service
|
||||
set ftp disabled=yes
|
||||
set telnet disabled=yes
|
||||
|
|
@ -458,22 +493,18 @@ add disabled=yes interface=ether6-tmobile type=external
|
|||
add interface=*14 type=external
|
||||
add interface=bridge type=internal
|
||||
/ipv6 address
|
||||
add address=::1 comment="home main LAN" disabled=yes from-pool=home-pd \
|
||||
add address=::1 comment="home LAN (verona PD /64)" from-pool=home-pd-vntx \
|
||||
interface=bridge
|
||||
add address=::1 comment="home servers LAN" disabled=yes from-pool=home-pd \
|
||||
interface=ether3-servers
|
||||
add address=::1:0:0:0:1 comment="servers LAN (verona PD /64)" from-pool=\
|
||||
home-pd-vntx interface=ether3-servers
|
||||
/ipv6 dhcp-client
|
||||
add add-default-route=yes comment=\
|
||||
"DHCPv6-PD from TMHI (disabled - TMHI runs SLAAC only, no PD server)" \
|
||||
disabled=yes interface=ether6-tmobile pool-name=home-pd \
|
||||
pool-prefix-length=64 prefix-hint=::/56 request=address,prefix
|
||||
add add-default-route=yes comment=\
|
||||
"DHCPv6-PD from verona (disabled, fallback)" default-route-distance=2 \
|
||||
disabled=yes interface=ether5-vntx-static pool-name=home-pd-vntx \
|
||||
pool-prefix-length=64 request=prefix
|
||||
add add-default-route=yes comment="DHCPv6-PD /56 from verona" \
|
||||
default-route-distance=2 default-route-tables=v6-verona interface=\
|
||||
ether5-vntx-static pool-name=home-pd-vntx pool-prefix-length=64 request=\
|
||||
prefix
|
||||
/ipv6 dhcp-server
|
||||
add interface=bridge name=home-bridge-stateless
|
||||
add interface=ether3-servers name=home-servers-stateless
|
||||
add dhcp-option=v6-dns interface=bridge name=home-bridge-stateless
|
||||
add dhcp-option=v6-dns interface=ether3-servers name=home-servers-stateless
|
||||
/ipv6 firewall filter
|
||||
add action=accept chain=input comment=established connection-state=\
|
||||
established,related
|
||||
|
|
@ -501,6 +532,11 @@ add advertise-dns=yes interface=ether3-servers other-configuration=yes
|
|||
/routing rule
|
||||
add action=lookup-only-in-table disabled=no dst-address=0.0.0.0/0 \
|
||||
src-address=10.0.16.1 table=*400
|
||||
add action=lookup-only-in-table comment=\
|
||||
"vntx-sourced to home /56 stays local" dst-address=\
|
||||
2606:1c80:1001:e00::/56 src-address=2606:1c80::/32 table=main
|
||||
add action=lookup-only-in-table comment="vntx /56 egress via verona only" \
|
||||
src-address=2606:1c80::/32 table=v6-verona
|
||||
/snmp
|
||||
set contact="Graham McIntire" enabled=yes location=Verona
|
||||
/system clock
|
||||
|
|
@ -512,7 +548,6 @@ set enabled=yes
|
|||
/system ntp client servers
|
||||
add address=ntp.vntx.net
|
||||
/system routerboard settings
|
||||
# Firmware upgraded successfully, please reboot for changes to take effect!
|
||||
set auto-upgrade=yes
|
||||
/tool e-mail
|
||||
set certificate-verification=no from=mikrotik@vntx.net port=2525 server=\
|
||||
|
|
@ -527,17 +562,3 @@ add allow-address=10.0.16.0/24
|
|||
set allowed-interface-list=LAN
|
||||
/tool mac-server mac-winbox
|
||||
set allowed-interface-list=LAN
|
||||
/tool netwatch
|
||||
add comment="TMO path" down-script="/ip route disable [find comment=\"Default \
|
||||
via TMO (primary)\"]; /ip route disable [find comment=\"tmo table default\
|
||||
\"]" host=4.2.2.1 interval=2s timeout=1s type=simple up-script="/ip route \
|
||||
enable [find comment=\"Default via TMO (primary)\"]; /ip route enable [fin\
|
||||
d comment=\"tmo table default\"]"
|
||||
add comment="VNTX path" down-script=\
|
||||
"/ip route disable [find comment=\"Default via VNTX (secondary)\"]" host=\
|
||||
4.2.2.2 interval=2s timeout=1s type=simple up-script=\
|
||||
"/ip route enable [find comment=\"Default via VNTX (secondary)\"]"
|
||||
add comment="Starlink path" down-script=\
|
||||
"/ip route disable [find comment=\"Default via Starlink (last resort)\"]" \
|
||||
host=4.2.2.3 interval=2s timeout=1s type=simple up-script=\
|
||||
"/ip route enable [find comment=\"Default via Starlink (last resort)\"]"
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Reference in a new issue