This commit is contained in:
Christof Schulze 2020-08-09 00:35:13 +02:00 committed by GitHub
commit 46e9566e4f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 447 additions and 111 deletions

View File

@ -24,8 +24,8 @@ CFLAGS_JSONC = $(shell pkg-config --cflags json-c)
LDFLAGS_JSONC = $(shell pkg-config --libs json-c) LDFLAGS_JSONC = $(shell pkg-config --libs json-c)
respondd.so: respondd.c handle_neighbour.c respondd.so: respondd.c
$(CC) $(CPPFLAGS) $(CFLAGS) $(LDFLAGS) $(LDLIBS) -shared $(LDFLAGS_JSONC) -o $@ $^ -lgluonutil -lblobmsg_json -lubox -lubus -luci $(CC) $(CPPFLAGS) $(CFLAGS) $(LDFLAGS) $(LDLIBS) -shared $(LDFLAGS_JSONC) -o $@ $^ -lgluonutil -lblobmsg_json -lubox -lubus -luci
neighbours-babel: neighbours-babel.c handle_neighbour.c neighbours-babel: neighbours-babel.c
$(CC) $(CPPFLAGS) $(CFLAGS) $(CFLAGS_JSONC) $(LDFLAGS) $(LDLIBS) $(LDFLAGS_JSONC) -o $@ $^ $(CC) $(CPPFLAGS) $(CFLAGS) $(CFLAGS_JSONC) $(LDFLAGS) $(LDLIBS) $(LDFLAGS_JSONC) -o $@ $^

View File

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

View File

