packages feed

rws-0.1.1.2: src/IPv6.hs

{-# LANGUAGE CPP #-}
module IPv6 where

import Data.Binary.Put
import Data.Bits
import qualified Data.ByteString.Lazy as B
import Data.Word
import Text.ParserCombinators.Parsec
import Text.ParserCombinators.Parsec.Perm

import Lexer
import Packet
import Util

#ifdef HRWS_TEST
import Debug.Trace
import Test.QuickCheck hiding ((.&.))
import Test.Framework (Test)
import Test.Framework.Providers.QuickCheck2 (testProperty)
#endif

parseIPv6Ver :: Parser Word8 
parseIPv6Ver = parseIntAttribute "ver"

parseIPv6Flow :: Parser Word32
parseIPv6Flow = parseIntAttribute "flow"

parseIPv6Tcl :: Parser Word8
parseIPv6Tcl = parseIntAttribute "tcl"

parseIPv6Len :: Parser Word16
parseIPv6Len = parseIntAttribute "len"

parseIPv6Hl :: Parser Word8
parseIPv6Hl = parseIntAttribute "hl"

parseIPv6Nh :: Parser Word8
parseIPv6Nh = parseIntAttribute "nh"

ipv6AddressDecl :: Parser IPv6Addr 
ipv6AddressDecl = do
    a0 <- integer
    char ':'
    a1 <- integer
    char ':'
    a2 <- integer
    char ':'
    a3 <- integer
    char ':'
    a4 <- integer
    char ':'
    a5 <- integer
    char ':'
    a6 <- integer
    char ':'
    a7 <- integer

    let fix a b = fromIntegral a `shiftL` 16 .|. b

    return (IPv6Addr (fromIntegral (fix a0 a1))
                     (fromIntegral (fix a2 a3))
                     (fromIntegral (fix a4 a5))
                     (fromIntegral (fix a6 a7)))

parseIPv6Address :: String -> Parser IPv6Addr
parseIPv6Address s = do
  symbol s
  symbol "="
  a <- ipv6AddressDecl
  whiteSpace
  return a

parseIPv6Pkt :: Parser Packet -> Parser IPv6Pkt
parseIPv6Pkt f = permute
  (tuple <$?> (IPv6Addr 0x2001000 0 0 1, parseIPv6Address "src")
         <|?> (IPv6Addr 0x2001000 0 0 2, parseIPv6Address "dst")
         <|?> (6, parseIPv6Ver)
         <|?> (0, try parseIPv6Tcl)
         <|?> (0, try parseIPv6Flow)
         <|?> (0, parseIPv6Len)
         <|?> (17, parseIPv6Nh)
         <|?> (64, parseIPv6Hl)
         <|?> ([PPayload defaultPayload], parsePayload f))
  where
    tuple s d v tcl fl l nh hl =
      IPv6Pkt (IPv6 v tcl fl l nh hl s d)

ipv6Decl :: Parser Packet -> Parser Packet
ipv6Decl f = do
 symbol "ipv6"
 p <- parseIPv6Pkt f
 return (PIPv6 p)

ipv6AddrWrite :: IPv6Addr -> Put
ipv6AddrWrite a = do
  putWord32be $ ipv6Addr0 a
  putWord32be $ ipv6Addr1 a
  putWord32be $ ipv6Addr2 a
  putWord32be $ ipv6Addr3 a

ipv6WriteHdr :: IPv6 -> Put
ipv6WriteHdr p = do
    -- pl_uint8_t vertcl
    putWord8 $ ipv6Ver p `shiftL` 4 .|. ipv6Tcl p `shiftR` 4
    -- pl_uint8_t tclflow XXX byte order of ipv6Flow!!!
    putWord8 $ ipv6Tcl p `shiftL` 4 .|. fromIntegral (ipv6Flow p `shiftR` 16)
    -- pl_uint16_t flow_id
    putWord16be $ fromIntegral $ ipv6Flow p .&. 0xffff
    putWord16be $ ipv6Length p
    putWord8 $ ipv6Nh p
    putWord8 $ ipv6Hl p
    ipv6AddrWrite $ ipv6Src p 
    ipv6AddrWrite $ ipv6Dst p

ipv6Write :: IPv6 -> Maybe Packet -> B.ByteString -> Put
ipv6Write h _ bs = do
  ipv6WriteHdr h {ipv6Length = fromIntegral $ B.length bs}
  putLazyByteString bs

pseudoIpv6Write :: IPv6 -> Word16 -> Put
pseudoIpv6Write p l = do
    ipv6AddrWrite $ ipv6Src p 
    ipv6AddrWrite $ ipv6Dst p
    putWord16be l
    putWord8 0
    putWord8 0
    putWord8 0
    putWord8 $ ipv6Nh p

instance PacketWriteable IPv6Pkt where packetWrite p = ipv6Write (ipv6PktHeader p)

#ifdef HRWS_TEST
{- Unit Tests -}

instance Arbitrary IPv6Addr where
  arbitrary = do
    a0 <- arbitrary
    a1 <- arbitrary
    a2 <- arbitrary
    a3 <- arbitrary
    return (IPv6Addr a0 a1 a2 a3)

instance Arbitrary IPv6 where
  arbitrary = do
    v    <- arbitrary
    tcl  <- arbitrary
    flow <- arbitrary
    len  <- arbitrary
    nh   <- arbitrary
    hl   <- arbitrary
    src  <- arbitrary
    dst  <- arbitrary
    return (IPv6 v tcl flow len nh hl src dst)

testValidParse :: String -> (IPv6 -> Bool) -> Bool
testValidParse str fn =
  case parse (dummyParsePacket ipv6Decl) "packet parse" str of
       Left  err -> trace (show err) False
       Right val -> fn $ testIPv6Pkt val

testIPv6Pkt :: Packet -> IPv6
testIPv6Pkt (PIPv6 p) = ipv6PktHeader p
testIPv6Pkt _ = error "Unexpected packet type"

testIPv6Default :: () -> Bool
testIPv6Default _ = let cmp f = defaultIPv6 == f in
  testValidParse "(ipv6)" cmp

ipv6ToPktStr :: IPv6 -> String
ipv6ToPktStr ip = "ipv6 ver=" ++ show (ipv6Ver ip) ++ " tcl=" ++ show (ipv6Tcl ip) ++
                  " flow=" ++ show (ipv6Flow ip) ++ " len=" ++ show (ipv6Length ip) ++
                  " nh=" ++ show (ipv6Nh ip) ++ " hl=" ++ show (ipv6Hl ip) ++
                  " src=" ++ show (ipv6Src ip) ++ " dst=" ++ show (ipv6Dst ip)


testIPv6Pkt0 :: IPv6 -> Bool
testIPv6Pkt0 ip = let cmp p = ip == p in
  testValidParse ("(" ++ ipv6ToPktStr ip ++ ")") cmp

testIPv6InIPv6 :: IPv6 -> Bool
testIPv6InIPv6 inner =
  let outer = inner {ipv6Nh = 41} in
  let cmp p = p == outer in
  testValidParse ("(" ++ ipv6ToPktStr outer ++ "(" ++ ipv6ToPktStr inner ++ "))") cmp
  

testIPv6Write :: () -> Bool
testIPv6Write _ =
  let expPkt = B.pack [0x60, 0xf0, 0xbe, 0xef, 0x00, 0x00, 0x04, 0x40,
                       0x07, 0xd1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
                       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
                       0x07, 0xd1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
                       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02] in
   let cmp p = runPut (ipv6Write p Nothing B.empty) == expPkt in
   testValidParse "(ipv6 ver=6 tcl=0xf flow=0xbeef len=0 hl=64 src=2001:0:0:0:0:0:0:1 dst=2001:0:0:0:0:0:0:2 nh=4)" cmp

testIPv6PseudoWrite :: () -> Bool
testIPv6PseudoWrite _ =
  let expPkt = B.pack [0x07, 0xd1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
                       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
                       0x07, 0xd1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
                       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02,
                       0x00, 0x54, 0x00, 0x00, 0x00, 0x04] in
   let cmp p = runPut (pseudoIpv6Write p 84) == expPkt in
   testValidParse "(ipv6 ver=6 tcl=0xf flow=0xbeef len=84 hl=64 src=2001:0:0:0:0:0:0:1 dst=2001:0:0:0:0:0:0:2 nh=4)" cmp

  


ipv6Tests :: [Test]
ipv6Tests = [
  testProperty "IPv6: Default" testIPv6Default,
  testProperty "IPv6 Packet 0" testIPv6Pkt0,
  testProperty "IPv6 in IPv6" testIPv6InIPv6,
  testProperty "IPv6 Write" testIPv6Write,
  testProperty "IPv6 Pesudo Write" testIPv6PseudoWrite
  ]

#endif