netlink 是一種特殊的 socket,它是 Linux 所特有的,相似於 BSD 中的AF_ROUTE 但又遠比它的功能強大,目前在最新的 Linux 內核(2.6.14)中使用netlink 進行應用與內核通訊的應用不少,包括:路由 daemon(NETLINK_ROUTE),1-wire 子系統(NETLINK_W1),用戶態 socket 協議(NETLINK_USERSOCK),防火牆(NETLINK_FIREWALL),socket 監視(NETLINK_INET_DIAG),netfilter 日誌(NETLINK_NFLOG),ipsec 安全策略(NETLINK_XFRM),SELinux 事件通知(NETLINK_SELINUX),iSCSI 子系統(NETLINK_ISCSI),進程審計(NETLINK_AUDIT),轉發信息表查詢 (NETLINK_FIB_LOOKUP),netlink connector(NETLINK_CONNECTOR),netfilter 子系統(NETLINK_NETFILTER),IPv6 防火牆(NETLINK_IP6_FW),DECnet 路由信息(NETLINK_DNRTMSG),內核事件向用戶態通知(NETLINK_KOBJECT_UEVENT),通用 netlink(NETLINK_GENERIC)。html
Netlink 是一種在內核與用戶應用間進行雙向數據傳輸的很是好的方式,用戶態應用使用標準的 socket API 就可使用 netlink 提供的強大功能,內核態須要使用專門的內核 API 來使用 netlink。linux
Netlink 相對於系統調用,ioctl 以及 /proc 文件系統而言具備如下優勢:緩存
1,爲了使用 netlink,用戶僅須要在 include/linux/netlink.h 中增長一個新類型的 netlink 協議定義便可, 如 #define NETLINK_MYTEST 17 而後,內核和用戶態應用就能夠當即經過 socket API 使用該 netlink 協議類型進行數據交換。但系統調用須要增長新的系統調用,ioctl 則須要增長設備或文件, 那須要很多代碼,proc 文件系統則須要在 /proc 下添加新的文件或目錄,那將使原本就混亂的 /proc 更加混亂。安全
2. netlink是一種異步通訊機制,在內核與用戶態應用之間傳遞的消息保存在socket緩存隊列中,發送消息只是把消息保存在接收者的socket的接 收隊列,而不須要等待接收者收到消息,但系統調用與 ioctl 則是同步通訊機制,若是傳遞的數據太長,將影響調度粒度。數據結構
3.使用 netlink 的內核部分能夠採用模塊的方式實現,使用 netlink 的應用部分和內核部分沒有編譯時依賴,但系統調用就有依賴,並且新的系統調用的實現必須靜態地鏈接到內核中,它沒法在模塊中實現,使用新系統調用的應用在編譯時須要依賴內核。異步
4.netlink 支持多播,內核模塊或應用能夠把消息多播給一個netlink組,屬於該neilink 組的任何內核模塊或應用都能接收到該消息,內核事件向用戶態的通知機制就使用了這一特性,任何對內核事件感興趣的應用都能收到該子系統發送的內核事件,在 後面的文章中將介紹這一機制的使用。socket
5.內核可使用 netlink 首先發起會話,但系統調用和 ioctl 只能由用戶應用發起調用。ide
6.netlink 使用標準的 socket API,所以很容易使用,但系統調用和 ioctl則須要專門的培訓才能使用。函數
用戶態應用使用標準的socket APIs, socket(), bind(), sendmsg(), recvmsg() 和 close() 就能很容易地使用 netlink socket,查詢手冊頁能夠了解這些函數的使用細節,本文只是講解使用 netlink 的用戶應該如何使用這些函數。注意,使用 netlink 的應用必須包含頭文件 linux/netlink.h。固然 socket 須要的頭文件也必不可少,sys/socket.h。
爲了建立一個 netlink socket,用戶須要使用以下參數調用 socket():
socket(AF_NETLINK, SOCK_RAW, netlink_type) |
第一個參數必須是 AF_NETLINK 或 PF_NETLINK,在 Linux 中,它們倆實際爲一個東西,它表示要使用netlink,第二個參數必須是SOCK_RAW或SOCK_DGRAM, 第三個參數指定netlink協議類型,如前面講的用戶自定義協議類型NETLINK_MYTEST, NETLINK_GENERIC是一個通用的協議類型,它是專門爲用戶使用的,所以,用戶能夠直接使用它,而沒必要再添加新的協議類型。內核預約義的協議類 型有:
#define NETLINK_ROUTE 0 /* Routing/device hook */ |
對於每個netlink協議類型,能夠有多達 32多播組,每個多播組用一個位表示,netlink 的多播特性使得發送消息給同一個組僅須要一次系統調用,於是對於須要多撥消息的應用而言,大大地下降了系統調用的次數。
函數 bind() 用於把一個打開的 netlink socket 與 netlink 源 socket 地址綁定在一塊兒。netlink socket 的地址結構以下:
struct sockaddr_nl |
字段 nl_family 必須設置爲 AF_NETLINK 或着 PF_NETLINK,字段 nl_pad 當前沒有使用,所以要老是設置爲 0,字段 nl_pid 爲接收或發送消息的進程的 ID,若是但願內核處理消息或多播消息,就把該字段設置爲 0,不然設置爲處理消息的進程 ID。字段 nl_groups 用於指定多播組,bind 函數用於把調用進程加入到該字段指定的多播組,若是設置爲 0,表示調用者不加入任何多播組。
傳遞給 bind 函數的地址的 nl_pid 字段應當設置爲本進程的進程 ID,這至關於 netlink socket 的本地地址。可是,對於一個進程的多個線程使用 netlink socket 的狀況,字段 nl_pid 則能夠設置爲其它的值,如:
pthread_self() << 16 | getpid(); |
所以字段 nl_pid 實際上未必是進程 ID,它只是用於區分不一樣的接收者或發送者的一個標識,用戶能夠根據本身須要設置該字段。函數 bind 的調用方式以下:
bind(fd, (struct sockaddr*)&nladdr, sizeof(struct sockaddr_nl)); |
fd爲前面的 socket 調用返回的文件描述符,參數 nladdr 爲 struct sockaddr_nl 類型的地址。爲了發送一個 netlink 消息給內核或其餘用戶態應用,須要填充目標 netlink socket 地址,此時,字段 nl_pid 和 nl_groups 分別表示接收消息者的進程 ID 與多播組。若是字段 nl_pid 設置爲 0,表示消息接收者爲內核或多播組,若是 nl_groups爲 0,表示該消息爲單播消息,不然表示多播消息。使用函數 sendmsg 發送 netlink 消息時還須要引用結構 struct msghdr、struct nlmsghdr 和 struct iovec,結構 struct msghdr 需以下設置:
struct msghdr msg; |
其中 nladdr 爲消息接收者的 netlink 地址。
struct nlmsghdr 爲 netlink socket 本身的消息頭,這用於多路複用和多路分解 netlink 定義的全部協議類型以及其它一些控制,netlink 的內核實現將利用這個消息頭來多路複用和多路分解已經其它的一些控制,所以它也被稱爲netlink 控制塊。所以,應用在發送 netlink 消息時必須提供該消息頭。
struct nlmsghdr |
字段 nlmsg_len 指定消息的總長度,包括緊跟該結構的數據部分長度以及該結構的大小,字段 nlmsg_type 用於應用內部定義消息的類型,它對 netlink 內核實現是透明的,所以大部分狀況下設置爲 0,字段 nlmsg_flags 用於設置消息標誌,可用的標誌包括:
/* Flags values */ |
標誌NLM_F_REQUEST用於表示消息是一個請求,全部應用首先發起的消息都應設置該標誌。
標誌NLM_F_MULTI 用於指示該消息是一個多部分消息的一部分,後續的消息能夠經過宏NLMSG_NEXT來得到。
宏NLM_F_ACK表示該消息是前一個請求消息的響應,順序號與進程ID能夠把請求與響應關聯起來。
標誌NLM_F_ECHO表示該消息是相關的一個包的回傳。
標誌NLM_F_ROOT 被許多 netlink 協議的各類數據獲取操做使用,該標誌指示被請求的數據表應當總體返回用戶應用,而不是一個條目一個條目地返回。有該標誌的請求一般致使響應消息設置 NLM_F_MULTI標誌。注意,當設置了該標誌時,請求是協議特定的,所以,須要在字段 nlmsg_type 中指定協議類型。
標誌 NLM_F_MATCH 表示該協議特定的請求只須要一個數據子集,數據子集由指定的協議特定的過濾器來匹配。
標誌 NLM_F_ATOMIC 指示請求返回的數據應當原子地收集,這預防數據在獲取期間被修改。
標誌 NLM_F_DUMP 未實現。
標誌 NLM_F_REPLACE 用於取代在數據表中的現有條目。
標誌 NLM_F_EXCL_ 用於和 CREATE 和 APPEND 配合使用,若是條目已經存在,將失敗。
標誌 NLM_F_CREATE 指示應當在指定的表中建立一個條目。
標誌 NLM_F_APPEND 指示在表末尾添加新的條目。
內核須要讀取和修改這些標誌,對於通常的使用,用戶把它設置爲 0 就能夠,只是一些高級應用(如 netfilter 和路由 daemon 須要它進行一些複雜的操做),字段 nlmsg_seq 和 nlmsg_pid 用於應用追蹤消息,前者表示順序號,後者爲消息來源進程 ID。下面是一個示例:
#define MAX_MSGSIZE 1024 |
結構 struct iovec 用於把多個消息經過一次系統調用來發送,下面是該結構使用示例:
struct iovec iov; |
在完成以上步驟後,消息就能夠經過下面語句直接發送:
sendmsg(fd, &msg, 0); |
應用接收消息時須要首先分配一個足夠大的緩存來保存消息頭以及消息的數據部分,而後填充消息頭,添完後就能夠直接調用函數 recvmsg() 來接收。
#define MAX_NL_MSG_LEN 1024 |
注意:fd爲socket調用打開的netlink socket描述符。
在消息接收後,nlhdr指向接收到的消息的消息頭,nladdr保存了接收到的消息的目標地址,宏NLMSG_DATA(nlhdr)返回指向消息的數據部分的指針。
在linux/netlink.h中定義了一些方便對消息進行處理的宏,這些宏包括:
#define NLMSG_ALIGNTO 4 |
宏NLMSG_ALIGN(len)用於獲得不小於len且字節對齊的最小數值。
#define NLMSG_LENGTH(len) ((len)+NLMSG_ALIGN(sizeof(struct nlmsghdr))) |
宏NLMSG_LENGTH(len)用於計算數據部分長度爲len時實際的消息長度。它通常用於分配消息緩存。
#define NLMSG_SPACE(len) NLMSG_ALIGN(NLMSG_LENGTH(len)) |
宏NLMSG_SPACE(len)返回不小於NLMSG_LENGTH(len)且字節對齊的最小數值,它也用於分配消息緩存。
#define NLMSG_DATA(nlh) ((void*)(((char*)nlh) + NLMSG_LENGTH(0))) |
宏NLMSG_DATA(nlh)用於取得消息的數據部分的首地址,設置和讀取消息數據部分時須要使用該宏。
#define NLMSG_NEXT(nlh,len) ((len) -= NLMSG_ALIGN((nlh)->nlmsg_len), \ |
宏NLMSG_NEXT(nlh,len)用於獲得下一個消息的首地址,同時len也減小爲剩餘消息的總長度,該宏通常在一個消息被分紅幾個部分發送或接收時使用。
#define NLMSG_OK(nlh,len) ((len) >= (int)sizeof(struct nlmsghdr) && \ |
宏NLMSG_OK(nlh,len)用於判斷消息是否有len這麼長。
#define NLMSG_PAYLOAD(nlh,len) ((nlh)->nlmsg_len - NLMSG_SPACE((len))) |
宏NLMSG_PAYLOAD(nlh,len)用於返回payload的長度。
函數close用於關閉打開的netlink socket。
netlink的內核實如今.c文件net/core/af_netlink.c中,內核模塊要想使用netlink,也必須包含頭文件linux /netlink.h。內核使用netlink須要專門的API,這徹底不一樣於用戶態應用對netlink的使用。若是用戶須要增長新的netlink協 議類型,必須經過修改linux/netlink.h來實現,固然,目前的netlink實現已經包含了一個通用的協議類型 NETLINK_GENERIC以方便用戶使用,用戶能夠直接使用它而沒必要增長新的協議類型。前面講到,爲了增長新的netlink協議類型,用戶僅需增 加以下定義到linux/netlink.h就能夠:
#define NETLINK_MYTEST 17 |
只要增長這個定義以後,用戶就能夠在內核的任何地方引用該協議。
在內核中,爲了建立一個netlink socket用戶須要調用以下函數:
struct sock * |
參數unit表示netlink協議類型,如NETLINK_MYTEST,參數input則爲內核模塊定義的netlink消息處理函數,當有消 息到達這個netlink socket時,該input函數指針就會被引用。函數指針input的參數sk實際上就是函數netlink_kernel_create返回的 struct sock指針,sock實際是socket的一個內核表示數據結構,用戶態應用建立的socket在內核中也會有一個struct sock結構來表示。下面是一個input函數的示例:
void input (struct sock *sk, int len) |
函數input()會在發送進程執行sendmsg()時被調用,這樣處理消息比較及時,可是,若是消息特別長時,這樣處理將增長系統調用 sendmsg()的執行時間,對於這種狀況,能夠定義一個內核線程專門負責消息接收,而函數input的工做只是喚醒該內核線程,這樣sendmsg將 很快返回。
函數skb = skb_dequeue(&sk->receive_queue)用於取得socket sk的接收隊列上的消息,返回爲一個struct sk_buff的結構,skb->data指向實際的netlink消息。
函數skb_recv_datagram(nl_sk)也用於在netlink socket nl_sk上接收消息,與skb_dequeue的不一樣指出是,若是socket的接收隊列上沒有消息,它將致使調用進程睡眠在等待隊列 nl_sk->sk_sleep,所以它必須在進程上下文使用,剛纔講的內核線程就能夠採用這種方式來接收消息。
下面的函數input就是這種使用的示例:
void input (struct sock *sk, int len) |
當內核中發送netlink消息時,也須要設置目標地址與源地址,並且內核中消息是經過struct sk_buff來管理的, linux/netlink.h中定義了一個宏:
#define NETLINK_CB(skb) (*(struct netlink_skb_parms*)&((skb)->cb)) |
來方便消息的地址設置。下面是一個消息地址設置的例子:
NETLINK_CB(skb).pid = 0; |
字段pid表示消息發送者進程ID,也即源地址,對於內核,它爲 0, dst_pid 表示消息接收者進程 ID,也即目標地址,若是目標爲組或內核,它設置爲 0,不然 dst_group 表示目標組地址,若是它目標爲某一進程或內核,dst_group 應當設置爲 0。
在內核中,模塊調用函數 netlink_unicast 來發送單播消息:
int netlink_unicast(struct sock *sk, struct sk_buff *skb, u32 pid, int nonblock); |
參數sk爲函數netlink_kernel_create()返回的socket,參數skb存放消息,它的data字段指向要發送的 netlink消息結構,而skb的控制塊保存了消息的地址信息,前面的宏NETLINK_CB(skb)就用於方便設置該控制塊, 參數pid爲接收消息進程的pid,參數nonblock表示該函數是否爲非阻塞,若是爲1,該函數將在沒有接收緩存可利用時當即返回,而若是爲0,該函 數在沒有接收緩存可利用時睡眠。
內核模塊或子系統也可使用函數netlink_broadcast來發送廣播消息:
void netlink_broadcast(struct sock *sk, struct sk_buff *skb, u32 pid, u32 group, int allocation); |
前面的三個參數與netlink_unicast相同,參數group爲接收消息的多播組,該參數的每個表明一個多播組,所以若是發送給多個多播 組,就把該參數設置爲多個多播組組ID的位或。參數allocation爲內核內存分配類型,通常地爲GFP_ATOMIC或 GFP_KERNEL,GFP_ATOMIC用於原子的上下文(即不能夠睡眠),而GFP_KERNEL用於非原子上下文。
在內核中使用函數sock_release來釋放函數netlink_kernel_create()建立的netlink socket:
void sock_release(struct socket * sock); |
注意函數netlink_kernel_create()返回的類型爲struct sock,所以函數sock_release應該這種調用:
sock_release(sk->sk_socket); |
sk爲函數netlink_kernel_create()的返回值。
在源代碼包中給出了一個使用 netlink 的示例,它包括一個內核模塊 netlink-exam-kern.c 和兩個應用程序 netlink-exam-user-recv.c, netlink-exam-user-send.c。內核模塊必須先插入到內核,而後在一個終端上運行用戶態接收程序,在另外一個終端上運行用戶態發送程 序,發送程序讀取參數指定的文本文件並把它做爲 netlink 消息的內容發送給內核模塊,內核模塊接受該消息保存到內核緩存中,它也經過proc接口出口到 procfs,所以用戶也可以經過 /proc/netlink_exam_buffer 看到所有的內容,同時內核也把該消息發送給用戶態接收程序,用戶態接收程序將把接收到的內容輸出到屏幕上。
轉載自http://www.cnblogs.com/iceocean/articles/1594195.html