@ -2,8 +2,30 @@
#include <string.h> #include <string.h>
#include <unistd.h> #include <unistd.h>
#include <json-c/json.h> #include <json-c/json.h>
#include <stdlib.h>
#include <libbabelhelper/babelhelper.h> #include <libbabelhelper/babelhelper.h>
#include "handle_neighbour.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;
}
int main(void) { int main(void) {
struct json_object *neighbours; struct json_object *neighbours;

View File

@ -29,6 +29,10 @@
#include <json-c/json.h> #include <json-c/json.h>
#include <libgluonutil.h> #include <libgluonutil.h>
#include <uci.h> #include <uci.h>
#include <sys/select.h>
#include <linux/rtnetlink.h>
#include <pthread.h>
#include <inttypes.h> #include <inttypes.h>
#include <stdbool.h> #include <stdbool.h>
@ -61,6 +65,245 @@
#define UBUS_TIMEOUT 30000 #define UBUS_TIMEOUT 30000
static struct babelhelper_ctx bhelper_ctx = {}; static struct babelhelper_ctx bhelper_ctx = {};
static struct json_object *babeld_version = NULL;
static char *model = NULL;
static struct json_object *neighbours = NULL;
static pthread_rwlock_t neighbours_lock;
static pthread_t babelmonitor;
struct thread_info {
pthread_t thread_id;
int thread_num;
char *argv_string;
};
struct kernel_route {
struct in6_addr prefix;
struct in6_addr src_prefix;
struct in6_addr gw;
int plen;
int src_plen; /* no source prefix <=> src_plen == 0 */
int metric;
int proto;
unsigned int ifindex;
unsigned int table;
};
struct nlrtreq {
struct nlmsghdr nl;
struct rtmsg rt;
char buf[1024];
};
#define ROUTE_PROTO 158
#define KERNEL_INFINITY 9999
static const char *print_ip(const struct in6_addr *addr, char *buf, size_t buflen) {
return inet_ntop(AF_INET6, &(addr->s6_addr), buf, buflen);
}
static int rtnl_addattr(struct nlmsghdr *n, size_t maxlen, int type, void *data, int datalen) {
int len = RTA_LENGTH(datalen);
struct rtattr *rta;
if (NLMSG_ALIGN(n->nlmsg_len) + len > maxlen)
return -1;
rta = (struct rtattr *)(((char *)n) + NLMSG_ALIGN(n->nlmsg_len));
rta->rta_type = type;
rta->rta_len = len;
memcpy(RTA_DATA(rta), data, datalen);
n->nlmsg_len = NLMSG_ALIGN(n->nlmsg_len) + len;
return 0;
}
static void rtmgr_rtnl_talk(int fd, struct nlmsghdr *req) {
struct sockaddr_nl nladdr = {.nl_family = AF_NETLINK};
struct iovec iov = {req, 0};
struct msghdr msg = {&nladdr, sizeof(nladdr), &iov, 1, NULL, 0, 0};
iov.iov_len = req->nlmsg_len;
if (sendmsg(fd, &msg, 0) < 0) {
perror("sendmsg on rtmgr_rtnl_talk()");
}
}
static void get_route(int fd, const int ifindex, struct in6_addr *address, const int prefix_length) {
struct nlrtreq req = {
.nl = {
.nlmsg_type = RTM_GETROUTE,
.nlmsg_flags = NLM_F_REQUEST,
.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg)),
},
.rt = {
.rtm_family = AF_INET6,
.rtm_protocol = ROUTE_PROTO,
.rtm_scope = RT_SCOPE_UNIVERSE,
.rtm_type = RTN_UNICAST,
.rtm_dst_len = prefix_length
},
};
rtnl_addattr(&req.nl, sizeof(req), RTA_DST, (void *)address, sizeof(struct in6_addr));
if (ifindex > 0 )
rtnl_addattr(&req.nl, sizeof(req), RTA_OIF, (void *)&ifindex, sizeof(ifindex));
rtmgr_rtnl_talk(fd, (struct nlmsghdr *)&req);
}
static int parse_kernel_route_rta(struct rtmsg *rtm, int len, struct kernel_route *route) {
len -= NLMSG_ALIGN(sizeof(*rtm));
memset(route, 0, sizeof(struct kernel_route));
route->proto = rtm->rtm_protocol;
for (struct rtattr *rta = RTM_RTA(rtm); RTA_OK(rta, len); rta = RTA_NEXT(rta, len)) {
switch (rta->rta_type) {
case RTA_DST:
route->plen = rtm->rtm_dst_len;
memcpy(route->prefix.s6_addr, RTA_DATA(rta), 16);
break;
case RTA_SRC:
route->src_plen = rtm->rtm_src_len;
memcpy(route->src_prefix.s6_addr, RTA_DATA(rta), 16);
break;
case RTA_GATEWAY:
memcpy(route->gw.s6_addr, RTA_DATA(rta), 16);
break;
case RTA_OIF:
route->ifindex = *(int *)RTA_DATA(rta);
break;
case RTA_PRIORITY:
route->metric = *(int *)RTA_DATA(rta);
if (route->metric < 0 || route->metric > KERNEL_INFINITY)
route->metric = KERNEL_INFINITY;
break;
default:
break;
}
}
return 1;
}
static bool handle_kernel_routes(const struct nlmsghdr *nh, struct kernel_route *route) {
int len = nh->nlmsg_len;
struct rtmsg *rtm;
rtm = (struct rtmsg *)NLMSG_DATA(nh);
len -= NLMSG_LENGTH(0);
/* Ignore cached routes, advertised by some kernels (linux 3.x). */
if (rtm->rtm_flags & RTM_F_CLONED) return false;
if (parse_kernel_route_rta(rtm, len, route) < 0) return false;
return true;
}
static bool rtnl_handle_msg(const struct nlmsghdr *nh,
struct kernel_route *route) {
if (nh->nlmsg_type == RTM_NEWROUTE) {
handle_kernel_routes(nh, route);
if (!(route->plen == 0 && route->metric >= KERNEL_INFINITY))
return true;
}
return false;
}
static int get_default_route(struct json_object *ret) {
int nlfd = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
if (nlfd < 0) {
perror("can't open RTNL socket");
return -1;
}
struct sockaddr_nl snl = {
.nl_family = AF_NETLINK,
.nl_groups = RTMGRP_IPV6_ROUTE,
};
if (bind(nlfd, (struct sockaddr *)&snl, sizeof(snl)) < 0)
perror("can't bind RTNL socket");
struct in6_addr addr = {};
inet_pton(AF_INET6, "2000::/3", &addr);
get_route(nlfd, 0, &addr, 3);
struct nlmsghdr readbuffer[8192/sizeof(struct nlmsghdr)];
int count = recv(nlfd, readbuffer, sizeof(readbuffer), 0);
struct nlmsghdr *nh;
struct nlmsgerr *ne;
struct kernel_route route;
nh = (struct nlmsghdr *)readbuffer;
if (NLMSG_OK(nh, count)) {
switch (nh->nlmsg_type) {
case NLMSG_DONE:
break;
case NLMSG_ERROR:
ne = NLMSG_DATA(nh);
if (ne->error <= 0)
break;
/* Falls through. */
default:
if (rtnl_handle_msg(nh, &route) == true) {
char strbuf[64];
// TODO: for each route that is retrieved, create a json object
// containing src, via, interface Yanic currently requires this layout
// but it makes sense to adjust it. See https://github.com/FreifunkBremen/yanic/issues/170
if (ret) {
json_object_object_add(ret, "gateway_src", json_object_new_string(print_ip(&route.src_prefix, strbuf, 64)));
json_object_object_add(ret, "gateway_nexthop", json_object_new_string(print_ip(&route.gw, strbuf, 64)));
char ifname[IFNAMSIZ];
json_object_object_add(ret, "gateway_interface", json_object_new_string(if_indextoname(route.ifindex, ifname)));
}
}
break;
}
}
close(nlfd);
return 0;
}
static int babeld_connect() {
int fd = -1;
fd_set rfds;
FD_ZERO(&rfds);
printf("connecting to babeld\n");
do {
fd = babelhelper_babel_connect(BABEL_PORT);
if (fd < 0) {
fprintf(stderr, "Connecting to babel socket failed. Retrying.\n");
sleep(1);
}
} while (fd < 0);
FD_SET(fd, &rfds);
// receive and ignore babel header
while (true) {
if ( babelhelper_input_pump(&bhelper_ctx, fd, NULL, babelhelper_discard_response))
break;
if (select(fd + 1, &rfds, NULL, NULL, NULL) < 0) {
perror("select (babel header):");
};
}
int amount = 0;
while (amount != 8) {
amount = babelhelper_sendcommand(&bhelper_ctx, fd, "monitor\n");
}
return fd;
}
static void obtain_if_addr(const char *ifname, char *lladdr) { static void obtain_if_addr(const char *ifname, char *lladdr) {
struct ifaddrs *ifaddr, *ifa; struct ifaddrs *ifaddr, *ifa;
@ -96,6 +339,104 @@ cleanup:
freeifaddrs(ifaddr); freeifaddrs(ifaddr);
} }
static void add_neighbour(char **data) {
struct json_object *neigh = json_object_new_object();
if (!neigh)
return;
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 = NULL;
if (data[IF] && !json_object_object_get_ex(neighbours, data[IF], &nif)) {
char str_ip[NI_MAXHOST] = {};
obtain_if_addr((const char *)data[IF], str_ip);
nif = json_object_new_object();
if (nif) {
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(neighbours, data[IF], nif);
}
}
struct json_object *nif_neighbours = NULL;
if (!json_object_object_get_ex(nif, "neighbours", &nif_neighbours)) {
nif_neighbours = json_object_new_object();
if (nif_neighbours) {
json_object_object_add(nif, "neighbours", nif_neighbours);
json_object_object_add(nif_neighbours, data[ADDRESS], neigh);
} else {
json_object_put(neigh);
}
} else {
json_object_object_add(nif_neighbours, data[ADDRESS], neigh);
}
}
static void del_neighbour(char **data) {
struct json_object *nif = NULL;
if (json_object_object_get_ex(neighbours, data[IF], &nif)) {
struct json_object *neighbour = NULL;
if (json_object_object_get_ex(nif, "neighbours", &neighbour)) {
json_object_object_del(neighbour, data[ADDRESS]);
}
}
}
static bool handle_neighbour(char **data, void *obj) {
if (data[NEIGHBOUR]) {
pthread_rwlock_wrlock(&neighbours_lock);
if (strncmp(data[VERB], "add", 3) == 0) {
del_neighbour(data);
add_neighbour(data);
} else if (strncmp(data[VERB], "del", 3) == 0) {
del_neighbour(data);
} else if (strncmp(data[VERB], "change", 6) == 0) {
del_neighbour(data);
add_neighbour(data);
}
pthread_rwlock_unlock(&neighbours_lock);
}
return false;
}
static bool babel_lineprocessor(char **data, void *object) {
return handle_neighbour(data, object);
}
static void *babeld_monitor_thread_start(void *arg) {
while (true) {
int babelfd = babeld_connect();
fd_set rfds;
FD_ZERO(&rfds);
FD_SET(babelfd, &rfds);
while (true) {
if ( babelhelper_input_pump(&bhelper_ctx, babelfd, NULL, babel_lineprocessor) < 0 ) {
perror("input pump");
break;
}
if (select(babelfd + 1, &rfds, NULL, NULL, NULL) < 0) {
perror("select (babel data):");
break;
};
}
close(babelfd);
}
return NULL;
}
static char *get_line_from_run(const char *command) { static char *get_line_from_run(const char *command) {
FILE *fp; FILE *fp;
@ -122,9 +463,47 @@ static char* get_line_from_run(const char* command) {
return line; return line;
} }
__attribute__((constructor)) static void init(void) {
if (pthread_rwlock_init(&neighbours_lock, NULL) != 0) {
perror("rwlock init failed for neighbours");
exit(-2);
}
neighbours = json_object_new_object();
char *version = get_line_from_run("exec babeld -V 2>&1");
babeld_version = gluonutil_wrap_string(version);
free(version);
model = gluonutil_read_line("/tmp/sysinfo/model");
if (neighbours) {
if (pthread_create(&babelmonitor, NULL, &babeld_monitor_thread_start, NULL) < 0 ) {
perror("error on pthread_create for babel monitor");
}
}
else {
fprintf(stderr, "Unable to allocate json object >neighbours< - not creating neighbour watcher thread. The neighbour structure will always be empty.\n");
}
}
__attribute__((destructor)) static void deinit(void) {
pthread_cancel(babelmonitor);
int s = pthread_join(babelmonitor, NULL);
if (s)
perror("pthread_cancel");
pthread_rwlock_destroy(&neighbours_lock);
}
static struct json_object * get_addresses(void) { static struct json_object * get_addresses(void) {
char *primarymac = gluonutil_get_sysconfig("primary_mac"); char *primarymac = gluonutil_get_sysconfig("primary_mac");
char *address = malloc(INET6_ADDRSTRLEN+1); char *address = malloc(INET6_ADDRSTRLEN+1);
if (!address) {
fprintf(stderr, "Could not allocate memory for ipv6 address, not adding addresses to json data.\n");
goto free;
}
char node_prefix_str[INET6_ADDRSTRLEN+1]; char node_prefix_str[INET6_ADDRSTRLEN+1];
struct in6_addr node_prefix = {}; struct in6_addr node_prefix = {};
struct json_object *retval = json_object_new_array(); struct json_object *retval = json_object_new_array();
@ -140,14 +519,20 @@ static struct json_object * get_addresses(void) {
} }
char *prefix_addresspart = strndup(node_prefix_str, INET6_ADDRSTRLEN); char *prefix_addresspart = strndup(node_prefix_str, INET6_ADDRSTRLEN);
if (! babelhelper_generateip_str(address, primarymac, prefix_addresspart) ) { if (!prefix_addresspart) {
fprintf(stderr, "IP-address could not be generated by babelhelper"); fprintf(stderr, "could not allocate memory to hold node_prefix_str. Not adding address to json data\n");
goto free;
}
if (! babelhelper_generateip_str(address, primarymac, prefix_addresspart) ) {
fprintf(stderr, "IP-address could not be generated by babelhelper\n");
goto free;
} }
free(prefix_addresspart);
json_object_array_add(retval, json_object_new_string(address)); json_object_array_add(retval, json_object_new_string(address));
free: free:
free(prefix_addresspart);
free(address); free(address);
free(primarymac); free(primarymac);
@ -176,57 +561,6 @@ static void mesh_add_if(const char *ifname, struct json_object *wireless,
json_object_array_add(tunnel, address); json_object_array_add(tunnel, address);
else else
json_object_array_add(other, address); 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, "dump", (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_list(struct blob_attr *attr, int len, bool array, struct json_object *wireless, struct json_object *tunnel, struct json_object *other);
@ -305,7 +639,6 @@ static void receive_call_result_data(struct ubus_request *req, int type, struct
*((struct json_object**)(req->priv)) = ret; *((struct json_object**)(req->priv)) = ret;
} }
static struct json_object * get_mesh_ifs() { static struct json_object * get_mesh_ifs() {
struct ubus_context *ubus_ctx; struct ubus_context *ubus_ctx;
struct json_object *ret = NULL; struct json_object *ret = NULL;
@ -344,11 +677,9 @@ static struct json_object * get_mesh(void) {
return ret; return ret;
} }
static struct json_object * get_babeld_version(void) { static struct json_object * get_babeld_version(void) {
char *version = get_line_from_run("exec babeld -V 2>&1"); return babeld_version ? json_object_get(babeld_version) : json_object_new_string("unknown");
struct json_object *ret = gluonutil_wrap_string(version);
free(version);
return ret;
} }
static struct json_object * respondd_provider_nodeinfo(void) { static struct json_object * respondd_provider_nodeinfo(void) {
@ -390,9 +721,7 @@ static struct json_object * read_number(const char *ifname, const char *stat) {
return ret; return ret;
} }
static struct json_object * get_traffic(void) { static struct json_object * get_traffic_if(const char *ifname) {
const char *ifname = "br-client";
struct json_object *ret = NULL; struct json_object *ret = NULL;
struct json_object *rx = json_object_new_object(); struct json_object *rx = json_object_new_object();
struct json_object *tx = json_object_new_object(); struct json_object *tx = json_object_new_object();
@ -407,21 +736,17 @@ static struct json_object * get_traffic(void) {
ret = json_object_new_object(); ret = json_object_new_object();
json_object_object_add(ret, "rx", rx); json_object_object_add(ret, "rx", rx);
json_object_object_add(ret, "tx", tx); json_object_object_add(ret, "tx", tx);
return ret; return ret;
} }
static bool handle_route_addgw_nexthop(char **data, void *arg) { static struct json_object * get_traffic(void) {
struct json_object *obj = (struct json_object*) arg; struct json_object *ret = get_traffic_if("br-client"); // keep for compatibility
if (data[PREFIX] && data[FROM] && data[VIA] && data[IF]) { json_object_object_add(ret, "local-node", get_traffic_if("local-node"));
if ( (! strncmp(data[PREFIX], "::/0", 4) ) && ( ! strncmp(data[FROM], "::/0", 4) ) ) { json_object_object_add(ret, "br-client", get_traffic_if("br-client"));
int gw_nexthoplen=strlen(data[VIA]) + strlen(data[IF])+2;
char gw_nexthop[gw_nexthoplen]; // TODO: add traffic stats for all mesh interfaces
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 ret;
}
}
return true;
} }
static int json_parse_get_clients(json_object * object) { static int json_parse_get_clients(json_object * object) {
@ -507,18 +832,35 @@ static struct json_object * respondd_provider_statistics(void) {
json_object_object_add(ret, "clients", get_clients()); json_object_object_add(ret, "clients", get_clients());
json_object_object_add(ret, "traffic", get_traffic()); json_object_object_add(ret, "traffic", get_traffic());
babelhelper_readbabeldata(&bhelper_ctx, "dump", (void*)ret, handle_route_addgw_nexthop ); get_default_route(ret);
return ret; return ret;
} }
static struct json_object * respondd_provider_neighbours(void) { static struct json_object * respondd_provider_neighbours(void) {
struct json_object *ret = json_object_new_object(); struct json_object *ret = json_object_new_object();
struct json_object *neighbours_copy = NULL;
struct json_object *babel = get_babel_neighbours(); if (!ret)
if (babel) return NULL;
json_object_object_add(ret, "babel", babel);
if (neighbours) {
pthread_rwlock_rdlock(&neighbours_lock);
#if (JSON_C_MINOR_VERSION >= 13)
int deepcopy_state = json_object_deep_copy(neighbours, &neighbours_copy, NULL);
#else
char *serialized_neighbours = json_object_to_json_string(neighbours);
#endif
pthread_rwlock_unlock(&neighbours_lock);
#if (JSON_C_MINOR_VERSION >= 13)
if (!deepcopy_state)
json_object_object_add(ret, "babel", neighbours_copy);
#else
if (serialized_neighbours)
json_object_object_add(ret, "babel", json_tokener_parse(serialized_neighbours));
#endif
}
return ret; return ret;
} }