Line data Source code
1 : /*
2 : * Copyright (c) 2013 Juniper Networks, Inc. All rights reserved.
3 : */
4 :
5 : #include "bgp/bgp_ribout.h"
6 :
7 : #include <boost/bind/bind.hpp>
8 :
9 : #include <algorithm>
10 :
11 : #include "sandesh/sandesh_trace.h"
12 : #include "base/string_util.h"
13 : #include "bgp/bgp_peer_types.h"
14 : #include "bgp/bgp_ribout_updates.h"
15 : #include "bgp/bgp_export.h"
16 : #include "bgp/bgp_factory.h"
17 : #include "bgp/bgp_route.h"
18 : #include "bgp/bgp_server.h"
19 : #include "bgp/bgp_table.h"
20 : #include "bgp/bgp_update.h"
21 : #include "bgp/bgp_update_sender.h"
22 : #include "bgp/ipeer.h"
23 : #include "bgp/routing-instance/routing_instance.h"
24 : #include "db/db.h"
25 :
26 : using std::find;
27 : using namespace boost::placeholders;
28 :
29 0 : RibOutAttr::NextHop::NextHop(const BgpTable *table, IpAddress address,
30 : const MacAddress &mac, uint32_t label, uint32_t l3_label,
31 : const ExtCommunity *ext_community, const LargeCommunity *large_community,
32 0 : bool vrf_originated)
33 0 : : address_(address),
34 0 : mac_(mac),
35 0 : label_(label),
36 0 : l3_label_(l3_label),
37 0 : origin_vn_index_(-1) {
38 0 : as_t asn = table ? table->server()->autonomous_system() : 0;
39 0 : bool all = table ?
40 0 : table->server()->global_config()->all_tags_are_global() : false;
41 0 : if (large_community != nullptr) {
42 0 : tag_list_ = large_community->GetTagList(all ? 0 : asn);
43 : }
44 0 : if (ext_community != nullptr) {
45 0 : encap_ = ext_community->GetTunnelEncap();
46 0 : origin_vn_index_ = ext_community->GetOriginVnIndex();
47 : }
48 :
49 0 : if (origin_vn_index_ < 0 && vrf_originated) {
50 0 : origin_vn_index_ =
51 0 : table ? table->routing_instance()->virtual_network_index() : 0;
52 : }
53 0 : }
54 :
55 0 : int RibOutAttr::NextHop::CompareTo(const NextHop &rhs) const {
56 0 : KEY_COMPARE(address_, rhs.address_);
57 0 : KEY_COMPARE(mac_, rhs.mac_) ;
58 0 : KEY_COMPARE(label_, rhs.label_);
59 0 : KEY_COMPARE(l3_label_, rhs.l3_label_);
60 0 : KEY_COMPARE(source_address_, rhs.source_address_);
61 0 : KEY_COMPARE(origin_vn_index_, rhs.origin_vn_index_);
62 0 : KEY_COMPARE(encap_.size(), rhs.encap_.size());
63 0 : for (size_t idx = 0; idx < encap_.size(); ++idx) {
64 0 : KEY_COMPARE(encap_[idx], rhs.encap_[idx]);
65 : }
66 0 : KEY_COMPARE(tag_list_.size(), rhs.tag_list_.size());
67 0 : for (size_t idx = 0; idx < tag_list_.size(); ++idx) {
68 0 : KEY_COMPARE(tag_list_[idx], rhs.tag_list_[idx]);
69 : }
70 0 : return 0;
71 : }
72 :
73 0 : bool RibOutAttr::NextHop::operator==(const NextHop &rhs) const {
74 0 : return CompareTo(rhs) == 0;
75 : }
76 :
77 0 : bool RibOutAttr::NextHop::operator!=(const NextHop &rhs) const {
78 0 : return CompareTo(rhs) != 0;
79 : }
80 :
81 0 : bool RibOutAttr::NextHop::operator<(const NextHop &rhs) const {
82 0 : return CompareTo(rhs) < 0;
83 : }
84 :
85 348 : RibOutAttr::RibOutAttr()
86 348 : : label_(0),
87 348 : l3_label_(0),
88 348 : is_xmpp_(false),
89 348 : vrf_originated_(false) {
90 348 : }
91 :
92 : //
93 : // Copy constructor.
94 : // Do not copy the string representation;
95 : //
96 186 : RibOutAttr::RibOutAttr(const RibOutAttr &rhs) {
97 186 : attr_out_ = rhs.attr_out_;
98 186 : nexthop_list_ = rhs.nexthop_list_;
99 186 : label_ = rhs.label_;
100 186 : l3_label_ = rhs.l3_label_;
101 186 : source_address_ = rhs.source_address_;
102 186 : is_xmpp_ = rhs.is_xmpp_;
103 186 : vrf_originated_ = rhs.vrf_originated_;
104 186 : }
105 :
106 0 : RibOutAttr::RibOutAttr(const BgpTable *table, const BgpAttr *attr,
107 0 : uint32_t label, uint32_t l3_label, bool is_xmpp)
108 0 : : attr_out_(attr),
109 0 : label_(label),
110 0 : l3_label_(l3_label),
111 0 : is_xmpp_(is_xmpp),
112 0 : vrf_originated_(false) {
113 0 : if (attr && is_xmpp) {
114 0 : nexthop_list_.push_back(NextHop(table, attr->nexthop(),
115 0 : attr->mac_address(), label, l3_label, attr->ext_community(),
116 : attr->large_community(), false));
117 : }
118 0 : }
119 :
120 0 : RibOutAttr::RibOutAttr(const BgpTable *table, const BgpRoute *route,
121 0 : const BgpAttr *attr, uint32_t label, bool include_nh, bool is_xmpp)
122 0 : : attr_out_(attr),
123 0 : label_(0),
124 0 : l3_label_(0),
125 0 : is_xmpp_(is_xmpp),
126 0 : vrf_originated_(route->BestPath()->IsVrfOriginated()) {
127 0 : if (attr && include_nh) {
128 0 : if (is_xmpp) {
129 0 : nexthop_list_.push_back(NextHop(table, attr->nexthop(),
130 0 : attr->mac_address(), label, 0, attr->ext_community(),
131 0 : attr->large_community(), vrf_originated_));
132 : } else {
133 0 : label_ = label;
134 0 : l3_label_ = 0;
135 : }
136 : }
137 0 : }
138 :
139 250 : RibOutAttr::RibOutAttr(const BgpRoute *route, const BgpAttr *attr,
140 250 : bool is_xmpp) :
141 250 : label_(0), l3_label_(0), is_xmpp_(is_xmpp), vrf_originated_(false) {
142 : // Attribute should not be set already
143 250 : assert(!attr_out_);
144 :
145 250 : const BgpTable *table = static_cast<const BgpTable *>(route->get_table());
146 :
147 : // Always encode best path's attributes (including it's nexthop) and label.
148 250 : if (!is_xmpp) {
149 250 : set_attr(table, attr, route->BestPath()->GetLabel(),
150 : route->BestPath()->GetL3Label(), false, is_xmpp);
151 250 : return;
152 : }
153 :
154 : // Encode ECMP nexthops only for XMPP peers.
155 : // Vrf Origination matters only for XMPP peers.
156 0 : set_attr(table, attr, route->BestPath()->GetLabel(),
157 0 : route->BestPath()->GetL3Label(), route->BestPath()->IsVrfOriginated(),
158 : is_xmpp);
159 :
160 0 : for (Route::PathList::const_iterator it = route->GetPathList().begin();
161 0 : it != route->GetPathList().end(); ++it) {
162 0 : const BgpPath *path = static_cast<const BgpPath *>(it.operator->());
163 :
164 : // Skip the best path.
165 0 : if (path == route->BestPath())
166 0 : continue;
167 :
168 : // Check if the path is ECMP eligible. If not, bail out, as the paths
169 : // are sorted in cost order anyways.
170 0 : if (route->BestPath()->PathCompare(*path, true))
171 0 : break;
172 :
173 : // We have an eligible ECMP path.
174 : // Remember if the path was originated in the VRF. This is used to
175 : // determine if VRF's VN name can be used as the origin VN for the
176 : // nexthop.
177 0 : NextHop nexthop(table, path->GetAttr()->nexthop(),
178 0 : path->GetAttr()->mac_address(), path->GetLabel(),
179 : path->GetL3Label(), path->GetAttr()->ext_community(),
180 0 : path->GetAttr()->large_community(), path->IsVrfOriginated());
181 :
182 : // Skip if we have already encoded this next-hop
183 0 : if (find(nexthop_list_.begin(), nexthop_list_.end(), nexthop) !=
184 0 : nexthop_list_.end()) {
185 0 : continue;
186 : }
187 0 : nexthop_list_.push_back(nexthop);
188 0 : }
189 0 : }
190 :
191 : //
192 : // Assignment operator.
193 : // Do not copy the string representation;
194 : //
195 250 : RibOutAttr &RibOutAttr::operator=(const RibOutAttr &rhs) {
196 250 : attr_out_ = rhs.attr_out_;
197 250 : nexthop_list_ = rhs.nexthop_list_;
198 250 : label_ = rhs.label_;
199 250 : l3_label_ = rhs.l3_label_;
200 250 : source_address_ = rhs.source_address_;
201 250 : is_xmpp_ = rhs.is_xmpp_;
202 250 : vrf_originated_ = rhs.vrf_originated_;
203 250 : return *this;
204 : }
205 :
206 : //
207 : // Comparator for RibOutAttr.
208 : // First compare the BgpAttr and then the nexthops.
209 : //
210 320 : int RibOutAttr::CompareTo(const RibOutAttr &rhs) const {
211 320 : KEY_COMPARE(attr_out_.get(), rhs.attr_out_.get());
212 64 : KEY_COMPARE(nexthop_list_.size(), rhs.nexthop_list_.size());
213 64 : for (size_t idx = 0; idx < nexthop_list_.size(); ++idx) {
214 0 : KEY_COMPARE(nexthop_list_[idx], rhs.nexthop_list_[idx]);
215 : }
216 64 : KEY_COMPARE(label_, rhs.label());
217 64 : KEY_COMPARE(l3_label_, rhs.l3_label());
218 64 : KEY_COMPARE(source_address_, rhs.source_address());
219 64 : KEY_COMPARE(is_xmpp_, rhs.is_xmpp());
220 64 : KEY_COMPARE(vrf_originated_, rhs.vrf_originated());
221 64 : return 0;
222 : }
223 :
224 306 : void RibOutAttr::set_attr(const BgpTable *table, const BgpAttrPtr &attrp,
225 : uint32_t label, uint32_t l3_label, bool vrf_originated, bool is_xmpp) {
226 306 : if (!attr_out_) {
227 250 : attr_out_ = attrp;
228 250 : assert(nexthop_list_.empty());
229 250 : if (is_xmpp) {
230 0 : NextHop nexthop(table, attrp->nexthop(), attrp->mac_address(),
231 : label, l3_label, attrp->ext_community(),
232 0 : attrp->large_community(), vrf_originated);
233 0 : nexthop_list_.push_back(nexthop);
234 0 : } else {
235 250 : label_ = label;
236 250 : l3_label_ = l3_label;
237 : }
238 250 : return;
239 : }
240 :
241 56 : if (!attrp) {
242 0 : clear();
243 0 : return;
244 : }
245 :
246 56 : assert(attr_out_->nexthop() == attrp->nexthop());
247 56 : attr_out_ = attrp;
248 : }
249 :
250 186 : RouteState::RouteState() {
251 186 : }
252 :
253 : //
254 : // Move history from RouteState to RouteUpdate.
255 : //
256 118 : void RouteState::MoveHistory(RouteUpdate *rt_update) {
257 118 : AdvertiseSList adv_slist;
258 118 : SwapHistory(adv_slist);
259 118 : rt_update->SwapHistory(adv_slist);
260 118 : }
261 :
262 : //
263 : // Find the AdvertiseInfo element with matching RibOutAttr.
264 : //
265 84 : const AdvertiseInfo *RouteState::FindHistory(
266 : const RibOutAttr &roattr) const {
267 104 : for (AdvertiseSList::List::const_iterator iter = advertised_->begin();
268 208 : iter != advertised_->end(); iter++) {
269 148 : if (iter->roattr == roattr) return iter.operator->();
270 : }
271 20 : return NULL;
272 : }
273 :
274 : //
275 : // Compare AdevrtiseInfos in this RouteState with the UpdateInfo elements
276 : // in the given list.
277 : //
278 : // Uses brute force since the UpdateInfo and AdvertiseInfo lists typically
279 : // contain just a single element.
280 : //
281 : // Return true if the information in the RouteState is same as that in the
282 : // UpdateInfoSList.
283 : //
284 182 : bool RouteState::CompareUpdateInfo(const UpdateInfoSList &uinfo_slist) const {
285 : // Both lists must have the same number of elements.
286 182 : if (uinfo_slist->size() != advertised_->size())
287 98 : return false;
288 :
289 : // Compare the peerset for each UpdateInfo in the UpdateInfoSList to
290 : // the peerset for the corresponding AdvertiseInfo in the advertised
291 : // list.
292 84 : for (UpdateInfoSList::List::const_iterator iter = uinfo_slist->begin();
293 296 : iter != uinfo_slist->end(); ++iter) {
294 84 : const AdvertiseInfo *ainfo = FindHistory(iter->roattr);
295 148 : if (!ainfo || iter->target != ainfo->bitset)
296 20 : return false;
297 : }
298 :
299 64 : return true;
300 : }
301 :
302 : //
303 : // Create a new RibOut based on the BgpTable and RibExportPolicy.
304 : //
305 144 : RibOut::RibOut(BgpTable *table, BgpUpdateSender *sender,
306 144 : const RibExportPolicy &policy)
307 144 : : table_(table),
308 144 : sender_(sender),
309 144 : policy_(policy),
310 144 : listener_id_(DBTableBase::kInvalidId),
311 144 : bgp_export_(BgpStaticObjectFactory::Create<BgpExport>(this)) {
312 144 : name_ = "RibOut";
313 144 : if (policy_.type == BgpProto::XMPP) {
314 0 : name_ += " Type: XMPP";
315 144 : } else if (policy_.type == BgpProto::IBGP) {
316 96 : name_ += " Type: IBGP";
317 : } else {
318 48 : name_ += " Type: EBGP";
319 48 : name_ += " (AS " + integerToString(policy_.as_number);
320 48 : if (!policy_.nexthop.is_unspecified())
321 0 : name_ += " Nexthop " + policy_.nexthop.to_string();
322 48 : if (policy_.as_override)
323 0 : name_ += " ASOverride";
324 48 : name_ += ")";
325 : }
326 720 : for (int idx = 0; idx < DB::PartitionCount(); ++idx) {
327 576 : updates_.push_back(BgpStaticObjectFactory::Create<RibOutUpdates>(this, idx));
328 : }
329 144 : }
330 :
331 : //
332 : // Destructor for RibOut. Takes care of unregistering the RibOut from
333 : // the DBTableBase.
334 : //
335 144 : RibOut::~RibOut() {
336 144 : if (listener_id_ != DBTableBase::kInvalidId) {
337 144 : table_->Unregister(listener_id_);
338 144 : listener_id_ = DBTableBase::kInvalidId;
339 : }
340 144 : STLDeleteValues(&updates_);
341 144 : }
342 :
343 : //
344 : // Register the RibOut as a listener with the underlying DBTableBase. This
345 : // is separated out from the constructor to let the unit testing code work
346 : // using the bare bones RibOut functionality.
347 : //
348 : // Note that the corresponding unregister from the DBTableBase will happen
349 : // implicitly from the destructor.
350 : //
351 144 : void RibOut::RegisterListener() {
352 144 : if (listener_id_ != DBTableBase::kInvalidId)
353 0 : return;
354 144 : listener_id_ = table_->Register(
355 : boost::bind(&BgpExport::Export, bgp_export_.get(), _1, _2),
356 : ToString());
357 : }
358 :
359 : //
360 : // Register a new peer to the RibOut. If the peer is not present in the
361 : // PeerStateMap, create a new PeerState and add it to the map.
362 : // Join the IPeerUpdate to the UPDATE and BULK queues for all RibOutUpdates
363 : // associated with the RibOut.
364 : //
365 144 : void RibOut::Register(IPeerUpdate *peer) {
366 144 : PeerState *ps = state_map_.Locate(peer);
367 144 : assert(ps != NULL);
368 144 : active_peerset_.set(ps->index);
369 144 : sender_->Join(this, peer);
370 720 : for (int idx = 0; idx < DB::PartitionCount(); ++idx) {
371 576 : if (updates_[idx]->QueueJoin(RibOutUpdates::QUPDATE, ps->index))
372 0 : sender_->RibOutActive(idx, this, RibOutUpdates::QUPDATE);
373 576 : if (updates_[idx]->QueueJoin(RibOutUpdates::QBULK, ps->index))
374 0 : sender_->RibOutActive(idx, this, RibOutUpdates::QBULK);
375 : }
376 144 : }
377 :
378 : //
379 : // Unregister a IPeerUpdate from the RibOut.
380 : // Leave the IPeerUpdate from the UPDATE and BULK queues for all RibOutUpdates
381 : // associated with the RibOut.
382 : // Removes the IPeerUpdate from the PeerStateMap.
383 : // If this was the last IPeerUpdate in the RibOut, remove the RibOut from the
384 : // BgpTable. That will cause this RibOut itself to get destroyed.
385 : //
386 144 : void RibOut::Unregister(IPeerUpdate *peer) {
387 144 : PeerState *ps = state_map_.Find(peer);
388 144 : assert(ps != NULL);
389 144 : assert(!active_peerset_.test(ps->index));
390 :
391 720 : for (int idx = 0; idx < DB::PartitionCount(); ++idx) {
392 576 : updates_[idx]->QueueLeave(RibOutUpdates::QUPDATE, ps->index);
393 576 : updates_[idx]->QueueLeave(RibOutUpdates::QBULK, ps->index);
394 : }
395 144 : sender_->Leave(this, peer);
396 144 : state_map_.Remove(peer, ps->index);
397 :
398 144 : if (state_map_.empty()) {
399 144 : table_->RibOutDelete(policy_);
400 : }
401 144 : }
402 :
403 : //
404 : // Return true if the IPeerUpdate is registered to this RibOut.
405 : //
406 0 : bool RibOut::IsRegistered(IPeerUpdate *peer) {
407 0 : PeerState *ps = state_map_.Find(peer);
408 0 : return (ps != NULL);
409 : }
410 :
411 : //
412 : // Deactivate a IPeerUpdate from the RibOut. Removes it from the RibPeerSet of
413 : // active peers without removing it from the PeerStateMap.
414 : //
415 : // This must be called when the peer starts the process of leaving the RibOut
416 : // in order to prevent any new or existing routes from getting exported while
417 : // the route table walk for the leave processing is in progress.
418 : //
419 144 : void RibOut::Deactivate(IPeerUpdate *peer) {
420 144 : PeerState *ps = state_map_.Find(peer);
421 144 : assert(ps != NULL);
422 144 : assert(active_peerset_.test(ps->index));
423 144 : active_peerset_.reset(ps->index);
424 144 : }
425 :
426 0 : bool RibOut::IsActive(IPeerUpdate *peer) const {
427 0 : int index = GetPeerIndex(peer);
428 0 : return (index < 0 ? false : active_peerset_.test(index));
429 : }
430 :
431 : //
432 : // Build the subset of given RibPeerSet in this RibOut that are send ready.
433 : //
434 0 : void RibOut::BuildSendReadyBitSet(const RibPeerSet &peerset,
435 : RibPeerSet *mready) const {
436 0 : for (size_t bit = peerset.find_first(); bit != RibPeerSet::npos;
437 0 : bit = peerset.find_next(bit)) {
438 0 : IPeerUpdate *peer = GetPeer(bit);
439 0 : if (peer->send_ready()) {
440 0 : mready->set(bit);
441 : }
442 : }
443 0 : }
444 :
445 : //
446 : // Return the number of peers this route has been advertised to.
447 : //
448 0 : int RibOut::RouteAdvertiseCount(const BgpRoute *rt) const {
449 0 : const DBState *dbstate = rt->GetState(table_, listener_id_);
450 0 : if (dbstate == NULL) {
451 0 : return 0;
452 : }
453 :
454 0 : const RouteState *rstate = dynamic_cast<const RouteState *>(dbstate);
455 0 : if (rstate != NULL) {
456 0 : int count = 0;
457 0 : for (AdvertiseSList::List::const_iterator iter =
458 0 : rstate->Advertised()->begin();
459 0 : iter != rstate->Advertised()->end(); ++iter) {
460 0 : count += iter->bitset.count();
461 : }
462 0 : return count;
463 : }
464 :
465 0 : const RouteUpdate *rt_update = dynamic_cast<const RouteUpdate *>(dbstate);
466 0 : if (rt_update != NULL) {
467 0 : int count = 0;
468 0 : for (AdvertiseSList::List::const_iterator iter =
469 0 : rt_update->History()->begin();
470 0 : iter != rt_update->History()->end(); ++iter) {
471 0 : count += iter->bitset.count();
472 : }
473 0 : return count;
474 : }
475 :
476 0 : const UpdateList *uplist = dynamic_cast<const UpdateList *>(dbstate);
477 0 : if (uplist != NULL) {
478 0 : int count = 0;
479 0 : for (AdvertiseSList::List::const_iterator iter =
480 0 : uplist->History()->begin();
481 0 : iter != uplist->History()->end(); ++iter) {
482 0 : count += iter->bitset.count();
483 : }
484 0 : return count;
485 : }
486 :
487 0 : return 0;
488 : }
489 :
490 : //
491 : // Return the total queue size across all RibOutUpdates and UpdateQueues.
492 : //
493 0 : uint32_t RibOut::GetQueueSize() const {
494 0 : uint32_t queue_size = 0;
495 0 : for (int idx = 0; idx < DB::PartitionCount(); ++idx) {
496 0 : const RibOutUpdates *updates = updates_[idx];
497 0 : for (int qid = RibOutUpdates::QFIRST; qid < RibOutUpdates::QCOUNT;
498 : ++qid) {
499 0 : queue_size += updates->queue_size(qid);
500 : }
501 : }
502 0 : return queue_size;
503 : }
504 :
505 : //
506 : // Return the active RibPeerSet for this RibOut. We keep track of the active
507 : // peers via the calls to Register and Deactivate.
508 : //
509 : // The active RibPeerSet is always a subset of the registered RibPeerSet that
510 : // is in the PeerStateMap.
511 : //
512 768 : const RibPeerSet &RibOut::PeerSet() const {
513 768 : return active_peerset_;
514 : }
515 :
516 : //
517 : // Clear the bit index corresponding to the specified peer.
518 : // Used to implement split horizon within an EBGP Ribout.
519 : //
520 0 : void RibOut::GetSubsetPeerSet(RibPeerSet *peerset,
521 : const IPeerUpdate *cpeer) const {
522 0 : assert(policy_.type == BgpProto::EBGP);
523 0 : IPeerUpdate *peer = const_cast<IPeerUpdate *>(cpeer);
524 0 : int index = GetPeerIndex(peer);
525 0 : if (index < 0)
526 0 : return;
527 0 : peerset->reset(index);
528 : }
529 :
530 : //
531 : // Return the peer corresponding to the specified bit index.
532 : //
533 863 : IPeerUpdate *RibOut::GetPeer(int index) const {
534 863 : PeerState *ps = state_map_.At(index);
535 863 : if (ps != NULL) {
536 863 : return ps->peer;
537 : }
538 0 : return NULL;
539 : }
540 :
541 : //
542 : // Return the bit index corresponding to the specified peer.
543 : //
544 428 : int RibOut::GetPeerIndex(IPeerUpdate *peer) const {
545 428 : PeerState *ps = state_map_.Find(peer);
546 428 : return (ps ? ps->index : -1);
547 : }
548 :
549 : //
550 : // Fill introspect information.
551 : // Accumulate counters from all RibOutUpdates.
552 : //
553 0 : void RibOut::FillStatisticsInfo(vector<ShowRibOutStatistics> *sros_list) const {
554 0 : for (int qid = RibOutUpdates::QFIRST; qid < RibOutUpdates::QCOUNT; ++qid) {
555 : RibOutUpdates::Stats stats;
556 0 : memset(&stats, 0, sizeof(stats));
557 0 : size_t queue_size = 0;
558 0 : size_t queue_marker_count = 0;
559 0 : for (int idx = 0; idx < DB::PartitionCount(); ++idx) {
560 0 : const RibOutUpdates *updates = updates_[idx];
561 0 : updates->AddStatisticsInfo(qid, &stats);
562 0 : queue_size += updates->queue_size(qid);
563 0 : queue_marker_count += updates->queue_marker_count(qid);
564 : }
565 :
566 0 : ShowRibOutStatistics sros;
567 0 : sros.set_table(table_->name());
568 0 : sros.set_encoding(EncodingString());
569 0 : sros.set_peer_type(BgpProto::BgpPeerTypeString(peer_type()));
570 0 : sros.set_peer_as(peer_as());
571 0 : sros.set_peers(state_map_.size());
572 0 : sros.set_queue(qid == RibOutUpdates::QBULK ? "BULK" : "UPDATE");
573 0 : sros.set_pending_updates(queue_size);
574 0 : sros.set_markers(queue_marker_count);
575 0 : sros.set_messages_built(stats.messages_built_count_);
576 0 : sros.set_messages_sent(stats.messages_sent_count_);
577 0 : sros.set_reach(stats.reach_count_);
578 0 : sros.set_unreach(stats.unreach_count_);
579 0 : sros.set_tail_dequeues(stats.tail_dequeue_count_);
580 0 : sros.set_peer_dequeues(stats.peer_dequeue_count_);
581 0 : sros.set_marker_splits(stats.marker_split_count_);
582 0 : sros.set_marker_merges(stats.marker_merge_count_);
583 0 : sros.set_marker_moves(stats.marker_move_count_);
584 0 : sros_list->push_back(sros);
585 0 : }
586 0 : }
|