DLNA und andere SSDP Dienste über lokale Subnetze hinweg nutzen (OpenWRT)

Zur besseren Trennung zwischen persönlichen Geräten (Laptop, PC), IoT Geräten und Servern setze ich Zuhause einen OpenWRT Router ein, der für die Gerätegruppen einzelne Subnetze aufspannt. So können Laptops beispielsweise auf alle anderen Dienste und Subnetze zugreifen, während Server sich nicht zum Laptop verbinden können uns in ihrem Netzwerk eingesperrt bleiben. Einzig der Internetzugang ist für sie freigegeben. Das Setup lässt mich Nachts ruhiger schlafen, bringt aber auch Nachteile mit sich: Einige der Dienste, die man in seinem Heimnetzwerk betreibt, sind für diese Auftrennung nicht konzipiert und funktionieren nicht auf Anhieb. So auch DLNA, über welches Medienserver ihre Inhalte beispielsweise an ein TV-Gerät freigeben können. Hinter DLNA verbirgt sich eigentlich eine ganze Gruppe von Protokollen und Standards. Ein wichtiges Protokoll im DLNA-Standard ist SSDP (Simple Service Discovery Protocol).

Das Problem: SSDP - Simple Service Discovery Protocol

SSDP ist ein Broadcast-basiertes Protokoll und wird im DLNA-Kontext dazu genutzt, einen DLNA-Client oder DLNA-Server im Netzwerk bekannt zu machen. Schließlich soll der Fernseher ja Bescheid wissen, a) dass ein DLNA-Server existriert und b) wie er ihn erreichen / ansprechen kann. Beide Informationen werden in Form von Broadcast-Paketen im Broadcast-IP-Bereich 239.255.255.250 (IPv4) bzw. ff02::c (IPv6) verkündet, sodass jedes Gerät im selben Netzsegment diese Informationen mitlesen und ggf. nutzen kann. Diese Broadcast-Nachrichten sind idR. auf das aktuelle Subnetz limitiert. Sie werden also nicht geroutet und somit nicht an andere Subnetze weitergegeben.

Hier liegt das Problem: Befindet sich der Fernseher also im IoT-Netz 192.168.4.0/24 und der DLNA-Server im Server-Subnetz 192.168.3.0/24, werden diese normalerweise die gegenseitigen SSDP-Broadcastpakete nicht empfangen und sich somit nicht gegenseitig erkennen können.

Die Lösung: Broadcast-Routing mit smcroute

Zum Glück gibt es eine Lösung für das Problem: Den “Static Multicast Routing Daemonsmcroute. Der Daemon routet dabei nicht selbst Pakete, sondern sorgt durch eine korrekte Konfiguration des Linux-Kernels dafür, dass zwischen definierten Subnetzen und Geräten Broadcastpakete ausgetauscht werden.

Broadcast vs. Multicast: Von Broadcast spricht man in einem Netzwerk, wenn alle Mitglieder dieses Netzwerks die entsprechenden Pakete mitlesen können. Macht man diese Pakete nur für bestimmte Teilnehmer oder für weitere Subnetze verfügbar, spricht man von Multicast. Da in smcroute die Quellen und Ziele konkret definiert werden (müssen) und Pakete über Netzgrenzen hinweg transportiert werden, wird der Begriff “Multicast Routing Daemon” verwendet.

Nach der Installation des smcroute Pakets kann der Daemon in der Konfigurationsdatei unter /etc/smcroute.conf konfiguriert werden. Hier verbirgt sich u.U. schon eine vorausgefüllte Beispieldatei, welche ich durch folgende ersetzt habe:

# Enable bridge interfaces for smcroute use 
phyint br-SRV enable
phyint br-lan enable
phyint br-IOT enable

# Allowed routing for IPv4
mgroup from br-SRV source 192.168.3.2 group 239.255.255.250
mroute from br-SRV source 192.168.3.2 group 239.255.255.250 to br-IOT br-lan
mgroup from br-IOT group 239.255.255.250
mroute from br-IOT group 239.255.255.250 to br-lan br-SRV
mgroup from br-lan group 239.255.255.250
mroute from br-lan group 239.255.255.250 to br-SRV br-IOT

# Same for IPv6
mgroup from br-SRV source fd00:0:0:3::2 group ff02::c
mroute from br-SRV source fd00:0:0:3::2 group ff02::c to br-IOT br-lan
mgroup from br-IOT group ff02::c
mroute from br-IOT group ff02::c to br-lan br-SRV
mgroup from br-lan group ff02::c
mroute from br-lan group ff02::c to br-SRV br-IOT

