Witness: WIP
[wireshark-wip.git] / mkcap.c
blob5f9515971a1269598f1e69cad06135c86bd2e202
1 /* mkcap.c
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.
5 * $Id$
7 * By Ronnie Sahlberg and Richard Sharpe. From a program initially
8 * written by Ronnie.
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
28 * For example:
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
35 #include <stdio.h>
36 #include <stdlib.h>
37 #include <unistd.h>
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"
46 char *eth_1 = ETH_1;
47 char *eth_2 = ETH_2;
48 char *ip_1 = IP_1;
49 char *ip_2 = IP_2;
50 char *port_1 = PORT_1;
51 char *port_2 = PORT_2;
53 int verbose = 0;
55 typedef enum {
56 normal = 0,
57 random_ack_drop = 1,
58 random_data_drop = 2,
59 } run_type_t;
61 typedef struct {
62 int drop_seg_start;
63 int drop_seg_count;
64 } seg_drop_t;
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;
78 int run_type = 0;
80 int seq_2=0;
81 int seq_1=0;
82 int ts=0;
83 int jitter = 0;
84 int send_spacing = 10;
85 int ack_delay = 5000;
86 int tcp_nodelay = 0;
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;
92 int snap_len = 1500;
93 int window = 32768;
94 int ssthresh = 16384;
95 int cwnd = 1460;
96 int used_win = 0;
97 int segment = 0;
99 #define SEG_ACK_LOST 1
100 #define SEG_SEG_LOST 2
102 struct seg_hist_s {
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 */
115 int next_slot = 0;
116 int first_slot = 0;
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 */
122 void
123 makeseg(char *eth1, char *eth2, char *ip1, char *ip2, char *p1, char *p2, int *s1, int *s2, char *flags, int len)
125 int i;
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,
131 ((*s1)>>24)&0xff,
132 ((*s1)>>16)&0xff,
133 ((*s1)>>8)&0xff,
134 ((*s1))&0xff,
135 ((*s2)>>24)&0xff,
136 ((*s2)>>16)&0xff,
137 ((*s2)>>8)&0xff,
138 ((*s2))&0xff,
139 flags );
140 for(i=0;i<(len<(snap_len-40)?len:snap_len-40);i++)printf(" 00");
141 printf("\n");
142 printf("\n");
143 (*s1)+=len;
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
152 int next_ack_due()
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)
176 ack_lost++;
177 if (seg_hist[slot].flags & SEG_SEG_LOST)
178 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);
193 if (seg_lost)
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
206 if (ack_lost) {
207 if (delayed_ack) {
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);
212 else
213 return seg_hist[(first_slot + 1 + 2 * ack_lost) % SEG_HIST_SIZE].ts +
214 ack_delay + jitter;
216 else
217 return seg_hist[slot].ts + ack_delay + jitter;
219 else {
220 if (delayed_ack)
221 return (seg_hist[(first_slot + 1)%SEG_HIST_SIZE].ts+ack_delay+jitter);
222 else
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
235 * stuff ...
237 /*if (next_slot == first_slot) ;*/
239 segment++;
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
261 #define FORCE_ACK 1
264 * Generate the next ACK. If we did not generate an ACK, return 0,
265 * else return 1.
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
275 * be dropped.
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)
301 ts++;
302 else
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? */
313 else
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;
320 void
321 makeackedrun(int len, int spacing, int ackdelay)
323 int next_ack_ts=0;
324 if (verbose) fprintf(stderr, "makeackedrun: Len=%d, spacing=%d, ackdelay=%d\n",
325 len, spacing, ackdelay);
326 while(len>0){
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;
340 len-=seglen;
341 saved_seq = seq_1;
342 if (verbose) fprintf(stderr, "Sending segment. ts: %d, jitter: %d\n", ts, jitter);
343 if(len){
344 makeseg(eth_1, eth_2, ip_1, ip_2, port_1, port_2, &seq_1, &seq_2, "10", seglen);
345 } else {
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
355 * outgoing segments
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) {
362 int old_ts = ts;
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)
375 ts++;
376 else
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 */
392 else {
393 ts+=(spacing+jitter);
396 if (verbose) fprintf(stderr, "Next Ack Due: %d\n", next_ack_ts);
402 void
403 makeackedrundroppedtail8kb(int len, int spacing, int ackdelay)
405 int old_seq1;
406 int dropped_tail;
407 int i;
408 int num_dupes;
409 if (verbose) fprintf(stderr, "makeackedrundroppedtail8kB: Len=%d, spacing=%d, ackdelay=%d\n",
410 len, spacing, ackdelay);
411 old_seq1=seq_1;
412 while(len>0){
413 int seglen;
414 seglen=(len>1460)?1460:len;
415 len-=seglen;
416 if(seglen==1460){
417 makeseg(eth_1, eth_2, ip_1, ip_2, port_1, port_2, &seq_1, &seq_2, "10", seglen);
418 } else {
419 makeseg(eth_1, eth_2, ip_1, ip_2, port_1, port_2, &seq_1, &seq_2, "18", seglen);
421 ts+=spacing;
424 ts+=ackdelay;
426 i=0;
427 num_dupes=-1;
428 dropped_tail=0;
429 while(old_seq1!=seq_1){
430 int ack_len;
432 ack_len=((seq_1-old_seq1)>2920)?2920:(seq_1-old_seq1);
434 i++;
435 if(i==6){
436 dropped_tail=old_seq1;
438 old_seq1+=ack_len;
439 if(i<6){
440 makeseg(eth_2, eth_1, ip_2, ip_1, port_2, port_1, &seq_2, &old_seq1, "10", 0);
441 } else if (i==6) {
442 makeseg(eth_2, eth_1, ip_2, ip_1, port_2, port_1, &seq_2, &dropped_tail, "10", 0);
443 num_dupes+=2;
444 } else {
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);
447 num_dupes+=2;
449 ts+=spacing/2;
452 if(!dropped_tail){
453 return;
456 if(num_dupes<3){
457 int seglen;
458 ts+=1000000;
459 seglen=((seq_1-dropped_tail)>1460)?1460:(seq_1-dropped_tail);
460 if(seglen==1460){
461 makeseg(eth_1, eth_2, ip_1, ip_2, port_1, port_2, &dropped_tail, &seq_2, "10", seglen);
462 } else {
463 makeseg(eth_1, eth_2, ip_1, ip_2, port_1, port_2, &dropped_tail, &seq_2, "18", seglen);
465 ts+=ackdelay;
467 makeseg(eth_2, eth_1, ip_2, ip_1, port_2, port_1, &seq_2, &seq_1, "10", 0);
468 ts+=spacing;
469 return;
472 while(dropped_tail!=seq_1){
473 int seglen;
474 int ack;
475 seglen=((seq_1-dropped_tail)>1460)?1460:(seq_1-dropped_tail);
476 if(seglen==1460){
477 makeseg(eth_1, eth_2, ip_1, ip_2, port_1, port_2, &dropped_tail, &seq_2, "10", seglen);
478 } else {
479 makeseg(eth_1, eth_2, ip_1, ip_2, port_1, port_2, &dropped_tail, &seq_2, "18", seglen);
481 ts+=ackdelay;
483 ack=dropped_tail;
484 makeseg(eth_2, eth_1, ip_2, ip_1, port_2, port_1, &seq_2, &ack, "10", 0);
485 ts+=spacing;
489 void usage()
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)
507 int i;
508 if (!str || !(*str)) {
509 return 0;
512 for (i = 0; str[i]; i++) {
513 if (!isdigit(str[i]))
514 return 0;
517 return 1;
521 * Process a list of drops. These are of the form:
523 * first_seg,seg_count[,first_seg,seg_count]*
525 void
526 process_drop_list(char *drop_list)
528 int commas=0;
529 char *tok, *save;
531 if (!drop_list || !(*drop_list)) {
532 fprintf(stderr, "Strange drop list. NULL or an empty string. No drops!\n");
533 return;
535 save = (char *)g_strdup(drop_list);
537 for (tok=(char *)strtok(drop_list, ","); tok; tok=(char *)strtok(NULL, ",")) {
538 commas++;
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);
545 if (!drops) {
546 fprintf(stderr, "Unable to allocate space for drops ... going without!\n");
547 seg_drop_count = 0;
548 g_free(save);
549 return;
552 /* Now, go through the list again and build the drop list. Any errors and */
553 /* we abort and print a usage message */
555 commas = 0;
556 for (tok=(char *)strtok(save, ","); tok; tok=(char *)strtok(NULL, ",")) {
557 int num = atoi(tok);
559 if (!all_digits(tok)) {
560 fprintf(stderr, "Error in segment offset or count. Not all digits: %s\n",
561 tok);
562 fprintf(stderr, "No packet drops being performed!\n");
563 g_free(save);
564 g_free(drops);
565 seg_drop_count = 0; drops = NULL;
566 return;
568 if (num == 0) num = 1;
569 if (commas % 2)
570 drops[commas / 2].drop_seg_count = num;
571 else
572 drops[commas / 2].drop_seg_start = num;
575 g_free(save);
579 int
580 main(int argc, char *argv[])
582 int i;
583 int len;
584 int type;
585 int cnt;
586 extern char *optarg;
587 extern int optind;
588 int opt;
590 while ((opt = getopt(argc, argv, "a:b:d:Di:I:j:l:n:N:p:P:r:s:vw:")) != EOF) {
591 switch (opt) {
592 case 'a':
593 ack_delay = atoi(optarg);
594 break;
596 case 'b': /* Bytes ... */
597 total_bytes = atoi(optarg);
598 break;
600 case 'd': /* A list of drops to simulate */
601 process_drop_list(optarg);
602 break;
604 case 'D': /* Toggle tcp_nodelay */
605 tcp_nodelay = (tcp_nodelay + 1) % 1;
606 break;
608 case 'i':
609 ip_1 = optarg;
610 break;
612 case 'I':
613 ip_2 = optarg;
614 break;
616 case 'l':
617 snap_len = atoi(optarg);
618 break;
620 case 'n': /* ISN for send dirn, ie, seq_1 */
621 seq_1 = atoi(optarg);
622 break;
624 case 'N': /* ISN for recv dirn, ie, seq_2 */
625 seq_2 = atoi(optarg);
626 break;
628 case 'p':
629 port_1 = optarg;
630 break;
632 case 'P':
633 port_2 = optarg;
634 break;
636 case 'r':
637 run_type = atoi(optarg);
638 break;
640 case 's':
641 send_spacing = atoi(optarg);
642 break;
644 case 'v':
645 verbose++;
646 break;
648 case 'w': /* Window ... */
649 window = atoi(optarg);
650 ssthresh = window / 2; /* Have to recalc this ... */
651 break;
653 default:
654 usage();
655 break;
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);
662 /*return 0; */
664 if (run_type == 0) {
665 makeackedrun(total_bytes, send_spacing, ack_delay);
667 else {
668 for(cnt=0;cnt<200;cnt++){
669 type=rand()%150;
670 if(type<75){
671 int j;
672 j=5+rand()%10;
673 for(i=0;i<j;i++){
674 makeackedrun(32768, send_spacing, ack_delay);
676 } else if(type<90) {
677 int j;
678 j=4+rand()%4;
679 for(i=0;i<j;i++){
680 len=100+rand()&0xfff;
681 makeackedrun(len, send_spacing, ack_delay);
683 } else {
684 for(i=0;i<5;i++){
685 len=100+rand()&0x3fff+0x1fff;
686 makeackedrun(len, send_spacing, ack_delay);
687 /*makeackedrundroppedtail8kb(len, send_spacing, ack_delay);*/
692 return 0;