# 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-` 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.