#include #include #ifdef WINDOWS #include #endif /* * ICMP - RFC 792 */ static char *unreach[] = { "Network Unreachable", "Host Unreachable", "Protocol Unreachable", "Port Unreachable", "Fragmentation needed and DF set", "Source Route Failed" }; static char *exceed[] = { "TTL exceeded in transit", "Frag ReAsm time exceeded" }; static char *redirect[] = { "Redirect for Network", "Redirect for Host", "Redirect for TOS and Network", "Redirect for TOS and Host" }; /* constants */ typedef struct icmp_unused { byte type; byte code; word checksum; longword unused; in_Header ip; byte spares[ 8 ]; }; typedef struct icmp_pointer { byte type; byte code; word checksum; byte pointer; byte unused[ 3 ]; in_Header ip; }; typedef struct icmp_ip { byte type; byte code; word checksum; longword ipaddr; in_Header ip; }; typedef struct icmp_echo { byte type; byte code; word checksum; word identifier; word sequence; }; typedef struct icmp_timestamp { byte type; byte code; word checksum; word identifier; word sequence; longword original; /* original timestamp */ longword receive; /* receive timestamp */ longword transmit; /* transmit timestamp */ }; typedef struct icmp_info { byte type; byte code; word checksum; word identifier; word sequence; }; typedef union { struct icmp_unused unused; struct icmp_pointer pointer; struct icmp_ip ip; struct icmp_echo echo; struct icmp_timestamp timestamp; struct icmp_info info; } icmp_pkt; typedef struct _pkt { in_Header in; icmp_pkt icmp; in_Header data; }; static word icmp_id = 0; static longword ping_hcache = 0; /* host */ static longword ping_tcache = 0; /* time */ static longword ping_number = 0; longword _chk_ping( longword host, longword *ptr ) { if ( ping_hcache == host ) { ping_hcache = 0xffffffffL; *ptr = ping_number; return( ping_tcache ); } return( 0xffffffffL ); } icmp_print( icmp_pkt *icmp, char *msg) { #ifdef WINDOWS MessageBox(GetFocus(), (LPSTR)msg, (LPSTR)"ICMP", MB_OK); #else outs("\n\rICMP: "); outs( msg ); outs("\n\r"); #endif } /* * */ struct _pkt *icmp_Format( longword destip ) { eth_address dest; char *temp; /* we use arp rather than supplied hardware address */ /* after first ping this will still be in cache */ if ( !_arp_resolve( destip , &dest )) return( NULL ); /* unable to find address */ return( (struct _pkt*)_eth_formatpacket( &dest, 8 )); } /* * icmp_Reply - format a reply packet * - note that src and dest are NETWORK order not host!!!! */ struct _pkt *icmp_Reply( struct _pkt *p, longword src, longword dest, int icmp_length ) { in_Header *ip; icmp_pkt *icmp; ip = &p->in; icmp = &p->icmp; /* finish the icmp checksum portion */ icmp->unused.checksum = 0; icmp->unused.checksum = ~checksum( icmp, icmp_length ); /* encapsulate into a nice ip packet */ ip->ver = 4; ip->hdrlen = 5; ip->length = intel16( sizeof( in_Header ) + icmp_length); ip->tos = 0; ip->identification = intel16( icmp_id ++); /* not using ip id */ ip->frag = 0; ip->ttl = 250; ip->proto = ICMP_PROTO; ip->checksum = 0; ip->source = src; ip->destination = dest; ip->checksum = ~ checksum( ip, sizeof( in_Header )); _eth_send( intel16( ip->length )); } icmp_handler( in_Header *ip ) { icmp_pkt *icmp, *newicmp; struct _pkt *pkt; int len, code; in_Header *ret; len = in_GetHdrlenBytes( ip ); icmp = (icmp_pkt*)((byte *)ip + len); len = intel16( ip->length ) - len; if ( checksum( icmp, len ) != 0xffff ) { #ifndef WINDOWS outs("ICMP received with bad checksum\n\r"); #endif return; } code = icmp->unused.code; switch ( icmp->unused.type) { case 0 : /* icmp echo reply received */ /* icmp_print( icmp, "received icmp echo receipt"); */ /* check if we were waiting for it */ ping_hcache = intel( ip->source ); ping_tcache = set_timeout( 1 ) - *(longword *)(&icmp->echo.identifier ); if (ping_tcache > 0xffffffffL) ping_tcache += 0x1800b0L; ping_number = *(longword*)( ((byte*)(&icmp->echo.identifier)) + 4 ); /* do more */ break; case 3 : /* destination unreachable message */ if (code < 6) { icmp_print( icmp, unreach[ code ]); /* handle udp or tcp socket */ ret = (byte*)(icmp) + sizeof( icmp_pkt ); if (ret->proto == TCP_PROTO) _tcp_cancel( ret, 1 ); if (ret->proto == UDP_PROTO) _udp_cancel( ret ); } break; case 4 : /* source quench */ icmp_print( icmp, "Source Quench"); if (ret->proto == TCP_PROTO) _tcp_cancel( ret, 2 ); break; case 5 : /* redirect */ if (code < 4) { _arp_register( intel( icmp->ip.ipaddr ), intel( icmp->ip.ip.destination )); icmp_print( icmp, redirect[ code ]); /* do it to some socket guy */ } break; case 8 : /* icmp echo request */ /* icmp_print( icmp, "PING requested of us"); */ /* do arp and create packet */ #ifdef OLD if (!(pkt = icmp_Format( intel( ip->source )))) break; #else /* format the packet with the request's hardware address */ pkt = (struct _pkt*)(_eth_formatpacket( _eth_hardware(ip), 8)); #endif OLD newicmp = &pkt->icmp; /* newicmp = (icmp_pkt*)((byte *)(pkt) + sizeof(in_Header)); */ movmem( icmp, newicmp, len ); newicmp->echo.type = 0; newicmp->echo.code = code; /* use supplied ip values in case we ever multi-home */ /* note that ip values are still in network order */ icmp_Reply( pkt,ip->destination, ip->source, len ); /* icmp_print( newicmp, "PING reply sent"); */ break; case 11 : /* time exceeded message */ if (code < 2 ) { icmp_print( icmp, exceed[ code ]); } break; case 12 : /* parameter problem message */ icmp_print( icmp, "IP Parameter problem"); break; case 13 : /* timestamp message */ icmp_print( icmp, "Timestamp message"); /* send reply */ break; case 14 : /* timestamp reply */ icmp_print( icmp, "Timestamp reply"); /* should store */ break; case 15 : /* info request */ icmp_print( icmp,"Info requested"); /* send reply */ break; case 16 : /* info reply */ icmp_print( icmp,"Info reply"); break; } }