Hi,
I am using igmpproxy 0.4 on OpenBSD 7.8 for IPTV (with an Amino STB). I noticed a consistent 10-second delay when switching channels.
After analyzing the igmp -d -vv output, the source and the OpenBSD-specific patches, with some AI help, I found a deadlock in the routing logic.
The simple verbose logs (-d -v) during a channel change:
# Here we see the Amino STB to change channels:
RECV Leave message from 192.168.100.48 to 224.0.0.2 (ip_hl 24, data 8)
Leaving group 224.0.251.125 on interface vlan4
RECV V2 member report from 192.168.100.48 to 224.0.251.124 (ip_hl 24, data 8)
Inserted route table entry for 224.0.251.124 on VIF #0
Joining group 224.0.251.124 on interface vlan4
# And then very quickly after we start to receive the actual upstream data from the provider, but the MFC is not updated as igmpproxy has the route still as inactive:
New origin for route 224.0.251.124 is 217.166.225.124, flood -1
# Why not update the route entry already here?!
# Now it takes a 10 seconds cycle for the Amino STB to again explain it wants the channel data.
# This looks like an unnecessary delay.
RECV V2 member report from 192.168.100.48 to 224.0.251.124 (ip_hl 24, data 8)
# And then finally the igmpproxy decides to update the route entry:
Updated route entry for 224.0.251.124 on VIF #0
Adding MFC: 217.166.225.124 -> 224.0.251.124, InpVIf: 1
Wouldn't it be better to just update the MFC at the moment we receive the upstream data?
As we already do know the Amino STB is interested in the stream?!
Detailed technical description:
Technical Description of the Amino IPTV Patch for igmpproxy 0.4
===============================================================
Problem Scenario:
-----------------
When an Amino Set-Top Box (STB) switches channels, it immediately sends an
IGMPv2 Membership Report (Join). The OpenBSD kernel, upon receiving the first
multicast packets from the upstream provider on the WAN interface, generates
an IGMPMSG_NOCACHE upcall to igmpproxy.
In the current OpenBSD port of igmpproxy 0.4, the following sequence causes a
significant delay (approx. 10 seconds):
igmpproxy receives the Join and creates a route entry in rtable.c.
However, the route is initialized with the state ROUTE_ST_INACTIVE.
Because the state is INACTIVE, the function updateKernelRoute is either not
called or blocked by a state-check.
The kernel's Multicast Forwarding Cache (MFC) remains empty for that specific
group.
Multicast traffic is dropped by the kernel until the next IGMP query cycle or
until the internal state-machine finally transitions the route to ACTIVE.
The Solution:
-------------
This patch modifies rtable.c (specifically within the OpenBSD Red-Black tree
implementation) to force a kernel MFC update the moment a route is inserted or
updated, regardless of the INACTIVE state.
Changes:
--------
In rtable.c, inside the function responsible for adding or activating routes
(typically insertRoute or activateRoute in the patched source), we added an
explicit call to:
internUpdateKernelRoute(croute, 1, o);
Why this works:
---------------
By calling internUpdateKernelRoute immediately when the first 'Origin' (source)
is identified for a group, we bypass the elective state-check. We instruct the
OpenBSD kernel to start forwarding the multicast stream from the upstream
interface to the downstream interface immediately. This reduces the
"black screen" time on Amino STBs from 10 seconds to under 3 seconds, as the
stream is available as soon as the hardware is ready to decode it.
Patch file to be applied on after of the already existing patches for this file:
openbsd# cat patch-src_rttable_c_zzz
--- src/rttable.c.orig Mon Feb 23 07:16:22 2026
+++ src/rttable.c Sun Feb 22 21:51:21 2026
@@ -445,6 +445,12 @@
o->flood = downIf;
o->pktcnt = 0;
TAILQ_INSERT_TAIL(&croute->originList, o, next);
+
+ // Fix to activate route pro-actively in early phase not waiting for second join:
+ my_log(LOG_INFO, 0, "Amino-fix: Forcing kernel update for group %s from origin %s",
+ inetFmt(croute->group, s1), inetFmt(originAddr, s2));
+ internUpdateKernelRoute(croute, 1, o);
+
} else {
my_log(LOG_INFO, 0, "Have origin for route %s at %s, pktcnt %d",
inetFmt(croute->group, s1),
I have tested this, and it reduces zap time from 10s to <3s without any side effects on system stability.
It works regardless of the Quickleave option.
I would highly appreciate your feedback on this findings. Specifically, I'm curious if you see any architectural risks in forcing the MFC update at this stage, or if there is a more 'OpenBSD-native' way to bridge this gap between the NOCACHE upcall and the route activation.
Looking forward to your thoughts.
Best regards,
Remco van den Berg
No comments:
Post a Comment