module Hans.Udp.Output (
primSendUdp,
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)
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
| L.length body > 65527 = return False
| otherwise = BC.tryWriteChan (view udpQueue ns)
$! SendDatagram ri dst hdr body
primSendUdp :: Network addr
=> NetworkStack
-> RouteInfo addr
-> addr
-> UdpPort
-> UdpPort
-> L.ByteString
-> IO Bool
primSendUdp ns ri dst udpSourcePort udpDestPort payload
| 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
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)