gluon-mesh-batman-adv: further cleanup of respondd provider
- Split into multiple files - Avoid alloca()
This commit is contained in:
		
							parent
							
								
									0f1fa243f7
								
							
						
					
					
						commit
						ecc29e0b09
					
				| @ -31,5 +31,8 @@ endif | |||||||
| CFLAGS += $(LIBBATADV_CFLAGS) | CFLAGS += $(LIBBATADV_CFLAGS) | ||||||
| LDLIBS += $(LIBBATADV_LDLIBS) | LDLIBS += $(LIBBATADV_LDLIBS) | ||||||
| 
 | 
 | ||||||
| respondd.so: respondd.c | 
 | ||||||
| 	$(CC) $(CPPFLAGS) $(CFLAGS) $(LDFLAGS) -shared -fPIC -D_GNU_SOURCE -o $@ $^ $(LDLIBS) -lgluonutil -luci | SOURCES = respondd.c respondd-nodeinfo.c respondd-statistics.c respondd-neighbours.c | ||||||
|  | 
 | ||||||
|  | respondd.so: $(SOURCES) respondd-common.h | ||||||
|  | 	$(CC) $(CPPFLAGS) $(CFLAGS) $(LDFLAGS) -shared -fPIC -fvisibility=hidden -D_GNU_SOURCE -o $@ $(SOURCES) $(LDLIBS) -lgluonutil | ||||||
|  | |||||||
							
								
								
									
										30
									
								
								package/gluon-mesh-batman-adv/src/respondd-common.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										30
									
								
								package/gluon-mesh-batman-adv/src/respondd-common.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,30 @@ | |||||||
|  | /*
 | ||||||
|  |   Copyright (c) 2016-2019, 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. | ||||||
|  | */ | ||||||
|  | 
 | ||||||
|  | #pragma once | ||||||
|  | 
 | ||||||
|  | struct json_object * respondd_provider_nodeinfo(void); | ||||||
|  | struct json_object * respondd_provider_statistics(void); | ||||||
|  | struct json_object * respondd_provider_neighbours(void); | ||||||
							
								
								
									
										176
									
								
								package/gluon-mesh-batman-adv/src/respondd-neighbours.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										176
									
								
								package/gluon-mesh-batman-adv/src/respondd-neighbours.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,176 @@ | |||||||
