]> git.ipfire.org Git - people/jschlag/network.git/blame - src/libnetwork/libnetwork.c
libnetwork: Properly handle errors from netlink messages
[people/jschlag/network.git] / src / libnetwork / libnetwork.c
CommitLineData
01f2b2e4
MT
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
6f4814db 21#include <ctype.h>
0e880048 22#include <errno.h>
c07b958a 23#include <linux/netlink.h>
e145b2f3 24#include <linux/nl80211.h>
83d23950
MT
25#include <netlink/genl/ctrl.h>
26#include <netlink/genl/genl.h>
27#include <netlink/netlink.h>
6f4814db
MT
28#include <stdarg.h>
29#include <stdio.h>
0e880048 30#include <stdlib.h>
6f4814db
MT
31#include <string.h>
32#include <syslog.h>
0e880048 33
01f2b2e4 34#include <network/libnetwork.h>
6f4814db 35#include <network/logging.h>
01f2b2e4
MT
36#include "libnetwork-private.h"
37
0e880048
MT
38struct network_ctx {
39 int refcount;
6f4814db
MT
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;
83d23950
MT
46
47 // Netlink
48 struct nl_sock* nl_socket;
49 int nl80211_id;
0e880048
MT
50};
51
6f4814db
MT
52void 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
62static 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
69static 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
83d23950
MT
89static 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
e145b2f3
MT
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
83d23950
MT
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
125static 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
0e880048
MT
135NETWORK_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
6f4814db
MT
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
83d23950
MT
151 // Initiate netlink connection
152 int r = init_netlink(c);
153 if (r) {
154 network_free(c);
155 return r;
156 }
157
6f4814db
MT
158 INFO(c, "network ctx %p created\n", c);
159 DEBUG(c, "log_priority=%d\n", c->log_priority);
160
0e880048
MT
161 *ctx = c;
162 return 0;
163}
164
165NETWORK_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
0e880048
MT
173NETWORK_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
6f4814db
MT
184NETWORK_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
191NETWORK_EXPORT int network_get_log_priority(struct network_ctx* ctx) {
192 return ctx->log_priority;
193}
194
195NETWORK_EXPORT void network_set_log_priority(struct network_ctx* ctx, int priority) {
196 ctx->log_priority = priority;
197}
198
01f2b2e4
MT
199NETWORK_EXPORT const char* network_version() {
200 return "network " VERSION;
201}
e145b2f3
MT
202
203// Creates a netlink message that can be sent with network_send_netlink_message
204struct 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
c07b958a
MT
218struct network_netlink_message_status {
219 struct network_ctx* ctx;
220 int r;
221};
222
e145b2f3 223static int __nl_ack_handler(struct nl_msg* msg, void* data) {
c07b958a
MT
224 struct network_netlink_message_status* status = data;
225 status->r = 0;
e145b2f3
MT
226
227 return NL_STOP;
228}
229
230static int __nl_finish_handler(struct nl_msg* msg, void* data) {
c07b958a
MT
231 struct network_netlink_message_status* status = data;
232 status->r = 0;
e145b2f3
MT
233
234 return NL_SKIP;
235}
236
c07b958a
MT
237static 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
e145b2f3
MT
273// Sends a netlink message and calls the handler to handle the result
274int network_send_netlink_message(struct network_ctx* ctx, struct nl_msg* msg,
275 int(*handler)(struct nl_msg* msg, void* data), void* data) {
c07b958a
MT
276 struct network_netlink_message_status status;
277 status.ctx = ctx;
278
e145b2f3
MT
279 DEBUG(ctx, "Sending netlink message %p\n", msg);
280
281 // Sending the message
c07b958a
MT
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;
e145b2f3
MT
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
c07b958a 297 status.r = 1;
e145b2f3
MT
298
299 nl_cb_set(callback, NL_CB_VALID, NL_CB_CUSTOM, handler, data);
c07b958a
MT
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);
e145b2f3 303
c07b958a 304 while (status.r > 0)
e145b2f3
MT
305 nl_recvmsgs(ctx->nl_socket, callback);
306
c07b958a 307 DEBUG(ctx, "Netlink message returned with status %d\n", status.r);
e145b2f3 308
c07b958a 309 return status.r;
e145b2f3 310}