module Net.Ethernet where

-- Ethernet protocol
-- reference: http://www.techfest.com/networking/lan/ethernet.htm
--            http://www.iana.org/assignments/ethernet-numbers

import Data.List(intersperse)
import Net.Bits
import Net.Packet
import Net.PacketParsing as P
import qualified Net.Interface as Net
import Net.Utils(Container(..))

-- Ethernet card drivers should provide the following interface:
data Interface m i o =
  Interface { myMAC :: Addr,
	      io    :: Net.Interface m i o }

rx = Net.rx . io
tx = Net.tx . io

data Addr           = Addr !Word8 !Word8 !Word8 !Word8 !Word8 !Word8
                      deriving (Eq,Ord,Bounded)

broadcastAddr = maxBound :: Addr
--zeroAddr = minBound :: Addr

instance Parse Addr where
  parse = Addr # parse <# parse <# parse <# parse <# parse <# parse

instance Unparse Addr where
  unparse (Addr b1 b2 b3 b4 b5 b6) = P.unparse ((b1,b2,b3),(b4,b5,b6))

instance Show Addr where
  show (Addr x1 x2 x3 x4 x5 x6)  =
      concat $ intersperse ":" $ map (showHex' 2) [x1,x2,x3,x4,x5,x6]

-- We omit the preambe, startframe delimeter & frame check sequence as
-- we assume that they will be dealt with by the hardware.
data Packet content = Packet
                    { dest      :: Addr
                    , source    :: Addr
                    , packType  :: PacketType
                    , content   :: content
                    }
                    deriving Show

instance Functor   Packet where fmap f p = p { content = f (content p) }
instance Container Packet where contents = content

data PacketType     = Ethernet !Int
                    | IPv4
                    | IPv6
                    | ARP
                    | Unknown !Word16
                    deriving (Eq,Show)

instance Enum PacketType where
  toEnum x
    | x < 0x600     = Ethernet x
  toEnum x          = case x of
                        0x0800  -> IPv4
                        0x86DD  -> IPv6
                        0x0806  -> ARP
                        _       -> Unknown (fromIntegral x)

  fromEnum x        = case x of
                        Ethernet x  -> x
                        IPv4        -> 0x0800
                        IPv6        -> 0x86DD
                        ARP         -> 0x0806
                        Unknown x   -> fromIntegral x

instance Parse PacketType where parse = toEnum . fromIntegral # word16


instance Parse content => Parse (Packet content) where
  parse = Packet # parse <# parse <# parse <# parse

{-
parse              :: InPacket -> Packet InPacket
parse p             = let ty          = toEnum (fromIntegral (p `wordAt` 12))
                      in Packet
                          { dest      = Addr d1 d2 d3 d4 d5 d6
                          , source    = Addr s1 s2 s3 s4 s5 s6
                          , packType  = ty
                          , content   = case ty of
                                          Ethernet n  -> p { from = 14, len = n }
                                          _           -> p { from = 14, len = len p - 14 }
                          }
  where d1          = p `byteAt` 0
        d2          = p `byteAt` 1
        d3          = p `byteAt` 2
        d4          = p `byteAt` 3
        d5          = p `byteAt` 4
        d6          = p `byteAt` 5

        s1          = p `byteAt` 6
        s2          = p `byteAt` 7
        s3          = p `byteAt` 8
        s4          = p `byteAt` 9
        s5          = p `byteAt` 10
        s6          = p `byteAt` 11
-}



-- Packets should be paddded elsewhere to satisfy minimum length requirement
-- (46 bytes of data, 64 bytes including headers and CRC)
unparse            :: Packet OutPacket -> OutPacket
unparse p           = addChunk (listArray (0,13) bytes) (content p)
  where bytes                 = [ d1, d2, d3, d4, d5, d6
                                , s1, s2, s3, s4, s5, s6
                                , ty .!. 1, ty .!. 0
                                ]
        Addr d1 d2 d3 d4 d5 d6 = dest p
        Addr s1 s2 s3 s4 s5 s6 = source p
        ty                    = fromEnum (packType p)