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/route_common.h"
8 : #include "oper/multicast.h"
9 : #include "oper/multicast_policy.h"
10 : #include "oper/nexthop.h"
11 :
12 : #ifdef __cplusplus
13 : extern "C" {
14 : #endif
15 : #include "mcast_common.h"
16 : #include "gmpx_basic_types.h"
17 : #include "gmp.h"
18 : #include "gmpx_environment.h"
19 : #include "gmp_intf.h"
20 : #include "gmp_map.h"
21 : #ifdef __cplusplus
22 : }
23 : #endif
24 :
25 : #include "task_map.h"
26 : #include "gmp_proto.h"
27 :
28 : extern SandeshTraceBufferPtr MulticastTraceBuf;
29 :
30 3 : GmpIntf::GmpIntf(const GmpProto *gmp_proto) : gmp_proto_(gmp_proto),
31 3 : vrf_name_(), ip_addr_() {
32 :
33 3 : gif_ = NULL;
34 3 : querying_ = false;
35 3 : }
36 :
37 : // GmpIntf is local to Agent IGMP implementation and is not known
38 : // by core IGMP module.
39 : // IP address change to be reflected in GMP interface also.
40 6 : bool GmpIntf::set_ip_address(const IpAddress &addr) {
41 :
42 6 : if (ip_addr_ != addr) {
43 6 : ip_addr_ = addr;
44 :
45 6 : uint32_t intf_addr = htonl(ip_addr_.to_v4().to_ulong());
46 : gmp_addr_string gmp_addr;
47 6 : memcpy(&gmp_addr, &intf_addr, IPV4_ADDR_LEN);
48 6 : gmp_update_intf_state(gmp_proto_->gd_, gif_,
49 6 : addr != IpAddress() ?
50 : (const gmp_addr_string *)&intf_addr : NULL);
51 6 : return true;
52 : }
53 :
54 0 : return false;
55 : }
56 :
57 : // Update the new VRF name for GmpIntf.
58 6 : bool GmpIntf::set_vrf_name(const string &vrf_name) {
59 6 : vrf_name_ = vrf_name;
60 6 : return true;
61 : }
62 :
63 : // Change of querying mode per-GmpIntf.
64 0 : bool GmpIntf::set_gmp_querying(bool querying) {
65 :
66 0 : if (querying_ != querying) {
67 0 : querying_ = querying;
68 0 : boolean ret = gmp_update_intf_querying(gmp_proto_->gd_, gif_,
69 : querying ? TRUE : FALSE);
70 0 : return ret ? true : false;
71 : }
72 :
73 0 : return true;
74 : }
75 :
76 1 : GmpProto::GmpProto(GmpType::Type type, Agent *agent,
77 : const std::string &task_name, int instance,
78 1 : boost::asio::io_context &io) :
79 1 : type_(type), agent_(agent), name_(task_name), instance_(instance), io_(io) {
80 :
81 1 : task_map_ = NULL;
82 1 : gmp_trigger_timer_ = NULL;
83 1 : gmp_notif_trigger_ = NULL;
84 1 : gd_ = NULL;
85 1 : cb_ = NULL;
86 :
87 1 : stats_.gmp_g_add_count_ = 0;
88 1 : stats_.gmp_g_del_count_ = 0;
89 :
90 1 : stats_.gmp_sg_add_count_ = 0;
91 1 : stats_.gmp_sg_del_count_ = 0;
92 1 : }
93 :
94 1 : GmpProto::~GmpProto() {
95 1 : }
96 :
97 : // Start the GmpProto that represents the agent's IGMP functionality.
98 1 : bool GmpProto::Start() {
99 :
100 1 : if (type_ != GmpType::IGMP) {
101 0 : return false;
102 : }
103 :
104 1 : task_map_ = TaskMapManager::CreateTaskMap(agent_, name_, instance_, io_);
105 1 : if (!task_map_) {
106 0 : return false;
107 : }
108 :
109 : // TaskTrigger instance for handling GMP notifications like join, leave
110 : // per-<S,G> and per-host as required.
111 2 : gmp_notif_trigger_ = new TaskTrigger(
112 : boost::bind(&GmpProto::GmpNotificationHandler,this),
113 1 : TaskScheduler::GetInstance()->GetTaskId(name_),
114 1 : instance_);
115 :
116 1 : gd_ = gmp_init(MCAST_AF_IPV4, (task *)task_map_->task_, this);
117 1 : if (!gd_) {
118 0 : if (task_map_) TaskMapManager::DeleteTaskMap(task_map_);
119 0 : task_map_ = NULL;
120 :
121 0 : if (gmp_notif_trigger_) {
122 0 : gmp_notif_trigger_->Reset();
123 0 : delete gmp_notif_trigger_;
124 : }
125 0 : gmp_notif_trigger_ = NULL;
126 :
127 0 : return false;
128 : }
129 :
130 1 : vn_listener_id_ = agent_->vn_table()->Register(
131 : boost::bind(&GmpProto::GmpVnNotify, this, _1, _2));
132 1 : itf_listener_id_ = agent_->interface_table()->Register(
133 : boost::bind(&GmpProto::GmpItfNotify, this, _1, _2));
134 :
135 1 : return true;
136 : }
137 :
138 : // Stop the GmpProto that represents the agent's IGMP functionality.
139 1 : bool GmpProto::Stop() {
140 :
141 1 : if (!gd_) {
142 0 : return true;
143 : }
144 :
145 1 : agent_->interface_table()->Unregister(itf_listener_id_);
146 1 : agent_->vn_table()->Unregister(vn_listener_id_);
147 :
148 1 : gmp_deinit(MCAST_AF_IPV4);
149 1 : gd_ = NULL;
150 :
151 1 : if (gmp_trigger_timer_) {
152 0 : gmp_trigger_timer_->Cancel();
153 0 : TimerManager::DeleteTimer(gmp_trigger_timer_);
154 0 : gmp_trigger_timer_ = NULL;
155 : }
156 :
157 1 : gmp_notif_trigger_->Reset();
158 1 : delete gmp_notif_trigger_;
159 1 : gmp_notif_trigger_ = NULL;
160 :
161 1 : TaskMapManager::DeleteTaskMap(task_map_);
162 1 : task_map_ = NULL;
163 :
164 1 : return true;
165 : }
166 :
167 : // Cleans up <S,G> per-GmpIntf. Also cleans up VMIs per-<S,G> from NH.
168 3 : void GmpProto::GmpIntfSGClear(VnGmpDBState *state,
169 : VnGmpDBState::VnGmpIntfState *gmp_intf_state) {
170 :
171 3 : MulticastHandler *m_handler = agent_->oper_db()->multicast();
172 :
173 3 : GmpSourceGroup *gmp_sg = NULL;
174 3 : std::set<GmpSourceGroup *> sg_to_delete;
175 : VnGmpDBState::VnGmpSGIntfListIter gif_sg_it =
176 3 : gmp_intf_state->gmp_intf_sg_list_.begin();
177 3 : for (; gif_sg_it != gmp_intf_state->gmp_intf_sg_list_.end(); ++gif_sg_it) {
178 :
179 0 : gmp_sg = *gif_sg_it;
180 :
181 0 : MCTRACE(LogSG, "Delete Mcast VRF SG for VN ",
182 : gmp_intf_state->gmp_intf_->get_vrf_name(),
183 : gmp_sg->source_.to_v4().to_string(),
184 : gmp_sg->group_.to_v4().to_string(), 0);
185 :
186 0 : gmp_sg->refcount_--;
187 0 : sg_to_delete.insert(gmp_sg);
188 0 : if (gmp_sg->refcount_ == 0) {
189 0 : state->gmp_sg_list_.erase(gmp_sg);
190 : // Delete VMIs from multicast list for the <S,G>
191 0 : m_handler->DeleteMulticastVrfSourceGroup(
192 0 : gmp_intf_state->gmp_intf_->get_vrf_name(),
193 0 : gmp_sg->source_.to_v4(), gmp_sg->group_.to_v4());
194 : }
195 : }
196 :
197 3 : for(std::set<GmpSourceGroup *>::iterator sg_it = sg_to_delete.begin();
198 3 : sg_it != sg_to_delete.end(); sg_it++) {
199 0 : gmp_sg = *sg_it;
200 0 : gmp_intf_state->gmp_intf_sg_list_.erase(gmp_sg);
201 0 : if (gmp_sg->refcount_ == 0) {
202 0 : delete gmp_sg;
203 : }
204 : }
205 :
206 6 : return;
207 3 : }
208 :
209 : // Handle VN change notification, specifically IPAM changes
210 19 : void GmpProto::GmpVnNotify(DBTablePartBase *part, DBEntryBase *entry) {
211 :
212 : // Registering/Unregistering every IPAM gateway (or) dns_server
213 : // present in the VN with the IGMP module.
214 : // Changes to VN, or VN IPAM info, or gateway or dns server is
215 : // handled below.
216 :
217 19 : VnEntry *vn = static_cast<VnEntry *>(entry);
218 :
219 : VnGmpDBState *state = static_cast<VnGmpDBState *>
220 19 : (entry->GetState(part->parent(), vn_listener_id_));
221 : VnGmpDBState::VnGmpIntfState *gmp_intf_state;
222 :
223 19 : if (vn->IsDeleted() || !vn->GetVrf()) {
224 16 : if (!state) {
225 10 : return;
226 : }
227 6 : VnGmpDBState::VnGmpIntfMap::iterator it = state->gmp_intf_map_.begin();
228 9 : for (;it != state->gmp_intf_map_.end(); ++it) {
229 3 : gmp_intf_state = it->second;
230 3 : GmpIntfSGClear(state, gmp_intf_state);
231 : // Cleanup the GMP database and timers
232 3 : gmp_intf_state->gmp_intf_->set_vrf_name(string());
233 3 : gmp_intf_state->gmp_intf_->set_ip_address(IpAddress(Ip4Address()));
234 3 : DeleteIntf(gmp_intf_state->gmp_intf_);
235 3 : delete gmp_intf_state;
236 3 : itf_attach_count_--;
237 : }
238 6 : state->gmp_intf_map_.clear();
239 :
240 6 : if (vn->IsDeleted()) {
241 3 : entry->ClearState(part->parent(), vn_listener_id_);
242 3 : delete state;
243 : }
244 6 : return;
245 : }
246 :
247 3 : if (!vn->GetVrf()) {
248 0 : return;
249 : }
250 :
251 6 : if ((vn->GetVrf()->GetName() == agent_->fabric_policy_vrf_name()) ||
252 3 : (vn->GetVrf()->GetName() == agent_->fabric_vrf_name())) {
253 0 : return;
254 : }
255 :
256 3 : if (state == NULL) {
257 3 : state = new VnGmpDBState();
258 :
259 3 : entry->SetState(part->parent(), vn_listener_id_, state);
260 : }
261 :
262 3 : VnGmpDBState::VnGmpIntfMap::iterator it = state->gmp_intf_map_.begin();
263 3 : while (it != state->gmp_intf_map_.end()) {
264 0 : const VnIpam *ipam = vn->GetIpam(it->first);
265 0 : if ((ipam != NULL) && ((ipam->default_gw == it->first) ||
266 0 : (ipam->dns_server == it->first))) {
267 0 : it++;
268 0 : continue;
269 : }
270 0 : gmp_intf_state = it->second;
271 0 : GmpIntfSGClear(state, gmp_intf_state);
272 : // Cleanup the GMP database and timers
273 0 : gmp_intf_state->gmp_intf_->set_vrf_name(string());
274 0 : gmp_intf_state->gmp_intf_->set_ip_address(IpAddress(Ip4Address()));
275 0 : DeleteIntf(gmp_intf_state->gmp_intf_);
276 0 : delete gmp_intf_state;
277 0 : itf_attach_count_--;
278 0 : state->gmp_intf_map_.erase(it++);
279 : }
280 :
281 3 : const std::vector<VnIpam> &ipam = vn->GetVnIpam();
282 6 : for (unsigned int i = 0; i < ipam.size(); ++i) {
283 3 : if (!ipam[i].IsV4()) {
284 0 : continue;
285 : }
286 3 : if ((ipam[i].default_gw == IpAddress(Ip4Address())) &&
287 3 : (ipam[i].dns_server == IpAddress(Ip4Address()))) {
288 0 : continue;
289 : }
290 :
291 3 : IpAddress gmp_address = IpAddress(Ip4Address());
292 3 : VnGmpDBState::VnGmpIntfMap::const_iterator it;
293 :
294 3 : if (ipam[i].dns_server != IpAddress(Ip4Address())) {
295 3 : it = state->gmp_intf_map_.find(ipam[i].dns_server);
296 3 : gmp_address = ipam[i].dns_server;
297 : }
298 3 : if (ipam[i].default_gw != IpAddress(Ip4Address())) {
299 3 : if ((it != state->gmp_intf_map_.end()) &&
300 0 : (ipam[i].default_gw != ipam[i].dns_server)) {
301 0 : gmp_intf_state = it->second;
302 0 : GmpIntfSGClear(state, gmp_intf_state);
303 : // Cleanup the GMP database and timers
304 0 : gmp_intf_state->gmp_intf_->set_vrf_name(string());
305 0 : gmp_intf_state->gmp_intf_->set_ip_address(IpAddress(Ip4Address()));
306 0 : DeleteIntf(gmp_intf_state->gmp_intf_);
307 0 : itf_attach_count_--;
308 0 : delete gmp_intf_state;
309 0 : state->gmp_intf_map_.erase(it->first);
310 : }
311 :
312 3 : gmp_address = ipam[i].default_gw;
313 : }
314 :
315 3 : it = state->gmp_intf_map_.find(gmp_address);
316 3 : if (it == state->gmp_intf_map_.end()) {
317 3 : gmp_intf_state = new VnGmpDBState::VnGmpIntfState();
318 3 : gmp_intf_state->gmp_intf_ = CreateIntf();
319 3 : itf_attach_count_++;
320 3 : state->gmp_intf_map_.insert(
321 6 : std::pair<IpAddress,VnGmpDBState::VnGmpIntfState*>
322 : (gmp_address, gmp_intf_state));
323 : } else {
324 0 : gmp_intf_state = it->second;
325 : }
326 3 : if (gmp_intf_state) {
327 3 : gmp_intf_state->gmp_intf_->set_ip_address(gmp_address);
328 3 : if (vn->GetVrf()) {
329 3 : gmp_intf_state->gmp_intf_->set_vrf_name(vn->GetVrf()->GetName());
330 : }
331 : }
332 : }
333 : }
334 :
335 : // Handle Interface notification, specifically VMI delete notification
336 : // Clean up VMI from NH for the <S,G>s on VMI delete
337 82 : void GmpProto::GmpItfNotify(DBTablePartBase *part, DBEntryBase *entry) {
338 :
339 82 : Interface *itf = static_cast<Interface *>(entry);
340 82 : if (itf->type() != Interface::VM_INTERFACE) {
341 15 : return;
342 : }
343 :
344 67 : VmInterface *vm_itf = static_cast<VmInterface *>(itf);
345 67 : if (vm_itf->vmi_type() == VmInterface::VHOST) {
346 1 : return;
347 : }
348 :
349 : VmiGmpDBState *vmi_state = static_cast<VmiGmpDBState *>
350 66 : (entry->GetState(part->parent(), itf_listener_id_));
351 :
352 66 : if (itf->IsDeleted() || !vm_itf->igmp_enabled()) {
353 66 : if (!vmi_state) {
354 66 : MCTRACE(IgmpIntf, "Itf Notify, no VMI state ",
355 : "no-vrf", vm_itf->primary_ip_addr().to_string(),
356 : vm_itf->igmp_enabled());
357 66 : return;
358 : }
359 :
360 0 : MCTRACE(IgmpIntf, "Itf Notify, VMI delete or IGMP disable ",
361 : vmi_state->vrf_name_,
362 : vm_itf->primary_ip_addr().to_string(),
363 : vm_itf->igmp_enabled());
364 0 : if (agent_->oper_db()->multicast()) {
365 0 : agent_->oper_db()->multicast()->DeleteVmInterfaceFromVrfSourceGroup(
366 0 : vmi_state->vrf_name_, vm_itf);
367 : }
368 0 : if (itf->IsDeleted()) {
369 0 : vm_ip_to_vmi_.erase(vmi_state->vmi_v4_addr_);
370 0 : entry->ClearState(part->parent(), itf_listener_id_);
371 0 : delete vmi_state;
372 : }
373 0 : return;
374 : }
375 :
376 0 : MCTRACE(IgmpIntf, "Itf Notify, VMI create or IGMP enable",
377 : vm_itf->vrf() ? vm_itf->vrf()->GetName() : "",
378 : vm_itf->primary_ip_addr().to_string(),
379 : vm_itf->igmp_enabled());
380 0 : if (vmi_state == NULL) {
381 0 : vmi_state = new VmiGmpDBState();
382 0 : entry->SetState(part->parent(), itf_listener_id_, vmi_state);
383 : }
384 :
385 0 : if (vm_itf->vrf()) {
386 0 : if (vmi_state->vrf_name_ != vm_itf->vrf()->GetName()) {
387 0 : if (agent_->oper_db()->multicast()) {
388 0 : agent_->oper_db()->multicast()->
389 0 : DeleteVmInterfaceFromVrfSourceGroup(
390 0 : vmi_state->vrf_name_, vm_itf);
391 : }
392 :
393 0 : vmi_state->vrf_name_ = vm_itf->vrf()->GetName();
394 : }
395 : }
396 :
397 0 : if (vmi_state->vmi_v4_addr_ != vm_itf->primary_ip_addr()) {
398 :
399 0 : if (vm_ip_to_vmi_.find(vmi_state->vmi_v4_addr_) !=
400 0 : vm_ip_to_vmi_.end()) {
401 0 : if (agent_->oper_db()->multicast()) {
402 0 : agent_->oper_db()->multicast()->
403 0 : DeleteVmInterfaceFromVrfSourceGroup(
404 0 : vmi_state->vrf_name_, vm_itf);
405 : }
406 :
407 0 : vm_ip_to_vmi_.erase(vmi_state->vmi_v4_addr_);
408 : }
409 :
410 0 : vmi_state->vmi_v4_addr_ = vm_itf->primary_ip_addr();
411 0 : vm_ip_to_vmi_.insert(std::pair<IpAddress,boost::uuids::uuid>
412 0 : (vmi_state->vmi_v4_addr_,
413 : vm_itf->GetUuid()));
414 : }
415 :
416 0 : return;
417 : }
418 :
419 : // Create a GmpIntf. Represents per-IPAM entry.
420 3 : GmpIntf *GmpProto::CreateIntf() {
421 :
422 3 : GmpIntf *gmp_intf = new GmpIntf(this);
423 :
424 3 : gmp_intf->SetGif(gmp_attach_intf(gd_, gmp_intf));
425 3 : if (!gmp_intf->GetGif()) {
426 0 : delete gmp_intf;
427 0 : return NULL;
428 : }
429 :
430 3 : return gmp_intf;
431 : }
432 :
433 : // Delete a GmpIntf.
434 3 : bool GmpProto::DeleteIntf(GmpIntf *gif) {
435 :
436 3 : gmp_detach_intf(gd_, gif->GetGif());
437 3 : delete gif;
438 :
439 3 : return true;
440 : }
441 :
442 : // Pass the IP header processed IGMP packet to the GMP
443 0 : bool GmpProto::GmpProcessPkt(const VmInterface *vm_itf,
444 : void *rcv_pkt, uint32_t packet_len,
445 : IpAddress ip_saddr, IpAddress ip_daddr) {
446 :
447 : uint32_t addr;
448 : gmp_addr_string src_addr, dst_addr;
449 :
450 0 : const VnEntry *vn = vm_itf->vn();
451 0 : VnGmpDBState *state = NULL;
452 0 : state = static_cast<VnGmpDBState *>(vn->GetState(
453 : vn->get_table_partition()->parent(),
454 : vn_listener_id_));
455 0 : if (!state) {
456 0 : return false;
457 : }
458 :
459 0 : const VnIpam *ipam = vn->GetIpam(ip_saddr);
460 : VnGmpDBState::VnGmpIntfMap::const_iterator it =
461 0 : state->gmp_intf_map_.find(ipam->default_gw);
462 0 : if (it == state->gmp_intf_map_.end()) {
463 0 : it = state->gmp_intf_map_.find(ipam->dns_server);
464 : }
465 0 : if (it == state->gmp_intf_map_.end()) {
466 0 : return false;
467 : }
468 :
469 0 : VnGmpDBState::VnGmpIntfState *gmp_intf_state = it->second;
470 0 : GmpIntf *gmp_intf = gmp_intf_state->gmp_intf_;
471 :
472 0 : addr = htonl(ip_saddr.to_v4().to_ulong());
473 0 : memcpy(&src_addr, &addr, IPV4_ADDR_LEN);
474 0 : addr = htonl(ip_daddr.to_v4().to_ulong());
475 0 : memcpy(&dst_addr, &addr, IPV4_ADDR_LEN);
476 :
477 0 : boolean ret = gmp_process_pkt(gd_, gmp_intf->GetGif(), rcv_pkt,
478 : packet_len, &src_addr, &dst_addr);
479 :
480 0 : return ret ? true : false;
481 : }
482 :
483 3 : uint8_t *GmpProto::GmpBufferGet() {
484 3 : return new uint8_t[GMP_TX_BUFF_LEN];
485 : }
486 :
487 3 : void GmpProto::GmpBufferFree(uint8_t *pkt) {
488 3 : delete [] pkt;
489 3 : }
490 :
491 : // TaskTrigger handler for IGMP notifications like join, leave.
492 0 : bool GmpProto::GmpNotificationHandler() {
493 :
494 0 : boolean pending = FALSE;
495 :
496 0 : pending = gmp_notification_handler(gd_);
497 :
498 0 : if ((pending == TRUE) && gmp_notif_trigger_) {
499 0 : gmp_trigger_timer_ = TimerManager::CreateTimer(io_, "GMP Trigger Timer",
500 : TaskScheduler::GetInstance()->GetTaskId(name_),
501 : instance_, true);
502 :
503 0 : gmp_trigger_timer_->Start(kGmpTriggerRestartTimer,
504 : boost::bind(&GmpProto::GmpNotificationTimer, this));
505 : }
506 :
507 0 : return true;
508 : }
509 :
510 : // Timer instance to handle pending notifications not handled in previous run
511 0 : bool GmpProto::GmpNotificationTimer() {
512 :
513 0 : if (gmp_notif_trigger_ && !gmp_notif_trigger_->IsSet()) {
514 0 : gmp_notif_trigger_->Set();
515 : }
516 :
517 0 : gmp_trigger_timer_ = NULL;
518 :
519 0 : return false;
520 : }
521 :
522 : // Handler registered with GMP to take care of per-<S,G> and also
523 : // per-host, per-<S,G> notifications. TaskTriggers instance created
524 : // above is triggered to further handle the notifications
525 0 : void GmpProto::GmpNotificationReady() {
526 :
527 0 : if (!gmp_notif_trigger_ || gmp_notif_trigger_->IsSet()) {
528 0 : return;
529 : }
530 :
531 0 : gmp_notif_trigger_->Set();
532 :
533 0 : return;
534 : }
535 :
536 : // <S,G>, including <*,G> notification handling
537 0 : void GmpProto::GroupNotify(GmpIntf *gif, IpAddress source, IpAddress group,
538 : int group_action) {
539 :
540 0 : if (agent_->params()->mvpn_ipv4_enable()) {
541 0 : return;
542 : }
543 :
544 : // Support for EVPN <*,G> only.
545 0 : if (!gif) {
546 0 : return;
547 : }
548 :
549 0 : VrfEntry *vrf = agent_->vrf_table()->FindVrfFromName(gif->get_vrf_name());
550 : VnEntry *vn;
551 0 : if (vrf) {
552 0 : vn = vrf->vn();
553 : }
554 0 : if (!vrf || !vn) {
555 0 : return;
556 : }
557 :
558 0 : VnGmpDBState *state = NULL;
559 0 : state = static_cast<VnGmpDBState *>(vn->GetState(
560 : vn->get_table_partition()->parent(),
561 : vn_listener_id_));
562 0 : if (!state) {
563 0 : return;
564 : }
565 :
566 0 : VnGmpDBState::VnGmpIntfMap::iterator gif_it = state->gmp_intf_map_.begin();
567 0 : VnGmpDBState::VnGmpIntfState *gmp_intf_state = NULL;
568 0 : while (gif_it != state->gmp_intf_map_.end()) {
569 0 : gmp_intf_state = gif_it->second;
570 0 : if (gmp_intf_state->gmp_intf_ == gif) {
571 0 : break;
572 : }
573 0 : gif_it++;
574 : }
575 :
576 0 : if (gif_it == state->gmp_intf_map_.end()) {
577 0 : return;
578 : }
579 :
580 0 : GmpSourceGroup *gmp_sg = NULL;
581 0 : VnGmpDBState::VnGmpSGListIter sg_it = state->gmp_sg_list_.begin();
582 0 : for (;sg_it != state->gmp_sg_list_.end(); ++sg_it) {
583 0 : gmp_sg = *sg_it;
584 0 : if ((gmp_sg->source_ == source) || (gmp_sg->group_ == group)) {
585 0 : break;
586 : }
587 : }
588 :
589 0 : if (sg_it == state->gmp_sg_list_.end()) {
590 0 : if (group_action == MGM_GROUP_REMOVED ||
591 : group_action == MGM_GROUP_SRC_REMOVED) {
592 :
593 0 : return;
594 : }
595 : }
596 :
597 0 : bool created = false;
598 0 : if (!gmp_sg) {
599 0 : gmp_sg = new GmpSourceGroup();
600 0 : gmp_sg->source_ = source;
601 0 : gmp_sg->group_ = group;
602 0 : gmp_sg->flags_ = GmpSourceGroup::IGMP_VERSION_V1 |
603 : GmpSourceGroup::IGMP_VERSION_V2;
604 0 : created = true;
605 0 : state->gmp_sg_list_.insert(gmp_sg);
606 : }
607 :
608 : VnGmpDBState::VnGmpSGIntfListIter gif_sg_it =
609 0 : gmp_intf_state->gmp_intf_sg_list_.begin();
610 :
611 0 : MulticastHandler *m_handler = agent_->oper_db()->multicast();
612 0 : if (group_action == MGM_GROUP_ADDED) {
613 0 : gif_sg_it = gmp_intf_state->gmp_intf_sg_list_.find(gmp_sg);
614 0 : if (gif_sg_it == gmp_intf_state->gmp_intf_sg_list_.end()) {
615 0 : gmp_intf_state->gmp_intf_sg_list_.insert(gmp_sg);
616 0 : gmp_sg->refcount_++;
617 : }
618 0 : if (created) {
619 0 : MCTRACE(LogSG, "Create Mcast VRF SG ", vrf->GetName(),
620 : source.to_string(), group.to_string(), 0);
621 0 : m_handler->CreateMulticastVrfSourceGroup(vrf->GetName(),
622 0 : vn->GetName(), source.to_v4(), group.to_v4());
623 0 : m_handler->SetEvpnMulticastSGFlags(vrf->GetName(),
624 0 : source.to_v4(), group.to_v4(), gmp_sg->flags_);
625 : }
626 : }
627 :
628 0 : if (group_action == MGM_GROUP_REMOVED ||
629 : group_action == MGM_GROUP_SRC_REMOVED) {
630 :
631 0 : gif_sg_it = gmp_intf_state->gmp_intf_sg_list_.find(gmp_sg);
632 0 : if (gif_sg_it != gmp_intf_state->gmp_intf_sg_list_.end()) {
633 0 : gmp_sg->refcount_--;
634 0 : gmp_intf_state->gmp_intf_sg_list_.erase(gmp_sg);
635 0 : if (gmp_sg->refcount_ == 0) {
636 0 : state->gmp_sg_list_.erase(gmp_sg);
637 0 : MCTRACE(LogSG, "Delete Mcast VRF SG ", vrf->GetName(),
638 : source.to_string(), group.to_string(), 0);
639 0 : m_handler->DeleteMulticastVrfSourceGroup(vrf->GetName(),
640 0 : source.to_v4(), group.to_v4());
641 0 : delete gmp_sg;
642 : }
643 : }
644 : }
645 :
646 0 : return;
647 : }
648 :
649 0 : void GmpProto::ResyncNotify(GmpIntf *gif, IpAddress source, IpAddress group) {
650 :
651 0 : return;
652 : }
653 :
654 : // Per-host, per-<S,G> handling. For now, only per-host, per-<*,G> handling
655 0 : void GmpProto::UpdateHostInSourceGroup(GmpIntf *gif, bool join, IpAddress host,
656 : IpAddress source, IpAddress group) {
657 :
658 0 : if (!agent_->oper_db()->multicast()) {
659 0 : return;
660 : }
661 :
662 0 : if (!host.is_v4()) {
663 0 : return;
664 : }
665 :
666 0 : if (source.to_v4() == Ip4Address()) {
667 0 : join ? stats_.gmp_g_add_count_++ : stats_.gmp_g_del_count_++;
668 : } else {
669 0 : join ? stats_.gmp_sg_add_count_++ : stats_.gmp_sg_del_count_++;
670 : }
671 :
672 0 : if (vm_ip_to_vmi_.find(host) == vm_ip_to_vmi_.end()) {
673 0 : MCTRACE(Info, "igmp_trace: No host found", host.to_string());
674 0 : return;
675 : }
676 :
677 0 : boost::uuids::uuid vmi_uuid = vm_ip_to_vmi_[host];
678 0 : InterfaceConstRef intf_ref = agent_->interface_table()->FindVmi(vmi_uuid);
679 0 : const VmInterface *vm_intf = static_cast<const VmInterface *>(intf_ref.get());
680 0 : if (!vm_intf) {
681 0 : MCTRACE(Info, "igmp_trace: No VM Interface for host", host.to_string());
682 0 : return;
683 : }
684 :
685 0 : if (agent_->params()->mvpn_ipv4_enable()) {
686 : // Support for MVPN <S,G> only.
687 0 : TriggerMvpnNotification(vm_intf, join, source, group);
688 : } else {
689 : // Support for EVPN <*,G> only.
690 0 : TriggerEvpnNotification(vm_intf, join, source, group);
691 : }
692 :
693 0 : return;
694 0 : }
695 :
696 : // Handling per-host, per-<S,G> notification for MVPN case
697 0 : void GmpProto::TriggerMvpnNotification(const VmInterface *vm_intf, bool join,
698 : IpAddress source, IpAddress group) {
699 :
700 : uint32_t src_addr;
701 0 : src_addr = htonl(source.to_v4().to_ulong());
702 :
703 0 : if (src_addr) {
704 0 : if (join) {
705 0 : agent_->oper_db()->multicast()->AddVmInterfaceToSourceGroup(
706 0 : agent_->fabric_policy_vrf_name(),
707 0 : agent_->fabric_vn_name(), vm_intf,
708 0 : source.to_v4(), group.to_v4());
709 : } else {
710 0 : agent_->oper_db()->multicast()->DeleteVmInterfaceFromSourceGroup(
711 0 : agent_->fabric_policy_vrf_name(), vm_intf,
712 0 : source.to_v4(), group.to_v4());
713 : }
714 : } else {
715 0 : if (!join) {
716 0 : agent_->oper_db()->multicast()->DeleteVmInterfaceFromSourceGroup(
717 0 : agent_->fabric_policy_vrf_name(), vm_intf,
718 0 : group.to_v4());
719 : }
720 : }
721 :
722 0 : return;
723 : }
724 :
725 : // Handling per-host, per-<S,G> notification for EVPN(SMET) case
726 0 : void GmpProto::TriggerEvpnNotification(const VmInterface *vm_intf, bool join,
727 : IpAddress source, IpAddress group) {
728 :
729 0 : if (join) {
730 0 : MCTRACE(LogSG, "Add VM Mcast VRF SG ",
731 : vm_intf->primary_ip_addr().to_string(),
732 : source.to_string(), group.to_string(), 0);
733 0 : agent_->oper_db()->multicast()->AddVmInterfaceToVrfSourceGroup(
734 : vm_intf->vrf()->GetName(),
735 0 : agent_->fabric_vn_name(), vm_intf,
736 0 : source.to_v4(), group.to_v4());
737 : } else {
738 0 : MCTRACE(LogSG, "Delete VM Mcast VRF SG ",
739 : vm_intf->primary_ip_addr().to_string(),
740 : source.to_string(), group.to_string(), 0);
741 0 : agent_->oper_db()->multicast()->DeleteVmInterfaceFromVrfSourceGroup(
742 : vm_intf->vrf()->GetName(), vm_intf,
743 0 : source.to_v4(), group.to_v4());
744 : }
745 :
746 0 : return;
747 : }
748 :
749 : // Accept/reject <S,G> based on the multicast policy configured
750 : // by the user/application.
751 0 : bool GmpProto::MulticastPolicyCheck(GmpIntf *gif, IpAddress source,
752 : IpAddress group) {
753 :
754 0 : if (!gif) {
755 0 : return false;
756 : }
757 :
758 0 : VrfEntry *vrf = agent_->vrf_table()->FindVrfFromName(gif->get_vrf_name());
759 0 : VnEntry *vn = vrf ? vrf->vn() : NULL;
760 0 : if (!vn) {
761 0 : return false;
762 : }
763 :
764 0 : const UuidList &mp_list = vn->mp_list();
765 0 : if (!mp_list.size()) {
766 0 : return true;
767 : }
768 :
769 0 : UuidList::const_iterator it = mp_list.begin();
770 0 : MulticastPolicyTable *table = agent_->mp_table();
771 0 : while (it != mp_list.end()) {
772 0 : MulticastPolicyKey key(*it);
773 0 : MulticastPolicyEntry *entry = static_cast<MulticastPolicyEntry *>
774 0 : (table->FindActiveEntry(&key));
775 0 : SourceGroupInfo::Action action = entry->GetAction(source, group);
776 0 : if (action == SourceGroupInfo::ACTION_PASS) {
777 0 : return true;
778 : }
779 0 : it++;
780 0 : }
781 :
782 0 : return false;
783 : }
784 :
785 : // Send IGMP packet generated by the GMP to particular destination
786 0 : bool GmpProto::SendPacket(GmpIntf *gif, uint8_t *pkt, uint32_t pkt_len,
787 : IpAddress dest) {
788 :
789 0 : GmpPacket packet(pkt, pkt_len, dest);
790 :
791 0 : if (!cb_) {
792 0 : return false;
793 : }
794 :
795 0 : const VrfEntry *vrf = agent_->vrf_table()->FindVrfFromName(
796 : gif->get_vrf_name());
797 0 : if (!vrf) {
798 0 : return false;
799 : }
800 :
801 0 : return cb_(vrf, gif->get_ip_address(), &packet);
802 : }
803 :
804 1 : GmpProto *GmpProtoManager::CreateGmpProto(GmpType::Type type, Agent *agent,
805 : const std::string &task_name, int instance,
806 : boost::asio::io_context &io) {
807 :
808 1 : GmpProto *proto_inst = new GmpProto(type, agent, task_name, instance, io);
809 1 : if (!proto_inst) {
810 0 : return NULL;
811 : }
812 :
813 1 : return proto_inst;
814 : }
815 :
816 1 : bool GmpProtoManager::DeleteGmpProto(GmpProto *proto_inst) {
817 1 : if (!proto_inst) {
818 0 : return false;
819 : }
820 :
821 1 : delete proto_inst;
822 :
823 1 : return true;
824 : }
825 :
826 : // Callback registered with GMP to check <S,G> for multicast policy
827 0 : boolean gmp_policy_check(mgm_global_data *gd, gmp_intf *intf,
828 : gmp_addr_string source, gmp_addr_string group)
829 : {
830 0 : if (!gd || !intf) {
831 0 : return FALSE;
832 : }
833 :
834 0 : if (gd->mgm_gd_af != MCAST_AF_IPV4) {
835 0 : return FALSE;
836 : }
837 :
838 0 : GmpProto *gmp_proto = (GmpProto *)gd->gmp_sm;
839 0 : GmpIntf *gif = (GmpIntf *)intf->vm_interface;
840 :
841 0 : if (!gmp_proto || !gif) {
842 0 : return FALSE;
843 : }
844 :
845 : uint32_t addr;
846 0 : memcpy(&addr, &source, IPV4_ADDR_LEN);
847 0 : IpAddress source_addr = Ip4Address(ntohl(addr));
848 0 : memcpy(&addr, &group, IPV4_ADDR_LEN);
849 0 : IpAddress group_addr = Ip4Address(ntohl(addr));
850 :
851 0 : bool permit = gmp_proto->MulticastPolicyCheck(gif, source_addr, group_addr);
852 :
853 0 : return (permit ? TRUE : FALSE);
854 : }
855 :
856 0 : void gmp_notification_ready(mgm_global_data *gd)
857 : {
858 0 : if (!gd) {
859 0 : return;
860 : }
861 :
862 0 : GmpProto *gmp_proto = (GmpProto *)gd->gmp_sm;
863 :
864 0 : gmp_proto->GmpNotificationReady();
865 :
866 0 : return;
867 : }
868 :
869 : // Function to handle per-<S,G> notifications
870 0 : void gmp_group_notify(mgm_global_data *gd, gmp_intf *intf,
871 : int group_action, gmp_addr_string source,
872 : gmp_addr_string group)
873 : {
874 0 : if (!gd || !intf) {
875 0 : return;
876 : }
877 :
878 0 : if (gd->mgm_gd_af != MCAST_AF_IPV4) {
879 0 : return;
880 : }
881 :
882 0 : GmpProto *gmp_proto = (GmpProto *)gd->gmp_sm;
883 0 : GmpIntf *gif = (GmpIntf *)intf->vm_interface;
884 :
885 0 : if (!gmp_proto || !gif) {
886 0 : return;
887 : }
888 :
889 : uint32_t addr;
890 0 : memcpy(&addr, &source, IPV4_ADDR_LEN);
891 0 : IpAddress source_addr = Ip4Address(ntohl(addr));
892 0 : memcpy(&addr, &group, IPV4_ADDR_LEN);
893 0 : IpAddress group_addr = Ip4Address(ntohl(addr));
894 :
895 0 : gmp_proto->GroupNotify(gif, source_addr, group_addr, group_action);
896 : }
897 :
898 : // C-based function to handle per-<S,G> resync notifications
899 0 : void gmp_cache_resync_notify(mgm_global_data *gd, gmp_intf *intf,
900 : gmp_addr_string source, gmp_addr_string group)
901 : {
902 0 : if (!gd || !intf) {
903 0 : return;
904 : }
905 :
906 0 : if (gd->mgm_gd_af != MCAST_AF_IPV4) {
907 0 : return;
908 : }
909 :
910 0 : GmpProto *gmp_proto = (GmpProto *)gd->gmp_sm;
911 0 : GmpIntf *gif = (GmpIntf *)intf->vm_interface;
912 :
913 0 : if (!gmp_proto || !gif) {
914 0 : return;
915 : }
916 :
917 : uint32_t addr;
918 0 : memcpy(&addr, &source, IPV4_ADDR_LEN);
919 0 : IpAddress source_addr = Ip4Address(ntohl(addr));
920 0 : memcpy(&addr, &group, IPV4_ADDR_LEN);
921 0 : IpAddress group_addr = Ip4Address(ntohl(addr));
922 :
923 0 : gmp_proto->ResyncNotify(gif, source_addr, group_addr);
924 : }
925 :
926 : // C-based function to handle per-host, per-<S,G> notifications
927 0 : void gmp_host_update(mgm_global_data *gd, gmp_intf *intf, boolean join,
928 : gmp_addr_string host, gmp_addr_string source,
929 : gmp_addr_string group)
930 : {
931 0 : if (!gd || !intf) {
932 0 : return;
933 : }
934 :
935 0 : if (gd->mgm_gd_af != MCAST_AF_IPV4) {
936 0 : return;
937 : }
938 :
939 0 : GmpProto *gmp_proto = (GmpProto *)gd->gmp_sm;
940 0 : GmpIntf *gif = (GmpIntf *)intf->vm_interface;
941 :
942 0 : if (!gmp_proto || !gif) {
943 0 : return;
944 : }
945 :
946 : uint32_t addr;
947 0 : memcpy(&addr, &host, IPV4_ADDR_LEN);
948 0 : IpAddress host_addr = Ip4Address(ntohl(addr));
949 0 : memcpy(&addr, &source, IPV4_ADDR_LEN);
950 0 : IpAddress source_addr = Ip4Address(ntohl(addr));
951 0 : memcpy(&addr, &group, IPV4_ADDR_LEN);
952 0 : IpAddress group_addr = Ip4Address(ntohl(addr));
953 :
954 0 : gmp_proto->UpdateHostInSourceGroup(gif, join ? true : false, host_addr,
955 : source_addr, group_addr);
956 : }
957 :
958 : // Allocate buffer for use in sending IGMP packet
959 3 : uint8_t *gmp_get_send_buffer(mgm_global_data *gd, gmp_intf *intf)
960 : {
961 3 : if (!gd || !intf) {
962 0 : return NULL;
963 : }
964 :
965 3 : GmpProto *gmp_proto = (GmpProto *)gd->gmp_sm;
966 3 : return gmp_proto->GmpBufferGet();
967 : }
968 :
969 : // Free the allocated buffer used in sending IGMP packet
970 3 : void gmp_free_send_buffer(mgm_global_data *gd, gmp_intf *intf, uint8_t *buffer)
971 : {
972 3 : if (!gd || !intf) {
973 0 : return;
974 : }
975 :
976 3 : GmpProto *gmp_proto = (GmpProto *)gd->gmp_sm;
977 3 : gmp_proto->GmpBufferFree(buffer);
978 :
979 3 : return;
980 : }
981 :
982 : // Send IGMP packet out to the VMs.
983 0 : void gmp_send_one_packet(mgm_global_data *gd, gmp_intf *intf, uint8_t *pkt,
984 : uint32_t pkt_len, gmp_addr_string dest)
985 : {
986 0 : if (!gd || !intf) {
987 0 : return;
988 : }
989 :
990 0 : GmpProto *gmp_proto = (GmpProto *)gd->gmp_sm;
991 0 : GmpIntf *gif = (GmpIntf *)intf->vm_interface;
992 :
993 : uint32_t addr;
994 0 : memcpy(&addr, &dest, IPV4_ADDR_LEN);
995 0 : IpAddress dst_addr = IpAddress(Ip4Address(ntohl(addr)));
996 :
997 0 : gmp_proto->SendPacket(gif, pkt, pkt_len, dst_addr);
998 :
999 0 : return;
1000 : }
|