MatrixAI / netlink-hs

Haskell Netlink Library
https://matrix.ai
BSD 3-Clause "New" or "Revised" License
0 stars 0 forks source link

Rework the constants generation #1

Closed CMCDragonkai closed 4 years ago

CMCDragonkai commented 5 years ago

Currently netlink-hs generate constants as actual numbers. I want to change to using ADTs so I can pattern match on them.

However this means we need to derive the Enum typeclass for the ADTs. This would be simple except the numbers don't exactly match the constant names.

For example, these 3 have the same value:

AF_FILE = 1
AF_LOCAL = 1
AF_UNIX = 1

And in fact, AF_LOCAL should be shown, as that's the primary name, and all the other names are synonyms for AF_LOCAL.

So ideally we would have:

instance Enum AddressFamily where
  toEnum 1 = AF_LOCAL
  fromEnum AF_FILE = 1
  fromEnum AF_LOCAL = 1
  fromEnum AF_UNIX = 1

But of course from the generation code, we don't know which one is meant to be the primary name. So we just have to take the first one as is.

This also is true for flags, which also can be defined out of order.

All of this can come from #define constants in C, or from actual enums.

CMCDragonkai commented 5 years ago

I'm coming up to this:

derivingEnum :: String -> Map String Integer -> String
derivingEnum name vals =
  "instance Enum " ++ name ++ " where\n" ++ toEnums ++ fromEnums
  where
    values = sortBy (compare `on` snd) $ toList vals
    toEnums = concatMap
      (\(k, v) -> "  toEnum " ++ show v ++ " = " ++ k ++ "\n") $
      nubBy ((==) `on` snd) values
    fromEnums = concatMap
      (\(k, v) -> "  fromEnum " ++ k ++ " = " ++ show v ++ "\n") $
      values

Need to plug it into the mkEnums, so we get proper enum derivation as well.

CMCDragonkai commented 5 years ago

It works:

data AddressFamily = AF_UNSPEC
                   | AF_FILE
                   | AF_LOCAL
                   | AF_UNIX
                   | AF_INET
                   | AF_AX25
                   | AF_IPX
                   | AF_APPLETALK
                   | AF_NETROM
                   | AF_BRIDGE
                   | AF_ATMPVC
                   | AF_X25
                   | AF_INET6
                   | AF_ROSE
                   | AF_DECnet
                   | AF_NETBEUI
                   | AF_SECURITY
                   | AF_KEY
                   | AF_NETLINK
                   | AF_ROUTE
                   | AF_PACKET
                   | AF_ASH
                   | AF_ECONET
                   | AF_ATMSVC
                   | AF_RDS
                   | AF_SNA
                   | AF_IRDA
                   | AF_PPPOX
                   | AF_WANPIPE
                   | AF_LLC
                   | AF_IB
                   | AF_MPLS
                   | AF_CAN
                   | AF_TIPC
                   | AF_BLUETOOTH
                   | AF_IUCV
                   | AF_RXRPC
                   | AF_ISDN
                   | AF_PHONET
                   | AF_IEEE802154
                   | AF_CAIF
                   | AF_ALG
                   | AF_NFC
                   | AF_VSOCK
                   | AF_KCM
                   | AF_QIPCRTR
                   | AF_SMC
                   | AF_MAX
                   deriving (Eq, Show)

