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: 124 175 70.9 %
Date: 2026-11-09 02:10:49 Functions: 9 12 75.0 %
Legend: Lines: hit not hit

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

Generated by: LCOV version 1.14