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

Generated by: LCOV version 1.14