packages feed

ping-0.1.0.0: src/Network/Icmp/Marshal.hsc

{-# language DataKinds #-}

#include <netinet/ip_icmp.h>
#include "custom.h"

module Network.Icmp.Marshal
  ( pokeIcmpHeader
  , peekIcmpHeaderSequenceNumber
  , peekIcmpHeaderPayload
  , peekIcmpHeaderType
  , sizeOfIcmpHeader
  ) where

import Data.Word (Word32,Word16,Word8)
import GHC.Exts (RealWorld)

import Data.Primitive (MutableByteArray)
import Data.Primitive (readByteArray,writeByteArray)

-- The linux kernel (version 4.19.9) defines icmphdr as:
--
-- struct icmphdr {
--   __u8		type;
--   __u8		code;
--   __sum16	checksum;
--   union {
-- 	struct {
-- 		__be16	id;
-- 		__be16	sequence;
-- 	} echo;
-- 	__be32	gateway;
-- 	struct {
-- 		__be16	__unused;
-- 		__be16	mtu;
-- 	} frag;
-- 	__u8	reserved[4];
--   } un;
-- };

sizeOfIcmpHeader :: Int
sizeOfIcmpHeader = #{size struct icmphdr}

-- This sets the type to ICMP_ECHO and the sequence number to
-- user-specified values. The sequence number is supposed to be
-- in network byte order, but this does not actually matter.
-- If it is mangled on the way out, it will also be
-- mangled when we receive it.
--
-- Why is the identifier (un.echo.id) missing? Linux overwrites
-- the identifier regardless of what the user puts there. This
-- makes sense since the operating system wants to make it as
-- easy as possible to hand the reply to the right process.
pokeIcmpHeader ::
     MutableByteArray RealWorld
  -> Word16 -- sequence number
  -> Word32 -- payload, we use this as a bigger sequence number
  -> IO ()
pokeIcmpHeader ptr sequenceNumber payload = do
  #{write struct icmphdr, type} ptr (#{const ICMP_ECHO} :: Word8)
  #{write struct icmphdr, un.echo.sequence} ptr sequenceNumber
  writeByteArray ptr #{elementize sizeof (struct icmphdr), 4} payload

peekIcmpHeaderType :: MutableByteArray RealWorld -> IO Word8
peekIcmpHeaderType ptr = do
  #{read struct icmphdr, type} ptr

peekIcmpHeaderSequenceNumber :: MutableByteArray RealWorld -> IO Word16
peekIcmpHeaderSequenceNumber ptr = do
  #{read struct icmphdr, un.echo.sequence} ptr

peekIcmpHeaderPayload :: MutableByteArray RealWorld -> IO Word32
peekIcmpHeaderPayload ptr = do
  readByteArray ptr #{elementize sizeof (struct icmphdr), 4}