Initialize more stuff
This commit is contained in:
parent
46184653cf
commit
09eb0b8179
1 changed files with 251 additions and 25 deletions
276
ffvisd/ffvisd.c
276
ffvisd/ffvisd.c
|
@ -26,6 +26,7 @@
|
||||||
|
|
||||||
#define _GNU_SOURCE
|
#define _GNU_SOURCE
|
||||||
|
|
||||||
|
#include <errno.h>
|
||||||
#include <libgen.h>
|
#include <libgen.h>
|
||||||
#include <stdarg.h>
|
#include <stdarg.h>
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
|
@ -33,18 +34,25 @@
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
#include <time.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#include <arpa/inet.h>
|
||||||
|
|
||||||
|
#include <linux/rtnetlink.h>
|
||||||
|
|
||||||
#include <net/ethernet.h>
|
#include <net/ethernet.h>
|
||||||
#include <net/if.h>
|
#include <net/if.h>
|
||||||
#include <net/if_arp.h>
|
#include <net/if_arp.h>
|
||||||
|
|
||||||
|
#include <netinet/in.h>
|
||||||
|
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
|
|
||||||
|
|
||||||
#define ANNOUNCE_INTERVAL 20
|
#define ANNOUNCE_INTERVAL 10000
|
||||||
#define ANNOUNCE_TIMEOUT 60
|
#define ANNOUNCE_TIMEOUT 30000
|
||||||
|
|
||||||
#define SYSFS_PATH_MAX 256
|
#define SYSFS_PATH_MAX 256
|
||||||
#define SYSFS_CLASS_NET "/sys/class/net"
|
#define SYSFS_CLASS_NET "/sys/class/net"
|
||||||
|
@ -52,32 +60,33 @@
|
||||||
#define FFD_MAGIC 0xffd
|
#define FFD_MAGIC 0xffd
|
||||||
#define FFVISD_VERSION 0
|
#define FFVISD_VERSION 0
|
||||||
|
|
||||||
|
#define FFD_PORT 0xffd
|
||||||
//static char *mesh = "bat0";
|
|
||||||
|
|
||||||
|
|
||||||
typedef struct _ffvisd_eth_address_t {
|
static const struct in6_addr ffd_addr = { .s6_addr = {0xff, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0xfd} };
|
||||||
|
|
||||||
|
|
||||||
|
typedef struct __attribute__((__packed__)) _ffvisd_eth_address_t {
|
||||||
uint8_t address[ETH_ALEN];
|
uint8_t address[ETH_ALEN];
|
||||||
} ffvisd_eth_address_t;
|
} ffvisd_eth_address_t;
|
||||||
|
|
||||||
typedef struct _ffvisd_ipv6_host_address_t {
|
typedef struct __attribute__((__packed__)) _ffvisd_ipv6_host_address_t {
|
||||||
uint8_t address[8];
|
uint8_t address[8];
|
||||||
} ffvisd_ipv6_host_address_t;
|
} ffvisd_ipv6_host_address_t;
|
||||||
|
|
||||||
typedef struct _ffvisd_server_announce_t {
|
typedef struct __attribute__((__packed__)) _ffvisd_packet_server_announce_t {
|
||||||
|
uint32_t seqno;
|
||||||
ffvisd_eth_address_t eth_address;
|
ffvisd_eth_address_t eth_address;
|
||||||
uint16_t reserved;
|
|
||||||
ffvisd_ipv6_host_address_t address;
|
ffvisd_ipv6_host_address_t address;
|
||||||
uint32_t age;
|
} ffvisd_packet_server_announce_t;
|
||||||
} ffvisd_server_announce_t;
|
|
||||||
|
|
||||||
typedef struct _ffvisd_announce_t {
|
typedef struct __attribute__((__packed__)) _ffvisd_packet_announce_t {
|
||||||
uint16_t magic;
|
uint16_t magic;
|
||||||
uint16_t version;
|
uint16_t version;
|
||||||
uint32_t n_servers;
|
uint32_t n_servers;
|
||||||
uint32_t reserved;
|
ffvisd_packet_server_announce_t servers[];
|
||||||
ffvisd_server_announce_t servers[];
|
} ffvisd_packet_announce_t;
|
||||||
} ffvisd_announce_t;
|
|
||||||
|
|
||||||
typedef enum _ffvisd_iftype_t {
|
typedef enum _ffvisd_iftype_t {
|
||||||
IF_UNKNOWN = 0,
|
IF_UNKNOWN = 0,
|
||||||
|
@ -97,6 +106,19 @@ typedef struct _ffvisd_iface_t {
|
||||||
char *mesh_iface;
|
char *mesh_iface;
|
||||||
} ffvisd_iface_t;
|
} ffvisd_iface_t;
|
||||||
|
|
||||||
|
typedef struct _ffvisd_orig_t {
|
||||||
|
struct _ffvisd_orig_t *next;
|
||||||
|
|
||||||
|
ffvisd_eth_address_t eth_address;
|
||||||
|
} ffvisd_orig_t;
|
||||||
|
|
||||||
|
typedef struct _ffvisd_announce_t {
|
||||||
|
struct _ffvisd_announce_t *next;
|
||||||
|
|
||||||
|
struct timeval received;
|
||||||
|
ffvisd_packet_server_announce_t announce;
|
||||||
|
} ffvisd_announce_t;
|
||||||
|
|
||||||
static const char *ffvisd_iftype_names[IF_MAX] = {
|
static const char *ffvisd_iftype_names[IF_MAX] = {
|
||||||
[IF_UNKNOWN] = "unknown",
|
[IF_UNKNOWN] = "unknown",
|
||||||
[IF_WIRED] = "wired",
|
[IF_WIRED] = "wired",
|
||||||
|
@ -106,13 +128,28 @@ static const char *ffvisd_iftype_names[IF_MAX] = {
|
||||||
[IF_MESH] = "mesh",
|
[IF_MESH] = "mesh",
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
typedef void (*iface_cb)(const ffvisd_iface_t *iface, void *arg);
|
typedef void (*iface_cb)(const ffvisd_iface_t *iface, void *arg);
|
||||||
|
|
||||||
|
|
||||||
void ffvisd_iface_free(ffvisd_iface_t *iface);
|
static int sockfd;
|
||||||
|
static char *mesh = "bat0";
|
||||||
|
|
||||||
|
|
||||||
bool file_readv(const char *file, const char *format, va_list ap) {
|
ffvisd_announce_t *announcements = NULL;
|
||||||
|
|
||||||
|
|
||||||
|
static void ffvisd_iface_free(ffvisd_iface_t *iface);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/* returns (tp1 - tp2) in milliseconds */
|
||||||
|
static inline int timespec_diff(const struct timespec *tp1, const struct timespec *tp2) {
|
||||||
|
return ((tp1->tv_sec - tp2->tv_sec))*1000 + (tp1->tv_nsec - tp2->tv_nsec)/1e6;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static bool file_readv(const char *file, const char *format, va_list ap) {
|
||||||
FILE *f = fopen(file, "r");
|
FILE *f = fopen(file, "r");
|
||||||
if (!f)
|
if (!f)
|
||||||
return false;
|
return false;
|
||||||
|
@ -124,7 +161,7 @@ bool file_readv(const char *file, const char *format, va_list ap) {
|
||||||
return (ret > 0);
|
return (ret > 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool iface_file_read(const char *ifname, const char *file, const char *format, ...) {
|
static bool iface_file_read(const char *ifname, const char *file, const char *format, ...) {
|
||||||
char filename[SYSFS_PATH_MAX];
|
char filename[SYSFS_PATH_MAX];
|
||||||
|
|
||||||
snprintf(filename, SYSFS_PATH_MAX, SYSFS_CLASS_NET"/%s/%s", ifname, file);
|
snprintf(filename, SYSFS_PATH_MAX, SYSFS_CLASS_NET"/%s/%s", ifname, file);
|
||||||
|
@ -137,7 +174,7 @@ bool iface_file_read(const char *ifname, const char *file, const char *format, .
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool iface_file_exists(const char *ifname, const char *file) {
|
static bool iface_file_exists(const char *ifname, const char *file) {
|
||||||
char filename[SYSFS_PATH_MAX];
|
char filename[SYSFS_PATH_MAX];
|
||||||
|
|
||||||
snprintf(filename, SYSFS_PATH_MAX, SYSFS_CLASS_NET"/%s/%s", ifname, file);
|
snprintf(filename, SYSFS_PATH_MAX, SYSFS_CLASS_NET"/%s/%s", ifname, file);
|
||||||
|
@ -146,7 +183,11 @@ bool iface_file_exists(const char *ifname, const char *file) {
|
||||||
return (stat(filename, &st) == 0);
|
return (stat(filename, &st) == 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
char* get_bridge(const char *ifname) {
|
static bool iface_is_mesh(const char *ifname) {
|
||||||
|
return iface_file_exists(ifname, "mesh");
|
||||||
|
}
|
||||||
|
|
||||||
|
static char* iface_get_bridge(const char *ifname) {
|
||||||
char filename[SYSFS_PATH_MAX], filename2[SYSFS_PATH_MAX] = {0};
|
char filename[SYSFS_PATH_MAX], filename2[SYSFS_PATH_MAX] = {0};
|
||||||
|
|
||||||
snprintf(filename, SYSFS_PATH_MAX, SYSFS_CLASS_NET"/%s/brport/bridge", ifname);
|
snprintf(filename, SYSFS_PATH_MAX, SYSFS_CLASS_NET"/%s/brport/bridge", ifname);
|
||||||
|
@ -156,7 +197,74 @@ char* get_bridge(const char *ifname) {
|
||||||
return strdup(basename(filename2));
|
return strdup(basename(filename2));
|
||||||
}
|
}
|
||||||
|
|
||||||
ffvisd_iface_t* ffvisd_iface_new(const char *ifname, unsigned ifindex) {
|
static struct in6_addr* iface_get_address(ffvisd_iface_t *iface, int scope) {
|
||||||
|
struct msg {
|
||||||
|
struct nlmsghdr nh;
|
||||||
|
struct ifaddrmsg addr;
|
||||||
|
uint8_t attrbuf[16384];
|
||||||
|
} msg;
|
||||||
|
|
||||||
|
memset(&msg, 0, sizeof(msg));
|
||||||
|
|
||||||
|
int rtnetlink_sk = socket(AF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE);
|
||||||
|
|
||||||
|
msg.nh.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifaddrmsg));
|
||||||
|
msg.nh.nlmsg_flags = NLM_F_REQUEST | NLM_F_ROOT;
|
||||||
|
msg.nh.nlmsg_type = RTM_GETADDR;
|
||||||
|
|
||||||
|
msg.addr.ifa_family = AF_INET6;
|
||||||
|
|
||||||
|
if(write(rtnetlink_sk, &msg, msg.nh.nlmsg_len) <= 0) {
|
||||||
|
fprintf(stderr, "error: write: %m\n");
|
||||||
|
close(rtnetlink_sk);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
int readlen = read(rtnetlink_sk, &msg, sizeof(msg));
|
||||||
|
|
||||||
|
if (readlen <= 0) {
|
||||||
|
fprintf(stderr, "error: read: %m\n");
|
||||||
|
close(rtnetlink_sk);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
close(rtnetlink_sk);
|
||||||
|
|
||||||
|
struct msg *chunk;
|
||||||
|
|
||||||
|
for (chunk = &msg; readlen > sizeof(sizeof(struct nlmsghdr));) {
|
||||||
|
int len = chunk->nh.nlmsg_len - sizeof(struct nlmsghdr);
|
||||||
|
|
||||||
|
if (len < sizeof(struct ifaddrmsg) || readlen < len)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
if (!NLMSG_OK(&chunk->nh, readlen))
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
if (chunk->nh.nlmsg_type == RTM_NEWADDR && chunk->addr.ifa_scope == scope && chunk->addr.ifa_index == iface->ifindex) {
|
||||||
|
struct rtattr *rta = (struct rtattr *)IFA_RTA(&chunk->addr);
|
||||||
|
int rtattrlen = IFA_PAYLOAD(&chunk->nh);
|
||||||
|
|
||||||
|
for (; RTA_OK(rta, rtattrlen); rta = RTA_NEXT(rta, rtattrlen)) {
|
||||||
|
if(rta->rta_type == IFA_ADDRESS && rta->rta_len == RTA_LENGTH(16)) {
|
||||||
|
struct in6_addr *ret = malloc(sizeof(struct in6_addr));
|
||||||
|
memcpy(ret->s6_addr, RTA_DATA(rta), 16);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
readlen -= NLMSG_ALIGN(chunk->nh.nlmsg_len);
|
||||||
|
chunk = (struct msg*)((uint8_t*)chunk + NLMSG_ALIGN(chunk->nh.nlmsg_len));
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static ffvisd_iface_t* ffvisd_iface_new(const char *ifname, unsigned ifindex) {
|
||||||
|
if (!ifname || !ifindex)
|
||||||
|
return false;
|
||||||
|
|
||||||
ffvisd_iface_t *iface = calloc(1, sizeof(ffvisd_iface_t));
|
ffvisd_iface_t *iface = calloc(1, sizeof(ffvisd_iface_t));
|
||||||
|
|
||||||
iface->ifname = strdup(ifname);
|
iface->ifname = strdup(ifname);
|
||||||
|
@ -167,7 +275,7 @@ ffvisd_iface_t* ffvisd_iface_new(const char *ifname, unsigned ifindex) {
|
||||||
iface->mesh_iface = NULL;
|
iface->mesh_iface = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
iface->bridge_iface = get_bridge(ifname);
|
iface->bridge_iface = iface_get_bridge(ifname);
|
||||||
|
|
||||||
int type;
|
int type;
|
||||||
if (iface_file_read(ifname, "type", "%i", &type) && type == ARPHRD_ETHER) {
|
if (iface_file_read(ifname, "type", "%i", &type) && type == ARPHRD_ETHER) {
|
||||||
|
@ -186,7 +294,7 @@ ffvisd_iface_t* ffvisd_iface_new(const char *ifname, unsigned ifindex) {
|
||||||
return iface;
|
return iface;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ffvisd_iface_free(ffvisd_iface_t *iface) {
|
static void ffvisd_iface_free(ffvisd_iface_t *iface) {
|
||||||
if (iface) {
|
if (iface) {
|
||||||
free(iface->ifname);
|
free(iface->ifname);
|
||||||
free(iface->bridge_iface);
|
free(iface->bridge_iface);
|
||||||
|
@ -195,11 +303,11 @@ void ffvisd_iface_free(ffvisd_iface_t *iface) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ffvisd_iface_foreach(iface_cb cb, void *arg) {
|
static void ffvisd_iface_foreach(iface_cb cb, void *arg) {
|
||||||
struct if_nameindex *ifaces = if_nameindex();
|
struct if_nameindex *ifaces = if_nameindex();
|
||||||
|
|
||||||
if (!ifaces) {
|
if (!ifaces) {
|
||||||
fprintf(stderr, "error: if_nameindex: %m");
|
fprintf(stderr, "error: if_nameindex: %m\n");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -217,13 +325,131 @@ void ffvisd_iface_foreach(iface_cb cb, void *arg) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void pr_iface_info(const ffvisd_iface_t *iface, void *arg) {
|
static void pr_iface_info(const ffvisd_iface_t *iface, void *arg) {
|
||||||
printf("Interface %s: ifindex=%i bridge_iface=%s mesh_iface=%s iftype=%s\n", iface->ifname, iface->ifindex, iface->bridge_iface, iface->mesh_iface, ffvisd_iftype_names[iface->iftype]);
|
printf("Interface %s: ifindex=%i bridge_iface=%s mesh_iface=%s iftype=%s\n", iface->ifname, iface->ifindex, iface->bridge_iface, iface->mesh_iface, ffvisd_iftype_names[iface->iftype]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void join_mcast(const ffvisd_iface_t *iface, void *arg) {
|
||||||
|
if (!iface->mesh_iface || strcmp(iface->mesh_iface, mesh))
|
||||||
|
return;
|
||||||
|
|
||||||
|
struct ipv6_mreq imr;
|
||||||
|
imr.ipv6mr_multiaddr = ffd_addr;
|
||||||
|
imr.ipv6mr_interface = iface->ifindex;
|
||||||
|
if (setsockopt(sockfd, IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP, &imr, sizeof(struct ipv6_mreq)) && errno != EADDRINUSE)
|
||||||
|
fprintf(stderr, "warning: setsockopt: %m\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool check_config() {
|
||||||
|
if (!iface_is_mesh(mesh)) {
|
||||||
|
fprintf(stderr, "error: configured interface is no mesh\n");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool init_socket() {
|
||||||
|
sockfd = socket(AF_INET6, SOCK_DGRAM, 0);
|
||||||
|
if (sockfd < 0) {
|
||||||
|
fprintf(stderr, "error: socket: %m\n");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
int val = 1;
|
||||||
|
if (setsockopt(sockfd, IPPROTO_IPV6, IPV6_V6ONLY, &val, sizeof(val))) {
|
||||||
|
fprintf(stderr, "error: setsockopt: %m\n");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
val = 0;
|
||||||
|
if (setsockopt(sockfd, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, &val, sizeof(val))) {
|
||||||
|
fprintf(stderr, "error: setsockopt: %m\n");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct sockaddr_in6 sa6;
|
||||||
|
memset(&sa6, 0, sizeof(sa6));
|
||||||
|
sa6.sin6_family = AF_INET6;
|
||||||
|
sa6.sin6_port = htons(FFD_PORT);
|
||||||
|
|
||||||
|
if (bind(sockfd, (struct sockaddr*)&sa6, sizeof(struct sockaddr_in6))) {
|
||||||
|
fprintf(stderr, "error: bind: %m\n");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool init_announcements() {
|
||||||
|
ffvisd_iface_t *iface;
|
||||||
|
|
||||||
|
char *ifname = iface_get_bridge(mesh);
|
||||||
|
|
||||||
|
if (ifname) {
|
||||||
|
iface = ffvisd_iface_new(ifname, if_nametoindex(ifname));
|
||||||
|
free(ifname);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
iface = ffvisd_iface_new(mesh, if_nametoindex(mesh));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!iface)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
ffvisd_announce_t *announce = announcements = calloc(1, sizeof(ffvisd_announce_t));
|
||||||
|
|
||||||
|
uint8_t *a = announce->announce.eth_address.address;
|
||||||
|
if (!iface_file_read(iface->ifname, "address", "%x:%x:%x:%x:%x:%x", &a[0], &a[1], &a[2], &a[3], &a[4], &a[5]))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
struct in6_addr *addr = iface_get_address(iface, RT_SCOPE_LINK);
|
||||||
|
if (!addr) {
|
||||||
|
ffvisd_iface_free(iface);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
char buf[INET6_ADDRSTRLEN];
|
||||||
|
inet_ntop(AF_INET6, addr, buf, sizeof(buf));
|
||||||
|
puts(buf);
|
||||||
|
|
||||||
|
memcpy(announce->announce.address.address, addr->s6_addr+8, 8);
|
||||||
|
free(addr);
|
||||||
|
|
||||||
|
ffvisd_iface_free(iface);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void maintenance() {
|
||||||
|
ffvisd_announce_t **announce;
|
||||||
|
|
||||||
|
for(announce = &announcements; *announce; announce = &(*announce)->next) {
|
||||||
|
if (!(*announce)->received.tv_sec) /* announcement is local */
|
||||||
|
(*announce)->announce.seqno++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
int main() {
|
int main() {
|
||||||
|
if (!check_config())
|
||||||
|
return 1;
|
||||||
|
|
||||||
ffvisd_iface_foreach(pr_iface_info, NULL);
|
ffvisd_iface_foreach(pr_iface_info, NULL);
|
||||||
|
|
||||||
|
if (!init_socket())
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
if (!init_announcements())
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
ffvisd_iface_foreach(join_mcast, NULL);
|
||||||
|
|
||||||
|
maintenance();
|
||||||
|
|
||||||
|
usleep(ANNOUNCE_INTERVAL*1000);
|
||||||
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
Reference in a new issue