自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

Linux Netlink機(jī)制:現(xiàn)代網(wǎng)絡(luò)通信的核心

系統(tǒng) Linux
在 Netlink 通信中,安全問(wèn)題不容忽視。如果通信過(guò)程中出現(xiàn)安全漏洞,可能會(huì)導(dǎo)致系統(tǒng)信息泄露、被惡意攻擊等嚴(yán)重后果。

在Linux的廣袤世界里,進(jìn)程間通信(IPC)是一個(gè)核心話題,它就像是系統(tǒng)的神經(jīng)系統(tǒng),讓不同的進(jìn)程能夠交流協(xié)作,共同維持系統(tǒng)的正常運(yùn)轉(zhuǎn)。而在眾多的 IPC 機(jī)制中,Netlink 猶如一把秘密武器,發(fā)揮著獨(dú)特而關(guān)鍵的作用。傳統(tǒng)的進(jìn)程間通信方式,如管道、消息隊(duì)列、共享內(nèi)存等,各自有著特定的應(yīng)用場(chǎng)景和局限性。管道雖然簡(jiǎn)單易用,但它是半雙工的,數(shù)據(jù)只能單向流動(dòng),并且通常只適用于具有親緣關(guān)系的進(jìn)程之間;消息隊(duì)列可以實(shí)現(xiàn)消息的異步傳遞,但它的效率相對(duì)較低,不太適合大量數(shù)據(jù)的傳輸;共享內(nèi)存雖然速度快,但需要復(fù)雜的同步機(jī)制來(lái)保證數(shù)據(jù)的一致性。

Netlink 的出現(xiàn),為 Linux 系統(tǒng)中的進(jìn)程間通信帶來(lái)了新的活力。它主要用于內(nèi)核空間與用戶空間的進(jìn)程之間的通信,為兩者搭建了一座高效溝通的橋梁。與傳統(tǒng)的系統(tǒng)調(diào)用相比,Netlink 提供了更為靈活且高效的通訊方式,特別適用于在多線程環(huán)境中進(jìn)行大量數(shù)據(jù)傳輸。在網(wǎng)絡(luò)配置、路由管理、設(shè)備驅(qū)動(dòng)等眾多場(chǎng)景中,Netlink 都有著廣泛的應(yīng)用。

比如,我們?nèi)粘J褂玫木W(wǎng)絡(luò)配置工具,就是通過(guò) Netlink 與內(nèi)核進(jìn)行交互,實(shí)現(xiàn)對(duì)網(wǎng)絡(luò)接口的配置和管理;路由守護(hù)進(jìn)程(routed)也依賴 Netlink 來(lái)獲取和更新路由信息,確保網(wǎng)絡(luò)數(shù)據(jù)包能夠正確地轉(zhuǎn)發(fā)。Netlink 之所以能夠在這些復(fù)雜的場(chǎng)景中發(fā)揮重要作用,得益于它的一些獨(dú)特優(yōu)勢(shì)。接下來(lái),就讓我們深入探索 Netlink 的奧秘,揭開(kāi)它神秘的面紗。

一、什么是Netlink?

Netlink 套接字是實(shí)現(xiàn)用戶進(jìn)程與內(nèi)核進(jìn)程通信的一種特殊的進(jìn)程間通信(IPC)方式,也是網(wǎng)絡(luò)應(yīng)用程序與內(nèi)核通信的最常用接口 。它是 Linux 所特有的一種特殊 socket,類似于 BSD 中的 AF_ROUTE,但功能遠(yuǎn)比其強(qiáng)大。在最新的 Linux 內(nèi)核(2.6.14 及以后版本)中,有眾多應(yīng)用借助 netlink 實(shí)現(xiàn)應(yīng)用與內(nèi)核的通信。

從本質(zhì)上講,Netlink 是一種在內(nèi)核與用戶應(yīng)用間進(jìn)行雙向數(shù)據(jù)傳輸?shù)臋C(jī)制。用戶態(tài)應(yīng)用可以使用標(biāo)準(zhǔn)的 socket API 來(lái)利用 netlink 提供的強(qiáng)大功能,而內(nèi)核態(tài)則需要使用專門的內(nèi)核 API 來(lái)操作 netlink。這就好比是兩個(gè)不同世界的居民,使用著不同的語(yǔ)言(API),卻通過(guò) Netlink 這座橋梁能夠順暢地交流。

Netlink 的通信基于 BSD socket 和 AF_NETLINK 地址簇,采用 32 位的端口號(hào)尋址(以前也稱作 PID) 。每個(gè) Netlink 協(xié)議(也可稱作總線,在 man 手冊(cè)中被稱為 netlink family),通常都與一個(gè)或一組內(nèi)核服務(wù) / 組件緊密相關(guān)聯(lián)。例如,NETLINK_ROUTE 專門用于獲取和設(shè)置路由與鏈路信息,當(dāng)我們需要配置網(wǎng)絡(luò)路由時(shí),相關(guān)的用戶空間程序就會(huì)通過(guò) NETLINK_ROUTE 協(xié)議與內(nèi)核進(jìn)行交互;NETLINK_KOBJECT_UEVENT 則用于內(nèi)核向用戶空間的 udev 進(jìn)程發(fā)送通知,在設(shè)備熱插拔等事件發(fā)生時(shí),內(nèi)核就會(huì)通過(guò)這個(gè)協(xié)議將事件信息傳遞給用戶空間,以便 udev 進(jìn)程做出相應(yīng)的處理。

一般來(lái)說(shuō)用戶空間和內(nèi)核空間的通信方式有三種:/proc、ioctl、Netlink。而前兩種都是單向的,而Netlink可以實(shí)現(xiàn)雙工通信。

Netlink 相對(duì)于系統(tǒng)調(diào)用,ioctl 以及 /proc 文件系統(tǒng)而言具有以下優(yōu)點(diǎn):

  1. 為了使用 netlink,用戶僅需要在 include/linux/netlink.h 中增加一個(gè)新類型的 netlink 協(xié)議定義即可, 如 #define NETLINK_MYTEST 17 然后,內(nèi)核和用戶態(tài)應(yīng)用就可以立即通過(guò) socket API 使用該 netlink 協(xié)議類型進(jìn)行數(shù)據(jù)交換。但系統(tǒng)調(diào)用需要增加新的系統(tǒng)調(diào)用,ioctl 則需要增加設(shè)備或文件, 那需要不少代碼,proc 文件系統(tǒng)則需要在 /proc 下添加新的文件或目錄,那將使本來(lái)就混亂的 /proc 更加混亂。
  2. netlink是一種異步通信機(jī)制,在內(nèi)核與用戶態(tài)應(yīng)用之間傳遞的消息保存在socket緩存隊(duì)列中,發(fā)送消息只是把消息保存在接收者的socket的接 收隊(duì)列,而不需要等待接收者收到消息,但系統(tǒng)調(diào)用與 ioctl 則是同步通信機(jī)制,如果傳遞的數(shù)據(jù)太長(zhǎng),將影響調(diào)度粒度。
  3. 使用 netlink 的內(nèi)核部分可以采用模塊的方式實(shí)現(xiàn),使用 netlink 的應(yīng)用部分和內(nèi)核部分沒(méi)有編譯時(shí)依賴,但系統(tǒng)調(diào)用就有依賴,而且新的系統(tǒng)調(diào)用的實(shí)現(xiàn)必須靜態(tài)地連接到內(nèi)核中,它無(wú)法在模塊中實(shí)現(xiàn),使用新系統(tǒng)調(diào)用的應(yīng)用在編譯時(shí)需要依賴內(nèi)核。
  4. netlink 支持多播,內(nèi)核模塊或應(yīng)用可以把消息多播給一個(gè)netlink組,屬于該neilink 組的任何內(nèi)核模塊或應(yīng)用都能接收到該消息,內(nèi)核事件向用戶態(tài)的通知機(jī)制就使用了這一特性,任何對(duì)內(nèi)核事件感興趣的應(yīng)用都能收到該子系統(tǒng)發(fā)送的內(nèi)核事件,在 后面的文章中將介紹這一機(jī)制的使用。
  5. 內(nèi)核可以使用 netlink 首先發(fā)起會(huì)話,但系統(tǒng)調(diào)用和 ioctl 只能由用戶應(yīng)用發(fā)起調(diào)用。
  6. netlink 使用標(biāo)準(zhǔn)的 socket API,因此很容易使用,但系統(tǒng)調(diào)用和 ioctl則需要專門的培訓(xùn)才能使用。

Netlink協(xié)議基于BSD socket和AF_NETLINK地址簇,使用32位的端口號(hào)尋址,每個(gè)Netlink協(xié)議通常與一個(gè)或一組內(nèi)核服務(wù)/組件相關(guān)聯(lián),如NETLINK_ROUTE用于獲取和設(shè)置路由與鏈路信息、NETLINK_KOBJECT_UEVENT用于內(nèi)核向用戶空間的udev進(jìn)程發(fā)送通知等。

二、用戶態(tài)數(shù)據(jù)結(jié)構(gòu)

用戶態(tài)應(yīng)用使用標(biāo)準(zhǔn)的socket APIs, socket(), bind(), sendmsg(), recvmsg() 和 close() 就能很容易地使用 netlink socket,查詢手冊(cè)頁(yè)可以了解這些函數(shù)的使用細(xì)節(jié),本文只是講解使用 netlink 的用戶應(yīng)該如何使用這些函數(shù)。注意,使用 netlink 的應(yīng)用必須包含頭文件 linux/netlink.h。當(dāng)然 socket 需要的頭文件也必不可少,sys/socket.h。Netlink通信跟常用UDP Socket通信類似,struct sockaddr_nl是netlink通信地址,跟普通socket struct sockaddr_in類似。

(1)struct sockaddr_nl結(jié)構(gòu):

struct sockaddr_nl {
     __kernel_sa_family_t    nl_family;  /* AF_NETLINK (跟AF_INET對(duì)應(yīng))*/
     unsigned short  nl_pad;     /* zero */
     __u32       nl_pid;     /* port ID  (通信端口號(hào))*/
     __u32       nl_groups;  /* multicast groups mask */
};

(2)struct nlmsghd 結(jié)構(gòu):

/* struct nlmsghd 是netlink消息頭*/
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_type:消息狀態(tài),內(nèi)核在include/uapi/linux/netlink.h中定義了以下4種通用的消息類型,它們分別是:

#define NLMSG_NOOP      0x1 /* Nothing.     */
#define NLMSG_ERROR     0x2 /* Error        */
#define NLMSG_DONE      0x3 /* End of a dump    */
#define NLMSG_OVERRUN       0x4 /* Data lost        */
#define NLMSG_MIN_TYPE      0x10    /* < 0x10: reserved control messages */

nlmsg_flags:消息標(biāo)記,它們用以表示消息的類型,如下:

/* Flags values */
#define NLM_F_REQUEST       1   /* It is request message.   */
#define NLM_F_MULTI     2   /* Multipart message, terminated by NLMSG_DONE */
#define NLM_F_ACK       4   /* Reply with ack, with zero or error code */
#define NLM_F_ECHO      8   /* Echo this request        */
#define NLM_F_DUMP_INTR     16  /* Dump was inconsistent due to sequence change */

/* Modifiers to GET request */
#define NLM_F_ROOT  0x100   /* specify tree root    */
#define NLM_F_MATCH 0x200   /* return all matching  */
#define NLM_F_ATOMIC    0x400   /* atomic GET       */
#define NLM_F_DUMP  (NLM_F_ROOT|NLM_F_MATCH)

/* Modifiers to NEW request */
#define NLM_F_REPLACE   0x100   /* Override existing        */
#define NLM_F_EXCL  0x200   /* Do not touch, if it exists   */
#define NLM_F_CREATE    0x400   /* Create, if it does not exist */
#define NLM_F_APPEND    0x800   /* Add to end of list       */

(3)struct msghdr 結(jié)構(gòu)體

struct iovec {                    /* Scatter/gather array items */
     void  *iov_base;              /* Starting address */
     size_t iov_len;               /* Number of bytes to transfer */
 };
  /* iov_base: iov_base指向數(shù)據(jù)包緩沖區(qū),即參數(shù)buff,iov_len是buff的長(zhǎng)度。msghdr中允許一次傳遞多個(gè)buff,以數(shù)組的形式組織在 msg_iov中,msg_iovlen就記錄數(shù)組的長(zhǎng)度 (即有多少個(gè)buff)  */
 struct msghdr {
     void         *msg_name;       /* optional address */
     socklen_t     msg_namelen;    /* size of address */
     struct iovec *msg_iov;        /* scatter/gather array */
     size_t        msg_iovlen;     /* # elements in msg_iov */
     void         *msg_control;    /* ancillary data, see below */
     size_t        msg_controllen; /* ancillary data buffer len */
     int           msg_flags;      /* flags on received message */
 };

