天天看點

netlink 程式設計介紹 netlink基礎知識 nlmsghdr結構介紹 解析nlmsghdr資料 TODO:還沒有找到資料 TODO:還沒有找到資料 TODO:還沒有找到資料 TODO:還沒有找到資料 TODO:還沒有找到資料

Linux從2.2開始支援PF_NETLINK 域的通訊方式,這個方式主要的用途是在Linux的核心空間和使用者空間進行通訊。目前在網絡上面關于netlink程式設計的中文資料很少,為了促進對 netlink程式設計的了解我編寫了這篇文章,由于我對netlink的了解不是很透徹,特别是對于核心部分不是很熟悉,是以文章中肯定有很多錯誤的地方還請大家指正。文章分下面幾個部分進行講述
  1. netlink 基礎知識
  2. nlmsghdr 結構介紹
  3. 解析nlmsghdr資料
  4. sockaddr_nl 結構介紹
  5. NETLINK_ROUTE 協定介紹
  6. NETLINK_SKIP 協定介紹
  7. NETLINK_USERSOCK協定介紹
  8. NETLINK_FIREWALL 協定介紹
  9. NETLINK_TCPDIAG 協定介紹
  10. NETLINK_NFLOG 協定介紹
  11. NETLINK_ARPD 協定介紹
  12. NETLINK_ROUTE6 協定介紹
  13. NETLINK_IP6_FW 協定介紹
  14. NETLINK_DNRTMSG 協定介紹
  15. NETLINK_TAPBASE 協定介紹
  16. 參考資料
  17. 版權說明
  18. 修改記錄
    • netlink基礎知識
    • nlmsghdr結構介紹
    • 解析nlmsghdr資料

    我們在使用socket(2)的man手冊時候可以找到man手冊中有下面一行說明

    PF_NETLINK Kernel user interface device netlink(7)

    在我們通過PF_NETLINK建立一個SOCKET以後表示我們期望同核心進行消息通訊。使用netlink(7)的手冊可以看到關于PF_NETLINK的詳細說明。

    #include <asm/types.h>

    #include <sys/socket.h>

    #include <linux/netlink.h>

    netlink_socket = socket(PF_NETLINK, socket_type, netlink_family);

    按照netlink的手冊,socket_type可以取SOCK_RAW和SOCK_DGRAM,不過核心不區分這兩個字段。netlink_family字段指定了我們期望的通訊協定,主要有:

    • NETLINK_ROUTE 用來擷取,建立和修改裝置的各種資訊,詳細參見 rtnetlink(7)
    • NETLINK_SKIP Enskip 的保留選項
    • NETLINK_USERSOCK 為今後使用者程式空間協定用保留選項
    • NETLINK_FIREWALL 接收 IPv4 防火牆編碼發送的資料包
    • NETLINK_TCPDIAG TCP套接字監控
    • NETLINK_NFLOG netfilter的使用者空間日志
    • NETLINK_ARPD 用以維護使用者位址空間裡的 arp 表
    • NETLINK_ROUTE6 接收和發送 IPv6 路由表更新消息
    • NETLINK_IP6_FW 接收未通過 IPv6 防火牆檢查的資料包(尚未實作)
    • NETLINK_TAPBASE 是 ethertap 裝置執行個體
    後面我們會對每一個協定進行解釋和說明.

    每一個發送給核心或者從核心介紹的封包都有一個相同的封包頭,這個封包頭的結構如下定義:

    struct nlmsghdr

    {

    __u32 nlmsg_len;

    __u16 nlmsg_type;

    __u16 nlmsg_flags;

    __u32 nlmsg_seq;

    __u32 nlmsg_pid;

    };

    所有發送給核心或者核心的封包的第一部分都必須使用這個機構,後面跟随相應的内容。nlmsg_type為後面消息的内容個數,對于前面我們提到的不同通訊協定有着不同的消息類型。下面是三個通用的消息類型

    • NLMSG_NOOP 這個消息類型表示消息内容為空,應用可以忽略該封包
    • NLMSG_ERROR 這個消息類型表示後面的消息是一個錯誤資訊,錯誤資訊的機構為nlmsgerrstruct nlmsgerr

      {

      int error;

      struct nlmsghdr msg;

      };

    • NLMSG_DONE 在我們接收或者發送消息給核心的時候,我們有可能一次發送多個封包,這個消息類型表示是封包的最後一個,類似于在連結清單中我們将最後一個成員的next指針設定為NULL。
    附加的标志用于控制或者表示消息的其它資訊,一些比較通用的标志是
    • NLM_F_REQUEST 表示這個消息是一個請求消息,這個消息可以同以下一個标志組合
      • NLM_F_ROOT 傳回樹的根
      • NLM_F_MATCH 傳回所有比對的
      • NLM_F_ATOMIC 傳回對象表的單一快照
      • NLM_F_DUMP 被定義為NLM_F_ROOT|NLM_F_MATCH
      • NLM_F_REPLACE 表示替換現有的規則
      • NLM_F_EXCL 如果現有規則存在則不修改
      • NLM_F_CREAT 建立一個規則
      • NLM_F_APPEND 追加一個規則
    • NLM_F_MULTI 表示這個消息是多個封包中的一個,封包的結尾通過NLMSG_DONE來表示
    • NLM_F_ACK 表示這個消息是一個應答消息
    • NLM_F_ECHO 表示這個消息是一個要求傳回請求資訊的消息

    為了擷取netlink封包中資料的友善,netlink提供了下面幾個宏進行資料的擷取和解包操作

    #include <asm/types.h>

    #include <linux/netlink.h>

    int NLMSG_ALIGN(size_t len);

    int NLMSG_LENGTH(size_t len);

    int NLMSG_SPACE(size_t len);

    void *NLMSG_DATA(struct nlmsghdr *nlh);

    struct nlmsghdr *NLMSG_NEXT(struct nlmsghdr *nlh, int len);

    int NLMSG_OK(struct nlmsghdr *nlh, int len);

    int NLMSG_PAYLOAD(struct nlmsghdr *nlh, int len);

    NLMSG_ALIGN:進行資料長度的對齊操作

    NLMSG_DATA:擷取通訊封包中的資料

    NLMSG_NEXT:擷取下一個封包

    NLMSG_OK:判斷是否資料可以繼續擷取

    NLMSG_PAYLOAD:擷取資料的長度

    在我們後面的執行個體中會介紹如何使用這幾個宏。

  1. 在socket程式中,如果我們要求接收封包則要求調用bind,表示我們期望接收什麼樣的封包。對于netlink也一樣,我們要求指定我們期望接收的位址資訊,不過同傳統的sockaddr不同,這個地方是一個sockaddr_nl的結構:

    struct sockaddr_nl

    {

    sa_family_t nl_family;

    unsigned short nl_pad;

    pid_t nl_pid;

    __u32 nl_groups;

    };

    每一個 netlink 資料類都有一個32位廣播分組,當對套接字調用 bind(2) 時, sockaddr_nl 中的 nl_groups 字段設定成所要偵聽的廣播組的位掩碼。其預設值為 0,表示不接收任何廣播,我們會在後面中看到如何使用這個廣播組的例子。

    • NETLINK_ROUTE協定介紹
      • netlink目前使用最廣泛的是通過這個選項來擷取網絡裝置或者網址的一些資訊。在使用這個協定時候支援的類型有:
      • RTM_NEWLINK, RTM_DELLINK, RTM_GETLINK 建立,删除或者擷取網絡裝置的資訊
      • RTM_NEWADDR, RTM_DELADDR, RTM_GETADDR 建立,删除或者擷取網絡裝置的IP資訊
      • RTM_NEWROUTE, RTM_DELROUTE, RTM_GETROUTE 建立,删除或者擷取網絡裝置的路由資訊
      • RTM_NEWNEIGH, RTM_DELNEIGH, RTM_GETNEIGH 建立,删除或者擷取網絡裝置的相鄰資訊
      • RTM_NEWRULE, RTM_DELRULE, RTM_GETRULE 建立,删除或者擷取路由規則資訊
      • RTM_NEWQDISC, RTM_DELQDISC, RTM_GETQDISC 建立,删除或者擷取隊列的原則
      • RTM_NEWTCLASS, RTM_DELTCLASS, RTM_GETTCLASS 建立,删除或者擷取流量的類别
      • RTM_NEWTFILTER, RTM_DELTFILTER, RTM_GETTFILTER 建立,删除或者擷取流量的過慮

      由于NETLINK_ROUTE支援的類型實在太多了,我們這個地方隻重點介紹一下RTM_GETLINK這個類型,然後通過一個例子介紹如何同核心進行通訊。關于其它類型的用法大家可以參考rtnetlink(7)的man手冊。

      按照rtnetlink的說法,在擷取裝置資訊的時候我們要求首先發送一個封包給核心表示我們的請求,這個封包的格式是1個nlmsghdr頭部機構+1個ifinfomsg接口結構+多個rtattr屬性機構。其中後面兩個結構的定義是:

      struct ifinfomsg

      {

      unsigned char ifi_family;

      unsigned short ifi_type;

      int ifi_index;

      unsigned int ifi_flags;

      unsigned int ifi_change;

      };

      struct rtattr

      {

      unsigned short rta_len;

      unsigned short rta_type;

      };

      ifi_type:這個字段包含了硬體的類型,可以參考<linux/if_arp.h>比較常見的是 ARPHRD_ETHER 10M以太網

      ARPHRD_PPP PPP撥号

      ARPHRD_LOOPBACK 環路裝置

      ifi_flags:這個字段包含了裝置的一些标志,相應的值為: IFF_UP 接口正在運作.

      IFF_BROADCAST 有效的廣播位址集.

      IFF_DEBUG 内部調試标志.

      IFF_LOOPBACK 這是自環接口.

      IFF_POINTOPOINT 這是點到點的鍊路接口.

      IFF_RUNNING 資源已配置設定.

      IFF_NOARP 無arp協定, 沒有設定第二層目的位址.

      IFF_PROMISC 接口為雜湊(promiscuous)模式.

      IFF_NOTRAILERS 避免使用trailer .

      IFF_ALLMULTI 接收所有多點傳播(multicast)封包.

      IFF_MASTER 主負載平衡群(bundle).

      IFF_SLAVE 從負載平衡群(bundle).

      IFF_MULTICAST 支援多點傳播(multicast).

      IFF_PORTSEL 可以通過ifmap選擇媒體(media)類型.

      IFF_AUTOMEDIA 自動選擇媒體.

      IFF_DYNAMIC 接口關閉時丢棄位址.

      rta_type:這個字段指定屬性的類型,相應的值為: IFLA_UNSPEC 後面的資料格式未指定

      IFLA_ADDRESS 後面的資料是一個硬體位址

      IFLA_BROADCAST 後面的資料是一個硬體廣播位址

      IFLA_IFNAME 後面的資料是一個char型的裝置名稱

      IFLA_MTU unsigned int型的裝置MTU值

      IFLA_LINK int型的鍊路類型

      IFLA_QDISC 字元串型的隊列規則

      IFLA_STATS struct net_device_stats型的裝置資訊

      在我們接收和發送資料的時候為了我們擷取屬性的友善,rtnetlink提供了一個宏供我們取擷取其中的結構,這些宏的定義為: #include <asm/types.h>

      #include <linux/netlink.h>

      #include <linux/rtnetlink.h>

      #include <sys/socket.h>

      int RTA_OK(struct rtattr *rta, int rtabuflen);

      void *RTA_DATA(struct rtattr *rta);

      unsigned int RTA_PAYLOAD(struct rtattr *rta);

      struct rtattr *RTA_NEXT(struct rtattr *rta, unsigned int rtabuflen);

      unsigned int RTA_LENGTH(unsigned int length);

      unsigned int RTA_SPACE(unsigned int length);

    • TODO:還沒有找到資料

      TODO:還沒有找到資料

      TODO:還沒有找到資料

      TODO:還沒有找到資料

      TODO:還沒有找到資料

      1. Linux 系統核心空間與使用者空間通信的實作與分析 這篇文章是陳鑫在IBM中國開發網站上的一篇文章,讨論了在LINUX下如何進行核心空間和使用者空間通訊。
      2. Netlink Sockets Tour 這是一篇英文的介紹Linux核心中是如何實作netlink通訊的文章。
      3. Netlink Sockets - Overview 這是一篇介紹netlink程式設計入門的英文資料,介紹了一些基礎性的東西。
      4. Understanding And Programming With Netlink Sockets 一篇介紹netlink程式的英文文章,介紹的非常詳細。
      5. Linux Netlink as an IP Services Protocol 這個可能是最詳細介紹netlink之間通訊協定封包的一篇RFC文檔,詳細的描述了每一個協定的通訊封包格式。
      6. NETLINK中文MAN手冊 CMPP已經将netlink(7)的man手冊翻譯成中文了,不過現在找不到這個的html版,隻找到了一個doc版。
      7. IPROUTE 路由工具 IPROUTE路由工具的源代碼中有大量的關于netlink的ROUTE代碼可以參考,同時也提供了一個libnetlink庫用來進行操作。
      8. IPTABLES IPTABLES是一個Linux下強有力的防火牆工具。在IPTABLES的源代碼中有大量的關于netlink程式設計的FIREWALL代碼可以參考,同時也提供了一個libipq庫來進行操作。
      9. ULOG 使用者層日志 ULOG通過使用netlink的NFLOG功能,在使用者層進行了通訊封包的日志記錄功能,您可以參考ULOG的源代碼來了解NFLOG的使用
    • NETLINK_ROUTE6 協定介紹
    • NETLINK_IP6_FW 協定介紹
    • NETLINK_DNRTMSG 協定介紹
    • NETLINK_TAPBASE 協定介紹
    • 參考資料
    • NETLINK_ARPD 協定介紹

繼續閱讀