內核版本:3.14.38
netlink是一種用於內核態和用戶態進程之間進行數據傳輸的特殊的IPC機制。
特點:1) 用戶態採用socket風格的API
2) 除了預定義的協議類型之外,支持自定義協議類型
3) 異步通訊
4) 支持消息組播
4) 全雙工(特別是支持內核主動發起會話)
netlink涉及的數據結構:
1) netlink地址結構
struct sockaddr_nl {
sa_family_t nl_family; // AF_NETLINK
unsigned short nl_pad; // 填充0
unsigned int nl_pid; // 進程ID
unsigned int nl_groups; // 多播組mask
}
NETLINK_ROUTE的多播組定義位於retnetlink.h,RTMGRP_*格式,這裏列出常用的幾個:
RTMGRP_LINK - 當網卡變動時會觸發這個多播組,例如插拔網線、增減網卡設備、啓用禁用接口等
RTMGRP_IPV4_IFADDR - 當ipv4地址變動時會觸發這個多播組,例如修改IP
RTMGRP_IPV4_ROUTE - 當ipv4路由變動時會觸發這個多播組
RTMGRP_IPV6_IFADDR - 當ipv6地址變動時會觸發這個多播組,例如修改IP
RTMGRP_IPV6_ROUTE - 當ipv6路由變動時會觸發這個多播組
2) netlink消息結構:
nlmsghdr + pad + payload + pad + nlmsghdr + pad + payload + pad ...
可以看出來,netlink消息在頂層呈現數組形式平行排列,也就是說,多條netlink消息可以以數組形式一次性傳輸
====================================================================================================
netlink消息頭結構
struct nlmsghdr {
unsigned int nlmsg_len; // 消息長(nlmsghdr + pad + payload)
unsigned short nlmsg_type; // 消息類型
unsigned short nlmsg_flags;// 附加的標誌位,用來對消息進行額外的控制,NLM_F_*
unsigned int nlmsg_seq; // 序號(用於追蹤)
unsigned int nlmsg_pid; // 進程ID(用於追蹤)
}
消息類型:
NETLINK_ROUTE的消息類型定義位於rtnetlink.h,RTM_*格式,這裏列出常用的幾個:
RTM_NEWLINK/RTM_DELLINK - 當網卡變動時內核會發出這個消息
RTM_NEWADDR/RTM_DELADDR - 當地址(IP)變動時內核會發出這個消息
RTM_NEWROUTE/RTM_DELROUTE - 當路由變動時內核會發出這個消息
附加標誌位:
NLM_F_REQUEST - 表示消息是一個請求。所有用戶首先發起的消息都要設置該標誌,可以和GET request和NEW request系列標誌組合
NLM_F_MULTI - 表示消息由多個分部組成,最後一個分部的消息會標註NLMSG_DONE
NLM_F_ACK - 表示這是一條ACK消息,具體內容就是承載了一個返回值(0或錯誤代號)
NLM_F_ECHO - echo this request
NLM_F_DUMP_INTER
GET request系列的標誌位:
NLM_F_ROOT - 表示被請求的數據應當整體返回用戶應用,而不是一條一條返回,有該標誌的request通常導致響應的消息設置NLM_F_MULTI標誌
NLM_F_MATCH - 表示會返回所有匹配的數據
NLM_F_ATOMIC
NLM_F_DUMP - NLM_F_ROOT和NLM_F_MATCH的合集
NEW request系列的標誌位:
暫略
================================================================================================
netlink消息payload結構:
family-header + pad + attributes
================================================================================================
payload中的family-header有不同的格式可以採用:
// retnetlink協議通用的族頭格式
struct rtgenmsg {
unsigned char rtgen_family; // 協議族
}
// rtnetlink協議的網絡接口消息(如RTM_NEWLINK)的族頭
struct ifinfomsg {
unsigned char ifi_family; // 協議族
unsigned char __ifi_pad; // 1字節填充,用於對齊,無含義
unsigned short ifi_type; // ARPHRD_* 格式
int ifi_index; // 接口序號
unsigned ifi_flags; // 標準的BSD風格接口標誌位集合,定義在/net/if.h中,IFF_* 格式
unsigned ifi_change; // IFF_* 格式
unsigned char ifa_family; // 協議族
unsigned char ifa_prefixlen;
unsigned char ifa_flags;
unsigned char ifa_scope;
unsigned int if
}
// rtnetlink協議的地址消息(如RTM_NEWADDR)的族頭
struct ifaddrmsg {a_index;
}
// rtnetlink協議的路由消息(如RTM_NEWROUTE)的族頭
struct rtmsg {
unsigned char rtm_family;
unsigned char rtm_dst_len;
unsigned char rtm_src_len;
unsigned char rtm_tos;
unsigned char rtm_table;
unsigned char rtm_protocol;
unsigned char rtm_scope;
unsigned char rtm_type;
unsigned rtm_flags;
}
=========================================================================================================
payload中的attributes結構:
header + pad + payload + pad + header + pad + payload + pad ....
可以看出,每個attribute結構也是呈數組形式平行排列,也就是說,一條獨立的netlink消息內可以攜帶多條attribute
=========================================================================================================
attribute中的header標準形式爲
struct nlattr {
unsigned short nla_len; // header + pad + payload
unsigned short nla_type; // attribute類型
}
實際使用時,不同的netlink協議會有相應的header格式,NETLINK_ROUTE協議對應的header爲
struct rtattr {
unsigned short rta_len;
unsigned short rta_type;
}
rta_type跟netlink消息類型密切相關
netlink socket API:
1) socket()函數
socket域(地址族)是AF_NETLINK
socket類型是SOCK_RAW或SOCK_DGRAM,因爲netlink是一種面向數據的服務
netlink協議類型定義在netlink.h(以下以NETLINK_ROUTE爲例),也可以自定義
2) bind()函數
bind函數是把一個本地netlink地址與一個打開的socket進行關聯
nl_pid作爲這個netlink socket的本地標識,可以設置爲當前進程的pid相關,以確保這是一個唯一的32位整數
公式一: nl_pid = getpid();
公式一直接使用進程pid作爲nl_pid的值,前提是該進程只需要一個該類型協議的netlink socket
公式二: nl_pid = pthread_self() << 16 | getpid();
公式二可以使同一進程的不同線程都能獲得屬於它們的相同協議類型的不同netlink socket
對於單進程/線程下多個同一類型的netlink socket,需要用其他方法去對nl_pid作區分
nl_groups用於實現對組播消息的接收,rtnetlink的nl_groups每一位對應的組播類型定義位於renetlink.h
如果應用程序想要接收指定類型的組播netlink消息,需要對nl_groups和該類型組播進行 " | "運算
如果應用程序只想接收發送給它的netlink消息,nl_groups設置爲0
3) sendto/sendmsg
發送netlink消息時需要指定一個目的地址
如果是發往內核,nl_pid和nl_groups都應該設置爲0;
如果是發往另外一個進程的單點傳輸消息,nl_pid設置爲接收進程的pid,nl_groups設置爲0;
如果是發送一個或多個組播消息,需要對nl_groups和每個類型組播消息進行 " | "運算(這種情況下nl_pid如何設置待考證)
發送netlink消息時需要一個自身的消息頭nlmsghdr,這樣做是爲了給所有協議類型的netlink消息提供一個通用的背景,
而且kernel中的netlink部分總是認爲在每個netlink消息中已經包含了消息頭。
發送netlink消息時採用的payload類型根據實際情況選擇