Chinaunix首页 | 论坛 | 博客
  • 博客访问: 3459906
  • 博文数量: 198
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 7256
  • 用 户 组: 普通用户
  • 注册时间: 2013-01-23 18:56
个人简介

将晦涩难懂的技术讲的通俗易懂

文章分类

全部博文(198)

文章存档

2024年(1)

2023年(9)

2022年(4)

2021年(12)

2020年(8)

2019年(18)

2018年(19)

2017年(9)

2016年(26)

2015年(18)

2014年(54)

2013年(20)

分类: LINUX

2016-01-03 17:24:42

    GENERIC NETLINK 介绍及使用

--lvyilong316

       之前一篇博文讲述了Netlink如何使用,当时我们是自己定义了一种协议NETLINK_TEST进行用户进程和内核的通信,这篇文章就讲一下如何使用内核为我们提供的NETLINK_GENERIC进行通信。

    如果把Netlink协议比作IP层,那么NETLINK_GENERIC可以比作UDP这一层。它们的具体关系如图1

1

图中NETLINK_GENERIC协议作为Netlink层的payload,当然NETLINK_GENERIC作为一个上层协议,也拥有自己的头部(图中的Family头部)和payload(图中的Family payload)。我们用户要填写的就是图中的Family payload,但是这个Family payload也是要有一定规则和格式的

首先,Family payload同样由头部和payload构成,即图中的User msgMsg payload,但是User msg不是必须的,可以没有,只是预留给特殊的用户需求。

其次,Msg payload也需要遵循一定格式,即TLVTypeLengthValue),不过这并不是NETLINK_GENERIC的特殊要求,其实Netlink协议的数据部分都是这个格式。每个TLV我们称之为一个Attr

1. 相关概念及数据结构

1.1 genlmsghdr

这是NETLINK_GENERIC的头部结构,即图1中的family头部。

点击(此处)折叠或打开

  1. struct genlmsghdr {
  2.         __u8 cmd; //命令,下文介绍genl_ops时再介绍
  3.         __u8 version; //版本号
  4.         __u16 reserved; //保留字段
  5. };

为了更好的理解NETLINK_GENERIC用到的相关字段,以及genlmsghdr的位置,我们画出NETLINK_GENERIC报文的详细格式。如图2.

2

1.2 family

     一个family即相当于我们的一个UDP addr,规定了我们一个连接的一些信息,用以区别不同的连接。Family通过向genl控制器请求获得,至于什么是genl控制器,这个用户不用关心,对使用者是透明的。genl family的结构体如下: 

点击(此处)折叠或打开

  1. struct genl_family {
  2. unsigned int id;
  3. unsigned int hdrsize;
  4. char name[GENL_NAMSIZ];
  5. unsigned int version;
  6. unsigned int maxattr;
  7. struct nlattr **attrbuf;
  8. struct list_head ops_list;
  9. struct list_head family_list;
  10.  };

对此结构体元素具体解释如下 

(1) id: family id用于标识一个family当新注册一个family的时候,应该用GENL_ID_GENERATE(0x0),表示请控制器自动为family分配的一个id0x00保留供genl控制器使用,其实就是GENL_ID_GENERATE 这个和我们Netlink头部中的pid不同,对比图2可以看出family id 其实就是netlink 头部的type字段

(2) hdrsize: 用户自定议头部长度即图1User Msg的长度如果没有用户自定义头部这个值被赋为0 

(3) version: 版本号一般填1即可 

(4) name:  family要求不同的family使用不同的名字以便控制器进行正确的查找 

(5) maxattrgenl使用netlink标准的attr来传输数据此字段定义了最大attr类型数注意不是一次传输多少个attr而是一共有多少种attr因此这个值可以被设为00代表不区分所收到的数据的attr type在接收数据时可以根据attr type获得指定的attr type的数据在整体数据中的位置 

(6) struct nlattr **attrbuf 

(7) struct list_head ops_list 

(8) struct list_head family_list 

以上的三个字段为私有字段,由系统自动配置,开发者不需要做配置。

    好了,介绍完family后这个family结构的主要作用就比较清晰了,family就是描述我们将要如何使用NETLINK_GENERICpayload,以及确定Netlink头部的type信息和genl netlink头部的version信息,你也可以理解为如何定义在NETLINK_GENERIC之上的一种协议。

1.3 genl_ops 结构体 

    首先说下这个结构体的作用,从名字也能看出这是一个操作(operation),下面使用operationgenl_ops的含义相同。对什么的操作呢?当然是对我们的发送的用户数据了,其实就是定义当我们收到数据该怎么处理的动作。可以理解为回调函数(成员doit就是)。但是为什么要封装成一个结构,因为NETLINK_GENERIC对回调动作提供了更加细粒度的控制。从图2中我们可以看到,NETLINK_GENERIC消息中在family head中有cmd字段,这个字段就是提供给用户对不通cmd的消息采取不同的操作(operation)用的。NETLINK_GENERIC提供更细粒度的报文回调操作还不仅体现在cmd字段。我们之前说了用户数据是由attr组成的,那么这里genl_ops就能够再调用回调函数前对各个attr进行过滤,这是通过其policy字段完成的。下面我们看详细定义。

