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 463145 : 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 463145 : bool vrf_originated)
33 463145 : : address_(address),
34 463208 : mac_(mac),
35 463207 : label_(label),
36 463207 : l3_label_(l3_label),
37 463207 : origin_vn_index_(-1) {
38 463199 : as_t asn = table ? table->server()->autonomous_system() : 0;
39 926343 : bool all = table ?
40 463165 : table->server()->global_config()->all_tags_are_global() : false;
41 463178 : if (large_community != nullptr) {
42 445693 : tag_list_ = large_community->GetTagList(all ? 0 : asn);
43 : }
44 463116 : if (ext_community != nullptr) {
45 449424 : encap_ = ext_community->GetTunnelEncap();
46 449359 : origin_vn_index_ = ext_community->GetOriginVnIndex();
47 : }
48 :
49 463066 : if (origin_vn_index_ < 0 && vrf_originated) {
50 51962 : origin_vn_index_ =
51 51962 : table ? table->routing_instance()->virtual_network_index() : 0;
52 : }
53 463066 : }
54 :
55 313512 : int RibOutAttr::NextHop::CompareTo(const NextHop &rhs) const {
56 313512 : KEY_COMPARE(address_, rhs.address_);
57 310061 : KEY_COMPARE(mac_, rhs.mac_) ;
58 310052 : KEY_COMPARE(label_, rhs.label_);
59 309478 : KEY_COMPARE(l3_label_, rhs.l3_label_);
60 309462 : KEY_COMPARE(source_address_, rhs.source_address_);
61 309447 : KEY_COMPARE(origin_vn_index_, rhs.origin_vn_index_);
62 309404 : KEY_COMPARE(encap_.size(), rhs.encap_.size());
63 608017 : for (size_t idx = 0; idx < encap_.size(); ++idx) {
64 298629 : KEY_COMPARE(encap_[idx], rhs.encap_[idx]);
65 : }
66 309361 : KEY_COMPARE(tag_list_.size(), rhs.tag_list_.size());
67 309376 : for (size_t idx = 0; idx < tag_list_.size(); ++idx) {
68 0 : KEY_COMPARE(tag_list_[idx], rhs.tag_list_[idx]);
69 : }
70 309375 : return 0;
71 : }
72 :
73 7330 : bool RibOutAttr::NextHop::operator==(const NextHop &rhs) const {
74 7330 : 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 306190 : bool RibOutAttr::NextHop::operator<(const NextHop &rhs) const {
82 306190 : return CompareTo(rhs) < 0;
83 : }
84 :
85 1092957 : RibOutAttr::RibOutAttr()
86 1092866 : : label_(0),
87 1092866 : l3_label_(0),
88 1092876 : is_xmpp_(false),
89 1092957 : vrf_originated_(false) {
90 1092875 : }
91 :
92 : //
93 : // Copy constructor.
94 : // Do not copy the string representation;
95 : //
96 595887 : RibOutAttr::RibOutAttr(const RibOutAttr &rhs) {
97 595862 : attr_out_ = rhs.attr_out_;
98 595930 : nexthop_list_ = rhs.nexthop_list_;
99 595821 : label_ = rhs.label_;
100 595821 : l3_label_ = rhs.l3_label_;
101 595821 : source_address_ = rhs.source_address_;
102 595827 : is_xmpp_ = rhs.is_xmpp_;
103 595827 : vrf_originated_ = rhs.vrf_originated_;
104 595827 : }
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 8451 : RibOutAttr::RibOutAttr(const BgpTable *table, const BgpRoute *route,
121 8451 : const BgpAttr *attr, uint32_t label, bool include_nh, bool is_xmpp)
122 8451 : : attr_out_(attr),
123 8451 : label_(0),
124 8451 : l3_label_(0),
125 8451 : is_xmpp_(is_xmpp),
126 8451 : vrf_originated_(route->BestPath()->IsVrfOriginated()) {
127 8451 : if (attr && include_nh) {
128 3567 : if (is_xmpp) {
129 7134 : nexthop_list_.push_back(NextHop(table, attr->nexthop(),
130 7134 : attr->mac_address(), label, 0, attr->ext_community(),
131 3567 : attr->large_community(), vrf_originated_));
132 : } else {
133 0 : label_ = label;
134 0 : l3_label_ = 0;
135 : }
136 : }
137 8451 : }
138 :
139 700144 : RibOutAttr::RibOutAttr(const BgpRoute *route, const BgpAttr *attr,
140 700144 : bool is_xmpp) :
141 700144 : label_(0), l3_label_(0), is_xmpp_(is_xmpp), vrf_originated_(false) {
142 : // Attribute should not be set already
143 700128 : assert(!attr_out_);
144 :
145 700124 : 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 700118 : if (!is_xmpp) {
149 247399 : set_attr(table, attr, route->BestPath()->GetLabel(),
150 : route->BestPath()->GetL3Label(), false, is_xmpp);
151 247406 : return;
152 : }
153 :
154 : // Encode ECMP nexthops only for XMPP peers.
155 : // Vrf Origination matters only for XMPP peers.
156 452863 : set_attr(table, attr, route->BestPath()->GetLabel(),
157 452719 : route->BestPath()->GetL3Label(), route->BestPath()->IsVrfOriginated(),
158 : is_xmpp);
159 :
160 453039 : for (Route::PathList::const_iterator it = route->GetPathList().begin();
161 1824584 : it != route->GetPathList().end(); ++it) {
162 462523 : const BgpPath *path = static_cast<const BgpPath *>(it.operator->());
163 :
164 : // Skip the best path.
165 462523 : if (path == route->BestPath())
166 456676 : 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 9626 : if (route->BestPath()->PathCompare(*path, true))
171 3126 : 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 6498 : NextHop nexthop(table, path->GetAttr()->nexthop(),
178 6498 : path->GetAttr()->mac_address(), path->GetLabel(),
179 : path->GetL3Label(), path->GetAttr()->ext_community(),
180 12996 : path->GetAttr()->large_community(), path->IsVrfOriginated());
181 :
182 : // Skip if we have already encoded this next-hop
183 6497 : if (find(nexthop_list_.begin(), nexthop_list_.end(), nexthop) !=
184 12995 : nexthop_list_.end()) {
185 3761 : continue;
186 : }
187 2737 : nexthop_list_.push_back(nexthop);
188 6498 : }
189 0 : }
190 :
191 : //
192 : // Assignment operator.
193 : // Do not copy the string representation;
194 : //
195 708750 : RibOutAttr &RibOutAttr::operator=(const RibOutAttr &rhs) {
196 708750 : attr_out_ = rhs.attr_out_;
197 708868 : nexthop_list_ = rhs.nexthop_list_;
198 708668 : label_ = rhs.label_;
199 708668 : l3_label_ = rhs.l3_label_;
200 708668 : source_address_ = rhs.source_address_;
201 708683 : is_xmpp_ = rhs.is_xmpp_;
202 708683 : vrf_originated_ = rhs.vrf_originated_;
203 708683 : return *this;
204 : }
205 :
206 : //
207 : // Comparator for RibOutAttr.
208 : // First compare the BgpAttr and then the nexthops.
209 : //
210 1093632 : int RibOutAttr::CompareTo(const RibOutAttr &rhs) const {
211 1093632 : KEY_COMPARE(attr_out_.get(), rhs.attr_out_.get());
212 274854 : KEY_COMPARE(nexthop_list_.size(), rhs.nexthop_list_.size());
213 425859 : for (size_t idx = 0; idx < nexthop_list_.size(); ++idx) {
214 153247 : KEY_COMPARE(nexthop_list_[idx], rhs.nexthop_list_[idx]);
215 : }
216 272600 : KEY_COMPARE(label_, rhs.label());
217 123832 : KEY_COMPARE(l3_label_, rhs.l3_label());
218 123823 : KEY_COMPARE(source_address_, rhs.source_address());
219 123827 : KEY_COMPARE(is_xmpp_, rhs.is_xmpp());
220 123827 : KEY_COMPARE(vrf_originated_, rhs.vrf_originated());
221 123825 : return 0;
222 : }
223 :
224 709116 : 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 709116 : if (!attr_out_) {
227 704971 : attr_out_ = attrp;
228 704918 : assert(nexthop_list_.empty());
229 704849 : if (is_xmpp) {
230 452843 : NextHop nexthop(table, attrp->nexthop(), attrp->mac_address(),
231 : label, l3_label, attrp->ext_community(),
232 905651 : attrp->large_community(), vrf_originated);
233 452604 : nexthop_list_.push_back(nexthop);
234 452798 : } else {
235 251955 : label_ = label;
236 251955 : l3_label_ = l3_label;
237 : }
238 704767 : return;
239 : }
240 :
241 4133 : if (!attrp) {
242 0 : clear();
243 0 : return;
244 : }
245 :
246 4133 : assert(attr_out_->nexthop() == attrp->nexthop());
247 4133 : attr_out_ = attrp;
248 : }
249 :
250 598985 : RouteState::RouteState() {
251 598992 : }
252 :
253 : //
254 : // Move history from RouteState to RouteUpdate.
255 : //
256 483652 : void RouteState::MoveHistory(RouteUpdate *rt_update) {
257 483652 : AdvertiseSList adv_slist;
258 483653 : SwapHistory(adv_slist);
259 483608 : rt_update->SwapHistory(adv_slist);
260 483590 : }
261 :
262 : //
263 : // Find the AdvertiseInfo element with matching RibOutAttr.
264 : //
265 172932 : const AdvertiseInfo *RouteState::FindHistory(
266 : const RibOutAttr &roattr) const {
267 229060 : for (AdvertiseSList::List::const_iterator iter = advertised_->begin();
268 458120 : iter != advertised_->end(); iter++) {
269 292168 : if (iter->roattr == roattr) return iter.operator->();
270 : }
271 54900 : 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 541639 : bool RouteState::CompareUpdateInfo(const UpdateInfoSList &uinfo_slist) const {
285 : // Both lists must have the same number of elements.
286 541639 : if (uinfo_slist->size() != advertised_->size())
287 375660 : 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 165968 : for (UpdateInfoSList::List::const_iterator iter = uinfo_slist->begin();
293 553785 : iter != uinfo_slist->end(); ++iter) {
294 165963 : const AdvertiseInfo *ainfo = FindHistory(iter->roattr);
295 276991 : if (!ainfo || iter->target != ainfo->bitset)
296 54998 : return false;
297 : }
298 :
299 110913 : return true;
300 : }
301 :
302 : //
303 : // Create a new RibOut based on the BgpTable and RibExportPolicy.
304 : //
305 34861 : RibOut::RibOut(BgpTable *table, BgpUpdateSender *sender,
306 34861 : const RibExportPolicy &policy)
307 34861 : : table_(table),
308 34861 : sender_(sender),
309 34861 : policy_(policy),
310 34861 : listener_id_(DBTableBase::kInvalidId),
311 34861 : bgp_export_(BgpStaticObjectFactory::Create<BgpExport>(this)) {
312 34861 : name_ = "RibOut";
313 34861 : if (policy_.type == BgpProto::XMPP) {
314 24122 : name_ += " Type: XMPP";
315 10739 : } else if (policy_.type == BgpProto::IBGP) {
316 5849 : name_ += " Type: IBGP";
317 : } else {
318 4890 : name_ += " Type: EBGP";
319 4890 : name_ += " (AS " + integerToString(policy_.as_number);
320 4890 : if (!policy_.nexthop.is_unspecified())
321 211 : name_ += " Nexthop " + policy_.nexthop.to_string();
322 4890 : if (policy_.as_override)
323 64 : name_ += " ASOverride";
324 4890 : name_ += ")";
325 : }
326 170441 : for (int idx = 0; idx < DB::PartitionCount(); ++idx) {
327 135580 : updates_.push_back(BgpStaticObjectFactory::Create<RibOutUpdates>(this, idx));
328 : }
329 34861 : }
330 :
331 : //
332 : // Destructor for RibOut. Takes care of unregistering the RibOut from
333 : // the DBTableBase.
334 : //
335 34861 : RibOut::~RibOut() {
336 34861 : if (listener_id_ != DBTableBase::kInvalidId) {
337 33585 : table_->Unregister(listener_id_);
338 33585 : listener_id_ = DBTableBase::kInvalidId;
339 : }
340 34861 : STLDeleteValues(&updates_);
341 34861 : }
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 81575 : void RibOut::RegisterListener() {
352 81575 : if (listener_id_ != DBTableBase::kInvalidId)
353 47990 : return;
354 33585 : 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 93031 : void RibOut::Register(IPeerUpdate *peer) {
366 93031 : PeerState *ps = state_map_.Locate(peer);
367 93031 : assert(ps != NULL);
368 93031 : active_peerset_.set(ps->index);
369 93031 : sender_->Join(this, peer);
370 460436 : for (int idx = 0; idx < DB::PartitionCount(); ++idx) {
371 367405 : if (updates_[idx]->QueueJoin(RibOutUpdates::QUPDATE, ps->index))
372 49 : sender_->RibOutActive(idx, this, RibOutUpdates::QUPDATE);
373 367405 : if (updates_[idx]->QueueJoin(RibOutUpdates::QBULK, ps->index))
374 57 : sender_->RibOutActive(idx, this, RibOutUpdates::QBULK);
375 : }
376 93031 : }
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 93031 : void RibOut::Unregister(IPeerUpdate *peer) {
387 93031 : PeerState *ps = state_map_.Find(peer);
388 93031 : assert(ps != NULL);
389 93031 : assert(!active_peerset_.test(ps->index));
390 :
391 460436 : for (int idx = 0; idx < DB::PartitionCount(); ++idx) {
392 367405 : updates_[idx]->QueueLeave(RibOutUpdates::QUPDATE, ps->index);
393 367405 : updates_[idx]->QueueLeave(RibOutUpdates::QBULK, ps->index);
394 : }
395 93031 : sender_->Leave(this, peer);
396 93031 : state_map_.Remove(peer, ps->index);
397 :
398 93031 : if (state_map_.empty()) {
399 34788 : table_->RibOutDelete(policy_);
400 : }
401 93031 : }
402 :
403 : //
404 : // Return true if the IPeerUpdate is registered to this RibOut.
405 : //
406 7202 : bool RibOut::IsRegistered(IPeerUpdate *peer) {
407 7202 : PeerState *ps = state_map_.Find(peer);
408 7202 : 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 93031 : void RibOut::Deactivate(IPeerUpdate *peer) {
420 93031 : PeerState *ps = state_map_.Find(peer);
421 93031 : assert(ps != NULL);
422 93031 : assert(active_peerset_.test(ps->index));
423 93031 : active_peerset_.reset(ps->index);
424 93031 : }
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 1755579 : const RibPeerSet &RibOut::PeerSet() const {
513 1755579 : 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 2251666 : IPeerUpdate *RibOut::GetPeer(int index) const {
534 2251666 : PeerState *ps = state_map_.At(index);
535 2251665 : if (ps != NULL) {
536 2251668 : return ps->peer;
537 : }
538 0 : return NULL;
539 : }
540 :
541 : //
542 : // Return the bit index corresponding to the specified peer.
543 : //
544 2055920 : int RibOut::GetPeerIndex(IPeerUpdate *peer) const {
545 2055920 : PeerState *ps = state_map_.Find(peer);
546 2055546 : 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 : }
|