植物百科网
当前位置: 首页 农业百科

linux网络参数查看方法(Linuxnetlink详解)

时间:2023-06-17 作者: 小编 阅读量: 2 栏目名: 农业百科

linux网络参数查看方法熟悉Linuxwifi的同学都知道,wpa_supplicant程序是基于netlink与wifi驱动进行通信的。本文学习下Linux的Netlink,给出用户空间与内核空间基于netlink通信的示例。示例包括netlink和Genericnetlink。用户空间程序包括基于原生LinuxAPI和基于libnlAPI。netlink基础netlink协议是一个基于socket的,用于内核与用户空间进程通信的一个协议。关于genericnetlinkAPI我们下一章节详细介绍。

linux网络参数查看方法?熟悉Linux wifi的同学都知道,wpa_supplicant程序是基于netlink与wifi驱动进行通信的 (wpa_supplicant是wifi station用户空间守护进程),今天小编就来聊一聊关于linux网络参数查看方法?接下来我们就一起去研究一下吧!

linux网络参数查看方法

熟悉Linux wifi的同学都知道,wpa_supplicant程序是基于netlink与wifi驱动进行通信的。 (wpa_supplicant是wifi station用户空间守护进程)

本文学习下Linux的Netlink,给出用户空间与内核空间基于netlink通信的示例。 示例包括netlink和Generic netlink。用户空间程序包括基于原生Linux API和基于libnl API。

netlink基础

netlink协议是一个基于socket的,用于内核与用户空间进程通信的一个协议。

用户态创建netlink socket的代码如下:

int fd = socket(AF_NETLINK, SOCK_RAW, MY_NETLINK)//socket接口的原型为:int socket(int domain, int type, int protocol);//这里用到了第三个入参protocol,即创建Netlink socket时要指定协议号

内核态创建netlink socket的接口原型为:

struct sock *netlink_kernel_create(struct net *net, int unit, struct netlink_kernel_cfg *cfg)//这里的第二个参数unit为协议号,与用户空间的protocol相同

Netlink消息有固定的格式,struct nlmsghdr

struct nlmsghdr {__u32nlmsg_len;/* Length of message including Header */__u16nlmsg_type; /* Message content */__u16nlmsg_flags;/* Additional flags */__u32nlmsg_seq;/* Sequence number */__u32nlmsg_pid;/* Sending process port ID */};

完整示例见github: https://github.com/jian-soft/netlink_examples

下文为关键代码注释。

原生Linux API示例内核侧示例

//定义自己的netlink协议号#define MY_NETLINK 31//接收回调,即内核侧收到用户发来的netlink消息回调static void netlink_recv_msg(struct sk_buff *skb){...nlh = (struct nlmsghdr *)skb->data;//取netlink消息头pid = nlh->nlmsg_pid; /* pid of sending process */msg = (char *)nlmsg_data(nlh);//取netlink消息data部分msg_size = strlen(msg);printk(KERN_INFO "netlink_kernel: Received from pid %d: %s\n", pid, msg);...}//定义netlink_kernel_cfg,即声明接收回调struct netlink_kernel_cfg cfg = {.input = netlink_recv_msg,};//创建内核测netlink socketg_nl_sock = netlink_kernel_create(&init_net, MY_NETLINK, &cfg);if (!g_nl_sock) {printk(KERN_ALERT "netlink_kernel: Error creating socket.\n");return -10;}

用户侧示例

