]> git.ipfire.org Git - network.git/blob - src/libnetwork/libnetwork.c
libnetwork: Properly handle errors from netlink messages
[network.git] / src / libnetwork / libnetwork.c
1 /*#############################################################################
2 # #
3 # IPFire.org - A linux based firewall #
4 # Copyright (C) 2017 IPFire Network Development Team #
5 # #
6 # This program is free software: you can redistribute it and/or modify #
7 # it under the terms of the GNU General Public License as published by #
8 # the Free Software Foundation, either version 3 of the License, or #
9 # (at your option) any later version. #
10 # #
11 # This program is distributed in the hope that it will be useful, #
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of #
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
14 # GNU General Public License for more details. #
15 # #
16 # You should have received a copy of the GNU General Public License #
17 # along with this program. If not, see <http://www.gnu.org/licenses/>. #
18 # #
19 #############################################################################*/
20
21 #include <ctype.h>
22 #include <errno.h>
23 #include <linux/netlink.h>
24 #include <linux/nl80211.h>
25 #include <netlink/genl/ctrl.h>
26 #include <netlink/genl/genl.h>
27 #include <netlink/netlink.h>
28 #include <stdarg.h>
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <string.h>
32 #include <syslog.h>
33
34 #include <network/libnetwork.h>
35 #include <network/logging.h>
36 #include "libnetwork-private.h"
37
38 struct network_ctx {
39 int refcount;
40
41 // Logging
42 void (*log_fn)(struct network_ctx* ctx,
43 int priority, const char *file, int line, const char *fn,
44 const char *format, va_list args);
45 int log_priority;
46
47 // Netlink
48 struct nl_sock* nl_socket;
49 int nl80211_id;
50 };
51
52 void network_log(struct network_ctx* ctx,
53 int priority, const char* file, int line, const char* fn,
54 const char* format, ...) {
55 va_list args;
56
57 va_start(args, format);
58 ctx->log_fn(ctx, priority, file, line, fn, format, args);
59 va_end(args);
60 }
61
62 static void log_stderr(struct network_ctx* ctx,
63 int priority, const char* file, int line, const char* fn,
64 const char* format, va_list args) {
65 fprintf(stderr, "libnetwork: %s: ", fn);
66 vfprintf(stderr, format, args);
67 }
68
69 static int log_priority(const char* priority) {
70 char *endptr;
71
72 int prio = strtol(priority, &endptr, 10);
73
74 if (endptr[0] == '\0' || isspace(endptr[0]))
75 return prio;
76
77 if (strncmp(priority, "err", 3) == 0)
78 return LOG_ERR;
79
80 if (strncmp(priority, "info", 4) == 0)
81 return LOG_INFO;
82
83 if (strncmp(priority, "debug", 5) == 0)
84 return LOG_DEBUG;
85
86 return 0;
87 }
88
89 static int init_netlink(struct network_ctx* ctx) {
90 // Allocate netlink socket
91 ctx->nl_socket = nl_socket_alloc();
92 if (!ctx->nl_socket) {
93 ERROR(ctx, "Failed to allocate netlink socket\n");
94 return -ENOMEM;
95 }
96
97 // Connect the socket
98 if (genl_connect(ctx->nl_socket)) {
99 ERROR(ctx, "Failed to connect to generic netlink");
100 return -ENOLINK;
101 }
102
103 // Set buffer size
104 nl_socket_set_buffer_size(ctx->nl_socket, 8192, 8192);
105
106 // Register socket callback
107 struct nl_cb* callback = nl_cb_alloc(NL_CB_DEFAULT);
108 if (!callback) {
109 ERROR(ctx, "Could not allocate socket callback\n");
110 return -ENOMEM;
111 }
112
113 nl_socket_set_cb(ctx->nl_socket, callback);
114
115 // Get nl80211 id
116 ctx->nl80211_id = genl_ctrl_resolve(ctx->nl_socket, "nl80211");
117 if (ctx->nl80211_id < 0) {
118 ERROR(ctx, "Could not find nl80211\n");
119 return -ENOENT;
120 }
121
122 return 0;
123 }
124
125 static void network_free(struct network_ctx* ctx) {
126 DEBUG(ctx, "network ctx %p released\n", ctx);
127
128 // Free netlink socket
129 if (ctx->nl_socket)
130 nl_socket_free(ctx->nl_socket);
131
132 free(ctx);
133 }
134
135 NETWORK_EXPORT int network_new(struct network_ctx** ctx) {
136 struct network_ctx* c = calloc(1, sizeof(*c));
137 if (!c)
138 return -ENOMEM;
139
140 // Initialise basic variables
141 c->refcount = 1;
142
143 // Initialise logging
144 c->log_fn = log_stderr;
145 c->log_priority = LOG_ERR;
146
147 const char* env = secure_getenv("NETWORK_LOG");
148 if (env)
149 network_set_log_priority(c, log_priority(env));
150
151 // Initiate netlink connection
152 int r = init_netlink(c);
153 if (r) {
154 network_free(c);
155 return r;
156 }
157
158 INFO(c, "network ctx %p created\n", c);
159 DEBUG(c, "log_priority=%d\n", c->log_priority);
160
161 *ctx = c;
162 return 0;
163 }
164
165 NETWORK_EXPORT struct network_ctx* network_ref(struct network_ctx* ctx) {
166 if (!ctx)
167 return NULL;
168
169 ctx->refcount++;
170 return ctx;
171 }
172
173 NETWORK_EXPORT struct network_ctx* network_unref(struct network_ctx* ctx) {
174 if (!ctx)
175 return NULL;
176
177 if (--ctx->refcount > 0)
178 return ctx;
179
180 network_free(ctx);
181 return NULL;
182 }
183
184 NETWORK_EXPORT void network_set_log_fn(struct network_ctx* ctx,
185 void (*log_fn)(struct network_ctx* ctx, int priority, const char* file,
186 int line, const char* fn, const char* format, va_list args)) {
187 ctx->log_fn = log_fn;
188 INFO(ctx, "custom logging function %p registered\n", log_fn);
189 }
190
191 NETWORK_EXPORT int network_get_log_priority(struct network_ctx* ctx) {
192 return ctx->log_priority;
193 }
194
195 NETWORK_EXPORT void network_set_log_priority(struct network_ctx* ctx, int priority) {
196 ctx->log_priority = priority;
197 }
198
199 NETWORK_EXPORT const char* network_version() {
200 return "network " VERSION;
201 }
202
203 // Creates a netlink message that can be sent with network_send_netlink_message
204 struct nl_msg* network_make_netlink_message(struct network_ctx* ctx,
205 enum nl80211_commands cmd, int flags) {
206 // Allocate message
207 struct nl_msg* msg = nlmsg_alloc();
208 if (!msg)
209 return NULL;
210
211 genlmsg_put(msg, 0, 0, ctx->nl80211_id, 0, flags, cmd, 0);
212
213 DEBUG(ctx, "Created new netlink message %p\n", msg);
214
215 return msg;
216 }
217
218 struct network_netlink_message_status {
219 struct network_ctx* ctx;
220 int r;
221 };
222
223 static int __nl_ack_handler(struct nl_msg* msg, void* data) {
224 struct network_netlink_message_status* status = data;
225 status->r = 0;
226
227 return NL_STOP;
228 }
229
230 static int __nl_finish_handler(struct nl_msg* msg, void* data) {
231 struct network_netlink_message_status* status = data;
232 status->r = 0;
233
234 return NL_SKIP;
235 }
236
237 static int __nl_error_handler(struct sockaddr_nl* nla, struct nlmsgerr* err, void* data) {
238 struct network_netlink_message_status* status = data;
239 status->r = 0;
240
241 struct nlattr *attrs;
242 struct nlattr *tb[NLMSGERR_ATTR_MAX + 1];
243 struct nlmsghdr* nlh = (struct nlmsghdr*)err - 1;
244
245 size_t len = nlh->nlmsg_len;
246 size_t ack_len = sizeof(*nlh) + sizeof(int) + sizeof(*nlh);
247
248 status->r = err->error;
249 if (!(nlh->nlmsg_flags & NLM_F_ACK_TLVS))
250 return NL_STOP;
251
252 if (!(nlh->nlmsg_flags & NLM_F_CAPPED))
253 ack_len += err->msg.nlmsg_len - sizeof(*nlh);
254
255 if (len <= ack_len)
256 return NL_STOP;
257
258 attrs = (void *)((unsigned char *)nlh + ack_len);
259 len -= ack_len;
260
261 nla_parse(tb, NLMSGERR_ATTR_MAX, attrs, len, NULL);
262 if (tb[NLMSGERR_ATTR_MSG]) {
263 len = strnlen((char *)nla_data(tb[NLMSGERR_ATTR_MSG]),
264 nla_len(tb[NLMSGERR_ATTR_MSG]));
265
266 ERROR(status->ctx, "Kernel reports: %*s\n", len,
267 (const char *)nla_data(tb[NLMSGERR_ATTR_MSG]));
268 }
269
270 return NL_STOP;
271 }
272
273 // Sends a netlink message and calls the handler to handle the result
274 int network_send_netlink_message(struct network_ctx* ctx, struct nl_msg* msg,
275 int(*handler)(struct nl_msg* msg, void* data), void* data) {
276 struct network_netlink_message_status status;
277 status.ctx = ctx;
278
279 DEBUG(ctx, "Sending netlink message %p\n", msg);
280
281 // Sending the message
282 status.r = nl_send_auto(ctx->nl_socket, msg);
283 if (status.r < 0) {
284 ERROR(ctx, "Error sending netlink message: %d\n", status.r);
285 return status.r;
286 }
287
288 // Register callback
289 struct nl_cb* callback = nl_cb_alloc(NL_CB_DEFAULT);
290 if (!callback) {
291 ERROR(ctx, "Could not allocate callback\n");
292 nlmsg_free(msg);
293
294 return -1;
295 }
296
297 status.r = 1;
298
299 nl_cb_set(callback, NL_CB_VALID, NL_CB_CUSTOM, handler, data);
300 nl_cb_set(callback, NL_CB_ACK, NL_CB_CUSTOM, __nl_ack_handler, &status);
301 nl_cb_set(callback, NL_CB_FINISH, NL_CB_CUSTOM, __nl_finish_handler, &status);
302 nl_cb_err(callback, NL_CB_CUSTOM, __nl_error_handler, &status);
303
304 while (status.r > 0)
305 nl_recvmsgs(ctx->nl_socket, callback);
306
307 DEBUG(ctx, "Netlink message returned with status %d\n", status.r);
308
309 return status.r;
310 }