aboutsummaryrefslogtreecommitdiffstats
path: root/src/Network/WireGuard/Foreign/UAPI.hsc
diff options
context:
space:
mode:
Diffstat (limited to 'src/Network/WireGuard/Foreign/UAPI.hsc')
-rw-r--r--src/Network/WireGuard/Foreign/UAPI.hsc172
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)