2 * A small program to generate the ASCII form of a capture with TCP
3 * segments of a reasonable nature. The payload is all zeros.
7 * By Ronnie Sahlberg and Richard Sharpe. From a program initially
9 * Copyright 2003 Ronnie Sahlberg and Richard Sharpe
11 * This program is free software; you can redistribute it and/or
12 * modify it under the terms of the GNU General Public License
13 * as published by the Free Software Foundation; either version 2
14 * of the License, or (at your option) any later version.
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU General Public License for more details.
21 * You should have received a copy of the GNU General Public License
22 * along with this program; if not, write to the Free Software
23 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
25 * Using it to generate a capture file:
26 * ./mkcap [some-flags] > some-file
27 * text2pcap [some-other-flags] some-file some-file.cap
30 ./mkcap -a 2500 -s 15 -I "02 03 04 05" -i "45 45 45 45" -P "00 14" > ftp.cap.asci
31 text2pcap -t "%Y/%m/%d%t%H:%M:%S." ftp.cap.asci ftp.cap
39 #define ETH_1 "00 00 9c fa 1d 74"
40 #define ETH_2 "00 1a b8 93 f6 71"
41 #define IP_1 "0a 01 01 17"
42 #define IP_2 "0a 01 01 ea"
43 #define PORT_1 "01 00"
44 #define PORT_2 "10 00"
50 char *port_1
= PORT_1
;
51 char *port_2
= PORT_2
;
67 * The array of which segments should be dropped ...
69 seg_drop_t
*drops
= NULL
;
70 int seg_drop_count
= 0;
71 /* The array of which ACKs should be dropped. This is complicated because
72 * An ack might not be generated for a segment because of delayed ACKs.
74 seg_drop_t
*ack_drops
= NULL
;
75 int ack_drop_count
= 0;
77 int total_bytes
= 32768;
84 int send_spacing
= 10;
87 int tcp_delay_time
= 1000; /* What is the real time here? */
89 * If tcp_nodelay is set, then this is the amount of data left ...
91 int remaining_data
= 0;
99 #define SEG_ACK_LOST 1
100 #define SEG_SEG_LOST 2
103 int seq_num
; /* First sequence number in segment */
104 int len
; /* Number of bytes in segment */
105 int ts
; /* Timestamp when sent */
106 int seg_num
; /* Segment number sent. This can change */
107 /* but a retransmit will have a new seg */
108 int flags
; /* Flags as above for ack and seg loss */
109 int acks_first_seq
; /* How many times we have seen an ack
110 for the first seq number in this seg */
113 #define SEG_HIST_SIZE 128
114 struct seg_hist_s seg_hist
[128]; /* This should be dynamic */
118 int delayed_ack
= 1; /* Default is delayed ACKs in use ... */
119 int delayed_ack_wait
= 30000; /* 30 mS before an ACK is generated if */
120 /* no other traffic */
123 makeseg(char *eth1
, char *eth2
, char *ip1
, char *ip2
, char *p1
, char *p2
, int *s1
, int *s2
, char *flags
, int len
)
127 printf("2002/01/07 00:00:%02d.%06d\n", ts
/1000000, ts
%1000000);
128 printf("0000 %s %s 08 00\n", eth1
, eth2
);
129 printf("000e 45 00 %02x %02x 00 00 00 00 40 06 00 00 %s %s\n", (len
+40)>>8, (len
+40)&0xff, ip1
, ip2
);
130 printf("0022 %s %s %02x %02x %02x %02x %02x %02x %02x %02x 50 %s 80 00 00 00 00 00", p1
, p2
,
140 for(i
=0;i
<(len
<(snap_len
-40)?len
:snap_len
-40);i
++)printf(" 00");
147 * Figure out when the next ack is due ... here we must skip the acks for
148 * frames that are marked as ACKs dropped as well as the frames marked as
149 * frames dropped. These will be marked by the routine that generates ACKs.
150 * Returns a timestamp value. Returns 2^^31-1 if none are due at all
154 int slot
= next_slot
;
155 int ack_lost
= 0, seg_lost
= 0;
157 if (next_slot
== first_slot
)
158 return (((unsigned int)(1<<31)) - 1);
161 * Figure out if we need to issue an ACK. We skip all outstanding packets
162 * that are marked as ack lost or packet lost.
164 * We would not usually come in here with a frame marked as lost or ack lost
165 * rather, we will come in here and specify that the ack was due at a
166 * certain time, and gen_next_ack would then determine that the ack
167 * should be lost or the packet lost.
171 * Look for a seg slot that is not lost or dropped
174 while (seg_hist
[slot
].flags
& (SEG_ACK_LOST
|| SEG_SEG_LOST
)) {
175 if (seg_hist
[slot
].flags
& SEG_ACK_LOST
)
177 if (seg_hist
[slot
].flags
& SEG_SEG_LOST
)
179 slot
= (slot
+ 1) % SEG_HIST_SIZE
;
182 if (slot
== next_slot
)
183 return (((unsigned int)(1<<31)) - 1);
186 * If there is only one slot occupied, or a segment was lost then
187 * an ACK is due after the last [good] segment left plus ack_delay
190 if (slot
== first_slot
&& next_slot
== ((first_slot
+ 1) % SEG_HIST_SIZE
))
191 return (seg_hist
[first_slot
].ts
+ ack_delay
+ jitter
);
194 return (seg_hist
[slot
].ts
+ ack_delay
+ jitter
);
197 * OK, now, either we have only seen lost acks, or there are more than
198 * one outstanding segments, so figure out when the ACK is due.
200 * If delayed ACK is in force, ACK is due after every second seg, but
201 * if we had a lost ack, then we must ignore 2*lost_ack segments. So,
202 * if there has not been that many segments sent, we return infinity
203 * as the next ACK time
208 if (((first_slot
+ 1 + 2 * ack_lost
) % SEG_HIST_SIZE
) >= next_slot
)
209 /* XXX: FIXME, what about when the window is closed */
210 /* XXX: FIXME, use the correct value for this */
211 return (((unsigned int)(1<<31)) - 1);
213 return seg_hist
[(first_slot
+ 1 + 2 * ack_lost
) % SEG_HIST_SIZE
].ts
+
217 return seg_hist
[slot
].ts
+ ack_delay
+ jitter
;
221 return (seg_hist
[(first_slot
+ 1)%SEG_HIST_SIZE
].ts
+ack_delay
+jitter
);
223 return (seg_hist
[first_slot
].ts
+ack_delay
+jitter
);
228 * Update the relevant info of the sent seg
230 add_seg_sent(int seq
, int len
)
234 * Should check we have not wrapped around and run into the unacked
237 /*if (next_slot == first_slot) ;*/
240 seg_hist
[next_slot
].seq_num
= seq
;
241 seg_hist
[next_slot
].len
= len
;
242 seg_hist
[next_slot
].ts
= ts
;
243 seg_hist
[next_slot
].seg_num
= segment
;
244 seg_hist
[next_slot
].flags
= 0;
245 seg_hist
[next_slot
].acks_first_seq
= 0;
246 used_win
= used_win
+ len
; /* Update the window used */
249 * Now, update next_slot ...
252 next_slot
= (next_slot
+ 1) % SEG_HIST_SIZE
;
257 * Generate the next ack based on the above reasoning ...
260 #define NO_FORCE_ACK 0
264 * Generate the next ACK. If we did not generate an ACK, return 0,
268 gen_next_ack(int force
, int spacing
)
270 int seq_to_ack
, new_ts
, data_acked
;
273 * We need to check if the segment that we are about to generate an
274 * ack for is a segment that should be dropped ... or an ack that should
277 * Figure out what we are doing before freeing segments ...
280 seq_to_ack
= seg_hist
[first_slot
].seq_num
+ seg_hist
[first_slot
].len
;
281 used_win
= used_win
- seg_hist
[first_slot
].len
;
282 data_acked
= seg_hist
[first_slot
].len
;
283 new_ts
= seg_hist
[first_slot
].ts
+ ack_delay
;
284 first_slot
= (first_slot
+ 1) % SEG_HIST_SIZE
;
287 * If delayed ACK in force, then ACK the next segment if there is one
289 if (delayed_ack
&& (first_slot
!= next_slot
)) {
290 seq_to_ack
+= seg_hist
[first_slot
].len
;
291 used_win
= used_win
- seg_hist
[first_slot
].len
;
292 data_acked
+= seg_hist
[first_slot
].len
;
293 new_ts
= seg_hist
[first_slot
].ts
+ ack_delay
;
294 first_slot
= (first_slot
+ 1) % SEG_HIST_SIZE
;
298 * We don't want time to go backward ...
300 if (new_ts
+ jitter
<= ts
)
303 ts
= new_ts
+ jitter
;
305 jitter
= (rand() % 10 - 5); /* Update jitter ... */
307 makeseg(eth_2
, eth_1
, ip_2
, ip_1
, port_2
, port_1
, &seq_2
, &seq_to_ack
, "10", 0);
309 * Do we want the exponential part or the linear part?
311 if (cwnd
>= ssthresh
)
312 cwnd
+= (1460*data_acked
)/cwnd
; /* is this right? */
314 cwnd
= cwnd
+ data_acked
;
315 if (verbose
) fprintf(stderr
, "Ack rcvd. ts: %d, data_acked: %d, cwnd: %d, window: %d\n",
316 ts
, data_acked
, cwnd
, window
);
317 if (cwnd
> window
) cwnd
= window
;
321 makeackedrun(int len
, int spacing
, int ackdelay
)
324 if (verbose
) fprintf(stderr
, "makeackedrun: Len=%d, spacing=%d, ackdelay=%d\n",
325 len
, spacing
, ackdelay
);
329 * Each time we output a segment, we should check to see if an
330 * ack is due back before the next segment is due ...
332 int seglen
, saved_seq
;
333 seglen
=(len
>1460)?1460:len
;
335 * Only output what is left in the cwnd.
336 * We assume there is space in the congestion window here
338 if (seglen
> (cwnd
- used_win
)) seglen
= cwnd
- used_win
;
342 if (verbose
) fprintf(stderr
, "Sending segment. ts: %d, jitter: %d\n", ts
, jitter
);
344 makeseg(eth_1
, eth_2
, ip_1
, ip_2
, port_1
, port_2
, &seq_1
, &seq_2
, "10", seglen
);
346 makeseg(eth_1
, eth_2
, ip_1
, ip_2
, port_1
, port_2
, &seq_1
, &seq_2
, "18", seglen
);
348 add_seg_sent(saved_seq
, seglen
);
351 * Now, if the window is closed, then we have to eject an
352 * ack, otherwise we can eject more data.
353 * Also, the other end will tend to ack two segments at
354 * a time ... and that ack might fall between two
357 jitter
= (rand()%10) - 5; /* What if spacing too small */
359 if (verbose
) fprintf(stderr
, "used win: %d, cwnd: %d\n", used_win
, cwnd
);
361 if ((next_ack_ts
= next_ack_due()) < ts
+ spacing
+ jitter
) {
365 * Generate the ack and retire the segments
366 * If delayed ACK in use, there should be two
367 * or more outstanding segments ...
369 if (verbose
) fprintf(stderr
, "Non forced ACK ...ts + spacing + jitter:%d, jitter: %d\n", ts
+ spacing
+ jitter
, jitter
);
370 gen_next_ack(NO_FORCE_ACK
, spacing
);
372 * We don't want time to go backwards ...
374 if (old_ts
+ spacing
+ jitter
<= ts
)
377 ts
= old_ts
+ spacing
+ jitter
;
379 } else if (used_win
== cwnd
) {
382 * We need an ACK, so generate it and retire the
383 * segments and advance the ts to the time of the ack
386 if (verbose
) fprintf(stderr
, "Forced ACK ... \n");
387 gen_next_ack(FORCE_ACK
, spacing
);
389 ts
+=(spacing
+jitter
); /* Should not use spacing here */
393 ts
+=(spacing
+jitter
);
396 if (verbose
) fprintf(stderr
, "Next Ack Due: %d\n", next_ack_ts
);
403 makeackedrundroppedtail8kb(int len
, int spacing
, int ackdelay
)
409 if (verbose
) fprintf(stderr
, "makeackedrundroppedtail8kB: Len=%d, spacing=%d, ackdelay=%d\n",
410 len
, spacing
, ackdelay
);
414 seglen
=(len
>1460)?1460:len
;
417 makeseg(eth_1
, eth_2
, ip_1
, ip_2
, port_1
, port_2
, &seq_1
, &seq_2
, "10", seglen
);
419 makeseg(eth_1
, eth_2
, ip_1
, ip_2
, port_1
, port_2
, &seq_1
, &seq_2
, "18", seglen
);
429 while(old_seq1
!=seq_1
){
432 ack_len
=((seq_1
-old_seq1
)>2920)?2920:(seq_1
-old_seq1
);
436 dropped_tail
=old_seq1
;
440 makeseg(eth_2
, eth_1
, ip_2
, ip_1
, port_2
, port_1
, &seq_2
, &old_seq1
, "10", 0);
442 makeseg(eth_2
, eth_1
, ip_2
, ip_1
, port_2
, port_1
, &seq_2
, &dropped_tail
, "10", 0);
445 makeseg(eth_2
, eth_1
, ip_2
, ip_1
, port_2
, port_1
, &seq_2
, &dropped_tail
, "10", 0);
446 makeseg(eth_2
, eth_1
, ip_2
, ip_1
, port_2
, port_1
, &seq_2
, &dropped_tail
, "10", 0);
459 seglen
=((seq_1
-dropped_tail
)>1460)?1460:(seq_1
-dropped_tail
);
461 makeseg(eth_1
, eth_2
, ip_1
, ip_2
, port_1
, port_2
, &dropped_tail
, &seq_2
, "10", seglen
);
463 makeseg(eth_1
, eth_2
, ip_1
, ip_2
, port_1
, port_2
, &dropped_tail
, &seq_2
, "18", seglen
);
467 makeseg(eth_2
, eth_1
, ip_2
, ip_1
, port_2
, port_1
, &seq_2
, &seq_1
, "10", 0);
472 while(dropped_tail
!=seq_1
){
475 seglen
=((seq_1
-dropped_tail
)>1460)?1460:(seq_1
-dropped_tail
);
477 makeseg(eth_1
, eth_2
, ip_1
, ip_2
, port_1
, port_2
, &dropped_tail
, &seq_2
, "10", seglen
);
479 makeseg(eth_1
, eth_2
, ip_1
, ip_2
, port_1
, port_2
, &dropped_tail
, &seq_2
, "18", seglen
);
484 makeseg(eth_2
, eth_1
, ip_2
, ip_1
, port_2
, port_1
, &seq_2
, &ack
, "10", 0);
491 fprintf(stderr
, "Usage: mkcap [OPTIONS], where\n");
492 fprintf(stderr
, "\t-a <ack-delay> is the delay to an ACK (SRT)\n");
493 fprintf(stderr
, "\t-b <bytes-to-send> is the bytes to send on connection\n");
494 fprintf(stderr
, "\t-i <ip-addr-hex> is the sender IP address in hex\n");
495 fprintf(stderr
, "\t-I <ip-addr-hex> is the recipient IP address in hex\n");
496 fprintf(stderr
, "\t-n <ISN> is almost the ISN for the sender\n");
497 fprintf(stderr
, "\t-N <ISN> is almost the ISN for the recipient\n");
498 fprintf(stderr
, "\t-p <port-number-hex> is the port number for sender\n");
499 fprintf(stderr
, "\t-P <port-number-hex> is the port number for recipient\n");
500 fprintf(stderr
, "\t-s <send-spacing> is the send spacing\n");
501 fprintf(stderr
, "\t-w <window-size> is the window size\n");
505 all_digits(char *str
)
508 if (!str
|| !(*str
)) {
512 for (i
= 0; str
[i
]; i
++) {
513 if (!isdigit(str
[i
]))
521 * Process a list of drops. These are of the form:
523 * first_seg,seg_count[,first_seg,seg_count]*
526 process_drop_list(char *drop_list
)
531 if (!drop_list
|| !(*drop_list
)) {
532 fprintf(stderr
, "Strange drop list. NULL or an empty string. No drops!\n");
535 save
= (char *)g_strdup(drop_list
);
537 for (tok
=(char *)strtok(drop_list
, ","); tok
; tok
=(char *)strtok(NULL
, ",")) {
541 /* Now, we have commas, divide by two and round up */
543 seg_drop_count
= (commas
+1)/2;
544 drops
= (seg_drop_t
*)g_malloc(sizeof(seg_drop_t
) * seg_drop_count
);
546 fprintf(stderr
, "Unable to allocate space for drops ... going without!\n");
552 /* Now, go through the list again and build the drop list. Any errors and */
553 /* we abort and print a usage message */
556 for (tok
=(char *)strtok(save
, ","); tok
; tok
=(char *)strtok(NULL
, ",")) {
559 if (!all_digits(tok
)) {
560 fprintf(stderr
, "Error in segment offset or count. Not all digits: %s\n",
562 fprintf(stderr
, "No packet drops being performed!\n");
565 seg_drop_count
= 0; drops
= NULL
;
568 if (num
== 0) num
= 1;
570 drops
[commas
/ 2].drop_seg_count
= num
;
572 drops
[commas
/ 2].drop_seg_start
= num
;
580 main(int argc
, char *argv
[])
590 while ((opt
= getopt(argc
, argv
, "a:b:d:Di:I:j:l:n:N:p:P:r:s:vw:")) != EOF
) {
593 ack_delay
= atoi(optarg
);
596 case 'b': /* Bytes ... */
597 total_bytes
= atoi(optarg
);
600 case 'd': /* A list of drops to simulate */
601 process_drop_list(optarg
);
604 case 'D': /* Toggle tcp_nodelay */
605 tcp_nodelay
= (tcp_nodelay
+ 1) % 1;
617 snap_len
= atoi(optarg
);
620 case 'n': /* ISN for send dirn, ie, seq_1 */
621 seq_1
= atoi(optarg
);
624 case 'N': /* ISN for recv dirn, ie, seq_2 */
625 seq_2
= atoi(optarg
);
637 run_type
= atoi(optarg
);
641 send_spacing
= atoi(optarg
);
648 case 'w': /* Window ... */
649 window
= atoi(optarg
);
650 ssthresh
= window
/ 2; /* Have to recalc this ... */
659 if (verbose
) fprintf(stderr
, "IP1: %s, IP2: %s, P1: %s, P2: %s, Ack Delay: %d, Send Spacing: %d\n",
660 ip_1
, ip_2
, port_1
, port_2
, ack_delay
, send_spacing
);
665 makeackedrun(total_bytes
, send_spacing
, ack_delay
);
668 for(cnt
=0;cnt
<200;cnt
++){
674 makeackedrun(32768, send_spacing
, ack_delay
);
680 len
=100+rand()&0xfff;
681 makeackedrun(len
, send_spacing
, ack_delay
);
685 len
=100+rand()&0x3fff+0x1fff;
686 makeackedrun(len
, send_spacing
, ack_delay
);
687 /*makeackedrundroppedtail8kb(len, send_spacing, ack_delay);*/