Re: Generic Netlink

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Thanks Paul,

I try to use this example source (netlabel) but it doesn't work.
I use the svn version of libnl and if i try the same code of netlabel
I've a segment fault on the function "nlmsg_append", so I initiate the
nl_msg with "nlmsg_alloc". The kernel code is lunch correctly and the
user code works but nothing happens.

Can you help me ? If someone have a simple example code ?

PS: I join my code (kernel and user).

Thanks,
Edouard.


2007/5/29, Paul Moore <paul.moore@xxxxxx>:
On Tuesday, May 29 2007 11:02:39 am Edouard Thuleau wrote:
> I try to use the generic netlink, and I found some examples and
> explications for the kernel space but for the user space, I don't know
> how to do.
> In the doc, it says to look in the libnl, but this lib use principaly
> the classic netlink. In svn version, an API for generic netlink
> appears but there aren't examples.
>
> Some one can help me ?

Don't forget that Generic Netlink is just an abstraction/multiplex layer that
sits on top of Netlink; the standard Netlink routines/API still applies to
Generic Netlink.  If you are looking for a a userspace application which uses
libnl to speak Generic Netlink using only standard libnl Netlink APIs you can
look at the NetLabel Tools package which can be found here:

 * http://netlabel.sf.net

--
paul moore
linux security @ hp

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <inttypes.h>
#include <errno.h>
#include <stdint.h>
#include <getopt.h>
#include <linux/types.h>
#include <sys/socket.h>
/*
#include <netlink/netlink.h>
#include <netlink/utils.h>
#include <netlink/genl/genl.h>
#include <netlink/genl/ctrl.h>
#include <netlink/genl/mngt.h>
*/
#include <netlink/netlink.h>
#include <netlink/msg.h>
#include <netlink/attr.h>

static uint16_t nl_cac_id = 0;

/* attributes */
enum {
	CAC_ATTR_DISCOVER,
	CAC_ATTR_MSG,
	__CAC_ATTR_AFTER_LAST,
};
#define CAC_ATTR_MAX (__CAC_ATTR_AFTER_LAST - 1)

/* commands */
enum {
	CAC_CMD_DISCOVER,
	CAC_CMD_MSG,
	__CAC_CMD_MAX,
};
#define CAC_CMD_MAX (__CAC_CMD_MAX - 1)

struct nl_msg *nl_msg_new(void)
{
	struct nl_msg *msg;
	struct genlmsghdr genl_hdr;

//	msg = (struct nl_msg *) nlmsg_build_no_hdr();
	if (msg == NULL)
		goto msg_new_failure;

	/* add a generic netlink header */
/*	genl_hdr.cmd = 0;
	genl_hdr.version = 1;
	genl_hdr.reserved = 0;*/
	printf("Coucou1\n");
	if (nlmsg_append(msg, &genl_hdr, sizeof(genl_hdr), 1) != 0)
		goto msg_new_failure;

	printf("Coucou2\n");
	return msg;

msg_new_failure:
	if (msg)
		nlmsg_free(msg);
	return NULL;
}

struct nlmsghdr *nl_msg_nlhdr(struct nl_msg *msg)
{
	if (msg != NULL)
		return nlmsg_hdr(msg);
	else
		return NULL;
}

struct genlmsghdr *nl_msg_genlhdr(struct nl_msg *msg)
{
	struct nlmsghdr *nl_hdr;

	if (msg == NULL)
		return NULL;

	nl_hdr = nlmsg_hdr(msg);
	if (nl_hdr == NULL)
		return NULL;

	return (struct genlmsghdr *)(&nl_hdr[1]);
}

static struct nl_msg *nl_mgmt_msg_new(uint16_t command, int flags)
{
	struct nl_msg *msg;
	struct nlmsghdr *nl_hdr;
	struct genlmsghdr *genl_hdr;

	/* create a new message */
//	msg = nl_msg_new();
	msg = nlmsg_alloc();
	if (msg == NULL)
		goto msg_new_failure;

	/* setup the netlink header */
	nl_hdr = nl_msg_nlhdr(msg);
	if (nl_hdr == NULL)
		goto msg_new_failure;
	nl_hdr->nlmsg_type = nl_cac_id;
	nl_hdr->nlmsg_flags = flags;

	/* setup the generic netlink header */
	genl_hdr = nl_msg_genlhdr(msg);
	if (genl_hdr == NULL)
		goto msg_new_failure;
	genl_hdr->cmd = command;

	return msg;

msg_new_failure:
	nlmsg_free(msg);
	return NULL;
}

int nl_comm_send(struct nl_handle *hndl, struct nl_msg *msg)
{
	struct nlmsghdr *nl_hdr;
	struct ucred creds;

	/* sanity checks */
	if (msg == NULL)
		return -EINVAL;

	creds.pid = getpid();
	creds.uid = geteuid();
	creds.gid = getegid();

	nlmsg_set_creds(msg, &creds);

	nl_hdr = nl_msg_nlhdr(msg);
	if (nl_hdr == NULL)
		return -EBADMSG;
	nl_hdr->nlmsg_flags |= NLM_F_ACK;

	return nl_send_auto_complete(hndl, msg);
}

