Repository : ssh://git@open-mesh.org/alfred
On branch : master
>---------------------------------------------------------------
commit f9e3aa0c2daafa5f24b281a491bbe1abbc1cac3a
Author: Sven Eckelmann <sven(a)open-mesh.com>
Date: Tue Jan 8 15:25:18 2013 +0100
alfred: Change transport protocol to link-local UDP/IPv6
The main purpose of alfred is to transfer additional data between different
batman-adv nodes. Therefore using a transport layer instead of the data layer
seems to be the right choice. This allows to use the functionality like
fragmentation or checksumming without implementing it again.
It makes it also possible to reduce the privileges to non-root users
capabilities to mitigate attacks against this daemon.
Signed-off-by: Sven Eckelmann <sven(a)open-mesh.com>
>---------------------------------------------------------------
f9e3aa0c2daafa5f24b281a491bbe1abbc1cac3a
alfred.h | 25 +++++++++------
batadv_query.c | 54 +++++++++++++++++++++++++++++++++
batadv_query.h | 4 +++
netsock.c | 50 +++++++++++-------------------
recv.c | 92 ++++++++++++++++++++++++++++++--------------------------
send.c | 13 ++++----
server.c | 11 ++++---
unix_sock.c | 4 +--
8 files changed, 155 insertions(+), 98 deletions(-)
diff --git a/alfred.h b/alfred.h
index 4340e66..ae3f8ba 100644
--- a/alfred.h
+++ b/alfred.h
@@ -25,6 +25,8 @@
#include <stdint.h>
#include <linux/if_ether.h>
+#include <netinet/ether.h>
+#include <netinet/in.h>
#include <time.h>
#include "hash.h"
#define __packed __attribute__ ((packed))
@@ -49,7 +51,7 @@ enum alfred_packet_type {
};
#define ALFRED_VERSION 0
-#define ETH_P_ALFRED 0x4242
+#define ALFRED_PORT 0x4242
#define ALFRED_MAX_RESERVED_TYPE 64
#define ALFRED_INTERVAL 10
#define ALFRED_REQUEST_TIMEOUT 1
@@ -76,7 +78,8 @@ struct dataset {
};
struct server {
- uint8_t address[ETH_ALEN];
+ struct ether_addr hwaddr;
+ struct in6_addr address;
time_t last_seen;
uint8_t tq;
};
@@ -93,7 +96,9 @@ enum clientmode {
};
struct globals {
- uint8_t hwaddr[ETH_ALEN];
+ struct ether_addr hwaddr;
+ struct in6_addr address;
+ uint32_t scope_id;
struct server *best_server; /* NULL if we are a server ourselves */
char *interface;
char *mesh_iface;
@@ -104,7 +109,6 @@ struct globals {
int netsock;
int unix_sock;
- int mtu;
struct hashtable_t *server_hash;
struct hashtable_t *data_hash;
@@ -113,10 +117,11 @@ struct globals {
#define debugMalloc(size, num) malloc(size)
#define debugFree(ptr, num) free(ptr)
-#define ALFRED_HEADLEN (sizeof(struct ethhdr) +\
- sizeof(struct alfred_packet))
+#define ALFRED_HEADLEN sizeof(struct alfred_packet)
-#define MAX_PAYLOAD 9000
+#define MAX_PAYLOAD ((1 << 16) - 1)
+
+extern const struct in6_addr in6addr_localmcast;
/* server.c */
int alfred_server(struct globals *globals);
@@ -127,13 +132,13 @@ int alfred_client_set_data(struct globals *globals);
/* recv.c */
int recv_alfred_packet(struct globals *globals);
/* send.c */
-int push_data(struct globals *globals, uint8_t *destination,
+int push_data(struct globals *globals, struct in6_addr *destination,
enum data_source max_source_level, int type_filter);
int announce_master(struct globals *globals);
int push_local_data(struct globals *globals);
int sync_data(struct globals *globals);
-int send_alfred_packet(struct globals *globals, uint8_t *dest, void *buf,
- int length);
+int send_alfred_packet(struct globals *globals, const struct in6_addr *dest,
+ void *buf, int length);
/* unix_sock.c */
int unix_sock_read(struct globals *globals);
int unix_sock_open_daemon(struct globals *globals, char *path);
diff --git a/batadv_query.c b/batadv_query.c
index 1390f57..db1aed5 100644
--- a/batadv_query.c
+++ b/batadv_query.c
@@ -24,11 +24,65 @@
#include <stdio.h>
#include <netinet/ether.h>
#include <stdlib.h>
+#include <errno.h>
#define DEBUG_BATIF_PATH_FMT "%s/batman_adv/%s"
#define DEBUG_TRANSTABLE_GLOBAL "transtable_global"
#define DEBUG_ORIGINATORS "originators"
+int mac_to_ipv6(const struct ether_addr *mac, struct in6_addr *addr)
+{
+ memset(addr, 0, sizeof(*addr));
+ addr->s6_addr[0] = 0xfe;
+ addr->s6_addr[1] = 0x80;
+
+ addr->s6_addr[8] = mac->ether_addr_octet[0] ^ 0x02;
+ addr->s6_addr[9] = mac->ether_addr_octet[1];
+ addr->s6_addr[10] = mac->ether_addr_octet[2];
+
+ addr->s6_addr[11] = 0xff;
+ addr->s6_addr[12] = 0xfe;
+
+ addr->s6_addr[13] = mac->ether_addr_octet[3];
+ addr->s6_addr[14] = mac->ether_addr_octet[4];
+ addr->s6_addr[15] = mac->ether_addr_octet[5];
+
+ return 0;
+}
+
+int is_ipv6_eui64(const struct in6_addr *addr)
+{
+ size_t i;
+
+ for (i = 2; i < 8; i++) {
+ if (addr->s6_addr[i] != 0x0)
+ return 0;
+ }
+
+ if (addr->s6_addr[0] != 0xfe ||
+ addr->s6_addr[1] != 0x80 ||
+ addr->s6_addr[11] != 0xff ||
+ addr->s6_addr[12] != 0xfe)
+ return 0;
+
+ return 1;
+}
+
+int ipv6_to_mac(const struct in6_addr *addr, struct ether_addr *mac)
+{
+ if (!is_ipv6_eui64(addr))
+ return -EINVAL;
+
+ mac->ether_addr_octet[0] = addr->s6_addr[8] ^ 0x02;
+ mac->ether_addr_octet[1] = addr->s6_addr[9];
+ mac->ether_addr_octet[2] = addr->s6_addr[10];
+ mac->ether_addr_octet[3] = addr->s6_addr[13];
+ mac->ether_addr_octet[4] = addr->s6_addr[14];
+ mac->ether_addr_octet[5] = addr->s6_addr[15];
+
+ return 0;
+}
+
int batadv_interface_check(char *mesh_iface)
{
char *debugfs_mnt;
diff --git a/batadv_query.h b/batadv_query.h
index f2a1f32..7dca319 100644
--- a/batadv_query.h
+++ b/batadv_query.h
@@ -22,9 +22,13 @@
#define _BATADV_QUERY_H
#include <stdint.h>
+#include <netinet/in.h>
struct ether_addr *translate_mac(char *mesh_iface, struct ether_addr *mac);
uint8_t get_tq(char *mesh_iface, struct ether_addr *mac);
int batadv_interface_check(char *mesh_iface);
+int mac_to_ipv6(const struct ether_addr *mac, struct in6_addr *addr);
+int ipv6_to_mac(const struct in6_addr *addr, struct ether_addr *mac);
+int is_ipv6_eui64(const struct in6_addr *addr);
#endif
diff --git a/netsock.c b/netsock.c
index e24687f..381d9b0 100644
--- a/netsock.c
+++ b/netsock.c
@@ -28,10 +28,13 @@
#include <netinet/in.h>
#include <sys/ioctl.h>
#include <linux/if.h>
-#include <linux/if_packet.h>
-#include <linux/if_ether.h>
-#include <linux/filter.h>
#include "alfred.h"
+#include "batadv_query.h"
+
+const struct in6_addr in6addr_localmcast = {{{ 0xff, 0x02, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x01 } } };
int netsock_close(int sock)
{
@@ -41,18 +44,10 @@ int netsock_close(int sock)
int netsock_open(struct globals *globals)
{
int sock;
- struct sock_fprog filter;
- struct sockaddr_ll sll;
+ struct sockaddr_in6 sin6;
struct ifreq ifr;
- struct sock_filter BPF[] = {
- /* tcpdump -dd "ether proto 0x4242" */
- { 0x28, 0, 0, 0x0000000c },
- { 0x15, 0, 1, 0x00004242 },
- { 0x6, 0, 0, 0x0000ffff },
- { 0x6, 0, 0, 0x00000000 },
- };
- sock = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALFRED));
+ sock = socket(PF_INET6, SOCK_DGRAM, 0);
if (sock < 0) {
fprintf(stderr, "can't open socket: %s\n", strerror(errno));
return -1;
@@ -65,41 +60,32 @@ int netsock_open(struct globals *globals)
return -1;
}
- memset(&sll, 0, sizeof(sll));
- sll.sll_family = AF_PACKET;
- sll.sll_protocol = htons(ETH_P_ALL);
- sll.sll_ifindex = ifr.ifr_ifindex;
+ globals->scope_id = ifr.ifr_ifindex;
+
+ memset(&sin6, 0, sizeof(sin6));
+ sin6.sin6_port = htons(ALFRED_PORT);
+ sin6.sin6_family = AF_INET6;
+ sin6.sin6_addr = in6addr_any;
+ sin6.sin6_scope_id = ifr.ifr_ifindex;
if (ioctl(sock, SIOCGIFHWADDR, &ifr) == -1) {
fprintf(stderr, "can't get MAC address: %s\n", strerror(errno));
return -1;
}
- memcpy(globals->hwaddr, &ifr.ifr_hwaddr.sa_data, 6);
+ memcpy(&globals->hwaddr, &ifr.ifr_hwaddr.sa_data, 6);
+ mac_to_ipv6(&globals->hwaddr, &globals->address);
if (ioctl(sock, SIOCGIFMTU, &ifr) == -1) {
fprintf(stderr, "can't get MTU: %s\n", strerror(errno));
return -1;
}
- globals->mtu = ifr.ifr_mtu;
- if (globals->mtu > MAX_PAYLOAD)
- globals->mtu = MAX_PAYLOAD;
-
- if (bind(sock, (struct sockaddr *)&sll, sizeof(sll)) < 0) {
+ if (bind(sock, (struct sockaddr *)&sin6, sizeof(sin6)) < 0) {
fprintf(stderr, "can't bind\n");
return -1;
}
- filter.len = sizeof(BPF) / sizeof(struct sock_filter);
- filter.filter = BPF;
-
- if (setsockopt(sock, SOL_SOCKET, SO_ATTACH_FILTER,
- &filter, sizeof(filter)) < 0) {
- fprintf(stderr, "can't attach filter on socket");
- return -1;
- }
-
fcntl(sock, F_SETFL, fcntl(sock, F_GETFL, 0) | O_NONBLOCK);
globals->netsock = sock;
diff --git a/recv.c b/recv.c
index a740b80..82ab1ff 100644
--- a/recv.c
+++ b/recv.c
@@ -29,19 +29,24 @@
#include <sys/ioctl.h>
#include <linux/if.h>
#include <linux/if_packet.h>
-#include <linux/if_ether.h>
-#include <linux/filter.h>
+#include <arpa/inet.h>
#include "alfred.h"
#include "batadv_query.h"
static int process_alfred_push_data(struct globals *globals,
- struct ethhdr *ethhdr,
+ struct in6_addr *source,
struct alfred_packet *packet)
{
int len, data_len;
struct alfred_data *data;
struct dataset *dataset;
uint8_t *pos;
+ struct ether_addr mac;
+ int ret;
+
+ ret = ipv6_to_mac(source, &mac);
+ if (ret < 0)
+ goto err;
len = ntohs(packet->length);
pos = (uint8_t *)(packet + 1);
@@ -89,7 +94,7 @@ static int process_alfred_push_data(struct globals *globals,
/* if the sender is also the the source of the dataset, we
* got a first hand dataset. */
- if (memcmp(ethhdr->h_source, data->source, ETH_ALEN) == 0)
+ if (memcmp(&mac, data->source, ETH_ALEN) == 0)
dataset->data_source = SOURCE_FIRST_HAND;
else
dataset->data_source = SOURCE_SYNCED;
@@ -103,22 +108,29 @@ err:
}
static int process_alfred_announce_master(struct globals *globals,
- struct ethhdr *ethhdr,
+ struct in6_addr *source,
struct alfred_packet *packet)
{
struct server *server;
struct ether_addr *macaddr;
+ struct ether_addr mac;
+ int ret;
+
+ ret = ipv6_to_mac(source, &mac);
+ if (ret < 0)
+ return -1;
if (packet->version != ALFRED_VERSION)
return -1;
- server = hash_find(globals->server_hash, ethhdr->h_source);
+ server = hash_find(globals->server_hash, &mac);
if (!server) {
server = malloc(sizeof(*server));
if (!server)
return -1;
- memcpy(server->address, ethhdr->h_source, ETH_ALEN);
+ memcpy(&server->hwaddr, &mac, ETH_ALEN);
+ memcpy(&server->address, source, sizeof(*source));
if (hash_add(globals->server_hash, server)) {
free(server);
@@ -129,7 +141,7 @@ static int process_alfred_announce_master(struct globals *globals,
server->last_seen = time(NULL);
if (strcmp(globals->mesh_iface, "none") != 0) {
macaddr = translate_mac(globals->mesh_iface,
- (struct ether_addr *)server->address);
+ (struct ether_addr *)&server->hwaddr);
if (macaddr)
server->tq = get_tq(globals->mesh_iface, macaddr);
else
@@ -146,7 +158,7 @@ static int process_alfred_announce_master(struct globals *globals,
static int process_alfred_request(struct globals *globals,
- struct ethhdr *ethhdr,
+ struct in6_addr *source,
struct alfred_packet *packet)
{
uint8_t type;
@@ -161,7 +173,7 @@ static int process_alfred_request(struct globals *globals,
return -1;
type = *((uint8_t *)(packet + 1));
- push_data(globals, ethhdr->h_source, SOURCE_SYNCED, type);
+ push_data(globals, source, SOURCE_SYNCED, type);
return 0;
}
@@ -170,32 +182,33 @@ static int process_alfred_request(struct globals *globals,
int recv_alfred_packet(struct globals *globals)
{
uint8_t buf[MAX_PAYLOAD];
- int length;
- struct ethhdr *ethhdr;
+ ssize_t length;
struct alfred_packet *packet;
- int headsize;
+ struct sockaddr_in6 source;
+ socklen_t sourcelen;
- length = read(globals->netsock, buf, sizeof(buf));
+ sourcelen = sizeof(source);
+ length = recvfrom(globals->netsock, buf, sizeof(buf), 0,
+ (struct sockaddr *)&source, &sourcelen);
if (length <= 0) {
fprintf(stderr, "read from network socket failed: %s\n",
strerror(errno));
return -1;
}
- /* drop too small packets */
- headsize = sizeof(*ethhdr) + sizeof(*packet);
- if (length < headsize)
- return -1;
+ packet = (struct alfred_packet *)buf;
- ethhdr = (struct ethhdr *)buf;
- packet = (struct alfred_packet *)(ethhdr + 1);
+ /* drop packets not sent over link-local ipv6 */
+ if (!is_ipv6_eui64(&source.sin6_addr))
+ return -1;
/* drop packets from ourselves */
- if (memcmp(ethhdr->h_source, globals->hwaddr, ETH_ALEN) == 0)
+ if (0 == memcmp(&source.sin6_addr, &globals->address,
+ sizeof(source.sin6_addr)))
return -1;
/* drop truncated packets */
- if (length - headsize < ((int)ntohs(packet->length)))
+ if (length < ((int)ntohs(packet->length)))
return -1;
/* drop incompatible packet */
@@ -204,13 +217,14 @@ int recv_alfred_packet(struct globals *globals)
switch (packet->type) {
case ALFRED_PUSH_DATA:
- process_alfred_push_data(globals, ethhdr, packet);
+ process_alfred_push_data(globals, &source.sin6_addr, packet);
break;
case ALFRED_ANNOUNCE_MASTER:
- process_alfred_announce_master(globals, ethhdr, packet);
+ process_alfred_announce_master(globals, &source.sin6_addr,
+ packet);
break;
case ALFRED_REQUEST:
- process_alfred_request(globals, ethhdr, packet);
+ process_alfred_request(globals, &source.sin6_addr, packet);
break;
default:
/* unknown packet type */
@@ -220,27 +234,21 @@ int recv_alfred_packet(struct globals *globals)
return 0;
}
-int send_alfred_packet(struct globals *globals, uint8_t *dest, void *buf,
- int length)
+int send_alfred_packet(struct globals *globals, const struct in6_addr *dest,
+ void *buf, int length)
{
int ret;
- struct ethhdr *ethhdr;
- char *sendbuf;
-
- sendbuf = malloc(sizeof(*ethhdr) + length);
- if (!sendbuf)
- return -1;
-
- ethhdr = (struct ethhdr *)sendbuf;
-
- memcpy(ethhdr->h_source, globals->hwaddr, ETH_ALEN);
- memcpy(ethhdr->h_dest, dest, ETH_ALEN);
- ethhdr->h_proto = htons(ETH_P_ALFRED);
- memcpy(sendbuf + sizeof(*ethhdr), buf, length);
+ struct sockaddr_in6 dest_addr;
- ret = write(globals->netsock, sendbuf, sizeof(*ethhdr) + length);
+ memset(&dest_addr, 0, sizeof(dest_addr));
+ dest_addr.sin6_family = AF_INET6;
+ dest_addr.sin6_port = htons(ALFRED_PORT);
+ dest_addr.sin6_scope_id = globals->scope_id;
+ memcpy(&dest_addr.sin6_addr, dest, sizeof(*dest));
- free(sendbuf);
+ ret = sendto(globals->netsock, buf, length, 0,
+ (struct sockaddr *)&dest_addr,
+ sizeof(struct sockaddr_in6));
return (ret == length);
}
diff --git a/send.c b/send.c
index f1f66b8..261185b 100644
--- a/send.c
+++ b/send.c
@@ -33,8 +33,6 @@
#include <linux/filter.h>
#include "alfred.h"
-static uint8_t bcast[ETH_ALEN] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
-
int announce_master(struct globals *globals)
{
struct alfred_packet announcement;
@@ -43,12 +41,13 @@ int announce_master(struct globals *globals)
announcement.version = ALFRED_VERSION;
announcement.length = htons(0);
- send_alfred_packet(globals, bcast, &announcement, sizeof(announcement));
+ send_alfred_packet(globals, &in6addr_localmcast, &announcement,
+ sizeof(announcement));
return 0;
}
-int push_data(struct globals *globals, uint8_t *destination,
+int push_data(struct globals *globals, struct in6_addr *destination,
enum data_source max_source_level, int type_filter)
{
struct hash_it_t *hashit = NULL;
@@ -73,7 +72,7 @@ int push_data(struct globals *globals, uint8_t *destination,
/* would the packet be too big? send so far aggregated data
* first */
if (total_length + dataset->data.length + sizeof(*data) >
- globals->mtu - ALFRED_HEADLEN) {
+ MAX_PAYLOAD - ALFRED_HEADLEN) {
packet->length = htons(total_length);
send_alfred_packet(globals, destination, packet,
sizeof(*packet) + total_length);
@@ -107,7 +106,7 @@ int sync_data(struct globals *globals)
while (NULL != (hashit = hash_iterate(globals->server_hash, hashit))) {
struct server *server = hashit->bucket->data;
- push_data(globals, server->address, SOURCE_FIRST_HAND,
+ push_data(globals, &server->address, SOURCE_FIRST_HAND,
NO_FILTER);
}
return 0;
@@ -119,7 +118,7 @@ int push_local_data(struct globals *globals)
if (!globals->best_server)
return -1;
- push_data(globals, globals->best_server->address, SOURCE_LOCAL,
+ push_data(globals, &globals->best_server->address, SOURCE_LOCAL,
NO_FILTER);
return 0;
diff --git a/server.c b/server.c
index 7218630..fd0d282 100644
--- a/server.c
+++ b/server.c
@@ -35,19 +35,20 @@ static int server_compare(void *d1, void *d2)
{
struct server *s1 = d1, *s2 = d2;
/* compare source and type */
- return ((memcmp(s1->address, s2->address, sizeof(s1->address)) == 0) ?
- 1 : 0);
+ if (memcmp(&s1->hwaddr, &s2->hwaddr, sizeof(s1->hwaddr)) == 0)
+ return 1;
+ else
+ return 0;
}
static int server_choose(void *d1, int size)
{
struct server *s1 = d1;
- unsigned char *key = s1->address;
uint32_t hash = 0;
size_t i;
- for (i = 0; i < sizeof(s1->address); i++) {
- hash += key[i];
+ for (i = 0; i < sizeof(s1->hwaddr); i++) {
+ hash += s1->hwaddr.ether_addr_octet[i];
hash += (hash << 10);
hash ^= (hash >> 6);
}
diff --git a/unix_sock.c b/unix_sock.c
index bf11869..f91ae73 100644
--- a/unix_sock.c
+++ b/unix_sock.c
@@ -104,7 +104,7 @@ static int unix_sock_add_data(struct globals *globals,
data = (struct alfred_data *)(packet + 1);
data_len = ntohs(data->length);
- memcpy(data->source, globals->hwaddr, sizeof(globals->hwaddr));
+ memcpy(data->source, &globals->hwaddr, sizeof(globals->hwaddr));
if ((int)(data_len + sizeof(*data)) > len)
return -1;
@@ -164,7 +164,7 @@ static int unix_sock_req_data(struct globals *globals,
if (globals->opmode == OPMODE_MASTER)
goto send_reply;
- send_alfred_packet(globals, globals->best_server->address,
+ send_alfred_packet(globals, &globals->best_server->address,
packet, sizeof(*packet) + len);
/* process incoming packets ... */