点击(此处)折叠或打开

  1. struct genl_ops {
  2.  u8 cmd;
  3.  unsigned int flags;
  4.  struct nla_policy *policy;
  5.  int (*doit)(structsk_buff*skb, struct genl_info *info);
  6.  int (*dumpit)(structsk_buff*skb, struct netlink_callback *cb);
  7.  struct list_head ops_list;
  8. };

(1) cmd: 命令名用于识别各genl_ops,根据数据包中的cmd字段会调用同名的operation处理。

(2) flag: 各种设置属性连接在需要admin特权级别时使用GENL_ADMIN_PERM

(3) policy定义了attr规则如果此指针非空genl在触发事件处理程序之前会使用这个字段来对帧中的attr做校验nlmsg_parse函数该字段可以为空表示在触发事件处理程序之前不做校验 policy是一个struct nla_policy的数组struct nla_policy结构体表示如下: 

点击(此处)折叠或打开

  1. struct nla_policy
  2. {
  3.    u16 type;
  4.    u16 len;
  5. };

        其中type字段表示attr中的数据类型这里一定不要搞混,这里的typeattr中的type可不是一回事,attr中的type是用户自己定义的类型,只有用户自己能够理解。而这里的typeattr中的value中内容的类型。   可被配置为 

1) NLA_UNSPEC--未定义 

2) NLA_U8, NLA_U16, NLA_U32, NLA_U648bits, 16bits, 32bits, 64bits的无符号整型 

3) NLA_STRING--字符串 

4) NLA_NUL_STRING--空终止符字符串 

5) NLA_NESTED--attr 

     len字段的意思是如果在type字段配置的是字符串有关的值要把len设置为字符串的最大长度不包含结尾的'\0'如果type字段未设置或被设置为NLA_UNSPEC那么这里要设置为attrpayload部分的长度 

(4) doit:这是一个回调函数。在generic netlink收到数据时触发,运行在进程上下文。doit传入两个参数,skb为触发此回调函数的socket buffer。第二个参数是一个genl_info结构体,当内核调用回调函数时这个结构体会被填充,定义如下:

点击(此处)折叠或打开

  1. struct genl_info
  2. {
  3.  u32 snd_seq;
  4.  u32 snd_pid;
  5.  struct nlmsghdr *nlhdr;
  6.  struct genlmsghdr *genlhdr;
  7.  void *userhdr;
  8. struct nlattr ** attrs;
  9. };

1) snd_seq:发送序号

2) snd_pid:发送客户端的PID  

3) nlhdrnetlink header的指针

4) genlmsghdrgenl头部的指针(即family头部);

5) userhdr:用户自定义头部指针

6) attrsattrs,如果定义了genl_ops->policy这里的attrs是被policy过滤以后的结果。在完成了操作以后,如果执行正确,返回0;否则,返回一个负数。负数的返回值会触发NLMSG_ERROR消息。当genl_opsflag标志被添加了NLMSG_ERROR时,即使doit返回0,也会触发NLMSG_ERROR消息

(5) dumpit这是一个回调函数,当genl_opsflag标志被添加了NLM_F_DUMP以后,每次收到genl消息即会回触发这个函数。dumpitdoit的区别是:dumpit的第一个参数skb不会携带从客户端发来的数据。相反地,开发者应该在skb中填入需要传给客户端的数据,然后,并skb的数据长度(可以用skb->lenreturnskb中携带的数据会被自动送到客户端。只要dumpit的返回值大于0dumpit函数就会再次被调用,并被要求在skb中填入数据。当服务端没有数据要传给客户端时,dumpit要返回0。如果函数中出错,要求返回一个负值。关于doitdumpit的触发过程,可以查看源码中的genl_rcv_msg函数。

6) ops_list为私有字段,由系统自动配置,开发者不需要做配置。

介绍完这些数据结构,我们总体看一下genl netlink协议由里到外的数据格式,以及于不同结构和操作函数的关系,如图3所示。

3

2. 客户端服务端操作流程

服务端的操作步骤如图4所示。

4

客户端的操作路程如图5所示

5

3. 服务端操作

3.1为注册family做准备

预定义attr类型

点击(此处)折叠或打开

  1. /* attribute type */
  2.   enum {
  3.         EXMPL_A_UNSPEC,
  4.         EXMPL_A_MSG,
  5.         __EXMPL_A_MAX,
  6.   };
  7. #define EXMPL_A_MAX (__EXMPL_A_MAX - 1)

   我们消息要使用的类型,我们仅仅使用EXMPL_A_MSG这一个消息EXMPL_A_MAX代表消息类型的个数。

3.2 为注册operation做准备

1. 定义cmd类型

点击(此处)折叠或打开

  1. enum {
  2.         EXMPL_C_UNSPEC,
  3.         EXMPL_C_ECHO,
  4.         __EXMPL_C_ECHO,
  5.   };
  6. #define EXMPL_C_MAX (__EXMPL_C_MAX - 1)
     我们只使用EXMPL_C_ECHO这一种命令,EXMPL_C_MAX为cmd的种类个数。

