diff options
Diffstat (limited to 'src/Network/WireGuard/Foreign/UAPI.hsc')
-rw-r--r-- | src/Network/WireGuard/Foreign/UAPI.hsc | 172 |
1 files changed, 172 insertions, 0 deletions
diff --git a/src/Network/WireGuard/Foreign/UAPI.hsc b/src/Network/WireGuard/Foreign/UAPI.hsc new file mode 100644 index 0000000..b3e2948 --- /dev/null +++ b/src/Network/WireGuard/Foreign/UAPI.hsc @@ -0,0 +1,172 @@ +{-# LANGUAGE RecordWildCards #-} +{-# OPTIONS_GHC -fno-warn-missing-signatures #-} + +module Network.WireGuard.Foreign.UAPI + ( WgIpmask(..) + , PeerFlags + , peerFlagRemoveMe + , peerFlagReplaceIpmasks + , WgPeer(..) + , DeviceFlags + , deviceFlagReplacePeers + , deviceFlagRemovePrivateKey + , deviceFlagRemovePresharedKey + , deviceFlagRemoveFwmark + , WgDevice(..) + , readConfig + , writeConfig + ) where + +import Data.ByteString.Internal (ByteString (..)) +import Network.Socket.Internal (HostAddress, HostAddress6, + SockAddr, peekSockAddr, + pokeSockAddr) +import System.IO.Unsafe (unsafePerformIO) + +import Data.Char +import Data.Int +import Data.Word +import Foreign +import Foreign.C.String + +import qualified Network.WireGuard.Foreign.Key as K +import Network.WireGuard.Internal.Util (zeroMemory) + +import Network.WireGuard.Foreign.In6Addr + +#include "uapi.h" + +data WgIpmask = WgIpmask + { ipmaskIp :: ! (Either HostAddress HostAddress6) + , ipmaskCidr :: ! #{type typeof((struct wgipmask){0}.cidr)} + } + +type PeerFlags = #{type typeof((struct wgpeer){0}.flags)} + +peerFlagRemoveMe = #{const WGPEER_REMOVE_ME} :: PeerFlags +peerFlagReplaceIpmasks = #{const WGPEER_REPLACE_IPMASKS} :: PeerFlags + +data WgPeer = WgPeer + { peerPubKey :: ! ByteString -- TODO: use Bytes + , peerFlags :: ! PeerFlags + , peerAddr :: ! (Maybe SockAddr) + , peerLastHandshakeTime :: ! #{type typeof((struct wgpeer){0}.last_handshake_time.tv_sec)} + , peerReceivedBytes :: ! #{type typeof((struct wgpeer){0}.rx_bytes)} + , peerTransferredBytes :: ! #{type typeof((struct wgpeer){0}.tx_bytes)} + , peerKeepaliveInterval :: ! #{type typeof((struct wgpeer){0}.persistent_keepalive_interval)} + , peerNumIpmasks :: ! #{type typeof((struct wgpeer){0}.num_ipmasks)} + } + +type DeviceFlags = #{type typeof((struct wgdevice){0}.flags)} + +deviceFlagReplacePeers = #{const WGDEVICE_REPLACE_PEERS} :: DeviceFlags +deviceFlagRemovePrivateKey = #{const WGDEVICE_REMOVE_PRIVATE_KEY} :: DeviceFlags +deviceFlagRemovePresharedKey = #{const WGDEVICE_REMOVE_PRESHARED_KEY} :: DeviceFlags +deviceFlagRemoveFwmark = #{const WGDEVICE_REMOVE_FWMARK} :: DeviceFlags + +data WgDevice = WgDevice + { deviceInterface :: ! String + , deviceFlags :: ! DeviceFlags + , devicePubkey :: ! ByteString -- TODO: use Bytes + , devicePrivkey :: ! ByteString -- TODO: use ScrubbedBytes + , devicePSK :: ! ByteString -- TODO: use ScrubbedBytes + , deviceFwmark :: ! #{type typeof((struct wgdevice){0}.fwmark)} + , devicePort :: ! #{type typeof((struct wgdevice){0}.port)} + , deviceNumPeers :: ! #{type typeof((struct wgdevice){0}.num_peers)} + } + +type IpmaskIpFamilyType = #{type typeof((struct wgipmask){0}.family)} + +instance Storable WgIpmask where + sizeOf _ = #{size struct wgipmask} + alignment _ = #{alignment struct wgipmask} + peek ptr = do + ipFamily <- #{peek struct wgipmask, family} ptr :: IO IpmaskIpFamilyType + ip <- case ipFamily of + #{const AF_INET} -> Left <$> #{peek struct wgipmask, ip4.s_addr} ptr + #{const AF_INET6} -> Right . fromIn6Addr <$> #{peek struct wgipmask, ip6} ptr + _ -> error "WgIpmask.peek: unknown ipfamily" + cidr <- #{peek struct wgipmask, cidr} ptr + return (WgIpmask ip cidr) + + poke ptr self@WgIpmask{..} = do + zeroMemory ptr $ fromIntegral $ sizeOf self + case ipmaskIp of + Left ip4 -> do + #{poke struct wgipmask, family} ptr (#{const AF_INET} :: IpmaskIpFamilyType) + #{poke struct wgipmask, ip4.s_addr} ptr ip4 + Right ip6 -> do + #{poke struct wgipmask, family} ptr (#{const AF_INET6} :: IpmaskIpFamilyType) + #{poke struct wgipmask, ip6} ptr (In6Addr ip6) + #{poke struct wgipmask, cidr} ptr ipmaskCidr + +type IpFamilyType = #{type sa_family_t} + +sockaddrOffset = #{offset struct wgpeer, endpoint.addr} +ipfamilyOffset = #{offset struct wgpeer, endpoint.addr.sa_family} + +instance Storable WgPeer where + sizeOf _ = #{size struct wgpeer} + alignment _ = #{alignment struct wgpeer} + peek ptr = do + ipfamily <- peek (ptr `plusPtr` ipfamilyOffset) :: IO IpFamilyType + let sockaddrM = case ipfamily of + 0 -> return Nothing + _ -> Just <$> peekSockAddr (ptr `plusPtr` sockaddrOffset) + WgPeer <$> (K.toByteString <$> #{peek struct wgpeer, public_key} ptr) + <*> #{peek struct wgpeer, flags} ptr + <*> sockaddrM + <*> #{peek struct wgpeer, last_handshake_time.tv_sec} ptr + <*> #{peek struct wgpeer, rx_bytes} ptr + <*> #{peek struct wgpeer, tx_bytes} ptr + <*> #{peek struct wgpeer, persistent_keepalive_interval} ptr + <*> #{peek struct wgpeer, num_ipmasks} ptr + poke ptr self@WgPeer{..} = do + zeroMemory ptr $ fromIntegral $ sizeOf self + #{poke struct wgpeer, public_key} ptr (K.fromByteString peerPubKey) + #{poke struct wgpeer, flags} ptr peerFlags + case peerAddr of + Just addr -> pokeSockAddr (ptr `plusPtr` sockaddrOffset) addr + Nothing -> poke (ptr `plusPtr` ipfamilyOffset) (0 :: IpFamilyType) + #{poke struct wgpeer, last_handshake_time.tv_sec} ptr peerLastHandshakeTime + #{poke struct wgpeer, rx_bytes} ptr peerReceivedBytes + #{poke struct wgpeer, tx_bytes} ptr peerTransferredBytes + #{poke struct wgpeer, persistent_keepalive_interval} ptr peerKeepaliveInterval + #{poke struct wgpeer, num_ipmasks} ptr peerNumIpmasks + +instance Storable WgDevice where + sizeOf _ = #{size struct wgdevice} + alignment _ = #{alignment struct wgdevice} + peek ptr = WgDevice <$> peekCString (ptr `plusPtr` #{offset struct wgdevice, interface}) + <*> #{peek struct wgdevice, flags} ptr + <*> (K.toByteString <$> #{peek struct wgdevice, public_key} ptr) + <*> (K.toByteString <$> #{peek struct wgdevice, private_key} ptr) + <*> (K.toByteString <$> #{peek struct wgdevice, preshared_key} ptr) + <*> #{peek struct wgdevice, fwmark} ptr + <*> #{peek struct wgdevice, port} ptr + <*> #{peek struct wgdevice, num_peers} ptr + poke ptr self@WgDevice{..} + | length deviceInterface >= #{const IFNAMSIZ} = error "interface name is too long" + | otherwise = do + zeroMemory ptr $ fromIntegral $ sizeOf self + pokeArray0 (0 :: Word8) (ptr `plusPtr` #{offset struct wgdevice, interface}) (map (fromIntegral.ord) deviceInterface) + #{poke struct wgdevice, flags} ptr deviceFlags + #{poke struct wgdevice, public_key} ptr (K.fromByteString devicePubkey) + #{poke struct wgdevice, private_key} ptr (K.fromByteString devicePrivkey) + #{poke struct wgdevice, preshared_key} ptr (K.fromByteString devicePSK) + #{poke struct wgdevice, fwmark} ptr deviceFwmark + #{poke struct wgdevice, port} ptr devicePort + #{poke struct wgdevice, num_peers} ptr deviceNumPeers + +readConfig :: Storable a => ByteString -> a +readConfig (PS fptr off len) + | len == sizeOf output = output + | otherwise = error "UAPI.readConfig: length mismatch" + where + output = unsafePerformIO $ withForeignPtr fptr $ \ptr -> peek (ptr `plusPtr` off) + +writeConfig :: Storable a => a -> ByteString +writeConfig input = unsafePerformIO $ do + fptr <- mallocForeignPtr + withForeignPtr fptr $ \ptr -> poke ptr input + return $ PS (castForeignPtr fptr) 0 (sizeOf input) |