rws-0.1.1.2: src/L2tp.hs
{-# LANGUAGE CPP #-}
module L2tp where
import Control.Monad
import Data.Binary.Put
import Data.Bits
import Text.ParserCombinators.Parsec
import qualified Data.ByteString.Lazy as B
import Text.ParserCombinators.Parsec.Perm
import Util
import Packet
import Lexer
#ifdef HRWS_TEST
import Debug.Trace
import Test.QuickCheck hiding ((.&.))
import Test.Framework (Test)
import Test.Framework.Providers.QuickCheck2 (testProperty)
import Text.Printf
#endif
parseL2tpPkt :: Parser Packet -> Parser L2tpPkt
parseL2tpPkt f = permute
(tuple <$?> (0x0002, parseIntAttribute "flags")
<|?> (0, parseIntAttribute "length")
<|?> (0x1234, parseIntAttribute "tid")
<|?> (0x5678, parseIntAttribute "sid")
<|?> (0, try (parseIntAttribute "ns"))
<|?> (0, try (parseIntAttribute "nr"))
<|?> (0, parseIntAttribute "offset")
<|?> (True, parseBoolAttribute "addoffset")
<|?> ([PPayload defaultPayload], parsePayload f))
where
tuple fl l tid sid ns nr offset ao =
L2tpPkt (L2tp fl l tid sid ns nr offset ao)
l2tpDecl :: Parser Packet -> Parser Packet
l2tpDecl f = do
symbol "l2tp"
g <- parseL2tpPkt f
return (PL2tp g)
l2tpWriteHdr :: L2tp -> Put
l2tpWriteHdr p = do
putWord16be (l2tpFlags p)
-- Optional Length Field
when (l2tpFlags p .&. 0x4000 == 0x4000) $
putWord16be $ l2tpLength p
putWord16be (l2tpTid p)
putWord16be (l2tpSid p)
-- Optional Ns and Nr Field
when (l2tpFlags p .&. 0x0800 == 0x0800) $
do
putWord16be (l2tpNs p)
putWord16be (l2tpNr p)
-- Optional Offset Size Field XXX add actual offset
when (l2tpFlags p .&. 0x0200 == 0x0200) $
do
putWord16be (l2tpOffset p)
when (l2tpCorrectOffset p) $
putLazyByteString $ B.replicate (fromIntegral (l2tpOffset p)) 0x0
l2tpWrite :: L2tp -> Maybe Packet -> B.ByteString -> Put
l2tpWrite h _ bs = do
l2tpWriteHdr h {l2tpLength = fromIntegral $ B.length bs}
putLazyByteString bs
instance PacketWriteable L2tpPkt where packetWrite p = l2tpWrite (l2tpPktHeader p)
#ifdef HRWS_TEST
{- Unit Tests -}
instance Arbitrary L2tp where
arbitrary = do
f <- arbitrary
l <- arbitrary
t <- arbitrary
s <- arbitrary
ns <- arbitrary
nr <- arbitrary
o <- arbitrary
return (L2tp f l t s ns nr o True)
testValidParse :: String -> (L2tp -> Bool) -> Bool
testValidParse str fn =
case parse (dummyParsePacket l2tpDecl) "packet parse" str of
Left err -> trace (show err) False
Right val -> fn $ testL2tpPkt val
testL2tpDefault :: () -> Bool
testL2tpDefault _ = let cmp f = defaultL2tp == f in
testValidParse "(l2tp)" cmp
testL2tpPkt :: Packet -> L2tp
testL2tpPkt (PL2tp f) = l2tpPktHeader f
testL2tpPkt _ = error "Unexpected packet type"
testL2tpPacket :: L2tp -> Bool
testL2tpPacket pkt =
let cmp p = pkt == p in
testValidParse (printf "(l2tp flags=%d length=%d tid=%d sid=%d ns=%d nr=%d offset=%d)"
(l2tpFlags pkt) (l2tpLength pkt) (l2tpTid pkt) (l2tpSid pkt)
(l2tpNs pkt) (l2tpNr pkt) (l2tpOffset pkt)) cmp
testL2tpWrite :: () -> Bool
testL2tpWrite _ =
let expPkt = B.pack [0x00, 0x02, 0xb0, 0xb1, 0xc1, 0xc2] in
let cmp p = runPut (l2tpWrite p Nothing B.empty) == expPkt in
testValidParse "(l2tp flags=0x2 tid=0xb0b1 sid=0xc1c2)" cmp
l2tpTests :: [Test]
l2tpTests = [
testProperty "L2TP: Default Packet" testL2tpDefault,
testProperty "L2TP: Packet" testL2tpPacket,
testProperty "L2TP: Write" testL2tpWrite
]
#endif