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