mptcpanalyzer-0.0.2.0: src/MptcpAnalyzer/Commands/Reinjections.hs
{-|
Module : MptcpAnalyzer.Commands.Reinjections
Description : Command to analyze reinjections
Maintainer : matt
-}
{-# LANGUAGE PackageImports #-}
module MptcpAnalyzer.Commands.Reinjections (
piListReinjections
, piQualifyReinjections
, qualifyReinjections
, cmdListReinjections
, cmdQualifyReinjections
)
where
import MptcpAnalyzer.ArtificialFields
import MptcpAnalyzer.Cache
import MptcpAnalyzer.Commands.Definitions as CMD
import MptcpAnalyzer.Commands.List as CMD
import MptcpAnalyzer.Loader
import MptcpAnalyzer.Merge
import MptcpAnalyzer.Pcap
import MptcpAnalyzer.Stream
import MptcpAnalyzer.Types
import Net.Mptcp
import Control.Lens ((^.))
import Control.Lens hiding (argument)
import Data.Either (lefts, rights)
import Data.Foldable (toList)
import qualified Data.Foldable as F
import Data.Function (on)
import Data.List (intercalate, intersperse, sortBy, sortOn)
import Data.Maybe
import Frames
import Frames.CSV
import Frames.Rec
import Options.Applicative
import qualified Pipes
import qualified Pipes.Prelude as Pipes
import Polysemy (Embed, Member, Members, Sem)
import qualified Polysemy as P
import Polysemy.Log (Log)
import qualified Polysemy.Log as Log
import Polysemy.State as P
import Polysemy.Trace as P
import Prelude hiding (log)
import Control.Monad
import qualified Debug.Trace as D
import Tshark.Main (defaultTsharkPrefs)
piListReinjections :: ParserInfo CommandArgs
piListReinjections = info (
parserListReinjections
<**> helper)
( progDesc "List MPTCP reinjections"
)
where
-- parserListReinjections :: Parser CommandArgs
parserListReinjections =
ArgsListReinjections <$>
-- strArgument (
-- metavar "PCAP1"
-- <> help "File to analyze"
-- )
-- <*>
argument readStreamId (
metavar "TCP_STREAM"
<> help "stream id to analyze"
)
piQualifyReinjections :: ParserInfo CommandArgs
piQualifyReinjections = info (
parserQualifyReinjections <**> helper)
( progDesc "Qualifies MPTCP reinjections"
<> footer "analyze examples/client_2_redundant.pcapng 0 examples/server_2_redundant.pcapng 0"
)
parserQualifyReinjections :: Parser CommandArgs
parserQualifyReinjections =
ArgsQualifyReinjections
<$> parserPcapMapping False
<*> switch (
long "verbose"
<> help "Verbose or not"
)
cmdListReinjections :: (Members '[Log, P.Trace, P.State MyState, Cache, Embed IO] r)
=> StreamId Mptcp
-> Sem r RetCode
cmdListReinjections streamId = do
state <- P.get
let loadedPcap = view loadedFile state
case loadedPcap of
Nothing -> do
trace "please load a pcap first"
return CMD.Continue
Just (frame :: FrameRec HostCols) -> do
let
reinjectedPacketsFrame = filterFrame (\x -> isJust $ x ^. reinjectionOf) frame
-- log $ "Number of rows " ++ show (frameLength frame)
outputs = fmap showReinjections reinjectedPacketsFrame
-- P.embed $ putStrLn $ "Number of MPTCP connections " ++ show (length mptcpStreams)
-- P.embed $ putStrLn $ show mptcpStreams
P.trace $ intercalate "\n" (toList outputs)
return CMD.Continue
where
-- packetid=757 (tcp.stream 1) is a reinjection of 1 packet(s):
-- - packet 256 (tcp.stream 7)
showReinjections row = "packetid=" ++ show (row ^. packetId) ++ " (tcp.stream " ++ show (row ^. tcpStream) ++ ")\n"
-- TODO map over the list
++ intercalate "\n" ( map showReinjection (fromJust $ row ^. reinjectionOf))
showReinjection reinjection = case toList $ filterFrame (\x -> x ^. packetId == reinjection) (frame) of
[] -> error "did not find original packet"
rows -> "- Reinjection of " ++ show reinjection ++ "(tcp.stream " ++ show ( (head rows) ^. tcpStream) ++ ")"
-- Analyzes row of reinject packets
-- Compares arrival time of the first send of a segment with the
analyzeReinjection :: FrameRec SenderReceiverCols -> Record SenderReceiverCols -> Double
analyzeReinjection mergedFrame row =
let
-- a list of packetIds
reinjectOf = fromJust (rgetField @SndReinjectionOf row)
initialPktId = D.traceShowId $ head reinjectOf
-- it is a frame
originalPkt :: Record SenderReceiverCols
originalPkt = let
originalFrame = filterFrame (\x -> x ^. sndPacketId == initialPktId) mergedFrame
in case frameLength originalFrame of
0 -> error "empty frame"
_ -> frameRow originalFrame 0
origArrival, reinjArrival :: Double
origArrival = rgetField @RcvRelTime originalPkt
reinjArrival = rgetField @RcvRelTime originalPkt
reinjPktId = row ^. sndPacketId
delta = reinjArrival - origArrival
in
delta
{- Tries to distinguish between useful and useless reinjections
Also tries to evalute the usefulness by providing a delta showing how much time
the reinjection made the connection win or lose
-}
cmdQualifyReinjections ::
Members '[
Log
, P.State MyState
, Cache
, P.Trace
, Embed IO
] r
=> PcapMapping Mptcp
-> [ConnectionRole]
-> Bool
-> Sem r RetCode
cmdQualifyReinjections (PcapMapping pcap1 streamId1 pcap2 streamId2) destinations verbose = do
eframe1 <- buildAFrameFromStreamIdMptcp defaultTsharkPrefs pcap1 streamId1
eframe2 <- buildAFrameFromStreamIdMptcp defaultTsharkPrefs pcap2 streamId2
res <- case (eframe1, eframe2 ) of
(Right aframe1, Right aframe2) -> do
mergedRes <- mergeMptcpConnectionsFromKnownStreams aframe1 aframe2
let
-- mergedRes = mergeMptcpConnectionsFromKnownStreams' aframe1 aframe2
reinjectedPacketsHost1 = filterFrame (\x -> isJust $ x ^. reinjectionOf) (ffFrame aframe1)
reinjectedPacketsHost2 = filterFrame (\x -> isJust $ x ^. reinjectionOf) (ffFrame aframe2)
-- mbRecs = map recMaybe mergedRes
-- packets that could be mapped in both pcaps
-- justRecs = catMaybes mbRecs
myFrame = convertToSenderReceiver mergedRes
reinjectedPacketsFrame = filterFrame (\x -> isJust $ x ^. sndReinjectionOf) myFrame
-- loop over these reinjectpackets
-- assume both were mapped
-- Log.info $ "Result of the analysis; reinjections:"
-- <> tshow (showReinjects justRecs)
-- Log.debug $ "reinjectionsOf in host1 frame " <> tshow $ showFrame myFrame
-- Log.debug $ "showing merged res" <> tshow (showMergedRes $ take 3 mergedRes)
-- P.embed $ writeMergedPcap ("mergedRes-" ++ ".csv") mergedRes
trace $ "Size after conversion to sender/receiver " ++ show (frameLength myFrame)
++ "( " ++ show (length mergedRes) ++ ")"
-- trace $ "Number of reinjected packets: " ++ show (frameLength reinjectedPacketsFrame)
-- trace $ "Merged mptcp connection\nFrame size: " ++ show (frameLength reinjectedPacketsFrame)
-- ++ "\n" ++ showFrame "," reinjectedPacketsFrame
forM_ destinations $ \x -> do
qualifyReinjections myFrame (assignRoles aframe1 aframe2) x
-- qualifyReinjections tempPath handle (getDests dest) (ffCon aframe1) mergedRes
return CMD.Continue
(Left err, _) -> return $ CMD.Error err
(_, Left err) -> return $ CMD.Error err
return CMD.Continue
-- mergedPcap
-- reinjectedPackets = filterFrame (sndReinjectionOf) (toFrame justRecs)
-- buildTcpConnectionFromSndRecord :: (
-- SndIpSource ∈ rs, SndIpDest ∈ rs, SndTcpSrcPort ∈ rs, SndTcpDestPort ∈ rs, SndTcpStream ∈ rs
-- -- rs ⊆ HostCols
-- )
-- => Record rs -> TcpConnection
-- buildTcpConnectionFromRecord r =
-- TcpConnection {
-- conTcpClientIp = r ^. sndIpSource
-- , conTcpServerIp = r ^. sndIpDest
-- , conTcpClientPort = r ^. sndTcpSrcPort
-- , conTcpServerPort = r ^. sndTcpDestPort
-- , conTcpStreamId = r ^. sndTcpStream
-- }
-- buildConnectionFromSndPacket :: Record SenderReceiverCols -> TcpConnection
-- buildConnectionFromSndPacket row ->
-- | Returns (Client,Server)
-- kinbda hackish
assignRoles :: FrameFiltered MptcpConnection Packet -> FrameFiltered MptcpConnection Packet
-> (FrameFiltered MptcpConnection Packet , FrameFiltered MptcpConnection Packet)
assignRoles aframe1 aframe2 =
if delta > 0 then
(aframe1, aframe2)
else
(aframe2, aframe1)
where
-- assume non empty
firstRow1 = frameRow (ffFrame aframe1) 0
firstRow2 = frameRow (ffFrame aframe2) 0
delta :: Double
delta = (firstRow2 ^. absTime) - (firstRow1 ^. absTime)
-- selectDest :: ConnectionRole -> FrameRec MergedHostCols
-- selectDest dest = (filterFrame (\x -> x ^. senderDest == dest) jframe)
-- TODO there should be a classification on a per mptcp basis
-- Here we should be able to tell who is the sender
qualifyReinjections :: Members '[
Log, P.State MyState
, Cache
, P.Trace
, Embed IO
] r
=> FrameRec SenderReceiverCols
-- (Client, server) pcaps
-> (FrameFiltered MptcpConnection Packet,FrameFiltered MptcpConnection Packet)
-> ConnectionRole
-> Sem r ()
qualifyReinjections frame (aframeH1, aframeH2) dest = do
let
-- "dest"frame
dstFrame = filterFrame (\x -> x ^. senderDest == dest) frame
-- mergedRes = mergeMptcpConnectionsFromKnownStreams' aframe1 aframe2
-- reinjectedPacketsHost1 = filterFrame (\x -> isJust $ x ^. reinjectionOf) (ffFrame aframe1)
-- reinjectedPacketsHost2 = filterFrame (\x -> isJust $ x ^. reinjectionOf) (ffFrame aframe2)
reinjectedPacketsFrame = filterFrame (\x -> isJust $ x ^. sndReinjectionOf) dstFrame
reinjects = fmap (analyzeReinjection frame) reinjectedPacketsFrame
trace $ "Qualify reinjections for dests " ++ show dest
P.embed $ writeDSV defaultParserOptions ("sndrcv-merged-" ++ show dest ++ ".csv") dstFrame
trace $ "Number of reinjected packets: " ++ show (frameLength reinjectedPacketsFrame)
-- trace $ "Result of the analysis; reinjections:" ++ showReinjects reinjects
forM_ reinjectedPacketsFrame $ \row -> do
let
reinjectOf = fromJust (rgetField @SndReinjectionOf row)
hostType = rgetField @SenderHost row
senderDestVal = rgetField @SenderDest row
-- originalFrame = if senderDest == RoleClient then (ffFrame aframeH2) else (ffFrame aframeH1)
originalFrame = frame
-- should be only one
originalPackets = filterFrame (\x -> x ^. sndPacketId == initialPktId) originalFrame
-- ((frameRow originalPacket 0) ^. senderHost)
hostBool = if frameLength originalPackets > 0 then show hostType else "unknown"
-- TODO we want to find
-- buildTcpConnectionFromSndRecord
initialPktId = D.traceShowId $ head reinjectOf
-- initialPktId = D.traceShowId $ head reinjectOf
-- of packet id " ++ show initialPktId
-- from host" ++ show hostType
trace $ show (row ^. sndPacketId) ++ " is a reinjection of packet id " ++ show initialPktId
trace $ "number of original packets " ++ show (frameLength originalPackets) ++ " Host " ++ show senderDestVal
trace $ describeReinjection row originalPackets
-- TODO check if pktId is available
where
showReinjects frame2 =
-- unlines (intercalate sep (columnHeaders (Proxy :: Proxy (Record rs))) : rows)
intercalate "," rows
where
rows = Pipes.toList (F.mapM_ (Pipes.yield . show ) frame2)
describeReinjection reinjectedPacket originalPackets = case frameLength originalPackets of
0 -> "No original packets found FISHY ?!"
_otherwise -> let
originalPacket = frameRow originalPackets 0
reinjArrivalTime = reinjectedPacket ^. rcvAbsTime
originalArrivalTime = originalPacket ^. rcvAbsTime
reinj_delta = reinjArrivalTime - originalArrivalTime
in
if reinj_delta < 0 then
"Efficient reinjection: latency gain: " ++ show reinj_delta
else
"Redundant reinjection" ++ show reinj_delta
--
-- # print("GOT A failed reinjection")
-- df_all.loc[df_all[_sender("packetid")] == reinjection.packetid, "redundant"] = True
-- #TODO set reinj_delta for reinjection.packetid
-- else:
-- # print("GOT a successful reinjection")
-- pass