babeld: add ubus bindings

A better integration of babeld with OpenWrt is to connect the daemon to
the IPC. So far, we can only communicate via a websocket. With ubus we
can send and receive commands in json format.

The commit adds a ubus interface to babeld with following functions:
- get_info
- get_neighbours
- get_xroutes
- get_routes

All output is divided into IPv4 and IPv6.

Ubus has to be enabled by setting "config general"
  option 'ubus_bindings' 'true'

Example:

root@OpenWrt:~# ubus call babeld get_info
{
	"babeld-version": "babeld-1.9.2",
	"my-id": "32:xx:xx:xx:xx:xx:xx:xx",
	"host": "OpenWrt"
}

root@OpenWrt:~# ubus call babeld get_neighbours
{
	"IPv4": {

	},
	"IPv6": {
		"fe80::xx:xx:xx:xxx": {
			"dev": "br-lan",
			"hello-reach": 65408,
			"uhello-reach": 0,
			"rxcost": 96,
			"txcost": 96,
			"rtt": 4338271,
			"channel": -2,
			"if_up": true
		}
	}
}

root@OpenWrt:~# ubus call babeld get_xroutes
{
	"IPv4": {
		"10.0.0.3/32": {
			"src-prefix": "0.0.0.0/0",
			"metric": 0
		},
		"10.0.0.0/24": {
			"src-prefix": "0.0.0.0/0",
			"metric": 0
		}
	},
	"IPv6": {
		"fdfa:xx:xx::1/128": {
			"src-prefix": "::/0",
			"metric": 0
		}
	}
}

root@OpenWrt:~# ubus call babeld get_routes
{
	"IPv4": {
		"10.2.0.1/32": {
			"src-prefix": "0.0.0.0/0",
			"route_metric": 96,
			"route_smoothed_metric": 96,
			"refmetric": 0,
			"id": "62:xx:xx:xx:xx:xx:xx:xx",
			"seqno": 41381,
			"channels": "",
			"age": 17,
			"via": "fe80::xx:xxxx:xxxx:xxxx",
			"nexthop": " nexthop ",
			"installed": true,
			"feasible": true
		},
	"IPv6": {

	}
}

Additional IPC functionality will follow.

Further, we changed the version to $version-ubus-mod.

Signed-off-by: Nick Hainke <vincent@systemli.org>
This commit is contained in:
Nick Hainke 2020-12-17 12:41:32 +01:00 committed by Gabriel Kerneis
parent bb95bffc16
commit ea4a3f28dc
5 changed files with 526 additions and 2 deletions

View File

@ -9,7 +9,7 @@ include $(TOPDIR)/rules.mk
PKG_NAME:=babeld
PKG_VERSION:=1.9.2
PKG_RELEASE:=1
PKG_RELEASE:=2
PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.gz
PKG_SOURCE_URL:=https://www.irif.fr/~jch/software/files/
@ -28,7 +28,7 @@ define Package/babeld
SUBMENU:=Routing and Redirection
TITLE:=A loop-avoiding distance-vector routing protocol
URL:=https://www.irif.fr/~jch/software/babel/
DEPENDS:=@IPV6
DEPENDS:=@IPV6 +libubus +libubox
endef
define Package/babeld/description
@ -47,6 +47,7 @@ endef
MAKE_FLAGS+= \
CFLAGS="$(TARGET_CFLAGS)" \
LDLIBS="" \
LDLIBS+="-lubus -lubox"
define Package/babeld/install
$(INSTALL_DIR) $(1)/usr/sbin

View File

@ -24,6 +24,7 @@ config general
## See comment at the top of this file for more details.
# option 'conf_file' '/etc/babeld.conf'
# option 'conf_dir' '/tmp/babel.d/'
# option 'ubus_bindings' 'false'
config interface
## Remove this line to enable babeld on this interface

View File

@ -0,0 +1,93 @@
--- 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) {

384
babeld/src/ubus.c Normal file
View File

@ -0,0 +1,384 @@
#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;
}

45
babeld/src/ubus.h Normal file
View File

@ -0,0 +1,45 @@
/*
IPC integration of babeld with OpenWrt.
The ubus interface offers following functions:
- get_info
- get_neighbours
- get_xroutes
- get_routes
All output is divided into IPv4 and IPv6.
*/
#include <libubus.h>
// Whether to enable ubus bindings (boolean option).
extern int ubus_bindings;
/**
* Initialize ubus interface.
*
* Connect to the ubus daemon and expose the ubus functions.
*
* @return if initializing ubus was successful
*/
bool babeld_add_ubus();
/**
* Add ubus socket to given filedescriptor set.
*
* We need to check repeatedly if the ubus socket has something to read.
* The functions allows to add the ubus socket to the normal while(1)-loop of babeld.
*
* @param readfs: the filedescriptor set
* @param maxfd: the current maximum file descriptor
* @return the maximum file descriptor
*/
int babeld_ubus_add_read_sock(fd_set* readfds, int maxfd);
/**
* Check and process ubus socket.
*
* If the ubus-socket signals that data is available, the ubus_handle_event is called.
*/
void babeld_ubus_receive(fd_set* readfds);