Network topology

                        OPNsense
                      10.33.1.1
                          |
                  LAN 10.33.1.0/24
                          |
             +------------+-------------+
             |                          |
      Unraid / MiniDLNA           GL-MT6000 Flint 2
        10.33.1.10                eth1: 10.33.1.2
                                         |
                                  br-lan: 10.33.2.1
                                         |
                                  Wi-Fi 10.33.2.0/24
                                         |
                                  HEOS / Marantz

                                  br-guest: 10.81.1.1
                                         |
                                   Guest Wi-Fi
                                   NOT INCLUDED

Important addresses:

OPNsense LAN:        10.33.1.1
Flint upstream:      10.33.1.2 / eth1
Flint trusted Wi-Fi: 10.33.2.1 / br-lan
Flint guest Wi-Fi:   10.81.1.1 / br-guest
Unraid / MiniDLNA:   10.33.1.10
MiniDLNA HTTP port:  8200
SSDP multicast:      239.255.255.250:1900/UDP

Guest Wi-Fi is deliberately excluded from SSDP forwarding.


Why it failed

UPnP/DLNA discovery uses SSDP multicast:

239.255.255.250:1900/UDP

Routers do not normally forward this multicast between subnets.

This meant:

HEOS 10.33.2.x
      |
      | SSDP discovery
      X
      |
MiniDLNA 10.33.1.10

However, bookmarked tracks continued to work because playback itself uses normal routed HTTP traffic:

HEOS 10.33.2.x
      |
      | HTTP
      v
10.33.1.10:8200

MiniDLNA configuration

MiniDLNA runs as the binhex-minidlna Docker container on Unraid using Host networking.

Relevant configuration:

port=8200
media_dir=/media
friendly_name=MiniDLNA
db_dir=/config
log_dir=/config
inotify=yes
notify_interval=900

network_interface does not need to be explicitly set, although the Unraid LAN interface is:

eth0 = 10.33.1.10/24

To confirm MiniDLNA itself is healthy:

http://10.33.1.10:8200/rootDesc.xml

It should return XML containing something similar to:

<deviceType>urn:schemas-upnp-org:device:MediaServer:1</deviceType>
<friendlyName>MiniDLNA</friendlyName>

On Unraid:

ss -lunp | grep ':1900'

Expected:

239.255.255.250:1900 ... minidlnad

Flint 2: SMCRoute

The GL-MT6000 runs GL.iNet firmware 4.9.1, based on OpenWrt 21.02.

The package installed through:

Applications → Plug-ins → smcroute

is:

smcroute

/etc/smcroute.conf

phyint br-lan enable
phyint eth1 enable

mgroup from br-lan group 239.255.255.250
mgroup from eth1 group 239.255.255.250

mroute from br-lan group 239.255.255.250 to eth1
mroute from eth1 group 239.255.255.250 to br-lan

br-guest is intentionally absent.

Enable and start SMCRoute:

/etc/init.d/smcroute enable
/etc/init.d/smcroute restart

Verify:

smcroutectl show groups

Expected:

GROUP (S,G)                                IIF
(*, 239.255.255.250)                       br-lan
(*, 239.255.255.250)                       eth1

And:

smcroutectl show routes

Expected template routes:

(*, 239.255.255.250)   br-lan   eth1
(*, 239.255.255.250)   eth1     br-lan

Once traffic exists, the kernel table will also contain concrete source addresses.


Flint 2: firewall configuration

The Flint's upstream eth1 belongs to the OpenWrt firewall zone called:

wan

The trusted Wi-Fi br-lan belongs to:

lan

Allow MiniDLNA SSDP replies

MiniDLNA answers an active M-SEARCH using UDP source port 1900, but sends it back to the client's temporary source port.

Current rule:

Name:             Allow-MiniDLNA-SSDP-Reply
Family:           IPv4
Source zone:      wan
Source IP:        10.33.1.10
Source port:      1900
Destination zone: lan
Destination IP:   10.33.2.0/24
Protocol:         UDP
Target:           ACCEPT

