220 lines
7.0 KiB
C
220 lines
7.0 KiB
C
|
/*
|
||
|
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;
|
||
|
}
|