I wanted to have a system that uses exclusively multicast to obtain on-demand streams.
Endpoints signal their insterest in a stream via MLDv2 or IGMP emmissions, and the source can react to that to start or stop streaming. Interstitial routers aggregate and backfeed this as group membership uses linklocal.
The usual system for transport of content is RTP on UDP, operating on port 5004. It has a media type octet that has common meanings for only a few types and most use "user defined" from 96 onwards that requre the receiver to either see an SDP message to define it, or emit an SDP message via SIP to pre-agree it.
I discovered that SAP on udp 9875 can emit the SDP that this needs, for now choosing to use the media RTP address instead of the common address for SAP FF0X:0:0:0:0:0:2:7FFE
, although with source specific multicast it would be possible to use the common address.
SAP has some dedicated assignments of 224.2.127.254, and FF0X::2:7FFE, though any system design has to consider scale, subscribing to these implies receiving the metadata every live stream in existence at once, although doing still may work with source-specific.
Instead have a block of multicast groups, one for each service, the sender detects when the number of subscribers enters and exits zero.
The sequence of events is like this:
I have used FFmpeg for the headend, FFplay for the client, with experimental alterations as needed: Here are the issues encounted, addressed or not:
HTTP/ICY | RTP/SDP |
---|---|
ICY StreamTitle | i= |
ICY StreamUrl | u= |
HTTP icy-name | s= |
Offset into current song, possibly determned by new ICY metadata | t= |
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:
Interface first is picked for multicast, and might not be wanted. This list is reorganised based on restarts.
For internet streaming, there are not that many directories that I know about that have facilities for programmatic access without registration.
Initially I had used mumudvb and later switched to dvblast as that works faster, especially on this elderly server, and allows switching between services without interrupting the stream. It does not provide SAP(9875) or RTSP(5005) like mumudvb but these could be added back via another program.
DVBlast also supports switching services on a carrier so no more need for tc qdisc tricks to do that.
With DVB, each transport stream has a selection of 16-bit service numbers, leaving the other 15 bits to select dvb and select a carrier. These do rarely change so we can have static mapping in the server.
Therefore, I have a primary file of dvblast commands, to select each transmission from Astra 28.2E
Nearly all the trasmissions are listed in the "NIT" that can be obtained, except 2:
dvblast -f 11426500 -v 18 -5 DVBS -s 27500000 -F 23 has the DAB transmissions
dvblast -f 12441000 -v 13 -5 DVBS2 -s 29500000 -F 89 -m QPSK astra UHD test
I had to recompile dvblast to make it accept "-F 89"
It is very desirable to have a static mapping for the multicast number, I noticed that the carrier frequencies where spaced in 250㎒ units, and is is possible to transmit on the same carrier with both vertical and horizontal polarisation:
There is a 31-bit service number in IPv6 multicast, so map the services to that. The first bit is meant to be set.
To select internet streams, map the low bits to enjoyable music services in the directory, and the high bits to select between directories.
The next problem is that http can return media, redirects, various playlists, and even web pages, no surprise as http is meant for hypertext transport.
Until an URL is invoked it is not known which it will be. It would be likely unfair on FFmpeg to handle every possibility, so it is patched to take a file descriptor handoff from a full featured http response handler, although it is further complicated with TLS.
Playlists are really a kind of multi target redirect.
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 239.255.255.250 produces connect: Network is unreachable
However ping -I int0 -r -b 239.255.255.250 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 255.255.255.255 -p udp -m multiport --dports 1900,6969 -j DNAT --to-destination 239.255.255.250 iptables -t nat -A PREROUTING -i ext0 -d 255.255.255.255 -p udp -m multiport --dports 1900,6969 -j DNAT --to-destination 239.255.255.250
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
We also add some static groups in casts()
#!/bin/sh # 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)) do 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` done } # also if get _MFC 22 errors, reinstall smcroute fixed, or even recompile for D in 239.255.255.250 do # probably not needed unless using multicast aware switch smcroute -j int0 $D for N in `range int0` do smcroute -a int0 $N $D ext0 done for N in `range ext0` do smcroute -a ext0 $N $D int0 done done # 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` do for N in `address int0` do smcroute -a int0 $N $DST ext0 done for N in `address ext0` do smcroute -a ext0 $N $DST int0 done done
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.
Although this is not supposed to be routed between networks, whenever dealing with semi-proprietary hardware or software from tech empires that like to wontfix hackers wishlist items, there are only guidelines, so do what works, where strict standards compliance is like a nice to have.
Instead, avahi can be used on routers to re-advertise mdns .local entries, but one problem is that it does not have built in isolation so that less trusted internet of things items are not allowed to get access to mdns used in higher trust vlans, so the next best thing is to run "untrusted" instances of avahi for that:
Do this manually for a first attempt, it may be best to use the system avahi instance as the "trusted" one for management vlan, and as below, copy the avahi config file for each new "untrusted" instance
The next step may be user avahi instances to explore untrust mdns safely, in proper user territory the network tends to be unshared as well to protect against more "accidents".
I'm using a function to userspace join a vlan, it assumes systemd-networkd is running on the host and attaches the veth to the host bridge. As I also have /etc/resolv.conf to /run/systemd/resolve/resolv.conf hostside, by overmounting that, different nameservers can be used in the container, such as the guest having internet access when the host does not.
Rather than use a random mac address, generate them using network namespace number, this appears to guarantee no clashes on a single host, if there are multiple hosts invoved then it may be needed to change the example such as fe:ff
After setup of the container and dhclient to get ipv4 address:
Multiple avahi instances is an ideal application of systemd.nspawn, so I generated an nspawn unit to run a distruted avahi instance.
In fstab we have / /var/lib/machines/example.avahi none noauto,fail,x-systemd.automount,bind,ro
Inside the nspawn container a second instance of systemd starts, this utilises the host filesystem as a base and overlays parts of it with config from /var/local/lib/machines/example.avahi
I found that the Netgear GS108tv2 can be set to send ntp requests, snmp traps and syslog all to IPv4 multicast addresses.
This is useful as only one ip address can be first and this means that admission to the management vlan allows machines to become the switch's first server without having to either reconfigure the switch, or the host ip address, and reduces any mac re-learning with changeover.
It is also faster, the source generally does not bother with ARP to discover a destination MAC address, and just skips directly to sending to the precomputed address.
NTP has the allocated group of 224.0.1.1:123 so we use that, whereas snmp and syslog have no group so for now use the group implied for 192.168.0.239/24 as 234.192.168.0, this means take the first 3 octets of a private address as the first octet, thus we have 234.192.168 for 192.168
The switch itself kept the factory ipv4 address 192.168.0.239 until we had more than one of them.
The remaining issue is that ntpd, snmptrapd and rsyslog get some help from iptables to consume messages.
This trick also works with gigaset dect equipment.