From 67e14682a12a6b2cb32a55c2e61fee848c33aca4 Mon Sep 17 00:00:00 2001 From: Chris Fiege Date: Sun, 20 Oct 2024 10:56:03 +0200 Subject: [PATCH] Add ffbs-parker-nodeconfig-respondd This adds ffbs-parker-nodeconfig-respondd - a package of the *parker*-flavor of Gluon. Previously this package has been managed in https://gitli.stratum0.org/ffbs/ffbs-packages under the name `gluon-ffbsnext-nodeconfig-respondd`. Last commit-id: 40b02317cf00f5c7ebee15d0bef084a8e51bfa47 --- ffbs-parker-nodeconfig-respondd/Makefile | 20 ++ ffbs-parker-nodeconfig-respondd/README.md | 52 +++++ ffbs-parker-nodeconfig-respondd/src/Makefile | 6 + .../src/respondd.c | 190 ++++++++++++++++++ 4 files changed, 268 insertions(+) create mode 100644 ffbs-parker-nodeconfig-respondd/Makefile create mode 100644 ffbs-parker-nodeconfig-respondd/README.md create mode 100644 ffbs-parker-nodeconfig-respondd/src/Makefile create mode 100644 ffbs-parker-nodeconfig-respondd/src/respondd.c diff --git a/ffbs-parker-nodeconfig-respondd/Makefile b/ffbs-parker-nodeconfig-respondd/Makefile new file mode 100644 index 00000000..5ba12838 --- /dev/null +++ b/ffbs-parker-nodeconfig-respondd/Makefile @@ -0,0 +1,20 @@ +include $(TOPDIR)/rules.mk + +PKG_NAME:=ffbs-parker-nodeconfig-respondd +PKG_VERSION:=1 + +PKG_MAINTAINER:=Chris Fiege +PKG_LICENSE:=MIT + +include $(TOPDIR)/../package/gluon.mk + +define Package/ffbs-parker-nodeconfig-respondd + TITLE:=respondd module for nexthop and gateway info on parker gateway nodes + DEPENDS:=+ffbs-mesh-vpn-parker +respondd +kmod-nlmon +endef + +define Package/ffbs-parker-nodeconfig-respondd/description + Respndd module for parker-based networks. +endef + +$(eval $(call BuildPackageGluon,ffbs-parker-nodeconfig-respondd)) diff --git a/ffbs-parker-nodeconfig-respondd/README.md b/ffbs-parker-nodeconfig-respondd/README.md new file mode 100644 index 00000000..93bd13ad --- /dev/null +++ b/ffbs-parker-nodeconfig-respondd/README.md @@ -0,0 +1,52 @@ +ffbs-parker-nodeconfig-respondd +=============================== + +This is a package of [gluon-parker](https://github.com/ffbs/gluon-parker), +a Gluon fork that uses routing between the nodes +(aka. Router devices) and the infrastructure. +It is currently in use at Freifunk Braunschweig. +Other communities are interested in adopting it as well. + +This module extends `respondd`'s `statistics` object with the following +info: + +* `gateway`: IPv4 default gateway for the client net - if set. +* `gateway6`: IPv6 default gateway for the client net - if set. +* `gateway_nexthop`: The name of the currently selected gateway - if set. + +For a node, that itself has a wireguard connection this can look like this: +(Other information removed for readability.) +```shell +root@hostname:~# gluon-neighbour-info -r statistics +{ + "gateway": "10.0.0.3", + "gateway6": "2001:bf7:381::1", + "gateway_nexthop": "wg_c3", + (...) +} +``` + +For a node that does not have a WireGuard connection (and is thus using another node +as gateway) this may look like this: + +```shell +root@anotherhostname:~# gluon-neighbour-info -r statistics +{ + "gateway": "8e:fd:c1:15:46:3b", + "gateway_nexthop": "86:7b:fe:68:65:15", + "gateway6": "2c:3a:fd:1b:fe:71", + (...) +} +``` + +Usage with Meshviewer +--------------------- + +Meshviewer, our trusty Freiunk Network Map, can consume and display these fields. + +* For a meshing node we replicate the behavior of *fastd*-based networks: + Meshviewer will resolve the MAC-addresses into the corresponding names and + display those. +* For a node with uplink Meshviewer will fail to parse the MAC-addresses and + fall back to display the strings instead. + This means that we can use a stock Meshviewer without customizations. diff --git a/ffbs-parker-nodeconfig-respondd/src/Makefile b/ffbs-parker-nodeconfig-respondd/src/Makefile new file mode 100644 index 00000000..55d2895c --- /dev/null +++ b/ffbs-parker-nodeconfig-respondd/src/Makefile @@ -0,0 +1,6 @@ +all: respondd.so + +CFLAGS += -Wall -fPIC + +respondd.so: respondd.c + $(CC) $(CPPFLAGS) $(CFLAGS) $(LDFLAGS) -shared -D_GNU_SOURCE -o $@ $^ $(LDLIBS) diff --git a/ffbs-parker-nodeconfig-respondd/src/respondd.c b/ffbs-parker-nodeconfig-respondd/src/respondd.c new file mode 100644 index 00000000..8e7edbeb --- /dev/null +++ b/ffbs-parker-nodeconfig-respondd/src/respondd.c @@ -0,0 +1,190 @@ +/* + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +*/ + +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define BUFFER_SIZE 4096 + +struct gatewayinfo { + char interface[IF_NAMESIZE]; + char gateway4[INET_ADDRSTRLEN]; + char gateway6[INET6_ADDRSTRLEN]; +}; + +int getgatewayandiface(struct gatewayinfo * gwinfo, sa_family_t family) { + int received_bytes = 0, msg_len = 0, route_attribute_len = 0; + int sock = -1, msgseq = 0; + struct nlmsghdr *nlh, *nlmsg; + struct rtmsg *route_entry, *req_rtmsg; + // This struct contain route attributes (route type) + struct rtattr *route_attribute; + char gateway_address[INET6_ADDRSTRLEN]; + char msgbuf[BUFFER_SIZE], buffer[BUFFER_SIZE]; + char *ptr = buffer; + struct timeval tv; + int error = 0; + + if ((sock = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE)) < 0) { + perror("socket failed"); + error = 1; + goto stop_now; + } + + memset(msgbuf, 0, sizeof(msgbuf)); + memset(gateway_address, 0, sizeof(gateway_address)); + memset(buffer, 0, sizeof(buffer)); + + /* point the header and the msg structure pointers into the buffer */ + nlmsg = (struct nlmsghdr *)msgbuf; + + req_rtmsg = (struct rtmsg *) (msgbuf + sizeof(struct nlmsghdr)); + + /* Fill in the nlmsg header*/ + nlmsg->nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg)); + nlmsg->nlmsg_type = RTM_GETROUTE; // Get the routes from kernel routing table . + nlmsg->nlmsg_flags = NLM_F_DUMP | NLM_F_REQUEST; // The message is a request for dump. + nlmsg->nlmsg_seq = msgseq++; // Sequence of the message packet. + nlmsg->nlmsg_pid = getpid(); // PID of process sending the request. + req_rtmsg->rtm_family = family; + + /* 1 Sec Timeout to avoid stall */ + tv.tv_sec = 1; + setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, (struct timeval *)&tv, sizeof(struct timeval)); + /* send msg */ + if (send(sock, nlmsg, nlmsg->nlmsg_len, 0) < 0) { + perror("send failed"); + error = 2; + goto stop_now; + } + + /* receive response */ + do + { + received_bytes = recv(sock, ptr, sizeof(buffer) - msg_len, 0); + if (received_bytes < 0) { + perror("Error in recv"); + error = 3; + goto stop_now; + } + + nlh = (struct nlmsghdr *) ptr; + + /* Check if the header is valid */ + if((NLMSG_OK(nlmsg, received_bytes) == 0) || + (nlmsg->nlmsg_type == NLMSG_ERROR)) + { + perror("Error in received packet"); + error = 4; + goto stop_now; + } + + /* If we received all data break */ + if (nlh->nlmsg_type == NLMSG_DONE) + break; + else { + ptr += received_bytes; + msg_len += received_bytes; + } + + /* Break if its not a multi part message */ + if ((nlmsg->nlmsg_flags & NLM_F_MULTI) == 0) + break; + } + while ((nlmsg->nlmsg_seq != msgseq) || (nlmsg->nlmsg_pid != getpid())); + + /* parse response */ + for ( ; NLMSG_OK(nlh, received_bytes); nlh = NLMSG_NEXT(nlh, received_bytes)) + { + /* Get the route data */ + route_entry = (struct rtmsg *) NLMSG_DATA(nlh); + + /* We are just interested in main routing table */ + if (route_entry->rtm_table != RT_TABLE_MAIN) + continue; + + route_attribute = (struct rtattr *) RTM_RTA(route_entry); + route_attribute_len = RTM_PAYLOAD(nlh); + + /* Loop through all attributes */ + for ( ; RTA_OK(route_attribute, route_attribute_len); route_attribute = RTA_NEXT(route_attribute, route_attribute_len)) + { + switch(route_attribute->rta_type) { + case RTA_OIF: + if (family == AF_INET) { + if_indextoname(*(int *)RTA_DATA(route_attribute), gwinfo->interface); + } + break; + case RTA_GATEWAY: + inet_ntop(req_rtmsg->rtm_family, RTA_DATA(route_attribute), + gateway_address, sizeof(gateway_address)); + break; + default: + break; + } + } + + if (*gateway_address) { + if (req_rtmsg->rtm_family == AF_INET) { + strncpy(gwinfo->gateway4, gateway_address, INET_ADDRSTRLEN); + } else if (req_rtmsg->rtm_family == AF_INET6) { + strncpy(gwinfo->gateway6, gateway_address, INET6_ADDRSTRLEN); + } + break; + } + } + + stop_now: + close(sock); + + return error; +} + + +static struct json_object * respondd_parker_gateway(void) { + struct json_object *ret = json_object_new_object(); + struct gatewayinfo gwinfo = {0}; + if (getgatewayandiface(&gwinfo, AF_INET) > 0) { + return ret; + } + if (getgatewayandiface(&gwinfo, AF_INET6) > 0) { + return ret; + } + if (strncmp(gwinfo.interface, "wg_", 3) == 0) { + json_object_object_add(ret, "gateway", json_object_new_string(gwinfo.gateway4)); + json_object_object_add(ret, "gateway6", json_object_new_string(gwinfo.gateway6)); + json_object_object_add(ret, "gateway_nexthop", json_object_new_string(gwinfo.interface)); + } + return ret; +} + + +const struct respondd_provider_info respondd_providers[] = { + {"statistics", respondd_parker_gateway}, + {} +};