為了創(chuàng)建一個(gè) netlink socket,用戶需要使用如下參數(shù)調(diào)用 socket():

socket(AF_NETLINK, SOCK_RAW, netlink_type)

第一個(gè)參數(shù)必須是 AF_NETLINK 或 PF_NETLINK,在 Linux 中,它們倆實(shí)際為一個(gè)東西,它表示要使用netlink,第二個(gè)參數(shù)必須是SOCK_RAW或SOCK_DGRAM, 第三個(gè)參數(shù)指定netlink協(xié)議類型,如前面講的用戶自定義協(xié)議類型NETLINK_MYTEST, NETLINK_GENERIC是一個(gè)通用的協(xié)議類型,它是專門為用戶使用的,因此,用戶可以直接使用它,而不必再添加新的協(xié)議類型。內(nèi)核預(yù)定義的協(xié)議類 型有:

#define NETLINK_ROUTE           0       /* Routing/device hook                          */
#define NETLINK_W1              1       /* 1-wire subsystem                             */
#define NETLINK_USERSOCK        2       /* Reserved for user mode socket protocols      */
#define NETLINK_FIREWALL        3       /* Firewalling hook                             */
#define NETLINK_INET_DIAG       4       /* INET socket monitoring                       */
#define NETLINK_NFLOG           5       /* netfilter/iptables ULOG */
#define NETLINK_XFRM            6       /* ipsec */
#define NETLINK_SELINUX         7       /* SELinux event notifications */
#define NETLINK_ISCSI           8       /* Open-iSCSI */
#define NETLINK_AUDIT           9       /* auditing */
#define NETLINK_FIB_LOOKUP      10
#define NETLINK_CONNECTOR       11
#define NETLINK_NETFILTER       12      /* netfilter subsystem */
#define NETLINK_IP6_FW          13
#define NETLINK_DNRTMSG         14      /* DECnet routing messages */
#define NETLINK_KOBJECT_UEVENT  15      /* Kernel messages to userspace */
#define NETLINK_GENERIC         16

對(duì)于每一個(gè)netlink協(xié)議類型,可以有多達(dá) 32多播組,每一個(gè)多播組用一個(gè)位表示,netlink 的多播特性使得發(fā)送消息給同一個(gè)組僅需要一次系統(tǒng)調(diào)用,因而對(duì)于需要多撥消息的應(yīng)用而言,大大地降低了系統(tǒng)調(diào)用的次數(shù)。

函數(shù) bind() 用于把一個(gè)打開(kāi)的 netlink socket 與 netlink 源 socket 地址綁定在一起。netlink socket 的地址結(jié)構(gòu)如下:

struct sockaddr_nl
{
  sa_family_t    nl_family;
  unsigned short nl_pad;
  __u32          nl_pid;
  __u32          nl_groups;
};

字段 nl_family 必須設(shè)置為 AF_NETLINK 或著 PF_NETLINK,字段 nl_pad 當(dāng)前沒(méi)有使用,因此要總是設(shè)置為 0,字段 nl_pid 為接收或發(fā)送消息的進(jìn)程的 ID,如果希望內(nèi)核處理消息或多播消息,就把該字段設(shè)置為 0,否則設(shè)置為處理消息的進(jìn)程 ID。字段 nl_groups 用于指定多播組,bind 函數(shù)用于把調(diào)用進(jìn)程加入到該字段指定的多播組,如果設(shè)置為 0,表示調(diào)用者不加入任何多播組。

傳遞給 bind 函數(shù)的地址的 nl_pid 字段應(yīng)當(dāng)設(shè)置為本進(jìn)程的進(jìn)程 ID,這相當(dāng)于 netlink socket 的本地地址。但是,對(duì)于一個(gè)進(jìn)程的多個(gè)線程使用 netlink socket 的情況,字段 nl_pid 則可以設(shè)置為其它的值,如:

pthread_self() << 16 | getpid();

因此字段 nl_pid 實(shí)際上未必是進(jìn)程 ID,它只是用于區(qū)分不同的接收者或發(fā)送者的一個(gè)標(biāo)識(shí),用戶可以根據(jù)自己需要設(shè)置該字段。函數(shù) bind 的調(diào)用方式如下:

bind(fd, (struct sockaddr*)&nladdr, sizeof(struct sockaddr_nl));

fd為前面的 socket 調(diào)用返回的文件描述符,參數(shù) nladdr 為 struct sockaddr_nl 類型的地址。為了發(fā)送一個(gè) netlink 消息給內(nèi)核或其他用戶態(tài)應(yīng)用,需要填充目標(biāo) netlink socket 地址,此時(shí),字段 nl_pid 和 nl_groups 分別表示接收消息者的進(jìn)程 ID 與多播組。如果字段 nl_pid 設(shè)置為 0,表示消息接收者為內(nèi)核或多播組,如果 nl_groups為 0,表示該消息為單播消息,否則表示多播消息。使用函數(shù) sendmsg 發(fā)送 netlink 消息時(shí)還需要引用結(jié)構(gòu) struct msghdr、struct nlmsghdr 和 struct iovec,結(jié)構(gòu) struct msghdr 需如下設(shè)置:

struct msghdr msg;
memset(&msg, 0, sizeof(msg));
msg.msg_name = (void *)&(nladdr);
msg.msg_namelen = sizeof(nladdr);

其中 nladdr 為消息接收者的 netlink 地址,struct nlmsghdr 為 netlink socket 自己的消息頭,這用于多路復(fù)用和多路分解 netlink 定義的所有協(xié)議類型以及其它一些控制,netlink 的內(nèi)核實(shí)現(xiàn)將利用這個(gè)消息頭來(lái)多路復(fù)用和多路分解已經(jīng)其它的一些控制,因此它也被稱為netlink 控制塊。因此,應(yīng)用在發(fā)送 netlink 消息時(shí)必須提供該消息頭。

struct nlmsghdr
{
  __u32 nlmsg_len;   /* Length of message */
  __u16 nlmsg_type;  /* Message type*/
  __u16 nlmsg_flags; /* Additional flags */
  __u32 nlmsg_seq;   /* Sequence number */
  __u32 nlmsg_pid;   /* Sending process PID */
};

字段 nlmsg_len 指定消息的總長(zhǎng)度,包括緊跟該結(jié)構(gòu)的數(shù)據(jù)部分長(zhǎng)度以及該結(jié)構(gòu)的大小,字段 nlmsg_type 用于應(yīng)用內(nèi)部定義消息的類型,它對(duì) netlink 內(nèi)核實(shí)現(xiàn)是透明的,因此大部分情況下設(shè)置為 0,字段 nlmsg_flags 用于設(shè)置消息標(biāo)志,可用的標(biāo)志包括:

/* Flags values */
#define NLM_F_REQUEST           1       /* It is request message.       */
#define NLM_F_MULTI             2       /* Multipart message, terminated by NLMSG_DONE */
#define NLM_F_ACK               4       /* Reply with ack, with zero or error code */
#define NLM_F_ECHO              8       /* Echo this request            */
/* Modifiers to GET request */
#define NLM_F_ROOT      0x100   /* specify tree root    */
#define NLM_F_MATCH     0x200   /* return all matching  */
#define NLM_F_ATOMIC    0x400   /* atomic GET           */
#define NLM_F_DUMP      (NLM_F_ROOT|NLM_F_MATCH)
/* Modifiers to NEW request */
#define NLM_F_REPLACE   0x100   /* Override existing            */
#define NLM_F_EXCL      0x200   /* Do not touch, if it exists   */
#define NLM_F_CREATE    0x400   /* Create, if it does not exist */
#define NLM_F_APPEND    0x800   /* Add to end of list           */
  • 標(biāo)志NLM_F_REQUEST用于表示消息是一個(gè)請(qǐng)求,所有應(yīng)用首先發(fā)起的消息都應(yīng)設(shè)置該標(biāo)志。
  • 標(biāo)志NLM_F_MULTI 用于指示該消息是一個(gè)多部分消息的一部分,后續(xù)的消息可以通過(guò)宏NLMSG_NEXT來(lái)獲得。
  • 宏NLM_F_ACK表示該消息是前一個(gè)請(qǐng)求消息的響應(yīng),順序號(hào)與進(jìn)程ID可以把請(qǐng)求與響應(yīng)關(guān)聯(lián)起來(lái)。
  • 標(biāo)志NLM_F_ECHO表示該消息是相關(guān)的一個(gè)包的回傳。
  • 標(biāo)志NLM_F_ROOT 被許多 netlink 協(xié)議的各種數(shù)據(jù)獲取操作使用,該標(biāo)志指示被請(qǐng)求的數(shù)據(jù)表應(yīng)當(dāng)整體返回用戶應(yīng)用,而不是一個(gè)條目一個(gè)條目地返回。有該標(biāo)志的請(qǐng)求通常導(dǎo)致響應(yīng)消息設(shè)置 NLM_F_MULTI標(biāo)志。注意,當(dāng)設(shè)置了該標(biāo)志時(shí),請(qǐng)求是協(xié)議特定的,因此,需要在字段 nlmsg_type 中指定協(xié)議類型。
  • 標(biāo)志 NLM_F_MATCH 表示該協(xié)議特定的請(qǐng)求只需要一個(gè)數(shù)據(jù)子集,數(shù)據(jù)子集由指定的協(xié)議特定的過(guò)濾器來(lái)匹配。
  • 標(biāo)志 NLM_F_ATOMIC 指示請(qǐng)求返回的數(shù)據(jù)應(yīng)當(dāng)原子地收集,這預(yù)防數(shù)據(jù)在獲取期間被修改。
  • 標(biāo)志 NLM_F_DUMP 未實(shí)現(xiàn)。
  • 標(biāo)志 NLM_F_REPLACE 用于取代在數(shù)據(jù)表中的現(xiàn)有條目。
  • 標(biāo)志 NLM_F_EXCL_ 用于和 CREATE 和 APPEND 配合使用,如果條目已經(jīng)存在,將失敗。
  • 標(biāo)志 NLM_F_CREATE 指示應(yīng)當(dāng)在指定的表中創(chuàng)建一個(gè)條目。
  • 標(biāo)志 NLM_F_APPEND 指示在表末尾添加新的條目。

內(nèi)核需要讀取和修改這些標(biāo)志,對(duì)于一般的使用,用戶把它設(shè)置為 0 就可以,只是一些高級(jí)應(yīng)用(如 netfilter 和路由 daemon 需要它進(jìn)行一些復(fù)雜的操作),字段 nlmsg_seq 和 nlmsg_pid 用于應(yīng)用追蹤消息,前者表示順序號(hào),后者為消息來(lái)源進(jìn)程 ID。下面是一個(gè)示例:

#define MAX_MSGSIZE 1024
char buffer[] = "An example message";
struct nlmsghdr nlhdr;
nlhdr = (struct nlmsghdr *)malloc(NLMSG_SPACE(MAX_MSGSIZE));
strcpy(NLMSG_DATA(nlhdr),buffer);
nlhdr->nlmsg_len = NLMSG_LENGTH(strlen(buffer));
nlhdr->nlmsg_pid = getpid();  /* self pid */
nlhdr->nlmsg_flags = 0;

結(jié)構(gòu) struct iovec 用于把多個(gè)消息通過(guò)一次系統(tǒng)調(diào)用來(lái)發(fā)送,下面是該結(jié)構(gòu)使用示例:

struct iovec iov;
iov.iov_base = (void *)nlhdr;
iov.iov_len = nlh->nlmsg_len;
msg.msg_iov = &iov;
msg.msg_iovlen = 1;

在完成以上步驟后,消息就可以通過(guò)下面語(yǔ)句直接發(fā)送:

sendmsg(fd, &msg, 0);

應(yīng)用接收消息時(shí)需要首先分配一個(gè)足夠大的緩存來(lái)保存消息頭以及消息的數(shù)據(jù)部分,然后填充消息頭,添完后就可以直接調(diào)用函數(shù) recvmsg() 來(lái)接收。

#define MAX_NL_MSG_LEN 1024
struct sockaddr_nl nladdr;
struct msghdr msg;
struct iovec iov;
struct nlmsghdr * nlhdr;
nlhdr = (struct nlmsghdr *)malloc(MAX_NL_MSG_LEN);
iov.iov_base = (void *)nlhdr;
iov.iov_len = MAX_NL_MSG_LEN;
msg.msg_name = (void *)&(nladdr);
msg.msg_namelen = sizeof(nladdr);
msg.msg_iov = &iov;
msg.msg_iovlen = 1;
recvmsg(fd, &msg, 0);