Die Konfiguration bewirkt, dass Multicast-Pakete zwischen den Netzwerkbrücken (und Interfaces) br-lan, br-IOT und br-SRV ausgetauscht werden, wenn sie in den von SSDP genutzen Broadcast-Adressbereich fallen.

(durch source 192.168.3.2 bzw. source fd00:0:0:3::2 gilt für Pakete aus dem SRV-Netz die zusätzliche Einschränkung, dass nur Pakete von meinem Jellyfin-Server aus diesem Netz geroutet werden dürfen)

Der smcroute Daemon muss danach neu gestartet werden:

service smcroute restart

Dabei kann es hilfreich sein, nach dem Start auch das Systemlog /var/log/messages im Blick zu behalten, um Fehler bei der Konfiguration zu erkennen.

Man könnte meinen, jetzt sollten sich DLNA-Server und -Clients bereits finden können. Wäre da nur nicht …

Der Showstopper: Die TTL (Time-to-Live)

Da Broadcast-Pakete (eigentlich!) das eigene Subnetz nicht verlassen sollen, haben sie eine Time-To-Live von 1. Das bewirkt, dass solche Pakete vom Router nicht weiter für das Routing in andere Netze in Betracht gezogen werden. Eigentlich eine vernünftige Sache - doch in unserem Fall soll ja gerade dies erreicht werden. Die TTL muss also angehoben werden, damit SSDP Broadcast-Pakete “es über den Router hinaus schaffen” können. Die Anpassung kann an verschiedenen Orten erfolgen

  1. Zum einen kann das Paket an der Quelle, z.B. DLNA-Server, eine höhere TTL verpasst bekommen ..
  2. … oder in der Prerouting-Queue des OpenWRT-Routers, bevor es aufgrund seiner TTL von 1 verworfen wird.

Weg 2) kann zentral verwaltet werden und an den Clients sind keine Änderungen nötig. Das ist Vorteilhaft, weil ich die TTL der DLNA-Pakete, die mein Fernseher schickt, ohnehin nicht erhöhen kann. Dazu müsste ich mir schon Zugriff auf sein Linux-System verschaffen.

An meinem OpenWRT Router habe ich also folgendes in die zusätzlichen Firewallregeln (/etc/firewall.user) eingefügt:

# SSDP: Increase DLNA Broadcast TTL
iptables -t mangle -A PREROUTING -i br-lan -d 239.255.255.250  -j TTL --ttl-inc 1
iptables -t mangle -A PREROUTING -i br-IOT -d 239.255.255.250 -j TTL --ttl-inc 1
iptables -t mangle -A PREROUTING -i br-SRV -d 239.255.255.250 -j TTL --ttl-inc 1
ip6tables -t mangle -A PREROUTING -i br-lan -d ff02::c -j TTL --ttl-inc 1
ip6tables -t mangle -A PREROUTING -i br-IOT -d ff02::c -j TTL --ttl-inc 1
ip6tables -t mangle -A PREROUTING -i br-SRV -d ff02::c -j TTL --ttl-inc 1

Mittels --ttl-inc wird die TTL um eins erhöht, sodass die betreffenden Pakete 1x (von einem Subnetz ins nächste) geroutet werden können.

Nicht zu vergessen: Firewallfreigaben für DLNA

Eine Sache fehlt noch: Sollte sich eine restriktive Firewall zwischen den Subnetzen befinden, müssen für SSDP und alle anderen DLNA-Übertragungen noch Firewallfreigaben erteilt werden. In meinem Netzwerk muss also konkret eine eingeschränkte Kommunikation zwischen den Netzen lan, iot und srv stattfinden können.

Für SSDP soll die Firewall für Zieladresse 239.255.255.250 bzw. ff02::c und Port 1900 (UDP) durchlässig werden:

config rule
    option name 'allow_iot_srv_ssdp'
    option dest_port '1900'
    option src 'iot'
    option dest 'srv'
    option target 'ACCEPT'
    list proto 'udp'
    list dest_ip '239.255.255.250'
    list dest_ip 'FF02::C'

config rule
    option name 'allow_iot_lan_ssdp'
    option dest_port '1900'
    option dest 'lan'
    option target 'ACCEPT'
    list proto 'udp'
    option src 'iot'
    list dest_ip '239.255.255.250'
    list dest_ip 'FF02::C'

config rule
    option name 'allow_srv_iot_ssdp'
    option dest_port '1900'
    option src 'srv'
    option dest 'iot'
    option target 'ACCEPT'
    list proto 'udp'
    list dest_ip '239.255.255.250'
    list dest_ip 'FF02::C'

