2017-07-04
netlink是一種基於網絡的通信機制,一般用於內核內部或者內核與用戶層之間的通信。其有一個明顯的特點就是異步性,通信的雙方不要求同時在線,也就不用阻塞等待。NetLink按照數據包的格式發送/接收消息,提供雙向通信,和其他內核與用戶層通信機制相比,NetLink有其特有的優勢:
- 任何一方不需要輪訓,如果通過文件傳遞狀態信息,則需要不斷的檢查是否有新消息到達。
- 系統調用和ioctl均可以從用戶層向內核傳遞消息,但比簡單的Netlink鏈接難於實現,另外,NetLink是系統提供的機制,不會和任何模塊其沖突,兼容性好。
- 使用NetLink可以讓內核主動的向用戶層發消息,此時內核扮演的就是一個邏輯用戶;而ioctl和系統調用、/proc均只能由用戶層發起請求,讓內核去被動響應。
- Netlink不僅支持單播,也支持多播即向多個進程發送消息。
從邏輯上來講,NetLink應該處於傳輸層,因此其同樣需要特定的協議。對於NetLink就是Netlink協議,內核中有netlink_family_ops,在用戶層創建netlink類型的socket的時候最終會調用到netlink_family_ops注冊的create函數
static const struct net_proto_family netlink_family_ops = { .family = PF_NETLINK, .create = netlink_create, .owner = THIS_MODULE, /* for consistency 8) */ };
每種協議都需要一組協議處理函數,針對Netlink有netlink_ops
static const struct proto_ops netlink_ops = { .family = PF_NETLINK, .owner = THIS_MODULE, .release = netlink_release, .bind = netlink_bind, .connect = netlink_connect, .socketpair = sock_no_socketpair, .accept = sock_no_accept, .getname = netlink_getname, .poll = netlink_poll, .ioctl = sock_no_ioctl, .listen = sock_no_listen, .shutdown = sock_no_shutdown, .setsockopt = netlink_setsockopt, .getsockopt = netlink_getsockopt, .sendmsg = netlink_sendmsg, .recvmsg = netlink_recvmsg, .mmap = netlink_mmap, .sendpage = sock_no_sendpage, };
我們重點關注netlink_sendmsg和netlink_recvmsg,這是netlink發送和接收數據的處理函數。
地址表示
NetLink的地址表示由sockaddr_nl負責
struct sockaddr_nl { __kernel_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_pid用以該套接字,對於內核套接字來講,nl_pid為0,而對於用戶層程序而言,該值可以為任何值,協商好並保證唯一性即可。一般取為進程id或線程ID。nl_groups用以多播,當不需要多播時,該字段為0。
NetLink消息
NetLink消息是作為套接字緩沖區sk_buff的數據部分傳遞的,其消息本身又分為頭部和數據。頭部為
struct nlmsghdr { __u32 nlmsg_len; /* Length of message including header */ __u16 nlmsg_type; /* Message content */ __u16 nlmsg_flags; /* Additional flags */ __u32 nlmsg_seq; /* Sequence number */ __u32 nlmsg_pid; /* Sending process port ID */ };
nlmsg_len為消息的長度,包含該頭部在內。nlmsg_pid為發送進程的端口ID,這個用戶可以自定義。其余的字段暫時用不到。在用戶層還用到了一個結構
struct msghdr { void * msg_name; /* Socket name */ int msg_namelen; /* Length of name */ struct iovec * msg_iov; /* Data blocks */ __kernel_size_t msg_iovlen; /* Number of blocks */ void * msg_control; /* Per protocol magic (eg BSD file descriptor passing) */ __kernel_size_t msg_controllen; /* Length of cmsg list */ unsigned int msg_flags; };
msg_name是目標socket的地址結構sockaddr_nl的地址,msg_namelen是長度。msg_iov指向一個iov向量,msg_iovlen是向量的個數。剩下的暫不考察。當用戶層向內核發送消息時,該結構作為參數傳遞。nlmsghdr 只能描述單個消息,而msghdr描述一組消息。通過iov向量聚合所有的消息,每個消息都有其對應的nlmsghdr.邏輯結構如下:

之前提到,socket也是一種特殊的文件,通過VFS的借口同樣可以對其實現管理。sokket本身就需要實現文件系統的相應接口,有自己的文件系統操作。
內核中的表示
netlink socket在內核中涉及到的結構有下:socket,sock,netlink_sock,前者是套接字通用結構,sock是套接字在網絡層的體現,而sock其實是內嵌在netlink_sock結構中,后者保存有鏈接兩端的端口ID,並包含一個函數,用於在接收數據時使用!
以馬內利
參考資料:
《linux3.10.1源碼》
