// -*- c-basic-offset: 4; tab-width: 8; indent-tabs-mode: t -*- // vim:set sts=4 ts=8: // Copyright (c) 2001-2005 International Computer Science Institute // // Permission is hereby granted, free of charge, to any person obtaining a // copy of this software and associated documentation files (the "Software") // to deal in the Software without restriction, subject to the conditions // listed in the XORP LICENSE file. These conditions include: you must // preserve this copyright notice, and you cannot mention the copyright // holders in advertising related to the Software without their permission. // The Software is provided WITHOUT ANY WARRANTY, EXPRESS OR IMPLIED. This // notice is a summary of the XORP LICENSE file; the license in that file is // legally binding. // $XORP: xorp/bgp/bgp.hh,v 1.57 2006/02/18 00:07:25 atanu Exp $ #ifndef __BGP_MAIN_HH__ #define __BGP_MAIN_HH__ #include "libxorp/eventloop.hh" #include "libxorp/status_codes.h" #include "libxipc/xrl_std_router.hh" #include "libxorp/profile.hh" #include "socket.hh" #include "packet.hh" #include "peer.hh" #include "peer_list.hh" #include "plumbing.hh" #include "iptuple.hh" #include "path_attribute.hh" #include "peer_handler.hh" #include "process_watch.hh" #include "libfeaclient/ifmgr_xrl_mirror.hh" #include "policy/backend/version_filters.hh" class XrlBgpTarget; class BGPMain : public ServiceBase, public IfMgrHintObserver, public ServiceChangeObserverBase { public: BGPMain(); ~BGPMain(); /** * Get the process status */ ProcessStatus status(string& reason); /** * Startup operation. * * @return true on success, false on failure. */ bool startup(); /** * Shutdown operation. * * @return true on success, false on failure. */ bool shutdown(); /** * A method that should be called when an internal subsystem comes up. * * @param component_name the name of the component. */ void component_up(const string& component_name); /** * A method that should be called when an internal subsystem goes down. * * @param component_name the name of the component. */ void component_down(const string& component_name); /** * Test whether an interface is enabled. * * @param interface the name of the interface to test. * @return true if it exists and is enabled, otherwise false. */ bool is_interface_enabled(const string& interface) const; /** * Test whether an interface/vif is enabled. * * @param interface the name of the interface to test. * @param vif the name of the vif to test. * @return true if it exists and is enabled, otherwise false. */ bool is_vif_enabled(const string& interface, const string& vif) const; /** * Test whether an IPv4 interface/vif/address is enabled. * * @param interface the name of the interface to test. * @param vif the name of the vif to test. * @param address the address to test. * @return true if it exists and is enabled, otherwise false. */ bool is_address_enabled(const string& interface, const string& vif, const IPv4& address) const; /** * Test whether an IPv6 interface/vif/address is enabled. * * @param interface the name of the interface to test. * @param vif the name of the vif to test. * @param address the address to test. * @return true if it exists and is enabled, otherwise false. */ bool is_address_enabled(const string& interface, const string& vif, const IPv6& address) const; typedef XorpCallback2<void, const string&, bool>::RefPtr InterfaceStatusCb; typedef XorpCallback3<void, const string&, const string&, bool>::RefPtr VifStatusCb; typedef XorpCallback4<void, const string&, const string&, const IPv4&, bool>::RefPtr AddressStatus4Cb; typedef XorpCallback4<void, const string&, const string&, const IPv6&, bool>::RefPtr AddressStatus6Cb; /** * Add a callback for tracking the interface status. * * The callback will be invoked whenever the status of the interface * is changed from disabled to enabled or vice-versa. * * @param cb the callback to register. */ void register_interface_status(InterfaceStatusCb cb) { _interface_status_cb = cb; } /** * Add a callback for tracking the interface/vif status. * * The callback will be invoked whenever the status of the interface/vif * is changed from disabled to enabled or vice-versa. * * @param cb the callback to register. */ void register_vif_status(VifStatusCb cb) { _vif_status_cb = cb; } /** * Add a callback for tracking the IPv4 interface/vif/address status. * * The callback will be invoked whenever the status of the tuple * (interface, vif, address) is changed from disabled to enabled * or vice-versa. * * @param cb the callback to register. */ void register_address_status(AddressStatus4Cb cb) { _address_status4_cb = cb; } /** * Add a callback for tracking the IPv6 interface/vif/address status. * * The callback will be invoked whenever the status of the tuple * (interface, vif, address) is changed from disabled to enabled * or vice-versa. * * @param cb the callback to register. */ void register_address_status(AddressStatus6Cb cb) { _address_status6_cb = cb; } /** * Obtain the subnet prefix length for an IPv4 interface/vif/address. * * @param interface the name of the interface. * @param vif the name of the vif. * @param address the address. * @return the subnet prefix length for the address. */ uint32_t get_prefix_length(const string& interface, const string& vif, const IPv4& address); /** * Obtain the subnet prefix length for an IPv6 interface/vif/address. * * @param interface the name of the interface. * @param vif the name of the vif. * @param address the address. * @return the subnet prefix length for the address. */ uint32_t get_prefix_length(const string& interface, const string& vif, const IPv6& address); /** * Obtain the MTU for an interface. * * @param the name of the interface. * @return the mtu for the interface. */ uint32_t get_mtu(const string& interface); /** * Is the address one of this routers interface addresses? */ bool interface_address4(IPv4 address) const; /** * Is the address one of this routers interface addresses? */ bool interface_address6(IPv6 address) const; /** * Set the local configuration. * * @param as as number. * * @param id router id. */ void local_config(const uint32_t& as, const IPv4& id); /** * Set or disable the confederation identifier. */ void set_confederation_identifier(const uint32_t& as, bool disable); /** * Set the cluster ID and enable or disable route reflection. */ void set_cluster_id(const IPv4& cluster_id, bool disable); /** * Set the route flap damping parameters. */ void set_damping(uint32_t half_life,uint32_t max_suppress,uint32_t reuse, uint32_t suppress, bool disable); /** * attach peer to peerlist * * @param p BGP peer. */ void attach_peer(BGPPeer *p); /** * detach peer from the peerlist. * * @param p BGP peer. */ void detach_peer(BGPPeer *p); /** * attach peer to deleted peerlist * * @param p BGP peer. */ void attach_deleted_peer(BGPPeer *p); /** * detach peer from the deleted peerlist. * * @param p BGP peer. */ void detach_deleted_peer(BGPPeer *p); /** * Find peer with this iptuple from the list provided * * @param search iptuple. * @param peers list to search. * * @return A pointer to a peer if one is found NULL otherwise. */ BGPPeer *find_peer(const Iptuple& search, list<BGPPeer *>& peers); /** * Find peer with this iptuple * * @param search iptuple. * * @return A pointer to a peer if one is found NULL otherwise. */ BGPPeer *find_peer(const Iptuple& search); /** * Find peer with this iptuple on the deleted peer list. * * @param search iptuple. * * @return A pointer to a peer if one is found NULL otherwise. */ BGPPeer *find_deleted_peer(const Iptuple& search); /** * create a new peer and attach it to the peerlist. * * @param pd BGP peer data. * * @return true on success */ bool create_peer(BGPPeerData *pd); /** * delete peer tear down connection and remove for peerlist. XrlBgpTarget xbt(bgp.get_router(), bgp); * * @param iptuple iptuple. * * @return true on success */ bool delete_peer(const Iptuple& iptuple); /** * enable peer * * @param iptuple iptuple. * * @return true on success */ bool enable_peer(const Iptuple& iptuple); /** * disable peer * * @param iptuple iptuple. * * @return true on success */ bool disable_peer(const Iptuple& iptuple); /** * Drop this peering and if it was configured up allow it attempt * a new peering. * * @param iptuple iptuple. * * @return true on success */ bool bounce_peer(const Iptuple& iptuple); /** * One of the local IP addresses of this router has changed. Where * a change can be an addition or removal or a change in the link * status. If the provided address matches any of the local ip * addresses of any of the peerings unconditionally bounce the * peering. Unconditional bouncing of the peering is all that is * required if a link has gone down the old session will be * dropped and the new one will fail. If the link has just come up * then a session will be made. */ void local_ip_changed(string local_address); /** * Change one of the tuple settings of this peering. * * @param iptuple original tuple. * @param iptuple new tuple. * * @return true on success */ bool change_tuple(const Iptuple& iptuple, const Iptuple& nptuple); /** * Find the tuple that has this peer address and both ports are * 179. This is a hack as at the moment of writing the rtrmgr * can't send both the old and new state of a variable. * * @param peer_addr of tuple. * @param otuple the tuple if one is found. * * @return true if a tuple is matched. */ bool find_tuple_179(string peer_addr, Iptuple& otuple); /** * Change the local IP address of this peering. * * @param iptuple iptuple. * @param local_ip new value. * * @return true on success */ bool change_local_ip(const Iptuple& iptuple, const string& local_ip); /** * Change the local IP port of this peering. * * @param iptuple iptuple. * @param local_port new value. * * @return true on success */ bool change_local_port(const Iptuple& iptuple, uint32_t local_port); /** * Change the peer IP port of this peering. * * @param iptuple iptuple. * @param peer_port new value. * * @return true on success */ bool change_peer_port(const Iptuple& iptuple, uint32_t peer_port); /** * set peer as * * @param iptuple iptuple. * @param peer_as new value. * * @return true on success */ bool set_peer_as(const Iptuple& iptuple, uint32_t peer_as); /** * set holdtime * * @param iptuple iptuple. * @param holdtime new value. * * @return true on success */ bool set_holdtime(const Iptuple& iptuple, uint32_t holdtime); /** * set delay open time * * @param iptuple iptuple. * @param delay_open_time new value. * * @return true on success */ bool set_delay_open_time(const Iptuple& iptuple, uint32_t delay_open_time); /** * set route reflector client * * @param iptuple iptuple. * @param rr true if this peer is a route reflector client. * * @return true on success */ bool set_route_reflector_client(const Iptuple& iptuple, bool rr); /** * set route confederation member * * @param iptuple iptuple. * @param conf true if this peer is a confederation member. * * @return true on success */ bool set_confederation_member(const Iptuple& iptuple, bool conf); /** * set IPv4 next-hop. * * @param iptuple iptuple. * @param next-hop * * @return true on success */ bool set_nexthop4(const Iptuple& iptuple, const IPv4& next_hop); /** * set IPv6 next-hop. * * @param iptuple iptuple. * @param next-hop * * @return true on success */ bool set_nexthop6(const Iptuple& iptuple, const IPv6& next_hop); /** * get IPv6 next-hop. * * @param iptuple iptuple. * @param next-hop * * @return true on success */ bool get_nexthop6(const Iptuple& iptuple, IPv6& next_hop); /** * Set peer state. * * @param iptuple iptuple. * @param state should the peering be enable or disabled. * * @ return true on success. */ bool set_peer_state(const Iptuple& iptuple, bool state); /** * Activate peer. * * Enable the peering based on the peer state. * * @param iptuple iptuple. * * @ return true on success. */ bool activate(const Iptuple& iptuple); /** * Set peer TCP-MD5 password. * * @param iptuple iptuple. * @param password The password to use for TCP-MD5 authentication; * if this is the empty string, then authentication will be disabled. * * @return true on success. */ bool set_peer_md5_password(const Iptuple& iptuple, const string& password); /* * Set next hop rewrite filter. * * @param iptuple iptuple. * @param next_hop next hop value zero to clear filter. * * @return true on success */ bool next_hop_rewrite_filter(const Iptuple& iptuple, const IPv4& next_hop); bool get_peer_list_start(uint32_t& token); bool get_peer_list_next(const uint32_t& token, string& local_ip, uint32_t& local_port, string& peer_ip, uint32_t& peer_port); bool get_peer_id(const Iptuple& iptuple, IPv4& peer_id); bool get_peer_status(const Iptuple& iptuple, uint32_t& peer_state, uint32_t& admin_status); bool get_peer_negotiated_version(const Iptuple& iptuple, int32_t& neg_version); bool get_peer_as(const Iptuple& iptuple, uint32_t& peer_as); bool get_peer_msg_stats(const Iptuple& iptuple, uint32_t& in_updates, uint32_t& out_updates, uint32_t& in_msgs, uint32_t& out_msgs, uint16_t& last_error, uint32_t& in_update_elapsed); bool get_peer_established_stats(const Iptuple& iptuple, uint32_t& transitions, uint32_t& established_time); bool get_peer_timer_config(const Iptuple& iptuple, uint32_t& retry_interval, uint32_t& hold_time, uint32_t& keep_alive, uint32_t& hold_time_configured, uint32_t& keep_alive_configured, uint32_t& min_as_origination_interval, uint32_t& min_route_adv_interval); bool register_ribname(const string& name); void main_loop(); /** * shutdown BGP cleanly */ void terminate() { _exit_loop = true; } bool run() {return !_exit_loop;} XorpFd create_listener(const Iptuple& iptuple); LocalData *get_local_data(); void start_server(const Iptuple& iptuple); void stop_server(const Iptuple& iptuple); /** * Stop listening for incoming connections. */ void stop_all_servers(); /** * Originate an IPv4 route * * @param nlri subnet to announce * @param next_hop to forward to * @param unicast if true install in unicast routing table * @param multicast if true install in multicast routing table * @param policytags policy-tags associated with route. * * @return true on success */ bool originate_route(const IPv4Net& nlri, const IPv4& next_hop, const bool& unicast, const bool& multicast, const PolicyTags& policytags); /** * Originate an IPv6 route * * @param nlri subnet to announce * @param next_hop to forward to * @param unicast if true install in unicast routing table * @param multicast if true install in multicast routing table * @param policytags policy-tags associated with route. * * @return true on success */ bool originate_route(const IPv6Net& nlri, const IPv6& next_hop, const bool& unicast, const bool& multicast, const PolicyTags& policytags); /** * Withdraw an IPv4 route * * @param nlri subnet to withdraw * @param unicast if true withdraw from unicast routing table * @param multicast if true withdraw from multicast routing table * * @return true on success */ bool withdraw_route(const IPv4Net& nlri, const bool& unicast, const bool& multicast) const; /** * Withdraw an IPv6 route * * @return true on success */ bool withdraw_route(const IPv6Net& nlri, const bool& unicast, const bool& multicast) const; template <typename A> bool get_route_list_start(uint32_t& token, const bool& unicast, const bool& multicast); template <typename A> bool get_route_list_next( // Input values, const uint32_t& token, // Output values, IPv4& peer_id, IPNet<A>& net, uint32_t& origin, vector<uint8_t>& aspath, A& nexthop, int32_t& med, int32_t& localpref, int32_t& atomic_agg, vector<uint8_t>& aggregator, int32_t& calc_localpref, vector<uint8_t>& attr_unknown, bool& best, bool& unicast, bool& multicast); bool rib_client_route_info_changed4( // Input values, const IPv4& addr, const uint32_t& prefix_len, const IPv4& nexthop, const uint32_t& metric); bool rib_client_route_info_changed6( // Input values, const IPv6& addr, const uint32_t& prefix_len, const IPv6& nexthop, const uint32_t& metric); bool rib_client_route_info_invalid4( // Input values, const IPv4& addr, const uint32_t& prefix_len); bool rib_client_route_info_invalid6( // Input values, const IPv6& addr, const uint32_t& prefix_len); /** * set parameter * * Typically called via XRL's to set which parameters we support * per peer. * * @param iptuple iptuple * @param parameter * @param toggle enable or disable parameter */ bool set_parameter( // Input values, const Iptuple& iptuple, const string& parameter, const bool toggle); /** * Originally inserted for testing. However, now used by all the * "rib_client_route_info_*" methods. */ BGPPlumbing *plumbing_unicast() const { return _plumbing_unicast; } BGPPlumbing *plumbing_multicast() const { return _plumbing_multicast; } XrlStdRouter *get_router() { return _xrl_router; } EventLoop& eventloop() { return _eventloop; } XrlBgpTarget *get_xrl_target() { return _xrl_target; } /** * Call via XrlBgpTarget when the finder reports that a process * has started. * * @param target_class Class of process that has started. * @param target_instance Instance name of process that has started. */ void notify_birth(const string& target_class, const string& target_instance) { _process_watch->birth(target_class, target_instance); } /** * Call via XrlBgpTarget when the finder reports that a process * has terminated. * * @param target_class Class of process that has terminated. * @param target_instance Instance name of process that has terminated. */ void notify_death(const string& target_class, const string& target_instance) { _process_watch->death(target_class, target_instance); } /** * @return Return true when all the processes that BGP is * dependent on are ready. */ bool processes_ready() { return _process_watch->ready(); } /** * @return Return the bgp mib name. */ string bgp_mib_name() const { return "bgp4_mib"; } /** * Check to see if the bgp snmp entity is running. */ bool do_snmp_trap() const { return _process_watch->target_exists(bgp_mib_name()); } /** * To be called when the finder dies. */ void finder_death(const char *file, const int lineno) { _process_watch->finder_death(file, lineno); } /** * Configure a policy filter * * @param filter Id of filter to configure. * @param conf Configuration of filter. */ void configure_filter(const uint32_t& filter, const string& conf); /** * Reset a policy filter. * * @param filter Id of filter to reset. */ void reset_filter(const uint32_t& filter); /** * Push routes through policy filters for re-filtering. */ void push_routes(); /** * @return a reference to the profiler. */ Profile& profile() {return _profile;} protected: private: /** * A method invoked when the status of a service changes. * * @param service the service whose status has changed. * @param old_status the old status. * @param new_status the new status. */ void status_change(ServiceBase* service, ServiceStatus old_status, ServiceStatus new_status); /** * Obtain a pointer to the interface manager service base. * * @return a pointer to the interface manager service base. */ const ServiceBase* ifmgr_mirror_service_base() const { return dynamic_cast<const ServiceBase*>(_ifmgr); } /** * Obtain a reference to the interface manager's interface tree. * * @return a reference to the interface manager's interface tree. */ const IfMgrIfTree& ifmgr_iftree() const { return _ifmgr->iftree(); } /** * An IfMgrHintObserver method invoked when the initial interface tree * information has been received. */ void tree_complete(); /** * An IfMgrHintObserver method invoked whenever the interface tree * information has been changed. */ void updates_made(); /** * Callback method that is invoked when the status of an address changes. */ void address_status_change4(const string& interface, const string& vif, const IPv4& source, bool state); /** * Callback method that is invoked when the status of an address changes. */ void address_status_change6(const string& interface, const string& vif, const IPv6& source, bool state); /** * Store the socket descriptor and iptuple together. */ struct Server { Server(XorpFd fd, Iptuple iptuple) : _serverfd(fd) { _tuples.push_back(iptuple); } Server(const Server& rhs) { _serverfd = rhs._serverfd; _tuples = rhs._tuples; } Server& operator=(const Server& rhs) { if (&rhs == this) return *this; _serverfd = rhs._serverfd; _tuples = rhs._tuples; return *this; } string str() { return c_format("fd(%s)", _serverfd.str().c_str()); } XorpFd _serverfd; list <Iptuple> _tuples; }; list<Server> _serverfds; /** * Callback method called when a connection attempt is made. */ void connect_attempt(XorpFd fd, IoEventType type, string laddr, uint16_t lport); template <typename A> void extract_attributes(// Input values, const PathAttributeList<A>& attributes, // Output values, uint32_t& origin, vector<uint8_t>& aspath, A& nexthop, int32_t& med, int32_t& localpref, int32_t& atomic_agg, vector<uint8_t>& aggregator, int32_t& calc_localpref, vector<uint8_t>& attr_unknown); bool _exit_loop; BGPPeerList *_peerlist; // List of current BGP peers. BGPPeerList *_deleted_peerlist; // List of deleted BGP peers. /** * Unicast Routing Table. SAFI = 1. */ BGPPlumbing *_plumbing_unicast; NextHopResolver<IPv4> *_next_hop_resolver_ipv4; /** * Multicast Routing Table. SAFI = 2. */ BGPPlumbing *_plumbing_multicast; NextHopResolver<IPv6> *_next_hop_resolver_ipv6; /** * Token generator to map between unicast and multicast. * */ struct RoutingTableToken { RoutingTableToken() : _last(0) {} uint32_t create(uint32_t& internal_token, const bool& unicast, const bool& multicast) { while(_tokens.find(_last) != _tokens.end()) _last++; _tokens[_last] = WhichTable(internal_token, unicast, multicast); return _last; } bool lookup(uint32_t& token, bool& unicast, bool& multicast) { map <uint32_t, WhichTable>::iterator i; i = _tokens.find(token); if (i == _tokens.end()) return false; WhichTable t = i->second; token = t._token; unicast = t._unicast; multicast = t._multicast; return true; } void erase(uint32_t& token) { _tokens.erase(token); } private: struct WhichTable { WhichTable() {} WhichTable(uint32_t token, bool unicast, bool multicast) : _token(token), _unicast(unicast), _multicast(multicast) {} uint32_t _token; bool _unicast; bool _multicast; }; map <uint32_t, WhichTable> _tokens; uint32_t _last; }; template <typename A> RoutingTableToken& get_token_table(); RoutingTableToken _table_ipv4; RoutingTableToken _table_ipv6; XrlBgpTarget *_xrl_target; RibIpcHandler *_rib_ipc_handler; AggregationHandler *_aggregation_handler; LocalData _local_data; XrlStdRouter *_xrl_router; static EventLoop _eventloop; ProcessWatch *_process_watch; VersionFilters _policy_filters; Profile _profile; size_t _component_count; IfMgrXrlMirror* _ifmgr; bool _is_ifmgr_ready; IfMgrIfTree _iftree; // The interface state information InterfaceStatusCb _interface_status_cb; VifStatusCb _vif_status_cb; AddressStatus4Cb _address_status4_cb; AddressStatus6Cb _address_status6_cb; set<IPv4> _interfaces_ipv4; // IPv4 interface addresses set<IPv6> _interfaces_ipv6; // IPv6 interface addresses }; template <typename A> bool BGPMain::get_route_list_start(uint32_t& token, const bool& unicast, const bool& multicast) { A dummy; if (unicast) { token = _plumbing_unicast->create_route_table_reader(dummy); } else if (multicast) { token = _plumbing_multicast->create_route_table_reader(dummy); } else { XLOG_ERROR("Must specify at least one of unicast or multicast"); return false; } token = get_token_table<A>().create(token, unicast, multicast); return true; } template <typename A> void BGPMain::extract_attributes(// Input values, const PathAttributeList<A>& attributes, // Output values, uint32_t& origin, vector<uint8_t>& aspath, A& nexthop, int32_t& med, int32_t& localpref, int32_t& atomic_agg, vector<uint8_t>& aggregator, int32_t& calc_localpref, vector<uint8_t>& attr_unknown) { origin = attributes.origin(); attributes.aspath().encode_for_mib(aspath); nexthop = attributes.nexthop(); const MEDAttribute* med_att = attributes.med_att(); if (med_att) { med = (int32_t)med_att->med(); if (med < 0) { med = 0x7ffffff; XLOG_WARNING("MED truncated in MIB from %u to %u\n", XORP_UINT_CAST(med_att->med()), XORP_UINT_CAST(med)); } } else { med = -1; } const LocalPrefAttribute* local_pref_att = attributes.local_pref_att(); if (local_pref_att) { localpref = (int32_t)local_pref_att->localpref(); if (localpref < 0) { localpref = 0x7ffffff; XLOG_WARNING("LOCAL_PREF truncated in MIB from %u to %u\n", XORP_UINT_CAST(local_pref_att->localpref()), XORP_UINT_CAST(localpref)); } } else { localpref = -1; } if (attributes.atomic_aggregate_att()) atomic_agg = 2; else atomic_agg = 1; const AggregatorAttribute* agg_att = attributes.aggregator_att(); if (agg_att) { aggregator.resize(6); agg_att->route_aggregator().copy_out(&aggregator[0]); agg_att->aggregator_as().copy_out(&aggregator[4]); } else { assert(aggregator.size()==0); } calc_localpref = 0; attr_unknown.resize(0); } template <typename A> bool BGPMain::get_route_list_next( // Input values, const uint32_t& token, // Output values, IPv4& peer_id, IPNet<A>& net, uint32_t& origin, vector<uint8_t>& aspath, A& nexthop, int32_t& med, int32_t& localpref, int32_t& atomic_agg, vector<uint8_t>& aggregator, int32_t& calc_localpref, vector<uint8_t>& attr_unknown, bool& best, bool& unicast_global, bool& multicast_global) { bool unicast, multicast; uint32_t internal_token, global_token; internal_token = global_token = token; A dummy; if (!get_token_table<A>().lookup(internal_token, unicast, multicast)) return false; const SubnetRoute<A>* route; if (unicast) { if (_plumbing_unicast->read_next_route(internal_token, route, peer_id)) { net = route->net(); extract_attributes(*route->attributes(), origin, aspath, nexthop, med, localpref, atomic_agg, aggregator, calc_localpref, attr_unknown); best = route->is_winner(); unicast_global = true; multicast_global = false; return true; } // We may have been asked for the unicast and multicast // routing tables. In which case once we have completed the // unicast routing table move onto providing the multicast // table. get_token_table<A>().erase(global_token); if (multicast) { internal_token = _plumbing_multicast->create_route_table_reader(dummy); global_token = get_token_table<A>(). create(internal_token, false, true); } } if (multicast) { if (_plumbing_multicast->read_next_route(internal_token, route, peer_id)) { net = route->net(); extract_attributes(*route->attributes(), origin, aspath, nexthop, med, localpref, atomic_agg, aggregator, calc_localpref, attr_unknown); best = route->is_winner(); unicast_global = false; multicast_global = true; return true; } get_token_table<A>().erase(global_token); } return false; } // template <typename A> // struct NameOf { // static const char* get() { return "Unknown"; } // }; // template <> const char* NameOf<IPv4>::get() { return "IPv4"; } // template <> const char* NameOf<IPv6>::get() { return "IPv6"; } inline const char * pb(const bool val) { return val ? "true" : "false"; } #endif // __BGP_MAIN_HH__