A dead listener on DD-WRT usually means the daemon exited while parsing its configuration, not that a firewall rule blocked the port. OpenVPN 2.5 tightened several defaults that older configs relied on, and a generated config that worked on an older build can be rejected before the socket is ever opened. The router's own log holds the reason, and the generated file holds the input. Reading both in that order is faster than any packet capture.

The daemon is not the firewall

The distinction matters because the two failures look identical from outside: a client sends a handshake and gets nothing back. One is a process that is not running, and the other is a process that is running and filtering.

Separate them with two checks before you change anything, because a firewall edit on a dead daemon just adds a second variable.

Read the config DD-WRT generated

DD-WRT does not hand OpenVPN the text you typed into the web interface. It writes a generated configuration to /tmp/openvpn/openvpn.conf and starts the daemon against that file, so the GUI fields and any Additional Config text are merged before OpenVPN ever sees them. If you debug the form instead of the generated file, you are reading an intention rather than the actual input. Copy that file somewhere persistent before a reboot clears /tmp.

Compare the generated file against what you intended, especially where the GUI writes a directive you never chose. A dropdown that emits an obsolete option is invisible in the form and fatal to the daemon.

If the generated file is empty or missing, the daemon never got as far as starting, and the problem sits upstream of OpenVPN.

What OpenVPN 2.5 stopped accepting

OpenVPN 2.5 replaced ncp-ciphers with data-ciphers, and it raised the default minimum TLS version to 1.2. It also changed compression handling so a peer that offers compression is refused unless the configuration explicitly allows it, and it deprecated older option names such as ns-cert-type in favor of remote-cert-tls. Each of those changes turns a previously working configuration into a parse or negotiation failure.

The failure mode differs by directive. An unknown or removed option stops the daemon at startup, which is your dead listener. A negotiation mismatch lets the daemon run and refuses clients, which looks like a firewall problem from the client side.

Read the first error in the log rather than the last one. Parsing stops at the first bad directive, and everything after it is noise from the shutdown.

The Broadcom-specific prerequisites

On Broadcom builds the tunnel device has to exist before the daemon can use it. Check that the tun module is loaded and that /dev/net/tun is present, because a missing device produces a startup failure that has nothing to do with certificates. The same applies to the helper scripts DD-WRT generates for routes and DNS: if they are missing or not executable, the daemon can start and still fail to finish its setup.

Run the daemon in the foreground with higher verbosity against the generated config when you need the real message. A foreground run prints the parse error that the init script swallowed.

Then restore the normal service, because a foreground daemon and the init script will fight over the port.

Prove the listener exists before you debug clients

Confirm the process and the socket separately: one command for the running daemon, another for a bound UDP port on the expected address. A process without a socket means it is still in setup or bound to the wrong interface. The pair is a useful test precisely because one half cannot be true without the other.

Only after the listener exists does client-side debugging make sense. Then check that the client's cipher and TLS settings match the server's data-ciphers list and minimum version, and that the port is actually forwarded to the router.

Keep the generated config and the log line that proved the fix. The next firmware update can change the defaults again, and that record is what turns a repeat into a five-minute check.

Tënë sô zo