注意:fd為socket調(diào)用打開(kāi)的netlink socket描述符,在消息接收后,nlhdr指向接收到的消息的消息頭,nladdr保存了接收到的消息的目標(biāo)地址,宏NLMSG_DATA(nlhdr)返回指向消息的數(shù)據(jù)部分的指針。

在linux/netlink.h中定義了一些方便對(duì)消息進(jìn)行處理的宏,這些宏包括:

#define NLMSG_ALIGNTO   4
#define NLMSG_ALIGN(len) ( ((len)+NLMSG_ALIGNTO-1) & ~(NLMSG_ALIGNTO-1) )

宏NLMSG_ALIGN(len)用于得到不小于len且字節(jié)對(duì)齊的最小數(shù)值。

#define NLMSG_LENGTH(len) ((len)+NLMSG_ALIGN(sizeof(struct nlmsghdr)))

宏NLMSG_LENGTH(len)用于計(jì)算數(shù)據(jù)部分長(zhǎng)度為len時(shí)實(shí)際的消息長(zhǎng)度。它一般用于分配消息緩存。

#define NLMSG_SPACE(len) NLMSG_ALIGN(NLMSG_LENGTH(len))

宏NLMSG_SPACE(len)返回不小于NLMSG_LENGTH(len)且字節(jié)對(duì)齊的最小數(shù)值,它也用于分配消息緩存。

#define NLMSG_DATA(nlh)  ((void*)(((char*)nlh) + NLMSG_LENGTH(0)))

宏NLMSG_DATA(nlh)用于取得消息的數(shù)據(jù)部分的首地址,設(shè)置和讀取消息數(shù)據(jù)部分時(shí)需要使用該宏。

#define NLMSG_NEXT(nlh,len)      ((len) -= NLMSG_ALIGN((nlh)->nlmsg_len), \
                      (struct nlmsghdr*)(((char*)(nlh)) + NLMSG_ALIGN((nlh)->nlmsg_len)))

宏NLMSG_NEXT(nlh,len)用于得到下一個(gè)消息的首地址,同時(shí)len也減少為剩余消息的總長(zhǎng)度,該宏一般在一個(gè)消息被分成幾個(gè)部分發(fā)送或接收時(shí)使用。

#define NLMSG_OK(nlh,len) ((len) >= (int)sizeof(struct nlmsghdr) && \
                           (nlh)->nlmsg_len >= sizeof(struct nlmsghdr) && \
                           (nlh)->nlmsg_len <= (len))

宏NLMSG_OK(nlh,len)用于判斷消息是否有l(wèi)en這么長(zhǎng)。

#define NLMSG_PAYLOAD(nlh,len) ((nlh)->nlmsg_len - NLMSG_SPACE((len)))

宏NLMSG_PAYLOAD(nlh,len)用于返回payload的長(zhǎng)度,函數(shù)close用于關(guān)閉打開(kāi)的netlink socket。

三、netlink內(nèi)核數(shù)據(jù)結(jié)構(gòu)

(1)netlink消息類型:

#define NETLINK_ROUTE       0   /* Routing/device hook              */
#define NETLINK_UNUSED      1   /* Unused number                */
#define NETLINK_USERSOCK    2   /* Reserved for user mode socket protocols  */
#define NETLINK_FIREWALL    3   /* Unused number, formerly ip_queue     */
#define NETLINK_SOCK_DIAG   4   /* socket monitoring                */
#define NETLINK_NFLOG       5   /* netfilter/iptables ULOG */
#define NETLINK_XFRM        6   /* ipsec */
#define NETLINK_SELINUX     7   /* SELinux event notifications */
#define NETLINK_ISCSI       8   /* Open-iSCSI */
#define NETLINK_AUDIT       9   /* auditing */
#define NETLINK_FIB_LOOKUP  10  
#define NETLINK_CONNECTOR   11
#define NETLINK_NETFILTER   12  /* netfilter subsystem */
#define NETLINK_IP6_FW      13
#define NETLINK_DNRTMSG     14  /* DECnet routing messages */
#define NETLINK_KOBJECT_UEVENT  15  /* Kernel messages to userspace */
#define NETLINK_GENERIC     16
/* leave room for NETLINK_DM (DM Events) */
#define NETLINK_SCSITRANSPORT   18  /* SCSI Transports */
#define NETLINK_ECRYPTFS    19
#define NETLINK_RDMA        20
#define NETLINK_CRYPTO      21  /* Crypto layer */

#define NETLINK_INET_DIAG   NETLINK_SOCK_DIAG

#define MAX_LINKS 32

(2)netlink常用宏:

#define NLMSG_ALIGNTO   4U
/* 宏NLMSG_ALIGN(len)用于得到不小于len且字節(jié)對(duì)齊的最小數(shù)值 */
#define NLMSG_ALIGN(len) ( ((len)+NLMSG_ALIGNTO-1) & ~(NLMSG_ALIGNTO-1) )

/* Netlink 頭部長(zhǎng)度 */
#define NLMSG_HDRLEN     ((int) NLMSG_ALIGN(sizeof(struct nlmsghdr)))

/* 計(jì)算消息數(shù)據(jù)len的真實(shí)消息長(zhǎng)度(消息體 + 消息頭)*/
#define NLMSG_LENGTH(len) ((len) + NLMSG_HDRLEN)

/* 宏NLMSG_SPACE(len)返回不小于NLMSG_LENGTH(len)且字節(jié)對(duì)齊的最小數(shù)值 */
#define NLMSG_SPACE(len) NLMSG_ALIGN(NLMSG_LENGTH(len))

/* 宏NLMSG_DATA(nlh)用于取得消息的數(shù)據(jù)部分的首地址,設(shè)置和讀取消息數(shù)據(jù)部分時(shí)需要使用該宏 */
#define NLMSG_DATA(nlh)  ((void*)(((char*)nlh) + NLMSG_LENGTH(0)))

/* 宏NLMSG_NEXT(nlh,len)用于得到下一個(gè)消息的首地址, 同時(shí)len 變?yōu)槭S嘞⒌拈L(zhǎng)度 */
#define NLMSG_NEXT(nlh,len)  ((len) -= NLMSG_ALIGN((nlh)->nlmsg_len), \
                  (struct nlmsghdr*)(((char*)(nlh)) + NLMSG_ALIGN((nlh)->nlmsg_len)))

/* 判斷消息是否 >len */
#define NLMSG_OK(nlh,len) ((len) >= (int)sizeof(struct nlmsghdr) && \
               (nlh)->nlmsg_len >= sizeof(struct nlmsghdr) && \
               (nlh)->nlmsg_len <= (len))

/* NLMSG_PAYLOAD(nlh,len) 用于返回payload的長(zhǎng)度*/
#define NLMSG_PAYLOAD(nlh,len) ((nlh)->nlmsg_len - NLMSG_SPACE((len)))

(3)netlink 內(nèi)核常用函數(shù)

netlink_kernel_create內(nèi)核函數(shù)用于創(chuàng)建內(nèi)核socket與用戶態(tài)通信

static inline struct sock *
netlink_kernel_create(struct net *net, int unit, struct netlink_kernel_cfg *cfg)
/* net: net指向所在的網(wǎng)絡(luò)命名空間, 一般默認(rèn)傳入的是&init_net(不需要定義);  定義在net_namespace.c(extern struct net init_net);
   unit:netlink協(xié)議類型
   cfg:cfg存放的是netlink內(nèi)核配置參數(shù)(如下)
*/

/* optional Netlink kernel configuration parameters */
struct netlink_kernel_cfg {
    unsigned int    groups;  
    unsigned int    flags;  
    void        (*input)(struct sk_buff *skb); /* input 回調(diào)函數(shù) */
    struct mutex    *cb_mutex; 
    void        (*bind)(int group); 
    bool        (*compare)(struct net *net, struct sock *sk);
};

(4)單播netlink_unicast() 和 多播netlink_broadcast()

/* 發(fā)送單播消息 */
extern int netlink_unicast(struct sock *ssk, struct sk_buff *skb, __u32 portid, int nonblock);
/*
 ssk: netlink socket 
 skb: skb buff 指針
 portid:通信的端口號(hào)
 nonblock:表示該函數(shù)是否為非阻塞,如果為1,該函數(shù)將在沒(méi)有接收緩存可利用時(shí)立即返回,而如果為0,該函數(shù)在沒(méi)有接收緩存可利用定時(shí)睡眠
*/

/* 發(fā)送多播消息 */
extern int netlink_broadcast(struct sock *ssk, struct sk_buff *skb, __u32 portid,
                 __u32 group, gfp_t allocation);
/* 
   ssk: 同上(對(duì)應(yīng)netlink_kernel_create 返回值)、
   skb: 內(nèi)核skb buff
   portid:端口id
   group: 是所有目標(biāo)多播組對(duì)應(yīng)掩碼的"OR"操作的合值。
   allocation: 指定內(nèi)核內(nèi)存分配方式,通常GFP_ATOMIC用于中斷上下文,而GFP_KERNEL用于其他場(chǎng)合。這個(gè)參數(shù)的存在是因?yàn)樵揂PI可能需要分配一個(gè)或多個(gè)緩沖區(qū)來(lái)對(duì)多播消息進(jìn)行clone
*/

四、netlink內(nèi)核API

netlink的內(nèi)核實(shí)現(xiàn)在.c文件net/core/af_netlink.c中,內(nèi)核模塊要想使用netlink,也必須包含頭文件linux /netlink.h。內(nèi)核使用netlink需要專門的API,這完全不同于用戶態(tài)應(yīng)用對(duì)netlink的使用。如果用戶需要增加新的netlink協(xié) 議類型,必須通過(guò)修改linux/netlink.h來(lái)實(shí)現(xiàn),當(dāng)然,目前的netlink實(shí)現(xiàn)已經(jīng)包含了一個(gè)通用的協(xié)議類型 NETLINK_GENERIC以方便用戶使用,用戶可以直接使用它而不必增加新的協(xié)議類型。前面講到,為了增加新的netlink協(xié)議類型,用戶僅需增 加如下定義到linux/netlink.h就可以:

#define NETLINK_MYTEST  17

只要增加這個(gè)定義之后,用戶就可以在內(nèi)核的任何地方引用該協(xié)議,在內(nèi)核中,為了創(chuàng)建一個(gè)netlink socket用戶需要調(diào)用如下函數(shù):

struct sock *netlink_kernel_create(
int unit, void (*input)(struct sock *sk, int len));

參數(shù)unit表示netlink協(xié)議類型,如NETLINK_MYTEST,參數(shù)input則為內(nèi)核模塊定義的netlink消息處理函數(shù),當(dāng)有消 息到達(dá)這個(gè)netlink socket時(shí),該input函數(shù)指針就會(huì)被引用。函數(shù)指針input的參數(shù)sk實(shí)際上就是函數(shù)netlink_kernel_create返回的 struct sock指針,sock實(shí)際是socket的一個(gè)內(nèi)核表示數(shù)據(jù)結(jié)構(gòu),用戶態(tài)應(yīng)用創(chuàng)建的socket在內(nèi)核中也會(huì)有一個(gè)struct sock結(jié)構(gòu)來(lái)表示。下面是一個(gè)input函數(shù)的示例:

void input (struct sock *sk, int len)
{
 struct sk_buff *skb;
 struct nlmsghdr *nlh = NULL;
 u8 *data = NULL;
 while ((skb = skb_dequeue(&sk->receive_queue)) 
       != NULL) {
 /* process netlink message pointed by skb->data */
 nlh = (struct nlmsghdr *)skb->data;
 data = NLMSG_DATA(nlh);
 /* process netlink message with header pointed by 
  * nlh and data pointed by data
  */
 }   
}

函數(shù)input()會(huì)在發(fā)送進(jìn)程執(zhí)行sendmsg()時(shí)被調(diào)用,這樣處理消息比較及時(shí),但是,如果消息特別長(zhǎng)時(shí),這樣處理將增加系統(tǒng)調(diào)用 sendmsg()的執(zhí)行時(shí)間,對(duì)于這種情況,可以定義一個(gè)內(nèi)核線程專門負(fù)責(zé)消息接收,而函數(shù)input的工作只是喚醒該內(nèi)核線程,這樣sendmsg將 很快返回。

