LCOV - code coverage report
Current view: top level - vnsw/agent/services - arp_entry.cc (source / functions) Hit Total Coverage
Test: OpenSDN C/C++ coverage (all TARGET_SET jobs) Lines: 125 231 54.1 %
Date: 2026-06-11 01:56:02 Functions: 11 16 68.8 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /*
       2             :  * Copyright (c) 2013 Juniper Networks, Inc. All rights reserved.
       3             :  */
       4             : 
       5             : #include "base/os.h"
       6             : #include "services/arp_proto.h"
       7             : #include "services/services_sandesh.h"
       8             : #include "services/services_init.h"
       9             : #include "oper/route_common.h"
      10             : 
      11          14 : ArpEntry::ArpEntry(boost::asio::io_context &io, ArpHandler *handler,
      12             :                    ArpKey &key, const VrfEntry *vrf, State state,
      13          14 :                    const Interface *itf)
      14          42 :     : io_(io), key_(key), nh_vrf_(vrf), state_(state), retry_count_(0),
      15          14 :       handler_(handler), arp_timer_(NULL), interface_(itf) {
      16          14 :     if (!IsDerived()) {
      17          14 :         arp_timer_ = TimerManager::CreateTimer(io, "Arp Entry timer",
      18             :                 TaskScheduler::GetInstance()->GetTaskId("Agent::Services"),
      19             :                 PktHandler::ARP);
      20             :     }
      21          14 : }
      22             : 
      23          28 : ArpEntry::~ArpEntry() {
      24          14 :     if (!IsDerived()) {
      25          14 :         arp_timer_->Cancel();
      26          14 :         TimerManager::DeleteTimer(arp_timer_);
      27             :     }
      28          14 :     handler_.reset(NULL);
      29          28 : }
      30             : 
      31           0 : void ArpEntry::HandleDerivedArpRequest() {
      32           0 :     ArpProto *arp_proto = handler_->agent()->GetArpProto();
      33             :     //Add ArpRoute for Derived entry
      34           0 :     AddArpRoute(IsResolved());
      35             : 
      36           0 :     ArpKey key(key_.ip, nh_vrf_);
      37           0 :     ArpEntry *entry = arp_proto->FindArpEntry(key);
      38           0 :     if (entry) {
      39           0 :         entry->HandleArpRequest();
      40             :     } else {
      41           0 :         entry = new ArpEntry(io_, handler_.get(), key, nh_vrf_, ArpEntry::INITING,
      42           0 :                              interface_.get());
      43           0 :         if (arp_proto->AddArpEntry(entry) == false) {
      44           0 :             delete entry;
      45           0 :             return;
      46             :         }
      47           0 :         entry->HandleArpRequest();
      48             :     }
      49             : }
      50             : 
      51           2 : bool ArpEntry::HandleArpRequest() {
      52           2 :     if (IsDerived()) {
      53           0 :         HandleDerivedArpRequest();
      54           0 :         return true;
      55             :     }
      56           2 :     if (IsResolved())
      57           0 :         AddArpRoute(true);
      58             :     else {
      59           2 :         AddArpRoute(false);
      60           2 :         if (state_ & ArpEntry::INITING) {
      61           1 :             state_ = ArpEntry::RESOLVING;
      62           1 :             SendArpRequest();
      63             :         }
      64             :     }
      65           2 :     return true;
      66             : }
      67             : 
      68           0 : void ArpEntry::HandleArpReply(const MacAddress &mac) {
      69             : 
      70           0 :     if (IsDerived()) {
      71             :         /* We don't expect ARP replies in derived Vrf */
      72           0 :         return;
      73             :     }
      74           0 :     if ((state_ == ArpEntry::RESOLVING) || (state_ == ArpEntry::ACTIVE) ||
      75           0 :         (state_ == ArpEntry::INITING) || (state_ == ArpEntry::RERESOLVING)) {
      76           0 :         ArpProto *arp_proto = handler_->agent()->GetArpProto();
      77           0 :         arp_timer_->Cancel();
      78           0 :         retry_count_ = 0;
      79           0 :         mac_address_ = mac;
      80           0 :         if (state_ == ArpEntry::RESOLVING) {
      81           0 :             arp_proto->IncrementStatsResolved();
      82           0 :             arp_proto->IncrementStatsResolved(interface_->id());
      83             :         }
      84           0 :         state_ = ArpEntry::ACTIVE;
      85           0 :         StartTimer(arp_proto->aging_timeout(), ArpProto::AGING_TIMER_EXPIRED);
      86           0 :         AddArpRoute(true);
      87             :     }
      88             : }
      89             : 
      90           0 : bool ArpEntry::RetryExpiry() {
      91           0 :     if (state_ & ArpEntry::ACTIVE)
      92           0 :         return true;
      93           0 :     ArpProto *arp_proto = handler_->agent()->GetArpProto();
      94           0 :     if (retry_count_ < arp_proto->max_retries()) {
      95           0 :         retry_count_++;
      96           0 :         SendArpRequest();
      97             :     } else {
      98             :         std::vector<Ip4Address> gateway_list =
      99           0 :             handler_->agent()->vhost_default_gateway();
     100           0 :         Ip4Address ip(key_.ip);
     101           0 :         if (std::find(gateway_list.begin(), gateway_list.end(), ip) !=
     102           0 :                 gateway_list.end()) {
     103           0 :             state_ = ArpEntry::RESOLVING;
     104           0 :             ARP_TRACE(Trace, "Retry exceeded, set arp to resolving",
     105             :                     ip.to_string(),  key_.vrf->GetName(), "");
     106             :         }
     107           0 :         ARP_TRACE(Trace, "Retry exceeded", ip.to_string(),
     108             :                   key_.vrf->GetName(), "");
     109           0 :         arp_proto->IncrementStatsMaxRetries();
     110             : 
     111             :         // if Arp NH is not present, let the entry be deleted
     112           0 :         if (DeleteArpRoute())
     113           0 :             return false;
     114             : 
     115             :         // keep retrying till Arp NH is deleted
     116           0 :         retry_count_ = 0;
     117           0 :         SendArpRequest();
     118           0 :     }
     119           0 :     return true;
     120             : }
     121             : 
     122           0 : bool ArpEntry::AgingExpiry() {
     123           0 :     Ip4Address ip(key_.ip);
     124           0 :     const string& vrf_name = key_.vrf->GetName();
     125           0 :     ArpNHKey nh_key(vrf_name, ip, false);
     126           0 :     ArpNH *arp_nh = static_cast<ArpNH *>(handler_->agent()->nexthop_table()->
     127           0 :                                          FindActiveEntry(&nh_key));
     128           0 :     if (!arp_nh) {
     129             :         // do not re-resolve if Arp NH doesnt exist
     130           0 :         return false;
     131             :     }
     132           0 :     state_ = ArpEntry::RERESOLVING;
     133           0 :     SendArpRequest();
     134           0 :     return true;
     135           0 : }
     136             : 
     137          52 : void ArpEntry::SendGratuitousArp() {
     138          52 :     Agent *agent = handler_->agent();
     139          52 :     ArpProto *arp_proto = agent->GetArpProto();
     140          52 :     if (agent->router_id_configured()) {
     141          52 :         if (interface_->type() == Interface::VM_INTERFACE) {
     142             :             const VmInterface *vmi =
     143          52 :                 static_cast<const VmInterface *>(interface_.get());
     144          52 :             MacAddress smac = vmi->GetVifMac(agent);
     145          52 :             if (key_.vrf && key_.vrf->vn()) {
     146          13 :                 IpAddress gw_ip = key_.vrf->vn()->GetGatewayFromIpam
     147          13 :                     (Ip4Address(key_.ip));
     148          13 :                 IpAddress dns_ip = key_.vrf->vn()->GetDnsFromIpam
     149          13 :                     (Ip4Address(key_.ip));
     150          13 :                 if (!gw_ip.is_unspecified() && gw_ip.is_v4())  {
     151          52 :                     handler_->SendArp(ARPOP_REQUEST, smac,
     152          13 :                                       gw_ip.to_v4().to_ulong(),
     153          26 :                                       smac, vmi->vm_mac(), gw_ip.to_v4().to_ulong(),
     154          13 :                                       vmi->id(), key_.vrf->vrf_id());
     155             :                 }
     156          26 :                 if (!dns_ip.is_unspecified() && dns_ip.is_v4() &&
     157          13 :                     dns_ip != gw_ip)  {
     158           0 :                     handler_->SendArp(ARPOP_REQUEST, smac,
     159           0 :                                       dns_ip.to_v4().to_ulong(),
     160           0 :                                       smac, vmi->vm_mac(), dns_ip.to_v4().to_ulong(),
     161           0 :                                       vmi->id(), key_.vrf->vrf_id());
     162             :                 }
     163             :             }
     164             :         } else {
     165           0 :             handler_->SendArp(ARPOP_REQUEST, arp_proto->ip_fabric_interface_mac(),
     166           0 :                               agent->router_id().to_ulong(), MacAddress(),
     167           0 :                               MacAddress::BroadcastMac(), agent->router_id().to_ulong(),
     168             :                               arp_proto->ip_fabric_interface_index(),
     169           0 :                               key_.vrf->vrf_id());
     170             :         }
     171             : 
     172             :     }
     173             : 
     174          52 :     retry_count_++;
     175          52 :     StartTimer(ArpProto::kGratRetryTimeout, ArpProto::GRATUITOUS_TIMER_EXPIRED);
     176          52 : }
     177             : 
     178           2 : bool ArpEntry::IsResolved() {
     179           2 :     return (state_ & (ArpEntry::ACTIVE | ArpEntry::RERESOLVING));
     180             : }
     181             : 
     182          32 : bool ArpEntry::IsDerived() {
     183          32 :     if (key_.vrf != nh_vrf_) {
     184           0 :         return true;
     185             :     }
     186          32 :     return false;
     187             : }
     188             : 
     189          53 : void ArpEntry::StartTimer(uint32_t timeout, uint32_t mtype) {
     190          53 :     arp_timer_->Cancel();
     191         106 :     arp_timer_->Start(timeout, boost::bind(&ArpProto::TimerExpiry,
     192          53 :                                            handler_->agent()->GetArpProto(),
     193          53 :                                            key_, mtype, interface_.get()));
     194          53 : }
     195             : 
     196           1 : void ArpEntry::SendArpRequest() {
     197           1 :     assert(!IsDerived());
     198           1 :     Agent *agent = handler_->agent();
     199           1 :     ArpProto *arp_proto = agent->GetArpProto();
     200           1 :     uint32_t vrf_id = VrfEntry::kInvalidIndex;
     201           1 :     uint32_t intf_id = arp_proto->ip_fabric_interface_index();
     202           1 :     Ip4Address ip;
     203           1 :     MacAddress smac;
     204           1 :     if (interface_->type() == Interface::VM_INTERFACE) {
     205             :         const VmInterface *vmi =
     206           1 :             static_cast<const VmInterface *>(interface_.get());
     207           1 :         ip = vmi->GetServiceIp(Ip4Address(key_.ip)).to_v4();
     208           1 :         if (vmi->vmi_type() == VmInterface::VHOST) {
     209           1 :             ip = agent->router_id();
     210             :         }
     211           1 :         vrf_id = nh_vrf_->vrf_id();
     212           1 :         if (vmi->parent()) {
     213           1 :             intf_id = vmi->parent()->id();
     214             :         }
     215           1 :         smac = vmi->GetVifMac(agent);
     216             :     } else {
     217           0 :         if (agent->is_l3mh() == true) {
     218           0 :             intf_id = interface_->id();
     219           0 :             ip = agent->ip_fabric_intf_addr_list()[intf_id];
     220             :         } else {
     221           0 :             ip = agent->router_id();
     222             :         }
     223             :         VrfEntry *vrf =
     224           0 :             agent->vrf_table()->FindVrfFromName(agent->fabric_vrf_name());
     225           0 :         if (vrf) {
     226           0 :             vrf_id = vrf->vrf_id();
     227             :         }
     228           0 :         smac = interface_->mac();
     229             :     }
     230             : 
     231           1 :     if (vrf_id != VrfEntry::kInvalidIndex) {
     232           2 :         handler_->SendArp(ARPOP_REQUEST, smac, ip.to_ulong(),
     233           2 :                           MacAddress(), MacAddress::BroadcastMac(), key_.ip, intf_id, vrf_id);
     234             :     }
     235             : 
     236           1 :     StartTimer(arp_proto->retry_timeout(), ArpProto::RETRY_TIMER_EXPIRED);
     237           1 : }
     238             : 
     239           2 : void ArpEntry::AddArpRoute(bool resolved) {
     240           2 :     if (key_.vrf->GetName() == handler_->agent()->linklocal_vrf_name()) {
     241             :         // Do not squash existing route entry.
     242             :         // should be smarter and not replace an existing route.
     243           2 :         return;
     244             :     }
     245             : 
     246           2 :     Ip4Address ip(key_.ip);
     247           2 :     const string& vrf_name = key_.vrf->GetName();
     248           2 :     ArpNHKey nh_key(nh_vrf_->GetName(), ip, false);
     249           4 :     ArpNH *arp_nh = static_cast<ArpNH *>(handler_->agent()->nexthop_table()->
     250           2 :                                          FindActiveEntry(&nh_key));
     251             : 
     252           2 :     MacAddress mac = mac_address();
     253           2 :     if (arp_nh && arp_nh->GetResolveState() &&
     254           0 :         mac.CompareTo(arp_nh->GetMac()) == 0) {
     255             :         // MAC address unchanged, ignore
     256           0 :         if (!IsDerived()) {
     257           0 :             return;
     258             :         } else {
     259             :             /* Return if the route is already existing */
     260             :             InetUnicastRouteKey *rt_key = new InetUnicastRouteKey(
     261           0 :                     handler_->agent()->local_peer(), vrf_name, ip, 32);
     262           0 :             AgentRoute *entry = key_.vrf->GetInet4UnicastRouteTable()->
     263           0 :                 FindActiveEntry(rt_key);
     264           0 :             delete rt_key;
     265           0 :             if (entry) {
     266           0 :                 return;
     267             :             }
     268           0 :             resolved = true;
     269             :         }
     270             :     }
     271             : 
     272           2 :     ARP_TRACE(Trace, "Add", ip.to_string(), vrf_name, mac.ToString());
     273           2 :     AgentRoute *entry = key_.vrf->GetInet4UnicastRouteTable()->FindLPM(ip);
     274             : 
     275           2 :     bool policy = false;
     276           2 :     SecurityGroupList sg;
     277           2 :     TagList tag;
     278           2 :     VnListType vn_list;
     279             :     const NextHop *anh;
     280           2 :     if (entry && (anh = entry->GetActiveNextHop()) != NULL) {
     281           2 :         policy = anh->PolicyEnabled();
     282           2 :         sg = entry->GetActivePath()->sg_list();
     283           2 :         tag = entry->GetActivePath()->tag_list();
     284           2 :         vn_list = entry->GetActivePath()->dest_vn_list();
     285             :     }
     286             : 
     287           2 :     const Interface *itf = handler_->agent()->GetArpProto()->ip_fabric_interface();
     288           2 :     if (interface_->type() == Interface::PHYSICAL) {
     289           0 :         itf = interface_.get();
     290             :     }
     291           2 :     if (interface_->type() == Interface::VM_INTERFACE) {
     292             :         const VmInterface *vintf =
     293           2 :             static_cast<const VmInterface *>(interface_.get());
     294           2 :         if (vintf->vmi_type() == VmInterface::VHOST) {
     295           4 :             for (InterfaceList::size_type i = 0; i != vintf->parent_list().size(); i++) {
     296           2 :                 itf = vintf->parent_list()[i];
     297           4 :                 handler_->agent()->fabric_inet4_unicast_table()->ArpRoute(
     298             :                         DBRequest::DB_ENTRY_ADD_CHANGE, vrf_name, ip, mac,
     299           2 :                         nh_vrf_->GetName(), *itf, resolved, 32, policy,
     300             :                         vn_list, sg, tag);
     301             :             }
     302           2 :             return;
     303             :         }
     304             :     }
     305             : 
     306           0 :     handler_->agent()->fabric_inet4_unicast_table()->ArpRoute(
     307             :                        DBRequest::DB_ENTRY_ADD_CHANGE, vrf_name, ip, mac,
     308           0 :                        nh_vrf_->GetName(), *itf, resolved, 32, policy,
     309             :                        vn_list, sg, tag);
     310           8 : }
     311             : 
     312           1 : bool ArpEntry::DeleteArpRoute() {
     313           1 :     if (key_.vrf->GetName() == handler_->agent()->linklocal_vrf_name()) {
     314           0 :         return true;
     315             :     }
     316             : 
     317           1 :     Ip4Address ip(key_.ip);
     318           1 :     const string& vrf_name = key_.vrf->GetName();
     319           1 :     ArpNHKey nh_key(nh_vrf_->GetName(), ip, false);
     320           2 :     ArpNH *arp_nh = static_cast<ArpNH *>(handler_->agent()->nexthop_table()->
     321           1 :                                          FindActiveEntry(&nh_key));
     322           1 :     if (!arp_nh)
     323           0 :         return true;
     324             : 
     325           1 :     MacAddress mac = mac_address();
     326           1 :     ARP_TRACE(Trace, "Delete", ip.to_string(), vrf_name, mac.ToString());
     327           1 :     if (IsDerived()) {
     328             :         //Just enqueue a delete, no need to mark nexthop invalid
     329           0 :         InetUnicastAgentRouteTable::Delete(handler_->agent()->local_peer(),
     330             :                                            vrf_name, ip, 32);
     331           0 :         return true;
     332             :     }
     333             : 
     334           3 :     handler_->agent()->fabric_inet4_unicast_table()->ArpRoute(
     335           1 :                        DBRequest::DB_ENTRY_DELETE, vrf_name, ip, mac, nh_vrf_->GetName(),
     336           1 :                        *interface_, false, 32, false, Agent::NullStringList(),
     337           2 :                        SecurityGroupList(), TagList());
     338           1 :     return false;
     339           1 : }
     340             : 
     341           0 : void ArpEntry::Resync(bool policy, const VnListType &vnlist,
     342             :                       const SecurityGroupList &sg,
     343             :                       const TagList &tag) {
     344           0 :     Ip4Address ip(key_.ip);
     345           0 :     const string& vrf_name = key_.vrf->GetName();
     346           0 :     ARP_TRACE(Trace, "Resync", ip.to_string(), vrf_name,
     347             :               mac_address().ToString());
     348           0 :     handler_->agent()->fabric_inet4_unicast_table()->ArpRoute(
     349           0 :                        DBRequest::DB_ENTRY_ADD_CHANGE, key_.vrf->GetName(), ip,
     350           0 :                        mac_address_, nh_vrf_->GetName(), *interface_, IsResolved(),
     351             :                        32, policy, vnlist, sg, tag);
     352           0 : }

Generated by: LCOV version 1.14