5 * Copyright (C) 2011-2012 Intel Corporation. All rights reserved.
6 * Copyright (C) 2013 BWM CarIT GmbH.
8 * This program is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public
10 * License version 2.1 as published by the Free Software Foundation.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU Lesser General Public License for more details.
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
24 * This file is a copy from ELL which has been ported to use GLib's
34 #include <sys/socket.h>
35 #include <linux/netlink.h>
39 #include "src/shared/util.h"
40 #include "src/shared/netlink.h"
43 #define SOL_NETLINK 270
50 netlink_command_func_t handler;
51 netlink_destroy_func_t destroy;
57 netlink_notify_func_t handler;
58 netlink_destroy_func_t destroy;
66 GQueue *command_queue;
67 GHashTable *command_pending;
68 GHashTable *command_lookup;
69 unsigned int next_command_id;
70 GHashTable *notify_groups;
71 GHashTable *notify_lookup;
72 unsigned int next_notify_id;
73 netlink_debug_func_t debug_handler;
74 netlink_destroy_func_t debug_destroy;
79 static void destroy_command(struct command *command)
82 command->destroy(command->user_data);
87 static void destroy_notify(struct notify *notify)
90 notify->destroy(notify->user_data);
95 static gboolean can_write_data(GIOChannel *chan,
96 GIOCondition cond, gpointer user_data)
98 struct netlink_info *netlink = user_data;
99 struct command *command;
100 struct sockaddr_nl addr;
105 command = g_queue_pop_head(netlink->command_queue);
109 sk = g_io_channel_unix_get_fd(chan);
111 memset(&addr, 0, sizeof(addr));
112 addr.nl_family = AF_NETLINK;
115 data = ((void *) command) + NLMSG_ALIGN(sizeof(struct command));
117 written = sendto(sk, data, command->len, 0,
118 (struct sockaddr *) &addr, sizeof(addr));
119 if (written < 0 || (uint32_t) written != command->len) {
120 g_hash_table_remove(netlink->command_lookup,
121 GUINT_TO_POINTER(command->id));
122 destroy_command(command);
126 util_hexdump('<', data, command->len,
127 netlink->debug_handler, netlink->debug_data);
129 g_hash_table_replace(netlink->command_pending,
130 GUINT_TO_POINTER(command->seq), command);
132 return g_queue_get_length(netlink->command_queue) > 0;
135 static void do_notify(gpointer key, gpointer value, gpointer user_data)
137 struct nlmsghdr *nlmsg = user_data;
138 struct notify *notify = value;
140 if (notify->handler) {
141 notify->handler(nlmsg->nlmsg_type, NLMSG_DATA(nlmsg),
142 nlmsg->nlmsg_len - NLMSG_HDRLEN, notify->user_data);
146 static void process_broadcast(struct netlink_info *netlink, uint32_t group,
147 struct nlmsghdr *nlmsg)
149 GHashTable *notify_list;
151 notify_list = g_hash_table_lookup(netlink->notify_groups,
152 GUINT_TO_POINTER(group));
156 g_hash_table_foreach(notify_list, do_notify, nlmsg);
159 static void process_message(struct netlink_info *netlink,
160 struct nlmsghdr *nlmsg)
162 const void *data = nlmsg;
163 struct command *command;
165 command = g_hash_table_lookup(netlink->command_pending,
166 GUINT_TO_POINTER(nlmsg->nlmsg_seq));
170 g_hash_table_remove(netlink->command_pending,
171 GUINT_TO_POINTER(nlmsg->nlmsg_seq));
173 if (!command->handler)
176 if (nlmsg->nlmsg_type < NLMSG_MIN_TYPE) {
177 const struct nlmsgerr *err;
179 switch (nlmsg->nlmsg_type) {
181 err = data + NLMSG_HDRLEN;
183 command->handler(-err->error, 0, NULL, 0,
188 command->handler(0, nlmsg->nlmsg_type, data + NLMSG_HDRLEN,
189 nlmsg->nlmsg_len - NLMSG_HDRLEN,
194 g_hash_table_remove(netlink->command_lookup,
195 GUINT_TO_POINTER(command->id));
197 destroy_command(command);
200 static void process_multi(struct netlink_info *netlink, struct nlmsghdr *nlmsg)
202 const void *data = nlmsg;
203 struct command *command;
205 command = g_hash_table_lookup(netlink->command_pending,
206 GUINT_TO_POINTER(nlmsg->nlmsg_seq));
210 if (!command->handler)
213 if (nlmsg->nlmsg_type < NLMSG_MIN_TYPE) {
214 const struct nlmsgerr *err;
216 switch (nlmsg->nlmsg_type) {
219 err = data + NLMSG_HDRLEN;
221 command->handler(-err->error, 0, NULL, 0,
226 command->handler(0, nlmsg->nlmsg_type, data + NLMSG_HDRLEN,
227 nlmsg->nlmsg_len - NLMSG_HDRLEN,
233 g_hash_table_remove(netlink->command_pending,
234 GUINT_TO_POINTER(nlmsg->nlmsg_seq));
236 g_hash_table_remove(netlink->command_lookup,
237 GUINT_TO_POINTER(command->id));
239 destroy_command(command);
242 static gboolean can_read_data(GIOChannel *chan,
243 GIOCondition cond, gpointer data)
245 struct netlink_info *netlink = data;
246 struct cmsghdr *cmsg;
249 struct nlmsghdr *nlmsg;
250 unsigned char buffer[4096];
251 unsigned char control[32];
256 sk = g_io_channel_unix_get_fd(chan);
258 iov.iov_base = buffer;
259 iov.iov_len = sizeof(buffer);
261 memset(&msg, 0, sizeof(msg));
264 msg.msg_control = control;
265 msg.msg_controllen = sizeof(control);
267 len = recvmsg(sk, &msg, 0);
271 util_hexdump('>', buffer, len, netlink->debug_handler,
272 netlink->debug_data);
274 for (cmsg = CMSG_FIRSTHDR(&msg); cmsg;
275 cmsg = CMSG_NXTHDR(&msg, cmsg)) {
276 struct nl_pktinfo *pktinfo;
278 if (cmsg->cmsg_level != SOL_NETLINK)
281 if (cmsg->cmsg_type != NETLINK_PKTINFO)
284 pktinfo = (void *) CMSG_DATA(cmsg);
286 group = pktinfo->group;
289 for (nlmsg = iov.iov_base; NLMSG_OK(nlmsg, (uint32_t) len);
290 nlmsg = NLMSG_NEXT(nlmsg, len)) {
291 if (group > 0 && nlmsg->nlmsg_seq == 0) {
292 process_broadcast(netlink, group, nlmsg);
296 if (nlmsg->nlmsg_pid != netlink->pid)
299 if (nlmsg->nlmsg_flags & NLM_F_MULTI)
300 process_multi(netlink, nlmsg);
302 process_message(netlink, nlmsg);
308 static gboolean netlink_event(GIOChannel *chan,
309 GIOCondition cond, gpointer data)
311 if (cond & (G_IO_NVAL | G_IO_HUP | G_IO_ERR))
317 static int create_netlink_socket(int protocol, uint32_t *pid)
319 struct sockaddr_nl addr;
320 socklen_t addrlen = sizeof(addr);
323 sk = socket(PF_NETLINK, SOCK_DGRAM | SOCK_CLOEXEC | SOCK_NONBLOCK,
328 memset(&addr, 0, sizeof(addr));
329 addr.nl_family = AF_NETLINK;
331 if (bind(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
336 if (getsockname(sk, (struct sockaddr *) &addr, &addrlen) < 0) {
341 if (setsockopt(sk, SOL_NETLINK, NETLINK_PKTINFO,
342 &pktinfo, sizeof(pktinfo)) < 0) {
353 struct netlink_info *netlink_new(int protocol)
355 struct netlink_info *netlink;
358 netlink = g_try_new0(struct netlink_info, 1);
362 netlink->next_seq = 1;
363 netlink->next_command_id = 1;
364 netlink->next_notify_id = 1;
366 sk = create_netlink_socket(protocol, &netlink->pid);
372 netlink->channel = g_io_channel_unix_new(sk);
373 g_io_channel_set_close_on_unref(netlink->channel, TRUE);
375 g_io_channel_set_encoding(netlink->channel, NULL, NULL);
376 g_io_channel_set_buffered(netlink->channel, FALSE);
378 g_io_add_watch(netlink->channel, G_IO_IN, can_read_data, netlink);
379 g_io_add_watch(netlink->channel, G_IO_NVAL | G_IO_HUP | G_IO_ERR,
380 netlink_event, netlink);
382 netlink->command_queue = g_queue_new();
383 netlink->command_pending = g_hash_table_new(g_direct_hash,
385 netlink->command_lookup = g_hash_table_new(g_direct_hash,
388 netlink->notify_groups = g_hash_table_new(g_direct_hash,
390 netlink->notify_lookup = g_hash_table_new(g_direct_hash,
396 static gboolean cleanup_notify(gpointer key, gpointer value, gpointer user_data)
398 struct notify *notify = value;
400 destroy_notify(notify);
406 static gboolean cleanup_notify_group(gpointer key, gpointer value,
409 GHashTable *notify_list = value;
411 g_hash_table_foreach_remove(notify_list, cleanup_notify, user_data);
412 g_hash_table_destroy(notify_list);
417 static gboolean cleanup_command(gpointer key, gpointer value,
420 struct command *command = value;
422 destroy_command(command);
427 void netlink_destroy(struct netlink_info *netlink)
429 g_hash_table_destroy(netlink->notify_lookup);
431 g_hash_table_foreach_remove(netlink->notify_groups,
432 cleanup_notify_group, NULL);
433 g_hash_table_destroy(netlink->notify_groups);
435 g_queue_free(netlink->command_queue);
437 g_hash_table_destroy(netlink->command_pending);
439 g_hash_table_foreach_remove(netlink->command_lookup,
440 cleanup_command, NULL);
441 g_hash_table_destroy(netlink->command_lookup);
443 g_io_channel_shutdown(netlink->channel, TRUE, NULL);
444 g_io_channel_unref(netlink->channel);
449 unsigned int netlink_send(struct netlink_info *netlink,
450 uint16_t type, uint16_t flags, const void *data,
451 uint32_t len, netlink_command_func_t function,
452 void *user_data, netlink_destroy_func_t destroy)
454 struct command *command;
455 struct nlmsghdr *nlmsg;
461 if (!netlink->command_queue ||
462 !netlink->command_pending ||
463 !netlink->command_lookup)
466 size = NLMSG_ALIGN(sizeof(struct command)) +
467 NLMSG_HDRLEN + NLMSG_ALIGN(len);
469 command = g_try_malloc0(size);
473 command->handler = function;
474 command->destroy = destroy;
475 command->user_data = user_data;
477 command->id = netlink->next_command_id;
479 g_hash_table_replace(netlink->command_lookup,
480 GUINT_TO_POINTER(command->id), command);
482 command->seq = netlink->next_seq++;
483 command->len = NLMSG_HDRLEN + NLMSG_ALIGN(len);
485 nlmsg = ((void *) command) + NLMSG_ALIGN(sizeof(struct command));
487 nlmsg->nlmsg_len = command->len;
488 nlmsg->nlmsg_type = type;
489 nlmsg->nlmsg_flags = NLM_F_REQUEST | flags;
490 nlmsg->nlmsg_seq = command->seq;
491 nlmsg->nlmsg_pid = netlink->pid;
494 memcpy(((void *) nlmsg) + NLMSG_HDRLEN, data, len);
496 g_queue_push_tail(netlink->command_queue, command);
498 netlink->next_command_id++;
500 /* Arm IOChannel to call can_write_data in case it is not armed yet. */
501 if (g_queue_get_length(netlink->command_queue) == 1)
502 g_io_add_watch(netlink->channel, G_IO_OUT, can_write_data,
508 bool netlink_cancel(struct netlink_info *netlink, unsigned int id)
510 struct command *command;
512 if (!netlink || id == 0)
515 if (!netlink->command_queue ||
516 !netlink->command_pending ||
517 !netlink->command_lookup)
520 command = g_hash_table_lookup(netlink->command_lookup,
521 GUINT_TO_POINTER(id));
525 g_hash_table_remove(netlink->command_lookup, GUINT_TO_POINTER(id));
527 if (!g_queue_remove(netlink->command_queue, command)) {
528 g_hash_table_remove(netlink->command_pending,
529 GUINT_TO_POINTER(command->seq));
532 destroy_command(command);
537 static bool add_membership(struct netlink_info *netlink, uint32_t group)
539 int sk, value = group;
541 sk = g_io_channel_unix_get_fd(netlink->channel);
543 if (setsockopt(sk, SOL_NETLINK, NETLINK_ADD_MEMBERSHIP,
544 &value, sizeof(value)) < 0)
550 static bool drop_membership(struct netlink_info *netlink, uint32_t group)
552 int sk, value = group;
554 sk = g_io_channel_unix_get_fd(netlink->channel);
556 if (setsockopt(sk, SOL_NETLINK, NETLINK_DROP_MEMBERSHIP,
557 &value, sizeof(value)) < 0)
563 unsigned int netlink_register(struct netlink_info *netlink,
564 uint32_t group, netlink_notify_func_t function,
565 void *user_data, netlink_destroy_func_t destroy)
567 GHashTable *notify_list;
568 struct notify *notify;
574 if (!netlink->notify_groups || !netlink->notify_lookup)
577 notify_list = g_hash_table_lookup(netlink->notify_groups,
578 GUINT_TO_POINTER(group));
580 notify_list = g_hash_table_new(g_direct_hash, g_direct_equal);
584 g_hash_table_replace(netlink->notify_groups,
585 GUINT_TO_POINTER(group), notify_list);
588 notify = g_new(struct notify, 1);
590 notify->group = group;
591 notify->handler = function;
592 notify->destroy = destroy;
593 notify->user_data = user_data;
595 id = netlink->next_notify_id;
597 g_hash_table_replace(netlink->notify_lookup,
598 GUINT_TO_POINTER(id), notify_list);
599 g_hash_table_replace(notify_list, GUINT_TO_POINTER(id), notify);
601 if (g_hash_table_size(notify_list) == 1) {
602 if (add_membership(netlink, notify->group) == false)
606 netlink->next_notify_id++;
611 g_hash_table_remove(notify_list, GUINT_TO_POINTER(id));
612 g_hash_table_remove(netlink->notify_lookup, GUINT_TO_POINTER(id));
618 bool netlink_unregister(struct netlink_info *netlink, unsigned int id)
620 GHashTable *notify_list;
621 struct notify *notify;
623 if (!netlink || id == 0)
626 if (!netlink->notify_groups || !netlink->notify_lookup)
629 notify_list = g_hash_table_lookup(netlink->notify_lookup,
630 GUINT_TO_POINTER(id));
635 g_hash_table_remove(netlink->notify_lookup, GUINT_TO_POINTER(id));
637 notify = g_hash_table_lookup(notify_list, GUINT_TO_POINTER(id));
641 g_hash_table_remove(notify_list, GUINT_TO_POINTER(id));
643 if (g_hash_table_size(notify_list) == 0)
644 drop_membership(netlink, notify->group);
646 destroy_notify(notify);
651 bool netlink_set_debug(struct netlink_info *netlink,
652 netlink_debug_func_t function,
653 void *user_data, netlink_destroy_func_t destroy)
658 if (netlink->debug_destroy)
659 netlink->debug_destroy(netlink->debug_data);
661 netlink->debug_handler = function;
662 netlink->debug_destroy = destroy;
663 netlink->debug_data = user_data;