LCOV - code coverage report
Current view: top level - bgp/inet - inet_table.cc (source / functions) Hit Total Coverage
Test: OpenSDN C/C++ coverage (all TARGET_SET jobs) Lines: 142 237 59.9 %
Date: 2026-09-21 02:11:39 Functions: 16 18 88.9 %
Legend: Lines: hit not hit

          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         159 : InetTable::InetTable(DB *db, const string &name)
      23         159 :     : BgpTable(db, name) {
      24         160 :     family_ = (name.at(name.length()-1) == '3') ?
      25             :         Address::INETMPLS : Address::INET;
      26         160 : }
      27             : 
      28        2699 : size_t InetTable::HashFunction(const Ip4Prefix &prefix) {
      29        2699 :     return boost::hash_value(prefix.ip4_addr().to_ulong());
      30             : }
      31             : 
      32         146 : unique_ptr<DBEntry> InetTable::AllocEntry(const DBRequestKey *key) const {
      33         146 :     const RequestKey *pfxkey = static_cast<const RequestKey *>(key);
      34         146 :     return unique_ptr<DBEntry>(new InetRoute(pfxkey->prefix));
      35             : }
      36             : 
      37           0 : unique_ptr<DBEntry> InetTable::AllocEntryStr(const string &key_str) const {
      38           0 :     Ip4Prefix prefix = Ip4Prefix::FromString(key_str);
      39           0 :     return unique_ptr<DBEntry>(new InetRoute(prefix));
      40             : }
      41             : 
      42         935 : size_t InetTable::Hash(const DBEntry *entry) const {
      43         935 :     const InetRoute *rt_entry = static_cast<const InetRoute *>(entry);
      44         935 :     size_t value = HashFunction(rt_entry->GetPrefix());
      45         935 :     return value % DB::PartitionCount();
      46             : }
      47             : 
      48         206 : size_t InetTable::Hash(const DBRequestKey *key) const {
      49         206 :     const RequestKey *rkey = static_cast<const RequestKey *>(key);
      50         206 :     size_t value = HashFunction(rkey->prefix);
      51         206 :     return value % DB::PartitionCount();
      52             : }
      53             : 
      54         112 : BgpRoute *InetTable::TableFind(DBTablePartition *rtp,
      55             :         const DBRequestKey *prefix) {
      56         112 :     const RequestKey *pfxkey = static_cast<const RequestKey *>(prefix);
      57         112 :     InetRoute rt_key(pfxkey->prefix);
      58         224 :     return static_cast<BgpRoute *>(rtp->Find(&rt_key));
      59         112 : }
      60             : 
      61         159 : DBTableBase *InetTable::CreateTable(DB *db, const string &name) {
      62         159 :     InetTable *table = new InetTable(db, name);
      63         160 :     table->Init();
      64         159 :     return table;
      65             : }
      66             : 
      67          40 : BgpRoute *InetTable::RouteReplicate(BgpServer *server,
      68             :         BgpTable *src_table, BgpRoute *src_rt, const BgpPath *path,
      69             :         ExtCommunityPtr community) {
      70          40 :     InetRoute *inet= dynamic_cast<InetRoute *> (src_rt);
      71             : 
      72          40 :     boost::scoped_ptr<Ip4Prefix> inet_prefix;
      73          40 :     RouteDistinguisher rd;
      74             : 
      75          40 :     if (inet) {
      76           8 :         inet_prefix.reset(new Ip4Prefix(inet->GetPrefix().ip4_addr(),
      77           4 :                                       inet->GetPrefix().prefixlen()));
      78             :     } else {
      79          36 :         InetVpnRoute *inetvpn = dynamic_cast<InetVpnRoute *> (src_rt);
      80          36 :         assert(inetvpn);
      81          36 :         rd = inetvpn->GetPrefix().route_distinguisher();
      82          72 :         inet_prefix.reset(new Ip4Prefix(inetvpn->GetPrefix().addr(),
      83          36 :                                   inetvpn->GetPrefix().prefixlen()));
      84             :     }
      85             : 
      86          40 :     InetRoute rt_key(*inet_prefix);
      87             :     DBTablePartition *rtp =
      88          40 :         static_cast<DBTablePartition *>(GetTablePartition(&rt_key));
      89          40 :     BgpRoute *dest_route = static_cast<BgpRoute *>(rtp->Find(&rt_key));
      90          40 :     if (dest_route == NULL) {
      91          36 :         dest_route = new InetRoute(rt_key.GetPrefix());
      92          36 :         rtp->Add(dest_route);
      93             :     } else {
      94           4 :         dest_route->ClearDelete();
      95             :     }
      96             : 
      97          40 :     BgpAttrDB *attr_db = server->attr_db();
      98             :     BgpAttrPtr new_attr = attr_db->ReplaceExtCommunityAndLocate(path->GetAttr(),
      99          40 :                                                                 community);
     100             : 
     101             :     // Set the RD attr if route is replicated from vpn table
     102          40 :     if (!inet) {
     103          36 :         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          40 :     BgpPath *dest_path = dest_route->FindSecondaryPath(src_rt,
     108             :                                           path->GetSource(), path->GetPeer(),
     109             :                                           path->GetPathId());
     110          40 :     uint32_t prev_flags = 0 , prev_label = 0;
     111          40 :     const BgpAttr *prev_attr = NULL;
     112          40 :     if (dest_path != NULL) {
     113           4 :         prev_flags = dest_path->GetFlags();
     114           4 :         prev_label = dest_path->GetLabel();
     115           4 :         prev_attr = dest_path->GetOriginalAttr();
     116           4 :         if ((new_attr != prev_attr) ||
     117           4 :             (path->GetFlags() != prev_flags) ||
     118           0 :             (path->GetLabel() != prev_label)) {
     119             :             // Update Attributes and notify (if needed)
     120           4 :             if (dest_path->NeedsResolution()) {
     121           0 :                 path_resolver()->StopPathResolution(rtp->index(), dest_path);
     122             :             }
     123           4 :             assert(dest_route->RemoveSecondaryPath(src_rt, path->GetSource(),
     124             :                         path->GetPeer(), path->GetPathId()));
     125             :         } else {
     126           0 :             return dest_route;
     127             :         }
     128             :     }
     129             : 
     130             :     BgpSecondaryPath *replicated_path =
     131          40 :         new BgpSecondaryPath(path->GetPeer(), path->GetPathId(),
     132          40 :             path->GetSource(), new_attr, path->GetFlags(), path->GetLabel());
     133          40 :     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          40 :     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          40 :     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          40 :     bool notify = true;
     155          40 :     if (dest_path != NULL) {
     156           4 :         if ((replicated_path->GetOriginalAttr() == prev_attr) &&
     157           4 :             (replicated_path->GetFlags() == prev_flags) &&
     158           0 :             (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          40 :     if (notify)
     168          40 :         rtp->Notify(dest_route);
     169             : 
     170          40 :     return dest_route;
     171          40 : }
     172             : 
     173          76 : bool InetTable::Export(RibOut *ribout, Route *route, const RibPeerSet &peerset,
     174             :         UpdateInfoSList &uinfo_slist) {
     175          76 :     BgpRoute *bgp_route = static_cast<BgpRoute *>(route);
     176             : 
     177          76 :     UpdateInfo *uinfo = GetUpdateInfo(ribout, bgp_route, peerset);
     178          76 :     if (!uinfo)
     179          20 :         return false;
     180             : 
     181          56 :     if (ribout->ExportPolicy().encoding == RibExportPolicy::BGP) {
     182          56 :         BgpAttrDB *attr_db = routing_instance()->server()->attr_db();
     183          56 :         UpdateExtendedCommunity(&uinfo->roattr);
     184             : 
     185             :         // Strip OriginVnPath.
     186          56 :         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          56 :     uinfo_slist->push_front(*uinfo);
     193             : 
     194          56 :     return true;
     195             : }
     196             : 
     197             : // Strip all extended-communities except OriginVN.
     198          56 : void InetTable::UpdateExtendedCommunity(RibOutAttr *roattr) {
     199          56 :     ExtCommunityPtr ext_commp = roattr->attr()->ext_community();
     200          56 :     if (!ext_commp)
     201           0 :         return;
     202             : 
     203             :     // Retrieve any origin_vn already present.
     204          56 :     ExtCommunity::ExtCommunityValue const *origin_vnp = NULL;
     205         324 :     BOOST_FOREACH(const ExtCommunity::ExtCommunityValue &comm,
     206             :                   ext_commp->communities()) {
     207         134 :         if (!ExtCommunity::is_origin_vn(comm))
     208         134 :             continue;
     209           0 :         origin_vnp = &comm;
     210           0 :         break;
     211             :     }
     212             : 
     213          56 :     BgpAttrDB *attr_db = routing_instance()->server()->attr_db();
     214             : 
     215             :     // If there is no origin-vn, then remove all other extended communities.
     216          56 :     if (!origin_vnp) {
     217             :         BgpAttrPtr new_attr = attr_db->ReplaceExtCommunityAndLocate(
     218          56 :             roattr->attr(), NULL);
     219          56 :         roattr->set_attr(this, new_attr);
     220          56 :         return;
     221          56 :     }
     222             : 
     223             :     // Remove all communities other than OriginVN by replacing all of the
     224             :     // extended-community with just OriginVN.
     225           0 :     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          56 : }
     236             : 
     237             : // Attach OriginVN extended-community from inetvpn path attribute if present
     238             : // into inet route path attribute.
     239           0 : BgpAttrPtr InetTable::UpdateAttributes(const BgpAttrPtr inetvpn_attrp,
     240             :                                        const BgpAttrPtr inet_attrp) {
     241           0 :     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           0 :     ExtCommunity::ExtCommunityValue const *inetvpn_rt_origin_vn = NULL;
     246           0 :     if (inetvpn_attrp && inetvpn_attrp->ext_community()) {
     247           0 :         BOOST_FOREACH(const ExtCommunity::ExtCommunityValue &comm,
     248             :                     inetvpn_attrp->ext_community()->communities()) {
     249           0 :             if (!ExtCommunity::is_origin_vn(comm))
     250           0 :                 continue;
     251           0 :             inetvpn_rt_origin_vn = &comm;
     252           0 :             break;
     253             :         }
     254             :     }
     255             : 
     256           0 :     ExtCommunity::ExtCommunityValue const *inet_rt_origin_vn = NULL;
     257           0 :     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           0 :     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           0 :     ExtCommunityPtr new_ext_community;
     273           0 :     if (!inetvpn_rt_origin_vn) {
     274           0 :         new_ext_community = server->extcomm_db()->RemoveOriginVnAndLocate(
     275           0 :             inet_attrp->ext_community());
     276             :     } else {
     277           0 :         new_ext_community = server->extcomm_db()->ReplaceOriginVnAndLocate(
     278           0 :             inet_attrp->ext_community(), *inetvpn_rt_origin_vn);
     279             :     }
     280             : 
     281             :     return server->attr_db()->ReplaceExtCommunityAndLocate(inet_attrp.get(),
     282           0 :                                                            new_ext_community);
     283           0 : }
     284             : 
     285          52 : BgpAttrPtr InetTable::GetAttributes(BgpRoute *rt,
     286             :                                     BgpAttrPtr inet_attrp, const IPeer *peer) {
     287          52 :     CHECK_CONCURRENCY("db::DBTable");
     288             : 
     289          52 :     BgpAttrPtr attrp = GetFabricAttributes(rt, inet_attrp, peer);
     290         104 :     return GetMvpnAttributes(attrp);
     291          52 : }
     292             : 
     293          52 : BgpAttrPtr InetTable::GetMvpnAttributes(BgpAttrPtr attrp) {
     294          52 :     BgpServer *server = routing_instance()->server();
     295          52 :     if (server->mvpn_ipv4_enable()) {
     296             :         VrfRouteImport vit(server->bgp_identifier(),
     297          52 :                            routing_instance()->index());
     298             :         ExtCommunityPtr ext =
     299             :             server->extcomm_db()->ReplaceVrfRouteImportAndLocate(
     300          52 :                 attrp->ext_community(), vit.GetExtCommunity());
     301          52 :         SourceAs sas(server->autonomous_system(), 0);
     302         156 :         ext = server->extcomm_db()->ReplaceSourceASAndLocate(ext.get(),
     303         104 :                     sas.GetExtCommunity());
     304             :         BgpAttrPtr new_attr =
     305          52 :             server->attr_db()->ReplaceExtCommunityAndLocate(attrp.get(), ext);
     306          52 :         return new_attr;
     307          52 :     }
     308           0 :     return attrp;
     309             : }
     310             : 
     311             : // Given an inet prefix, update OriginVN with corresponding inetvpn route's
     312             : // path attribute.
     313          52 : BgpAttrPtr InetTable::GetFabricAttributes(BgpRoute *rt,
     314             :                                     BgpAttrPtr inet_attrp, const IPeer *peer) {
     315             : 
     316          52 :     if (!routing_instance()->IsMasterRoutingInstance())
     317          52 :         return inet_attrp;
     318           0 :     if (!inet_attrp || inet_attrp->source_rd().IsZero())
     319           0 :         return inet_attrp;
     320             : 
     321           0 :     const InetRoute *inet_rt = dynamic_cast<InetRoute *>(rt);
     322           0 :     const Ip4Prefix inet_prefix = inet_rt->GetPrefix();
     323           0 :     RequestKey inet_rt_key(inet_prefix, NULL);
     324           0 :     DBTablePartition *inet_partition = dynamic_cast<DBTablePartition *>(
     325           0 :         GetTablePartition(&inet_rt_key));
     326             : 
     327           0 :     InetVpnTable *inetvpn_table = dynamic_cast<InetVpnTable *>(
     328           0 :         routing_instance()->GetTable(Address::INETVPN));
     329           0 :     assert(inetvpn_table);
     330             :     InetVpnPrefix inetvpn_prefix(inet_attrp->source_rd(),
     331           0 :                                  inet_prefix.ip4_addr(),
     332           0 :                                  inet_prefix.prefixlen());
     333           0 :     InetVpnTable::RequestKey inetvpn_rt_key(inetvpn_prefix, NULL);
     334           0 :     DBTablePartition *inetvpn_partition = dynamic_cast<DBTablePartition *>(
     335           0 :         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           0 :     assert(inet_partition->index() == inetvpn_partition->index());
     340           0 :     InetVpnRoute *inetvpn_route = dynamic_cast<InetVpnRoute *>(
     341           0 :         inetvpn_table->TableFind(inetvpn_partition, &inetvpn_rt_key));
     342           0 :     if (!inetvpn_route)
     343           0 :         return inet_attrp;
     344           0 :     BgpPath *inetvpn_path = inetvpn_route->FindPath(peer, true);
     345           0 :     if (!inetvpn_path)
     346           0 :         return inet_attrp;
     347           0 :     return UpdateAttributes(inetvpn_path->GetAttr(), inet_attrp);
     348           0 : }
     349             : 
     350             : // Update inet route path attributes with OriginVN from corresponding inetvpn
     351             : // route path attribute.
     352          52 : void InetTable::UpdateRoute(const InetVpnPrefix &inetvpn_prefix,
     353             :                             const IPeer *peer, BgpAttrPtr inetvpn_attrp) {
     354          52 :     CHECK_CONCURRENCY("db::DBTable");
     355          52 :     assert(routing_instance()->IsMasterRoutingInstance());
     356             : 
     357             :     // Check if a route is present in inet.0 table for this prefix.
     358          52 :     Ip4Prefix inet_prefix(inetvpn_prefix.addr(), inetvpn_prefix.prefixlen());
     359          52 :     InetTable::RequestKey inet_rt_key(inet_prefix, NULL);
     360          52 :     DBTablePartition *inet_partition = dynamic_cast<DBTablePartition *>(
     361          52 :         GetTablePartition(&inet_rt_key));
     362             : 
     363          52 :     InetVpnTable *inetvpn_table = dynamic_cast<InetVpnTable *>(
     364          52 :         routing_instance()->GetTable(Address::INETVPN));
     365          52 :     assert(inetvpn_table);
     366          52 :     InetVpnTable::RequestKey inetvpn_rt_key(inetvpn_prefix, NULL);
     367             :     DBTablePartition *inetvpn_partition =
     368          52 :         static_cast<DBTablePartition *>(inetvpn_table->GetTablePartition(
     369             :             &inetvpn_rt_key));
     370          52 :     assert(inet_partition->index() == inetvpn_partition->index());
     371             : 
     372          52 :     InetRoute *inet_route = dynamic_cast<InetRoute *>(
     373          52 :         TableFind(inet_partition, &inet_rt_key));
     374          52 :     if (!inet_route)
     375          52 :         return;
     376             : 
     377           0 :     BgpPath *inet_path = inet_route->FindPath(peer);
     378           0 :     if (!inet_path)
     379           0 :         return;
     380           0 :     BgpAttrPtr inet_attrp = inet_path->GetAttr();
     381           0 :     if (!inet_attrp)
     382           0 :         return;
     383             : 
     384             :     // Bail if the RDs do not match.
     385           0 :     if (!(inet_attrp->source_rd() == inetvpn_prefix.route_distinguisher())) {
     386           0 :         return;
     387             :     }
     388           0 :     BgpAttrPtr new_inet_attrp = UpdateAttributes(inetvpn_attrp, inet_attrp);
     389           0 :     if (new_inet_attrp == inet_attrp)
     390           0 :         return;
     391             : 
     392             :     // Update route with OriginVN path attribute.
     393           0 :     inet_path->SetAttr(new_inet_attrp, inet_path->GetOriginalAttr());
     394           0 :     inet_route->Notify();
     395         104 : }
     396             : 
     397          96 : PathResolver *InetTable::CreatePathResolver() {
     398          96 :     if (routing_instance()->IsMasterRoutingInstance())
     399           0 :         return NULL;
     400          96 :     return (new PathResolver(this));
     401             : }
     402             : 
     403           1 : static void RegisterFactory() {
     404           1 :     DB::RegisterFactory("inet.0", &InetTable::CreateTable);
     405           1 :     DB::RegisterFactory("inet.3", &InetTable::CreateTable);
     406           1 : }
     407             : 
     408             : MODULE_INITIALIZER(RegisterFactory);

Generated by: LCOV version 1.14