/* Copyright 2004 Lodewijk Voge / Stichting Wireless Leiden, * All Rights Reserved. * License: http://www.wirelessleiden.nl/LICENSE. * * This is a daemon to calculate the ETX metric for network links as described * in http://research.microsoft.com/mesh/papers/metrics.pdf . * * Compiles on FreeBSD and MacOS X. * * lvoge@cs.vu.nl */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "array.h" #include "etx.h" #define ETXTYPE 12345 #define EXPIRYTIME 10 /* the prefixes of the interfaces we're interested in. for wirelessleiden, * this is the wireless interfaces used, and these are all 'wi', and some * 'ath's in testing. */ static const char *iface_prefixes[] = { "wi", "ath", "en", /* test */ "rl", /* test */ 0 }; typedef struct { u_int addr; u_char count; } counter_t __attribute__((packed)); typedef struct { u_short numcounts; counter_t counts[0]; } packet_t __attribute__((packed)); typedef struct { u_int saddr; u_char my_count; array_t *timevals; } peer_t; static array_t *peers = 0; static array_t *my_addrs = 0; static array_t *bcast_addrs = 0; static int sockfd; static struct sockaddr_in send_sockaddr; static struct sockaddr_in res_sockaddr; static peer_t *peer_new(u_int saddr) { peer_t *p; p = malloc(sizeof(peer_t)); p->saddr = saddr; p->timevals = array_new(0); p->my_count = 0; array_append(peers, p); return p; } static void packet_register(struct sockaddr_in *from, packet_t *packet) { int i, j, numcounts; peer_t *p; struct timeval *timeval; /* check whether this isn't one of our own broadcasted packets */ j = from->sin_addr.s_addr; for (i = 0; i < my_addrs->num_elements; i++) if (*(in_addr_t *)array_index(my_addrs, i) == j) break; if (i < my_addrs->num_elements) return; /* see if this is a previously seen peer and make a new entry if * necessary */ p = 0; for (i = 0; i < peers->num_elements; i++) { p = array_index(peers, i); if (p->saddr == from->sin_addr.s_addr) break; } if (p == 0) p = peer_new(from->sin_addr.s_addr); /* record the timeval */ timeval = malloc(sizeof(struct timeval)); gettimeofday(timeval, 0); array_append(p->timevals, timeval); /* search the packet for any counters for one of our addresses */ numcounts = ntohs(packet->numcounts); for (i = 0; i < numcounts; i++) { for (j = 0; j < my_addrs->num_elements; j++) if (*(int *)array_index(my_addrs, j) == packet->counts[i].addr) break; /* there should be only one, so stop looking */ if (j < my_addrs->num_elements) break; } if (i < numcounts && j < my_addrs->num_elements) p->my_count = packet->counts[i].count; } static int packet_fill(packet_t **ppacket) { packet_t *packet; peer_t *peer; int i, size; size = sizeof(packet_t) + peers->num_elements * sizeof(counter_t); packet = malloc(size); packet->numcounts = htons(peers->num_elements); for (i = 0; i < peers->num_elements; i++) { peer = array_index(peers, i); packet->counts[i].addr = peer->saddr; packet->counts[i].count = peer->timevals->num_elements; } *ppacket = packet; return size; } static int timevalfilter(void *em, void *data) { struct timeval *t, *lower_limit; t = em; lower_limit = data; return timercmp(t, lower_limit, >); } static void expire() { peer_t *p; int i; struct timeval lower_limit, t1, t2; gettimeofday(&t1, 0); t2.tv_sec = EXPIRYTIME; t2.tv_usec = 0; timersub(&t1, &t2, &lower_limit); for (i = 0; i < peers->num_elements; i++) { p = array_index(peers, i); array_filter(p->timevals, timevalfilter, &lower_limit); } } static float etx(peer_t *q) { return (1.0 - (EXPIRYTIME - (float)q->timevals->num_elements) / EXPIRYTIME) * (1.0 - (EXPIRYTIME - (float)q->my_count) / EXPIRYTIME); } static void bcast(int i) { packet_t *packet; int size; result_t *results; peer_t *p; expire(); size = packet_fill(&packet); for (i = 0; i < bcast_addrs->num_elements; i++) { send_sockaddr.sin_addr.s_addr = *(in_addr_t *)bcast_addrs->elements[i]; if (sendto(sockfd, packet, size, 0, (struct sockaddr *)&send_sockaddr, sizeof(send_sockaddr)) == -1) perror("sendto"); } free(packet); if (peers->num_elements == 0) printf("I have no peers\n"); else { size = peers->num_elements * sizeof(result_t); results = malloc(size); for (i = 0; i < peers->num_elements; i++) { p = array_index(peers, i); results[i].addr = p->saddr; results[i].mask = 30; results[i].etx = etx(p); } if (sendto(sockfd, results, size, 0, (struct sockaddr *)&res_sockaddr, sizeof(res_sockaddr)) == -1) perror("sendto"); free(results); } alarm(1); } static void look_for_addrs() { struct ifaddrs *ifap, *ifp; in_addr_t *a; int i; if (getifaddrs(&ifap) == -1) { perror("getifaddrs"); exit(1); } for (ifp = ifap; ifp; ifp = ifp->ifa_next) { for (i = 0; iface_prefixes[i]; i++) if (strncmp(ifp->ifa_name, iface_prefixes[i], strlen(iface_prefixes[i])) == 0) break; if (iface_prefixes[i]) { a = malloc(sizeof(in_addr_t)); *a = ((struct sockaddr_in *)ifp->ifa_addr)->sin_addr.s_addr; array_append(my_addrs, a); a = malloc(sizeof(in_addr_t)); *a = ((struct sockaddr_in *)ifp->ifa_broadaddr)->sin_addr.s_addr; array_append(bcast_addrs, a); } } freeifaddrs(ifap); } int main(int argc, char *argv[]) { int i, bufsize, size; packet_t *packet; struct sockaddr_in from; if (argc < 2) { fprintf(stderr, "Usage: etxd
\n"); return 1; } sockfd = socket(PF_INET, SOCK_DGRAM, 0); if (sockfd == -1) { perror("socket"); return 0; } memset(&from, 0, sizeof(from)); from.sin_len = sizeof(from); from.sin_family = AF_INET; from.sin_port = htons(PEERPORT); if (bind(sockfd, (struct sockaddr *)&from, sizeof(from)) == -1) { perror("bind"); return 0; } i = 1; if (setsockopt(sockfd, SOL_SOCKET, SO_BROADCAST, &i, sizeof(i)) == -1) { perror("setsockopt"); return 0; } memset(&res_sockaddr, 0, sizeof(res_sockaddr)); res_sockaddr.sin_len = sizeof(res_sockaddr); res_sockaddr.sin_family = AF_INET; res_sockaddr.sin_port = htons(RESULTPORT); if (inet_aton(argv[1], &res_sockaddr.sin_addr) == 0) { fprintf(stderr, "Error parsing catcher address.\n"); return 1; } memset(&send_sockaddr, 0, sizeof(send_sockaddr)); send_sockaddr.sin_len = sizeof(send_sockaddr); send_sockaddr.sin_family = AF_INET; send_sockaddr.sin_port = htons(PEERPORT); peers = array_new(0); my_addrs = array_new(0); bcast_addrs = array_new(0); look_for_addrs(); signal(SIGALRM, bcast); alarm(1); bufsize = sizeof(packet_t) + MAX_PEERS * sizeof(counter_t); packet = malloc(bufsize); while (1) { i = sizeof(from); size = recvfrom(sockfd, packet, bufsize, 0, (struct sockaddr *)&from, &i); if (size == -1) { perror("recvfrom"); } packet_register(&from, packet); } free(packet); array_free(peers, free); return 0; }