Adventures with DLNA
I recently acquired a Dell PowerEdge R430, and I’m running it as a (for now) single-node Kubernetes cluster that will host all of the various services currently running on a smattering of Raspberry Pis and my desktop machine. (That will be a blog post all on its own, once it’s done — there’s still a few services to migrate.) In the meantime, the setup for Plex’s DLNA server was a nightmare and a half, and I wanted to document the process here.
Digital Living Network Alliance (DLNA) is a corporation that publishes a (paid) specification that narrows UPnP to a well-defined request/response format specific to home network applications. It allows devices in their UPnP broadcast to the local network to specify, “Hey, I’m a printer!” or, in Plex’s case, “Hey, I’m a media server!” Importantly, either is able to say, “Here’s how to connect to my service,” and any client can configure itself automatically.
I had two major issues with this in my specific setup. The first, more minor issue was that, since Plex is running inside Kubernetes, it’s binding and broadcasting to Flannel’s 10.244.0.0/16
network. To fix this, I set the Pod
to use the node’s hostNetwork
, forcing the container to bind to my home 10.10.0.0/16
subnet.
apiVersion: apps/v1
kind: Deployment
spec:
containers:
- name: plex
# ...
hostNetwork: true
This isn’t an ideal solution (and I’m actually not 100% sure how it’ll function once I eventually set up more nodes in the Kubernetes cluster), but I don’t know of a better way to do it without ripping out Flannel and swapping to a CNI provider that provides broadcast forwarding functionality.
Speaking of broadcast forwarding… the second issue. I have all my IoT crap on an isolated VLAN, so (similar to the Kubernetes problem), so at this point, I could connect to Plex and play media from my desktop, but broadcasts weren’t reaching the receiver. The fix for this was multi-pronged. (For context, my router is a Ubiquiti UDM Pro.)
-
Inside the Network application, inside
Settings > Networks
, turn onMulticast DNS
for myLAN
andIoT
networks. (NB: I’m not 100% sure if this is necessary, but a few Ubiquiti forum posts mentioned it, and I had to turn it on anyways to get AirPlay/Chromecast to work. YMMV.) I did not have to turn onIGMP Snooping
, as some forum posts suggested; in fact, turning it on made no difference at all, so I left it off. -
If either device (client or server) is on WiFi, you may need to turn on
Multicast Enhancement
in the Network app underWiFi > (network) > Advanced > Multicast Management
. My devices are wired, do I didn’t do this, but some forum posts suggested this may be necessary. -
Allow the desired service ports through your firewall. I have a “Lan In” rule explicitly dropping all packets from
IoT
->LAN
, so I created a higher-priority “Lan In” rule to explicitly allow from anywhere ->LAN
, ports1900
(UPnP/SSDP),32400
(Plex), or32469
(Plex DLNA service). -
Run
docker.io/scyto/multicast-relay
on your UDM. This is where the magic happens, and I owe it all to a blog post about Sonos.-
SSH into your UDM:
ssh -o HostKeyAlgorithms=ssh-rsa root@unifi
Remember that the SSH password here is not the one from the Network application (in
Settings > System > Network Device SSH Authentication
), but rather the one from the UnifiOS Dashboard (inConsole Settings > Advanced > SSH
). Additionally, you may need to manually specifyssh-rsa
as an allowedHostKeyAlgorithms
(reflected above) — that algorithm was disabled by default on my machine, which threw an error at first. -
Test the relay:
podman run \ --env INTERFACES="br0 ${VLAN_INTERFACES}" \ --env OPTS="--verbose --noMDNS" \ --network host \ --rm \ docker.io/scyto/multicast-relay
Replace the
VLAN_INTERFACES
variable with the bridge interfaces for your VLANs. On my UDM, they were named likebr${VLAN_ID}
, but YMMV. You can check withip address show
.At this point, try to use your service! It can take a while for the DLNA process to complete and the client to recognize the server, so let it sit for ~5-10 minutes if you don’t see the server show up immediately.
-
^C
and run a daemonized version:podman run \ --detach \ --env INTERFACES="br0 ${VLAN_INTERFACES}" \ --env OPTS="--verbose --noMDNS" \ --network host \ --restart unless-stopped \ docker.io/scyto/multicast-relay
This container (despite the
--restart unless-stopped
flag) will not survive reboots of the UDM. I chose to install and utilize the very handyon-boot-script
fromunifios-utilities
:# install `on-boot-script` unifi-os shell cd curl -fsSL https://udm-boot.boostchicken.dev \ -o udm-boot_1.0.5_all.deb dpkg -i udm-boot_1.0.5_all.deb systemctl enable udm-boot exit # create the script to run on boot # with the command from before cat <<EOF > /mnt/data/on_boot.d/multicast-relay.sh #!/usr/bin/env sh podman run \ --detach \ --env INTERFACES="br0 ${VLAN_INTERFACES}" \ --env OPTS="--verbose --noMDNS" \ --network host \ --restart unless-stopped \ docker.io/scyto/multicast-relay EOF
-
Congratulations — you now have DLNA working between isolated VLANs! 🚀
I truly wish DLNA wasn’t built on UPnP/SSDP, because it’s (among other things) a security nightmare. A better solution would have been the ability to configure my Plex’s DLNA server IP address manually, instead of only accepting it through SSDP (like I can with a printer). Most home users are familiar with this process already, and virtually no one is going to want to take the time to get this shit working if it doesn’t out of the box; most will just continue to stream from Spotify or Apple Music.
With that said… I hope getting to listen to all your favorite tracks in full 16-bit 44.1KHz lossless glory makes this trek as worth it for you as it did for me.