// $Id: route-list.cc,v 4.10 2002/06/18 11:00:11 katie Exp $ // 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. // // // 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): Cengiz Alaettinoglu #include "config.h" #include "route-list.hh" #include "object.hh" #include #include "roe.hh" #include "util/rusage.hh" #include "irr/irr.hh" // Added by wlee@isi.edu extern IRR *whois; extern IRR *RLWhois; char *RouteList::format(RouteList::Route *vr) { static char line[256]; char *c; for (c = line; c < line + vr->indentation; ++c) *c = ' '; vr->route.get_text(line + vr->indentation); for (c = line + strlen(line); c < line + 31; ++c) *c = ' '; *c = 0; if (vr->reg_status & VR_REGISTERED_NO) strcat(line, " NotReg"); else if (vr->reg_status & VR_REGISTERED_MULTI) strcat(line, " "); else if (vr->reg_status & VR_REGISTERED_SINGLY) strcat(line, " "); else strcat(line, " --- "); if (vr->rtd_status & VR_ROUTED_NO) strcat(line, " NotRtd"); else if (vr->rtd_status & VR_ROUTED_YES) strcat(line, " "); else strcat(line, " --- "); if (vr->hmd_status & VR_HOMED_OTHER) strcat(line, " OtrHmd"); else if (vr->hmd_status & VR_HOMED_MULTI_ME) strcat(line, " MltHmd"); else if (vr->hmd_status & VR_HOMED_MULTI_OTHER) strcat(line, " MHmOtr"); else if (vr->hmd_status & VR_HOMED_ME) strcat(line, " "); else strcat(line, " --- "); strcat(line, " "); RouteList::Route::db_as* p; for (p = vr->db_as_l.head(); p; p = vr->db_as_l.next(p)) sprintf(line + strlen(line), " %s:AS%d", p->db, p->as); return line; } void RouteList::display() { matching_reg = 0; matching_rtd = 0; matching_hmd = 0; matching_reg |= roe_show_reg_no ? VR_REGISTERED_NO : 0; matching_reg |= roe_show_reg_single ? VR_REGISTERED_SINGLY : 0; matching_reg |= roe_show_reg_multi ? VR_REGISTERED_MULTI : 0; matching_reg |= roe_show_reg_undet ? VR_REGISTERED_UNDTRMND : 0; matching_rtd |= roe_show_routed_no ? VR_ROUTED_NO : 0; matching_rtd |= roe_show_routed_yes ? VR_ROUTED_YES : 0; matching_rtd |= roe_show_routed_undet ? VR_ROUTED_UNDTRMND : 0; matching_hmd |= roe_show_homed_you ? VR_HOMED_ME : 0; matching_hmd |= roe_show_homed_other ? VR_HOMED_OTHER : 0; matching_hmd |= roe_show_homed_multi_me ? VR_HOMED_MULTI_ME : 0; matching_hmd |= roe_show_homed_multi_other ? VR_HOMED_MULTI_OTHER : 0; matching_hmd |= roe_show_homed_undet ? VR_HOMED_UNDTRMND : 0; index = 0; indent = 0; //cerr << "measure_now before display" << endl; //cerr << ru << endl; if (vrdisplayed_size != rlist.size() + 1) { vrdisplayed_size = rlist.size() + 1; if (vrdisplayed) delete [] vrdisplayed; vrdisplayed = new (RouteList::Route*)[vrdisplayed_size]; } tcl_Eval(".routes.list delete 0 end"); display_i(&radix); vrdisplayed[index] = (RouteList::Route *) NULL; if (index) { // if there is at least one route, select it tcl_Eval(".routes.list selection set 0"); select(0); } //cerr << "measure_now after display" << endl; //cerr << ru << endl; } void RouteList::display_i(RadixNode *rdnode) { int oldindent = indent; if (!rdnode) return; if (rdnode->state == RADIX_FULL) { RouteList::Route *vr = (Route *) rdnode->data; int potential_IBGP = 0; if (!roe_show_potential_IBGP) { RouteList::Route *vr1 = vr; RadixNode *parent = rdnode; while (vr1 && parent && ! potential_IBGP && vr1->reg_status & VR_REGISTERED_NO && vr1->rtd_status & VR_ROUTED_YES) { for (parent = parent->prnt; parent && parent->state != RADIX_FULL; parent = parent->prnt) ; if (parent) { vr1 = (Route *)parent->data; if (vr1) if (vr1->reg_status & VR_REGISTERED_YES) potential_IBGP = 1; } } } if (vr->reg_status & matching_reg && vr->rtd_status & matching_rtd && vr->hmd_status & matching_hmd && ! potential_IBGP) { vr->display_index = index; vr->indentation = indent; vrdisplayed[index++] = vr; tcl_Eval(".routes.list insert end {%s}", format(vr)); indent++; } else vr->display_index = -1; } display_i(rdnode->left); display_i(rdnode->rght); indent = oldindent; } void RouteList::select(int index) { char buffer[1024]; char buffer2[64]; char *buffer3; char rc; int code; int bytes_read; RouteList::Route::db_as *p; RouteList::Route *vr = vrdisplayed[index]; tcl_Eval("destroy_children .buttonbar"); get_registration_now(vr); for (p = vr->db_as_l.head(); p; p = vr->db_as_l.next(p)) { tcl_Eval("button .buttonbar.a%s:%d -text {%s %d} -command { roe_show_object %s %d %s}", p->db, p->as, p->db, p->as, p->db, p->as, vr->route.get_text(buffer2)); tcl_Eval("pack .buttonbar.a%s:%d -side left", p->db, p->as); } p = vr->db_as_l.head(); if (p) object.display(p->db, p->as, vr->route.get_text(buffer2)); else object.clear(); } void RouteList::get_registration_now(RouteList::Route *vr) { char buffer[64]; char *response = NULL; if ((vr->status & VR_IRR_QUERIED) || (vr->status & VR_IRR_DONT_QUERY)) return; // Modified by wlee@isi.edu // whois->QueryKillResponse("!ufo=0"); whois->setFullObjectFlag(false); // whois->QueryResponse(response, "!r%s,o", vr->route.get_text(buffer)); whois->getSourceOrigin(response, vr->route.get_text(buffer)); if (!response) { tcl_Eval("showWarning { No %s on IRR!}", buffer); return; } parse_registration_response(response, vr); delete [] response; if (vr->display_index != -1) { sprintf(buffer, "%d", vr->display_index); tcl_Eval(".routes.list delete %s", buffer); tcl_Eval(".routes.list insert %s {%s}", buffer, routelist.format(vr)); } } // response contains all the (database,AS#) pair having the route vr->route // For example, query sent to server: // !ufo=0 // !r128.223.0.0/16,o // Response from the server // MCI AS3582 // RADB AS3582 void RouteList::parse_registration_response(char *response, RouteList::Route *vr) { char dbase[64], *line, *next; char *as; RouteList::Route::db_as *p, *fp; vr->status |= VR_IRR_QUERIED; next = response; while (*next) { line = next; extern char *my_strchr(char *s, int c); if (next = my_strchr(next, '\n')) { *next = 0; next++; } strcpy(dbase, strtok(line, " \t")); for (as = strtok(NULL, " \t"); as; as = strtok(NULL, " \t")) { p = new RouteList::Route::db_as(dbase, atoi(as + 2)); vr->db_as_l.append(p); } } if (vr->db_as_l.isEmpty()) vr->reg_status = VR_REGISTERED_NO; else if (vr->db_as_l.isSingleton()) vr->reg_status = VR_REGISTERED_SINGLY; else vr->reg_status = VR_REGISTERED_MULTI; fp = vr->db_as_l.head(); if (fp) { int multi_homed = 0; int homed_to_me = 0; for (p = fp; p; p = vr->db_as_l.next(p)) { if (p->as != fp->as) multi_homed = 1; if (p->as == routelist.as) homed_to_me = 1; } if (multi_homed && homed_to_me) vr->hmd_status = VR_HOMED_MULTI_ME; else if (multi_homed) vr->hmd_status = VR_HOMED_MULTI_OTHER; else if (homed_to_me) vr->hmd_status = VR_HOMED_ME; else vr->hmd_status = VR_HOMED_OTHER; } } // Make it global -- wlee@isi.edu int irr_pending_replies; RouteList::Route *RouteList::get_registration_response() { char dbase[64], buffer[1024], *response, *line, *next; char *as; char last_as[16]; RouteList::Route *vr, *p; if (!routelist.vr_response_next) return NULL; for (vr = routelist.vr_response_next; vr && (vr->status & VR_IRR_DONT_QUERY); vr = routelist.rlist.next(vr)) ; if (vr) { for (p = routelist.rlist.next(vr); p && (p->status & VR_IRR_DONT_QUERY); p = routelist.rlist.next(p)) ; routelist.vr_response_next = p; // if (! RLWhois->Response(response)) if (!RLWhois->getSourceOrigin(response)) { return vr; } tcl_Eval(".statusbar.right configure -text {Pending Replies: %d}", --irr_pending_replies); if (! (vr->status & VR_IRR_QUERIED)) parse_registration_response(response, vr); delete [] response; } else routelist.vr_response_next = NULL; return vr; } RouteList::Route *RouteList::ask_registration() { RouteList::Route *vr; char buffer[64]; if (!routelist.vr_query_next) return NULL; // Added by wlee@isi.edu // if (!RLWhois.WriteReady()) return NULL; for (vr = routelist.vr_query_next; vr && (vr->status & VR_IRR_DONT_QUERY); vr = routelist.rlist.next(vr)) ; if (vr) { routelist.vr_query_next = routelist.rlist.next(vr); // RLWhois->Query("!r%s,o", vr->route.get_text(buffer)); RLWhois->querySourceOrigin(vr->route.get_text(buffer)); } else routelist.vr_query_next = NULL; return vr; } void RouteList::get_registrations(ClientData clientdata) { char buffer[1024]; RouteList::Route *vr; if (! routelist.vr_response_next && ! routelist.vr_query_next) return; // if (! RLWhois->PendingData()) { if (!RLWhois->readReady()) { tcl_Eval("update"); Tk_DoWhenIdle(get_registrations, (ClientData) NULL); return; } // Modified by wlee // Send query before reading would improve the performance, but some servers // couldn't handle bursty queries. Read before sending queries again would be the right way. // ask_registration(); if (vr = get_registration_response()) { if (vr->display_index != -1) { sprintf(buffer, "%d", vr->display_index); tcl_Eval(".routes.list delete %s", buffer); tcl_Eval(".routes.list insert %s {%s}", buffer, routelist.format(vr)); } if (vr->display_index == 0) routelist.select(0); tcl_Eval("update"); } // Moved by wlee ask_registration(); Tk_DoWhenIdle(get_registrations, (ClientData) NULL); } #define PENDING_REQ_WINDOW 1 void RouteList::get_registrations_init(RouteList::Route *first) { RouteList::Route *vr1 = (RouteList::Route *) 1; RouteList::Route *vr; // Modified by wlee@isi.edu // if (!RLWhois->is_open()) { // RLWhois->QueryKillResponse("!ufo=0"); // } RLWhois->setFullObjectFlag(false); for (vr = first; vr; vr = rlist.next(vr)) if (! (vr->status & VR_IRR_DONT_QUERY)) irr_pending_replies++; tcl_Eval(".statusbar.right configure -text {Pending Replies: %d}", irr_pending_replies); if (! vr_query_next && first) { vr_response_next = first; vr_query_next = first; // ??? A quick hack for ripe if (IRR::GetProtocol() == IRR::ripe || IRR::GetProtocol() == IRR::bird) vr1 = ask_registration(); else // Quick hack end here for (int i = 0; i < PENDING_REQ_WINDOW && vr1; ++i) vr1 = ask_registration(); // Changed by wlee@isi.edu // get_registrations((ClientData) NULL); Tk_DoWhenIdle(get_registrations, (ClientData) NULL); } } void RouteList::init(void) { vrdisplayed = NULL; vrdisplayed_size = 0; as = 0; vr_query_next = NULL; vr_response_next = NULL; matching_reg = 0; matching_rtd = 0; matching_hmd = 0; if (irr_radix) { delete irr_radix; irr_radix = NULL; } real_routes.clear(); rlist.unlink(); radix.clear(); irr_pending_replies = 0; } void RouteList::load(ASt _as) { // Modified by wlee@isi.edu as = _as; // strncpy(as, _as, sizeof(as)); const PrefixRanges *registered_routes = whois->expandAS(as); if (!registered_routes) { tcl_Eval("showWarning { No object for AS%d}", as); return; } // Added by wlee if (registered_routes->isEmpty()) { tcl_Eval("showWarning { No route for AS%d}", as); return; } RouteList::Route *vr; for (int i = registered_routes->low(); i < registered_routes->fence(); i++) radix.insert(new RouteList::Route((*registered_routes)[i])); // below code is less eficient than above commented code // however user interface looks better // undo this once expand returns a RadixSet for (RadixNode *nd = &radix; nd; nd = nd->next()) if (nd->state == RADIX_FULL) rlist.append((Route *) nd->data); get_registrations_init(rlist.head()); } void RouteList::read_bgp_dump(char *fname) { char r[1024]; char *status; Pix rpix; RouteList::Route *vr, *tail; ifstream dump(fname); tail = rlist.tail(); extern int readbgpdump(RadixSet &); extern FILE *bgpin; bgpin = fopen(fname, "r"); if (! bgpin) { cerr << "File " << fname << " not found." << endl; return; } readbgpdump(real_routes); fclose(bgpin); for (vr = rlist.head(); vr; vr = rlist.next(vr)) if (real_routes.contains(vr->route)) vr->rtd_status = VR_ROUTED_YES; else if (! (vr->rtd_status & VR_ROUTED_YES)) vr->rtd_status = VR_ROUTED_NO; if (!define_irr_radix()) return; PrefixRange pr; RadixSet::PrefixRangeIterator prIterator(&real_routes); for (bool b = prIterator.first(pr); b; b = prIterator.next(pr)) { if (! radix.find(&pr)){ vr = new RouteList::Route(pr); vr->rtd_status = VR_ROUTED_YES; if (irr_radix->left || irr_radix->rght) { // has a node other than 0/0 if (! irr_radix->find(&vr->route)) { vr->reg_status = VR_REGISTERED_NO; vr->status = VR_IRR_DONT_QUERY; } } else { vr->reg_status = VR_REGISTERED_UNDTRMND; } rlist.append(vr); radix.insert(vr); } } display(); if (tail) get_registrations_init(rlist.next(tail)); } // Modified by wlee from void to bool bool RouteList::define_irr_radix() { //cerr << "measure_now before expand(IRR_ROUTES)" << endl; //cerr << ru << endl; if (! irr_radix) { tcl_Eval("popup_message {This will take 3 to 5 mins to finish, during which, your roe will freeze. Please be patient!}"); const PrefixRanges *irr_routes = whois->expandRSSet("RS-ANY"); // Added by wlee if (!irr_routes) return false; //cerr << "measure_now after expand(IRR_ROUTES)" << endl; //cerr << ru << endl; irr_radix = new Radix; for (int i = irr_routes->low(); i < irr_routes->fence(); i++) { irr_radix->insert(new RouteList::Route((*irr_routes)[i])); } } //cerr << "measure_now after define_irr_radix()" << endl; //cerr << ru << endl; return true; } void RouteList::get_more_specifics(char *selection) { RouteList::Route *vr; Route *tail; if (!define_irr_radix()) return; tail = rlist.tail(); for (char *s = strtok(selection, " \t"); s; s = strtok(NULL, " \t")) { vr = vrdisplayed[atoi(s)]; RadixNode *rdnode = irr_radix->find(&vr->route); if (rdnode) { get_more_specifics_add(rdnode->left); get_more_specifics_add(rdnode->rght); } } //cerr << "measure_now after get_more_specifics_add(t)" << endl; //cerr << ru << endl; if (tail != rlist.tail()) { display(); if (tail) get_registrations_init(rlist.next(tail)); } } void RouteList::get_more_specifics_add(RadixNode *rdnode) { RouteList::Route *vr; if (!rdnode) return; PrefixRange *prefixRange = &((RouteList::Route *)rdnode->data)->route; if (rdnode->state == RADIX_FULL) { // is it actually a route in IRR RadixNode *node = radix.find(prefixRange); if (!node || node->state == RADIX_EMPTY) { // is not in radix vr = new RouteList::Route(*prefixRange); rlist.append(vr); if (!node) radix.insert(vr); else { node->state |= RADIX_FULL; node->data = vr; } } } if (rdnode->left) get_more_specifics_add(rdnode->left); if (rdnode->rght) get_more_specifics_add(rdnode->rght); } void RouteList::get_less_specifics(char *selection) { RouteList::Route *vr; Route *tail; RadixNode *irr_node; RadixNode *node; if (!define_irr_radix()) return; tail = rlist.tail(); for (char *s = strtok(selection, " \t"); s; s = strtok(NULL, " \t")) { vr = vrdisplayed[atoi(s)]; irr_node = irr_radix->find(&vr->route); node = radix.find(&vr->route); if (irr_node && node) { // add the parents of irr_node for (irr_node = irr_node->prnt, node = node->prnt; irr_node; irr_node = irr_node->prnt, node = node->prnt) { if (irr_node->state == RADIX_FULL && node->state == RADIX_EMPTY) { // the node exists in irr_radix but not in radix, so add it PrefixRange *prefixRange = &((RouteList::Route *)irr_node->data)->route; vr = new RouteList::Route(*prefixRange); rlist.append(vr); node->state |= RADIX_FULL; node->data = vr; } } } } if (tail != rlist.tail()) { display(); if (tail) get_registrations_init(rlist.next(tail)); } }