2. 定义policy

     要提供给operation用的是一个policy数组,数组的个数要是我们之前定义的attr的数量,为什么呢?因为要对每个attr对应提供的policy。那attrpolicy怎么对应起来呢?答案是通过下标,假如有attr A,则其对应的policy即为policys[A],所以我们定义的policy数组如下:

点击(此处)折叠或打开

  1. static struct nla_policy exmpl_genl_policy[EXMPL_A_MAX + 1] = {
  2.         [EXMPL_A_MSG] = { .type = NLA_NUL_STRING },
  3.   };

(可以根据attrpolicy的对应关系想一下为什么policy数组的大小要设置为“attr个数+1”)

3.3 定义family

点击(此处)折叠或打开

  1. static struct genl_family exmpl_gnl_family = {
  2.        .id = GENL_ID_GENERATE, //标示我们要控制器给我们分配
  3.        .hdrsize = 0, //我们不需要用户数据头部
  4.        .name = "myfamily", //我们的family名字
  5.        .version = 1,
  6.        .maxattr = EXMPL_A_MAX, //在我们的协议中可能用到的消息类型数
  7.   };

3.4 定义operation

点击(此处)折叠或打开

  1. struct genl_ops exmpl_gnl_ops_echo = {
  2.         .cmd = EXMPL_C_ECHO, //我们只关心cmd为EXMPL_C_ECHO的消息
  3.         .flags = 0,
  4.         .policy = exmpl_genl_policy,
  5.         .doit = genl_recv_doit, //后面回调函数详细分析
  6.         .dumpit = NULL,
  7.   };

3.5 注册family

点击(此处)折叠或打开

  1. state = genl_register_family(&exmpl_gnl_family);

3.6 注册operation

点击(此处)折叠或打开

  1. state = genl_register_ops(&exmpl_gnl_family, &exmpl_gnl_ops_echo);

3.7 定义回调函数

    下面需要定义genl_ops结构中的doit回调函数。我们的回调函数主要做的就是将客户端传来的数据打印出来,并向客户端回发一个字符串。所以回调函数的重点就是如何根据genl netlink报文的格式收到的客户端信息进行解析,以及如何构造回发报文。参考图3,下面程序并不难理解。

点击(此处)折叠或打开

  1. int genl_recv_doit(struct sk_buff *skb, struct genl_info *info)
  2. {
  3.     struct nlmsghdr *nlhdr;
  4.     struct genlmsghdr *genlhdr;
  5.     struct nlattr *nla;
  6.     int len;
  7.     char* str;
  8.     char* data = "I am from kernel!"; //要回发给客户端的数据
  9.     int state = 0;
  10.     nlhdr = nlmsg_hdr(skb); //获取netlink首部
  11.     genlhdr = nlmsg_data(nlhdr); //获取genl netlink首部
  12.     nla = genlmsg_data(genlhdr); //获取genl_netlink的payload
  13.     printk ("received\n");
  14.     str = (char *)NLA_DATA(nla); //用户数据是一个attr,我们要获取其value
  15.     printk("%s\n",str);
  16.     len = stringlength(data);
  17.     //向客户端回发数据
  18.     if ( (state = genl_msg_send_to_user(data, len, nlhdr->nlmsg_pid)) <0 )
  19.     {
  20.         printk(KERN_ERR "genl_msg_send_to_user error!");
  21.         return 1;
  22.     }
  23.     return 0;
  24. }

   注意,这里我们并没有用genl_info结构中的信息,genl_info中包含了很多系统为我们已经解析填充过的报文字段,祥见genl_info结构的定义,其中获取payload中的attr可以直接通过genl_info获取,但是我们还是选择从最原始的报文一段段的解析获取,就是为了让大家更加了解报文的结构。

另外需要特别提醒的是接收回调函数同Netlink回调函数一样,用户发往内核是同步的,这个回调函数执行结束用户态的发送函数才能够返回。

在一个注意回发给用户的pid一定不要错了,要是从Netlink消息头中获取的pid,其含义在之前Netlink博文中已经介绍过了。

   下面看回发客户端的函数:

点击(此处)折叠或打开

  1. int genl_msg_send_to_user(void *data, int len, pid_t pid)
  2. {
  3.     struct sk_buff *skb;
  4.     size_t size;
  5.     void *head;
  6.     int rc;
  7.     printk("begin send to user\n");
  8.     size = nla_total_size(len); /* total length of attribute including padding */
  9.     //构造netlink头部和genl netlink头部
  10.     rc = genl_msg_prepare_usr_msg(EXMPL_C_ECHO, size, pid, &skb);
  11.     printk("genl_msg_prepare_usr_msg\n");
  12.     if (rc) {
  13.         return rc;
  14.     }
  15.     //构造genl netlink的payload
  16.     rc = genl_msg_mk_usr_msg(skb, EXMPL_A_MSG, data, len);
  17.     printk("genl_msg_mk_usr_msg\n");
  18.     if (rc) {
  19.         kfree_skb(skb);
  20.         return rc;
  21.    }
  22.     rc = genlmsg_unicast(&init_net, skb, pid); //向客户端回发数据
  23.     printk("send end....\n");
  24.     if (rc < 0) {
  25.         return rc;
  26.     }
  27.     return 0;
  28. }