函數(shù)skb = skb_dequeue(&sk->receive_queue)用于取得socket sk的接收隊(duì)列上的消息,返回為一個(gè)struct sk_buff的結(jié)構(gòu),skb->data指向?qū)嶋H的netlink消息。

函數(shù)skb_recv_datagram(nl_sk)也用于在netlink socket nl_sk上接收消息,與skb_dequeue的不同指出是,如果socket的接收隊(duì)列上沒(méi)有消息,它將導(dǎo)致調(diào)用進(jìn)程睡眠在等待隊(duì)列 nl_sk->sk_sleep,因此它必須在進(jìn)程上下文使用,剛才講的內(nèi)核線程就可以采用這種方式來(lái)接收消息。

下面的函數(shù)input就是這種使用的示例:

void input (struct sock *sk, int len)
{
  wake_up_interruptible(sk->sk_sleep);
}

當(dāng)內(nèi)核中發(fā)送netlink消息時(shí),也需要設(shè)置目標(biāo)地址與源地址,而且內(nèi)核中消息是通過(guò)struct sk_buff來(lái)管理的, linux/netlink.h中定義了一個(gè)宏:

#define NETLINK_CB(skb)         (*(struc
t netlink_skb_parms*)&((skb)->cb))

來(lái)方便消息的地址設(shè)置。下面是一個(gè)消息地址設(shè)置的例子:

NETLINK_CB(skb).pid = 0;
NETLINK_CB(skb).dst_pid = 0;
NETLINK_CB(skb).dst_group = 1;

字段pid表示消息發(fā)送者進(jìn)程ID,也即源地址,對(duì)于內(nèi)核,它為 0, dst_pid 表示消息接收者進(jìn)程 ID,也即目標(biāo)地址,如果目標(biāo)為組或內(nèi)核,它設(shè)置為 0,否則 dst_group 表示目標(biāo)組地址,如果它目標(biāo)為某一進(jìn)程或內(nèi)核,dst_group 應(yīng)當(dāng)設(shè)置為 0。

在內(nèi)核中,模塊調(diào)用函數(shù) netlink_unicast 來(lái)發(fā)送單播消息:

int netlink_unicast(
struct sock *sk, struct sk_buff *skb, u32 pid, int nonblock);

參數(shù)sk為函數(shù)netlink_kernel_create()返回的socket,參數(shù)skb存放消息,它的data字段指向要發(fā)送的 netlink消息結(jié)構(gòu),而skb的控制塊保存了消息的地址信息,前面的宏NETLINK_CB(skb)就用于方便設(shè)置該控制塊, 參數(shù)pid為接收消息進(jìn)程的pid,參數(shù)nonblock表示該函數(shù)是否為非阻塞,如果為1,該函數(shù)將在沒(méi)有接收緩存可利用時(shí)立即返回,而如果為0,該函 數(shù)在沒(méi)有接收緩存可利用時(shí)睡眠。

內(nèi)核模塊或子系統(tǒng)也可以使用函數(shù)netlink_broadcast來(lái)發(fā)送廣播消息:

void netlink_broadcast(
struct sock *sk, struct sk_bu
ff *skb, u32 pid, u32 group, int allocation
);

前面的三個(gè)參數(shù)與netlink_unicast相同,參數(shù)group為接收消息的多播組,該參數(shù)的每一個(gè)代表一個(gè)多播組,因此如果發(fā)送給多個(gè)多播 組,就把該參數(shù)設(shè)置為多個(gè)多播組組ID的位或。參數(shù)allocation為內(nèi)核內(nèi)存分配類型,一般地為GFP_ATOMIC或 GFP_KERNEL,GFP_ATOMIC用于原子的上下文(即不可以睡眠),而GFP_KERNEL用于非原子上下文。

在內(nèi)核中使用函數(shù)sock_release來(lái)釋放函數(shù)netlink_kernel_create()創(chuàng)建的netlink socket:

void sock_release(struct socket * sock);

注意函數(shù)netlink_kernel_create()返回的類型為struct sock,因此函數(shù)sock_release應(yīng)該這種調(diào)用:

sock_release(sk->sk_socket);

sk為函數(shù)netlink_kernel_create()的返回值。在源代碼包中 給出了一個(gè)使用 netlink 的示例,它包括一個(gè)內(nèi)核模塊 netlink-exam-kern.c 和兩個(gè)應(yīng)用程序 netlink-exam-user-recv.c, netlink-exam-user-send.c。內(nèi)核模塊必須先插入到內(nèi)核,然后在一個(gè)終端上運(yùn)行用戶態(tài)接收程序,在另一個(gè)終端上運(yùn)行用戶態(tài)發(fā)送程 序,發(fā)送程序讀取參數(shù)指定的文本文件并把它作為 netlink 消息的內(nèi)容發(fā)送給內(nèi)核模塊,內(nèi)核模塊接受該消息保存到內(nèi)核緩存中,它也通過(guò)proc接口出口到 procfs,因此用戶也能夠通過(guò) /proc/netlink_exam_buffer 看到全部的內(nèi)容,同時(shí)內(nèi)核也把該消息發(fā)送給用戶態(tài)接收程序,用戶態(tài)接收程序?qū)呀邮盏降膬?nèi)容輸出到屏幕上。

五、Netlink性能優(yōu)勢(shì)

(1)異步通信,效率飛升

Netlink 是一種異步通信機(jī)制 ,這是它的一大亮點(diǎn)。在異步通信中,當(dāng)發(fā)送方發(fā)送消息時(shí),消息會(huì)被暫存在 socket 接收緩存中,發(fā)送方無(wú)需等待接收者立即處理消息,就可以繼續(xù)執(zhí)行其他任務(wù)。就好比我們寄快遞,把包裹交給快遞員后,我們不用一直等著包裹被收件人簽收,就可以去做別的事情了。

與之相對(duì)的同步通信,就像打電話,在對(duì)方接聽(tīng)并回應(yīng)之前,我們只能干等著,啥也做不了。在系統(tǒng)調(diào)用和 ioctl 這類同步通信機(jī)制中,如果傳遞的數(shù)據(jù)量較大或者接收方處理速度較慢,發(fā)送方就會(huì)被阻塞,這無(wú)疑會(huì)影響整個(gè)系統(tǒng)的調(diào)度粒度和效率。而 Netlink 的異步通信機(jī)制避免了這種等待,大大提升了系統(tǒng)的效率,使得系統(tǒng)能夠更高效地處理多個(gè)任務(wù)。

(2)全雙工通信,雙向奔赴

全雙工通信是指在通信的任意時(shí)刻,線路上可以同時(shí)存在 A 到 B 和 B 到 A 的雙向信號(hào)傳輸。Netlink 就支持全雙工通信,這意味著在內(nèi)核與用戶空間之間,數(shù)據(jù)能夠同時(shí)在兩個(gè)方向上傳輸 。例如,當(dāng)用戶空間的網(wǎng)絡(luò)配置工具向內(nèi)核發(fā)送配置請(qǐng)求時(shí),內(nèi)核可以同時(shí)將網(wǎng)絡(luò)狀態(tài)信息反饋給用戶空間,雙方的交流就像兩個(gè)人面對(duì)面交談一樣順暢,無(wú)需等待一方說(shuō)完另一方才能開(kāi)口。

這種雙向數(shù)據(jù)傳輸?shù)哪芰?,使得?nèi)核和用戶空間能夠及時(shí)地交換信息,極大地增強(qiáng)了系統(tǒng)的交互性和響應(yīng)速度。在網(wǎng)絡(luò)監(jiān)控場(chǎng)景中,用戶空間的監(jiān)控程序可以實(shí)時(shí)向內(nèi)核詢問(wèn)網(wǎng)絡(luò)流量等信息,內(nèi)核也能隨時(shí)將新的網(wǎng)絡(luò)事件通知給監(jiān)控程序,確保監(jiān)控的實(shí)時(shí)性和準(zhǔn)確性。

(3)多播功能,一對(duì)多的狂歡

Netlink 支持多播功能,這為它在一些特定場(chǎng)景下的應(yīng)用提供了強(qiáng)大的支持。通過(guò)多播,內(nèi)核模塊或應(yīng)用可以把消息發(fā)送給一個(gè) Netlink 組,屬于該組的任何內(nèi)核模塊或應(yīng)用都能接收到該消息 。每個(gè) Netlink 協(xié)議類型最多可以有 32 個(gè)多播組,每個(gè)多播組用一個(gè)位表示,發(fā)送消息給同一個(gè)組僅需要一次系統(tǒng)調(diào)用,大大降低了系統(tǒng)調(diào)用的次數(shù)。

在網(wǎng)絡(luò)管理中,當(dāng)內(nèi)核檢測(cè)到網(wǎng)絡(luò)拓?fù)浒l(fā)生變化時(shí),它可以通過(guò)多播將這一消息同時(shí)發(fā)送給多個(gè)關(guān)注網(wǎng)絡(luò)狀態(tài)的用戶空間進(jìn)程,如網(wǎng)絡(luò)監(jiān)控程序、路由守護(hù)進(jìn)程等,這些進(jìn)程就能及時(shí)做出相應(yīng)的調(diào)整,而不需要內(nèi)核分別向每個(gè)進(jìn)程單獨(dú)發(fā)送消息,大大提高了信息傳遞的效率。

(4)簡(jiǎn)單易用,上手輕松

對(duì)于開(kāi)發(fā)者來(lái)說(shuō),Netlink 的易用性也是它的一大優(yōu)勢(shì)。用戶態(tài)應(yīng)用可以使用標(biāo)準(zhǔn)的 socket API,如 socket ()、bind ()、sendmsg ()、recvmsg () 和 close () 等函數(shù)來(lái)使用 Netlink socket 。這對(duì)于熟悉 socket 編程的開(kāi)發(fā)者來(lái)說(shuō),幾乎沒(méi)有學(xué)習(xí)成本,能夠快速上手進(jìn)行 Netlink 相關(guān)的開(kāi)發(fā)。不需要像使用系統(tǒng)調(diào)用和 ioctl 那樣,需要專門的培訓(xùn)才能使用,降低了開(kāi)發(fā)的門檻,使得開(kāi)發(fā)者能夠更專注于業(yè)務(wù)邏輯的實(shí)現(xiàn)。

(5)無(wú)編譯依賴,靈活部署

使用 Netlink 的內(nèi)核部分可以采用模塊的方式實(shí)現(xiàn),并且使用 Netlink 的應(yīng)用部分和內(nèi)核部分沒(méi)有編譯時(shí)依賴 。這意味著在開(kāi)發(fā)過(guò)程中,我們可以獨(dú)立地對(duì)內(nèi)核模塊和用戶空間應(yīng)用進(jìn)行開(kāi)發(fā)、調(diào)試和更新,而不需要因?yàn)橐环降母膭?dòng)而重新編譯另一方。當(dāng)我們需要更新內(nèi)核模塊的功能時(shí),只需要重新編譯內(nèi)核模塊并加載,而用戶空間應(yīng)用無(wú)需重新編譯就可以繼續(xù)使用;反之,當(dāng)用戶空間應(yīng)用進(jìn)行功能升級(jí)時(shí),也不會(huì)影響到內(nèi)核模塊。這種靈活性大大提高了開(kāi)發(fā)和部署的效率,使得系統(tǒng)的維護(hù)和升級(jí)更加便捷。

六、Netlink常用場(chǎng)景

6.1網(wǎng)絡(luò)配置與管理

在網(wǎng)絡(luò)配置與管理領(lǐng)域,Netlink 堪稱一把 “瑞士軍刀”,發(fā)揮著舉足輕重的作用。

在網(wǎng)絡(luò)接口管理方面,Netlink 為用戶空間程序與內(nèi)核之間搭建了一座溝通的橋梁,使得對(duì)網(wǎng)絡(luò)接口的各種操作變得輕而易舉。通過(guò) Netlink,我們可以創(chuàng)建、刪除虛擬接口,就像在搭建一個(gè)虛擬網(wǎng)絡(luò)世界時(shí),能夠自由地添加或移除各種 “虛擬橋梁” 。比如,在容器網(wǎng)絡(luò)中,經(jīng)常會(huì)用到 veth 對(duì)(虛擬以太網(wǎng)設(shè)備對(duì))來(lái)實(shí)現(xiàn)容器與外部網(wǎng)絡(luò)的通信,而創(chuàng)建 veth 對(duì)就是通過(guò) Netlink 與內(nèi)核交互完成的。我們還能靈活地配置接口屬性,像設(shè)置 MTU(最大傳輸單元),就如同為網(wǎng)絡(luò)傳輸?shù)?“通道” 設(shè)定合適的寬度,確保數(shù)據(jù)能夠高效傳輸;修改 MAC 地址,就像是給網(wǎng)絡(luò)設(shè)備換了一個(gè)獨(dú)特的 “身份標(biāo)識(shí)”;以及啟用或禁用接口,控制網(wǎng)絡(luò)設(shè)備的 “開(kāi)關(guān)” 。在企業(yè)網(wǎng)絡(luò)中,當(dāng)需要調(diào)整網(wǎng)絡(luò)架構(gòu)時(shí),管理員可以利用 Netlink 相關(guān)工具,快速地對(duì)網(wǎng)絡(luò)接口進(jìn)行配置,保障網(wǎng)絡(luò)的穩(wěn)定運(yùn)行。