int main(int argc, char* argv[]){//创建socketsock_fd = socket(PF_NETLINK, SOCK_RAW, MY_NETLINK);memset(&src_addr, 0, sizeof(src_addr));src_addr.nl_family = AF_NETLINK;src_addr.nl_pid = getpid(); /* self pid *///绑定端口bind(sock_fd, (struct sockaddr*)&src_addr, sizeof(src_addr));//设置目标地址为内核netlinkmemset(&dest_addr, 0, sizeof(dest_addr));dest_addr.nl_family = AF_NETLINK;dest_addr.nl_pid = 0; /* For Linux Kernel */dest_addr.nl_groups = 0; /* unicast *///可以通过sendmsg或sendto两种接口向内核发送消息//相应的有recvmsg和recvfrom两种接口接收来自内核的消息//详见github,此处略}

libnl API示例

libnl对用户空间Linu原生的netlink API进行了封装,使得用户空间程序更容易编写,尤其是对于generic netlink API。 关于generic netlink API我们下一章节详细介绍。

另外wpa_supplicant与内核wifi驱动的通信就是用的libnl generic netlink API。

先介绍下libnl的主要接口,定义在头文件<netlink/netlink.h>

//创建netlink socket, libnl中用struct nl_sock表示一个socket#include <netlink/socket.h>struct nl_sock *nl_socket_alloc(void)void nl_socket_free(struct nl_sock *sk)//回调配置struct nl_cb *nl_socket_get_cb(const struct nl_sock *sk);void nl_socket_set_cb(struct nl_sock *sk, struct nl_cb *cb);int nl_socket_modify_cb(struct nl_sock *, enum nl_cb_type, enum nl_cb_kind, nl_recvmsg_msg_cb_t, void *);//发送int nl_send_auto(struct nl_sock *sk, struct nl_msg *msg)int nl_send(struct nl_sock *sk, struct nl_msg *msg)int nl_send_iovec(struct nl_sock *sk, struct nl_msg *msg, struct iovec *iov, unsigned iovlen)int nl_sendmsg(struct nl_sock *sk, struct nl_msg *msg, struct msghdr *hdr) //nl_sendmsg里调用Linux原生sendmsg接口int nl_sendto(struct nl_sock *sk, void *buf, size_t size) //nl_sendto调用Linux原生sendto接口int nl_send_simple(struct nl_sock *sk, int type, int flags, void *buf, size_t size)//接收int nl_recvmsgs_default(struct nl_sock *sk)int nl_recvmsgs(struct nl_sock *sk, struct nl_cb *cb)//如果socket是阻塞的,就阻塞式接收。recv到数据之后,通过cb进行处理

libnl用户侧示例

基于上一节的例子,内核测代码不变,用户侧使用libnl重写。

#include <netlink/netlink.h>#include <netlink/msg.h>#define MY_NETLINK 31#define MY_NETLINK_TYPE_SET 0//接收回调static int my_input(struct nl_msg *msg, void *arg){struct nlmsghdr *nlh = nlmsg_hdr(msg);char *data = nlmsg_data(nlh);int datalen = nlmsg_datalen(nlh);printf("input cb: datalen:%d, data:%d\n", datalen, data);return 0;}int main(int argc, char* argv[]){struct nl_sock *sk;int ret;//创建并绑定socketsk = nl_socket_alloc();ret = nl_connect(sk, MY_NETLINK);//修改接收回调函数,收到任何消息都会回调my_inputnl_socket_modify_cb(sk, NL_CB_MSG_IN, NL_CB_CUSTOM, my_input, NULL);char msg[] = "Hello libnl!\n"ret = nl_send_simple(sk, MY_NETLINK_TYPE_SET, 0, msg, sizeof(msg));//阻塞式等待接收。接收到内核发来的消息后,会进入接收回调my_inputnl_recvmsgs_default(sk);nl_socket_free(sk);}

generic netlink示例,基于libnl

netlink通信协议在不修改内核源码的情况下,最大只支持定义32种协议。 随着netlink的使用越来越多,32个协议号已不够用,所以引入了generic netlink。 generic netlink其实是对netlink报文进行了又一次封装,generic netlink使用的netlink协议号是NETLINK_GENERIC=16。

genl的消息格式如下:

01230 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -|Netlink message header (nlmsghdr)|- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -| Generic Netlink message header (genlmsghdr)|- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -|Optional user specific message header|- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -| Optional Generic Netlink message payload|- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -struct genlmsghdr {__u8cmd;__u8version;__u16reserved;};//genl的message payload基于netlink的属性机制,即payload是由一个个nlattr组成/* *<------- NLA_hdrLEN ------> <-- NLA_ALIGN(payload)--> *--------------------- - - - - - - - - - - - - - - - -* |Header| Pad |Payload| Pad | * |(struct nlattr)| ing || ing | *--------------------- - - - - - - - - - - - - - - - -*<-------------- nlattr->nla_len --------------> */struct nlattr {__u16 nla_len;__u16 nla_type;};

genl内核侧示例

参考: https://wiki.linuxfoundation.org/networking/generic_netlink_howto

注意:genl_register_ops接口只在3.12及之前版本有; 3.13~4.9版本用genl_register_family_with_ops; 4.10版本及以后没有注册ops的接口,只有注册family的接口,ops要直接定义在family内。

本文示例基于4.15内核。

注册generic netlink family需要3步:

  1. 定义操作
  2. 定义family
  3. 注册family

/* Step1: 定义操作 *//* attributes */enum {EXMPL_A_UNSPEC,EXMPL_A_MSG,_EXMPL_A_MAX,};#define EXMPL_A_MAX (_EXMPL_A_MAX - 1)/* attribute policy */static struct nla_policy exmpl_genl_policy[EXMPL_A_MAX1] = {[EXMPL_A_MSG] = {.type = NLA_NUL_STRING},};// handlerstatic int exmpl_ECHO(struct sk_buff *skb, struct genl_info *info);// commandsenum {EXMPL_C_UNSPEC,EXMPL_C_ECHO,_EXMPL_C_MAX,};#define EXMPL_C_MAX (_EXMPL_C_MAX - 1)// operation definitionstruct genl_ops exmpl_genl_ops[EXMPL_C_MAX] = {{.cmd = EXMPL_C_ECHO,.doit = exmpl_echo,.policy = exmpl_genl_policy,}};

#define FAMILY_NAME "my_genl"/* Step2: 定义family */// family definitionstatic struct genl_family my_genl_family = {.id = 0,.hdrsize = 0,//表示没有用户自定义的额外header.name = FAMILY_NAME,.version = 1,.ops = exmpl_genl_ops,.n_ops = ARRAY_SIZE(exmpl_genl_ops),.maxattr = EXMPL_A_MAX1,};// handler的具体定义static int exmpl_echo(struct sk_buff *skb, struct genl_info *info){struct nlattr *na;struct sk_buff *reply_skb;void *msg_head;int ret;printk("%s in.\n", __func__);//内核已经解析好了每个attrna = info->attrs[EXMPL_A_MSG];if (!na) {printk("Error: attr EXMPL_A_MSG is null\n");return -EINVAL;}printk("Recv message: %s\n", nla_data(na));//将收到的消息发回去reply_skb = genlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);//填写genl消息头msg_head = genlmsg_put(reply_skb, info->snd_portid, info->snd_seq, &my_genl_family, 0, EXMPL_C_ECHO);//向skb尾部填写attrnla_put_string(reply_skb, EXMPL_A_MSG, nla_data(na));//Finalize the message: 更新nlmsghdr中的nlmsg_len字段genlmsg_end(reply_skb, msg_head);//Send the message backret = genlmsg_reply(reply_skb, info);if (ret != 0) {printk("genlmsg_reply return fail: %d\n", ret);return -ret;}return 0;}

/* Step3: 注册famliy */int ret;ret = genl_register_family(&my_genl_family);if (err != 0) {printk("genl_register_family fail, ret:%d\n", ret);return ret;}

genl用户侧示例(基于libnl)

#define MY_FAMILY_NAME "my_genl"//用户侧需要定义和内核侧相同的属性以及命令,所以通常把这一部分摘成一个独立的.h,内核和app共用//这里没有摘成一个独立的.h,用户侧也重复定义一份/* attributes */enum {EXMPL_A_UNSPEC,EXMPL_A_MSG,_EXMPL_A_MAX,};#define EXMPL_A_MAX (_EXMPL_A_MAX - 1)// define attribute policystatic struct nla_policy exmpl_genl_policy[EXMPL_A_MAX1] = {[EXMPL_A_MSG] = {.type = NLA_STRING},};// commandsenum {EXMPL_C_UNSPEC,EXMPL_C_ECHO,_EXMPL_C_MAX,};#define EXMPL_C_MAX (_EXMPL_C_MAX - 1)//接收回调定义int recv_callback(struct nl_msg* recv_msg, void* arg){struct nlmsghdr *nlh = nlmsg_hdr(recv_msg);struct nlattr *tb_msg[EXMPL_A_MAX1];if (nlh->nlmsg_type == NLMSG_ERROR) {printf("Received NLMSG_ERROR message!\n");return NL_STOP;}struct genlmsghdr *gnlh = (struct genlmsghdr*)nlmsg_data(nlh);//按照每attr解析内核发来的genl消息nla_parse(tb_msg, EXMPL_A_MAX,genlmsg_attrdata(gnlh, 0),genlmsg_attrlen(gnlh, 0),exmpl_genl_policy);//判断是否包含属性EXMPL_A_MSGif (tb_msg[EXMPL_A_MSG]) {// parse it as stringchar * payload_msg = nla_get_string(tb_msg[EXMPL_A_MSG]);printf("Kernel replied: %s\n", payload_msg);} else {printf("Attribute EXMPL_A_MSG is missing\n");}return NL_OK;}int main(int argc, char* argv[]){//创建并连接genl socketstruct nl_sock *sk = nl_socket_alloc();genl_connect(sk);//根据FAMILY_NAME获得对应的famlily_idint family_id;family_id = genl_ctrl_resolve(sk, FAMILY_NAME);if (family_id < 0) {printf("generic netlink family '" FAMILY_NAME "' NOT REGISTERED\n");nl_socket_free(sk);exit(-1);} else {printf("Family-ID of generic netlink family '" FAMILY_NAME "' is: %d\n", family_id);}//设置接收回调nl_socket_modify_cb(sk, NL_CB_MSG_IN, NL_CB_CUSTOM, recv_callback, NULL);//发送消息struct nl_msg *msg = nlmsg_alloc();genlmsg_put(msg, NL_AUTO_PORT, NL_AUTO_SEQ, family_id,0, NLM_F_REQUEST, EXMPL_C_ECHO, 1);NLA_PUT_STRING(msg, EXMPL_A_MSG, "genl message from user to kernel");int res = nl_send_auto(sk, msg);nlmsg_free(msg);if (res < 0) {printf("nl_send_auto fail, ret:%d\n", res);} else {printf("nl_send_auto OK, ret: %d\n", res);}//接收消息。接收到内核发来的消息后,触发回调recv_callbacknl_recvmsgs_default(sk);nla_put_failure: //referenced by NLA_PUT_STRINGnl_socket_free(sk);return 0;}

这里的示例是内核收到用户空间发来的genl消息后,根据发送端的struct genl_info *info, 调用genlmsg_reply(reply_skb, info),将内核的genl消息单播给用户空间app。

如果内核不知道用户空间的socket信息,内核如何将消息发送到用户空间呢? 这时一般用组播netlink消息,即内核将消息组播出去。用户空间谁订阅了这个组播,谁就能收到内核发来的消息。 关于组播netlink示例,后续有空再补一下。。。


完整示例见github: https://github.com/jian-soft/netlink_examples

    推荐阅读
  • 空气含量中最多的气体(空气含量中最多的气体介绍)

    空气中含量最多的气体是氮气,氮气约占空气体积分数的百分比约为78%。通过实验测定,空气的成分按体积计算,氮气大约占78%、氧气占21%、稀有气体0.94%、二氧化碳0.03%、其他气体和杂质0.03%,也就是说空气中含量最多的物质是氮气。氮气化学性质很不活泼,在高温高压及催化剂条件下才能和氢气反应生成氨气;在放电的情况下才能和氧气化合生成一氧化氮;即使Ca、Mg、Sr和Ba等活泼金属也只有在加热的情形下才能与其反应。

  • 文思豆腐羹如何做好吃(文思豆腐羹用什么豆腐)

    文思豆腐是一道有名的淮扬菜,需要的就是精湛的刀工,这样做出的文思豆腐会有嫩滑的口感,打造入口即化的口感。文思豆腐羹如何做好吃文思豆腐羹材料和做法步骤一、文思豆腐羹材料准备好豆腐400克,鸡脯肉,火腿还有香菇,再有准备好生菜,冬笋,调料需要准备盐和味精。

  • 结构性存款可以买理财吗(结构性存款是存款吗)

    雪球产品就是今年年初监管向信托公司进行窗口指导,要求叫停的产品。简单的说,这是一种高风险的金融衍生品,它通过持有一定结构的金融衍生品,来实现在某一特定情况下获利。这样的投资结构就能保证我不论涨,还是跌,只要在一定幅度内都可以盈利。交易期权等金融衍生品,是非常高风险的投资。

  • 正言厉色意思(正言厉色的意思)

    下面内容希望能帮助到你,我们来一起看看吧!正言厉色意思正言厉色,汉语成语,拼音是zhènɡyánlìsè,意思是形容板着脸,神情非常严厉。出自《汉书·王莽传》。宝玉突然想出一个主意,一本正经地给她讲扬州黛山林子洞耗子精偷香芋的故事,黛玉见他正言厉色,以为真有其事,后来才发现原来是在取笑她。

  • 面谈调薪酬有什么技巧(跟老板谈调薪的技巧有哪些)

    如果你在老板心目中分量很大,一般老板都会给你加薪的。和老板谈加薪时目的一定要明确,让老板知道你只是为了加薪,而不是辞职走人。和老板谈加薪后,一定要给老板一个考虑的时间,不要咄咄逼人,逼着老板加薪。老板也要有足够的思考时间,来考虑你是否值得加薪,给你加薪后对公司有没有什么影响。不仅口头上要表示感谢,工作中要更加努力,让老板觉得给你加薪是值得的。

  • 杏花有没有香味(杏花闻起来会特别香吗)

    杏树是中国著名的观赏树木,可配植于庭前、墙隅、道路旁、水边,也可群植、片植于山坡、水畔,是春季主要的观赏树种。杏花直径2至3厘米,先于叶开放。花梗短,长1至3毫米,被短柔毛。花萼紫绿色,萼筒圆筒形,外面基部被短柔毛。萼片卵形至卵状长圆形,先端急尖或圆钝,花后反折。花瓣圆形至倒卵形,白色或带红色,具短爪。

  • 减肥减肚子的方法(怎么减肚子呢)

    减肥减肚子的方法食用健康食品:酸奶与发酵的牛奶能激活消化必须的物质,有助于改善肠道微生物系统,从而防止腹部隆起。走路、喝水、按摩:走路及喝水有利腹部扁平。

  • 新坑翡翠手镯多少钱(新坑翡翠手镯的价格)

    新坑翡翠手镯多少钱?新坑翡翠手镯多少钱翡翠手镯作为大件翡翠制品,用料特别多,只有大块、质量好的翡翠原石才能打造成手镯,因此翡翠手镯的价格都比较高,商家们拿到质量比较好的原石也尽可能打造成手镯。具体到新坑种翡翠,因为大多数新坑种翡翠透明度都不高,质地也不够细腻,因此种水一般都是以糯种或豆种为主,极少出现冰种或冰种以上的种水,这样的翡翠价格价格自然不会太高,一个品质比较好的糯种翡翠手镯大概在十万以内。

  • 宁波毛蚶做法水煮几分钟(毛蚶煮多长时间可以吃)

    宁波毛蚶做法水煮几分钟毛蚬是很多人喜欢吃的食物,不过建议大家在做之前都要先用开水煮以下。强精益气,提高精液质量,增强精子活力。适用于治疗肾阳虚所致的阳痿、腰痛、小便频数及补五脏之气不足。可治疗全身水肿,小便不利等。能软化和保护血管,有降低人体中血脂和胆固醇的作用。

  • 2022洛阳湿地公园最新名单 洛阳生态公园最新消息

    国家级湿地自然保护区河南黄河湿地国家级自然保护区,面积24000公顷。国家级湿地公园嵩县陆浑湖国家湿地公园,面积4222.39公顷伊川伊河国家湿地公园,面积1384.36公顷。