/* Copyright (C) 2000-2001 Opnix, Inc. 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; either version 2 of the License, or (at your option) any later version. 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 "oproute.h" #include #include #include #include #include #include #include #include #include #include #include #include #include "jivastring.h" #include #include // just some utility functions... void sock_set_port(struct sockaddr *sa, socklen_t salen, int port); char * sock_ntop_host(const struct sockaddr *sa, socklen_t salen); void tv_sub(struct timeval *out, struct timeval *in); OpRouteApp::OpRouteApp(int argc, char *argv[]) { nBeginTTL = 1; nMaxTTL = 30; nQueries = 3; fWaitTime = 5.0; ParseArgs(argc, argv); if(args["-c"] != "no") { bCompare = TRUE; } else bCompare = FALSE; if(args.find("-n") != args.end()) { bNoResolve = true; } else bNoResolve = false; } OpRouteApp::~OpRouteApp() { } void OpRouteApp::run() { cerr << "\n=============================================\n"; cerr << "OpRoute v 0.7 - Opnix route information tool." << endl; cerr << "Created by Opnix, http://www.oproute.net" << endl; cerr << "=============================================\n\n"; string sDest = sDestinationIP; OpRoute *pOproute = new OpRoute(sDestinationIP, nBeginTTL, nMaxTTL, nQueries, fWaitTime, bNoResolve); cerr << "-----------------------------------------\n" ; cerr << "Your Network\n\n"; pOproute->_bNoResolve = bNoResolve; pOproute->Run(); cerr << "-----------------------------------------" ; if(bCompare) { cerr << "\nGetting score and Opnix Network...\n"; cerr << "-----------------------------------------\n" ; pOproute->Score(); OpRoute *pOpnixRoute = new OpRoute(); pOpnixRoute->GetFromServer(sDestinationIP); pOpnixRoute->Score(); pOpnixRoute->_bNoResolve = bNoResolve; cerr << "Opnix Network\n"; cout << pOpnixRoute->AsString(); cerr << "-----------------------------------------\n" ; cout << pOproute->Summary(pOpnixRoute); delete pOpnixRoute; } else { cout << pOproute->Summary(); } cerr << "=============================================\n\n"; } void OpRouteApp::ParseArgs(int argc, char *argv[]) { for(int n = 0; n < argc; ++n) { string sSwitch = argv[n]; string sParam; if(n != argc-1) sParam = argv[n+1]; else sParam = ""; if(sSwitch.c_str()[0] == '-') { if(sParam.c_str()[0] != '-') args[sSwitch] = sParam; else args[sSwitch] = ""; } else if(n > 0) { sDestinationIP = sSwitch; } } } OpRoute::OpRoute(string sDestination, int nTheBeginTTL, int nTheMaxTTL, int nTheQueries, float fTheWaitTime, bool bNoResolve): _bNoResolve(bNoResolve) { cerr << "Doing DNS lookup..." << endl; if(sDestination == "") { cout << "Must have a host to check out!" << endl; exit(1); } struct hostent *he = gethostbyname(sDestination.c_str()); if(he == NULL) { cout << "Could not resolve host... " << endl; exit(1); } struct in_addr *ia = (struct in_addr *) *he->h_addr_list; sDestinationIP = inet_ntoa(*ia); nPort = getpid(); recvfd = socket(PF_INET, SOCK_RAW, 1); long nBufSize = 10000000; setsockopt(recvfd, SOL_SOCKET, SO_RCVBUF, (char *)&nBufSize, sizeof(nBufSize)); nCurrentTTL = nBeginTTL = nTheBeginTTL; nMaxTTL = nTheMaxTTL; nQueries = nTheQueries; nASHops = 0; nNAPHops = 0; nOpScore = 0; fReliability = 0; nLostPackets = 0; nTotalPackets = 0; fTotalLatency = 0; } OpRoute::OpRoute() { nOpScore = 0; fReliability = 0; nASHops = 0; nNAPHops = 0; fTotalLatency = 0; nLostPackets = 0; nTotalPackets = 0; } OpRoute::~OpRoute() { } void OpRoute::Score() { struct sockaddr *sa; socklen_t sa_len; int iorsock = socket(PF_INET, SOCK_STREAM, 0); struct addrinfo *ai; struct hostent *he = gethostbyname("opscore.oproute.net"); if(he == NULL) { cout << "Could not resolve opscore host... " << endl; exit(1); } struct in_addr *ia = (struct in_addr *) *he->h_addr_list; string sOpscoreHost = inet_ntoa(*ia); getaddrinfo(sOpscoreHost.c_str(), NULL, NULL, &ai); sa = ai->ai_addr; sa_len = ai->ai_addrlen; sock_set_port(sa, sa_len, htons(13131)); if(connect(iorsock, sa, sa_len) != -1) { ifstream hInbound(iorsock); ofstream hOutbound(iorsock); // send our command to the server hOutbound << "scoreit=begin" << endl; vector vMyTR = CopyToRouteInfoDB(); for(vector::iterator sLine = vMyTR.begin(); sLine != vMyTR.end(); ++sLine) { hOutbound << *sLine << endl; } hOutbound << "scoreit=end" << endl; vector vData; // snag each line of the output until disconnected. while(!hInbound.eof()) { string sLine; hInbound >> sLine; vData.push_back(sLine); } // process the data. CopyFromRouteInfoDB(vData); close(iorsock); } else { cout << "Unable to contact Opnix core. Proceeding with non-scored analysis.\n"; } } void OpRoute::GetFromServer(string sDest) { struct sockaddr *sa; socklen_t sa_len; int iorsock = socket(PF_INET, SOCK_STREAM, 0); struct addrinfo *ai; struct hostent *he = gethostbyname("opscore.oproute.net"); if(he == NULL) { cout << "Could not resolve opscore host... " << endl; exit(1); } struct in_addr *ia = (struct in_addr *) *he->h_addr_list; string sOpscoreHost = inet_ntoa(*ia); getaddrinfo(sOpscoreHost.c_str(), NULL, NULL, &ai); sa = ai->ai_addr; sa_len = ai->ai_addrlen; sock_set_port(sa, sa_len, htons(13131)); if(connect(iorsock, sa, sa_len) != -1) { // establish our inbound and outbound handles. ifstream hInbound(iorsock); ofstream hOutbound(iorsock); // send our command to the server hOutbound << "traceto=" << sDest << endl; vector vData; // snag each line of the output until disconnected. while(!hInbound.eof()) { string sLine; hInbound >> sLine; vData.push_back(sLine); } // process the data. CopyFromRouteInfoDB(vData); close(iorsock); } else { cout << "Unable to contact Opnix core. Proceeding with non-scored analysis.\n"; } } /* format: totallatency=5.0 ashops=1 naphops=1 reliability=10 opscore=55 hop=latency:asnum:nap:routerip:state:port hop=latency:asnum:nap:routerip:state:port hop=latency:asnum:nap:routerip:state:port */ void OpRoute::CopyFromRouteInfoDB(vector &vData) { vRouterHops.clear(); for(vector::iterator i = vData.begin(); i != vData.end(); ++i) { if(*i != "") { vector vNameValPair; vNameValPair = split("=", i); // okay this sucks, am open to suggestions on a better way. if(vNameValPair[0] == "totallatency") { this->fTotalLatency = atof(vNameValPair[1].c_str()); } else if(vNameValPair[0] == "ashops") { this->nASHops = atoi(vNameValPair[1].c_str()); } else if(vNameValPair[0] == "naphops") { this->nNAPHops = atoi(vNameValPair[1].c_str()); } else if(vNameValPair[0] == "reliability") { this->fReliability = atof(vNameValPair[1].c_str()); } else if(vNameValPair[0] == "opscore") { this->nOpScore = atof(vNameValPair[1].c_str()); } else if(vNameValPair[0] == "queries") { this->nQueries = atoi(vNameValPair[1].c_str()); } else if(vNameValPair[0] == "hop") { // format: latency:asnum:nap:routerip:state:port string sHopInfo = vNameValPair[1]; vector vValues = split(":", &sHopInfo); Hop oHop; oHop.fLatency = atof(vValues[0].c_str()); oHop.sASNumber = atoi(vValues[1].c_str()); oHop.sNap = atoi(vValues[2].c_str()); oHop.sRouterIP = vValues[3]; oHop.state = atoi(vValues[4].c_str()); oHop.nPort = atoi(vValues[5].c_str()); vRouterHops.push_back(oHop); } else if(vNameValPair[0] == "scoreit") { // ignore it. } else { cerr << "Unrecognized response! - " << vNameValPair[0] << endl; } } } } vector OpRoute::CopyToRouteInfoDB() { vector vRet; vRet.push_back("totallatency="+ftos(this->fTotalLatency)); vRet.push_back("ashops="+ftos(this->nASHops)); vRet.push_back("naphops="+ftos(this->nNAPHops)); vRet.push_back("reliability="+ftos(this->fReliability)); vRet.push_back("opscore="+ftos(this->nOpScore)); vRet.push_back("queries="+itos(this->nQueries)); for(vector::iterator iHop = vRouterHops.begin(); iHop != vRouterHops.end(); ++iHop) { string sLine = "hop="; sLine += ftos(iHop->fLatency)+":"; sLine += iHop->sASNumber+":"; sLine += iHop->sNap + ":"; sLine += iHop->sRouterIP + ":"; sLine += itos(iHop->state) + ":"; sLine += itos(iHop->nPort); vRet.push_back(sLine); } return(vRet); } float OpRoute::GetTotalLatency() { if(fTotalLatency > 0) { return(fTotalLatency); } else { float fSecondMaxLatency = 0; float fMaxLatency1 = 0; for(vector::iterator iHop = vRouterHops.begin(); iHop != vRouterHops.end(); ++iHop) { fMaxLatency1 = max(iHop->fLatency, fMaxLatency1); } return(fMaxLatency1); } } int OpRoute::GetRouterHops() { return(vRouterHops.size()/nQueries); } string OpRoute::AsString() { string sRet = ""; int n = nQueries; int nHop = 1; for(vector::iterator iHop = vRouterHops.begin(); iHop != vRouterHops.end(); ++iHop) { char cHop[1024]; snprintf(cHop, 255, "\0"); if(((n) % nQueries) == 0) { if(_bNoResolve || (iHop->state == HOP_FAILED) || (iHop->sRouterIP == "")) { snprintf(cHop, 255, "\n%d %s ", nHop, iHop->sRouterIP.c_str()); } else { struct in_addr ai; inet_aton(iHop->sRouterIP.c_str(), &ai); struct hostent *he2 = gethostbyaddr((const char *)&ai, 4, AF_INET); if(he2 != NULL) snprintf(cHop, 255, "\n%d %s(%s)", nHop, he2->h_name, iHop->sRouterIP.c_str()); else snprintf(cHop, 255, "\n%d %s ", nHop, iHop->sRouterIP.c_str()); } nHop++; } sRet += cHop; if(iHop->state == HOP_COMPLETED) { snprintf(cHop, 255, " %.3f ms", iHop->fLatency); sRet += cHop; } else if(iHop->state == HOP_FAILED) { sRet += " *"; nLostPackets++; } else if(iHop->state == HOP_ENDOFLINE) { snprintf(cHop, 255, " %.3f ms", iHop->fLatency); sRet += cHop; nTotalPackets++; } else { sRet += " *"; } ++n; } sRet += "\n"; return(sRet); } string OpRoute::Summary() { char cBuf[1024]; string sRet = "-----------------------------------------\n" ; sRet += "Summary information\n"; sRet += "** Opnix Network and Opnix Score Disabled By User **\n"; sRet += "-----------------------------------------\n" ; snprintf(cBuf, 1024, "Total # of layer 3: %d\n", GetRouterHops()); sRet += cBuf; snprintf(cBuf, 1024, "Total packet loss: %0.1f%%\n", GetTotalPacketLoss()); sRet += cBuf; snprintf(cBuf, 1024, "Total latency: %.3f\n", GetTotalLatency()); sRet += cBuf; snprintf(cBuf, 1024, "Total AS Hops: %d\n", nASHops); sRet += cBuf; snprintf(cBuf, 1024, "Total NAP Hops: %d\n", nNAPHops); sRet += cBuf; snprintf(cBuf, 1024, "OpScore: %.2f\n", nOpScore); sRet += cBuf; return(sRet); } string OpRoute::Summary(OpRoute *pOpnixRoute) { char cBuf[1024]; string sRet = "-----------------------------------------\n" ; sRet += "Summary information \n"; sRet += "-----------------------------------------\n" ; sRet += " YOU (OPNIX)\n"; snprintf(cBuf, 1024, "Total # of layer 3: %d (%d)\n", GetRouterHops(), pOpnixRoute->GetRouterHops()); sRet += cBuf; snprintf(cBuf, 1024, "Total packet loss: %0.1f%% (%0.1f%%)\n", GetTotalPacketLoss(), pOpnixRoute->GetTotalPacketLoss()); sRet += cBuf; snprintf(cBuf, 1024, "Total latency: %.3f (%.3f)\n", GetTotalLatency(), pOpnixRoute->GetTotalLatency()); sRet += cBuf; snprintf(cBuf, 1024, "Total AS Hops: %d (%d)\n", nASHops, pOpnixRoute->nASHops); sRet += cBuf; snprintf(cBuf, 1024, "Total NAP Hops: %d (%d)\n", nNAPHops, pOpnixRoute->nNAPHops); sRet += cBuf; snprintf(cBuf, 1024, "OpScore: %.2f (%.2f)\n", nOpScore, pOpnixRoute->nOpScore); sRet += cBuf; return(sRet); } float OpRoute::GetTotalPacketLoss() { float fLostPackets = 0; float fTotalPackets = vRouterHops.size(); float fRel = 0; for(vector::iterator it = vRouterHops.begin(); it != vRouterHops.end(); ++it) { if(it->state == HOP_FAILED) fLostPackets++; } fRel = (fLostPackets/fTotalPackets) * 100; return(fRel); } string OpRoute::Run() { cerr.flush(); int j = 1; for(int n = nBeginTTL; n <= nMaxTTL; ++n) { cerr.flush(); cout << j ; ++j; if(Step() == HOP_ENDOFLINE) break; } Hop LatencyHop; SendProbe(30); CatchICMP(&LatencyHop); if(LatencyHop.state == HOP_ENDOFLINE) { fTotalLatency = LatencyHop.fLatency; } } void OpRoute::SendProbe(int ttl) { int sendfd; char sendbuf = 'a'; struct sockaddr *sa_sendto, *sa_bindto; struct sockaddr_in *sin; struct addrinfo *ai; socklen_t sa_len; int n; n = getaddrinfo(sDestinationIP.c_str(), NULL, NULL, &ai); sa_sendto = ai->ai_addr; sa_len = ai->ai_addrlen; sendfd = socket(AF_INET, SOCK_DGRAM, 0); setsockopt(sendfd, IPPROTO_IP, IP_TTL, (char *)&ttl, sizeof(ttl)); sin = (struct sockaddr_in *)sa_sendto; sin->sin_port = nPort; sa_bindto = (sockaddr *)calloc(1, ai->ai_addrlen); sock_set_port(sa_bindto, sa_len, htons(nPort)); bind(sendfd, sa_bindto, sa_len); sock_set_port(sa_sendto, sa_len, htons(nPort)); int nRet = sendto(sendfd, &sendbuf, 1, 0, sa_sendto, sa_len); gettimeofday(&tvTimeOfLastSend, NULL); if(nRet < 0) { int nError = errno; cerr << "Error in sending..." << strerror(nError) << endl; } free(sa_bindto); close(sendfd); } int OpRoute::Step() { Hop oHop; for(int n = 0; n < nQueries; ++n) { oHop.state = HOP_PENDING; SendProbe(nCurrentTTL); CatchICMP(&oHop); // if(oHop.state == HOP_ENDOFLINE) // fTotalLatency = oHop.fLatency; if(n == 0) { if(_bNoResolve || (oHop.state == HOP_FAILED) || (oHop.sRouterIP == "")) { cout << " " << oHop.sRouterIP; } else { // TODO: Fix this 16 here... struct in_addr ia; inet_aton(oHop.sRouterIP.c_str(), &ia); struct hostent *he = gethostbyaddr((const char *)&ia, 4, AF_INET); if(he == NULL) { cout << " " << oHop.sRouterIP; } else { cout << " " << he->h_name << "(" << oHop.sRouterIP << ") "; } } } if(oHop.state != HOP_FAILED) { string sLatency = ftos(oHop.fLatency); sLatency += " ms"; cout << " " << sLatency; } else { cout << " * "; nLostPackets++; } nTotalPackets++; vRouterHops.push_back(oHop); } cout << endl; nCurrentTTL++; return(oHop.state); } void OpRoute::CatchICMP(Hop *pHop) { int hlen1, hlen2, n; struct sockaddr *sa_recv; socklen_t sa_len; struct ip *ip, *hip; struct icmp *icmp; struct udphdr *udp; char recvbuf[1500]; sa_recv = (sockaddr *)calloc(1, 16); sa_len = 16; int flags; flags = fcntl(recvfd, F_GETFL, 0); flags |= O_NONBLOCK; fcntl(recvfd, F_SETFL, flags); n = 0; int nCount = 0; struct timeval tvNow; // tight nasty loop... better than using a signal tho. do { n = recvfrom(recvfd, recvbuf, sizeof(recvbuf), 0, (struct sockaddr *)sa_recv, &sa_len); ++nCount; gettimeofday(&tvNow, NULL); } while ((n < 1) && ((tvNow.tv_sec - tvTimeOfLastSend.tv_sec) < 3)); // need to expand on this error handling a bit more. if(n > 0) { ip = (struct ip *)recvbuf; hlen1 = ip->ip_hl << 2; icmp = (struct icmp *)(recvbuf + hlen1); if(icmp->icmp_type == ICMP_TIMXCEED && icmp->icmp_code == ICMP_TIMXCEED_INTRANS) { hip = (struct ip *)(recvbuf + hlen1 + 8); hlen2 = hip->ip_hl << 2; udp = (struct udphdr *)(recvbuf + hlen1 + 8 +hlen2); if(hip->ip_p == IPPROTO_UDP) { pHop->nPort = ntohs(udp->uh_dport); pHop->sRouterIP = sock_ntop_host((sockaddr *)sa_recv, sa_len); pHop->state = HOP_COMPLETED; pHop->fLatency = GetLatencyFromTV(tvNow, tvTimeOfLastSend); } } else if (icmp->icmp_type == ICMP_UNREACH) { hip = (struct ip *) (recvbuf + hlen1 + 8); hlen2 = hip->ip_hl << 2; udp = (struct udphdr *) (recvbuf + hlen1 + 8 + hlen2); if (hip->ip_p == IPPROTO_UDP) { if (icmp->icmp_code == ICMP_UNREACH_PORT) { pHop->nPort = ntohs(udp->uh_dport); pHop->sRouterIP = sock_ntop_host((sockaddr *)sa_recv, sa_len); pHop->state = HOP_ENDOFLINE; pHop->fLatency = GetLatencyFromTV(tvNow, tvTimeOfLastSend); } else { pHop->nPort = ntohs(udp->uh_dport); pHop->sRouterIP = sock_ntop_host((sockaddr *)sa_recv, sa_len); pHop->state = HOP_FAILED; } } } } else { pHop->state = HOP_FAILED; } free(sa_recv); // return(Ret); } void sock_set_port(struct sockaddr *sa, socklen_t salen, int port) { struct sockaddr_in *sin = (struct sockaddr_in *) sa; sin->sin_port = port; return; } char * sock_ntop_host(const struct sockaddr *sa, socklen_t salen) { static char str[128]; /* Unix domain is largest */ for(int n = 0; n < 128; ++n) { str[n] = '\0'; } struct sockaddr_in *sin = (struct sockaddr_in *) sa; if (inet_ntop(AF_INET, &sin->sin_addr, str, sizeof(str)) == NULL) return(NULL); return(str); } float OpRoute::GetLatencyFromTV(struct timeval now, struct timeval then) { float fRet; fRet = now.tv_usec - then.tv_usec; if(fRet < 0) { for(int n = then.tv_sec; n <= now.tv_sec; ++n) fRet += 1000000; } return(fRet * .001); } void tv_sub(struct timeval *out, struct timeval *in) { if ((out->tv_usec -= in->tv_usec) < 0) { /* out -= in */ --out->tv_sec; out->tv_usec += 1000000; } out->tv_sec -= in->tv_sec; } /* Converts ascii text to in_addr struct. NULL is returned if the address can not be found. */ struct in_addr *atoaddr(char *address) { struct hostent *host; static struct in_addr saddr; /* First try it as aaa.bbb.ccc.ddd. */ saddr.s_addr = inet_addr(address); if (saddr.s_addr != -1) { return &saddr; } host = gethostbyname(address); if (host != NULL) { return (struct in_addr *) *host->h_addr_list; } return NULL; }