int nl_comm_recv_raw(struct nl_handle *hndl, unsigned char **data)
{
	int ret_val;
	struct sockaddr_nl peer_nladdr;
	struct ucred *creds = NULL;
	int nl_fd;
	fd_set read_fds;
	struct timeval timeout;

	/* sanity checks */
	if (data == NULL)
		return -EINVAL;

	/* we use blocking sockets so do enforce a timeout using select() if no data
	* is waiting to be read from the handle */
	timeout.tv_sec = 10;
	timeout.tv_usec = 0;
	nl_fd = nl_socket_get_fd(hndl);
	FD_ZERO(&read_fds);
	FD_SET(nl_fd, &read_fds);
	ret_val = select(nl_fd + 1, &read_fds, NULL, NULL, &timeout);
	if (ret_val < 0)
		return -errno;
	else if (ret_val == 0)
		return -EAGAIN;

	/* perform the read operation */
	*data = NULL;
	ret_val = nl_recv(hndl, &peer_nladdr, data, &creds);
	if (ret_val < 0)
		return ret_val;

	/* if we are setup to receive credentials, only accept messages from the
	* kernel (ignore all others and send an -EAGAIN) */
	if (creds != NULL && creds->pid != 0) {
		ret_val = -EAGAIN;
		goto recv_raw_failure;
	}

	return ret_val;

recv_raw_failure:
	if (*data) {
		free(*data);
		*data = NULL;
	}
	return ret_val;
}

int cac_protocols(struct nl_handle *handle)
{
	int ret_val = -ENOMEM;
	unsigned char *data = NULL;
	struct nl_msg *msg = NULL;
	struct nlmsghdr *nl_hdr;
	struct genlmsghdr *genl_hdr;
	struct nlattr *nla_head;
	struct nlattr *nla;
	int data_len;
	int data_attrlen;

	if (nl_cac_id == 0)
		return -ENOPROTOOPT;

	/* create a new message */
	msg = nl_mgmt_msg_new(CAC_CMD_DISCOVER, NLM_F_DUMP);
	if (msg == NULL) {
		ret_val = -ENOMEM;
		goto protocols_return;
	}

	/* send the request */
	ret_val = nl_comm_send(handle, msg);
	if (ret_val <= 0) {
		if (ret_val == 0)
			ret_val = -ENODATA;
		goto protocols_return;
	}

	/* read all of the messages (multi-message response) */
	do {
		if (data) {
			free(data);
			data = NULL;
		}

		/* get the next set of messages */
		ret_val = nl_comm_recv_raw(handle, &data);
		if (ret_val <= 0) {
			if (ret_val == 0)
				ret_val = -ENODATA;
			goto protocols_return;
		}
		data_len = ret_val;
		nl_hdr = (struct nlmsghdr *)data;

		/* check to see if this is a netlink control message we don't care about */
		if (nl_hdr->nlmsg_type == NLMSG_NOOP ||
				nl_hdr->nlmsg_type == NLMSG_ERROR ||
				nl_hdr->nlmsg_type == NLMSG_OVERRUN) {
			ret_val = -EBADMSG;
			goto protocols_return;
		}

		/* loop through the messages */
		while (nlmsg_ok(nl_hdr, data_len) && nl_hdr->nlmsg_type != NLMSG_DONE) {
			/* get the header pointers */
			genl_hdr = (struct genlmsghdr *)nlmsg_data(nl_hdr);
			if (genl_hdr == NULL || genl_hdr->cmd != CAC_CMD_DISCOVER) {
				ret_val = -EBADMSG;
				goto protocols_return;
			}
			nla_head = (struct nlattr *)(&genl_hdr[1]);
			data_attrlen = nlmsg_len(nl_hdr) - NLMSG_ALIGN(sizeof(*genl_hdr));

			/* get the attribute information */
			nla = nla_find(nla_head, data_attrlen, CAC_CMD_DISCOVER);
			if (nla == NULL)
				goto protocols_return;

			printf("Réponse du kernel : %u\n",nla_get_u32(nla));

			/* next message */
			nl_hdr = nlmsg_next(nl_hdr, &data_len);
		}
	} while (data_len > 0 && nl_hdr->nlmsg_type != NLMSG_DONE);

	ret_val = 0;

protocols_return:
	if (handle == NULL) {
		nl_close(handle);
		nl_handle_destroy(handle);
	}
	if (data)
		free(data);
	nlmsg_free(msg);
	return ret_val;
}


