OpenSDN source code
bgp_update.cc
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2013 Juniper Networks, Inc. All rights reserved.
3  * Copyright Contributors to the OpenSDN project
4  */
5 
6 #include "bgp/bgp_update.h"
7 
8 #include "bgp/bgp_route.h"
9 #include "bgp/bgp_table.h"
10 
11 //
12 // Find the UpdateInfo element with matching RibOutAttr.
13 //
15  const RibOutAttr &roattr) const {
16  for (List::const_iterator iter = list_.begin();
17  iter != list_.end(); ++iter) {
18  if (iter->roattr == roattr) return iter.operator->();
19  }
20  return NULL;
21 }
22 
23 RouteUpdate::RouteUpdate(BgpRoute *route, int queue_id)
25  route_(route),
26  queue_id_(queue_id),
27  flags_(0) {
28 }
29 
32  ClearHistory();
33 }
34 
35 //
36 // Set the given UpdateInfoSList in the RouteUpdate to the given value.
37 //
38 // Note that the back pointers from the individual UpdateInfo elements
39 // to the RouteUpdate will be set up when the RouteUpdate gets enqueued
40 // to an UpdateQueue.
41 //
43  assert(updates_->empty());
44  updates_.swap(uinfo_slist);
45 }
46 
47 //
48 // Get rid of all the UpdateInfo elements in the RouteUpdate. Each element
49 // is also deleted.
50 //
52  updates_->clear_and_dispose(UpdateInfoDisposer());
53 }
54 
55 //
56 // Remove the UpdateInfo element from the UpdateInfoSList and delete the
57 // element.
58 //
59 // Return true if the UpdateInfoSList is now empty.
60 //
62  updates_->erase_and_dispose(updates_->iterator_to(*uinfo),
64  return updates_->empty();
65 }
66 
67 //
68 // Find the UpdateInfo element with matching RibOutAttr.
69 //
71  for (UpdateInfoSList::List::iterator iter = updates_->begin();
72  iter != updates_->end(); ++iter) {
73  if (iter->roattr == roattr) return iter.operator->();
74  }
75  return NULL;
76 }
77 
78 //
79 // Find the UpdateInfo element with matching RibOutAttr, full of const
80 // goodness so it can be used by CompareUpdateInfo.
81 //
82 const UpdateInfo *RouteUpdate::FindUpdateInfo(const RibOutAttr &roattr) const {
83  for (UpdateInfoSList::List::const_iterator iter = updates_->begin();
84  iter != updates_->end(); ++iter) {
85  if (iter->roattr == roattr) return iter.operator->();
86  }
87  return NULL;
88 }
89 
90 //
91 // Reset the given RibPeerSet from the target of all UpdateInfo elements. Get
92 // rid of any elements whose target becomes empty.
93 //
95  for (UpdateInfoSList::List::iterator iter = updates_->begin();
96  iter != updates_->end(); ) {
97  iter->target.Reset(peerset);
98  if (iter->target.empty()) {
99  iter = updates_->erase_and_dispose(iter, UpdateInfoDisposer());
100  } else {
101  ++iter;
102  }
103  }
104 }
105 
106 //
107 // Compare this RouteUpdate with the UpdateInfo elements in the given list.
108 // The UpdateInfos and AdvertiseInfos in the RouteUpdate represent the old
109 // state and the UpdateInfoSList represents the new state.
110 //
111 // Uses brute force since the UpdateInfo and AdvertiseInfo lists typically
112 // contain just a single element.
113 //
114 // Return true if the information in the RouteUpdate is logically the same
115 // as that in the UpdateInfoSList.
116 //
117 bool RouteUpdate::CompareUpdateInfo(const UpdateInfoSList &uinfo_slist) const {
118  // Handle the special case where the given UpdateInfoSList is empty.
119  //
120  // If we already have withdraws scheduled to all peers to which the
121  // route was sent earlier, and there are no other scheduled updates,
122  // we can safely consider the RouteUpdate to have the same state as
123  // an empty UpdateInfoSList.
124  RibOutAttr roattr_null;
125  if (uinfo_slist->empty() &&
126  updates_->size() == 1 && updates_->begin()->roattr == roattr_null) {
127  RibPeerSet withdraw_peerset = updates_->begin()->target;
128  for (AdvertiseSList::List::const_iterator iter = history_->begin();
129  iter != history_->end(); ++iter) {
130  if (!withdraw_peerset.Contains(iter->bitset))
131  return false;
132  }
133  return true;
134  }
135 
136  // All the peers that we sent the route to previously must be covered
137  // by the UpdateInfos in the given UpdateInfoSList. If this is not the
138  // case, we would need to schedule withdraws to the peers that are not
139  // covered.
140  RibPeerSet old_peerset, new_peerset;
141  for (AdvertiseSList::List::const_iterator iter = history_->begin();
142  iter != history_->end(); ++iter) {
143  old_peerset.Set(iter->bitset);
144  }
145  for (UpdateInfoSList::List::const_iterator iter = uinfo_slist->begin();
146  iter != uinfo_slist->end(); ++iter) {
147  new_peerset.Set(iter->target);
148  }
149  if (!new_peerset.Contains(old_peerset))
150  return false;
151 
152  // Compare the peerset for each UpdateInfo in the UpdateInfoSList to
153  // the peerset for the corresponding UpdateInfo and AdvertiseInfo in
154  // in the RouteUpdate i.e. the ones for the same RibOutAttr. Each
155  // peer in the new UpdateInfo must either be in the old UpdateInfo or
156  // AdvertiseInfo.
157  for (UpdateInfoSList::List::const_iterator iter = uinfo_slist->begin();
158  iter != uinfo_slist->end(); ++iter) {
159  RibPeerSet attr_peerset;
160  const UpdateInfo *uinfo = FindUpdateInfo(iter->roattr);
161  if (uinfo)
162  attr_peerset.Set(uinfo->target);
163  const AdvertiseInfo *ainfo = FindHistory(iter->roattr);
164  if (ainfo)
165  attr_peerset.Set(ainfo->bitset);
166  if (iter->target != attr_peerset)
167  return false;
168  }
169 
170  // Compare the peerset for each UpdateInfo in the RouteUpdate to the
171  // corresponding UpdateInfo in the given UpdateInfoSList. The peerset
172  // from the old UpdateInfo needs to be a subset of the peerset from
173  // the new UpdateInfo. It need not be equal since we may already have
174  // sent the old state to some peers and moved them to the history.
175  for (UpdateInfoSList::List::const_iterator iter = updates_->begin();
176  iter != updates_->end(); ++iter) {
177  const UpdateInfo *uinfo = uinfo_slist.FindUpdateInfo(iter->roattr);
178  if (!uinfo || !uinfo->target.Contains(iter->target))
179  return false;
180  }
181 
182  return true;
183 }
184 
185 //
186 // Merge the list of UpdateInfo elements into the RouteUpdate. If we find
187 // an UpdateInfo with the same RibOutAttr, we modify it's target in place.
188 // Otherwise, we reset the target bits in any existing UpdateInfos in the
189 // RouteUpdate and move the UpdateInfo from the list into the RouteUpdate.
190 //
192  for (UpdateInfoSList::List::iterator iter = uinfo_slist->begin();
193  iter != uinfo_slist->end(); ) {
194  UpdateInfo *uinfo = FindUpdateInfo(iter->roattr);
195  if (uinfo) {
196  uinfo->target |= iter->target;
197  iter = uinfo_slist->erase_and_dispose(iter, UpdateInfoDisposer());
198  } else {
199  UpdateInfo &temp_uinfo = *iter;
200  ResetUpdateInfo(temp_uinfo.target);
201  iter = uinfo_slist->erase(iter);
202  updates_->push_front(temp_uinfo);
203  }
204  }
205 }
206 
207 //
208 // The AdvertiseSList in the history contains state that has been sent to
209 // a set of peers, and the given UpdateInfoSList represents state that we
210 // intend to send to a possibly different set of peers.
211 //
212 // If there are peers in any AdvertiseInfo in the history for which there
213 // is no state in the UpdateInfoSList, create an UpdateInfo with negative
214 // state to withdraw what we sent out previously. This UpdateInfo has null
215 // RibOutAttr and a RibPeerSet of all peers from which we need to withdraw
216 // the route.
217 //
219  RibPeerSet peerset;
220 
221  // Build the bitset of peers to which we previously advertised something.
222  for (AdvertiseSList::List::const_iterator iter = history_->begin();
223  iter != history_->end(); ++iter) {
224  peerset.Set(iter->bitset);
225  }
226 
227  // Remove the peers to which we are going to send updated state.
228  for (UpdateInfoSList::List::const_iterator iter = uinfo_slist->begin();
229  iter != uinfo_slist->end(); ++iter) {
230  peerset.Reset(iter->target);
231  }
232 
233  // Push an UpdateInfo for a withdraw if the bitset is non-empty.
234  if (!peerset.empty()) {
235  UpdateInfo *uinfo = new UpdateInfo(peerset);
236  uinfo_slist->push_front(*uinfo);
237  }
238 }
239 
240 //
241 // If any UpdateInfo element in the UpdateInfoSList describes state that's
242 // already in one of the AdvertiseInfos in the history, trim that state to
243 // avoid sending duplicate information.
244 //
245 // The RibPeerSet in the UpdateInfo need not be exactly the same as that in
246 // the AdevrtiseInfo. If the RibOutAttrs match, we can trim the RibPeerSet
247 // in the UpdateInfo.
248 //
250  for (AdvertiseSList::List::const_iterator iter = history_->begin();
251  iter != history_->end(); ++iter) {
252  const AdvertiseInfo *ainfo = iter.operator->();
253 
254  for (UpdateInfoSList::List::iterator iter = uinfo_slist->begin();
255  iter != uinfo_slist->end(); ++iter) {
256  // Keep going if the attributes are different.
257  if (iter->roattr != ainfo->roattr)
258  continue;
259 
260  // Found one with matching attributes. Reset the target bits in
261  // the UpdateInfo. Get rid of the UpdateInfo if the target is now
262  // empty.
263  iter->target.Reset(ainfo->bitset);
264  if (iter->target.empty()) {
265  uinfo_slist->erase_and_dispose(iter, UpdateInfoDisposer());
266  }
267 
268  // Since a given UpdateInfoSList can have at most one UpdateInfo
269  // with any given attributes, there's no point in looking at any
270  // more UpdateInfos.
271  break;
272  }
273  }
274 }
275 
276 //
277 // Set the given AdvertiseSList in the RouteUpdate to the given value.
278 //
279 // Should be used only for testing.
280 //
282  assert(history_->empty());
283  history_.swap(history);
284 }
285 
286 //
287 // Get rid of all the AdvertiseInfo elements in the RouteUpdate. Each element
288 // is also deleted.
289 //
291  history_->clear_and_dispose(AdvertiseInfoDisposer());
292 }
293 
294 //
295 // Move history from RouteUpdate to RouteState.
296 //
298  AdvertiseSList adv_slist;
299  SwapHistory(adv_slist);
300  rstate->SwapHistory(adv_slist);
301 }
302 
303 //
304 // Find the history element with matching RibOutAttr.
305 //
306 const AdvertiseInfo *RouteUpdate::FindHistory(const RibOutAttr &roattr) const {
307  for (AdvertiseSList::List::const_iterator iter = history_->begin();
308  iter != history_->end(); ++iter) {
309  if (iter->roattr == roattr) return iter.operator->();
310  }
311  return NULL;
312 }
313 
314 //
315 // Update the history information for the RouteUpdate to include the given
316 // RibOutAttr and the RibPeerSet. This may entail an update to an existing
317 // AdvertiseInfo or the creation of a new one, as well as possible deletion
318 // of an existing one.
319 //
320 void RouteUpdate::UpdateHistory(RibOut *ribout, const RibOutAttr *roattr,
321  const RibPeerSet &bits) {
322  // The history information may reside in the RouteUpdate itself or in
323  // the associated UpdateList if the RouteUpdate in on an UpdateList.
324  AdvertiseSList &adv_slist =
325  (OnUpdateList() ? GetUpdateList(ribout)->History() : history_);
326 
327  // Traipse through all the AdvertiseInfo elements in the history. We
328  // obviously need to find one with a matching RibOutAttr if it exists
329  // and update it's RibPeerSet. Additionally, we also want to reset
330  // the RibPeerSet in all other elements since we may have previously
331  // advertised different attributes to some of the peers in the set.
332  AdvertiseInfo *ainfo = NULL;
333  for (AdvertiseSList::List::iterator iter = adv_slist->begin();
334  iter != adv_slist->end(); ) {
335  // Remember if we find the matching element, and move on to the
336  // next one.
337  if (iter->roattr == *roattr) {
338  assert(ainfo == NULL);
339  ainfo = iter.operator->();
340  ++iter;
341  continue;
342  }
343 
344  // TBD: optimize to reuse an element with the same RibPeerSet but
345  // different attributes.
346 
347  // Reset the bits in the current element. If the RibPeerSet in the
348  // element becomes empty, get rid of it.
349  iter->bitset.Reset(bits);
350  if (iter->bitset.empty()) {
351  AdvertiseSList::List::iterator prev_iter = iter++;
352  adv_slist->erase_and_dispose(prev_iter, AdvertiseInfoDisposer());
353  } else {
354  ++iter;
355  }
356  }
357 
358  // Update an existing element if we found one or create a new one with
359  // the appropriate state.
360  if (roattr->IsReachable()) {
361  if (ainfo == NULL) {
362  ainfo = new AdvertiseInfo(roattr);
363  adv_slist->push_front(*ainfo);
364  }
365  ainfo->bitset |= bits;
366  } else {
367  assert(ainfo == NULL);
368  }
369 }
370 
371 //
372 // Return true if this RouteUpdate has been advertised to anyone. We simply
373 // go through all the AdvertiseInfo elements in the history and check if we
374 // have at least one that's reachable.
375 //
377  for (AdvertiseSList::List::const_iterator iter = history_->begin();
378  iter != history_->end(); ++iter) {
379  if (iter->roattr.IsReachable()) {
380  return true;
381  }
382  }
383  return false;
384 }
385 
386 //
387 // Upgrade this RouteUpdate to an UpdateList. Creates a new UpdateList and
388 // adds the RouteUpdate to it. Also moves the history from the RouteUpdate
389 // to the UpdateList.
390 //
391 // Returns the new UpdateList.
392 //
394  UpdateList *uplist = new UpdateList;
395  AdvertiseSList history;
396  SwapHistory(history);
397  uplist->SwapHistory(history);
398  uplist->AddUpdate(this);
399  return uplist;
400 }
401 
402 //
403 // Return the UpdateList that contains this RouteUpdate. Assumes that the
404 // RouteUpdate is on an UpdateList.
405 //
408  DBState *dbstate = route_->GetState(ribout->table(), ribout->listener_id());
409  UpdateList *uplist = dynamic_cast<UpdateList *>(dbstate);
410  assert(uplist);
411  return uplist;
412 }
413 
414 //
415 // Set the given AdvertiseSList in the UpdateList to the given value.
416 //
417 // Should be used only for testing.
418 //
420  assert(history_->empty());
421  history_.swap(history);
422 }
423 
424 //
425 // Move history from UpdateList to RouteState.
426 //
428  AdvertiseSList adv_slist;
429  SwapHistory(adv_slist);
430  rstate->SwapHistory(adv_slist);
431 }
432 
433 //
434 // Move history from UpdateList to RouteUpdate.
435 //
437  AdvertiseSList adv_slist;
438  SwapHistory(adv_slist);
439  rt_update->SwapHistory(adv_slist);
440 }
441 
442 //
443 // Find the history element with matching RibOutAttr.
444 //
445 const AdvertiseInfo *UpdateList::FindHistory(const RibOutAttr &roattr) const {
446  for (AdvertiseSList::List::const_iterator iter = history_->begin();
447  iter != history_->end(); ++iter) {
448  if (iter->roattr == roattr) return iter.operator->();
449  }
450  return NULL;
451 }
452 
453 //
454 // Add a RouteUpdate to this UpdateList. Sets up the association between
455 // the RouteUpdate and the UpdateList.
456 //
457 // Assumes that RouteUpdate has no history i.e. AdvertiseSList is empty.
458 //
460  list_.push_back(rt_update);
461  rt_update->set_update_list();
462 }
463 
464 //
465 // Remove a RouteUpdate from this UpdateList. Takes care of removing the
466 // linkage between the UpdateList and the RouteUpdate.
467 //
469  rt_update->clear_update_list();
470  list_.remove(rt_update);
471 }
472 
473 //
474 // Find the RouteUpdate for the given queue.
475 //
477  for (List::iterator iter = list_.begin(); iter != list_.end(); ++iter) {
478  if ((*iter)->queue_id() == queue_id)
479  return *iter;
480  }
481  return NULL;
482 }
483 
484 //
485 // Downgrade this UpdateList to a RouteUpdate if there's only 1 RouteUpdate
486 // on it. Takes care of removing the linkage between the UpdateList and the
487 // last RouteUpdate and moves history from the UpdateList to the RouteUpdate.
488 //
489 // Note that the caller is responsible for deleting the UpdateList.
490 //
491 // Returns the last/only RouteUpdate if successful, NULL otherwise.
492 //
494  assert(list_.size() != 0);
495 
496  if (list_.size() > 1)
497  return NULL;
498 
499  RouteUpdate *rt_update = list_.front();
500  rt_update->clear_update_list();
501  MoveHistory(rt_update);
502  return rt_update;
503 }
void swap(AdvertiseSList &adv_slist)
Definition: bgp_ribout.h:210
void Set(const BitSet &rhs)
Definition: bitset.cc:463
void Reset(const BitSet &rhs)
Definition: bitset.cc:471
bool empty() const
Definition: bitset.cc:166
bool Contains(const BitSet &rhs) const
Definition: bitset.cc:537
DBState * GetState(DBTableBase *tbl_base, ListenerId listener) const
Definition: db_entry.cc:38
bool IsReachable() const
Definition: bgp_ribout.h:96
BgpTable * table()
Definition: bgp_ribout.h:306
DBTableBase::ListenerId listener_id() const
Definition: bgp_ribout.h:315
void SwapHistory(AdvertiseSList &history)
Definition: bgp_ribout.h:238
AdvertiseSList history_
Definition: bgp_update.h:248
bool CompareUpdateInfo(const UpdateInfoSList &uinfo_slist) const
Definition: bgp_update.cc:117
UpdateInfo * FindUpdateInfo(const RibOutAttr &roattr)
Definition: bgp_update.cc:70
void ClearUpdateInfo()
Definition: bgp_update.cc:51
BgpRoute * route_
Definition: bgp_update.h:245
void clear_update_list()
Definition: bgp_update.h:226
void SwapHistory(AdvertiseSList &history)
Definition: bgp_update.h:206
bool FlagIsSet(Flag flag) const
Definition: bgp_update.h:241
void BuildNegativeUpdateInfo(UpdateInfoSList &uinfo_slist) const
Definition: bgp_update.cc:218
RouteUpdate(BgpRoute *route, int queue_id)
Definition: bgp_update.cc:23
UpdateList * GetUpdateList(RibOut *ribout)
Definition: bgp_update.cc:406
void ClearHistory()
Definition: bgp_update.cc:290
UpdateInfoSList updates_
Definition: bgp_update.h:249
void ResetUpdateInfo(const RibPeerSet &peerset)
Definition: bgp_update.cc:94
bool IsAdvertised() const
Definition: bgp_update.cc:376
void MoveHistory(RouteState *rstate)
Definition: bgp_update.cc:297
bool RemoveUpdateInfo(UpdateInfo *uinfo)
Definition: bgp_update.cc:61
void TrimRedundantUpdateInfo(UpdateInfoSList &uinfo_slist) const
Definition: bgp_update.cc:249
void SetUpdateInfo(UpdateInfoSList &uinfo_slist)
Definition: bgp_update.cc:42
void UpdateHistory(RibOut *ribout, const RibOutAttr *roattr, const RibPeerSet &bits)
Definition: bgp_update.cc:320
UpdateList * MakeUpdateList()
Definition: bgp_update.cc:393
void SetHistory(AdvertiseSList &history)
Definition: bgp_update.cc:281
void set_update_list()
Definition: bgp_update.h:225
const AdvertiseInfo * FindHistory(const RibOutAttr &roattr) const
Definition: bgp_update.cc:306
bool OnUpdateList()
Definition: bgp_update.h:221
void MergeUpdateInfo(UpdateInfoSList &uinfo_slist)
Definition: bgp_update.cc:191
void swap(UpdateInfoSList &uinfo_slist)
Definition: bgp_update.h:155
const UpdateInfo * FindUpdateInfo(const RibOutAttr &roattr) const
Definition: bgp_update.cc:14
void RemoveUpdate(RouteUpdate *rt_update)
Definition: bgp_update.cc:468
void AddUpdate(RouteUpdate *rt_update)
Definition: bgp_update.cc:459
void MoveHistory(RouteState *rstate)
Definition: bgp_update.cc:427
List list_
Definition: bgp_update.h:296
void SwapHistory(AdvertiseSList &history)
Definition: bgp_update.h:279
AdvertiseSList history_
Definition: bgp_update.h:295
RouteUpdate * FindUpdate(int queue_id)
Definition: bgp_update.cc:476
AdvertiseSList & History()
Definition: bgp_update.h:275
const AdvertiseInfo * FindHistory(const RibOutAttr &roattr) const
Definition: bgp_update.cc:445
RouteUpdate * MakeRouteUpdate()
Definition: bgp_update.cc:493
void SetHistory(AdvertiseSList &history)
Definition: bgp_update.cc:419
RibOutAttr roattr
Definition: bgp_ribout.h:175
RibPeerSet bitset
Definition: bgp_ribout.h:174
RibPeerSet target
Definition: bgp_update.h:104