packages feed

hans-2.1.0.0: src/Hans/Layer/Icmp4.hs

{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE TypeSynonymInstances  #-}
{-# LANGUAGE FlexibleInstances     #-}

module Hans.Layer.Icmp4 (
    Icmp4Handle
  , runIcmp4Layer
  , addIcmp4Handler
  ) where

import Hans.Address.IP4
import Hans.Channel
import Hans.Layer
import Hans.Layer.IP4
import Hans.Message.Icmp4
import Hans.Message.Ip4
import Hans.Utils

import Control.Concurrent (forkIO)
import Data.Serialize (decode,encode)
import MonadLib (get,set)

type Handler = Icmp4Packet -> IO ()

type Icmp4Handle = Channel (Icmp4 ())

icmpProtocol :: IP4Protocol
icmpProtocol  = IP4Protocol 0x1

runIcmp4Layer :: Icmp4Handle -> IP4Handle -> IO ()
runIcmp4Layer h ip4 = do
  let handles = Icmp4Handles ip4 []
  addIP4Handler ip4 icmpProtocol
    $ \src dst bs -> send h (handleIncoming src dst bs)
  void (forkIO (loopLayer handles (receive h) id))

data Icmp4Handles = Icmp4Handles
  { icmpIp4      :: IP4Handle
  , icmpHandlers :: [Handler]
  }

type Icmp4 = Layer Icmp4Handles

ip4Handle :: Icmp4 IP4Handle
ip4Handle  = icmpIp4 `fmap` get

sendPacket :: IP4 -> Icmp4Packet -> Icmp4 ()
sendPacket dst pkt = do
  ip4 <- ip4Handle
  output $ sendIP4Packet ip4 icmpProtocol dst (encode pkt)

-- | Add a handler for Icmp4 messages that match the provided predicate.
addIcmp4Handler :: Icmp4Handle -> Handler -> IO ()
addIcmp4Handler h k = send h (handleAdd k)

-- Message Handling ------------------------------------------------------------

-- | Handle incoming ICMP packets
handleIncoming :: IP4 -> IP4 -> Packet -> Icmp4 ()
handleIncoming src _dst bs = do
  pkt <- liftRight (decode bs)
  matchHandlers pkt
  case pkt of
    -- XXX: Only echo-request is handled at the moment
    Echo ident seqNum dat -> handleEchoRequest src ident seqNum dat
    _ty         -> do
      --output (putStrLn ("Unhandled ICMP message type: " ++ show ty))
      dropPacket


-- | Add an icmp packet handler.
handleAdd :: Handler -> Icmp4 ()
handleAdd k = do
  s <- get
  set s { icmpHandlers = k : icmpHandlers s }


-- | Respond to an echo request
handleEchoRequest :: IP4 -> Identifier -> SequenceNumber -> Packet -> Icmp4 ()
handleEchoRequest src ident seqNum dat = do
  sendPacket src (EchoReply ident seqNum dat)


-- | Output the IO actions for each handler that's registered.
matchHandlers :: Icmp4Packet -> Icmp4 ()
matchHandlers pkt = do
  s <- get
  output (mapM_ ($ pkt) (icmpHandlers s))