Initialize more stuff

This commit is contained in:
Matthias Schiffer 2012-08-29 02:18:07 +02:00
parent 46184653cf
commit 09eb0b8179

View file

@ -26,6 +26,7 @@
#define _GNU_SOURCE
#include <errno.h>
#include <libgen.h>
#include <stdarg.h>
#include <stdbool.h>
@ -33,18 +34,25 @@
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <linux/rtnetlink.h>
#include <net/ethernet.h>
#include <net/if.h>
#include <net/if_arp.h>
#include <netinet/in.h>
#include <sys/types.h>
#include <sys/stat.h>
#define ANNOUNCE_INTERVAL 20
#define ANNOUNCE_TIMEOUT 60
#define ANNOUNCE_INTERVAL 10000
#define ANNOUNCE_TIMEOUT 30000
#define SYSFS_PATH_MAX 256
#define SYSFS_CLASS_NET "/sys/class/net"
@ -52,32 +60,33 @@
#define FFD_MAGIC 0xffd
#define FFVISD_VERSION 0
//static char *mesh = "bat0";
#define FFD_PORT 0xffd
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];
} ffvisd_eth_address_t;
typedef struct _ffvisd_ipv6_host_address_t {
typedef struct __attribute__((__packed__)) _ffvisd_ipv6_host_address_t {
uint8_t address[8];
} 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;
uint16_t reserved;
ffvisd_ipv6_host_address_t address;
uint32_t age;
} ffvisd_server_announce_t;
} ffvisd_packet_server_announce_t;
typedef struct _ffvisd_announce_t {
typedef struct __attribute__((__packed__)) _ffvisd_packet_announce_t {
uint16_t magic;
uint16_t version;
uint32_t n_servers;
uint32_t reserved;
ffvisd_server_announce_t servers[];
} ffvisd_announce_t;
ffvisd_packet_server_announce_t servers[];
} ffvisd_packet_announce_t;
typedef enum _ffvisd_iftype_t {
IF_UNKNOWN = 0,
@ -97,6 +106,19 @@ typedef struct _ffvisd_iface_t {
char *mesh_iface;
} 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] = {
[IF_UNKNOWN] = "unknown",
[IF_WIRED] = "wired",
@ -106,13 +128,28 @@ static const char *ffvisd_iftype_names[IF_MAX] = {
[IF_MESH] = "mesh",
};
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");
if (!f)
return false;
@ -124,7 +161,7 @@ bool file_readv(const char *file, const char *format, va_list ap) {
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];
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;
}
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];
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);
}
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};
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));
}
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));
iface->ifname = strdup(ifname);
@ -167,7 +275,7 @@ ffvisd_iface_t* ffvisd_iface_new(const char *ifname, unsigned ifindex) {
iface->mesh_iface = NULL;
}
iface->bridge_iface = get_bridge(ifname);
iface->bridge_iface = iface_get_bridge(ifname);
int type;
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;
}
void ffvisd_iface_free(ffvisd_iface_t *iface) {
static void ffvisd_iface_free(ffvisd_iface_t *iface) {
if (iface) {
free(iface->ifname);
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();
if (!ifaces) {
fprintf(stderr, "error: if_nameindex: %m");
fprintf(stderr, "error: if_nameindex: %m\n");
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]);
}
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() {
if (!check_config())
return 1;
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;
}