三个参数的含义分别为data: 发送数据缓存,len: 数据长度,单位:bytepid: 发送到的客户端pid。这个函数的重点就是构造回发的数据包,这个构造过程通过两个函数完成。具体如下:

点击(此处)折叠或打开

  1. static inline int genl_msg_prepare_usr_msg(u8 cmd, size_t size, pid_t pid, struct sk_buff **skbp)
  2. {
  3.     struct sk_buff *skb;
  4.     /* create a new netlink msg */
  5.     skb = genlmsg_new(size, GFP_KERNEL);
  6.     if (skb == NULL) {
  7.         return -ENOMEM;
  8.     }
  9.     /* Add a new netlink message to an skb */
  10.     genlmsg_put(skb, pid, 0, &exmpl_gnl_family, 0, cmd);
  11.     *skbp = skb;
  12.     return 0;
  13. }

    参数含义分别为:cmd : genl_opscmdpid:客户端的pid; size : gen_netlink用户数据的长度(包括用户定义的首部),这个函数主要用来构造netlink的头部以及genl netlink的头部,以及给用户payload分配空间。需要前两个参数,是因为这两个字段要填充到报文中,见图2,最后一个参数用来分配空间。

点击(此处)折叠或打开

  1. static inline int genl_msg_mk_usr_msg(struct sk_buff *skb, int type, void *data, int len)
  2. {
  3.     int rc;
  4.     /* add a netlink attribute to a socket buffer */
  5.     if ((rc = nla_put(skb, type, len, data)) != 0) {
  6.         return rc;
  7.     }
  8.     return 0;
  9. }

这个函数主要用来填充用户数据,由于用户数据是一个attr结构(TLV),所以后三个参数分别就是typevaluelength

4. 客户端操作

4.1 创建socket

点击(此处)折叠或打开

  1. sock_fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_GENERIC);

4.2 bind socket

点击(此处)折叠或打开

  1. struct sockaddr_nl src_addr, dest_addr;
  2. // To prepare binding
  3. memset(&src_addr, 0, sizeof(src_addr));
  4. src_addr.nl_family = AF_NETLINK;
  5. src_addr.nl_pid = 1234; //我们选用的pid
  6. src_addr.nl_groups = 0;
  7. //Bind
  8. retval = bind(sock_fd, (struct sockaddr*)&src_addr, sizeof(src_addr));

4.3 获取family id

点击(此处)折叠或打开

  1. family_id = genl_get_family_id(sock_fd ,"myfamily");

    其中myfamily是我们之前再服务端注册的family namegenl_get_family_id的定义如下:

点击(此处)折叠或打开

  1. static int genl_get_family_id(int sock_fd, char *family_name)
  2. {
  3.     msgtemplate_t ans;
  4.     int id, rc;
  5.     struct nlattr *na;
  6.     int rep_len;
  7.     //通过发送genl netlink消息获取family id
  8.     rc = genl_send_msg(sock_fd, GENL_ID_CTRL, 0, CTRL_CMD_GETFAMILY, 1,
  9.                     CTRL_ATTR_FAMILY_NAME, (void *)family_name,
  10.                     strlen(family_name)+1);
  11.     //接收返回消息,其中含有family id
  12.     rep_len = recv(sock_fd, &ans, sizeof(ans), 0);
  13.     if (rep_len < 0) {
  14.         return 1;
  15.     }
  16. if (ans.nlh.nlmsg_type == NLMSG_ERROR || !NLMSG_OK((&ans.nlh), rep_len))
  17. {
  18.         return 1;
  19.     }
  20.     na = (struct nlattr *) GENLMSG_DATA(&ans);
  21.     na = (struct nlattr *) ((char *) na + NLA_ALIGN(na->nla_len));
  22.     if (na->nla_type == CTRL_ATTR_FAMILY_ID) {
  23.         id = *(__u16 *) NLA_DATA(na);
  24.     } else {
  25.         id = 0;
  26.     }
  27.     return id;
  28. }

    由于family id的获取也是需要发送genl netlink消息获取的,所以我们使用稍后发送genl netlink数据同样的函数。只是这里不是向服务端发送消息,而是向控制器发送。

    这个函数用到的msgtemplate_t是我们自己定义的一个对Netlinkgenl netlink头部的封装,定义如下:

点击(此处)折叠或打开

  1. typedef struct msgtemplate {
  2.     struct nlmsghdr nlh;
  3.     struct genlmsghdr gnlh;
  4.     char data[MAX_MSG_SIZE];
  5. } msgtemplate_t;

4.4 向服务端发送消息

    这个函数的参数比较多,不过并不难理解,可以想一下假如自己写这么一个函数需要什么信息。首先,用户发送的是一个attr,所以肯定需要用户数据的typelengthvalue;其次对比图2genl netlink消息的格式,我们需要填写pidtypefamily id)、cmdgenl_ops.cmd)、versionfamily.version);最后发送消息当然还需要socket。所以所需参数就是如下这些:

1) sock_fd: 客户端socket

2) family_id: family id

3) nlmsg_pid: 客户端pid

