babeld: add ubus bindings
[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 route_stream *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 void 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
78 // Appends an exported route message entry to the buffer.
79 static void babeld_add_xroute_buf(struct xroute *xroute, struct blob_buf *b) {
80 void *prefix;
81
82 prefix = blobmsg_open_table(b, format_prefix(xroute->prefix, xroute->plen));
83
84 blobmsg_add_string(b, "src-prefix",
85 format_prefix(xroute->src_prefix, xroute->src_plen));
86 blobmsg_add_u32(b, "metric", xroute->metric);
87 blobmsg_close_table(b, prefix);
88 }
89
90 // Sends an exported routes message on ubus socket, splitting apart IPv4 and IPv6 routes.
91 static void babeld_ubus_get_xroutes(struct ubus_context *ctx_local,
92 struct ubus_object *obj,
93 struct ubus_request_data *req,
94 const char *method, struct blob_attr *msg) {
95 struct blob_buf b = {0};
96 struct xroute_stream *xroutes;
97 struct xroute_list_entry *cur, *tmp;
98 void *ipv4, *ipv6;
99 int ret;
100 LIST_HEAD(xroute_ipv4_list);
101 LIST_HEAD(xroute_ipv6_list);
102
103 xroutes = xroute_stream();
104 if (xroutes) {
105 while (1) {
106 struct xroute *xroute = xroute_stream_next(xroutes);
107 if (xroute == NULL)
108 break;
109
110 struct xroute_list_entry *xr =
111 calloc(1, sizeof(struct xroute_list_entry));
112 xr->xroute = xroute;
113
114 if (v4mapped(xroute->prefix)) {
115 list_add(&xr->list, &xroute_ipv4_list);
116 } else {
117 list_add(&xr->list, &xroute_ipv6_list);
118 }
119 }
120 xroute_stream_done(xroutes);
121 }
122
123 blob_buf_init(&b, 0);
124 ipv4 = blobmsg_open_table(&b, "IPv4");
125 list_for_each_entry_safe(cur, tmp, &xroute_ipv4_list, list) {
126 babeld_add_xroute_buf(cur->xroute, &b);
127 list_del(&cur->list);
128 free(cur);
129 }
130 blobmsg_close_table(&b, ipv4);
131
132 ipv6 = blobmsg_open_table(&b, "IPv6");
133 list_for_each_entry_safe(cur, tmp, &xroute_ipv6_list, list) {
134 babeld_add_xroute_buf(cur->xroute, &b);
135 list_del(&cur->list);
136 free(cur);
137 }
138 blobmsg_close_table(&b, ipv6);
139
140 ret = ubus_send_reply(ctx_local, req, b.head);
141 if (ret)
142 fprintf(stderr, "Failed to send reply: %s\n", ubus_strerror(ret));
143 }
144
145 // Appends an route message entry to the buffer.
146 static void babeld_add_route_buf(struct babel_route *route,
147 struct blob_buf *b) {
148 void *prefix;
149 char channels[100];
150
151 if (route->channels_len == 0) {
152 channels[0] = '\0';
153 } else {
154 int i, j = 0;
155 snprintf(channels, sizeof(channels), " chan (");
156 j = strlen(channels);
157 for (i = 0; i < route->channels_len; i++) {
158 if (i > 0)
159 channels[j++] = ',';
160 snprintf(channels + j, sizeof(channels) - j, "%u",
161 (unsigned)route->channels[i]);
162 j = strlen(channels);
163 }
164 snprintf(channels + j, sizeof(channels) - j, ")");
165 }
166
167 prefix = blobmsg_open_table(
168 b, format_prefix(route->src->prefix, route->src->plen));
169
170 blobmsg_add_string(
171 b, "src-prefix",
172 format_prefix(route->src->src_prefix, route->src->src_plen));
173 blobmsg_add_u32(b, "route_metric", route_metric(route));
174 blobmsg_add_u32(b, "route_smoothed_metric", route_smoothed_metric(route));
175 blobmsg_add_u32(b, "refmetric", route->refmetric);
176 blobmsg_add_string(b, "id", format_eui64(route->src->id));
177 blobmsg_add_u32(b, "seqno", (uint32_t)route->seqno);
178 blobmsg_add_string(b, "channels", channels);
179 blobmsg_add_u32(b, "age", (int)(now.tv_sec - route->time));
180 blobmsg_add_string(b, "via", format_address(route->neigh->address));
181 if (memcmp(route->nexthop, route->neigh->address, 16) != 0)
182 blobmsg_add_string(b, "nexthop", format_address(route->nexthop));
183
184 blobmsg_add_u8(b, "installed", route->installed);
185 blobmsg_add_u8(b, "feasible", route_feasible(route));
186
187 blobmsg_close_table(b, prefix);
188 }
189
190 // Sends received routes message on ubus socket, splitting apart IPv4 and IPv6 routes.
191 static void babeld_ubus_get_routes(struct ubus_context *ctx_local,
192 struct ubus_object *obj,
193 struct ubus_request_data *req,
194 const char *method, struct blob_attr *msg) {
195 struct blob_buf b = {0};
196 struct route_stream *routes;
197 struct route_list_entry *cur, *tmp;
198 void *prefix, *ipv4, *ipv6;
199 int ret;
200 LIST_HEAD(route_ipv4_list);
201 LIST_HEAD(route_ipv6_list);
202
203 blob_buf_init(&b, 0);
204
205 routes = route_stream(ROUTE_ALL);
206 if (routes) {
207 while (1) {
208 struct babel_route *route = route_stream_next(routes);
209 if (route == NULL)
210 break;
211 struct route_list_entry *r = calloc(1, sizeof(struct route_list_entry));
212 r->route = route;
213
214 if (v4mapped(route->src->prefix)) {
215 list_add(&r->list, &route_ipv4_list);
216 } else {
217 list_add(&r->list, &route_ipv6_list);
218 }
219 }
220 route_stream_done(routes);
221 }
222
223 blob_buf_init(&b, 0);
224 ipv4 = blobmsg_open_table(&b, "IPv4");
225 list_for_each_entry_safe(cur, tmp, &route_ipv4_list, list) {
226 babeld_add_route_buf(cur->route, &b);
227 list_del(&cur->list);
228 free(cur);
229 }
230 blobmsg_close_table(&b, ipv4);
231
232 ipv6 = blobmsg_open_table(&b, "IPv6");
233 list_for_each_entry_safe(cur, tmp, &route_ipv6_list, list) {
234 babeld_add_route_buf(cur->route, &b);
235 list_del(&cur->list);
236 free(cur);
237 }
238 blobmsg_close_table(&b, ipv6);
239
240 ret = ubus_send_reply(ctx_local, req, b.head);
241 if (ret)
242 fprintf(stderr, "Failed to send reply: %s\n", ubus_strerror(ret));
243 }
244
245 // Appends an neighbour entry to the buffer.
246 static void babeld_add_neighbour_buf(struct neighbour *neigh,
247 struct blob_buf *b) {
248 void *neighbour;
249
250 neighbour = blobmsg_open_table(b, format_address(neigh->address));
251 blobmsg_add_string(b, "dev", neigh->ifp->name);
252 blobmsg_add_u32(b, "hello-reach", neigh->hello.reach);
253 blobmsg_add_u32(b, "uhello-reach", neigh->uhello.reach);
254 blobmsg_add_u32(b, "rxcost", neighbour_rxcost(neigh));
255 blobmsg_add_u32(b, "txcost", neigh->txcost);
256 blobmsg_add_u32(b, "rtt", format_thousands(neigh->rtt));
257 blobmsg_add_u32(b, "channel", neigh->ifp->channel);
258 blobmsg_add_u8(b, "if_up", if_up(neigh->ifp));
259 blobmsg_close_table(b, neighbour);
260 }
261
262 // Sends neighbours message on ubus socket, splitting apart IPv4 and IPv6 neighbours.
263 static void babeld_ubus_get_neighbours(struct ubus_context *ctx_local,
264 struct ubus_object *obj,
265 struct ubus_request_data *req,
266 const char *method,
267 struct blob_attr *msg) {
268 struct blob_buf b = {0};
269 struct neighbour *neigh;
270 struct neighbour_list_entry *cur, *tmp;
271 void *ipv4, *ipv6;
272 int ret;
273 LIST_HEAD(neighbour_ipv4_list);
274 LIST_HEAD(neighbour_ipv6_list);
275
276 blob_buf_init(&b, 0);
277
278 FOR_ALL_NEIGHBOURS(neigh) {
279 struct neighbour_list_entry *n =
280 calloc(1, sizeof(struct neighbour_list_entry));
281 n->neighbour = neigh;
282 if (v4mapped(neigh->address)) {
283 list_add(&n->list, &neighbour_ipv4_list);
284 } else {
285 list_add(&n->list, &neighbour_ipv6_list);
286 }
287 }
288
289 blob_buf_init(&b, 0);
290
291 ipv4 = blobmsg_open_table(&b, "IPv4");
292 list_for_each_entry_safe(cur, tmp, &neighbour_ipv4_list, list) {
293 babeld_add_neighbour_buf(cur->neighbour, &b);
294 list_del(&cur->list);
295 free(cur);
296 }
297 blobmsg_close_table(&b, ipv4);
298
299 ipv6 = blobmsg_open_table(&b, "IPv6");
300 list_for_each_entry_safe(cur, tmp, &neighbour_ipv6_list, list) {
301 babeld_add_neighbour_buf(cur->neighbour, &b);
302 list_del(&cur->list);
303 free(cur);
304 }
305 blobmsg_close_table(&b, ipv6);
306
307 ret = ubus_send_reply(ctx_local, req, b.head);
308 if (ret)
309 fprintf(stderr, "Failed to send reply: %s\n", ubus_strerror(ret));
310 }
311
312 // List of functions we expose via the ubus bus.
313 static const struct ubus_method babeld_methods[] = {
314 UBUS_METHOD_NOARG("get_info", babeld_ubus_babeld_info),
315 UBUS_METHOD_NOARG("get_xroutes", babeld_ubus_get_xroutes),
316 UBUS_METHOD_NOARG("get_routes", babeld_ubus_get_routes),
317 UBUS_METHOD_NOARG("get_neighbours", babeld_ubus_get_neighbours),
318 };
319
320 // Definition of the ubus object type.
321 static struct ubus_object_type babeld_object_type =
322 UBUS_OBJECT_TYPE("babeld", babeld_methods);
323
324 // Object we announce via the ubus bus.
325 static struct ubus_object babeld_object = {
326 .name = "babeld",
327 .type = &babeld_object_type,
328 .methods = babeld_methods,
329 .n_methods = ARRAY_SIZE(babeld_methods),
330 };
331
332 // Registers handlers for babel methods in the global ubus context.
333 static bool ubus_init_object() {
334 int ret;
335
336 ret = ubus_add_object(shared_ctx, &babeld_object);
337 if (ret) {
338 fprintf(stderr, "Failed to add object: %s\n", ubus_strerror(ret));
339 return false;
340 }
341
342 return true;
343 }
344
345 // Initializes the global ubus context, connecting to the bus to be able to receive and send messages.
346 static bool babeld_ubus_init(void) {
347 if (shared_ctx)
348 return true;
349
350 shared_ctx = ubus_connect(NULL);
351 if (!shared_ctx)
352 return false;
353
354 return true;
355 }
356
357 void babeld_ubus_receive(fd_set *readfds) {
358 if (!shared_ctx)
359 return;
360 if (FD_ISSET(shared_ctx->sock.fd, readfds))
361 ubus_handle_event(shared_ctx);
362 }
363
364 int babeld_ubus_add_read_sock(fd_set *readfds, int maxfd) {
365 if (!shared_ctx)
366 return maxfd;
367
368 FD_SET(shared_ctx->sock.fd, readfds);
369 return MAX(maxfd, shared_ctx->sock.fd);
370 }
371
372 bool babeld_add_ubus() {
373 if (!babeld_ubus_init()) {
374 fprintf(stderr, "Failed to initialize ubus!\n");
375 return false;
376 }
377
378 if (!ubus_init_object()) {
379 fprintf(stderr, "Failed to add objects to ubus!\n");
380 return false;
381 }
382
383 return true;
384 }