LCOV - code coverage report
Current view: top level - vnsw/agent/services - igmp_proto.cc (source / functions) Hit Total Coverage
Test: OpenSDN C/C++ coverage (all TARGET_SET jobs) Lines: 65 175 37.1 %
Date: 2026-06-11 01:56:02 Functions: 6 12 50.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /*
       2             :  * Copyright (c) 2017 Juniper Networks, Inc. All rights reserved.
       3             :  */
       4             : 
       5             : #include "cmn/agent_cmn.h"
       6             : #include "init/agent_param.h"
       7             : #include "oper/vn.h"
       8             : #include "oper/route_common.h"
       9             : #include "oper/multicast.h"
      10             : #include "services/igmp_proto.h"
      11             : 
      12           1 : IgmpProto::IgmpProto(Agent *agent, boost::asio::io_context &io) :
      13             :     Proto(agent, "Agent::Services", PktHandler::IGMP, io),
      14           1 :     task_name_("Agent::Services"), io_(io) {
      15             : 
      16           1 :     IgmpProtoInit();
      17           1 : }
      18             : 
      19           2 : IgmpProto::~IgmpProto() {
      20           2 : }
      21             : 
      22           1 : void IgmpProto::IgmpProtoInit(void) {
      23             : 
      24             :     // limit the number of entries in the workqueue
      25           1 :     work_queue_.SetSize(agent_->params()->services_queue_limit());
      26           1 :     work_queue_.SetBounded(true);
      27             : 
      28           2 :     gmp_proto_ = GmpProtoManager::CreateGmpProto(GmpType::IGMP, agent_,
      29           1 :                                     task_name_, PktHandler::IGMP, io_);
      30           1 :     if (gmp_proto_) {
      31           1 :         gmp_proto_->Register(
      32             :                     boost::bind(&IgmpProto::SendIgmpPacket, this, _1, _2, _3));
      33           1 :         gmp_proto_->Start();
      34             :     }
      35             : 
      36           1 :     vn_listener_id_ = agent_->vn_table()->Register(
      37             :             boost::bind(&IgmpProto::VnNotify, this, _1, _2));
      38             : 
      39           1 :     ClearStats();
      40           1 : }
      41             : 
      42           1 : void IgmpProto::Shutdown() {
      43             : 
      44           1 :     agent_->vn_table()->Unregister(vn_listener_id_);
      45             : 
      46           1 :     if (gmp_proto_) {
      47           1 :         gmp_proto_->Stop();
      48           1 :         GmpProtoManager::DeleteGmpProto(gmp_proto_);
      49             :     }
      50           1 : }
      51             : 
      52           0 : ProtoHandler *IgmpProto::AllocProtoHandler(boost::shared_ptr<PktInfo> info,
      53             :                                            boost::asio::io_context &io) {
      54           0 :     return new IgmpHandler(agent(), info, io);
      55             : }
      56             : 
      57          19 : void IgmpProto::VnNotify(DBTablePartBase *part, DBEntryBase *entry) {
      58             : 
      59             :     // Registering/Unregistering every IPAM gateway (or) dns_server
      60             :     // present in the VN with the IGMP module.
      61             :     // Changes to VN, or VN IPAM info, or gateway or dns server is
      62             :     // handled below.
      63             : 
      64          19 :     VnEntry *vn = static_cast<VnEntry *>(entry);
      65          19 :     IgmpInfo::IgmpSubnetState *igmp_intf = NULL;
      66             : 
      67             :     IgmpInfo::VnIgmpDBState *state =
      68             :                         static_cast<IgmpInfo::VnIgmpDBState *>
      69          19 :                         (entry->GetState(part->parent(), vn_listener_id_));
      70             : 
      71          19 :     if (vn->IsDeleted() || !vn->GetVrf()) {
      72          16 :         if (!state) {
      73          10 :             return;
      74             :         }
      75             :         IgmpInfo::VnIgmpDBState::IgmpSubnetStateMap::iterator it =
      76           6 :                             state->igmp_state_map_.begin();
      77           9 :         for (;it != state->igmp_state_map_.end(); ++it) {
      78           3 :             igmp_intf = it->second;
      79           3 :             delete igmp_intf;
      80             :         }
      81           6 :         state->igmp_state_map_.clear();
      82             : 
      83           6 :         if (vn->IsDeleted()) {
      84           3 :             entry->ClearState(part->parent(), vn_listener_id_);
      85           3 :             delete state;
      86             :         }
      87           6 :         return;
      88             :     }
      89             : 
      90           3 :     if (!vn->GetVrf()) {
      91           0 :         return;
      92             :     }
      93             : 
      94           6 :     if ((vn->GetVrf()->GetName() == agent_->fabric_policy_vrf_name()) ||
      95           3 :         (vn->GetVrf()->GetName() == agent_->fabric_vrf_name())) {
      96           0 :         return;
      97             :     }
      98             : 
      99           3 :     if (state == NULL) {
     100           3 :         state = new IgmpInfo::VnIgmpDBState();
     101             : 
     102           3 :         entry->SetState(part->parent(), vn_listener_id_, state);
     103             :     }
     104             : 
     105             :     IgmpInfo::VnIgmpDBState::IgmpSubnetStateMap::iterator it =
     106           3 :                             state->igmp_state_map_.begin();
     107           3 :     while (it != state->igmp_state_map_.end()) {
     108           0 :         const VnIpam *ipam = vn->GetIpam(it->first);
     109           0 :         if ((ipam != NULL) && ((ipam->default_gw == it->first) ||
     110           0 :                 (ipam->dns_server == it->first))) {
     111           0 :             it++;
     112           0 :             continue;
     113             :         }
     114           0 :         igmp_intf = it->second;
     115           0 :         delete igmp_intf;
     116           0 :         state->igmp_state_map_.erase(it++);
     117             :     }
     118             : 
     119           3 :     const std::vector<VnIpam> &ipam = vn->GetVnIpam();
     120           6 :     for (unsigned int i = 0; i < ipam.size(); ++i) {
     121           3 :         if (!ipam[i].IsV4()) {
     122           0 :             continue;
     123             :         }
     124           3 :         if ((ipam[i].default_gw == IpAddress(Ip4Address())) &&
     125           3 :             (ipam[i].dns_server == IpAddress(Ip4Address()))) {
     126           0 :             continue;
     127             :         }
     128             : 
     129           3 :         IpAddress igmp_address = IpAddress(Ip4Address());
     130           3 :         IgmpInfo::VnIgmpDBState::IgmpSubnetStateMap::const_iterator it;
     131             : 
     132           3 :         if (ipam[i].dns_server != IpAddress(Ip4Address())) {
     133           3 :             it = state->igmp_state_map_.find(ipam[i].dns_server);
     134           3 :             igmp_address = ipam[i].dns_server;
     135             :         }
     136           3 :         if (ipam[i].default_gw != IpAddress(Ip4Address())) {
     137           3 :             if (it != state->igmp_state_map_.end()) {
     138           0 :                 igmp_intf = it->second;
     139           0 :                 delete igmp_intf;
     140           0 :                 state->igmp_state_map_.erase(it->first);
     141             :             }
     142             : 
     143           3 :             igmp_address = ipam[i].default_gw;
     144             :         }
     145             : 
     146           3 :         it = state->igmp_state_map_.find(igmp_address);
     147           3 :         if (it == state->igmp_state_map_.end()) {
     148           3 :             igmp_intf = new IgmpInfo::IgmpSubnetState;
     149           3 :             state->igmp_state_map_.insert(
     150           6 :                             std::pair<IpAddress, IgmpInfo::IgmpSubnetState*>
     151             :                             (igmp_address, igmp_intf));
     152             :         }
     153             :     }
     154             : }
     155             : 
     156           0 : DBTableBase::ListenerId IgmpProto::vn_listener_id () {
     157           0 :     return vn_listener_id_;
     158             : }
     159             : 
     160             : // Send IGMP packets to the VMs part of the IPAM VN
     161           0 : bool IgmpProto::SendIgmpPacket(const VrfEntry *vrf, IpAddress gmp_addr,
     162             :                             GmpPacket *packet) {
     163             : 
     164           0 :     if (!vrf || !packet) {
     165           0 :         return false;
     166             :     }
     167             : 
     168           0 :     if (!gmp_addr.is_v4()) {
     169           0 :         return false;
     170             :     }
     171             : 
     172           0 :     VnEntry *vn = vrf->vn();
     173           0 :     if (!vn) {
     174           0 :         return false;
     175             :     }
     176             : 
     177           0 :     const VnIpam *ipam = vn->GetIpam(gmp_addr);
     178           0 :     if (!ipam) {
     179           0 :         return false;
     180             :     }
     181             : 
     182           0 :     Ip4Address subnet = (ipam->GetSubnetAddress()).to_v4();
     183             :     InetUnicastAgentRouteTable *inet_table =
     184           0 :                                     vrf->GetInet4UnicastRouteTable();
     185           0 :     const InetUnicastRouteEntry *rt = inet_table->FindRoute(subnet);
     186           0 :     if (!rt) {
     187           0 :         return false;
     188             :     }
     189             : 
     190             :     boost::shared_ptr<PktInfo> pkt(new PktInfo(agent_, 1024, PktHandler::IGMP,
     191           0 :                                     0));
     192             :     IgmpHandler igmp_handler(agent_, pkt,
     193           0 :                                     *(agent_->event_manager()->io_service()));
     194             : 
     195             :     do {
     196           0 :         const NextHop *nh = rt->GetActiveNextHop();
     197           0 :         if (!nh || nh->IsDeleted()) {
     198           0 :             continue;
     199             :         }
     200           0 :         const InterfaceNH *inh = dynamic_cast<const InterfaceNH *>(nh);
     201           0 :         if (!inh) {
     202           0 :             continue;
     203             :         }
     204           0 :         const Interface *itf = inh->GetInterface();
     205           0 :         if (!itf) {
     206           0 :             continue;
     207             :         }
     208           0 :         const VmInterface *vm_itf = dynamic_cast<const VmInterface *>(itf);
     209           0 :         if (!vm_itf || vm_itf->IsDeleted()) {
     210           0 :             continue;
     211             :         }
     212           0 :         if (vm_itf->vmi_type() == VmInterface::VHOST) {
     213           0 :             continue;
     214             :         }
     215           0 :         if (vm_itf->vrf() && vrf->GetName() != vm_itf->vrf()->GetName()) {
     216           0 :             continue;
     217             :         }
     218           0 :         if (!ipam->IsSubnetMember(IpAddress(vm_itf->primary_ip_addr()))) {
     219           0 :             break;
     220             :         }
     221           0 :         if (!vm_itf->igmp_enabled()) {
     222           0 :             IncrSendStats(vm_itf, false);
     223           0 :             continue;
     224             :         }
     225             : 
     226           0 :         igmp_handler.SendPacket(vm_itf, vrf, gmp_addr, packet);
     227             : 
     228           0 :     } while ((rt = inet_table->GetNext(rt)) != NULL);
     229             : 
     230           0 :     return true;
     231           0 : }
     232             : 
     233           0 : void IgmpProto::IncrSendStats(const VmInterface *vm_itf, bool tx_done) {
     234             : 
     235           0 :     const VnEntry *vn = vm_itf->vn();
     236           0 :     IgmpInfo::VnIgmpDBState *state = NULL;
     237           0 :     state = static_cast<IgmpInfo::VnIgmpDBState *>(vn->GetState(
     238             :                                 vn->get_table_partition()->parent(),
     239             :                                 vn_listener_id()));
     240           0 :     if (!state) {
     241           0 :         return;
     242             :     }
     243           0 :     const VnIpam *ipam = vn->GetIpam(vm_itf->primary_ip_addr());
     244           0 :     if (!ipam) {
     245           0 :         return;
     246             :     }
     247             :     IgmpInfo::VnIgmpDBState::IgmpSubnetStateMap::const_iterator it =
     248           0 :                             state->igmp_state_map_.find(ipam->default_gw);
     249           0 :     if (it == state->igmp_state_map_.end()) {
     250           0 :         return;
     251             :     }
     252             : 
     253           0 :     IgmpInfo::IgmpSubnetState *igmp_intf = NULL;
     254           0 :     igmp_intf = it->second;
     255             : 
     256           0 :     if (tx_done) {
     257           0 :         igmp_intf->IncrTxPkt();
     258             :     } else {
     259           0 :         igmp_intf->IncrTxDropPkt();
     260             :     }
     261             : 
     262           0 :     return;
     263             : }
     264             : 
     265           0 : const bool IgmpProto::GetItfStats(const VnEntry *vn, IpAddress gateway,
     266             :                             IgmpInfo::IgmpItfStats &stats) {
     267             : 
     268           0 :     const VnIpam *ipam = vn->GetIpam(gateway);
     269           0 :     if (!ipam) {
     270           0 :         return false;
     271             :     }
     272             : 
     273           0 :     IgmpInfo::VnIgmpDBState *state = NULL;
     274           0 :     state = static_cast<IgmpInfo::VnIgmpDBState *>(vn->GetState(
     275             :                             vn->get_table_partition()->parent(), vn_listener_id_));
     276           0 :     if (!state) {
     277           0 :         return false;
     278             :     }
     279             : 
     280             :     IgmpInfo::VnIgmpDBState::IgmpSubnetStateMap::const_iterator it =
     281           0 :                             state->igmp_state_map_.find(ipam->default_gw);
     282           0 :     if (it == state->igmp_state_map_.end()) {
     283           0 :         return false;
     284             :     }
     285             : 
     286           0 :     IgmpInfo::IgmpSubnetState *igmp_intf = it->second;
     287             : 
     288           0 :     stats = igmp_intf->GetItfStats();
     289             : 
     290           0 :     return true;
     291             : }
     292             : 
     293           0 : void IgmpProto::ClearItfStats(const VnEntry *vn, IpAddress gateway) {
     294             : 
     295           0 :     if (!vn) {
     296           0 :         return;
     297             :     }
     298             : 
     299           0 :     const VnIpam *ipam = vn->GetIpam(gateway);
     300           0 :     if (!ipam) {
     301           0 :         return;
     302             :     }
     303             : 
     304           0 :     IgmpInfo::VnIgmpDBState *state = NULL;
     305           0 :     state = static_cast<IgmpInfo::VnIgmpDBState *>(vn->GetState(
     306             :                         vn->get_table_partition()->parent(), vn_listener_id_));
     307           0 :     if (!state) {
     308           0 :         return;
     309             :     }
     310             : 
     311             :     IgmpInfo::VnIgmpDBState::IgmpSubnetStateMap::const_iterator it =
     312           0 :                             state->igmp_state_map_.find(ipam->default_gw);
     313           0 :     if (it == state->igmp_state_map_.end()) {
     314           0 :         return;
     315             :     }
     316             : 
     317           0 :     IgmpInfo::IgmpSubnetState *igmp_intf = it->second;
     318           0 :     igmp_intf->ClearItfStats();
     319             : 
     320           0 :     return;
     321             : }

Generated by: LCOV version 1.14