rtnetlink-0.1.0.1: src/System/Linux/RTNetlink/Address.hsc
{-|
Module : System.Linux.RTNetlink.Address
Description : ADTs for creating, destroying, modifying, and getting info
about layer-3 addresses.
Copyright : (c) Formaltech Inc. 2017
License : BSD3
Maintainer : protob3n@gmail.com
Stability : experimental
Portability : Linux
-}
{-# LANGUAGE CPP #-}
{-# LANGUAGE ForeignFunctionInterface #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE RecordWildCards #-}
{-# LANGUAGE TypeFamilies #-}
module System.Linux.RTNetlink.Address
( IfInetAddress(..)
, IfInet6Address(..)
, IfIndex(..)
, IfPrefix(..)
, AnyInterface(..)
, IfAddrMsg(..)
-- * Re-exports
, InetAddress
, Inet6Address
) where
import Control.Applicative ((<$>), (<*>))
import Control.Monad (guard)
import Data.Monoid (mempty)
import Data.Serialize (Serialize, Get, Putter, get, put, runPut)
import Data.Serialize (getWord32host, putWord32host, getWord8)
import Data.Serialize (putWord8, getWord16be, putWord16be)
import Data.Word (Word8, Word32)
import System.Socket.Family.Inet (InetAddress, inetAddressToTuple)
import System.Socket.Family.Inet (inetAddressFromTuple)
import System.Socket.Family.Inet6 (Inet6Address, inet6AddressToTuple)
import System.Socket.Family.Inet6 (inet6AddressFromTuple)
import System.Linux.RTNetlink.Message
import System.Linux.RTNetlink.Packet
#include <linux/if_addr.h>
#include <linux/rtnetlink.h>
#include <netinet/in.h>
-- | Construct a network-byte-order representation of an 'InetAddress'.
putInetAddress :: Putter InetAddress
putInetAddress i = putWord8 a >> putWord8 b >> putWord8 c >> putWord8 d
where (a,b,c,d) = inetAddressToTuple i
-- | Parse a network-byte-order representation of an 'InetAddress'.
getInetAddress :: Get InetAddress
getInetAddress = inetAddressFromTuple <$> getTuple
where getTuple = (,,,) <$> getWord8 <*> getWord8 <*> getWord8 <*> getWord8
instance Message InetAddress where
type MessageHeader InetAddress = IfAddrMsg
messageAttrs address = AttributeList
[ Attribute #{const RTA_SRC} ipv4
, Attribute #{const RTA_DST} ipv4
] where ipv4 = runPut $ putInetAddress address
instance Reply InetAddress where
type ReplyHeader InetAddress = IfAddrMsg
replyTypeNumbers _ = [#{const RTM_NEWADDR}]
fromNLMessage NLMessage {..} = do
let IfAddrMsg {..} = nlmHeader
guard $ addrFamily == #{const AF_INET}
attr <- findAttribute [#{const RTA_DST}] nlmAttrs
bs <- attributeData attr
runGetMaybe getInetAddress bs
-- | Construct a network-byte-order representation of an 'InetAddress'.
putInet6Address :: Putter Inet6Address
putInet6Address i = mapM_ putWord16be [a,b,c,d,e,f,g,h]
where (a,b,c,d,e,f,g,h) = inet6AddressToTuple i
-- | Parse a network-byte-order representation of an 'Inet6Address'.
getInet6Address :: Get Inet6Address
getInet6Address = inet6AddressFromTuple <$> getTuple
where
getTuple = (,,,,,,,)
<$> getWord16be
<*> getWord16be
<*> getWord16be
<*> getWord16be
<*> getWord16be
<*> getWord16be
<*> getWord16be
<*> getWord16be
instance Message Inet6Address where
type MessageHeader Inet6Address = IfAddrMsg
messageAttrs address = AttributeList
[ Attribute #{const RTA_SRC} ipv6
, Attribute #{const RTA_DST} ipv6
] where ipv6 = runPut $ putInet6Address address
instance Reply Inet6Address where
type ReplyHeader Inet6Address = IfAddrMsg
replyTypeNumbers _ = [#{const RTM_NEWADDR}]
fromNLMessage NLMessage {..} = do
let IfAddrMsg {..} = nlmHeader
guard $ addrFamily == #{const AF_INET6}
attr <- findAttribute [#{const RTA_DST}] nlmAttrs
bs <- attributeData attr
runGetMaybe getInet6Address bs
-- | Interface wildcard. Use this to get information about all layer-3
-- interfaces.
data AnyInterface = AnyInterface
deriving (Show, Eq)
instance Message AnyInterface where
type MessageHeader AnyInterface = IfAddrMsg
messageAttrs AnyInterface = mempty
instance Request AnyInterface where
requestTypeNumber = const #{const RTM_GETADDR}
requestNLFlags = const dumpNLFlags
-- | The index of a layer-3 interface.
newtype IfIndex = IfIndex {ifIndex :: Int}
deriving (Show, Eq, Num, Ord)
instance Message IfIndex where
type MessageHeader IfIndex = IfAddrMsg
messageHeader (IfIndex ix) = IfAddrMsg 0 0 0 0 (fromIntegral ix)
instance Reply IfIndex where
type ReplyHeader IfIndex = IfAddrMsg
replyTypeNumbers _ = [#{const RTM_NEWADDR}]
fromNLMessage = Just . IfIndex . fromIntegral . addrIndex . nlmHeader
-- | A netmask in CIDR notation.
newtype IfPrefix = IfPrefix {ifPrefix :: Word8}
deriving (Show, Eq, Num, Ord)
instance Message IfPrefix where
type MessageHeader IfPrefix = IfAddrMsg
messageHeader (IfPrefix p) = IfAddrMsg 0 p 0 0 0
instance Reply IfPrefix where
type ReplyHeader IfPrefix = IfAddrMsg
replyTypeNumbers _ = [#{const RTM_NEWADDR}]
fromNLMessage = Just . IfPrefix . addrPrefix . nlmHeader
-- | An ipv4 address and netmask associated with an interface.
data IfInetAddress = IfInetAddress
{ ifInetAddress :: InetAddress -- ^ The ip4v address itself.
, ifInetPrefix :: IfPrefix -- ^ The netmask in CIDR notation.
, ifInetIfIndex :: IfIndex -- ^ Index of the associated interface.
} deriving (Show, Eq)
instance Message IfInetAddress where
type MessageHeader IfInetAddress = IfAddrMsg
messageAttrs IfInetAddress {..} = messageAttrs ifInetAddress
messageHeader IfInetAddress {..} = IfAddrMsg
{ addrFamily = #{const AF_INET}
, addrPrefix = ifPrefix ifInetPrefix
, addrFlags = 0
, addrScope = 0
, addrIndex = fromIntegral $ ifIndex ifInetIfIndex
}
instance Create IfInetAddress where
createTypeNumber = const #{const RTM_NEWADDR}
instance Destroy IfInetAddress where
destroyTypeNumber = const #{const RTM_DELADDR}
instance Reply IfInetAddress where
type ReplyHeader IfInetAddress = IfAddrMsg
replyTypeNumbers _ = [#{const RTM_NEWADDR}]
fromNLMessage m =
IfInetAddress <$> fromNLMessage m <*> fromNLMessage m <*> fromNLMessage m
-- | An ipv6 address and netmask associated with an interface.
data IfInet6Address = IfInet6Address
{ ifInet6Address :: Inet6Address -- ^ The ip4v address itself.
, ifInet6Prefix :: IfPrefix -- ^ The netmask in CIDR notation.
, ifInet6IfIndex :: IfIndex -- ^ Index of the associated interface.
} deriving (Show, Eq)
instance Message IfInet6Address where
type MessageHeader IfInet6Address = IfAddrMsg
messageAttrs IfInet6Address {..} = messageAttrs ifInet6Address
messageHeader IfInet6Address {..} = IfAddrMsg
{ addrFamily = #{const AF_INET6}
, addrPrefix = ifPrefix ifInet6Prefix
, addrFlags = 0
, addrScope = 0
, addrIndex = fromIntegral $ ifIndex ifInet6IfIndex
}
instance Create IfInet6Address where
createTypeNumber = const #{const RTM_NEWADDR}
instance Destroy IfInet6Address where
destroyTypeNumber = const #{const RTM_DELADDR}
instance Reply IfInet6Address where
type ReplyHeader IfInet6Address = IfAddrMsg
replyTypeNumbers _ = [#{const RTM_NEWADDR}]
fromNLMessage m =
IfInet6Address <$> fromNLMessage m <*> fromNLMessage m <*> fromNLMessage m
-- | The header corresponding to address messages, based on 'struct ifaddrmsg'
-- from 'linux/if_addr.h'.
data IfAddrMsg = IfAddrMsg
{ addrFamily :: Word8 -- ^ Address family (AF_* from @sys/socket.h@)
, addrPrefix :: Word8 -- ^ CIDR netmask for this address.
, addrFlags :: Word8 -- ^ Operational flags for this address.
, addrScope :: Word8 -- ^ Address scope.
, addrIndex :: Word32 -- ^ Index of the associated interface.
} deriving (Show, Eq)
instance Sized IfAddrMsg where
size = const #{const sizeof(struct ifaddrmsg)}
instance Serialize IfAddrMsg where
put IfAddrMsg {..} = do
putWord8 addrFamily
putWord8 addrPrefix
putWord8 addrFlags
putWord8 addrScope
putWord32host addrIndex
get = IfAddrMsg
<$> getWord8
<*> getWord8
<*> getWord8
<*> getWord8
<*> getWord32host
instance Header IfAddrMsg where
emptyHeader = IfAddrMsg 0 0 0 0 0