#include #include #include #include #include #include #include #include /* ICMP packet declaration */ #include "ping.h" #include "abwm.h" struct sockaddr_in whereto; /* Who to ping */ struct sockaddr_in from; /* Our (source) address */ static int icmp_socket; /* Socket file descriptor */ hw_time_t last_time; hw_time_t this_time; hw_time_t recv_time; hw_time_t rtt = (hw_time_t) 0; /* round trip time of packet */ hw_time_t rtts[MAX_PROBES]; /* rtt of each packet */ hw_time_t depart[MAX_PROBES]; /* dparture time of each packet */ hw_time_t arrive[MAX_PROBES]; /* arrival time of each packet */ int bytes[MAX_PROBES]; /* size of each packet */ u_char packets[MAX_PROBES][MAXPACKET]; /* Incoming packet */ u_char outpack[MAXPACKET]; /* Outgoing packet */ int datalen = 1480 - ICMP_MINLEN; /* How much data (was 64) */ /* did this packet come in order ?*/ int orderOK[MAX_PROBES] = {0,0,0,0,0,0,0,0,0,0}; int returned[MAX_PROBES] = {0,0,0,0,0,0,0,0,0,0}; int npackets; int numPaths = 0; /* how many paths did the packets take ? */ int routeData = 0; /* did we find RR data ? */ int inOrder = 1; /* were the packets in order ? */ int duplicatePkts = 0; /* were there any duplicate packets ? */ int packet_id; u_char optval[MAX_IPOPTLEN]; optstr *sumopt = NULL, *curopt; /* BC for RR option */ volatile int TimeOutFlag; #define MAX_PROBES 100 int equalBWTimeGap = 1; int msgSize = 600; int noOfMsgs = 10; int debug = 0; int packetSize[MAX_PROBES]; double sendingTime[MAX_PROBES]; /* sending time of each packet */ double receivingTime[MAX_PROBES]; /* receiving time of each packet */ double receivingTimeAA[MAX_PROBES]; /* receiving time of each packet */ double sendingTimeGap[MAX_PROBES]; /* sending time gap of each packet */ double absSendingTimeGap[MAX_PROBES];/* absolute sending time gap */ double receivingTimeGap[MAX_PROBES]; double absReceivingTimeGap[MAX_PROBES]; double gapDifference[MAX_PROBES]; double absGapDifference[MAX_PROBES]; double bwrLowerBound, bwrUpperBound; BWRange bwr; BWRange curBWR; struct timeval tv1, tv2, stv[100], etv[100], starttv, tv; struct timezone tz; char *cp, *ap; char *HostName, *hostname, *my_name; int fp; struct protoent *proto; /* the following shared memory code is in Advanced Unix Programming p. 194 */ #define MAXMSG 4096 #define MAXOPEN 20 #define BADADDR (char *) (-1) int sndsid, rcvsid, segid; char *addr; static int findinfo(key, sndsidp, rcvsidp, addrp, segidp) int key; int *sndsidp, *rcvsidp, *segidp; char **addrp; { static struct { int segid; /* segment id */ int key; /* key */ char *addr; /* addr of shared memory segment */ int sndsid; /* semaphore-ID for writing totalBytes */ int rcvsid; /* semaphore-ID for reading totalBytes */ } sems[MAXOPEN]; int i, avail; extern int errno; char *shmat(); avail = -1; for (i=0; iip_hl << 2; cp = (unsigned char *) ip + 20; /* point to options */ printf("Vr HL TOS Len ID Flg off TTL Pro cks Src Dst Data\n"); printf(" %1d %2d %02x %04d %04x", ip->ip_v, ip->ip_hl, ip->ip_tos, ip->ip_len, ip->ip_id); printf(" %1x %04x", ((ip->ip_off) & 0xe000) >> 13, (ip->ip_off) & 0x1fff); printf(" %3d %3d %04x", ip->ip_ttl, ip->ip_p, ip->ip_sum); bobsInAddr.s_addr = ip->ip_src.s_addr; bobString1 = inet_ntoa(bobsInAddr); bobsInAddr.s_addr = ip->ip_dst.s_addr; bobString2 = inet_ntoa(bobsInAddr); printf(" %-15s %-15s ", bobString1, bobString2); /* printf(" %-15s %-15s ", inet_ntoa(ip->ip_src.s_addr), inet_ntoa(ip->ip_dst.s_addr)); */ /* dump and option bytes */ while (hlen-- > 20) { printf("%02x", *cp++); } printf("\n"); } /* * Dump some info on a returned (via ICMP) IP packet. */ void pr_retip(struct ip *ip) { int hlen; unsigned char *cp; pr_iph(ip); hlen = ip->ip_hl << 2; cp = (unsigned char *) ip + hlen; if (ip->ip_p == 6) { printf("TCP: from port %d, to port %d (decimal)\n", (*cp * 256 + *(cp + 1)), (*(cp + 2) * 256 + *(cp + 3))); } else if (ip->ip_p == 17) { printf("UDP: from port %d, to port %d (decimal)\n", (*cp * 256 + *(cp + 1)), (*(cp + 2) * 256 + *(cp + 3))); } } /* * * Subtract 2 hw_time_t's (which are in microseconds). * Clock rolls over every 40*2^32 ns (171 sec) so this * routine is not guaranteed if measurements are more * than 171 sec apart. * */ hw_time_t timesub(hw_time_t *first, hw_time_t *second) { hw_time_t result; /* since this is a 32 bit counter, wraparound works fine */ result = *first - *second; return result; } /* * Return an ascii host address * as a dotted quad and optionally with a hostname */ char *pr_addr(struct in_addr addr) { struct hostent *hp; static char buf[80]; if ((hp = gethostbyaddr(&addr.s_addr, 4, AF_INET)) == NULL) sprintf(buf, "%s", inet_ntoa(addr)); else sprintf(buf, "%s (%s)", hp->h_name, inet_ntoa(addr)); return (buf); } void print_ip_msg(u_char buf[], int count) { /* register int i; for (i=0; iicmp_type, icp->icmp_code, icp->icmp_cksum, icp->icmp_seq, icp->icmp_id, *((hw_time_t *) icp+8)); */ } /* Parse a numeric IP address or symbolic host name and return a sockaddr_in */ char *a2sockaddr(struct sockaddr_in *addr, char *cp, char *name) { static struct hostent *hp; static char buf[80]; #undef HOST_NOT_FOUND #define HOST_NOT_FOUND 0 /* #define h_errno 0 */ #define h_nerr 1 const char *h_errlist[h_nerr] = { "unknown host" }; bzero((caddr_t) addr, sizeof(*addr)); addr->sin_family = AF_INET; addr->sin_addr.s_addr = inet_addr(cp); if (addr->sin_addr.s_addr == -1) { hp = (struct hostent *)NULL; hp = gethostbyname(cp); if (hp) { addr->sin_family = hp->h_addrtype; bcopy(hp->h_addr, (caddr_t) & addr->sin_addr, hp->h_length); cp = hp->h_name; } else { sprintf(buf, "%s: %s", h_errlist[h_errno < h_nerr ? h_errno : HOST_NOT_FOUND], cp); return buf; } } if (name) { strncpy(name, cp, MAXHOSTNAMELEN); } return (char *) 0; } /* * tv_sub -- * Subtract 2 timeval structs: out = out - in. Out is assumed to * be >= in. */ int tv_sub(register struct timeval *out, register struct timeval *in) { return (out->tv_sec - in->tv_sec)*1000000+(out->tv_usec - in->tv_usec); } /* * tvsub -- * Subtract 2 timeval structs: out = out - in. Out is assumed to * be >= in. */ void tvsub(register struct timeval *out, register struct timeval *in) { if ((out->tv_usec -= in->tv_usec) < 0) { --out->tv_sec; out->tv_usec += 1000000; } out->tv_sec -= in->tv_sec; } /* * I N _ C K S U M * * Checksum routine for Internet Protocol family headers (C Version) * */ u_short in_cksum(u_short *addr, int len) { register int nleft = len; register u_short *w = addr; register u_short answer; register int sum = 0; /* * Our algorithm is simple, using a 32 bit accumulator (sum), * we add sequential 16 bit words to it, and at the end, fold * back all the carry bits from the top 16 bits into the lower * 16 bits. */ while (nleft > 1) { sum += *w++; nleft -= 2; } /* mop up an odd byte, if necessary */ if (nleft == 1) { sum += *(u_char *) w; } /* * add back carry outs from top 16 bits to low 16 bits */ sum = (sum >> 16) + (sum & 0xffff); /* add hi 16 to low 16 */ sum += (sum >> 16); /* add carry */ answer = ~sum; /* truncate to 16 bits */ return (answer); } /* * P R _ P A C K * * Print out the packet, if it came from us. This logic is necessary * because ALL readers of the ICMP socket get a copy of ALL ICMP packets * which arrive ('tis only fair). This permits multiple copies of this * program to be run without having intermingled output (or statistics!). */ int recv_icmp(int s, u_char *buf, int len, int i) { int cc; struct sockaddr_in from; int fromlen = (sizeof from); struct ip *ip; register struct icmp *icp; int hlen, dupflag, matchflag = 1; u_char *ipopt; if ((cc = recvfrom(s, buf, len, 0, &from, &fromlen)) < 0) { if (errno != EINTR) fprintf(stderr, "ping: recvfrom"); return (cc); } /* Chow: this_time is relative to starttv and in 1 usec */ (void)gettimeofday((struct timeval *)&etv[i], (struct timezone *)&tz); ip = (struct ip *) buf; hlen = ip->ip_hl << 2; /* Is this packet the right length? */ if (cc < hlen + ICMP_MINLEN) { /* fprintf(stderr, "packet too short (%d bytes) from %s\n", cc, inet_ntoa(from.sin_addr)); */ return (-1); } cc -= hlen; icp = (struct icmp *) (buf + hlen); if (debug) { printf("%d bytes (no including ip header %d bytes) from %s:\n", cc, hlen, pr_addr(from.sin_addr)); printf("msg received in hexadecimal:\n"); print_ip_msg(buf, cc+hlen); printf("\n"); print_icmp_msg(icp); } /* is this packet the right type? */ if (icp->icmp_type != ICMP_ECHOREPLY && icp->icmp_type != ICMP_TSTAMPREPLY) { /* fprintf(stderr, "wrong type: len=%d addr=%s: ", cc, pr_addr(from.sin_addr)); */ pr_icmph(icp); return (-1); } /* was it our ECHO? */ if (icp->icmp_id != out_icmp->icmp_id) return (-1); /* move the rest of the code to packetAnalysis() executed after after all packets received. It avoids missing the next sending time */ /* calculate this packet's rtt in ms */ packet_id = icp->icmp_seq; return (cc+hlen); } /* * Print a descriptive string about an ICMP header. */ void pr_icmph(struct icmp *icp) { switch (icp->icmp_type) { case ICMP_ECHOREPLY: printf("Echo Reply\n"); /* XXX ID + Seq + Data */ break; case ICMP_UNREACH: switch (icp->icmp_code) { case ICMP_UNREACH_NET: printf("Destination Net Unreachable\n"); break; case ICMP_UNREACH_HOST: printf("Destination Host Unreachable\n"); break; case ICMP_UNREACH_PROTOCOL: printf("Destination Protocol Unreachable\n"); break; case ICMP_UNREACH_PORT: printf("Destination Port Unreachable\n"); break; case ICMP_UNREACH_NEEDFRAG: printf("frag needed and DF set\n"); break; case ICMP_UNREACH_SRCFAIL: printf("Source Route Failed\n"); break; default: printf("Dest Unreachable, Bad Code: %d\n", icp->icmp_code); break; } /* Print returned IP header information */ pr_retip((struct ip *)icp->icmp_data); break; case ICMP_SOURCEQUENCH: printf("Source Quench\n"); pr_retip((struct ip *)icp->icmp_data); break; case ICMP_REDIRECT: switch (icp->icmp_code) { case ICMP_REDIRECT_NET: printf("Redirect Network"); break; case ICMP_REDIRECT_HOST: printf("Redirect Host"); break; case ICMP_REDIRECT_TOSNET: printf("Redirect Type of Service and Network"); break; case ICMP_REDIRECT_TOSHOST: printf("Redirect Type of Service and Host"); break; default: printf("Redirect, Bad Code: %d", icp->icmp_code); break; } printf(" (New addr: 0x%08x)\n", icp->icmp_hun.ih_gwaddr); pr_retip((struct ip *)icp->icmp_data); break; case ICMP_ECHO: printf("Echo Request\n"); /* XXX ID + Seq + Data */ break; case ICMP_TIMXCEED: switch (icp->icmp_code) { case ICMP_TIMXCEED_INTRANS: printf("Time to live exceeded\n"); break; case ICMP_TIMXCEED_REASS: printf("Frag reassembly time exceeded\n"); break; default: printf("Time exceeded, Bad Code: %d\n", icp->icmp_code); break; } pr_retip((struct ip *)icp->icmp_data); break; case ICMP_PARAMPROB: printf("Parameter problem: pointer = 0x%02x\n", icp->icmp_hun.ih_pptr); pr_retip((struct ip *)icp->icmp_data); break; case ICMP_TSTAMP: printf("Timestamp\n"); /* XXX ID + Seq + 3 timestamps */ break; case ICMP_TSTAMPREPLY: printf("Timestamp Reply\n"); /* XXX ID + Seq + 3 timestamps */ break; case ICMP_IREQ: printf("Information Request\n"); /* XXX ID + Seq */ break; case ICMP_IREQREPLY: printf("Information Reply\n"); /* XXX ID + Seq */ break; case ICMP_MASKREQ: printf("Address Mask Request\n"); break; case ICMP_MASKREPLY: printf("Address Mask Reply\n"); break; default: printf("Bad ICMP type: %d\n", icp->icmp_type); } } void finish() { int i; /* set TimeOutFlag to stop spinning on recv socket */ TimeOutFlag = 1; printf("Timeout exceeded.\n"); return; } void help_msg() { printf("format: abwm -[dn:l:u:s:] hostname \n"); printf(" option: d set debug mode\n"); printf(" option: n set number of messages sent\n"); printf(" option: l set lower bound of probing bandwidth\n"); printf(" option: u set upper bound of probing bandwidth\n"); printf(" option: s set message size\n"); } void printsockaddr(struct sockaddr_in *arg) { struct hostent *hp; printf(" sockaddr_in: "); printf("Domain=%d, ", arg->sin_family); hp=gethostbyaddr((const char *)&(arg->sin_addr), sizeof(arg->sin_addr), AF_INET); printf("Hostname=%s, ", hp->h_name); printf("Port=%d, ", ntohs(arg->sin_port)); printf("Address=%s,\n", inet_ntoa(arg->sin_addr)); } void waitTime(int time) { (void) gettimeofday(&tv1, &tz); while (1) { (void) gettimeofday(&tv2, &tz); if ((tv2.tv_sec-tv1.tv_sec)*1000000+(tv2.tv_usec-tv1.tv_usec)- time>=-4) break; } } void waitRelativeTime(int time) { while (1) { (void) gettimeofday(&tv2, &tz); if ((tv2.tv_sec-starttv.tv_sec)*1000000+(tv2.tv_usec-starttv.tv_usec)- time>=-4) break; } } void setupICMPacket(){ /* Init packet fields */ out_icmp->icmp_id = getpid() & 0xFFFF; out_icmp->icmp_type = ICMP_ECHO; out_icmp->icmp_code = 0; /******** printf("get socket address %s\n", HostName); ********/ /* get Internet address from hostname or dot-notation */ if (cp = a2sockaddr(&whereto, HostName, hostname)) { fprintf(stderr, "%s: %s\n", my_name, cp); exit(1); } printsockaddr(&whereto); /******** printf("fill packet\n"); ********/ /* fill outgoing packet with position info */ for (fp = 0; fp < MAXDATA; fp++) { out_pattern[fp] = fp + sizeof(struct timeval); } /******** printf("open the socket\n"); ********/ /* open the socket */ if ((proto = getprotobyname("icmp")) == NULL) { fprintf(stderr, "icmp: unknown protocol\n"); exit(10); } if ((icmp_socket = socket(AF_INET, SOCK_RAW, proto->p_proto)) < 0) { fprintf(stderr, "rpr: socket"); exit(5); } /******** printf("set RR options\n"); ********/ /* BC 5-30-95 : add record route options from Ping+ */ /* RR IP_OPTIONS */ /* Chow: disable RR option difficult to pass most routers */ bzero(optval, sizeof(optval)); optval[IPOPT_OPTVAL] = (char) IPOPT_RR; optval[IPOPT_OLEN] = (char) MAX_IPOPTLEN - 1, optval[IPOPT_OFFSET] = (char) IPOPT_MINOFF; optval[MAX_IPOPTLEN - 1] = (char) IPOPT_EOL; if ((proto = getprotobyname("ip")) == NULL) { fprintf(stderr, "ip: unknown protocol\n"); exit(8); } if (setsockopt(icmp_socket, proto->p_proto, IP_OPTIONS, optval, MAX_IPOPTLEN) < 0) { perror("setsockopt: IP_OPTIONS"); exit(42); } /* */ /* RR IP_OPTIONS */ (void) gettimeofday(&starttv, &tz); ap = asctime(localtime((const time_t *)&starttv.tv_sec)); ap[strlen(ap) -1] = '\0' ; } void sendMsg(int reqID, hw_time_t this_time){ /* send probing msg out */ datalen = packetSize[reqID]; bytes[reqID] = datalen; returned[reqID] = 0; if (debug) printf("sendMsg(): ready to send icmp msg reqID=%d, this_time=%d\n", reqID, this_time); send_icmp(icmp_socket, reqID, &whereto); if (debug) printf("sendMsg(): icmp msg sent, reqID=%d, this_time=%d\n", reqID, this_time); reqID++; } /* * P I N G E R * * Compose and transmit an ICMP ECHO REQUEST packet. The IP packet * will be added on by the kernel. The ID field is our UNIX process ID, * and the sequence number is an ascending integer. The first 8 bytes * of the data portion are used to hold a UNIX "timeval" struct in VAX * byte-order, to compute the round-trip time. */ void send_icmp(int s, int seq, struct sockaddr_in *addr) { int i, cc; int lt; struct timeval tv; struct icmp *icp; out_icmp->icmp_cksum = 0; out_icmp->icmp_seq = seq; cc = datalen + ICMP_MINLEN; /* skips ICMP portion */ /* put current time into the packet header */ /* Chow: this_time is relative to starttv and in 1 usec */ (void)gettimeofday((struct timeval *)&tv, (struct timezone *)&tz); tvsub(&tv, &starttv); this_time = tv.tv_sec * 1000000 + tv.tv_usec; *out_tp = this_time; depart[seq] = this_time; /* Compute ICMP checksum here */ out_icmp->icmp_cksum = in_cksum((u_short *)out_icmp, cc); last_time = this_time; /* Note that now this_time is different from what in the ICMP packet header since it include the checksum calculation time. This may lead to some calculation error. */ /* printf("sending...%d bytes", cc); */ /* print_icmp_msg(out_icmp); */ i = sendto(s, outpack, cc, 0, addr, sizeof(*addr)); if (i != cc) { fprintf(stderr, "error: tried to write %d chars to %s, ret=%d\n", cc, inet_ntoa(addr->sin_addr), i); } } void setupNextIteration(double bwrLowerBound, double bwrUpperBound) { int i; /* initialize receiving time vector */ for (i = 1; i< noOfMsgs; i++) { receivingTimeGap[i] = 0; receivingTime[i] = 0; } /* initialize sendingTime Vector */ sendingTime[1] = 0.01; /* startProbeTime */ sendingTimeGap[0] = 0.01; printf("sendingTime[1]=%f\n", sendingTime[1]); for (i = 1; i< noOfMsgs; i++) { if (equalBWTimeGap) { sendingTimeGap[i]= msgSize/(bwrLowerBound+i*(bwrUpperBound-bwrLowerBound)/(noOfMsgs-1)); sendingTime[i+1] = sendingTime[i]+sendingTimeGap[i]; if (debug) printf("eqTgap: sendingTime[%d]=%f, sendingTimeGap[%d]=%f\n", i+1, sendingTime[i+1], i, sendingTimeGap[i]); } else { double intervalStepA = msgSize*(bwrUpperBound-bwrLowerBound)/(bwrUpperBound*bwrLowerBound*(noOfMsgs-2)); sendingTimeGap[i]= msgSize/bwrLowerBound-intervalStepA*(i-1); sendingTime[i+1] = sendingTime[i]+sendingTimeGap[i]; if (debug) printf("noneqTgap: sendingTime[%d]=%f\n",i+1, sendingTime[i+1]); } } for (i=1; i<=noOfMsgs; i++) { absSendingTimeGap[i] = sendingTime[i]-sendingTime[1]; } for (i=1; i 0) bwrLowerBound = lbw; break; case 'u': if ((ubw = atoi(optarg)) > 0) bwrUpperBound = ubw; break; case 's': if ((msgSize = atoi(optarg)) < 1) msgSize = 600; if (debug) printf("msgSize is set to %d\n", msgSize); break; case '?': help_msg(); exit (1); default: fprintf (stderr, "unrecognized arg >%s<\n", optarg); help_msg(); exit (1); } } printf("Lower bound of probing bandwidth is set to %g\n", bwrLowerBound); printf("Upper bound of probing bandwidth is set to %g\n", bwrUpperBound); HostName = argv[optind++]; printf("HostName=%s\n", HostName); setupICMPacket(); bwrangeInit(); printf("Setup ICMP Packet\n"); if (fork()==0) { sendProbingMsg(icmp_socket); printf("sendProbingMsg()\n"); departPackets(); exit(); } else { receiveProbingMsg(icmp_socket); } }