{-# LANGUAGE RecordWildCards #-} {-# LANGUAGE PatternSynonyms #-} module Hans.Udp.Output ( primSendUdp, -- ** Fast-path Output responder, queueUdp, ) where import Hans.Addr.Types (Addr) import Hans.Checksum (finalizeChecksum,extendChecksum) import Hans.Device.Types (ChecksumOffload(..),txOffload) import Hans.Lens (view) import Hans.Network import Hans.Serialize (runPutPacket) import Hans.Udp.Packet import Hans.Types import qualified Control.Concurrent.BoundedChan as BC import Control.Monad (forever) import qualified Data.ByteString.Lazy as L import Data.Serialize (putWord16be) -- Fast-path Output ------------------------------------------------------------ responder :: NetworkStack -> IO () responder ns = forever $ do msg <- BC.readChan chan case msg of SendDatagram ri dst hdr body -> do _ <- sendUdp ns ri dst hdr body return () where chan = view udpQueue ns queueUdp :: NetworkStack -> RouteInfo Addr -> Addr -> UdpHeader -> L.ByteString -> IO Bool queueUdp ns ri dst hdr body -- XXX should this record an error? | L.length body > 65527 = return False | otherwise = BC.tryWriteChan (view udpQueue ns) $! SendDatagram ri dst hdr body -- Output ---------------------------------------------------------------------- -- | Send Udp over IP4 with a pre-computed route. primSendUdp :: Network addr => NetworkStack -> RouteInfo addr -> addr -- ^ Destination addr -> UdpPort -- ^ Source port -> UdpPort -- ^ Destination port -> L.ByteString -- ^ Payload -> IO Bool primSendUdp ns ri dst udpSourcePort udpDestPort payload -- XXX should this record an error? | L.length payload > 65527 = return False | otherwise = sendUdp ns ri dst UdpHeader { udpChecksum = 0, .. } payload sendUdp :: Network addr => NetworkStack -> RouteInfo addr -> addr -> UdpHeader -> L.ByteString -> IO Bool sendUdp ns ri dst hdr payload = do let bytes = renderUdpPacket (view txOffload ri) (riSource ri) dst hdr payload sendDatagram ns ri dst False PROT_UDP bytes return True -- | Given a way to make the pseudo header, render the UDP packet. renderUdpPacket :: Network addr => ChecksumOffload -> addr -> addr -> UdpHeader -> L.ByteString -> L.ByteString renderUdpPacket ChecksumOffload { .. } src dst hdr body | coUdp = bytes | otherwise = beforeCS `L.append` withCS where pktlen = fromIntegral (L.length body) bytes = runPutPacket udpHeaderSize 0 body (putUdpHeader hdr pktlen) udplen = udpHeaderSize + pktlen cs = finalizeChecksum $ extendChecksum bytes $ pseudoHeader src dst PROT_UDP udplen beforeCS = L.take (fromIntegral udpHeaderSize - 2) bytes afterCS = L.drop (fromIntegral udpHeaderSize ) bytes withCS = runPutPacket 2 0 afterCS (putWord16be cs)