kernel
/* file : genl_kernel.c
* kernel module for test genetlink
* sudochen@163.com
*
*/
#include <linux/init.h>
#include <linux/module.h>
#include <net/genetlink.h>
static int cmd_echo(struct sk_buff *skb_2, struct genl_info *info);
enum {
CMD_NONE,
CMD_ECHO,
CMD_MAX
};
static struct genl_family cmd_genl_family = {
.id = GENL_ID_GENERATE,
.hdrsize = 0,
.name = "cmd",
.version = 1,
.maxattr = CMD_MAX,
};
static struct nla_policy cmd_nla_policy[CMD_MAX + 1] = {
[CMD_NONE] = {
.type = NLA_UNSPEC,
},
[CMD_ECHO] = {
.type = NLA_STRING,
.len = 0x100,
},
};
static struct genl_ops cmd_ops[] = {
{
.cmd = CMD_ECHO,
.flags = 0,
.policy = cmd_nla_policy,
.doit = cmd_echo,
.dumpit = NULL,
}
};
static int cmd_echo(struct sk_buff *skb_2, struct genl_info *info)
{
struct nlattr *attr = NULL;
struct sk_buff *skb = NULL;
int ret = -1;
char *data;
char *head;
if (NULL == info) {
return -1;
}
attr = info->attrs[CMD_ECHO];
if (attr) {
data = (char*)nla_data(attr);
if (data) {
printk("genl recv data %s\n",data);
} else {
printk("genl recv data NULL\n");
}
} else {
printk("no attr %d\n",CMD_ECHO);
}
skb = genlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
if (NULL == skb) {
goto out;
}
head = genlmsg_put(skb, 0, info->snd_seq+1,&cmd_genl_family,0,CMD_ECHO);
if (NULL == head) {
goto out;
}
ret = nla_put_string(skb, CMD_ECHO, "hello world form kernel space");
if (ret)
goto out;
genlmsg_end(skb,head);
ret = genlmsg_unicast(&init_net,skb,info->snd_pid);
if (ret)
goto out;
return 0;
out:
printk("genl an error ocured\n");
return -1;
}
int __init cmd_init(void)
{
int error;
error = genl_register_family_with_ops(&cmd_genl_family, cmd_ops, ARRAY_SIZE(cmd_ops));
if (error) {
printk("register family error\n");
return error;
}
printk("register family ok\n");
return 0;
}
void __exit cmd_exit(void)
{
genl_unregister_family(&cmd_genl_family);
printk("unregister family ok\n");
}
MODULE_LICENSE("GPL");
module_init(cmd_init);
module_exit(cmd_exit);
Makefile
KERNELDIR := /usr/src/kernels/2.6.32-358.el6.i686
obj-m += genl_kernel.o
modules:
make -C $(KERNELDIR) M=$(PWD) $@
clean:
make -C $(KERNELDIR) M=$(PWD) $@
user
/* file : genl_user.c
* test for genetlink in user space
* sudochen@163.com
*
*/
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>
#include <poll.h>
#include <string.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <signal.h>
#include <linux/netlink.h>
#include <linux/genetlink.h>
// copy from kernel module source
enum {
CMD_NONE,
CMD_ECHO,
CMD_MAX
};
struct msgtemplate {
struct nlmsghdr n;
struct genlmsghdr g;
char buf[256];
};
#define NLA_DATA(na) ((void *)((char*)(na) + NLA_HDRLEN))
#define GENLMSG_DATA(glh) ((void *)(NLMSG_DATA(glh) + GENL_HDRLEN))
/*
* Create a raw netlink socket and bind
*/
static int create_nl_socket(int protocol, int groups)
{
socklen_t addr_len;
int fd;
struct sockaddr_nl local;
fd = socket(AF_NETLINK, SOCK_RAW, protocol);
if (fd < 0){
perror("socket");
return -1;
}
memset(&local, 0, sizeof(local));
local.nl_family = AF_NETLINK;
local.nl_groups = groups;
if (bind(fd, (struct sockaddr *) &local, sizeof(local)) < 0)
goto error;
return fd;
error:
close(fd);
return -1;
}
/*
* Send netlink message to kernel
*/
int sendto_fd(int s, const char *buf, int bufLen)
{
struct sockaddr_nl nladdr;
int r;
memset(&nladdr, 0, sizeof(nladdr));
nladdr.nl_family = AF_NETLINK;
while ((r = sendto(s, buf, bufLen, 0, (struct sockaddr *) &nladdr,
sizeof(nladdr))) < bufLen) {
if (r > 0) {
buf += r;
bufLen -= r;
} else if (errno != EAGAIN)
return -1;
}
return 0;
}
/*
* Probe the controller in genetlink to find the family id
* for the CONTROL_EXMPL family
*/
int get_family_id(int sd, char *family)
{
struct msgtemplate msg;
struct msgtemplate out;
int id;
struct nlattr *na;
int rep_len;
memset(&msg, 0x00, sizeof(msg));
memset(&out, 0x00, sizeof(out));
/* Get family name */
msg.n.nlmsg_type = GENL_ID_CTRL;
msg.n.nlmsg_flags = NLM_F_REQUEST;
msg.n.nlmsg_seq = 0;
msg.n.nlmsg_pid = getpid();
msg.n.nlmsg_len = NLMSG_LENGTH(GENL_HDRLEN);
msg.g.cmd = CTRL_CMD_GETFAMILY;
msg.g.version = 0x1;
na = (struct nlattr *) GENLMSG_DATA(&msg);
na->nla_type = CTRL_ATTR_FAMILY_NAME;
na->nla_len = strlen(family) + 1 + NLA_HDRLEN;
strcpy((char *)NLA_DATA(na),(const char *)family);
msg.n.nlmsg_len += NLMSG_ALIGN(na->nla_len);
if (sendto_fd(sd, (char *) &msg, msg.n.nlmsg_len) < 0)
return -1;
rep_len = recv(sd, &out, sizeof(out), 0);
if (rep_len < 0){
perror("recv");
return -1;
}
printf("recv from netlink socket len is %d\n",rep_len);
/* Validate response message */
if (!NLMSG_OK((&out.n), rep_len)){
fprintf(stderr, "invalid reply message\n");
return -1;
}
if (out.n.nlmsg_type == NLMSG_ERROR) { /* error */
fprintf(stderr, "received error\n");
return -1;
}
na = (struct nlattr *) GENLMSG_DATA(&out);
// because the first params is CTRL_ATTR_FAMILY_NAME,
// we wanna the CTRL_ATTR_FAMILY_ID,
// so skip one ATTR
na = (struct nlattr *)((char*)na + NLA_ALIGN(na->nla_len));
if (na->nla_type == CTRL_ATTR_FAMILY_ID) {
id = *(__u16 *) NLA_DATA(na);
printf("get CTRL_ATTR_FAMILY_ID %d\n",id);
}
return id;
}
int main(int argc, char **argv)
{
int nl_sd = -1;
int id = -1;
struct msgtemplate msg;
struct msgtemplate out;
struct nlattr *na = NULL;
int rep_len = -1;
char *resp;
memset(&out, 0x00, sizeof(out));
memset(&msg, 0x00, sizeof(msg));
nl_sd = create_nl_socket(NETLINK_GENERIC,0);
if(nl_sd < 0){
printf("create failure\n");
return -1;;
}
id = get_family_id(nl_sd,"cmd");
if (id < 0) {
printf("get family id by name error\n");
close(nl_sd);
return -1;
}
printf("get family id is %d\n",id);
msg.n.nlmsg_len = NLMSG_LENGTH(GENL_HDRLEN);
msg.n.nlmsg_type = id;
msg.n.nlmsg_flags = NLM_F_REQUEST;
msg.n.nlmsg_seq = 0;
msg.n.nlmsg_pid = getpid();
msg.g.cmd = CMD_ECHO;
na = (struct nlattr*) GENLMSG_DATA(&msg);
na->nla_type = CMD_ECHO;
na->nla_len = strlen("123") + NLA_HDRLEN + 1;
memcpy(NLA_DATA(na), "123", na->nla_len);
msg.n.nlmsg_len += NLMSG_ALIGN(na->nla_len);
if (sendto_fd(nl_sd, (char *) &msg, msg.n.nlmsg_len) < 0) {
perror("sendto");
close(nl_sd);
return -1;
}
rep_len = recv(nl_sd, &out, sizeof(out), 0);
if (rep_len < 0){
perror("recv");
close(nl_sd);
return -1;
}
printf("genl rep_len is %d\n",rep_len);
if (!NLMSG_OK((&out.n),rep_len)) {
perror("recv len");
close(nl_sd);
return -1;
}
if (out.n.nlmsg_type == NLMSG_ERROR) {
perror("recv nlmsg_type error");
close(nl_sd);
return -1;
}
na = (struct nlattr *) GENLMSG_DATA(&out);
if (na->nla_type == CMD_ECHO) {
resp = NLA_DATA(na);
if (resp) {
printf("kernel : %s\n", resp);
}
}
return 0;
}