/* * scamper_file_traceroute.c * * $Id: scamper_file_traceroute.c,v 1.44 2007/05/07 22:20:00 mjl Exp $ * * code to read scamper's traceroute-like file format into scamper_hop * structures. * * Copyright (C) 2004-2006 The University of Waikato * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 2. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #if defined(__linux__) #define __FAVOR_BSD #endif #include #include #if defined(__APPLE__) #include #endif #include #if defined(DMALLOC) #include #endif #include "scamper_addr.h" #include "scamper_list.h" #include "scamper_tlv.h" #include "scamper_trace.h" #include "scamper_ping.h" #include "scamper_file.h" #include "scamper_file_traceroute.h" #include "utils.h" #if !defined(ICMP_UNREACH_FILTER_PROHIB) #if defined(ICMP_UNREACH_ADMIN_PROHIBIT) /* NetBSD */ #define ICMP_UNREACH_FILTER_PROHIB ICMP_UNREACH_ADMIN_PROHIBIT #else #define ICMP_UNREACH_FILTER_PROHIB 13 #endif #endif #if !defined(ICMP6_DST_UNREACH_BEYONDSCOPE) #if defined(ICMP6_DST_UNREACH_NOTNEIGHBOR) #define ICMP6_DST_UNREACH_BEYONDSCOPE ICMP6_DST_UNREACH_NOTNEIGHBOR #else #define ICMP6_DST_UNREACH_BEYONDSCOPE 2 #endif #endif #if !defined(TH_ECE) #define TH_ECE 0x40 #endif #if !defined(TH_CWR) #define TH_CWR 0x80 #endif #define ISEOL(c) ((c) == '\n' || (c) == '\0' || (c) == '\r') #define ISSPACE(c) isspace((int)c) /* ((c) == ' ' || (c) == '\t') */ #define ISQUOTE(c) ((c) == '"') #define ISCOMMENT(c) ((c) == '#') #define ISNUMBER(c) isdigit((int)c) /* ((c) >= '0' && (c) <= '9') */ /* * file_state * * this struct keeps state for reading / writing traceroute-like files. * this struct is only used when reading from these types of files. */ typedef struct file_state { FILE *file; } file_state_t; /* * gettokens * * parse the line into tokens, modifying the contents of line in the * process and returning a pointer to the tokens, and how many tokens in there * are valid */ static int gettokens(char *line, char *tokens[], int *num_tokens) { char *ptr; int i; /* sanity check */ if(line == NULL || tokens == NULL || num_tokens == NULL) { return 0; } /* ignore any white space to begin with */ ptr = line; while(ISSPACE(*ptr)) { ptr++; } /* * if there are no tokens on this line, or this line is a comment then we * return now and say there are no tokens of interest */ if(ISEOL(*ptr) || ISCOMMENT(*ptr)) { *num_tokens = 0; return 1; } /* * find as many tokens as we can hold in the string */ for(i = 0; i < *num_tokens; i++) { /* * we need to zip along to the next token, which could have arbirary * white space * */ while(ISSPACE(*ptr) && !ISEOL(*ptr)) { ptr++; } if(ISEOL(*ptr)) { break; } /* * if the token starts with a quote it is a string so we parse it as such */ if(ISQUOTE(*ptr)) { tokens[i] = ++ptr; while(!ISEOL(*ptr) && !ISQUOTE(*ptr)) { ptr++; } if(ISEOL(*ptr)) { printf("gettokens: no matching '\"'\n"); return 0; } /* * terminate this token, and then start looking for the next one */ *ptr = '\0'; ptr++; } /* * its a string, but not in quotes so we stop this token as soon as we * get to whitespace */ else { tokens[i] = ptr++; while(!ISSPACE(*ptr) && !ISEOL(*ptr)) { ptr++; } if(ISEOL(*ptr)) { *ptr = '\0'; } else { *ptr = '\0'; ptr++; } } } *num_tokens = i; return 1; } /* * msec_to_timeval * * this function converts the ms string into a numerical representation * in tv. */ static int msec_to_timeval(const char *ms, struct timeval *tv) { char *tmp; int i; char *d; assert(ms != NULL); assert(tv != NULL); /* make a copy of the ms string, because we're going to be modifying it */ if((d = strdup(ms)) == NULL) { return -1; } tmp = d; /* look for the decimal place */ while(tmp[0] >= '0' && tmp[0] <= '9') { tmp++; } if(tmp[0] != '.') { free(d); return -1; } /* * once we've found the decimal place, shift the 3 digits to the right of * it over so we can convert the entire thing into a millisecond held in * an integer */ i = 0; while((tmp[1] >= '0' && tmp[1] <= '9') && i < 3) { tmp[0] = tmp[1]; tmp++; i++; } if(tmp[1] != '\0' || i != 3) { free(d); return -1; } tmp[0] = '\0'; i = atoi(d); free(d); tv->tv_sec = (i / 1000); tv->tv_usec = (i % 1000) * 1000; return 0; } static char *addr_tostr(const scamper_addr_t *addr,char *buf,const size_t len) { if(addr != NULL) { scamper_addr_tostr(addr, buf, len); } else { snprintf(buf, len, "*"); } return buf; } /* * icmp_tostr * * the caller must pass a pointer to a str buffer at least 14 chars in length * to be safe. */ static char *icmp_tostr(const scamper_trace_hop_t *hop, char *str, const size_t len) { if((hop->hop_flags & SCAMPER_TRACE_HOP_FLAG_TCP) != 0) { if((hop->hop_tcp_flags & TH_RST) != 0) { snprintf(str, len, " [closed]"); } else if((hop->hop_tcp_flags & (TH_SYN|TH_ACK)) == (TH_SYN|TH_ACK)) { if((hop->hop_tcp_flags & TH_ECE) != 0) snprintf(str, len, " [open, ecn]"); else snprintf(str, len, " [open]"); } else { if(hop->hop_tcp_flags == 0) snprintf(str, len, " [unknown, no flags]"); else snprintf(str, len, " [unknown,%s%s%s%s%s%s%s%s]", (hop->hop_tcp_flags & TH_RST) ? " RST" : "", (hop->hop_tcp_flags & TH_SYN) ? " SYN" : "", (hop->hop_tcp_flags & TH_ACK) ? " ACK" : "", (hop->hop_tcp_flags & TH_PUSH) ? " PSH" : "", (hop->hop_tcp_flags & TH_FIN) ? " FIN" : "", (hop->hop_tcp_flags & TH_URG) ? " URG" : "", (hop->hop_tcp_flags & TH_CWR) ? " CWR" : "", (hop->hop_tcp_flags & TH_ECE) ? " ECE" : ""); } } else if(SCAMPER_TRACE_HOP_IS_ICMP_TTL_EXP(hop) || SCAMPER_TRACE_HOP_IS_ICMP_ECHO_REPLY(hop)) { str[0] = '\0'; } else if(hop->hop_addr->type == SCAMPER_ADDR_TYPE_IPV4) { if(hop->hop_icmp_type == ICMP_UNREACH) { switch(hop->hop_icmp_code) { case ICMP_UNREACH_FILTER_PROHIB: snprintf(str, len, " !X"); break; case ICMP_UNREACH_HOST: snprintf(str, len, " !H"); break; case ICMP_UNREACH_NEEDFRAG: snprintf(str, len, " !F"); break; case ICMP_UNREACH_SRCFAIL: snprintf(str, len, " !S"); break; case ICMP_UNREACH_PROTOCOL: snprintf(str, len, " !P"); break; case ICMP_UNREACH_NET: snprintf(str, len, " !N"); break; case ICMP_UNREACH_PORT: str[0] = '\0'; break; default: snprintf(str, len, " !<%d>", hop->hop_icmp_code); break; } } else { snprintf(str,len," !<%d,%d>",hop->hop_icmp_type,hop->hop_icmp_code); } } else if(hop->hop_addr->type == SCAMPER_ADDR_TYPE_IPV6) { if(hop->hop_icmp_type == ICMP6_DST_UNREACH) { switch(hop->hop_icmp_code) { case ICMP6_DST_UNREACH_ADDR: snprintf(str, len," !A"); break; case ICMP6_DST_UNREACH_BEYONDSCOPE: snprintf(str, len," !S"); break; case ICMP6_DST_UNREACH_ADMIN: snprintf(str, len," !P"); break; case ICMP6_DST_UNREACH_NOROUTE: snprintf(str, len," !N"); break; case ICMP6_DST_UNREACH_NOPORT: str[0] = '\0'; break; default: snprintf(str, len, " !<%d>", hop->hop_icmp_code); break; } } else if(hop->hop_icmp_type == ICMP6_PACKET_TOO_BIG) { snprintf(str,len," !F"); } else { snprintf(str,len," !<%d,%d>",hop->hop_icmp_type,hop->hop_icmp_code); } } return str; } static char *rtt_tostr(const struct timeval *rtt, char *str, const size_t len) { uint32_t usec = (rtt->tv_sec * 1000000) + rtt->tv_usec; snprintf(str, len, "%d.%03d", usec / 1000, usec % 1000); return str; } /* * header_tostr * */ static char *header_tostr(const scamper_trace_t *trace) { char src[64], dst[64], header[192]; if(trace->dst == NULL) { return NULL; } scamper_addr_tostr(trace->dst, dst, sizeof(dst)); if(trace->src != NULL) { scamper_addr_tostr(trace->src, src, sizeof(src)); snprintf(header, sizeof(header), "traceroute from %s to %s", src, dst); } else { snprintf(header, sizeof(header), "traceroute to %s", dst); } return strdup(header); } /* * hop_to_str * * given a hop (with other hops possibly linked to it) create a string that * holds the hop. */ static char *hop_tostr(const scamper_trace_t *trace, const int h) { scamper_trace_hop_t *hop; char *str = NULL; char **str_addrs = NULL; size_t *len_addrs = NULL; char **str_rtts = NULL; size_t *len_rtts = NULL; size_t len; int i; char str_hop[128]; char str_addr[64]; char str_rtt[24]; char str_icmp[24]; int spare; int allattempts; allattempts = (trace->flags & SCAMPER_TRACE_FLAG_ALLATTEMPTS) ? 1 : 0; /* if we got no responses at all for this hop */ if(trace->hops[h] == NULL) { if(allattempts == 0) { snprintf(str_hop, sizeof(str_hop), "%2d *", h+1); str = strdup(str_hop); } else if((str = malloc((len = 4 + (2 * trace->attempts)))) != NULL) { snprintf(str, len, "%2d ", h+1); for(i=0; iattempts; i++) { str[4+(i*2)] = '*'; str[4+(i*2)+1] = ' '; } str[4+((i-1)*2)+1] = '\0'; } return str; } /* if we only care about the first response, the print that out */ if(allattempts == 0) { hop = trace->hops[h]; scamper_addr_tostr(hop->hop_addr, str_addr, sizeof(str_addr)); rtt_tostr(&hop->hop_rtt, str_rtt, sizeof(str_rtt)); icmp_tostr(hop, str_icmp, sizeof(str_icmp)); snprintf(str_hop, sizeof(str_hop), "%2d %s %s ms%s", h+1, str_addr, str_rtt, str_icmp); return strdup(str_hop); } /* we have to print out the attempt made for each hop */ len = sizeof(char *) * trace->attempts; if((str_addrs = malloc_zero(len)) == NULL) { goto out; } if((str_rtts = malloc_zero(len)) == NULL) { goto out; } /* keep track of the length of each string in the arrays */ len = sizeof(size_t) * trace->attempts; if((len_addrs = malloc_zero(len)) == NULL) { goto out; } if((len_rtts = malloc_zero(len)) == NULL) { goto out; } /* for each response we have, record an entry in the array */ spare = 0; for(hop = trace->hops[h]; hop != NULL; hop = hop->hop_next) { assert(hop->hop_probe_id > 0); /* if we get two responses for the same probe, only print the first */ if(str_addrs[hop->hop_probe_id-1] != NULL) { continue; } /* * calculate the length of the address to record for this hop probe, * and then generate and store the string */ addr_tostr(hop->hop_addr, str_addr, sizeof(str_addr)); len = strlen(str_addr); if((str_addrs[hop->hop_probe_id-1] = malloc(len+1)) == NULL) { goto out; } memcpy(str_addrs[hop->hop_probe_id-1], str_addr, len+1); len_addrs[hop->hop_probe_id-1] = len; /* * calculate the length of the rtt and icmp data for this hop probe, * and then generate and store the string */ rtt_tostr(&hop->hop_rtt, str_rtt, sizeof(str_rtt)); icmp_tostr(hop, str_icmp, sizeof(str_icmp)); len = strlen(str_rtt) + 3 + strlen(str_icmp); if((str_rtts[hop->hop_probe_id-1] = malloc(len+1)) == NULL) { goto out; } snprintf(str_rtts[hop->hop_probe_id-1],len+1,"%s ms%s",str_rtt,str_icmp); len_rtts[hop->hop_probe_id-1] = len; } /* * go through and figure how long our string should be * we reserve 5 characters to start with so that we can print 3 digits * hop number + 2 digits space ahead of the hop information. */ len = 5; spare = -1; for(i=0; iattempts; i++) { /* if no data for this probe, then print '* ' */ if(str_addrs[i] == NULL) { len += 2; } /* * if we've printed an address before, check to see if it is the same * as the previous address printed. if so, we just have to print the * rtt and be done */ else if(spare != -1 && strcmp(str_addrs[spare], str_addrs[i]) == 0) { len += len_rtts[i] + 2; } /* print out the IP address and the RTT to the hop */ else { spare = i; len += len_addrs[i] + 2 + len_rtts[i] + 2; } } /* allocate a string long enough to store the hop data */ if((str = malloc(len)) == NULL) { goto out; } /* build the string up */ snprintf(str, len, "%2d ", h+1); len = strlen(str); spare = -1; for(i=0; iattempts; i++) { if(str_addrs[i] == NULL) { str[len++] = '*'; str[len++] = ' '; } else if(spare != -1 && strcmp(str_addrs[spare], str_addrs[i]) == 0) { memcpy(str+len, str_rtts[i], len_rtts[i]); len += len_rtts[i]; str[len++] = ' '; str[len++] = ' '; } else { spare = i; memcpy(str+len, str_addrs[i], len_addrs[i]); len += len_addrs[i]; str[len++] = ' '; str[len++] = ' '; memcpy(str+len, str_rtts[i], len_rtts[i]); len += len_rtts[i]; str[len++] = ' '; str[len++] = ' '; } } /* cut off the unnecessary trailing white space */ while(str[len-1] == ' ') len--; str[len] = '\0'; out: /* clean up */ if(str_addrs != NULL) { for(i=0; iattempts; i++) { if(str_addrs[i] != NULL) free(str_addrs[i]); } free(str_addrs); } if(str_rtts != NULL) { for(i=0; iattempts; i++) { if(str_rtts[i] != NULL) free(str_rtts[i]); } free(str_rtts); } if(len_addrs != NULL) free(len_addrs); if(len_rtts != NULL) free(len_rtts); return str; } static char *mtu_tostr(const int mtu, const int size) { char str[24]; if(mtu != size) { snprintf(str, sizeof(str), " [*mtu: %d]", size); } else { snprintf(str, sizeof(str), " [mtu: %d]", mtu); } return strdup(str); } /* * scamper_file_traceroute_write * * return 0 on successful write, -1 otherwise. */ int scamper_file_traceroute_write_trace(const scamper_file_t *sf, const scamper_trace_t *trace) { /* current return code */ int ret = -1; /* variables for creating the string representing the trace */ int i; size_t len; char *str = NULL; char *header = NULL; char **hops = NULL; size_t *hop_lens = NULL; char **mtus = NULL; size_t *mtu_lens = NULL; /* variables for creating mtu strings */ scamper_trace_hop_t *hop; uint16_t mtu; uint16_t size; uint8_t turn_ttl; /* variables for writing to the file */ off_t off = 0; int fd; size_t wc; if(trace->dst->type != SCAMPER_ADDR_TYPE_IPV4 && trace->dst->type != SCAMPER_ADDR_TYPE_IPV6) { return -1; } /* * get the current offset into the file, incase the write fails and a * truncation is required */ fd = scamper_file_getfd(sf); if(fd != 1 && (off = lseek(fd, 0, SEEK_CUR)) == -1) { return -1; } if((hops = malloc_zero(sizeof(char *) * trace->hop_count)) == NULL) { return -1; } if((hop_lens = malloc(sizeof(size_t) * trace->hop_count)) == NULL) { goto cleanup; } /* get a string that specifies the source and destination of the trace */ header = header_tostr(trace); len = strlen(header) + 2; for(i=0; i < trace->hop_count; i++) { if((hops[i] = hop_tostr(trace, i)) == NULL) { goto cleanup; } hop_lens[i] = strlen(hops[i]); len += hop_lens[i]; } /* if we have PMTU data to print for the trace, then write it too */ if(trace->pmtud != NULL) { if((mtus = malloc_zero(sizeof(char *) * trace->hop_count)) == NULL) { goto cleanup; } /* * if we did not get any responses from the path, then the path MTU * is zero */ if((hop = trace->pmtud->hops) == NULL) { mtu = size = trace->pmtud->pmtu; } else { mtu = trace->pmtud->ifmtu; SCAMPER_TRACE_PMTUD_GET_OUTMTU(trace->pmtud, size); } for(i=0; ihop_count; i++) { /* no response for this hop */ if(trace->hops[i] == NULL) { mtus[i] = NULL; continue; } /* if there is no pmtud data then skip this bit */ if(hop == NULL) { continue; } /* * if this hop has the same address as an ICMP message, then * change the MTU to reach the next hop after recording the size * of the packet that reached this hop successfully */ if(scamper_addr_cmp(hop->hop_addr, trace->hops[i]->hop_addr) == 0) { if((mtus[i] = mtu_tostr(mtu, size)) == NULL) { goto cleanup; } if(SCAMPER_TRACE_HOP_IS_ICMP_PACKET_TOO_BIG(hop)) { SCAMPER_TRACE_HOP_GET_NHMTU(hop, mtu); size = mtu; } hop = hop->hop_next; if(hop == NULL) size = trace->pmtud->pmtu; else size = hop->hop_probe_size; continue; } /* * if this hop has the same ttl as the probe packet, then the * egress interface returned the frag required message. record * the MTU for the current working hop */ SCAMPER_TRACE_HOP_GET_TURN_TTL(hop, turn_ttl); if(i >= hop->hop_probe_ttl - turn_ttl) { if(SCAMPER_TRACE_HOP_IS_ICMP_PACKET_TOO_BIG(hop)) { SCAMPER_TRACE_HOP_GET_NHMTU(hop, mtu); size = mtu; } if((mtus[i] = mtu_tostr(mtu, size)) == NULL) { goto cleanup; } hop = hop->hop_next; if(hop == NULL) size = trace->pmtud->pmtu; else size = hop->hop_probe_size; continue; } if((mtus[i] = mtu_tostr(mtu, size)) == NULL) { goto cleanup; } } if((mtu_lens = malloc(sizeof(size_t) * trace->hop_count)) == NULL) { goto cleanup; } for(i=0; ihop_count; i++) { if(mtus[i] != NULL) { mtu_lens[i] = strlen(mtus[i]); len += mtu_lens[i]; } } } /* \n on each line */ len += trace->hop_count; if((str = malloc(len)) == NULL) { goto cleanup; } snprintf(str, len, "%s\n", header); len = strlen(header) + 1; for(i=0; i < trace->hop_count; i++) { memcpy(str+len, hops[i], hop_lens[i]); len += hop_lens[i]; if(trace->pmtud != NULL && mtus[i] != NULL) { memcpy(str+len, mtus[i], mtu_lens[i]); len += mtu_lens[i]; } str[len++] = '\n'; } /* * try and write the string to disk. if it fails, then truncate the * write and fail */ if(write_wrap(fd, str, &wc, len) != 0) { if(fd != 1) ftruncate(fd, off); goto cleanup; } ret = 0; /* we succeeded */ cleanup: for(i=0; ihop_count; i++) { if(hops[i] != NULL) free(hops[i]); } if(hop_lens != NULL) free(hop_lens); if(hops != NULL) free(hops); if(mtus != NULL) { for(i=0; ihop_count; i++) { if(mtus[i] != NULL) free(mtus[i]); } free(mtus); if(mtu_lens != NULL) free(mtu_lens); } if(header != NULL) free(header); if(str != NULL) free(str); return ret; } /* * scamper_file_traceroute_read * * */ scamper_trace_t *scamper_file_traceroute_read_trace(const scamper_file_t *sf) { scamper_trace_t *trace = NULL; scamper_trace_hop_t *head, *hop; char line[256]; char *tokens[20]; int num_tokens; fpos_t pos; int i; file_state_t *state; sa_family_t af; state = scamper_file_getstate(sf); /* read the first line, and parse the `traceroute to' line */ if(fgets(line, sizeof(line), state->file) == NULL) { return NULL; } num_tokens = sizeof(tokens) / sizeof(char *); if(gettokens(line, tokens, &num_tokens) != 1) { printf("could not gettokens [1]\n"); return NULL; } if(num_tokens == 0) { printf("num_tokens == 0\n"); return NULL; } if(strcmp(tokens[0], "traceroute") != 0 || num_tokens < 3) { printf("%s != traceroute || %d < 3\n", tokens[0], num_tokens); return NULL; } if((trace = scamper_trace_alloc()) == NULL) { return NULL; } af = AF_UNSPEC; /* check to see if the file has the source address of the trace embedded */ if(strcmp(tokens[1], "from") == 0) { if((trace->src = scamper_addr_resolve(af, tokens[2])) == NULL) { goto err; } i = 3; } else { i = 1; } /* we expect to see a 'to' token now, followed with the target address */ if(strcmp(tokens[i], "to") != 0) { goto err; } if((trace->dst = scamper_addr_resolve(af, tokens[i+1])) == NULL) { goto err; } if(trace->dst->type == SCAMPER_ADDR_TYPE_IPV4) af = AF_INET; else if(trace->dst->type == SCAMPER_ADDR_TYPE_IPV6) af = AF_INET6; else goto err; trace->start.tv_sec = 0; trace->start.tv_usec = 0; /* is this unnecessary? */ fgetpos(state->file, &pos); i = 0; head = NULL; /* now, read every hop in the traceroute */ while(fgets(line, sizeof(line), state->file) != NULL) { /* * break the traceroute ascii text up into tokens */ num_tokens = sizeof(tokens)/sizeof(char *); if(gettokens(line, tokens, &num_tokens) != 1) { printf("could not gettokens [2]\n"); goto err; } /* * if we get to the next traceroute record, then we need * to rewind back to the start of this line so we're ready to read * the record when we're actually asked for it */ if(num_tokens == 3 && strcmp(tokens[0], "traceroute") == 0) { fsetpos(state->file, &pos); break; } i++; /* did we get an answer for this hop? */ if(num_tokens == 2 && tokens[1][0] == '*') { fgetpos(state->file, &pos); continue; } if(num_tokens < 4) { printf("numtokens %d < 4\n", num_tokens); goto err; } /* we got an answer */ if((hop = scamper_trace_hop_alloc()) == NULL) { goto err; } hop->hop_probe_ttl = i; hop->hop_next = head; head = hop; /* resolve the string to an address */ if((hop->hop_addr = scamper_addr_resolve(af, tokens[1])) == NULL) { goto err; } /* turn the ascii millisecond figure into a timeval structure */ if(msec_to_timeval(tokens[2], &hop->hop_rtt) == -1) { printf("couldn't convert the ms to a timeval for %s\n", tokens[2]); goto err; } if(num_tokens == 5 && tokens[4][0] == '!') { switch(tokens[4][1]) { case 'L': trace->stop_reason = SCAMPER_TRACE_STOP_LOOP; break; case 'D': trace->stop_reason = SCAMPER_TRACE_STOP_DEAD; break; case 'E': trace->stop_reason = SCAMPER_TRACE_STOP_ERROR; break; case 'N': trace->stop_reason = SCAMPER_TRACE_STOP_UNREACH; if(af == AF_INET6) trace->stop_data = ICMP6_DST_UNREACH_NOROUTE; else /* AF_INET */ trace->stop_data = ICMP_UNREACH_NET; break; case 'P': trace->stop_reason = SCAMPER_TRACE_STOP_UNREACH; if(af == AF_INET6) trace->stop_data = ICMP6_DST_UNREACH_ADMIN; else /* AF_INET */ trace->stop_data = ICMP_UNREACH_PROTOCOL; break; case 'S': trace->stop_reason = SCAMPER_TRACE_STOP_UNREACH; if(af == AF_INET6) trace->stop_data = ICMP6_DST_UNREACH_BEYONDSCOPE; else /* AF_INET */ trace->stop_data = ICMP_UNREACH_SRCFAIL; break; case 'A': trace->stop_reason = SCAMPER_TRACE_STOP_UNREACH; trace->stop_data = ICMP6_DST_UNREACH_ADDR; break; case 'F': trace->stop_reason = SCAMPER_TRACE_STOP_UNREACH; trace->stop_data = ICMP_UNREACH_NEEDFRAG; break; case 'H': trace->stop_reason = SCAMPER_TRACE_STOP_UNREACH; trace->stop_data = ICMP_UNREACH_HOST; break; case 'X': trace->stop_reason = SCAMPER_TRACE_STOP_UNREACH; trace->stop_data = ICMP_UNREACH_FILTER_PROHIB; break; default:/* snprintf(file->error_str, sizeof(file->error_str), "unknown stop reason !%c", tokens[4][1]);*/ break; } } /* * before we read the next line, we need to know where we are * in the file, because we might start reading the next trace * record and need to go back. */ fgetpos(state->file, &pos); } if(scamper_trace_hops_alloc(trace, i) == -1) { goto err; } trace->hop_count = i; while(head != NULL) { hop = head; head = head->hop_next; trace->hops[hop->hop_probe_ttl-1] = hop; hop->hop_next = NULL; } return trace; err: if(trace != NULL) scamper_trace_free(trace); return NULL; } static char *ping_header(const scamper_ping_t *ping) { char header[192], src[64], dst[64]; snprintf(header, sizeof(header), "ping %s to %s: %d byte packets\n", scamper_addr_tostr(ping->src, src, sizeof(src)), scamper_addr_tostr(ping->dst, dst, sizeof(dst)), ping->probe_size); return strdup(header); } static char *ping_reply(const scamper_ping_reply_t *reply) { char buf[192], addr[64], rtt[32]; snprintf(buf, sizeof(buf), "%d bytes from %s, icmp_seq=%d ttl=%d time=%s ms\n", reply->reply_size, scamper_addr_tostr(reply->addr, addr, sizeof(addr)), reply->probe_id, reply->reply_ttl, rtt_tostr(&reply->rtt, rtt, sizeof(rtt))); return strdup(buf); } static char *ping_stats(const scamper_ping_t *ping) { struct timeval min, max, avg, stddev; uint16_t loss; uint32_t replies, dups; char min_str[32], max_str[32], avg_str[32], stddev_str[32], dup_str[32]; char buf[512]; char dst[64]; if(scamper_ping_stats(ping,&replies,&dups,&loss,&min,&max,&avg,&stddev) != 0) { return NULL; } if(dups != 0) { snprintf(dup_str, sizeof(dup_str), "+%d duplicates, ", dups); } else dup_str[0] = '\0'; snprintf(buf, sizeof(buf), "--- %s ping statistics ---\n" "%d packets transmitted, %d packets received, %s%d%% packet loss\n" "round-trip min/avg/max/stddev = %s/%s/%s/%s ms\n", scamper_addr_tostr(ping->dst, dst, sizeof(dst)), ping->ping_sent, replies, dup_str, ((ping->ping_sent - replies) * 100) / ping->ping_sent, rtt_tostr(&min, min_str, sizeof(min_str)), rtt_tostr(&max, max_str, sizeof(max_str)), rtt_tostr(&avg, avg_str, sizeof(avg_str)), rtt_tostr(&stddev, stddev_str, sizeof(stddev_str))); return strdup(buf); } int scamper_file_traceroute_write_ping(const scamper_file_t *sf, const scamper_ping_t *ping) { scamper_ping_reply_t *reply; int fd = scamper_file_getfd(sf); off_t off = 0; uint32_t reply_count = scamper_ping_reply_count(ping); char *header = NULL; size_t header_len = 0; char **replies = NULL; size_t *reply_lens = NULL; char *stats = NULL; size_t stats_len = 0; char *str = NULL; size_t len = 0; size_t wc = 0; int ret = -1; uint32_t i,j; /* get current position incase trunction is required */ if(fd != 1 && (off = lseek(fd, 0, SEEK_CUR)) == -1) { return -1; } /* get the header string */ if((header = ping_header(ping)) == NULL) { goto cleanup; } len = (header_len = strlen(header)); /* put together a string for each reply */ if(reply_count > 0) { if((replies = malloc_zero(sizeof(char *) * reply_count)) == NULL || (reply_lens = malloc_zero(sizeof(size_t) * reply_count)) == NULL) { goto cleanup; } for(i=0, j=0; iping_sent; i++) { reply = ping->ping_replies[i]; while(reply != NULL) { /* build string representation of this reply */ if((replies[j] = ping_reply(reply)) == NULL) { goto cleanup; } len += (reply_lens[j] = strlen(replies[j])); reply = reply->next; j++; } } } /* put together the summary stats */ stats = ping_stats(ping); len += (stats_len = strlen(stats)); /* allocate a string long enough to combine the above strings */ if((str = malloc(len)) == NULL) { goto cleanup; } /* combine the strings created above */ memcpy(str+wc, header, header_len); wc += header_len; for(i=0; ifile = fdopen(fd, "r")) == NULL) { goto err; } scamper_file_setstate(sf, state); return 0; err: free(state); return -1; } int scamper_file_traceroute_is(const scamper_file_t *sf) { char buf[10]; int fd; fd = scamper_file_getfd(sf); if(lseek(fd, 0, SEEK_SET) == -1) { return 0; } if(read_wrap(fd, buf, NULL, sizeof(buf)) != 0) { return 0; } if(strncmp(buf, "traceroute", 10) == 0) { if(lseek(fd, 0, SEEK_SET) == -1) { return 0; } return 1; } return 0; } void scamper_file_traceroute_free_state(scamper_file_t *sf) { file_state_t *state; if((state = scamper_file_getstate(sf)) != NULL) { free(state); } return; }