現在、一種のイーサネット パケット処理ライブラリを開発しています。基本的な考え方は、パケットにはネットワーク インターフェイスと pcap ダンプ ファイルの 2 つの異なるソースがあるということです。パケットはフローごとにグループ化する必要があり、フローはフィルタリングする必要があり、UDP フローはある方法で処理し、TCP は別の方法で処理する必要があります。コンジットなしでバージョンを開発しましたが、現在、重複するコードが多すぎることがわかりました。私は抽象化しようとしていますが、コンジットに似たものを発明しています。それで、コンジットに切り替えようとしましたが、行き詰まりました。
したがって、写真は次のようになります。
[UDP processing]
[pcap source] | / \
|---[flow map]-->* *->[dump to many files]
| \ /
[iface source] | [TCP processing]
最初の問題はフロー マップです。フローを蓄積する必要があり、フローにあるしきい値よりも多くのパケットがある場合は、それを処理に渡します。
2 つ目の問題は、UDP と TCP の処理に別のパイプを使用したいので、パイプを何らかの方法で分割する必要があることです。
そして、このすべてのものをマルチスレッド化する必要があるという別の問題があるため、プロデューサーとコンシューマーは異なるスレッドにある必要があります。
では、この図では、コンジットに関して何をすべきでしょうか?
ソースはソースです、それは明らかです。しかし、フロー マップはどうあるべきでしょうか。さらに処理するためのソースを生成するシンク? フローの数は膨大であるため、さらに処理する前にすべてのパケットをメモリに蓄積することは避けなければなりません。
何か案は?繰り返しになりますが、これらすべてをコンジットなしで行う方法は非常に明確なので、コンジットを使用して適切に設計する方法が問題になります。
アップデート。
data FlowFrame = FlowFrame { flowKey :: !F.FlowKey
, flowFrame :: [Packet]
}
data FlowState
flowFrames :: MonadIO m => Conduit Packet m FlowFrame
flowFrames = awaitForever $ \p -> do
let (Right (l3, _)) = runGet F.readL3Headers (pktData p)
let fk = F.flowKey l3
yield (FlowFrame fk [p])
sinkPrintFlow :: MonadIO m => Consumer FlowFrame m ()
sinkPrintFlow = awaitForever $ liftIO.putStrLn.show.pPrint.flowKey
isA :: F.Protocol -> FlowFrame -> Bool
isA p frame =
case ((flowKey frame)) of
F.FlowKey p _ _ -> True
_ -> False
processUDP :: MonadIO m => Conduit FlowFrame m FlowFrame
processUDP = CL.filter (isA F.PROTO_UDP)
processTCP :: MonadIO m => Conduit FlowFrame m FlowFrame
processTCP = CL.filter (isA F.PROTO_TCP)
main = do
(file:_) <- getArgs
input <- openOffline file
sourcePcap input
$$ flowFrames =$= void (sequenceConduits [processUDP, processTCP])
$= sinkPrintFlow
putStrLn "done"