#include #include #include #include #include #include #include #include #include #include #include #include #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; }