MRP_Test frames arrive every 10-20 ms
Monitor the gap between test frames to detect timing issues before the issues cause outages.
The previous chapter explained how MRP operates: the MRM blocks a port, sends test frames, and unblocks on detected inoperability. Understanding MRP at the frame level enables diagnosis of ring issues that HiOS diagnostics alone do not explain.
HiOS reports “Ring Open” but cables are connected. The event log shows topology changes every few seconds. The ring recovers but some devices are unreachable. These situations require looking at the actual MRP frames on the wire to determine the cause.
MRP frames use EtherType 0x88E3 and destination MAC 01:15:4E:00:00:01 (MRP multicast). The payload starts with a 2-byte Frame Type field:
| Frame Type | Value | Sent by | Purpose |
|---|---|---|---|
| MRP_Test | 0x0001 | MRM | Verify ring integrity every 10 to 20 ms |
| MRP_TopologyChange | 0x0002 | MRM | Trigger MAC table flush on MRCs |
| MRP_LinkDown | 0x0003 | MRC | Notify MRM of link inoperability immediately |
| MRP_LinkUp | 0x0004 | MRC | Notify MRM of link recovery |
| Bytes | Field |
|---|---|
| 0 to 1 | Frame Type (0x0001) |
| 2 to 3 | Version (0x0001) |
| 4 to 5 | Sequence Number |
| 6 to 21 | Domain UUID (16 bytes) |
| 22 to 23 | Interval (test interval in ms x 10) |
| 24 to 25 | Transition count |
The sequence number increments with each test frame. A gap in the sequence indicates a lost frame. The interval field confirms the configured test timing.
The following script captures MRP frames on a mirror port and decodes each frame type, sequence number, and domain UUID.
from scapy.all import sniff, Etherimport struct, time
MRP_FRAME_TYPES = { 0x0001: "MRP_Test", 0x0002: "MRP_TopologyChange", 0x0003: "MRP_LinkDown", 0x0004: "MRP_LinkUp",}
def parse_mrp_frame(pkt): if not pkt.haslayer(Ether) or pkt[Ether].type != 0x88E3: return payload = bytes(pkt[Ether].payload) if len(payload) < 4: return frame_type = struct.unpack_from(">H", payload, 0)[0] name = MRP_FRAME_TYPES.get(frame_type, f"UNKNOWN(0x{frame_type:04x})") src_mac = pkt[Ether].src ts = time.strftime("%H:%M:%S") print(f"[{ts}] {name:25s} from {src_mac}", end="") if frame_type == 0x0001 and len(payload) >= 24: seq = struct.unpack_from(">H", payload, 4)[0] uuid = payload[6:22].hex("-") print(f" seq={seq} uuid={uuid[:23]}") elif frame_type == 0x0002: print(f" MAC TABLE FLUSH TRIGGERED") elif frame_type in (0x0003, 0x0004): print(f" {'LINK DOWN' if frame_type == 0x0003 else 'LINK UP'}") else: print()
sniff(iface="eth0", filter="ether proto 0x88e3", prn=parse_mrp_frame, store=False)Running this script during a ring failover shows the exact sequence: MRP_LinkDown from the MRC adjacent to the inoperability, followed by MRP_TopologyChange from the MRM. If MRP_TopologyChange does not appear, then the MRM is not reacting. This behavior points to a VLAN or UUID mismatch.
The following script tracks MRP_Test frame timing to detect ring issues before the issues cause outages. The script alerts when test frames stop arriving or when topology changes occur too frequently.
from scapy.all import sniff, Etherimport struct, time, threadingfrom collections import deque
class MRPRingMonitor: def __init__(self, iface: str, expected_interval_ms: float = 20.0, alert_threshold_ms: float = 60.0): self.iface = iface self.expected_ms = expected_interval_ms self.alert_ms = alert_threshold_ms self.last_test_time: float | None = None self.test_intervals: deque = deque(maxlen=100) self.topology_changes = 0
def _handle_frame(self, pkt): if not pkt.haslayer(Ether) or pkt[Ether].type != 0x88E3: return payload = bytes(pkt[Ether].payload) if len(payload) < 2: return frame_type = struct.unpack_from(">H", payload, 0)[0] now = time.time() if frame_type == 0x0001: if self.last_test_time is not None: interval_ms = (now - self.last_test_time) * 1000 self.test_intervals.append(interval_ms) if interval_ms > self.alert_ms: print(f"Test frame gap: {interval_ms:.0f} ms (threshold: {self.alert_ms} ms)") self.last_test_time = now elif frame_type == 0x0002: self.topology_changes += 1 print(f"Topology change #{self.topology_changes} at {time.strftime('%H:%M:%S')}") elif frame_type == 0x0003: print(f"Ring OPEN at {time.strftime('%H:%M:%S')}")
def start(self): print(f"MRP Ring Monitor on {self.iface}") sniff(iface=self.iface, filter="ether proto 0x88e3", prn=self._handle_frame, store=False)
MRPRingMonitor(iface="eth0").start()More than 5 topology changes per minute indicates a flapping link or dual MRM. The monitor makes this condition visible immediately.
2 Ring Managers cause continuous ring oscillation. Only the MRM sends MRP_Test frames. If test frames arrive from 2 different source MACs, then the ring has 2 MRMs.
from scapy.all import sniff, Etherimport structfrom collections import defaultdict
mrm_candidates: dict[str, int] = defaultdict(int)
def detect_dual_mrm(pkt): if not pkt.haslayer(Ether) or pkt[Ether].type != 0x88E3: return payload = bytes(pkt[Ether].payload) if len(payload) < 2: return if struct.unpack_from(">H", payload, 0)[0] == 0x0001: src = pkt[Ether].src mrm_candidates[src] += 1 if len(mrm_candidates) > 1: print(f"DUAL MRM DETECTED: {list(mrm_candidates.keys())}")
sniff(iface="eth0", filter="ether proto 0x88e3", prn=detect_dual_mrm, count=100, store=False)If this script prints 2 MAC addresses, then reconfigure 1 of the switches from Manager to Client immediately.
MRP_Test frames arrive every 10-20 ms
Monitor the gap between test frames to detect timing issues before the issues cause outages.
MRP_TopologyChange triggers MAC flush
More than 5 topology changes per minute indicates a flapping link or dual MRM.
Dual MRM is detectable from the wire
Only the MRM sends MRP_Test frames. 2 source MACs means 2 MRMs.
Understanding MRP frames enables diagnosis. Understanding MRP timing enables optimization. The next chapter covers MRP timing parameters, convergence time calculation, and tuning MRP for faster recovery.