asp網(wǎng)站編輯教程汕頭seo優(yōu)化項(xiàng)目
文章目錄
- 前言
- 示例
- 示例代碼
- 構(gòu)造請(qǐng)求
- 創(chuàng)建套接字
- 發(fā)送請(qǐng)求
- 簡化示例
前言
前置閱讀要求:libnl教程(1):訂閱內(nèi)核的netlink廣播通知
本文介紹,libnl如何向內(nèi)核發(fā)送請(qǐng)求。這包含三個(gè)部分:構(gòu)建請(qǐng)求;創(chuàng)建套接字;發(fā)送請(qǐng)求。
同樣,本文使用示例說明libnl的API該如何組合使用。
本文使用的示例是,發(fā)送netlink請(qǐng)求,以創(chuàng)建一張dummy網(wǎng)卡。
示例
示例代碼
運(yùn)行該示例代碼,即可創(chuàng)建一個(gè)dummy
類型的網(wǎng)卡。網(wǎng)卡名為dummy0
。
代碼參考自:https://github.com/FDio/vpp/blob/master/src/vnet/devices/netlink.c
上面的參考代碼很好,完整的顯示了構(gòu)建請(qǐng)求-創(chuàng)建套接字-發(fā)送請(qǐng)求-接收回復(fù)的過程。
但是上面參考代碼的路子有點(diǎn)野。因?yàn)樗苌僬{(diào)用libnl的API。它作為參考是好的。但是日常編程中,還是盡量調(diào)用libnl的API。
下面的示例代碼中,我在展示邏輯結(jié)構(gòu)的基礎(chǔ)上,盡量調(diào)用了libnl的API。
#include <linux/rtnetlink.h>
#include <netlink/msg.h>
#include <netlink/netlink.h>
#include <netlink/route/link.h>
#include <netlink/socket.h>int netlink_add(const char *iftype, const char *ifname) {int ret = 0;struct nl_msg *msg = NULL;struct nl_sock *sk = NULL;// 構(gòu)建請(qǐng)求msg = nlmsg_alloc_simple(RTM_NEWLINK, NLM_F_REQUEST | NLM_F_ACK |NLM_F_CREATE | NLM_F_EXCL);struct ifinfomsg ifi = {};ifi.ifi_family = AF_UNSPEC;ret = nlmsg_append(msg, &ifi, sizeof(ifi), NLMSG_ALIGNTO);if (ret < 0) {printf("%s", nl_geterror(ret));goto end;}#if 0ret = nla_put_string(msg, IFLA_INFO_KIND, iftype);if (ret < 0) {goto end;}
#endifstruct nlattr *info = nla_nest_start(msg, IFLA_LINKINFO);ret = nla_put_string(msg, IFLA_INFO_KIND, iftype);if (ret < 0) {printf("%s", nl_geterror(ret));goto end;}nla_nest_end(msg, info);ret = nla_put_string(msg, IFLA_IFNAME, ifname);if (ret < 0) {printf("%s", nl_geterror(ret));goto end;}// 創(chuàng)建套接字sk = nl_socket_alloc();nl_connect(sk, NETLINK_ROUTE);// 發(fā)送請(qǐng)求ret = nl_send_auto(sk, msg);if (ret < 0) {printf("%s", nl_geterror(ret));goto end;}// 接收回復(fù)ret = nl_recvmsgs_default(sk);if (ret < 0) {printf("%s", nl_geterror(ret));goto end;}end:nlmsg_free(msg);nl_socket_free(sk);return 0;
}int main(int argc, char *argv[]) { netlink_add("dummy", "dummy0"); }
構(gòu)造請(qǐng)求
一個(gè)Link請(qǐng)求包含三部分:
- netlink header(struct nlmsghdr): netlink消息本身的頭。其余部分都是netlink消息的負(fù)載。整個(gè)消息都遵循TLV(Type–length–value)。消息頭中記錄著消息的整體長度。后面的負(fù)載中的屬性也遵循TLV。
- netlink link messages header(struct ifinfomsg): Link請(qǐng)求的消息頭。
- netlink attributes(struct nlattr): 一個(gè)屬性的類型和長度,后面要跟著具體的屬性。
請(qǐng)求的整體格式如下。
在內(nèi)存中,有對(duì)齊要求,格式如下。
接下來介紹,該如何填充這些內(nèi)容。
- struct nlmsghdr的填充:可以使用
nlmsg_alloc_simple(int nlmsg_type, int flags)
函數(shù)填充。調(diào)用這些API的好處是,可以屏蔽 sequence numbers、port等細(xì)節(jié)。代碼中的消息類型是RTM_NEWLINK
表示創(chuàng)建網(wǎng)卡。標(biāo)志的含義表示,這是一個(gè)請(qǐng)求,需要回復(fù),請(qǐng)求創(chuàng)建一張網(wǎng)卡,如果網(wǎng)卡已經(jīng)存在,則不在創(chuàng)建。 - struct ifinfomsg的填充:示例代碼沒有填充任何內(nèi)容。因?yàn)槭莿?chuàng)建網(wǎng)卡。如果是查詢/修改網(wǎng)卡等操作,需要根據(jù)不同情況填充不同內(nèi)容。
- struct nlattr的填充:示例追加了兩個(gè)屬性,分別用來設(shè)置網(wǎng)卡類型和網(wǎng)卡名稱。為什么網(wǎng)卡類型使用嵌套屬性。因?yàn)槲覀冇脩魧邮前l(fā)起請(qǐng)求,這個(gè)是內(nèi)核路由部分的要求。我是咋知道的呢?因?yàn)槲胰タ磥韑ibnl中
rtnl_link_add()
函數(shù)的源碼知道的。
創(chuàng)建套接字
使用libnl的接口創(chuàng)建套接字。當(dāng)然,我們也可以跳過libnl的API,直接使用socket創(chuàng)建套接字,但是沒必要。
sk = nl_socket_alloc();
nl_connect(sk, NETLINK_ROUTE);
發(fā)送請(qǐng)求
通過netlink套接字,發(fā)送netlink消息的標(biāo)準(zhǔn)方法是,使用nl_send_auto()
函數(shù)。它將自動(dòng)補(bǔ)充netlink消息頭中丟失的內(nèi)容信息,然后將消息傳遞給nl_send()
。
簡化示例
上面示例中,最麻煩的一步是構(gòu)造請(qǐng)求。
其實(shí),我們想一想,構(gòu)造請(qǐng)求基本都是固定的,只有很少的字段需要用戶指定。
再想一想,其實(shí)請(qǐng)求和回復(fù)也基本是固定的。
這些都可以按照目的進(jìn)行封裝,形成更高層的接口。
下面,我們使用libnl的接口,可以更簡單的實(shí)現(xiàn)我們的目標(biāo)。(因?yàn)檫@個(gè)完全失去了請(qǐng)求的細(xì)節(jié),所以我構(gòu)造了上面的示例。)
#include <linux/rtnetlink.h>
#include <netlink/msg.h>
#include <netlink/netlink.h>
#include <netlink/route/link.h>
#include <netlink/socket.h>int netlink_add(const char *iftype, const char *ifname) {struct rtnl_link *link = rtnl_link_alloc();rtnl_link_set_type(link, iftype);rtnl_link_set_name(link, ifname);struct nl_sock *sk = nl_socket_alloc();nl_connect(sk, NETLINK_ROUTE);rtnl_link_add(sk, link, NLM_F_CREATE | NLM_F_EXCL);return 0;
}int main(int argc, char *argv[]) { netlink_add("dummy", "dummy0"); }