instance Enum AddressFamily where
  toEnum 0 = AF_UNSPEC
  toEnum 1 = AF_FILE
  toEnum 2 = AF_INET
  toEnum 3 = AF_AX25
  toEnum 4 = AF_IPX
  toEnum 5 = AF_APPLETALK
  toEnum 6 = AF_NETROM
  toEnum 7 = AF_BRIDGE
  toEnum 8 = AF_ATMPVC
  toEnum 9 = AF_X25
  toEnum 10 = AF_INET6
  toEnum 11 = AF_ROSE
  toEnum 12 = AF_DECnet
  toEnum 13 = AF_NETBEUI
  toEnum 14 = AF_SECURITY
  toEnum 15 = AF_KEY
  toEnum 16 = AF_NETLINK
  toEnum 17 = AF_PACKET
  toEnum 18 = AF_ASH
  toEnum 19 = AF_ECONET
  toEnum 20 = AF_ATMSVC
  toEnum 21 = AF_RDS
  toEnum 22 = AF_SNA
  toEnum 23 = AF_IRDA
  toEnum 24 = AF_PPPOX
  toEnum 25 = AF_WANPIPE
  toEnum 26 = AF_LLC
  toEnum 27 = AF_IB
  toEnum 28 = AF_MPLS
  toEnum 29 = AF_CAN
  toEnum 30 = AF_TIPC
  toEnum 31 = AF_BLUETOOTH
  toEnum 32 = AF_IUCV
  toEnum 33 = AF_RXRPC
  toEnum 34 = AF_ISDN
  toEnum 35 = AF_PHONET
  toEnum 36 = AF_IEEE802154
  toEnum 37 = AF_CAIF
  toEnum 38 = AF_ALG
  toEnum 39 = AF_NFC
  toEnum 40 = AF_VSOCK
  toEnum 41 = AF_KCM
  toEnum 42 = AF_QIPCRTR
  toEnum 43 = AF_SMC
  toEnum 44 = AF_MAX
  fromEnum AF_UNSPEC = 0
  fromEnum AF_FILE = 1
  fromEnum AF_LOCAL = 1
  fromEnum AF_UNIX = 1
  fromEnum AF_INET = 2
  fromEnum AF_AX25 = 3
  fromEnum AF_IPX = 4
  fromEnum AF_APPLETALK = 5
  fromEnum AF_NETROM = 6
  fromEnum AF_BRIDGE = 7
  fromEnum AF_ATMPVC = 8
  fromEnum AF_X25 = 9
  fromEnum AF_INET6 = 10
  fromEnum AF_ROSE = 11
  fromEnum AF_DECnet = 12
  fromEnum AF_NETBEUI = 13
  fromEnum AF_SECURITY = 14
  fromEnum AF_KEY = 15
  fromEnum AF_NETLINK = 16
  fromEnum AF_ROUTE = 16
  fromEnum AF_PACKET = 17
  fromEnum AF_ASH = 18
  fromEnum AF_ECONET = 19
  fromEnum AF_ATMSVC = 20
  fromEnum AF_RDS = 21
  fromEnum AF_SNA = 22
  fromEnum AF_IRDA = 23
  fromEnum AF_PPPOX = 24
  fromEnum AF_WANPIPE = 25
  fromEnum AF_LLC = 26
  fromEnum AF_IB = 27
  fromEnum AF_MPLS = 28
  fromEnum AF_CAN = 29
  fromEnum AF_TIPC = 30
  fromEnum AF_BLUETOOTH = 31
  fromEnum AF_IUCV = 32
  fromEnum AF_RXRPC = 33
  fromEnum AF_ISDN = 34
  fromEnum AF_PHONET = 35
  fromEnum AF_IEEE802154 = 36
  fromEnum AF_CAIF = 37
  fromEnum AF_ALG = 38
  fromEnum AF_NFC = 39
  fromEnum AF_VSOCK = 40
  fromEnum AF_KCM = 41
  fromEnum AF_QIPCRTR = 42
  fromEnum AF_SMC = 43
  fromEnum AF_MAX = 44
CMCDragonkai commented 5 years ago

Again we can see that we are defaulting to the first name appearing to be the "primary name". So that's AF_FILE instead of what glibc says it is, which is AF_LOCAL.

CMCDragonkai commented 5 years ago

Both flags and enums are now standardised to ADTs with Enum typeclasses.

Figuring out the best names for the all the ADTs is still a bit of a challenge as the C names aren't really modularised.

CMCDragonkai commented 5 years ago

One thing is that I've separated out the RTM_ from the NLMSG_. This means the way in which the family types is worked out needs to be done differently.

CMCDragonkai commented 5 years ago

@ramwan

CMCDragonkai commented 5 years ago

The other thing is that there are currently 3 generation scripts:

I'm not sure what to use those 2 other ones for. So for now I'm removing them. Unless you have something to add @ramwan?

So we will only have 1 constants file to deal with for now.

ramwan commented 5 years ago