4) genl_cmd: 命令类型

5) genl_version: genl版本号

6) nla_type: netlink attr类型

7) nla_data: 发送的数据

8) nla_len: 发送数据长度

点击(此处)折叠或打开

  1. int genl_send_msg(int sock_fd, u_int16_t family_id, u_int32_t nlmsg_pid,
  2.         u_int8_t genl_cmd, u_int8_t genl_version, u_int16_t nla_type,
  3.         void *nla_data, int nla_len)
  4. {
  5.     struct nlattr *na;
  6.     struct sockaddr_nl dst_addr;
  7.     int r, buflen;
  8.     char *buf;
  9.     msgtemplate_t msg;
  10.     //0保留给控制器使用,其实就是GENL_ID_GENERATE,注册family才要
  11.     if (family_id == 0) {
  12.         return 0;
  13.     }
  14.     //构造netlink头部
  15.     msg.nlh.nlmsg_len = NLMSG_LENGTH(GENL_HDRLEN);
  16.     msg.nlh.nlmsg_type = family_id;
  17.     //如果消息中有NLM_F_REQUEST标记位,说明这是一个请求消息。所有从用户空间到内核空间的消息都要设置该位,否则内核将向用户返回一个EINVAL无效参数的错误
  18.     msg.nlh.nlmsg_flags = NLM_F_REQUEST;
  19.     msg.nlh.nlmsg_seq = 0;
  20.     msg.nlh.nlmsg_pid = nlmsg_pid;
  21.     //构造genl_netlink头部
  22.     msg.gnlh.cmd = genl_cmd;
  23.     msg.gnlh.version = genl_version;
  24.     na = (struct nlattr *) GENLMSG_DATA(&msg);
  25.     na->nla_type = nla_type;
  26.     na->nla_len = nla_len + 1 + NLA_HDRLEN;
  27.     memcpy(NLA_DATA(na), nla_data, nla_len);
  28.     msg.nlh.nlmsg_len += NLMSG_ALIGN(na->nla_len);
  29.     buf = (char *) &msg;
  30.     buflen = msg.nlh.nlmsg_len ;
  31.     //构造目的地址
  32.     memset(&dst_addr, 0, sizeof(dst_addr));
  33.     dst_addr.nl_family = AF_NETLINK;
  34.     dst_addr.nl_pid = 0; //向内核发送,目的pid为0
  35.     dst_addr.nl_groups = 0; //单播
  36.     while ((r = sendto(sock_fd, buf, buflen, 0, (struct sockaddr *) &dst_addr
  37.             , sizeof(dst_addr))) < buflen) {
  38.         if (r > 0) {
  39.             buf += r;
  40.             buflen -= r;
  41.         } else if (errno != EAGAIN) {
  42.             return -1;
  43.         }
  44.     }
  45.     return 0;
  46. }

   其实函数的关机问题还是如何去构造genl netlink格式的报文。

4.5 从服务端接收消息

    有的人可能一直对这个family id再报文中的作用比较疑惑,也就是Netlink头部的type字段,在这个函数中就能比较清晰的看出其作用了。其实如果把pid比作ip地址的话,那么family就可以看作是端口号。设想一下我们的用户进程使用pid1234向内核发送了一个消息,然后等待接收。这时内核恰好有一个服务使用其他Netlink协议,比如自己定义的NETLINK_TEST也向pid1234发送一个消息,我们的进程能收到吗?答案是肯定的,那我们怎么过滤这些其他协议的消息呢?这就是通过消息中的type字段。

点击(此处)折叠或打开

  1. void genl_rcv_msg(int family_id, int sock_fd, char *buf)
  2. {
  3.     int ret;
  4.     struct msgtemplate msg;
  5.     struct nlattr *na;
  6.     ret = recv(sock_fd, &msg, sizeof(msg), 0);
  7.     if (ret < 0) {
  8.         return;
  9.     }
  10.     printf("received length %d\n", ret);
  11.     if (msg.nlh.nlmsg_type == NLMSG_ERROR || !NLMSG_OK((&msg.nlh), ret)) {
  12.         return ;
  13.     }
  14.     //检验一下是不是我们需要的
  15.     if (msg.nlh.nlmsg_type == family_id && family_id != 0) {
  16.         na = (struct nlattr *) GENLMSG_DATA(&msg);
  17.         strcpy(buf,(char *)NLA_DATA(na));
  18.     }
  19. }

最后附上这个genl netlink的完整例子代码。

5. Kernel代码(server)

