Demand IP pure multicast headend

We want to allow endpoints in the network to get streams via pure multicast but most sources do not provide this and stream as http. A headend is needed to connect to the services and transcode from HTTP to RTP to let it be multicast in our network. Doing this is expensive on data so only the streams that are being received to shall be transcoded. So how to detect receivers:

When a receiver opens or closes a multicast group that system can send IGMP or MLD. The headend shall detect these and starts or stops a stream depending on when the receiver count on that stream is zero or not.

The reception of a group number can then be mapped to a stream by the headend. As common formats are MP3, AAC and OGG, plus video go outside the codecs specified in RTP, we also send need to SAP packets at the destination enclosing the additional data needed, and this is also where to include the stream metadata from ICY.

It is important to notice that we cannot tell if we will be served a stream or HTML, XML, redirect, m3u playlist or other assorted random things via HTTP until we have parsed the response headers!

This is why we pass the descriptor to the transcoding application after a stream is detected, since the transcoding app mainline might or might not like to implement all of the above.

  1. Client indicates it wants a multicast group such as ff3e:40:2001:db8:d0be:f00d:8000:0000 via MLDv2 (or IGMPv3 for IPv4)
  2. Headend notices the request and looks up the group in the configured stream directories. http connection to the Internet radio station initiated.
  3. Headend eventually gets a stream, decodes what codec it is using and then sends RTP on UDP port 5004 and SAP on UDP port 9875 to that same multicast group.
  4. Other clients join the group and headend will notice that too, if they are on the same IP segment.
  5. Clients reply with RTCP on UDP 5005 to the group, these are seen and aggregated by the headend.
  6. When the last client unsubscribes the group, headend will shutdown the stream.

Why send to the RTP group rather than FF0X:0:0:0:0:0:2:7FFE? We don't know what codec the source will pick before we open the stream, and SAP would scale better doing it one by one.

I have used FFmpeg for the headend, FFplay for the client, with experimental alterations as needed: Here are the issues encounted, addressed or not:

Metadata Mapping
ICY StreamTitlei=
ICY StreamUrlu=
HTTP icy-names=
Offset into current song, possibly determned by new ICY metadatat=

Multicast routing table issues

Whilst the preferred method to issue multicast addresses is to supply the intended ingress/egress interface with % notation, if the app does not support that it may look in the regular routing tables, thus ip -6 route show table local | grep ^ff00 gives:

  1. ff00::/8 dev first table local metric 256 pref medium
  2. ff00::/8 dev second table local metric 256 pref medium
  3. ff00::/8 dev third table local metric 256 pref medium

Interface first is picked for multicast, and might not be wanted. This list is reorganised based on restarts.

UPnP bridge between networks

smcroute is chosen as it does not depend on correct use of IGMP for IPv4 or MLD for IPv6 for multicast forwarding to function. I generally do on-demand multicast.

apt-get install smcroute sipcalc

To assure us that both ethernets get equal multicast preference even in the presence of any kernel faults, multicast addresses originated remotely are carved out of the main routing table, though for now multicast destinations we originate locally has routes installed there until kernel forwarding for that is fixed.

This let the sonos controller on Android mobile phones aand tablets find the zoneplayers

I use smcroute to flood uPNP between IEEE802.3 int0 and IEEE802.11 ext0 networks, these are bridges to allow ebtables to function, they have 1 interface so do not need STP

So ping produces connect: Network is unreachable

However ping -I int0 -r -b produces responses or nothing if no device answers the request.

Some UPNP systems like Sonos also uses local broadcast, so we convert them to multicast using linux DNAT and now they can be forwarded to the other networks on our site.

iptables -t nat -A PREROUTING -i int0 -d -p udp -m multiport --dports 1900,6969 -j DNAT --to-destination
iptables -t nat -A PREROUTING -i ext0 -d -p udp -m multiport --dports 1900,6969 -j DNAT --to-destination

It may also be needed to disable IGMP controlled multicast filtering in the bridges used as the implementation may be broken and prevent multicasting functioning properly.

echo 0 > /sys/devices/virtual/net/int0/bridge/multicast_snooping
echo 0 > /sys/devices/virtual/net/ext0/bridge/multicast_snooping

If system is a border firewall, we also need ip6tables rules to let smcroute pass, here are examples, notice the input and output interface is the same.

ip6tables -A FORWARD -s 2001:db8:1337:2::/64 -d ff00::/8 -i int0 -o int0 -j ACCEPT
ip6tables -A FORWARD -s 2001:db8:1337:1::/64 -d ff00::/8 -i ext0 -o ext0 -j ACCEPT

For /etc/smcroute/

We also add some static groups in casts()

# This script is executed at startup by /etc/init.d/smcroute
# Add your calls to smcroute to setup your multicast routes here

range () {
        RANGE=`ip -4 -o addr show dev $1  primary | tr -s " " | cut -d " " -f4`
        echo `sipcalc -s 32 ${RANGE} | grep ^Network | cut -d"-" -f 2`

address () {
        echo `ip -6 -o addr show dev $1 scope global | tr -s " " | cut -d" " -f4 | cut -d"/" -f1`

casts () {
        for N in `seq $((0x80000000)) $((0x8000000F))` `seq $((0x90000000)) $((0x9000000F))` $((0xd0bef00d))
                N1=`printf %08x $N | cut -c1-4`
                N2=`printf %08x $N | cut -c5-8`
                printf "%s " `ip -6 -o addr show dev $1 scope global | tr -s " " | cut -d" " -f4 | cut -d"/" -f1 | cut -d":" -f1-4 \
                | sed -s s/^/ff3e:40:/g | sed -s s/$/:$N1:$N2/g`

# also if get _MFC 22 errors, reinstall smcroute fixed, or even recompile

for D in
	# probably not needed unless using multicast aware switch smcroute -j int0 $D
	for N in `range int0`
		smcroute -a int0 $N $D ext0
	for N in `range ext0`
		smcroute -a ext0 $N $D int0

# ff0e:0:0:0:0:0:2:7ffe = SAP
# ff02::16 = MLD
for DST in ff0e:0:0:0:0:0:2:7ffe `casts int0`
        for N in `address int0`
                smcroute -a int0 $N $DST ext0
        for N in `address ext0`
                smcroute -a ext0 $N $DST int0

If smcroute does not install routes, or if you get MRT_ADD_MFC errors, reinstall a fixed smcroute, or even recompile it, this is due to changes in one structure mfcctl in the C source.

In normal operation, expect tens of lines in cat /proc/net/ip_mr_cache && ip mroute show these show the actual multicast forwarders.