The tunnel drops, the kill switch engages, and a download that was three quarters finished either freezes or fails immediately. Those two outcomes come from different enforcement choices, not from different levels of protection. A dropped packet leaves the connection waiting, while a rejected one closes it and tells the application. Deciding which behaviour you want is a design question, and testing it is a separate exercise.

Dropped, Rejected and Reset Are Different

A firewall rule that drops traffic discards the packet and sends nothing back, so the sender keeps retransmitting on its own timers. A rule that rejects with a TCP reset closes the connection immediately and the application sees an error. Most kill switches drop, because a rejection would reveal the policy and because dropping resembles how a dead network behaves.

Blocking is also not the same as closing the socket. The application still holds a descriptor in an established state, and only the packets are missing. That is why a page can sit on a spinner for minutes while the operating system insists the connection exists. Read the documented enforcement rather than inferring it from the application's message, since both behaviours look like the network being down.

What the Application Does While the Gap Lasts

TCP retransmits with increasing delays, and the default Linux retry budget expires somewhere around a quarter of an hour rather than in seconds. Until then the socket is neither usefully open nor closed. Applications with their own keepalives or request timeouts often give up earlier, which is why two programs on the same machine behave differently during one outage.

When the tunnel returns, the local socket is unchanged but the path may not be. A new public source address produces a different four-tuple at the server, so a stateful firewall on the far side no longer matches the session. The connection is dead even though the client never saw an error.

Decide Whether Recovery Is Required

Choose between two contracts. Either the session must survive a short gap, which needs stable addressing and a server that keeps its state, or the application must reconnect cleanly, which needs retry logic that is safe to repeat. Anything in between produces the worst outcome: a client that resubmits what the server already committed, or one that reports success for a session nobody can see.

Stable addressing is the part people forget, because a reconnect that lands on a different public address invalidates server-side state even when the client's socket looks healthy. Write down which of the two contracts applies to each application, since they rarely all match.

Check the Block and the Recovery Separately

Treat leak protection and session survival as two properties, because passing one says nothing about the other. A session that survives is not proof that direct traffic was blocked, and a session that dies is not proof that every direct route was.

ss -tnp state established '( dport = :443 )'
sudo conntrack -L -p tcp --dport 443
sudo tcpdump -ni any 'tcp port 443 and tcp[tcpflags] & tcp-syn != 0'

Use a harmless test operation when observing recovery, so a duplicated submission does not become the incident. Record the failure mode you actually saw: what the application displayed, whether the socket survived, and when it recovered by itself.

Write the expected behaviour next to the enforcement rule and date it. The next person to change that rule needs to know which of the two contracts the deployment promised.

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