IGMP Proxy on Linux with igmpproxy: Route IPTV Multicast Streams Through Your Router and Firewall

Networking tutorial - IT technology blog
Networking tutorial - IT technology blog

Six months ago my ISP’s IPTV service refused to work anywhere except the router’s direct LAN port — no Wi-Fi, no switch ports, nothing. The root cause was multicast. IGMP membership reports weren’t crossing the boundary between the ISP’s upstream interface and my internal LAN. I tried pimd first, then smcroute, but igmpproxy turned out to be the cleanest fix. It’s been running without a single restart since.

This guide walks through the full setup — from a minimal working config to firewall rules and multi-VLAN routing — following the same path I took to get there.

Quick Start: igmpproxy Running in 5 Minutes

Install igmpproxy

On Debian/Ubuntu:

sudo apt update && sudo apt install igmpproxy -y

On RHEL/Rocky/AlmaLinux:

sudo dnf install igmpproxy -y

Enable IP multicast routing in the kernel

igmpproxy won’t do anything useful until the kernel has multicast routing support loaded:

sudo modprobe ip_mr_route
echo "net.ipv4.conf.all.mc_forwarding = 1" | sudo tee -a /etc/sysctl.conf
sudo sysctl -p

Minimal /etc/igmpproxy.conf

Assume eth0 is your upstream (WAN/ISP) interface and eth1 is your internal LAN:

##
## igmpproxy.conf — minimal working config
##

phyint eth0 upstream  ratelimit 0  threshold 1
    altnet 0.0.0.0/0

phyint eth1 downstream  ratelimit 0  threshold 1

Start and enable the service:

sudo systemctl enable --now igmpproxy
sudo systemctl status igmpproxy

Multicast streams from the upstream should now flow to any device on eth1 that sends IGMP join requests. Verify with:

sudo tcpdump -i eth1 'udp and dst net 224.0.0.0/4' -n -c 20

UDP packets hitting the LAN interface means the proxy is working. That’s the five-minute path. Everything below turns a working prototype into a solid production setup.

Deep Dive: How igmpproxy Actually Works

IGMP and multicast routing — the basics

Multicast works differently from unicast. A single sender transmits one stream. The network delivers copies only to routers and hosts that have explicitly joined the multicast group via IGMP (Internet Group Management Protocol). Your IPTV decoder sends an IGMP Join for a group address like 239.1.1.1, and upstream routers need to receive that join and route traffic down to the requesting segment.

Here’s where Linux routing gets in the way: a Linux router doesn’t forward IGMP membership reports between interfaces by default. igmpproxy plugs that gap. It listens for IGMP reports on downstream interfaces, generates corresponding IGMP joins toward the upstream, and programs kernel multicast routes accordingly.

Network topology in practice

ISP Network (multicast source)
        │
       eth0  ← upstream (WAN) — receives multicast from ISP
  [Linux Router running igmpproxy]
       eth1  ← downstream (LAN) — delivers multicast to IPTV decoders
        │
   Home LAN (IPTV STBs, smart TVs)

igmpproxy maintains a forwarding table in the kernel’s multicast routing socket (MRT_ADD_MFC). When a client on eth1 sends an IGMP Membership Report, igmpproxy receives it, sends an IGMP Join upstream on eth0, then programs the kernel to forward matching multicast packets from eth0 to eth1.

The altnet directive — why it matters

The altnet directive under the upstream interface tells igmpproxy which source addresses to accept multicast from. Many ISPs send IPTV streams from private ranges — 10.x.x.x or 172.x.x.x are common. Using 0.0.0.0/0 accepts everything, which is fine during initial testing. For production, narrowing it down prevents igmpproxy from forwarding unexpected multicast that arrives from the ISP side for unrelated traffic:

phyint eth0 upstream  ratelimit 0  threshold 1
    altnet 10.0.0.0/8
    altnet 172.16.0.0/12

Advanced Usage

Firewall rules for multicast (iptables / nftables)

Linux firewalls block multicast traffic by default. These rules cover the most common gaps:

iptables:

# Allow IGMP (protocol 2) on all relevant interfaces
sudo iptables -A INPUT -p igmp -j ACCEPT
sudo iptables -A OUTPUT -p igmp -j ACCEPT
sudo iptables -A FORWARD -p igmp -j ACCEPT

# Allow multicast UDP forwarding (224.0.0.0/4 covers all multicast groups)
sudo iptables -A FORWARD -d 224.0.0.0/4 -j ACCEPT
sudo iptables -A FORWARD -s 224.0.0.0/4 -j ACCEPT