UCI equivalent:

uci add firewall rule
uci set firewall.@rule[-1].name='Allow-MiniDLNA-SSDP-Reply'
uci set firewall.@rule[-1].family='ipv4'
uci set firewall.@rule[-1].src='wan'
uci set firewall.@rule[-1].src_ip='10.33.1.10'
uci set firewall.@rule[-1].src_port='1900'
uci set firewall.@rule[-1].dest='lan'
uci set firewall.@rule[-1].dest_ip='10.33.2.0/24'
uci set firewall.@rule[-1].proto='udp'
uci set firewall.@rule[-1].target='ACCEPT'

Allow MiniDLNA SSDP advertisements

MiniDLNA periodically sends NOTIFY messages to the multicast group.

The source port is not necessarily 1900. In packet captures MiniDLNA used ports such as 33852 and 59968.

Therefore only the destination port should be restricted.

Name:             Allow-MiniDLNA-SSDP-Notify
Family:           IPv4
Source zone:      wan
Source IP:        10.33.1.10
Destination zone: lan
Destination IP:   239.255.255.250
Destination port: 1900
Protocol:         UDP
Target:           ACCEPT

UCI equivalent:

uci add firewall rule
uci set firewall.@rule[-1].name='Allow-MiniDLNA-SSDP-Notify'
uci set firewall.@rule[-1].family='ipv4'
uci set firewall.@rule[-1].src='wan'
uci set firewall.@rule[-1].src_ip='10.33.1.10'
uci set firewall.@rule[-1].dest='lan'
uci set firewall.@rule[-1].dest_ip='239.255.255.250'
uci set firewall.@rule[-1].dest_port='1900'
uci set firewall.@rule[-1].proto='udp'
uci set firewall.@rule[-1].target='ACCEPT'

Commit changes:

uci commit firewall
/etc/init.d/firewall restart

GL.iNet may print a number of warnings during a full firewall restart relating to inactive interfaces, VPN zones and GL.iNet-specific firewall extensions. These did not prevent the SSDP rules from working.


Critical NAT exception

This was the key part of the fix.

The Flint normally masquerades all traffic leaving the trusted Wi-Fi toward its upstream interface.

Without an exception, an SSDP search changed from:

10.33.2.x:37462 → 239.255.255.250:1900

to:

10.33.1.2:37462 → 239.255.255.250:1900

MiniDLNA then replied to the Flint itself instead of the original HEOS client.

The WAN firewall zone is:

firewall.@zone[1]

and contains:

name='wan'
masq='1'

The following exclusion was added:

uci add_list firewall.@zone[1].masq_dest='!239.255.255.250/32'
uci commit firewall
/etc/init.d/firewall restart

Verify:

uci show firewall.@zone[1]

Expected:

firewall.cfgXXXXXX.name='wan'
firewall.cfgXXXXXX.masq='1'
firewall.cfgXXXXXX.masq_dest='!239.255.255.250/32'

This means:

Normal Wi-Fi → upstream traffic:
    NAT normally

Traffic to 239.255.255.250:
    DO NOT NAT

The actual anonymous UCI zone index could change in the future, so before recreating the rule always identify the WAN zone first:

uci show firewall | grep -E "=zone|\.name='wan'|\.masq"

OPNsense return route

Once NAT is removed from SSDP, MiniDLNA sees the real client address:

10.33.2.x

MiniDLNA sends its SSDP response directly to that address.

Unraid's normal gateway is OPNsense at 10.33.1.1, so OPNsense must know that 10.33.2.0/24 lives behind the Flint.

Gateway

Create:

Name:                GLINET_WIFI
Interface:           LAN
Address Family:      IPv4
IP Address:          10.33.1.2
Upstream Gateway:    No
Far Gateway:         No
Gateway Monitoring:  Disabled
Description:         Flint 2 WiFi router

