mptcpanalyzer-0.0.2.0: src/Net/Mptcp/Stats.hs
{-|
Module : Net.Mptcp.Stats
Description : Compute basic MPTCP statistics
Maintainer : matt
License : GPL-3
-}
{-# LANGUAGE DeriveAnyClass #-}
{-# LANGUAGE StandaloneDeriving #-}
module Net.Mptcp.Stats (
TcpSubflowUnidirectionalStats(..)
, MptcpUnidirectionalStats(..)
, getMptcpStats
, getMptcpGoodput
, getMptcpStatsDuration
, getSubflowStats
, showMptcpUnidirectionalStats
)
where
import MptcpAnalyzer.ArtificialFields
import MptcpAnalyzer.Pcap
import MptcpAnalyzer.Stream
import MptcpAnalyzer.Units
import qualified Data.Map as Map
import Net.Mptcp.Connection
import Net.Tcp
import Net.Tcp.Stats
import Data.Either (fromRight)
import Data.Text (Text)
import qualified Data.Text as T
import Control.Lens
import Control.Lens hiding (argument)
import Data.List (sort, sortBy, sortOn)
import Data.Map (Map, fromList, mapKeys)
import qualified Data.Map as Map
import Data.Maybe (catMaybes, fromJust)
import Data.Set (toList)
import Data.Vinyl
import Data.Word (Word32, Word64)
import qualified Data.Foldable as F
import qualified Frames as F
import qualified Frames.InCore as F
import MptcpAnalyzer.Types
import MptcpAnalyzer.Utils.Text
import Control.Exception (assert)
-- import MptcpAnalyzer.Pcap (addTcpDestinationsToAFrame)
-- | Useful to show DSN
data TcpSubflowUnidirectionalStats = TcpSubflowUnidirectionalStats {
-- tssStats :: TcpUnidirectionalStats
tssStats :: TcpUnidirectionalStats
, tssMinDsn :: Word64
, tssMaxDsn :: Word64
} deriving Show
-- newtype TcpSubflowUnidirectionalStats = TcpSubflowUnidirectionalStats
instance Semigroup TcpSubflowUnidirectionalStats where
-- (<>) :: a -> a -> a
-- TODO this does nothing
(<>) a b = a
instance Monoid TcpSubflowUnidirectionalStats where
mempty = TcpSubflowUnidirectionalStats {
tssStats = mempty
, tssMinDsn = 0
, tssMaxDsn = 0
}
-- | Holds MPTCP application level statistics for one direction
data MptcpUnidirectionalStats = MptcpUnidirectionalStats {
musDirection :: ConnectionRole
, musApplicativeBytes :: Word64
, musMaxDsn :: Word64
, musMinDsn :: Word64
, musSubflowStats :: Map MptcpSubflow TcpSubflowUnidirectionalStats
} deriving Show
instance Monoid MptcpUnidirectionalStats where
mempty = MptcpUnidirectionalStats RoleServer 0 0 0 mempty
instance Semigroup MptcpUnidirectionalStats where
-- TODO fix
(<>) s1 s2 = assert (musDirection s1 == musDirection s2)
s1 {
musMaxDsn = max (musMaxDsn s1) (musMaxDsn s2)
, musMinDsn = min (musMinDsn s1) (musMinDsn s2)
-- , musApplicativeBytes = musApplicativeBytes s1 ++ musApplicativeBytes s2
}
-- ''' application data = goodput = useful bytes '''
-- ''' max(dsn)- min(dsn) - 1'''
-- mptcp_application_bytes: Byte
-- '''Total duration of the mptcp connection'''
-- mptcp_duration: datetime.timedelta
-- subflow_stats: List[TcpUnidirectionalStats]
-- @property
-- def mptcp_throughput_bytes(self) -> Byte:
-- ''' sum of total bytes transferred '''
-- return Byte(sum(map(lambda x: x.throughput_bytes, self.subflow_stats)))
-- |Goodput is defined as the amount of effective data exchanged over time
-- I.e., (maxDsn - minDsn) / (Mptcp communication Duration)
getMptcpGoodput :: MptcpUnidirectionalStats -> Throughput
getMptcpGoodput s = Throughput (Bytes $ musApplicativeBytes s) ((getMptcpStatsDuration s) ^. _1)
-- fromIntegral
-- | return max - min across subflows
getMptcpStatsDuration :: MptcpUnidirectionalStats -> (Duration, Timestamp, Timestamp)
getMptcpStatsDuration s = (diffTime end start, start, end)
where
start = Timestamp $ head $ sort starts
end = Timestamp $ last $ sort ends
-- min of
-- TODO get min
starts = map (tusStartTime . tssStats) (Map.elems $ musSubflowStats s)
-- take the maximum
ends = map (tusEndTime . tssStats) (Map.elems $ musSubflowStats s)
-- | TcpSubflowUnidirectionalStats
getSubflowStats ::
(TcpSeq F.∈ rs, F.RecVec rs, RelTime F.∈ rs, TcpLen F.∈ rs
, PacketId F.∈ rs
, IpSource ∈ rs, IpDest ∈ rs, TcpSrcPort ∈ rs, TcpDestPort ∈ rs, TcpStream ∈ rs
-- , TcpDest F.∈ rs
)
=> FrameFiltered MptcpSubflow (F.Record rs) -> ConnectionRole -> TcpSubflowUnidirectionalStats
getSubflowStats aframe role = TcpSubflowUnidirectionalStats {
tssStats = getTcpStatsFromAFrame (addTcpDestinationsToAFrame aframe') role
, tssMinDsn = 0
, tssMaxDsn = 0
}
where
aframe' = FrameTcp (sfConn $ ffCon aframe) (ffFrame aframe)
-- mptcp_compute_throughput est bourrin il calcule tout d'un coup, je veux avoir une version qui marche iterativement
-- | Generates Stats for one direction only
getMptcpStats ::
(
-- TcpDest F.∈ rs
MptcpDsn F.∈ rs, TcpSeq F.∈ rs, IpDest F.∈ rs, IpSource F.∈ rs
, TcpLen F.∈ rs
, PacketId F.∈ rs
, TcpDestPort F.∈ rs, MptcpRecvToken F.∈ rs
, TcpFlags F.∈ rs, TcpSrcPort F.∈ rs, TcpStream F.∈ rs, RelTime F.∈ rs
, rs F.⊆ HostCols
, F.RecVec rs
)
=> FrameFiltered MptcpConnection (F.Record rs)
-> ConnectionRole
-> MptcpUnidirectionalStats
getMptcpStats (FrameTcp mptcpConn frame) dest =
MptcpUnidirectionalStats {
musDirection = dest
, musApplicativeBytes = getSeqRange maxDsn minDsn
, musMaxDsn = maxDsn
, musMinDsn = minDsn
-- we need the stream id / FrameFiltered MptcpSubflow (Record rs)
, musSubflowStats = Map.fromList $ map (\sf -> (sf, getStats dest sf)) (toList $ _mpconSubflows mptcpConn)
}
where
-- buildTcpConnectionFromStreamId :: SomeFrame -> StreamId Tcp -> Either String (FrameFiltered TcpConnection Packet)
-- traverse a set
getStats role sf = let
sfFrame = fromRight (error "could not build sfFrame") (buildSubflowFromTcpStreamId frame (conTcpStreamId $ sfConn sf))
-- sfFrame' = addTcpDestinationsToAFrame sfFrame
in
getSubflowStats sfFrame role
-- frame = addTcpDestToFrame $ ffFrame aframe
-- these return Maybes
-- minSeq = minimum (F.toList $ view tcpSeq <$> frame)
-- maxSeq = maximum $ F.toList $ view tcpSeq <$> frame
maxTime = maximum $ F.toList $ view relTime <$> frame
minTime = minimum $ F.toList $ view relTime <$> frame
-- dsn_range, dsn_max, dsn_min = transmitted_seq_range(df, "dsn")
-- mbRecs = map recMaybe mergedRes
-- justRecs = catMaybes mbRecs
-- in
-- (toFrame justRecs, [])
dsns = catMaybes $ F.toList $ view mptcpDsn <$> frame
-- mergedPcapToFrame
maxDsn, minDsn :: Word64
maxDsn = maximum dsns
minDsn = minimum dsns
showMptcpUnidirectionalStats :: MptcpUnidirectionalStats -> Text
showMptcpUnidirectionalStats stats = T.unlines [
"Max/min dsn: " <> tshow (musMinDsn stats) <> "/" <> tshow (musMaxDsn stats) <> " towards " <> tshow (musDirection stats)
]