LCOV - code coverage report
Current view: top level - bgp - bgp_export.cc (source / functions) Hit Total Coverage
Test: OpenSDN C/C++ coverage (all TARGET_SET jobs) Lines: 93 93 100.0 %
Date: 2026-11-09 02:10:49 Functions: 5 5 100.0 %
Legend: Lines: hit not hit

          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_export.h"
       7             : 
       8             : #include <boost/bind/bind.hpp>
       9             : 
      10             : #include "bgp/bgp_ribout_updates.h"
      11             : #include "bgp/bgp_route.h"
      12             : #include "bgp/bgp_table.h"
      13             : #include "bgp/bgp_update.h"
      14             : #include "bgp/bgp_update_monitor.h"
      15             : #include "bgp/bgp_update_sender.h"
      16             : 
      17             : using namespace boost::placeholders;
      18             : 
      19       34970 : BgpExport::BgpExport(RibOut *ribout)
      20       34970 :     : ribout_(ribout) {
      21       34970 : }
      22             : 
      23             : //
      24             : // Check if the desired state as expressed by the UpdateInfoSList is exactly
      25             : // the same as the state already stored in the RouteUpdate.
      26             : //
      27             : // Return true if we have a duplicate, false otherwise.
      28             : //
      29        1760 : static bool IsDuplicate(const RouteUpdate *rt_update,
      30             :         const UpdateInfoSList *uinfo_slist) {
      31        1760 :     return rt_update->CompareUpdateInfo(*uinfo_slist);
      32             : }
      33             : 
      34             : //
      35             : // Export Processing.
      36             : // 1. Calculate the desired attributes (UpdateInfo list) via BgpTable::Export.
      37             : // 2. Dequeue the existing update if present.
      38             : //    a) If the current update is the same as desired update then the operation
      39             : //    is a NOP.
      40             : //    b) If there is a current update, then the code must obtain a lock on it
      41             : //    in order to make sure that the output process is not currently reading
      42             : //    and modifying its state.
      43             : // 3. Calculate the delta between previous state and new state.
      44             : // 4. Enqueue a new update at tail.
      45             : //
      46     1472758 : void BgpExport::Export(DBTablePartBase *root, DBEntryBase *db_entry) {
      47             :     RouteUpdate *rt_update;
      48     1472758 :     UpdateInfoSList uinfo_slist;
      49             : 
      50             :     // Calculate attributes by running through export policy.
      51     1472799 :     BgpRoute *route = static_cast<BgpRoute *>(db_entry);
      52     1472799 :     bool reach = false;
      53     1472799 :     if (!db_entry->IsDeleted() && !ribout_->PeerSet().empty()) {
      54      817255 :         reach = ribout_->table()->Export(ribout_, route, ribout_->PeerSet(),
      55             :                 uinfo_slist);
      56             :     }
      57     1473001 :     assert(!reach || !uinfo_slist->empty());
      58             : 
      59             :     // Find and dequeue any existing DBState.
      60     1473009 :     bool duplicate = false;
      61     1473009 :     RibOutUpdates *updates = ribout_->updates(root->index());
      62     1472946 :     RibUpdateMonitor *monitor = updates->monitor();
      63     1472939 :     DBState *dbstate = monitor->GetDBStateAndDequeue(db_entry,
      64             :             boost::bind(IsDuplicate, _1, &uinfo_slist),
      65             :             &duplicate);
      66             : 
      67             :     // Handle the DBState as appropriate.
      68     1473762 :     if (dbstate == NULL) {
      69             :         // A NULL DBState implies that we either have no previous state or
      70             :         // that the previously scheduled updates are duplicates of what we
      71             :         // want to send now.
      72             : 
      73             :         // Nothing to do if we are looking at duplicates.
      74      926352 :         if (duplicate)
      75         290 :             return;
      76             : 
      77             :         // If we have no previous state and the route is not reachable we
      78             :         // are done.
      79      926062 :         if (!reach)
      80      469717 :             return;
      81             : 
      82             :         // We have no previous state and the route is reachable.  Need to
      83             :         // schedule a new update.
      84      456345 :         rt_update = new RouteUpdate(route, RibOutUpdates::QUPDATE);
      85             :     } else {
      86             :         // The DBState in the DBEntryBase must be the same as what we found.
      87             :         // This is a paranoid check to make sure that GetDBStateAndDequeue
      88             :         // did not forget to update the DBState when handing a RouteUpdate
      89             :         // from the QBULK queue or when handling an UpdateList.
      90             :         const DBState *entry_db_state =
      91      547410 :             db_entry->GetState(root->parent(), ribout_->listener_id());
      92      547518 :         assert(entry_db_state == dbstate);
      93             : 
      94             :         // We got here because we have previous state, either a RouteState
      95             :         // or a RouteUpdate. Note that it cannot be an UpdateList since we
      96             :         // get rid of UpdateLists in GetDBStateAndDequeue and return either
      97             :         // the RouteUpdate for QUPDATE or a new RouteState with the history.
      98      547518 :         assert(dynamic_cast<UpdateList *>(dbstate) == NULL);
      99             : 
     100      547518 :         rt_update = dynamic_cast<RouteUpdate *>(dbstate);
     101      547518 :         if (rt_update == NULL)  {
     102             :             // Previous state is not a RouteUpdate, must be a RouteState.
     103      545911 :             RouteState *rstate = static_cast<RouteState *>(dbstate);
     104             : 
     105             :             // Bail if the new state that we want to schedule is identical
     106             :             // to what we have previously advertised. This can happen when
     107             :             // there are back to back changes to a route such that it goes
     108             :             // from state A to B and then back to A but the Export routine
     109             :             // never saw state B.
     110      545911 :             if (rstate->CompareUpdateInfo(uinfo_slist))
     111      112040 :                 return;
     112             : 
     113             :             // We need a new RouteUpdate to advertise the new state and/or
     114             :             // withdraw part or all of the previous state. Move history to
     115             :             // a new RouteUpdate and get rid of the RouteState.
     116      433710 :             rt_update = new RouteUpdate(route, RibOutUpdates::QUPDATE);
     117      433524 :             rstate->MoveHistory(rt_update);
     118      433456 :             delete rstate;
     119      433486 :             dbstate = NULL;
     120      433486 :             rstate = NULL;
     121             :         } else {
     122             :             // The RouteUpdate must be for QUPDATE. Any RouteUpdate that got
     123             :             // dequeued from QBULK is also converted to be for QUPDATE.
     124        1607 :             assert(rt_update->queue_id() == RibOutUpdates::QUPDATE);
     125             : 
     126             :             // The previous state is a RouteUpdate. Get rid of any scheduled
     127             :             // UpdateInfos since we have fresh new UpdateInfos to schedule.
     128             :             // Note that the history information is still in the RouteUpdate.
     129        1595 :             rt_update->ClearUpdateInfo();
     130             : 
     131             :             // If the history is empty and the route is not reachable, there
     132             :             // is nothing to withdraw. Get rid of the RouteUpdate and return.
     133        1595 :             if (rt_update->History()->empty() && !reach) {
     134          44 :                 db_entry->ClearState(root->parent(), ribout_->listener_id());
     135          44 :                 delete rt_update;
     136          44 :                 return;
     137             :             }
     138             :         }
     139             : 
     140             :         // The new UpdateInfos that we want to schedule may not cover all the
     141             :         // peers that we advertised state to previously. May need to generate
     142             :         // negative state for such peers based on the history in RouteUpdate.
     143      435037 :         rt_update->BuildNegativeUpdateInfo(uinfo_slist);
     144             : 
     145             :         // At this point we have a RouteUpdate with history information and
     146             :         // an UpdateInfoSList that contains the state that we are going to
     147             :         // schedule. There may be some commonality between them. We want to
     148             :         // trim redundant state in the UpdateInfoSList i.e. if some of it's
     149             :         // the same as what's already in the history.
     150      434917 :         rt_update->TrimRedundantUpdateInfo(uinfo_slist);
     151             : 
     152             :         // If there are no more UpdateInfos in the list, move the history to
     153             :         // a new RouteState and get rid of the RouteUpdate.  This can happen
     154             :         // if the route goes from state A to B, the Export routine sees B and
     155             :         // schedules updates but the state goes back to A before the updates
     156             :         // can be sent to anyone.
     157      434935 :         if (uinfo_slist->empty()) {
     158          36 :             RouteState *rstate = new RouteState();
     159          36 :             rt_update->MoveHistory(rstate);
     160          36 :             db_entry->SetState(root->parent(), ribout_->listener_id(), rstate);
     161          36 :             delete rt_update;
     162          36 :             return;
     163             :         }
     164             :     }
     165             : 
     166             :     // Associate the new UpdateInfos we want to send with the RouteUpdate
     167             :     // and enqueue the RouteUpdate.
     168      891119 :     assert(!uinfo_slist->empty());
     169      891098 :     rt_update->SetUpdateInfo(uinfo_slist);
     170      891064 :     updates->Enqueue(db_entry, rt_update);
     171     1473528 : }
     172             : 
     173             : //
     174             : // Join Processing.
     175             : // 1. Detect if we have've already sent or scheduled updates to the bitset of
     176             : //    peers for the QUPDATE queue.
     177             : // 2. Calculate the desired attributes (UpdateInfo list) via BgpTable::Export.
     178             : // 3. Create a new RouteUpdate for the QBULK queue and merge it with existing
     179             : //    state.
     180             : //
     181      453700 : bool BgpExport::Join(DBTablePartBase *root, const RibPeerSet &mjoin,
     182             :         DBEntryBase *db_entry) {
     183      453700 :     RibOutUpdates *updates = ribout_->updates(root->index());
     184      453689 :     RibUpdateMonitor *monitor = updates->monitor();
     185             : 
     186             :     // Bail if the route is already deleted.
     187      453678 :     if (db_entry->IsDeleted())
     188       43604 :         return true;
     189             : 
     190             :     // There may have been a route change before the walk gets to this route.
     191             :     // Trim mjoin by resetting mcurrent and mscheduled to prevent enqueueing
     192             :     // duplicate updates.
     193             :     //
     194             :     // TBD:: tweak this further to handle route refresh.
     195      410096 :     RibPeerSet mcurrent, mscheduled;
     196      410128 :     monitor->GetPeerSetCurrentAndScheduled(db_entry, RibOutUpdates::QUPDATE,
     197             :             &mcurrent, &mscheduled);
     198      410176 :     RibPeerSet mjoin_subset = mjoin;
     199      410125 :     mjoin_subset.Reset(mcurrent);
     200      410075 :     mjoin_subset.Reset(mscheduled);
     201      410096 :     if (mjoin_subset.empty()) {
     202      305607 :         return true;
     203             :     }
     204             : 
     205             :     // Run export policy to generate the update infos.
     206      104462 :     BgpRoute *route = static_cast<BgpRoute *>(db_entry);
     207      104462 :     UpdateInfoSList uinfo_slist;
     208      104471 :     bool reach = ribout_->table()->Export(ribout_, route, mjoin_subset,
     209             :             uinfo_slist);
     210      104538 :     assert(!reach || !uinfo_slist->empty());
     211      104549 :     if (!reach) {
     212       25142 :         return true;
     213             :     }
     214             : 
     215             :     // Create a new update.
     216       79407 :     RouteUpdate *rt_update = new RouteUpdate(route, RibOutUpdates::QBULK);
     217       79397 :     rt_update->SetUpdateInfo(uinfo_slist);
     218             : 
     219             :     // Merge the update into the BULK queue. If there is an entry present
     220             :     // already then the update info is merged.  Kick the BgpUpdateSender
     221             :     // machinery if needed.
     222       79402 :     bool need_tail_dequeue = monitor->MergeUpdate(db_entry, rt_update);
     223       79346 :     if (need_tail_dequeue) {
     224       79318 :         BgpUpdateSender *sender = ribout_->sender();
     225       79317 :         sender->RibOutActive(root->index(), ribout_, RibOutUpdates::QBULK);
     226             :     }
     227             : 
     228       79427 :     return true;
     229      410176 : }
     230             : 
     231             : //
     232             : // Leave Processing.
     233             : // 1. Detect if there's no history or scheduled updates for the bitset of
     234             : //    peers.
     235             : // 2. Remove any history or scheduled updates.
     236             : //
     237      453910 : bool BgpExport::Leave(DBTablePartBase *root, const RibPeerSet &mleave,
     238             :         DBEntryBase *db_entry) {
     239      453910 :     RibOutUpdates *updates = ribout_->updates(root->index());
     240      453900 :     RibUpdateMonitor *monitor = updates->monitor();
     241             : 
     242             :     // Nothing to do if there are no current or scheduled updates for any
     243             :     // peers in mleave.
     244      453900 :     RibPeerSet mcurrent, mscheduled;
     245      453963 :     monitor->GetPeerSetCurrentAndScheduled(db_entry, RibOutUpdates::QCOUNT,
     246             :             &mcurrent, &mscheduled);
     247      453960 :     RibPeerSet munion, mleave_subset;
     248      453966 :     munion.Set(mcurrent);
     249      453889 :     munion.Set(mscheduled);
     250      453827 :     mleave_subset.BuildIntersection(mleave, munion);
     251      453824 :     if (mleave_subset.empty()) {
     252      192716 :         return true;
     253             :     }
     254             : 
     255             :     // Cancel scheduled updates for the route and/or remove AdvertiseInfo
     256             :     // for current advertised state.
     257      261099 :     monitor->ClearPeerSetCurrentAndScheduled(db_entry, mleave_subset);
     258             : 
     259      261326 :     return true;
     260      454042 : }

Generated by: LCOV version 1.14