213 lines
7.4 KiB
Markdown
213 lines
7.4 KiB
Markdown
# MPLS / LDP enablement: verona ↔ climax ↔ core
|
||
|
||
Goal: bring up MPLS forwarding with LDP across the verona–climax–core spine
|
||
on RouterOS 7, leaving the rest of the network untouched. Once these three
|
||
are running labeled forwarding, expanding to other PoPs is incremental
|
||
(add the LDP instance + interface on each new router).
|
||
|
||
## Topology being labeled
|
||
|
||
```
|
||
verona (10.254.254.101)
|
||
└── ether3-climax-11ghz 10.250.1.25/29 ──┐
|
||
│ 10.250.1.24/29 (AF11 backhaul)
|
||
┌── ether6-verona-11ghz 10.250.1.30/29 ──┘
|
||
climax (10.254.254.102)
|
||
└── ether4-380-airfiber24 10.250.1.94/29 ──┐
|
||
│ 10.250.1.88/29 (AF24 backhaul)
|
||
┌── ether5-climax 10.250.1.89/29 ─┘
|
||
core (10.254.254.253)
|
||
```
|
||
|
||
OSPFv2 (`backbone-v2`, area 0) already redistributes connected, so all three
|
||
loopbacks are reachable. That is the IGP we attach LDP to. Nothing else
|
||
changes.
|
||
|
||
RouterOS versions in play: verona 7.20.8, climax 7.22, core 7.20.6 — all
|
||
include MPLS in the system package, no extra `.npk` to install.
|
||
|
||
## Pre-flight (run on each of the three before configuring)
|
||
|
||
```
|
||
# OSPF must be up and loopbacks reachable end-to-end.
|
||
/routing ospf neighbor print
|
||
/ip route print where dst-address~"10.254.254.10[12]/32" or dst-address~"10.254.254.253/32"
|
||
|
||
# Confirm the underlay MTU on the AF11 / AF24 ports — MPLS adds 4 bytes per
|
||
# label, so we want headroom. Check both physical (l2mtu) and IP MTU:
|
||
/interface print detail where name~"ether3-climax-11ghz|ether6-verona-11ghz|ether4-380-airfiber|ether5-climax"
|
||
```
|
||
|
||
Expected: l2mtu ≥ 1600 on the AF radios (default on CCR ports). If
|
||
anything is at 1500 we'll bump `mpls-mtu` to 1500 explicitly so we never
|
||
generate a 1504-byte frame the radio drops.
|
||
|
||
## Step 1 — verona (10.254.254.101)
|
||
|
||
```
|
||
# LDP instance, lsr-id and transport address pinned to the loopback.
|
||
/mpls ldp
|
||
add disabled=no lsr-id=10.254.254.101 transport-addresses=10.254.254.101 vrf=main
|
||
|
||
# Enable LDP discovery on the climax-facing link only.
|
||
/mpls ldp interface
|
||
add interface=ether3-climax-11ghz
|
||
|
||
# Pin the MPLS MTU explicitly. mpls-mtu is the cap on the *labeled* frame,
|
||
# so a 1500-byte IP payload + 4-byte label = 1504 needs at least 1508 to
|
||
# pass; 1508 also leaves room for one extra stacked label (VPN/FRR/etc.).
|
||
/mpls interface
|
||
add interface=ether3-climax-11ghz mpls-mtu=1508
|
||
```
|
||
|
||
## Step 2 — climax (10.254.254.102)
|
||
|
||
```
|
||
/mpls ldp
|
||
add disabled=no lsr-id=10.254.254.102 transport-addresses=10.254.254.102 vrf=main
|
||
|
||
/mpls ldp interface
|
||
add interface=ether6-verona-11ghz
|
||
add interface=ether4-380-airfiber24
|
||
|
||
/mpls interface
|
||
add interface=ether6-verona-11ghz mpls-mtu=1508
|
||
add interface=ether4-380-airfiber24 mpls-mtu=1508
|
||
```
|
||
|
||
## Step 3 — core (10.254.254.253)
|
||
|
||
```
|
||
/mpls ldp
|
||
add disabled=no lsr-id=10.254.254.253 transport-addresses=10.254.254.253 vrf=main
|
||
|
||
/mpls ldp interface
|
||
add interface=ether5-climax
|
||
|
||
/mpls interface
|
||
add interface=ether5-climax mpls-mtu=1508
|
||
```
|
||
|
||
Order doesn't matter — LDP discovery is symmetric. As soon as both ends of
|
||
a link have `/mpls ldp interface` populated, hellos start flowing on
|
||
224.0.0.2:646 and a TCP session forms between the two transport addresses.
|
||
|
||
## Verification (the actually-works part)
|
||
|
||
### A. Control plane: LDP adjacencies
|
||
|
||
Run on **each** router:
|
||
|
||
```
|
||
/mpls ldp neighbor print detail
|
||
```
|
||
|
||
Required state:
|
||
|
||
- `peer=` lists the neighbor's lsr-id (a loopback IP),
|
||
- `transport=` is the neighbor's loopback IP,
|
||
- flag column shows `O` (operational); `D` (dynamic) is fine — `nD` (not
|
||
discovered) or missing entries means hellos aren't reaching the far side
|
||
(firewall, wrong interface, MTU).
|
||
|
||
Expected counts:
|
||
|
||
| Router | LDP neighbors |
|
||
|--------|---------------|
|
||
| verona | 1 (climax) |
|
||
| climax | 2 (verona, core) |
|
||
| core | 1 (climax) |
|
||
|
||
### B. Control plane: label bindings exist for the remote loopbacks
|
||
|
||
```
|
||
/mpls ldp local-binding print where dst-address~"10.254.254.(101|102|253)/32"
|
||
/mpls ldp remote-binding print where dst-address~"10.254.254.(101|102|253)/32"
|
||
```
|
||
|
||
Each router should advertise an `implicit-null` (or label 3) for its own
|
||
loopback in `local-binding`, and learn labels for the two other loopbacks
|
||
in `remote-binding`.
|
||
|
||
### C. Forwarding plane: FIB has the labels
|
||
|
||
```
|
||
/mpls forwarding-table print
|
||
```
|
||
|
||
You should see entries with `out-label`, `out-interface`, and `nexthop`
|
||
matching the OSPF next-hop toward each remote loopback. On climax you'll
|
||
see two entries — one swapping toward verona, one toward core. On verona
|
||
and core you'll see one entry for each remote loopback (the second loopback
|
||
is reached via climax with stacked labels collapsing to a single label
|
||
because we have only three nodes).
|
||
|
||
### D. Data plane: traceroute prints labels
|
||
|
||
From **verona**, source-routed off the loopback:
|
||
|
||
```
|
||
/tool traceroute 10.254.254.253 src-address=10.254.254.101 count=3
|
||
```
|
||
|
||
A working MPLS path prints `MPLS Label=NNNN E=0 ...` on the first hop
|
||
(climax) — that is the smoking gun. Without MPLS the same traceroute
|
||
succeeds but no `MPLS Label=` field appears.
|
||
|
||
Repeat from **core** in the other direction:
|
||
|
||
```
|
||
/tool traceroute 10.254.254.101 src-address=10.254.254.253 count=3
|
||
```
|
||
|
||
### E. Data plane: MTU check
|
||
|
||
We pinned `mpls-mtu=1508` — verify a full-size packet still passes once
|
||
labeled (1500 IP + 4 label = 1504 on the wire, well below both the 1508
|
||
cap and the radio l2mtu of 2024):
|
||
|
||
```
|
||
# from verona, send 1500-byte ICMP DF=yes to core's loopback
|
||
/ping 10.254.254.253 src-address=10.254.254.101 size=1500 do-not-fragment count=5
|
||
```
|
||
|
||
100% return = path is large enough for label + 1500 payload. Any loss
|
||
means the labeled frame (1504 bytes) doesn't fit somewhere — most likely
|
||
an l2mtu issue on a backhaul radio. Either bump l2mtu on the radio side,
|
||
or *raise* `mpls-mtu` if you increased the IP MTU. **Do not lower
|
||
`mpls-mtu` below 1508** — that caps the labeled frame and will cause
|
||
ICMP-frag-needed for ordinary 1500-byte DF customer traffic.
|
||
|
||
## Rollback (per router)
|
||
|
||
If anything misbehaves, MPLS is purely additive — removing the two tables
|
||
returns the box to plain IP forwarding. OSPF is untouched.
|
||
|
||
```
|
||
/mpls ldp interface remove [find]
|
||
/mpls ldp remove [find]
|
||
# leave /mpls interface mpls-mtu settings as-is; harmless without LDP.
|
||
```
|
||
|
||
## Notes & caveats
|
||
|
||
- **Don't enable LDP on customer-facing or CGNAT interfaces.** Only the
|
||
three backbone ports listed above. LDP advertised onto a customer port
|
||
would expose label bindings to anyone who joined the broadcast domain.
|
||
- **No BGP-LU, no L3VPN, no TE yet.** This is plain IPv4 LDP only — the
|
||
point is to get labeled IGP forwarding working between these three
|
||
before layering services on top.
|
||
- **iBGP next-hop-self is not required** for this phase because we have no
|
||
BGP-painted services riding the LSPs yet. When that changes (e.g.,
|
||
bringing public /24s in over iBGP), confirm next-hop-self on the
|
||
ingress LSR so labeled paths terminate at a router with an LSP.
|
||
- **OSPFv3 / IPv6 LDP is intentionally out of scope.** RouterOS 7 LDP is
|
||
IPv4-only; v6 transport would need MPLSv6 / 6PE which we are not
|
||
setting up here.
|
||
- **BFD on the AF radios** (already enabled on the verona–climax and
|
||
climax–core OSPF templates with `use-bfd=yes`) is what fast-fails the
|
||
underlay; LDP itself follows OSPF and tears down accordingly.
|
||
- **Expanding to other PoPs:** repeat steps 1–3 on the new router with
|
||
its loopback as `lsr-id`/`transport-addresses`, then add `/mpls ldp
|
||
interface` on the link to whichever existing MPLS router it peers with.
|
||
No flag day.
|