/*
** CBLM v3
** $Id: util.c,v 1.11 2008/02/04 00:40:14 sella Exp $
** Copyright (c) 2004-2007 James M. Sella. All Rights Reserved.
** Released under the GPL Version 2 License.
** http://www.digitalgenesis.com
** 
** Cheap Bastard Latency Monitor v3
*/

static const char rcsid[] = "$Id: util.c,v 1.11 2008/02/04 00:40:14 sella Exp $";

#include "util.h"

#include <string.h>
#include <stdlib.h>

extern struct prefs_t prefs;

unsigned short	csum(unsigned short *buf, int nwords) {
  unsigned long sum;
  for (sum = 0; nwords > 0; nwords--)
    sum += *buf++;
  sum = (sum >> 16) + (sum & 0xffff);
  sum += (sum >> 16);
  return ~sum;
}

// Stolen from Newsgroup example by Floyd Davidson (floyd@ptialaska.net) on 2002-07-16 04:50:07 PST
void source_ip(char *interface, struct in_addr *ip) {
	unsigned char      *u;
	int                sockfd, size  = 1;
	struct ifreq       *ifr;
	struct ifconf      ifc;
	struct sockaddr_in sa;

	if (0 > (sockfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP))) {
		syslog(LOG_EMERG, "Failed to open socket.");
		exit(EXIT_FAILURE);
	}

	ifc.ifc_len = IFRSIZE;
	ifc.ifc_req = NULL;

	do {
		++size;
		//FIXME: Does this realloc cause a leak??
		/* realloc buffer size until no overflow occurs  */
		if (NULL == (ifc.ifc_req = (struct ifreq*) realloc(ifc.ifc_req, IFRSIZE))) {
			syslog(LOG_EMERG, "Out of memory.");
			exit(EXIT_FAILURE);
		}
		ifc.ifc_len = IFRSIZE;
		if (ioctl(sockfd, SIOCGIFCONF, &ifc)) {
			syslog(LOG_EMERG, "ioctl(SIOCFIFCONF) error");
			exit(EXIT_FAILURE);
		}
	} while  (IFRSIZE <= ifc.ifc_len);

	ifr = ifc.ifc_req;
	for (;(char *) ifr < (char *) ifc.ifc_req + ifc.ifc_len; ++ifr) {

		if (ifr->ifr_addr.sa_data == (ifr+1)->ifr_addr.sa_data) {
			continue;  /* duplicate, skip it */
		}

		if (ioctl(sockfd, SIOCGIFFLAGS, ifr)) {
			continue;  /* failed to get flags, skip it */
		}

		if (strncmp(ifr->ifr_name, "lo", 2) && (strlen(interface) < 1 || !strcmp(ifr->ifr_name, interface))) {
			//fprintf(stderr, "Interface: %s\n", ifr->ifr_name);
			*ip = inaddrr(ifr_addr.sa_data);
			//fprintf(stderr, "IP Address: %s\n", inet_ntoa(*ip));

			break;
		}
	}

	/* Free memory. */
	free(ifc.ifc_req);

	close(sockfd);
}

int subtract_timeval(struct timeval *dst, struct timeval *src) {
	assert(src != NULL);
	assert(dst != NULL);

   if ((dst->tv_usec -= src->tv_usec) < 0) {
      dst->tv_sec--;
      dst->tv_usec += 1000000;
   }
   dst->tv_sec -= src->tv_sec;

   return 1;
}

