/*
Packet reordering test implementation, intended to cause packets to be
reordered for testing pptpd and other servers. Avoids the use of
pqueue.c so that it can be tested independently.
*/
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include "util.h"
#include "test-redirections.h"
/* whether we are asked to test ordering, obtained from command line */
extern int test_type;
/* rate at which to do test ordering changes */
extern int test_rate;
/* trigger cycle */
static int test_ordering_cycle = 0;
/* phase of reordering */
static int test_ordering_phase = 0;
/* swap a packet every now and then */
static ssize_t write_reordered_swap(int fd, const void *buf, size_t count)
{
static void *pocket_buf = NULL;
static int pocket_count = 0;
int stat;
switch (test_ordering_phase) {
case 0: /* between triggers, send as normal */
test_ordering_cycle++;
if (test_ordering_cycle == test_rate) test_ordering_phase++;
return write(fd, buf, count);
case 1: /* triggered, swap a packet */
test_ordering_cycle++;
if (test_ordering_cycle == (test_rate+1)) {
/* pocket the packet */
pocket_count = count;
pocket_buf = malloc(count);
memcpy(pocket_buf, buf, count);
log("test order swap, packet buffered");
/* lie about the result */
return count;
} else {
/* after this, reset to normal */
test_ordering_cycle = 0;
test_ordering_phase = 0;
/* send the new packet first */
stat = write(fd, buf, count);
if ((size_t)stat != count) return stat;
/* then send the old packet next */
stat = write(fd, pocket_buf, pocket_count);
free(pocket_buf);
log("test order swap, packets sent");
return count;
}
default:
return write(fd, buf, count);
}
}
/* hold ten packets and send the eleventh, then the ten in order */
static ssize_t write_reordered_retransmit(int fd, const void *buf, size_t count)
{
int test_length = 10;
static void *pocket_buf[10];
static int pocket_count[10];
int stat, n;
switch (test_ordering_phase) {
case 0: /* between triggers, send as normal */
test_ordering_cycle++;
if (test_ordering_cycle == test_rate) test_ordering_phase++;
return write(fd, buf, count);
case 1: /* triggered, buffer the packets */
test_ordering_cycle++;
if (test_ordering_cycle == (test_rate+test_length)) {
test_ordering_phase = 2;
}
/* pocket the packet */
n = test_ordering_cycle - test_rate - 1;
pocket_count[n] = count;
pocket_buf[n] = malloc(count);
memcpy(pocket_buf[n], buf, count);
log("test order retransmit, packet buffered");
/* lie about the result */
return count;
case 2:
/* after this, reset to normal */
test_ordering_cycle = 0;
test_ordering_phase = 0;
/* send the new packet first */
stat = write(fd, buf, count);
if ((size_t)stat != count) return stat;
/* send the buffered packets in normal order */
for (n=0; n<test_length; n++) {
stat = write(fd, pocket_buf[n], pocket_count[n]);
/* ignores failures */
free(pocket_buf[n]);
}
log("test order retransmit, packets sent");
return count;
default:
return write(fd, buf, count);
}
}
/* hold ten packets and send them in reverse order */
static ssize_t write_reordered_reverse(int fd, const void *buf, size_t count)
{
int test_length = 10;
static void *pocket_buf[10];
static int pocket_count[10];
int stat, n;
switch (test_ordering_phase) {
case 0: /* between triggers, send as normal */
test_ordering_cycle++;
if (test_ordering_cycle == test_rate) test_ordering_phase++;
return write(fd, buf, count);
case 1: /* triggered, buffer the packets */
test_ordering_cycle++;
if (test_ordering_cycle == (test_rate+test_length)) {
test_ordering_phase = 2;
}
/* pocket the packet */
n = test_ordering_cycle - test_rate - 1;
pocket_count[n] = count;
pocket_buf[n] = malloc(count);
memcpy(pocket_buf[n], buf, count);
log("test order reverse, packet buffered");
/* lie about the result */
return count;
case 2:
/* after this, reset to normal */
test_ordering_cycle = 0;
test_ordering_phase = 0;
/* send the new packet first */
stat = write(fd, buf, count);
if ((size_t)stat != count) return stat;
/* send the buffered packets in reverse order */
for (n=test_length-1; n>0; n--) {
stat = write(fd, pocket_buf[n], pocket_count[n]);
/* ignores failures */
free(pocket_buf[n]);
}
log("test order reverse, packets sent");
return count;
default:
return write(fd, buf, count);
}
}
/* dispatcher for write reordering tests */
static ssize_t write_reordered(int fd, const void *buf, size_t count)
{
switch (test_type) {
case 1: /* swap a packet every now and then */
return write_reordered_swap(fd, buf, count);
case 2: /* hold ten packets and send the eleventh, then the ten in order */
return write_reordered_retransmit(fd, buf, count);
case 3: /* hold ten packets and send them in reverse order */
return write_reordered_reverse(fd, buf, count);
default:
return write(fd, buf, count);
}
}
struct test_redirections *test_redirections(void)
{
static struct test_redirections *my = NULL;
if (my == NULL) my = malloc(sizeof(struct test_redirections));
my->write = write;
if (test_type) my->write = write_reordered;
return my;
}