OpenConnect can finish the tunnel handshake and still leave you without a route to the private subnet, because the client and the helper that edits local network state are two different programs. Authentication proves the gateway accepted the session; it proves nothing about which interface, routes or resolver settings were installed afterwards. Most confusing reports come from replacing the helper with a copy from an old answer, which changes behaviour for every connection on the machine. Locate the helper the running client actually invokes before editing any of it.

Two Programs, Two Responsibilities

The client owns transport, authentication and the tunnel device. A separate helper owns what happens on the local system once the tunnel exists: addressing on the interface, routes for the subnets the gateway advertised, and resolver configuration. The OpenConnect helper documentation describes that split and the environment the helper receives. The distinction matters during a failure, because a rejected password and a missing route are not the same incident even when the desktop shows one dialog for both.

Keeping the split in mind also explains why a connection can report success while private names still fail to resolve. The tunnel is up in both cases, and only the local integration stage differs.

Locate the Helper the Running Client Uses

The path is set when the package is built and can be overridden per invocation, so the file you remember from another distribution may have nothing to do with this connection. Ask the running process rather than the documentation you happen to have open.

openconnect --version
ps -o args= -C openconnect
dpkg -L openconnect | grep -i script
ls -l /etc/vpnc/ 2>/dev/null

If the process line carries a --script argument, that file wins over the compiled default. Older paths such as /etc/vpnc/vpnc-script can exist and still be unused, which is why deleting or editing them changes nothing on a recent build. Check the modification date as well, since a helper nobody has touched in years is a strong sign that the packaged default is doing the work.

NetworkManager integration is a third owner, with its own helper and its own profile settings. Stacking a second route or resolver script on top of that integration duplicates routes and leaves a resolver that two programs both claim to manage. Pick one owner per connection and disable the other before testing.

Read the First Failed Operation, Not the Last Warning

Interface configuration, route installation, resolver selection and stale-state cleanup are separate steps, and the warning printed last is often a cleanup complaint rather than the failure that broke the connection. Read upward until the first specific operation that failed. A helper error that names a file or an address is worth more than ten lines of teardown noise.

Compare the intended local result with the observed one: the tunnel device exists, a route to a known private address goes through it, and the resolver answers for a private name. A helper that succeeds at the first two and fails at the third points at resolver policy rather than routing. A helper that never configures the device at all was probably never executed, which is a permissions question rather than a networking one. Confirm the executable bit and the service account before rewriting any logic.

Verify Connect, Reconnect and Disconnect

Setup is only half the contract, because the same helper must restore or remove the state it owns when the tunnel closes. A helper that adds routes and never removes them leaves the machine with a stale path that outlives the session.

ip -brief address show tun0
ip route get 10.24.8.15
resolvectl status tun0
journalctl -u NetworkManager -b --no-pager | grep -i -E 'vpnc|script'

Run the first three while connected, disconnect, then run them again and confirm the route and resolver entry are gone. The fourth line shows whether the helper was invoked at all, and which executable path the service used. Capture that pair of outputs in the ticket, because a before-and-after is what makes a cleanup bug obvious.

Keep the helper packaged and executable by the service account, since a script that runs in your shell can still be denied under a confined service policy. When a customization is genuinely required, document its purpose and owner and compare it against the packaged version at each upgrade. An unexplained fork of a helper is one of the few things that can make a working connection fail a year later.

འབྲེལ་བའི་རྩོམ་ཡིག