范文健康探索娱乐情感热点
投稿投诉
热点动态
科技财经
情感日志
励志美文
娱乐时尚
游戏搞笑
探索旅游
历史星座
健康养生
美丽育儿
范文作文
教案论文

Linuxnetlink详解

  熟悉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 {     __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 */ };
  完整示例见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 ", pid, msg);     ... }  //定义netlink_kernel_cfg,即声明接收回调 struct netlink_kernel_cfg cfg = {     .input = netlink_recv_msg, };  //创建内核测netlink socket g_nl_sock = netlink_kernel_create(&init_net, MY_NETLINK, &cfg); if (!g_nl_sock) {     printk(KERN_ALERT "netlink_kernel: Error creating socket. ");     return -10; } 用户侧示例int main(int argc, char* argv[]) {     //创建socket     sock_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));          //设置目标地址为内核netlink     memset(&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 socket, libnl中用struct nl_sock表示一个socket #include  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  #include   #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 ", datalen, data);      return 0; }  int main(int argc, char* argv[]) {     struct nl_sock *sk;     int ret;     //创建并绑定socket     sk = nl_socket_alloc();     ret = nl_connect(sk, MY_NETLINK);      //修改接收回调函数,收到任何消息都会回调my_input     nl_socket_modify_cb(sk, NL_CB_MSG_IN, NL_CB_CUSTOM, my_input, NULL);      char msg[] = "Hello libnl! "     ret = nl_send_simple(sk, MY_NETLINK_TYPE_SET, 0, msg, sizeof(msg));      //阻塞式等待接收。接收到内核发来的消息后,会进入接收回调my_input     nl_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的消息格式如下:    0                   1                   2                   3   0 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 {     __u8    cmd;     __u8    version;     __u16   reserved; };  //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步:  定义操作  定义family  注册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_MAX + 1] = {     [EXMPL_A_MSG] = {.type = NLA_NUL_STRING}, }; // handler static int exmpl_echo(struct sk_buff *skb, struct genl_info *info); // commands enum {     EXMPL_C_UNSPEC,     EXMPL_C_ECHO,     _EXMPL_C_MAX, }; #define EXMPL_C_MAX (_EXMPL_C_MAX - 1) // operation definition struct 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 definition static 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_MAX + 1, };  // 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. ", __func__);      //内核已经解析好了每个attr     na = info->attrs[EXMPL_A_MSG];     if (!na) {         printk("Error: attr EXMPL_A_MSG is null ");         return -EINVAL;     }     printk("Recv message: %s ", 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尾部填写attr     nla_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 back     ret = genlmsg_reply(reply_skb, info);     if (ret != 0) {         printk("genlmsg_reply return fail: %d ", 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 ", 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 policy static struct nla_policy exmpl_genl_policy[EXMPL_A_MAX + 1] = {     [EXMPL_A_MSG] = {.type = NLA_STRING}, }; // commands enum {     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_MAX + 1];      if (nlh->nlmsg_type == NLMSG_ERROR) {         printf("Received NLMSG_ERROR message! ");         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_MSG     if (tb_msg[EXMPL_A_MSG]) {         // parse it as string         char * payload_msg = nla_get_string(tb_msg[EXMPL_A_MSG]);         printf("Kernel replied: %s ", payload_msg);     } else {         printf("Attribute EXMPL_A_MSG is missing ");     }      return NL_OK; }  int main(int argc, char* argv[]) {     //创建并连接genl socket     struct nl_sock *sk = nl_socket_alloc();     genl_connect(sk);     //根据FAMILY_NAME获得对应的famlily_id     int family_id;     family_id = genl_ctrl_resolve(sk, FAMILY_NAME);     if (family_id < 0) {         printf("generic netlink family "" FAMILY_NAME "" NOT REGISTERED ");         nl_socket_free(sk);         exit(-1);     } else {         printf("Family-ID of generic netlink family "" FAMILY_NAME "" is: %d ", 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 ", res);     } else {         printf("nl_send_auto OK, ret: %d ", res);     }      //接收消息。接收到内核发来的消息后,触发回调recv_callback     nl_recvmsgs_default(sk);  nla_put_failure: //referenced by NLA_PUT_STRING      nl_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

为什么华为和中兴手机的充电线接口不是标准的接口?先讲一下几种手机接口吧。1MiniUSB接口这种接口很早了,现在应该很少用到的。我给我爸买的老年收音机用的就是这种接口。这种接口一般用于小型设备与电脑之间传送数据和充电,比如我给老为什么很宁愿每年买一个国产机,也不愿意三年买一部苹果手机?谢邀。头条这两天官方问答的题目是越来越让人不明白了。如果为了保值,几千块钱买个金戒指或者一对耳环更合算。买手机是为了保值还是为了使用?5年前我从苹果7P转到华为荣耀就是因为苹果不好为何微软将服务器沉入海底,而华为挖空大山置放,谁更胜一筹?微软和华为都是世界顶级的科技公司。微软是老牌PC软件提供商,近几年大刀阔斧业务整改,重点向云服务方向发展。华为是中国最值得骄傲的民族企业之一,在短短的三十年时间里,做到了世界通信领华为明天举行鸿蒙智联新品发布会IT之家2月27日消息,据华为官方消息,2月28日1808,华为商城将举行鸿蒙智联新品发布直播,届时将发布更多智联新品。IT之家曾报道,据华为消费者业务软件部总裁王成录消息,去年底华为Mate50Pro曝光,我就服气这个外观配置前言已经一年多没有发布新机的华为mate系列,沉寂了很久,近期,终于传来了华为Mate50Pro将会在下半年发布的消息。华为Mate50Pro曝光,你对它有哪些期待?这外观,这后置实锤!华为Mate50快来了华为将于2月27日2130召开智慧办公春季发布会,发布会之前,有不少网友期待华为新一代顶级旗舰Mate50系列会重磅登场。对此,华为消费者BG战略Marketing部副总裁Mate大国钟匠记中科院国家授时中心首席科学家张首刚和他的时间团队时间的产生保持,以及把时间信息通过一定的方式传送出去,叫作授时。自20世纪60年代以来,我国一代代授时人扎根大西北,甘坐冷板凳,筚路蓝缕创新自强,铸就了精准的中国时间。题记在举世闻农业物联网技术在蔬菜温室大棚的应用农业物联网技术在蔬菜温室大棚的应用农业物联网就是物联网技术在农业生产经营管理和服务中的具体应用。按照物联网技术架构,农业物联网仍然通过感知传输应用的途径来实现对农业的应用。感知就是字节跳动的28岁员工,身体透支后41小时离世2月23日,在抢救41小时后,字节跳动的员工28岁的吴某不幸离世。在这一年他的妻子刚怀孕两个月,还有按揭三十年的房贷。吴某是一名程序员,下班后去公司健身房健身一小时,后出现头晕呕吐36氪首发布局干细胞治疗,血霁生物完成1亿元preA轮融资近日,36氪获悉,苏州血霁生物科技有限公司(以下简称血霁生物)宣布已完成一亿元人民币的preA轮融资,此轮融资由招银国际领投,北极光创投鼎晖投资红杉中国碧桂园创投贯邦资本和君子兰资用过苹果手机后还能接受其他国产手机吗?选择哪一个品牌?苹果手机随着近些年的发布其实对消费者的吸引力有所减弱。这可以从两方面来看,一个是苹果手机自身创新力的疲软,另一个则是国产安卓手机的强势崛起。如果曾经是苹果手机用户,现在想选择国产安
你们买的红米k408256用着怎么样,有点犹豫要不要退?我用的12256,看你预算,预算有限就不要退,基本上这个价位,K40是性价比最高的,天玑1200毕竟GPU差点爽的一批。搞不懂那些说什么发烫卡屏幕等问题的。我就是充电发热。王者荣耀作为一个学生党,购买哪种手机好呢?华为小米魅族不知道哪个好?学生买实用的就行其实没有所有品牌的固定哪种好的问题,只有最适合的问题。比如预算外形某些功能,如摄影拍照等,但我觉得最重要的是预算哦。这个问题实际上提得非常宽广。因为不管是华为还是小荣耀30系列是否会更新鸿蒙系统?我的是荣耀30pro去年入手的,我买的时候荣耀还没有和华为分家,要的就是华为思想,谁知道买过之后就分家了,也挺心痛的,不过还好我的手机也能升级鸿蒙系统这让我挺开心的,非常的期待啊!为什么iPhone很多方面已落后于其他手机却仍有很多人购买?苹果最大的优势就是像你这种明明买了秒天秒地秒空气的安卓党,你自己用着开心就好!却还在不断关注苹果公司的动态销量配置。还替它操心是不是落后了?这就是实力!你气不气?等到哪天各大手机厂柳青从滴滴离职了吗?早点离开滴滴早点造福乘客!卸载滴滴使用高德首汽,我这几天感觉非常好,一个网约车平台总拿自己后台大去压迫司机威胁记者,存在的意义何在?!柳青已经从滴滴离职了。此前,柳青曾在一封公司内华为反悔,鸿蒙系统发布会展示游戏应用,曾说过不做游戏和短视频昨天华为举行了鸿蒙发布会,在发布会上,华为全方位展示了鸿蒙系统的各种操作和表现特征。当天成功引起了全体网民的大讨论,各种喜提微博知乎抖音的热榜,俨然一副鸿蒙刚发布离成功就只差临门一vivoTWS2真无线降噪耳机体验评测6月的天气逐渐开始燥热起来,上班途中公交车上地铁里拥挤的人群中,汗味弥漫下人人焦躁不安。想要掏出耳机让心情静下来乘乘凉却又被杂乱的耳机线所束缚住,心乱如麻形容的即是当时的心情,也是想买个屏幕好和音质好的手机,求推荐?感谢您的阅读!想买个屏幕好和音质好的手机,求推荐?我们想买屏幕和音质好的手机,其实考虑的一定是多方面综合性体验的。其实对于我们来说,如果说到音质的话,我可能会考虑到手机的HiFi音挖过矿的显卡用来玩绝地求生等电脑游戏,是否会有非常短的寿命?你好,这个问题是要看概率的,无论新显卡卡还是矿卡,都无法保证用几个月绝对不会坏,只是矿卡损坏的概率远远高于新卡。矿卡是用来进行大量计算来获取虚拟货币,这意味着显卡需要24小时满载运易事特最新公告以541。1万元回购宁德时代持有的新能易事特10股权易事特公告,公司近期以自有资金合计541。0959万元回购宁德时代持有的新能易事特10股权,本次交易完成后,公司持有新能易事特100股权。截至2022年5月9日收盘,易事特(300全球13台根服务器10台在美国,如果美国关闭服务,我国会断网吗?现如今,全球已进入互联网时代。无论是工作中的网络视频会议,还是平时看电视剧玩游戏,或是收集一些目前所需的知识,都无法离开互联网。现如今,互联网已经成为人类生活娱乐工作必不可少的一部