// // Copyright (c) 2001,2002 RIPE NCC // // All Rights Reserved // // Permission to use, copy, modify, and distribute this software and its // documentation for any purpose and without fee is hereby granted, // provided that the above copyright notice appear in all copies and that // both that copyright notice and this permission notice appear in // supporting documentation, and that the name of the author not be // used in advertising or publicity pertaining to distribution of the // software without specific, written prior permission. // // THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING // ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS; IN NO EVENT SHALL // AUTHOR BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY // DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN // AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF // OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. // // NOTE: the 'traceroute' part of this code was taken from // traceroute v1.2, and munged into my somewhat confused idea // of c++ classes. the 'pr' part of the program was inspired by // the original PRIDE prtraceroute. The policy evaluation uses // the ratoolset libs. // /* * Copyright (c) 1988 Regents of the University of California. * All rights reserved. * * Redistribution and use in source and binary forms are permitted * provided that the above copyright notice and this paragraph are * duplicated in all such forms and that any documentation, * advertising materials, and other materials related to such * distribution and use acknowledge that the software was developed * by the University of California, Berkeley. The name of the * University may not be used to endorse or promote products derived * from this software without specific prior written permission. * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. */ // // $Id: prtraceroute.cc,v 4.9 2002/04/09 11:48:47 katie Exp $ // // Copyright (c) 1994 by the University of Southern California // All rights reserved. // // Permission to use, copy, modify, and distribute this software and its // documentation in source and binary forms for lawful non-commercial // purposes and without fee is hereby granted, provided that the above // copyright notice appear in all copies and that both the copyright // notice and this permission notice appear in supporting documentation, // and that any documentation, advertising materials, and other materials // related to such distribution and use acknowledge that the software was // developed by the University of Southern California, Information // Sciences Institute. The name of the USC may not be used to endorse or // promote products derived from this software without specific prior // written permission. // // THE UNIVERSITY OF SOUTHERN CALIFORNIA DOES NOT MAKE ANY // REPRESENTATIONS ABOUT THE SUITABILITY OF THIS SOFTWARE FOR ANY // PURPOSE. THIS SOFTWARE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR // IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED // WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, // TITLE, AND NON-INFRINGEMENT. // // IN NO EVENT SHALL USC, OR ANY OTHER CONTRIBUTOR BE LIABLE FOR ANY // SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES, WHETHER IN CONTRACT, TORT, // OR OTHER FORM OF ACTION, ARISING OUT OF OR IN CONNECTION WITH, THE USE // OR PERFORMANCE OF THIS SOFTWARE. // // Questions concerning this software should be directed to // ratoolset@isi.edu. // // Author(s): Rusty Eddy // John Mehringer Updated for RAToolSet 4.x // // Missing or unworking options: [-s -d -r] // #include "config.h" #include "prtraceroute.hh" #include "util/Argv.hh" #include "util/debug.hh" #include "util/trace.hh" #include "util/version.hh" #include "rpsl/schema.hh" #include "irr/irr.hh" //#include "version.hh" //#include "Argv.hh" //Rusage ru; //////////////////////////////////////////////////////////////////////// extern IRR * irr; // defined in irr.cc int opt_rusage = 0; int policy_flag = 1; int nflag = 0; int nprobes = 3; int max_ttl = 30; int socket_options = 0; int verbose = 0; int waittime = 5; int datalen = sizeof (probe_pkt); char *dst; int port = 32768+666; int tos = 0; char *sourceif; char *progname; int debug = 0; // Used for bgpropute.executeAction() static ActionDictionary * actiondictionary; // source routing #define MAXSOURCES 9 int lsrr = 0; ipAddr *source_routes[MAXSOURCES] = { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL }; void init_and_set_options (int, char **, char **); void process_policies (Hop path[], int ttl); Path path; //extern WhoisParser whois; //////////////////////////////////////////////////////////////////////// main (int argc, char **argv, char **envp) { progname = argv[0]; schema.initialize(); init_and_set_options (argc, argv, envp); /* if (!verbose && policy_flag) whois.error.ignore (1); else whois.error.immediate(0);*/ // dst and port are globals ipAddr *dstip = new ipAddr (dst); // ipaddrs for src and dst dstip->error.die(progname); ttlProbe probe(dst, INADDR_ANY, port, lsrr, source_routes); probe.error.die (progname); u_long lastaddr = probe.srcInaddr(); u_long dstaddr = dstip->getInaddr(); u_long newaddr; ipAddr *localip = new ipAddr (lastaddr); localip->error.die (progname); if (policy_flag) { path.addHop (localip, 0); // set the first hop path.addDestination (dstip); // set the destination } ICMPProbeReply reply; // wait for incoming icmp's Timer t1, t2; int ttl, got_there = 0, unreachable = 0; printf ("prtraceroute to %s (%s), %d hops max, %d byte packets\n", dst, dstip->getIpaddr(), max_ttl, datalen); for (ttl = 1; ttl <= max_ttl; ++ttl ) { printf ("%2d ", ttl); for (int p = 0, seq = 0; p < nprobes; ++p) { int cc, i; t1.gettimeofday(); probe.send (++seq, ttl); if (probe.error.warn(progname)) continue; while ((cc = reply.wait_for_reply (waittime)) != 0) { t2.gettimeofday(); if ((i = reply.ok(cc, probe.get_ident(), seq)) != 0) { newaddr = reply.srcInaddr(); if (newaddr != lastaddr) { ipAddr *ipaddr = new ipAddr (newaddr); if (policy_flag) { path.addHop(ipaddr, ttl); printf (" [AS%d]", path.getHopAS(ttl)); } if (!nflag) { printf (" %s (%s)", ipaddr->getName(), ipaddr->getIpaddr()); } else { printf (" %s", ipaddr->getIpaddr()); } lastaddr = newaddr; } printf (" %g ms", t2.delta (t1)); switch (i-1) { case ICMP_UNREACH_PORT: if (reply.getTTL() <= 1) printf (" !"); ++got_there; break; case ICMP_UNREACH_NET: ++unreachable; printf (" !N"); break; case ICMP_UNREACH_HOST: ++unreachable; printf (" !H"); break; case ICMP_UNREACH_PROTOCOL: ++got_there; printf (" !P"); break; case ICMP_UNREACH_NEEDFRAG: ++unreachable; printf (" !F"); break; case ICMP_UNREACH_SRCFAIL: ++unreachable; printf (" !S"); break; // The following are added by rusty. case 6: ++unreachable; // UNKNOWN NET printf (" ?N"); break; case 7: ++unreachable; // DST UNKNOWN HOST printf (" ?H"); break; case 8: ++unreachable; printf (" IH"); // host isolated OBSOLETE! break; case 9: ++unreachable; printf (" !AN"); // dst host admin, prohibited break; case 10: ++unreachable; printf (" !AH"); // dst net admin, prohibited break; case 11: ++unreachable; printf (" !TN"); // net unreach for TOS break; case 12: ++unreachable; printf (" !TH"); // dst unreach for TOS break; case 13: ++unreachable; printf (" !AF"); // comm. admin prohibited break; case 14: ++unreachable; printf (" !PV"); // host preced. violation break; case 15: ++unreachable; printf (" !PC"); // precedence cut-off break; } // switch break; } // if reply.ok } // while wait_for_reply if (cc == 0) { // If something times out print * printf (" *"); if (verbose) reply.error.warn(" *"); else reply.error.reset(); } fflush (stdout); } putchar ('\n'); /*if (policy_flag && verbose && whois.error()) whois.error.warn ("prtraceroute:");*/ if (got_there || unreachable >= nprobes - 1) break; } if (policy_flag) path.process_policies(); exit (0); } //////////////////////////////////////////////////////////////////////// void usage(char *argv[]) { cerr << "\nUsage: " << argv[0] << " [options] host [data size]" << endl; cerr << "Options: [-dnrv] [-np] [-w wait] [-m max_ttl] [-p port#] [-t tos]" << endl; cerr << " [-s src_addr] [-q nqueries] [-g gateway]" << endl; cerr << " [-irr_server host] [-irr_port port#] [-irr_databases dbs]\n" << endl; exit(1); } //-------------------------------------------------------------- // int start_tracing(char *dst, char *key, char *nextArg) // // //-------------------------------------------------------------- int start_tracing(char *dst, char *key, char *nextArg) { if (nextArg) { trace.enable(nextArg); return 1; // return 1 to signify nextArg is used by us } return 0; } //-------------------------------------------------------------- // int start_debugging(char *dst, char *key, char *nextArg) // // //-------------------------------------------------------------- int start_debugging(char *dst, char *key, char *nextArg) { if (nextArg) { Debug(dbg.enable(atoi(nextArg))); return 1; // return 1 to signify nextArg is used by us } return 0; } //-------------------------------------------------------------- // int gFlag(char *dst, char *key, char *nextArg) // // //-------------------------------------------------------------- int gFlag(char *dst, char *key, char *nextArg) { int limit = ((MAX_IPOPTLEN - IPOPT_MINOFF) / sizeof (u_long)); if ((lsrr+1) >= limit) { fprintf (stderr, "No more than %d gateways\n", (u_int) limit - 1); exit (1); } source_routes[lsrr++] = new ipAddr(nextArg); return 1; } //-------------------------------------------------------------- // int mFlag(char *dst, char *key, char *nextArg) // // //-------------------------------------------------------------- int mFlag(char *dst, char *key, char *nextArg) { if (nextArg) max_ttl = atoi(nextArg); if (max_ttl < 1) { fprintf (stderr, "TTL must be greater than one."); exit (1); } return 1; } //------------------------------------------------------------ // int sFlag(char *dst, char *key, char *nextArg) // // //------------------------------------------------------------ int sFlag(char *dst, char *key, char *nextArg) { if (nextArg) sourceif = nextArg; fprintf (stderr, "Sorry '-s source' not supported yet'\n"); return 1; } //------------------------------------------------------------ // void // init_and_set_options (int argc, char **argv, char **envp) // //------------------------------------------------------------ void init_and_set_options (int argc, char **argv, char **envp) { ArgvInfo argTable[] = { // RAToolSet common arguments // key, type, src, dst, help {"-T", ARGV_FUNC, (char *) &start_tracing, (char *) NULL, "Start tracing the next argument"}, {"-D", ARGV_FUNC, (char *) &start_debugging, (char *) NULL, "Start debugging the next argument"}, {"-version", ARGV_FUNC, (char *) &version, (char *) NULL, "Show version"}, {"-rusage", ARGV_BOOL, (char *) NULL, (char *) &opt_rusage, "On termination print resource usage"}, // {"-prompt", ARGV_STRING, (char *) NULL, (char *) &opt_prompt, // "Prompt"}, IRR_COMMAND_LINE_OPTIONS, // prtraceroute specific arguments {"-g", ARGV_FUNC, (char *)gFlag, (char *)NULL, "Gateway"}, {"-m", ARGV_FUNC, (char *)mFlag, (char *)NULL, "Max TTL"}, {"-n", ARGV_CONSTANT, (char *)1, (char *)&nflag, "N flag"}, {"-np", ARGV_CONSTANT, (char *)0, (char *)&policy_flag, "No policy"}, {"-p", ARGV_INT, (char *)NULL, (char *)&port, "Probe port"}, {"-q", ARGV_INT, (char *)NULL, (char *)&nprobes, "Query"}, {"-s", ARGV_FUNC, (char *)sFlag, (char *)NULL, "'-s source' not supported yet"}, {"-t", ARGV_INT, (char *)NULL, (char *)&tos, "Type of service"}, {"-v", ARGV_CONSTANT, (char *)1, (char *)&verbose, "Verbose"}, {"-w", ARGV_INT, (char *)NULL, (char *)&waittime, "Wait time"}, {(char *) NULL, ARGV_END, (char *) NULL, (char *) NULL, (char *) NULL} }; IRR::handleEnvironmentVariables(envp); IRR::IgnoreErrors(NULL, NULL, NULL); if (ParseArgv(&argc, argv, argTable, 0) != ARGV_OK) { cerr << endl; exit(1); } // Setup Connection to IRR server if (! (irr = IRR::newClient())) { cerr << "Unknown protocol!" << endl; exit(1); } switch (argc) { case 3: if (*argv[--argc] == '-') { fprintf (stderr, "unknown option: %s\n", argv[argc]); usage (argv); } for (char *c = argv[argc]; *c; c++) if (!isdigit(*c)) { fprintf (stderr, "bad packet len: %s\n", argv[argc]); break; } datalen += atoi (argv[argc]); case 2: if (*argv[--argc] == '-') { fprintf (stderr, "unknown option: %s\n", argv[argc-1]); usage (argv); } dst = new char[strlen (argv[argc])+1]; strcpy (dst, argv[argc]); break; // just ignore all extra default: usage (argv); break; } } //////////////////////////////////////////////////////////////////////// //------------------------------------------------------------ // //ttlProbe::ttlProbe(char *dst, int sport, int dport, // int lsrr, ipAddr **source_routes) // //------------------------------------------------------------ ttlProbe::ttlProbe(char *dst, int sport, int dport, int lsrr, ipAddr **source_routes) : rawUDP (dst) { int on = 1; if (error()) { error.syserr (Yes); error.die ("ttlProbe: "); } if (lsrr && source_routes) { addLSRR (lsrr, source_routes); error.die ("ttlProbe: "); } #ifdef IP_HDRINCL setsockopt (IPPROTO_IP, IP_HDRINCL, (char *) &on, sizeof (on)); #endif // IP_HDRINCL init_udp (sport, dport); if (error()) error.die ("ttlProbe: "); ident = (getpid() & 0xffff) | 0x8000; ip_hdr->ip_id = ntohs(ident); probepkt = (probe_pkt *) udp_data; size = sizeof(probe_pkt); // set sizes udp_hdr->uh_ulen += htons(size); // sets the udp len ip_hdr->ip_len += size; // maps to the ip len } //------------------------------------------------------------ // int // ttlProbe::send (u_char seq, u_char ttl) // //------------------------------------------------------------ int ttlProbe::send (u_char seq, u_char ttl) { if (error()) return error.warning("ttlProbe: "); struct timezone tz; probepkt->ttl = ip_hdr->ip_ttl = ttl; probepkt->seq = seq; gettimeofday (&(probepkt->tv), &tz); udp_hdr->uh_dport = htons(port + seq); udp_hdr->uh_sport = ip_hdr->ip_id; ip_hdr->ip_p = IPPROTO_UDP; return sendto (); } ////////////////////////////////////////////////////////////// //------------------------------------------------------------ // int // ICMPProbeReply::ok(int size, int ident, int seq) // //------------------------------------------------------------ int ICMPProbeReply::ok(int size, int ident, int seq) { #ifdef STRUCT_IP_USES_VHL int hlen = (ip_hdr->ip_vhl & 0xF) << 2; #else // STRUCT_IP_USES_VHL int hlen = ip_hdr->ip_hl << 2; #endif // STRUCT_IP_USES_VHL if (size < hlen + ICMP_MINLEN) { if (verbose) printf ("Packet too short (%d bytes) from %s\n", size, inet_ntoa (ip_hdr->ip_src)); return 0; } size -= hlen; int type = icmp_hdr->icmp_type; int code = icmp_hdr->icmp_code; struct ip *hip = (struct ip *) NULL; if ((type == ICMP_TIMXCEED && code == ICMP_TIMXCEED_INTRANS) || type == ICMP_UNREACH) { struct ip *hip = &icmp_hdr->icmp_ip; #ifdef STRUCT_IP_USES_VHL hlen = (hip->ip_vhl & 0xF) << 2; #else // STRUCT_IP_USES_VHL hlen = hip->ip_hl << 2; #endif // STRUCT_IP_USES_VHL struct udphdr *up = (struct udphdr *) ((u_char *) hip + hlen); if (hlen + 12 <= size && hip->ip_p == IPPROTO_UDP && up->uh_sport == htons(ident) && up->uh_dport == htons (port+seq)) { return (type == ICMP_TIMXCEED ? -1 : code + 1); } } if (verbose) { u_long *lp = (u_long *) &icmp_hdr->icmp_ip; struct in_addr in; in.s_addr = get_srcInaddr(); printf ("\n%d bytes from %s to %s", size, in, inet_ntoa (ip_hdr->ip_dst)); printf (": icmp type %d (%s) code %d (%s)\n", type, icmp_type_string(), code, icmp_code_string()); } return (0); } //////////////////////////////////////////////////////////////////////// //------------------------------------------------------------ // void // Hop::_hop () common intitializer for Hop class to zero // everything out first // //------------------------------------------------------------ void Hop::_hop () { ipaddr = (ipAddr *) NULL; as = 0; //route = new Route(); autnum = (AutNum *) NULL; } //------------------------------------------------------------ // void // Hop::_add (ipAddr *ip) // Fills in all the Hop info from a given ip //------------------------------------------------------------ void Hop::_add (ipAddr *ip) { if (ip) { ipaddr = ip; as = irr->getOrigin("%s/32",ip->getIpaddr()); } else { ipaddr = NULL; as = 0; } /* if (ip == (ipAddr *) NULL) return; ipaddr = ip; rtpix = Prefask_map.add_entry(ipaddr->getIpaddrMask()); route->nlri.pix = rtpix; route->define_less_or_equal_specific(); ListNodePix *p; char *asptr = (char *) NULL; for (p = route->origin.head(); p; p = route->origin.next(p->l)) { char *ptr = (char *) AS_map (p->pix); // the following checks can be removed when we put // in Jerry's corrected radbserver. if (ptr != (char *) NULL) { if (!strcmp (ptr, "AS0") || !strcmp (ptr, "AS1800")) continue; else { asname = asptr = ptr; ipaddr->setAsname (ptr); } } } if (!asptr) return; aspix = AS_map.add_entry(asptr); autnum = AS_map.define(aspix); */ } //////////////////////////////////////////////////////////////////////// //------------------------------------------------------------ // void // Path::addHop(ipAddr *ip, int i) // //------------------------------------------------------------ void Path::addHop(ipAddr *ip, int i) { if (i > last_ttl) last_ttl = i; hops[i] = new Hop(ip); } //------------------------------------------------------------ // ASt // Path::getHopAS(int ttl) // //------------------------------------------------------------ ASt Path::getHopAS(int ttl) { if (ttl <= last_ttl && hops[ttl]) return hops[ttl]->getAS(); else return 0; } //------------------------------------------------------------ // void // Path::process_policies() // //------------------------------------------------------------ void Path::process_policies() { int i; ASt export_as, import_as; BGPRoute bgproute(getHopAS(0),destination.getAS()); const AutNum * autnum; char test[80]; actiondictionary = new ActionDictionary; // just print out the path taken, as gathered while the trace // was running cout << endl << "Path taken: "; if (!hops[0]->getAS()) { // Empty path cout << endl << "(???)"; } else { cout << endl << "AS" << hops[0]->getAS() << " "; for (i = 1; i <= last_ttl; i++) { if (hops[i]->getAS() && hops[i-1]->getAS()) { if (getHopAS(i) != getHopAS(i-1)) cout << "AS" << hops[i]->getAS() << " "; } else if (hops[i]->getAS()) { cout << "AS" << hops[i]->getAS() << " "; } else { cout << "(???) "; } } // end for } cout << endl << endl; //--- Policy Analysis of the Path Taken //--- analyze from destination to source if (getHopAS(last_ttl) == destination.getAS()) strcpy (imports[last_ttl], "destination"); else strcpy (imports[last_ttl], "!reached"); for (i = last_ttl; i > 0; i--) { export_as = getHopAS(i); // hops[i]->getHopAS(); import_as = getHopAS(i-1); // hops[i-1]->getHopAS(); // Check to see if we have changed to different AS if (export_as == import_as) { if (import_as) { strcpy(exports[i], "internal"); strcpy(imports[i-1], "internal"); } else { strcpy (exports[i], "!registered"); strcpy (imports[i-1], "!registered"); } // continue; } else { // Check for export policies to new AS if (export_as) { bgproute.add(export_as); autnum = irr->getAutNum(export_as); if (autnum) { if (import_as) { AutNumSelector export_itr(autnum, "export", NULL, import_as, NULL, NULL); if (export_itr.first()) strcpy(exports[i], "export"); else // No Exports for Peer AS strcpy(exports[i], "!export"); } else // No Peer AS given strcpy(exports[i], "peer unregistered"); } else // No Autnum found strcpy(exports[i], "!registered"); } else // No export_as given strcpy(exports[i], "!registered"); // Check for import policies of new as to previous as if (import_as) { /* irr is global variable */ const AutNum * import_autnum = irr->getAutNum(import_as); /* Get All Imports */ if (import_autnum) { import_pref_list.clear(); AutNumPeeringIterator itr(import_autnum); for (const Peering *peering = itr.first(); peering; peering = itr.next()) { // Check for Match & Build preference list findImportPreference(bgproute, import_as, import_autnum, peering->peerAS, &(peering->peerIP), &(peering->localIP)); } // End for /* Based upon preference list write out the preference */ int import_pref = 1; for (ASPref * listpref = import_pref_list.head(); listpref; listpref = import_pref_list.next(listpref)) { if (listpref->as != export_as) import_pref++; } sprintf(imports[i-1], "import: %d",import_pref); } else { strcpy(imports [i-1], "!registered"); } } else strcpy(imports [i-1], "!registered"); } } strcpy (exports[0], "source"); // now print the proceessed policies for (i = last_ttl; i >= 0; i--) { if (hops[i] != NULL) { //cout << i << " AS" << getHopAS(i) << " "; printf ("%3d AS%d", i, getHopAS(i)); if (nflag) // XXX, this is a global, and is bad!!! //cout << hops[i]->ipaddr->getIpaddr(); printf (" %-35s", hops[i]->ipaddr->getIpaddr()); else // cout << "Ip Address Name "; //cout << hops[i]->ipaddr->getName(); printf (" %-35s", hops[i]->ipaddr->getName()); //cout << imports[i] << " -> " << exports[i] << endl; printf (" %10s -> %s \n", imports[i], exports[i]); } else { //cout << i << " (NULL) (???)" << endl; printf ("%3d %s", i, "(NULL)"); printf (" (???)\n"); } } } void Path::findImportPreference(BGPRoute &bgproute, ASt as, const AutNum * as_autnum, ASt peer_as, const IPAddr * peer_ip, const IPAddr * local_ip) { AutNumSelector import_itr(as_autnum, "import", NULL, peer_as, peer_ip, local_ip); if (const FilterAction * fa = import_itr.first()) { for (; fa; fa = import_itr.next()) { if (bgproute.isMatching(as, fa->filter, irr)) { bgproute.executeAction(fa->action, actiondictionary); // Insert AS & its preference into the sorted preference list ASPref * aspref = new ASPref(as, bgproute.pref.pref); if (!import_pref_list.isEmpty()) { for (ASPref * listpref = import_pref_list.head(); listpref; listpref = import_pref_list.next(listpref)) { if (listpref->pref > aspref->pref) { import_pref_list.insertBefore(listpref, aspref); break; } } } else { import_pref_list.append(aspref); } } } } }