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