Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found
Select Git revision
Loading items

Target

Select target project
  • firmware/gluon
  • 0x4A6F/gluon
  • patrick/gluon
3 results
Select Git revision
Loading items
Show changes
Showing
with 68 additions and 1022 deletions
include $(TOPDIR)/rules.mk
PKG_NAME:=gluon-mesh-babel
PKG_VERSION:=1
PKG_BUILD_DEPENDS := libbabelhelper
PKG_BUILD_DEPENDS += libjson-c
include ../gluon.mk
define Package/gluon-mesh-babel
TITLE:=Babel mesh
DEPENDS:=+gluon-core +babeld +mmfd +libiwinfo +libgluonutil +firewall +libjson-c +libnl-tiny +libubus +libubox +libblobmsg-json +libbabelhelper +luabitop
PROVIDES:=gluon-mesh-provider
endef
define Package/gluon-mesh-babel/install
$(Gluon/Build/Install)
$(INSTALL_DIR) $(1)/lib/gluon/status-page/providers
$(INSTALL_BIN) $(PKG_BUILD_DIR)/neighbours-babel $(1)/lib/gluon/status-page/providers/
endef
$(eval $(call BuildPackageGluon,gluon-mesh-babel))
need_string_match(in_domain({'node_prefix6'}), '^[%x:]+/64$')
need_string_match(in_domain({'node_client_prefix6'}), '^[%x:]+/64$')
need_string_match(in_domain({'next_node', 'ip6'}), '^[%x:]+$', false)
need_string_match(in_domain({'next_node', 'ip4'}), '^%d+.%d+.%d+.%d+$', false)
need_string_match(in_domain({'next_node', 'mac'}), '^%x[02468aAcCeE]:%x%x:%x%x:%x%x:%x%x:%x%x$', false)
#!/bin/sh
case "$ACTION"
in
ifup)
echo 1 > /sys/devices/virtual/net/br-client/bridge/multicast_snooping
echo 2 > /sys/devices/virtual/net/br-client/bridge/multicast_router
;;
esac
#!/bin/sh /etc/rc.common
USE_PROCD=1
START=45
PORT=33123
DAEMON=/usr/sbin/babeld
pidfile=/var/run/gluon-babeld.pid
CONFIGFILE=/etc/gluon-babeld.conf
BABELOPTSFILE=/tmp/addn-babelopts
touch $BABELOPTSFILE
start_service() {
procd_open_instance
procd_set_param command $DAEMON
procd_append_param command -D -I "$pidfile" -G "$PORT" -c "$CONFIGFILE" $(cat $BABELOPTSFILE) babeldummydoesnotexist
procd_set_param respawn ${respawn_threshold:-60} ${respawn_timeout:-5} ${respawn_retry:-0}
procd_set_param stderr 1
procd_set_param stdout 1
procd_close_instance
}
echotobabel() {
local count=0
local line="$1"
local maxretries=10
while ! (echo -e "$line" | busybox nc ::1 "$PORT" >/dev/null 2>&1)
do
sleep 1
echo retrying to connect to babeld in PID $$, waited ${count}s >&2
count=$((count+1))
if [ $count -gt $maxretries ]; then
return 1
fi
done
return 0
}
waitforsocket() {
echotobabel "dump"
[ $? -gt 0 ] && { echo "Failed to connect to babeld socket on port $PORT, assuming the service was not started properly"; exit 43; }
}
reload_service() {
waitforsocket
for i in $(ubus call network.interface dump | jsonfilter -e "@.interface[@.proto='gluon_mesh' && @.up=true].device")
do
if ! echotobabel dump|grep "add interface"|grep -q $i
then
echotobabel "interface $i update-interval 300"
fi
done
for i in $(echotobabel "dump"|grep "add interface"|cut -d" " -f3)
do
if ! ubus call network.interface dump | jsonfilter -e "@.interface[@.proto='gluon_mesh' && @.up=true].device"|grep -q $i
then
echotobabel "flush interface $i"
fi
done
}
service_triggers() {
local script=$(readlink "$initscript")
local name=$(basename "${script:-$initscript}")
procd_open_trigger
procd_add_raw_trigger "interface.*" 0 "/etc/init.d/$name" reload
procd_close_trigger
}
service_started() {
# make sure the init script does not finish until babeld is actually up.
# unfortunately procd will still start multiple instances of the same script which is why waitforsocket is also run on reload
waitforsocket
}
stop_service(){
kill $(pgrep -P 1 babeld)
}
status() {
kill -USR1 $(pgrep -P 1 babeld)
}
#!/usr/bin/lua
local site = require 'gluon.site'
local babelconf='/etc/gluon-babeld.conf'
local file = io.open(babelconf, "w")
file:write("ipv6-subtrees true\n")
file:write("reflect-kernel-metric true\n")
file:write("export-table 254\n")
file:write("import-table 254\n")
file:write("out ip " .. site.next_node.ip6() .. "/128 deny\n")
file:write("redistribute ip " .. site.next_node.ip6() .. "/128 deny\n")
file:write("redistribute ip " .. site.prefix6() .. " eq 128 allow\n")
file:write("redistribute ip " .. site.node_client_prefix6() .. " eq 128 allow\n")
file:write("redistribute ip " .. site.node_prefix6() .. " eq 128 allow\n")
file:write("redistribute local if br-wan deny\n")
file:write("redistribute local ip 0.0.0.0/0 deny\n")
file:close()
all: respondd.so neighbours-babel
CFLAGS += -Wall -g -fPIC -D_GNU_SOURCE
ifeq ($(origin PKG_CONFIG), undefined)
PKG_CONFIG = pkg-config
ifeq ($(shell which $(PKG_CONFIG) 2>/dev/null),)
$(error $(PKG_CONFIG) not found)
endif
endif
ifeq ($(origin LIBBABEL_CFLAGS) $(origin LIBBABEL_LDLIBS), undefined undefined)
LIBBABEL_NAME ?= libbabelhelper
ifeq ($(shell $(PKG_CONFIG) --modversion $(LIBBABEL_NAME) 2>/dev/null),)
$(error No $(LIBBABEL_NAME) development libraries found!)
endif
LIBBABEL_CFLAGS += $(shell $(PKG_CONFIG) --cflags $(LIBBABEL_NAME))
LIBBABEL_LDLIBS += $(shell $(PKG_CONFIG) --libs $(LIBBABEL_NAME))
endif
CFLAGS += $(LIBBABEL_CFLAGS)
LDLIBS += $(LIBBABEL_LDLIBS)
CFLAGS_JSONC = $(shell pkg-config --cflags json-c)
LDFLAGS_JSONC = $(shell pkg-config --libs json-c)
respondd.so: respondd.c handle_neighbour.c
$(CC) $(CPPFLAGS) $(CFLAGS) $(LDFLAGS) $(LDLIBS) -shared $(LDFLAGS_JSONC) -o $@ $^ -lgluonutil -lblobmsg_json -lubox -lubus -liwinfo -luci
neighbours-babel: neighbours-babel.c handle_neighbour.c
$(CC) $(CPPFLAGS) $(CFLAGS) $(CFLAGS_JSONC) $(LDFLAGS) $(LDLIBS) $(LDFLAGS_JSONC) -o $@ $^
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include "handle_neighbour.h"
#include <libbabelhelper/babelhelper.h>
bool handle_neighbour(char **data, void *arg) {
struct json_object *obj = (struct json_object*)arg;
if (data[NEIGHBOUR]) {
struct json_object *neigh = json_object_new_object();
if (data[RXCOST])
json_object_object_add(neigh, "rxcost", json_object_new_int(atoi(data[RXCOST])));
if (data[TXCOST])
json_object_object_add(neigh, "txcost", json_object_new_int(atoi(data[TXCOST])));
if (data[COST])
json_object_object_add(neigh, "cost", json_object_new_int(atoi(data[COST])));
if (data[REACH])
json_object_object_add(neigh, "reachability", json_object_new_double(strtod(data[REACH], NULL)));
if (data[IF])
json_object_object_add(neigh, "ifname", json_object_new_string(data[IF]));
if (data[ADDRESS])
json_object_object_add(obj, data[ADDRESS] , neigh);
}
return true;
}
#include <json-c/json.h>
#include <stdbool.h>
bool handle_neighbour(char **line, void *arg);
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <json-c/json.h>
#include <libbabelhelper/babelhelper.h>
#include "handle_neighbour.h"
int main(void) {
struct json_object *neighbours;
printf("Content-type: text/event-stream\n\n");
fflush(stdout);
struct babelhelper_ctx bhelper_ctx = {};
while (1) {
neighbours = json_object_new_object();
if (!neighbours)
continue;
bhelper_ctx.debug = false;
babelhelper_readbabeldata(&bhelper_ctx, (void*)neighbours, handle_neighbour);
printf("data: %s\n\n", json_object_to_json_string(neighbours));
fflush(stdout);
json_object_put(neighbours);
neighbours = NULL;
sleep(10);
}
return 0;
}
/*
Copyright (c) 2016, Matthias Schiffer <mschiffer@universe-factory.net>
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <respondd.h>
#include <iwinfo.h>
#include <json-c/json.h>
#include <libgluonutil.h>
#include <uci.h>
#include <alloca.h>
#include <glob.h>
#include <inttypes.h>
#include <stdbool.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <net/if.h>
#include <netinet/in.h>
#include <sys/types.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <ifaddrs.h>
#include <linux/ethtool.h>
#include <linux/if_addr.h>
#include <linux/sockios.h>
#include <netdb.h>
#include "errno.h"
#include <libbabelhelper/babelhelper.h>
#include <libubox/blobmsg_json.h>
#include "libubus.h"
#define _STRINGIFY(s) #s
#define STRINGIFY(s) _STRINGIFY(s)
#include <stdlib.h>
#define MAX_INACTIVITY 60000
#define SOCKET_INPUT_BUFFER_SIZE 255
#define BABEL_PORT 33123
#define VPN_INTERFACE "mesh-vpn"
#define l3rdctl "/var/run/l3roamd.sock"
#define IFNAMELEN 32
#define PROTOLEN 32
#define UBUS_TIMEOUT 30
#define UBUS_SOCKET "/var/run/ubus.sock"
static struct babelhelper_ctx bhelper_ctx = {};
static void obtain_if_addr(const char *ifname, char *lladdr) {
struct ifaddrs *ifaddr, *ifa;
int family, n;
if (getifaddrs(&ifaddr) == -1) {
perror("getifaddrs");
exit(EXIT_FAILURE);
}
for (ifa = ifaddr, n = 0; ifa != NULL; ifa = ifa->ifa_next, n++) {
if (ifa->ifa_addr == NULL)
continue;
family = ifa->ifa_addr->sa_family;
if ( (family == AF_INET6) && ( ! strncmp(ifname, ifa->ifa_name, strlen(ifname)) ) ) {
char lhost[INET6_ADDRSTRLEN];
struct in6_addr *address = &((struct sockaddr_in6*)ifa->ifa_addr)->sin6_addr;
if (inet_ntop(AF_INET6, address, lhost, INET6_ADDRSTRLEN) == NULL) {
fprintf(stderr, "obtain_if_addr: could not convert ip to string\n");
goto cleanup;
}
if (! strncmp("fe80:", lhost, 5) ) {
snprintf( lladdr, NI_MAXHOST, "%s", lhost );
goto cleanup;
}
}
}
cleanup:
freeifaddrs(ifaddr);
}
static char* get_line_from_run(const char* command) {
FILE *fp;
char *line = NULL;
size_t len = 0;
fp = popen(command, "r");
if (fp != NULL) {
ssize_t r = getline(&line, &len, fp);
if (r >= 0) {
len = strlen(line);
if (len && line[len-1] == '\n')
line[len-1] = 0;
}
else {
free(line);
line = NULL;
}
pclose(fp);
}
return line;
}
static struct json_object * get_addresses(void) {
char *primarymac = gluonutil_get_sysconfig("primary_mac");
char *address = malloc(INET6_ADDRSTRLEN+1);
char node_prefix_str[INET6_ADDRSTRLEN+1];
struct in6_addr node_prefix = {};
struct json_object *retval = json_object_new_array();
if (!gluonutil_get_node_prefix6(&node_prefix)) {
fprintf(stderr, "get_addresses: could not obtain mesh-prefix from site.conf. Not adding addresses to json data\n");
goto free;
}
if (inet_ntop(AF_INET6, &(node_prefix.s6_addr), node_prefix_str, INET6_ADDRSTRLEN) == NULL) {
fprintf(stderr, "get_addresses: could not convert mesh-prefix from site.conf to string\n");
goto free;
}
char *prefix_addresspart = strndup(node_prefix_str, INET6_ADDRSTRLEN);
if (! babelhelper_generateip_str(address, primarymac, prefix_addresspart) ) {
fprintf(stderr, "IP-address could not be generated by babelhelper");
}
free(prefix_addresspart);
json_object_array_add(retval, json_object_new_string(address));
free:
free(address);
free(primarymac);
return retval;
}
static bool interface_file_exists(const char *ifname, const char *name) {
const char *format = "/sys/class/net/%s/%s";
char path[strlen(format) + strlen(ifname) + strlen(name)+1];
snprintf(path, sizeof(path), format, ifname, name);
return !access(path, F_OK);
}
static void mesh_add_if(const char *ifname, struct json_object *wireless,
struct json_object *tunnel, struct json_object *other) {
char str_ip[NI_MAXHOST] = {};
obtain_if_addr(ifname, str_ip);
struct json_object *address = json_object_new_string(str_ip);
if (interface_file_exists(ifname, "wireless"))
json_object_array_add(wireless, address);
else if (interface_file_exists(ifname, "tun_flags"))
json_object_array_add(tunnel, address);
else
json_object_array_add(other, address);
}
static bool handle_neighbour(char **data, void *obj) {
if (data[NEIGHBOUR]) {
struct json_object *neigh = json_object_new_object();
if (data[RXCOST])
json_object_object_add(neigh, "rxcost", json_object_new_int(atoi(data[RXCOST])));
if (data[TXCOST])
json_object_object_add(neigh, "txcost", json_object_new_int(atoi(data[TXCOST])));
if (data[COST])
json_object_object_add(neigh, "cost", json_object_new_int(atoi(data[COST])));
if (data[REACH])
json_object_object_add(neigh, "reachability", json_object_new_double(strtod(data[REACH], NULL)));
struct json_object *nif = 0;
if (data[IF] && !json_object_object_get_ex(obj, data[IF], &nif)) {
char str_ip[NI_MAXHOST] = {};
obtain_if_addr( (const char*)data[IF], str_ip );
nif = json_object_new_object();
json_object_object_add(nif, "ll-addr", json_object_new_string(str_ip));
json_object_object_add(nif, "protocol", json_object_new_string("babel"));
json_object_object_add(obj, data[IF], nif);
}
struct json_object *neighborcollector = 0;
if (!json_object_object_get_ex(nif, "neighbours", &neighborcollector)) {
neighborcollector = json_object_new_object();
json_object_object_add(nif, "neighbours", neighborcollector);
}
json_object_object_add(neighborcollector, data[ADDRESS], neigh);
}
return true;
}
static struct json_object * get_babel_neighbours(void) {
struct json_object *neighbours;
neighbours = json_object_new_object();
if (!neighbours)
return NULL;
babelhelper_readbabeldata(&bhelper_ctx, (void*)neighbours, handle_neighbour);
return(neighbours);
}
static void blobmsg_handle_list(struct blob_attr *attr, int len, bool array, struct json_object *wireless, struct json_object *tunnel, struct json_object *other);
static void blobmsg_handle_element(struct blob_attr *attr, bool head, char **ifname, char **proto, struct json_object *wireless, struct json_object *tunnel, struct json_object *other) {
void *data;
if (!blobmsg_check_attr(attr, false))
return;
data = blobmsg_data(attr);
switch (blob_id(attr)) {
case BLOBMSG_TYPE_STRING:
if (!strncmp(blobmsg_name(attr),"device", 6)) {
free(*ifname);
*ifname = strndup(data, IFNAMELEN);
} else if (!strncmp(blobmsg_name(attr), "proto", 5)) {
free(*proto);
*proto = strndup(data, PROTOLEN);
}
return;
case BLOBMSG_TYPE_ARRAY:
blobmsg_handle_list(data, blobmsg_data_len(attr), true, wireless, tunnel, other);
return;
case BLOBMSG_TYPE_TABLE:
blobmsg_handle_list(data, blobmsg_data_len(attr), false, wireless, tunnel, other);
}
}
static void blobmsg_handle_list(struct blob_attr *attr, int len, bool array, struct json_object *wireless, struct json_object *tunnel, struct json_object *other) {
struct blob_attr *pos;
int rem = len;
char *ifname = NULL;
char *proto = NULL;
__blob_for_each_attr(pos, attr, rem) {
blobmsg_handle_element(pos, array, &ifname, &proto, wireless, tunnel, other);
}
if (ifname && proto) {
if (!strncmp(proto, "gluon_mesh", 10)) {
mesh_add_if(ifname, wireless, tunnel, other);
}
}
free(ifname);
free(proto);
}
static void receive_call_result_data(struct ubus_request *req, int type, struct blob_attr *msg) {
struct json_object *ret = json_object_new_object();
struct json_object *wireless = json_object_new_array();
struct json_object *tunnel = json_object_new_array();
struct json_object *other = json_object_new_array();
if (!ret || !wireless || !tunnel || !other) {
json_object_put(wireless);
json_object_put(tunnel);
json_object_put(other);
json_object_put(ret);
return;
}
if (!msg) {
printf("empty message\n");
return;
}
blobmsg_handle_list(blobmsg_data(msg), blobmsg_data_len(msg), false, wireless, tunnel, other);
json_object_object_add(ret, "wireless", wireless);
json_object_object_add(ret, "tunnel", tunnel);
json_object_object_add(ret, "other", other);
*((struct json_object**)(req->priv)) = ret;
}
static struct json_object * get_mesh_ifs() {
struct ubus_context *ubus_ctx;
struct json_object *ret = NULL;
struct blob_buf b = {};
unsigned int id=8;
ubus_ctx = ubus_connect(UBUS_SOCKET);
if (!ubus_ctx) {
fprintf(stderr,"could not connect to ubus, not providing mesh-data\n");
goto end;
}
blob_buf_init(&b, 0);
ubus_lookup_id(ubus_ctx, "network.interface", &id);
int uret = ubus_invoke(ubus_ctx, id, "dump", b.head, receive_call_result_data, &ret, UBUS_TIMEOUT * 1000);
if (uret > 0)
fprintf(stderr, "ubus command failed: %s\n", ubus_strerror(uret));
else if (uret == -2)
fprintf(stderr, "invalid call, exiting\n");
blob_buf_free(&b);
end:
ubus_free(ubus_ctx);
return ret;
}
static struct json_object * get_mesh(void) {
struct json_object *ret = json_object_new_object();
struct json_object *interfaces = NULL;
interfaces = json_object_new_object();
json_object_object_add(interfaces, "interfaces", get_mesh_ifs());
json_object_object_add(ret, "babel", interfaces);
return ret;
}
static struct json_object * get_babeld_version(void) {
char *version = get_line_from_run("exec babeld -V 2>&1");
struct json_object *ret = gluonutil_wrap_string(version);
free(version);
return ret;
}
static struct json_object * respondd_provider_nodeinfo(void) {
bhelper_ctx.debug=false;
struct json_object *ret = json_object_new_object();
struct json_object *network = json_object_new_object();
json_object_object_add(network, "addresses", get_addresses());
json_object_object_add(network, "mesh", get_mesh());
json_object_object_add(ret, "network", network);
struct json_object *software = json_object_new_object();
struct json_object *software_babeld = json_object_new_object();
json_object_object_add(software_babeld, "version", get_babeld_version());
json_object_object_add(software, "babeld", software_babeld);
json_object_object_add(ret, "software", software);
return ret;
}
static uint64_t getnumber(const char *ifname, const char *stat) {
const char *format = "/sys/class/net/%s/statistics/%s";
char path[strlen(format) + strlen(ifname) + strlen(stat)];
snprintf(path, sizeof(path), format, ifname, stat);
if (! access(path, F_OK))
{
char *line=gluonutil_read_line(path);
long long i = atoll(line);
free(line);
return(i);
}
return 0;
}
static struct json_object * get_traffic(void) {
char ifname[16];
strncpy(ifname, "br-client", 16);
struct json_object *ret = NULL;
struct json_object *rx = json_object_new_object();
struct json_object *tx = json_object_new_object();
json_object_object_add(rx, "packets", json_object_new_int64(getnumber(ifname, "rx_packets")));
json_object_object_add(rx, "bytes", json_object_new_int64(getnumber(ifname, "rx_bytes")));
json_object_object_add(rx, "dropped", json_object_new_int64(getnumber(ifname, "rx_dropped")));
json_object_object_add(tx, "packets", json_object_new_int64(getnumber(ifname, "tx_packets")));
json_object_object_add(tx, "dropped", json_object_new_int64(getnumber(ifname, "tx_dropped")));
json_object_object_add(tx, "bytes", json_object_new_int64(getnumber(ifname, "tx_bytes")));
ret = json_object_new_object();
json_object_object_add(ret, "rx", rx);
json_object_object_add(ret, "tx", tx);
return ret;
}
static void count_iface_stations(size_t *wifi24, size_t *wifi5, const char *ifname) {
const struct iwinfo_ops *iw = iwinfo_backend(ifname);
if (!iw)
return;
int freq;
if (iw->frequency(ifname, &freq) < 0)
return;
size_t *wifi;
if (freq >= 2400 && freq < 2500)
wifi = wifi24;
else if (freq >= 5000 && freq < 6000)
wifi = wifi5;
else
return;
int len;
char buf[IWINFO_BUFSIZE];
if (iw->assoclist(ifname, buf, &len) < 0)
return;
struct iwinfo_assoclist_entry *entry;
for (entry = (struct iwinfo_assoclist_entry *)buf; (char*)(entry+1) <= buf + len; entry++) {
if (entry->inactive > MAX_INACTIVITY)
continue;
(*wifi)++;
}
}
static void count_stations(size_t *wifi24, size_t *wifi5) {
struct uci_context *ctx = uci_alloc_context();
ctx->flags &= ~UCI_FLAG_STRICT;
struct uci_package *p;
if (uci_load(ctx, "wireless", &p))
goto end;
struct uci_element *e;
uci_foreach_element(&p->sections, e) {
struct uci_section *s = uci_to_section(e);
if (strcmp(s->type, "wifi-iface"))
continue;
const char *network = uci_lookup_option_string(ctx, s, "network");
if (!network || strcmp(network, "client"))
continue;
const char *mode = uci_lookup_option_string(ctx, s, "mode");
if (!mode || strcmp(mode, "ap"))
continue;
const char *ifname = uci_lookup_option_string(ctx, s, "ifname");
if (!ifname)
continue;
count_iface_stations(wifi24, wifi5, ifname);
}
end:
uci_free_context(ctx);
}
static bool handle_route_addgw_nexthop(char **data, void *arg) {
struct json_object *obj = (struct json_object*) arg;
if (data[PREFIX] && data[FROM] && data[VIA] && data[IF]) {
if ( (! strncmp(data[PREFIX], "::/0", 4) ) && ( ! strncmp(data[FROM], "::/0", 4) ) ) {
int gw_nexthoplen=strlen(data[VIA]) + strlen(data[IF])+2;
char gw_nexthop[gw_nexthoplen];
snprintf(gw_nexthop, gw_nexthoplen , "%s%%%s", data[VIA], data[IF]);
json_object_object_add(obj, "gateway_nexthop", json_object_new_string(gw_nexthop));
}
}
return true;
}
static int json_parse_get_clients(json_object * object) {
if (object) {
json_object_object_foreach(object, key, val) {
if (! strncmp("clients", key, 7)) {
return(json_object_get_int(val));
}
}
}
return(-1);
}
static int ask_l3roamd_for_client_count() {
struct sockaddr_un addr;
const char *socket_path = "/var/run/l3roamd.sock";
int fd;
int clients = -1;
char *buf = NULL;
int already_read = 0;
if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) {
fprintf(stderr, "could not setup l3roamd-control-socket\n");
return(-1);
}
memset(&addr, 0, sizeof(addr));
addr.sun_family = AF_UNIX;
strncpy(addr.sun_path, socket_path, sizeof(addr.sun_path)-1);
if (connect(fd, (struct sockaddr*)&addr, sizeof(addr)) == -1) {
fprintf(stderr, "connect error\n");
return(-1);
}
if (write(fd,"get_clients\n",12) != 12) {
perror("could not send command to l3roamd socket: get_clients");
goto end;
}
int rc = 0;
do {
char *buf_tmp = realloc(buf, already_read + SOCKET_INPUT_BUFFER_SIZE + 1);
if (buf_tmp == NULL) {
fprintf(stderr, "could not allocate memory for buffer\n");
goto end;
}
buf = buf_tmp;
rc = read(fd, &buf[already_read], SOCKET_INPUT_BUFFER_SIZE);
already_read+=rc;
if (rc < 0) {
perror("error on read in ask_l3roamd_for_client_count():");
goto end;
}
buf[already_read]='\0';
} while (rc == SOCKET_INPUT_BUFFER_SIZE);
json_object * jobj = json_tokener_parse(buf);
clients = json_parse_get_clients(jobj);
json_object_put(jobj);
end:
free(buf);
close(fd);
return clients;
}
static struct json_object * get_clients(void) {
size_t wifi24 = 0, wifi5 = 0;
count_stations(&wifi24, &wifi5);
int total = ask_l3roamd_for_client_count();
size_t wifi = wifi24 + wifi5;
struct json_object *ret = json_object_new_object();
if (total >= 0)
json_object_object_add(ret, "total", json_object_new_int(total));
json_object_object_add(ret, "wifi", json_object_new_int(wifi));
json_object_object_add(ret, "wifi24", json_object_new_int(wifi24));
json_object_object_add(ret, "wifi5", json_object_new_int(wifi5));
return ret;
}
static struct json_object * respondd_provider_statistics(void) {
struct json_object *ret = json_object_new_object();
json_object_object_add(ret, "clients", get_clients());
json_object_object_add(ret, "traffic", get_traffic());
babelhelper_readbabeldata(&bhelper_ctx, (void*)ret, handle_route_addgw_nexthop );
return ret;
}
static struct json_object * get_wifi_neighbours(const char *ifname) {
const struct iwinfo_ops *iw = iwinfo_backend(ifname);
if (!iw)
return NULL;
int len;
char buf[IWINFO_BUFSIZE];
if (iw->assoclist(ifname, buf, &len) < 0)
return NULL;
struct json_object *neighbours = json_object_new_object();
struct iwinfo_assoclist_entry *entry;
for (entry = (struct iwinfo_assoclist_entry *)buf; (char*)(entry+1) <= buf + len; entry++) {
if (entry->inactive > MAX_INACTIVITY)
continue;
struct json_object *obj = json_object_new_object();
json_object_object_add(obj, "signal", json_object_new_int(entry->signal));
json_object_object_add(obj, "noise", json_object_new_int(entry->noise));
json_object_object_add(obj, "inactive", json_object_new_int(entry->inactive));
char mac[18];
snprintf(mac, sizeof(mac), "%02x:%02x:%02x:%02x:%02x:%02x",
entry->mac[0], entry->mac[1], entry->mac[2],
entry->mac[3], entry->mac[4], entry->mac[5]);
json_object_object_add(neighbours, mac, obj);
}
struct json_object *ret = json_object_new_object();
if (json_object_object_length(neighbours))
json_object_object_add(ret, "neighbours", neighbours);
else
json_object_put(neighbours);
return ret;
}
static struct json_object * get_wifi(void) {
struct uci_context *ctx = uci_alloc_context();
ctx->flags &= ~UCI_FLAG_STRICT;
struct json_object *ret = json_object_new_object();
struct uci_package *p;
if (uci_load(ctx, "network", &p))
goto end;
struct uci_element *e;
uci_foreach_element(&p->sections, e) {
struct uci_section *s = uci_to_section(e);
if (strcmp(s->type, "interface"))
continue;
const char *proto = uci_lookup_option_string(ctx, s, "proto");
if (!proto || strcmp(proto, "gluon_mesh"))
continue;
const char *ifname = uci_lookup_option_string(ctx, s, "ifname");
if (!ifname)
continue;
char *ifaddr = gluonutil_get_interface_address(ifname);
if (!ifaddr)
continue;
struct json_object *neighbours = get_wifi_neighbours(ifname);
if (neighbours)
json_object_object_add(ret, ifaddr, neighbours);
free(ifaddr);
}
end:
uci_free_context(ctx);
return ret;
}
static struct json_object * respondd_provider_neighbours(void) {
struct json_object *ret = json_object_new_object();
struct json_object *babel = get_babel_neighbours();
if (babel)
json_object_object_add(ret, "babel", babel);
struct json_object *wifi = get_wifi();
if (wifi)
json_object_object_add(ret, "wifi", wifi);
return ret;
}
const struct respondd_provider_info respondd_providers[] = {
{"nodeinfo", respondd_provider_nodeinfo},
{"statistics", respondd_provider_statistics},
{"neighbours", respondd_provider_neighbours},
{}
};
include $(TOPDIR)/rules.mk
PKG_NAME:=gluon-mesh-batman-adv
PKG_VERSION:=1
include ../gluon.mk
define Package/gluon-mesh-batman-adv/common
define Package/gluon-mesh-batman-adv-15
PROVIDES:=gluon-mesh-batman-adv
DEPENDS:= \
TITLE:=Support for batman-adv meshing (compat level 15)
DEPENDS+= \
+kmod-batman-adv \
+gluon-core \
+libgluonutil \
+gluon-client-bridge \
......@@ -17,38 +18,7 @@ define Package/gluon-mesh-batman-adv/common
+kmod-dummy \
+libnl-tiny \
+libbatadv \
+batctl \
+@GLUON_SPECIALIZE_KERNEL:KERNEL_DUMMY \
+@GLUON_SPECIALIZE_KERNEL:KERNEL_CRC16 \
+@GLUON_SPECIALIZE_KERNEL:KERNEL_LIBCRC32C
endef
define Package/gluon-mesh-batman-adv-14
$(Package/gluon-mesh-batman-adv/common)
TITLE:=Support for batman-adv meshing (compat level 14)
CONFLICTS:=gluon-mesh-batman-adv-15
DEPENDS+=+kmod-batman-adv-legacy
endef
define Package/gluon-mesh-batman-adv-15
$(Package/gluon-mesh-batman-adv/common)
TITLE:=Support for batman-adv meshing (compat level 15)
DEPENDS+=+kmod-batman-adv
endef
define Package/gluon-mesh-batman-adv-14/install
$(Gluon/Build/Install)
$(INSTALL_DIR) $(1)/lib/gluon/mesh-batman-adv
echo 14 > $(1)/lib/gluon/mesh-batman-adv/compat
endef
define Package/gluon-mesh-batman-adv-15/install
$(Gluon/Build/Install)
$(INSTALL_DIR) $(1)/lib/gluon/mesh-batman-adv
echo 15 > $(1)/lib/gluon/mesh-batman-adv/compat
+batctl
endef
$(eval $(call BuildPackageGluon,gluon-mesh-batman-adv-14))
$(eval $(call BuildPackageGluon,gluon-mesh-batman-adv-15))
......@@ -2,4 +2,4 @@
need_boolean(in_domain({'mesh', 'vxlan'}), not this_domain())
need_number({'mesh', 'batman_adv', 'gw_sel_class'}, false)
need_one_of({'mesh', 'batman_adv', 'routing_algo'}, {'BATMAN_IV', 'BATMAN_V'}, false)
need_one_of({'mesh', 'batman_adv', 'routing_algo'}, {'BATMAN_IV', 'BATMAN_V'})
#!/bin/sh
if [ "$FIXED_MTU" -eq 0 ]; then
# In case on VLAN on IBSS, first set MTU of the underlying interface
for lower in /sys/class/net/"$IFNAME"/lower_*/wireless; do
lower="${lower%%\/wireless}"
lower="${lower##*\/lower_}"
ip link set dev "$lower" mtu 1536
break
done
ip link set dev "$IFNAME" mtu 1532
fi
#!/bin/sh
lock /var/lock/gluon_bat0.lock
(echo 'none' > "/sys/class/net/$IFNAME/batman_adv/mesh_iface") 2>/dev/null
batctl interface -M del "$IFNAME" 2>/dev/null
lock -u /var/lock/gluon_bat0.lock
#!/bin/sh
out=$(batctl gwl -H 2>/dev/null) && [ -n "$out" ]
#!/bin/sh
out=$(batctl n -H 2>/dev/null) && [ -n "$out" ]
#!/bin/sh
# shellcheck disable=SC1091,SC2034
. /lib/functions.sh
. ../netifd-proto.sh
init_proto "$@"
......@@ -8,6 +10,18 @@ proto_gluon_bat0_init_config() {
no_device=1
available=1
renew_handler=1
proto_config_add_string 'gw_mode'
}
lookup_site() {
local path="$1" default="$2"
lua -e "print(require('gluon.site').$path('$default'))"
}
lookup_uci() {
local path="$1" default="$2"
uci -q get "$path" || echo "$default"
}
proto_gluon_bat0_renew() {
......@@ -15,20 +29,9 @@ proto_gluon_bat0_renew() {
lock /var/lock/gluon_bat0.lock
local ifdump="$(ubus call network.interface dump)"
echo "$ifdump" | jsonfilter \
-e "@.interface[@.proto='gluon_mesh' && @.up=true]['device','data']" \
| while read dev; do
read data
echo bat0 > "/sys/class/net/$dev/batman_adv/mesh_iface"
! [ "$(echo "$data" | jsonfilter -e "@.transitive")" = 'true' ]
transitive=$?
(echo "$transitive" > "/sys/class/net/$dev/batman_adv/no_rebroadcast") 2>/dev/null
done
ubus call network.interface dump | jsonfilter \
-e "@.interface[@.proto='gluon_mesh' && @.up=true].device" \
| xargs -r -n 1 batctl interface add
lock -u /var/lock/gluon_bat0.lock
}
......@@ -36,16 +39,46 @@ proto_gluon_bat0_renew() {
proto_gluon_bat0_setup() {
local config="$1"
local primary0_mac="$(lua -e 'print(require("gluon.util").generate_mac(3))')"
local routing_algo
routing_algo="$(lookup_site 'mesh.batman_adv.routing_algo' 'BATMAN_IV')"
local gw_mode
json_get_vars gw_mode
batctl routing_algo "$routing_algo"
batctl interface create
batctl orig_interval 5000
batctl hop_penalty "$(lookup_uci 'gluon.mesh_batman_adv.hop_penalty' 15)"
batctl noflood_mark 0x4/0x4
case "$gw_mode" in
server)
batctl gw_mode "server"
;;
client)
local gw_sel_class
gw_sel_class="$(lookup_site 'mesh.batman_adv.gw_sel_class')"
if [ -n "$gw_sel_class" ]; then
batctl gw_mode "client" "$gw_sel_class"
else
batctl gw_mode "client"
fi
;;
*)
batctl gw_mode "off"
;;
esac
local primary0_mac
primary0_mac="$(lua -e 'print(require("gluon.util").generate_mac(3))')"
ip link add primary0 type dummy
echo 1 > /proc/sys/net/ipv6/conf/primary0/disable_ipv6
ip link set primary0 address "$primary0_mac" mtu 1532 up
local routing_algo="$(uci -q get batman-adv.bat0.routing_algo || echo 'BATMAN_IV')"
(echo "$routing_algo" >/sys/module/batman_adv/parameters/routing_algo) 2>/dev/null
echo bat0 > /sys/class/net/primary0/batman_adv/mesh_iface
batctl interface add primary0
proto_init_update primary0 1
proto_send_update "$config"
......@@ -56,7 +89,7 @@ proto_gluon_bat0_setup() {
proto_gluon_bat0_teardown() {
local config="$1"
ip link del bat0
batctl interface destroy
ip link del primary0
}
......
#!/bin/sh
# shellcheck source=package/gluon-autoupdater/files/lib/gluon/autoupdater/lib.sh
. /lib/gluon/autoupdater/lib.sh
pidof netifd >/dev/null || start_enabled network
#!/bin/sh
# shellcheck source=package/gluon-autoupdater/files/lib/gluon/autoupdater/lib.sh
. /lib/gluon/autoupdater/lib.sh
wifi down
......
#!/usr/bin/lua
local sysconfig = require 'gluon.sysconfig'
local site = require 'gluon.site'
local util = require 'gluon.util'
local uci = require('simple-uci').cursor()
local gw_mode = uci:get('batman-adv', 'bat0', 'gw_mode') or 'client'
local gw_sel_class = site.mesh.batman_adv.gw_sel_class()
local routing_algo = site.mesh.batman_adv.routing_algo()
uci:delete('batman-adv', 'bat0')
uci:section('batman-adv', 'mesh', 'bat0', {
orig_interval = 5000,
gw_mode = gw_mode,
gw_sel_class = gw_sel_class,
hop_penalty = 15,
routing_algo = routing_algo,
multicast_mode = true,
})
uci:save('batman-adv')
uci:delete('network', 'gluon_bat0')
local gw_mode = uci:get('network_gluon-old', 'gluon_bat0', 'gw_mode') or 'client'
uci:section('network', 'interface', 'gluon_bat0', {
proto = 'gluon_bat0',
gw_mode = gw_mode,
})
uci:delete('network', 'bat0')
uci:section('network', 'interface', 'bat0', {
ifname = 'bat0',
proto = 'none',
......