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-06-22 02:21:21 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             :  */
       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       67719 : InetTable::InetTable(DB *db, const string &name)
      23       67719 :     : BgpTable(db, name) {
      24       67729 :     family_ = (name.at(name.length()-1) == '3') ?
      25             :         Address::INETMPLS : Address::INET;
      26       67722 : }
      27             : 
      28     6049098 : size_t InetTable::HashFunction(const Ip4Prefix &prefix) {
      29     6049098 :     return boost::hash_value(prefix.ip4_addr().to_ulong());
      30             : }
      31             : 
      32      256433 : unique_ptr<DBEntry> InetTable::AllocEntry(const DBRequestKey *key) const {
      33      256433 :     const RequestKey *pfxkey = static_cast<const RequestKey *>(key);
      34      256433 :     return unique_ptr<DBEntry>(new InetRoute(pfxkey->prefix));
      35             : }
      36             : 
      37       48316 : unique_ptr<DBEntry> InetTable::AllocEntryStr(const string &key_str) const {
      38       48316 :     Ip4Prefix prefix = Ip4Prefix::FromString(key_str);
      39       96632 :     return unique_ptr<DBEntry>(new InetRoute(prefix));
      40             : }
      41             : 
      42     4024374 : size_t InetTable::Hash(const DBEntry *entry) const {
      43     4024374 :     const InetRoute *rt_entry = static_cast<const InetRoute *>(entry);
      44     4024374 :     size_t value = HashFunction(rt_entry->GetPrefix());
      45     4022974 :     return value % DB::PartitionCount();
      46             : }
      47             : 
      48      105911 : size_t InetTable::Hash(const DBRequestKey *key) const {
      49      105911 :     const RequestKey *rkey = static_cast<const RequestKey *>(key);
      50      105911 :     size_t value = HashFunction(rkey->prefix);
      51      105912 :     return value % DB::PartitionCount();
      52             : }
      53             : 
      54       83864 : BgpRoute *InetTable::TableFind(DBTablePartition *rtp,
      55             :         const DBRequestKey *prefix) {
      56       83864 :     const RequestKey *pfxkey = static_cast<const RequestKey *>(prefix);
      57       83864 :     InetRoute rt_key(pfxkey->prefix);
      58      167749 :     return static_cast<BgpRoute *>(rtp->Find(&rt_key));
      59       83881 : }
      60             : 
      61       59343 : DBTableBase *InetTable::CreateTable(DB *db, const string &name) {
      62       59343 :     InetTable *table = new InetTable(db, name);
      63       59356 :     table->Init();
      64       59353 :     return table;
      65             : }
      66             : 
      67      300153 : BgpRoute *InetTable::RouteReplicate(BgpServer *server,
      68             :         BgpTable *src_table, BgpRoute *src_rt, const BgpPath *path,
      69             :         ExtCommunityPtr community) {
      70      300153 :     InetRoute *inet= dynamic_cast<InetRoute *> (src_rt);
      71             : 
      72      300153 :     boost::scoped_ptr<Ip4Prefix> inet_prefix;
      73      300166 :     RouteDistinguisher rd;
      74             : 
      75      300167 :     if (inet) {
      76       58234 :         inet_prefix.reset(new Ip4Prefix(inet->GetPrefix().ip4_addr(),
      77       29117 :                                       inet->GetPrefix().prefixlen()));
      78             :     } else {
      79      271050 :         InetVpnRoute *inetvpn = dynamic_cast<InetVpnRoute *> (src_rt);
      80      271050 :         assert(inetvpn);
      81      271050 :         rd = inetvpn->GetPrefix().route_distinguisher();
      82      542051 :         inet_prefix.reset(new Ip4Prefix(inetvpn->GetPrefix().addr(),
      83      271026 :                                   inetvpn->GetPrefix().prefixlen()));
      84             :     }
      85             : 
      86      300127 :     InetRoute rt_key(*inet_prefix);
      87             :     DBTablePartition *rtp =
      88      300157 :         static_cast<DBTablePartition *>(GetTablePartition(&rt_key));
      89      300128 :     BgpRoute *dest_route = static_cast<BgpRoute *>(rtp->Find(&rt_key));
      90      300164 :     if (dest_route == NULL) {
      91      174749 :         dest_route = new InetRoute(rt_key.GetPrefix());
      92      174742 :         rtp->Add(dest_route);
      93             :     } else {
      94      125415 :         dest_route->ClearDelete();
      95             :     }
      96             : 
      97      300159 :     BgpAttrDB *attr_db = server->attr_db();
      98             :     BgpAttrPtr new_attr = attr_db->ReplaceExtCommunityAndLocate(path->GetAttr(),
      99      300145 :                                                                 community);
     100             : 
     101             :     // Set the RD attr if route is replicated from vpn table
     102      300176 :     if (!inet) {
     103      271059 :         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      300179 :     BgpPath *dest_path = dest_route->FindSecondaryPath(src_rt,
     108             :                                           path->GetSource(), path->GetPeer(),
     109             :                                           path->GetPathId());
     110      300153 :     uint32_t prev_flags = 0 , prev_label = 0;
     111      300153 :     const BgpAttr *prev_attr = NULL;
     112      300153 :     if (dest_path != NULL) {
     113      123322 :         prev_flags = dest_path->GetFlags();
     114      123322 :         prev_label = dest_path->GetLabel();
     115      123322 :         prev_attr = dest_path->GetOriginalAttr();
     116      238866 :         if ((new_attr != prev_attr) ||
     117      238866 :             (path->GetFlags() != prev_flags) ||
     118      102217 :             (path->GetLabel() != prev_label)) {
     119             :             // Update Attributes and notify (if needed)
     120       21294 :             if (dest_path->NeedsResolution()) {
     121           0 :                 path_resolver()->StopPathResolution(rtp->index(), dest_path);
     122             :             }
     123       21294 :             assert(dest_route->RemoveSecondaryPath(src_rt, path->GetSource(),
     124             :                         path->GetPeer(), path->GetPathId()));
     125             :         } else {
     126      102027 :             return dest_route;
     127             :         }
     128             :     }
     129             : 
     130             :     BgpSecondaryPath *replicated_path =
     131      198125 :         new BgpSecondaryPath(path->GetPeer(), path->GetPathId(),
     132      198126 :             path->GetSource(), new_attr, path->GetFlags(), path->GetLabel());
     133      198139 :     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      198138 :     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      198132 :     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      198138 :     bool notify = true;
     155      198138 :     if (dest_path != NULL) {
     156       34813 :         if ((replicated_path->GetOriginalAttr() == prev_attr) &&
     157       34813 :             (replicated_path->GetFlags() == prev_flags) &&
     158         190 :             (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      198138 :     if (notify)
     168      198136 :         rtp->Notify(dest_route);
     169             : 
     170      198135 :     return dest_route;
     171      300162 : }
     172             : 
     173      245049 : bool InetTable::Export(RibOut *ribout, Route *route, const RibPeerSet &peerset,
     174             :         UpdateInfoSList &uinfo_slist) {
     175      245049 :     BgpRoute *bgp_route = static_cast<BgpRoute *>(route);
     176             : 
     177      245049 :     UpdateInfo *uinfo = GetUpdateInfo(ribout, bgp_route, peerset);
     178      245109 :     if (!uinfo)
     179        5231 :         return false;
     180             : 
     181      239878 :     if (ribout->ExportPolicy().encoding == RibExportPolicy::BGP) {
     182        5559 :         BgpAttrDB *attr_db = routing_instance()->server()->attr_db();
     183        5559 :         UpdateExtendedCommunity(&uinfo->roattr);
     184             : 
     185             :         // Strip OriginVnPath.
     186        5559 :         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      239877 :     uinfo_slist->push_front(*uinfo);
     193             : 
     194      239878 :     return true;
     195             : }
     196             : 
     197             : // Strip all extended-communities except OriginVN.
     198        5559 : void InetTable::UpdateExtendedCommunity(RibOutAttr *roattr) {
     199        5559 :     ExtCommunityPtr ext_commp = roattr->attr()->ext_community();
     200        5559 :     if (!ext_commp)
     201        3062 :         return;
     202             : 
     203             :     // Retrieve any origin_vn already present.
     204        2497 :     ExtCommunity::ExtCommunityValue const *origin_vnp = NULL;
     205       12461 :     BOOST_FOREACH(const ExtCommunity::ExtCommunityValue &comm,
     206             :                   ext_commp->communities()) {
     207        4985 :         if (!ExtCommunity::is_origin_vn(comm))
     208        4979 :             continue;
     209           6 :         origin_vnp = &comm;
     210           6 :         break;
     211             :     }
     212             : 
     213        2497 :     BgpAttrDB *attr_db = routing_instance()->server()->attr_db();
     214             : 
     215             :     // If there is no origin-vn, then remove all other extended communities.
     216        2497 :     if (!origin_vnp) {
     217             :         BgpAttrPtr new_attr = attr_db->ReplaceExtCommunityAndLocate(
     218        2491 :             roattr->attr(), NULL);
     219        2491 :         roattr->set_attr(this, new_attr);
     220        2491 :         return;
     221        2491 :     }
     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        5559 : }
     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       30503 : BgpAttrPtr InetTable::GetAttributes(BgpRoute *rt,
     286             :                                     BgpAttrPtr inet_attrp, const IPeer *peer) {
     287       30503 :     CHECK_CONCURRENCY("db::DBTable");
     288             : 
     289       30502 :     BgpAttrPtr attrp = GetFabricAttributes(rt, inet_attrp, peer);
     290       61006 :     return GetMvpnAttributes(attrp);
     291       30503 : }
     292             : 
     293       30503 : BgpAttrPtr InetTable::GetMvpnAttributes(BgpAttrPtr attrp) {
     294       30503 :     BgpServer *server = routing_instance()->server();
     295       30503 :     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       30389 :     return attrp;
     309             : }
     310             : 
     311             : // Given an inet prefix, update OriginVN with corresponding inetvpn route's
     312             : // path attribute.
     313       30501 : BgpAttrPtr InetTable::GetFabricAttributes(BgpRoute *rt,
     314             :                                     BgpAttrPtr inet_attrp, const IPeer *peer) {
     315             : 
     316       30501 :     if (!routing_instance()->IsMasterRoutingInstance())
     317       23773 :         return inet_attrp;
     318        6730 :     if (!inet_attrp || inet_attrp->source_rd().IsZero())
     319        6720 :         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       29826 : void InetTable::UpdateRoute(const InetVpnPrefix &inetvpn_prefix,
     353             :                             const IPeer *peer, BgpAttrPtr inetvpn_attrp) {
     354       29826 :     CHECK_CONCURRENCY("db::DBTable");
     355       29826 :     assert(routing_instance()->IsMasterRoutingInstance());
     356             : 
     357             :     // Check if a route is present in inet.0 table for this prefix.
     358       29826 :     Ip4Prefix inet_prefix(inetvpn_prefix.addr(), inetvpn_prefix.prefixlen());
     359       29826 :     InetTable::RequestKey inet_rt_key(inet_prefix, NULL);
     360       29826 :     DBTablePartition *inet_partition = dynamic_cast<DBTablePartition *>(
     361       29826 :         GetTablePartition(&inet_rt_key));
     362             : 
     363       29826 :     InetVpnTable *inetvpn_table = dynamic_cast<InetVpnTable *>(
     364       29826 :         routing_instance()->GetTable(Address::INETVPN));
     365       29826 :     assert(inetvpn_table);
     366       29826 :     InetVpnTable::RequestKey inetvpn_rt_key(inetvpn_prefix, NULL);
     367             :     DBTablePartition *inetvpn_partition =
     368       29826 :         static_cast<DBTablePartition *>(inetvpn_table->GetTablePartition(
     369             :             &inetvpn_rt_key));
     370       29826 :     assert(inet_partition->index() == inetvpn_partition->index());
     371             : 
     372       29826 :     InetRoute *inet_route = dynamic_cast<InetRoute *>(
     373       29826 :         TableFind(inet_partition, &inet_rt_key));
     374       29826 :     if (!inet_route)
     375       28614 :         return;
     376             : 
     377        1212 :     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       60860 : }
     396             : 
     397       42903 : PathResolver *InetTable::CreatePathResolver() {
     398       42903 :     if (routing_instance()->IsMasterRoutingInstance())
     399           0 :         return NULL;
     400       42904 :     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);

Generated by: LCOV version 1.14