Line data Source code
1 : /* 2 : * Copyright (c) 2013 Juniper Networks, Inc. All rights reserved. 3 : */ 4 : 5 : #include "bgp/bgp_export.h" 6 : 7 : #include <boost/bind/bind.hpp> 8 : 9 : #include "bgp/bgp_ribout_updates.h" 10 : #include "bgp/bgp_route.h" 11 : #include "bgp/bgp_table.h" 12 : #include "bgp/bgp_update.h" 13 : #include "bgp/bgp_update_monitor.h" 14 : #include "bgp/bgp_update_sender.h" 15 : 16 : using namespace boost::placeholders; 17 : 18 34861 : BgpExport::BgpExport(RibOut *ribout) 19 34861 : : ribout_(ribout) { 20 34861 : } 21 : 22 : // 23 : // Check if the desired state as expressed by the UpdateInfoSList is exactly 24 : // the same as the state already stored in the RouteUpdate. 25 : // 26 : // Return true if we have a duplicate, false otherwise. 27 : // 28 1691 : static bool IsDuplicate(const RouteUpdate *rt_update, 29 : const UpdateInfoSList *uinfo_slist) { 30 1691 : return rt_update->CompareUpdateInfo(*uinfo_slist); 31 : } 32 : 33 : // 34 : // Export Processing. 35 : // 1. Calculate the desired attributes (UpdateInfo list) via BgpTable::Export. 36 : // 2. Dequeue the existing update if present. 37 : // a) If the current update is the same as desired update then the operation 38 : // is a NOP. 39 : // b) If there is a current update, then the code must obtain a lock on it 40 : // in order to make sure that the output process is not currently reading 41 : // and modifying its state. 42 : // 3. Calculate the delta between previous state and new state. 43 : // 4. Enqueue a new update at tail. 44 : // 45 1437345 : void BgpExport::Export(DBTablePartBase *root, DBEntryBase *db_entry) { 46 : RouteUpdate *rt_update; 47 1437345 : UpdateInfoSList uinfo_slist; 48 : 49 : // Calculate attributes by running through export policy. 50 1437356 : BgpRoute *route = static_cast<BgpRoute *>(db_entry); 51 1437356 : bool reach = false; 52 1437356 : if (!db_entry->IsDeleted() && !ribout_->PeerSet().empty()) { 53 815774 : reach = ribout_->table()->Export(ribout_, route, ribout_->PeerSet(), 54 : uinfo_slist); 55 : } 56 1437580 : assert(!reach || !uinfo_slist->empty()); 57 : 58 : // Find and dequeue any existing DBState. 59 1437577 : bool duplicate = false; 60 1437577 : RibOutUpdates *updates = ribout_->updates(root->index()); 61 1437523 : RibUpdateMonitor *monitor = updates->monitor(); 62 1437491 : DBState *dbstate = monitor->GetDBStateAndDequeue(db_entry, 63 : boost::bind(IsDuplicate, _1, &uinfo_slist), 64 : &duplicate); 65 : 66 : // Handle the DBState as appropriate. 67 1437937 : if (dbstate == NULL) { 68 : // A NULL DBState implies that we either have no previous state or 69 : // that the previously scheduled updates are duplicates of what we 70 : // want to send now. 71 : 72 : // Nothing to do if we are looking at duplicates. 73 894776 : if (duplicate) 74 259 : return; 75 : 76 : // If we have no previous state and the route is not reachable we 77 : // are done. 78 894517 : if (!reach) 79 438138 : return; 80 : 81 : // We have no previous state and the route is reachable. Need to 82 : // schedule a new update. 83 456379 : rt_update = new RouteUpdate(route, RibOutUpdates::QUPDATE); 84 : } else { 85 : // The DBState in the DBEntryBase must be the same as what we found. 86 : // This is a paranoid check to make sure that GetDBStateAndDequeue 87 : // did not forget to update the DBState when handing a RouteUpdate 88 : // from the QBULK queue or when handling an UpdateList. 89 : const DBState *entry_db_state = 90 543161 : db_entry->GetState(root->parent(), ribout_->listener_id()); 91 543250 : assert(entry_db_state == dbstate); 92 : 93 : // We got here because we have previous state, either a RouteState 94 : // or a RouteUpdate. Note that it cannot be an UpdateList since we 95 : // get rid of UpdateLists in GetDBStateAndDequeue and return either 96 : // the RouteUpdate for QUPDATE or a new RouteState with the history. 97 543250 : assert(dynamic_cast<UpdateList *>(dbstate) == NULL); 98 : 99 543250 : rt_update = dynamic_cast<RouteUpdate *>(dbstate); 100 543250 : if (rt_update == NULL) { 101 : // Previous state is not a RouteUpdate, must be a RouteState. 102 541646 : RouteState *rstate = static_cast<RouteState *>(dbstate); 103 : 104 : // Bail if the new state that we want to schedule is identical 105 : // to what we have previously advertised. This can happen when 106 : // there are back to back changes to a route such that it goes 107 : // from state A to B and then back to A but the Export routine 108 : // never saw state B. 109 541646 : if (rstate->CompareUpdateInfo(uinfo_slist)) 110 110941 : return; 111 : 112 : // We need a new RouteUpdate to advertise the new state and/or 113 : // withdraw part or all of the previous state. Move history to 114 : // a new RouteUpdate and get rid of the RouteState. 115 430648 : rt_update = new RouteUpdate(route, RibOutUpdates::QUPDATE); 116 430566 : rstate->MoveHistory(rt_update); 117 430464 : delete rstate; 118 430510 : dbstate = NULL; 119 430510 : rstate = NULL; 120 : } else { 121 : // The RouteUpdate must be for QUPDATE. Any RouteUpdate that got 122 : // dequeued from QBULK is also converted to be for QUPDATE. 123 1604 : assert(rt_update->queue_id() == RibOutUpdates::QUPDATE); 124 : 125 : // The previous state is a RouteUpdate. Get rid of any scheduled 126 : // UpdateInfos since we have fresh new UpdateInfos to schedule. 127 : // Note that the history information is still in the RouteUpdate. 128 1576 : rt_update->ClearUpdateInfo(); 129 : 130 : // If the history is empty and the route is not reachable, there 131 : // is nothing to withdraw. Get rid of the RouteUpdate and return. 132 1576 : if (rt_update->History()->empty() && !reach) { 133 56 : db_entry->ClearState(root->parent(), ribout_->listener_id()); 134 56 : delete rt_update; 135 56 : return; 136 : } 137 : } 138 : 139 : // The new UpdateInfos that we want to schedule may not cover all the 140 : // peers that we advertised state to previously. May need to generate 141 : // negative state for such peers based on the history in RouteUpdate. 142 432030 : rt_update->BuildNegativeUpdateInfo(uinfo_slist); 143 : 144 : // At this point we have a RouteUpdate with history information and 145 : // an UpdateInfoSList that contains the state that we are going to 146 : // schedule. There may be some commonality between them. We want to 147 : // trim redundant state in the UpdateInfoSList i.e. if some of it's 148 : // the same as what's already in the history. 149 431891 : rt_update->TrimRedundantUpdateInfo(uinfo_slist); 150 : 151 : // If there are no more UpdateInfos in the list, move the history to 152 : // a new RouteState and get rid of the RouteUpdate. This can happen 153 : // if the route goes from state A to B, the Export routine sees B and 154 : // schedules updates but the state goes back to A before the updates 155 : // can be sent to anyone. 156 431903 : if (uinfo_slist->empty()) { 157 36 : RouteState *rstate = new RouteState(); 158 36 : rt_update->MoveHistory(rstate); 159 36 : db_entry->SetState(root->parent(), ribout_->listener_id(), rstate); 160 36 : delete rt_update; 161 36 : return; 162 : } 163 : } 164 : 165 : // Associate the new UpdateInfos we want to send with the RouteUpdate 166 : // and enqueue the RouteUpdate. 167 888206 : assert(!uinfo_slist->empty()); 168 888202 : rt_update->SetUpdateInfo(uinfo_slist); 169 888167 : updates->Enqueue(db_entry, rt_update); 170 1437872 : } 171 : 172 : // 173 : // Join Processing. 174 : // 1. Detect if we have've already sent or scheduled updates to the bitset of 175 : // peers for the QUPDATE queue. 176 : // 2. Calculate the desired attributes (UpdateInfo list) via BgpTable::Export. 177 : // 3. Create a new RouteUpdate for the QBULK queue and merge it with existing 178 : // state. 179 : // 180 455624 : bool BgpExport::Join(DBTablePartBase *root, const RibPeerSet &mjoin, 181 : DBEntryBase *db_entry) { 182 455624 : RibOutUpdates *updates = ribout_->updates(root->index()); 183 455626 : RibUpdateMonitor *monitor = updates->monitor(); 184 : 185 : // Bail if the route is already deleted. 186 455611 : if (db_entry->IsDeleted()) 187 41720 : return true; 188 : 189 : // There may have been a route change before the walk gets to this route. 190 : // Trim mjoin by resetting mcurrent and mscheduled to prevent enqueueing 191 : // duplicate updates. 192 : // 193 : // TBD:: tweak this further to handle route refresh. 194 413905 : RibPeerSet mcurrent, mscheduled; 195 413918 : monitor->GetPeerSetCurrentAndScheduled(db_entry, RibOutUpdates::QUPDATE, 196 : &mcurrent, &mscheduled); 197 414099 : RibPeerSet mjoin_subset = mjoin; 198 414029 : mjoin_subset.Reset(mcurrent); 199 413965 : mjoin_subset.Reset(mscheduled); 200 413963 : if (mjoin_subset.empty()) { 201 310123 : return true; 202 : } 203 : 204 : // Run export policy to generate the update infos. 205 103811 : BgpRoute *route = static_cast<BgpRoute *>(db_entry); 206 103811 : UpdateInfoSList uinfo_slist; 207 103831 : bool reach = ribout_->table()->Export(ribout_, route, mjoin_subset, 208 : uinfo_slist); 209 103937 : assert(!reach || !uinfo_slist->empty()); 210 103936 : if (!reach) { 211 24986 : return true; 212 : } 213 : 214 : // Create a new update. 215 78950 : RouteUpdate *rt_update = new RouteUpdate(route, RibOutUpdates::QBULK); 216 78942 : rt_update->SetUpdateInfo(uinfo_slist); 217 : 218 : // Merge the update into the BULK queue. If there is an entry present 219 : // already then the update info is merged. Kick the BgpUpdateSender 220 : // machinery if needed. 221 78951 : bool need_tail_dequeue = monitor->MergeUpdate(db_entry, rt_update); 222 78936 : if (need_tail_dequeue) { 223 78810 : BgpUpdateSender *sender = ribout_->sender(); 224 78810 : sender->RibOutActive(root->index(), ribout_, RibOutUpdates::QBULK); 225 : } 226 : 227 78966 : return true; 228 414075 : } 229 : 230 : // 231 : // Leave Processing. 232 : // 1. Detect if there's no history or scheduled updates for the bitset of 233 : // peers. 234 : // 2. Remove any history or scheduled updates. 235 : // 236 455915 : bool BgpExport::Leave(DBTablePartBase *root, const RibPeerSet &mleave, 237 : DBEntryBase *db_entry) { 238 455915 : RibOutUpdates *updates = ribout_->updates(root->index()); 239 455910 : RibUpdateMonitor *monitor = updates->monitor(); 240 : 241 : // Nothing to do if there are no current or scheduled updates for any 242 : // peers in mleave. 243 455903 : RibPeerSet mcurrent, mscheduled; 244 455896 : monitor->GetPeerSetCurrentAndScheduled(db_entry, RibOutUpdates::QCOUNT, 245 : &mcurrent, &mscheduled); 246 455967 : RibPeerSet munion, mleave_subset; 247 455934 : munion.Set(mcurrent); 248 455880 : munion.Set(mscheduled); 249 455841 : mleave_subset.BuildIntersection(mleave, munion); 250 455809 : if (mleave_subset.empty()) { 251 189563 : return true; 252 : } 253 : 254 : // Cancel scheduled updates for the route and/or remove AdvertiseInfo 255 : // for current advertised state. 256 266239 : monitor->ClearPeerSetCurrentAndScheduled(db_entry, mleave_subset); 257 : 258 266400 : return true; 259 455963 : }