config rule
    option name 'allow_srv_lan_ssdp'
    option dest_port '1900'
    option src 'srv'
    option dest 'lan'
    option target 'ACCEPT'
    list proto 'udp'
    list dest_ip '239.255.255.250'
    list dest_ip 'FF02::C'

Mögliche Verbindungen sehen also so aus:

IoT => SRV
IoT => LAN
SRV => IoT
SRV => LAN

Vom LAN-Netz ausgehende Verbindungen sind hier nicht explizit aufgelistet, denn sie sind in meinem Netzwerk generell erlaubt und nicht eingeschränkt.

Die SSDP-Freigaben sind hiermit erledigt. Die Geräte könnten sich schon untereinander finden, doch noch keine Inhalte austauschen. Hier für muss eine Freigabe für den eigentlichen Stream-Port einegrichtet werden. Im Fall von meinem Jellyfin-DLNA-Server werden Inhalte via Port 8096 (TCP) gestreamed. Meine Freigaben dafür sehen so aus:

config rule
    option name 'allow_iot_srv_nas_dlna_http'
    option src 'iot'
    option dest 'srv'
    list dest_ip '192.168.3.2'
    list dest_ip 'fd00:0:0:3::2'
    option target 'ACCEPT'
    list proto 'tcp'
    option dest_port '8096'

Mehr Freigaben sind nicht nötig, denn Geräte aus dem LAN-Bereich dürfen ohnehin auf alles Zugreifen und Geräte aus dem SRV-Netz sollen niemals Streams aus dem IoT Netz oder dem LAN-Netz empfangen. Beim SSDP-Protokoll wurden bewusst mehrere Richtungen und Kombinationen berücksichtigt und freigeben, damit nicht nur SSDP NOTIFY (announcements) den richtigen Weg finden, sondern auch Fragen nach SSDP-Diensten (M-SEARCH). Generell ist hier aber nur das streamen vom SRV-Bereich entweder in LAN oder IOT vorgesehen.

Vergesst nicht, eure Änderungen an der Firewall zu aktivieren!

uci commit firewall
service firewall restart

Nach idR. weniger als 2 Minuten sollten sich die Geräte gemäß der FW-Konfiguration finden können. In meinem Beispiel der Fernseher im IoT Netz und der DLNA-Server im SRV Netz.

Die Sache mit IGMP

Je nach Konfiguration der Bridges auf dem OpenWRT-Router (bei mir br-lan, br-IOT und br-SRV) kann es vorkommen, dass sich DLNA-Geräte nicht oder nicht sofort finden. Das hängt mit dem Einsatz des IGMP-Protokolls zusammen…

Routet man Multicast-Pakete über Netzwerkgrenzen, kann es vorkommen, dass Netzsegmente von Multicast-Paketen geflutet werden, die keinen einzigen Client haben, welcher an diesen Paketen interessiert ist. Besonders dramatisch kann das Problem bei Multicast-Streams auftreten, wie z.B. IPTV, da hier entsprechend größere Mengen für den Videostream verteilt werden. Um unnötige Belastungen in größeren Netzwerken zu vermeiden, wurde IGMP (Internet Group Messaging Protocol) erfunden: Mittels IGMP kann ein Gerät an den Router melden, ob und an welchen Multicast-Paketen es interessiert ist. Erkennt der Router an einem Interface ein Gerät, das Interessen an Multicast-Paketen bekundet, leitet dieser die entsprechenden Pakete an dieses Subnetz weiter. Andernfalls nicht.

Normalerweise sollte IGMP auf dem OpenWRT Router für alle Bridges aktiviert sein. Das bedeutet: Wollen Geräte SSDP Pakete für DLNA empfangen, müssen sie diese beim Router zunächst “abonnieren”. Das funktioniert je nach Gerät mal mehr, mal weniger gut. Wer mit seinem Setup auf Probleme stößt oder gar keine SSDP Pakete in seinem Netzwerk erkennen kann, sollte einmal versuchen, IGMP auf allen involvierten Bridges am Router zu deaktivieren. Der Traffic erreicht dann immer alle Bridges (und somit Subnetze).

Wer in seinem Netzwerk ohnehin keine Multicast-Videostreams verteilt, kann IGMP getrost deaktiviert lassen. SSDP verursacht nur minimalen Traffic, der nicht ins Gewicht fällt. Wer hingegen IPTV im Netzwerk laufen hat und mehrere Subnetze betreibt, will IGMP sehr wahrscheinlich aktiviert lassen.


Weiterführende Links: