module Hans.Layer.Icmp4 (
Icmp4Handle
, runIcmp4Layer
, addIcmp4Handler
, destUnreachable
) where
import Hans.Address.IP4 (IP4,broadcastIP4)
import Hans.Channel
import Hans.Layer
import Hans.Message.Icmp4
import Hans.Message.Ip4
import Hans.Utils
import qualified Hans.Layer.IP4 as IP4
import Control.Concurrent (forkIO)
import Data.Serialize (runPut,putByteString)
import MonadLib (get,set)
import qualified Data.ByteString as S
type Handler = Icmp4Packet -> IO ()
type Icmp4Handle = Channel (Icmp4 ())
icmpProtocol :: IP4Protocol
icmpProtocol = IP4Protocol 0x1
runIcmp4Layer :: Icmp4Handle -> IP4.IP4Handle -> IO ()
runIcmp4Layer h ip4 = do
let handles = Icmp4Handles ip4 []
IP4.addIP4Handler ip4 icmpProtocol
$ \ hdr bs -> send h (handleIncoming hdr bs)
void (forkIO (loopLayer "icmp4" handles (receive h) id))
data Icmp4Handles = Icmp4Handles
{ icmpIp4 :: IP4.IP4Handle
, icmpHandlers :: [Handler]
}
type Icmp4 = Layer Icmp4Handles
ip4Handle :: Icmp4 IP4.IP4Handle
ip4Handle = icmpIp4 `fmap` get
addIcmp4Handler :: Icmp4Handle -> Handler -> IO ()
addIcmp4Handler h k = send h (handleAdd k)
destUnreachable :: Icmp4Handle -> DestinationUnreachableCode
-> IP4Header -> Int -> S.ByteString -> IO ()
destUnreachable h code hdr len body
| ip4DestAddr hdr == broadcastIP4 = return ()
| otherwise = send h $ do
let bytes = runPut $ do
putIP4Header hdr len
putByteString (S.take 8 body)
sendPacket True (ip4SourceAddr hdr) (DestinationUnreachable code bytes)
sendPacket :: Bool -> IP4 -> Icmp4Packet -> Icmp4 ()
sendPacket df dst pkt = do
ip4 <- ip4Handle
let hdr = emptyIP4Header
{ ip4DestAddr = dst
, ip4Protocol = icmpProtocol
, ip4DontFragment = df
}
output $ IP4.sendIP4Packet ip4 hdr
$ renderIcmp4Packet pkt
handleIncoming :: IP4Header -> S.ByteString -> Icmp4 ()
handleIncoming hdr bs = do
pkt <- liftRight (parseIcmp4Packet bs)
matchHandlers pkt
case pkt of
Echo ident seqNum dat -> handleEchoRequest hdr ident seqNum dat
_ty -> dropPacket
handleAdd :: Handler -> Icmp4 ()
handleAdd k = do
s <- get
set s { icmpHandlers = k : icmpHandlers s }
handleEchoRequest :: IP4Header -> Identifier -> SequenceNumber -> S.ByteString
-> Icmp4 ()
handleEchoRequest hdr ident seqNum dat =
sendPacket (ip4DontFragment hdr) (ip4SourceAddr hdr)
(EchoReply ident seqNum dat)
matchHandlers :: Icmp4Packet -> Icmp4 ()
matchHandlers pkt = do
s <- get
output (mapM_ ($ pkt) (icmpHandlers s))