2 * Copyright (C) 2014 Martin Willi
3 * Copyright (C) 2014 revosec AG
4 * Copyright (C) 2008 Tobias Brunner
5 * HSR Hochschule fuer Technik Rapperswil
7 * This program is free software; you can redistribute it and/or modify it
8 * under the terms of the GNU General Public License as published by the
9 * Free Software Foundation; either version 2 of the License, or (at your
10 * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
12 * This program is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
14 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
19 * Copyright (C) 2016 secunet Security Networks AG
20 * Copyright (C) 2016 Thomas Egerer
22 * Permission is hereby granted, free of charge, to any person obtaining a copy
23 * of this software and associated documentation files (the "Software"), to deal
24 * in the Software without restriction, including without limitation the rights
25 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
26 * copies of the Software, and to permit persons to whom the Software is
27 * furnished to do so, subject to the following conditions:
29 * The above copyright notice and this permission notice shall be included in
30 * all copies or substantial portions of the Software.
32 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
33 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
34 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
35 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
36 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
37 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
41 #include <sys/socket.h>
42 #include <linux/netlink.h>
43 #include <linux/rtnetlink.h>
44 #include <linux/xfrm.h>
48 #include "kernel_netlink_shared.h"
50 #include <utils/debug.h>
51 #include <threading/mutex.h>
52 #include <threading/condvar.h>
53 #include <collections/array.h>
54 #include <collections/hashtable.h>
56 typedef struct private_netlink_socket_t private_netlink_socket_t
;
59 * Private variables and functions of netlink_socket_t class.
61 struct private_netlink_socket_t
{
64 * public part of the netlink_socket_t object.
66 netlink_socket_t
public;
69 * mutex to lock access entries
74 * Netlink request entries currently active, uintptr_t seq => entry_t
79 * Current sequence number for Netlink requests
94 * Enum names for Netlink messages
99 * Timeout for Netlink replies, in ms
104 * Number of times to repeat timed out queries
109 * Buffer size for received Netlink messages
114 * Use parallel netlink queries
119 * Ignore errors potentially resulting from a retransmission
121 bool ignore_retransmit_errors
;
125 * #definable hook to simulate request message loss
127 #ifdef NETLINK_MSG_LOSS_HOOK
128 bool NETLINK_MSG_LOSS_HOOK(struct nlmsghdr
*msg
);
129 #define msg_loss_hook(msg) NETLINK_MSG_LOSS_HOOK(msg)
131 #define msg_loss_hook(msg) FALSE
135 * Request entry the answer for a waiting thread is collected in
138 /** Condition variable thread is waiting */
140 /** Array of hdrs in a multi-message response, as struct nlmsghdr* */
142 /** All response messages received? */
147 * Clean up a thread waiting entry
149 static void destroy_entry(entry_t
*entry
)
151 entry
->condvar
->destroy(entry
->condvar
);
152 array_destroy_function(entry
->hdrs
, (void*)free
, NULL
);
157 * Write a Netlink message to socket
159 static bool write_msg(private_netlink_socket_t
*this, struct nlmsghdr
*msg
)
161 struct sockaddr_nl addr
= {
162 .nl_family
= AF_NETLINK
,
166 if (msg_loss_hook(msg
))
173 len
= sendto(this->socket
, msg
, msg
->nlmsg_len
, 0,
174 (struct sockaddr
*)&addr
, sizeof(addr
));
175 if (len
!= msg
->nlmsg_len
)
181 DBG1(DBG_KNL
, "netlink write error: %s", strerror(errno
));
189 * Read a single Netlink message from socket, return 0 on error, -1 on timeout
191 static ssize_t
read_msg(private_netlink_socket_t
*this,
192 char *buf
, size_t buflen
, bool block
)
202 FD_SET(this->socket
, &set
);
203 timeval_add_ms(&tv
, this->timeout
);
205 if (select(this->socket
+ 1, &set
, NULL
, NULL
,
206 this->timeout
? &tv
: NULL
) <= 0)
211 len
= recv(this->socket
, buf
, buflen
, MSG_TRUNC
|(block
? 0 : MSG_DONTWAIT
));
214 DBG1(DBG_KNL
, "netlink response exceeds buffer size");
219 if (errno
!= EAGAIN
&& errno
!= EWOULDBLOCK
&& errno
!= EINTR
)
221 DBG1(DBG_KNL
, "netlink read error: %s", strerror(errno
));
229 * Queue received response message
231 static bool queue(private_netlink_socket_t
*this, struct nlmsghdr
*buf
)
233 struct nlmsghdr
*hdr
;
237 seq
= (uintptr_t)buf
->nlmsg_seq
;
239 this->mutex
->lock(this->mutex
);
240 entry
= this->entries
->get(this->entries
, (void*)seq
);
243 hdr
= malloc(buf
->nlmsg_len
);
244 memcpy(hdr
, buf
, buf
->nlmsg_len
);
245 array_insert(entry
->hdrs
, ARRAY_TAIL
, hdr
);
246 if (hdr
->nlmsg_type
== NLMSG_DONE
|| !(hdr
->nlmsg_flags
& NLM_F_MULTI
))
248 entry
->complete
= TRUE
;
249 entry
->condvar
->signal(entry
->condvar
);
254 DBG1(DBG_KNL
, "received unknown netlink seq %u, ignored", seq
);
256 this->mutex
->unlock(this->mutex
);
258 return entry
!= NULL
;
262 * Read and queue response message, optionally blocking, returns TRUE on timeout
264 static bool read_and_queue(private_netlink_socket_t
*this, bool block
)
266 struct nlmsghdr
*hdr
;
267 char buf
[this->buflen
];
268 ssize_t len
, read_len
;
271 len
= read_len
= read_msg(this, buf
, sizeof(buf
), block
);
278 hdr
= (struct nlmsghdr
*)buf
;
279 while (NLMSG_OK(hdr
, len
))
281 if (this->protocol
== NETLINK_XFRM
&&
282 hdr
->nlmsg_type
== XFRM_MSG_NEWSA
)
283 { /* wipe potential IPsec SA keys */
286 if (!queue(this, hdr
))
290 hdr
= NLMSG_NEXT(hdr
, len
);
295 memwipe(buf
, read_len
);
300 CALLBACK(watch
, bool,
301 private_netlink_socket_t
*this, int fd
, watcher_event_t event
)
303 if (event
== WATCHER_READ
)
305 read_and_queue(this, FALSE
);
311 * Send a netlink request, try once
313 static status_t
send_once(private_netlink_socket_t
*this, struct nlmsghdr
*in
,
314 uintptr_t seq
, struct nlmsghdr
**out
, size_t *out_len
)
316 struct nlmsghdr
*hdr
;
322 in
->nlmsg_pid
= getpid();
326 DBG3(DBG_KNL
, "sending %N %u: %b", this->names
, in
->nlmsg_type
,
327 (u_int
)seq
, in
, in
->nlmsg_len
);
330 this->mutex
->lock(this->mutex
);
331 if (!write_msg(this, in
))
333 this->mutex
->unlock(this->mutex
);
338 .condvar
= condvar_create(CONDVAR_TYPE_DEFAULT
),
339 .hdrs
= array_create(0, 0),
341 this->entries
->put(this->entries
, (void*)seq
, entry
);
343 while (!entry
->complete
)
345 if (this->parallel
&&
346 lib
->watcher
->get_state(lib
->watcher
) != WATCHER_STOPPED
&&
347 lib
->processor
->get_total_threads(lib
->processor
))
351 if (entry
->condvar
->timed_wait(entry
->condvar
, this->mutex
,
359 entry
->condvar
->wait(entry
->condvar
, this->mutex
);
363 { /* During (de-)initialization, no watcher thread is active.
364 * collect responses ourselves. */
365 if (read_and_queue(this, TRUE
))
371 this->entries
->remove(this->entries
, (void*)seq
);
373 this->mutex
->unlock(this->mutex
);
375 if (!entry
->complete
)
377 destroy_entry(entry
);
381 for (i
= 0, *out_len
= 0; i
< array_count(entry
->hdrs
); i
++)
383 array_get(entry
->hdrs
, i
, &hdr
);
384 *out_len
+= NLMSG_ALIGN(hdr
->nlmsg_len
);
386 ptr
= malloc(*out_len
);
387 *out
= (struct nlmsghdr
*)ptr
;
389 while (array_remove(entry
->hdrs
, ARRAY_HEAD
, &hdr
))
393 DBG3(DBG_KNL
, "received %N %u: %b", this->names
, hdr
->nlmsg_type
,
394 hdr
->nlmsg_seq
, hdr
, hdr
->nlmsg_len
);
396 memcpy(ptr
, hdr
, hdr
->nlmsg_len
);
397 ptr
+= NLMSG_ALIGN(hdr
->nlmsg_len
);
400 destroy_entry(entry
);
405 * Ignore errors for message types that might have completed previously
407 static void ignore_retransmit_error(private_netlink_socket_t
*this,
408 struct nlmsgerr
*err
, int type
)
413 switch (this->protocol
)
418 case XFRM_MSG_NEWPOLICY
:
439 switch (this->protocol
)
444 case XFRM_MSG_DELPOLICY
:
467 METHOD(netlink_socket_t
, netlink_send
, status_t
,
468 private_netlink_socket_t
*this, struct nlmsghdr
*in
, struct nlmsghdr
**out
,
474 seq
= ref_get(&this->seq
);
476 for (try = 0; try <= this->retries
; ++try)
478 struct nlmsghdr
*hdr
;
484 DBG1(DBG_KNL
, "retransmitting Netlink request (%u/%u)",
487 status
= send_once(this, in
, seq
, &hdr
, &len
);
497 if (hdr
->nlmsg_type
== NLMSG_ERROR
)
499 struct nlmsgerr
* err
;
501 err
= NLMSG_DATA(hdr
);
502 if (err
->error
== -EBUSY
)
508 if (this->ignore_retransmit_errors
&& try > 0)
510 ignore_retransmit_error(this, err
, in
->nlmsg_type
);
517 DBG1(DBG_KNL
, "Netlink request timed out after %u retransmits",
522 METHOD(netlink_socket_t
, netlink_send_ack
, status_t
,
523 private_netlink_socket_t
*this, struct nlmsghdr
*in
)
525 struct nlmsghdr
*out
, *hdr
;
528 if (netlink_send(this, in
, &out
, &len
) != SUCCESS
)
533 while (NLMSG_OK(hdr
, len
))
535 switch (hdr
->nlmsg_type
)
539 struct nlmsgerr
* err
= NLMSG_DATA(hdr
);
543 if (-err
->error
== EEXIST
)
544 { /* do not report existing routes */
548 if (-err
->error
== ESRCH
)
549 { /* do not report missing entries */
553 DBG1(DBG_KNL
, "received netlink error: %s (%d)",
554 strerror(-err
->error
), -err
->error
);
562 hdr
= NLMSG_NEXT(hdr
, len
);
569 DBG1(DBG_KNL
, "netlink request not acknowledged");
574 METHOD(netlink_socket_t
, destroy
, void,
575 private_netlink_socket_t
*this)
577 if (this->socket
!= -1)
581 lib
->watcher
->remove(lib
->watcher
, this->socket
);
585 this->entries
->destroy(this->entries
);
586 this->mutex
->destroy(this->mutex
);
591 * Described in header
593 u_int
netlink_get_buflen()
597 buflen
= lib
->settings
->get_int(lib
->settings
,
598 "%s.plugins.kernel-netlink.buflen", 0, lib
->ns
);
601 long pagesize
= sysconf(_SC_PAGESIZE
);
607 /* base this on NLMSG_GOODSIZE */
608 buflen
= min(pagesize
, 8192);
614 * Described in header
616 netlink_socket_t
*netlink_socket_create(int protocol
, enum_name_t
*names
,
619 private_netlink_socket_t
*this;
620 struct sockaddr_nl addr
= {
621 .nl_family
= AF_NETLINK
,
623 bool force_buf
= FALSE
;
628 .send
= _netlink_send
,
629 .send_ack
= _netlink_send_ack
,
633 .mutex
= mutex_create(MUTEX_TYPE_RECURSIVE
),
634 .socket
= socket(AF_NETLINK
, SOCK_RAW
, protocol
),
635 .entries
= hashtable_create(hashtable_hash_ptr
, hashtable_equals_ptr
, 4),
636 .protocol
= protocol
,
638 .buflen
= netlink_get_buflen(),
639 .timeout
= lib
->settings
->get_int(lib
->settings
,
640 "%s.plugins.kernel-netlink.timeout", 0, lib
->ns
),
641 .retries
= lib
->settings
->get_int(lib
->settings
,
642 "%s.plugins.kernel-netlink.retries", 0, lib
->ns
),
643 .ignore_retransmit_errors
= lib
->settings
->get_bool(lib
->settings
,
644 "%s.plugins.kernel-netlink.ignore_retransmit_errors",
646 .parallel
= parallel
,
649 if (this->socket
== -1)
651 DBG1(DBG_KNL
, "unable to create netlink socket: %s (%d)",
652 strerror(errno
), errno
);
656 if (bind(this->socket
, (struct sockaddr
*)&addr
, sizeof(addr
)))
658 DBG1(DBG_KNL
, "unable to bind netlink socket: %s (%d)",
659 strerror(errno
), errno
);
663 rcvbuf_size
= lib
->settings
->get_int(lib
->settings
,
664 "%s.plugins.kernel-netlink.receive_buffer_size",
665 rcvbuf_size
, lib
->ns
);
670 force_buf
= lib
->settings
->get_bool(lib
->settings
,
671 "%s.plugins.kernel-netlink.force_receive_buffer_size",
673 optname
= force_buf
? SO_RCVBUFFORCE
: SO_RCVBUF
;
675 if (setsockopt(this->socket
, SOL_SOCKET
, optname
, &rcvbuf_size
,
676 sizeof(rcvbuf_size
)) == -1)
678 DBG1(DBG_KNL
, "failed to %supdate receive buffer size to %d: %s",
679 force_buf
? "forcibly " : "", rcvbuf_size
, strerror(errno
));
684 lib
->watcher
->add(lib
->watcher
, this->socket
, WATCHER_READ
, watch
, this);
687 return &this->public;
691 * Described in header.
693 void netlink_add_attribute(struct nlmsghdr
*hdr
, int rta_type
, chunk_t data
,
698 if (NLMSG_ALIGN(hdr
->nlmsg_len
) + RTA_LENGTH(data
.len
) > buflen
)
700 DBG1(DBG_KNL
, "unable to add attribute, buffer too small");
704 rta
= (struct rtattr
*)(((char*)hdr
) + NLMSG_ALIGN(hdr
->nlmsg_len
));
705 rta
->rta_type
= rta_type
;
706 rta
->rta_len
= RTA_LENGTH(data
.len
);
707 memcpy(RTA_DATA(rta
), data
.ptr
, data
.len
);
708 hdr
->nlmsg_len
= NLMSG_ALIGN(hdr
->nlmsg_len
) + RTA_ALIGN(rta
->rta_len
);
712 * Described in header.
714 void* netlink_reserve(struct nlmsghdr
*hdr
, int buflen
, int type
, int len
)
718 if (NLMSG_ALIGN(hdr
->nlmsg_len
) + RTA_LENGTH(len
) > buflen
)
720 DBG1(DBG_KNL
, "unable to add attribute, buffer too small");
724 rta
= ((void*)hdr
) + NLMSG_ALIGN(hdr
->nlmsg_len
);
725 rta
->rta_type
= type
;
726 rta
->rta_len
= RTA_LENGTH(len
);
727 hdr
->nlmsg_len
= NLMSG_ALIGN(hdr
->nlmsg_len
) + RTA_ALIGN(rta
->rta_len
);
729 return RTA_DATA(rta
);