Send and receive announcement, rewrite a lot of code

This commit is contained in:
Matthias Schiffer 2012-08-29 22:39:12 +02:00
parent 88feb0c9f3
commit 6c79dcef39

View file

@ -26,8 +26,10 @@
#define _GNU_SOURCE #define _GNU_SOURCE
#include <alloca.h>
#include <errno.h> #include <errno.h>
#include <libgen.h> #include <libgen.h>
#include <poll.h>
#include <stdarg.h> #include <stdarg.h>
#include <stdbool.h> #include <stdbool.h>
#include <stdio.h> #include <stdio.h>
@ -47,12 +49,15 @@
#include <netinet/in.h> #include <netinet/in.h>
#include <netpacket/packet.h>
#include <sys/types.h> #include <sys/types.h>
#include <sys/stat.h> #include <sys/stat.h>
#define ANNOUNCE_INTERVAL 10000 #define ANNOUNCE_INTERVAL 10
#define ANNOUNCE_TIMEOUT 30000 #define ANNOUNCE_TIMEOUT (3*ANNOUNCE_INTERVAL)
#define ANNOUNCE_PRUNE (3*ANNOUNCE_TIMEOUT)
#define SYSFS_PATH_MAX 256 #define SYSFS_PATH_MAX 256
#define SYSFS_CLASS_NET "/sys/class/net" #define SYSFS_CLASS_NET "/sys/class/net"
@ -60,31 +65,29 @@
#define FFD_MAGIC 0xffd #define FFD_MAGIC 0xffd
#define FFVISD_VERSION 0 #define FFVISD_VERSION 0
#define FFD_PROTO 0xffd
#define FFD_PORT 0xffd #define FFD_PORT 0xffd
static const struct in6_addr ffd_addr = { .s6_addr = {0xff, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, typedef struct __attribute__((__packed__)) _eth_addr_t {
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; } eth_addr_t;
typedef struct __attribute__((__packed__)) _ffvisd_ipv6_host_address_t { typedef struct __attribute__((__packed__)) _ffvisd_ipv6_host_addr_t {
uint8_t address[8]; uint8_t address[8];
} ffvisd_ipv6_host_address_t; } ffvisd_ipv6_host_addr_t;
typedef struct __attribute__((__packed__)) _ffvisd_packet_server_announce_t { typedef struct __attribute__((__packed__)) _ffvisd_packet_server_announce_t {
uint32_t seqno; uint16_t seqno;
ffvisd_eth_address_t eth_address; eth_addr_t eth_address;
ffvisd_ipv6_host_address_t address; ffvisd_ipv6_host_addr_t address;
} ffvisd_packet_server_announce_t; } ffvisd_packet_server_announce_t;
typedef struct __attribute__((__packed__)) _ffvisd_packet_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; uint16_t csum;
uint16_t n_servers;
ffvisd_packet_server_announce_t servers[]; ffvisd_packet_server_announce_t servers[];
} ffvisd_packet_announce_t; } ffvisd_packet_announce_t;
@ -98,20 +101,6 @@ typedef enum _ffvisd_iftype_t {
IF_MAX IF_MAX
} ffvisd_iftype_t; } ffvisd_iftype_t;
typedef struct _ffvisd_iface_t {
unsigned ifindex;
ffvisd_iftype_t iftype;
char *ifname;
char *bridge_iface;
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 { typedef struct _ffvisd_announce_t {
struct _ffvisd_announce_t *next; struct _ffvisd_announce_t *next;
@ -119,17 +108,35 @@ typedef struct _ffvisd_announce_t {
ffvisd_packet_server_announce_t announce; ffvisd_packet_server_announce_t announce;
} ffvisd_announce_t; } 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",
[IF_WIRELESS] = "wireless", [IF_WIRELESS] = "wireless",
[IF_VIRTUAL] = "virtual", [IF_VIRTUAL] = "virtual",
[IF_BRIDGE] = "bridge", [IF_BRIDGE] = "bridge",
[IF_MESH] = "mesh", [IF_MESH] = "mesh",
}; };*/
typedef void (*iface_cb)(const ffvisd_iface_t *iface, void *arg); typedef void (*iface_cb)(const char *ifname, unsigned ifindex, void *arg);
static const eth_addr_t eth_addr_unspec = {{0x00, 0x00, 0x00, 0x00, 0x00, 0x00}};
static const eth_addr_t ffd_addr = {{0x03, 0x00, 0x00, 0x00, 0x0f, 0xfd}};
static inline bool is_eth_addr_unspec(const eth_addr_t *address) {
const uint8_t *a = address->address;
if (a[0]||a[1]||a[2]||a[3]||a[4]||a[5])
return false;
else
return true;
}
#define ETH_ADDR_IS_UNSPEC(a) (!((a).address[0]||(a).address[0]||(a).address[0]||(a).address[0]||(a).address[0]||(a).address[0]))
static int sockfd; static int sockfd;
@ -139,10 +146,6 @@ static char *mesh = "bat0";
ffvisd_announce_t *announcements = NULL; ffvisd_announce_t *announcements = NULL;
static void ffvisd_iface_free(ffvisd_iface_t *iface);
/* returns (tp1 - tp2) in milliseconds */ /* returns (tp1 - tp2) in milliseconds */
static inline int timespec_diff(const struct timespec *tp1, const struct timespec *tp2) { 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; return ((tp1->tv_sec - tp2->tv_sec))*1000 + (tp1->tv_nsec - tp2->tv_nsec)/1e6;
@ -187,6 +190,17 @@ static bool iface_is_mesh(const char *ifname) {
return iface_file_exists(ifname, "mesh"); return iface_file_exists(ifname, "mesh");
} }
static char* iface_get_mesh(const char *ifname) {
char *mesh = NULL;
if (!iface_file_read(ifname, "batman_adv/mesh_iface", "%as", &mesh) || !strcmp(mesh, "none")) {
free(mesh);
return NULL;
}
return mesh;
}
static char* iface_get_bridge(const char *ifname) { 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};
@ -197,7 +211,17 @@ static char* iface_get_bridge(const char *ifname) {
return strdup(basename(filename2)); return strdup(basename(filename2));
} }
static struct in6_addr* iface_get_address(ffvisd_iface_t *iface, int scope) { static eth_addr_t iface_get_eth_address(const char *ifname) {
eth_addr_t ret;
uint8_t *a = ret.address;
if (!iface_file_read(ifname, "address", "%x:%x:%x:%x:%x:%x", &a[0], &a[1], &a[2], &a[3], &a[4], &a[5]))
return eth_addr_unspec;
return ret;
}
static struct in6_addr iface_get_address(unsigned ifindex, int scope) {
struct msg { struct msg {
struct nlmsghdr nh; struct nlmsghdr nh;
struct ifaddrmsg addr; struct ifaddrmsg addr;
@ -217,7 +241,7 @@ static struct in6_addr* iface_get_address(ffvisd_iface_t *iface, int scope) {
if(write(rtnetlink_sk, &msg, msg.nh.nlmsg_len) <= 0) { if(write(rtnetlink_sk, &msg, msg.nh.nlmsg_len) <= 0) {
fprintf(stderr, "error: write: %m\n"); fprintf(stderr, "error: write: %m\n");
close(rtnetlink_sk); close(rtnetlink_sk);
return NULL; return in6addr_any;
} }
int readlen = read(rtnetlink_sk, &msg, sizeof(msg)); int readlen = read(rtnetlink_sk, &msg, sizeof(msg));
@ -225,7 +249,7 @@ static struct in6_addr* iface_get_address(ffvisd_iface_t *iface, int scope) {
if (readlen <= 0) { if (readlen <= 0) {
fprintf(stderr, "error: read: %m\n"); fprintf(stderr, "error: read: %m\n");
close(rtnetlink_sk); close(rtnetlink_sk);
return NULL; return in6addr_any;
} }
close(rtnetlink_sk); close(rtnetlink_sk);
@ -236,19 +260,19 @@ static struct in6_addr* iface_get_address(ffvisd_iface_t *iface, int scope) {
int len = chunk->nh.nlmsg_len - sizeof(struct nlmsghdr); int len = chunk->nh.nlmsg_len - sizeof(struct nlmsghdr);
if (len < sizeof(struct ifaddrmsg) || readlen < len) if (len < sizeof(struct ifaddrmsg) || readlen < len)
return NULL; return in6addr_any;
if (!NLMSG_OK(&chunk->nh, readlen)) if (!NLMSG_OK(&chunk->nh, readlen))
return NULL; return in6addr_any;
if (chunk->nh.nlmsg_type == RTM_NEWADDR && chunk->addr.ifa_scope == scope && chunk->addr.ifa_index == iface->ifindex) { if (chunk->nh.nlmsg_type == RTM_NEWADDR && chunk->addr.ifa_scope == scope && chunk->addr.ifa_index == ifindex) {
struct rtattr *rta = (struct rtattr *)IFA_RTA(&chunk->addr); struct rtattr *rta = (struct rtattr *)IFA_RTA(&chunk->addr);
int rtattrlen = IFA_PAYLOAD(&chunk->nh); int rtattrlen = IFA_PAYLOAD(&chunk->nh);
for (; RTA_OK(rta, rtattrlen); rta = RTA_NEXT(rta, rtattrlen)) { for (; RTA_OK(rta, rtattrlen); rta = RTA_NEXT(rta, rtattrlen)) {
if(rta->rta_type == IFA_ADDRESS && rta->rta_len == RTA_LENGTH(16)) { if(rta->rta_type == IFA_ADDRESS && rta->rta_len == RTA_LENGTH(16)) {
struct in6_addr *ret = malloc(sizeof(struct in6_addr)); struct in6_addr ret;
memcpy(ret->s6_addr, RTA_DATA(rta), 16); memcpy(ret.s6_addr, RTA_DATA(rta), 16);
return ret; return ret;
} }
} }
@ -258,49 +282,7 @@ static struct in6_addr* iface_get_address(ffvisd_iface_t *iface, int scope) {
chunk = (struct msg*)((uint8_t*)chunk + NLMSG_ALIGN(chunk->nh.nlmsg_len)); chunk = (struct msg*)((uint8_t*)chunk + NLMSG_ALIGN(chunk->nh.nlmsg_len));
} }
return NULL; return in6addr_any;
}
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);
iface->ifindex = ifindex;
if (!iface_file_read(ifname, "batman_adv/mesh_iface", "%as", &iface->mesh_iface) || !strcmp(iface->mesh_iface, "none")) {
free(iface->mesh_iface);
iface->mesh_iface = NULL;
}
iface->bridge_iface = iface_get_bridge(ifname);
int type;
if (iface_file_read(ifname, "type", "%i", &type) && type == ARPHRD_ETHER) {
if(iface_file_exists(ifname, "mesh"))
iface->iftype = IF_MESH;
else if(iface_file_exists(ifname, "bridge"))
iface->iftype = IF_BRIDGE;
else if(iface_file_exists(ifname, "tun_flags"))
iface->iftype = IF_VIRTUAL;
else if(iface_file_exists(ifname, "wireless"))
iface->iftype = IF_WIRELESS;
else
iface->iftype = IF_WIRED;
}
return iface;
}
static void ffvisd_iface_free(ffvisd_iface_t *iface) {
if (iface) {
free(iface->ifname);
free(iface->bridge_iface);
free(iface->mesh_iface);
free(iface);
}
} }
static void ffvisd_iface_foreach(iface_cb cb, void *arg) { static void ffvisd_iface_foreach(iface_cb cb, void *arg) {
@ -312,33 +294,37 @@ static void ffvisd_iface_foreach(iface_cb cb, void *arg) {
} }
int i; int i;
for (i = 0; ifaces[i].if_name; i++) { for (i = 0; ifaces[i].if_name; i++)
ffvisd_iface_t *iface = ffvisd_iface_new(ifaces[i].if_name, ifaces[i].if_index); cb(ifaces[i].if_name, ifaces[i].if_index, arg);
if (iface)
cb(iface, arg);
ffvisd_iface_free(iface);
}
if_freenameindex(ifaces); if_freenameindex(ifaces);
} }
static void pr_iface_info(const ffvisd_iface_t *iface, void *arg) { static void join_mcast(const char *ifname, unsigned ifindex, 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]); char *if_mesh = iface_get_mesh(ifname);
if (if_mesh && !strcmp(if_mesh, mesh)) {
struct packet_mreq mr;
memset(&mr, 0, sizeof(mr));
mr.mr_ifindex = ifindex;
mr.mr_type = PACKET_MR_MULTICAST;
mr.mr_alen = ETH_ALEN;
memcpy(mr.mr_address, ffd_addr.address, ETH_ALEN);
if (setsockopt(sockfd, SOL_PACKET, PACKET_ADD_MEMBERSHIP, &mr, sizeof(mr)) && errno != EADDRINUSE)
fprintf(stderr, "warning: setsockopt: %m\n");
} }
free(if_mesh);
}
static void join_mcast(const ffvisd_iface_t *iface, void *arg) { static void pr_announcement_packet(const ffvisd_packet_server_announce_t *announce) {
if (!iface->mesh_iface || strcmp(iface->mesh_iface, mesh)) const uint8_t *e = announce->eth_address.address, *a = announce->address.address;
return;
struct ipv6_mreq imr; printf("seqno: %u, ether: %02x:%02x:%02x:%02x:%02x:%02x, address: fe80::%02x%02x:%02x%02x:%02x%02x:%02x%02x\n",
imr.ipv6mr_multiaddr = ffd_addr; announce->seqno,
imr.ipv6mr_interface = iface->ifindex; e[0], e[1], e[2], e[3], e[4], e[5],
if (setsockopt(sockfd, IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP, &imr, sizeof(struct ipv6_mreq)) && errno != EADDRINUSE) a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7]);
fprintf(stderr, "warning: setsockopt: %m\n");
} }
static void pr_announcement(const ffvisd_announce_t *announce) { static void pr_announcement(const ffvisd_announce_t *announce) {
@ -351,12 +337,7 @@ static void pr_announcement(const ffvisd_announce_t *announce) {
printf("age: %.1fs, ", timespec_diff(&tv, &announce->received)/1000.0); printf("age: %.1fs, ", timespec_diff(&tv, &announce->received)/1000.0);
} }
const uint8_t *e = announce->announce.eth_address.address, *a = announce->announce.address.address; pr_announcement_packet(&announce->announce);
printf("seqno: %u, ether: %02x:%02x:%02x:%02x:%02x:%02x, address: fe80::%02x%02x:%02x%02x:%02x%02x:%02x%02x\n",
announce->announce.seqno,
e[0], e[1], e[2], e[3], e[4], e[5],
a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7]);
} }
static bool check_config() { static bool check_config() {
@ -369,69 +350,45 @@ static bool check_config() {
} }
static bool init_socket() { static bool init_socket() {
sockfd = socket(AF_INET6, SOCK_DGRAM, 0); sockfd = socket(AF_PACKET, SOCK_DGRAM, htons(FFD_PROTO));
if (sockfd < 0) { if (sockfd < 0) {
fprintf(stderr, "error: socket: %m\n"); fprintf(stderr, "error: socket: %m\n");
return false; 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; return true;
} }
static bool init_announcements() { static bool init_announcements() {
ffvisd_iface_t *iface;
char *ifname = iface_get_bridge(mesh); char *ifname = iface_get_bridge(mesh);
if (ifname) { if (!ifname)
iface = ffvisd_iface_new(ifname, if_nametoindex(ifname)); ifname = strdup(mesh);
free(ifname);
}
else {
iface = ffvisd_iface_new(mesh, if_nametoindex(mesh));
}
if (!iface) unsigned ifindex = if_nametoindex(ifname);
if (!ifindex) {
free(ifname);
return false; return false;
}
ffvisd_announce_t *announce = announcements = calloc(1, sizeof(ffvisd_announce_t)); ffvisd_announce_t *announce = announcements = calloc(1, sizeof(ffvisd_announce_t));
uint8_t *a = announce->announce.eth_address.address; announce->announce.eth_address = iface_get_eth_address(ifname);
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])) if (is_eth_addr_unspec(&announce->announce.eth_address)) {
return false; free(ifname);
struct in6_addr *addr = iface_get_address(iface, RT_SCOPE_LINK);
if (!addr) {
ffvisd_iface_free(iface);
return false; return false;
} }
memcpy(announce->announce.address.address, addr->s6_addr+8, 8); struct in6_addr addr = iface_get_address(ifindex, RT_SCOPE_LINK);
free(addr); if (IN6_IS_ADDR_UNSPECIFIED(&addr)) {
free(ifname);
return false;
}
ffvisd_iface_free(iface); memcpy(announce->announce.address.address, addr.s6_addr+8, 8);
free(ifname);
return true; return true;
} }
@ -445,19 +402,81 @@ static void maintenance() {
} }
} }
static void send_announcement(const char *ifname, unsigned ifindex, void *arg) {
ffvisd_packet_announce_t *packet = arg;
struct sockaddr_ll sa;
memset(&sa, 0, sizeof(sa));
sa.sll_family = AF_PACKET;
sa.sll_protocol = htons(FFD_PROTO);
sa.sll_ifindex = ifindex;
sa.sll_halen = ETH_ALEN;
memcpy(sa.sll_addr, ffd_addr.address, ETH_ALEN);
sendto(sockfd, packet, sizeof(ffvisd_packet_announce_t) + ntohs(packet->n_servers)*sizeof(ffvisd_packet_server_announce_t), 0, (struct sockaddr*)&sa, sizeof(sa));
}
static void send_announcements() {
ffvisd_packet_announce_t *packet = alloca(sizeof(ffvisd_packet_announce_t) + 75*sizeof(ffvisd_packet_server_announce_t));
packet->magic = htons(FFD_MAGIC);
packet->version = htons(FFVISD_VERSION);
packet->csum = 0;
uint16_t n_servers = 0;
ffvisd_announce_t *announce;
for(announce = announcements; announce; announce = announce->next) {
packet->servers[n_servers++] = announce->announce;
}
packet->n_servers = htons(n_servers);
ffvisd_iface_foreach(send_announcement, packet);
}
static void receive_announcement() {
ffvisd_packet_announce_t *packet = alloca(sizeof(ffvisd_packet_announce_t) + 75*sizeof(ffvisd_packet_server_announce_t));
int readlen = read(sockfd, packet, sizeof(ffvisd_packet_announce_t) + 75*sizeof(ffvisd_packet_server_announce_t));
if (readlen < 0) {
fprintf(stderr, "error: read: %m\n");
return;
}
if (readlen < sizeof(ffvisd_packet_announce_t)) {
fprintf(stderr, "warning: short read");
return;
}
int n_servers = ntohs(packet->n_servers);
if (readlen < sizeof(ffvisd_packet_announce_t) + n_servers*sizeof(ffvisd_packet_server_announce_t)) {
fprintf(stderr, "warning: short read");
return;
}
puts("Received announcements:");
int i;
for(i = 0; i < n_servers; i++)
pr_announcement_packet(&packet->servers[i]);
puts("");
}
int main() { int main() {
if (!check_config()) if (!check_config())
return 1; return 1;
ffvisd_iface_foreach(pr_iface_info, NULL);
puts("");
if (!init_socket()) if (!init_socket())
return 1; return 1;
if (!init_announcements()) if (!init_announcements())
return 1; return 1;
struct timespec next_announce;
clock_gettime(CLOCK_MONOTONIC, &next_announce);
while (true) { while (true) {
ffvisd_iface_foreach(join_mcast, NULL); ffvisd_iface_foreach(join_mcast, NULL);
@ -469,7 +488,28 @@ int main() {
pr_announcement(announce); pr_announcement(announce);
puts(""); puts("");
usleep(ANNOUNCE_INTERVAL*1000); send_announcements();
next_announce.tv_sec += ANNOUNCE_INTERVAL;
while (true) {
struct timespec now;
clock_gettime(CLOCK_MONOTONIC, &now);
int timeout = timespec_diff(&next_announce, &now);
if (timeout <= 0)
break;
struct pollfd fds[1];
fds[0].fd = sockfd;
fds[0].events = POLLIN;
poll(fds, 1, timeout);
if (fds[0].revents & POLLIN)
receive_announcement();
}
} }
return 0; return 0;