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/inet/inet_table.h"
7 :
8 : #include <boost/foreach.hpp>
9 :
10 : #include "base/task_annotations.h"
11 : #include "bgp/bgp_server.h"
12 : #include "bgp/bgp_update.h"
13 : #include "bgp/extended-community/source_as.h"
14 : #include "bgp/extended-community/vrf_route_import.h"
15 : #include "bgp/l3vpn/inetvpn_route.h"
16 : #include "bgp/l3vpn/inetvpn_table.h"
17 : #include "bgp/routing-instance/path_resolver.h"
18 : #include "bgp/routing-instance/routing_instance.h"
19 :
20 : using std::unique_ptr;
21 : using std::string;
22 :
23 67666 : InetTable::InetTable(DB *db, const string &name)
24 67666 : : BgpTable(db, name) {
25 67719 : family_ = (name.at(name.length()-1) == '3') ?
26 : Address::INETMPLS : Address::INET;
27 67686 : }
28 :
29 6085208 : size_t InetTable::HashFunction(const Ip4Prefix &prefix) {
30 6085208 : return boost::hash_value(prefix.ip4_addr().to_ulong());
31 : }
32 :
33 253256 : unique_ptr<DBEntry> InetTable::AllocEntry(const DBRequestKey *key) const {
34 253256 : const RequestKey *pfxkey = static_cast<const RequestKey *>(key);
35 253256 : return unique_ptr<DBEntry>(new InetRoute(pfxkey->prefix));
36 : }
37 :
38 48325 : unique_ptr<DBEntry> InetTable::AllocEntryStr(const string &key_str) const {
39 48325 : Ip4Prefix prefix = Ip4Prefix::FromString(key_str);
40 96652 : return unique_ptr<DBEntry>(new InetRoute(prefix));
41 : }
42 :
43 4014825 : size_t InetTable::Hash(const DBEntry *entry) const {
44 4014825 : const InetRoute *rt_entry = static_cast<const InetRoute *>(entry);
45 4014825 : size_t value = HashFunction(rt_entry->GetPrefix());
46 4013350 : return value % DB::PartitionCount();
47 : }
48 :
49 105515 : size_t InetTable::Hash(const DBRequestKey *key) const {
50 105515 : const RequestKey *rkey = static_cast<const RequestKey *>(key);
51 105515 : size_t value = HashFunction(rkey->prefix);
52 105511 : return value % DB::PartitionCount();
53 : }
54 :
55 83714 : BgpRoute *InetTable::TableFind(DBTablePartition *rtp,
56 : const DBRequestKey *prefix) {
57 83714 : const RequestKey *pfxkey = static_cast<const RequestKey *>(prefix);
58 83714 : InetRoute rt_key(pfxkey->prefix);
59 167495 : return static_cast<BgpRoute *>(rtp->Find(&rt_key));
60 83773 : }
61 :
62 59257 : DBTableBase *InetTable::CreateTable(DB *db, const string &name) {
63 59257 : InetTable *table = new InetTable(db, name);
64 59311 : table->Init();
65 59320 : return table;
66 : }
67 :
68 309960 : BgpRoute *InetTable::RouteReplicate(BgpServer *server,
69 : BgpTable *src_table, BgpRoute *src_rt, const BgpPath *path,
70 : ExtCommunityPtr community) {
71 309960 : InetRoute *inet= dynamic_cast<InetRoute *> (src_rt);
72 :
73 309960 : boost::scoped_ptr<Ip4Prefix> inet_prefix;
74 309961 : RouteDistinguisher rd;
75 :
76 309961 : if (inet) {
77 58011 : inet_prefix.reset(new Ip4Prefix(inet->GetPrefix().ip4_addr(),
78 29006 : inet->GetPrefix().prefixlen()));
79 : } else {
80 280954 : InetVpnRoute *inetvpn = dynamic_cast<InetVpnRoute *> (src_rt);
81 280954 : assert(inetvpn);
82 280954 : rd = inetvpn->GetPrefix().route_distinguisher();
83 561841 : inet_prefix.reset(new Ip4Prefix(inetvpn->GetPrefix().addr(),
84 280917 : inetvpn->GetPrefix().prefixlen()));
85 : }
86 :
87 309911 : InetRoute rt_key(*inet_prefix);
88 : DBTablePartition *rtp =
89 309954 : static_cast<DBTablePartition *>(GetTablePartition(&rt_key));
90 309910 : BgpRoute *dest_route = static_cast<BgpRoute *>(rtp->Find(&rt_key));
91 309957 : if (dest_route == NULL) {
92 174782 : dest_route = new InetRoute(rt_key.GetPrefix());
93 174771 : rtp->Add(dest_route);
94 : } else {
95 135175 : dest_route->ClearDelete();
96 : }
97 :
98 309938 : BgpAttrDB *attr_db = server->attr_db();
99 : BgpAttrPtr new_attr = attr_db->ReplaceExtCommunityAndLocate(path->GetAttr(),
100 309920 : community);
101 :
102 : // Set the RD attr if route is replicated from vpn table
103 309977 : if (!inet) {
104 280970 : new_attr = attr_db->ReplaceSourceRdAndLocate(new_attr.get(), rd);
105 : }
106 :
107 : // Check whether there's already a path with the given peer and path id.
108 309974 : BgpPath *dest_path = dest_route->FindSecondaryPath(src_rt,
109 : path->GetSource(), path->GetPeer(),
110 : path->GetPathId());
111 309962 : uint32_t prev_flags = 0 , prev_label = 0;
112 309962 : const BgpAttr *prev_attr = NULL;
113 309962 : if (dest_path != NULL) {
114 133014 : prev_flags = dest_path->GetFlags();
115 133010 : prev_label = dest_path->GetLabel();
116 133009 : prev_attr = dest_path->GetOriginalAttr();
117 258231 : if ((new_attr != prev_attr) ||
118 258231 : (path->GetFlags() != prev_flags) ||
119 111883 : (path->GetLabel() != prev_label)) {
120 : // Update Attributes and notify (if needed)
121 21334 : if (dest_path->NeedsResolution()) {
122 0 : path_resolver()->StopPathResolution(rtp->index(), dest_path);
123 : }
124 21334 : assert(dest_route->RemoveSecondaryPath(src_rt, path->GetSource(),
125 : path->GetPeer(), path->GetPathId()));
126 : } else {
127 111676 : return dest_route;
128 : }
129 : }
130 :
131 : BgpSecondaryPath *replicated_path =
132 198281 : new BgpSecondaryPath(path->GetPeer(), path->GetPathId(),
133 198278 : path->GetSource(), new_attr, path->GetFlags(), path->GetLabel());
134 198287 : replicated_path->SetReplicateInfo(src_table, src_rt);
135 :
136 : // For VPN to VRF replication, start path resolution if fast convergence is
137 : // enabled and update path flag to indicate need for resolution.
138 198282 : if (!inet && (server->IsNextHopCheckEnabled()) &&
139 0 : (replicated_path->GetSource() == BgpPath::BGP_XMPP)) {
140 0 : Address::Family family = replicated_path->GetAttr()->nexthop_family();
141 0 : RoutingInstanceMgr *mgr = server->routing_instance_mgr();
142 0 : RoutingInstance *master_ri = mgr->GetDefaultRoutingInstance();
143 0 : BgpTable *table = master_ri->GetTable(family);
144 0 : replicated_path->SetResolveNextHop();
145 0 : path_resolver()->StartPathResolution(dest_route, replicated_path,
146 : table);
147 : }
148 :
149 198276 : dest_route->InsertPath(replicated_path);
150 :
151 : // BgpRoute::InsertPath() triggers routing policies processing, which may
152 : // change the flags (namely, RoutingPolicyReject). So we check the flags
153 : // once again and only notify the partition if the new path is still
154 : // different after routing policies were applied.
155 198277 : bool notify = true;
156 198277 : if (dest_path != NULL) {
157 34879 : if ((replicated_path->GetOriginalAttr() == prev_attr) &&
158 34879 : (replicated_path->GetFlags() == prev_flags) &&
159 207 : (replicated_path->GetLabel() == prev_label))
160 0 : notify = false;
161 : }
162 :
163 : // Notify the route even if the best path may not have changed. For XMPP
164 : // peers, we support sending multiple ECMP next-hops for a single route.
165 : //
166 : // TODO(ananth): Can be optimized for changes that does not result in
167 : // any change to ECMP list.
168 198277 : if (notify)
169 198283 : rtp->Notify(dest_route);
170 :
171 198283 : return dest_route;
172 309959 : }
173 :
174 245029 : bool InetTable::Export(RibOut *ribout, Route *route, const RibPeerSet &peerset,
175 : UpdateInfoSList &uinfo_slist) {
176 245029 : BgpRoute *bgp_route = static_cast<BgpRoute *>(route);
177 :
178 245029 : UpdateInfo *uinfo = GetUpdateInfo(ribout, bgp_route, peerset);
179 245087 : if (!uinfo)
180 5213 : return false;
181 :
182 239874 : if (ribout->ExportPolicy().encoding == RibExportPolicy::BGP) {
183 5507 : BgpAttrDB *attr_db = routing_instance()->server()->attr_db();
184 5507 : UpdateExtendedCommunity(&uinfo->roattr);
185 :
186 : // Strip OriginVnPath.
187 5507 : if (uinfo->roattr.attr()->origin_vn_path()) {
188 : BgpAttrPtr new_attr = attr_db->ReplaceOriginVnPathAndLocate(
189 0 : uinfo->roattr.attr(), NULL);
190 0 : uinfo->roattr.set_attr(this, new_attr);
191 0 : }
192 : }
193 239887 : uinfo_slist->push_front(*uinfo);
194 :
195 239884 : return true;
196 : }
197 :
198 : // Strip all extended-communities except OriginVN.
199 5507 : void InetTable::UpdateExtendedCommunity(RibOutAttr *roattr) {
200 5507 : ExtCommunityPtr ext_commp = roattr->attr()->ext_community();
201 5507 : if (!ext_commp)
202 3034 : return;
203 :
204 : // Retrieve any origin_vn already present.
205 2473 : ExtCommunity::ExtCommunityValue const *origin_vnp = NULL;
206 12337 : BOOST_FOREACH(const ExtCommunity::ExtCommunityValue &comm,
207 : ext_commp->communities()) {
208 4935 : if (!ExtCommunity::is_origin_vn(comm))
209 4929 : continue;
210 6 : origin_vnp = &comm;
211 6 : break;
212 : }
213 :
214 2473 : BgpAttrDB *attr_db = routing_instance()->server()->attr_db();
215 :
216 : // If there is no origin-vn, then remove all other extended communities.
217 2473 : if (!origin_vnp) {
218 : BgpAttrPtr new_attr = attr_db->ReplaceExtCommunityAndLocate(
219 2467 : roattr->attr(), NULL);
220 2467 : roattr->set_attr(this, new_attr);
221 2467 : return;
222 2467 : }
223 :
224 : // Remove all communities other than OriginVN by replacing all of the
225 : // extended-community with just OriginVN.
226 6 : if (ext_commp->communities().size() > 1) {
227 0 : ExtCommunity::ExtCommunityList list;
228 0 : list.push_back(*origin_vnp);
229 : ExtCommunityDB *extcomm_db =
230 0 : routing_instance()->server()->extcomm_db();
231 0 : ext_commp = extcomm_db->AppendAndLocate(NULL, list);
232 : BgpAttrPtr new_attr = attr_db->ReplaceExtCommunityAndLocate(
233 0 : roattr->attr(), ext_commp);
234 0 : roattr->set_attr(this, new_attr);
235 0 : }
236 5507 : }
237 :
238 : // Attach OriginVN extended-community from inetvpn path attribute if present
239 : // into inet route path attribute.
240 6 : BgpAttrPtr InetTable::UpdateAttributes(const BgpAttrPtr inetvpn_attrp,
241 : const BgpAttrPtr inet_attrp) {
242 6 : BgpServer *server = routing_instance()->server();
243 :
244 : // Check if origin-vn path attribute in inet.0 table path is identical to
245 : // what is in inetvpn table path.
246 6 : ExtCommunity::ExtCommunityValue const *inetvpn_rt_origin_vn = NULL;
247 6 : if (inetvpn_attrp && inetvpn_attrp->ext_community()) {
248 48 : BOOST_FOREACH(const ExtCommunity::ExtCommunityValue &comm,
249 : inetvpn_attrp->ext_community()->communities()) {
250 24 : if (!ExtCommunity::is_origin_vn(comm))
251 18 : continue;
252 6 : inetvpn_rt_origin_vn = &comm;
253 6 : break;
254 : }
255 : }
256 :
257 6 : ExtCommunity::ExtCommunityValue const *inet_rt_origin_vn = NULL;
258 6 : if (inet_attrp && inet_attrp->ext_community()) {
259 0 : BOOST_FOREACH(const ExtCommunity::ExtCommunityValue &comm,
260 : inet_attrp->ext_community()->communities()) {
261 0 : if (!ExtCommunity::is_origin_vn(comm))
262 0 : continue;
263 0 : inet_rt_origin_vn = &comm;
264 0 : break;
265 : }
266 : }
267 :
268 : // Ignore if there is no change.
269 6 : if (inetvpn_rt_origin_vn == inet_rt_origin_vn)
270 0 : return inet_attrp;
271 :
272 : // Update/Delete inet route attributes with updated OriginVn community.
273 6 : ExtCommunityPtr new_ext_community;
274 6 : if (!inetvpn_rt_origin_vn) {
275 0 : new_ext_community = server->extcomm_db()->RemoveOriginVnAndLocate(
276 0 : inet_attrp->ext_community());
277 : } else {
278 12 : new_ext_community = server->extcomm_db()->ReplaceOriginVnAndLocate(
279 6 : inet_attrp->ext_community(), *inetvpn_rt_origin_vn);
280 : }
281 :
282 : return server->attr_db()->ReplaceExtCommunityAndLocate(inet_attrp.get(),
283 6 : new_ext_community);
284 6 : }
285 :
286 30453 : BgpAttrPtr InetTable::GetAttributes(BgpRoute *rt,
287 : BgpAttrPtr inet_attrp, const IPeer *peer) {
288 30453 : CHECK_CONCURRENCY("db::DBTable");
289 :
290 30444 : BgpAttrPtr attrp = GetFabricAttributes(rt, inet_attrp, peer);
291 60904 : return GetMvpnAttributes(attrp);
292 30451 : }
293 :
294 30453 : BgpAttrPtr InetTable::GetMvpnAttributes(BgpAttrPtr attrp) {
295 30453 : BgpServer *server = routing_instance()->server();
296 30453 : if (server->mvpn_ipv4_enable()) {
297 : VrfRouteImport vit(server->bgp_identifier(),
298 114 : routing_instance()->index());
299 : ExtCommunityPtr ext =
300 : server->extcomm_db()->ReplaceVrfRouteImportAndLocate(
301 114 : attrp->ext_community(), vit.GetExtCommunity());
302 114 : SourceAs sas(server->autonomous_system(), 0);
303 342 : ext = server->extcomm_db()->ReplaceSourceASAndLocate(ext.get(),
304 228 : sas.GetExtCommunity());
305 : BgpAttrPtr new_attr =
306 114 : server->attr_db()->ReplaceExtCommunityAndLocate(attrp.get(), ext);
307 114 : return new_attr;
308 114 : }
309 30339 : return attrp;
310 : }
311 :
312 : // Given an inet prefix, update OriginVN with corresponding inetvpn route's
313 : // path attribute.
314 30455 : BgpAttrPtr InetTable::GetFabricAttributes(BgpRoute *rt,
315 : BgpAttrPtr inet_attrp, const IPeer *peer) {
316 :
317 30455 : if (!routing_instance()->IsMasterRoutingInstance())
318 23765 : return inet_attrp;
319 6685 : if (!inet_attrp || inet_attrp->source_rd().IsZero())
320 6675 : return inet_attrp;
321 :
322 10 : const InetRoute *inet_rt = dynamic_cast<InetRoute *>(rt);
323 10 : const Ip4Prefix inet_prefix = inet_rt->GetPrefix();
324 10 : RequestKey inet_rt_key(inet_prefix, NULL);
325 10 : DBTablePartition *inet_partition = dynamic_cast<DBTablePartition *>(
326 10 : GetTablePartition(&inet_rt_key));
327 :
328 10 : InetVpnTable *inetvpn_table = dynamic_cast<InetVpnTable *>(
329 10 : routing_instance()->GetTable(Address::INETVPN));
330 10 : assert(inetvpn_table);
331 : InetVpnPrefix inetvpn_prefix(inet_attrp->source_rd(),
332 10 : inet_prefix.ip4_addr(),
333 20 : inet_prefix.prefixlen());
334 10 : InetVpnTable::RequestKey inetvpn_rt_key(inetvpn_prefix, NULL);
335 10 : DBTablePartition *inetvpn_partition = dynamic_cast<DBTablePartition *>(
336 10 : inetvpn_table->GetTablePartition(&inetvpn_rt_key));
337 :
338 : // Assert that the partition indicies are identical. This is a MUST
339 : // requirement as we need to peek into tables across different families.
340 10 : assert(inet_partition->index() == inetvpn_partition->index());
341 10 : InetVpnRoute *inetvpn_route = dynamic_cast<InetVpnRoute *>(
342 10 : inetvpn_table->TableFind(inetvpn_partition, &inetvpn_rt_key));
343 10 : if (!inetvpn_route)
344 6 : return inet_attrp;
345 4 : BgpPath *inetvpn_path = inetvpn_route->FindPath(peer, true);
346 4 : if (!inetvpn_path)
347 0 : return inet_attrp;
348 4 : return UpdateAttributes(inetvpn_path->GetAttr(), inet_attrp);
349 10 : }
350 :
351 : // Update inet route path attributes with OriginVN from corresponding inetvpn
352 : // route path attribute.
353 29755 : void InetTable::UpdateRoute(const InetVpnPrefix &inetvpn_prefix,
354 : const IPeer *peer, BgpAttrPtr inetvpn_attrp) {
355 29755 : CHECK_CONCURRENCY("db::DBTable");
356 29753 : assert(routing_instance()->IsMasterRoutingInstance());
357 :
358 : // Check if a route is present in inet.0 table for this prefix.
359 29753 : Ip4Prefix inet_prefix(inetvpn_prefix.addr(), inetvpn_prefix.prefixlen());
360 29753 : InetTable::RequestKey inet_rt_key(inet_prefix, NULL);
361 29752 : DBTablePartition *inet_partition = dynamic_cast<DBTablePartition *>(
362 29728 : GetTablePartition(&inet_rt_key));
363 :
364 29750 : InetVpnTable *inetvpn_table = dynamic_cast<InetVpnTable *>(
365 29752 : routing_instance()->GetTable(Address::INETVPN));
366 29750 : assert(inetvpn_table);
367 29750 : InetVpnTable::RequestKey inetvpn_rt_key(inetvpn_prefix, NULL);
368 : DBTablePartition *inetvpn_partition =
369 29751 : static_cast<DBTablePartition *>(inetvpn_table->GetTablePartition(
370 : &inetvpn_rt_key));
371 29750 : assert(inet_partition->index() == inetvpn_partition->index());
372 :
373 29754 : InetRoute *inet_route = dynamic_cast<InetRoute *>(
374 29750 : TableFind(inet_partition, &inet_rt_key));
375 29754 : if (!inet_route)
376 28542 : return;
377 :
378 1212 : BgpPath *inet_path = inet_route->FindPath(peer);
379 1212 : if (!inet_path)
380 0 : return;
381 1212 : BgpAttrPtr inet_attrp = inet_path->GetAttr();
382 1212 : if (!inet_attrp)
383 0 : return;
384 :
385 : // Bail if the RDs do not match.
386 1212 : if (!(inet_attrp->source_rd() == inetvpn_prefix.route_distinguisher())) {
387 1210 : return;
388 : }
389 4 : BgpAttrPtr new_inet_attrp = UpdateAttributes(inetvpn_attrp, inet_attrp);
390 2 : if (new_inet_attrp == inet_attrp)
391 0 : return;
392 :
393 : // Update route with OriginVN path attribute.
394 2 : inet_path->SetAttr(new_inet_attrp, inet_path->GetOriginalAttr());
395 2 : inet_route->Notify();
396 60715 : }
397 :
398 42909 : PathResolver *InetTable::CreatePathResolver() {
399 42909 : if (routing_instance()->IsMasterRoutingInstance())
400 0 : return NULL;
401 42909 : return (new PathResolver(this));
402 : }
403 :
404 159 : static void RegisterFactory() {
405 159 : DB::RegisterFactory("inet.0", &InetTable::CreateTable);
406 159 : DB::RegisterFactory("inet.3", &InetTable::CreateTable);
407 159 : }
408 :
409 : MODULE_INITIALIZER(RegisterFactory);
|