unet-cli: strip initial newline in usage message
[project/unetd.git] / rtnl.c
1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3 * Copyright (C) 2022 Felix Fietkau <nbd@nbd.name>
4 */
5 #define _GNU_SOURCE
6 #include <sys/types.h>
7 #include <sys/socket.h>
8 #include <netlink/msg.h>
9 #include <netlink/attr.h>
10 #include <netlink/socket.h>
11 #include <linux/rtnetlink.h>
12 #include "unetd.h"
13
14 static struct nl_sock *rtnl;
15 bool rtnl_ignore_errors;
16
17 static int
18 unetd_nl_error_cb(struct sockaddr_nl *nla, struct nlmsgerr *err,
19 void *arg)
20 {
21 struct nlmsghdr *nlh = (struct nlmsghdr *) err - 1;
22 struct nlattr *tb[NLMSGERR_ATTR_MAX + 1];
23 struct nlattr *attrs;
24 int ack_len = sizeof(*nlh) + sizeof(int) + sizeof(*nlh);
25 int len = nlh->nlmsg_len;
26 const char *errstr = "(unknown)";
27
28 if (rtnl_ignore_errors)
29 return NL_STOP;
30
31 if (!(nlh->nlmsg_flags & NLM_F_ACK_TLVS))
32 return NL_STOP;
33
34 if (!(nlh->nlmsg_flags & NLM_F_CAPPED))
35 ack_len += err->msg.nlmsg_len - sizeof(*nlh);
36
37 attrs = (void *) ((unsigned char *) nlh + ack_len);
38 len -= ack_len;
39
40 nla_parse(tb, NLMSGERR_ATTR_MAX, attrs, len, NULL);
41 if (tb[NLMSGERR_ATTR_MSG])
42 errstr = nla_data(tb[NLMSGERR_ATTR_MSG]);
43
44 D("Netlink error(%d): %s\n", err->error, errstr);
45
46 return NL_STOP;
47 }
48
49 int rtnl_call(struct nl_msg *msg)
50 {
51 int ret;
52
53 ret = nl_send_auto_complete(rtnl, msg);
54 nlmsg_free(msg);
55
56 if (ret < 0)
57 return ret;
58
59 return nl_wait_for_ack(rtnl);
60 }
61
62 int rtnl_init(void)
63 {
64 int fd, opt;
65
66 if (rtnl)
67 return 0;
68
69 rtnl = nl_socket_alloc();
70 if (!rtnl)
71 return -1;
72
73 if (nl_connect(rtnl, NETLINK_ROUTE))
74 goto free;
75
76 nl_socket_disable_seq_check(rtnl);
77 nl_socket_set_buffer_size(rtnl, 65536, 0);
78 nl_cb_err(nl_socket_get_cb(rtnl), NL_CB_CUSTOM, unetd_nl_error_cb, NULL);
79
80 fd = nl_socket_get_fd(rtnl);
81
82 opt = 1;
83 setsockopt(fd, SOL_NETLINK, NETLINK_EXT_ACK, &opt, sizeof(opt));
84
85 opt = 1;
86 setsockopt(fd, SOL_NETLINK, NETLINK_CAP_ACK, &opt, sizeof(opt));
87
88 return 0;
89
90 free:
91 nl_socket_free(rtnl);
92 rtnl = NULL;
93 return -1;
94 }