|  | /*
 | ||||||
|  |   Copyright (c) 2016-2019, 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-common.h" | ||||||
|  | 
 | ||||||
|  | #include <batadv-genl.h> | ||||||
|  | #include <libgluonutil.h> | ||||||
|  | 
 | ||||||
|  | #include <json-c/json.h> | ||||||
|  | 
 | ||||||
|  | #include <netlink/netlink.h> | ||||||
|  | #include <netlink/genl/genl.h> | ||||||
|  | 
 | ||||||
|  | #include <stdio.h> | ||||||
|  | #include <stdlib.h> | ||||||
|  | #include <string.h> | ||||||
|  | 
 | ||||||
|  | #include <net/if.h> | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | struct neigh_netlink_opts { | ||||||
|  | 	struct json_object *interfaces; | ||||||
|  | 	struct batadv_nlquery_opts query_opts; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | static struct json_object * ifnames2addrs(struct json_object *interfaces) { | ||||||
|  | 	struct json_object *ret = json_object_new_object(); | ||||||
|  | 
 | ||||||
|  | 	json_object_object_foreach(interfaces, ifname, interface) { | ||||||
|  | 		char *ifaddr = gluonutil_get_interface_address(ifname); | ||||||
|  | 		if (!ifaddr) | ||||||
|  | 			continue; | ||||||
|  | 
 | ||||||
|  | 		struct json_object *obj = json_object_new_object(); | ||||||
|  | 		json_object_object_add(obj, "neighbours", json_object_get(interface)); | ||||||
|  | 		json_object_object_add(ret, ifaddr, obj); | ||||||
|  | 
 | ||||||
|  | 		free(ifaddr); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	json_object_put(interfaces); | ||||||
|  | 
 | ||||||
|  | 	return ret; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static const enum batadv_nl_attrs parse_orig_list_mandatory[] = { | ||||||
|  | 	BATADV_ATTR_ORIG_ADDRESS, | ||||||
|  | 	BATADV_ATTR_NEIGH_ADDRESS, | ||||||
|  | 	BATADV_ATTR_TQ, | ||||||
|  | 	BATADV_ATTR_HARD_IFINDEX, | ||||||
|  | 	BATADV_ATTR_LAST_SEEN_MSECS, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | static int parse_orig_list_netlink_cb(struct nl_msg *msg, void *arg) | ||||||
|  | { | ||||||
|  | 	struct nlattr *attrs[BATADV_ATTR_MAX+1]; | ||||||
|  | 	struct nlmsghdr *nlh = nlmsg_hdr(msg); | ||||||
|  | 	struct batadv_nlquery_opts *query_opts = arg; | ||||||
|  | 	struct genlmsghdr *ghdr; | ||||||
|  | 	uint8_t *orig; | ||||||
|  | 	uint8_t *dest; | ||||||
|  | 	uint8_t tq; | ||||||
|  | 	uint32_t hardif; | ||||||
|  | 	uint32_t lastseen; | ||||||
|  | 	char ifname_buf[IF_NAMESIZE], *ifname; | ||||||
|  | 	struct neigh_netlink_opts *opts; | ||||||
|  | 	char mac1[18]; | ||||||
|  | 
 | ||||||
|  | 	opts = batadv_container_of(query_opts, struct neigh_netlink_opts, | ||||||
|  | 				   query_opts); | ||||||
|  | 
 | ||||||
|  | 	if (!genlmsg_valid_hdr(nlh, 0)) | ||||||
|  | 		return NL_OK; | ||||||
|  | 
 | ||||||
|  | 	ghdr = nlmsg_data(nlh); | ||||||
|  | 
 | ||||||
|  | 	if (ghdr->cmd != BATADV_CMD_GET_ORIGINATORS) | ||||||
|  | 		return NL_OK; | ||||||
|  | 
 | ||||||
|  | 	if (nla_parse(attrs, BATADV_ATTR_MAX, genlmsg_attrdata(ghdr, 0), | ||||||
|  | 		      genlmsg_len(ghdr), batadv_genl_policy)) | ||||||
|  | 		return NL_OK; | ||||||
|  | 
 | ||||||
|  | 	if (batadv_genl_missing_attrs(attrs, parse_orig_list_mandatory, | ||||||
|  | 				      BATADV_ARRAY_SIZE(parse_orig_list_mandatory))) | ||||||
|  | 		return NL_OK; | ||||||
|  | 
 | ||||||
|  | 	orig = nla_data(attrs[BATADV_ATTR_ORIG_ADDRESS]); | ||||||
|  | 	dest = nla_data(attrs[BATADV_ATTR_NEIGH_ADDRESS]); | ||||||
|  | 	tq = nla_get_u8(attrs[BATADV_ATTR_TQ]); | ||||||
|  | 	hardif = nla_get_u32(attrs[BATADV_ATTR_HARD_IFINDEX]); | ||||||
|  | 	lastseen = nla_get_u32(attrs[BATADV_ATTR_LAST_SEEN_MSECS]); | ||||||
|  | 
 | ||||||
|  | 	if (memcmp(orig, dest, 6) != 0) | ||||||
|  | 		return NL_OK; | ||||||
|  | 
 | ||||||
|  | 	ifname = if_indextoname(hardif, ifname_buf); | ||||||
|  | 	if (!ifname) | ||||||
|  | 		return NL_OK; | ||||||
|  | 
 | ||||||
|  | 	sprintf(mac1, "%02x:%02x:%02x:%02x:%02x:%02x", | ||||||
|  | 		orig[0], orig[1], orig[2], orig[3], orig[4], orig[5]); | ||||||
|  | 
 | ||||||
|  | 	struct json_object *obj = json_object_new_object(); | ||||||
|  | 	if (!obj) | ||||||
|  | 		return NL_OK; | ||||||
|  | 
 | ||||||
|  | 	struct json_object *interface; | ||||||
|  | 	if (!json_object_object_get_ex(opts->interfaces, ifname, &interface)) { | ||||||
|  | 		interface = json_object_new_object(); | ||||||
|  | 		json_object_object_add(opts->interfaces, ifname, interface); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	json_object_object_add(obj, "tq", json_object_new_int(tq)); | ||||||
|  | 	json_object_object_add(obj, "lastseen", json_object_new_double(lastseen / 1000.)); | ||||||
|  | 	json_object_object_add(obj, "best", json_object_new_boolean(!!attrs[BATADV_ATTR_FLAG_BEST])); | ||||||
|  | 	json_object_object_add(interface, mac1, obj); | ||||||
|  | 
 | ||||||
|  | 	return NL_OK; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static struct json_object * get_batadv(void) { | ||||||
|  | 	struct neigh_netlink_opts opts = { | ||||||
|  | 		.query_opts = { | ||||||
|  | 			.err = 0, | ||||||
|  | 		}, | ||||||
|  | 	}; | ||||||
|  | 	int ret; | ||||||
|  | 
 | ||||||
|  | 	opts.interfaces = json_object_new_object(); | ||||||
|  | 	if (!opts.interfaces) | ||||||
|  | 		return NULL; | ||||||
|  | 
 | ||||||
|  | 	ret = batadv_genl_query("bat0", BATADV_CMD_GET_ORIGINATORS, | ||||||
|  | 				parse_orig_list_netlink_cb, NLM_F_DUMP, | ||||||
|  | 				&opts.query_opts); | ||||||
|  | 	if (ret < 0) { | ||||||
|  | 		json_object_put(opts.interfaces); | ||||||
|  | 		return NULL; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	return ifnames2addrs(opts.interfaces); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | struct json_object * respondd_provider_neighbours(void) { | ||||||
|  | 	struct json_object *ret = json_object_new_object(); | ||||||
|  | 
 | ||||||
|  | 	struct json_object *batadv = get_batadv(); | ||||||
|  | 	if (batadv) | ||||||
|  | 		json_object_object_add(ret, "batadv", batadv); | ||||||
|  | 
 | ||||||
|  | 	return ret; | ||||||
|  | } | ||||||
							
								
								
									
										219
									
								
								package/gluon-mesh-batman-adv/src/respondd-nodeinfo.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										219
									
								
								package/gluon-mesh-batman-adv/src/respondd-nodeinfo.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,219 @@ | |||||||
|  | /*
 | ||||||
|  |   Copyright (c) 2016-2019, 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-common.h" | ||||||
|  | 
 | ||||||
|  | #include <libgluonutil.h> | ||||||
|  | 
 | ||||||
|  | #include <json-c/json.h> | ||||||
|  | 
 | ||||||
|  | #include <netlink/netlink.h> | ||||||
|  | #include <netlink/msg.h> | ||||||
|  | 
 | ||||||
|  | #include <glob.h> | ||||||
|  | #include <stdbool.h> | ||||||
|  | #include <stdio.h> | ||||||
|  | #include <stdlib.h> | ||||||
|  | #include <string.h> | ||||||
|  | #include <unistd.h> | ||||||
|  | 
 | ||||||
|  | #include <arpa/inet.h> | ||||||
|  | #include <net/if.h> | ||||||
|  | #include <netinet/in.h> | ||||||
|  | 
 | ||||||
|  | #include <linux/if_addr.h> | ||||||
|  | #include <linux/rtnetlink.h> | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | struct ip_address_information { | ||||||
|  | 	unsigned int ifindex; | ||||||
|  | 	struct json_object *addresses; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | static int get_addresses_cb(struct nl_msg *msg, void *arg) { | ||||||
|  | 	struct ip_address_information *info = (struct ip_address_information*) arg; | ||||||
|  | 
 | ||||||
|  | 	struct nlmsghdr *nlh = nlmsg_hdr(msg); | ||||||
|  | 	struct ifaddrmsg *msg_content = NLMSG_DATA(nlh); | ||||||
|  | 	int remaining = nlh->nlmsg_len - NLMSG_LENGTH(sizeof(struct ifaddrmsg)); | ||||||
|  | 	struct rtattr *hdr; | ||||||
|  | 
 | ||||||
|  | 	for (hdr = IFA_RTA(msg_content); RTA_OK(hdr, remaining); hdr = RTA_NEXT(hdr, remaining)) { | ||||||
|  | 		char addr_str_buf[INET6_ADDRSTRLEN]; | ||||||
|  | 
 | ||||||
|  | 		/* We are only interested in IP-addresses of br-client */ | ||||||
|  | 		if (hdr->rta_type != IFA_ADDRESS || | ||||||
|  | 			msg_content->ifa_index != info->ifindex || | ||||||
|  | 			msg_content->ifa_flags & (IFA_F_TENTATIVE|IFA_F_DEPRECATED)) { | ||||||
|  | 			continue; | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		if (inet_ntop(AF_INET6, (struct in6_addr *) RTA_DATA(hdr), addr_str_buf, INET6_ADDRSTRLEN)) { | ||||||
|  | 			json_object_array_add(info->addresses, json_object_new_string(addr_str_buf)); | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	return NL_OK; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static struct json_object *get_addresses(void) { | ||||||
|  | 	struct ip_address_information info = { | ||||||
|  | 		.ifindex = if_nametoindex("br-client"), | ||||||
|  | 		.addresses = json_object_new_array(), | ||||||
|  | 	}; | ||||||
|  | 	int err; | ||||||
|  | 
 | ||||||
|  | 	/* Open socket */ | ||||||
|  | 	struct nl_sock *socket = nl_socket_alloc(); | ||||||
|  | 	if (!socket) { | ||||||
|  | 		return info.addresses; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	err = nl_connect(socket, NETLINK_ROUTE); | ||||||
|  | 	if (err < 0) { | ||||||
|  | 		goto out_free; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	/* Send message */ | ||||||
|  | 	struct ifaddrmsg rt_hdr = { .ifa_family = AF_INET6, }; | ||||||
|  | 	err = nl_send_simple(socket, RTM_GETADDR, NLM_F_REQUEST | NLM_F_ROOT, &rt_hdr, sizeof(struct ifaddrmsg)); | ||||||
|  | 	if (err < 0) { | ||||||
|  | 		goto out_free; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	/* Retrieve answer. Message is handled by get_addresses_cb */ | ||||||
|  | 	nl_socket_modify_cb(socket, NL_CB_VALID, NL_CB_CUSTOM, get_addresses_cb, &info); | ||||||
|  | 	nl_recvmsgs_default(socket); | ||||||
|  | 
 | ||||||
|  | out_free: | ||||||
|  | 	nl_socket_free(socket); | ||||||
|  | 	return info.addresses; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void add_if_not_empty(struct json_object *obj, const char *key, struct json_object *val) { | ||||||
|  | 	if (json_object_array_length(val)) | ||||||
|  | 		json_object_object_add(obj, key, val); | ||||||
|  | 	else | ||||||
|  | 		json_object_put(val); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 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)]; | ||||||
|  | 	snprintf(path, sizeof(path), format, ifname, name); | ||||||
|  | 
 | ||||||
|  | 	return !access(path, F_OK); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void mesh_add_subif(const char *ifname, struct json_object *wireless, | ||||||
|  | 			   struct json_object *tunnel, struct json_object *other) { | ||||||
|  | 	struct json_object *address = gluonutil_wrap_and_free_string(gluonutil_get_interface_address(ifname)); | ||||||
|  | 
 | ||||||
|  | 	char lowername[IFNAMSIZ]; | ||||||
|  | 	strncpy(lowername, ifname, sizeof(lowername)-1); | ||||||
|  | 	lowername[sizeof(lowername)-1] = 0; | ||||||
|  | 
 | ||||||
|  | 	const char *format = "/sys/class/net/%s/lower_*"; | ||||||
|  | 	char pattern[strlen(format) + IFNAMSIZ]; | ||||||
|  | 
 | ||||||
|  | 	/* In case of VLAN and bridge interfaces, we want the lower interface
 | ||||||
|  | 	 * to determine the interface type (but not for the interface address) */ | ||||||
|  | 	while (true) { | ||||||
|  | 		snprintf(pattern, sizeof(pattern), format, lowername); | ||||||
|  | 		size_t pattern_len = strlen(pattern); | ||||||
|  | 
 | ||||||
|  | 		glob_t lower; | ||||||
|  | 		if (glob(pattern, GLOB_NOSORT, NULL, &lower)) | ||||||
|  | 			break; | ||||||
|  | 
 | ||||||
|  | 		strncpy(lowername, lower.gl_pathv[0] + pattern_len - 1, sizeof(lowername)-1); | ||||||
|  | 
 | ||||||
|  | 		globfree(&lower); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	if (interface_file_exists(lowername, "wireless")) | ||||||
|  | 		json_object_array_add(wireless, address); | ||||||
|  | 	else if (interface_file_exists(lowername, "tun_flags")) | ||||||
|  | 		json_object_array_add(tunnel, address); | ||||||
|  | 	else | ||||||
|  | 		json_object_array_add(other, address); | ||||||
|  | 
 | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static struct json_object * get_mesh_subifs(const char *ifname) { | ||||||
|  | 	struct json_object *wireless = json_object_new_array(); | ||||||
|  | 	struct json_object *tunnel = json_object_new_array(); | ||||||
|  | 	struct json_object *other = json_object_new_array(); | ||||||
|  | 
 | ||||||
|  | 	const char *format = "/sys/class/net/%s/lower_*"; | ||||||
|  | 	char pattern[strlen(format) + strlen(ifname) - 1]; | ||||||
|  | 	snprintf(pattern, sizeof(pattern), format, ifname); | ||||||
|  | 
 | ||||||
|  | 	size_t pattern_len = strlen(pattern); | ||||||
|  | 
 | ||||||
|  | 	glob_t lower; | ||||||
|  | 	if (!glob(pattern, GLOB_NOSORT, NULL, &lower)) { | ||||||
|  | 		size_t i; | ||||||
|  | 		for (i = 0; i < lower.gl_pathc; i++) { | ||||||
|  | 			mesh_add_subif(lower.gl_pathv[i] + pattern_len - 1, | ||||||
|  | 				       wireless, tunnel, other); | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		globfree(&lower); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	struct json_object *ret = json_object_new_object(); | ||||||
|  | 	add_if_not_empty(ret, "wireless", wireless); | ||||||
|  | 	add_if_not_empty(ret, "tunnel", tunnel); | ||||||
|  | 	add_if_not_empty(ret, "other", other); | ||||||
|  | 	return ret; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static struct json_object * get_mesh(void) { | ||||||
|  | 	struct json_object *ret = json_object_new_object(); | ||||||
|  | 	struct json_object *bat0_interfaces = json_object_new_object(); | ||||||
|  | 	json_object_object_add(bat0_interfaces, "interfaces", get_mesh_subifs("bat0")); | ||||||
|  | 	json_object_object_add(ret, "bat0", bat0_interfaces); | ||||||
|  | 	return ret; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | struct json_object * respondd_provider_nodeinfo(void) { | ||||||
|  | 	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_batman_adv = json_object_new_object(); | ||||||
|  | 	json_object_object_add(software_batman_adv, "version", | ||||||
|  | 		gluonutil_wrap_and_free_string(gluonutil_read_line("/sys/module/batman_adv/version"))); | ||||||
|  | 	json_object_object_add(software_batman_adv, "compat", json_object_new_int(15)); | ||||||
|  | 	json_object_object_add(software, "batman-adv", software_batman_adv); | ||||||
|  | 	json_object_object_add(ret, "software", software); | ||||||
|  | 
 | ||||||
|  | 	return ret; | ||||||
|  | } | ||||||
							
								
								
									
										322
									
								
								package/gluon-mesh-batman-adv/src/respondd-statistics.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										322
									
								
								package/gluon-mesh-batman-adv/src/respondd-statistics.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,322 @@ | |||||||
|  | /*
 | ||||||
|  |   Copyright (c) 2016-2019, 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-common.h" | ||||||
|  | 
 | ||||||
|  | #include <batadv-genl.h> | ||||||
|  | 
 | ||||||
|  | #include <json-c/json.h> | ||||||
|  | 
 | ||||||
|  | #include <netlink/netlink.h> | ||||||
|  | #include <netlink/genl/genl.h> | ||||||
|  | 
 | ||||||
|  | #include <stdbool.h> | ||||||
|  | #include <stdio.h> | ||||||
|  | #include <stdlib.h> | ||||||
|  | #include <string.h> | ||||||
|  | #include <unistd.h> | ||||||
|  | 
 | ||||||
|  | #include <net/if.h> | ||||||
|  | #include <sys/types.h> | ||||||
|  | #include <sys/ioctl.h> | ||||||
|  | 
 | ||||||
|  | #include <linux/ethtool.h> | ||||||
|  | #include <linux/sockios.h> | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | #define MAX_INACTIVITY 60000 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | struct clients_netlink_opts { | ||||||
|  | 	size_t clients; | ||||||
|  | 	struct batadv_nlquery_opts query_opts; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | struct gw_netlink_opts { | ||||||
|  | 	struct json_object *obj; | ||||||
|  | 	struct batadv_nlquery_opts query_opts; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | static const enum batadv_nl_attrs gateways_mandatory[] = { | ||||||
|  | 	BATADV_ATTR_ORIG_ADDRESS, | ||||||
|  | 	BATADV_ATTR_ROUTER, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | static int parse_gw_list_netlink_cb(struct nl_msg *msg, void *arg) | ||||||
|  | { | ||||||
|  | 	struct nlattr *attrs[BATADV_ATTR_MAX+1]; | ||||||
|  | 	struct nlmsghdr *nlh = nlmsg_hdr(msg); | ||||||
|  | 	struct batadv_nlquery_opts *query_opts = arg; | ||||||
|  | 	struct genlmsghdr *ghdr; | ||||||
|  | 	uint8_t *orig; | ||||||
|  | 	uint8_t *router; | ||||||
|  | 	struct gw_netlink_opts *opts; | ||||||
|  | 	char addr[18]; | ||||||
|  | 
 | ||||||
|  | 	opts = batadv_container_of(query_opts, struct gw_netlink_opts, | ||||||
|  | 				   query_opts); | ||||||
|  | 
 | ||||||
|  | 	if (!genlmsg_valid_hdr(nlh, 0)) | ||||||
|  | 		return NL_OK; | ||||||
|  | 
 | ||||||
|  | 	ghdr = nlmsg_data(nlh); | ||||||
|  | 
 | ||||||
|  | 	if (ghdr->cmd != BATADV_CMD_GET_GATEWAYS) | ||||||
|  | 		return NL_OK; | ||||||
|  | 
 | ||||||
|  | 	if (nla_parse(attrs, BATADV_ATTR_MAX, genlmsg_attrdata(ghdr, 0), | ||||||
|  | 		      genlmsg_len(ghdr), batadv_genl_policy)) | ||||||
|  | 		return NL_OK; | ||||||
|  | 
 | ||||||
|  | 	if (batadv_genl_missing_attrs(attrs, gateways_mandatory, | ||||||
|  | 				      BATADV_ARRAY_SIZE(gateways_mandatory))) | ||||||
|  | 		return NL_OK; | ||||||
|  | 
 | ||||||
|  | 	if (!attrs[BATADV_ATTR_FLAG_BEST]) | ||||||
|  | 		return NL_OK; | ||||||
|  | 
 | ||||||
|  | 	orig = nla_data(attrs[BATADV_ATTR_ORIG_ADDRESS]); | ||||||
|  | 	router = nla_data(attrs[BATADV_ATTR_ROUTER]); | ||||||
|  | 
 | ||||||
|  | 	sprintf(addr, "%02x:%02x:%02x:%02x:%02x:%02x", | ||||||
|  | 		orig[0], orig[1], orig[2], orig[3], orig[4], orig[5]); | ||||||
|  | 
 | ||||||
|  | 	json_object_object_add(opts->obj, "gateway", json_object_new_string(addr)); | ||||||
|  | 
 | ||||||
|  | 	sprintf(addr, "%02x:%02x:%02x:%02x:%02x:%02x", | ||||||
|  | 		router[0], router[1], router[2], router[3], router[4], router[5]); | ||||||
|  | 
 | ||||||
|  | 	json_object_object_add(opts->obj, "gateway_nexthop", json_object_new_string(addr)); | ||||||
|  | 
 | ||||||
|  | 	return NL_STOP; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void add_gateway(struct json_object *obj) { | ||||||
|  | 	struct gw_netlink_opts opts = { | ||||||
|  | 		.obj = obj, | ||||||
|  | 		.query_opts = { | ||||||
|  | 			.err = 0, | ||||||
|  | 		}, | ||||||
|  | 	}; | ||||||
|  | 
 | ||||||
|  | 	batadv_genl_query("bat0", BATADV_CMD_GET_GATEWAYS, | ||||||
|  | 			  parse_gw_list_netlink_cb, NLM_F_DUMP, | ||||||
|  | 			  &opts.query_opts); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static inline bool ethtool_ioctl(int fd, struct ifreq *ifr, void *data) { | ||||||
|  | 	ifr->ifr_data = data; | ||||||
|  | 
 | ||||||
|  | 	return (ioctl(fd, SIOCETHTOOL, ifr) >= 0); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static uint32_t ethtool_get_stats_length(int fd, struct ifreq *ifr) { | ||||||
|  | 	struct { | ||||||
|  | 		struct ethtool_sset_info info; | ||||||
|  | 		uint32_t buf; | ||||||
|  | 	} sset = {}; | ||||||
|  | 
 | ||||||
|  | 	sset.info.cmd = ETHTOOL_GSSET_INFO; | ||||||
|  | 	sset.info.sset_mask = (uint64_t)1 << ETH_SS_STATS; | ||||||
|  | 
 | ||||||
|  | 	if (!ethtool_ioctl(fd, ifr, &sset.info)) | ||||||
|  | 		return 0; | ||||||
|  | 
 | ||||||
|  | 	return sset.info.sset_mask ? sset.info.data[0] : 0; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static struct ethtool_gstrings * ethtool_get_stats_strings(int fd, struct ifreq *ifr) { | ||||||
|  | 	uint32_t n_stats = ethtool_get_stats_length(fd, ifr); | ||||||
|  | 
 | ||||||
|  | 	if (!n_stats) | ||||||
|  | 		return NULL; | ||||||
|  | 
 | ||||||
|  | 	struct ethtool_gstrings *strings = calloc(1, sizeof(*strings) + n_stats * ETH_GSTRING_LEN); | ||||||
|  | 
 | ||||||
|  | 	strings->cmd = ETHTOOL_GSTRINGS; | ||||||
|  | 	strings->string_set = ETH_SS_STATS; | ||||||
|  | 	strings->len = n_stats; | ||||||
|  | 
 | ||||||
|  | 	if (!ethtool_ioctl(fd, ifr, strings)) { | ||||||
|  | 		free(strings); | ||||||
|  | 		return NULL; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	return strings; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | static struct json_object * get_traffic(void) { | ||||||
|  | 	struct ethtool_gstrings *strings = NULL; | ||||||
|  | 	struct ethtool_stats *stats = NULL; | ||||||
|  | 
 | ||||||
|  | 	struct ifreq ifr = {}; | ||||||
|  | 	strncpy(ifr.ifr_name, "bat0", IF_NAMESIZE); | ||||||
|  | 
 | ||||||
|  | 	struct json_object *ret = NULL; | ||||||
|  | 
 | ||||||
|  | 	int fd = socket(AF_INET, SOCK_DGRAM, 0); | ||||||
|  | 	if (fd < 0) | ||||||
|  | 		return NULL; | ||||||
|  | 
 | ||||||
|  | 	strings = ethtool_get_stats_strings(fd, &ifr); | ||||||
|  | 	if (!strings) | ||||||
|  | 		goto out; | ||||||
|  | 
 | ||||||
|  | 	stats = calloc(1, sizeof(struct ethtool_stats) + strings->len * sizeof(uint64_t)); | ||||||
|  | 	stats->cmd = ETHTOOL_GSTATS; | ||||||
|  | 	stats->n_stats = strings->len; | ||||||
|  | 
 | ||||||
|  | 	if (!ethtool_ioctl(fd, &ifr, stats)) | ||||||
|  | 		goto out; | ||||||
|  | 
 | ||||||
|  | 	struct json_object *rx = json_object_new_object(); | ||||||
|  | 	struct json_object *tx = json_object_new_object(); | ||||||
|  | 	struct json_object *forward = json_object_new_object(); | ||||||
|  | 	struct json_object *mgmt_rx = json_object_new_object(); | ||||||
|  | 	struct json_object *mgmt_tx = json_object_new_object(); | ||||||
|  | 
 | ||||||
|  | 	size_t i; | ||||||
|  | 	for (i = 0; i < strings->len; i++) { | ||||||
|  | 		if (!strncmp((const char*)&strings->data[i * ETH_GSTRING_LEN], "rx", ETH_GSTRING_LEN)) | ||||||
|  | 			json_object_object_add(rx, "packets", json_object_new_int64(stats->data[i])); | ||||||
|  | 		else if (!strncmp((const char*)&strings->data[i * ETH_GSTRING_LEN], "rx_bytes", ETH_GSTRING_LEN)) | ||||||
|  | 			json_object_object_add(rx, "bytes", json_object_new_int64(stats->data[i])); | ||||||
|  | 		else if (!strncmp((const char*)&strings->data[i * ETH_GSTRING_LEN], "tx", ETH_GSTRING_LEN)) | ||||||
|  | 			json_object_object_add(tx, "packets", json_object_new_int64(stats->data[i])); | ||||||
|  | 		else if (!strncmp((const char*)&strings->data[i * ETH_GSTRING_LEN], "tx_dropped", ETH_GSTRING_LEN)) | ||||||
|  | 			json_object_object_add(tx, "dropped", json_object_new_int64(stats->data[i])); | ||||||
|  | 		else if (!strncmp((const char*)&strings->data[i * ETH_GSTRING_LEN], "tx_bytes", ETH_GSTRING_LEN)) | ||||||
|  | 			json_object_object_add(tx, "bytes", json_object_new_int64(stats->data[i])); | ||||||
|  | 		else if (!strncmp((const char*)&strings->data[i * ETH_GSTRING_LEN], "forward", ETH_GSTRING_LEN)) | ||||||
|  | 			json_object_object_add(forward, "packets", json_object_new_int64(stats->data[i])); | ||||||
|  | 		else if (!strncmp((const char*)&strings->data[i * ETH_GSTRING_LEN], "forward_bytes", ETH_GSTRING_LEN)) | ||||||
|  | 			json_object_object_add(forward, "bytes", json_object_new_int64(stats->data[i])); | ||||||
|  | 		else if (!strncmp((const char*)&strings->data[i * ETH_GSTRING_LEN], "mgmt_rx", ETH_GSTRING_LEN)) | ||||||
|  | 			json_object_object_add(mgmt_rx, "packets", json_object_new_int64(stats->data[i])); | ||||||
|  | 		else if (!strncmp((const char*)&strings->data[i * ETH_GSTRING_LEN], "mgmt_rx_bytes", ETH_GSTRING_LEN)) | ||||||
|  | 			json_object_object_add(mgmt_rx, "bytes", json_object_new_int64(stats->data[i])); | ||||||
|  | 		else if (!strncmp((const char*)&strings->data[i * ETH_GSTRING_LEN], "mgmt_tx", ETH_GSTRING_LEN)) | ||||||
|  | 			json_object_object_add(mgmt_tx, "packets", json_object_new_int64(stats->data[i])); | ||||||
|  | 		else if (!strncmp((const char*)&strings->data[i * ETH_GSTRING_LEN], "mgmt_tx_bytes", ETH_GSTRING_LEN)) | ||||||
|  | 			json_object_object_add(mgmt_tx, "bytes", json_object_new_int64(stats->data[i])); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	ret = json_object_new_object(); | ||||||
|  | 	json_object_object_add(ret, "rx", rx); | ||||||
|  | 	json_object_object_add(ret, "tx", tx); | ||||||
|  | 	json_object_object_add(ret, "forward", forward); | ||||||
|  | 	json_object_object_add(ret, "mgmt_rx", mgmt_rx); | ||||||
|  | 	json_object_object_add(ret, "mgmt_tx", mgmt_tx); | ||||||
|  | 
 | ||||||
|  |  out: | ||||||
|  | 	free(stats); | ||||||
|  | 	free(strings); | ||||||
|  | 	close(fd); | ||||||
|  | 	return ret; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static const enum batadv_nl_attrs clients_mandatory[] = { | ||||||
|  | 	BATADV_ATTR_TT_FLAGS, | ||||||
|  | 	/* Entries without the BATADV_TT_CLIENT_NOPURGE flag do not have a
 | ||||||
|  | 	 * BATADV_ATTR_LAST_SEEN_MSECS attribute. We can still make this attr | ||||||
|  | 	 * mandatory here, as entries without BATADV_TT_CLIENT_NOPURGE are | ||||||
|  | 	 * ignored anyways. | ||||||
|  | 	 */ | ||||||
|  | 	BATADV_ATTR_LAST_SEEN_MSECS, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | static int parse_clients_list_netlink_cb(struct nl_msg *msg, void *arg) | ||||||
|  | { | ||||||
|  | 	struct nlattr *attrs[BATADV_ATTR_MAX+1]; | ||||||
|  | 	struct nlmsghdr *nlh = nlmsg_hdr(msg); | ||||||
|  | 	struct batadv_nlquery_opts *query_opts = arg; | ||||||
|  | 	struct genlmsghdr *ghdr; | ||||||
|  | 	struct clients_netlink_opts *opts; | ||||||
|  | 	uint32_t flags, lastseen; | ||||||
|  | 
 | ||||||
|  | 	opts = batadv_container_of(query_opts, struct clients_netlink_opts, | ||||||
|  | 				   query_opts); | ||||||
|  | 
 | ||||||
|  | 	if (!genlmsg_valid_hdr(nlh, 0)) | ||||||
|  | 		return NL_OK; | ||||||
|  | 
 | ||||||
|  | 	ghdr = nlmsg_data(nlh); | ||||||
|  | 
 | ||||||
|  | 	if (ghdr->cmd != BATADV_CMD_GET_TRANSTABLE_LOCAL) | ||||||
|  | 		return NL_OK; | ||||||
|  | 
 | ||||||
|  | 	if (nla_parse(attrs, BATADV_ATTR_MAX, genlmsg_attrdata(ghdr, 0), | ||||||
|  | 		      genlmsg_len(ghdr), batadv_genl_policy)) | ||||||
|  | 		return NL_OK; | ||||||
|  | 
 | ||||||
|  | 	if (batadv_genl_missing_attrs(attrs, clients_mandatory, | ||||||
|  | 				      BATADV_ARRAY_SIZE(clients_mandatory))) | ||||||
|  | 		return NL_OK; | ||||||
|  | 
 | ||||||
|  | 	flags = nla_get_u32(attrs[BATADV_ATTR_TT_FLAGS]); | ||||||
|  | 
 | ||||||
|  | 	if (flags & (BATADV_TT_CLIENT_NOPURGE)) | ||||||
|  | 		return NL_OK; | ||||||
|  | 
 | ||||||
|  | 	lastseen = nla_get_u32(attrs[BATADV_ATTR_LAST_SEEN_MSECS]); | ||||||
|  | 	if (lastseen > MAX_INACTIVITY) | ||||||
|  | 		return NL_OK; | ||||||
|  | 
 | ||||||
|  | 	opts->clients++; | ||||||
|  | 
 | ||||||
|  | 	return NL_OK; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static struct json_object * get_clients(void) { | ||||||
|  | 	struct clients_netlink_opts opts = { | ||||||
|  | 		.clients = 0, | ||||||
|  | 		.query_opts = { | ||||||
|  | 			.err = 0, | ||||||
|  | 		}, | ||||||
|  | 	}; | ||||||
|  | 
 | ||||||
|  | 	batadv_genl_query("bat0", BATADV_CMD_GET_TRANSTABLE_LOCAL, | ||||||
|  | 			  parse_clients_list_netlink_cb, NLM_F_DUMP, | ||||||
|  | 			  &opts.query_opts); | ||||||
|  | 
 | ||||||
|  | 	struct json_object *ret = json_object_new_object(); | ||||||
|  | 
 | ||||||
|  | 	json_object_object_add(ret, "total", json_object_new_int(opts.clients)); | ||||||
|  | 
 | ||||||
|  | 	return ret; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 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()); | ||||||
|  | 
 | ||||||
|  | 	add_gateway(ret); | ||||||
|  | 
 | ||||||
|  | 	return ret; | ||||||
|  | } | ||||||
| @ -1,5 +1,5 @@ | |||||||
| /*
 | /*
 | ||||||
|   Copyright (c) 2016, Matthias Schiffer <mschiffer@universe-factory.net> |   Copyright (c) 2016-2019, Matthias Schiffer <mschiffer@universe-factory.net> | ||||||
|   All rights reserved. |   All rights reserved. | ||||||
| 
 | 
 | ||||||
|   Redistribution and use in source and binary forms, with or without |   Redistribution and use in source and binary forms, with or without | ||||||
| @ -23,621 +23,12 @@ | |||||||
|   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||||||
| */ | */ | ||||||
| 
 | 
 | ||||||
|  | #include "respondd-common.h" | ||||||
| 
 | 
 | ||||||
| #include <respondd.h> | #include <respondd.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 <netlink/netlink.h> |  | ||||||
| #include <netlink/genl/genl.h> |  | ||||||
| 
 |  | ||||||
| #include <sys/types.h> |  | ||||||
| #include <sys/ioctl.h> |  | ||||||
| #include <sys/socket.h> |  | ||||||
| 
 |  | ||||||
| #include <linux/ethtool.h> |  | ||||||
| #include <linux/if_addr.h> |  | ||||||
| #include <linux/rtnetlink.h> |  | ||||||
| #include <linux/sockios.h> |  | ||||||
| 
 |  | ||||||
| #include <batadv-genl.h> |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| #define MAX_INACTIVITY 60000 |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| struct neigh_netlink_opts { |  | ||||||
| 	struct json_object *interfaces; |  | ||||||
| 	struct batadv_nlquery_opts query_opts; |  | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| struct gw_netlink_opts { |  | ||||||
| 	struct json_object *obj; |  | ||||||
| 	struct batadv_nlquery_opts query_opts; |  | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| struct clients_netlink_opts { |  | ||||||
| 	size_t clients; |  | ||||||
| 	struct batadv_nlquery_opts query_opts; |  | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| struct ip_address_information { |  | ||||||
| 	unsigned int ifindex; |  | ||||||
| 	struct json_object *addresses; |  | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| static int get_addresses_cb(struct nl_msg *msg, void *arg) { |  | ||||||
| 	struct ip_address_information *info = (struct ip_address_information*) arg; |  | ||||||
| 
 |  | ||||||
| 	struct nlmsghdr *nlh = nlmsg_hdr(msg); |  | ||||||
| 	struct ifaddrmsg *msg_content = NLMSG_DATA(nlh); |  | ||||||
| 	int remaining = nlh->nlmsg_len - NLMSG_LENGTH(sizeof(struct ifaddrmsg)); |  | ||||||
| 	struct rtattr *hdr; |  | ||||||
| 
 |  | ||||||
| 	for (hdr = IFA_RTA(msg_content); RTA_OK(hdr, remaining); hdr = RTA_NEXT(hdr, remaining)) { |  | ||||||
| 		char addr_str_buf[INET6_ADDRSTRLEN]; |  | ||||||
| 
 |  | ||||||
| 		/* We are only interested in IP-addresses of br-client */ |  | ||||||
| 		if (hdr->rta_type != IFA_ADDRESS || |  | ||||||
| 			msg_content->ifa_index != info->ifindex || |  | ||||||
| 			msg_content->ifa_flags & (IFA_F_TENTATIVE|IFA_F_DEPRECATED)) { |  | ||||||
| 			continue; |  | ||||||
| 		} |  | ||||||
| 
 |  | ||||||
| 		if (inet_ntop(AF_INET6, (struct in6_addr *) RTA_DATA(hdr), addr_str_buf, INET6_ADDRSTRLEN)) { |  | ||||||
| 			json_object_array_add(info->addresses, json_object_new_string(addr_str_buf)); |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	return NL_OK; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| static struct json_object *get_addresses(void) { |  | ||||||
| 	struct ip_address_information info = { |  | ||||||
| 		.ifindex = if_nametoindex("br-client"), |  | ||||||
| 		.addresses = json_object_new_array(), |  | ||||||
| 	}; |  | ||||||
| 	int err; |  | ||||||
| 
 |  | ||||||
| 	/* Open socket */ |  | ||||||
| 	struct nl_sock *socket = nl_socket_alloc(); |  | ||||||
| 	if (!socket) { |  | ||||||
| 		return info.addresses; |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	err = nl_connect(socket, NETLINK_ROUTE); |  | ||||||
| 	if (err < 0) { |  | ||||||
| 		goto out_free; |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	/* Send message */ |  | ||||||
| 	struct ifaddrmsg rt_hdr = { .ifa_family = AF_INET6, }; |  | ||||||
| 	err = nl_send_simple(socket, RTM_GETADDR, NLM_F_REQUEST | NLM_F_ROOT, &rt_hdr, sizeof(struct ifaddrmsg)); |  | ||||||
| 	if (err < 0) { |  | ||||||
| 		goto out_free; |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	/* Retrieve answer. Message is handled by get_addresses_cb */ |  | ||||||
| 	nl_socket_modify_cb(socket, NL_CB_VALID, NL_CB_CUSTOM, get_addresses_cb, &info); |  | ||||||
| 	nl_recvmsgs_default(socket); |  | ||||||
| 
 |  | ||||||
| out_free: |  | ||||||
| 	nl_socket_free(socket); |  | ||||||
| 	return info.addresses; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| static void add_if_not_empty(struct json_object *obj, const char *key, struct json_object *val) { |  | ||||||
| 	if (json_object_array_length(val)) |  | ||||||
| 		json_object_object_add(obj, key, val); |  | ||||||
| 	else |  | ||||||
| 		json_object_put(val); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| 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)]; |  | ||||||
| 	snprintf(path, sizeof(path), format, ifname, name); |  | ||||||
| 
 |  | ||||||
| 	return !access(path, F_OK); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| static void mesh_add_subif(const char *ifname, struct json_object *wireless, |  | ||||||
| 			   struct json_object *tunnel, struct json_object *other) { |  | ||||||
| 	struct json_object *address = gluonutil_wrap_and_free_string(gluonutil_get_interface_address(ifname)); |  | ||||||
| 
 |  | ||||||
| 	char lowername[IFNAMSIZ]; |  | ||||||
| 	strncpy(lowername, ifname, sizeof(lowername)-1); |  | ||||||
| 	lowername[sizeof(lowername)-1] = 0; |  | ||||||
| 
 |  | ||||||
| 	const char *format = "/sys/class/net/%s/lower_*"; |  | ||||||
| 	char pattern[strlen(format) + IFNAMSIZ]; |  | ||||||
| 
 |  | ||||||
| 	/* In case of VLAN and bridge interfaces, we want the lower interface
 |  | ||||||
| 	 * to determine the interface type (but not for the interface address) */ |  | ||||||
| 	while (true) { |  | ||||||
| 		snprintf(pattern, sizeof(pattern), format, lowername); |  | ||||||
| 		size_t pattern_len = strlen(pattern); |  | ||||||
| 
 |  | ||||||
| 		glob_t lower; |  | ||||||
| 		if (glob(pattern, GLOB_NOSORT, NULL, &lower)) |  | ||||||
| 			break; |  | ||||||
| 
 |  | ||||||
| 		strncpy(lowername, lower.gl_pathv[0] + pattern_len - 1, sizeof(lowername)-1); |  | ||||||
| 
 |  | ||||||
| 		globfree(&lower); |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	if (interface_file_exists(lowername, "wireless")) |  | ||||||
| 		json_object_array_add(wireless, address); |  | ||||||
| 	else if (interface_file_exists(lowername, "tun_flags")) |  | ||||||
| 		json_object_array_add(tunnel, address); |  | ||||||
| 	else |  | ||||||
| 		json_object_array_add(other, address); |  | ||||||
| 
 |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| static struct json_object * get_mesh_subifs(const char *ifname) { |  | ||||||
| 	struct json_object *wireless = json_object_new_array(); |  | ||||||
| 	struct json_object *tunnel = json_object_new_array(); |  | ||||||
| 	struct json_object *other = json_object_new_array(); |  | ||||||
| 
 |  | ||||||
| 	const char *format = "/sys/class/net/%s/lower_*"; |  | ||||||
| 	char pattern[strlen(format) + strlen(ifname) - 1]; |  | ||||||
| 	snprintf(pattern, sizeof(pattern), format, ifname); |  | ||||||
| 
 |  | ||||||
| 	size_t pattern_len = strlen(pattern); |  | ||||||
| 
 |  | ||||||
| 	glob_t lower; |  | ||||||
| 	if (!glob(pattern, GLOB_NOSORT, NULL, &lower)) { |  | ||||||
| 		size_t i; |  | ||||||
| 		for (i = 0; i < lower.gl_pathc; i++) { |  | ||||||
| 			mesh_add_subif(lower.gl_pathv[i] + pattern_len - 1, |  | ||||||
| 				       wireless, tunnel, other); |  | ||||||
| 		} |  | ||||||
| 
 |  | ||||||
| 		globfree(&lower); |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	struct json_object *ret = json_object_new_object(); |  | ||||||
| 	add_if_not_empty(ret, "wireless", wireless); |  | ||||||
| 	add_if_not_empty(ret, "tunnel", tunnel); |  | ||||||
| 	add_if_not_empty(ret, "other", other); |  | ||||||
| 	return ret; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| static struct json_object * get_mesh(void) { |  | ||||||
| 	struct json_object *ret = json_object_new_object(); |  | ||||||
| 	struct json_object *bat0_interfaces = json_object_new_object(); |  | ||||||
| 	json_object_object_add(bat0_interfaces, "interfaces", get_mesh_subifs("bat0")); |  | ||||||
| 	json_object_object_add(ret, "bat0", bat0_interfaces); |  | ||||||
| 	return ret; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| static struct json_object * respondd_provider_nodeinfo(void) { |  | ||||||
| 	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_batman_adv = json_object_new_object(); |  | ||||||
| 	json_object_object_add(software_batman_adv, "version", |  | ||||||
| 		gluonutil_wrap_and_free_string(gluonutil_read_line("/sys/module/batman_adv/version"))); |  | ||||||
| 	json_object_object_add(software_batman_adv, "compat", json_object_new_int(15)); |  | ||||||
| 	json_object_object_add(software, "batman-adv", software_batman_adv); |  | ||||||
| 	json_object_object_add(ret, "software", software); |  | ||||||
| 
 |  | ||||||
| 	return ret; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| static const enum batadv_nl_attrs gateways_mandatory[] = { |  | ||||||
| 	BATADV_ATTR_ORIG_ADDRESS, |  | ||||||
| 	BATADV_ATTR_ROUTER, |  | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| static int parse_gw_list_netlink_cb(struct nl_msg *msg, void *arg) |  | ||||||
| { |  | ||||||
| 	struct nlattr *attrs[BATADV_ATTR_MAX+1]; |  | ||||||
| 	struct nlmsghdr *nlh = nlmsg_hdr(msg); |  | ||||||
| 	struct batadv_nlquery_opts *query_opts = arg; |  | ||||||
| 	struct genlmsghdr *ghdr; |  | ||||||
| 	uint8_t *orig; |  | ||||||
| 	uint8_t *router; |  | ||||||
| 	struct gw_netlink_opts *opts; |  | ||||||
| 	char addr[18]; |  | ||||||
| 
 |  | ||||||
| 	opts = batadv_container_of(query_opts, struct gw_netlink_opts, |  | ||||||
| 				   query_opts); |  | ||||||
| 
 |  | ||||||
| 	if (!genlmsg_valid_hdr(nlh, 0)) |  | ||||||
| 		return NL_OK; |  | ||||||
| 
 |  | ||||||
| 	ghdr = nlmsg_data(nlh); |  | ||||||
| 
 |  | ||||||
| 	if (ghdr->cmd != BATADV_CMD_GET_GATEWAYS) |  | ||||||
| 		return NL_OK; |  | ||||||
| 
 |  | ||||||
| 	if (nla_parse(attrs, BATADV_ATTR_MAX, genlmsg_attrdata(ghdr, 0), |  | ||||||
| 		      genlmsg_len(ghdr), batadv_genl_policy)) |  | ||||||
| 		return NL_OK; |  | ||||||
| 
 |  | ||||||
| 	if (batadv_genl_missing_attrs(attrs, gateways_mandatory, |  | ||||||
| 				      BATADV_ARRAY_SIZE(gateways_mandatory))) |  | ||||||
| 		return NL_OK; |  | ||||||
| 
 |  | ||||||
| 	if (!attrs[BATADV_ATTR_FLAG_BEST]) |  | ||||||
| 		return NL_OK; |  | ||||||
| 
 |  | ||||||
| 	orig = nla_data(attrs[BATADV_ATTR_ORIG_ADDRESS]); |  | ||||||
| 	router = nla_data(attrs[BATADV_ATTR_ROUTER]); |  | ||||||
| 
 |  | ||||||
| 	sprintf(addr, "%02x:%02x:%02x:%02x:%02x:%02x", |  | ||||||
| 		orig[0], orig[1], orig[2], orig[3], orig[4], orig[5]); |  | ||||||
| 
 |  | ||||||
| 	json_object_object_add(opts->obj, "gateway", json_object_new_string(addr)); |  | ||||||
| 
 |  | ||||||
| 	sprintf(addr, "%02x:%02x:%02x:%02x:%02x:%02x", |  | ||||||
| 		router[0], router[1], router[2], router[3], router[4], router[5]); |  | ||||||
| 
 |  | ||||||
| 	json_object_object_add(opts->obj, "gateway_nexthop", json_object_new_string(addr)); |  | ||||||
| 
 |  | ||||||
| 	return NL_STOP; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| static void add_gateway(struct json_object *obj) { |  | ||||||
| 	struct gw_netlink_opts opts = { |  | ||||||
| 		.obj = obj, |  | ||||||
| 		.query_opts = { |  | ||||||
| 			.err = 0, |  | ||||||
| 		}, |  | ||||||
| 	}; |  | ||||||
| 
 |  | ||||||
| 	batadv_genl_query("bat0", BATADV_CMD_GET_GATEWAYS, |  | ||||||
| 			  parse_gw_list_netlink_cb, NLM_F_DUMP, |  | ||||||
| 			  &opts.query_opts); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| static inline bool ethtool_ioctl(int fd, struct ifreq *ifr, void *data) { |  | ||||||
| 	ifr->ifr_data = data; |  | ||||||
| 
 |  | ||||||
| 	return (ioctl(fd, SIOCETHTOOL, ifr) >= 0); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| static uint32_t ethtool_get_stats_length(int fd, struct ifreq *ifr) { |  | ||||||
| 	const size_t sset_info_len = sizeof(struct ethtool_sset_info) + sizeof(uint32_t); |  | ||||||
| 	struct ethtool_sset_info *sset_info = alloca(sset_info_len); |  | ||||||
| 	memset(sset_info, 0, sset_info_len); |  | ||||||
| 
 |  | ||||||
| 	sset_info->cmd = ETHTOOL_GSSET_INFO; |  | ||||||
| 	sset_info->sset_mask = 1ull << ETH_SS_STATS; |  | ||||||
| 
 |  | ||||||
| 	if (!ethtool_ioctl(fd, ifr, sset_info)) |  | ||||||
| 		return 0; |  | ||||||
| 
 |  | ||||||
| 	return sset_info->sset_mask ? sset_info->data[0] : 0; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| static struct ethtool_gstrings * ethtool_get_stats_strings(int fd, struct ifreq *ifr) { |  | ||||||
| 	uint32_t n_stats = ethtool_get_stats_length(fd, ifr); |  | ||||||
| 
 |  | ||||||
| 	if (!n_stats) |  | ||||||
| 		return NULL; |  | ||||||
| 
 |  | ||||||
| 	struct ethtool_gstrings *strings = calloc(1, sizeof(*strings) + n_stats * ETH_GSTRING_LEN); |  | ||||||
| 
 |  | ||||||
| 	strings->cmd = ETHTOOL_GSTRINGS; |  | ||||||
| 	strings->string_set = ETH_SS_STATS; |  | ||||||
| 	strings->len = n_stats; |  | ||||||
| 
 |  | ||||||
| 	if (!ethtool_ioctl(fd, ifr, strings)) { |  | ||||||
| 		free(strings); |  | ||||||
| 		return NULL; |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	return strings; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| static struct json_object * get_traffic(void) { |  | ||||||
| 	struct ethtool_gstrings *strings = NULL; |  | ||||||
| 	struct ethtool_stats *stats = NULL; |  | ||||||
| 
 |  | ||||||
| 	struct ifreq ifr = {}; |  | ||||||
| 	strncpy(ifr.ifr_name, "bat0", IF_NAMESIZE); |  | ||||||
| 
 |  | ||||||
| 	struct json_object *ret = NULL; |  | ||||||
| 
 |  | ||||||
| 	int fd = socket(AF_INET, SOCK_DGRAM, 0); |  | ||||||
| 	if (fd < 0) |  | ||||||
| 		return NULL; |  | ||||||
| 
 |  | ||||||
| 	strings = ethtool_get_stats_strings(fd, &ifr); |  | ||||||
| 	if (!strings) |  | ||||||
| 		goto out; |  | ||||||
| 
 |  | ||||||
| 	stats = calloc(1, sizeof(struct ethtool_stats) + strings->len * sizeof(uint64_t)); |  | ||||||
| 	stats->cmd = ETHTOOL_GSTATS; |  | ||||||
| 	stats->n_stats = strings->len; |  | ||||||
| 
 |  | ||||||
| 	if (!ethtool_ioctl(fd, &ifr, stats)) |  | ||||||
| 		goto out; |  | ||||||
| 
 |  | ||||||
| 	struct json_object *rx = json_object_new_object(); |  | ||||||
| 	struct json_object *tx = json_object_new_object(); |  | ||||||
| 	struct json_object *forward = json_object_new_object(); |  | ||||||
| 	struct json_object *mgmt_rx = json_object_new_object(); |  | ||||||
| 	struct json_object *mgmt_tx = json_object_new_object(); |  | ||||||
| 
 |  | ||||||
| 	size_t i; |  | ||||||
| 	for (i = 0; i < strings->len; i++) { |  | ||||||
| 		if (!strncmp((const char*)&strings->data[i * ETH_GSTRING_LEN], "rx", ETH_GSTRING_LEN)) |  | ||||||
| 			json_object_object_add(rx, "packets", json_object_new_int64(stats->data[i])); |  | ||||||
| 		else if (!strncmp((const char*)&strings->data[i * ETH_GSTRING_LEN], "rx_bytes", ETH_GSTRING_LEN)) |  | ||||||
| 			json_object_object_add(rx, "bytes", json_object_new_int64(stats->data[i])); |  | ||||||
| 		else if (!strncmp((const char*)&strings->data[i * ETH_GSTRING_LEN], "tx", ETH_GSTRING_LEN)) |  | ||||||
| 			json_object_object_add(tx, "packets", json_object_new_int64(stats->data[i])); |  | ||||||
| 		else if (!strncmp((const char*)&strings->data[i * ETH_GSTRING_LEN], "tx_dropped", ETH_GSTRING_LEN)) |  | ||||||
| 			json_object_object_add(tx, "dropped", json_object_new_int64(stats->data[i])); |  | ||||||
| 		else if (!strncmp((const char*)&strings->data[i * ETH_GSTRING_LEN], "tx_bytes", ETH_GSTRING_LEN)) |  | ||||||
| 			json_object_object_add(tx, "bytes", json_object_new_int64(stats->data[i])); |  | ||||||
| 		else if (!strncmp((const char*)&strings->data[i * ETH_GSTRING_LEN], "forward", ETH_GSTRING_LEN)) |  | ||||||
| 			json_object_object_add(forward, "packets", json_object_new_int64(stats->data[i])); |  | ||||||
| 		else if (!strncmp((const char*)&strings->data[i * ETH_GSTRING_LEN], "forward_bytes", ETH_GSTRING_LEN)) |  | ||||||
| 			json_object_object_add(forward, "bytes", json_object_new_int64(stats->data[i])); |  | ||||||
| 		else if (!strncmp((const char*)&strings->data[i * ETH_GSTRING_LEN], "mgmt_rx", ETH_GSTRING_LEN)) |  | ||||||
| 			json_object_object_add(mgmt_rx, "packets", json_object_new_int64(stats->data[i])); |  | ||||||
| 		else if (!strncmp((const char*)&strings->data[i * ETH_GSTRING_LEN], "mgmt_rx_bytes", ETH_GSTRING_LEN)) |  | ||||||
| 			json_object_object_add(mgmt_rx, "bytes", json_object_new_int64(stats->data[i])); |  | ||||||
| 		else if (!strncmp((const char*)&strings->data[i * ETH_GSTRING_LEN], "mgmt_tx", ETH_GSTRING_LEN)) |  | ||||||
| 			json_object_object_add(mgmt_tx, "packets", json_object_new_int64(stats->data[i])); |  | ||||||
| 		else if (!strncmp((const char*)&strings->data[i * ETH_GSTRING_LEN], "mgmt_tx_bytes", ETH_GSTRING_LEN)) |  | ||||||
| 			json_object_object_add(mgmt_tx, "bytes", json_object_new_int64(stats->data[i])); |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	ret = json_object_new_object(); |  | ||||||
| 	json_object_object_add(ret, "rx", rx); |  | ||||||
| 	json_object_object_add(ret, "tx", tx); |  | ||||||
| 	json_object_object_add(ret, "forward", forward); |  | ||||||
| 	json_object_object_add(ret, "mgmt_rx", mgmt_rx); |  | ||||||
| 	json_object_object_add(ret, "mgmt_tx", mgmt_tx); |  | ||||||
| 
 |  | ||||||
|  out: |  | ||||||
| 	free(stats); |  | ||||||
| 	free(strings); |  | ||||||
| 	close(fd); |  | ||||||
| 	return ret; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| static const enum batadv_nl_attrs clients_mandatory[] = { |  | ||||||
| 	BATADV_ATTR_TT_FLAGS, |  | ||||||
| 	/* Entries without the BATADV_TT_CLIENT_NOPURGE flag do not have a
 |  | ||||||
| 	 * BATADV_ATTR_LAST_SEEN_MSECS attribute. We can still make this attr |  | ||||||
| 	 * mandatory here, as entries without BATADV_TT_CLIENT_NOPURGE are |  | ||||||
| 	 * ignored anyways. |  | ||||||
| 	 */ |  | ||||||
| 	BATADV_ATTR_LAST_SEEN_MSECS, |  | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| static int parse_clients_list_netlink_cb(struct nl_msg *msg, void *arg) |  | ||||||
| { |  | ||||||
| 	struct nlattr *attrs[BATADV_ATTR_MAX+1]; |  | ||||||
| 	struct nlmsghdr *nlh = nlmsg_hdr(msg); |  | ||||||
| 	struct batadv_nlquery_opts *query_opts = arg; |  | ||||||
| 	struct genlmsghdr *ghdr; |  | ||||||
| 	struct clients_netlink_opts *opts; |  | ||||||
| 	uint32_t flags, lastseen; |  | ||||||
| 
 |  | ||||||
| 	opts = batadv_container_of(query_opts, struct clients_netlink_opts, |  | ||||||
| 				   query_opts); |  | ||||||
| 
 |  | ||||||
| 	if (!genlmsg_valid_hdr(nlh, 0)) |  | ||||||
| 		return NL_OK; |  | ||||||
| 
 |  | ||||||
| 	ghdr = nlmsg_data(nlh); |  | ||||||
| 
 |  | ||||||
| 	if (ghdr->cmd != BATADV_CMD_GET_TRANSTABLE_LOCAL) |  | ||||||
| 		return NL_OK; |  | ||||||
| 
 |  | ||||||
| 	if (nla_parse(attrs, BATADV_ATTR_MAX, genlmsg_attrdata(ghdr, 0), |  | ||||||
| 		      genlmsg_len(ghdr), batadv_genl_policy)) |  | ||||||
| 		return NL_OK; |  | ||||||
| 
 |  | ||||||
| 	if (batadv_genl_missing_attrs(attrs, clients_mandatory, |  | ||||||
| 				      BATADV_ARRAY_SIZE(clients_mandatory))) |  | ||||||
| 		return NL_OK; |  | ||||||
| 
 |  | ||||||
| 	flags = nla_get_u32(attrs[BATADV_ATTR_TT_FLAGS]); |  | ||||||
| 
 |  | ||||||
| 	if (flags & (BATADV_TT_CLIENT_NOPURGE)) |  | ||||||
| 		return NL_OK; |  | ||||||
| 
 |  | ||||||
| 	lastseen = nla_get_u32(attrs[BATADV_ATTR_LAST_SEEN_MSECS]); |  | ||||||
| 	if (lastseen > MAX_INACTIVITY) |  | ||||||
| 		return NL_OK; |  | ||||||
| 
 |  | ||||||
| 	opts->clients++; |  | ||||||
| 
 |  | ||||||
| 	return NL_OK; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| static struct json_object * get_clients(void) { |  | ||||||
| 	struct clients_netlink_opts opts = { |  | ||||||
| 		.clients = 0, |  | ||||||
| 		.query_opts = { |  | ||||||
| 			.err = 0, |  | ||||||
| 		}, |  | ||||||
| 	}; |  | ||||||
| 
 |  | ||||||
| 	batadv_genl_query("bat0", BATADV_CMD_GET_TRANSTABLE_LOCAL, |  | ||||||
| 			  parse_clients_list_netlink_cb, NLM_F_DUMP, |  | ||||||
| 			  &opts.query_opts); |  | ||||||
| 
 |  | ||||||
| 	struct json_object *ret = json_object_new_object(); |  | ||||||
| 
 |  | ||||||
| 	json_object_object_add(ret, "total", json_object_new_int(opts.clients)); |  | ||||||
| 
 |  | ||||||
| 	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()); |  | ||||||
| 
 |  | ||||||
| 	add_gateway(ret); |  | ||||||
| 
 |  | ||||||
| 	return ret; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| static struct json_object * ifnames2addrs(struct json_object *interfaces) { |  | ||||||
| 	struct json_object *ret = json_object_new_object(); |  | ||||||
| 
 |  | ||||||
| 	json_object_object_foreach(interfaces, ifname, interface) { |  | ||||||
| 		char *ifaddr = gluonutil_get_interface_address(ifname); |  | ||||||
| 		if (!ifaddr) |  | ||||||
| 			continue; |  | ||||||
| 
 |  | ||||||
| 		struct json_object *obj = json_object_new_object(); |  | ||||||
| 		json_object_object_add(obj, "neighbours", json_object_get(interface)); |  | ||||||
| 		json_object_object_add(ret, ifaddr, obj); |  | ||||||
| 
 |  | ||||||
| 		free(ifaddr); |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	json_object_put(interfaces); |  | ||||||
| 
 |  | ||||||
| 	return ret; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| static const enum batadv_nl_attrs parse_orig_list_mandatory[] = { |  | ||||||
| 	BATADV_ATTR_ORIG_ADDRESS, |  | ||||||
| 	BATADV_ATTR_NEIGH_ADDRESS, |  | ||||||
| 	BATADV_ATTR_TQ, |  | ||||||
| 	BATADV_ATTR_HARD_IFINDEX, |  | ||||||
| 	BATADV_ATTR_LAST_SEEN_MSECS, |  | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| static int parse_orig_list_netlink_cb(struct nl_msg *msg, void *arg) |  | ||||||
| { |  | ||||||
| 	struct nlattr *attrs[BATADV_ATTR_MAX+1]; |  | ||||||
| 	struct nlmsghdr *nlh = nlmsg_hdr(msg); |  | ||||||
| 	struct batadv_nlquery_opts *query_opts = arg; |  | ||||||
| 	struct genlmsghdr *ghdr; |  | ||||||
| 	uint8_t *orig; |  | ||||||
| 	uint8_t *dest; |  | ||||||
| 	uint8_t tq; |  | ||||||
| 	uint32_t hardif; |  | ||||||
| 	uint32_t lastseen; |  | ||||||
| 	char ifname_buf[IF_NAMESIZE], *ifname; |  | ||||||
| 	struct neigh_netlink_opts *opts; |  | ||||||
| 	char mac1[18]; |  | ||||||
| 
 |  | ||||||
| 	opts = batadv_container_of(query_opts, struct neigh_netlink_opts, |  | ||||||
| 				   query_opts); |  | ||||||
| 
 |  | ||||||
| 	if (!genlmsg_valid_hdr(nlh, 0)) |  | ||||||
| 		return NL_OK; |  | ||||||
| 
 |  | ||||||
| 	ghdr = nlmsg_data(nlh); |  | ||||||
| 
 |  | ||||||
| 	if (ghdr->cmd != BATADV_CMD_GET_ORIGINATORS) |  | ||||||
| 		return NL_OK; |  | ||||||
| 
 |  | ||||||
| 	if (nla_parse(attrs, BATADV_ATTR_MAX, genlmsg_attrdata(ghdr, 0), |  | ||||||
| 		      genlmsg_len(ghdr), batadv_genl_policy)) |  | ||||||
| 		return NL_OK; |  | ||||||
| 
 |  | ||||||
| 	if (batadv_genl_missing_attrs(attrs, parse_orig_list_mandatory, |  | ||||||
| 				      BATADV_ARRAY_SIZE(parse_orig_list_mandatory))) |  | ||||||
| 		return NL_OK; |  | ||||||
| 
 |  | ||||||
| 	orig = nla_data(attrs[BATADV_ATTR_ORIG_ADDRESS]); |  | ||||||
| 	dest = nla_data(attrs[BATADV_ATTR_NEIGH_ADDRESS]); |  | ||||||
| 	tq = nla_get_u8(attrs[BATADV_ATTR_TQ]); |  | ||||||
| 	hardif = nla_get_u32(attrs[BATADV_ATTR_HARD_IFINDEX]); |  | ||||||
| 	lastseen = nla_get_u32(attrs[BATADV_ATTR_LAST_SEEN_MSECS]); |  | ||||||
| 
 |  | ||||||
| 	if (memcmp(orig, dest, 6) != 0) |  | ||||||
| 		return NL_OK; |  | ||||||
| 
 |  | ||||||
| 	ifname = if_indextoname(hardif, ifname_buf); |  | ||||||
| 	if (!ifname) |  | ||||||
| 		return NL_OK; |  | ||||||
| 
 |  | ||||||
| 	sprintf(mac1, "%02x:%02x:%02x:%02x:%02x:%02x", |  | ||||||
| 		orig[0], orig[1], orig[2], orig[3], orig[4], orig[5]); |  | ||||||
| 
 |  | ||||||
| 	struct json_object *obj = json_object_new_object(); |  | ||||||
| 	if (!obj) |  | ||||||
| 		return NL_OK; |  | ||||||
| 
 |  | ||||||
| 	struct json_object *interface; |  | ||||||
| 	if (!json_object_object_get_ex(opts->interfaces, ifname, &interface)) { |  | ||||||
| 		interface = json_object_new_object(); |  | ||||||
| 		json_object_object_add(opts->interfaces, ifname, interface); |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	json_object_object_add(obj, "tq", json_object_new_int(tq)); |  | ||||||
| 	json_object_object_add(obj, "lastseen", json_object_new_double(lastseen / 1000.)); |  | ||||||
| 	json_object_object_add(obj, "best", json_object_new_boolean(!!attrs[BATADV_ATTR_FLAG_BEST])); |  | ||||||
| 	json_object_object_add(interface, mac1, obj); |  | ||||||
| 
 |  | ||||||
| 	return NL_OK; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| static struct json_object * get_batadv(void) { |  | ||||||
| 	struct neigh_netlink_opts opts = { |  | ||||||
| 		.query_opts = { |  | ||||||
| 			.err = 0, |  | ||||||
| 		}, |  | ||||||
| 	}; |  | ||||||
| 	int ret; |  | ||||||
| 
 |  | ||||||
| 	opts.interfaces = json_object_new_object(); |  | ||||||
| 	if (!opts.interfaces) |  | ||||||
| 		return NULL; |  | ||||||
| 
 |  | ||||||
| 	ret = batadv_genl_query("bat0", BATADV_CMD_GET_ORIGINATORS, |  | ||||||
| 				parse_orig_list_netlink_cb, NLM_F_DUMP, |  | ||||||
| 				&opts.query_opts); |  | ||||||
| 	if (ret < 0) { |  | ||||||
| 		json_object_put(opts.interfaces); |  | ||||||
| 		return NULL; |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	return ifnames2addrs(opts.interfaces); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| static struct json_object * respondd_provider_neighbours(void) { |  | ||||||
| 	struct json_object *ret = json_object_new_object(); |  | ||||||
| 
 |  | ||||||
| 	struct json_object *batadv = get_batadv(); |  | ||||||
| 	if (batadv) |  | ||||||
| 		json_object_object_add(ret, "batadv", batadv); |  | ||||||
| 
 |  | ||||||
| 	return ret; |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| 
 | 
 | ||||||
|  | __attribute__ ((visibility ("default"))) | ||||||
| const struct respondd_provider_info respondd_providers[] = { | const struct respondd_provider_info respondd_providers[] = { | ||||||
| 	{"nodeinfo", respondd_provider_nodeinfo}, | 	{"nodeinfo", respondd_provider_nodeinfo}, | ||||||
| 	{"statistics", respondd_provider_statistics}, | 	{"statistics", respondd_provider_statistics}, | ||||||
|  | |||||||
		Loading…
	
		Reference in New Issue
	
	Block a user