点击(此处)折叠或打开

  1. #include <linux/module.h>
  2. #include <linux/timer.h>
  3. #include <linux/time.h>
  4. #include <linux/types.h>
  5. #include <net/sock.h>
  6. #include <net/genetlink.h>
  7. #define MAXLEN 256
  8. #define NLA_DATA(na) ((void *)((char *)(na) + NLA_HDRLEN))
  9.   /* attribute type */
  10.   enum {
  11.         EXMPL_A_UNSPEC,
  12.         EXMPL_A_MSG,
  13.         __EXMPL_A_MAX,
  14.   };
  15. #define EXMPL_A_MAX (__EXMPL_A_MAX - 1)
  16.   /* commands */
  17.   enum {
  18.         EXMPL_C_UNSPEC,
  19.         EXMPL_C_ECHO,
  20.         __EXMPL_C_ECHO,
  21.   };
  22. #define EXMPL_C_MAX (__EXMPL_C_MAX - 1)

  23. int stringlength(char *s);
  24. int genl_recv_doit(struct sk_buff *skb, struct genl_info *info);
  25. static inline int genl_msg_prepare_usr_msg(u8 cmd, size_t size, pid_t pid, struct sk_buff **skbp);
  26. static inline int genl_msg_mk_usr_msg(struct sk_buff *skb, int type, void *data, int len);
  27. int genl_msg_send_to_user(void *data, int len, pid_t pid);

  28.  static struct genl_family exmpl_gnl_family = {
  29.        .id = GENL_ID_GENERATE,
  30.        .hdrsize = 0,
  31.        .name = "myfamily",
  32.        .version = 1,
  33.        .maxattr = EXMPL_A_MAX,
  34.   };
  35.   /* attribute policy */
  36.   static struct nla_policy exmpl_genl_policy[EXMPL_A_MAX + 1] = {
  37.         [EXMPL_A_MSG] = { .type = NLA_NUL_STRING },
  38.   };
  39.   /* operation definition */
  40.   struct genl_ops exmpl_gnl_ops_echo = {
  41.         .cmd = EXMPL_C_ECHO,
  42.         .flags = 0,
  43.         .policy = exmpl_genl_policy,
  44.         .doit = genl_recv_doit,
  45.         .dumpit = NULL,
  46.   };

  47. int stringlength(char *s)
  48. {
  49.     int slen = 0;
  50.     for(; *s; s++)
  51.     {
  52.         slen++;
  53.     }
  54.     return slen;
  55. }

  56. /*
  57. * genl_msg_prepare_usr_msg : 构建netlink及gennetlink首部
  58. * @cmd : genl_ops的cmd
  59. * @size : gen_netlink用户数据的长度(包括用户定义的首部)
  60. */
  61. static inline int genl_msg_prepare_usr_msg(u8 cmd, size_t size, pid_t pid, struct sk_buff **skbp)
  62. {
  63.     struct sk_buff *skb;
  64.     /* create a new netlink msg */
  65.     skb = genlmsg_new(size, GFP_KERNEL);
  66.     if (skb == NULL) {
  67.         return -ENOMEM;
  68.     }
  69.     /* Add a new netlink message to an skb */
  70.     genlmsg_put(skb, pid, 0, &exmpl_gnl_family, 0, cmd);
  71.     *skbp = skb;
  72.     return 0;
  73. }

  74. /*
  75. * 添加用户数据,及添加一个netlink addribute
  76. *@type : nlattr的type
  77. *@len : nlattr中的len
  78. *@data : 用户数据
  79. */
  80. static inline int genl_msg_mk_usr_msg(struct sk_buff *skb, int type, void *data, int len)
  81. {
  82.     int rc;
  83.     /* add a netlink attribute to a socket buffer */
  84.     if ((rc = nla_put(skb, type, len, data)) != 0) {
  85.         return rc;
  86.     }
  87.     return 0;
  88. }

  89. /**
  90. * genl_msg_send_to_user - 通过generic netlink发送数据到netlink
  91. *
  92. * @data: 发送数据缓存
  93. * @len: 数据长度 单位:byte
  94. * @pid: 发送到的客户端pid
  95. */
  96. int genl_msg_send_to_user(void *data, int len, pid_t pid)
  97. {
  98.     struct sk_buff *skb;
  99.     size_t size;
  100.     void *head;
  101.     int rc;
  102.     printk("begin send to user\n");
  103.     size = nla_total_size(len); /* total length of attribute including padding */
  104.     rc = genl_msg_prepare_usr_msg(EXMPL_C_ECHO, size, pid, &skb);
  105.     printk("genl_msg_prepare_usr_msg\n");
  106.     if (rc) {
  107.         return rc;
  108.     }
  109.     rc = genl_msg_mk_usr_msg(skb, EXMPL_A_MSG, data, len);
  110.     printk("genl_msg_mk_usr_msg\n");
  111.     if (rc) {
  112.         kfree_skb(skb);
  113.         return rc;
  114.    }
  115.     printk("pid :%d",pid);
  116.     rc = genlmsg_unicast(&init_net, skb, pid);
  117.     printk("send end....\n");
  118.     if (rc < 0) {
  119.         return rc;
  120.     }
  121.     return 0;
  122. }

  123. int genl_recv_doit(struct sk_buff *skb, struct genl_info *info)
  124. {
  125.     struct nlmsghdr *nlhdr;
  126.     struct genlmsghdr *genlhdr;
  127.     struct nlattr *nla;
  128.     int len;
  129.     char* str;
  130.     char* data = "I am from kernel!";
  131.     int state = 0;
  132.     nlhdr = nlmsg_hdr(skb);
  133.     genlhdr = nlmsg_data(nlhdr);
  134.     nla = genlmsg_data(genlhdr);
  135.     printk ("received\n");
  136.     str = (char *)NLA_DATA(nla);
  137.     printk("%s\n",str);
  138.     len = stringlength(data);
  139.     if ( (state = genl_msg_send_to_user(data, len, nlhdr->nlmsg_pid)) <0 )
  140.     {
  141.         printk(KERN_ERR "genl_msg_send_to_user error!");
  142.         return 1;
  143.     }
  144.     return 0;
  145. }

  146. int genetlink_init(void)
  147. {
  148.     int state=0;
  149.     state = genl_register_family(&exmpl_gnl_family);
  150.     if(state)
  151.     {
  152.        printk(KERN_ERR "genl_register_family error!!!\n");
  153.        return 1;
  154.     }
  155.     state = genl_register_ops(&exmpl_gnl_family, &exmpl_gnl_ops_echo);
  156.     if(state)
  157.     {
  158.         printk(KERN_ERR "genl_register_ops error!!!");
  159.         return 1;
  160.     }
  161.     printk(KERN_ERR "gennetlink register success!!!\n");
  162.     return 0;
  163. }
  164. void genetlink_exit(void)
  165. {
  166.     genl_unregister_family(&exmpl_gnl_family);
  167.     printk(KERN_ERR "gennetlink unregister.....\n");
  168. }
  169. module_init(genetlink_init);
  170. module_exit(genetlink_exit);
  171. MODULE_AUTHOR("yilong");
  172. MODULE_LICENSE("GPL");