void display_packet(struct packet_t* p, size_t len) {
	char buf[64];
	long pattern_len;
	struct timeval tv;

	assert(p != NULL);

	syslog(LOG_DEBUG, "[Packet]\n");

	syslog(LOG_DEBUG, " source: %s\n", inet_ntoa(p->src));

	syslog(LOG_DEBUG, " destination: %s\n", inet_ntoa(p->dst));

	tv = usectotv(ntohll(p->tx));
	syslog(LOG_DEBUG, " tx: %lu.%lu\n", tv.tv_sec, tv.tv_usec);

	tv = usectotv(ntohll(p->ox));
	syslog(LOG_DEBUG, " ox: %lu.%lu\n", tv.tv_sec, tv.tv_usec);

	tv = usectotv(ntohll(p->rx));
	syslog(LOG_DEBUG, " rx: %lu.%lu\n", tv.tv_sec, tv.tv_usec);

	memcpy(buf, p->key, sizeof(p->key));
	buf[17] = '\0';
	syslog(LOG_DEBUG, " key: %s\n", buf);

	syslog(LOG_DEBUG, " set: %lu\n", p->set);

	syslog(LOG_DEBUG, " tot: %lu\n", ntohs(p->tot));

	syslog(LOG_DEBUG, " seq: %lu\n", ntohs(p->seq));

	syslog(LOG_DEBUG, " tx_tos: %u (dscp: %u, prec: %u)\n", p->tx_tos, p->tx_tos >> 2, p->tx_tos >> 5);

	syslog(LOG_DEBUG, " ox_tos: %u (dscp: %u, prec: %u)\n", p->ox_tos, p->ox_tos >> 2, p->ox_tos >> 5);

	syslog(LOG_DEBUG, " rx_tos: %u (dscp: %u, prec: %u)\n", p->rx_tos, p->rx_tos >> 2, p->rx_tos >> 5);

	syslog(LOG_DEBUG, " tx_pri: %u\n", p->tx_pri);

	syslog(LOG_DEBUG, " ox_pri: %u\n", p->ox_pri);

	syslog(LOG_DEBUG, " rx_pri: %u\n", p->rx_pri);

	if (len - sizeof(struct packet_t) < 1) {
		pattern_len = 0;
	} else if (len - sizeof(struct packet_t) > sizeof(buf)) {
		pattern_len = sizeof(buf) - 1;
	} else {
		pattern_len = len - sizeof(struct packet_t);
	}
	if (!strncmp(prefs.pattern, p->pattern, pattern_len > strlen(prefs.pattern) ? strlen(prefs.pattern) : pattern_len)) {
		syslog(LOG_DEBUG, " pattern: %s\n", prefs.pattern);
	} else {
		memcpy(buf, p->pattern, pattern_len);
		buf[pattern_len] = '\0';
		syslog(LOG_DEBUG, " pattern: %s\n", buf);
	}
}

void display_data(struct data_t *data) {
	syslog(LOG_DEBUG, "[data_t]\n");
	syslog(LOG_DEBUG, " source: %s\n", inet_ntoa(data->src));
	syslog(LOG_DEBUG, " destination: %s\n", inet_ntoa(data->dst));
	syslog(LOG_DEBUG, " rx/tx: %lu/%lu\n", data->rx, data->tx);
	syslog(LOG_DEBUG, " rtt (min/max/avg): %d/%d/%lu\n", data->rtt_min, data->rtt_max, data->rtt_avg);
	syslog(LOG_DEBUG, " ott (min/max/avg): %d/%d/%lu\n", data->ott_min, data->ott_max, data->ott_avg);
	syslog(LOG_DEBUG, " jtr (min/max/avg): %d/%d/%lu\n", data->jtr_min, data->jtr_max, data->jtr_avg);
	syslog(LOG_DEBUG, " tos: %lu\n", data->tos);
	syslog(LOG_DEBUG, " pri: %lu\n", data->pri);
	syslog(LOG_DEBUG, " set: %lu\n", data->set);
	syslog(LOG_DEBUG, " seq: %lu\n", data->seq);
	syslog(LOG_DEBUG, " tos_a2z: %lu\n", data->tos_a2z);
	syslog(LOG_DEBUG, " tos_z2a: %lu\n", data->tos_z2a);
	syslog(LOG_DEBUG, " pri_a2z: %lu\n", data->pri_a2z);
	syslog(LOG_DEBUG, " pri_z2a: %lu\n", data->pri_z2a);
	syslog(LOG_DEBUG, " pkt_ooo: %lu\n", data->pkt_ooo);
	syslog(LOG_DEBUG, " pkt_dup: %lu\n", data->pkt_dup);
}

struct timeval htontv(const struct timeval src) {
	struct timeval ret_val;

	ret_val.tv_sec = htonl(src.tv_sec);
	ret_val.tv_usec = htonl(src.tv_usec);

	return ret_val;
}

struct timeval ntohtv(const struct timeval src) {
	struct timeval ret_val;

	ret_val.tv_sec = ntohl(src.tv_sec);
	ret_val.tv_usec = ntohl(src.tv_usec);

	return ret_val;
}

uint64_t tvtousec(const struct timeval src) {
	uint64_t ret_val = (src.tv_usec + (src.tv_sec * 1000000));

	return ret_val;
}

struct timeval usectotv(const uint64_t src) {
	struct timeval ret_val;

	ret_val.tv_sec = src / 1000000;
	ret_val.tv_usec = src - (ret_val.tv_sec * 1000000);
}

/*
** Local Variables:
** c-basic-offset: 3
** tab-width: 3
** End:
** vim: noet ts=3 sw=3
*/
