OpenSDN source code
bgp_export.cc
Go to the documentation of this file.
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 
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 
19  : ribout_(ribout) {
20 }
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 static bool IsDuplicate(const RouteUpdate *rt_update,
29  const UpdateInfoSList *uinfo_slist) {
30  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 //
46  RouteUpdate *rt_update;
47  UpdateInfoSList uinfo_slist;
48 
49  // Calculate attributes by running through export policy.
50  BgpRoute *route = static_cast<BgpRoute *>(db_entry);
51  bool reach = false;
52  if (!db_entry->IsDeleted() && !ribout_->PeerSet().empty()) {
53  reach = ribout_->table()->Export(ribout_, route, ribout_->PeerSet(),
54  uinfo_slist);
55  }
56  assert(!reach || !uinfo_slist->empty());
57 
58  // Find and dequeue any existing DBState.
59  bool duplicate = false;
60  RibOutUpdates *updates = ribout_->updates(root->index());
61  RibUpdateMonitor *monitor = updates->monitor();
62  DBState *dbstate = monitor->GetDBStateAndDequeue(db_entry,
63  boost::bind(IsDuplicate, _1, &uinfo_slist),
64  &duplicate);
65 
66  // Handle the DBState as appropriate.
67  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  if (duplicate)
74  return;
75 
76  // If we have no previous state and the route is not reachable we
77  // are done.
78  if (!reach)
79  return;
80 
81  // We have no previous state and the route is reachable. Need to
82  // schedule a new update.
83  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  db_entry->GetState(root->parent(), ribout_->listener_id());
91  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  assert(dynamic_cast<UpdateList *>(dbstate) == NULL);
98 
99  rt_update = dynamic_cast<RouteUpdate *>(dbstate);
100  if (rt_update == NULL) {
101  // Previous state is not a RouteUpdate, must be a RouteState.
102  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  if (rstate->CompareUpdateInfo(uinfo_slist))
110  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  rt_update = new RouteUpdate(route, RibOutUpdates::QUPDATE);
116  rstate->MoveHistory(rt_update);
117  delete rstate;
118  dbstate = NULL;
119  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  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  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  if (rt_update->History()->empty() && !reach) {
133  db_entry->ClearState(root->parent(), ribout_->listener_id());
134  delete rt_update;
135  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  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  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  if (uinfo_slist->empty()) {
157  RouteState *rstate = new RouteState();
158  rt_update->MoveHistory(rstate);
159  db_entry->SetState(root->parent(), ribout_->listener_id(), rstate);
160  delete rt_update;
161  return;
162  }
163  }
164 
165  // Associate the new UpdateInfos we want to send with the RouteUpdate
166  // and enqueue the RouteUpdate.
167  assert(!uinfo_slist->empty());
168  rt_update->SetUpdateInfo(uinfo_slist);
169  updates->Enqueue(db_entry, rt_update);
170 }
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 bool BgpExport::Join(DBTablePartBase *root, const RibPeerSet &mjoin,
181  DBEntryBase *db_entry) {
182  RibOutUpdates *updates = ribout_->updates(root->index());
183  RibUpdateMonitor *monitor = updates->monitor();
184 
185  // Bail if the route is already deleted.
186  if (db_entry->IsDeleted())
187  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  RibPeerSet mcurrent, mscheduled;
196  &mcurrent, &mscheduled);
197  RibPeerSet mjoin_subset = mjoin;
198  mjoin_subset.Reset(mcurrent);
199  mjoin_subset.Reset(mscheduled);
200  if (mjoin_subset.empty()) {
201  return true;
202  }
203 
204  // Run export policy to generate the update infos.
205  BgpRoute *route = static_cast<BgpRoute *>(db_entry);
206  UpdateInfoSList uinfo_slist;
207  bool reach = ribout_->table()->Export(ribout_, route, mjoin_subset,
208  uinfo_slist);
209  assert(!reach || !uinfo_slist->empty());
210  if (!reach) {
211  return true;
212  }
213 
214  // Create a new update.
215  RouteUpdate *rt_update = new RouteUpdate(route, RibOutUpdates::QBULK);
216  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  bool need_tail_dequeue = monitor->MergeUpdate(db_entry, rt_update);
222  if (need_tail_dequeue) {
223  BgpUpdateSender *sender = ribout_->sender();
224  sender->RibOutActive(root->index(), ribout_, RibOutUpdates::QBULK);
225  }
226 
227  return true;
228 }
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 bool BgpExport::Leave(DBTablePartBase *root, const RibPeerSet &mleave,
237  DBEntryBase *db_entry) {
238  RibOutUpdates *updates = ribout_->updates(root->index());
239  RibUpdateMonitor *monitor = updates->monitor();
240 
241  // Nothing to do if there are no current or scheduled updates for any
242  // peers in mleave.
243  RibPeerSet mcurrent, mscheduled;
245  &mcurrent, &mscheduled);
246  RibPeerSet munion, mleave_subset;
247  munion.Set(mcurrent);
248  munion.Set(mscheduled);
249  mleave_subset.BuildIntersection(mleave, munion);
250  if (mleave_subset.empty()) {
251  return true;
252  }
253 
254  // Cancel scheduled updates for the route and/or remove AdvertiseInfo
255  // for current advertised state.
256  monitor->ClearPeerSetCurrentAndScheduled(db_entry, mleave_subset);
257 
258  return true;
259 }
static bool IsDuplicate(const RouteUpdate *rt_update, const UpdateInfoSList *uinfo_slist)
Definition: bgp_export.cc:28
BgpExport(RibOut *ribout)
Definition: bgp_export.cc:18
bool Join(DBTablePartBase *root, const RibPeerSet &mjoin, DBEntryBase *db_entry)
Definition: bgp_export.cc:180
void Export(DBTablePartBase *root, DBEntryBase *db_entry)
Definition: bgp_export.cc:45
RibOut * ribout_
Definition: bgp_export.h:36
bool Leave(DBTablePartBase *root, const RibPeerSet &mleave, DBEntryBase *db_entry)
Definition: bgp_export.cc:236
virtual bool Export(RibOut *ribout, Route *route, const RibPeerSet &peerset, UpdateInfoSList &uinfo_slist)=0
void RibOutActive(int index, RibOut *ribout, int queue_id)
void Set(const BitSet &rhs)
Definition: bitset.cc:462
void Reset(const BitSet &rhs)
Definition: bitset.cc:470
bool empty() const
Definition: bitset.cc:165
void BuildIntersection(const BitSet &lhs, const BitSet &rhs)
Definition: bitset.cc:509
DBState * GetState(DBTableBase *tbl_base, ListenerId listener) const
Definition: db_entry.cc:37
void ClearState(DBTableBase *tbl_base, ListenerId listener)
Definition: db_entry.cc:73
bool IsDeleted() const
Definition: db_entry.h:48
void SetState(DBTableBase *tbl_base, ListenerId listener, DBState *state)
Definition: db_entry.cc:22
DBTableBase * parent()
void Enqueue(DBEntryBase *db_entry, RouteUpdate *rt_update)
RibUpdateMonitor * monitor()
RibOutUpdates * updates(int idx)
Definition: bgp_ribout.h:316
const RibPeerSet & PeerSet() const
Definition: bgp_ribout.cc:506
BgpTable * table()
Definition: bgp_ribout.h:304
BgpUpdateSender * sender()
Definition: bgp_ribout.h:306
DBTableBase::ListenerId listener_id() const
Definition: bgp_ribout.h:313
bool GetPeerSetCurrentAndScheduled(DBEntryBase *db_entry, int queue_id, RibPeerSet *mcurrent, RibPeerSet *mscheduled)
DBState * GetDBStateAndDequeue(DBEntryBase *db_entry, UpdateCmp cmp, bool *duplicate)
bool MergeUpdate(DBEntryBase *db_entry, RouteUpdate *rt_update)
void ClearPeerSetCurrentAndScheduled(DBEntryBase *db_entry, const RibPeerSet &mleave)
bool CompareUpdateInfo(const UpdateInfoSList &uinfo_slist) const
Definition: bgp_ribout.cc:278
void MoveHistory(RouteUpdate *rt_update)
Definition: bgp_ribout.cc:250
bool CompareUpdateInfo(const UpdateInfoSList &uinfo_slist) const
Definition: bgp_update.cc:116
void ClearUpdateInfo()
Definition: bgp_update.cc:50
int queue_id() const
Definition: bgp_update.h:229
void BuildNegativeUpdateInfo(UpdateInfoSList &uinfo_slist) const
Definition: bgp_update.cc:217
AdvertiseSList & History()
Definition: bgp_update.h:211
void MoveHistory(RouteState *rstate)
Definition: bgp_update.cc:296
void TrimRedundantUpdateInfo(UpdateInfoSList &uinfo_slist) const
Definition: bgp_update.cc:248
void SetUpdateInfo(UpdateInfoSList &uinfo_slist)
Definition: bgp_update.cc:41