module Hans.IP4.Input (
processArp,
processIP4,
handleIP4,
) where
import Hans.Checksum (computeChecksum)
import Hans.Device (Device(..),ChecksumOffload(..),rxOffload)
import Hans.Ethernet (Mac,pattern ETYPE_ARP,sendEthernet)
import Hans.IP4.ArpTable (addEntry,lookupEntry)
import Hans.IP4.Fragments (processFragment)
import Hans.IP4.Icmp4 (Icmp4Packet(..),getIcmp4Packet)
import Hans.IP4.Output (queueIcmp4,portUnreachable)
import Hans.IP4.Packet
import Hans.IP4.RoutingTable (Route(..))
import Hans.Lens (view)
import Hans.Monad (Hans,io,dropPacket,escape,decode,decode')
import Hans.Network.Types
import Hans.Serialize (runPutPacket)
import Hans.Types
import Hans.Udp.Input (processUdp)
import Hans.Tcp.Input (processTcp)
import Control.Monad (when,unless)
import qualified Data.ByteString as S
import qualified Data.ByteString.Lazy as L
processArp :: NetworkStack -> Device -> S.ByteString -> Hans ()
processArp ns dev payload =
do ArpPacket { .. } <- decode (devStats dev) getArpPacket payload
merge <- io (updateEntry ns arpSHA arpSPA)
mb <- io (isLocalAddr ns arpTPA)
dev' <- case mb of
Just route -> return (routeDevice route)
Nothing -> escape
let lha = devMac dev'
unless merge (io (addEntry (ip4ArpTable (view ip4State ns)) arpSPA arpSHA))
when (arpOper == ArpRequest)
$ io
$ sendEthernet dev' arpSHA ETYPE_ARP
$ runPutPacket 28 100 L.empty
$ putArpPacket ArpPacket { arpSHA = lha, arpSPA = arpTPA
, arpTHA = arpSHA, arpTPA = arpSPA
, arpOper = ArpReply }
updateEntry :: NetworkStack -> Mac -> IP4 -> IO Bool
updateEntry ns sha spa =
do mb <- lookupEntry (ip4ArpTable (view ip4State ns)) spa
case mb of
Just _ -> do addEntry (ip4ArpTable (view ip4State ns)) spa sha
return True
Nothing -> return False
processIP4 :: NetworkStack -> Device -> S.ByteString -> Hans ()
processIP4 ns dev payload =
do ((hdr,hdrLen,bodyLen),body) <- decode' (devStats dev) getIP4Packet payload
let packetValid = coIP4 (view rxOffload dev)
|| 0 == computeChecksum (S.take hdrLen payload)
unless packetValid (dropPacket (devStats dev))
checkDestination ns dev (ip4DestAddr hdr)
handleIP4 ns dev (Just (hdrLen,payload)) hdr (S.take bodyLen body)
handleIP4 :: NetworkStack -> Device -> Maybe (Int,S.ByteString)
-> IP4Header -> S.ByteString -> Hans ()
handleIP4 ns dev mbOrig hdr body =
do (IP4Header { .. },body') <-
processFragment (ip4Fragments (view ip4State ns)) hdr body
case ip4Protocol of
PROT_ICMP4 -> processICMP ns dev ip4SourceAddr ip4DestAddr body'
PROT_UDP ->
do routed <- processUdp ns dev ip4SourceAddr ip4DestAddr body'
case (routed,mbOrig) of
(False,Just(ihl,orig)) -> io
$ portUnreachable ns dev (SourceIP4 ip4DestAddr) ip4SourceAddr
$ S.take (ihl + 8) orig
_ -> return ()
PROT_TCP -> processTcp ns dev ip4SourceAddr ip4DestAddr body'
_ -> dropPacket (devStats dev)
checkDestination :: NetworkStack -> Device -> IP4 -> Hans ()
checkDestination _ _ BroadcastIP4 = return ()
checkDestination ns dev dest =
do mb <- io (isLocalAddr ns dest)
case mb of
Just Route { .. }
| routeDevice == dev -> return ()
| otherwise -> escape
Nothing ->
do routes <- io (routesForDev ns dev)
unless (null routes) escape
processICMP :: NetworkStack -> Device -> IP4 -> IP4 -> S.ByteString -> Hans ()
processICMP ns dev src dst body =
do let packetValid = coIcmp4 (view rxOffload dev) || 0 == computeChecksum body
unless packetValid (dropPacket (devStats dev))
msg <- decode (devStats dev) getIcmp4Packet body
case msg of
Echo ident seqNum bytes ->
do io (queueIcmp4 ns dev (SourceIP4 dst) src (EchoReply ident seqNum bytes))
escape
_ -> escape