路由表更新也是 Netlink 的重要應(yīng)用場(chǎng)景之一。它能夠幫助我們添加、刪除路由條目,如同在網(wǎng)絡(luò)的 “地圖” 上標(biāo)記或抹去特定的路徑 。當(dāng)我們要設(shè)置靜態(tài)路由,讓數(shù)據(jù)包按照指定的路線傳輸時(shí),就可以借助 Netlink 向內(nèi)核發(fā)送相應(yīng)的消息。例如,在一個(gè)擁有多個(gè)子網(wǎng)的企業(yè)網(wǎng)絡(luò)中,為了實(shí)現(xiàn)不同子網(wǎng)之間的通信,管理員可以通過(guò) Netlink 添加靜態(tài)路由條目。查詢當(dāng)前路由表狀態(tài)也不在話下,通過(guò) Netlink,我們能隨時(shí)獲取路由表的信息,了解網(wǎng)絡(luò)的 “交通路線” 狀況 。這對(duì)于網(wǎng)絡(luò)故障排查非常重要,當(dāng)網(wǎng)絡(luò)出現(xiàn)連接問(wèn)題時(shí),管理員可以通過(guò)查詢路由表,判斷數(shù)據(jù)包的傳輸路徑是否正確,從而快速定位問(wèn)題所在。

在實(shí)際應(yīng)用中,有許多強(qiáng)大的工具都依賴 Netlink 來(lái)實(shí)現(xiàn)網(wǎng)絡(luò)配置與管理功能。其中,iproute2 工具集就是一個(gè)典型代表 。它包含了眾多實(shí)用的命令,如 ip addr 用于管理網(wǎng)絡(luò)接口地址,ip route 用于操作路由表等。這些命令底層都是通過(guò) Netlink 與內(nèi)核進(jìn)行交互,替代了傳統(tǒng)的 ifconfig 和 route 命令。當(dāng)我們執(zhí)行 “ip route add 192.168.2.0/24 via 10.0.0.1” 這條命令時(shí),實(shí)際上是通過(guò) NETLINK_ROUTE 協(xié)議向內(nèi)核發(fā)送 RTM_NEWROUTE 消息,告知內(nèi)核添加一條到 192.168.2.0/24 網(wǎng)絡(luò)的路由,下一跳為 10.0.0.1 。還有一些網(wǎng)絡(luò)自動(dòng)化配置工具,在大規(guī)模網(wǎng)絡(luò)部署中,通過(guò)調(diào)用 Netlink 接口,能夠快速、批量地對(duì)網(wǎng)絡(luò)設(shè)備進(jìn)行配置,大大提高了網(wǎng)絡(luò)部署的效率。

6.2系統(tǒng)監(jiān)控與安全

Netlink 在系統(tǒng)監(jiān)控與安全領(lǐng)域同樣有著不可忽視的作用。

在系統(tǒng)資源監(jiān)控方面,Netlink 為用戶空間的監(jiān)控工具打開(kāi)了一扇通往內(nèi)核信息寶庫(kù)的大門。通過(guò) Netlink,監(jiān)控工具可以實(shí)時(shí)獲取系統(tǒng)資源的使用情況,如 CPU 使用率、內(nèi)存占用、磁盤 I/O 等 。這就好比我們?cè)隈{駛汽車時(shí),儀表盤上的各種指針和數(shù)據(jù)能夠?qū)崟r(shí)反饋汽車的運(yùn)行狀態(tài),讓我們隨時(shí)了解車輛的情況。以 top 命令為例,它能夠動(dòng)態(tài)顯示系統(tǒng)中各個(gè)進(jìn)程的資源占用情況,而這背后就離不開(kāi) Netlink 的支持。top 命令通過(guò) Netlink 與內(nèi)核通信,獲取進(jìn)程的相關(guān)信息,然后進(jìn)行整理和展示,讓用戶對(duì)系統(tǒng)的運(yùn)行狀態(tài)一目了然。在服務(wù)器運(yùn)維中,管理員可以利用基于 Netlink 的監(jiān)控工具,實(shí)時(shí)監(jiān)控服務(wù)器的資源使用情況,當(dāng)發(fā)現(xiàn)資源使用率過(guò)高時(shí),及時(shí)采取措施進(jìn)行優(yōu)化,保障服務(wù)器的穩(wěn)定運(yùn)行。

在安全策略管理方面,Netlink 扮演著重要的角色。它為用戶空間的安全工具與內(nèi)核之間的通信提供了通道,使得安全策略的管理和配置變得更加高效 。防火墻是保障網(wǎng)絡(luò)安全的重要防線,用戶空間的防火墻配置工具(如 iptables、nftables)可以通過(guò) Netlink 與內(nèi)核中的 netfilter 模塊進(jìn)行通信,實(shí)現(xiàn)對(duì)網(wǎng)絡(luò)數(shù)據(jù)包的過(guò)濾和安全策略的設(shè)置 。我們可以通過(guò)這些工具,根據(jù)實(shí)際需求制定規(guī)則,允許或禁止特定的網(wǎng)絡(luò)連接,就像在城堡的大門設(shè)置守衛(wèi),對(duì)進(jìn)出的人員進(jìn)行嚴(yán)格的檢查和篩選。入侵檢測(cè)系統(tǒng)(IDS)和入侵防御系統(tǒng)(IPS)也可以利用 Netlink 獲取網(wǎng)絡(luò)數(shù)據(jù)包和系統(tǒng)狀態(tài)信息,及時(shí)發(fā)現(xiàn)并阻止?jié)撛诘陌踩{ 。當(dāng) IDS 檢測(cè)到異常的網(wǎng)絡(luò)流量時(shí),它可以通過(guò) Netlink 向內(nèi)核發(fā)送相關(guān)信息,觸發(fā)相應(yīng)的防御機(jī)制,保障系統(tǒng)的安全。

6.3內(nèi)核與用戶空間交互

Netlink 作為內(nèi)核與用戶空間通信的橋梁,其重要性不言而喻,在眾多場(chǎng)景中都發(fā)揮著關(guān)鍵作用。

設(shè)備驅(qū)動(dòng)與用戶空間程序的交互是一個(gè)常見(jiàn)的場(chǎng)景。設(shè)備驅(qū)動(dòng)在內(nèi)核空間負(fù)責(zé)與硬件設(shè)備進(jìn)行通信,而用戶空間程序則需要與設(shè)備驅(qū)動(dòng)交互,以實(shí)現(xiàn)對(duì)硬件設(shè)備的控制和數(shù)據(jù)傳輸 。以網(wǎng)絡(luò)設(shè)備驅(qū)動(dòng)為例,當(dāng)用戶空間的網(wǎng)絡(luò)應(yīng)用程序需要發(fā)送或接收網(wǎng)絡(luò)數(shù)據(jù)包時(shí),它會(huì)通過(guò) Netlink 與網(wǎng)絡(luò)設(shè)備驅(qū)動(dòng)進(jìn)行通信。用戶空間程序?qū)?shù)據(jù)包發(fā)送給設(shè)備驅(qū)動(dòng),設(shè)備驅(qū)動(dòng)再將數(shù)據(jù)包發(fā)送到物理網(wǎng)絡(luò)上;反之,當(dāng)設(shè)備驅(qū)動(dòng)接收到來(lái)自網(wǎng)絡(luò)的數(shù)據(jù)包時(shí),也會(huì)通過(guò) Netlink 將數(shù)據(jù)包傳遞給用戶空間程序 。這就像在一個(gè)工廠中,生產(chǎn)線上的工人(設(shè)備驅(qū)動(dòng))與管理人員(用戶空間程序)需要密切配合,通過(guò)特定的溝通渠道(Netlink)來(lái)協(xié)調(diào)工作,確保生產(chǎn)的順利進(jìn)行。

系統(tǒng)服務(wù)與內(nèi)核之間的通信也經(jīng)常依賴 Netlink。許多系統(tǒng)服務(wù),如網(wǎng)絡(luò)服務(wù)、存儲(chǔ)服務(wù)等,需要與內(nèi)核進(jìn)行交互,獲取系統(tǒng)資源或執(zhí)行特定的操作 。在網(wǎng)絡(luò)服務(wù)中,DHCP(動(dòng)態(tài)主機(jī)配置協(xié)議)服務(wù)器需要與內(nèi)核通信,獲取網(wǎng)絡(luò)接口信息,為客戶端分配 IP 地址 。這一過(guò)程中,DHCP 服務(wù)器就會(huì)通過(guò) Netlink 向內(nèi)核發(fā)送請(qǐng)求,內(nèi)核響應(yīng)請(qǐng)求并返回相關(guān)信息,實(shí)現(xiàn)了系統(tǒng)服務(wù)與內(nèi)核之間的信息交互,保障了網(wǎng)絡(luò)服務(wù)的正常運(yùn)行。

七、Netlink 工作原理

Netlink 的架構(gòu)就像是一個(gè)精心構(gòu)建的通信網(wǎng)絡(luò),各個(gè)部分協(xié)同工作,實(shí)現(xiàn)了內(nèi)核與用戶空間之間高效的通信。我們來(lái)看下面這張:

圖片圖片

從圖中可以看出,Netlink 主要由以下幾個(gè)部分組成:

  • 用戶空間應(yīng)用:這是我們?nèi)粘J褂玫母鞣N應(yīng)用程序,它們通過(guò)標(biāo)準(zhǔn)的 socket API 與 Netlink 套接字進(jìn)行交互。比如我們前面提到的網(wǎng)絡(luò)配置工具、系統(tǒng)監(jiān)控程序等,它們通過(guò) Netlink 向內(nèi)核發(fā)送請(qǐng)求,獲取系統(tǒng)信息或者執(zhí)行特定的操作。
  • Netlink 套接字:作為用戶空間與內(nèi)核空間通信的橋梁,Netlink 套接字負(fù)責(zé)在兩者之間傳遞數(shù)據(jù)。它基于 BSD socket 和 AF_NETLINK 地址簇,采用 32 位的端口號(hào)尋址 。每個(gè) Netlink 套接字都有一個(gè)對(duì)應(yīng)的協(xié)議類型,用于標(biāo)識(shí)通信的內(nèi)容和目的。
  • 內(nèi)核空間:內(nèi)核是 Linux 系統(tǒng)的核心,它包含了各種設(shè)備驅(qū)動(dòng)、網(wǎng)絡(luò)協(xié)議棧等重要組件。內(nèi)核通過(guò) Netlink 與用戶空間進(jìn)行通信,接收用戶空間的請(qǐng)求并返回相應(yīng)的結(jié)果,同時(shí)也可以主動(dòng)向用戶空間發(fā)送通知和事件信息。
  • Netlink 協(xié)議族:Netlink 支持多種協(xié)議類型,每種協(xié)議類型都與特定的內(nèi)核服務(wù)或組件相關(guān)聯(lián)。例如,NETLINK_ROUTE 用于網(wǎng)絡(luò)路由相關(guān)的操作,NETLINK_KOBJECT_UEVENT 用于內(nèi)核向用戶空間發(fā)送設(shè)備事件通知等 。不同的協(xié)議類型使得 Netlink 能夠滿足各種不同的通信需求。

7.1Netlink 協(xié)議類型全解析

Netlink 協(xié)議族豐富多樣,目前支持 32 種協(xié)議類型 ,它們?cè)诓煌膱?chǎng)景中發(fā)揮著關(guān)鍵作用。以下是一些常見(jiàn)的 Netlink 協(xié)議類型及其用途:

