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