464 lines
13 KiB
C
464 lines
13 KiB
C
#include <stdint.h>
|
|
#include <stdlib.h>
|
|
#include <sys/select.h>
|
|
|
|
#include <libubus.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 "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 babel_route *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 int 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));
|
|
|
|
blob_buf_free(&b);
|
|
|
|
return 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 int 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);
|
|
|
|
blob_buf_init(&b, 0);
|
|
|
|
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);
|
|
}
|
|
|
|
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));
|
|
|
|
blob_buf_free(&b);
|
|
|
|
return 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 int 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(0);
|
|
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);
|
|
}
|
|
|
|
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));
|
|
|
|
blob_buf_free(&b);
|
|
|
|
return 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_string(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 int 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);
|
|
}
|
|
}
|
|
|
|
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));
|
|
|
|
blob_buf_free(&b);
|
|
|
|
return 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 ubus_notify_route(struct babel_route *route, int kind) {
|
|
struct blob_buf b = {0};
|
|
char method[50]; // possible methods are route.change, route.add, route.flush
|
|
|
|
if (!babeld_object.has_subscribers)
|
|
return;
|
|
|
|
if (!route)
|
|
return;
|
|
|
|
if (!shared_ctx)
|
|
return;
|
|
|
|
blob_buf_init(&b, 0);
|
|
babeld_add_route_buf(route, &b);
|
|
snprintf(method, sizeof(method), "route.%s", local_kind(kind));
|
|
ubus_notify(shared_ctx, &babeld_object, method, b.head, -1);
|
|
blob_buf_free(&b);
|
|
}
|
|
|
|
void ubus_notify_xroute(struct xroute *xroute, int kind) {
|
|
struct blob_buf b = {0};
|
|
char method[50]; // possible methods are xroute.change, xroute.add,
|
|
// xroute.flush
|
|
|
|
if (!babeld_object.has_subscribers)
|
|
return;
|
|
|
|
if (!xroute)
|
|
return;
|
|
|
|
if (!shared_ctx)
|
|
return;
|
|
|
|
blob_buf_init(&b, 0);
|
|
babeld_add_xroute_buf(xroute, &b);
|
|
snprintf(method, sizeof(method), "xroute.%s", local_kind(kind));
|
|
ubus_notify(shared_ctx, &babeld_object, method, b.head, -1);
|
|
blob_buf_free(&b);
|
|
}
|
|
|
|
void ubus_notify_neighbour(struct neighbour *neigh, int kind) {
|
|
struct blob_buf b = {0};
|
|
char method[50]; // possible methods are neigh.change, neigh.add, neigh.flush
|
|
|
|
if (!babeld_object.has_subscribers)
|
|
return;
|
|
|
|
if (!neigh)
|
|
return;
|
|
|
|
if (!shared_ctx)
|
|
return;
|
|
|
|
blob_buf_init(&b, 0);
|
|
babeld_add_neighbour_buf(neigh, &b);
|
|
snprintf(method, sizeof(method), "neigh.%s", local_kind(kind));
|
|
ubus_notify(shared_ctx, &babeld_object, method, b.head, -1);
|
|
blob_buf_free(&b);
|
|
}
|
|
|
|
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;
|
|
}
|