放假回家的第一天,呵呵。
緬懷Stevens大師。
最好的參考資料:
1.師從互連網。
2.Linux man 命令:man netlink,man rtnetlink。
3.UNP v1第18章 。
4.http://blog.csdn.net/unbutun/archive/2010/01/10/5170059.aspx
http://en.wikipedia.org/wiki/Netlink
http://yongqig.onlyblog.com/blog2/enchen/8605.html
http://linux.chinaunix.net/techdoc/develop/2006/10/15/942169.shtml
http://yangelc.blog.sohu.com/68245920.html
http://www.chinaunix.net/jh/4/822500.html
http://www.ibm.com/developerworks/cn/linux/l-kerns-usrs/
第一條:概述。
簡單說來,linux通過Netlink機制與核心中相應的模組進行通訊來掌控裝置(網路方面的居多)。
man手冊如是說:
Netlink is used to transfer information between kernel and userspace processes. It consists of a standard sockets-based interface for userspace processes and an internal kernel API for kernel modules. The internal kernel interface is not documented in this manual page. There is also an obsolete netlink interface via netlink character devices;this interface is not documented here and is only provided for backwards compatibility.
我翻譯的:
Netlink用於在核心和使用者空間的進程之間通訊。Netlink機制由在使用者空間的SocketAPI和核心模組的系統調用組成。手冊不提及核心的系統調用介面。至於,Netlink字元裝置的介面API也沒說,Netlink字元裝置的介面API是為了向後相容。
另外:UNPv1第18章的路由Socket不適用於Linux,linux使用netlink機制全面代替BSD的路由套介面機制。
#definePF_ROUTEPF_NETLINK /* Alias to emulate 4.4BSD. */
題外話:什麼是通訊端?即IP地址和連接埠的組合。
第二條:Netlink通訊端描述符
int sockfd = socket(AF_NETLINK, sock_type, netlink_faimly);
1.sock_type:man手冊中說:Netlink is a datagram-oriented service。netlink機制是面向資料報的服務,故可以使用SOCK_RAW 和SOCK_DGRAM,不能使用SOCK_STREAM。手冊中也提到:netlink機制不區分資料報(datagram)通訊端和原始(raw)通訊端。
2.netlink_faimly:指定與哪個核心模組進行通訊的協議,如下:
NETLINK_ROUTE:路由 daemon
Receives routing and link updates and may be used to modify the routing tables (both IPv4 and IPv6), IP addresses, link parameters, neighbor setups, queueing disciplines, traffic classes and packet classifiers (see rtnetlink(7)).
NETLINK_W1:1-wire 子系統
Messages from 1-wire subsystem.
NETLINK_USERSOCK:使用者態 socket 協議
Reserved for user-mode socket protocols.
NETLINK_FIREWALL:防火牆
Transport IPv4 packets from netfilter to userspace. Used by ip_queue kernel module.
NETLINK_INET_DIAG:socket 監視
INET socket monitoring.
NETLINK_NFLOG:netfilter 日誌
Netfilter/iptables ULOG.
NETLINK_XFRM:ipsec 安全性原則
IPsec.
NETLINK_SELINUX:SELinux 事件通知
SELinux event notifications.
NETLINK_ISCSI:iSCSI 子系統
Open-iSCSI.
NETLINK_AUDIT:進程審計
Auditing.
NETLINK_FIB_LOOKUP:轉寄資訊表查詢
Access to FIB lookup from userspace.
NETLINK_CONNECTOR:netlink connector Kernel connector. See Documentation/connector/* in the kernel source for further information. NETLINK_NETFILTER:netfilter 子系統 Netfilter subsystem. NETLINK_IP6_FW:IPv6 防火牆 Transport IPv6 packets from netfilter to userspace. Used by ip6_queue kernel module. NETLINK_DNRTMSG:DECnet 路由資訊 DECnet routing messages. NETLINK_KOBJECT_UEVENT:核心事件向使用者態通知 Kernel messages to userspace. NETLINK_GENERIC:通用 netlink Generic netlink family for simplified netlink usage.函數close用於關閉開啟的netlink socket。第三條:Netlink 通訊端地址結構
#inlcude<linux/netlink.h>
struct sockaddr_nl {//幾乎和TCP裡的sockaddr一樣。
sa_family_tnl_family;/* 必須為AF_NETLINK或PF_NETLINK */
unsigned shortnl_pad;/*保留未用,初始為0*/
__u32nl_pid;/* port ID,*/
__u32nl_groups;/* multicast groups mask 多播 組掩碼 */
};
nl_pid:1.當作為bind函數的參數時,就是給沒有名字的socketfd賦上一個名字,只有一個要求在有多個Netlink socketfd時要保證唯一性,方式一:由使用者保證唯一性:一個進程只有一個Netlink socketfd時可以指定nl_pid為任意整數,getpid()是個不錯的選擇。但是一個進程有多個Netlinksocketfd時 就不能都指定為getpid(),必須加以區別。方式二:man手冊指出當把nl_pid賦為0,無論一個進程內有幾個 Netlink socketfd,核心將保證他們唯一性。2.作為sendto等函數的參數:是用來指定發送資料目的地,當目的地是其他的進程,就賦上那個進程的pid就可,這個幾乎用不到。當放送到核心,直接賦為0.
nl_groups:
對於Netlink 的每個協議,都有一個容納32個多播組的集合。nl_groups的一個二進位位代表一個組,共有32個。1.作為bind 函數的參數,用於把調用進程加入到該nl_groups指定的多播組(是否可以同時被添加進多個組,就是nl_groups多位為1,未驗證),如果設定為 0,表示調用者不加入任何多播組。2.作為sendto等函數的參數時。若nl_groups為0,配合nl_pid發送單播資料,當nl_groups不為0,配合nl_pid發送多播。第四條:Netlink訊息。
Netlink與核心通訊的訊息有兩部分:首部和資料。
首先,Netlink socket 和TCP協議中接收放送資料一樣,也需要首部。主要用於多工和多路分解,以及其它的一些控制。
struct nlmsghdr {//這個結構就是用於表示首部(頭部)。
__u32 nlmsg_len; /* Length of message including header. 整個資料的大小,包括這個首部和要接收/發送的資料*/
__u16 nlmsg_type; /* Type of message content. 接收/發送資料的用途 */
__u16 nlmsg_flags; /* Additional flags. 附加標誌*/
__u32 nlmsg_seq; /* Sequence number. 序號,就是這個訊息是第幾個*/
__u32 nlmsg_pid; /* PID of the sending process. */
};
nlmsg_seq 和 nlmsg_pid 用於應用追蹤訊息,前者表示順序號,後者為訊息來源進程 ID。
如果一個訊息是由多個資料報組成,也就是說這個訊息有多個首部,當然每個首部後面跟著資料部分。那麼除了最後資料報,每個部分的首部都要在nlmsg_flags設定NLM_F_MULTI,最後資料報的首部nlmsg_type設定為NLMSG_DONE。這種情況多是由核心向使用者空間造成的,所以這些標誌一般都是由核心賦值的我們不需要賦值,只在接收訊息檢測這些標誌位。
nlmsg_type:取值如下:
以下四個值一般由核心設定,用於我們接收資料後,檢測。
1.NLMSG_NOOP : message is to be ignored;這個訊息類型表示資料內容為空白,應用可以忽略該報文
2.NLMSG_ERROR:message signals an error and the payload contains an nlmsgerr structure 這個訊息類型表示資料部分是一個錯誤資訊,資料部分的結構如下
struct nlmsgerr {
int error; /* Negative errno or 0 for acknowledgements;負數表示的出錯號 errno 或為 0 要求確認 acks*/
struct nlmsghdr msg; /* Message header that caused the error:造成出錯的訊息前序*/
};
3.NLMSG_DONE :message terminates a multipart message.在我們接收或者發送訊息給核心的時候,可能一次發送多個報文,這個訊息類型標誌最後一個報文。
4.NLMSG_OVERRUN: Data lost。
以下是由程式員設定的,這裡的是NETLINK_ROUTE協議支援的類型,至於其他協議的有待研究,每種類型對應著後面資料部分的不同承載結構。使用NETLINK_ROUTE協議時候支援的類型如下,他們都定義在linux/rtnetlink.h中:
1.Link Layer:建立,刪除、擷取、設定網路裝置的資訊:RTM_NEWLINK, RTM_DELLINK, RTM_GETLINK,RTM_SETLINK
對應資料部分資料結構:在linux/rtnetlink.h中
struct ifinfomsg {/* struct ifinfomsg passes link level specific information, not dependent on network protocol.*/
unsigned charifi_family;
unsigned char__ifi_pad;
unsigned shortifi_type;/* ARPHRD_* */
intifi_index;/* Link index*/
unsignedifi_flags;/* IFF_* flags*/
unsignedifi_change;/* IFF_* change mask */
};
2.Address Settings:建立,刪除、擷取網路裝置的IP資訊:RTM_NEWADDR, RTM_DELADDR, RTM_GETADDR
對應資料部分資料結構 :在linux/if_addr.h中
struct ifaddrmsg {
__u8ifa_family;
__u8ifa_prefixlen;/* The prefix length*/
__u8ifa_flags;/* Flags*/
__u8ifa_scope;/* Address scope*/
__u32ifa_index;/* Link index*/
};
3.Routing Tables: 建立,刪除、擷取網路裝置的路由資訊:RTM_NEWROUTE, RTM_DELROUTE, RTM_GETROUTE
對應資料部分資料結構 :在linux/rtnetlink.h中
struct rtmsg {//Definitions used in routing table administration.
unsigned charrtm_family;/* 路由表地址族 */
unsigned charrtm_dst_len; /* 目的長度 */
unsigned charrtm_src_len; /* 源長度 */
unsigned charrtm_tos; /* TOS */
unsigned charrtm_table;/* Routing table id */ /* 路由表選取 */
unsigned charrtm_protocol;/* Routing protocol; see below*//* 路由協議 */
unsigned charrtm_scope;/* See below */
unsigned charrtm_type;/* See below*/
unsignedrtm_flags;
};
4.Neighbor Cache:建立,刪除、擷取網路裝置的相鄰資訊:RTM_NEWNEIGH, RTM_DELNEIGH, RTM_GETNEIGH
對應資料部分資料結構 :在linux/neighbour.h中
struct ndmsg {
__u8ndm_family;
__u8ndm_pad1;
__u16ndm_pad2;
__s32ndm_ifindex;
__u16ndm_state;
__u8ndm_flags;
__u8ndm_type;
};
struct nda_cacheinfo {
__u32ndm_confirmed;
__u32ndm_used;
__u32ndm_updated;
__u32ndm_refcnt;
};
5.Routing Rules:建立,刪除、擷取路由規則資訊:RTM_NEWRULE, RTM_DELRULE, RTM_GETRULE
對應資料部分資料結構 :在linux/rtnetlink.h中struct rtmsg
6.Queuing Discipline Settings:建立,刪除、擷取隊列的原則:RTM_NEWQDISC, RTM_DELQDISC, RTM_GETQDISC
對應資料部分資料結構 :在linux/rtnetlink.h中
struct tcmsg {//Traffic control messages.
unsigned chartcm_family;
unsigned chartcm__pad1;
unsigned shorttcm__pad2;
inttcm_ifindex;
__u32tcm_handle;
__u32tcm_parent;
__u32tcm_info;
};
7.Traffic Classes used with Queues:建立,刪除、擷取流量的類別:RTM_NEWTCLASS, RTM_DELTCLASS, RTM_GETTCLASS
對應資料部分資料結構 :linux/rtnetlink.h中struct tcmsg
8.Traffic filters:建立,刪除、擷取流量的過慮:RTM_NEWTFILTER, RTM_DELTFILTER, RTM_GETTFILTER
對應資料部分資料結構 : linux/rtnetlink.h中struct tcmsg
9.Others: RTM_NEWACTION, RTM_DELACTION, RTM_GETACTION, RTM_NEWPREFIX, RTM_GETPREFIX, RTM_GETMULTICAST,
RTM_GETANYCAST,RTM_NEWNEIGHTBL,RTM_GETNEIGHTBL, RTM_SETNEIGHTBL
nlmsg_flags:這個成員用於控制和表示訊息,值如下:
1.Standard flag bits in nlmsg_flags
NLM_F_REQUEST Must be set on all request messages.表示訊息是一個請求,所有應用首先發起的訊息都應設定該標誌。,這個標誌可以和以下的一個標誌組合
- NLM_F_ROOT 被許多 netlink 協議的各種資料擷取操作使用,該標誌指示被請求的資料表應當整體返回使用者應用,而不是一個條目一個條目地返回。有該標誌的請求通常導致響應訊息設定NLM_F_MULTI標誌。注意,當設定了該標誌時,請求是協議特定的,因此,需要在欄位 nlmsg_type 中指定協議類型。
- NLM_F_MATCH 表示該協議特定的請求只需要一個資料子集,資料子集由指定的協議特定的過濾器來匹配。
- NLM_F_ATOMIC 返回對象表的快照
- NLM_F_DUMP 被定義為NLM_F_ROOT|NLM_F_MATCH
- NLM_F_REPLACE 用於取代在資料表中的現有條目。
- NLM_F_EXCL 用於和 CREATE 和 APPEND 配合使用,如果條目已經存在,將失敗。
- NLM_F_CREAT 指示應當在指定的表中建立一個條目。
- NLM_F_APPEND 指示在表末尾添加新的條目。
NLM_F_MULTI The message is part of a multipart message terminated by NLMSG_DONE. 用於指示該訊息是一個多部分訊息的一部分,後續的訊息可以通過宏NLMSG_NEXT來獲得。
NLM_F_ACK Request for an acknowledgment on success.表示該訊息是前一個請求訊息的響應,順序號與進程ID可以把請求與響應關聯起來。
NLM_F_ECHO Echo this request.表示該訊息是相關的一個包的回傳。
2.Additional flag bits for GET requests
NLM_F_ROOT Return the complete table instead of a single entry.
NLM_F_MATCH Return all entries matching criteria(標準,要求) passed in message content. Not implemented yet.
NLM_F_ATOMIC Return an atomic snapshot of the table.
NLM_F_DUMP Convenience macro; equivalent to (NLM_F_ROOT|NLM_F_MATCH).
Note that NLM_F_ATOMIC requires the CAP_NET_ADMIN capability or an effective UID of 0.
3.Additional flag bits for NEW requests
NLM_F_REPLACE Replace existing matching object.
NLM_F_EXCL Don't replace if the object already exists.
NLM_F_CREATE Create object if it doesn't already exist.
NLM_F_APPEND Add to the end of the object list.
第五條Netlink與核心通訊Linux定義了多個宏,來輔助我們發送和接收Netlink訊息,與核心進行通訊。
#include <asm/types.h>
#include <linux/netlink.h>1.int NLMSG_ALIGN(size_t len);#define NLMSG_ALIGNTO4#define NLMSG_ALIGN(len) ( ((len)+NLMSG_ALIGNTO-1) & ~(NLMSG_ALIGNTO-1) )//宏NLMSG_ALIGN(len)用於得到不小於len且位元組對齊的最小數值。2.#define NLMSG_HDRLEN ((int) NLMSG_ALIGN(sizeof(struct nlmsghdr)))//頭部長度3.int NLMSG_LENGTH(size_t len);#define NLMSG_LENGTH(len) ((len)+NLMSG_ALIGN(NLMSG_HDRLEN))//宏NLMSG_LENGTH(len)用於計算資料部分長度為len時實際的訊息長度。它一般用於分配訊息緩衝。4.int NLMSG_SPACE(size_t len);#define NLMSG_SPACE(len) NLMSG_ALIGN(NLMSG_LENGTH(len))//宏NLMSG_SPACE(len)返回不小於NLMSG_LENGTH(len)且位元組對齊的最小數值,它也用於分配訊息緩衝。5.void *NLMSG_DATA(struct nlmsghdr *nlh);#define NLMSG_DATA(nlh) ((void*)(((char*)nlh) + NLMSG_LENGTH(0)))//宏NLMSG_DATA(nlh)用於取得訊息的資料部分的首地址,設定和讀取訊息資料部分時需要使用該宏。6.struct nlmsghdr *NLMSG_NEXT(struct nlmsghdr *nlh, int len);#define NLMSG_NEXT(nlh,len)((len) -= NLMSG_ALIGN((nlh)->nlmsg_len), (struct nlmsghdr*)(((char*)(nlh)) + NLMSG_ALIGN((nlh)->nlmsg_len)))//宏NLMSG_NEXT(nlh,len)用於得到下一個訊息的首地址,同時len也減少為剩餘訊息的總長度,該宏一般在一個訊息被分成幾個部分發送或接收時使用。7.int NLMSG_OK(struct nlmsghdr *nlh, int len);#define NLMSG_OK(nlh,len) ((len) >= (int)sizeof(struct nlmsghdr) && (nlh)->nlmsg_len >= sizeof(struct nlmsghdr) && (nlh)->nlmsg_len <= (len))//宏NLMSG_OK(nlh,len)用於判斷訊息是否有len這麼長。8.int NLMSG_PAYLOAD(struct nlmsghdr *nlh, int len);#define NLMSG_PAYLOAD(nlh,len) ((nlh)->nlmsg_len - NLMSG_SPACE((len))) //宏NLMSG_PAYLOAD(nlh,len)用於返回payload的長度。設定好上面訊息後,我們就可以用sendto和recv分別發送和接收資料了。