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: 205 237 86.5 %
Date: 2026-11-09 02:10:49 Functions: 18 18 100.0 %
Legend: Lines: hit not hit

          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);

Generated by: LCOV version 1.14