NETLINK_ROUTE:這是最為常用的協(xié)議類型之一,主要用于網(wǎng)絡(luò)路由和設(shè)備相關(guān)的操作 。通過(guò)它,我們可以獲取和設(shè)置路由信息,就像為網(wǎng)絡(luò)數(shù)據(jù)包規(guī)劃行進(jìn)的路線;管理網(wǎng)絡(luò)接口,如創(chuàng)建、刪除接口,配置接口屬性等,相當(dāng)于對(duì)網(wǎng)絡(luò)的 “出入口” 進(jìn)行管控;還能監(jiān)控網(wǎng)絡(luò)狀態(tài),實(shí)時(shí)掌握網(wǎng)絡(luò)的運(yùn)行情況 。在企業(yè)網(wǎng)絡(luò)中,網(wǎng)絡(luò)管理員經(jīng)常會(huì)使用基于 NETLINK_ROUTE 的工具來(lái)配置和管理網(wǎng)絡(luò)路由,確保網(wǎng)絡(luò)的高效運(yùn)行。

NETLINK_SOCK_DIAG:主要用于監(jiān)控系統(tǒng)中的套接字信息 。它可以獲取套接字的狀態(tài),比如是處于監(jiān)聽(tīng)狀態(tài)、連接狀態(tài)還是關(guān)閉狀態(tài);查看套接字選項(xiàng),了解套接字的各種配置參數(shù) 。在網(wǎng)絡(luò)故障排查中,NETLINK_SOCK_DIAG 非常有用,管理員可以通過(guò)它來(lái)檢查網(wǎng)絡(luò)連接是否正常,是否存在套接字資源泄漏等問(wèn)題。例如,當(dāng)我們發(fā)現(xiàn)某個(gè)網(wǎng)絡(luò)應(yīng)用無(wú)法正常連接時(shí),可以使用基于 NETLINK_SOCK_DIAG 的工具來(lái)查看相關(guān)套接字的狀態(tài),找出問(wèn)題所在。

NETLINK_NFLOG:是 netfilter/iptables ULOG 的通信接口,在防火墻和網(wǎng)絡(luò)安全領(lǐng)域有著重要應(yīng)用 。它允許用戶空間的防火墻工具(如 iptables)與內(nèi)核中的 netfilter 模塊進(jìn)行通信,實(shí)現(xiàn)對(duì)網(wǎng)絡(luò)數(shù)據(jù)包的過(guò)濾和日志記錄 。當(dāng)有網(wǎng)絡(luò)數(shù)據(jù)包進(jìn)入系統(tǒng)時(shí),netfilter 模塊會(huì)根據(jù)預(yù)設(shè)的規(guī)則對(duì)數(shù)據(jù)包進(jìn)行檢查,然后通過(guò) NETLINK_NFLOG 將相關(guān)信息(如數(shù)據(jù)包是否被允許通過(guò)、被丟棄的原因等)傳遞給用戶空間的防火墻工具,以便進(jìn)行進(jìn)一步的處理和分析。在企業(yè)網(wǎng)絡(luò)安全防護(hù)中,管理員可以利用 NETLINK_NFLOG 來(lái)監(jiān)控網(wǎng)絡(luò)流量,及時(shí)發(fā)現(xiàn)和阻止?jié)撛诘陌踩{。

NETLINK_KOBJECT_UEVENT:用于內(nèi)核向用戶空間發(fā)送設(shè)備事件通知,是內(nèi)核熱插拔機(jī)制的基礎(chǔ) 。當(dāng)有設(shè)備插入或拔出系統(tǒng)時(shí),內(nèi)核會(huì)通過(guò) NETLINK_KOBJECT_UEVENT 協(xié)議向用戶空間發(fā)送相應(yīng)的事件消息 。用戶空間的 udev 進(jìn)程接收到這些消息后,會(huì)根據(jù)設(shè)備的屬性和規(guī)則,自動(dòng)完成設(shè)備的識(shí)別、驅(qū)動(dòng)加載等操作。在我們?nèi)粘J褂玫碾娔X中,當(dāng)插入 U 盤時(shí),系統(tǒng)能夠自動(dòng)識(shí)別并掛載 U 盤,這背后就離不開(kāi) NETLINK_KOBJECT_UEVENT 的支持。

NETLINK_GENERIC:作為一種通用的 Netlink 協(xié)議類型,它就像是一個(gè)靈活的 “瑞士軍刀”,為用戶提供了自定義協(xié)議的能力 。當(dāng)現(xiàn)有的協(xié)議類型無(wú)法滿足特定需求時(shí),用戶可以基于 NETLINK_GENERIC 定義自己的子協(xié)議類型 。在一些特定的行業(yè)應(yīng)用中,可能需要與內(nèi)核進(jìn)行特定的數(shù)據(jù)交互,此時(shí)就可以利用 NETLINK_GENERIC 來(lái)實(shí)現(xiàn)自定義的通信協(xié)議,滿足業(yè)務(wù)的特殊需求。

7.2數(shù)據(jù)結(jié)構(gòu)與函數(shù)

在 Netlink 的世界里,有一些關(guān)鍵的數(shù)據(jù)結(jié)構(gòu)和函數(shù),它們是實(shí)現(xiàn)高效通信的基礎(chǔ)。

⑴數(shù)據(jù)結(jié)構(gòu)

struct sockaddr_nl:這是 Netlink 通信地址的數(shù)據(jù)結(jié)構(gòu),類似于普通 socket 編程中的 struct sockaddr_in 。它包含以下幾個(gè)重要成員:

  • nl_family:固定為 AF_NETLINK,用于標(biāo)識(shí)地址族,就像給通信地址貼上一個(gè) “Netlink 專屬” 的標(biāo)簽 。
  • nl_pad:目前未使用,通常填充為 0,就像一個(gè)暫時(shí)閑置的小隔間。
  • nl_pid:表示端口 ID,通常設(shè)置為當(dāng)前進(jìn)程的進(jìn)程號(hào),用于唯一標(biāo)識(shí)一個(gè)基于 netlink 的 socket 通道 。當(dāng)用戶空間的進(jìn)程與內(nèi)核進(jìn)行通信時(shí),內(nèi)核可以通過(guò)這個(gè) nl_pid 來(lái)識(shí)別是哪個(gè)進(jìn)程在發(fā)送請(qǐng)求。
  • nl_groups:用于指定多播組,是多播組掩碼 。每個(gè) Netlink 協(xié)議最多支持 32 個(gè)多播組,每個(gè)多播組用一個(gè) bit 表示。如果進(jìn)程希望加入某個(gè)多播組,就需要設(shè)置相應(yīng)的 bit 位 。在網(wǎng)絡(luò)監(jiān)控場(chǎng)景中,多個(gè)監(jiān)控程序可以加入同一個(gè)多播組,接收內(nèi)核發(fā)送的網(wǎng)絡(luò)狀態(tài)信息。

struct nlmsghdr:這是 Netlink 消息頭的數(shù)據(jù)結(jié)構(gòu),每個(gè) Netlink 消息都包含這樣一個(gè)消息頭 。它的成員如下:

  • nlmsg_len:表示整個(gè)消息的長(zhǎng)度,包括消息頭和消息體就像一個(gè)包裹的總重量包含了包裝盒和里面的物品 。
  • nlmsg_type:用于標(biāo)識(shí)消息的類型,比如是數(shù)據(jù)消息還是控制消息 。內(nèi)核定義了一些標(biāo)準(zhǔn)的消息類型,如 NLMSG_NOOP(空消息,什么也不做)、NLMSG_ERROR(表示消息中包含錯(cuò)誤)、NLMSG_DONE(用于標(biāo)記消息隊(duì)列的結(jié)束)等 。
  • nlmsg_flags:是消息的附加標(biāo)志位,用于對(duì)消息進(jìn)行額外的控制 。例如,NLM_F_REQUEST 表示這是一個(gè)請(qǐng)求消息,NLM_F_MULTI 表示消息由多個(gè)部分組成,最后一個(gè)部分會(huì)標(biāo)注 NLMSG_DONE 。
  • nlmsg_seq:是序列號(hào),用于追蹤消息的順序,類似于快遞單號(hào),方便接收方對(duì)消息進(jìn)行排序和處理 。
  • nlmsg_pid:表示發(fā)送進(jìn)程的端口 ID,用于接收方識(shí)別消息的來(lái)源 。

⑵函數(shù)

  • socket():在用戶空間創(chuàng)建 Netlink 套接字時(shí)使用,它的參數(shù)包括地址族(AF_NETLINK)、套接字類型(SOCK_RAW 或 SOCK_DGRAM)和協(xié)議類型 。就像是在通信網(wǎng)絡(luò)中搭建一個(gè)新的 “通信站點(diǎn)”,指定它的類型和所屬的 “通信頻道”。例如,int sock_fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE); 這行代碼創(chuàng)建了一個(gè)基于 NETLINK_ROUTE 協(xié)議的 RAW 類型的 Netlink 套接字。
  • bind():用于將 Netlink 套接字與本地地址綁定,也就是給這個(gè) “通信站點(diǎn)” 確定一個(gè)具體的位置 。它的參數(shù)包括套接字描述符、本地地址和地址長(zhǎng)度 。通過(guò)綁定,內(nèi)核和其他進(jìn)程就知道該去哪里與這個(gè)套接字進(jìn)行通信。例如,bind(sock_fd, (struct sockaddr*)&nl_addr, sizeof(nl_addr)); 這行代碼將之前創(chuàng)建的套接字 sock_fd 與本地地址 nl_addr 進(jìn)行綁定。
  • sendmsg():用于發(fā)送 Netlink 消息,它需要一個(gè) struct msghdr 結(jié)構(gòu)體來(lái)指定消息的各種參數(shù),包括目標(biāo)地址、消息內(nèi)容等 。就像是把裝滿信息的 “包裹” 發(fā)送出去,告訴快遞員(內(nèi)核或其他進(jìn)程)要送到哪里。在發(fā)送消息前,需要填充好 struct msghdr 結(jié)構(gòu)體的各個(gè)成員,然后調(diào)用 sendmsg 函數(shù)將消息發(fā)送出去。
  • recvmsg():用于接收 Netlink 消息,接收方通過(guò)這個(gè)函數(shù)從套接字接收內(nèi)核或其他進(jìn)程發(fā)送過(guò)來(lái)的消息 。它就像是在 “通信站點(diǎn)” 等待接收 “包裹”,當(dāng)有消息到達(dá)時(shí),將其接收并進(jìn)行處理。例如,recvmsg(sock_fd, &msg, 0); 這行代碼從套接字 sock_fd 接收消息,并將消息存儲(chǔ)在 msg 結(jié)構(gòu)體中。

7.3通信流程大起底

Netlink 的通信流程就像是一場(chǎng)有序的接力賽,每個(gè)環(huán)節(jié)都緊密相連,確保數(shù)據(jù)能夠準(zhǔn)確、高效地傳輸。下面我們來(lái)詳細(xì)了解一下 Netlink 的通信流程:

①消息發(fā)送

  • 用戶空間應(yīng)用首先使用 socket () 函數(shù)創(chuàng)建一個(gè) Netlink 套接字,并指定協(xié)議類型 。比如要進(jìn)行網(wǎng)絡(luò)路由相關(guān)的操作,就創(chuàng)建一個(gè)基于 NETLINK_ROUTE 協(xié)議的套接字。
  • 然后,應(yīng)用使用 bind () 函數(shù)將套接字與本地地址綁定,確定通信的端點(diǎn) 。
  • 接下來(lái),應(yīng)用構(gòu)造一個(gè) Netlink 消息,填充 struct nlmsghdr 消息頭和消息體 。消息頭中設(shè)置消息類型、標(biāo)志位、序列號(hào)等信息,消息體中包含具體的數(shù)據(jù)內(nèi)容。
  • 最后,應(yīng)用使用 sendmsg () 函數(shù)將消息發(fā)送出去 。在發(fā)送時(shí),需要指定目標(biāo)地址(如果是發(fā)送給內(nèi)核,nl_pid 和 nl_groups 通常設(shè)置為 0;如果是發(fā)送給其他進(jìn)程,則設(shè)置為目標(biāo)進(jìn)程的 pid 和相應(yīng)的多播組掩碼)。

②消息接收

  • 內(nèi)核或其他接收方進(jìn)程在接收到 Netlink 消息后,首先會(huì)根據(jù)消息頭中的信息進(jìn)行初步處理 。
  • 內(nèi)核會(huì)檢查消息的合法性,包括消息類型是否正確、消息長(zhǎng)度是否符合要求等 。如果消息不合法,會(huì)返回錯(cuò)誤信息。
  • 對(duì)于合法的消息,內(nèi)核會(huì)根據(jù)消息類型和協(xié)議類型進(jìn)行進(jìn)一步的處理 。例如,如果是 NETLINK_ROUTE 協(xié)議的消息,內(nèi)核會(huì)將其轉(zhuǎn)發(fā)到網(wǎng)絡(luò)路由模塊進(jìn)行處理。

