Netlink 介绍(译)
原文地址:http://people.redhat.com/nhorman/papers/netlink.pdf
?
譯文:
1 介紹
在Linux和Unix的眾多發行版中的網絡配置功能, 都是編程者事后需求的功能, 導致像添加路由、鄰居表條目和配置接口等功能有著很多雜亂的方法, 比如raw socket, ioctl調用以及專門的偽網絡協議等方法。在Linux 2.4內核中, 開發者努力實現了一種更標準化的配置網絡的方法。這種方法被命名為netlink sockets, 它旨在創建一個適合所有網絡控制方面的通信框架,雖然建立的netlink子系統不是完善的, 但是這是一種新的網絡配置方法, 也是可靠的基礎。此文檔旨在介紹如何使用netlink socket族和其實現的協議。
? ? 本文假設讀者有C和socket編程的基礎。
2 Netlink 地址族
2.1 socket創建
netlink地址族使用標準的BSD socket API作為用戶空間程序和內核交互的使者。創建一個netlink套接字和創建其它套接字是類似的方式。
socket fd=socket(AF_NETLINK, SOCK_RAW, protocol);地址族參數總是AF_NETLINK, 并且類型值總是SOCK_RAW, 唯一可變的參數 就是協議protocol域, 此域將會繼續添加可選項, 增加了它的可配置性, 下表是protocol的可選項(來自linux-2.6.32 kernel)。
#define NETLINK_ROUTE 0 /* Routing/device hook */ #define NETLINK_UNUSED 1 /* Unused number */ #define NETLINK_USERSOCK 2 /* Reserved for user mode socket protocols */ #define NETLINK_FIREWALL 3 /* Firewalling hook */ #define NETLINK_INET_DIAG 4 /* INET socket monitoring */ #define NETLINK_NFLOG 5 /* netfilter/iptables ULOG */ #define NETLINK_XFRM 6 /* ipsec */ #define NETLINK_SELINUX 7 /* SELinux event notifications */ #define NETLINK_ISCSI 8 /* Open-iSCSI */ #define NETLINK_AUDIT 9 /* auditing */ #define NETLINK_FIB_LOOKUP 10 #define NETLINK_CONNECTOR 11 #define NETLINK_NETFILTER 12 /* netfilter subsystem */ #define NETLINK_IP6_FW 13 #define NETLINK_DNRTMSG 14 /* DECnet routing messages */ #define NETLINK_KOBJECT_UEVENT 15 /* Kernel messages to userspace */ #define NETLINK_GENERIC 16 /* leave room for NETLINK_DM (DM Events) */ #define NETLINK_SCSITRANSPORT 18 /* SCSI Transports */ #define NETLINK_ECRYPTFS 19#define NETLINK_L2TP 20#if defined(CONFIG_RTL_819X) #define NETLINK_RTK_DEBUG 21 #define NETLINK_RTK_FILTER 22 #define NETLINK_MULTICAST_DELETE 23 #define NETLINK_RTK_FB 24 #define NETLINK_RTK_HW_QOS 25 #endif2.2 發送和接收數據包
netlink套接字是無連接的, 收發的數據報就表示類似UDP套接字, 發送數據報通過sendto或者sendmsg系統調用, 用recvfrom或者recvmsg接收數據報。 注意, netlink套接字不使用send和recv交互, 這是因為netlink套接字是無連接的。就像UDP套接字, netlink消息是數據報格式, 雖然netlink消息頭部有一些機制設計用于編程者增加協議的可靠性, 但仍舊不能保證連接是可靠的。
2.3 netlink套接字地址結構
struct sockaddr_nl 是它的地址結構, 用于netlink套接字的接收和發送, 定義如下:
struct sockaddr_nl {sa_family_t nl_family; /* AF_NETLINK */unsigned short nl_pad; /* zero */__u32 nl_pid; /* port ID */__u32 nl_groups; /* multicast groups mask */ };- nl_family:此域定義了消息的地址族, 應該總是AF_NETLINK
- nl_pad? ?: 總是為0
- nl_pid : 一般設置為本進程的pid或者填0理, 如果是進程接收來自內核netlink消息, 此域應為本進程PID,? 如果是向內核發送netlink消息, 此域應置0
- nl_groups :用于指定多播組, 如對接收來自內核的netlink消息來說, 內核可將要發送的消息指定一個多播組, 那么此消息就會發向同一多播組的接收端。而對于進程發送消息來講, 設置了多播組就只會發送到此多播組的內核接收端。此域是32位, 最多可支持32個多播組。
3 Netlink 消息格式
? 與每個IP消息頭一樣, netlink消息也有類似的頭部, 然而和其他協議不同的是, 編程者需要為每個數據包構建這個頭部(一般的TCP/UDP socket都是直接操作報文的payload部分), 這個頭部用來保存每個消息和格式的元數據, 這個頭部也是netlink協議的基礎。
struct nlmsghdr {u32 nlmsg_len;u16 nlmsg_type;u16 nlmsg_flags;u32 nlmsg_seq;u32 nlmsg_pid; }- nlmsg_len:每個netlink頭后面跟隨者0個或者多個字節的輔助數據, 此域記錄了消息的整個長度, 包括了頭部在內。
- nlmsg_type:此域標識了頭部后面數據的格式。此域的取值和2.1中的protocol有關。
- nlmsg_flags:此域標識了消息由誰進行處理和解析, 有如下取值 NLM_F_REQUEST - 這個標志暗示這是一個請求消息, 它應該被設置到大多數應用程序的初始化消息中。 NLM_F_ACK - 這個標志暗示對前一個請求消息的回應, 序列號和pid值能夠辨別請求的回應報文。 NLM_F_ECHO - 這個標志表示發出的報文將回響給發送進程一份。 NLM_F_MULTI - 這個標志表示此消息是多個消息的一部分, 可用宏NLMSG_NEXT獲得下一個消息。 NLM_F_ROOT - 用于請求多個netlink消息, 有此標志的請求消息表示請求回復整個條目表而不是一條, 回復的報文通常是 NLM_F_MULTI標志的。注意:此標志只適合特定的nlmsg_type才有效。 NLM_F_ATOMIC - 標志任何通過get-->回復報文的過程都是原子的, 防止在中間有資源改變引起歧義。 NLM_F_REPLACE - 替換條目表中的一條, 可用于覆蓋條目表。 NLM_F_CREATE - 在條目表中設置一個新的條目(比如添加一條新路由) NLM_F_APPEND - 在條目表末尾添加一個條目 NLM_F_EXCL - 結合了CREATE和APPEND, 如果要添加的條目已存在將返回錯誤(推薦使用)
-
nlmsg_seq:seq用來聯系請求和回復報文, 順序標志的作用
- nlmsg_pid:和seq類似
3.2 netlink 實用宏
#define NLMSG_ALIGNTO 4/* 返回不小于len的以4字節對齊的最近數字 (ex: len=9 --> 返回值為12)*/ #define NLMSG_ALIGN(len) ( ((len)+NLMSG_ALIGNTO-1) & ~(NLMSG_ALIGNTO-1) )
/* 返回netlink消息頭的字節數, 且務必是以4字節對齊的 */ #define NLMSG_HDRLEN ((int) NLMSG_ALIGN(sizeof(struct nlmsghdr)))
/* 返回'len + netlink消息頭長度' 一般len指定為消息的payload長度, 此宏可用來填充nlmsg_len域 */ #define NLMSG_LENGTH(len) ((len)+NLMSG_ALIGN(NLMSG_HDRLEN))
/* 返回值同 NLMSG_LENGTH 宏一樣有效, 主要是確保整個netlink消息長度4字節對齊(nlmsg_len域是否最好用此域來填充) */ #define NLMSG_SPACE(len) NLMSG_ALIGN(NLMSG_LENGTH(len))
/* 返回指向netlink消息payload處的指針(nlh 參數一般是指向struct nlmsghdr結構的指針) */
#define NLMSG_DATA(nlh) ((void*)(((char*)nlh) + NLMSG_LENGTH(0))) ----------------------------- | struct nlmsghdr | payload | -----------------------------|NLMSG_DATA(nlh)
/* 返回指向下一個netlink消息的指針 */ /* 很多netlink回應由多個netlink消息組成 */ *參數:nlh --> 指向struct nlmsghdr結構的指針len --> 一般為recvmsg函數的返回值(整個消息的長度) #define NLMSG_NEXT(nlh,len) ((len) -= NLMSG_ALIGN((nlh)->nlmsg_len), \(struct nlmsghdr*)(((char*)(nlh)) + NLMSG_ALIGN((nlh)->nlmsg_len))) --------------------------------------------------------- | struct nlmsghdr | payload | struct nlmsghdr | payload | --------------------------------------------------------- | | nlh |NLMSG_NEXT(nlh,len)|--------------------------len--------------------------| *此宏先用len(整個消息的長度)減掉第一個消息的長度, 如果為0, 表示沒有下一個消息了, 返回空, 若為非0, 表示后邊還有消息, 指針移動指向下一個消息處并返回.
/* 確保nlh指向的消息大小不大于len */ *參數:nlh --> 指向struct nlmsghdr結構的指針len --> 一般為recvmsg函數的返回值 #define NLMSG_OK(nlh,len) ((len) >= (int)sizeof(struct nlmsghdr) && \(nlh)->nlmsg_len >= sizeof(struct nlmsghdr) && \(nlh)->nlmsg_len <= (len))
?3.3 總結
下圖是netlink消息的內存分布圖:
4?NETLINK_FIREWALL 協議
由3可知, netlink消息頭nlmsg_type成員的取值和2.1中的protocol有關, 現在就介紹protocol為NETLINK_FIREWALL時的情況。
這個協議是非常有用的開發協議。首先它是有意被設計來在用戶空間來調試iptables模塊的架構,這個協議與很多iptables模塊相關聯。ip-queue模塊就是其中一個(詳見:http://blog.csdn.net/u010807313/article/details/9236581),在創建此協議的netlink套接字之前都需要先安裝相關模塊,發往相關模塊的報文都會同樣發給由NETLINK_FIREWALL協議創建的netlink套接字, 由此實現在用戶空間監聽流經netfilter的報文的目的。例如:
iptables -I OUTPUT -j QUEUE -p tcp –destination-port 7551上面一條命令表示在OUTPUT鏈上, 發往端口7551的TCP報文都交給QUEUE鏈來處理, 而ip-queue模塊正好和NETLINK_FIREWALL協議的套接字關聯(內核實現的), 所以套接字同樣也會收到這樣的報文, 實現了監聽的目的, 自行修改iptables命令可達到監聽多種類型報文的目的。
4.1 創建和使用
socket fd=socket(AF_NETLINK, SOCK_RAW, NETLINK_FIREWALL);此協議沒有使用多播組, 所以地址結構struct sockaddr_nl中的nl_groups應該總是設置為0,并且此協議的套接字不需要bind函數,因為報文只是在進程和內核中傳輸,所以從進程發向內核的報文struct sockaddr_nl中的nl_pid應該設置為0。
4.2 消息類型
NETLINK_FIREWALL協議的套接字有三種消息類型(如3中所述, 成員nlmsg_type的取值),每個消息類型都有它各自的數據結構來描述。
- IPQM_MODE
- IPQM_PACKET < 這個是內核向用戶空間返回的報文類型 >
- IPQM_VERDICT
4.2.1 IPQM_MODE
此類型是使用NETLINK_FIREWALL協議需要第一個發向內核的包, 內核收到之后才會將匹配的報文從內核發至用戶空間的netlink套接字。此報文的數據結構如下, 它是緊隨在struct nlmsghdr之后的:
typedef struct ipq mode msg {unsigned char value;size t range; };value的取值有三種:
- IPQ_COPY_NONE - 不常用, 設置此值發給內核將導致iptables將所有到QUEUE鏈的報文丟棄。
- IPQ_COPY_META - 表示我希望內核返回報文的元數據(我理解是struct nlmsghdr + struct ipq_packet_msg 兩個頭部)部分。
- IPQ_COPY_PACKET - 表示希望內核返回報文, 報文長度由range控制, 若range為0表示返回整個報文。如果你需要在用戶空間分析流經QUEUE鏈的報文應該設置此項并將range設置為0。
range:
此域只在value = IPQ_COPY_PACKET時才有效。
也就是說, 用戶進程使用IPQM_MODE類型的報文告訴內核, 我需要你返回給我的報文是什么樣的(不需要 or 要元數據 or 要range長的報文)
4.2.2 IPQM_PACKET
這個類型的報文是根據4.2.1之后內核根據需求返回的報文。只要之前設置的value不是IPQ_COPY_NONE, socket就會收到此類型的報文, 結構如下:
typedef struct ipq packet msg {unsigned long packet_id;unsigned long mark;long timestamp sec;long timestamp usec;unsigned int hook;char indev name[IFNAMSIZ];char outdev name[IFNAMSIZ];unsigned short hw_protocol;unsigned short hw_type;unsigned char hw_addrlen;unsigned char hw_addr[8];size t data len;unsigned char payload[0]; };- packet_id - 這個是內核產生的獨一無二的標識, 在4.2.3中發送IPQM_VERDICT報文需要。
- mark - //
- timestap_sec - 報文抵達時間(秒)
- timestap_usec - 報文抵達時間(微秒)
- hook - 報文被重定向到QUEUE的hook number
- indev_name - //
- outdev_name - //
- hw_protocol - 通常是ETH_P_IP
- hw_type - 通常是ARPHDR_ETHER
- hw_addrlen - 通常為6
- hw_addr - 報文的源MAC地址
- data_len - payload數據長度
- payload - 柔性數組頭部, 指向了payload數據的頭部
4.2.3 IPQM_VERDICT
此類型的報文是在收到內核的回復報文之后, 用戶經過自己的檢測, 決定對此報文執行何種操作, IPQM_MODE --> IPQM_PACKET <----> IPQM_VERDICT, 是順序的過程。也就是說, 只有你向內核發送IPQM_VERDICT說明了報文處理方式之后, 你才能recvmsg下一個到達QUEUE鏈的報文, 否則recvmsg會一直阻塞。結構如下:
typedef struct ipq verdict msg {unsigned int value;unsigned long id;size t data_len;unsigned char payload; };value 指示了對報文的處理方式:
- NF_DROP - 立即丟棄報文
- NF_ACCEPT - 接收報文(不參與之后的iptables鏈了)
- NF_STOLEN - //
- NF_QUEUE - 不常使用
- NF_REPEAT - 將報文移入下一個iptables鏈
id - 指示了要對哪個報文進行處理, 對應4.2.2的packet_id成員, 這個成員唯一關聯了一個進入QUEUE的報文
data_len - 指verdict報文的payload數據長度, 因為verdict是用戶發向內核的, 此域一般設置為0
payload - //
5?NETLINK_ROUTE 協議
5.1 創建和使用
socket fd=socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);NETLINK_ROUTE協議是netlink套接字最大且是最成熟的協議, 它有它自己消息的處理宏(類似3.2節), 這些NETLINK_ROUTE的宏是為了添加它的輔助數據段和定制化特別的消息類型而做的。
每個族都有同樣的命名空間和輔助數據結構, 輔助數據結構后又跟著一個或多個消息。
每個族都包含三個方法, NEW, DEL, GET; 這些方法用于創建、刪除和接收路由相關條目。
5.2 NETLINK_ROUTE消息宏
NETLINK_ROUTE消息實際有自己的數據結構, 如下所示。
struct rtattr {unsigned short rta_len;unsigned short rta_type; }下面是NETLINK_ROUTE的消息內存布局:
對于struct rtattr結構, 與netlink消息頭struct nlmsghdr結構相似, 有一些宏進行輔助處理, 參考3.2節。
? int RTA OK(struct rtattr *rta, int rtabuflen); - Verify the data integrity of the data which succedes this rtattr header. ? void * RTA DATA(struct rtattr *rta); - Return a pointer to the ancilliary data associated with this rtattr header. ? struct rtattr *RTA NEXT(struct rtattr *rta); - Return a pointer to the next rtattr header in the chain. ? unsigned int RTA PAYLOAD(struct rtattr *rta); - Return the length of the ancilliary data associated with the passed rtattr header. ? unsigned int RTA LENGTH(unsigned int length); - Return the aligned length for the passed payload length. This value is assigned to the rta len field of the rtattr header ? unsigned int RTA SPACE(unsigned int length); - Return the length of the ancilliary data, when aligned.5.3 消息類型
在使用NETLINK_ROUTE協議的情況下, netlink控制塊struct nlmsghdr中的nlmsg_type域標識了多種消息類型,舉例如下:
5.3.1 LINK消息
LINK消息族允許設置和獲取關于系統接口的消息nlmsg_type有如下取值:
- RTM_NEWLINK? - 創建一個新接口/有一個新接口被創建
- RTM_DELLINK? ?- 刪除一個接口
- RTM_GETLINK? - 接收一個接口消息
每個消息的輔助數據結構是struct ifinfomsg:
struct ifinfomsg {unsigned char ifi_family;unsigned short ifi_type;int ifi index;unsigned int ifi_flags;unsigned int ifi_change; };5.3.2 LINK消息struct rtattr結構rta_type取值
5.3.3 LINK消息內存布局
5.4 其它消息類型
比如ADDR消息, ROUTE消息等和LINK消息類似, 不同的是它們有各自的struct ifinfomsg消息和支持不同的rta_type。??
轉載于:https://www.cnblogs.com/Flychown/p/8065649.html
總結
以上是生活随笔為你收集整理的Netlink 介绍(译)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 洛谷 P1583 魔法照片
- 下一篇: JS 实现图片的预加载(转载)