netlink 編程介紹

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; /* 發送進程號 PID */
};

所有發送給內核或者內核的報文的第一部分都必須使用這個機構,後面跟隨相應的內容。nlmsg_type爲後面消息的內容個數,對於前面我們提到的不同通訊協議有着不同的消息類型。下面是三個通用的消息類型

  • NLMSG_NOOP 這個消息類型表示消息內容爲空,應用可以忽略該報文
  • NLMSG_ERROR 這個消息類型表示後面的消息是一個錯誤信息,錯誤信息的機構爲nlmsgerrstruct nlmsgerr
    {
    int error; /* 負數表示的出錯號 errno 或爲 0 要求確認 acks*/
    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:獲取數據的長度
在我們後面的實例中會介紹如何使用這幾個宏。

在socket程序中,如果我們要求接收報文則要求調用bind,表示我們期望接收什麼樣的報文。對於netlink也一樣,我們要求指定我們期望接收的地址信息,不過同傳統的sockaddr不同,這個地方是一個sockaddr_nl的結構:

struct sockaddr_nl
{
sa_family_t nl_family; /* AF_NETLINK */
unsigned short nl_pad; /* 用來填充的字段,賦值爲0 */
pid_t nl_pid; /* 進程標識號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; /* AF_UNSPEC */
unsigned short ifi_type; /* Device type */
int ifi_index; /* Interface index */
unsigned int ifi_flags; /* Device flags */
unsigned int ifi_change; /* change mask */
};
struct rtattr
{
unsigned short rta_len; /* Length of option */
unsigned short rta_type; /* Type of option */
/* Data follows */
};
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 協議介紹

發佈了7 篇原創文章 · 獲贊 11 · 訪問量 22萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章