附上Makefile文件:

点击(此处)折叠或打开

  1. ifneq ($(KERNELRELEASE),)
  2. obj-m :=gen_netl.o
  3. else
  4. KERNELDIR ?=/lib/modules/$(shell uname -r)/build
  5. PWD :=$(shell pwd)
  6. default:
  7. $(MAKE) -C $(KERNELDIR) M=$(PWD) modules
  8. endif

6. 用户态代码(client

点击(此处)折叠或打开

  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <unistd.h>
  4. #include <sys/socket.h>
  5. #include <string.h>
  6. #include <errno.h>
  7. #include <linux/genetlink.h>

  8. #define MAX_MSG_SIZE 256
  9. #define GENLMSG_DATA(glh) ((void *)(NLMSG_DATA(glh) + GENL_HDRLEN))
  10. #define NLA_DATA(na) ((void *)((char *)(na) + NLA_HDRLEN))
  11. /* attribute type */
  12.   enum {
  13.         EXMPL_A_UNSPEC,
  14.         EXMPL_A_MSG,
  15.         __EXMPL_A_MAX,
  16.   };
  17.   #define EXMPL_A_MAX (__EXMPL_A_MAX - 1)
  18.   /* commands */
  19.   enum {
  20.         EXMPL_C_UNSPEC,
  21.         EXMPL_C_ECHO,
  22.         __EXMPL_C_ECHO,
  23.   };
  24.   #define EXMPL_C_MAX (__EXMPL_C_MAX - 1)

  25. int genl_send_msg(int sock_fd, u_int16_t family_id, u_int32_t nlmsg_pid,
  26.         u_int8_t genl_cmd, u_int8_t genl_version, u_int16_t nla_type,
  27.         void *nla_data, int nla_len);
  28. static int genl_get_family_id(int sock_fd, char *family_name);
  29. void genl_rcv_msg(int family_id, int sock_fd, char *buf);

  30. typedef struct msgtemplate {
  31.     struct nlmsghdr nlh;
  32.     struct genlmsghdr gnlh;
  33.     char data[MAX_MSG_SIZE];
  34. } msgtemplate_t;

  35. static int genl_get_family_id(int sock_fd, char *family_name)
  36. {
  37.     msgtemplate_t ans;
  38.     int id, rc;
  39.     struct nlattr *na;
  40.     int rep_len;
  41.     rc = genl_send_msg(sock_fd, GENL_ID_CTRL, 0, CTRL_CMD_GETFAMILY, 1,
  42.                     CTRL_ATTR_FAMILY_NAME, (void *)family_name,
  43.                     strlen(family_name)+1);
  44.     rep_len = recv(sock_fd, &ans, sizeof(ans), 0);
  45.     if (rep_len < 0) {
  46.         return 1;
  47.     }
  48.     if (ans.nlh.nlmsg_type == NLMSG_ERROR || !NLMSG_OK((&ans.nlh), rep_len))
  49.     {
  50.         return 1;
  51.     }
  52.     na = (struct nlattr *) GENLMSG_DATA(&ans);
  53.     na = (struct nlattr *) ((char *) na + NLA_ALIGN(na->nla_len));
  54.     if (na->nla_type == CTRL_ATTR_FAMILY_ID) {
  55.         id = *(__u16 *) NLA_DATA(na);
  56.     } else {
  57.         id = 0;
  58.     }
  59.     return id;
  60. }

  61. /**
  62. * genl_send_msg - 通过generic netlink给内核发送数据
  63. *
  64. * @sock_fd: 客户端socket
  65. * @family_id: family id
  66. * @nlmsg_pid: 客户端pid
  67. * @genl_cmd: 命令类型
  68. * @genl_version: genl版本号
  69. * @nla_type: netlink attr类型
  70. * @nla_data: 发送的数据
  71. * @nla_len: 发送数据长度
  72. *
  73. * return:
  74. * 0: 成功
  75. * -1: 失败
  76. */
  77. int genl_send_msg(int sock_fd, u_int16_t family_id, u_int32_t nlmsg_pid,
  78.         u_int8_t genl_cmd, u_int8_t genl_version, u_int16_t nla_type,
  79.         void *nla_data, int nla_len)
  80. {
  81.     struct nlattr *na;
  82.     struct sockaddr_nl dst_addr;
  83.     int r, buflen;
  84.     char *buf;
  85.     msgtemplate_t msg;
  86.     if (family_id == 0) {
  87.         return 0;
  88.     }
  89.     msg.nlh.nlmsg_len = NLMSG_LENGTH(GENL_HDRLEN);
  90.     msg.nlh.nlmsg_type = family_id;
  91.     msg.nlh.nlmsg_flags = NLM_F_REQUEST;
  92.     msg.nlh.nlmsg_seq = 0;
  93.     msg.nlh.nlmsg_pid = nlmsg_pid;
  94.     msg.gnlh.cmd = genl_cmd;
  95.     msg.gnlh.version = genl_version;
  96.     na = (struct nlattr *) GENLMSG_DATA(&msg);
  97.     na->nla_type = nla_type;
  98.     na->nla_len = nla_len + 1 + NLA_HDRLEN;
  99.     memcpy(NLA_DATA(na), nla_data, nla_len);
  100.     msg.nlh.nlmsg_len += NLMSG_ALIGN(na->nla_len);
  101.     buf = (char *) &msg;
  102.     buflen = msg.nlh.nlmsg_len ;
  103.     memset(&dst_addr, 0, sizeof(dst_addr));
  104.     dst_addr.nl_family = AF_NETLINK;
  105.     dst_addr.nl_pid = 0;
  106.     dst_addr.nl_groups = 0;
  107.     while ((r = sendto(sock_fd, buf, buflen, 0, (struct sockaddr *) &dst_addr
  108.             , sizeof(dst_addr))) < buflen) {
  109.         if (r > 0) {
  110.             buf += r;
  111.             buflen -= r;
  112.         } else if (errno != EAGAIN) {
  113.             return -1;
  114.         }
  115.     }
  116.     return 0;
  117. }

  118. void genl_rcv_msg(int family_id, int sock_fd, char *buf)
  119. {
  120.     int ret;
  121.     struct msgtemplate msg;
  122.     struct nlattr *na;
  123.     ret = recv(sock_fd, &msg, sizeof(msg), 0);
  124.     if (ret < 0) {
  125.         return;
  126.     }
  127.     printf("received length %d\n", ret);
  128.     if (msg.nlh.nlmsg_type == NLMSG_ERROR || !NLMSG_OK((&msg.nlh), ret)) {
  129.         return ;
  130.     }
  131.     if (msg.nlh.nlmsg_type == family_id && family_id != 0) {
  132.         na = (struct nlattr *) GENLMSG_DATA(&msg);
  133.         strcpy(buf,(char *)NLA_DATA(na));
  134.     }
  135. }

  136. int main(int argc, char* argv[])
  137. {
  138.     struct sockaddr_nl src_addr, dest_addr;
  139.     struct nlmsghdr *nlh = NULL;
  140.     int sock_fd, retval;
  141.     int family_id = 0;
  142.     char *data;
  143.     // Create a socket
  144.     sock_fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_GENERIC);
  145.     if(sock_fd == -1){
  146.         printf("error getting socket: %s", strerror(errno));
  147.         return -1;
  148.     }
  149.     // To prepare binding
  150.     memset(&src_addr, 0, sizeof(src_addr));
  151.     src_addr.nl_family = AF_NETLINK;
  152.     src_addr.nl_pid = 1234;
  153.     src_addr.nl_groups = 0;
  154.     //Bind
  155.     retval = bind(sock_fd, (struct sockaddr*)&src_addr, sizeof(src_addr));
  156.     if(retval < 0){
  157.         printf("bind failed: %s", strerror(errno));
  158.         close(sock_fd);
  159.         return -1;
  160.     }
  161.     family_id = genl_get_family_id(sock_fd ,"myfamily");
  162.     printf("family_id:%d\n",family_id);
  163.     data =(char*)malloc(256);
  164.     if(!data)
  165.     {
  166.         perror("malloc error!");
  167.         exit(1);
  168.     }
  169.     memset(data,0,256);
  170.     strcpy(data,"Hello you!");
  171.     retval = genl_send_msg(sock_fd, family_id, 1234,EXMPL_C_ECHO, 1, EXMPL_A_MSG,(void *)data, strlen(data)+1);
  172.     printf("send message %d\n",retval);
  173.     if(retval<0)
  174.     {
  175.         perror("genl_send_msg error");
  176.         exit(1);
  177.     }
  178.     memset(data,0,256);
  179.     genl_rcv_msg(family_id,sock_fd,data);
  180.     printf("receive:%s",data);
  181.     close(sock_fd);
  182.     return 0;
  183. }

阅读(14253) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~