I'm not quite comfortable with how the toEnum s only work on a single constructor from the AddressFamily type. Even though in your example AF_UNIX and AF_LOCAL have the same value, it may be confusing to future users who read documentation specifying AF_LOCAL and cannot find it in the constants list. We will need to use generic netlink (the generategenl.hs?) for wireguard and maybe any other kernel modules we load in.

Rather than using toEnum, we might have to use another function that pattern matches on the constructors. It'll be some real big functions.

CMCDragonkai commented 5 years ago

This problem existed in the original netlink code. Its just fundamentally ambiguous.

On 10 May 2019 13:46:16 GMT+10:00, ramwan notifications@github.com wrote:

I'm not quite comfortable with how the toEnum s only work on a single constructor from the AddressFamily type. Even though in your example AF_UNIX and AF_LOCAL have the same value, it may be confusing to future users who read documentation specifying AF_LOCAL and cannot find it in the constants list.We will need to use generic netlink (the generategenl.hs?) for wireguard and maybe any other kernel modules we load in.

-- You are receiving this because you authored the thread. Reply to this email directly or view it on GitHub: https://github.com/MatrixAI/netlink-hs/issues/1#issuecomment-491145304

-- Sent from my Android device with K-9 Mail. Please excuse my brevity.

ramwan commented 5 years ago

are we able to restructure it and fix this issue or is that too much work

CMCDragonkai commented 5 years ago

Well the problem is that conversion from number to "name" is fundamentally ambiguous so we have to choose one. So here we are just choosing the first one.

On 10 May 2019 16:38:09 GMT+10:00, ramwan notifications@github.com wrote:

are we able to restructure it and fix this issue or is that too much work

-- You are receiving this because you authored the thread. Reply to this email directly or view it on GitHub: https://github.com/MatrixAI/netlink-hs/issues/1#issuecomment-491174724

-- Sent from my Android device with K-9 Mail. Please excuse my brevity.

CMCDragonkai commented 5 years ago

I counted that there are 21 official netlink families. And apparently a total limit of 32 families. So that's why everything else uses NETLINK_GENERIC.

These are the families that may be relevant to us:

  1. NETLINK_ROUTE
  2. NETLINK_INET_DIAG
  3. NETLINK_NFLOG
  4. NETLINK_NETFILTER
  5. NETLINK_GENERIC - https://wiki.linuxfoundation.org/networking/generic_netlink_howto

What do you think @ramwan?

CMCDragonkai commented 5 years ago

The gen netlink architecture is:

     +---------------------+      +---------------------+
     | (3) application "A" |      | (3) application "B" |
     +------+--------------+      +--------------+------+
            |                                    |
            \                                    /
             \                                  /
              |                                |
      +-------+--------------------------------+-------+
      |        :                               :       |   user-space
 =====+        :   (5)  kernel socket API      :       +================
      |        :                               :       |   kernel-space
      +--------+-------------------------------+-------+
               |                               |
         +-----+-------------------------------+----+
         |        (1)  Netlink subsystem            |
         +---------------------+--------------------+
                               |
         +---------------------+--------------------+
         |       (2) Generic Netlink bus            |
         +--+--------------------------+-------+----+
            |                          |       |
    +-------+---------+                |       |
    |  (4) controller |               /         \
    +-----------------+              /           \
                                     |           |
                  +------------------+--+     +--+------------------+
                  | (3) kernel user "X" |     | (3) kernel user "Y" |
                  +---------------------+     +---------------------+

Generic Netlink communications are essentially a series of different communication channels which are multiplexed on a single Netlink family. Communication channels are uniquely identified by channel numbers which are dynamically allocated by the Generic Netlink controller. The controller is a special Generic Netlink user which listens on a fixed communication channel, number 0x10, which is always present. Kernel or userspace users which provide services over the Generic Netlink bus establish new communication channels by registering their services with the Generic Netlink controller. Users who want to use a service query the controller to see if the service exists and to determine the correct channel number.

The control constants are the main default constants for gen netlink. Then each service within gennetlink has their own set of constants. As we can see in the upstream repo, that's NL80211. But we're not really interested in those.

I've added genetlink.h and added 4 extra constant ADTs for the genetlink control service.

CMCDragonkai commented 4 years ago

Constants are now generated into ADTs. The only thing left is to add in all relevant family constants into the single generation script.