diff --git a/LICENSE b/LICENSE
new file mode 100644
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,30 @@
+Copyright David Anderson 2010
+
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+    * Redistributions of source code must retain the above copyright
+      notice, this list of conditions and the following disclaimer.
+
+    * Redistributions in binary form must reproduce the above
+      copyright notice, this list of conditions and the following
+      disclaimer in the documentation and/or other materials provided
+      with the distribution.
+
+    * Neither the name of David Anderson nor the names of other
+      contributors may be used to endorse or promote products derived
+      from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/Setup.hs b/Setup.hs
new file mode 100644
--- /dev/null
+++ b/Setup.hs
@@ -0,0 +1,3 @@
+#!/usr/bin/env runhaskell
+import Distribution.Simple
+main = defaultMain
diff --git a/System/Linux/Netlink.hs b/System/Linux/Netlink.hs
new file mode 100644
--- /dev/null
+++ b/System/Linux/Netlink.hs
@@ -0,0 +1,1 @@
+module System.Linux.Netlink where
diff --git a/System/Linux/Netlink/Attributes.hs b/System/Linux/Netlink/Attributes.hs
new file mode 100644
--- /dev/null
+++ b/System/Linux/Netlink/Attributes.hs
@@ -0,0 +1,115 @@
+{-# LANGUAGE OverloadedStrings #-}
+
+module System.Linux.Netlink.Attributes 
+    ( getLinkAddress
+    , getLinkBroadcast
+    , getLinkName
+    , getLinkMTU
+    , getLinkQDisc
+    , getLinkTXQLen
+
+    , putLinkAddress
+    , putLinkBroadcast
+    , putLinkName
+    , putLinkMTU
+    , putLinkQDisc
+    , putLinkTXQLen
+    ) where
+
+import Prelude hiding (init, lookup)
+
+import Control.Applicative ((<$>))
+import Data.ByteString.Char8 (ByteString, append, init, pack, unpack)
+import Data.Char (chr, ord)
+import Data.Map (insert, lookup)
+import Data.Serialize.Get (runGet, getWord32host)
+import Data.Serialize.Put (runPut, putWord32host)
+import Data.Word (Word8, Word32)
+
+import System.Linux.Netlink.Constants
+import System.Linux.Netlink.Protocol
+
+type AttributeReader a = Attributes -> Maybe a
+
+type AttributeWriter a = a -> Attributes -> Attributes
+
+--
+-- Link message attributes
+--
+type LinkAddress = (Word8, Word8, Word8, Word8, Word8, Word8)
+
+getLinkAddress :: AttributeReader LinkAddress
+getLinkAddress attrs = decodeMAC <$> lookup eIFLA_ADDRESS attrs
+
+putLinkAddress :: AttributeWriter LinkAddress
+putLinkAddress addr = insert eIFLA_ADDRESS (encodeMAC addr)
+
+getLinkBroadcast :: AttributeReader LinkAddress
+getLinkBroadcast attrs = decodeMAC <$> lookup eIFLA_BROADCAST attrs
+
+putLinkBroadcast :: AttributeWriter LinkAddress
+putLinkBroadcast addr = insert eIFLA_BROADCAST (encodeMAC addr)
+
+getLinkName :: AttributeReader String
+getLinkName attrs = getString <$> lookup eIFLA_IFNAME attrs
+
+putLinkName :: AttributeWriter String
+putLinkName ifname = insert eIFLA_IFNAME (putString ifname)
+
+getLinkMTU :: AttributeReader Word32
+getLinkMTU attrs = get32 =<< lookup eIFLA_MTU attrs
+
+putLinkMTU :: AttributeWriter Word32
+putLinkMTU mtu = insert eIFLA_MTU (put32 mtu)
+
+-- TODO: IFLA_LINK - need to understand what it does
+
+getLinkQDisc :: AttributeReader String
+getLinkQDisc attrs = getString <$> lookup eIFLA_QDISC attrs
+
+putLinkQDisc :: AttributeWriter String
+putLinkQDisc disc = insert eIFLA_QDISC (putString disc)
+
+-- TODO: IFLA_STATS - bloody huge message, will deal with it later.
+
+-- TODO: IFLA_{COST,PRIORITY,MASTER,WIRELESS,PROTINFO} - need to
+-- understand what they do.
+
+getLinkTXQLen :: AttributeReader Word32
+getLinkTXQLen attrs = get32 =<< lookup eIFLA_TXQLEN attrs
+
+putLinkTXQLen :: AttributeWriter Word32
+putLinkTXQLen len = insert eIFLA_TXQLEN (put32 len)
+
+-- TODO: IFLA_{MAP,WEIGHT} - need to figure out
+
+-- TODO: IFLA_{LINKMODE,LINKINFO} - see Documentation/networking/operstates.txt
+
+-- TODO: IFLA_{NET_NS_PID,IFALIAS} - need to figure out
+
+--
+-- Helpers
+--
+
+decodeMAC :: ByteString -> LinkAddress
+decodeMAC = tuplify . map (fromIntegral . ord) . unpack
+  where tuplify [a,b,c,d,e,f] = (a,b,c,d,e,f)
+        tuplify _ = error "Bad encoded MAC"
+
+encodeMAC :: LinkAddress -> ByteString
+encodeMAC = pack . map (chr . fromIntegral) . listify
+  where listify (a,b,c,d,e,f) = [a,b,c,d,e,f]
+
+getString :: ByteString -> String
+getString b = unpack (init b)
+
+putString :: String -> ByteString
+putString s = append (pack s) "\0"
+
+get32 :: ByteString -> Maybe Word32
+get32 bs = case runGet getWord32host bs of
+    Left  _ -> Nothing
+    Right w -> Just w
+
+put32 :: Word32 -> ByteString
+put32 w = runPut (putWord32host w)
diff --git a/System/Linux/Netlink/C.chs b/System/Linux/Netlink/C.chs
new file mode 100644
--- /dev/null
+++ b/System/Linux/Netlink/C.chs
@@ -0,0 +1,150 @@
+{-# LANGUAGE ForeignFunctionInterface #-}
+{-# OPTIONS_GHC -fno-warn-overlapping-patterns #-}
+
+module System.Linux.Netlink.C
+    (
+      NetlinkSocket
+    , makeSocket
+    , closeSocket
+    , sendmsg
+    , recvmsg
+
+    , cFromEnum
+    , cToEnum
+    ) where
+
+import Control.Applicative ((<$>), (<*))
+import Control.Monad (when)
+import Data.Bits (Bits, (.|.), shiftL)
+import Data.ByteString (ByteString)
+import Data.ByteString.Internal (createAndTrim, toForeignPtr)
+import Data.Unique (hashUnique, newUnique)
+import Data.Word (Word32)
+import Foreign.C.Error (throwErrnoIf, throwErrnoIfMinus1, throwErrnoIfMinus1_)
+import Foreign.C.Types
+import Foreign.ForeignPtr (touchForeignPtr)
+import Foreign.ForeignPtr.Unsafe (unsafeForeignPtrToPtr)
+import Foreign.Marshal.Array (withArrayLen)
+import Foreign.Marshal.Utils (with)
+import Foreign.Ptr (Ptr, castPtr, plusPtr)
+import Foreign.Storable (Storable(..))
+import System.Posix.Process (getProcessID)
+
+import System.Linux.Netlink.Constants (eAF_NETLINK)
+
+#include <unistd.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <linux/netlink.h>
+
+#c
+typedef struct msghdr msdhdr;
+typedef struct iovec iovec;
+typedef struct sockaddr_nl sockaddr_nl;
+#endc
+
+newtype NetlinkSocket = NS CInt
+
+makeSocket :: IO NetlinkSocket
+makeSocket = do
+    fd <- throwErrnoIfMinus1 "makeSocket.socket" $
+          ({#call socket #}
+           eAF_NETLINK
+           (cFromEnum Raw)
+           (cFromEnum Route))
+    unique <- fromIntegral . hashUnique <$> newUnique
+    pid <- fromIntegral <$> getProcessID
+    let sockId = (unique `shiftL` 16) .|. pid
+    with (SockAddrNetlink sockId) $ \addr ->
+        throwErrnoIfMinus1_ "makeSocket.bind" $ do
+            {#call bind #} fd (castPtr addr) {#sizeof sockaddr_nl #}
+    return $ NS fd
+
+closeSocket :: NetlinkSocket -> IO ()
+closeSocket (NS fd) = throwErrnoIfMinus1_ "closeSocket" $ {#call close #} fd
+
+sendmsg :: NetlinkSocket -> [ByteString] -> IO ()
+sendmsg (NS fd) bs =
+    useManyAsPtrLen bs $ \ptrs ->
+    withArrayLen (map IoVec ptrs) $ \iovlen iov ->
+    with (MsgHdr (castPtr iov, iovlen)) $ \msg ->
+    throwErrnoIfMinus1_ "sendmsg" $ do
+        {#call sendmsg as _sendmsg #} fd (castPtr msg) (0 :: CInt)
+
+recvmsg :: NetlinkSocket -> Int -> IO ByteString
+recvmsg (NS fd) len =
+    createAndTrim len $ \ptr ->
+    with (IoVec (castPtr ptr, len)) $ \vec ->
+    with (MsgHdr (castPtr vec, 1)) $ \msg ->
+    fmap fromIntegral . throwErrnoIf (<= 0) "recvmsg" $ do
+        {#call recvmsg as _recvmsg #} fd (castPtr msg) (0 :: CInt)
+
+{#enum define PF { NETLINK_ROUTE as Route } #}
+{#enum define ST { SOCK_RAW as Raw } #}
+
+data IoVec = IoVec (Ptr (), Int)
+
+instance Storable IoVec where
+    sizeOf    _ = {#sizeof iovec #}
+    alignment _ = 4
+    peek p = do
+        addr <- {#get iovec.iov_base #} p
+        len  <- {#get iovec.iov_len #}  p
+        return $ IoVec (addr, (fromIntegral len))
+    poke p (IoVec (addr, len)) = do
+        zero p
+        {#set iovec.iov_base #} p addr
+        {#set iovec.iov_len  #} p (fromIntegral len)
+
+data MsgHdr = MsgHdr (Ptr (), Int)
+
+instance Storable MsgHdr where
+    sizeOf    _ = {#sizeof iovec #}
+    alignment _ = 4
+    peek p = do
+        iov     <- {#get msghdr.msg_iov     #} p
+        iovlen  <- {#get msghdr.msg_iovlen  #} p
+        return $ MsgHdr (iov, fromIntegral iovlen)
+    poke p (MsgHdr (iov, iovlen)) = do
+        zero p
+        {#set msghdr.msg_iov     #} p iov
+        {#set msghdr.msg_iovlen  #} p (fromIntegral iovlen)
+
+data SockAddrNetlink = SockAddrNetlink Word32
+
+instance Storable SockAddrNetlink where
+    sizeOf    _ = {#sizeof sockaddr_nl #}
+    alignment _ = 4
+    peek p = do
+        family <- {#get sockaddr_nl.nl_family #} p
+        when (family /= eAF_NETLINK) $ fail "Bad address family"
+        SockAddrNetlink . fromIntegral <$> {#get sockaddr_nl.nl_pid #} p
+    poke p (SockAddrNetlink pid) = do
+        zero p
+        {#set sockaddr_nl.nl_family #} p eAF_NETLINK
+        {#set sockaddr_nl.nl_pid    #} p (fromIntegral pid)
+
+useManyAsPtrLen :: [ByteString] -> ([(Ptr (), Int)] -> IO a) -> IO a
+useManyAsPtrLen bs act =
+    let makePtrLen (fptr, off, len) =
+            let ptr = plusPtr (unsafeForeignPtrToPtr fptr) off
+            in (ptr, len)
+        touchByteStringPtr (fptr, _, _) = touchForeignPtr fptr
+        foreigns = map toForeignPtr bs
+    in act (map makePtrLen foreigns) <* mapM_ touchByteStringPtr foreigns
+
+sizeOfPtr :: (Storable a, Integral b) => Ptr a -> b
+sizeOfPtr = fromIntegral . sizeOf . (undefined :: Ptr a -> a)
+
+zero :: Storable a => Ptr a -> IO ()
+zero p = void $ {#call memset #} (castPtr p) 0 (sizeOfPtr p)
+
+void :: Monad m => m a -> m ()
+void act = act >> return ()
+
+cFromEnum :: (Enum e, Integral i) => e -> i
+cFromEnum = fromIntegral . fromEnum
+
+cToEnum :: (Integral i, Enum e) => i -> e
+cToEnum = toEnum . fromIntegral
diff --git a/System/Linux/Netlink/Constants.hs b/System/Linux/Netlink/Constants.hs
new file mode 100644
--- /dev/null
+++ b/System/Linux/Netlink/Constants.hs
@@ -0,0 +1,603 @@
+{-# LANGUAGE GeneralizedNewtypeDeriving #-}
+module System.Linux.Netlink.Constants (AddressFamily, eAF_UNSPEC, eAF_FILE, eAF_LOCAL, eAF_UNIX, eAF_INET, eAF_AX25, eAF_IPX, eAF_APPLETALK, eAF_NETROM, eAF_BRIDGE, eAF_ATMPVC, eAF_X25, eAF_INET6, eAF_ROSE, eAF_DECnet, eAF_NETBEUI, eAF_SECURITY, eAF_KEY, eAF_NETLINK, eAF_ROUTE, eAF_PACKET, eAF_ASH, eAF_ECONET, eAF_ATMSVC, eAF_RDS, eAF_SNA, eAF_IRDA, eAF_PPPOX, eAF_WANPIPE, eAF_LLC, eAF_CAN, eAF_TIPC, eAF_BLUETOOTH, eAF_IUCV, eAF_RXRPC, eAF_ISDN, eAF_PHONET, eAF_IEEE802154, eAF_MAX, MessageType, eNLMSG_NOOP, eNLMSG_ERROR, eNLMSG_DONE, eNLMSG_OVERRUN, eNLMSG_MIN_TYPE, eRTM_BASE, eRTM_NEWLINK, eRTM_DELLINK, eRTM_GETLINK, eRTM_SETLINK, eRTM_NEWADDR, eRTM_DELADDR, eRTM_GETADDR, eRTM_NEWROUTE, eRTM_DELROUTE, eRTM_GETROUTE, eRTM_NEWNEIGH, eRTM_DELNEIGH, eRTM_GETNEIGH, eRTM_NEWRULE, eRTM_DELRULE, eRTM_GETRULE, eRTM_NEWQDISC, eRTM_DELQDISC, eRTM_GETQDISC, eRTM_NEWTCLASS, eRTM_DELTCLASS, eRTM_GETTCLASS, eRTM_NEWTFILTER, eRTM_DELTFILTER, eRTM_GETTFILTER, eRTM_NEWACTION, eRTM_DELACTION, eRTM_GETACTION, eRTM_NEWPREFIX, eRTM_GETMULTICAST, eRTM_GETANYCAST, eRTM_NEWNEIGHTBL, eRTM_GETNEIGHTBL, eRTM_SETNEIGHTBL, eRTM_NEWNDUSEROPT, eRTM_NEWADDRLABEL, eRTM_DELADDRLABEL, eRTM_GETADDRLABEL, eRTM_GETDCB, eRTM_SETDCB, MessageFlags, fNLM_F_REQUEST, fNLM_F_MULTI, fNLM_F_ACK, fNLM_F_ECHO, fNLM_F_REPLACE, fNLM_F_ROOT, fNLM_F_EXCL, fNLM_F_MATCH, fNLM_F_ATOMIC, fNLM_F_CREATE, fNLM_F_APPEND, LinkType, eARPHRD_NETROM, eARPHRD_ETHER, eARPHRD_EETHER, eARPHRD_AX25, eARPHRD_PRONET, eARPHRD_CHAOS, eARPHRD_IEEE802, eARPHRD_ARCNET, eARPHRD_APPLETLK, eARPHRD_DLCI, eARPHRD_ATM, eARPHRD_METRICOM, eARPHRD_IEEE1394, eARPHRD_EUI64, eARPHRD_INFINIBAND, eARPHRD_SLIP, eARPHRD_CSLIP, eARPHRD_SLIP6, eARPHRD_CSLIP6, eARPHRD_RSRVD, eARPHRD_ADAPT, eARPHRD_ROSE, eARPHRD_X25, eARPHRD_HWX25, eARPHRD_CAN, eARPHRD_PPP, eARPHRD_CISCO, eARPHRD_HDLC, eARPHRD_LAPB, eARPHRD_DDCMP, eARPHRD_RAWHDLC, eARPHRD_TUNNEL, eARPHRD_TUNNEL6, eARPHRD_FRAD, eARPHRD_SKIP, eARPHRD_LOOPBACK, eARPHRD_LOCALTLK, eARPHRD_FDDI, eARPHRD_BIF, eARPHRD_SIT, eARPHRD_IPDDP, eARPHRD_IPGRE, eARPHRD_PIMREG, eARPHRD_HIPPI, eARPHRD_ASH, eARPHRD_ECONET, eARPHRD_IRDA, eARPHRD_FCPP, eARPHRD_FCAL, eARPHRD_FCPL, eARPHRD_FCFABRIC, eARPHRD_IEEE802_TR, eARPHRD_IEEE80211, eARPHRD_IEEE80211_PRISM, eARPHRD_IEEE80211_RADIOTAP, eARPHRD_IEEE802154, eARPHRD_PHONET, eARPHRD_PHONET_PIPE, LinkFlags, fIFF_802_1Q_VLAN, fIFF_TUN, fIFF_UP, fIFF_BROADCAST, fIFF_EBRIDGE, fIFF_TAP, fIFF_DEBUG, fIFF_SLAVE_INACTIVE, fIFF_LOOPBACK, fIFF_MASTER_8023AD, fIFF_MASTER_ALB, fIFF_POINTOPOINT, fIFF_BONDING, fIFF_NOTRAILERS, fIFF_RUNNING, fIFF_SLAVE_NEEDARP, fIFF_ISATAP, fIFF_NOARP, fIFF_MASTER_ARPMON, fIFF_PROMISC, fIFF_ALLMULTI, fIFF_WAN_HDLC, fIFF_MASTER, fIFF_XMIT_DST_RELEASE, fIFF_SLAVE, fIFF_MULTICAST, fIFF_NO_PI, fIFF_ONE_QUEUE, fIFF_PORTSEL, fIFF_AUTOMEDIA, fIFF_VNET_HDR, fIFF_DYNAMIC, fIFF_TUN_EXCL, fIFF_LOWER_UP, fIFF_DORMANT, fIFF_ECHO, LinkAttrType, eIFLA_UNSPEC, eIFLA_ADDRESS, eIFLA_BROADCAST, eIFLA_IFNAME, eIFLA_MTU, eIFLA_LINK, eIFLA_QDISC, eIFLA_STATS, eIFLA_COST, eIFLA_PRIORITY, eIFLA_MASTER, eIFLA_WIRELESS, eIFLA_PROTINFO, eIFLA_TXQLEN, eIFLA_MAP, eIFLA_WEIGHT, eIFLA_OPERSTATE, eIFLA_LINKMODE, eIFLA_LINKINFO, eIFLA_NET_NS_PID, eIFLA_IFALIAS, AddrFlags, fIFA_F_SECONDARY, fIFA_F_TEMPORARY, fIFA_F_NODAD, fIFA_F_OPTIMISTIC, fIFA_F_DADFAILED, fIFA_F_HOMEADDRESS, fIFA_F_DEPRECATED, fIFA_F_TENTATIVE, fIFA_F_PERMANENT, Scope, eRT_SCOPE_UNIVERSE, eRT_SCOPE_SITE, eRT_SCOPE_LINK, eRT_SCOPE_HOST, eRT_SCOPE_NOWHERE, AddrAttrType, eIFA_UNSPEC, eIFA_ADDRESS, eIFA_LOCAL, eIFA_LABEL, eIFA_BROADCAST, eIFA_ANYCAST, eIFA_CACHEINFO, eIFA_MULTICAST, RouteTableId, eRT_TABLE_UNSPEC, eRT_TABLE_COMPAT, eRT_TABLE_DEFAULT, eRT_TABLE_MAIN, eRT_TABLE_LOCAL, eRT_TABLE_MAX, RouteProto, eRTPROT_UNSPEC, eRTPROT_REDIRECT, eRTPROT_KERNEL, eRTPROT_BOOT, eRTPROT_STATIC, eRTPROT_GATED, eRTPROT_RA, eRTPROT_MRT, eRTPROT_ZEBRA, eRTPROT_BIRD, eRTPROT_DNROUTED, eRTPROT_XORP, eRTPROT_NTK, eRTPROT_DHCP, RouteType, eRTN_UNSPEC, eRTN_UNICAST, eRTN_LOCAL, eRTN_BROADCAST, eRTN_ANYCAST, eRTN_MULTICAST, eRTN_BLACKHOLE, eRTN_UNREACHABLE, eRTN_PROHIBIT, eRTN_THROW, eRTN_NAT, eRTN_XRESOLVE, RouteFlags, fRTM_F_NOTIFY, fRTM_F_CLONED, fRTM_F_EQUALIZE, fRTM_F_PREFIX, RouteAttrType, eRTA_UNSPEC, eRTA_DST, eRTA_SRC, eRTA_IIF, eRTA_OIF, eRTA_GATEWAY, eRTA_PRIORITY, eRTA_PREFSRC, eRTA_METRICS, eRTA_MULTIPATH, eRTA_PROTOINFO, eRTA_FLOW, eRTA_CACHEINFO, eRTA_SESSION, eRTA_MP_ALGO, eRTA_TABLE) where
+
+import Data.Bits
+
+newtype AddressFamily = AddressFamily Int deriving (Eq, Enum, Integral, Num, Ord, Real, Show)
+
+eAF_UNSPEC :: (Num a) => a
+eAF_UNSPEC = 0
+eAF_FILE :: (Num a) => a
+eAF_FILE = 1
+eAF_LOCAL :: (Num a) => a
+eAF_LOCAL = 1
+eAF_UNIX :: (Num a) => a
+eAF_UNIX = 1
+eAF_INET :: (Num a) => a
+eAF_INET = 2
+eAF_AX25 :: (Num a) => a
+eAF_AX25 = 3
+eAF_IPX :: (Num a) => a
+eAF_IPX = 4
+eAF_APPLETALK :: (Num a) => a
+eAF_APPLETALK = 5
+eAF_NETROM :: (Num a) => a
+eAF_NETROM = 6
+eAF_BRIDGE :: (Num a) => a
+eAF_BRIDGE = 7
+eAF_ATMPVC :: (Num a) => a
+eAF_ATMPVC = 8
+eAF_X25 :: (Num a) => a
+eAF_X25 = 9
+eAF_INET6 :: (Num a) => a
+eAF_INET6 = 10
+eAF_ROSE :: (Num a) => a
+eAF_ROSE = 11
+eAF_DECnet :: (Num a) => a
+eAF_DECnet = 12
+eAF_NETBEUI :: (Num a) => a
+eAF_NETBEUI = 13
+eAF_SECURITY :: (Num a) => a
+eAF_SECURITY = 14
+eAF_KEY :: (Num a) => a
+eAF_KEY = 15
+eAF_NETLINK :: (Num a) => a
+eAF_NETLINK = 16
+eAF_ROUTE :: (Num a) => a
+eAF_ROUTE = 16
+eAF_PACKET :: (Num a) => a
+eAF_PACKET = 17
+eAF_ASH :: (Num a) => a
+eAF_ASH = 18
+eAF_ECONET :: (Num a) => a
+eAF_ECONET = 19
+eAF_ATMSVC :: (Num a) => a
+eAF_ATMSVC = 20
+eAF_RDS :: (Num a) => a
+eAF_RDS = 21
+eAF_SNA :: (Num a) => a
+eAF_SNA = 22
+eAF_IRDA :: (Num a) => a
+eAF_IRDA = 23
+eAF_PPPOX :: (Num a) => a
+eAF_PPPOX = 24
+eAF_WANPIPE :: (Num a) => a
+eAF_WANPIPE = 25
+eAF_LLC :: (Num a) => a
+eAF_LLC = 26
+eAF_CAN :: (Num a) => a
+eAF_CAN = 29
+eAF_TIPC :: (Num a) => a
+eAF_TIPC = 30
+eAF_BLUETOOTH :: (Num a) => a
+eAF_BLUETOOTH = 31
+eAF_IUCV :: (Num a) => a
+eAF_IUCV = 32
+eAF_RXRPC :: (Num a) => a
+eAF_RXRPC = 33
+eAF_ISDN :: (Num a) => a
+eAF_ISDN = 34
+eAF_PHONET :: (Num a) => a
+eAF_PHONET = 35
+eAF_IEEE802154 :: (Num a) => a
+eAF_IEEE802154 = 36
+eAF_MAX :: (Num a) => a
+eAF_MAX = 37
+newtype MessageType = MessageType Int deriving (Eq, Enum, Integral, Num, Ord, Real, Show)
+
+eNLMSG_NOOP :: (Num a) => a
+eNLMSG_NOOP = 1
+eNLMSG_ERROR :: (Num a) => a
+eNLMSG_ERROR = 2
+eNLMSG_DONE :: (Num a) => a
+eNLMSG_DONE = 3
+eNLMSG_OVERRUN :: (Num a) => a
+eNLMSG_OVERRUN = 4
+eNLMSG_MIN_TYPE :: (Num a) => a
+eNLMSG_MIN_TYPE = 16
+eRTM_BASE :: (Num a) => a
+eRTM_BASE = 16
+eRTM_NEWLINK :: (Num a) => a
+eRTM_NEWLINK = 16
+eRTM_DELLINK :: (Num a) => a
+eRTM_DELLINK = 17
+eRTM_GETLINK :: (Num a) => a
+eRTM_GETLINK = 18
+eRTM_SETLINK :: (Num a) => a
+eRTM_SETLINK = 19
+eRTM_NEWADDR :: (Num a) => a
+eRTM_NEWADDR = 20
+eRTM_DELADDR :: (Num a) => a
+eRTM_DELADDR = 21
+eRTM_GETADDR :: (Num a) => a
+eRTM_GETADDR = 22
+eRTM_NEWROUTE :: (Num a) => a
+eRTM_NEWROUTE = 24
+eRTM_DELROUTE :: (Num a) => a
+eRTM_DELROUTE = 25
+eRTM_GETROUTE :: (Num a) => a
+eRTM_GETROUTE = 26
+eRTM_NEWNEIGH :: (Num a) => a
+eRTM_NEWNEIGH = 28
+eRTM_DELNEIGH :: (Num a) => a
+eRTM_DELNEIGH = 29
+eRTM_GETNEIGH :: (Num a) => a
+eRTM_GETNEIGH = 30
+eRTM_NEWRULE :: (Num a) => a
+eRTM_NEWRULE = 32
+eRTM_DELRULE :: (Num a) => a
+eRTM_DELRULE = 33
+eRTM_GETRULE :: (Num a) => a
+eRTM_GETRULE = 34
+eRTM_NEWQDISC :: (Num a) => a
+eRTM_NEWQDISC = 36
+eRTM_DELQDISC :: (Num a) => a
+eRTM_DELQDISC = 37
+eRTM_GETQDISC :: (Num a) => a
+eRTM_GETQDISC = 38
+eRTM_NEWTCLASS :: (Num a) => a
+eRTM_NEWTCLASS = 40
+eRTM_DELTCLASS :: (Num a) => a
+eRTM_DELTCLASS = 41
+eRTM_GETTCLASS :: (Num a) => a
+eRTM_GETTCLASS = 42
+eRTM_NEWTFILTER :: (Num a) => a
+eRTM_NEWTFILTER = 44
+eRTM_DELTFILTER :: (Num a) => a
+eRTM_DELTFILTER = 45
+eRTM_GETTFILTER :: (Num a) => a
+eRTM_GETTFILTER = 46
+eRTM_NEWACTION :: (Num a) => a
+eRTM_NEWACTION = 48
+eRTM_DELACTION :: (Num a) => a
+eRTM_DELACTION = 49
+eRTM_GETACTION :: (Num a) => a
+eRTM_GETACTION = 50
+eRTM_NEWPREFIX :: (Num a) => a
+eRTM_NEWPREFIX = 52
+eRTM_GETMULTICAST :: (Num a) => a
+eRTM_GETMULTICAST = 58
+eRTM_GETANYCAST :: (Num a) => a
+eRTM_GETANYCAST = 62
+eRTM_NEWNEIGHTBL :: (Num a) => a
+eRTM_NEWNEIGHTBL = 64
+eRTM_GETNEIGHTBL :: (Num a) => a
+eRTM_GETNEIGHTBL = 66
+eRTM_SETNEIGHTBL :: (Num a) => a
+eRTM_SETNEIGHTBL = 67
+eRTM_NEWNDUSEROPT :: (Num a) => a
+eRTM_NEWNDUSEROPT = 68
+eRTM_NEWADDRLABEL :: (Num a) => a
+eRTM_NEWADDRLABEL = 72
+eRTM_DELADDRLABEL :: (Num a) => a
+eRTM_DELADDRLABEL = 73
+eRTM_GETADDRLABEL :: (Num a) => a
+eRTM_GETADDRLABEL = 74
+eRTM_GETDCB :: (Num a) => a
+eRTM_GETDCB = 78
+eRTM_SETDCB :: (Num a) => a
+eRTM_SETDCB = 79
+newtype MessageFlags = MessageFlags Int deriving (Bits, Eq, Enum, Integral, Num, Ord, Real, Show)
+
+fNLM_F_REQUEST :: (Num a, Bits a) => a
+fNLM_F_REQUEST = 1
+fNLM_F_MULTI :: (Num a, Bits a) => a
+fNLM_F_MULTI = 2
+fNLM_F_ACK :: (Num a, Bits a) => a
+fNLM_F_ACK = 4
+fNLM_F_ECHO :: (Num a, Bits a) => a
+fNLM_F_ECHO = 8
+fNLM_F_REPLACE :: (Num a, Bits a) => a
+fNLM_F_REPLACE = 256
+fNLM_F_ROOT :: (Num a, Bits a) => a
+fNLM_F_ROOT = 256
+fNLM_F_EXCL :: (Num a, Bits a) => a
+fNLM_F_EXCL = 512
+fNLM_F_MATCH :: (Num a, Bits a) => a
+fNLM_F_MATCH = 512
+fNLM_F_ATOMIC :: (Num a, Bits a) => a
+fNLM_F_ATOMIC = 1024
+fNLM_F_CREATE :: (Num a, Bits a) => a
+fNLM_F_CREATE = 1024
+fNLM_F_APPEND :: (Num a, Bits a) => a
+fNLM_F_APPEND = 2048
+newtype LinkType = LinkType Int deriving (Eq, Enum, Integral, Num, Ord, Real, Show)
+
+eARPHRD_NETROM :: (Num a) => a
+eARPHRD_NETROM = 0
+eARPHRD_ETHER :: (Num a) => a
+eARPHRD_ETHER = 1
+eARPHRD_EETHER :: (Num a) => a
+eARPHRD_EETHER = 2
+eARPHRD_AX25 :: (Num a) => a
+eARPHRD_AX25 = 3
+eARPHRD_PRONET :: (Num a) => a
+eARPHRD_PRONET = 4
+eARPHRD_CHAOS :: (Num a) => a
+eARPHRD_CHAOS = 5
+eARPHRD_IEEE802 :: (Num a) => a
+eARPHRD_IEEE802 = 6
+eARPHRD_ARCNET :: (Num a) => a
+eARPHRD_ARCNET = 7
+eARPHRD_APPLETLK :: (Num a) => a
+eARPHRD_APPLETLK = 8
+eARPHRD_DLCI :: (Num a) => a
+eARPHRD_DLCI = 15
+eARPHRD_ATM :: (Num a) => a
+eARPHRD_ATM = 19
+eARPHRD_METRICOM :: (Num a) => a
+eARPHRD_METRICOM = 23
+eARPHRD_IEEE1394 :: (Num a) => a
+eARPHRD_IEEE1394 = 24
+eARPHRD_EUI64 :: (Num a) => a
+eARPHRD_EUI64 = 27
+eARPHRD_INFINIBAND :: (Num a) => a
+eARPHRD_INFINIBAND = 32
+eARPHRD_SLIP :: (Num a) => a
+eARPHRD_SLIP = 256
+eARPHRD_CSLIP :: (Num a) => a
+eARPHRD_CSLIP = 257
+eARPHRD_SLIP6 :: (Num a) => a
+eARPHRD_SLIP6 = 258
+eARPHRD_CSLIP6 :: (Num a) => a
+eARPHRD_CSLIP6 = 259
+eARPHRD_RSRVD :: (Num a) => a
+eARPHRD_RSRVD = 260
+eARPHRD_ADAPT :: (Num a) => a
+eARPHRD_ADAPT = 264
+eARPHRD_ROSE :: (Num a) => a
+eARPHRD_ROSE = 270
+eARPHRD_X25 :: (Num a) => a
+eARPHRD_X25 = 271
+eARPHRD_HWX25 :: (Num a) => a
+eARPHRD_HWX25 = 272
+eARPHRD_CAN :: (Num a) => a
+eARPHRD_CAN = 280
+eARPHRD_PPP :: (Num a) => a
+eARPHRD_PPP = 512
+eARPHRD_CISCO :: (Num a) => a
+eARPHRD_CISCO = 513
+eARPHRD_HDLC :: (Num a) => a
+eARPHRD_HDLC = 513
+eARPHRD_LAPB :: (Num a) => a
+eARPHRD_LAPB = 516
+eARPHRD_DDCMP :: (Num a) => a
+eARPHRD_DDCMP = 517
+eARPHRD_RAWHDLC :: (Num a) => a
+eARPHRD_RAWHDLC = 518
+eARPHRD_TUNNEL :: (Num a) => a
+eARPHRD_TUNNEL = 768
+eARPHRD_TUNNEL6 :: (Num a) => a
+eARPHRD_TUNNEL6 = 769
+eARPHRD_FRAD :: (Num a) => a
+eARPHRD_FRAD = 770
+eARPHRD_SKIP :: (Num a) => a
+eARPHRD_SKIP = 771
+eARPHRD_LOOPBACK :: (Num a) => a
+eARPHRD_LOOPBACK = 772
+eARPHRD_LOCALTLK :: (Num a) => a
+eARPHRD_LOCALTLK = 773
+eARPHRD_FDDI :: (Num a) => a
+eARPHRD_FDDI = 774
+eARPHRD_BIF :: (Num a) => a
+eARPHRD_BIF = 775
+eARPHRD_SIT :: (Num a) => a
+eARPHRD_SIT = 776
+eARPHRD_IPDDP :: (Num a) => a
+eARPHRD_IPDDP = 777
+eARPHRD_IPGRE :: (Num a) => a
+eARPHRD_IPGRE = 778
+eARPHRD_PIMREG :: (Num a) => a
+eARPHRD_PIMREG = 779
+eARPHRD_HIPPI :: (Num a) => a
+eARPHRD_HIPPI = 780
+eARPHRD_ASH :: (Num a) => a
+eARPHRD_ASH = 781
+eARPHRD_ECONET :: (Num a) => a
+eARPHRD_ECONET = 782
+eARPHRD_IRDA :: (Num a) => a
+eARPHRD_IRDA = 783
+eARPHRD_FCPP :: (Num a) => a
+eARPHRD_FCPP = 784
+eARPHRD_FCAL :: (Num a) => a
+eARPHRD_FCAL = 785
+eARPHRD_FCPL :: (Num a) => a
+eARPHRD_FCPL = 786
+eARPHRD_FCFABRIC :: (Num a) => a
+eARPHRD_FCFABRIC = 787
+eARPHRD_IEEE802_TR :: (Num a) => a
+eARPHRD_IEEE802_TR = 800
+eARPHRD_IEEE80211 :: (Num a) => a
+eARPHRD_IEEE80211 = 801
+eARPHRD_IEEE80211_PRISM :: (Num a) => a
+eARPHRD_IEEE80211_PRISM = 802
+eARPHRD_IEEE80211_RADIOTAP :: (Num a) => a
+eARPHRD_IEEE80211_RADIOTAP = 803
+eARPHRD_IEEE802154 :: (Num a) => a
+eARPHRD_IEEE802154 = 804
+eARPHRD_PHONET :: (Num a) => a
+eARPHRD_PHONET = 820
+eARPHRD_PHONET_PIPE :: (Num a) => a
+eARPHRD_PHONET_PIPE = 821
+newtype LinkFlags = LinkFlags Int deriving (Bits, Eq, Enum, Integral, Num, Ord, Real, Show)
+
+fIFF_802_1Q_VLAN :: (Num a, Bits a) => a
+fIFF_802_1Q_VLAN = 1
+fIFF_TUN :: (Num a, Bits a) => a
+fIFF_TUN = 1
+fIFF_UP :: (Num a, Bits a) => a
+fIFF_UP = 1
+fIFF_BROADCAST :: (Num a, Bits a) => a
+fIFF_BROADCAST = 2
+fIFF_EBRIDGE :: (Num a, Bits a) => a
+fIFF_EBRIDGE = 2
+fIFF_TAP :: (Num a, Bits a) => a
+fIFF_TAP = 2
+fIFF_DEBUG :: (Num a, Bits a) => a
+fIFF_DEBUG = 4
+fIFF_SLAVE_INACTIVE :: (Num a, Bits a) => a
+fIFF_SLAVE_INACTIVE = 4
+fIFF_LOOPBACK :: (Num a, Bits a) => a
+fIFF_LOOPBACK = 8
+fIFF_MASTER_8023AD :: (Num a, Bits a) => a
+fIFF_MASTER_8023AD = 8
+fIFF_MASTER_ALB :: (Num a, Bits a) => a
+fIFF_MASTER_ALB = 16
+fIFF_POINTOPOINT :: (Num a, Bits a) => a
+fIFF_POINTOPOINT = 16
+fIFF_BONDING :: (Num a, Bits a) => a
+fIFF_BONDING = 32
+fIFF_NOTRAILERS :: (Num a, Bits a) => a
+fIFF_NOTRAILERS = 32
+fIFF_RUNNING :: (Num a, Bits a) => a
+fIFF_RUNNING = 64
+fIFF_SLAVE_NEEDARP :: (Num a, Bits a) => a
+fIFF_SLAVE_NEEDARP = 64
+fIFF_ISATAP :: (Num a, Bits a) => a
+fIFF_ISATAP = 128
+fIFF_NOARP :: (Num a, Bits a) => a
+fIFF_NOARP = 128
+fIFF_MASTER_ARPMON :: (Num a, Bits a) => a
+fIFF_MASTER_ARPMON = 256
+fIFF_PROMISC :: (Num a, Bits a) => a
+fIFF_PROMISC = 256
+fIFF_ALLMULTI :: (Num a, Bits a) => a
+fIFF_ALLMULTI = 512
+fIFF_WAN_HDLC :: (Num a, Bits a) => a
+fIFF_WAN_HDLC = 512
+fIFF_MASTER :: (Num a, Bits a) => a
+fIFF_MASTER = 1024
+fIFF_XMIT_DST_RELEASE :: (Num a, Bits a) => a
+fIFF_XMIT_DST_RELEASE = 1024
+fIFF_SLAVE :: (Num a, Bits a) => a
+fIFF_SLAVE = 2048
+fIFF_MULTICAST :: (Num a, Bits a) => a
+fIFF_MULTICAST = 4096
+fIFF_NO_PI :: (Num a, Bits a) => a
+fIFF_NO_PI = 4096
+fIFF_ONE_QUEUE :: (Num a, Bits a) => a
+fIFF_ONE_QUEUE = 8192
+fIFF_PORTSEL :: (Num a, Bits a) => a
+fIFF_PORTSEL = 8192
+fIFF_AUTOMEDIA :: (Num a, Bits a) => a
+fIFF_AUTOMEDIA = 16384
+fIFF_VNET_HDR :: (Num a, Bits a) => a
+fIFF_VNET_HDR = 16384
+fIFF_DYNAMIC :: (Num a, Bits a) => a
+fIFF_DYNAMIC = 32768
+fIFF_TUN_EXCL :: (Num a, Bits a) => a
+fIFF_TUN_EXCL = 32768
+fIFF_LOWER_UP :: (Num a, Bits a) => a
+fIFF_LOWER_UP = 65536
+fIFF_DORMANT :: (Num a, Bits a) => a
+fIFF_DORMANT = 131072
+fIFF_ECHO :: (Num a, Bits a) => a
+fIFF_ECHO = 262144
+newtype LinkAttrType = LinkAttrType Int deriving (Eq, Enum, Integral, Num, Ord, Real, Show)
+
+eIFLA_UNSPEC :: (Num a) => a
+eIFLA_UNSPEC = 0
+eIFLA_ADDRESS :: (Num a) => a
+eIFLA_ADDRESS = 1
+eIFLA_BROADCAST :: (Num a) => a
+eIFLA_BROADCAST = 2
+eIFLA_IFNAME :: (Num a) => a
+eIFLA_IFNAME = 3
+eIFLA_MTU :: (Num a) => a
+eIFLA_MTU = 4
+eIFLA_LINK :: (Num a) => a
+eIFLA_LINK = 5
+eIFLA_QDISC :: (Num a) => a
+eIFLA_QDISC = 6
+eIFLA_STATS :: (Num a) => a
+eIFLA_STATS = 7
+eIFLA_COST :: (Num a) => a
+eIFLA_COST = 8
+eIFLA_PRIORITY :: (Num a) => a
+eIFLA_PRIORITY = 9
+eIFLA_MASTER :: (Num a) => a
+eIFLA_MASTER = 10
+eIFLA_WIRELESS :: (Num a) => a
+eIFLA_WIRELESS = 11
+eIFLA_PROTINFO :: (Num a) => a
+eIFLA_PROTINFO = 12
+eIFLA_TXQLEN :: (Num a) => a
+eIFLA_TXQLEN = 13
+eIFLA_MAP :: (Num a) => a
+eIFLA_MAP = 14
+eIFLA_WEIGHT :: (Num a) => a
+eIFLA_WEIGHT = 15
+eIFLA_OPERSTATE :: (Num a) => a
+eIFLA_OPERSTATE = 16
+eIFLA_LINKMODE :: (Num a) => a
+eIFLA_LINKMODE = 17
+eIFLA_LINKINFO :: (Num a) => a
+eIFLA_LINKINFO = 18
+eIFLA_NET_NS_PID :: (Num a) => a
+eIFLA_NET_NS_PID = 19
+eIFLA_IFALIAS :: (Num a) => a
+eIFLA_IFALIAS = 20
+newtype AddrFlags = AddrFlags Int deriving (Bits, Eq, Enum, Integral, Num, Ord, Real, Show)
+
+fIFA_F_SECONDARY :: (Num a, Bits a) => a
+fIFA_F_SECONDARY = 1
+fIFA_F_TEMPORARY :: (Num a, Bits a) => a
+fIFA_F_TEMPORARY = 1
+fIFA_F_NODAD :: (Num a, Bits a) => a
+fIFA_F_NODAD = 2
+fIFA_F_OPTIMISTIC :: (Num a, Bits a) => a
+fIFA_F_OPTIMISTIC = 4
+fIFA_F_DADFAILED :: (Num a, Bits a) => a
+fIFA_F_DADFAILED = 8
+fIFA_F_HOMEADDRESS :: (Num a, Bits a) => a
+fIFA_F_HOMEADDRESS = 16
+fIFA_F_DEPRECATED :: (Num a, Bits a) => a
+fIFA_F_DEPRECATED = 32
+fIFA_F_TENTATIVE :: (Num a, Bits a) => a
+fIFA_F_TENTATIVE = 64
+fIFA_F_PERMANENT :: (Num a, Bits a) => a
+fIFA_F_PERMANENT = 128
+newtype Scope = Scope Int deriving (Eq, Enum, Integral, Num, Ord, Real, Show)
+
+eRT_SCOPE_UNIVERSE :: (Num a) => a
+eRT_SCOPE_UNIVERSE = 0
+eRT_SCOPE_SITE :: (Num a) => a
+eRT_SCOPE_SITE = 200
+eRT_SCOPE_LINK :: (Num a) => a
+eRT_SCOPE_LINK = 253
+eRT_SCOPE_HOST :: (Num a) => a
+eRT_SCOPE_HOST = 254
+eRT_SCOPE_NOWHERE :: (Num a) => a
+eRT_SCOPE_NOWHERE = 255
+newtype AddrAttrType = AddrAttrType Int deriving (Eq, Enum, Integral, Num, Ord, Real, Show)
+
+eIFA_UNSPEC :: (Num a) => a
+eIFA_UNSPEC = 0
+eIFA_ADDRESS :: (Num a) => a
+eIFA_ADDRESS = 1
+eIFA_LOCAL :: (Num a) => a
+eIFA_LOCAL = 2
+eIFA_LABEL :: (Num a) => a
+eIFA_LABEL = 3
+eIFA_BROADCAST :: (Num a) => a
+eIFA_BROADCAST = 4
+eIFA_ANYCAST :: (Num a) => a
+eIFA_ANYCAST = 5
+eIFA_CACHEINFO :: (Num a) => a
+eIFA_CACHEINFO = 6
+eIFA_MULTICAST :: (Num a) => a
+eIFA_MULTICAST = 7
+newtype RouteTableId = RouteTableId Int deriving (Eq, Enum, Integral, Num, Ord, Real, Show)
+
+eRT_TABLE_UNSPEC :: (Num a) => a
+eRT_TABLE_UNSPEC = 0
+eRT_TABLE_COMPAT :: (Num a) => a
+eRT_TABLE_COMPAT = 252
+eRT_TABLE_DEFAULT :: (Num a) => a
+eRT_TABLE_DEFAULT = 253
+eRT_TABLE_MAIN :: (Num a) => a
+eRT_TABLE_MAIN = 254
+eRT_TABLE_LOCAL :: (Num a) => a
+eRT_TABLE_LOCAL = 255
+eRT_TABLE_MAX :: (Num a) => a
+eRT_TABLE_MAX = 4294967295
+newtype RouteProto = RouteProto Int deriving (Eq, Enum, Integral, Num, Ord, Real, Show)
+
+eRTPROT_UNSPEC :: (Num a) => a
+eRTPROT_UNSPEC = 0
+eRTPROT_REDIRECT :: (Num a) => a
+eRTPROT_REDIRECT = 1
+eRTPROT_KERNEL :: (Num a) => a
+eRTPROT_KERNEL = 2
+eRTPROT_BOOT :: (Num a) => a
+eRTPROT_BOOT = 3
+eRTPROT_STATIC :: (Num a) => a
+eRTPROT_STATIC = 4
+eRTPROT_GATED :: (Num a) => a
+eRTPROT_GATED = 8
+eRTPROT_RA :: (Num a) => a
+eRTPROT_RA = 9
+eRTPROT_MRT :: (Num a) => a
+eRTPROT_MRT = 10
+eRTPROT_ZEBRA :: (Num a) => a
+eRTPROT_ZEBRA = 11
+eRTPROT_BIRD :: (Num a) => a
+eRTPROT_BIRD = 12
+eRTPROT_DNROUTED :: (Num a) => a
+eRTPROT_DNROUTED = 13
+eRTPROT_XORP :: (Num a) => a
+eRTPROT_XORP = 14
+eRTPROT_NTK :: (Num a) => a
+eRTPROT_NTK = 15
+eRTPROT_DHCP :: (Num a) => a
+eRTPROT_DHCP = 16
+newtype RouteType = RouteType Int deriving (Eq, Enum, Integral, Num, Ord, Real, Show)
+
+eRTN_UNSPEC :: (Num a) => a
+eRTN_UNSPEC = 0
+eRTN_UNICAST :: (Num a) => a
+eRTN_UNICAST = 1
+eRTN_LOCAL :: (Num a) => a
+eRTN_LOCAL = 2
+eRTN_BROADCAST :: (Num a) => a
+eRTN_BROADCAST = 3
+eRTN_ANYCAST :: (Num a) => a
+eRTN_ANYCAST = 4
+eRTN_MULTICAST :: (Num a) => a
+eRTN_MULTICAST = 5
+eRTN_BLACKHOLE :: (Num a) => a
+eRTN_BLACKHOLE = 6
+eRTN_UNREACHABLE :: (Num a) => a
+eRTN_UNREACHABLE = 7
+eRTN_PROHIBIT :: (Num a) => a
+eRTN_PROHIBIT = 8
+eRTN_THROW :: (Num a) => a
+eRTN_THROW = 9
+eRTN_NAT :: (Num a) => a
+eRTN_NAT = 10
+eRTN_XRESOLVE :: (Num a) => a
+eRTN_XRESOLVE = 11
+newtype RouteFlags = RouteFlags Int deriving (Bits, Eq, Enum, Integral, Num, Ord, Real, Show)
+
+fRTM_F_NOTIFY :: (Num a, Bits a) => a
+fRTM_F_NOTIFY = 256
+fRTM_F_CLONED :: (Num a, Bits a) => a
+fRTM_F_CLONED = 512
+fRTM_F_EQUALIZE :: (Num a, Bits a) => a
+fRTM_F_EQUALIZE = 1024
+fRTM_F_PREFIX :: (Num a, Bits a) => a
+fRTM_F_PREFIX = 2048
+newtype RouteAttrType = RouteAttrType Int deriving (Eq, Enum, Integral, Num, Ord, Real, Show)
+
+eRTA_UNSPEC :: (Num a) => a
+eRTA_UNSPEC = 0
+eRTA_DST :: (Num a) => a
+eRTA_DST = 1
+eRTA_SRC :: (Num a) => a
+eRTA_SRC = 2
+eRTA_IIF :: (Num a) => a
+eRTA_IIF = 3
+eRTA_OIF :: (Num a) => a
+eRTA_OIF = 4
+eRTA_GATEWAY :: (Num a) => a
+eRTA_GATEWAY = 5
+eRTA_PRIORITY :: (Num a) => a
+eRTA_PRIORITY = 6
+eRTA_PREFSRC :: (Num a) => a
+eRTA_PREFSRC = 7
+eRTA_METRICS :: (Num a) => a
+eRTA_METRICS = 8
+eRTA_MULTIPATH :: (Num a) => a
+eRTA_MULTIPATH = 9
+eRTA_PROTOINFO :: (Num a) => a
+eRTA_PROTOINFO = 10
+eRTA_FLOW :: (Num a) => a
+eRTA_FLOW = 11
+eRTA_CACHEINFO :: (Num a) => a
+eRTA_CACHEINFO = 12
+eRTA_SESSION :: (Num a) => a
+eRTA_SESSION = 13
+eRTA_MP_ALGO :: (Num a) => a
+eRTA_MP_ALGO = 14
+eRTA_TABLE :: (Num a) => a
+eRTA_TABLE = 15
diff --git a/System/Linux/Netlink/Internal.hs b/System/Linux/Netlink/Internal.hs
new file mode 100644
--- /dev/null
+++ b/System/Linux/Netlink/Internal.hs
@@ -0,0 +1,55 @@
+module System.Linux.Netlink.Internal
+    (
+      query
+    , queryOne
+      
+    , module System.Linux.Netlink.Attributes
+    , module System.Linux.Netlink.C
+    , module System.Linux.Netlink.Constants
+    , module System.Linux.Netlink.Protocol
+    ) where
+
+import Control.Applicative ((<$>))
+import Control.Monad (when)
+import Data.Bits (Bits, (.&.))
+
+import System.Linux.Netlink.Attributes
+import System.Linux.Netlink.C
+import System.Linux.Netlink.Constants
+import System.Linux.Netlink.Protocol
+
+query :: NetlinkSocket -> Packet -> IO [Packet]
+query sock req = do
+    sendmsg sock (putPacket req)
+    recvMulti sock
+
+queryOne :: NetlinkSocket -> Packet -> IO Packet
+queryOne sock req = do
+    sendmsg sock (putPacket req)
+    pkts <- recvMulti sock
+    let len = length pkts
+    when (len /= 1) $ fail ("Expected one packet, received " ++ show len)
+    return $ head pkts
+
+recvMulti :: NetlinkSocket -> IO [Packet]
+recvMulti sock = do
+    pkts <- recvOne sock
+    if isMulti (head pkts)
+        then if isDone (last pkts)
+             then return $ init pkts
+             else (pkts ++) <$> recvMulti sock
+        else return pkts
+  where
+    isMulti = isFlagSet fNLM_F_MULTI . messageFlags . packetHeader
+    isDone  = (== eNLMSG_DONE) . messageType . packetHeader
+
+recvOne :: NetlinkSocket -> IO [Packet]
+recvOne sock = recvmsg sock bufferSize >>= \b -> case (getPacket b) of
+    Left err   -> fail err
+    Right pkts -> return pkts
+
+isFlagSet :: Bits a => a -> a -> Bool
+isFlagSet f v = (f .&. v) == f
+
+bufferSize :: Num a => a
+bufferSize = 8192
diff --git a/System/Linux/Netlink/Protocol.hs b/System/Linux/Netlink/Protocol.hs
new file mode 100644
--- /dev/null
+++ b/System/Linux/Netlink/Protocol.hs
@@ -0,0 +1,191 @@
+module System.Linux.Netlink.Protocol 
+    (
+      Header(..)
+    , Message(..)
+    , Attributes
+    , Packet(..)
+    
+    , getPacket
+    , putPacket
+    ) where
+
+import Prelude hiding (length)
+
+import Control.Applicative ((<$>))
+import Control.Monad (when)
+import Control.Monad.Loops (whileM)
+import Data.ByteString (ByteString, length)
+import Data.Map (Map, fromList, toList)
+import Data.Serialize.Get
+import Data.Serialize.Put
+import Data.Word (Word8, Word16, Word32)
+import Foreign.C.Types (CInt)
+
+import System.Linux.Netlink.Constants
+
+data Header = Header
+    {
+      messageType   :: MessageType
+    , messageFlags  :: Word16
+    , messageSeqNum :: Word32
+    , messagePID    :: Word32
+    } deriving (Eq, Show)
+
+data Message = DoneMsg
+             | ErrorMsg
+    {
+      errorCode    :: CInt
+    , errorPacket  :: Packet
+    }
+             | LinkMsg
+    {
+      interfaceType  :: LinkType
+    , interfaceIndex :: Word32
+    , interfaceFlags :: Word32
+    }
+             | AddrMsg
+    {
+      addrFamily         :: AddressFamily
+    , addrMaskLength     :: Word8
+    , addrFlags          :: Word8
+    , addrScope          :: Word8
+    , addrInterfaceIndex :: Word32
+    } deriving (Eq, Show)
+
+type Attributes = Map Int ByteString
+
+data Packet = Packet
+    {
+      packetHeader     :: Header 
+    , packetMessage    :: Message
+    , packetAttributes :: Attributes
+    } deriving (Eq, Show)
+
+--
+-- Packet decoding
+--
+getPacket :: ByteString -> Either String [Packet]
+getPacket bytes = flip runGet bytes $ do
+    pkts <- whileM (not <$> isEmpty) getPacketInternal
+    isEmpty >>= \e -> when (not e) $ fail "Incomplete message parse"
+    return pkts
+
+getPacketInternal :: Get Packet
+getPacketInternal = do
+    len <- fromIntegral <$> g32
+    isolate (len - 4) $ do
+        header <- getHeader
+        msg    <- getMessage (messageType header)
+        attrs <- whileM (not <$> isEmpty) getSingleAttribute
+        return $ Packet header msg (fromList attrs)
+
+getHeader :: Get Header
+getHeader = do
+    ty     <- fromIntegral <$> g16
+    flags  <- fromIntegral <$> g16
+    seqnum <- g32
+    pid    <- g32
+    return $ Header ty flags seqnum pid
+
+getMessage :: MessageType -> Get Message
+getMessage msgtype | msgtype == eNLMSG_DONE  = skip 4 >> return DoneMsg
+                   | msgtype == eNLMSG_ERROR = getMessageError
+                   | msgtype == eRTM_NEWLINK = getMessageLink
+                   | msgtype == eRTM_GETLINK = getMessageLink
+                   | msgtype == eRTM_DELLINK = getMessageLink
+                   | msgtype == eRTM_NEWADDR = getMessageAddr
+                   | msgtype == eRTM_GETADDR = getMessageAddr
+                   | msgtype == eRTM_DELADDR = getMessageAddr
+                   | otherwise               =
+                       error $ "Can't decode message " ++ show msgtype
+
+getMessageError :: Get Message
+getMessageError = do
+    code <- abs . fromIntegral <$> g32
+    pkt  <- getPacketInternal
+    return $ ErrorMsg code pkt
+
+getMessageLink :: Get Message
+getMessageLink = do
+    skip 2
+    ty    <- fromIntegral <$> g16
+    idx   <- g32
+    flags <- g32
+    skip 4
+    return $ LinkMsg ty idx flags
+
+getMessageAddr :: Get Message
+getMessageAddr = do
+    fam <- fromIntegral <$> g8
+    maskLen <- g8
+    flags <- g8
+    scope <- fromIntegral <$> g8
+    idx <- g32
+    return $ AddrMsg fam maskLen flags scope idx
+
+getSingleAttribute :: Get (Int, ByteString)
+getSingleAttribute = do
+    len <- fromIntegral <$> g16
+    ty <- fromIntegral <$> g16
+    val <- getByteString (len - 4)
+    isEmpty >>= \e -> when (not e && len `mod` 4 /= 0) $ skip (4 - (len `mod` 4))
+    return (ty, val)
+
+--
+-- Packet serialization
+--
+putPacket :: Packet -> [ByteString]
+putPacket (Packet header message attributes) =
+    let attrs  = runPut $ putAttributes attributes
+        msg    = runPut $ putMessage message
+        hdr = runPut $ putHeader (length msg + length attrs + 16) header
+    in [hdr, msg, attrs]
+
+putHeader :: Int -> Header -> Put
+putHeader len (Header ty flags seqnum pid) = do
+    p32 (fromIntegral len)
+    p16 (fromIntegral ty)
+    p16 (fromIntegral flags)
+    p32 seqnum
+    p32 pid
+
+putMessage :: Message -> Put
+putMessage DoneMsg = p32 0
+putMessage (LinkMsg ty idx flags) = do
+    p8 eAF_UNSPEC >> p8 0
+    p16 (fromIntegral ty)
+    p32 idx
+    p32 flags
+    p32 0xFFFFFFFF
+putMessage (AddrMsg fam maskLen flags scope idx) = do
+    p8 (fromIntegral fam)
+    p8 maskLen
+    p8 flags
+    p8 (fromIntegral scope)
+    p32 idx
+putMessage _ = error "Can't transmit this message to the kernel."
+
+putAttributes :: Attributes -> Put
+putAttributes = mapM_ putAttr . toList
+  where
+    putAttr (ty, value) = do
+        p16 (fromIntegral ty)
+        p16 (fromIntegral . length $ value)
+        putByteString value
+
+--
+-- Helpers
+--
+p8 :: Word8 -> Put
+p8  = putWord8
+p16 :: Word16 -> Put
+p16 = putWord16host
+p32 :: Word32 -> Put
+p32 = putWord32host
+
+g8 :: Get Word8
+g8  = getWord8
+g16:: Get Word16
+g16 = getWord16host
+g32 :: Get Word32
+g32 = getWord32host
diff --git a/netlink.cabal b/netlink.cabal
new file mode 100644
--- /dev/null
+++ b/netlink.cabal
@@ -0,0 +1,35 @@
+Name:                netlink
+Version:             0.1
+Synopsis:            Netlink communication for Haskell
+Description:         Library to comminicate with Linux kernel via Netlink
+Homepage:            http://netlink-hs.googlecode.com/
+License:             BSD3
+License-file:        LICENSE
+Author:              David Anderson
+Maintainer:          Max Treskin <mtreskin@metachord.com>
+Copyright:           Google Inc. 2010
+Stability:           Alpha
+Category:            System
+Build-type:          Simple
+Cabal-version:       >=1.6
+
+Source-repository head
+  type:       git
+  location:   https://github.com/metachord/netlink-hs
+
+Library
+  Exposed-modules:   System.Linux.Netlink
+                     System.Linux.Netlink.Internal
+  Other-modules:     System.Linux.Netlink.C
+                     System.Linux.Netlink.Constants
+                     System.Linux.Netlink.Protocol
+                     System.Linux.Netlink.Attributes
+  Build-depends:     base >=4 && <5,
+                     bytestring >=0.9,
+                     cereal >=0.3 && <0.4,
+                     containers >=0.3,
+                     monad-loops >=0.3,
+                     unix >=2.4
+  Build-tools:       c2hs > 0.16
+  Ghc-options:       -Wall -fwarn-tabs
+  Ghc-prof-options:  -auto-all
