Generic Netlink(genl)介绍与例子
一、Generic Netlink介绍
介绍Generic Netlink之前,不得不说Netlink。Netlink是一种灵活的并且健壮的通讯方式,可以用于kernel to user、user to kernel、kernel to kernel甚至user to user的通讯。Netlink的通道是通过Family来组织的,但随着使用越来越多,Family ID已经不够分配了,所以才有了Generic Netlink。
所以,Generic Netlink其实是对Netlink报文中的数据进行了再次封装。
二、genl架构
+---------------------+ +---------------------+| (3) application "A" | | (3) application "B" |+------+--------------+ +--------------+------+| |\ /\ /| |+-------+--------------------------------+-------+| : : | user-space=====+ : (5) kernel socket API : +================| : : | kernel-space+--------+-------------------------------+-------+| |+-----+-------------------------------+----+| (1) Netlink subsystem |+---------------------+--------------------+|+---------------------+--------------------+| (2) Generic Netlink bus |+--+--------------------------+-------+----+| | |+-------+---------+ | || (4) controller | / \+-----------------+ / \| |+------------------+--+ +--+------------------+| (3) kernel user "X" | | (3) kernel user "Y" |+---------------------+ +---------------------+genl架构图,https://wiki.linuxfoundation.org/networking/generic_netlink_howto#architectural-overview
三、genl数据包结构
要理解genl的程序,必须先了解genl数据包的结构。数据包的结构如下图所示
genl机制的数据包分了4层,用户的实际数据封装在attribute里,一个或多个attribute可以被封装在用户自定义的一个family报文里,一个family报文又被封装在genlmsg里,最后genlmsg被封装在nlmsg里,总共4层。
四、流程说明
1. 对于从user to kernel的通讯,driver必须先向内核注册一个struct genl_family,并且注册一些cmd的处理函数。这些cmd是跟某个family关联起来的。注册family的时候我们可以让内核自动为这个family分配一个ID。每个family都有一个唯一的ID,其中ID号0x10是被内核的nlctrl family所使用。当注册成功以后,如果user program向某个family发送cmd,那么内核就会回调对应cmd的处理函数。对于user program,使用前,除了要创建一个socket并绑定地址以外,还需要先通过family的名字获取family的ID。获取方法,就是向nlctrl这个family查询。详细的方法可以看后面的例子。有了family的ID以后,才能向该family发送cmd。
2.对于从kernel to user的通讯,采用的是广播的形式,只要user program监听了,都能收到。但是同样的,user program在监听前,也必须先查询到family的ID。
五、示例
网上很多例子都不是很完整,这里贴一个完整的例子源码供参考。
例子1:user到kernel,user program通过发送一个命令到内核,内核相应的模块接收到命令以后,执行对应的回调函数
kernel端:
- #include
- #include
- #include
-
- #define TEST_GENL_MSG_FROM_KERNEL "Hello from kernel space!!!"
-
- /* handler
- * message handling code goes here; return 0 on success, negative
- * values on failure
- */
- static int doc_exmpl_echo(struct sk_buff *skb, struct genl_info *info);
-
-
- /* netlink attributes */
- enum {
- DOC_EXMPL_A_UNSPEC,
- DOC_EXMPL_A_MSG,
- __DOC_EXMPL_A_MAX,
- };
- #define DOC_EXMPL_A_MAX (__DOC_EXMPL_A_MAX - 1)
-
- /* attribute policy */
- static struct nla_policy doc_exmpl_genl_policy[DOC_EXMPL_A_MAX + 1] = {
- [DOC_EXMPL_A_MSG] = { .type = NLA_NUL_STRING },
- };
-
- /* commands 定义命令类型,用户空间以此来表明需要执行的命令 */
- enum {
- DOC_EXMPL_C_UNSPEC,
- DOC_EXMPL_C_ECHO,
- __DOC_EXMPL_C_MAX,
- };
- #define DOC_EXMPL_C_MAX (__DOC_EXMPL_C_MAX - 1)
-
- /* family definition */
- static struct genl_family doc_exmpl_genl_family = {
- .id = GENL_ID_GENERATE, //这里不指定family ID,由内核进行分配
- .hdrsize = 0, //自定义的头部长度,参考genl数据包结构
- .name = "DOC_EXMPL", //这里定义family的名称,user program需要根据这个名字来找到对应的family ID。
- .version = 1,
- .maxattr = DOC_EXMPL_A_MAX,
- };
-
- /* operation definition 将命令command echo和具体的handler对应起来 */
- static struct genl_ops doc_exmpl_genl_ops_echo = {
- .cmd = DOC_EXMPL_C_ECHO,
- .flags = 0,
- .policy = doc_exmpl_genl_policy,
- .doit = doc_exmpl_echo,
- .dumpit = NULL,
- };
-
- static struct genl_multicast_group doc_exmpl_genl_mcgrp = {
- .name = "DOC_EXMPL_GRP",
- };
-
- static inline int genl_msg_prepare_usr_msg(u8 cmd, size_t size, pid_t pid, struct sk_buff **skbp)
- {
- struct sk_buff *skb;
-
- /* create a new netlink msg */
- skb = genlmsg_new(size, GFP_KERNEL);
- if (skb == NULL) {
- return -ENOMEM;
- }
-
- /* Add a new netlink message to an skb */
- genlmsg_put(skb, pid, 0, &doc_exmpl_genl_family, 0, cmd);
-
- *skbp = skb;
- return 0;
- }
-
- static inline int genl_msg_mk_usr_msg(struct sk_buff *skb, int type, void *data, int len)
- {
- int rc;
-
- /* add a netlink attribute to a socket buffer */
- if ((rc = nla_put(skb, type, len, data)) != 0) {
- return rc;
- }
- return 0;
- }
-
- /**
- * genl_msg_send_to_user - 通过generic netlink发送数据到netlink
- *
- * @data: 发送数据缓存
- * @len: 数据长度 单位:byte
- * @pid: 发送到的客户端pid
- *
- * return:
- * 0: 成功
- * -1: 失败
- */
- int genl_msg_send_to_user(void *data, int len, pid_t pid)
- {
- struct sk_buff *skb;
- size_t size;
- void *head;
- int rc;
-
- size = nla_total_size(len); /* total length of attribute including padding */
-
- rc = genl_msg_prepare_usr_msg(DOC_EXMPL_C_ECHO, size, pid, &skb);
- if (rc) {
- return rc;
- }
-
- rc = genl_msg_mk_usr_msg(skb, DOC_EXMPL_A_MSG, data, len);
- if (rc) {
- kfree_skb(skb);
- return rc;
- }
-
- head = genlmsg_data(nlmsg_data(nlmsg_hdr(skb)));
-
- rc = genlmsg_end(skb, head);
- if (rc < 0) {
- kfree_skb(skb);
- return rc;
- }
-
- rc = genlmsg_unicast(&init_net, skb, pid);
- if (rc < 0) {
- return rc;
- }
-
- return 0;
- }
-
- //echo command handler, 命令处理函数,当接收到user program发出的命令后,这个函数会被内核调用
- static int doc_exmpl_echo(struct sk_buff *skb, struct genl_info *info)
- {
- /* message handling code goes here; return 0 on success, negative values on failure */
- struct nlmsghdr *nlhdr;
- struct genlmsghdr *genlhdr;
- struct nlattr *nlh;
- char *str;
- int ret;
-
- nlhdr = nlmsg_hdr(skb);
- genlhdr = nlmsg_data(nlhdr);
- nlh = genlmsg_data(genlhdr);
- str = nla_data(nlh);
- printk("doc_exmpl_echo get: %s\n", str);
-
- ret = genl_msg_send_to_user(TEST_GENL_MSG_FROM_KERNEL,
- strlen(TEST_GENL_MSG_FROM_KERNEL) + 1, nlhdr->nlmsg_pid);
-
- return ret;
- }
-
- static int genetlink_init(void)
- {
- int rc;
-
- /**
- * 1. Registering A Family
- * This function doesn't exist past linux 3.12
- */
- rc = genl_register_family(&doc_exmpl_genl_family);
- if (rc != 0)
- goto err_out1;
-
- rc = genl_register_ops(&doc_exmpl_genl_family, &doc_exmpl_genl_ops_echo);
- if (rc != 0)
- goto err_out2;
-
- /*
- * for multicast
- */
- rc = genl_register_mc_group(&doc_exmpl_genl_family, &doc_exmpl_genl_mcgrp);
- if (rc != 0)
- goto err_out3;
-
- LOGC("doc_exmpl_genl_mcgrp.id=%d", doc_exmpl_genl_mcgrp.id);
- LOGC("genetlink_init OK");
- return 0;
-
- err_out3:
- genl_unregister_ops(&doc_exmpl_genl_family, &doc_exmpl_genl_ops_echo);
- err_out2:
- genl_unregister_family(&doc_exmpl_genl_family);
- err_out1:
- LOGC("Error occured while inserting generic netlink example module\n");
- return rc;
- }
-
- static void genetlink_exit(void)
- {
- LOGC("Generic Netlink Example Module unloaded.");
-
- genl_unregister_mc_group(&doc_exmpl_genl_family, &doc_exmpl_genl_mcgrp);
- genl_unregister_ops(&doc_exmpl_genl_family, &doc_exmpl_genl_ops_echo);
- genl_unregister_family(&doc_exmpl_genl_family);
- }
-
user program
- #include
- #include
- #include
- #include
- #include
- #include
- #include
- #include
- #include
- #include
- #include
- #include
-
- #include
-
- #define GENLMSG_DATA(glh) ((void*)(((char*)glh) + GENL_HDRLEN))
- #define NLA_DATA(nla) ((void *)((char*)(nla) + NLA_HDRLEN))
- #define NLA_NEXT(nla,len) ((len) -= NLA_ALIGN((nla)->nla_len), \
- (struct nlattr*)(((char*)(nla)) + NLA_ALIGN((nla)->nla_len)))
- #define NLA_OK(nla,len) ((len) >= (int)sizeof(struct nlattr) && \
- (nla)->nla_len >= sizeof(struct nlattr) && \
- (nla)->nla_len <= (len))
-
- //copy from kernel driver genl_ops's cmd
- enum {
- DOC_EXMPL_C_UNSPEC,
- DOC_EXMPL_C_ECHO,
- __DOC_EXMPL_C_MAX,
- };
-
- //copy from kernel driver netlink attribute
- enum {
- DOC_EXMPL_A_UNSPEC,
- DOC_EXMPL_A_MSG,
- __DOC_EXMPL_A_MAX,
- };
-
-
- #define MESSAGE_TO_KERNEL "Hello World from user space!"
-
-
-
- /**
- * nla_attr_size - length of attribute size, NOT including padding
- * @param payload length of payload
- * @return
- */
- static inline int nla_attr_size(int payload)
- {
- return NLA_HDRLEN + payload;
- }
-
- /**
- * nla_total_size - total length of attribute including padding
- * @param payload length of payload, NOT including NLA_HDR
- */
- static inline int nla_total_size(int payload)
- {
- return NLA_ALIGN(nla_attr_size(payload));
- }
-
- static int genlmsg_open(void)
- {
- int sockfd;
- struct sockaddr_nl nladdr;
- int ret;
-
- sockfd = socket(AF_NETLINK, SOCK_RAW, NETLINK_GENERIC);
- if (sockfd < 0)
- {
- LOGC("socket: %m");
- return -1;
- }
-
- memset(&nladdr, 0, sizeof(nladdr));
- nladdr.nl_family = AF_NETLINK;
- nladdr.nl_pid = getpid();
- nladdr.nl_groups = 0xffffffff; //这个是mask值,如果family ID & nl_groups为0,
- //则这个family的广播就接收不到,所以这里设为0xffffffff就可以接收所有的family消息
-
- ret = bind(sockfd, (struct sockaddr *)&nladdr, sizeof(nladdr));
- if (ret < 0)
- {
- LOGC("bind: %m");
- ret = -1;
- goto err_out;
- }
-
- return sockfd;
-
- err_out:
- close(sockfd);
- return ret;
- }
-
- static void *genlmsg_alloc(int *size)
- {
- unsigned char *buf;
- int len;
-
- /*
- * attribute len
- * attr len = (nla_hdr + pad) + (payload(user data) + pad)
- */
- len = nla_total_size(*size);
- /*
- * family msg len,
- * but actually we have NOT custom family header
- * family msg len = family_hdr + payload(attribute)
- */
- len += 0;
- /*
- * generic netlink msg len
- * genlmsg len = (genlhdr + pad) + payload(family msg)
- */
- len += GENL_HDRLEN;
- /*
- * netlink msg len
- * nlmsg len = (nlmsghdr + pad) + (payload(genlmsg) + pad)
- */
- len = NLMSG_SPACE(len);
-
- buf = malloc(len);
- if (!buf)
- return NULL;
-
- memset(buf, 0, len);
- *size = len;
-
- return buf;
- }
-
- static void genlmsg_free(void *buf)
- {
- if (buf)
- free(buf);
- }
-
- static int genlmsg_send(int sockfd, unsigned short nlmsg_type, unsigned int nlmsg_pid,
- unsigned char genl_cmd, unsigned char genl_version,
- unsigned short nla_type, const void *nla_data, unsigned int nla_len)
- {
- struct nlmsghdr *nlh; //netlink message header
- struct genlmsghdr *glh; //generic netlink message header
- struct nlattr *nla; //netlink attribute header
-
- struct sockaddr_nl nladdr;
- unsigned char *buf;
- int len;
-
- int count;
- int ret;
-
- if ((nlmsg_type == 0) || (!nla_data) || (nla_len <= 0))
- {
- return -1;
- }
-
- len = nla_len;
- buf = genlmsg_alloc(&len);
- if (!buf)
- return -1;
-
- nlh = (struct nlmsghdr *)buf;
- nlh->nlmsg_len = len;
- nlh->nlmsg_type = nlmsg_type;
- nlh->nlmsg_flags = NLM_F_REQUEST;
- nlh->nlmsg_seq = 0;
- nlh->nlmsg_pid = nlmsg_pid;
-
- glh = (struct genlmsghdr *)NLMSG_DATA(nlh);
- glh->cmd = genl_cmd;
- glh->version = genl_version;
-
-
- nla = (struct nlattr *)GENLMSG_DATA(glh);
- nla->nla_type = nla_type;
- nla->nla_len = nla_attr_size(nla_len);
- memcpy(NLA_DATA(nla), nla_data, nla_len);
-
- memset(&nladdr, 0, sizeof(nladdr));
- nladdr.nl_family = AF_NETLINK;
-
- count = 0;
- ret = 0;
- do {
-
- ret = sendto(sockfd, &buf[count], len - count, 0,
- (struct sockaddr *)&nladdr, sizeof(nladdr));
- if (ret < 0)
- {
- if (errno != EAGAIN)
- {
- count = -1;
- goto out;
- }
- }
- else
- {
- count += ret;
- }
-
- }while (count < len);
-
-
- out:
- genlmsg_free(buf);
-
- LOGC("send return %d", count);
- return count;
- }
-
- /**
- *
- * @param sockfd generic netlink socket fd
- * @param buf the 'buf' is including the struct nlmsghdr,
- * struct genlmsghdr and struct nlattr
- * @param len size of 'buf'
- * @return >0 size of genlmsg
- * <0 error occur
- */
- static int genlmsg_recv(int sockfd, unsigned char *buf, unsigned int len)
- {
- struct sockaddr_nl nladdr;
- struct msghdr msg;
- struct iovec iov;
-
- int ret;
-
- nladdr.nl_family = AF_NETLINK;
- nladdr.nl_pid = getpid();
- nladdr.nl_groups = 0xffffffff;
-
- iov.iov_base = buf;
- iov.iov_len = len;
-
- msg.msg_name = (void *)&nladdr;
- msg.msg_namelen = sizeof(nladdr);
- msg.msg_iov = &iov;
- msg.msg_iovlen = 1;
- msg.msg_control = NULL;
- msg.msg_controllen = 0;
- msg.msg_flags = 0;
- ret = recvmsg(sockfd, &msg, 0);
- ret = ret > 0 ? ret : -1;
- LOGC("recv return %d", ret);
- return ret;
- }
-
- static int genlmsg_dispatch(struct nlmsghdr *nlmsghdr, unsigned int nlh_len,
- int nlmsg_type, int nla_type, unsigned char *buf, int *len)
- {
- struct nlmsghdr *nlh;
- struct genlmsghdr *glh;
- struct nlattr *nla;
- int nla_len;
-
- int l;
- int i;
- int ret = -1;
-
- if (!nlmsghdr || !buf || !len)
- return -1;
-
- LOGC("nlmsg_type = %d", nlmsghdr->nlmsg_type);
- if (nlmsg_type && (nlmsghdr->nlmsg_type != nlmsg_type))
- return -1;
-
- //读取到的数据流里面,可能会包含多条nlmsg
- for (nlh = nlmsghdr; NLMSG_OK(nlh, nlh_len); nlh = NLMSG_NEXT(nlh, nlh_len))
- {
- /* The end of multipart message. */
- if (nlh->nlmsg_type == NLMSG_DONE)
- {
- LOGC("get NLMSG_DONE");
- ret = 0;
- break;
- }
-
- if (nlh->nlmsg_type == NLMSG_ERROR)
- {
- LOGC("get NLMSG_ERROR");
- ret = -1;
- break;
- }
-
- glh = (struct genlmsghdr *)NLMSG_DATA(nlh);
- nla = (struct nlattr *)GENLMSG_DATA(glh); //the first attribute
- nla_len = nlh->nlmsg_len - GENL_HDRLEN; //len of attributes
- for (i = 0; NLA_OK(nla, nla_len); nla = NLA_NEXT(nla, nla_len), ++i)
- {
- //一条nlmsg里面,可能会包含多个attr
- LOGC("%d. nla->nla_type = %d", i, nla->nla_type);
- /* Match the family ID, copy the data to user */
- if (nla_type == nla->nla_type)
- {
- l = nla->nla_len - NLA_HDRLEN; //attribute里的payload就是内核返回给用户的实际数据
- *len = *len > l ? l : *len;
- memcpy(buf, NLA_DATA(nla), *len);
- ret = 0;
- break;
- }
- }
- }
-
- return ret;
- }
-
- static int genlmsg_get_family_id(int sockfd, const char *family_name)
- {
- void *buf;
- int len;
- __u16 id;
- int l;
- int ret;
-
- ret = genlmsg_send(sockfd, GENL_ID_CTRL, 0, CTRL_CMD_GETFAMILY, 1,
- CTRL_ATTR_FAMILY_NAME, family_name, strlen(family_name) + 1);
- if (ret < 0)
- return -1;
-
- len = 256;
- buf = genlmsg_alloc(&len);
- if (!buf)
- return -1;
-
- len = genlmsg_recv(sockfd, buf, len);
- if (len < 0)
- return len;
-
- id = 0;
- l = sizeof(id);
- genlmsg_dispatch((struct nlmsghdr *)buf, len, 0, CTRL_ATTR_FAMILY_ID, (unsigned char *)&id, &l);
-
- genlmsg_free(buf);
-
- return id > 0 ? id : -1;
- }
-
- static void genlmsg_close(int sockfd)
- {
- if (sockfd >= 0)
- close(sockfd);
- }
-
-
-
- #define BUF_SIZE 256
- static int test_netlink_unicast(void)
- {
- struct nlmsghdr *nlh = NULL;
- int sockfd = -1;
- unsigned char buf[BUF_SIZE];
- int len;
- int id;
- pid_t pid;
- int ret;
-
- len = BUF_SIZE;
- nlh = genlmsg_alloc(&len);
- if (!nlh)
- return -1;
-
- sockfd = genlmsg_open();
- if (sockfd < 0)
- return -1;
-
- id = genlmsg_get_family_id(sockfd, "DOC_EXMPL"); //这里必须先通过family的名字获取到family ID,名字需要与驱动里的一致
- LOGC("get family ID[%d]", id);
- if (id <= 0)
- {
- ret = -1;
- goto out;
- }
-
- pid = getpid();
- ret = genlmsg_send(sockfd, id, pid, DOC_EXMPL_C_ECHO, 1,
- DOC_EXMPL_A_MSG, MESSAGE_TO_KERNEL, strlen(MESSAGE_TO_KERNEL) + 1); //向内核发送genl消息
- if (ret < 0)
- {
- goto out;
- }
-
- ret = genlmsg_recv(sockfd, (unsigned char *)nlh, len); //等待内核的回复
- if (ret > 0)
- {
- memset(buf, 0, sizeof(buf));
- len = sizeof(buf);
- ret = genlmsg_dispatch(nlh, ret, id, DOC_EXMPL_A_MSG, buf, &len);
- if (ret == 0)
- {
- printf("get: %s\n", buf);
- }
- }
-
- out:
- genlmsg_close(sockfd);
- genlmsg_free(nlh);
-
- return ret;
- }
-
-
- int main(int argc, char *argv[])
- {
- /*
- * test netlink unicast
- */
- test_netlink_unicast();
-
- return 0;
- }
例子2:kernel到user,user program先创建一个socket并监听,内核发生某个事件以后,就可以发送一个广播,通知user program
kernel端:
- #include
- #include
- #include
-
-
- #define TEST_GENL_FAMILY_NAME "my-test-family"
- #define TEST_GENL_MCAST_GROUP_NAME "my-test-group"
- #define TEST_GENL_MSG_FROM_KERNEL "Hello from kernel space!!!"
-
- /* handler
- * message handling code goes here; return 0 on success, negative
- * values on failure
- */
- static int doc_exmpl_echo(struct sk_buff *skb, struct genl_info *info);
-
-
-
- /* netlink attributes 可以通过枚举索引找到对应的类型,用户空间应用程序要传递这样的信息 */
- enum {
- DOC_EXMPL_A_UNSPEC,
- DOC_EXMPL_A_MSG,
- __DOC_EXMPL_A_MAX,
- };
- #define DOC_EXMPL_A_MAX (__DOC_EXMPL_A_MAX - 1)
-
- /* attribute policy */
- static struct nla_policy doc_exmpl_genl_policy[DOC_EXMPL_A_MAX + 1] = {
- [DOC_EXMPL_A_MSG] = { .type = NLA_NUL_STRING },
- };
-
- /* commands 定义命令类型,用户空间以此来表明需要执行的命令 */
- enum {
- DOC_EXMPL_C_UNSPEC,
- DOC_EXMPL_C_ECHO,
- __DOC_EXMPL_C_MAX,
- };
- #define DOC_EXMPL_C_MAX (__DOC_EXMPL_C_MAX - 1)
-
- #if LINUX_VERSION_CODE < KERNEL_VERSION(3,13,0)
-
- /* family definition */
- static struct genl_family doc_exmpl_genl_family = {
- .id = GENL_ID_GENERATE, //request a new channel number, assigned by kernel, NOT driver specific
- .hdrsize = 0,
- .name = "DOC_EXMPL",
- .version = 1,
- .maxattr = DOC_EXMPL_A_MAX,
- };
-
- /* operation definition 将命令command echo和具体的handler对应起来 */
- static struct genl_ops doc_exmpl_genl_ops_echo = {
- .cmd = DOC_EXMPL_C_ECHO,
- .flags = 0,
- .policy = doc_exmpl_genl_policy,
- .doit = doc_exmpl_echo,
- .dumpit = NULL,
- };
-
- static struct genl_multicast_group doc_exmpl_genl_mcgrp = {
- .name = "DOC_EXMPL_GRP",
- };
-
- //需要在其他地方主动调用这个函数发送广播
- static int test_netlink_send(void)
- {
- struct sk_buff *skb = NULL;
- void *msg_header = NULL;
- int size;
- int rc;
-
- /* allocate memory */
- size = nla_total_size(strlen(TEST_GENL_MSG_FROM_KERNEL) + 1) + nla_total_size(0);
-
- skb = genlmsg_new(size, GFP_KERNEL);
- if (!skb)
- return -ENOMEM;
-
- /* add the genetlink message header */
- msg_header = genlmsg_put(skb, 0, 0,
- &doc_exmpl_genl_family, 0, DOC_EXMPL_C_ECHO);
- if (!msg_header)
- {
- rc = -ENOMEM;
- goto err_out;
- }
-
- /* add a DOC_EXMPL_A_MSG attribute */
- rc = nla_put_string(skb, DOC_EXMPL_A_MSG, TEST_GENL_MSG_FROM_KERNEL);
- if (rc != 0)
- goto err_out;
-
- /* finalize the message */
- genlmsg_end(skb, msg_header);
-
- //multicast is send a message to a logical group
- rc = genlmsg_multicast(skb, 0, doc_exmpl_genl_mcgrp.id, GFP_KERNEL);
- if (rc != 0 && rc != -ESRCH)
- {
- /* if NO one is waitting the message in user space,
- * genlmsg_multicast return -ESRCH
- */
- LOGC("genlmsg_multicast to user failed, return %d", rc);
-
- /*
- * attention:
- * If you NOT call genlmsg_unicast/genlmsg_multicast and error occurs,
- * call nlmsg_free(skb).
- * But if you call genlmsg_unicast/genlmsg_multicast, NO need to call
- * nlmsg_free(skb). If NOT, kernel crash.
- */
- return rc;
- }
-
- LOGC("genlmsg_multicast Success");
-
- /*
- * Attention:
- * Should NOT call nlmsg_free(skb) here. If NOT, kernel crash!!!
- */
- return 0;
-
- err_out:
- if (skb)
- nlmsg_free(skb);
- return rc;
- }
-
-
- static int genetlink_init(struct my_module_priv *ctx)
- {
- int rc;
-
- /**
- * 1. Registering A Family
- * This function doesn't exist past linux 3.12
- */
- rc = genl_register_family(&doc_exmpl_genl_family);
- if (rc != 0)
- goto err_out1;
-
- rc = genl_register_ops(&doc_exmpl_genl_family, &doc_exmpl_genl_ops_echo);
- if (rc != 0)
- goto err_out2;
-
- /*
- * for multicast
- */
- rc = genl_register_mc_group(&doc_exmpl_genl_family, &doc_exmpl_genl_mcgrp);
- if (rc != 0)
- goto err_out3;
-
- LOGC("doc_exmpl_genl_mcgrp.id=%d", doc_exmpl_genl_mcgrp.id);
- LOGC("genetlink_init OK");
- return 0;
-
- err_out3:
- genl_unregister_ops(&doc_exmpl_genl_family, &doc_exmpl_genl_ops_echo);
- err_out2:
- genl_unregister_family(&doc_exmpl_genl_family);
- err_out1:
- LOGC("Error occured while inserting generic netlink example module\n");
- return rc;
- }
-
- static void genetlink_exit(struct my_module_priv *ctx)
- {
- LOGC("Generic Netlink Example Module unloaded.");
-
- genl_unregister_mc_group(&doc_exmpl_genl_family, &doc_exmpl_genl_mcgrp);
- genl_unregister_ops(&doc_exmpl_genl_family, &doc_exmpl_genl_ops_echo);
- genl_unregister_family(&doc_exmpl_genl_family);
- }
-
- static struct my_module_work g_my_module_work = {
- .do_test_probe = genetlink_init,
- .do_test_remove = genetlink_exit,
- };
user program
- #include
- #include
- #include
- #include
- #include
- #include
- #include
- #include
- #include
- #include
- #include
- #include
-
- #include
-
- #define GENLMSG_DATA(glh) ((void*)(((char*)glh) + GENL_HDRLEN))
- #define NLA_DATA(nla) ((void *)((char*)(nla) + NLA_HDRLEN))
- #define NLA_NEXT(nla,len) ((len) -= NLA_ALIGN((nla)->nla_len), \
- (struct nlattr*)(((char*)(nla)) + NLA_ALIGN((nla)->nla_len)))
- #define NLA_OK(nla,len) ((len) >= (int)sizeof(struct nlattr) && \
- (nla)->nla_len >= sizeof(struct nlattr) && \
- (nla)->nla_len <= (len))
-
- //copy from kernel driver genl_ops's cmd
- enum {
- DOC_EXMPL_C_UNSPEC,
- DOC_EXMPL_C_ECHO,
- __DOC_EXMPL_C_MAX,
- };
-
- //copy from kernel driver netlink attribute
- enum {
- DOC_EXMPL_A_UNSPEC,
- DOC_EXMPL_A_MSG,
- __DOC_EXMPL_A_MAX,
- };
-
-
- #define MESSAGE_TO_KERNEL "Hello World from user space!"
-
-
-
- /**
- * nla_attr_size - length of attribute size, NOT including padding
- * @param payload length of payload
- * @return
- */
- static inline int nla_attr_size(int payload)
- {
- return NLA_HDRLEN + payload;
- }
-
- /**
- * nla_total_size - total length of attribute including padding
- * @param payload length of payload, NOT including NLA_HDR
- */
- static inline int nla_total_size(int payload)
- {
- return NLA_ALIGN(nla_attr_size(payload));
- }
-
- static int genlmsg_open(void)
- {
- int sockfd;
- struct sockaddr_nl nladdr;
- int ret;
-
- sockfd = socket(AF_NETLINK, SOCK_RAW, NETLINK_GENERIC);
- if (sockfd < 0)
- {
- LOGC("socket: %m");
- return -1;
- }
-
- memset(&nladdr, 0, sizeof(nladdr));
- nladdr.nl_family = AF_NETLINK;
- nladdr.nl_pid = getpid();
- nladdr.nl_groups = 0xffffffff;
-
- ret = bind(sockfd, (struct sockaddr *)&nladdr, sizeof(nladdr));
- if (ret < 0)
- {
- LOGC("bind: %m");
- ret = -1;
- goto err_out;
- }
-
- return sockfd;
-
- err_out:
- close(sockfd);
- return ret;
- }
-
- static void *genlmsg_alloc(int *size)
- {
- unsigned char *buf;
- int len;
-
- /*
- * attribute len
- * attr len = (nla_hdr + pad) + (payload(user data) + pad)
- */
- len = nla_total_size(*size);
- /*
- * family msg len,
- * but actually we have NOT custom family header
- * family msg len = family_hdr + payload(attribute)
- */
- len += 0;
- /*
- * generic netlink msg len
- * genlmsg len = (genlhdr + pad) + payload(family msg)
- */
- len += GENL_HDRLEN;
- /*
- * netlink msg len
- * nlmsg len = (nlmsghdr + pad) + (payload(genlmsg) + pad)
- */
- len = NLMSG_SPACE(len);
-
- buf = malloc(len);
- if (!buf)
- return NULL;
-
- memset(buf, 0, len);
- *size = len;
-
- return buf;
- }
-
- static void genlmsg_free(void *buf)
- {
- if (buf)
- free(buf);
- }
-
- static int genlmsg_send(int sockfd, unsigned short nlmsg_type, unsigned int nlmsg_pid,
- unsigned char genl_cmd, unsigned char genl_version,
- unsigned short nla_type, const void *nla_data, unsigned int nla_len)
- {
- struct nlmsghdr *nlh; //netlink message header
- struct genlmsghdr *glh; //generic netlink message header
- struct nlattr *nla; //netlink attribute header
-
- struct sockaddr_nl nladdr;
- unsigned char *buf;
- int len;
-
- int count;
- int ret;
-
- if ((nlmsg_type == 0) || (!nla_data) || (nla_len <= 0))
- {
- return -1;
- }
-
- len = nla_len;
- buf = genlmsg_alloc(&len);
- if (!buf)
- return -1;
-
- nlh = (struct nlmsghdr *)buf;
- nlh->nlmsg_len = len;
- nlh->nlmsg_type = nlmsg_type;
- nlh->nlmsg_flags = NLM_F_REQUEST;
- nlh->nlmsg_seq = 0;
- nlh->nlmsg_pid = nlmsg_pid;
-
- glh = (struct genlmsghdr *)NLMSG_DATA(nlh);
- glh->cmd = genl_cmd;
- glh->version = genl_version;
-
-
- nla = (struct nlattr *)GENLMSG_DATA(glh);
- nla->nla_type = nla_type;
- nla->nla_len = nla_attr_size(nla_len);
- memcpy(NLA_DATA(nla), nla_data, nla_len);
-
- memset(&nladdr, 0, sizeof(nladdr));
- nladdr.nl_family = AF_NETLINK;
-
- count = 0;
- ret = 0;
- do {
-
- ret = sendto(sockfd, &buf[count], len - count, 0,
- (struct sockaddr *)&nladdr, sizeof(nladdr));
- if (ret < 0)
- {
- if (errno != EAGAIN)
- {
- count = -1;
- goto out;
- }
- }
- else
- {
- count += ret;
- }
-
- }while (count < len);
-
-
- out:
- genlmsg_free(buf);
-
- LOGC("send return %d", count);
- return count;
- }
-
- /**
- *
- * @param sockfd generic netlink socket fd
- * @param buf the 'buf' is including the struct nlmsghdr,
- * struct genlmsghdr and struct nlattr
- * @param len size of 'buf'
- * @return >0 size of genlmsg
- * <0 error occur
- */
- static int genlmsg_recv(int sockfd, unsigned char *buf, unsigned int len)
- {
- struct sockaddr_nl nladdr;
- struct msghdr msg;
- struct iovec iov;
-
- int ret;
-
- nladdr.nl_family = AF_NETLINK;
- nladdr.nl_pid = getpid();
- nladdr.nl_groups = 0xffffffff;
-
- iov.iov_base = buf;
- iov.iov_len = len;
-
- msg.msg_name = (void *)&nladdr;
- msg.msg_namelen = sizeof(nladdr);
- msg.msg_iov = &iov;
- msg.msg_iovlen = 1;
- msg.msg_control = NULL;
- msg.msg_controllen = 0;
- msg.msg_flags = 0;
- ret = recvmsg(sockfd, &msg, 0);
- ret = ret > 0 ? ret : -1;
- LOGC("recv return %d", ret);
- return ret;
- }
-
- static int genlmsg_dispatch(struct nlmsghdr *nlmsghdr, unsigned int nlh_len,
- int nlmsg_type, int nla_type, unsigned char *buf, int *len)
- {
- struct nlmsghdr *nlh;
- struct genlmsghdr *glh;
- struct nlattr *nla;
- int nla_len;
-
- int l;
- int i;
- int ret = -1;
-
- if (!nlmsghdr || !buf || !len)
- return -1;
-
- LOGC("nlmsg_type = %d", nlmsghdr->nlmsg_type);
本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!