Static route

Create:

Network:     10.33.2.0/24
Gateway:     GLINET_WIFI
Description: WiFi via Flint 2

Result:

10.33.2.0/24 via 10.33.1.2

If the OPNsense LAN firewall policy is restrictive, it must also permit traffic from the LAN toward 10.33.2.0/24.

With a normal LAN net → any allow policy, no additional rule should be necessary.


Expected working SSDP flow

A HEOS client searches for a media server:

10.33.2.x:ephemeral
        |
        | M-SEARCH
        v
239.255.255.250:1900
        |
        | SMCRoute
        v
Flint eth1
        |
        | source still 10.33.2.x
        v
MiniDLNA 10.33.1.10

MiniDLNA responds:

10.33.1.10:1900
        |
        | HTTP/1.1 200 OK over UDP
        v
10.33.2.x:ephemeral

The response contains:

ST: urn:schemas-upnp-org:device:MediaServer:1
SERVER: ... MiniDLNA/1.3.3
LOCATION: http://10.33.1.10:8200/rootDesc.xml

After receiving this, HEOS can retrieve rootDesc.xml via ordinary routed HTTP and display MiniDLNA under Music Servers.


Troubleshooting

1. Check MiniDLNA itself

From another client:

http://10.33.1.10:8200/rootDesc.xml

If this does not work, troubleshoot MiniDLNA or normal IP routing first.

On Unraid:

ss -lunp | grep ':1900'

Expected:

239.255.255.250:1900 ... minidlnad

Restart MiniDLNA if necessary:

docker restart binhex-minidlna

2. Check Flint interfaces

ip -br addr

Important entries should still resemble:

eth1       UP   10.33.1.2/24
br-lan     UP   10.33.2.1/24
br-guest   UP   10.81.1.1/24

Check routes:

ip route

Expected:

default via 10.33.1.1 dev eth1
10.33.1.0/24 dev eth1
10.33.2.0/24 dev br-lan
10.81.1.0/24 dev br-guest

3. Check SMCRoute

/etc/init.d/smcroute status

Then:

smcroutectl show groups
smcroutectl show routes

Expected interfaces:

br-lan
eth1

Expected group:

239.255.255.250

There should be no br-guest route.

If SMCRoute is not running:

/etc/init.d/smcroute enable
/etc/init.d/smcroute restart

4. Check firewall rules

uci show firewall | grep -E 'MiniDLNA|masq_dest'

Expected to find:

Allow-MiniDLNA-SSDP-Reply
Allow-MiniDLNA-SSDP-Notify
masq_dest='!239.255.255.250/32'

If there are duplicate MiniDLNA rules, inspect them with:

uci show firewall

and delete only the unwanted duplicate, for example:

uci delete firewall.@rule[24]
uci commit firewall
/etc/init.d/firewall restart

Do not blindly reuse a rule number from this document; anonymous UCI indexes can change.


5. Check the critical NAT behavior

Run on the Flint:

tcpdump -ni eth1 -vv -A 'udp port 1900'

Then refresh:

HEOS → Music Servers

A working MediaServer search on eth1 should retain the Wi-Fi client source:

10.33.2.x.37462 > 239.255.255.250.1900
M-SEARCH * HTTP/1.1
ST: urn:schemas-upnp-org:device:MediaServer:1

Bad

If instead you see:

10.33.1.2.37462 > 239.255.255.250.1900

the Flint is still NATing SSDP.

Check:

uci show firewall.@zone[1]

and confirm:

masq_dest='!239.255.255.250/32'

6. Check MiniDLNA response

After an M-SEARCH, a working capture should show:

10.33.1.10.1900 > 10.33.2.x.37462
HTTP/1.1 200 OK
...
LOCATION: http://10.33.1.10:8200/rootDesc.xml

