packages feed

mptcpanalyzer-0.0.2.0: src/Tshark/Main.hs

{-|
Module      : Tshark.Main
Description : Interface between wireshark output format and haskell
Maintainer  : matt


WIRESHARK_CONFIG_DIR'

-}
module Tshark.Main (
  TsharkParams(csvDelimiter, tsharkReadFilter)
  , generateCsvCommand
  , defaultTsharkPrefs
  , defaultTsharkOptions
  , genReadFilterFromTcpConnection
  , genReadFilterMptcpFromMptcpConnection
)
where

import Data.List (intercalate)
import qualified Data.Text as T
import MptcpAnalyzer.ArtificialFields (ConnectionRole(RoleClient, RoleServer))
import qualified Net.IP
import Net.Tcp (TcpConnection(..))
import System.Process

-- http://acowley.github.io/Frames/#orgf328b25
defaultTsharkOptions :: [(String, String)]
defaultTsharkOptions = [
      -- TODO join these
      ("gui.column.format", intercalate "," [ "Time","%At","ipsrc","%s","ipdst","%d"]),
      -- "tcp.relative_sequence_numbers": True if tcp_relative_seq else False,
      ("tcp.analyze_sequence_numbers", "true"),
      ("mptcp.analyze_mappings", "true"),
      ("mptcp.relative_sequence_numbers", "true"),
      ("mptcp.intersubflows_retransmission", "true"),
      -- # Disable DSS checks which consume quite a lot
      ("mptcp.analyze_mptcp", "true")
      ]


defaultTsharkPrefs :: TsharkParams
defaultTsharkPrefs = TsharkParams {
      tsharkBinary = "tshark",
      tsharkOptions = defaultTsharkOptions
      , csvDelimiter = '|'
      , tsharkReadFilter = Just "mptcp or tcp and not icmp"
      , tsharkProfile = Nothing
    }

showIP :: Net.IP.IP -> String
showIP = T.unpack . Net.IP.encode


-- |One way filter
-- genReadFilterUnidirectional ::
-- genReadFilterUnidirectional =

-- genReadFilterBidirectional ::
-- genReadFilterBidirectional =

-- |Create a tshark read filter from a 'TcpConnection'
genReadFilterFromTcpConnection ::
  TcpConnection
  -> Maybe ConnectionRole   -- ^If we care about direction (ignored for now)
  -> String
genReadFilterFromTcpConnection con dest =
  case dest of
    Just RoleClient ->
      -- TODO should depend on destination
      "tcp and ip.src==" ++ (showIP . conTcpClientIp) con ++ " and ip.dst==" ++ (showIP . conTcpServerIp) con
        ++ " and tcp.srcport==" ++ show (conTcpClientPort con) ++ " and tcp.dstport==" ++ show (conTcpServerPort con)
    Just RoleServer ->
      "tcp and ip.src==" ++ (showIP . conTcpServerIp) con ++ " and ip.dst==" ++ (showIP . conTcpClientIp) con
        ++ " and tcp.srcport==" ++ show (conTcpServerPort con) ++ " and tcp.dstport==" ++ show (conTcpClientPort con)

        -- error "not implemented"
    -- TODO 2 requretes srcport dstport puis alterne
    _nodestination -> "tcp and ip.addr==" ++ (showIP . conTcpClientIp) con ++ " and ip.addr==" ++ (showIP . conTcpServerIp) con
        ++ " and tcp.port==" ++ show (conTcpServerPort con) ++ " and tcp.port==" ++ show (conTcpClientPort con)

-- |Create a tshark read filter from a 'MptcpConnection'
-- genReadFilterFromMptcpConnection :: MptcpConnection -> String
-- genReadFilterFromMptcpConnection con =


-- we cant update the filter on the fly so we need to look at all the mptcp traffic
-- and match on master subflow
genReadFilterMptcpFromMptcpConnection :: String
genReadFilterMptcpFromMptcpConnection = "mptcp"



-- | Basic wireshark settings to enforce reproducible results across users
data TsharkParams = TsharkParams {
      tsharkBinary     :: String,
      -- |(Name, Value) of tshark options, see 'defaultTsharkOptions'
      tsharkOptions    :: [(String, String)],
      -- | Flags to add on the command line
      -- tsharkFlags     :: [String],
      -- | How to separate the different fields
      csvDelimiter     :: Char
      -- | for instance "mptcp" or "tcp"
      , tsharkReadFilter :: Maybe String

      -- | Path towards the tshark profile to use (passed as `tshark -C ...`)
      , tsharkProfile :: Maybe FilePath
    }


-- |Generate the tshark command to export a pcap into a csv
generateCsvCommand :: [T.Text] -- ^Fields to exports e.g., "mptcp.stream"
          -> Either String FilePath    -- ^ (interface, path towards the pcap file)
          -> TsharkParams
          -> CmdSpec
generateCsvCommand fieldNames source tsharkParams =
    RawCommand (tsharkBinary tsharkParams ) args
    where
    -- for some reasons, -Y does not work so I use -2 -R instead
    -- quote=d|s|n Set the quote character to use to surround fields.  d uses double-quotes, s
    -- single-quotes, n no quotes (the default).
    -- the -2 is important, else some mptcp parameters are not exported
        start = [
            "-E", "separator=" ++ [csvDelimiter tsharkParams]
          ] ++ (case source of
              Right pcapFilename -> ["-r", pcapFilename]
              Left ifname  -> ["-i", ifname])


        args :: [String]
        args = (start ++ opts ++ readFilter ) ++ map T.unpack  fields

        opts :: [String]
        opts = foldr (\(opt, val) l -> l ++ ["-o", opt ++ ":" ++ val]) [] (tsharkOptions tsharkParams)

        readFilter :: [String]
        readFilter = case tsharkReadFilter tsharkParams of
            Just x  -> (case source of
              Right pcapFilename -> ["-2", "-R"]
              Left ifname  -> ["-Y"]) ++ [x]
            Nothing -> []

        fields :: [T.Text]
        fields = ["-T", "fields"]
            ++ Prelude.foldr (\fieldName l -> ["-e", fieldName] ++ l) [] fieldNames