nftables (modern systems):

sudo nft add rule inet filter forward ip daddr 224.0.0.0/4 accept
sudo nft add rule inet filter forward ip protocol igmp accept

One thing that burned me early on: nf_conntrack doesn’t handle multicast well. Got an INVALID state drop rule in your FORWARD chain? Multicast UDP packets will silently disappear — no error, no log entry. Either exempt multicast from conntrack entirely, or insert an explicit ACCEPT before the state check:

# Insert at position 1 to run before conntrack state rules
sudo iptables -I FORWARD 1 -d 224.0.0.0/4 -j ACCEPT

Multiple downstream interfaces (VLANs)

Using VLANs — say eth1.10 for a media VLAN and eth1.20 for general devices — just add each as a downstream entry:

phyint eth0 upstream  ratelimit 0  threshold 1
    altnet 0.0.0.0/0

phyint eth1.10 downstream  ratelimit 0  threshold 1
phyint eth1.20 downstream  ratelimit 0  threshold 1

igmpproxy replicates streams to whichever downstream interface has active IGMP members. No extra config needed — it works automatically once the VLAN interfaces exist at the kernel level:

# Create VLAN interfaces if not already present
sudo ip link add link eth1 name eth1.10 type vlan id 10
sudo ip link add link eth1 name eth1.20 type vlan id 20
sudo ip link set eth1.10 up
sudo ip link set eth1.20 up

quickleave — reduce channel switching latency

Drop quickleave at the top of /etc/igmpproxy.conf and igmpproxy will stop forwarding the moment it receives the first IGMP Leave, instead of waiting for the full query/response cycle:

quickleave

phyint eth0 upstream  ratelimit 0  threshold 1
    altnet 0.0.0.0/0

phyint eth1 downstream  ratelimit 0  threshold 1

For a single IPTV client, this brings channel switching latency from 3–5 seconds down to under 1 second. With multiple clients watching different channels at the same time, skip it — one client leaving will kill the stream for everyone else.

Practical Tips from 6 Months of Production Use

igmpproxy won’t start? Check this first

The most common silent failure on first run: the multicast routing module isn’t loaded.

# Verify the module is loaded
lsmod | grep mroute

# If missing:
sudo modprobe ip_mr_route

# Confirm multicast forwarding is active
cat /proc/sys/net/ipv4/conf/all/mc_forwarding

igmpproxy exits immediately if the multicast routing socket can’t be opened, and systemctl status igmpproxy won’t always show a clear error. Check the full journal output:

journalctl -u igmpproxy -n 50

Persist the module across reboots:

echo "ip_mr_route" | sudo tee /etc/modules-load.d/igmpproxy.conf

Verify the full forwarding path with tcpdump

When something isn’t working, check each hop separately:

# Step 1: Is multicast arriving on the upstream interface from the ISP?
sudo tcpdump -i eth0 'udp and dst net 224.0.0.0/4' -n -c 20

# Step 2: Are IGMP joins leaving on eth0 toward the ISP?
sudo tcpdump -i eth0 igmp -n

# Step 3: Is the stream reaching the LAN?
sudo tcpdump -i eth1 'udp and dst net 224.0.0.0/4' -n -c 20

Three steps, three possible failure points: upstream delivery, IGMP signaling, or downstream forwarding. Where the chain breaks tells you exactly what to fix.

Inspect the kernel multicast routing table directly

# Active multicast routes
cat /proc/net/ip_mr_cache

# Virtual interfaces registered with the kernel
cat /proc/net/ip_mr_vif

Active entries in ip_mr_cache confirm the kernel is forwarding traffic. An empty table means igmpproxy isn’t receiving IGMP joins from downstream clients — most likely a firewall blocking IGMP, or the client not sending joins at all.

igmpproxy vs smcroute

smcroute takes a different approach: static multicast routes instead of dynamic IGMP proxying. That’s simpler when you have a fixed, well-known list of multicast groups — but it means a manual route entry for every channel. igmpproxy handles all IGMP negotiation automatically. Add a new IPTV device or channel package and nothing in the config needs to change.

Running a Linux router long enough means eventually dealing with multicast. IPTV is usually what forces the issue. Getting it right the first time is genuinely tricky — but once igmpproxy is configured and the firewall rules are locked in, it stays out of your way. Same config file for six months. The only change I made was adding a second VLAN interface for a new media player in the bedroom.

Share: