Skip to content

11.2 MRP Roles and Frames

The previous chapter explained how MRP operates: the MRM blocks a port, sends test frames, and unblocks on failure. Understanding MRP at the frame level enables you to diagnose ring problems that HiOS diagnostics alone cannot explain.

HiOS reports “Ring Open” but all cables are connected. The event log shows topology changes every few seconds. The ring recovers but some devices are unreachable. These problems require looking at the actual MRP frames on the wire to determine what is happening and why.

All 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 TypeValueSent byPurpose
MRP_Test0x0001MRMVerify ring integrity every 10 to 20 ms
MRP_TopologyChange0x0002MRMTrigger MAC table flush on all MRCs
MRP_LinkDown0x0003MRCNotify MRM of link failure immediately
MRP_LinkUp0x0004MRCNotify MRM of link recovery
BytesField
0 to 1Frame Type (0x0001)
2 to 3Version (0x0001)
4 to 5Sequence Number
6 to 21Domain UUID (16 bytes)
22 to 23Interval (test interval in ms x 10)
24 to 25Transition 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, Ether
import 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 during a ring failover shows the exact sequence: MRP_LinkDown from the MRC adjacent to the failure, followed by MRP_TopologyChange from the MRM. If MRP_TopologyChange does not appear, the MRM is not reacting, which points to a VLAN or UUID mismatch.

The following script tracks MRP_Test frame timing to detect ring problems before they cause outages. It alerts when test frames stop arriving or when topology changes occur too frequently.

from scapy.all import sniff, Ether
import struct, time, threading
from 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 visible immediately.

Two Ring Managers cause continuous ring oscillation. Only the MRM sends MRP_Test frames. If test frames arrive from two different source MACs, the ring has two MRMs.

from scapy.all import sniff, Ether
import struct
from 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 two MAC addresses, reconfigure one 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 problems before they 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. Two source MACs means two MRMs.

Understanding MRP frames enables diagnosis. Understanding MRP timing enables optimization. The next chapter covers MRP timing parameters, convergence time calculation, and how to tune MRP for faster recovery.

  • IEC 62439-2:2016 — Media Redundancy Protocol (MRP)
  • Hirschmann. (2023). User Manual: HiOS MRP Configuration. Belden/Hirschmann.