module Hans.Layer.Tcp.Window (
module Hans.Layer.Tcp.Window
, module Hans.Layer.Tcp.WaitBuffer
) where
import Hans.Layer.Tcp.WaitBuffer
import Hans.Message.Tcp
import Control.Monad (mzero)
import Data.Bits (shiftL)
import Data.Time.Clock.POSIX (POSIXTime)
import Data.Word (Word16,Word32)
import qualified Data.ByteString as S
import qualified Data.ByteString.Lazy as L
import qualified Data.Foldable as F
import qualified Data.Sequence as Seq
import qualified Data.Traversable as T
data RemoteWindow = RemoteWindow
{ rwSegments :: OutSegments
, rwAvailable :: !Word32
, rwSize :: !Word32
, rwSndWind :: !Word16
, rwSndWindScale :: !Int
} deriving (Show)
emptyRemoteWindow :: Word16 -> Int -> RemoteWindow
emptyRemoteWindow size scale = refreshRemoteWindow $! RemoteWindow
{ rwSegments = Seq.empty
, rwAvailable = 0
, rwSize = 0
, rwSndWind = fromIntegral size
, rwSndWindScale = scale
}
refreshRemoteWindow :: RemoteWindow -> RemoteWindow
refreshRemoteWindow rw = rw
{ rwSize = size
, rwAvailable = avail
}
where
size = fromIntegral (rwSndWind rw) `shiftL` rwSndWindScale rw
used = rwSize rw rwAvailable rw
avail | used > size = 0
| otherwise = size used
setSndWindScale :: Int -> RemoteWindow -> RemoteWindow
setSndWindScale scale rw = refreshRemoteWindow $! rw
{ rwSndWindScale = scale
}
setSndWind :: Word16 -> RemoteWindow -> RemoteWindow
setSndWind size rw = refreshRemoteWindow $! rw
{ rwSndWind = size
}
releaseSpace :: Word32 -> RemoteWindow -> RemoteWindow
releaseSpace len rw = rw
{ rwAvailable = min (rwSize rw) (rwAvailable rw + len)
}
addSegment :: OutSegment -> RemoteWindow -> RemoteWindow
addSegment seg win = win
{ rwSegments = rwSegments win Seq.|> seg
, rwAvailable = rwAvailable win outSize seg
}
receiveAck :: TcpHeader -> RemoteWindow -> Maybe (OutSegment,RemoteWindow)
receiveAck hdr win = do
let match seg = tcpSeqNum (outHeader seg) < tcpAckNum hdr
&& outAckNum seg <= tcpAckNum hdr
(acks,rest) = Seq.spanl match (rwSegments win)
case Seq.viewr acks of
_ Seq.:> seg -> do
let len = F.sum (outSize `fmap` acks)
win' = setSndWind (tcpWindow hdr)
$ releaseSpace len
$ win { rwSegments = rest }
return (seg, win')
Seq.EmptyR -> mzero
genRetransmitSegments :: RemoteWindow -> (OutSegments,RemoteWindow)
genRetransmitSegments win = (outSegs, win { rwSegments = segs' })
where
(outSegs,segs') = T.mapAccumL step Seq.empty (rwSegments win)
step rts seg = (rts',seg')
where
seg' = decrementRTO seg
rts' | outRTO seg' <= 0 = rts Seq.|> seg'
| otherwise = rts
clearRetransmit :: RemoteWindow -> RemoteWindow
clearRetransmit rw = rw { rwSegments = Seq.empty }
retransmitEmpty :: RemoteWindow -> Bool
retransmitEmpty rw = Seq.null (rwSegments rw)
type OutSegments = Seq.Seq OutSegment
data OutSegment = OutSegment
{ outAckNum :: !TcpSeqNum
, outTime :: !POSIXTime
, outFresh :: Bool
, outRTO :: !Int
, outHeader :: !TcpHeader
, outBody :: !L.ByteString
} deriving (Show)
mkOutSegment :: POSIXTime -> Int -> TcpHeader -> L.ByteString -> OutSegment
mkOutSegment created rto hdr body =
OutSegment { outAckNum = tcpSeqNum hdr + outSize' hdr body
, outTime = created
, outFresh = True
, outRTO = rto
, outHeader = hdr
, outBody = body
}
ctlLength :: Num len => Bool -> len
ctlLength True = 1
ctlLength False = 0
outSize :: Num a => OutSegment -> a
outSize OutSegment { .. } = outSize' outHeader outBody
outSize' :: Num a => TcpHeader -> L.ByteString -> a
outSize' TcpHeader { .. } body = fromIntegral (L.length body)
+ ctlLength tcpSyn
+ ctlLength tcpFin
decrementRTO :: OutSegment -> OutSegment
decrementRTO seg
| outRTO seg' <= 0 = seg' { outFresh = False }
| otherwise = seg'
where
seg' = seg { outRTO = outRTO seg 1 }
data LocalWindow = LocalWindow
{ lwBuffer :: Seq.Seq InSegment
, lwRcvNxt :: !TcpSeqNum
, lwAvailable :: !Word32
, lwSize :: !Word32
, lwRcvWind :: !Word16
, lwRcvWindScale :: !Int
} deriving (Show)
emptyLocalWindow :: TcpSeqNum -> Word16 -> Int -> LocalWindow
emptyLocalWindow sn size scale = refreshLocalWindow $! LocalWindow
{ lwBuffer = Seq.empty
, lwRcvNxt = sn
, lwAvailable = 0
, lwSize = 0
, lwRcvWind = size
, lwRcvWindScale = scale
}
localWindowSackBlocks :: LocalWindow -> Seq.Seq SackBlock
localWindowSackBlocks lw = case Seq.viewl (fmap mkSackBlock (lwBuffer lw)) of
b Seq.:< rest -> uncurry (Seq.|>) (F.foldl step (Seq.empty,b) rest)
Seq.EmptyL -> Seq.empty
where
step (bs,b) b'
| sbRight b == sbLeft b' = (bs,b { sbRight = sbRight b' })
| otherwise = (bs Seq.|> b, b')
refreshLocalWindow :: LocalWindow -> LocalWindow
refreshLocalWindow lw = lw
{ lwSize = fromIntegral (lwRcvWind lw) `shiftL` lwRcvWindScale lw
}
setRcvNxt :: TcpSeqNum -> LocalWindow -> LocalWindow
setRcvNxt sn lw = lw { lwRcvNxt = sn }
addRcvNxt :: TcpSeqNum -> LocalWindow -> LocalWindow
addRcvNxt sn win = win { lwRcvNxt = lwRcvNxt win + sn }
setRcvWind :: Word16 -> LocalWindow -> LocalWindow
setRcvWind size lw = refreshLocalWindow $! lw
{ lwRcvWind = size
}
setRcvWindScale :: Int -> LocalWindow -> LocalWindow
setRcvWindScale scale lw = refreshLocalWindow $! lw
{ lwRcvWindScale = scale
}
incomingPacket :: TcpHeader -> S.ByteString -> LocalWindow
-> (Seq.Seq InSegment, LocalWindow)
incomingPacket hdr body win = stepWindow (addInSegment hdr body win)
addInSegment :: TcpHeader -> S.ByteString -> LocalWindow -> LocalWindow
addInSegment hdr body win = win { lwBuffer = insert (lwBuffer win) }
where
seg = mkInSegment (lwRcvNxt win) hdr body
insert buf = case Seq.viewl buf of
a Seq.:< rest -> case compare (inRelSeqNum a) (inRelSeqNum seg) of
LT -> a Seq.<| insert rest
EQ -> seg Seq.<| rest
GT -> seg Seq.<| buf
Seq.EmptyL -> Seq.singleton seg
stepWindow :: LocalWindow -> (Seq.Seq InSegment, LocalWindow)
stepWindow = loop 0 Seq.empty
where
loop expect segs win = case Seq.viewl (lwBuffer win) of
a Seq.:< rest | inRelSeqNum a == expect -> keep a segs rest win
| otherwise -> finalize segs win
Seq.EmptyL -> finalize segs win
keep seg segs rest win = loop (inRelSeqNum seg + len) (segs Seq.|> seg) win
{ lwBuffer = rest
, lwRcvNxt = lwRcvNxt win + len
}
where
len = fromIntegral (S.length (inBody seg))
finalize segs win = (segs,win { lwBuffer = update `fmap` lwBuffer win })
where
len = F.sum (inLength `fmap` segs)
update is = is { inRelSeqNum = inRelSeqNum is len }
data InSegment = InSegment
{ inRelSeqNum :: !TcpSeqNum
, inHeader :: !TcpHeader
, inBody :: !S.ByteString
} deriving (Show)
inSeqNum :: InSegment -> TcpSeqNum
inSeqNum = tcpSeqNum . inHeader
inLength :: Num a => InSegment -> a
inLength = fromIntegral . S.length . inBody
mkInSegment :: TcpSeqNum -> TcpHeader -> S.ByteString -> InSegment
mkInSegment rcvNxt hdr body = InSegment
{ inRelSeqNum = rel
, inHeader = hdr
, inBody = body
}
where
rel | tcpSeqNum hdr < rcvNxt = maxBound rcvNxt + tcpSeqNum hdr + 1
| otherwise = tcpSeqNum hdr rcvNxt
mkSackBlock :: InSegment -> SackBlock
mkSackBlock is = SackBlock
{ sbLeft = sn
, sbRight = sn + fromIntegral (S.length (inBody is))
}
where
sn = tcpSeqNum (inHeader is)