③消息處理

  • 內(nèi)核或接收方進(jìn)程根據(jù)消息的內(nèi)容執(zhí)行相應(yīng)的操作 。如果是網(wǎng)絡(luò)配置請(qǐng)求,內(nèi)核會(huì)更新網(wǎng)絡(luò)配置信息;如果是設(shè)備事件通知,內(nèi)核會(huì)通知相關(guān)的設(shè)備驅(qū)動(dòng)進(jìn)行處理。
  • 處理完成后,內(nèi)核或接收方進(jìn)程可能會(huì)返回一個(gè)響應(yīng)消息給發(fā)送方 。響應(yīng)消息同樣包含消息頭和消息體,消息頭中會(huì)設(shè)置相應(yīng)的標(biāo)志位和消息類型(如 NLMSG_ACK 表示確認(rèn)消息,NLMSG_ERROR 表示錯(cuò)誤消息)。
  • 發(fā)送方接收到響應(yīng)消息后,根據(jù)消息內(nèi)容進(jìn)行相應(yīng)的處理 。如果是確認(rèn)消息,發(fā)送方知道自己的請(qǐng)求已經(jīng)被成功處理;如果是錯(cuò)誤消息,發(fā)送方會(huì)根據(jù)錯(cuò)誤信息進(jìn)行調(diào)試和修正。

八、Netlink 應(yīng)用開(kāi)發(fā)實(shí)戰(zhàn)

8.1準(zhǔn)備工作

在開(kāi)始 Netlink 應(yīng)用開(kāi)發(fā)之前,我們需要搭建好開(kāi)發(fā)環(huán)境,準(zhǔn)備好必要的工具。

首先,確保你的開(kāi)發(fā)機(jī)器上安裝了 Linux 系統(tǒng),推薦使用較新的版本,如 Ubuntu 20.04 或 CentOS 8,因?yàn)樗鼈儗?duì) Netlink 的支持更加完善 。如果你還沒(méi)有安裝 Linux 系統(tǒng),可以通過(guò)虛擬機(jī)軟件(如 VirtualBox 或 VMware)來(lái)安裝,這就好比在你的電腦里搭建了一個(gè)虛擬的 Linux 世界,讓你可以在不影響原有系統(tǒng)的情況下進(jìn)行開(kāi)發(fā)。

接下來(lái),安裝開(kāi)發(fā)工具。GCC(GNU Compiler Collection)是 Linux 下常用的編譯器,我們需要它來(lái)編譯我們的代碼 。在 Ubuntu 系統(tǒng)中,可以通過(guò)以下命令安裝:

sudo apt-get update
sudo apt-get install build-essential

在 CentOS 系統(tǒng)中,安裝命令如下:

sudo yum groupinstall "Development Tools"

除了 GCC,還需要安裝一些開(kāi)發(fā)庫(kù),如 libnl。libnl 是一個(gè)用于簡(jiǎn)化 Netlink 編程的庫(kù),它提供了一些封裝好的函數(shù),讓我們可以更方便地使用 Netlink 。在 Ubuntu 系統(tǒng)中,可以通過(guò)以下命令安裝:

sudo apt-get install libnl-3-dev libnl-genl-3-dev

在 CentOS 系統(tǒng)中,安裝命令如下:

sudo yum install libnl3-devel libnl3-genl-devel

8.2用戶態(tài)編程示例

下面我們來(lái)看一個(gè)用戶態(tài)的 Netlink 編程示例,通過(guò)這個(gè)示例,你將學(xué)會(huì)如何創(chuàng)建 Netlink 套接字、綁定地址、發(fā)送和接收消息。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <linux/netlink.h>

#define NETLINK_USER 31 // 自定義Netlink協(xié)議號(hào)
#define MSG_LEN 1024

int main() {
    // 創(chuàng)建Netlink套接字
    int sock_fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_USER);
    if (sock_fd < 0) {
        perror("socket");
        return -1;
    }

    // 本地地址配置
    struct sockaddr_nl src_addr;
    memset(&src_addr, 0, sizeof(src_addr));
    src_addr.nl_family = AF_NETLINK;
    src_addr.nl_pid = getpid(); // 綁定到當(dāng)前進(jìn)程
    src_addr.nl_groups = 0; // 不訂閱多播

    // 綁定套接字
    if (bind(sock_fd, (struct sockaddr*)&src_addr, sizeof(src_addr)) < 0) {
        perror("bind");
        close(sock_fd);
        return -1;
    }

    // 目標(biāo)地址配置(內(nèi)核)
    struct sockaddr_nl dest_addr;
    memset(&dest_addr, 0, sizeof(dest_addr));
    dest_addr.nl_family = AF_NETLINK;
    dest_addr.nl_pid = 0; // 發(fā)送到內(nèi)核
    dest_addr.nl_groups = 0; // 不訂閱多播

    // 構(gòu)造發(fā)送消息
    struct nlmsghdr *nlh = (struct nlmsghdr*)malloc(NLMSG_SPACE(MSG_LEN));
    memset(nlh, 0, NLMSG_SPACE(MSG_LEN));
    nlh->nlmsg_len = NLMSG_SPACE(MSG_LEN); // 消息長(zhǎng)度
    nlh->nlmsg_pid = getpid(); // 發(fā)送者PID
    nlh->nlmsg_flags = 0; // 無(wú)特殊標(biāo)志位
    strcpy((char*)NLMSG_DATA(nlh), "Hello from user space!"); // 消息內(nèi)容

    // 發(fā)送消息到內(nèi)核
    if (sendto(sock_fd, nlh, nlh->nlmsg_len, 0, (struct sockaddr*)&dest_addr, sizeof(dest_addr)) < 0) {
        perror("sendto");
        free(nlh);
        close(sock_fd);
        return -1;
    }
    printf("Message sent to kernel: %s\n", (char*)NLMSG_DATA(nlh));

    // 接收內(nèi)核響應(yīng)
    memset(nlh, 0, NLMSG_SPACE(MSG_LEN));
    if (recv(sock_fd, nlh, NLMSG_SPACE(MSG_LEN), 0) < 0) {
        perror("recv");
        free(nlh);
        close(sock_fd);
        return -1;
    }
    printf("Message received from kernel: %s\n", (char*)NLMSG_DATA(nlh));

    // 清理資源
    free(nlh);
    close(sock_fd);

    return 0;
}
  • 創(chuàng)建 Netlink 套接字:socket(AF_NETLINK, SOCK_RAW, NETLINK_USER) 函數(shù)用于創(chuàng)建一個(gè) Netlink 套接字,AF_NETLINK 表示地址族,SOCK_RAW 表示套接字類型為原始套接字,NETLINK_USER 是我們自定義的 Netlink 協(xié)議號(hào) 。這一步就像是在網(wǎng)絡(luò)世界中搭建了一個(gè)專門用于 Netlink 通信的 “站點(diǎn)”。
  • 綁定地址:通過(guò) bind 函數(shù)將套接字與本地地址綁定,src_addr.nl_pid = getpid() 將端口 ID 設(shè)置為當(dāng)前進(jìn)程的進(jìn)程號(hào),這樣內(nèi)核就知道該套接字屬于哪個(gè)進(jìn)程 。就好比給這個(gè) “站點(diǎn)” 貼上了一個(gè)獨(dú)一無(wú)二的 “標(biāo)簽”,方便識(shí)別。
  • 構(gòu)造發(fā)送消息:創(chuàng)建一個(gè) nlmsghdr 結(jié)構(gòu)體來(lái)表示消息頭,設(shè)置消息長(zhǎng)度、發(fā)送者 PID、標(biāo)志位等信息,并將消息內(nèi)容復(fù)制到消息體中 。這就像是把要發(fā)送的信息裝進(jìn)一個(gè) “包裹”,并填寫好收件人和寄件人的信息。
  • 發(fā)送消息:使用 sendto 函數(shù)將消息發(fā)送到內(nèi)核,指定目標(biāo)地址為內(nèi)核地址(dest_addr.nl_pid = 0) 。就像是把 “包裹” 寄給內(nèi)核這個(gè) “收件人”。
  • 接收消息:通過(guò) recv 函數(shù)接收內(nèi)核返回的響應(yīng)消息,并打印出來(lái) 。就像是在 “站點(diǎn)” 等待接收內(nèi)核寄回的 “包裹”,并查看里面的內(nèi)容。

8.3內(nèi)核態(tài)編程示例

接下來(lái)是內(nèi)核態(tài)的 Netlink 編程示例,展示如何在內(nèi)核模塊中創(chuàng)建 Netlink socket、注冊(cè)回調(diào)函數(shù)、發(fā)送和接收消息。

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/netlink.h>
#include <linux/skbuff.h>
#include <net/sock.h>

#define NETLINK_USER 31
struct sock *nl_sk = NULL;

static void netlink_recv_msg(struct sk_buff *skb) {
    struct nlmsghdr *nlh;
    int pid;
    struct sk_buff *skb_out;
    char *msg = "Hello from kernel!";
    int msg_size = strlen(msg);
    int res;

    // 獲取Netlink消息頭部
    nlh = (struct nlmsghdr*)skb->data;
    printk(KERN_INFO "Kernel received message: %s\n", (char*)NLMSG_DATA(nlh));

    pid = nlh->nlmsg_pid; // 獲取用戶進(jìn)程PID

    // 構(gòu)造響應(yīng)消息
    skb_out = nlmsg_new(msg_size, 0);
    if (!skb_out) {
        printk(KERN_ERR "Failed to allocate new skb\n");
        return;
    }

    nlh = nlmsg_put(skb_out, 0, 0, NLMSG_DONE, msg_size, 0);
    strncpy(NLMSG_DATA(nlh), msg, msg_size);

    res = nlmsg_unicast(nl_sk, skb_out, pid); // 發(fā)送響應(yīng)
    if (res < 0) {
        printk(KERN_INFO "Error sending message to user\n");
    }
}

static int __init netlink_init(void) {
    struct netlink_kernel_cfg cfg = {
       .input = netlink_recv_msg // 注冊(cè)消息接收回調(diào)
    };

    nl_sk = netlink_kernel_create(&init_net, NETLINK_USER, &cfg);
    if (!nl_sk) {
        printk(KERN_ALERT "Error creating Netlink socket\n");
        return -10;
    }

    printk(KERN_INFO "Netlink module loaded\n");
    return 0;
}

static void __exit netlink_exit(void) {
    netlink_kernel_release(nl_sk);
    printk(KERN_INFO "Netlink module unloaded\n");
}

module_init(netlink_init);
module_exit(netlink_exit);
MODULE_LICENSE("GPL");
  • 創(chuàng)建 Netlink socket 并注冊(cè)回調(diào)函數(shù):netlink_kernel_create 函數(shù)用于在內(nèi)核中創(chuàng)建一個(gè) Netlink socket,并注冊(cè)一個(gè)回調(diào)函數(shù) netlink_recv_msg 。當(dāng)有 Netlink 消息到達(dá)時(shí),內(nèi)核會(huì)調(diào)用這個(gè)回調(diào)函數(shù)進(jìn)行處理 。這就像是在內(nèi)核中設(shè)置了一個(gè) “消息接收站”,并指定了處理消息的 “工作人員”。
  • 接收消息:在 netlink_recv_msg 函數(shù)中,首先獲取消息頭部,打印接收到的消息內(nèi)容,并獲取發(fā)送方的 PID 。這一步就像是 “工作人員” 接收消息,并查看寄件人的信息。
  • 構(gòu)造響應(yīng)消息:使用 nlmsg_new 函數(shù)創(chuàng)建一個(gè)新的套接字緩沖區(qū)來(lái)存放響應(yīng)消息,然后使用 nlmsg_put 函數(shù)填充消息頭和消息體 。這就像是 “工作人員” 準(zhǔn)備好要回復(fù)的 “包裹”,并填寫好相關(guān)信息。
  • 發(fā)送響應(yīng)消息:通過(guò) nlmsg_unicast 函數(shù)將響應(yīng)消息發(fā)送回給用戶空間的進(jìn)程,指定目標(biāo) PID 為發(fā)送方的 PID 。這就像是 “工作人員” 把回復(fù)的 “包裹” 寄給寄件人。

8.4測(cè)試與調(diào)試

在完成代碼編寫后,我們需要對(duì)程序進(jìn)行測(cè)試和調(diào)試,以確保其正常運(yùn)行。

測(cè)試方法

編譯內(nèi)核模塊:將上述內(nèi)核態(tài)代碼保存為 netlink_kernel.c 文件,然后使用以下命令編譯:

