1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538
|
/*
* Unofficial release 1.3
* B I N G
*
*/
/* $Id: bing_probes.c,v 1.12 1999/10/24 23:28:14 fgouget Exp $ */
#include <stdio.h>
#include <errno.h>
#include <malloc.h>
#include <stdlib.h>
/* types.h provides u_short on HPUX10 and Solaris */
#include <sys/types.h>
#ifdef WIN32
#include <winsock.h>
#include "win32/types.h"
#else
#include <netinet/in_systm.h>
#include <netinet/in.h>
#endif
#include <netinet/ip.h>
#include <netinet/ip_icmp.h>
#include "bing_misc.h"
#include "bing_probes.h"
#include "mod_icmp.h"
#ifdef NO_RANDOM
#define random rand
#define srandom srand
#endif
/* (!!) this will have to move somewhere else */
#ifndef ICMP_ROUTERADVERT
#define ICMP_ROUTERADVERT 9 /* router advertisement */
#endif
#ifndef ICMP_ROUTERSOLICIT
#define ICMP_ROUTERSOLICIT 10 /* router solicitation */
#endif
#ifndef ICMP_TIMXCEED
#define ICMP_TIMXCEED 11 /* time exceeded, code: */
#endif
#ifndef ICMP_PARAMPROB
#define ICMP_PARAMPROB 12 /* ip header bad */
#endif
#ifndef ICMP_TSTAMP
#define ICMP_TSTAMP 13 /* timestamp request */
#endif
#ifndef ICMP_TSTAMPREPLY
#define ICMP_TSTAMPREPLY 14 /* timestamp reply */
#endif
#ifndef ICMP_IREQ
#define ICMP_IREQ 15 /* information request */
#endif
#ifndef ICMP_IREQREPLY
#define ICMP_IREQREPLY 16 /* information reply */
#endif
#ifndef ICMP_MASKREQ
#define ICMP_MASKREQ 17 /* address mask request */
#endif
#ifndef ICMP_MASKREPLY
#define ICMP_MASKREPLY 18 /* address mask reply */
#endif
/*
* bing_probes default settings
*/
#define DEF_UDP_PORT 28933
/*
* This structure holds the information kept in a probe handle.
*/
typedef struct {
/*
* Options modified via probe_set_option
*/
unsigned char bp_om:2, /* specifies the probe method */
bp_fill:2, /* specifies the type of packet filling */
bp_unused:4; /* not used */
short udp_port; /* the invalid UDP port to probe in BP_OM_UNREACH_PORT mode */
int pattern_size; /* the pattern size if one exists */
char* pattern; /* the pattern data */
/*
* Internal data house-keeping
*/
icmp_handle icmp_handle; /* the icmp module handle used to send icmp packets */
struct icmp* packet; /* the icmp packet contents, contains the header and data */
/* (!!) does it contain the ip header or just the icmp header? */
int packet_options_size; /* the options size packet+packet_options_size point to the data */
int packet_data_size; /* the packet data size */
int packet_data_update; /* 1 if the packet data needs updating */
} bp_state_t;
#define handle2state(h) ((bp_state_t*)h)
/*
* Some internal functions.
*/
void dump_packet(char* packet, int size)
{
struct ip* ip;
struct sockaddr src_addr,dst_addr;
char* ipopt;
int hlen;
int j;
/* Dump the ip header */
ip=(struct ip*)packet;
hlen=ip->ip_hl<<2;
SOCKADDR_IN(&src_addr)->sin_family=AF_INET;
SOCKADDR_IN(&src_addr)->sin_port=0;
SOCKADDR_IN(&src_addr)->sin_addr.s_addr=ip->ip_src.s_addr;
SOCKADDR_IN(&dst_addr)->sin_family=AF_INET;
SOCKADDR_IN(&dst_addr)->sin_port=0;
SOCKADDR_IN(&dst_addr)->sin_addr.s_addr=ip->ip_dst.s_addr;
printf("\n");
for (j=0;j<hlen;j++) {
printf("%02x ",(unsigned char)*(packet+j));
}
printf("\nVr HL TOS Len ID Flg off TTL Pro cks Src Dst\n");
printf(" %1x %2d %02x %5d %04x",
ip->ip_v, ip->ip_hl, ip->ip_tos, (unsigned short)ip->ip_len, ip->ip_id);
printf(" %1x %04x", ((ip->ip_off) & 0xe000) >> 13,
(ip->ip_off) & 0x1fff);
printf(" %02x %02x %04x", ip->ip_ttl, ip->ip_p, ip->ip_sum);
printf(" %-15s", inet_ntoa(*(struct in_addr *)&ip->ip_src.s_addr));
printf(" %s\n", inet_ntoa(*(struct in_addr *)&ip->ip_dst.s_addr));
/* dump any option bytes */
if (hlen>sizeof(struct ip)) {
printf("IP Options: ");
ipopt=(char*)ip+sizeof(struct ip); /* point to options */
while (hlen-->sizeof(struct ip)) {
printf("%02x", *ipopt++);
}
putchar('\n');
}
/* Dump the icmp header if any*/
if (ip->ip_p==1) {
struct icmp* icp;
icp=(struct icmp*)(packet+(ip->ip_hl<<2));
switch (icp->icmp_type) {
case ICMP_ECHOREPLY:
printf("Echo Reply: code=%d id=%d seq=%d\n",icp->icmp_code,icp->icmp_id,icp->icmp_seq);
break;
case ICMP_SOURCEQUENCH:
printf("ICMP_SOURCEQUENCH");
break;
case ICMP_REDIRECT:
printf("ICMP_REDIRECT");
break;
case ICMP_ECHO:
printf("Echo Request: code=%d id=%d seq=%d\n",icp->icmp_code,icp->icmp_id,icp->icmp_seq);
break;
case ICMP_ROUTERADVERT:
printf("ICMP_ROUTERADVERT");
break;
case ICMP_ROUTERSOLICIT:
printf("ICMP_ROUTERSOLICIT");
break;
case ICMP_TIMXCEED:
printf("ICMP_TIMXCEED");
break;
case ICMP_PARAMPROB:
printf("ICMP_PARAMPROB");
break;
case ICMP_TSTAMP:
printf("ICMP_TSTAMP");
break;
case ICMP_TSTAMPREPLY:
printf("ICMP_TSTAMPREPLY");
break;
case ICMP_IREQ:
printf("ICMP_IREQ");
break;
case ICMP_IREQREPLY:
printf("ICMP_IREQREPLY");
break;
case ICMP_MASKREQ:
printf("ICMP_MASKREQ");
break;
case ICMP_MASKREPLY:
printf("ICMP_MASKREPLY");
break;
default:
printf("unknown type %d\n",icp->icmp_type);
}
}
/* Give short info about the payload */
}
/*
--------------------------------------------------------------------------
Builds the data field to be sent with the packet. The data put
in the packet is either dynamic, e.g. random data, or static (i.e.
does not change depending on the packet), e.g. a user specified
pattern. In the first case the data is built on the fly while in the
second case it is precomputed and recomputed each time the packet
size increases.
------------+----+-----+--------------------------------------------------
Parameter | IN | OUT | Role
------------+----+-----+--------------------------------------------------
state | X | | Internal state information
size | X | | The required packet size
------------+----+-----+--------------------------------------------------
RETURN | | X | No return value
------------+----+-----+--------------------------------------------------
*/
static int generate_data(bp_state_t* state, int size)
{
int i;
/* maybe reallocate the data buffer */
if (size>state->packet_data_size) {
/* Allocate buffers which size is a multiple of 1024 */
state->packet_data_size=(size+state->packet_options_size+1024) & ~1023;
state->packet=realloc(state->packet,state->packet_data_size);
state->packet_data_size-=state->packet_options_size;
if (state->packet==NULL)
return -1;
state->packet_data_update=1;
}
/* maybe update the data in the buffer */
if (state->packet_data_update==1) {
unsigned char* packet_data;
packet_data=((unsigned char*)state->packet)+state->packet_options_size;
switch (state->bp_fill) {
case BP_FILL_SEQ:
/* Default case: just put some predictible data in
* the packet.
*/
for (i=0;i<state->packet_data_size;i++) {
packet_data[i]=(unsigned char)(i & 0xff);
}
state->packet_data_update=0;
break;
case BP_FILL_PATTERN:
/* Fill the buffer with the pattern. Since this
* pattern does not change we can compute it ahead
* of time
*/
i=0;
while (i<state->packet_data_size) {
memcpy(packet_data+i,state->pattern,
MIN(state->pattern_size,state->packet_data_size-i));
i+=state->pattern_size;
}
state->packet_data_update=0;
break;
case BP_FILL_RANDOM:
/* Initialise the packet payload with random data.
* Note that on some platforms (e.g. Win32) RAND_MAX is less
* than INT_MAX. Thus we only use the lower byte of the returned
* int which in turn relies on the assumption that RAND_MAX+1
* is a multiple of 256. Fortunately this seems to always be the
* case.
*/
/*printf("Filling the packet with %d random bytes:",state->packet_data_size);*/
for (i=0;i<state->packet_data_size;i++) {
packet_data[i]=(unsigned char)(random() & 0xff);
/*if (i%16==0)
printf("\n%3d: ",i);
printf("%2x,",packet_data[i]);*/
}
/*printf("\n");*/
/* Unlike the other types of "filling" this one must be
* updated with each outgoing packet, thus we do not reset
* packet_data_update.
*/
break;
default:
return -1;
}
}
return 0;
}
/*
* Implementation of the bing_probe interface.
*/
bp_handle probe_open()
{
bp_state_t* h;
/* Allocate the data structure */
h=malloc(sizeof(*h));
if (h==NULL)
return NULL;
/* Open the ICMP handle. This requires root privilege on Unix */
h->icmp_handle=icmp_open();
if (h->icmp_handle==NULL) {
free(h);
return NULL;
}
/* And initialise the other fields */
/* (!!) what should be the default ??? */
h->bp_om=BP_OM_ECHO_REPLY;
h->bp_fill=BP_FILL_SEQ;
h->udp_port=DEF_UDP_PORT;
h->pattern_size=0;
h->pattern=NULL;
h->packet=NULL;
/* (!!) and what if the options are bigger, like if we do loose source routing or some such (which means the packet would also contain the ip options, not only the icmp options */
h->packet_options_size=(char*)h->packet->icmp_data-(char*)h->packet;
h->packet_data_size=0;
h->packet_data_update=1;
return h;
}
int probe_set_option(bp_handle handle, int level, int option, void* optval, int optlen)
{
bp_state_t* state;
int intval;
if (handle==NULL) {
errno=EINVAL;
return -1;
}
state=handle2state(handle);
if (level!=SOL_BP)
return icmp_set_option(state->icmp_handle,level,option,optval,optlen);
switch (option) {
case BPO_FILLING:
intval=*((int*)optval);
if (((intval & ~BP_FILL_MASK)!=0) || (intval==BP_FILL_MASK)) {
errno=EINVAL;
return -1;
}
if ((intval==BP_FILL_PATTERN) && (state->pattern==NULL)) {
errno=EFAULT;
return -1;
}
state->bp_fill=intval;
break;
case BPO_PATTERN:
/* check parameter */
if ((optlen==0) && (state->bp_fill==BP_FILL_PATTERN)) {
errno=EINVAL;
return -1;
}
/* and reallocate the pattern buffer */
state->pattern_size=optlen;
state->pattern=realloc(state->pattern,state->pattern_size);
if (state->pattern==NULL)
return (optlen==0?0:-1);
state->packet_data_update=1; /* the old pattern is lost */
memcpy(state->pattern,(char*)optval,state->pattern_size);
break;
case BPO_OM:
intval=*((int*)optval);
if (((intval & ~BP_OM_MASK)!=0) || (intval==BP_OM_MASK)){
errno=EINVAL;
return -1;
}
state->bp_om=intval;
break;
case BPO_UDP_PORT:
handle2state(handle)->udp_port=(int)optval;
break;
default:
errno=EINVAL;
return -1;
}
return 0;
}
int do_probe(bp_handle handle, struct sockaddr* target, int size, int ttl, bp_probedata_t *probe)
{
bp_state_t* state;
static int icp_seq=0;
int res;
int src_addr_size;
int ret;
probe->contents=NULL;
if ((handle==NULL) || (size<PAD_PKT_SIZE)){
errno=EINVAL;
return -1;
}
state=handle2state(handle);
ret=-1;
/* (!!) hack ??? */
if (ttl==0)
ttl=1;
/* Prepare the packet payload */
generate_data(state,size);
/* Probe the remote host according to the chosen method */
switch (state->bp_om) {
case BP_OM_UNREACH_PORT:
/* BP_OM_UNREACH_PORT: send a UDP packet to a non-existing port.
* We will either receive a TTL Exceeded or a Port Unreachable
* ICMP message.
*/
return -1;
break;
case BP_OM_ECHO_REPLY:
case BP_OM_TTL_EXCEEDED:
probe->size=state->packet_options_size+size;
probe->contents=malloc(sizeof(struct ip)+probe->size);
/* BP_OM_TTL_EXCEEDED and BP_OM_ECHO_REPLY: send an ICMP EchoRequest
* message.
* In the first case the upper layer should have arranged to receive
* TTL Exceeded ICMP messages while in the second case we should receive
* Echo Reply ICMP messages.
*/
state->packet->icmp_type=ICMP_ECHO;
state->packet->icmp_code=0;
/* icmp_id is set by icmp_send */
state->packet->icmp_seq = icp_seq++;
/* (TODO) target should be in the sockaddr format.
* Find something else to compute the packet size.
* The ttl is not handled.
*/
if (ttl>0) {
probe_set_option(handle, IPPROTO_IP, IP_TTL, &ttl, 4);
}
res=icmp_send(state->icmp_handle,state->packet,
probe->size,
target,sizeof(*target));
if (res<0) {
return -1;
}
/* Now try to get the answer to our packet. Since we receive all ICMP
* packets for this host we may receive other packets before our own,
* we may even receive the packet that we just sent !
*/
while (1) {
src_addr_size=sizeof(probe->src_addr);
res=icmp_recv(state->icmp_handle,
probe->contents,probe->size+sizeof(struct ip),
&probe->src_addr,&src_addr_size,
&probe->rtt);
if (res>0) {
struct ip* ip;
struct icmp* icp;
/* On linux the returned size does not take into account the ip header !
* Furthermore the ip->ip_len field does not take into account endianess
* issues. That's why I'm using res+(ip->ip_hl<<2).
*/
/* ip header */
ip=(struct ip*)(probe->contents);
/* icmp header */
icp=(struct icmp*)(probe->contents+(ip->ip_hl<<2));
probe->dst_addr.sa_family=PF_INET;
memcpy(&((struct sockaddr_in*)(&probe->dst_addr))->sin_addr,&ip->ip_dst,4);
((struct sockaddr_in*)&(probe->dst_addr))->sin_port=0;
switch (icp->icmp_type) {
case ICMP_ECHOREPLY:
/*DEBUG_MSG((stderr,"icmp_type/code=%d/%d rtt=%g",icp->icmp_type,icp->icmp_code,probe->rtt));*/
if ((icp->icmp_seq+1==icp_seq) && (icp->icmp_id==icmp_get_id(state->icmp_handle))) {
probe->size=res+(ip->ip_hl<<2);
return BP_RES_HIT;
} else {
printf("received a stale Echo Reply id=%d seq=%d\n",icp->icmp_id,icp->icmp_seq);
}
break;
case ICMP_UNREACH:
DEBUG_MSG((stderr,"icmp_type/code=%d/%d rtt=%g",icp->icmp_type,icp->icmp_code,probe->rtt));
if (icp->icmp_code==ICMP_UNREACH_PORT) {
probe->size=res+(ip->ip_hl<<2);
return BP_RES_HIT;
}
break;
case ICMP_TIMXCEED:
DEBUG_MSG((stderr,"icmp_type/code=%d/%d rtt=%g",icp->icmp_type,icp->icmp_code,probe->rtt));
if (icp->icmp_code==ICMP_TIMXCEED_INTRANS) {
probe->size=res+(ip->ip_hl<<2);
return BP_RES_TTL_EXCEEDED;
}
break;
case ICMP_ECHO:
/* this is most likely the echo that we sent */
break;
default:
DEBUG_MSG((stderr,"icmp_type/code=%d/%d",icp->icmp_type,icp->icmp_code));
dump_packet(probe->contents,probe->size);
}
} else if (res==0) {
DEBUG_MSG((stderr,"probe timeout"));
return BP_RES_TIMEOUT;
} else {
DEBUG_MSG((stderr,"res=%d errno=%d",res,errno));
return res;
}
/*DEBUG_MSG((stderr,"*** retrying"));*/
}
break;
}
return ret;
}
int probe_close(bp_handle handle)
{
bp_state_t* state;
if (handle==NULL) {
errno=EINVAL;
return -1;
}
state=handle2state(handle);
if (state->pattern!=NULL)
free(state->pattern);
icmp_close(state->icmp_handle);
if (state->packet!=NULL)
free(state->packet);
free(state);
return 0;
}
|