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