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