--- /dev/null
+--- a/babeld.c
++++ b/babeld.c
+@@ -55,6 +55,8 @@ THE SOFTWARE.
+ #include "rule.h"
+ #include "version.h"
+
++#include "ubus.h"
++
+ struct timeval now;
+
+ unsigned char myid[8];
+@@ -536,6 +538,9 @@ main(int argc, char **argv)
+ }
+ }
+
++ if(ubus_bindings)
++ babeld_add_ubus();
++
+ init_signals();
+ rc = resize_receive_buffer(1500);
+ if(rc < 0)
+@@ -635,6 +640,8 @@ main(int argc, char **argv)
+ FD_SET(local_sockets[i].fd, &readfds);
+ maxfd = MAX(maxfd, local_sockets[i].fd);
+ }
++ if(ubus_bindings)
++ maxfd = babeld_ubus_add_read_sock(&readfds, maxfd);
+ rc = select(maxfd + 1, &readfds, NULL, NULL, &tv);
+ if(rc < 0) {
+ if(errno != EINTR) {
+@@ -703,6 +710,9 @@ main(int argc, char **argv)
+ i++;
+ }
+
++ if(ubus_bindings)
++ babeld_ubus_receive(&readfds);
++
+ if(reopening) {
+ kernel_dump_time = now.tv_sec;
+ check_neighbours_timeout = now;
+--- a/Makefile
++++ b/Makefile
+@@ -11,11 +11,11 @@ LDLIBS = -lrt
+
+ SRCS = babeld.c net.c kernel.c util.c interface.c source.c neighbour.c \
+ route.c xroute.c message.c resend.c configuration.c local.c \
+- disambiguation.c rule.c
++ disambiguation.c rule.c ubus.c
+
+ OBJS = babeld.o net.o kernel.o util.o interface.o source.o neighbour.o \
+ route.o xroute.o message.o resend.o configuration.o local.o \
+- disambiguation.o rule.o
++ disambiguation.o rule.o ubus.o
+
+ babeld: $(OBJS)
+ $(CC) $(CFLAGS) $(LDFLAGS) -o babeld $(OBJS) $(LDLIBS)
+--- a/generate-version.sh
++++ b/generate-version.sh
+@@ -10,4 +10,4 @@ else
+ version="unknown"
+ fi
+
+-echo "#define BABELD_VERSION \"$version\""
++echo "#define BABELD_VERSION \"$version-ubus-mod\""
+--- a/configuration.c
++++ b/configuration.c
+@@ -41,6 +41,7 @@ THE SOFTWARE.
+ #include "kernel.h"
+ #include "configuration.h"
+ #include "rule.h"
++#include "ubus.h"
+
+ static struct filter *input_filters = NULL;
+ static struct filter *output_filters = NULL;
+@@ -850,7 +851,8 @@ parse_option(int c, gnc_t gnc, void *clo
+ strcmp(token, "daemonise") == 0 ||
+ strcmp(token, "skip-kernel-setup") == 0 ||
+ strcmp(token, "ipv6-subtrees") == 0 ||
+- strcmp(token, "reflect-kernel-metric") == 0) {
++ strcmp(token, "reflect-kernel-metric") == 0 ||
++ strcmp(token, "ubus-bindings") == 0) {
+ int b;
+ c = getbool(c, &b, gnc, closure);
+ if(c < -1)
+@@ -868,6 +870,8 @@ parse_option(int c, gnc_t gnc, void *clo
+ has_ipv6_subtrees = b;
+ else if(strcmp(token, "reflect-kernel-metric") == 0)
+ reflect_kernel_metric = b;
++ else if(strcmp(token, "ubus-bindings") == 0)
++ ubus_bindings = b;
+ else
+ abort();
+ } else if(strcmp(token, "protocol-group") == 0) {
--- /dev/null
+#include <stdint.h>
+#include <stdlib.h>
+#include <sys/select.h>
+
+#include <libubox/blob.h>
+#include <libubox/blobmsg.h>
+#include <libubox/list.h>
+
+#include <arpa/inet.h>
+#include <net/if.h>
+#include <netinet/in.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+
+#include "babeld.h"
+#include "configuration.h"
+#include "interface.h"
+#include "kernel.h"
+#include "local.h"
+#include "message.h"
+#include "neighbour.h"
+#include "net.h"
+#include "resend.h"
+#include "route.h"
+#include "rule.h"
+#include "source.h"
+#include "util.h"
+#include "version.h"
+#include "xroute.h"
+
+#include "ubus.h"
+
+// Definition of header variable whether to enable ubus bindings.
+int ubus_bindings = 0;
+
+// Shared state maintained throughout calls to handle ubus messages.
+static struct ubus_context *shared_ctx;
+
+// List of exported routes (to be used with ubox's list helpers).
+struct xroute_list_entry {
+ struct list_head list;
+ struct xroute *xroute;
+};
+
+// List of received routes (to be used with ubox's list helpers).
+struct route_list_entry {
+ struct list_head list;
+ struct route_stream *route;
+};
+
+// List of neighbours (to be used with ubox's list helpers).
+struct neighbour_list_entry {
+ struct list_head list;
+ struct neighbour *neighbour;
+};
+
+// Sends a babel info message on ubus socket.
+static void babeld_ubus_babeld_info(struct ubus_context *ctx_local,
+ struct ubus_object *obj,
+ struct ubus_request_data *req,
+ const char *method, struct blob_attr *msg) {
+ struct blob_buf b = {0};
+ void *prefix;
+ char host[64];
+ int ret;
+
+ blob_buf_init(&b, 0);
+ blobmsg_add_string(&b, "babeld-version", BABELD_VERSION);
+ blobmsg_add_string(&b, "my-id", format_eui64(myid));
+ if (!gethostname(host, sizeof(host)))
+ blobmsg_add_string(&b, "host", host);
+
+ ret = ubus_send_reply(ctx_local, req, b.head);
+ if (ret)
+ fprintf(stderr, "Failed to send reply: %s\n", ubus_strerror(ret));
+}
+
+// Appends an exported route message entry to the buffer.
+static void babeld_add_xroute_buf(struct xroute *xroute, struct blob_buf *b) {
+ void *prefix;
+
+ prefix = blobmsg_open_table(b, format_prefix(xroute->prefix, xroute->plen));
+
+ blobmsg_add_string(b, "src-prefix",
+ format_prefix(xroute->src_prefix, xroute->src_plen));
+ blobmsg_add_u32(b, "metric", xroute->metric);
+ blobmsg_close_table(b, prefix);
+}
+
+// Sends an exported routes message on ubus socket, splitting apart IPv4 and IPv6 routes.
+static void babeld_ubus_get_xroutes(struct ubus_context *ctx_local,
+ struct ubus_object *obj,
+ struct ubus_request_data *req,
+ const char *method, struct blob_attr *msg) {
+ struct blob_buf b = {0};
+ struct xroute_stream *xroutes;
+ struct xroute_list_entry *cur, *tmp;
+ void *ipv4, *ipv6;
+ int ret;
+ LIST_HEAD(xroute_ipv4_list);
+ LIST_HEAD(xroute_ipv6_list);
+
+ xroutes = xroute_stream();
+ if (xroutes) {
+ while (1) {
+ struct xroute *xroute = xroute_stream_next(xroutes);
+ if (xroute == NULL)
+ break;
+
+ struct xroute_list_entry *xr =
+ calloc(1, sizeof(struct xroute_list_entry));
+ xr->xroute = xroute;
+
+ if (v4mapped(xroute->prefix)) {
+ list_add(&xr->list, &xroute_ipv4_list);
+ } else {
+ list_add(&xr->list, &xroute_ipv6_list);
+ }
+ }
+ xroute_stream_done(xroutes);
+ }
+
+ blob_buf_init(&b, 0);
+ ipv4 = blobmsg_open_table(&b, "IPv4");
+ list_for_each_entry_safe(cur, tmp, &xroute_ipv4_list, list) {
+ babeld_add_xroute_buf(cur->xroute, &b);
+ list_del(&cur->list);
+ free(cur);
+ }
+ blobmsg_close_table(&b, ipv4);
+
+ ipv6 = blobmsg_open_table(&b, "IPv6");
+ list_for_each_entry_safe(cur, tmp, &xroute_ipv6_list, list) {
+ babeld_add_xroute_buf(cur->xroute, &b);
+ list_del(&cur->list);
+ free(cur);
+ }
+ blobmsg_close_table(&b, ipv6);
+
+ ret = ubus_send_reply(ctx_local, req, b.head);
+ if (ret)
+ fprintf(stderr, "Failed to send reply: %s\n", ubus_strerror(ret));
+}
+
+// Appends an route message entry to the buffer.
+static void babeld_add_route_buf(struct babel_route *route,
+ struct blob_buf *b) {
+ void *prefix;
+ char channels[100];
+
+ if (route->channels_len == 0) {
+ channels[0] = '\0';
+ } else {
+ int i, j = 0;
+ snprintf(channels, sizeof(channels), " chan (");
+ j = strlen(channels);
+ for (i = 0; i < route->channels_len; i++) {
+ if (i > 0)
+ channels[j++] = ',';
+ snprintf(channels + j, sizeof(channels) - j, "%u",
+ (unsigned)route->channels[i]);
+ j = strlen(channels);
+ }
+ snprintf(channels + j, sizeof(channels) - j, ")");
+ }
+
+ prefix = blobmsg_open_table(
+ b, format_prefix(route->src->prefix, route->src->plen));
+
+ blobmsg_add_string(
+ b, "src-prefix",
+ format_prefix(route->src->src_prefix, route->src->src_plen));
+ blobmsg_add_u32(b, "route_metric", route_metric(route));
+ blobmsg_add_u32(b, "route_smoothed_metric", route_smoothed_metric(route));
+ blobmsg_add_u32(b, "refmetric", route->refmetric);
+ blobmsg_add_string(b, "id", format_eui64(route->src->id));
+ blobmsg_add_u32(b, "seqno", (uint32_t)route->seqno);
+ blobmsg_add_string(b, "channels", channels);
+ blobmsg_add_u32(b, "age", (int)(now.tv_sec - route->time));
+ blobmsg_add_string(b, "via", format_address(route->neigh->address));
+ if (memcmp(route->nexthop, route->neigh->address, 16) != 0)
+ blobmsg_add_string(b, "nexthop", format_address(route->nexthop));
+
+ blobmsg_add_u8(b, "installed", route->installed);
+ blobmsg_add_u8(b, "feasible", route_feasible(route));
+
+ blobmsg_close_table(b, prefix);
+}
+
+// Sends received routes message on ubus socket, splitting apart IPv4 and IPv6 routes.
+static void babeld_ubus_get_routes(struct ubus_context *ctx_local,
+ struct ubus_object *obj,
+ struct ubus_request_data *req,
+ const char *method, struct blob_attr *msg) {
+ struct blob_buf b = {0};
+ struct route_stream *routes;
+ struct route_list_entry *cur, *tmp;
+ void *prefix, *ipv4, *ipv6;
+ int ret;
+ LIST_HEAD(route_ipv4_list);
+ LIST_HEAD(route_ipv6_list);
+
+ blob_buf_init(&b, 0);
+
+ routes = route_stream(ROUTE_ALL);
+ if (routes) {
+ while (1) {
+ struct babel_route *route = route_stream_next(routes);
+ if (route == NULL)
+ break;
+ struct route_list_entry *r = calloc(1, sizeof(struct route_list_entry));
+ r->route = route;
+
+ if (v4mapped(route->src->prefix)) {
+ list_add(&r->list, &route_ipv4_list);
+ } else {
+ list_add(&r->list, &route_ipv6_list);
+ }
+ }
+ route_stream_done(routes);
+ }
+
+ blob_buf_init(&b, 0);
+ ipv4 = blobmsg_open_table(&b, "IPv4");
+ list_for_each_entry_safe(cur, tmp, &route_ipv4_list, list) {
+ babeld_add_route_buf(cur->route, &b);
+ list_del(&cur->list);
+ free(cur);
+ }
+ blobmsg_close_table(&b, ipv4);
+
+ ipv6 = blobmsg_open_table(&b, "IPv6");
+ list_for_each_entry_safe(cur, tmp, &route_ipv6_list, list) {
+ babeld_add_route_buf(cur->route, &b);
+ list_del(&cur->list);
+ free(cur);
+ }
+ blobmsg_close_table(&b, ipv6);
+
+ ret = ubus_send_reply(ctx_local, req, b.head);
+ if (ret)
+ fprintf(stderr, "Failed to send reply: %s\n", ubus_strerror(ret));
+}
+
+// Appends an neighbour entry to the buffer.
+static void babeld_add_neighbour_buf(struct neighbour *neigh,
+ struct blob_buf *b) {
+ void *neighbour;
+
+ neighbour = blobmsg_open_table(b, format_address(neigh->address));
+ blobmsg_add_string(b, "dev", neigh->ifp->name);
+ blobmsg_add_u32(b, "hello-reach", neigh->hello.reach);
+ blobmsg_add_u32(b, "uhello-reach", neigh->uhello.reach);
+ blobmsg_add_u32(b, "rxcost", neighbour_rxcost(neigh));
+ blobmsg_add_u32(b, "txcost", neigh->txcost);
+ blobmsg_add_u32(b, "rtt", format_thousands(neigh->rtt));
+ blobmsg_add_u32(b, "channel", neigh->ifp->channel);
+ blobmsg_add_u8(b, "if_up", if_up(neigh->ifp));
+ blobmsg_close_table(b, neighbour);
+}
+
+// Sends neighbours message on ubus socket, splitting apart IPv4 and IPv6 neighbours.
+static void babeld_ubus_get_neighbours(struct ubus_context *ctx_local,
+ struct ubus_object *obj,
+ struct ubus_request_data *req,
+ const char *method,
+ struct blob_attr *msg) {
+ struct blob_buf b = {0};
+ struct neighbour *neigh;
+ struct neighbour_list_entry *cur, *tmp;
+ void *ipv4, *ipv6;
+ int ret;
+ LIST_HEAD(neighbour_ipv4_list);
+ LIST_HEAD(neighbour_ipv6_list);
+
+ blob_buf_init(&b, 0);
+
+ FOR_ALL_NEIGHBOURS(neigh) {
+ struct neighbour_list_entry *n =
+ calloc(1, sizeof(struct neighbour_list_entry));
+ n->neighbour = neigh;
+ if (v4mapped(neigh->address)) {
+ list_add(&n->list, &neighbour_ipv4_list);
+ } else {
+ list_add(&n->list, &neighbour_ipv6_list);
+ }
+ }
+
+ blob_buf_init(&b, 0);
+
+ ipv4 = blobmsg_open_table(&b, "IPv4");
+ list_for_each_entry_safe(cur, tmp, &neighbour_ipv4_list, list) {
+ babeld_add_neighbour_buf(cur->neighbour, &b);
+ list_del(&cur->list);
+ free(cur);
+ }
+ blobmsg_close_table(&b, ipv4);
+
+ ipv6 = blobmsg_open_table(&b, "IPv6");
+ list_for_each_entry_safe(cur, tmp, &neighbour_ipv6_list, list) {
+ babeld_add_neighbour_buf(cur->neighbour, &b);
+ list_del(&cur->list);
+ free(cur);
+ }
+ blobmsg_close_table(&b, ipv6);
+
+ ret = ubus_send_reply(ctx_local, req, b.head);
+ if (ret)
+ fprintf(stderr, "Failed to send reply: %s\n", ubus_strerror(ret));
+}
+
+// List of functions we expose via the ubus bus.
+static const struct ubus_method babeld_methods[] = {
+ UBUS_METHOD_NOARG("get_info", babeld_ubus_babeld_info),
+ UBUS_METHOD_NOARG("get_xroutes", babeld_ubus_get_xroutes),
+ UBUS_METHOD_NOARG("get_routes", babeld_ubus_get_routes),
+ UBUS_METHOD_NOARG("get_neighbours", babeld_ubus_get_neighbours),
+};
+
+// Definition of the ubus object type.
+static struct ubus_object_type babeld_object_type =
+ UBUS_OBJECT_TYPE("babeld", babeld_methods);
+
+// Object we announce via the ubus bus.
+static struct ubus_object babeld_object = {
+ .name = "babeld",
+ .type = &babeld_object_type,
+ .methods = babeld_methods,
+ .n_methods = ARRAY_SIZE(babeld_methods),
+};
+
+// Registers handlers for babel methods in the global ubus context.
+static bool ubus_init_object() {
+ int ret;
+
+ ret = ubus_add_object(shared_ctx, &babeld_object);
+ if (ret) {
+ fprintf(stderr, "Failed to add object: %s\n", ubus_strerror(ret));
+ return false;
+ }
+
+ return true;
+}
+
+// Initializes the global ubus context, connecting to the bus to be able to receive and send messages.
+static bool babeld_ubus_init(void) {
+ if (shared_ctx)
+ return true;
+
+ shared_ctx = ubus_connect(NULL);
+ if (!shared_ctx)
+ return false;
+
+ return true;
+}
+
+void babeld_ubus_receive(fd_set *readfds) {
+ if (!shared_ctx)
+ return;
+ if (FD_ISSET(shared_ctx->sock.fd, readfds))
+ ubus_handle_event(shared_ctx);
+}
+
+int babeld_ubus_add_read_sock(fd_set *readfds, int maxfd) {
+ if (!shared_ctx)
+ return maxfd;
+
+ FD_SET(shared_ctx->sock.fd, readfds);
+ return MAX(maxfd, shared_ctx->sock.fd);
+}
+
+bool babeld_add_ubus() {
+ if (!babeld_ubus_init()) {
+ fprintf(stderr, "Failed to initialize ubus!\n");
+ return false;
+ }
+
+ if (!ubus_init_object()) {
+ fprintf(stderr, "Failed to add objects to ubus!\n");
+ return false;
+ }
+
+ return true;
+}