# =============================================================================
# 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
