Watt-32 tcp/ip  2.2 dev-rel.10
ip4_frag.c
Go to the documentation of this file.
1 
5 /*
6  * Packet de-fragmentation code for WATTCP
7  * Written by and COPYRIGHT (c)1993 to Quentin Smart
8  * smart@actrix.gen.nz
9  * all rights reserved.
10  *
11  * This software is distributed in the hope that it will be useful,
12  * but without any warranty; without even the implied warranty of
13  * merchantability or fitness for a particular purpose.
14  *
15  * You may freely distribute this source code, but if distributed for
16  * financial gain then only executables derived from the source may be
17  * sold.
18  *
19  * Murf = Murf@perftech.com
20  * other fragfix = mdurkin@tsoft.net
21  *
22  * Based on RFC815
23  *
24  * Code used to use pktbuf[] as reassembly buffer. It now allocates
25  * a "bucket" dynamically at startup. There are 'MAX_IP_FRAGS' buckets
26  * to handle at the same time.
27  *
28  * G.Vanem 1998 <gvanem@yahoo.no>
29  */
30 
31 #include <stdio.h>
32 #include <stdlib.h>
33 #include <limits.h>
34 #include <arpa/inet.h>
35 
36 #include "wattcp.h"
37 #include "strings.h"
38 #include "language.h"
39 #include "misc.h"
40 #include "timer.h"
41 #include "chksum.h"
42 #include "pcconfig.h"
43 #include "pcqueue.h"
44 #include "pcstat.h"
45 #include "pcpkt.h"
46 #include "pcicmp.h"
47 #include "pctcp.h"
48 #include "pcdbug.h"
49 #include "netaddr.h"
50 #include "run.h"
51 #include "ip4_in.h"
52 #include "ip4_out.h"
53 #include "ip4_frag.h"
54 
55 #define MAX_IP_FRAGS 2 /* max # of fragmented IP-packets */
56 #define MAX_IP_HOLDTIME 15 /* time (in sec) to hold before discarding */
57 
58 int _ip4_frag_reasm = MAX_IP_HOLDTIME; /* configurable; pcconfig.c */
59 
60 #if defined(USE_FRAGMENTS)
61 
62 #undef TRACE_MSG
63 
64 #if defined(TEST_PROG)
65  #define TRACE_MSG(color, args) \
66  do { \
67  SET_ATTR (color); \
68  printf ("%s(%u): ", __FILE__, __LINE__); \
69  printf args ; \
70  NORM_TEXT(); \
71  } while (0)
72 
73 #else
74  #define TRACE_MSG(color, args) TCP_TRACE_MSG (args)
75 #endif
76 
77 /*@-nullderef@*/
78 
79 #if 0
80  #undef MAX_FRAGMENTS
81  #define MAX_FRAGMENTS 20U /* !! test */
82  #define MAX_FRAG_SIZE (MAX_FRAGMENTS * MAX_IP4_DATA) /* 29600 */
83 #endif
84 
85 #define BUCKET_MARKER 0xDEAFABBA
86 
87 /*
88  * Number of 'fd_set' required to hold MAX_SOCKETS.
89  */
90 #define NUM_FD_SETS ((MAX_FRAG_SIZE+sizeof(fd_set)-1) / sizeof(fd_set))
91 
92 typedef fd_set used_map [NUM_FD_SETS];
93 
94 struct huge_ip {
95  in_Header hdr;
96  BYTE data [MAX_FRAG_SIZE]; /* 66600 for DOSX */
97  DWORD marker;
98  };
99 
100 struct frag_key {
101  DWORD source;
102  DWORD destin;
103  WORD ident;
104  BYTE proto;
105  };
106 
107 struct frag_bucket {
108  BOOL used; /* this bucket is taken */
109  BOOL got_ofs0; /* we've received ofs-0 fragment */
110  int active; /* # of active fragments */
111  used_map map; /* map of bits received */
112  mac_address mac_src; /* remember for icmp_send_timexceed() */
113  struct frag_key key; /* key for matching new fragments */
114  struct huge_ip *ip; /* calloc'ed on startup */
115  };
116 
117 struct hole { /* structure for missing chunks */
118  struct hole *next;
119  long start;
120  long end;
121  };
122 
123 struct frag_ctrl {
124  BOOL used; /* this position in table is in use */
125  DWORD timer; /* expiry for this fragment */
126  struct hole *hole_first;
127  in_Header *ip;
128  BYTE *data_offset;
129  };
130 
131 static struct frag_ctrl frag_control [MAX_IP_FRAGS][MAX_FRAGMENTS];
132 static struct frag_bucket frag_buckets [MAX_IP_FRAGS];
133 
134 static long data_start; /* NB! global data; not reentrant */
135 static long data_end;
136 static long data_length;
137 
138 enum frag_bits {
139  IS_FRAG = 0x01,
140  IS_LAST = 0x02,
141  IS_FIRST = 0x04
142  };
143 
144 /*
145  frag_bucket[]: Holds the fragments with offsets (0-MAX_FRAG_SIZE)
146  ------------------------------------------------------
147  0: | struct huge_ip |
148  ------------------------------------------------------
149  . .
150  . .
151  ------------------------------------------------------
152  MAX_IP_FRAGS-1: | |
153  ------------------------------------------------------
154  ^ ^
155  | |
156  0 MAX_FRAG_SIZE
157 
158  */
159 
160 static enum frag_bits fbits; /* IPv4 fragment bits */
161 
162 #if defined(TEST_PROG)
163 static void dump_frag_holes (const char *where, const struct frag_ctrl *fc, const struct hole *hole)
164 {
165  const struct hole *h;
166 
167  if (!fc)
168  {
169  TRACE_MSG (6, ("%s, No frag!\n", where));
170  return;
171  }
172 
173  TRACE_MSG (15, ("%s: fc->used: %d, fc->data_offset: %" ADDR_FMT "\n",
174  where, fc->used, fc->data_offset));
175 
176  for (h = hole; h; h = h->next)
177  {
178  TRACE_MSG (15, ("hole: %" ADDR_FMT ", h->start: %d, h->end: %d\n",
179  h, h->start, h->end));
180  }
181 }
182 #endif
183 
184 #if defined(USE_DEBUG)
185 static const char *decode_fbits (int fb)
186 {
187  static char buf[50];
188  char *end;
189 
190  buf[0] = '\0';
191  if (fb & IS_FRAG)
192  strcat (buf, "IS_FRAG+");
193 
194  if (fb & IS_LAST)
195  strcat (buf, "IS_LAST+");
196 
197  if (fb & IS_FIRST)
198  strcat (buf, "IS_FIRST+");
199 
200  end = strrchr (buf, '+');
201  if (end)
202  *end = '\0';
203  return (buf);
204 }
205 #endif
206 
207 
208 /*
209  * Check if this packet is part of a fragment-chain. It might be
210  * a last fragment (MF=0) with ofs 0. This would be normal for
211  * fragments sent from Linux. Look in 'frag_buckets' for a match.
212  *
213  * If the 'fk' was not found, set '*free_bucket' to next free bucket
214  * and return FALSE.
215  *
216  * If found, set '*bucket' to matching bucket and '*slot' to free
217  * slot in '*bucket' and return TRUE.
218  */
219 static BOOL match_frag (const in_Header *ip, int *slot, int *bucket, int *free_bucket)
220 {
221  struct frag_key fk;
222  int i, b;
223 
224  *bucket = -1;
225  *slot = -1;
226  *free_bucket = -1;
227 
228  fk.source = ip->source;
229  fk.destin = ip->destination;
230  fk.ident = ip->identification;
231  fk.proto = ip->proto;
232 
233  for (b = 0; b < DIM(frag_buckets); b++)
234  {
235  const struct frag_bucket *fb = frag_buckets + b;
236  const struct frag_ctrl *fc = &frag_control[b][0];
237 
238  if (!fb->used)
239  {
240  if (*free_bucket == -1) /* get 1st vacant bucket */
241  *free_bucket = b;
242  continue;
243  }
244 
245  if (memcmp(&fk,&fb->key,sizeof(fk)))
246  continue;
247 
248  for (i = 0; i < (int)MAX_FRAGMENTS; i++, fc++)
249  {
250  if (!fc->used)
251  {
252  *bucket = b;
253  return (TRUE);
254  }
255  *slot = i; /* This frag-control is used. Return it's index */
256  }
257  }
258  return (FALSE);
259 }
260 
261 /*
262  * Check and report if fragment data 'ofs' and 'end' are okay
263  */
264 static __inline BOOL check_frag_ofs (const in_Header *ip, DWORD ofs, DWORD end)
265 {
266  const struct huge_ip hip;
267 
268  if (ofs + end <= sizeof(hip.data) && /* fragment offset okay, < 65528 */
269  ofs + end <= USHRT_MAX-8) /* must not wrap around 64kB */
270  return (TRUE);
271 
272  TCP_CONSOLE_MSG (2, (_LANG("Bad frag-ofs: %lu, frag-end: %lu, ip-prot %u (%s -> %s)\n"),
273  ofs, end, ip->proto,
274  _inet_ntoa(NULL,intel(ip->source)),
275  _inet_ntoa(NULL,intel(ip->destination))));
276  ARGSUSED (ip);
277  return (FALSE);
278 }
279 
280 static __inline void set_fbits (DWORD offset, WORD flags)
281 {
282  fbits = 0;
283 
284  if (flags & IP_MF)
285  {
286  fbits |= IS_FRAG;
287  if (offset == 0)
288  fbits |= IS_FIRST;
289  }
290  else if (offset)
291  fbits = (IS_FRAG | IS_LAST);
292 }
293 
294 /*
295  * Prepare and setup for reassembly
296  */
297 static BOOL setup_first_frag (const in_Header *ip, int idx)
298 {
299  struct frag_ctrl *fc;
300  struct frag_bucket *fb = frag_buckets + idx; /* do fragment hanlding in this bucket */
301  struct huge_ip *hip = fb->ip;
302  struct hole *hole;
303  unsigned i;
304 
305  /* Marker destroyed!
306  */
307  if (hip->marker != BUCKET_MARKER)
308  TCP_CONSOLE_MSG (0, ("frag_buckets[%d] destroyed\n!", idx));
309 
310  TRACE_MSG (7, ("frag_bucket[%d] = %" ADDR_FMT "\n", idx, ADDR_CAST(hip)));
311 
312  /* Remember MAC source address
313  */
314  if (_pktserial)
315  memset (&fb->mac_src, 0, sizeof(mac_address));
316  else memcpy (&fb->mac_src, MAC_SRC(ip), sizeof(mac_address));
317 
318  WATT_ASSERT (fb->used == 0);
319 
320  fb->used = TRUE;
321  fb->got_ofs0 = FALSE;
322 
323  /* Find first empty slot
324  */
325  fc = &frag_control[idx][0];
326  for (i = 0; i < MAX_FRAGMENTS; i++, fc++)
327  if (!fc->used)
328  break;
329 
330  if (i == MAX_FRAGMENTS)
331  return (FALSE);
332 
333  fc->used = TRUE; /* mark as used */
334  fb->active++; /* inc active frags counter */
335 
336  WATT_ASSERT (fb->active == 1);
337  TRACE_MSG (7, ("bucket=%d, active=%u, i=%u\n", idx, fb->active, i));
338 
339  /* Setup frag header data, first packet
340  */
341  fb->key.proto = ip->proto;
342  fb->key.source = ip->source;
343  fb->key.destin = ip->destination;
344  fb->key.ident = ip->identification;
345 
346  fc->ip = &fb->ip->hdr;
347  fc->timer = set_timeout (1000 * min(_ip4_frag_reasm, ip->ttl));
348 
349  /* Set pointers to beginning of IP packet data
350  */
351  fc->data_offset = (BYTE*)fc->ip + in_GetHdrLen(ip);
352 
353  /* Setup initial hole-list.
354  */
355  if (data_start == 0) /* 1st fragment sent is 1st fragment received */
356  {
357  WORD ip_len = intel16 (ip->length);
358  BYTE *dst = (BYTE*) fc->ip;
359 
360  ip_len = min (ip_len, _mtu);
361  memcpy (dst, ip, ip_len);
362  hole = (struct hole*) (dst + ip_len + 1);
363  fc->hole_first = hole;
364  fb->got_ofs0 = TRUE;
365  }
366  else
367  {
368  /* !!fix-me: assumes header length of this fragment is same as
369  * in reassembled IP packet (may have IP-options)
370  */
371  BYTE *src = (BYTE*)ip + in_GetHdrLen(ip);
372  BYTE *dst = fc->data_offset + data_start;
373 
374  memcpy (dst, src, (size_t)data_length);
375 
376  /* Bracket beginning of data
377  */
378  hole = fc->hole_first = (struct hole*)fc->data_offset;
379  hole->start = 0;
380  hole->end = data_start - 1;
381 
382  if (!(fbits & IS_LAST))
383  {
384  hole->next = (struct hole*) (fc->data_offset + data_length + 1);
385  hole = hole->next;
386  }
387  else
388  {
389  hole = fc->hole_first->next = NULL;
390  /* Adjust length */
391  fc->ip->length = intel16 ((WORD)(data_end + in_GetHdrLen(ip)));
392  }
393  }
394 
395  if (hole)
396  {
397  hole->start = data_length;
398  hole->end = MAX_FRAG_SIZE;
399  hole->next = NULL;
400 
401  TRACE_MSG (7, ("hole %" ADDR_FMT ", hole->start %lu, hole->end %lu\n",
402  ADDR_CAST(hole), hole->start, hole->end));
403  }
404 
405 #if defined(TEST_PROG)
406  dump_frag_holes ("setup_first_frag", fc, hole);
407 #endif
408 
409  return (TRUE);
410 }
411 
412 /*
413  * ip4_defragment() is called if '*ip_ptr' is part of a new or existing
414  * fragment chain.
415  *
416  * IP header already checked in _ip4_handler().
417  * Return the reassembled segment (ICMP, UDP or TCP) in '*ip_ptr'.
418  * We assume MAC-header is the same on all fragments.
419  * We return a packet with the MAC-header of the first
420  * fragment received.
421  */
422 int ip4_defragment (const in_Header **ip_ptr, DWORD offset, WORD flags)
423 {
424  struct frag_ctrl *fc = NULL;
425  struct frag_bucket *fb = NULL;
426  struct hole *hole = NULL;
427  struct hole *prev_hole = NULL;
428  const in_Header *ip = *ip_ptr;
429 
430  BOOL found = FALSE;
431  BOOL got_hole = FALSE;
432  int slot, bucket, free_bucket;
433 
434  /* Not a fragment (or part of a chain) if offset is 0 and !IP_MF.
435  */
436  if (offset == 0 && !(flags & IP_MF))
437  return (1);
438 
439  set_fbits (offset, flags);
440 
441  /* Check if part of an existing frag-chain or a new fragment
442  */
443  if (!match_frag(ip,&slot,&bucket,&free_bucket))
444  {
445  if (slot == MAX_FRAGMENTS-1) /* found but no slots free */
446  {
447  STAT (ip4stats.ips_fragments++);
448  goto drop_frag;
449  }
450 
451 #if 0
452  if (!(fbits & IS_FRAG)) /* A normal non-fragmented IP-packet */
453  return (1);
454 #endif
455 
456  /* Not in frag-list, but ofs > 0 or MF set. Must be a part of
457  * a (possibly new) fragment chain.
458  */
459  }
460 
461  STAT (ip4stats.ips_fragments++);
462 
463  /* Calculate where data should go
464  */
465  data_start = (long) offset;
466  data_length = (long) intel16 (ip->length) - in_GetHdrLen (ip);
467  data_end = data_start + data_length;
468 
469  TRACE_MSG (7, ("\nip4_defrag: %s -> %s, id 0x%04X, len %ld, ofs %ld, fbits %s\n",
470  _inet_ntoa(NULL,intel(ip->source)),
471  _inet_ntoa(NULL,intel(ip->destination)),
472  intel16(ip->identification), data_length, data_start,
473  decode_fbits(fbits)));
474 
475  if ((flags & IP_MF) && data_length == 0 && offset != 0)
476  {
477  TRACE_MSG (7, ("No data.\n"));
478  goto drop_frag;
479  }
480 
481  if ((flags & IP_MF) && (data_length & 7) != 0)
482  {
483  TRACE_MSG (7, ("Frag-data not multiple of 8.\n"));
484  goto drop_frag;
485  }
486 
487  if (!check_frag_ofs(ip,data_start,data_end))
488  goto drop_frag;
489 
490  found = (bucket > -1);
491 
492  if (!found)
493  {
494  TRACE_MSG (7, ("bucket=%d, i=%d, key not found\n", free_bucket, slot));
495  bucket = free_bucket;
496  fb = frag_buckets + bucket;
497 
498  /* Can't handle any new frags, drop packet
499  */
500  if (bucket == -1 || fb->active == MAX_FRAGMENTS)
501  goto drop_frag;
502 
503  if (!setup_first_frag (ip, bucket)) /* Setup first fragment received */
504  goto drop_frag;
505 
506  /* Okay so far. Wait for next IP and continue defragmentation.
507  */
508  DEBUG_RX (NULL, ip);
509  return (0);
510  }
511 
512  fc = &frag_control [bucket][slot];
513  fb = frag_buckets + bucket;
514 
515  TRACE_MSG (7, ("bucket=%d, slot=%d key found, fbits: %s, active=%d\n",
516  bucket, slot, decode_fbits(fbits), fb->active));
517 
518  if (fbits & IS_LAST) /* Adjust length */
519  fc->ip->length = intel16 ((WORD)(data_end + in_GetHdrLen(ip)));
520 
521  if (offset == 0)
522  fb->got_ofs0 = TRUE;
523 
524  hole = fc->hole_first; /* Hole handling */
525 
526  do
527  {
528 #if defined(TEST_PROG)
529  dump_frag_holes ("ip4_defragment(1)", fc, hole);
530 #endif
531 
532  if (hole && (data_start <= hole->end) && /* We've found the spot */
533  (data_end >= hole->start))
534  {
535  long last_end = hole->end; /* Pick up old hole end for later */
536 
537  got_hole = TRUE;
538 
539  /* Find where to insert fragment.
540  * Check if there's a hole before the new frag
541  */
542  if (data_start > hole->start)
543  {
544  hole->end = data_start - 1;
545  prev_hole = hole; /* We have a new prev */
546  }
547  else
548  {
549  /* No, delete current hole
550  */
551  if (!prev_hole)
552  fc->hole_first = hole->next;
553  else prev_hole->next = hole->next;
554  }
555 
556  /* Is there a hole after the current fragment?
557  * Only if we're not last and more to come
558  */
559  if (data_end < hole->end)
560  {
561  hole = (struct hole*) (data_end + 1 + fc->data_offset);
562  hole->start = data_end + 1;
563  hole->end = last_end;
564 
565  /* prev_hole = NULL if first
566  */
567  if (!prev_hole)
568  {
569  hole->next = fc->hole_first;
570  fc->hole_first = hole;
571  }
572  else
573  {
574  hole->next = prev_hole->next;
575  prev_hole->next = hole;
576  }
577  }
578  }
579  prev_hole = hole;
580  hole = hole->next;
581 
582 #if defined(TEST_PROG)
583  dump_frag_holes ("ip4_defragment(2)", fc, hole);
584 #endif
585  }
586  while (hole); /* Until we got to the end or found */
587 
588 
589  /* Thats all setup so copy in the data
590  */
591  if (got_hole)
592  memcpy (fc->data_offset + data_start,
593  (BYTE*)ip + in_GetHdrLen(ip), (size_t)data_length);
594 
595  TRACE_MSG (7, ("got_hole %d, fc->hole_first %" ADDR_FMT "\n",
596  got_hole, ADDR_CAST(fc->hole_first)));
597 
598  if (fc->hole_first == NULL) /* Now we have all the parts */
599  {
600  if (fb->active >= 1)
601  fb->active--;
602 
603  /* Redo checksum as we've changed the length in the header
604  */
605  fc->ip->frag_ofs = 0; /* no MF or frag-ofs */
606  fc->ip->checksum = 0;
607  fc->ip->checksum = ~CHECKSUM (fc->ip, sizeof(in_Header));
608 
609  STAT (ip4stats.ips_reassembled++);
610  *ip_ptr = fc->ip; /* MAC-header is in front of IP */
611  return (1);
612  }
613 
614  STAT (ip4stats.ips_fragdropped--);
615 
616 drop_frag:
617  STAT (ip4stats.ips_fragdropped++);
618  DEBUG_RX (NULL, ip);
619  return (0);
620 }
621 
622 /*
623  * Release a reassembled IP-packet.
624  * Or mark a frag_bucket as inactive because of reassembly timeout.
625  */
626 int ip4_free_fragment (const in_Header *ip)
627 {
628  unsigned i, j;
629 
630  for (j = 0; j < DIM(frag_buckets); j++)
631  {
632  struct frag_bucket *fb = frag_buckets + j;
633  struct huge_ip *hip = fb->ip;
634 
635  if (hip->marker != BUCKET_MARKER)
636  TCP_CONSOLE_MSG (0, ("frag_buckets[%d] destroyed\n!", j));
637 
638  if (fb->used && ip == &hip->hdr)
639  {
640  TRACE_MSG (7, ("ip4_free_fragment(%" ADDR_FMT "), bucket=%d, active=%d\n",
641  ADDR_CAST(ip), j, fb->active));
642 
643  fb->used = FALSE;
644  fb->active = 0;
645  for (i = 0; i < MAX_FRAGMENTS; i++)
646  frag_control[j][i].used = FALSE;
647  return (1);
648  }
649  }
650  return (0);
651 }
652 
653 /*
654  * Check if any fragment chains has timed-out
655  */
656 static void chk_timeout_frags (void)
657 {
658  unsigned i, j;
659 
660  for (j = 0; j < DIM(frag_buckets); j++)
661  {
662  struct frag_bucket *fb = frag_buckets + j;
663  struct frag_ctrl *fc = &frag_control[j][0];
664 
665  TRACE_MSG (14, ("chk_timeout_frags(), bucket %u, active %d, used %d\n",
666  j, fb->active, fb->used));
667  if (!fb->active)
668  continue;
669 
670  for (i = 0; i < MAX_FRAGMENTS; i++, fc++)
671  {
672  if (!fc->used || !chk_timeout(fc->timer))
673  continue;
674 
675  if (fb->got_ofs0)
676  {
677  struct in_Header ip;
678 
679  memset (&ip, 0, sizeof(ip));
680  ip.identification = fb->key.ident;
681  ip.proto = fb->key.proto;
682  ip.source = fb->key.source;
683  ip.destination = fb->key.destin;
684 
685  /* send an ICMP_TIMXCEED (code 1)
686  */
687  icmp_send_timexceed (&ip, (const void*)&fb->mac_src);
688  }
689 
690  STAT (ip4stats.ips_fragtimeout++);
691 
692  TRACE_MSG (14, ("chk_timeout_frags(), bucket %u, slot %d, id %04X\n",
693  j, i, intel16(fb->key.ident)));
694 
695  if (ip4_free_fragment(&fb->ip->hdr))
696  return;
697  }
698  }
699 }
700 
701 /*
702  * Free memeory allocated at startup by ip4_frag_init().
703  */
704 static void free_frag_buckets (void)
705 {
706  int i;
707 
708  for (i = 0; i < DIM(frag_buckets); i++)
709  {
710  struct frag_bucket *fb = frag_buckets + i;
711  struct huge_ip *hip = fb->ip;
712 
713  if (hip && hip->marker == BUCKET_MARKER)
714  {
715  hip->marker = 0;
716  free (hip);
717  }
718  }
719 }
720 
721 /*
722  * Allocate 'frag_buckets[]::ip' for doing reassembly.
723  */
724 void ip4_frag_init (void)
725 {
726  int i;
727 
728  for (i = 0; i < DIM(frag_buckets); i++)
729  {
730  struct frag_bucket *fb = frag_buckets + i;
731  struct huge_ip *hip = calloc (sizeof(*hip), 1);
732 
733  if (!hip)
734  break;
735 
736  memset (fb, '\0', sizeof(*fb));
737  fb->ip = hip;
738  hip->marker = BUCKET_MARKER;
739  }
740 
741  /* Add a daemon to check for IPv4-fragment time-outs.
742  */
743  DAEMON_ADD (chk_timeout_frags);
744  RUNDOWN_ADD (free_frag_buckets, 260);
745 }
746 
747 /*----------------------------------------------------------------------*/
748 
749 #if defined(TEST_PROG) /* a small test program */
750 #undef FP_OFF
751 #undef enable
752 #undef disable
753 
754 #ifndef _MSC_VER
755 #include <unistd.h>
756 #endif
757 
758 #ifdef _WIN32
759 #define sleep(sec) Sleep (1000*(sec))
760 #endif
761 
762 #include "sock_ini.h"
763 #include "loopback.h"
764 #include "pcarp.h"
765 
766 static DWORD to_host = 0;
767 static WORD frag_ofs = 0;
768 static int max_frags = 5;
769 static int frag_size = 1000;
770 static int rand_frag = 0;
771 static int rev_order = 0;
772 static int time_frag = 0;
773 
774 #ifdef WIN32
775 
776 /* Put this somewhere else */
777 
778 static struct {
779  const char *col_name;
780  int col_value;
781  } colors[] = {
782  "black", 0,
783  "blue", FOREGROUND_BLUE,
784  "green", FOREGROUND_GREEN,
785  "cyan", FOREGROUND_BLUE | FOREGROUND_GREEN,
786  "red", FOREGROUND_RED,
787  "magenta", FOREGROUND_BLUE | FOREGROUND_RED,
788  "brown", FOREGROUND_RED | FOREGROUND_GREEN,
789  "lightgray", FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_RED,
790  "darkgray", FOREGROUND_INTENSITY | 0,
791  "lightblue", FOREGROUND_INTENSITY | FOREGROUND_BLUE,
792  "lightgreen", FOREGROUND_INTENSITY | FOREGROUND_GREEN,
793  "lightcyan", FOREGROUND_INTENSITY | FOREGROUND_BLUE | FOREGROUND_GREEN,
794  "lightred", FOREGROUND_INTENSITY | FOREGROUND_RED,
795  "lightmagenta", FOREGROUND_INTENSITY | FOREGROUND_BLUE | FOREGROUND_RED,
796  "yellow", FOREGROUND_INTENSITY | FOREGROUND_RED | FOREGROUND_GREEN,
797  "white", FOREGROUND_INTENSITY | FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_RED,
798  };
799 
800 void test_colors (void)
801 {
802  int i;
803 
804  for (i = 0; i < DIM(colors); i++)
805  {
806  SetConsoleTextAttribute (stdout_hnd, (console_info.wAttributes & ~7) | colors[i].col_value);
807  printf ("Color %2d: %2d = %s\n", i, colors[i].col_value, colors[i].col_name);
808  SetConsoleTextAttribute (stdout_hnd, console_info.wAttributes);
809  }
810 }
811 #endif
812 
813 void usage (char *argv0)
814 {
815  printf ("%s [-n num] [-s size] [-h ip] [-r] [-R] [-t]\n"
816  "Send fragmented ICMP Echo Request (ping)\n\n"
817  "options:\n"
818  " -n number of fragments to send (default %d)\n"
819  " -s size of each fragment (default %d)\n"
820  " -h specify destination IP (default 127.0.0.1)\n"
821  " -r send fragments in random order (default %s)\n"
822  " -R send fragments in reverse order (default %s)\n"
823  " -t simulate fragment timeout (default %s)\n",
824  argv0, max_frags, frag_size,
825  rand_frag ? "yes" : "no",
826  rev_order ? "yes" : "no",
827  time_frag ? "yes" : "no");
828  exit (0);
829 }
830 
831 BYTE *init_frag (int argc, char **argv)
832 {
833  int i, ch;
834  BYTE *data;
835 
836  while ((ch = getopt(argc, argv, "h:n:s:rRt?")) != EOF)
837  switch (ch)
838  {
839  case 'h': to_host = inet_addr (optarg);
840  if (to_host == INADDR_NONE)
841  {
842  printf ("Illegal IP-address\n");
843  exit (-1);
844  }
845  break;
846  case 'n': max_frags = atoi (optarg);
847  break;
848  case 's': frag_size = atoi (optarg) / 8; /* multiples of 8 */
849  frag_size <<= 3;
850  break;
851  case 'r': rand_frag = 1;
852  break;
853  case 'R': rev_order = 1;
854  break;
855  case 't': time_frag = 1;
856  break;
857  case '?':
858  default : usage (argv[0]);
859  }
860 
861  if (max_frags < 1 || max_frags > FD_SETSIZE)
862  {
863  printf ("# of fragments is 1 - %d\n", FD_SETSIZE);
864  exit (-1);
865  }
866 
867  if (frag_size < 8 || frag_size > MAX_IP4_DATA)
868  {
869  printf ("Fragsize range is 8 - %ld\n", MAX_IP4_DATA);
870  exit (-1);
871  }
872 
873  if (frag_size * max_frags > USHRT_MAX)
874  {
875  printf ("Total fragsize must be < 64kB\n");
876  exit (-1);
877  }
878 
879  data = calloc (frag_size * max_frags, 1);
880  if (!data)
881  {
882  printf ("no memory\n");
883  exit (-1);
884  }
885 
886  for (i = 0; i < max_frags; i++)
887  memset (data + i*frag_size, 'a'+i, frag_size);
888 
889  loopback_mode |= LBACK_MODE_ENABLE;
890  dbug_init();
891  sock_init();
892 
893  if (!to_host)
894  to_host = htonl (INADDR_LOOPBACK);
895  return (data);
896 }
897 
898 /*----------------------------------------------------------------------*/
899 
900 int rand_packet (fd_set *fd, int max)
901 {
902  int count = 0;
903 
904  while (1)
905  {
906  int i = Random (0, max);
907  if (i < max && !FD_ISSET(i,fd))
908  {
909  FD_SET (i, fd);
910  return (i);
911  }
912  if (++count == 10*max)
913  return (-1);
914  }
915 }
916 
917 /*----------------------------------------------------------------------*/
918 
919 int main (int argc, char **argv)
920 {
921  fd_set is_sent;
922  int i;
923  eth_address eth;
924  in_Header *ip;
925  ICMP_PKT *icmp;
926  WORD frag_flag;
927  BYTE *data = init_frag (argc, argv);
928 
929  if (!_arp_resolve(ntohl(to_host), &eth))
930  {
931  printf ("ARP failed\n");
932  return (-1);
933  }
934 
935  _ip4_frag_reasm = 3;
936 
937  ip = (in_Header*) _eth_formatpacket (&eth, IP4_TYPE);
938  icmp = (ICMP_PKT*) data;
939 
940  ip->hdrlen = sizeof(*ip)/4;
941  ip->ver = 4;
942  ip->tos = _default_tos;
943  ip->identification = _get_ip4_id();
944  ip->ttl = 15; /* max 15sec reassembly time */
945  ip->proto = ICMP_PROTO;
946 
947  icmp->echo.type = ICMP_ECHO;
948  icmp->echo.code = 0;
949  icmp->echo.identifier = 0;
950  icmp->echo.index = 1;
951  icmp->echo.sequence = set_timeout (1) & 0xFFFF; /* "random" id */
952  icmp->echo.checksum = 0;
953  icmp->echo.checksum = ~CHECKSUM (icmp, max_frags*frag_size);
954 
955  FD_ZERO (&is_sent);
956 
957 #if 0 /* test random generation */
958  if (rand_frag)
959  for (i = 0; i < max_frags; i++)
960  {
961  int j = rand_packet (&is_sent, max_frags);
962  printf ("index %d\n", j);
963  }
964  exit (0);
965 #endif
966 
967  for (i = 0; i < max_frags; i++)
968  {
969  int j;
970 
971  if (rand_frag)
972  {
973  j = rand_packet (&is_sent, max_frags);
974  if (j < 0)
975  break;
976  }
977  if (rev_order)
978  {
979  j = max_frags - i - 1;
980  frag_flag = (j > 0) ? IP_MF : 0;
981  }
982  else
983  {
984  j = i;
985  frag_flag = (j == max_frags-1) ? 0 : IP_MF;
986  }
987 
988  frag_ofs = (j * frag_size);
989  memcpy ((BYTE*)(ip+1), data+frag_ofs, frag_size);
990 
991  /* The loopback device swaps src/dest IP; hence we must set them
992  * on each iteration of the loop
993  */
994  ip->source = intel (my_ip_addr);
995  ip->destination = to_host;
996  ip->frag_ofs = intel16 (frag_ofs/8 | frag_flag);
997  ip->length = intel16 (frag_size + sizeof(*ip));
998  ip->checksum = 0;
999  ip->checksum = ~CHECKSUM (ip, sizeof(*ip));
1000 
1001  _eth_send (frag_size+sizeof(*ip), NULL, __FILE__, __LINE__);
1002 
1003  STAT (ip4stats.ips_ofragments++);
1004  }
1005 
1006  puts ("Calling tcp_tick(). Press a key to quit.");
1007  debug_on = 2;
1008 
1009  while (!kbhit())
1010  {
1011  tcp_tick (NULL);
1012  sleep (1);
1013  }
1014 
1015  free (data);
1016  return (0);
1017 }
1018 #endif /* TEST_PROG */
1019 #endif /* USE_FRAGMENTS */
1020 
Definition: wtypes.h:197
unsigned W32_CALL Random(unsigned a, unsigned b)
Returns a random integer in range [a..b].
Definition: misc.c:742
Definition: ip4_frag.c:117
int W32_CALL _eth_send(WORD len, const void *sock, const char *file, unsigned line)
_eth_send() does the actual transmission once we are complete with filling the buffer.
Definition: pcsed.c:306
Core definitions.
Definition: ip_icmp.h:62
WORD _get_ip4_id(void)
Increment IPv4-identifier before returning it.
Definition: ip4_out.c:65
u_long ips_reassembled
Definition: ip_var.h:149
DWORD W32_CALL set_timeout(DWORD msec)
Return time for when given timeout (msec) expires.
Definition: timer.c:503
int icmp_send_timexceed(const in_Header *ip, const void *mac_dest)
Send an "ICMP Time Exceeded" (reassebly timeout) back to 'ip->source'.
Definition: pcicmp.c:225
void *W32_CALL _eth_formatpacket(const void *mac_dest, WORD eth_type)
_eth_format_packet() places the next packet to be transmitted into the above link-layer output packet...
Definition: pcsed.c:135
Definition: ip.h:67
BOOL _pktserial
using serial driver, SLIP/PPP
Definition: pcpkt.c:54
BYTE * init_frag(int argc, char **argv)
Definition: ip4_frag.c:831
DWORD my_ip_addr
our IP address
Definition: pctcp.c:70
WORD W32_CALL tcp_tick(sock_type *s)
Must be called periodically by user application (or BSD socket API).
Definition: pctcp.c:1389
BOOL W32_CALL _arp_resolve(DWORD ip, eth_address *eth)
The blocking lookup function visible to higher functions.
Definition: pcarp.c:1279
char *W32_CALL _inet_ntoa(char *s, DWORD ip)
Convert an IP-address 'ip' into a string.
Definition: netaddr.c:43
BOOL W32_CALL chk_timeout(DWORD value)
Check if milli-sec value has expired:
Definition: timer.c:547
BYTE _default_tos
Definition: ip4_out.c:53
int main(int argc, char **argv)
Definition: echo.c:223