netlink怎么读_ovs源码阅读--netlink使用
netlink
netlink socket是一種用于用戶態進程和內核態進程之間的通信機制。它通過為內核模塊提供一組特殊的API,并為用戶程序提供了一組標準的socket接口的方式,實現了全雙工的通訊連接。
特點:
雙向傳輸,異步通信
用戶空間中使用標準socket API
內核空間中使用專門的API
支持多播
可由內核端發起通信
支持32種協議類型
netlink僅支持32種協議類型,這在實際應用中可能并不足夠,因此產生了generic netlink(以下簡稱為genl),
generic netlink支持1023個子協議號,彌補了netlink協議類型較少的缺陷。
通信架構
Netlink子系統:所有genl通信的基礎,Netlink子系統中收到的所有Generic類型的netlink數據都被送到genl總線上;從內核發出的數據也經由genl總線送至netlink子系統,再打包送至用戶空間
Generic Netlink控制器:作為內核的一部分,負責動態地分配genl通道(即genl family id),并管理genl任務,genl控制器是一個特殊的genl內核用戶,它負責監聽genl bus上的通信通道
genl通信建立在一系列的通信通道的基礎上,每個genl family對應多個通道,這些通道由genl控制器動態分配
相關結構體
genl family
Generic Netlink是基于客戶端-服務端模型的通信機制,服務端注冊family(family是對genl服務的各項定義的集合),控制器和客戶端都通過已注冊的信息與服務端通信。
//genl_family主要字段
struct genl_family
{
unsigned intid;//family id
unsigned int hdrsize; //用戶自定議頭部長度
char name[GENL_NAMSIZ]; //family名,要求不同的family使用不同的名字
unsigned int version;//版本
unsigned int maxattr;//最大attr類型數,使用netlink標準的attr來傳輸數據
genl_ops *ops;// 操作集合
};
genl_ops
定義了netlink family相關的操作
// genl_ops主要字段
struct genl_ops
{
u8 cmd;//命令名,用于識別genl_ops
unsigned int flags;//設置屬性
struct nla_policy *policy; //定義了attr規則,genl在觸發事件處理程序之前,會用其進行attr校驗
int (*doit)(struct sk_buff *skb, struct genl_info *info);
int (*dumpit)(struct sk_buff *skb, struct netlink_callback *cb);
};
doit:回調函數,在generic netlink收到數據時觸發,運行在進程上下文
dumpit:回調函數,當genl_ops的flag標志被添加了NLM_F_DUMP以后,每次收到genl消息即會回觸發這個函數
dumpit與doit的區別是:dumpit的第一個參數skb不會攜帶從客戶端發來的數據。相反地,開發者應該在skb中填入需要傳給客戶端的數據,skb中攜帶的數據會被自動送到客戶端。只要dumpit的返回值大于0,dumpit函數就會再次被調用,并被要求在skb中填入數據。當服務端沒有數據要傳給客戶端時,dumpit要返回0。如果函數中出錯,要求返回一個負值。
nal_policy
定義了attr規則
struct nla_policy
{
u16 type;//attr中的數據類型
u16 len;//如果在type字段配置的是字符串有關的值,要把len設置為字符串的最大長度
};
genl_info
內核在接收到用戶的genetlink消息后,會對消息解析并封裝成genl_info結構
struct genl_info
{
u32 snd_seq; //發送序號
u32 snd_pid; //發送客戶端的PID
struct nlmsghdr * nlhdr; //netlink header的指針
struct genlmsghdr * genlhdr; //genl頭部的指針(即family頭部)
void * userhdr; //用戶自定義頭部指針
struct nlattr ** attrs; //如果定義了genl_ops->policy,保存被policy過濾以后的結果
};
Generic Netlink服務端(內核)初始化
這里以OVS中packet的處理為例:
1. 定義family
//定義packet family
static struct genl_family dp_packet_genl_family __ro_after_init = {
.hdrsize = sizeof(struct ovs_header),
.name = OVS_PACKET_FAMILY,
.version = OVS_PACKET_VERSION,
.maxattr = OVS_PACKET_ATTR_MAX,
.netnsok = true,
.parallel_ops = true,
.ops = dp_packet_genl_ops, //操作集合
.n_ops = ARRAY_SIZE(dp_packet_genl_ops),
.module = THIS_MODULE,
};
2. 定義operation
// 定義packet family 的操作 --- packet類型的操作只支持OVS_PACKET_CMD_EXECUTE
static struct genl_ops dp_packet_genl_ops[] = {
{ .cmd = OVS_PACKET_CMD_EXECUTE,
.flags = GENL_UNS_ADMIN_PERM,
.policy = packet_policy,
.doit = ovs_packet_cmd_execute //接受數據包時,調用ovs_packet_cmd_execute進行處理
}
};
// 定義packet family 的過濾規則
static const struct nla_policy packet_policy[OVS_PACKET_ATTR_MAX + 1] = {
[OVS_PACKET_ATTR_PACKET] = { .len = ETH_HLEN },
[OVS_PACKET_ATTR_KEY] = { .type = NLA_NESTED },
[OVS_PACKET_ATTR_ACTIONS] = { .type = NLA_NESTED },
[OVS_PACKET_ATTR_PROBE] = { .type = NLA_FLAG },
[OVS_PACKET_ATTR_MRU] = { .type = NLA_U16 },
};
3. 注冊family
genl_register_family(&dp_packet_genl_family);
Generic Netlink客戶端(用戶空間)初始化
struct sockaddr_nl saddr;
int sock;
sock = socket(AF_NETLINK, SOCK_RAW, NETLINK_GENERIC); //創建一個netlink類型的socket
if (sock < 0) {
return -1;
}
memset(&saddr, 0, sizeof(saddr));
saddr.nl_family = AF_NETLINK;
saddr.nl_pid = getpid();//獲取family id
if (bind(sock, (struct sockaddr*)&saddr, sizeof(saddr)) < 0) {//綁定
printf("bind fail!\n");
close(*p_sock);
return -1;
}
內核空間接受發送數據
接受數據:內核端一旦收到generic netlink數據,會觸發doit函數運行,通過回調函數進行處理
發送數據:將數據打包好之后,可通過單播(genlmsg_unicast)或多播()的形式進行發送
用戶空間接受發送數據
接受數據:調用recv函數即可完成從內核來的數據的接收
發送數據:調用sendto來發送數據
netlink收發數據—以ovs中packet為例
參考內容
與50位技術專家面對面20年技術見證,附贈技術全景圖總結
以上是生活随笔為你收集整理的netlink怎么读_ovs源码阅读--netlink使用的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: linux系统sql语句报错_如果数据库
- 下一篇: mllib调参 spark_从Spark