make -C /lib/modules/$(uname -r)/build M=$(pwd) modules

編譯完成后,會(huì)生成 netlink_kernel.ko 文件。

加載內(nèi)核模塊:使用以下命令加載內(nèi)核模塊:

sudo insmod netlink_kernel.ko

編譯用戶態(tài)程序:將上述用戶態(tài)代碼保存為 netlink_user.c 文件,然后使用以下命令編譯:

gcc -o netlink_user netlink_user.c

運(yùn)行用戶態(tài)程序:執(zhí)行編譯好的用戶態(tài)程序:

./netlink_user

如果一切正常,你應(yīng)該能看到用戶態(tài)程序發(fā)送消息到內(nèi)核,并接收到內(nèi)核返回的響應(yīng)消息。

調(diào)試技巧

①打印調(diào)試信息:在內(nèi)核模塊中,可以使用 printk 函數(shù)打印調(diào)試信息 。通過(guò)查看內(nèi)核日志(使用 dmesg 命令),可以了解內(nèi)核模塊的運(yùn)行情況。比如,在 netlink_recv_msg 函數(shù)中,我們使用 printk 打印了接收到的消息內(nèi)容,這樣在調(diào)試時(shí)就可以清楚地知道內(nèi)核接收到了什么消息。

②使用 GDB 調(diào)試用戶態(tài)程序:對(duì)于用戶態(tài)程序,可以使用 GDB(GNU Debugger)進(jìn)行調(diào)試 。在編譯用戶態(tài)程序時(shí),加上 -g 選項(xiàng),生成包含調(diào)試信息的可執(zhí)行文件:

gcc -g -o netlink_user netlink_user.c

然后使用 GDB 啟動(dòng)調(diào)試:

gdb netlink_user

在 GDB 中,可以設(shè)置斷點(diǎn)、單步執(zhí)行等,幫助我們找出程序中的問(wèn)題。比如,我們可以在 sendto 函數(shù)調(diào)用處設(shè)置斷點(diǎn),查看發(fā)送消息時(shí)的參數(shù)是否正確。

檢查 Netlink 套接字狀態(tài):使用 netstat -anp | grep netlink 命令可以查看 Netlink 套接字的狀態(tài),包括是否綁定成功、是否正在監(jiān)聽(tīng)等 。這對(duì)于排查網(wǎng)絡(luò)連接問(wèn)題非常有幫助。如果發(fā)現(xiàn)套接字沒(méi)有正確綁定,就需要檢查綁定代碼和地址配置是否正確。

九、Netlink 使用中的注意事項(xiàng)

9.1錯(cuò)誤處理

在 Netlink 通信中,錯(cuò)誤處理至關(guān)重要。它就像是通信過(guò)程中的 “安全衛(wèi)士”,能夠確保通信的穩(wěn)定性和可靠性。如果忽視錯(cuò)誤處理,一旦出現(xiàn)問(wèn)題,程序可能會(huì)出現(xiàn)異常行為,甚至導(dǎo)致系統(tǒng)崩潰。常見(jiàn)的錯(cuò)誤類型包括:

套接字創(chuàng)建失?。涸谑褂?socket () 函數(shù)創(chuàng)建 Netlink 套接字時(shí),可能會(huì)因?yàn)橄到y(tǒng)資源不足、地址族或協(xié)議類型錯(cuò)誤等原因?qū)е聞?chuàng)建失敗 。比如,當(dāng)系統(tǒng)中同時(shí)創(chuàng)建的套接字?jǐn)?shù)量過(guò)多,超過(guò)了系統(tǒng)的限制時(shí),就會(huì)出現(xiàn)套接字創(chuàng)建失敗的情況。此時(shí),我們需要檢查返回值,如果返回值小于 0,說(shuō)明創(chuàng)建失敗,應(yīng)使用perror函數(shù)打印錯(cuò)誤信息,以便定位問(wèn)題所在。

綁定地址失?。篵ind () 函數(shù)用于將套接字與本地地址綁定,如果綁定失敗,可能是因?yàn)榈刂芬驯徽加?、?quán)限不足等原因 。在一個(gè)多進(jìn)程的系統(tǒng)中,如果多個(gè)進(jìn)程嘗試綁定同一個(gè)地址,就會(huì)出現(xiàn)地址沖突,導(dǎo)致綁定失敗。當(dāng)綁定失敗時(shí),同樣需要打印錯(cuò)誤信息,根據(jù)錯(cuò)誤提示進(jìn)行處理,比如更換綁定地址或者提升權(quán)限。

消息發(fā)送失?。簊endmsg () 函數(shù)在發(fā)送 Netlink 消息時(shí),可能會(huì)因?yàn)榫W(wǎng)絡(luò)故障、目標(biāo)地址錯(cuò)誤、消息長(zhǎng)度超過(guò)限制等原因?qū)е掳l(fā)送失敗 。當(dāng)網(wǎng)絡(luò)出現(xiàn)中斷時(shí),消息就無(wú)法正常發(fā)送。如果消息發(fā)送失敗,我們需要根據(jù)具體的錯(cuò)誤情況進(jìn)行處理,比如重新發(fā)送消息、檢查目標(biāo)地址等。

消息接收失敗:recvmsg () 函數(shù)接收消息時(shí),也可能會(huì)遇到各種問(wèn)題,如套接字未正確連接、緩沖區(qū)溢出等 。當(dāng)接收緩沖區(qū)的大小小于接收到的消息長(zhǎng)度時(shí),就會(huì)發(fā)生緩沖區(qū)溢出,導(dǎo)致消息接收失敗。在這種情況下,我們可以通過(guò)調(diào)整緩沖區(qū)大小、檢查套接字連接狀態(tài)等方式來(lái)解決問(wèn)題。

9.2性能優(yōu)化

為了提高 Netlink 通信的性能,我們可以從以下幾個(gè)方面入手:

合理設(shè)置緩沖區(qū)大?。壕彌_區(qū)的大小直接影響著通信的效率。如果緩沖區(qū)過(guò)小,可能會(huì)導(dǎo)致消息丟失或需要頻繁地進(jìn)行數(shù)據(jù)傳輸;如果緩沖區(qū)過(guò)大,又會(huì)浪費(fèi)系統(tǒng)資源 。在實(shí)際應(yīng)用中,我們需要根據(jù)消息的大小和通信的頻率來(lái)合理設(shè)置緩沖區(qū)大小。對(duì)于一些實(shí)時(shí)性要求較高、消息量較小的通信場(chǎng)景,可以適當(dāng)減小緩沖區(qū)大小,以提高消息的處理速度;而對(duì)于一些大數(shù)據(jù)量的傳輸場(chǎng)景,則需要增大緩沖區(qū)大小,減少數(shù)據(jù)傳輸?shù)拇螖?shù)。

優(yōu)化消息處理邏輯:在處理 Netlink 消息時(shí),應(yīng)盡量減少不必要的計(jì)算和操作,提高消息處理的速度 ??梢圆捎枚嗑€程或異步處理的方式,將消息處理任務(wù)分配到不同的線程中,避免主線程被阻塞。在處理網(wǎng)絡(luò)配置消息時(shí),如果需要進(jìn)行復(fù)雜的網(wǎng)絡(luò)計(jì)算,可以將這些計(jì)算任務(wù)放到單獨(dú)的線程中執(zhí)行,主線程繼續(xù)接收和處理其他消息,從而提高系統(tǒng)的整體性能。

減少系統(tǒng)調(diào)用次數(shù):系統(tǒng)調(diào)用是一種比較耗時(shí)的操作,應(yīng)盡量減少不必要的系統(tǒng)調(diào)用 。在 Netlink 通信中,可以通過(guò)批量處理消息的方式,減少 sendmsg () 和 recvmsg () 等系統(tǒng)調(diào)用的次數(shù)。當(dāng)需要發(fā)送多個(gè)小消息時(shí),可以將這些消息合并成一個(gè)大消息進(jìn)行發(fā)送,從而減少系統(tǒng)調(diào)用的開(kāi)銷。

使用高效的數(shù)據(jù)結(jié)構(gòu)和算法:選擇合適的數(shù)據(jù)結(jié)構(gòu)和算法可以顯著提高程序的性能 。在存儲(chǔ)和處理 Netlink 消息時(shí),可以使用鏈表、哈希表等數(shù)據(jù)結(jié)構(gòu)來(lái)提高數(shù)據(jù)的查找和訪問(wèn)效率。對(duì)于一些需要頻繁查找消息的場(chǎng)景,使用哈希表可以大大提高查找速度,減少處理時(shí)間。

9.3安全問(wèn)題

在 Netlink 通信中,安全問(wèn)題不容忽視。如果通信過(guò)程中出現(xiàn)安全漏洞,可能會(huì)導(dǎo)致系統(tǒng)信息泄露、被惡意攻擊等嚴(yán)重后果。以下是一些需要注意的安全問(wèn)題:

防止非法訪問(wèn):Netlink 通信涉及到內(nèi)核與用戶空間的交互,需要確保只有合法的進(jìn)程能夠訪問(wèn) Netlink 套接字 ??梢酝ㄟ^(guò)權(quán)限控制、身份驗(yàn)證等方式來(lái)防止非法訪問(wèn)。在創(chuàng)建 Netlink 套接字時(shí),可以設(shè)置適當(dāng)?shù)臋?quán)限,只有具有相應(yīng)權(quán)限的用戶或進(jìn)程才能訪問(wèn);也可以采用身份驗(yàn)證機(jī)制,如使用數(shù)字證書、密鑰等方式,驗(yàn)證通信雙方的身份,確保通信的安全性。

防止數(shù)據(jù)篡改:在 Netlink 消息傳輸過(guò)程中,需要防止消息被篡改 。可以使用消息摘要、加密等技術(shù)來(lái)保證數(shù)據(jù)的完整性和保密性。消息摘要算法(如 MD5、SHA-1 等)可以生成消息的唯一摘要,接收方可以通過(guò)驗(yàn)證摘要來(lái)判斷消息是否被篡改;加密技術(shù)(如 AES、RSA 等)可以對(duì)消息進(jìn)行加密,只有擁有正確密鑰的接收方才能解密消息,從而保證消息的保密性。

避免緩沖區(qū)溢出:緩沖區(qū)溢出是一種常見(jiàn)的安全漏洞,可能會(huì)被攻擊者利用來(lái)執(zhí)行惡意代碼 。在處理 Netlink 消息時(shí),要確保接收和發(fā)送緩沖區(qū)的大小足夠,并且對(duì)輸入的數(shù)據(jù)進(jìn)行嚴(yán)格的邊界檢查,防止緩沖區(qū)溢出的發(fā)生。在接收消息時(shí),要檢查消息的長(zhǎng)度是否超過(guò)了緩沖區(qū)的大小,如果超過(guò),應(yīng)采取相應(yīng)的措施,如拒絕接收或調(diào)整緩沖區(qū)大小。

責(zé)任編輯:武曉燕 來(lái)源: 深度Linux
相關(guān)推薦

2020-11-12 08:52:16

Python

2019-04-29 10:26:49

TCP網(wǎng)絡(luò)協(xié)議網(wǎng)絡(luò)通信

2009-08-24 17:20:13

C#網(wǎng)絡(luò)通信TCP連接

2022-12-05 09:25:17

Kubernetes網(wǎng)絡(luò)模型網(wǎng)絡(luò)通信

2010-07-01 15:45:22

網(wǎng)絡(luò)通信協(xié)議

2017-01-15 17:44:56

node網(wǎng)絡(luò)通信Socket

2024-02-20 19:53:57

網(wǎng)絡(luò)通信協(xié)議

2014-09-16 17:00:02

UDP

2010-06-09 11:57:42

網(wǎng)絡(luò)通信協(xié)議

2010-06-14 19:13:28

網(wǎng)絡(luò)通信協(xié)議

2010-06-29 10:15:31

局域網(wǎng)故障

2020-07-06 07:52:10

Kubernetes網(wǎng)絡(luò)通信

2021-08-13 11:27:25

網(wǎng)絡(luò)通信數(shù)據(jù)

2009-10-16 08:48:08

2019-09-25 08:25:49

RPC網(wǎng)絡(luò)通信

2021-08-30 13:08:56

Kafka網(wǎng)絡(luò)通信

2010-04-22 16:10:48

Aix操作系統(tǒng)網(wǎng)絡(luò)通信

2022-05-13 10:59:14

容器網(wǎng)絡(luò)通信

2010-06-09 11:31:55

網(wǎng)絡(luò)通信協(xié)議

2016-08-25 11:17:16

CaaS華為
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)