view net-traffic/rtp-stream-gen.c @ 14:e7f43262efa1

rtp-stream-dump: add hex dump option
author Mychaela Falconia <falcon@freecalypso.org>
date Sun, 28 Jul 2024 01:53:41 +0000
parents e686bc92c7d8
children
line wrap: on
line source

/*
 * This program generates a dummy RTP stream, sending RTP packets with
 * a dummy payload to the specified destination IP:port, paced in time
 * every 20 ms.  The purpose is to test the behaviour of affordably-available
 * Internet connections when presented with such traffic: the intent is
 * to capture this packet stream on the receiving end with tcpdump and then
 * analyze the pcap for problem signs of packet loss, jitter and reordering.
 */

#include <sys/types.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include <unistd.h>

#define	RTP_PACKET_HDR_SIZE	12
#define	RTP_MAX_PAYLOAD		160
#define	GSM_FR_BYTES		33

struct rtp_packet {
	uint8_t		v_p_x_cc;
	uint8_t		m_pt;
	uint16_t	seq;
	uint32_t	tstamp;
	uint32_t	ssrc;
	uint8_t		payload[RTP_MAX_PAYLOAD];
};

static const uint8_t gsmfr_silence_frame[GSM_FR_BYTES] = {
	0xDA, 0xA7, 0xAA, 0xA5, 0x1A,
	0x50, 0x20, 0x38, 0xE4, 0x6D, 0xB9, 0x1B,
	0x50, 0x20, 0x38, 0xE4, 0x6D, 0xB9, 0x1B,
	0x50, 0x20, 0x38, 0xE4, 0x6D, 0xB9, 0x1B,
	0x50, 0x20, 0x38, 0xE4, 0x6D, 0xB9, 0x1B,
};

static struct sockaddr_in sin_src, sin_dest;
static struct rtp_packet rtp_pkt;
static unsigned rtp_pkt_len, req_packet_count;
static int udp_fd;

static void
parse_ip_port(arg, sin)
	char *arg;
	struct sockaddr_in *sin;
{
	char *cp;
	int rc;

	cp = index(arg, ':');
	if (!cp) {
		fprintf(stderr, "error: missing ':' in IP:port argument\n");
		exit(1);
	}
	*cp++ = '\0';
	sin->sin_family = AF_INET;
	rc = inet_aton(arg, &sin->sin_addr);
	if (!rc) {
		fprintf(stderr, "error: invalid IP address argument\n");
		exit(1);
	}
	sin->sin_port = htons(atoi(cp));
}

static void
prepare_rtp_payload(type_arg)
	char *type_arg;
{
	unsigned pl_len;

	if (!strcmp(type_arg, "pcmu")) {
		rtp_pkt.m_pt = 0;
		memset(rtp_pkt.payload, 0xFF, RTP_MAX_PAYLOAD);
		pl_len = RTP_MAX_PAYLOAD;
	} else if (!strcmp(type_arg, "pcma")) {
		rtp_pkt.m_pt = 8;
		memset(rtp_pkt.payload, 0xD5, RTP_MAX_PAYLOAD);
		pl_len = RTP_MAX_PAYLOAD;
	} else if (!strcmp(type_arg, "gsm")) {
		rtp_pkt.m_pt = 3;
		memcpy(rtp_pkt.payload, gsmfr_silence_frame, GSM_FR_BYTES);
		pl_len = GSM_FR_BYTES;
	} else {
		fprintf(stderr, "error: invalid PL type argument \"%s\"\n",
			type_arg);
		exit(1);
	}
	rtp_pkt_len = RTP_PACKET_HDR_SIZE + pl_len;
}

static void
assign_rtpout_ssrc()
{
	struct timeval curtime;

	gettimeofday(&curtime, 0);
	rtp_pkt.ssrc = curtime.tv_sec ^ curtime.tv_usec ^ getpid();
}

static void
prepare_udp_socket()
{
	int rc;

	udp_fd = socket(AF_INET, SOCK_DGRAM, 0);
	if (udp_fd < 0) {
		perror("socket(AF_INET, SOCK_DGRAM, 0)");
		exit(1);
	}
	rc = bind(udp_fd, (struct sockaddr *) &sin_src, sizeof sin_src);
	if (rc < 0) {
		perror("bind");
		exit(1);
	}
	rc = connect(udp_fd, (struct sockaddr *) &sin_dest, sizeof sin_dest);
	if (rc < 0) {
		perror("connect");
		exit(1);
	}
}

static void
send_out_packets()
{
	unsigned n, seq, ts;

	seq = 0;
	ts = 0;
	for (n = 0; n < req_packet_count; n++) {
		rtp_pkt.seq = htons(seq);
		rtp_pkt.tstamp = htonl(ts);
		send(udp_fd, &rtp_pkt, rtp_pkt_len, 0);
		seq++;
		ts += 160;
		usleep(20000);
	}
}

main(argc, argv)
	char **argv;
{
	if (argc != 5) {
		fprintf(stderr, "usage: %s src-IP:port dest-IP:port type num\n",
			argv[0]);
		exit(1);
	}
	parse_ip_port(argv[1], &sin_src);
	parse_ip_port(argv[2], &sin_dest);
	prepare_rtp_payload(argv[3]);
	rtp_pkt.v_p_x_cc = 0x80;
	assign_rtpout_ssrc();
	req_packet_count = atoi(argv[4]);
	prepare_udp_socket();
	send_out_packets();
	exit(0);
}