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 463078 : 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 463078 : bool vrf_originated)
33 463078 : : address_(address),
34 463088 : mac_(mac),
35 463086 : label_(label),
36 463086 : l3_label_(l3_label),
37 463086 : origin_vn_index_(-1) {
38 463082 : as_t asn = table ? table->server()->autonomous_system() : 0;
39 926111 : bool all = table ?
40 463048 : table->server()->global_config()->all_tags_are_global() : false;
41 463063 : if (large_community != nullptr) {
42 445475 : tag_list_ = large_community->GetTagList(all ? 0 : asn);
43 : }
44 462993 : if (ext_community != nullptr) {
45 449227 : encap_ = ext_community->GetTunnelEncap();
46 449142 : origin_vn_index_ = ext_community->GetOriginVnIndex();
47 : }
48 :
49 462893 : if (origin_vn_index_ < 0 && vrf_originated) {
50 51929 : origin_vn_index_ =
51 51928 : table ? table->routing_instance()->virtual_network_index() : 0;
52 : }
53 462894 : }
54 :
55 314706 : int RibOutAttr::NextHop::CompareTo(const NextHop &rhs) const {
56 314706 : KEY_COMPARE(address_, rhs.address_);
57 311267 : KEY_COMPARE(mac_, rhs.mac_) ;
58 311260 : KEY_COMPARE(label_, rhs.label_);
59 310685 : KEY_COMPARE(l3_label_, rhs.l3_label_);
60 310669 : KEY_COMPARE(source_address_, rhs.source_address_);
61 310653 : KEY_COMPARE(origin_vn_index_, rhs.origin_vn_index_);
62 310615 : KEY_COMPARE(encap_.size(), rhs.encap_.size());
63 610538 : for (size_t idx = 0; idx < encap_.size(); ++idx) {
64 299928 : KEY_COMPARE(encap_[idx], rhs.encap_[idx]);
65 : }
66 310574 : KEY_COMPARE(tag_list_.size(), rhs.tag_list_.size());
67 310606 : for (size_t idx = 0; idx < tag_list_.size(); ++idx) {
68 0 : KEY_COMPARE(tag_list_[idx], rhs.tag_list_[idx]);
69 : }
70 310605 : return 0;
71 : }
72 :
73 7285 : bool RibOutAttr::NextHop::operator==(const NextHop &rhs) const {
74 7285 : 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 307423 : bool RibOutAttr::NextHop::operator<(const NextHop &rhs) const {
82 307423 : return CompareTo(rhs) < 0;
83 : }
84 :
85 1093929 : RibOutAttr::RibOutAttr()
86 1093851 : : label_(0),
87 1093851 : l3_label_(0),
88 1093836 : is_xmpp_(false),
89 1093929 : vrf_originated_(false) {
90 1093830 : }
91 :
92 : //
93 : // Copy constructor.
94 : // Do not copy the string representation;
95 : //
96 596540 : RibOutAttr::RibOutAttr(const RibOutAttr &rhs) {
97 596610 : attr_out_ = rhs.attr_out_;
98 596718 : nexthop_list_ = rhs.nexthop_list_;
99 596597 : label_ = rhs.label_;
100 596597 : l3_label_ = rhs.l3_label_;
101 596597 : source_address_ = rhs.source_address_;
102 596600 : is_xmpp_ = rhs.is_xmpp_;
103 596600 : vrf_originated_ = rhs.vrf_originated_;
104 596600 : }
105 :
106 9750 : RibOutAttr::RibOutAttr(const BgpTable *table, const BgpAttr *attr,
107 9750 : uint32_t label, uint32_t l3_label, bool is_xmpp)
108 9750 : : attr_out_(attr),
109 9750 : label_(label),
110 9750 : l3_label_(l3_label),
111 9750 : is_xmpp_(is_xmpp),
112 9750 : vrf_originated_(false) {
113 9750 : if (attr && is_xmpp) {
114 768 : nexthop_list_.push_back(NextHop(table, attr->nexthop(),
115 768 : attr->mac_address(), label, l3_label, attr->ext_community(),
116 : attr->large_community(), false));
117 : }
118 9750 : }
119 :
120 8418 : RibOutAttr::RibOutAttr(const BgpTable *table, const BgpRoute *route,
121 8418 : const BgpAttr *attr, uint32_t label, bool include_nh, bool is_xmpp)
122 8418 : : attr_out_(attr),
123 8418 : label_(0),
124 8418 : l3_label_(0),
125 8418 : is_xmpp_(is_xmpp),
126 8418 : vrf_originated_(route->BestPath()->IsVrfOriginated()) {
127 8418 : if (attr && include_nh) {
128 3598 : if (is_xmpp) {
129 7196 : nexthop_list_.push_back(NextHop(table, attr->nexthop(),
130 7196 : attr->mac_address(), label, 0, attr->ext_community(),
131 3598 : attr->large_community(), vrf_originated_));
132 : } else {
133 0 : label_ = label;
134 0 : l3_label_ = 0;
135 : }
136 : }
137 8418 : }
138 :
139 700722 : RibOutAttr::RibOutAttr(const BgpRoute *route, const BgpAttr *attr,
140 700722 : bool is_xmpp) :
141 700722 : label_(0), l3_label_(0), is_xmpp_(is_xmpp), vrf_originated_(false) {
142 : // Attribute should not be set already
143 700715 : assert(!attr_out_);
144 :
145 700711 : 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 700708 : if (!is_xmpp) {
149 248067 : set_attr(table, attr, route->BestPath()->GetLabel(),
150 : route->BestPath()->GetL3Label(), false, is_xmpp);
151 248071 : return;
152 : }
153 :
154 : // Encode ECMP nexthops only for XMPP peers.
155 : // Vrf Origination matters only for XMPP peers.
156 452768 : set_attr(table, attr, route->BestPath()->GetLabel(),
157 452641 : route->BestPath()->GetL3Label(), route->BestPath()->IsVrfOriginated(),
158 : is_xmpp);
159 :
160 452934 : for (Route::PathList::const_iterator it = route->GetPathList().begin();
161 1823883 : it != route->GetPathList().end(); ++it) {
162 462366 : const BgpPath *path = static_cast<const BgpPath *>(it.operator->());
163 :
164 : // Skip the best path.
165 462366 : if (path == route->BestPath())
166 456491 : 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 9615 : if (route->BestPath()->PathCompare(*path, true))
171 3155 : 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 6460 : NextHop nexthop(table, path->GetAttr()->nexthop(),
178 6460 : path->GetAttr()->mac_address(), path->GetLabel(),
179 : path->GetL3Label(), path->GetAttr()->ext_community(),
180 12920 : path->GetAttr()->large_community(), path->IsVrfOriginated());
181 :
182 : // Skip if we have already encoded this next-hop
183 6460 : if (find(nexthop_list_.begin(), nexthop_list_.end(), nexthop) !=
184 12920 : nexthop_list_.end()) {
185 3738 : continue;
186 : }
187 2722 : nexthop_list_.push_back(nexthop);
188 6460 : }
189 0 : }
190 :
191 : //
192 : // Assignment operator.
193 : // Do not copy the string representation;
194 : //
195 709174 : RibOutAttr &RibOutAttr::operator=(const RibOutAttr &rhs) {
196 709174 : attr_out_ = rhs.attr_out_;
197 709393 : nexthop_list_ = rhs.nexthop_list_;
198 709151 : label_ = rhs.label_;
199 709151 : l3_label_ = rhs.l3_label_;
200 709151 : source_address_ = rhs.source_address_;
201 709170 : is_xmpp_ = rhs.is_xmpp_;
202 709170 : vrf_originated_ = rhs.vrf_originated_;
203 709170 : return *this;
204 : }
205 :
206 : //
207 : // Comparator for RibOutAttr.
208 : // First compare the BgpAttr and then the nexthops.
209 : //
210 1094011 : int RibOutAttr::CompareTo(const RibOutAttr &rhs) const {
211 1094011 : KEY_COMPARE(attr_out_.get(), rhs.attr_out_.get());
212 275401 : KEY_COMPARE(nexthop_list_.size(), rhs.nexthop_list_.size());
213 427201 : for (size_t idx = 0; idx < nexthop_list_.size(); ++idx) {
214 153861 : KEY_COMPARE(nexthop_list_[idx], rhs.nexthop_list_[idx]);
215 : }
216 273323 : KEY_COMPARE(label_, rhs.label());
217 123917 : KEY_COMPARE(l3_label_, rhs.l3_label());
218 123909 : KEY_COMPARE(source_address_, rhs.source_address());
219 123911 : KEY_COMPARE(is_xmpp_, rhs.is_xmpp());
220 123912 : KEY_COMPARE(vrf_originated_, rhs.vrf_originated());
221 123912 : return 0;
222 : }
223 :
224 709667 : 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 709667 : if (!attr_out_) {
227 705525 : attr_out_ = attrp;
228 705478 : assert(nexthop_list_.empty());
229 705390 : if (is_xmpp) {
230 452743 : NextHop nexthop(table, attrp->nexthop(), attrp->mac_address(),
231 : label, l3_label, attrp->ext_community(),
232 905406 : attrp->large_community(), vrf_originated);
233 452458 : nexthop_list_.push_back(nexthop);
234 452612 : } else {
235 252620 : label_ = label;
236 252620 : l3_label_ = l3_label;
237 : }
238 705262 : return;
239 : }
240 :
241 4130 : if (!attrp) {
242 0 : clear();
243 0 : return;
244 : }
245 :
246 4131 : assert(attr_out_->nexthop() == attrp->nexthop());
247 4131 : attr_out_ = attrp;
248 : }
249 :
250 599418 : RouteState::RouteState() {
251 599416 : }
252 :
253 : //
254 : // Move history from RouteState to RouteUpdate.
255 : //
256 484426 : void RouteState::MoveHistory(RouteUpdate *rt_update) {
257 484426 : AdvertiseSList adv_slist;
258 484401 : SwapHistory(adv_slist);
259 484366 : rt_update->SwapHistory(adv_slist);
260 484340 : }
261 :
262 : //
263 : // Find the AdvertiseInfo element with matching RibOutAttr.
264 : //
265 172944 : const AdvertiseInfo *RouteState::FindHistory(
266 : const RibOutAttr &roattr) const {
267 228984 : for (AdvertiseSList::List::const_iterator iter = advertised_->begin();
268 457957 : iter != advertised_->end(); iter++) {
269 292269 : if (iter->roattr == roattr) return iter.operator->();
270 : }
271 54805 : 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 542520 : bool RouteState::CompareUpdateInfo(const UpdateInfoSList &uinfo_slist) const {
285 : // Both lists must have the same number of elements.
286 542520 : if (uinfo_slist->size() != advertised_->size())
287 376481 : 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 165992 : for (UpdateInfoSList::List::const_iterator iter = uinfo_slist->begin();
293 553987 : iter != uinfo_slist->end(); ++iter) {
294 165984 : const AdvertiseInfo *ainfo = FindHistory(iter->roattr);
295 277093 : if (!ainfo || iter->target != ainfo->bitset)
296 54917 : return false;
297 : }
298 :
299 110978 : return true;
300 : }
301 :
302 : //
303 : // Create a new RibOut based on the BgpTable and RibExportPolicy.
304 : //
305 34966 : RibOut::RibOut(BgpTable *table, BgpUpdateSender *sender,
306 34966 : const RibExportPolicy &policy)
307 34966 : : table_(table),
308 34966 : sender_(sender),
309 34966 : policy_(policy),
310 34966 : listener_id_(DBTableBase::kInvalidId),
311 34966 : bgp_export_(BgpStaticObjectFactory::Create<BgpExport>(this)) {
312 34966 : name_ = "RibOut";
313 34966 : if (policy_.type == BgpProto::XMPP) {
314 24128 : name_ += " Type: XMPP";
315 10838 : } else if (policy_.type == BgpProto::IBGP) {
316 5841 : name_ += " Type: IBGP";
317 : } else {
318 4997 : name_ += " Type: EBGP";
319 4997 : name_ += " (AS " + integerToString(policy_.as_number);
320 4997 : if (!policy_.nexthop.is_unspecified())
321 211 : name_ += " Nexthop " + policy_.nexthop.to_string();
322 4997 : if (policy_.as_override)
323 64 : name_ += " ASOverride";
324 4997 : name_ += ")";
325 : }
326 170966 : for (int idx = 0; idx < DB::PartitionCount(); ++idx) {
327 136000 : updates_.push_back(BgpStaticObjectFactory::Create<RibOutUpdates>(this, idx));
328 : }
329 34966 : }
330 :
331 : //
332 : // Destructor for RibOut. Takes care of unregistering the RibOut from
333 : // the DBTableBase.
334 : //
335 34966 : RibOut::~RibOut() {
336 34966 : if (listener_id_ != DBTableBase::kInvalidId) {
337 33690 : table_->Unregister(listener_id_);
338 33690 : listener_id_ = DBTableBase::kInvalidId;
339 : }
340 34966 : STLDeleteValues(&updates_);
341 34966 : }
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 81680 : void RibOut::RegisterListener() {
352 81680 : if (listener_id_ != DBTableBase::kInvalidId)
353 47990 : return;
354 33690 : 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 93136 : void RibOut::Register(IPeerUpdate *peer) {
366 93136 : PeerState *ps = state_map_.Locate(peer);
367 93136 : assert(ps != NULL);
368 93136 : active_peerset_.set(ps->index);
369 93136 : sender_->Join(this, peer);
370 460961 : for (int idx = 0; idx < DB::PartitionCount(); ++idx) {
371 367825 : if (updates_[idx]->QueueJoin(RibOutUpdates::QUPDATE, ps->index))
372 40 : sender_->RibOutActive(idx, this, RibOutUpdates::QUPDATE);
373 367825 : if (updates_[idx]->QueueJoin(RibOutUpdates::QBULK, ps->index))
374 61 : sender_->RibOutActive(idx, this, RibOutUpdates::QBULK);
375 : }
376 93136 : }
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 93136 : void RibOut::Unregister(IPeerUpdate *peer) {
387 93136 : PeerState *ps = state_map_.Find(peer);
388 93136 : assert(ps != NULL);
389 93136 : assert(!active_peerset_.test(ps->index));
390 :
391 460961 : for (int idx = 0; idx < DB::PartitionCount(); ++idx) {
392 367825 : updates_[idx]->QueueLeave(RibOutUpdates::QUPDATE, ps->index);
393 367825 : updates_[idx]->QueueLeave(RibOutUpdates::QBULK, ps->index);
394 : }
395 93136 : sender_->Leave(this, peer);
396 93136 : state_map_.Remove(peer, ps->index);
397 :
398 93136 : if (state_map_.empty()) {
399 34893 : table_->RibOutDelete(policy_);
400 : }
401 93136 : }
402 :
403 : //
404 : // Return true if the IPeerUpdate is registered to this RibOut.
405 : //
406 7169 : bool RibOut::IsRegistered(IPeerUpdate *peer) {
407 7169 : PeerState *ps = state_map_.Find(peer);
408 7169 : 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 93136 : void RibOut::Deactivate(IPeerUpdate *peer) {
420 93136 : PeerState *ps = state_map_.Find(peer);
421 93136 : assert(ps != NULL);
422 93136 : assert(active_peerset_.test(ps->index));
423 93136 : active_peerset_.reset(ps->index);
424 93136 : }
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 103 : void RibOut::BuildSendReadyBitSet(const RibPeerSet &peerset,
435 : RibPeerSet *mready) const {
436 320 : for (size_t bit = peerset.find_first(); bit != RibPeerSet::npos;
437 217 : bit = peerset.find_next(bit)) {
438 217 : IPeerUpdate *peer = GetPeer(bit);
439 217 : if (peer->send_ready()) {
440 209 : mready->set(bit);
441 : }
442 : }
443 103 : }
444 :
445 : //
446 : // Return the number of peers this route has been advertised to.
447 : //
448 1332 : int RibOut::RouteAdvertiseCount(const BgpRoute *rt) const {
449 1332 : const DBState *dbstate = rt->GetState(table_, listener_id_);
450 1332 : if (dbstate == NULL) {
451 289 : return 0;
452 : }
453 :
454 1043 : const RouteState *rstate = dynamic_cast<const RouteState *>(dbstate);
455 1043 : if (rstate != NULL) {
456 225 : int count = 0;
457 225 : for (AdvertiseSList::List::const_iterator iter =
458 225 : rstate->Advertised()->begin();
459 1484 : iter != rstate->Advertised()->end(); ++iter) {
460 517 : count += iter->bitset.count();
461 : }
462 225 : return count;
463 : }
464 :
465 818 : const RouteUpdate *rt_update = dynamic_cast<const RouteUpdate *>(dbstate);
466 818 : if (rt_update != NULL) {
467 608 : int count = 0;
468 608 : for (AdvertiseSList::List::const_iterator iter =
469 608 : rt_update->History()->begin();
470 2454 : iter != rt_update->History()->end(); ++iter) {
471 619 : count += iter->bitset.count();
472 : }
473 608 : return count;
474 : }
475 :
476 210 : const UpdateList *uplist = dynamic_cast<const UpdateList *>(dbstate);
477 210 : if (uplist != NULL) {
478 210 : int count = 0;
479 210 : for (AdvertiseSList::List::const_iterator iter =
480 210 : uplist->History()->begin();
481 910 : iter != uplist->History()->end(); ++iter) {
482 245 : count += iter->bitset.count();
483 : }
484 210 : 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 1750469 : const RibPeerSet &RibOut::PeerSet() const {
513 1750469 : 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 40 : void RibOut::GetSubsetPeerSet(RibPeerSet *peerset,
521 : const IPeerUpdate *cpeer) const {
522 40 : assert(policy_.type == BgpProto::EBGP);
523 40 : IPeerUpdate *peer = const_cast<IPeerUpdate *>(cpeer);
524 40 : int index = GetPeerIndex(peer);
525 40 : if (index < 0)
526 20 : return;
527 20 : peerset->reset(index);
528 : }
529 :
530 : //
531 : // Return the peer corresponding to the specified bit index.
532 : //
533 2273785 : IPeerUpdate *RibOut::GetPeer(int index) const {
534 2273785 : PeerState *ps = state_map_.At(index);
535 2273773 : if (ps != NULL) {
536 2273767 : return ps->peer;
537 : }
538 6 : return NULL;
539 : }
540 :
541 : //
542 : // Return the bit index corresponding to the specified peer.
543 : //
544 2077237 : int RibOut::GetPeerIndex(IPeerUpdate *peer) const {
545 2077237 : PeerState *ps = state_map_.Find(peer);
546 2076808 : return (ps ? ps->index : -1);
547 : }
548 :
549 : //
550 : // Fill introspect information.
551 : // Accumulate counters from all RibOutUpdates.
552 : //
553 2 : void RibOut::FillStatisticsInfo(vector<ShowRibOutStatistics> *sros_list) const {
554 6 : for (int qid = RibOutUpdates::QFIRST; qid < RibOutUpdates::QCOUNT; ++qid) {
555 : RibOutUpdates::Stats stats;
556 4 : memset(&stats, 0, sizeof(stats));
557 4 : size_t queue_size = 0;
558 4 : size_t queue_marker_count = 0;
559 20 : for (int idx = 0; idx < DB::PartitionCount(); ++idx) {
560 16 : const RibOutUpdates *updates = updates_[idx];
561 16 : updates->AddStatisticsInfo(qid, &stats);
562 16 : queue_size += updates->queue_size(qid);
563 16 : queue_marker_count += updates->queue_marker_count(qid);
564 : }
565 :
566 4 : ShowRibOutStatistics sros;
567 4 : sros.set_table(table_->name());
568 4 : sros.set_encoding(EncodingString());
569 4 : sros.set_peer_type(BgpProto::BgpPeerTypeString(peer_type()));
570 4 : sros.set_peer_as(peer_as());
571 4 : sros.set_peers(state_map_.size());
572 4 : sros.set_queue(qid == RibOutUpdates::QBULK ? "BULK" : "UPDATE");
573 4 : sros.set_pending_updates(queue_size);
574 4 : sros.set_markers(queue_marker_count);
575 4 : sros.set_messages_built(stats.messages_built_count_);
576 4 : sros.set_messages_sent(stats.messages_sent_count_);
577 4 : sros.set_reach(stats.reach_count_);
578 4 : sros.set_unreach(stats.unreach_count_);
579 4 : sros.set_tail_dequeues(stats.tail_dequeue_count_);
580 4 : sros.set_peer_dequeues(stats.peer_dequeue_count_);
581 4 : sros.set_marker_splits(stats.marker_split_count_);
582 4 : sros.set_marker_merges(stats.marker_merge_count_);
583 4 : sros.set_marker_moves(stats.marker_move_count_);
584 4 : sros_list->push_back(sros);
585 4 : }
586 2 : }
|