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 <[EMAIL PROTECTED]>:
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");