If MiniDLNA replies but the packet never appears on br-lan, investigate:

  • OPNsense static route
  • OPNsense LAN firewall policy
  • Flint Allow-MiniDLNA-SSDP-Reply rule

Capture the trusted Wi-Fi side:

tcpdump -ni br-lan -vv -A 'udp port 1900 and host 10.33.1.10'

7. Check OPNsense route

OPNsense must have:

10.33.2.0/24 → 10.33.1.2

If this route is missing, MiniDLNA's direct reply to 10.33.2.x will not return through the Flint.


Useful packet signatures

HEOS / Marantz advertisement

Typical:

NOTIFY * HTTP/1.1
HOST: 239.255.255.250:1900
SERVER: LINUX UPnP/1.0 Denon-Heos/...

HEOS searching for a media server

M-SEARCH * HTTP/1.1
HOST: 239.255.255.250:1900
MAN: "ssdp:discover"
ST: urn:schemas-upnp-org:device:MediaServer:1

MiniDLNA advertisement

NOTIFY * HTTP/1.1
HOST:239.255.255.250:1900
LOCATION:http://10.33.1.10:8200/rootDesc.xml
SERVER: ... MiniDLNA/1.3.3
NT:urn:schemas-upnp-org:device:MediaServer:1
NTS:ssdp:alive

MiniDLNA discovery response

HTTP/1.1 200 OK
ST: urn:schemas-upnp-org:device:MediaServer:1
SERVER: ... MiniDLNA/1.3.3
LOCATION: http://10.33.1.10:8200/rootDesc.xml

Firmware upgrades and persistence

The setup survives normal router reboots.

The following are stored persistently:

  • OpenWrt UCI firewall configuration
  • OPNsense gateway/static route
  • /etc/smcroute.conf
  • SMCRoute init enablement

However, a GL.iNet firmware upgrade may remove user-installed packages even when Keep Settings is selected.

The smcroute package may therefore need to be reinstalled after a firmware upgrade.

Recommended additional persistence setting:

grep -qxF '/etc/smcroute.conf' /etc/sysupgrade.conf || \
    echo '/etc/smcroute.conf' >> /etc/sysupgrade.conf

Verify:

cat /etc/sysupgrade.conf

After a GL.iNet firmware upgrade:

smcroutectl show routes

If the command does not exist, reinstall smcroute through:

Applications → Plug-ins

Then:

/etc/init.d/smcroute enable
/etc/init.d/smcroute restart

Finally verify:

uci show firewall | grep -E 'MiniDLNA|masq_dest'
cat /etc/smcroute.conf
smcroutectl show groups
smcroutectl show routes

Quick health check

When HEOS discovery stops working, run these in order on the Flint:

ip -br addr

smcroutectl show groups
smcroutectl show routes

uci show firewall | grep -E 'MiniDLNA|masq_dest'

tcpdump -ni eth1 -vv -A 'udp port 1900'

Then refresh HEOS → Music Servers.

The most important check is:

M-SEARCH leaving eth1 MUST have source 10.33.2.x

and not:

10.33.1.2

If the source is 10.33.1.2, the SSDP NAT exclusion is missing or no longer active.


Final configuration summary

MiniDLNA:
    10.33.1.10:8200
    SSDP 239.255.255.250:1900

Flint:
    eth1   = 10.33.1.2
    br-lan = 10.33.2.1
    br-guest excluded

SMCRoute:
    br-lan <-> eth1
    multicast group 239.255.255.250

Flint firewall:
    Allow MiniDLNA SSDP response wan -> lan
    Allow MiniDLNA SSDP NOTIFY wan -> lan

Flint NAT:
    Normal masquerading enabled
    Exception:
        !239.255.255.250/32

OPNsense:
    Gateway GLINET_WIFI = 10.33.1.2
    Static route:
        10.33.2.0/24 via 10.33.1.2

Guest:
    10.81.1.0/24
    no SSDP forwarding