/*
** CBLM v3
** $Id: transmit.c,v 1.17 2008/02/04 01:43:38 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
*/

#include "transmit.h"
#include "util.h"

#include <string.h>
#include <errno.h>

static const char rcsid[] = "$Id: transmit.c,v 1.17 2008/02/04 01:43:38 sella Exp $";

/* Global variables */
extern struct args_t args;
extern struct prefs_t prefs;

int transmit_packet(int s, struct in_addr *dst, uint32_t pending, struct tos_t *tos, uint32_t set) {
	int i, c, ret_val = 1, pattern_len;
	struct sockaddr_in addr;
	char buf[MAX_SIZE];
	struct packet_t* packet = (struct packet_t*) buf;
	size_t packet_len = (prefs.size < sizeof(struct packet_t)) ? sizeof(struct packet_t) : prefs.size;
	struct in_addr in;
	static uint32_t seq = 0, last_set = -1;
	fd_set fds;
	struct timeval timeout, tv;
	unsigned int opt;

	if (args.debug >= DEBUG_DUMP) {
		syslog(LOG_DEBUG, "Transmitting %lu pkts to %s with ToS %u (dscp: %u, prec: %u) and PRI %u (size %lu).", pending, inet_ntoa(*dst), tos->tos, tos->tos >> 2, tos->tos >> 5, tos->pri, packet_len);
	}

	/* Reset the seq, if needed. */
	if (set != last_set) {
		seq = 0;
		last_set = set;
	}

	/* Initialize our addr. */
	memset(&addr, 0, sizeof(addr));
	addr.sin_family = AF_INET;
	addr.sin_addr = *dst;
	addr.sin_port = prefs.port;

#ifdef IP_TOS
	opt = tos->tos & 0x000000FF;
	if (setsockopt(s, IPPROTO_IP, IP_TOS, (void*) &opt, sizeof(opt)) < 0) {
		syslog(LOG_CRIT, "Failed to set ToS value %u (dscp: %u, prec: %u): %s", opt, opt >> 2, opt >> 5, strerror(errno));
	}
#endif

#ifdef SO_PRIORITY
	opt = tos->pri & 0x00000007;
	if (setsockopt(s, SOL_SOCKET, SO_PRIORITY, (void*) &opt, sizeof(opt)) < 0) {
		syslog(LOG_CRIT, "Failed to set PRI value %u: %s", opt, strerror(errno));
	}
#endif

	/* Set generic portions of our packet. */
	memset(packet, 0, packet_len);

	packet->version = 0x0;
	packet->src = args.source;
	packet->dst = *dst;
	memcpy(packet->key, prefs.key, sizeof(packet->key));
	packet->tx_tos = tos->tos;
	packet->tx_pri = tos->pri;
	packet->set = set;
	packet->tot = htons(prefs.rate);

	pattern_len = strlen(prefs.pattern);
	for (i = 0; i < packet_len - sizeof(struct packet_t); i += pattern_len) {
		if (pattern_len + i < packet_len - sizeof(struct packet_t)) {
			memcpy(packet->pattern + i, prefs.pattern, pattern_len);
		} else {
			memcpy(packet->pattern + i, prefs.pattern, packet_len - sizeof(struct packet_t) - i + 1);
		}
	}

	/* Transmit our packets. */
	for (i = 0; i < pending; i++) {
		/* Copy our fdset, select screws with it. */
		FD_ZERO(&fds);
		FD_SET(s, &fds);

		/* Use a 3/4 sec timeout. */
		timeout.tv_sec = 0;
		timeout.tv_usec = 750000;

		/* Wait for a connection. */
		c = select(s + 1, 0, &fds, 0, &timeout);
		if (c == -1) {
			syslog(LOG_CRIT, "select() %s", strerror(errno));
			return;
		} else if (c > 0) {
			/* Set dynamic portions of packet. */
			gettimeofday(&tv, NULL);
			packet->tx = htonll(tvtousec(tv));

			packet->seq = htons(seq++);

			if (sendto(s, (void*) packet, packet_len, 0, (struct sockaddr*) &addr, sizeof(struct sockaddr_in)) == -1) {
				syslog(LOG_CRIT, "sendto() %s", strerror(errno));
				goto exit;
			}
		}
	}

	/* Success */
	ret_val = 0;

exit:
	return ret_val;
}

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