natpmp: when reopening laptop, reestablish a connection to the router.

This commit is contained in:
Elliot Glaysher 2020-08-18 11:22:28 -04:00
parent 33cb1c1dce
commit 15f0df81f1

View File

@ -69,6 +69,14 @@ portLeaseLifetime = 15 * 60
portRenewalTime :: Word32
portRenewalTime = portLeaseLifetime - 60
-- Number of retries before we give up on performing nat operations.
maxRetries :: Int
maxRetries = 3
-- How long to wait between retries.
networkRetryDelay :: Int
networkRetryDelay = 5 * 1000000
-- Messages sent from the main thread to the port mapping communication thread.
data PortThreadMsg
= PTMOpen Word16 (STM ())
@ -163,6 +171,8 @@ portThread q stderr = do
logInfo $
displayShow ("port: sending initial request to NAT-PMP for port ", p)
setPortMapping pmp PTUdp p p portLeaseLifetime >>= \case
Left err | isResetAndRetry err -> do
attemptReestablishNatPmpThen (\pmp -> handlePTM pmp msg nextRenew)
Left err -> do
logError $
displayShow ("port: failed to request NAT-PMP for port ", p,
@ -197,10 +207,12 @@ portThread q stderr = do
displayShow ("port: sending renewing request to NAT-PMP for port ",
p)
setPortMapping pmp PTUdp p p portLeaseLifetime >>= \case
Left err | isResetAndRetry err -> do
attemptReestablishNatPmpThen (\pmp -> handleRenew pmp nextRenew)
Left err -> do
logError $
displayShow ("port: failed to request NAT-PMP for port ", p,
":", err, ", disabling NAT-PMP")
":", err, ". disabling NAT-PMP")
loopErr q
Right _ -> do
-- We don't need to filter the port because we just did.
@ -210,6 +222,29 @@ portThread q stderr = do
rest
loop pmp withRenew
-- If the internal natpmp socket is closed (laptop lid closed, network
-- change, etc), attempt to reestablish a connection.
attemptReestablishNatPmpThen :: (NatPmpHandle -> RIO e ())
-> RIO e ()
attemptReestablishNatPmpThen andThen = do
logInfo $
displayShow ("port: network changed. Attempting NAT reconnect");
loop 0
where
loop :: Int -> RIO e ()
loop tryNum = do
initNatPmp >>= \case
Left err -> do
if tryNum == maxRetries
then do
stderr $ "port: failed to reestablish a connection to your router"
loopErr q
else do
threadDelay networkRetryDelay
loop (tryNum + 1)
Right pmp -> do
andThen pmp
filterPort :: Word16
-> DH.MinPrioHeap POSIXTime RenewAction
-> DH.MinPrioHeap POSIXTime RenewAction
@ -252,6 +287,13 @@ likelyBehindRouter = do
Just ip@(10, _, _, _) -> pure True
_ -> pure False
-- Some of the errors that we encounter happen when the underlying sockets have
-- closed out from under us. When this happens, we want to wait a short time
-- and reset the system.
isResetAndRetry :: Error -> Bool
isResetAndRetry ErrRecvFrom = True
isResetAndRetry ErrSendErr = True
isResetAndRetry _ = False
-- Acquire a port for the duration of the RAcquire.
requestPortAccess :: forall e. (HasPortControlApi e) => Word16 -> RAcquire e ()