??libnl
庫提供了一套應(yīng)用于Linux系統(tǒng)基于Netlink協(xié)議通信的API接口。從本質(zhì)上看,Netlink其實(shí)是一種典型的IPC機(jī)制茴迁,只不過此IPC主要是介于用戶空間與內(nèi)核空間之間的通信,而非傳統(tǒng)意義上用戶空間進(jìn)程之間的通信。Netlink是設(shè)計(jì)出來替換ioctl
機(jī)制顷编,就目前的使用情況,這一點(diǎn)顯然還沒有完全達(dá)到剑刑。
構(gòu)成
??libnl
在設(shè)計(jì)上被分割為若干個(gè)小型庫(small libraries
)媳纬,應(yīng)用程序在鏈接時(shí)可以擇庫鏈接,無需將所有庫鏈接到應(yīng)用程序中施掏。
-
libnl
:libnl的核心庫钮惠,構(gòu)建Netlink通信的基礎(chǔ),提供套接字操作七芭、消息構(gòu)建/解析素挽、收發(fā)報(bào)文等操作 -
libnl-route
:提供NETLINK_ROUTE
家族的API接口庫,包括網(wǎng)絡(luò)設(shè)備狸驳、路由功能预明、IP地址、鄰居功能等操作 -
libnl-genl
:通用Netlink操作 -
libnl-nf
:NetFilter以及接口監(jiān)控相關(guān)的Netlink操作
libnl庫構(gòu)成及基本特性
使用方法
頭文件
libnl提供的主要頭文件為<netlink/netlink.h>
耙箍,此外還提供了一些其他頭文件撰糠,可以視應(yīng)用程序的需要酌情增刪。
#include <netlink/netlink.h>
#include <netlink/cache.h>
#include <netlink/route/link.h>
#include <netlink/route/addr.h>
鏈接
可以在gcc中直接鏈接libnl庫
$ gcc myprogram.c -o myprogram $(pkgconfig --cflags --libs libnl-3.0)
$ #或采取直接鏈接方式
& gcc -Wall -Wextra -pedantic listif.c -o lsif -lnl-3 -lnl-route-3 -isystem /usr/include/libnl3
Netlink協(xié)議
??Netlink 協(xié)議是基于套接字的進(jìn)程間通信(IPC)機(jī)制辩昆,它可用于用戶空間進(jìn)程和內(nèi)核之間或者用戶空間進(jìn)程之間的通信阅酪。Netlink協(xié)議基于BSD套接字并使用AF_NETLINK地址簇。每一個(gè)Netlink協(xié)議都有自己的協(xié)議號(hào)(比如:NETLINK_ROUTE卤材、NETLINK_NETFILTER等)遮斥。它的尋址方案是基于 32 位的端口號(hào)(之前被稱為 PID),此端口號(hào)用于唯一地標(biāo)識(shí)每一個(gè)對(duì)等通信節(jié)點(diǎn)扇丛。
尋址
??Netlink 地址(端口)由一個(gè) 32 位的整數(shù)組成术吗。端口(port)零保留給內(nèi)核使用,表示每個(gè)Netlink協(xié)議簇中內(nèi)核部分的套接字帆精,其他的端口則通常指的是用戶空間的套接字较屿。
??注意: 最初通常使用進(jìn)程標(biāo)識(shí)符(PID)作為本地端口號(hào)隧魄,但這種方式隨著線程化Netlink應(yīng)用程序的引入而失效,因?yàn)檫@類進(jìn)程需要多個(gè)套接字隘蝎。為解決此項(xiàng)沖突购啄,libnl以進(jìn)程標(biāo)識(shí)符為基數(shù)再加上一個(gè)偏移量來生成唯一的端口號(hào),此方式可以讓一個(gè)進(jìn)程使用多個(gè)套接字嘱么。出于向后兼容方面的考慮狮含,第一個(gè)套接字還是以進(jìn)程標(biāo)識(shí)符作為端口號(hào)。
??上圖中用戶空間有三個(gè)應(yīng)用程序曼振,對(duì)應(yīng)有5個(gè)套接字几迄;而內(nèi)核空間則創(chuàng)建了2個(gè)套接字。同時(shí)冰评,此圖也展示了Netlink的常見應(yīng)用場景:
- 用戶空間的進(jìn)程和內(nèi)核的通信
- 用戶空間內(nèi)進(jìn)程之間的通信
- 偵聽內(nèi)核的多播通知
應(yīng)用場景
用戶空間進(jìn)程和內(nèi)核的通信
??Netlink最常見的應(yīng)用場景就是用戶空間應(yīng)用程序發(fā)送請(qǐng)求給內(nèi)核映胁,比如設(shè)置/讀取接口的IP地址,然后接受并處理內(nèi)核返回的信息甲雅,這個(gè)回復(fù)信息要么是請(qǐng)求出錯(cuò)的信息解孙,要么就是請(qǐng)求成功的通知信息。
用戶空間進(jìn)程之間的通信
??Netlink也可以直接作為用戶空間應(yīng)用程序之間的進(jìn)程間通信機(jī)制抛人,但這種方式并不常見弛姜,通常會(huì)代之以u(píng)nix域套接字。這種通信并不限制在兩個(gè)對(duì)等通信節(jié)點(diǎn)之間函匕,任意一個(gè)節(jié)點(diǎn)都可以和其他的對(duì)等點(diǎn)進(jìn)行通信娱据。此外因?yàn)镹etlink支持多播,一條消息可以由多個(gè)節(jié)點(diǎn)同時(shí)接收到盅惜。
??注意:為了讓套接字對(duì)通信雙方可見中剩,這兩個(gè)套接字必須在同一個(gè)Netlink協(xié)議簇下創(chuàng)建。
偵聽內(nèi)核的多播通知信息
??此類Netlink通信是一種非常常見的應(yīng)用方式抒寂,此方式可以讓用戶空間那些需要處理特定內(nèi)核事件的的守護(hù)進(jìn)程偵聽內(nèi)核反饋的消息/事件结啼。這些應(yīng)用程序進(jìn)程通常會(huì)訂閱內(nèi)核使用的某個(gè)多播組,內(nèi)核則在某些事件發(fā)生的時(shí)候通過它來通知訂閱該組播組的進(jìn)程屈芜。
??相對(duì)于直接尋址來說郊愧,多播(組播)尋址是一個(gè)更好的方式,因?yàn)樗懈叩撵`活性井佑,可以在無需通知內(nèi)核的情況下隨時(shí)與用戶空間應(yīng)用組件交換信息属铁。
消息格式
??Netlink協(xié)議通常是基于消息的,消息則通常是由Netlink消息頭部(struct nlmsghdr)加上有效載荷組成躬翁。雖然有效載荷可以由任何數(shù)據(jù)組成焦蘑,但是它通常的格式是一 個(gè)固定大小的協(xié)議相關(guān)頭部后面緊跟一系列的屬性。
- 總長度(32 位):消息包括 netlink 消息頭部在內(nèi)的總字節(jié)數(shù)
- 消息類型(16 位):消息類型指明了消息的有效載荷的類型盒发。netlink 協(xié)議定義了多個(gè)標(biāo)準(zhǔn)的消息類型例嘱。每個(gè)協(xié) 議簇都可能定義了額外的消息類型狡逢。
- 消息標(biāo)志(16 位):消息標(biāo)志可以用來更改消息類型的行為。
- 序列號(hào)(32 位):序列號(hào)的使用是可選的拼卵,它可以用來引用前一條消息奢浑。比如一條錯(cuò)誤消息中可以引用導(dǎo)致錯(cuò) 誤的那條請(qǐng)求消息。
- 端口號(hào):端口號(hào)指明了這條消息需要發(fā)往哪個(gè)對(duì)等節(jié)點(diǎn)腋腮。如果沒有指定端口號(hào)雀彼,那么這條消息會(huì)被投 遞給同一個(gè)協(xié)議簇中第一個(gè)匹配的內(nèi)核端套接字。
消息類型
??Netlink 在請(qǐng)求消息(requests)低葫、通知消息(notifications)和應(yīng)答消息(replies)的 處理上是有區(qū)別的详羡。請(qǐng)求消息設(shè)有 NLM_F_REQUEST 標(biāo)志位,它用來向接收方請(qǐng)求某種響應(yīng) 嘿悬。一般來說請(qǐng)求消息都是從用戶空間發(fā)送到內(nèi)核的。雖然不是強(qiáng)制規(guī)定水泉,但每次發(fā)送的請(qǐng)求 消息序列號(hào)都應(yīng)該是上一個(gè)序列號(hào)加一善涨。
??由于請(qǐng)求自身的特性,接收方在收到請(qǐng)求消息之后可能會(huì)發(fā)送另一條 netlink 消息來響應(yīng) 這個(gè)請(qǐng)求草则。應(yīng)答消息的序列號(hào)必須和它響應(yīng)的那條請(qǐng)求消息的序列號(hào)一致钢拧。
??通知消息則沒有那么嚴(yán)謹(jǐn),它不需要應(yīng)答炕横,所以序列號(hào)通常是被設(shè)置成 0 的源内。
消息的類型主要是由消息頭部中 16 位的消息類型字段確定的。Netlink 定義了如下標(biāo)準(zhǔn)消息類型:
- NLMSG_NOOP - 無需任何操作份殿,消息必須被丟棄
- NLMSG_ERROR - 錯(cuò)誤消息或者是 ACK膜钓,
- NLMSG_DONE - 分段序列的結(jié)束
- NLMSG_OVERRUN - 通知消息越界錯(cuò)誤
??每個(gè) netlink 協(xié)議都可以自由的定義自己的消息類型。需要注意的是小于 NLMSG_MIN_TYPE(0x10) 的類型是保留的卿嘲,所以不能使用颂斜。通常我們會(huì)定義自己的消息類型來實(shí)現(xiàn) RPC 模式。假設(shè)你的 netlink 協(xié)議的目的是允許你 配置一個(gè)網(wǎng)絡(luò)設(shè)備的某些部分拾枣,所以你想要提供各種配置選項(xiàng)的讀/寫訪問沃疮。完成這項(xiàng)任務(wù) 典型的 “netlik 解決方案” 是定義兩種消息類型MSG_SETCFG,MSG_GETCFG
:
#define MSG_SETCFG 0x11
#define MSG_GETCFG 0x12
??發(fā)送一條 MSG_GETCFG
請(qǐng)求消息通常會(huì)收到一條包含當(dāng)前配置信息的類型為 MSG_SETCFG
的應(yīng)答消息梅肤。用面向?qū)ο蟮男g(shù)語來說這叫做“內(nèi)核在用戶空間設(shè)置了配置信息的本地拷貝”
配置信息可以通過發(fā)送一條
MSG_SETCFG
信息來更改司蔬,這條消息會(huì)收到一條 ACK
響應(yīng)信息 或是一條錯(cuò)誤信息。??此外姨蝴,內(nèi)核也可以在配置信息發(fā)生改變的時(shí)候發(fā)送通知信息俊啼,這樣用戶空間應(yīng)用程序就可以 使用監(jiān)聽而不是頻繁輪詢的方式來獲取改變信息。通知消息通常是使用目前已有的消息類型 這就需要應(yīng)用程序使用不同的套接字處理請(qǐng)求消息和通知消息似扔,當(dāng)然你也可以使用特殊的消息類型來單獨(dú)表示通知信息吨些。
分片消息
??理論上一條 netlink 消息的最大長度是 4GiB搓谆,但是套接字的緩沖區(qū)一般不會(huì)設(shè)置有如此大的空間來容納4GiB消息。通常情況下豪墅,消息的長度被限制在頁的大腥帧(PAGE_SIZE )之內(nèi),然后使用分片機(jī)制把大消息分割為多個(gè)消息偶器。分片消息設(shè)有NLM_F_MULTI
標(biāo)志位斩萌,接收者需要不斷的接收和解析消息,直至收到一個(gè)消息類型為NLMSG_DONE
的消息為止屏轰。
??和分片之后的ip包不一樣颊郎,分片之后的Netlink消息無需重組,但是如果協(xié)議需要重組霎苗,也可以執(zhí)行重組動(dòng)作姆吭。分片消息經(jīng)常會(huì)被用來發(fā)送對(duì)象列表或者是對(duì)象樹,這種情況下消息段只是簡單的承載幾個(gè)對(duì)象唁盏,一般允許單獨(dú)處理每個(gè)消息分片内狸。
錯(cuò)誤消息
??錯(cuò)誤消息可以作為請(qǐng)求消息的響應(yīng)發(fā)送回去,錯(cuò)誤消息必須使用標(biāo)準(zhǔn)的消息類型 NLMSG_ERROR
厘擂,它的有效載荷是由錯(cuò)誤碼和原來的請(qǐng)求消息頭部組成昆淡。
注意:錯(cuò)誤消息的序列號(hào)必須和導(dǎo)致錯(cuò)誤發(fā)生的請(qǐng)求消息的序列號(hào)設(shè)置為一致。
ACKs消息
??發(fā)送方可以通過設(shè)置NLM_F_ACK
標(biāo)志位來要求接收方為處理過的每一個(gè)請(qǐng)求消息都發(fā)送一 個(gè)ACK消息刽严。這種方式通常是用來讓發(fā)送方在請(qǐng)求被接收方處理之后同步下一步的操作昂灵。
??ACK 消息和錯(cuò)誤消息使用一樣的消息類型
(NLMSG_ERROR)
和負(fù)荷格式,不同的是 ACK 消息 的錯(cuò)誤代碼被設(shè)置為 0舞萄。
消息標(biāo)志
#define NLM_F_REQUEST 1
#define NLM_F_MULTI 2
#define NLM_F_ACK 4
#define NLM_F_ECHO 8
-
NLM_F_REQUEST
- 請(qǐng)求消息 -
NLM_F_MULTI
- 分片消息 -
NLM_F_ACK
- 請(qǐng)求了 ACK 回復(fù) -
NLM_F_ECHO
- 請(qǐng)求回應(yīng)這個(gè)請(qǐng)求消息眨补。
??NLM_F_ECHO
標(biāo)志和NLM_F_ACK
標(biāo)志類似,它可以和NLM_F_REQUEST
標(biāo)志位一起使 用鹏氧,使得發(fā)送者能夠收到作為這條請(qǐng)求消息的響應(yīng)而產(chǎn)生的通知信息渤涌,無論發(fā)送者是否訂閱過相應(yīng)的多播組。
GET 請(qǐng)求還定義了一些額外的通用標(biāo)志位:
#define NLM_F_ROOT 0x100
#define NLM_F_MATCH 0x200
#define NLM_F_ATOMIC 0x400
#define NLM_F_DUMP (NLM_F_ROOT|NLM_F_MATCH)
- NLM_F_ROOT - 返回樹的根節(jié)點(diǎn)把还。
- NLM_F_MATCH - 返回所有匹配的節(jié)點(diǎn)实蓬。
- NLM_F_ATOMIC - 已廢棄,以前用來請(qǐng)求一個(gè)原子操作
- NLM_F_DUMP - 返回一個(gè)含有所有對(duì)象的列表(NLM_F_ROOT|NLM_F_MATCH)
??這些標(biāo)志的使用是完全可選的吊履,許多netlink協(xié)議都只使用NLM_F_DUMP標(biāo)志安皱。這個(gè)標(biāo)志通常用來請(qǐng)求接收者發(fā)送一個(gè)包含所有對(duì)象的列表,而這個(gè)列表則通常是一系列的消息分片艇炎。
??還有一些和 NEW 或者是 SET 請(qǐng)求相關(guān)的標(biāo)志酌伊。這些標(biāo)志和 GET 的那些標(biāo)志是彼此互斥的 :
#define NLM_F_REPLACE 0x100
#define NLM_F_EXCL 0x200
#define NLM_F_CREATE 0x400
#define NLM_F_APPEND 0x800
- NLM_F_REPLACE - 如果對(duì)象存在的話,替換它。
- NLM_F_EXCL - 如果這個(gè)對(duì)象存在的話居砖,就不用更新它虹脯。
- NLM_F_CREATE - 如果對(duì)象不存在的話,創(chuàng)建它奏候。
- NLM_F_APPEND - 在對(duì)象列表的末尾添加新的對(duì)象循集。
這些標(biāo)志含義在不同的 netlink 協(xié)議中可能會(huì)有細(xì)微的差別。
序列號(hào)
??Netlink允許通過序列號(hào)來關(guān)聯(lián)回復(fù)和請(qǐng)求蔗草。需要注意的是咒彤,這里的序列號(hào)和 TCP 這類的協(xié)議的序列號(hào)是不一樣的,Netlink的序列號(hào)并不強(qiáng)制使用咒精。序列號(hào)唯一的用途就是把一條應(yīng)答消息和相應(yīng)的請(qǐng)求消息聯(lián)系起來镶柱,序列號(hào)是以單個(gè)套接字為基礎(chǔ)來管理。