LCOV - code coverage report
Current view: top level - vnsw/agent/services - arp_handler.cc (source / functions) Hit Total Coverage
Test: OpenSDN C/C++ coverage (all TARGET_SET jobs) Lines: 102 274 37.2 %
Date: 2026-06-04 02:06:09 Functions: 11 13 84.6 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /*
       2             :  * Copyright (c) 2013 Juniper Networks, Inc. All rights reserved.
       3             :  */
       4             : 
       5             : #include <stdint.h>
       6             : #include <math.h>
       7             : #include "vr_defs.h"
       8             : #include "init/agent_param.h"
       9             : #include "oper/route_common.h"
      10             : #include "oper/operdb_init.h"
      11             : #include "oper/path_preference.h"
      12             : #include "pkt/pkt_init.h"
      13             : #include "services/arp_proto.h"
      14             : #include "services/services_init.h"
      15             : #include "services/services_sandesh.h"
      16             : #include "mac_learning/mac_learning_proto.h"
      17             : 
      18         506 : ArpHandler::ArpHandler(Agent *agent, boost::shared_ptr<PktInfo> info,
      19         506 :                        boost::asio::io_context &io)
      20         506 :     : ProtoHandler(agent, info, io), arp_(NULL), arp_tpa_(0) {
      21         506 :         refcount_ = 0;
      22         506 : }
      23             : 
      24         646 : ArpHandler::~ArpHandler() {
      25         646 : }
      26             : 
      27         140 : bool ArpHandler::Run() {
      28             : 
      29             :     // Process ARP only when the IP Fabric interface is configured
      30             : 
      31         140 :     assert(agent());
      32         140 :     assert(agent()->GetArpProto());
      33         140 :     ArpProto *arp_proto = agent()->GetArpProto();
      34             : 
      35         140 :     if (agent()->GetArpProto()->ip_fabric_interface() == NULL) {
      36           0 :         arp_proto->IncrementStatsIPFabricNotInst();
      37           0 :         delete pkt_info_->ipc;
      38           0 :         return true;
      39             :     }
      40             : 
      41         140 :     switch(pkt_info_->type) {
      42         140 :         case PktType::MESSAGE:
      43         140 :             return HandleMessage();
      44             : 
      45           0 :         default:
      46           0 :             return HandlePacket();
      47             :     }
      48             : }
      49             : 
      50           0 : bool ArpHandler::HandlePacket() {
      51           0 :     ArpProto *arp_proto = agent()->GetArpProto();
      52             :     uint16_t arp_cmd;
      53           0 :     if (pkt_info_->ip) {
      54           0 :         arp_tpa_ = ntohl(pkt_info_->ip->ip_dst.s_addr);
      55           0 :         arp_cmd = ARPOP_REQUEST;
      56           0 :     } else if (pkt_info_->arp) {
      57           0 :         arp_ = pkt_info_->arp;
      58           0 :         if ((ntohs(arp_->ea_hdr.ar_hrd) != ARPHRD_ETHER) ||
      59           0 :             (ntohs(arp_->ea_hdr.ar_pro) != ETHERTYPE_IP) ||
      60           0 :             (arp_->ea_hdr.ar_hln != ETHER_ADDR_LEN) ||
      61           0 :             (arp_->ea_hdr.ar_pln != IPv4_ALEN)) {
      62           0 :             arp_proto->IncrementStatsInvalidPackets();
      63           0 :             ARP_TRACE(Error, "Received Invalid ARP packet");
      64           0 :             return true;
      65             :         }
      66           0 :         arp_cmd = ntohs(arp_->ea_hdr.ar_op);
      67             :         union {
      68             :             uint8_t data[sizeof(in_addr_t)];
      69             :             in_addr_t addr;
      70             :         } bytes;
      71           0 :         memcpy(bytes.data, arp_->arp_tpa, sizeof(in_addr_t));
      72           0 :         in_addr_t tpa = ntohl(bytes.addr);
      73           0 :         memcpy(bytes.data, arp_->arp_spa, sizeof(in_addr_t));
      74           0 :         in_addr_t spa = ntohl(bytes.addr);
      75           0 :         if (arp_cmd == ARPOP_REQUEST)
      76           0 :             arp_tpa_ = tpa;
      77             :         else
      78           0 :             arp_tpa_ = spa;
      79             : 
      80             :         // if it is our own, ignore
      81           0 :         if (arp_tpa_ == agent()->router_id().to_ulong()) {
      82           0 :             arp_proto->IncrementStatsGratuitous();
      83           0 :             return true;
      84             :         }
      85             : 
      86           0 :         if (tpa == spa || spa == 0) {
      87           0 :             arp_cmd = GRATUITOUS_ARP;
      88             :         }
      89             :     } else {
      90           0 :         arp_proto->IncrementStatsInvalidPackets();
      91           0 :         ARP_TRACE(Error, "ARP : Received Invalid packet");
      92           0 :         return true;
      93             :     }
      94             : 
      95             :     const Interface *itf =
      96           0 :         agent()->interface_table()->FindInterface(GetInterfaceIndex());
      97           0 :     if (!itf || !itf->IsActive()) {
      98           0 :         arp_proto->IncrementStatsInvalidInterface();
      99           0 :         ARP_TRACE(Error, "Received ARP packet from invalid / inactive interface");
     100           0 :         return true;
     101             :     }
     102             : 
     103           0 :     const VrfEntry *vrf = agent()->vrf_table()->FindVrfFromId(pkt_info_->vrf);
     104           0 :     if (!vrf || !vrf->IsActive()) {
     105           0 :         arp_proto->IncrementStatsInvalidVrf();
     106           0 :         ARP_TRACE(Error, "ARP : AgentHdr " + itf->name() +
     107             :                          " has no / inactive VRF ");
     108           0 :         return true;
     109             :     }
     110           0 :     const VrfEntry *nh_vrf = itf->vrf();
     111           0 :     if (!nh_vrf || !nh_vrf->IsActive()) {
     112           0 :         arp_proto->IncrementStatsInvalidVrf();
     113           0 :         ARP_TRACE(Error, "ARP : Interface " + itf->name() +
     114             :                          " has no / inactive VRF");
     115           0 :         return true;
     116             :     }
     117             : 
     118             :     // if broadcast ip, return
     119           0 :     Ip4Address arp_addr(arp_tpa_);
     120           0 :     if (arp_tpa_ == 0xFFFFFFFF || !arp_tpa_) {
     121           0 :         arp_proto->IncrementStatsInvalidAddress();
     122           0 :         ARP_TRACE(Error, "ARP : ignoring broadcast address" +
     123             :                   arp_addr.to_string());
     124           0 :         return true;
     125             :     }
     126             : 
     127             :     //Look for subnet broadcast
     128             :     AgentRoute *route =
     129             :         static_cast<InetUnicastAgentRouteTable *>(vrf->
     130           0 :             GetInet4UnicastRouteTable())->FindLPM(arp_addr);
     131           0 :     if (route) {
     132           0 :         const NextHop *anh = route->GetActiveNextHop();
     133           0 :         if (route->is_multicast()) {
     134           0 :             arp_proto->IncrementStatsInvalidAddress();
     135           0 :             ARP_TRACE(Error, "ARP : ignoring multicast address" +
     136             :                       arp_addr.to_string());
     137           0 :             return true;
     138             :         }
     139           0 :         if (anh == NULL) {
     140           0 :             arp_proto->IncrementStatsInvalidAddress();
     141           0 :             ARP_TRACE(Error, "ARP : no active nexthop" +
     142             :                       arp_addr.to_string());
     143           0 :             return true;
     144             :         }
     145           0 :         if(anh->GetType() == NextHop::RESOLVE) {
     146           0 :             const ResolveNH *nh =
     147             :                 static_cast<const ResolveNH *>(anh);
     148           0 :             itf = nh->get_interface();
     149           0 :             nh_vrf = itf->vrf();
     150             :         }
     151             :     }
     152             : 
     153           0 :     ArpKey key(arp_tpa_, vrf);
     154           0 :     ArpEntry *entry = arp_proto->FindArpEntry(key);
     155             : 
     156           0 :     if (nh_vrf->forwarding_vrf()) {
     157           0 :         nh_vrf = nh_vrf->forwarding_vrf();
     158             :     }
     159             : 
     160           0 :     switch (arp_cmd) {
     161           0 :         case ARPOP_REQUEST: {
     162           0 :             arp_proto->IncrementStatsArpReq();
     163           0 :             arp_proto->IncrementStatsArpRequest(itf->id());
     164           0 :             if (entry) {
     165           0 :                 entry->HandleArpRequest();
     166           0 :                 return true;
     167             :             } else {
     168           0 :                 entry = new ArpEntry(io_, this, key, nh_vrf, ArpEntry::INITING,
     169           0 :                                      itf);
     170           0 :                 if (arp_proto->AddArpEntry(entry) == false) {
     171           0 :                     delete entry;
     172           0 :                     return true;
     173             :                 }
     174           0 :                 entry->HandleArpRequest();
     175           0 :                 return false;
     176             :             }
     177             :         }
     178             : 
     179           0 :         case ARPOP_REPLY:  {
     180           0 :             arp_proto->IncrementStatsArpReplies();
     181           0 :             arp_proto->IncrementStatsArpReply(itf->id());
     182           0 :             if (itf->type() == Interface::VM_INTERFACE) {
     183             :                 uint32_t ip;
     184           0 :                 memcpy(&ip, arp_->arp_spa, sizeof(ip));
     185           0 :                 ip = ntohl(ip);
     186             :                 /* Enqueue a request to trigger state machine. The prefix-len
     187             :                  * of 32 passed below is not used. We do LPMFind on IP to
     188             :                  * figure out the actual prefix-len inside
     189             :                  * EnqueueTrafficSeen
     190             :                  */
     191             :                 agent()->oper_db()->route_preference_module()->
     192           0 :                     EnqueueTrafficSeen(Ip4Address(ip), 32, itf->id(),
     193             :                                        vrf->vrf_id(),
     194           0 :                                        MacAddress(arp_->arp_sha));
     195           0 :                 arp_proto->HandlePathPreferenceArpReply(vrf, itf->id(),
     196           0 :                                                         Ip4Address(ip));
     197             : 
     198           0 :                 if(entry) {
     199           0 :                     entry->HandleArpReply(MacAddress(arp_->arp_sha));
     200             :                 }
     201           0 :                 return true;
     202             :             }
     203           0 :             if(entry) {
     204           0 :                 entry->HandleArpReply(MacAddress(arp_->arp_sha));
     205           0 :                 return true;
     206             :             } else {
     207           0 :                 entry = new ArpEntry(io_, this, key, nh_vrf, ArpEntry::INITING,
     208           0 :                                      itf);
     209           0 :                 if (arp_proto->AddArpEntry(entry) == false) {
     210           0 :                     delete entry;
     211           0 :                     return true;
     212             :                 }
     213           0 :                 entry->HandleArpReply(MacAddress(arp_->arp_sha));
     214           0 :                 arp_ = NULL;
     215           0 :                 return false;
     216             :             }
     217             :         }
     218             : 
     219           0 :         case GRATUITOUS_ARP: {
     220           0 :             arp_proto->IncrementStatsGratuitous();
     221           0 :             if (itf->type() == Interface::VM_INTERFACE) {
     222             :                 uint32_t ip;
     223           0 :                 memcpy(&ip, arp_->arp_spa, sizeof(ip));
     224           0 :                 ip = ntohl(ip);
     225             :                 //Enqueue a request to trigger state machine
     226             :                 agent()->oper_db()->route_preference_module()->
     227           0 :                     EnqueueTrafficSeen(Ip4Address(ip), 32, itf->id(),
     228             :                                        vrf->vrf_id(),
     229           0 :                                        MacAddress(arp_->arp_sha));
     230           0 :                 return true;
     231           0 :             } else if (entry) {
     232           0 :                 entry->HandleArpReply(MacAddress(arp_->arp_sha));
     233           0 :                 return true;
     234             :             } else {
     235             :                 // ignore gratuitous ARP when entry is not present in cache
     236           0 :                 return true;
     237             :             }
     238             :         }
     239             : 
     240           0 :         default:
     241           0 :             ARP_TRACE(Error, "Received Invalid ARP command : " +
     242             :                       integerToString(arp_cmd));
     243           0 :             return true;
     244             :     }
     245             : }
     246             : 
     247             : /* This API is invoked from the following paths
     248             :    - NextHop notification for ARP_NH
     249             :    - ARP Timer expiry
     250             :    - Sending Gratituous ARP for Receive Nexthops
     251             :    In all these above paths we expect route_vrf and nh_vrf for ArpRoute to
     252             :    be same
     253             :    */
     254         140 : bool ArpHandler::HandleMessage() {
     255         140 :     bool ret = true;
     256         140 :     ArpProto::ArpIpc *ipc = static_cast<ArpProto::ArpIpc *>(pkt_info_->ipc);
     257         140 :     ArpProto *arp_proto = agent()->GetArpProto();
     258         140 :     switch(pkt_info_->ipc->cmd) {
     259           4 :         case ArpProto::ARP_RESOLVE: {
     260           4 :             ArpEntry *entry = arp_proto->FindArpEntry(ipc->key);
     261           4 :             if (!entry) {
     262           4 :                 entry = new ArpEntry(io_, this, ipc->key, ipc->key.vrf,
     263           2 :                                      ArpEntry::INITING, ipc->interface_.get());
     264           2 :                 if (arp_proto->AddArpEntry(entry) == false) {
     265           0 :                     delete entry;
     266           0 :                     break;
     267             :                 }
     268           2 :                 ret = false;
     269             :             }
     270           4 :             arp_proto->IncrementStatsArpReq();
     271           4 :             arp_proto->IncrementStatsArpRequest(ipc->interface_->id());
     272           4 :             entry->HandleArpRequest();
     273           4 :             break;
     274             :         }
     275             : 
     276         134 :         case ArpProto::ARP_SEND_GRATUITOUS: {
     277         134 :             bool key_valid = false;
     278             :             ArpProto::GratuitousArpIterator it =
     279         134 :             arp_proto->GratuitousArpEntryIterator(ipc->key, &key_valid);
     280         134 :             if (key_valid && !ipc->interface_->IsDeleted()) {
     281         109 :                 ArpEntry *entry = NULL;
     282         109 :                 ArpProto::ArpEntrySet::iterator sit = it->second.begin();
     283         110 :                 for (; sit != it->second.end(); sit++) {
     284          83 :                     entry = *sit;
     285          83 :                     if (entry->get_interface() == ipc->interface_.get())
     286          82 :                         break;
     287             :                 }
     288         109 :                 if (sit == it->second.end()) {
     289          54 :                     entry = new ArpEntry(io_, this, ipc->key, ipc->key.vrf,
     290          27 :                                          ArpEntry::ACTIVE, ipc->interface_.get());
     291          27 :                     it->second.insert(entry);
     292          27 :                     ret = false;
     293             :                 }
     294         109 :                 if (entry)
     295         109 :                     entry->SendGratuitousArp();
     296         109 :                 break;
     297             :             }
     298             :         }
     299             : 
     300             :         case ArpProto::ARP_DELETE: {
     301          27 :             EntryDelete(ipc->key);
     302          27 :             break;
     303             :         }
     304             : 
     305           0 :         case ArpProto::RETRY_TIMER_EXPIRED: {
     306           0 :             ArpEntry *entry = arp_proto->FindArpEntry(ipc->key);
     307           0 :             if (entry && !entry->RetryExpiry()) {
     308           0 :                 arp_proto->DeleteArpEntry(entry);
     309             :             }
     310           0 :             break;
     311             :         }
     312             : 
     313           0 :         case ArpProto::AGING_TIMER_EXPIRED: {
     314           0 :             ArpEntry *entry = arp_proto->FindArpEntry(ipc->key);
     315           0 :             if (entry && !entry->AgingExpiry()) {
     316           0 :                 arp_proto->DeleteArpEntry(entry);
     317             :             }
     318           0 :             break;
     319             :         }
     320             : 
     321           0 :         case ArpProto::GRATUITOUS_TIMER_EXPIRED: {
     322             :             ArpEntry *entry =
     323           0 :                 arp_proto->GratuitousArpEntry(ipc->key, ipc->interface_.get());
     324           0 :             if (entry && entry->retry_count() <= ArpProto::kGratRetries) {
     325           0 :                 entry->SendGratuitousArp();
     326             :             } else {
     327             :                 // Need to validate deleting the Arp entry upon fabric vrf Delete only
     328           0 :                 if (ipc->key.vrf->GetName() != agent()->fabric_vrf_name()) {
     329           0 :                     arp_proto->DeleteGratuitousArpEntry(entry);
     330             :                 }
     331             :             }
     332           0 :             break;
     333             :         }
     334             : 
     335           0 :         default:
     336           0 :             ARP_TRACE(Error, "Received Invalid internal ARP message : " +
     337             :                       integerToString(pkt_info_->ipc->cmd));
     338           0 :             break;
     339             :     }
     340         140 :     delete ipc;
     341         140 :     return ret;
     342             : }
     343             : 
     344          27 : void ArpHandler::EntryDelete(ArpKey &key) {
     345          27 :     ArpProto *arp_proto = agent()->GetArpProto();
     346          27 :     ArpEntry *entry = arp_proto->FindArpEntry(key);
     347          27 :     if (entry) {
     348           2 :         arp_proto->DeleteArpEntry(entry);
     349             :         // this request comes when ARP NH is deleted; nothing more to do
     350             :     }
     351          27 : }
     352             : 
     353         217 : uint16_t ArpHandler::ArpHdr(const MacAddress &smac, in_addr_t sip,
     354             :          const MacAddress &tmac, in_addr_t tip, uint16_t op) {
     355         217 :     arp_->ea_hdr.ar_hrd = htons(ARPHRD_ETHER);
     356         217 :     arp_->ea_hdr.ar_pro = htons(0x800);
     357         217 :     arp_->ea_hdr.ar_hln = ETHER_ADDR_LEN;
     358         217 :     arp_->ea_hdr.ar_pln = IPv4_ALEN;
     359         217 :     arp_->ea_hdr.ar_op = htons(op);
     360         217 :     smac.ToArray(arp_->arp_sha, sizeof(arp_->arp_sha));
     361         217 :     sip = htonl(sip);
     362         217 :     memcpy(arp_->arp_spa, &sip, sizeof(in_addr_t));
     363         217 :     tmac.ToArray(arp_->arp_tha, sizeof(arp_->arp_tha));
     364         217 :     tip = htonl(tip);
     365         217 :     memcpy(arp_->arp_tpa, &tip, sizeof(in_addr_t));
     366         217 :     return sizeof(ether_arp);
     367             : }
     368             : 
     369         217 : void ArpHandler::SendArp(uint16_t op, const MacAddress &smac, in_addr_t sip,
     370             :                          const MacAddress &tmac, const MacAddress &dmac,
     371             :                          in_addr_t tip, uint32_t itf, uint32_t vrf) {
     372             : 
     373         217 :     if (pkt_info_->packet_buffer() == NULL) {
     374          19 :         pkt_info_->AllocPacketBuffer(agent(), PktHandler::ARP, ARP_TX_BUFF_LEN,
     375             :                                      0);
     376             :     }
     377             : 
     378         217 :     char *buf = (char *)pkt_info_->packet_buffer()->data();
     379         217 :     memset(buf, 0, pkt_info_->packet_buffer()->data_len());
     380         217 :     pkt_info_->eth = (struct ether_header *)buf;
     381         217 :     int l2_len = EthHdr(buf, pkt_info_->packet_buffer()->data_len(),
     382             :                         itf, smac, dmac, ETHERTYPE_ARP);
     383         217 :     arp_ = pkt_info_->arp = (ether_arp *) (buf + l2_len);
     384         217 :     arp_tpa_ = tip;
     385             : 
     386         217 :     ArpHdr(smac, sip, tmac, tip, op);
     387         217 :     pkt_info_->set_len(l2_len + sizeof(ether_arp));
     388             : 
     389         217 :     Send(itf, vrf, AgentHdr::TX_SWITCH, PktHandler::ARP);
     390         217 : }
     391             : 
     392         189 : void ArpHandler::SendArpRequestByPlen(const VmInterface *vm_interface, const MacAddress &smac,
     393             :                                       const ArpPathPreferenceState *data,
     394             :                                       const Ip4Address &tpa) {
     395         189 :     Ip4Address service_ip = vm_interface->GetServiceIp(data->ip()).to_v4();
     396         189 :     bool aap_ip = vm_interface->MatchAapIp(data->ip(), data->plen());
     397             : #if 0
     398             :     MacAddress mac = agent()->mac_learning_proto()->
     399             :                         GetMacIpLearningTable()->GetPairedMacAddress(
     400             :                                 vm_interface->vrf()->vrf_id(),
     401             :                                 data->ip());
     402             : #endif
     403         189 :     MacAddress mac = data->mac();
     404             : 
     405         189 :     if (mac == MacAddress()) {
     406          24 :         mac = vm_interface->vm_mac();
     407             :     }
     408             : 
     409         189 :     if (data->plen() == Address::kMaxV4PrefixLen) {
     410         378 :         SendArp(ARPOP_REQUEST, smac, service_ip.to_ulong(), MacAddress(),
     411           0 :                 ((aap_ip) ? MacAddress::BroadcastMac(): mac),
     412         189 :                 data->ip().to_v4().to_ulong(), vm_interface->id(), data->vrf_id());
     413         189 :         agent()->GetArpProto()->IncrementStatsVmArpReq();
     414             :     } else {
     415           0 :        if (!tpa.is_unspecified()) {
     416           0 :             SendArp(ARPOP_REQUEST, smac, service_ip.to_ulong(),
     417           0 :                     MacAddress(), ((aap_ip) ? MacAddress::BroadcastMac(): mac),
     418           0 :                     tpa.to_ulong(), vm_interface->id(), data->vrf_id());
     419           0 :             agent()->GetArpProto()->IncrementStatsVmArpReq();
     420           0 :             return;
     421             :         }
     422             :         /* Loop through all the IPs for the prefix-len and send Arp for each
     423             :          * IP*/
     424           0 :         uint8_t diff_plen = Address::kMaxV4PrefixLen - data->plen();
     425           0 :         uint32_t num_addresses = pow(2, diff_plen);
     426           0 :         const uint32_t &max_addresses = MaxArpProbeAddresses();
     427           0 :         if (num_addresses > max_addresses) {
     428           0 :             num_addresses = max_addresses;
     429             :             /* When min_aap_prefix_len (configured by user in agent config file)
     430             :              * we need to visit all addresses specified by user, because
     431             :              * base address in this case will be formed by prefix-len lower
     432             :              * than min_aap_prefix_len. This prefix len is determined by
     433             :              * data->plen(). The loop below which goes through all the
     434             :              * addresses will visit 2 addresses less after discounting base
     435             :              * address and broadcast address. Because base address in this case
     436             :              * is formed by prefix-len lower than min_aap_prefix_len, we should
     437             :              * not discount the two addresses. Hence increment by 2.
     438             :              */
     439           0 :             num_addresses += 2;
     440             :         }
     441           0 :         uint32_t base_addr = data->ip().to_v4().to_ulong();
     442           0 :         for (uint32_t i = 1; i < num_addresses; ++i) {
     443           0 :             uint32_t addr = base_addr + i;
     444           0 :             SendArp(ARPOP_REQUEST, smac, service_ip.to_ulong(),
     445           0 :                     MacAddress(), ((aap_ip) ? MacAddress::BroadcastMac(): mac),
     446             :                     addr, vm_interface->id(), data->vrf_id());
     447           0 :             agent()->GetArpProto()->IncrementStatsVmArpReq();
     448             :         }
     449             :     }
     450             : }
     451             : 
     452           0 : uint32_t ArpHandler::MaxArpProbeAddresses() const {
     453           0 :     uint32_t diff_plen = Address::kMaxV4PrefixLen -
     454           0 :                          agent()->params()->min_aap_prefix_len();
     455           0 :     return pow(2, diff_plen);
     456             : }
     457             : 
     458          29 : void intrusive_ptr_add_ref(const ArpHandler *p) {
     459          29 :     p->refcount_++;
     460          29 : }
     461             : 
     462          29 : void intrusive_ptr_release(const ArpHandler *p) {
     463          58 :     if (p->refcount_.fetch_sub(1) == 1) {
     464          29 :         delete p;
     465             :     }
     466          29 : }

Generated by: LCOV version 1.14