55 #define MAX_IP_FRAGS 2
56 #define MAX_IP_HOLDTIME 15
58 int _ip4_frag_reasm = MAX_IP_HOLDTIME;
60 #if defined(USE_FRAGMENTS)
64 #if defined(TEST_PROG)
65 #define TRACE_MSG(color, args) \
68 printf ("%s(%u): ", __FILE__, __LINE__); \
74 #define TRACE_MSG(color, args) TCP_TRACE_MSG (args)
81 #define MAX_FRAGMENTS 20U
82 #define MAX_FRAG_SIZE (MAX_FRAGMENTS * MAX_IP4_DATA)
85 #define BUCKET_MARKER 0xDEAFABBA
90 #define NUM_FD_SETS ((MAX_FRAG_SIZE+sizeof(fd_set)-1) / sizeof(fd_set))
92 typedef fd_set used_map [NUM_FD_SETS];
96 BYTE data [MAX_FRAG_SIZE];
126 struct hole *hole_first;
131 static struct frag_ctrl frag_control [MAX_IP_FRAGS][MAX_FRAGMENTS];
132 static struct frag_bucket frag_buckets [MAX_IP_FRAGS];
134 static long data_start;
135 static long data_end;
136 static long data_length;
160 static enum frag_bits fbits;
162 #if defined(TEST_PROG)
163 static void dump_frag_holes (
const char *where,
const struct frag_ctrl *fc,
const struct hole *
hole)
165 const struct hole *h;
169 TRACE_MSG (6, (
"%s, No frag!\n", where));
173 TRACE_MSG (15, (
"%s: fc->used: %d, fc->data_offset: %" ADDR_FMT
"\n",
174 where, fc->used, fc->data_offset));
176 for (h = hole; h; h = h->next)
178 TRACE_MSG (15, (
"hole: %" ADDR_FMT
", h->start: %d, h->end: %d\n",
179 h, h->start, h->end));
184 #if defined(USE_DEBUG)
185 static const char *decode_fbits (
int fb)
192 strcat (buf,
"IS_FRAG+");
195 strcat (buf,
"IS_LAST+");
198 strcat (buf,
"IS_FIRST+");
200 end = strrchr (buf,
'+');
219 static BOOL match_frag (
const in_Header *
ip,
int *slot,
int *bucket,
int *free_bucket)
228 fk.source = ip->source;
229 fk.destin = ip->destination;
230 fk.ident = ip->identification;
231 fk.proto = ip->proto;
233 for (b = 0; b < DIM(frag_buckets); b++)
236 const struct frag_ctrl *fc = &frag_control[b][0];
240 if (*free_bucket == -1)
245 if (memcmp(&fk,&fb->key,
sizeof(fk)))
248 for (i = 0; i < (int)MAX_FRAGMENTS; i++, fc++)
264 static __inline BOOL check_frag_ofs (
const in_Header *ip, DWORD ofs, DWORD end)
268 if (ofs + end <=
sizeof(hip.data) &&
269 ofs + end <= USHRT_MAX-8)
272 TCP_CONSOLE_MSG (2, (_LANG(
"Bad frag-ofs: %lu, frag-end: %lu, ip-prot %u (%s -> %s)\n"),
280 static __inline
void set_fbits (DWORD offset, WORD flags)
291 fbits = (IS_FRAG | IS_LAST);
297 static BOOL setup_first_frag (
const in_Header *ip,
int idx)
307 if (hip->marker != BUCKET_MARKER)
308 TCP_CONSOLE_MSG (0, (
"frag_buckets[%d] destroyed\n!", idx));
310 TRACE_MSG (7, (
"frag_bucket[%d] = %" ADDR_FMT
"\n", idx, ADDR_CAST(hip)));
315 memset (&fb->mac_src, 0,
sizeof(mac_address));
316 else memcpy (&fb->mac_src, MAC_SRC(ip),
sizeof(mac_address));
318 WATT_ASSERT (fb->used == 0);
321 fb->got_ofs0 = FALSE;
325 fc = &frag_control[idx][0];
326 for (i = 0; i < MAX_FRAGMENTS; i++, fc++)
330 if (i == MAX_FRAGMENTS)
336 WATT_ASSERT (fb->active == 1);
337 TRACE_MSG (7, (
"bucket=%d, active=%u, i=%u\n", idx, fb->active, i));
341 fb->key.proto = ip->proto;
342 fb->key.source = ip->source;
343 fb->key.destin = ip->destination;
344 fb->key.ident = ip->identification;
346 fc->ip = &fb->ip->hdr;
347 fc->timer =
set_timeout (1000 * min(_ip4_frag_reasm, ip->ttl));
351 fc->data_offset = (BYTE*)fc->ip + in_GetHdrLen(ip);
357 WORD ip_len = intel16 (ip->length);
358 BYTE *dst = (BYTE*) fc->ip;
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;
371 BYTE *src = (BYTE*)ip + in_GetHdrLen(ip);
372 BYTE *dst = fc->data_offset + data_start;
374 memcpy (dst, src, (
size_t)data_length);
378 hole = fc->hole_first = (
struct hole*)fc->data_offset;
380 hole->end = data_start - 1;
382 if (!(fbits & IS_LAST))
384 hole->next = (
struct hole*) (fc->data_offset + data_length + 1);
389 hole = fc->hole_first->next = NULL;
391 fc->ip->length = intel16 ((WORD)(data_end + in_GetHdrLen(ip)));
397 hole->start = data_length;
398 hole->end = MAX_FRAG_SIZE;
401 TRACE_MSG (7, (
"hole %" ADDR_FMT
", hole->start %lu, hole->end %lu\n",
402 ADDR_CAST(hole), hole->start, hole->end));
405 #if defined(TEST_PROG)
406 dump_frag_holes (
"setup_first_frag", fc, hole);
422 int ip4_defragment (
const in_Header **ip_ptr, DWORD offset, WORD flags)
426 struct hole *hole = NULL;
427 struct hole *prev_hole = NULL;
431 BOOL got_hole = FALSE;
432 int slot, bucket, free_bucket;
436 if (offset == 0 && !(flags & IP_MF))
439 set_fbits (offset, flags);
443 if (!match_frag(ip,&slot,&bucket,&free_bucket))
445 if (slot == MAX_FRAGMENTS-1)
447 STAT (ip4stats.ips_fragments++);
452 if (!(fbits & IS_FRAG))
461 STAT (ip4stats.ips_fragments++);
465 data_start = (long) offset;
466 data_length = (long) intel16 (ip->length) - in_GetHdrLen (ip);
467 data_end = data_start + data_length;
469 TRACE_MSG (7, (
"\nip4_defrag: %s -> %s, id 0x%04X, len %ld, ofs %ld, fbits %s\n",
472 intel16(ip->identification), data_length, data_start,
473 decode_fbits(fbits)));
475 if ((flags & IP_MF) && data_length == 0 && offset != 0)
477 TRACE_MSG (7, (
"No data.\n"));
481 if ((flags & IP_MF) && (data_length & 7) != 0)
483 TRACE_MSG (7, (
"Frag-data not multiple of 8.\n"));
487 if (!check_frag_ofs(ip,data_start,data_end))
490 found = (bucket > -1);
494 TRACE_MSG (7, (
"bucket=%d, i=%d, key not found\n", free_bucket, slot));
495 bucket = free_bucket;
496 fb = frag_buckets + bucket;
500 if (bucket == -1 || fb->active == MAX_FRAGMENTS)
503 if (!setup_first_frag (ip, bucket))
512 fc = &frag_control [bucket][slot];
513 fb = frag_buckets + bucket;
515 TRACE_MSG (7, (
"bucket=%d, slot=%d key found, fbits: %s, active=%d\n",
516 bucket, slot, decode_fbits(fbits), fb->active));
519 fc->ip->length = intel16 ((WORD)(data_end + in_GetHdrLen(ip)));
524 hole = fc->hole_first;
528 #if defined(TEST_PROG)
529 dump_frag_holes (
"ip4_defragment(1)", fc, hole);
532 if (hole && (data_start <= hole->end) &&
533 (data_end >= hole->start))
535 long last_end = hole->end;
542 if (data_start > hole->start)
544 hole->end = data_start - 1;
552 fc->hole_first = hole->next;
553 else prev_hole->next = hole->next;
559 if (data_end < hole->end)
561 hole = (
struct hole*) (data_end + 1 + fc->data_offset);
562 hole->start = data_end + 1;
563 hole->end = last_end;
569 hole->next = fc->hole_first;
570 fc->hole_first = hole;
574 hole->next = prev_hole->next;
575 prev_hole->next = hole;
582 #if defined(TEST_PROG)
583 dump_frag_holes (
"ip4_defragment(2)", fc, hole);
592 memcpy (fc->data_offset + data_start,
593 (BYTE*)ip + in_GetHdrLen(ip), (
size_t)data_length);
595 TRACE_MSG (7, (
"got_hole %d, fc->hole_first %" ADDR_FMT
"\n",
596 got_hole, ADDR_CAST(fc->hole_first)));
598 if (fc->hole_first == NULL)
605 fc->ip->frag_ofs = 0;
606 fc->ip->checksum = 0;
607 fc->ip->checksum = ~CHECKSUM (fc->ip,
sizeof(
in_Header));
614 STAT (ip4stats.ips_fragdropped--);
617 STAT (ip4stats.ips_fragdropped++);
626 int ip4_free_fragment (
const in_Header *ip)
630 for (j = 0; j < DIM(frag_buckets); j++)
635 if (hip->marker != BUCKET_MARKER)
636 TCP_CONSOLE_MSG (0, (
"frag_buckets[%d] destroyed\n!", j));
638 if (fb->used && ip == &hip->hdr)
640 TRACE_MSG (7, (
"ip4_free_fragment(%" ADDR_FMT
"), bucket=%d, active=%d\n",
641 ADDR_CAST(ip), j, fb->active));
645 for (i = 0; i < MAX_FRAGMENTS; i++)
646 frag_control[j][i].used = FALSE;
656 static void chk_timeout_frags (
void)
660 for (j = 0; j < DIM(frag_buckets); j++)
663 struct frag_ctrl *fc = &frag_control[j][0];
665 TRACE_MSG (14, (
"chk_timeout_frags(), bucket %u, active %d, used %d\n",
666 j, fb->active, fb->used));
670 for (i = 0; i < MAX_FRAGMENTS; i++, fc++)
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;
690 STAT (ip4stats.ips_fragtimeout++);
692 TRACE_MSG (14, (
"chk_timeout_frags(), bucket %u, slot %d, id %04X\n",
693 j, i, intel16(fb->key.ident)));
695 if (ip4_free_fragment(&fb->ip->hdr))
704 static void free_frag_buckets (
void)
708 for (i = 0; i < DIM(frag_buckets); i++)
713 if (hip && hip->marker == BUCKET_MARKER)
724 void ip4_frag_init (
void)
728 for (i = 0; i < DIM(frag_buckets); i++)
731 struct huge_ip *hip = calloc (
sizeof(*hip), 1);
736 memset (fb,
'\0',
sizeof(*fb));
738 hip->marker = BUCKET_MARKER;
743 DAEMON_ADD (chk_timeout_frags);
744 RUNDOWN_ADD (free_frag_buckets, 260);
749 #if defined(TEST_PROG)
759 #define sleep(sec) Sleep (1000*(sec))
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;
779 const char *col_name;
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,
800 void test_colors (
void)
804 for (i = 0; i < DIM(colors); i++)
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);
813 void usage (
char *argv0)
815 printf (
"%s [-n num] [-s size] [-h ip] [-r] [-R] [-t]\n"
816 "Send fragmented ICMP Echo Request (ping)\n\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");
836 while ((ch = getopt(argc, argv,
"h:n:s:rRt?")) != EOF)
839 case 'h': to_host = inet_addr (optarg);
840 if (to_host == INADDR_NONE)
842 printf (
"Illegal IP-address\n");
846 case 'n': max_frags = atoi (optarg);
848 case 's': frag_size = atoi (optarg) / 8;
851 case 'r': rand_frag = 1;
853 case 'R': rev_order = 1;
855 case 't': time_frag = 1;
858 default : usage (argv[0]);
861 if (max_frags < 1 || max_frags > FD_SETSIZE)
863 printf (
"# of fragments is 1 - %d\n", FD_SETSIZE);
867 if (frag_size < 8 || frag_size > MAX_IP4_DATA)
869 printf (
"Fragsize range is 8 - %ld\n", MAX_IP4_DATA);
873 if (frag_size * max_frags > USHRT_MAX)
875 printf (
"Total fragsize must be < 64kB\n");
879 data = calloc (frag_size * max_frags, 1);
882 printf (
"no memory\n");
886 for (i = 0; i < max_frags; i++)
887 memset (data + i*frag_size,
'a'+i, frag_size);
889 loopback_mode |= LBACK_MODE_ENABLE;
894 to_host = htonl (INADDR_LOOPBACK);
900 int rand_packet (
fd_set *fd,
int max)
907 if (i < max && !FD_ISSET(i,fd))
912 if (++count == 10*max)
919 int main (
int argc,
char **argv)
931 printf (
"ARP failed\n");
940 ip->hdrlen =
sizeof(*ip)/4;
945 ip->proto = ICMP_PROTO;
947 icmp->echo.type = ICMP_ECHO;
949 icmp->echo.identifier = 0;
950 icmp->echo.index = 1;
952 icmp->echo.checksum = 0;
953 icmp->echo.checksum = ~CHECKSUM (icmp, max_frags*frag_size);
959 for (i = 0; i < max_frags; i++)
961 int j = rand_packet (&is_sent, max_frags);
962 printf (
"index %d\n", j);
967 for (i = 0; i < max_frags; i++)
973 j = rand_packet (&is_sent, max_frags);
979 j = max_frags - i - 1;
980 frag_flag = (j > 0) ? IP_MF : 0;
985 frag_flag = (j == max_frags-1) ? 0 : IP_MF;
988 frag_ofs = (j * frag_size);
989 memcpy ((BYTE*)(ip+1), data+frag_ofs, frag_size);
995 ip->destination = to_host;
996 ip->frag_ofs = intel16 (frag_ofs/8 | frag_flag);
997 ip->length = intel16 (frag_size +
sizeof(*ip));
999 ip->checksum = ~CHECKSUM (ip,
sizeof(*ip));
1001 _eth_send (frag_size+
sizeof(*ip), NULL, __FILE__, __LINE__);
1003 STAT (ip4stats.ips_ofragments++);
1006 puts (
"Calling tcp_tick(). Press a key to quit.");
unsigned W32_CALL Random(unsigned a, unsigned b)
Returns a random integer in range [a..b].
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.
WORD _get_ip4_id(void)
Increment IPv4-identifier before returning it.
DWORD W32_CALL set_timeout(DWORD msec)
Return time for when given timeout (msec) expires.
int icmp_send_timexceed(const in_Header *ip, const void *mac_dest)
Send an "ICMP Time Exceeded" (reassebly timeout) back to 'ip->source'.
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...
BOOL _pktserial
using serial driver, SLIP/PPP
BYTE * init_frag(int argc, char **argv)
DWORD my_ip_addr
our IP address
WORD W32_CALL tcp_tick(sock_type *s)
Must be called periodically by user application (or BSD socket API).
BOOL W32_CALL _arp_resolve(DWORD ip, eth_address *eth)
The blocking lookup function visible to higher functions.
char *W32_CALL _inet_ntoa(char *s, DWORD ip)
Convert an IP-address 'ip' into a string.
BOOL W32_CALL chk_timeout(DWORD value)
Check if milli-sec value has expired:
int main(int argc, char **argv)