c97ee0a18bee4c4a27a86271bce6542fc88b32ce
[feed/routing.git] / babeld / src / ubus.c
1 #include <stdint.h>
2 #include <stdlib.h>
3 #include <sys/select.h>
4
5 #include <libubox/blob.h>
6 #include <libubox/blobmsg.h>
7 #include <libubox/list.h>
8
9 #include <arpa/inet.h>
10 #include <net/if.h>
11 #include <netinet/in.h>
12 #include <sys/ioctl.h>
13 #include <sys/socket.h>
14
15 #include "babeld.h"
16 #include "configuration.h"
17 #include "interface.h"
18 #include "kernel.h"
19 #include "local.h"
20 #include "message.h"
21 #include "neighbour.h"
22 #include "net.h"
23 #include "resend.h"
24 #include "route.h"
25 #include "rule.h"
26 #include "source.h"
27 #include "util.h"
28 #include "version.h"
29 #include "xroute.h"
30
31 #include "ubus.h"
32
33 // Definition of header variable whether to enable ubus bindings.
34 int ubus_bindings = 0;
35
36 // Shared state maintained throughout calls to handle ubus messages.
37 static struct ubus_context *shared_ctx;
38
39 // List of exported routes (to be used with ubox's list helpers).
40 struct xroute_list_entry {
41 struct list_head list;
42 struct xroute *xroute;
43 };
44
45 // List of received routes (to be used with ubox's list helpers).
46 struct route_list_entry {
47 struct list_head list;
48 struct babel_route *route;
49 };
50
51 // List of neighbours (to be used with ubox's list helpers).
52 struct neighbour_list_entry {
53 struct list_head list;
54 struct neighbour *neighbour;
55 };
56
57 // Sends a babel info message on ubus socket.
58 static int babeld_ubus_babeld_info(struct ubus_context *ctx_local,
59 struct ubus_object *obj,
60 struct ubus_request_data *req,
61 const char *method, struct blob_attr *msg) {
62 struct blob_buf b = {0};
63 void *prefix;
64 char host[64];
65 int ret;
66
67 blob_buf_init(&b, 0);
68 blobmsg_add_string(&b, "babeld-version", BABELD_VERSION);
69 blobmsg_add_string(&b, "my-id", format_eui64(myid));
70 if (!gethostname(host, sizeof(host)))
71 blobmsg_add_string(&b, "host", host);
72
73 ret = ubus_send_reply(ctx_local, req, b.head);
74 if (ret)
75 fprintf(stderr, "Failed to send reply: %s\n", ubus_strerror(ret));
76
77 return ret;
78 }
79
80 // Appends an exported route message entry to the buffer.
81 static void babeld_add_xroute_buf(struct xroute *xroute, struct blob_buf *b) {
82 void *prefix;
83
84 prefix = blobmsg_open_table(b, format_prefix(xroute->prefix, xroute->plen));
85
86 blobmsg_add_string(b, "src-prefix",
87 format_prefix(xroute->src_prefix, xroute->src_plen));
88 blobmsg_add_u32(b, "metric", xroute->metric);
89 blobmsg_close_table(b, prefix);
90 }
91
92 // Sends an exported routes message on ubus socket, splitting apart IPv4 and
93 // IPv6 routes.
94 static int babeld_ubus_get_xroutes(struct ubus_context *ctx_local,
95 struct ubus_object *obj,
96 struct ubus_request_data *req,
97 const char *method, struct blob_attr *msg) {
98 struct blob_buf b = {0};
99 struct xroute_stream *xroutes;
100 struct xroute_list_entry *cur, *tmp;
101 void *ipv4, *ipv6;
102 int ret;
103 LIST_HEAD(xroute_ipv4_list);
104 LIST_HEAD(xroute_ipv6_list);
105
106 xroutes = xroute_stream();
107 if (xroutes) {
108 while (1) {
109 struct xroute *xroute = xroute_stream_next(xroutes);
110 if (xroute == NULL)
111 break;
112
113 struct xroute_list_entry *xr =
114 calloc(1, sizeof(struct xroute_list_entry));
115 xr->xroute = xroute;
116
117 if (v4mapped(xroute->prefix)) {
118 list_add(&xr->list, &xroute_ipv4_list);
119 } else {
120 list_add(&xr->list, &xroute_ipv6_list);
121 }
122 }
123 xroute_stream_done(xroutes);
124 }
125
126 blob_buf_init(&b, 0);
127 ipv4 = blobmsg_open_table(&b, "IPv4");
128 list_for_each_entry_safe(cur, tmp, &xroute_ipv4_list, list) {
129 babeld_add_xroute_buf(cur->xroute, &b);
130 list_del(&cur->list);
131 free(cur);
132 }
133 blobmsg_close_table(&b, ipv4);
134
135 ipv6 = blobmsg_open_table(&b, "IPv6");
136 list_for_each_entry_safe(cur, tmp, &xroute_ipv6_list, list) {
137 babeld_add_xroute_buf(cur->xroute, &b);
138 list_del(&cur->list);
139 free(cur);
140 }
141 blobmsg_close_table(&b, ipv6);
142
143 ret = ubus_send_reply(ctx_local, req, b.head);
144 if (ret)
145 fprintf(stderr, "Failed to send reply: %s\n", ubus_strerror(ret));
146
147 return ret;
148 }
149
150 // Appends an route message entry to the buffer.
151 static void babeld_add_route_buf(struct babel_route *route,
152 struct blob_buf *b) {
153 void *prefix;
154 char channels[100];
155
156 if (route->channels_len == 0) {
157 channels[0] = '\0';
158 } else {
159 int i, j = 0;
160 snprintf(channels, sizeof(channels), " chan (");
161 j = strlen(channels);
162 for (i = 0; i < route->channels_len; i++) {
163 if (i > 0)
164 channels[j++] = ',';
165 snprintf(channels + j, sizeof(channels) - j, "%u",
166 (unsigned)route->channels[i]);
167 j = strlen(channels);
168 }
169 snprintf(channels + j, sizeof(channels) - j, ")");
170 }
171
172 prefix = blobmsg_open_table(
173 b, format_prefix(route->src->prefix, route->src->plen));
174
175 blobmsg_add_string(
176 b, "src-prefix",
177 format_prefix(route->src->src_prefix, route->src->src_plen));
178 blobmsg_add_u32(b, "route_metric", route_metric(route));
179 blobmsg_add_u32(b, "route_smoothed_metric", route_smoothed_metric(route));
180 blobmsg_add_u32(b, "refmetric", route->refmetric);
181 blobmsg_add_string(b, "id", format_eui64(route->src->id));
182 blobmsg_add_u32(b, "seqno", (uint32_t)route->seqno);
183 blobmsg_add_string(b, "channels", channels);
184 blobmsg_add_u32(b, "age", (int)(now.tv_sec - route->time));
185 blobmsg_add_string(b, "via", format_address(route->neigh->address));
186 if (memcmp(route->nexthop, route->neigh->address, 16) != 0)
187 blobmsg_add_string(b, "nexthop", format_address(route->nexthop));
188
189 blobmsg_add_u8(b, "installed", route->installed);
190 blobmsg_add_u8(b, "feasible", route_feasible(route));
191
192 blobmsg_close_table(b, prefix);
193 }
194
195 // Sends received routes message on ubus socket, splitting apart IPv4 and IPv6
196 // routes.
197 static int babeld_ubus_get_routes(struct ubus_context *ctx_local,
198 struct ubus_object *obj,
199 struct ubus_request_data *req,
200 const char *method, struct blob_attr *msg) {
201 struct blob_buf b = {0};
202 struct route_stream *routes;
203 struct route_list_entry *cur, *tmp;
204 void *prefix, *ipv4, *ipv6;
205 int ret;
206 LIST_HEAD(route_ipv4_list);
207 LIST_HEAD(route_ipv6_list);
208
209 blob_buf_init(&b, 0);
210
211 routes = route_stream(ROUTE_ALL);
212 if (routes) {
213 while (1) {
214 struct babel_route *route = route_stream_next(routes);
215 if (route == NULL)
216 break;
217 struct route_list_entry *r = calloc(1, sizeof(struct route_list_entry));
218 r->route = route;
219
220 if (v4mapped(route->src->prefix)) {
221 list_add(&r->list, &route_ipv4_list);
222 } else {
223 list_add(&r->list, &route_ipv6_list);
224 }
225 }
226 route_stream_done(routes);
227 }
228
229 blob_buf_init(&b, 0);
230 ipv4 = blobmsg_open_table(&b, "IPv4");
231 list_for_each_entry_safe(cur, tmp, &route_ipv4_list, list) {
232 babeld_add_route_buf(cur->route, &b);
233 list_del(&cur->list);
234 free(cur);
235 }
236 blobmsg_close_table(&b, ipv4);
237
238 ipv6 = blobmsg_open_table(&b, "IPv6");
239 list_for_each_entry_safe(cur, tmp, &route_ipv6_list, list) {
240 babeld_add_route_buf(cur->route, &b);
241 list_del(&cur->list);
242 free(cur);
243 }
244 blobmsg_close_table(&b, ipv6);
245
246 ret = ubus_send_reply(ctx_local, req, b.head);
247 if (ret)
248 fprintf(stderr, "Failed to send reply: %s\n", ubus_strerror(ret));
249
250 return ret;
251 }
252
253 // Appends an neighbour entry to the buffer.
254 static void babeld_add_neighbour_buf(struct neighbour *neigh,
255 struct blob_buf *b) {
256 void *neighbour;
257
258 neighbour = blobmsg_open_table(b, format_address(neigh->address));
259 blobmsg_add_string(b, "dev", neigh->ifp->name);
260 blobmsg_add_u32(b, "hello-reach", neigh->hello.reach);
261 blobmsg_add_u32(b, "uhello-reach", neigh->uhello.reach);
262 blobmsg_add_u32(b, "rxcost", neighbour_rxcost(neigh));
263 blobmsg_add_u32(b, "txcost", neigh->txcost);
264 blobmsg_add_string(b, "rtt", format_thousands(neigh->rtt));
265 blobmsg_add_u32(b, "channel", neigh->ifp->channel);
266 blobmsg_add_u8(b, "if_up", if_up(neigh->ifp));
267 blobmsg_close_table(b, neighbour);
268 }
269
270 // Sends neighbours message on ubus socket, splitting apart IPv4 and IPv6
271 // neighbours.
272 static int babeld_ubus_get_neighbours(struct ubus_context *ctx_local,
273 struct ubus_object *obj,
274 struct ubus_request_data *req,
275 const char *method,
276 struct blob_attr *msg) {
277 struct blob_buf b = {0};
278 struct neighbour *neigh;
279 struct neighbour_list_entry *cur, *tmp;
280 void *ipv4, *ipv6;
281 int ret;
282 LIST_HEAD(neighbour_ipv4_list);
283 LIST_HEAD(neighbour_ipv6_list);
284
285 blob_buf_init(&b, 0);
286
287 FOR_ALL_NEIGHBOURS(neigh) {
288 struct neighbour_list_entry *n =
289 calloc(1, sizeof(struct neighbour_list_entry));
290 n->neighbour = neigh;
291 if (v4mapped(neigh->address)) {
292 list_add(&n->list, &neighbour_ipv4_list);
293 } else {
294 list_add(&n->list, &neighbour_ipv6_list);
295 }
296 }
297
298 blob_buf_init(&b, 0);
299
300 ipv4 = blobmsg_open_table(&b, "IPv4");
301 list_for_each_entry_safe(cur, tmp, &neighbour_ipv4_list, list) {
302 babeld_add_neighbour_buf(cur->neighbour, &b);
303 list_del(&cur->list);
304 free(cur);
305 }
306 blobmsg_close_table(&b, ipv4);
307
308 ipv6 = blobmsg_open_table(&b, "IPv6");
309 list_for_each_entry_safe(cur, tmp, &neighbour_ipv6_list, list) {
310 babeld_add_neighbour_buf(cur->neighbour, &b);
311 list_del(&cur->list);
312 free(cur);
313 }
314 blobmsg_close_table(&b, ipv6);
315
316 ret = ubus_send_reply(ctx_local, req, b.head);
317 if (ret)
318 fprintf(stderr, "Failed to send reply: %s\n", ubus_strerror(ret));
319
320 return ret;
321 }
322
323 // List of functions we expose via the ubus bus.
324 static const struct ubus_method babeld_methods[] = {
325 UBUS_METHOD_NOARG("get_info", babeld_ubus_babeld_info),
326 UBUS_METHOD_NOARG("get_xroutes", babeld_ubus_get_xroutes),
327 UBUS_METHOD_NOARG("get_routes", babeld_ubus_get_routes),
328 UBUS_METHOD_NOARG("get_neighbours", babeld_ubus_get_neighbours),
329 };
330
331 // Definition of the ubus object type.
332 static struct ubus_object_type babeld_object_type =
333 UBUS_OBJECT_TYPE("babeld", babeld_methods);
334
335 // Object we announce via the ubus bus.
336 static struct ubus_object babeld_object = {
337 .name = "babeld",
338 .type = &babeld_object_type,
339 .methods = babeld_methods,
340 .n_methods = ARRAY_SIZE(babeld_methods),
341 };
342
343 // Registers handlers for babel methods in the global ubus context.
344 static bool ubus_init_object() {
345 int ret;
346
347 ret = ubus_add_object(shared_ctx, &babeld_object);
348 if (ret) {
349 fprintf(stderr, "Failed to add object: %s\n", ubus_strerror(ret));
350 return false;
351 }
352
353 return true;
354 }
355
356 // Initializes the global ubus context, connecting to the bus to be able to
357 // receive and send messages.
358 static bool babeld_ubus_init(void) {
359 if (shared_ctx)
360 return true;
361
362 shared_ctx = ubus_connect(NULL);
363 if (!shared_ctx)
364 return false;
365
366 return true;
367 }
368
369 void ubus_notify_route(struct babel_route *route, int kind) {
370 struct blob_buf b = {0};
371 char method[50]; // possible methods are route.change, route.add, route.flush
372
373 if (!babeld_object.has_subscribers)
374 return;
375
376 if (!route)
377 return;
378
379 if (!shared_ctx)
380 return;
381
382 blob_buf_init(&b, 0);
383 babeld_add_route_buf(route, &b);
384 snprintf(method, sizeof(method), "route.%s", local_kind(kind));
385 ubus_notify(shared_ctx, &babeld_object, method, b.head, -1);
386 }
387
388 void ubus_notify_xroute(struct xroute *xroute, int kind) {
389 struct blob_buf b = {0};
390 char method[50]; // possible methods are xroute.change, xroute.add,
391 // xroute.flush
392
393 if (!babeld_object.has_subscribers)
394 return;
395
396 if (!xroute)
397 return;
398
399 if (!shared_ctx)
400 return;
401
402 blob_buf_init(&b, 0);
403 babeld_add_xroute_buf(xroute, &b);
404 snprintf(method, sizeof(method), "xroute.%s", local_kind(kind));
405 ubus_notify(shared_ctx, &babeld_object, method, b.head, -1);
406 }
407
408 void ubus_notify_neighbour(struct neighbour *neigh, int kind) {
409 struct blob_buf b = {0};
410 char method[50]; // possible methods are neigh.change, neigh.add, neigh.flush
411
412 if (!babeld_object.has_subscribers)
413 return;
414
415 if (!neigh)
416 return;
417
418 if (!shared_ctx)
419 return;
420
421 blob_buf_init(&b, 0);
422 babeld_add_neighbour_buf(neigh, &b);
423 snprintf(method, sizeof(method), "neigh.%s", local_kind(kind));
424 ubus_notify(shared_ctx, &babeld_object, method, b.head, -1);
425 }
426
427 void babeld_ubus_receive(fd_set *readfds) {
428 if (!shared_ctx)
429 return;
430 if (FD_ISSET(shared_ctx->sock.fd, readfds))
431 ubus_handle_event(shared_ctx);
432 }
433
434 int babeld_ubus_add_read_sock(fd_set *readfds, int maxfd) {
435 if (!shared_ctx)
436 return maxfd;
437
438 FD_SET(shared_ctx->sock.fd, readfds);
439 return MAX(maxfd, shared_ctx->sock.fd);
440 }
441
442 bool babeld_add_ubus() {
443 if (!babeld_ubus_init()) {
444 fprintf(stderr, "Failed to initialize ubus!\n");
445 return false;
446 }
447
448 if (!ubus_init_object()) {
449 fprintf(stderr, "Failed to add objects to ubus!\n");
450 return false;
451 }
452
453 return true;
454 }