1 /*
2 
3 Packet reordering test implementation, intended to cause packets to be
4 reordered for testing pptpd and other servers.  Avoids the use of
5 pqueue.c so that it can be tested independently.
6 
7 */
8 
9 #include <unistd.h>
10 #include <stdlib.h>
11 #include <string.h>
12 #include "util.h"
13 #include "test-redirections.h"
14 
15 /* whether we are asked to test ordering, obtained from command line */
16 extern int test_type;
17 
18 /* rate at which to do test ordering changes */
19 extern int test_rate;
20 
21 /* trigger cycle */
22 static int test_ordering_cycle = 0;
23 
24 /* phase of reordering */
25 static int test_ordering_phase = 0;
26 
27 /* swap a packet every now and then */
write_reordered_swap(int fd,const void * buf,size_t count)28 static ssize_t write_reordered_swap(int fd, const void *buf, size_t count)
29 {
30   static void *pocket_buf = NULL;
31   static int pocket_count = 0;
32   int stat;
33 
34   switch (test_ordering_phase) {
35   case 0: /* between triggers, send as normal */
36     test_ordering_cycle++;
37     if (test_ordering_cycle == test_rate) test_ordering_phase++;
38     return write(fd, buf, count);
39   case 1: /* triggered, swap a packet */
40     test_ordering_cycle++;
41     if (test_ordering_cycle == (test_rate+1)) {
42       /* pocket the packet */
43       pocket_count = count;
44       pocket_buf = malloc(count);
45       memcpy(pocket_buf, buf, count);
46       log("test order swap, packet buffered");
47       /* lie about the result */
48       return count;
49     } else {
50       /* after this, reset to normal */
51       test_ordering_cycle = 0;
52       test_ordering_phase = 0;
53       /* send the new packet first */
54       stat = write(fd, buf, count);
55       if ((size_t)stat != count) return stat;
56       /* then send the old packet next */
57       stat = write(fd, pocket_buf, pocket_count);
58       free(pocket_buf);
59       log("test order swap, packets sent");
60       return count;
61     }
62   default:
63     return write(fd, buf, count);
64   }
65 }
66 
67 /* hold ten packets and send the eleventh, then the ten in order */
write_reordered_retransmit(int fd,const void * buf,size_t count)68 static ssize_t write_reordered_retransmit(int fd, const void *buf, size_t count)
69 {
70   int test_length = 10;
71   static void *pocket_buf[10];
72   static int pocket_count[10];
73   int stat, n;
74 
75   switch (test_ordering_phase) {
76   case 0: /* between triggers, send as normal */
77     test_ordering_cycle++;
78     if (test_ordering_cycle == test_rate) test_ordering_phase++;
79     return write(fd, buf, count);
80   case 1: /* triggered, buffer the packets */
81     test_ordering_cycle++;
82     if (test_ordering_cycle == (test_rate+test_length)) {
83       test_ordering_phase = 2;
84     }
85     /* pocket the packet */
86     n = test_ordering_cycle - test_rate - 1;
87     pocket_count[n] = count;
88     pocket_buf[n] = malloc(count);
89     memcpy(pocket_buf[n], buf, count);
90     log("test order retransmit, packet buffered");
91     /* lie about the result */
92     return count;
93   case 2:
94     /* after this, reset to normal */
95     test_ordering_cycle = 0;
96     test_ordering_phase = 0;
97     /* send the new packet first */
98     stat = write(fd, buf, count);
99     if ((size_t)stat != count) return stat;
100     /* send the buffered packets in normal order */
101     for (n=0; n<test_length; n++) {
102       stat = write(fd, pocket_buf[n], pocket_count[n]);
103       /* ignores failures */
104       free(pocket_buf[n]);
105     }
106     log("test order retransmit, packets sent");
107     return count;
108   default:
109     return write(fd, buf, count);
110   }
111 }
112 
113 /* hold ten packets and send them in reverse order */
write_reordered_reverse(int fd,const void * buf,size_t count)114 static ssize_t write_reordered_reverse(int fd, const void *buf, size_t count)
115 {
116   int test_length = 10;
117   static void *pocket_buf[10];
118   static int pocket_count[10];
119   int stat, n;
120 
121   switch (test_ordering_phase) {
122   case 0: /* between triggers, send as normal */
123     test_ordering_cycle++;
124     if (test_ordering_cycle == test_rate) test_ordering_phase++;
125     return write(fd, buf, count);
126   case 1: /* triggered, buffer the packets */
127     test_ordering_cycle++;
128     if (test_ordering_cycle == (test_rate+test_length)) {
129       test_ordering_phase = 2;
130     }
131     /* pocket the packet */
132     n = test_ordering_cycle - test_rate - 1;
133     pocket_count[n] = count;
134     pocket_buf[n] = malloc(count);
135     memcpy(pocket_buf[n], buf, count);
136     log("test order reverse, packet buffered");
137     /* lie about the result */
138     return count;
139   case 2:
140     /* after this, reset to normal */
141     test_ordering_cycle = 0;
142     test_ordering_phase = 0;
143     /* send the new packet first */
144     stat = write(fd, buf, count);
145     if ((size_t)stat != count) return stat;
146     /* send the buffered packets in reverse order */
147     for (n=test_length-1; n>0; n--) {
148       stat = write(fd, pocket_buf[n], pocket_count[n]);
149       /* ignores failures */
150       free(pocket_buf[n]);
151     }
152     log("test order reverse, packets sent");
153     return count;
154   default:
155     return write(fd, buf, count);
156   }
157 }
158 
159 /* dispatcher for write reordering tests */
write_reordered(int fd,const void * buf,size_t count)160 static ssize_t write_reordered(int fd, const void *buf, size_t count)
161 {
162   switch (test_type) {
163   case 1: /* swap a packet every now and then */
164     return write_reordered_swap(fd, buf, count);
165   case 2: /* hold ten packets and send the eleventh, then the ten in order */
166     return write_reordered_retransmit(fd, buf, count);
167   case 3: /* hold ten packets and send them in reverse order */
168     return write_reordered_reverse(fd, buf, count);
169   default:
170     return write(fd, buf, count);
171   }
172 }
173 
test_redirections(void)174 struct test_redirections *test_redirections(void)
175 {
176   static struct test_redirections *my = NULL;
177 
178   if (my == NULL) my = malloc(sizeof(struct test_redirections));
179 
180   my->write = write;
181   if (test_type) my->write = write_reordered;
182 
183   return my;
184 }
185