int main(void) {
	struct nl_handle *handle;
	struct nl_cahche *nl_c;
	struct genl_family *family;
	struct nl_msg *msg;
	void *hdr;
	int err;

	handle = nl_handle_alloc();
	if (handle == NULL)
	    goto open_failure;

	if (genl_connect(handle) != 0)
	    goto open_failure;

	nl_c = genl_ctrl_alloc_cache(handle);
	if(nl_c == NULL)
		goto open_failure;

	family = genl_ctrl_search_by_name(nl_c, "cac");
	if(family == NULL) {
		printf("famille pas trouvé \n");
		goto open_failure;
	}

	printf("famille \"%s\"(version : %i) trouvée ID : %i\n", 
		genl_family_get_name(family), 
		genl_family_get_version(family), 
		nl_cac_id = genl_family_get_id(family));

	cac_protocols(handle);

	return 0;

open_failure:
	if (handle) {
		nl_close(handle);
		nl_handle_destroy(handle);
	}
	printf("Erreur open_failure\n");
	return NULL;
}

#include <linux/kernel.h>
#include <linux/module.h>
#include <net/sock.h>
#include <linux/netlink.h>

#include <linux/rtnetlink.h>
#include <net/genetlink.h>

#define CAC_MAX_STRING_LEN 1024

/* attributes */
enum {
	CAC_ATTR_DISCOVER,
	CAC_ATTR_MSG,
	__CAC_ATTR_AFTER_LAST,
};
#define CAC_ATTR_MAX (__CAC_ATTR_AFTER_LAST - 1)

/* the netlink family */
static struct genl_family cac_family = {
	.id = GENL_ID_GENERATE,
	.name = "cac",
	.hdrsize = 0,
	.version = 1,
	.maxattr = CAC_ATTR_MAX,
};

static struct nla_policy cac_policy[CAC_ATTR_MAX + 1] = {
	[CAC_ATTR_DISCOVER] = { .type = NLA_U32 },
	[CAC_ATTR_MSG] = { .type = NLA_STRING, .minlen = CAC_MAX_STRING_LEN },
};

/* commands */
enum {
	CAC_CMD_DISCOVER,
	CAC_CMD_MSG,
	__CAC_CMD_MAX,
};
#define CAC_CMD_MAX (__CAC_CMD_MAX - 1)

/* handler */
int cac_discover(struct sk_buff *skb, struct genl_info *info)
{
	struct sk_buff *msg;
	void *hdr;
	int err;

	printk("Discover reçu !!!\n");
	msg = nlmsg_new(NLMSG_GOODSIZE);
	if (!msg)
		return -ENOBUFS;

	hdr = genlmsg_put(msg, info->snd_pid, info->snd_seq, cac_family.id, 0, 0, CAC_CMD_MSG, cac_family.version);
	if (!hdr) {
		nlmsg_free(skb);
		return -ENOBUFS;
	}

	err = nla_put_string(msg, CAC_ATTR_MSG, "Message CAC : Kernel speak !!");
	if (err != 0)
    	return err;

	// finalize the message 
	genlmsg_end(msg, hdr);
	
	return genlmsg_unicast(msg, info->snd_pid);
}

int cac_msg(struct sk_buff *skb, struct genl_info *info)
{
	return 0;
}

struct genl_ops cac_ops[] = {
	{
		.cmd = CAC_CMD_DISCOVER,
		.flags = 0,
		.policy = cac_policy,
		.doit = cac_discover,
		.dumpit = NULL,
	},
	{
		.cmd = CAC_CMD_MSG,
		.flags = 0,
		.policy = cac_policy,
		.doit = cac_msg,
		.dumpit = NULL,
	},
};

int init_mod(void)
{
	int err = 0, i;
/*	struct sk_buff *skb;
	void *hdr;*/

	err = genl_register_family(&cac_family);
	if (err) {
		printk("Error register family");
		return err;
	}
	
	for (i = 0; i < ARRAY_SIZE(cac_ops); i++) {
		if((err = genl_register_ops(&cac_family, &cac_ops[i])) < 0) {
			printk("error register ops : %i\n", i);
			return err;
		}
	}

/*	skb = nlmsg_new(NLMSG_GOODSIZE);
	if (!skb)
		return -ENOBUFS;

	hdr = genlmsg_put(skb, 0, 0, cac_family.id, 0, 0, CAC_CMD_MSG, cac_family.version);
	if (!hdr) {
		nlmsg_free(skb);
		return -ENOBUFS;
	}

	err = nla_put_string(skb, CAC_ATTR_MSG, "Message CAC");
	if (err != 0)
    	return -1;

	// finalize the message 
	genlmsg_end(skb, hdr);
	
	genlmsg_multicast(skb, 0, */

	return err;
}

static void __exit exit_mod(void)
{
	genl_unregister_family(&cac_family);

	printk(KERN_INFO "Goodbye\n");
}

module_init(init_mod);
module_exit(exit_mod);

MODULE_LICENSE("GPL");


[Index of Archives]     [Netdev]     [Ethernet Bridging]     [Linux 802.1Q VLAN]     [Linux Wireless]     [Kernel Newbies]     [Security]     [Linux for Hams]     [Netfilter]     [Git]     [Bugtraq]     [Yosemite News and Information]     [MIPS Linux]     [ARM Linux]     [Linux RAID]     [Linux PCI]     [Linux Admin]     [Samba]

  Powered by Linux