netlink-socket.c revision 9c5a882b7fc256ddc0b227677fa06546f0e944a8
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering This file is part of systemd.
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering Copyright 2013 Tom Gundersen <teg@jklm.no>
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering systemd is free software; you can redistribute it and/or modify it
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering under the terms of the GNU Lesser General Public License as published by
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering the Free Software Foundation; either version 2.1 of the License, or
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering (at your option) any later version.
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering systemd is distributed in the hope that it will be useful, but
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering WITHOUT ANY WARRANTY; without even the implied warranty of
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering Lesser General Public License for more details.
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering You should have received a copy of the GNU Lesser General Public License
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering along with systemd; If not, see <http://www.gnu.org/licenses/>.
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering fd = socket(PF_NETLINK, SOCK_RAW|SOCK_CLOEXEC|SOCK_NONBLOCK, family);
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poetteringstatic int broadcast_groups_get(sd_netlink *nl) {
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering unsigned i, j;
eed857b71702f8551b46b66b31fa0d08583cf23cLennart Poettering r = getsockopt(nl->fd, SOL_NETLINK, NETLINK_LIST_MEMBERSHIPS, NULL, &len);
d74fb368b18f0fbd9a4fe6f15691bbea7f3c4a01Tom Gundersen r = getsockopt(nl->fd, SOL_NETLINK, NETLINK_LIST_MEMBERSHIPS, groups, &len);
0b58db658b5c3f586ac3a837427f1f7fec2abb2eLennart Poettering r = hashmap_ensure_allocated(&nl->broadcast_group_refs, NULL);
eed857b71702f8551b46b66b31fa0d08583cf23cLennart Poettering for (i = 0; i < len; i++) {
0b58db658b5c3f586ac3a837427f1f7fec2abb2eLennart Poettering for (j = 0; j < sizeof(uint32_t) * 8; j ++) {
eed857b71702f8551b46b66b31fa0d08583cf23cLennart Poettering group = i * sizeof(uint32_t) * 8 + j + 1;
0b58db658b5c3f586ac3a837427f1f7fec2abb2eLennart Poettering r = hashmap_put(nl->broadcast_group_refs, UINT_TO_PTR(group), UINT_TO_PTR(1));
3e684349c2cead2e6fd2f816c34eb17daba23a49Lennart Poettering r = setsockopt(nl->fd, SOL_NETLINK, NETLINK_PKTINFO, &one, sizeof(one));
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering r = bind(nl->fd, &nl->sockaddr.sa, addrlen);
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering /* ignore EINVAL to allow opening an already bound socket */
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poettering r = getsockname(nl->fd, &nl->sockaddr.sa, &addrlen);
0eac462399c8e87bcce252cf058eba9f2678f2bdLennart Poetteringstatic unsigned broadcast_group_get_ref(sd_netlink *nl, unsigned group) {
0eac462399c8e87bcce252cf058eba9f2678f2bdLennart Poettering return PTR_TO_UINT(hashmap_get(nl->broadcast_group_refs, UINT_TO_PTR(group)));
74b2466e14a1961bf3ac0e8a60cfaceec705bd59Lennart Poetteringstatic int broadcast_group_set_ref(sd_netlink *nl, unsigned group, unsigned n_ref) {
0eac462399c8e87bcce252cf058eba9f2678f2bdLennart Poettering r = hashmap_replace(nl->broadcast_group_refs, UINT_TO_PTR(group), UINT_TO_PTR(n_ref));
0eac462399c8e87bcce252cf058eba9f2678f2bdLennart Poetteringstatic int broadcast_group_join(sd_netlink *nl, unsigned group) {
eed857b71702f8551b46b66b31fa0d08583cf23cLennart Poettering r = setsockopt(nl->fd, SOL_NETLINK, NETLINK_ADD_MEMBERSHIP, &group, sizeof(group));
eed857b71702f8551b46b66b31fa0d08583cf23cLennart Poetteringint socket_broadcast_group_ref(sd_netlink *nl, unsigned group) {
eed857b71702f8551b46b66b31fa0d08583cf23cLennart Poettering n_ref = broadcast_group_get_ref(nl, group);
0eac462399c8e87bcce252cf058eba9f2678f2bdLennart Poettering r = hashmap_ensure_allocated(&nl->broadcast_group_refs, NULL);
0eac462399c8e87bcce252cf058eba9f2678f2bdLennart Poettering r = broadcast_group_set_ref(nl, group, n_ref);
91b14d6ff362b938a72db17b095ee9903d07381bTom Gundersen /* not yet in the group */
0b58db658b5c3f586ac3a837427f1f7fec2abb2eLennart Poetteringstatic int broadcast_group_leave(sd_netlink *nl, unsigned group) {
0b58db658b5c3f586ac3a837427f1f7fec2abb2eLennart Poettering r = setsockopt(nl->fd, SOL_NETLINK, NETLINK_DROP_MEMBERSHIP, &group, sizeof(group));
0b58db658b5c3f586ac3a837427f1f7fec2abb2eLennart Poetteringint socket_broadcast_group_unref(sd_netlink *nl, unsigned group) {
0b58db658b5c3f586ac3a837427f1f7fec2abb2eLennart Poettering n_ref = broadcast_group_get_ref(nl, group);
0b58db658b5c3f586ac3a837427f1f7fec2abb2eLennart Poettering r = broadcast_group_set_ref(nl, group, n_ref);
d74fb368b18f0fbd9a4fe6f15691bbea7f3c4a01Tom Gundersen /* still refs left */
d74fb368b18f0fbd9a4fe6f15691bbea7f3c4a01Tom Gundersen/* returns the number of bytes sent, or a negative error code */
d74fb368b18f0fbd9a4fe6f15691bbea7f3c4a01Tom Gundersenint socket_write_message(sd_netlink *nl, sd_netlink_message *m) {
9df3ba6c6cb65eecec06f39dfe85a3596cedac4eTom Gundersenstatic int socket_recv_message(int fd, struct iovec *iov, uint32_t *_group, bool peek) {
9df3ba6c6cb65eecec06f39dfe85a3596cedac4eTom Gundersen uint8_t cmsg_buffer[CMSG_SPACE(sizeof(struct nl_pktinfo))];
4e0b8b17a7465653f4e7b819dad5f8e30d64c0c4Tom Gundersen r = recvmsg(fd, &msg, MSG_TRUNC | (peek ? MSG_PEEK : 0));
4e0b8b17a7465653f4e7b819dad5f8e30d64c0c4Tom Gundersen /* no data */
4e0b8b17a7465653f4e7b819dad5f8e30d64c0c4Tom Gundersen log_debug("rtnl: kernel receive buffer overrun");
b652d4a2099d1c167584dcc1d179d47c58dc38a2Lennart Poettering return (errno == EAGAIN || errno == EINTR) ? 0 : -errno;
b652d4a2099d1c167584dcc1d179d47c58dc38a2Lennart Poettering /* not from the kernel, ignore */
b652d4a2099d1c167584dcc1d179d47c58dc38a2Lennart Poettering log_debug("rtnl: ignoring message from portid %"PRIu32, sender.nl.nl_pid);
b652d4a2099d1c167584dcc1d179d47c58dc38a2Lennart Poettering /* drop the message */
be808ea083fa07271116b4519c3c27fd20c5f077Tom Gundersen return (errno == EAGAIN || errno == EINTR) ? 0 : -errno;
be808ea083fa07271116b4519c3c27fd20c5f077Tom Gundersen cmsg->cmsg_len == CMSG_LEN(sizeof(struct nl_pktinfo))) {
be808ea083fa07271116b4519c3c27fd20c5f077Tom Gundersen struct nl_pktinfo *pktinfo = (void *)CMSG_DATA(cmsg);
be808ea083fa07271116b4519c3c27fd20c5f077Tom Gundersen /* multi-cast group */
be808ea083fa07271116b4519c3c27fd20c5f077Tom Gundersen/* On success, the number of bytes received is returned and *ret points to the received message
be808ea083fa07271116b4519c3c27fd20c5f077Tom Gundersen * which has a valid header and the correct size.
be808ea083fa07271116b4519c3c27fd20c5f077Tom Gundersen * If nothing useful was received 0 is returned.
be808ea083fa07271116b4519c3c27fd20c5f077Tom Gundersen * On failure, a negative error code is returned.
be808ea083fa07271116b4519c3c27fd20c5f077Tom Gundersen _cleanup_netlink_message_unref_ sd_netlink_message *first = NULL;
be808ea083fa07271116b4519c3c27fd20c5f077Tom Gundersen unsigned i = 0;
be808ea083fa07271116b4519c3c27fd20c5f077Tom Gundersen assert(rtnl->rbuffer_allocated >= sizeof(struct nlmsghdr));
be808ea083fa07271116b4519c3c27fd20c5f077Tom Gundersen /* read nothing, just get the pending message size */
be808ea083fa07271116b4519c3c27fd20c5f077Tom Gundersen r = socket_recv_message(rtnl->fd, &iov, NULL, true);
519ef04651b07a547f010d6462603669d7fde4e5Lennart Poettering /* make room for the pending message */
519ef04651b07a547f010d6462603669d7fde4e5Lennart Poettering if (!greedy_realloc((void **)&rtnl->rbuffer,
519ef04651b07a547f010d6462603669d7fde4e5Lennart Poettering /* read the pending message */
519ef04651b07a547f010d6462603669d7fde4e5Lennart Poettering r = socket_recv_message(rtnl->fd, &iov, &group, false);
519ef04651b07a547f010d6462603669d7fde4e5Lennart Poettering /* message did not fit in read buffer */
519ef04651b07a547f010d6462603669d7fde4e5Lennart Poettering if (NLMSG_OK(rtnl->rbuffer, len) && rtnl->rbuffer->nlmsg_flags & NLM_F_MULTI) {
519ef04651b07a547f010d6462603669d7fde4e5Lennart Poettering for (i = 0; i < rtnl->rqueue_partial_size; i++) {
519ef04651b07a547f010d6462603669d7fde4e5Lennart Poettering if (rtnl_message_get_serial(rtnl->rqueue_partial[i]) ==
87f5a19343acf8ba697acc5a62bdb1a2b8c9eda3Lennart Poettering for (new_msg = rtnl->rbuffer; NLMSG_OK(new_msg, len) && !done; new_msg = NLMSG_NEXT(new_msg, len)) {
b826ab586c9e0a9c0d438a75c28cf3a8ab485929Tom Gundersen _cleanup_netlink_message_unref_ sd_netlink_message *m = NULL;
87f5a19343acf8ba697acc5a62bdb1a2b8c9eda3Lennart Poettering if (!group && new_msg->nlmsg_pid != rtnl->sockaddr.nl.nl_pid)
d5099efc47d4e6ac60816b5381a5f607ab03f06eMichal Schmidt /* not broadcast and not for us */
87f5a19343acf8ba697acc5a62bdb1a2b8c9eda3Lennart Poettering /* silently drop noop messages */
87f5a19343acf8ba697acc5a62bdb1a2b8c9eda3Lennart Poettering /* finished reading multi-part message */
d5099efc47d4e6ac60816b5381a5f607ab03f06eMichal Schmidt /* if first is not defined, put NLMSG_DONE into the receive queue. */
4b95f1798f22c1bb75295f448188560cb6ec9eceLennart Poettering /* check that we support this message type */
4b95f1798f22c1bb75295f448188560cb6ec9eceLennart Poettering r = type_system_get_type(&type_system_root, &nl_type, new_msg->nlmsg_type);
4b95f1798f22c1bb75295f448188560cb6ec9eceLennart Poettering log_debug("sd-netlink: ignored message with unknown type: %i",
0eac462399c8e87bcce252cf058eba9f2678f2bdLennart Poettering /* check that the size matches the message type */
0eac462399c8e87bcce252cf058eba9f2678f2bdLennart Poettering if (new_msg->nlmsg_len < NLMSG_LENGTH(type_get_size(nl_type))) {
4b95f1798f22c1bb75295f448188560cb6ec9eceLennart Poettering log_debug("sd-netlink: message larger than expected, dropping");
636e813dc98ea40c58c6c85bc5e7e3c9f0904ea2Lennart Poettering m->hdr = memdup(new_msg, new_msg->nlmsg_len);
4b95f1798f22c1bb75295f448188560cb6ec9eceLennart Poettering /* seal and parse the top-level message */
4b95f1798f22c1bb75295f448188560cb6ec9eceLennart Poettering /* push the message onto the multi-part message stack */
4b95f1798f22c1bb75295f448188560cb6ec9eceLennart Poettering log_debug("sd-netlink: discarding %zu bytes of incoming message", len);
f2f1dbe50fea13abadc9c1e845a29031b90b40f3Lennart Poettering /* we got a complete message, push it on the read queue */
4b95f1798f22c1bb75295f448188560cb6ec9eceLennart Poettering rtnl->rqueue[rtnl->rqueue_size ++] = first;
4b95f1798f22c1bb75295f448188560cb6ec9eceLennart Poettering if (multi_part && (i < rtnl->rqueue_partial_size)) {
4b95f1798f22c1bb75295f448188560cb6ec9eceLennart Poettering /* remove the message form the partial read queue */
4b95f1798f22c1bb75295f448188560cb6ec9eceLennart Poettering memmove(rtnl->rqueue_partial + i,rtnl->rqueue_partial + i + 1,
4b95f1798f22c1bb75295f448188560cb6ec9eceLennart Poettering sizeof(sd_netlink_message*) * (rtnl->rqueue_partial_size - i - 1));
f2f1dbe50fea13abadc9c1e845a29031b90b40f3Lennart Poettering /* we only got a partial multi-part message, push it on the
f2f1dbe50fea13abadc9c1e845a29031b90b40f3Lennart Poettering partial read queue */