Re: Generic Netlink

From: Edouard Thuleau
Date: Thu May 31 2007 - 05:34:35 EST


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");