blob: 192c8e2ef33a5d0a9e95c84adae5a112fb43326a [file] [log] [blame]
Tinoj Josephcf161be2022-07-07 19:47:47 +05301/*
2* Copyright 2022-present Open Networking Foundation
3* Licensed under the Apache License, Version 2.0 (the "License");
4* you may not use this file except in compliance with the License.
5* You may obtain a copy of the License at
6*
7* http://www.apache.org/licenses/LICENSE-2.0
8*
9* Unless required by applicable law or agreed to in writing, software
10* distributed under the License is distributed on an "AS IS" BASIS,
11* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12* See the License for the specific language governing permissions and
13* limitations under the License.
14*/
15
16package application
17
18import (
Tinoj Joseph07cc5372022-07-18 22:53:51 +053019 "context"
Tinoj Josephcf161be2022-07-07 19:47:47 +053020 "encoding/json"
21 "errors"
22 "net"
23 "strconv"
24 "strings"
25 "sync"
26
27 cntlr "voltha-go-controller/internal/pkg/controller"
28 "voltha-go-controller/internal/pkg/types"
29 "voltha-go-controller/database"
30 "voltha-go-controller/internal/pkg/of"
31 "voltha-go-controller/internal/pkg/util"
32 "voltha-go-controller/log"
33)
34
35// ------------------------------------------------------------
36// MVLAN related implemnetation
37//
38// Each MVLAN is configured with groups of multicast IPs. The idea of
39// groups is to be able to group some multicast channels into an individual
40// PON group and have a unique multicast GEM port for that set. However, in
41// the current implementation, the concept of grouping is not fully utilized.
42
43// MvlanGroup structure
44// A set of MC IPs form a group
45
46// MCGroupProxy identifies source specific multicast(SSM) config.
47type MCGroupProxy struct {
48 // Mode represents source list include/exclude
49 Mode common.MulticastSrcListMode
50 // SourceList represents list of multicast server IP addresses.
51 SourceList []net.IP
52}
53
54// MvlanGroup identifies MC group info
55type MvlanGroup struct {
56 Name string
57 Wildcard bool
58 McIPs []string
59 IsStatic bool
60}
61
62// OperInProgress type
63type OperInProgress uint8
64
65const (
66 // UpdateInProgress constant
67 UpdateInProgress OperInProgress = 2
68 // NoOp constant
69 NoOp OperInProgress = 1
70 // Nil constant
71 Nil OperInProgress = 0
72)
73
74// MvlanProfile : A set of groups of MC IPs for a MVLAN profile. It is assumed that
75// the MVLAN IP is not repeated within multiples groups and across
76// MVLAN profiles. The first match is used up on search to lcoate the
77// MVLAN profile for an MC IP
78type MvlanProfile struct {
79 Name string
80 Mvlan of.VlanType
81 PonVlan of.VlanType
82 Groups map[string]*MvlanGroup
83 Proxy map[string]*MCGroupProxy
84 Version string
85 IsPonVlanPresent bool
86 IsChannelBasedGroup bool
87 DevicesList map[string]OperInProgress //device serial number //here
88 oldGroups map[string]*MvlanGroup
89 oldProxy map[string]*MCGroupProxy
90 MaxActiveChannels uint32
91 PendingDeleteFlow map[string]map[string]bool
92 DeleteInProgress bool
93 IgmpServVersion map[string]*uint8
94 mvpLock sync.RWMutex
95 mvpFlowLock sync.RWMutex
96}
97
98// NewMvlanProfile is constructor for MVLAN profile.
99func NewMvlanProfile(name string, mvlan of.VlanType, ponVlan of.VlanType, isChannelBasedGroup bool, OLTSerialNums []string, actChannelPerPon uint32) *MvlanProfile {
100 var mvp MvlanProfile
101 mvp.Name = name
102 mvp.Mvlan = mvlan
103 mvp.PonVlan = ponVlan
104 mvp.mvpLock = sync.RWMutex{}
105 mvp.Groups = make(map[string]*MvlanGroup)
106 mvp.Proxy = make(map[string]*MCGroupProxy)
107 mvp.DevicesList = make(map[string]OperInProgress)
108 mvp.PendingDeleteFlow = make(map[string]map[string]bool)
109 mvp.IsChannelBasedGroup = isChannelBasedGroup
110 mvp.MaxActiveChannels = actChannelPerPon
111 mvp.DeleteInProgress = false
112 mvp.IgmpServVersion = make(map[string]*uint8)
113
114 if (ponVlan != of.VlanNone) && (ponVlan != 0) {
115 mvp.IsPonVlanPresent = true
116 }
117 return &mvp
118}
119
120// AddMvlanProxy for addition of groups to an MVLAN profile
121func (mvp *MvlanProfile) AddMvlanProxy(name string, proxyInfo common.MulticastGroupProxy) {
122 proxy := &MCGroupProxy{}
123 proxy.Mode = proxyInfo.Mode
124 proxy.SourceList = util.GetExpIPList(proxyInfo.SourceList)
125
126 if _, ok := mvp.Proxy[name]; !ok {
127 logger.Debugw(ctx, "Added MVLAN Proxy", log.Fields{"Name": name, "Proxy": proxy})
128 } else {
129 logger.Debugw(ctx, "Updated MVLAN Proxy", log.Fields{"Name": name, "Proxy": proxy})
130 }
131 if proxyInfo.IsStatic == common.IsStaticYes {
132 mvp.Groups[name].IsStatic = true
133 }
134 mvp.Proxy[name] = proxy
135}
136
137// AddMvlanGroup for addition of groups to an MVLAN profile
138func (mvp *MvlanProfile) AddMvlanGroup(name string, ips []string) {
139 mvg := &MvlanGroup{}
140 mvg.Name = name
141 mvg.Wildcard = len(ips) == 0
142 mvg.McIPs = ips
143 mvg.IsStatic = false
144 if _, ok := mvp.Groups[name]; !ok {
145 logger.Debugw(ctx, "Added MVLAN Group", log.Fields{"VLAN": mvp.Mvlan, "Name": name, "mvg": mvg, "IPs": mvg.McIPs})
146 } else {
147 logger.Debugw(ctx, "Updated MVLAN Group", log.Fields{"VLAN": mvp.Mvlan, "Name": name})
148 }
149 mvp.Groups[name] = mvg
150}
151
152// GetUsMatchVlan provides mvlan for US Match parameter
153func (mvp *MvlanProfile) GetUsMatchVlan() of.VlanType {
154 if mvp.IsPonVlanPresent {
155 return mvp.PonVlan
156 }
157 return mvp.Mvlan
158}
159
160// WriteToDb is utility to write Mvlan Profile Info to database
Tinoj Joseph07cc5372022-07-18 22:53:51 +0530161func (mvp *MvlanProfile) WriteToDb(cntx context.Context) error {
Tinoj Josephcf161be2022-07-07 19:47:47 +0530162
163 if mvp.DeleteInProgress {
164 logger.Warnw(ctx, "Skipping Redis Update for MvlanProfile, MvlanProfile delete in progress", log.Fields{"Mvlan": mvp.Mvlan})
165 return nil
166 }
167
168 mvp.Version = database.PresentVersionMap[database.MvlanPath]
169 b, err := json.Marshal(mvp)
170 if err != nil {
171 return err
172 }
Tinoj Joseph07cc5372022-07-18 22:53:51 +0530173 if err1 := db.PutMvlan(cntx, uint16(mvp.Mvlan), string(b)); err1 != nil {
Tinoj Josephcf161be2022-07-07 19:47:47 +0530174 return err1
175 }
176 return nil
177}
178
179//isChannelStatic - Returns true if the given channel is part of static group in the Mvlan Profile
180func (mvp *MvlanProfile) isChannelStatic(channel net.IP) bool {
181 for _, mvg := range mvp.Groups {
182 if mvg.IsStatic {
183 if isChannelStatic := doesIPMatch(channel, mvg.McIPs); isChannelStatic {
184 return true
185 }
186 }
187 }
188 return false
189}
190
191//containsStaticChannels - Returns if any static channels is part of the Mvlan Profile
192func (mvp *MvlanProfile) containsStaticChannels() bool {
193 for _, mvg := range mvp.Groups {
194 if mvg.IsStatic && len(mvg.McIPs) != 0 {
195 return true
196 }
197 }
198 return false
199}
200
201//getAllStaticChannels - Returns all static channels in the Mvlan Profile
202func (mvp *MvlanProfile) getAllStaticChannels() ([]net.IP, bool) {
203 channelList := []net.IP{}
204 containsStatic := false
205 for _, mvg := range mvp.Groups {
206 if mvg.IsStatic {
207 staticChannels, _ := mvg.getAllChannels()
208 channelList = append(channelList, staticChannels...)
209 }
210 }
211 if len(channelList) > 0 {
212 containsStatic = true
213 }
214 return channelList, containsStatic
215}
216
217//getAllOldGroupStaticChannels - Returns all static channels in the Mvlan Profile
218func (mvp *MvlanProfile) getAllOldGroupStaticChannels() ([]net.IP, bool) {
219 channelList := []net.IP{}
220 containsStatic := false
221 for _, mvg := range mvp.oldGroups {
222 if mvg.IsStatic {
223 staticChannels, _ := mvg.getAllChannels()
224 channelList = append(channelList, staticChannels...)
225 }
226 }
227 if len(channelList) > 0 {
228 containsStatic = true
229 }
230 return channelList, containsStatic
231}
232
233//getAllChannels - Returns all channels in the Mvlan Profile
234func (mvg *MvlanGroup) getAllChannels() ([]net.IP, bool) {
235 channelList := []net.IP{}
236
237 if mvg == nil || len(mvg.McIPs) == 0 {
238 return []net.IP{}, false
239 }
240
241 grpChannelOrRange := mvg.McIPs
242 for _, channelOrRange := range grpChannelOrRange {
243 if strings.Contains(channelOrRange, "-") {
244 var splits = strings.Split(channelOrRange, "-")
245 ipStart := util.IP2LongConv(net.ParseIP(splits[0]))
246 ipEnd := util.IP2LongConv(net.ParseIP(splits[1]))
247
248 for i := ipStart; i <= ipEnd; i++ {
249 channelList = append(channelList, util.Long2ipConv(i))
250 }
251 } else {
252 channelList = append(channelList, net.ParseIP(channelOrRange))
253 }
254 }
255 return channelList, true
256}
257
258//SetUpdateStatus - Sets profile update status for devices
259func (mvp *MvlanProfile) SetUpdateStatus(serialNum string, status OperInProgress) {
260 if serialNum != "" {
261 mvp.DevicesList[serialNum] = status
262 return
263 }
264
265 for srNo := range mvp.DevicesList {
266 mvp.DevicesList[srNo] = status
267 }
268}
269
270//isUpdateInProgress - checking is update is in progress for the mvlan profile
271func (mvp *MvlanProfile) isUpdateInProgress() bool {
272
273 for srNo := range mvp.DevicesList {
274 if mvp.DevicesList[srNo] == UpdateInProgress {
275 return true
276 }
277 }
278 return false
279}
280
281//IsUpdateInProgressForDevice - Checks is Mvlan Profile update is is progress for the given device
282func (mvp *MvlanProfile) IsUpdateInProgressForDevice(device string) bool {
283 if vd := GetApplication().GetDevice(device); vd != nil {
284 if mvp.DevicesList[vd.SerialNum] == UpdateInProgress {
285 return true
286 }
287 }
288 return false
289}
290
291// DelFromDb to delere mvlan from database
Tinoj Joseph07cc5372022-07-18 22:53:51 +0530292func (mvp *MvlanProfile) DelFromDb(cntx context.Context) {
293 _ = db.DelMvlan(cntx, uint16(mvp.Mvlan))
Tinoj Josephcf161be2022-07-07 19:47:47 +0530294}
295
296//DelFlows - Triggers flow deletion after registering for flow indication event
Tinoj Joseph07cc5372022-07-18 22:53:51 +0530297func (mvp *MvlanProfile) DelFlows(cntx context.Context, device *VoltDevice, flow *of.VoltFlow) error {
Tinoj Josephcf161be2022-07-07 19:47:47 +0530298 mvp.mvpFlowLock.Lock()
299 defer mvp.mvpFlowLock.Unlock()
300
301 var flowMap map[string]bool
302 var ok bool
303
304 for cookie := range flow.SubFlows {
305 cookie := strconv.FormatUint(cookie, 10)
306 fe := &FlowEvent{
307 eType: EventTypeMcastFlowRemoved,
308 device: device.Name,
309 cookie: cookie,
310 eventData: mvp,
311 }
312 device.RegisterFlowDelEvent(cookie, fe)
313
314 if flowMap, ok = mvp.PendingDeleteFlow[device.Name]; !ok {
315 flowMap = make(map[string]bool)
316 }
317 flowMap[cookie] = true
318 mvp.PendingDeleteFlow[device.Name] = flowMap
319 }
Tinoj Joseph07cc5372022-07-18 22:53:51 +0530320 if err := mvp.WriteToDb(cntx); err != nil {
Tinoj Josephcf161be2022-07-07 19:47:47 +0530321 logger.Errorw(ctx, "Mvlan profile write to DB failed", log.Fields{"ProfileName": mvp.Name})
322 }
Tinoj Joseph07cc5372022-07-18 22:53:51 +0530323 return cntlr.GetController().DelFlows(cntx, device.NniPort, device.Name, flow)
Tinoj Josephcf161be2022-07-07 19:47:47 +0530324}
325
326//FlowRemoveSuccess - Process flow success indication
Tinoj Joseph07cc5372022-07-18 22:53:51 +0530327func (mvp *MvlanProfile) FlowRemoveSuccess(cntx context.Context, cookie string, device string) {
Tinoj Josephcf161be2022-07-07 19:47:47 +0530328 mvp.mvpFlowLock.Lock()
329 defer mvp.mvpFlowLock.Unlock()
330
331 logger.Infow(ctx, "Mvlan Flow Remove Success Notification", log.Fields{"MvlanProfile": mvp.Name, "Cookie": cookie, "Device": device})
332
333 if _, ok := mvp.PendingDeleteFlow[device]; ok {
334 delete(mvp.PendingDeleteFlow[device], cookie)
335 }
336
Tinoj Joseph07cc5372022-07-18 22:53:51 +0530337 if err := mvp.WriteToDb(cntx); err != nil {
Tinoj Josephcf161be2022-07-07 19:47:47 +0530338 logger.Errorw(ctx, "Mvlan profile write to DB failed", log.Fields{"ProfileName": mvp.Name})
339 }
340}
341
342//FlowRemoveFailure - Process flow failure indication
343func (mvp *MvlanProfile) FlowRemoveFailure(cookie string, device string, errorCode uint32, errReason string) {
344
345 mvp.mvpFlowLock.Lock()
346 defer mvp.mvpFlowLock.Unlock()
347
348 if flowMap, ok := mvp.PendingDeleteFlow[device]; ok {
349 if _, ok := flowMap[cookie]; ok {
350 logger.Errorw(ctx, "Mvlan Flow Remove Failure Notification", log.Fields{"MvlanProfile": mvp.Name, "Cookie": cookie, "ErrorCode": errorCode, "ErrorReason": errReason, "Device": device})
351 return
352 }
353 }
354 logger.Errorw(ctx, "Mvlan Flow Del Failure Notification for Unknown cookie", log.Fields{"MvlanProfile": mvp.Name, "Cookie": cookie, "ErrorCode": errorCode, "ErrorReason": errReason})
355
356}
357
358// IsStaticGroup to check if group is static
359func (mvp *MvlanProfile) IsStaticGroup(groupName string) bool {
360 return mvp.Groups[groupName].IsStatic
361}
362
363// generateGroupKey to generate group key
364func (mvp *MvlanProfile) generateGroupKey(name string, ipAddr string) string {
365 if mvp.IsChannelBasedGroup {
366 return mvp.Mvlan.String() + "_" + ipAddr
367 }
368 return mvp.Mvlan.String() + "_" + name
369}
370
371// GetStaticGroupName to get static igmp group
372func (mvp *MvlanProfile) GetStaticGroupName(gip net.IP) string {
373 for _, mvg := range mvp.Groups {
374 if mvg.IsStatic {
375 if doesIPMatch(gip, mvg.McIPs) {
376 return mvg.Name
377 }
378 }
379 }
380 return ""
381}
382
383// GetStaticIgmpGroup to get static igmp group
384func (mvp *MvlanProfile) GetStaticIgmpGroup(gip net.IP) *IgmpGroup {
385
386 staticGroupName := mvp.GetStaticGroupName(gip)
387 grpKey := mvp.generateGroupKey(staticGroupName, gip.String())
388 logger.Debugw(ctx, "Get Static IGMP Group", log.Fields{"Group": grpKey})
389 ig, ok := GetApplication().IgmpGroups.Load(grpKey)
390 if ok {
391 logger.Debugw(ctx, "Get Static IGMP Group Success", log.Fields{"Group": grpKey})
392 return ig.(*IgmpGroup)
393 }
394 return nil
395}
396
397//pushIgmpMcastFlows - Adds all IGMP related flows (generic DS flow & static group flows)
Tinoj Joseph07cc5372022-07-18 22:53:51 +0530398func (mvp *MvlanProfile) pushIgmpMcastFlows(cntx context.Context, OLTSerialNum string) {
Tinoj Josephcf161be2022-07-07 19:47:47 +0530399
400 mvp.mvpLock.RLock()
401 defer mvp.mvpLock.RUnlock()
402
403 if mvp.DevicesList[OLTSerialNum] == Nil {
404 logger.Infow(ctx, "Mvlan Profile not configure for device", log.Fields{"Device": OLTSerialNum, "Mvlan": mvp.Mvlan})
405 return
406 }
407
408 d := GetApplication().GetDeviceBySerialNo(OLTSerialNum)
409 if d == nil {
410 logger.Warnw(ctx, "Skipping Igmp & Mcast Flow processing: Device Not Found", log.Fields{"Device_SrNo": OLTSerialNum, "Mvlan": mvp.Mvlan})
411 return
412 }
413
414 p := d.GetPort(d.NniPort)
415
416 if p != nil && p.State == PortStateUp {
417 logger.Infow(ctx, "NNI Port Status is: UP & Vlan Enabled", log.Fields{"Device": d, "port": p})
418
419 //Push Igmp DS Control Flows
Tinoj Joseph07cc5372022-07-18 22:53:51 +0530420 err := mvp.ApplyIgmpDSFlowForMvp(cntx, d.Name)
Tinoj Josephcf161be2022-07-07 19:47:47 +0530421 if err != nil {
422 logger.Errorw(ctx, "DS IGMP Flow Add Failed for device",
423 log.Fields{"Reason": err.Error(), "device": d.Name})
424 }
425
426 //Trigger Join for static channels
427 if channelList, containsStatic := mvp.getAllStaticChannels(); containsStatic {
Tinoj Joseph07cc5372022-07-18 22:53:51 +0530428 mvp.ProcessStaticGroup(cntx, d.Name, channelList, true)
Tinoj Josephcf161be2022-07-07 19:47:47 +0530429 } else {
430 logger.Infow(ctx, "No Static Channels Present", log.Fields{"mvp": mvp.Name, "Mvlan": mvp.Mvlan})
431 }
432 }
433}
434//removeIgmpMcastFlows - Removes all IGMP related flows (generic DS flow & static group flows)
Tinoj Joseph07cc5372022-07-18 22:53:51 +0530435func (mvp *MvlanProfile) removeIgmpMcastFlows(cntx context.Context, oltSerialNum string) {
Tinoj Josephcf161be2022-07-07 19:47:47 +0530436
437 mvp.mvpLock.RLock()
438 defer mvp.mvpLock.RUnlock()
439
440 if d := GetApplication().GetDeviceBySerialNo(oltSerialNum); d != nil {
441 p := d.GetPort(d.NniPort)
442 if p != nil {
443 logger.Infow(ctx, "NNI Port Status is: UP", log.Fields{"Device": d, "port": p})
444
445 // ***Do not change the order***
446 // When Vlan is disabled, the process end is determined by the DS Igmp flag in device
447
448 //Trigger Leave for static channels
449 if channelList, containsStatic := mvp.getAllStaticChannels(); containsStatic {
Tinoj Joseph07cc5372022-07-18 22:53:51 +0530450 mvp.ProcessStaticGroup(cntx, d.Name, channelList, false)
Tinoj Josephcf161be2022-07-07 19:47:47 +0530451 } else {
452 logger.Infow(ctx, "No Static Channels Present", log.Fields{"mvp": mvp.Name, "Mvlan": mvp.Mvlan})
453 }
454
455 //Remove all dynamic members for the Mvlan Profile
456 GetApplication().IgmpGroups.Range(func(key, value interface{}) bool {
457 ig := value.(*IgmpGroup)
458 if ig.Mvlan == mvp.Mvlan {
459 igd := ig.Devices[d.Name]
Tinoj Joseph07cc5372022-07-18 22:53:51 +0530460 ig.DelIgmpGroupDevice(cntx, igd)
Tinoj Josephcf161be2022-07-07 19:47:47 +0530461 if ig.NumDevicesActive() == 0 {
Tinoj Joseph07cc5372022-07-18 22:53:51 +0530462 GetApplication().DelIgmpGroup(cntx, ig)
Tinoj Josephcf161be2022-07-07 19:47:47 +0530463 }
464 }
465 return true
466 })
467
468 //Remove DS Igmp trap flow
Tinoj Joseph07cc5372022-07-18 22:53:51 +0530469 err := mvp.RemoveIgmpDSFlowForMvp(cntx, d.Name)
Tinoj Josephcf161be2022-07-07 19:47:47 +0530470 if err != nil {
471 logger.Errorw(ctx, "DS IGMP Flow Del Failed", log.Fields{"Reason": err.Error(), "device": d.Name})
472 }
473 }
474 }
475}
476
477// ApplyIgmpDSFlowForMvp to apply Igmp DS flow for mvlan.
Tinoj Joseph07cc5372022-07-18 22:53:51 +0530478func (mvp *MvlanProfile) ApplyIgmpDSFlowForMvp(cntx context.Context, device string) error {
Tinoj Josephcf161be2022-07-07 19:47:47 +0530479 va := GetApplication()
480 dIntf, ok := va.DevicesDisc.Load(device)
481 if !ok {
482 return errors.New("Device Doesn't Exist")
483 }
484 d := dIntf.(*VoltDevice)
485 mvlan := mvp.Mvlan
486
487 flowAlreadyApplied, ok := d.IgmpDsFlowAppliedForMvlan[uint16(mvlan)]
488 if !ok || !flowAlreadyApplied {
489 flows, err := mvp.BuildIgmpDSFlows(device)
490 if err == nil {
Tinoj Joseph07cc5372022-07-18 22:53:51 +0530491 err = cntlr.GetController().AddFlows(cntx, d.NniPort, device, flows)
Tinoj Josephcf161be2022-07-07 19:47:47 +0530492 if err != nil {
493 logger.Warnw(ctx, "Configuring IGMP Flow for device failed ", log.Fields{"Device": device, "err": err})
494 return err
495 }
496 d.IgmpDsFlowAppliedForMvlan[uint16(mvlan)] = true
497 logger.Infow(ctx, "Updating voltDevice that IGMP DS flow as \"added\" for ",
498 log.Fields{"device": d.SerialNum, "mvlan": mvlan})
499 } else {
500 logger.Errorw(ctx, "DS IGMP Flow Add Failed", log.Fields{"Reason": err.Error(), "Mvlan": mvlan})
501 }
502 }
503
504 return nil
505}
506
507// RemoveIgmpDSFlowForMvp to remove Igmp DS flow for mvlan.
Tinoj Joseph07cc5372022-07-18 22:53:51 +0530508func (mvp *MvlanProfile) RemoveIgmpDSFlowForMvp(cntx context.Context, device string) error {
Tinoj Josephcf161be2022-07-07 19:47:47 +0530509
510 va := GetApplication()
511 mvlan := mvp.Mvlan
512
513 dIntf, ok := va.DevicesDisc.Load(device)
514 if !ok {
515 return errors.New("Device Doesn't Exist")
516 }
517 d := dIntf.(*VoltDevice)
518 /* No need of strict check during DS IGMP deletion
519 flowAlreadyApplied, ok := d.IgmpDsFlowAppliedForMvlan[uint16(mvlan)]
520 if ok && flowAlreadyApplied
521 */
522 flows, err := mvp.BuildIgmpDSFlows(device)
523 if err == nil {
524 flows.ForceAction = true
525
Tinoj Joseph07cc5372022-07-18 22:53:51 +0530526 err = mvp.DelFlows(cntx, d, flows)
Tinoj Josephcf161be2022-07-07 19:47:47 +0530527 if err != nil {
528 logger.Warnw(ctx, "De-Configuring IGMP Flow for device failed ", log.Fields{"Device": device, "err": err})
529 return err
530 }
531 d.IgmpDsFlowAppliedForMvlan[uint16(mvlan)] = false
532 logger.Infow(ctx, "Updating voltDevice that IGMP DS flow as \"removed\" for ",
533 log.Fields{"device": d.SerialNum, "mvlan": mvlan})
534 } else {
535 logger.Errorw(ctx, "DS IGMP Flow Del Failed", log.Fields{"Reason": err.Error()})
536 }
537
538 return nil
539}
540
541// BuildIgmpDSFlows to build Igmp DS flows for NNI port
542func (mvp *MvlanProfile) BuildIgmpDSFlows(device string) (*of.VoltFlow, error) {
543 dIntf, ok := GetApplication().DevicesDisc.Load(device)
544 if !ok {
545 return nil, errors.New("Device Doesn't Exist")
546 }
547 d := dIntf.(*VoltDevice)
548
549 logger.Infow(ctx, "Building DS IGMP Flow for NNI port", log.Fields{"vs": d.NniPort, "Mvlan": mvp.Mvlan})
550 flow := &of.VoltFlow{}
551 flow.SubFlows = make(map[uint64]*of.VoltSubFlow)
552 subFlow := of.NewVoltSubFlow()
553 subFlow.SetTableID(0)
554 subFlow.SetMatchVlan(mvp.Mvlan)
555
556 nniPort, err := GetApplication().GetNniPort(device)
557 if err != nil {
558 return nil, err
559 }
560 nniPortID, err1 := GetApplication().GetPortID(nniPort)
561 if err1 != nil {
562 return nil, errors.New("Unknown NNI outport")
563 }
564 subFlow.SetInPort(nniPortID)
565 subFlow.SetIgmpMatch()
566 subFlow.SetReportToController()
567 subFlow.Cookie = uint64(nniPortID)<<32 | uint64(mvp.Mvlan)
568 subFlow.Priority = of.IgmpFlowPriority
569
570 flow.SubFlows[subFlow.Cookie] = subFlow
571 logger.Infow(ctx, "Built DS IGMP flow", log.Fields{"cookie": subFlow.Cookie, "subflow": subFlow})
572 return flow, nil
573}
574
575//updateStaticGroups - Generates static joins & leaves for newly added and removed static channels respectively
Tinoj Joseph07cc5372022-07-18 22:53:51 +0530576func (mvp *MvlanProfile) updateStaticGroups(cntx context.Context, deviceID string, added []net.IP, removed []net.IP) {
Tinoj Josephcf161be2022-07-07 19:47:47 +0530577
578 //Update static group configs for all associated devices
579 updateGroups := func(key interface{}, value interface{}) bool {
580 d := value.(*VoltDevice)
581
582 if mvp.DevicesList[d.SerialNum] == Nil {
583 logger.Infow(ctx, "Mvlan Profile not configure for device", log.Fields{"Device": d, "Profile Device List": mvp.DevicesList})
584 return true
585 }
586 //TODO if mvp.IsChannelBasedGroup {
Tinoj Joseph07cc5372022-07-18 22:53:51 +0530587 mvp.ProcessStaticGroup(cntx, d.Name, added, true)
588 mvp.ProcessStaticGroup(cntx, d.Name, removed, false)
Tinoj Josephcf161be2022-07-07 19:47:47 +0530589 //}
590 return true
591 }
592
593 if deviceID != "" {
594 vd := GetApplication().GetDevice(deviceID)
595 updateGroups(deviceID, vd)
596 } else {
597 GetApplication().DevicesDisc.Range(updateGroups)
598 }
599}
600
601//updateDynamicGroups - Generates joins with updated sources for existing channels
Tinoj Joseph07cc5372022-07-18 22:53:51 +0530602func (mvp *MvlanProfile) updateDynamicGroups(cntx context.Context, deviceID string, added []net.IP, removed []net.IP) {
Tinoj Josephcf161be2022-07-07 19:47:47 +0530603
604 //mvlan := mvp.Mvlan
605 va := GetApplication()
606
607 updateGroups := func(key interface{}, value interface{}) bool {
608 d := value.(*VoltDevice)
609
610 if mvp.DevicesList[d.SerialNum] == Nil {
611 logger.Infow(ctx, "Mvlan Profile not configure for device", log.Fields{"Device": d, "Profile Device List": mvp.DevicesList})
612 return true
613 }
614 for _, groupAddr := range added {
615
616 _, gName := va.GetMvlanProfileForMcIP(mvp.Name, groupAddr)
617 grpKey := mvp.generateGroupKey(gName, groupAddr.String())
618 logger.Debugw(ctx, "IGMP Group", log.Fields{"Group": grpKey, "groupAddr": groupAddr})
619 if igIntf, ok := va.IgmpGroups.Load(grpKey); ok {
620 ig := igIntf.(*IgmpGroup)
Tinoj Joseph07cc5372022-07-18 22:53:51 +0530621 if igd, ok := ig.getIgmpGroupDevice(cntx, d.Name); ok {
Tinoj Josephcf161be2022-07-07 19:47:47 +0530622 if igcIntf, ok := igd.GroupChannels.Load(groupAddr.String()); ok {
623 igc := igcIntf.(*IgmpGroupChannel)
624 incl := false
625 var ip []net.IP
626 var groupModified = false
627 if _, ok := mvp.Proxy[igc.GroupName]; ok {
628 if mvp.Proxy[igc.GroupName].Mode == common.Include {
629 incl = true
630 }
631 ip = mvp.Proxy[igc.GroupName].SourceList
632 }
633 for port, igp := range igc.NewReceivers {
634 // Process the include/exclude list which may end up modifying the group
Tinoj Joseph07cc5372022-07-18 22:53:51 +0530635 if change, _ := igc.ProcessSources(cntx, port, ip, incl); change {
Tinoj Josephcf161be2022-07-07 19:47:47 +0530636 groupModified = true
637 }
638 igc.ProcessMode(port, incl)
639
Tinoj Joseph07cc5372022-07-18 22:53:51 +0530640 if err := igp.WriteToDb(cntx, igc.Mvlan, igc.GroupAddr, igc.Device); err != nil {
Tinoj Josephcf161be2022-07-07 19:47:47 +0530641 logger.Errorw(ctx, "Igmp group port Write to DB failed", log.Fields{"mvlan": igc.Mvlan, "GroupAddr": igc.GroupAddr})
642 }
643 }
644 // If the group is modified as this is the first receiver or due to include/exclude list modification
645 // send a report to the upstream multicast servers
646 if groupModified {
647 logger.Debug(ctx, "Group Modified and IGMP report sent to the upstream server")
648 igc.SendReport(false)
649 }
Tinoj Joseph07cc5372022-07-18 22:53:51 +0530650 if err := igc.WriteToDb(cntx); err != nil {
Tinoj Josephcf161be2022-07-07 19:47:47 +0530651 logger.Errorw(ctx, "Igmp group channel Write to DB failed", log.Fields{"mvlan": igc.Mvlan, "GroupAddr": igc.GroupAddr})
652 }
653 }
654 }
655 }
656 }
657
658 return true
659 }
660
661 if deviceID != "" {
662 vd := GetApplication().GetDevice(deviceID)
663 updateGroups(deviceID, vd)
664 } else {
665 GetApplication().DevicesDisc.Range(updateGroups)
666 }
667}
668
669//GroupsUpdated - Handles removing of Igmp Groups, flows & group table entries for
670//channels removed as part of update
Tinoj Joseph07cc5372022-07-18 22:53:51 +0530671func (mvp *MvlanProfile) GroupsUpdated(cntx context.Context, deviceID string) {
Tinoj Josephcf161be2022-07-07 19:47:47 +0530672
673 deleteChannelIfRemoved := func(key interface{}, value interface{}) bool {
674 ig := value.(*IgmpGroup)
675
676 if ig.Mvlan != mvp.Mvlan {
677 return true
678 }
679 grpName := ig.GroupName
680 logger.Infow(ctx, "###Update Cycle", log.Fields{"IG": ig.GroupName, "Addr": ig.GroupAddr})
681 //Check if group exists and remove the entire group object otherwise
682 if currentChannels := mvp.Groups[grpName]; currentChannels != nil {
683
684 if mvp.IsChannelBasedGroup {
685 channelPresent := doesIPMatch(ig.GroupAddr, currentChannels.McIPs)
686 if channelPresent || mvp.isChannelStatic(ig.GroupAddr) {
687 return true
688 }
689 } else {
690 allExistingChannels := ig.GetAllIgmpChannelForDevice(deviceID)
691 for channel := range allExistingChannels {
692 channelIP := net.ParseIP(channel)
693 channelPresent := mvp.IsChannelPresent(channelIP, currentChannels.McIPs, mvp.IsStaticGroup(ig.GroupName))
694 if channelPresent {
695 staticChannel := mvp.isChannelStatic(channelIP)
696 logger.Infow(ctx, "###Channel Comparision", log.Fields{"staticChannel": staticChannel, "Group": mvp.IsStaticGroup(ig.GroupName), "Channel": channel})
697 // Logic:
698 // If channel is Static & existing Group is also static - No migration required
699 // If channel is not Static & existing Group is also not static - No migration required
700
701 // If channel is Static and existing Group is not static - Migrate (from dynamic to static)
702 // (Channel already part of dynamic, added to static)
703
704 // If channel is not Static but existing Group is static - Migrate (from static to dynamic)
705 // (Channel removed from satic but part of dynamic)
706 if (staticChannel != mvp.IsStaticGroup(ig.GroupName)) || (ig.IsGroupStatic != mvp.IsStaticGroup(ig.GroupName)) { // Equivalent of XOR
Tinoj Joseph07cc5372022-07-18 22:53:51 +0530707 ig.HandleGroupMigration(cntx, deviceID, channelIP)
Tinoj Josephcf161be2022-07-07 19:47:47 +0530708 } else {
709 if (ig.IsGroupStatic) && mvp.IsStaticGroup(ig.GroupName) {
710 if ig.GroupName != mvp.GetStaticGroupName(channelIP) {
Tinoj Joseph07cc5372022-07-18 22:53:51 +0530711 ig.HandleGroupMigration(cntx, deviceID, channelIP)
Tinoj Josephcf161be2022-07-07 19:47:47 +0530712 }
713 }
714 continue
715 }
716 } else {
717 logger.Debugw(ctx, "Channel Removed", log.Fields{"Channel": channel, "Group": grpName})
Tinoj Joseph07cc5372022-07-18 22:53:51 +0530718 ig.DelIgmpChannel(cntx, deviceID, net.ParseIP(channel))
Tinoj Josephcf161be2022-07-07 19:47:47 +0530719 if ig.NumDevicesActive() == 0 {
Tinoj Joseph07cc5372022-07-18 22:53:51 +0530720 GetApplication().DelIgmpGroup(cntx, ig)
Tinoj Josephcf161be2022-07-07 19:47:47 +0530721 }
722 }
723 }
724 ig.IsGroupStatic = mvp.IsStaticGroup(ig.GroupName)
Tinoj Joseph07cc5372022-07-18 22:53:51 +0530725 if err := ig.WriteToDb(cntx); err != nil {
Tinoj Josephcf161be2022-07-07 19:47:47 +0530726 logger.Errorw(ctx, "Igmp group Write to DB failed", log.Fields{"groupName": ig.GroupName})
727 }
728 return true
729 }
730 }
731 logger.Debugw(ctx, "Group Removed", log.Fields{"Channel": ig.GroupAddr, "Group": grpName, "ChannelBasedGroup": ig.IsChannelBasedGroup})
Tinoj Joseph07cc5372022-07-18 22:53:51 +0530732 ig.DelIgmpGroup(cntx)
Tinoj Josephcf161be2022-07-07 19:47:47 +0530733 logger.Debugw(ctx, "Removed Igmp Group", log.Fields{"Channel": ig.GroupAddr, "Group": grpName})
734 return true
735 }
736 GetApplication().IgmpGroups.Range(deleteChannelIfRemoved)
737}
738
739// IsChannelPresent to check if channel is present
740func (mvp *MvlanProfile) IsChannelPresent(channelIP net.IP, groupChannelList []string, IsStaticGroup bool) bool {
741 // Only in case of static group, migration need to be supported.
742 // Dynamic to dynamic group migration not supported currently
743 if doesIPMatch(channelIP, groupChannelList) || mvp.isChannelStatic(channelIP) {
744 return true
745 } else if IsStaticGroup {
746 return (mvp.GetMvlanGroup(channelIP) != "")
747 }
748
749 return false
750}
751
752
753// GetMvlanGroup to get mvlan group
754func (mvp *MvlanProfile) GetMvlanGroup(ip net.IP) string {
755 //Check for Static Group First
756 if mvp.containsStaticChannels() {
757 grpName := mvp.GetStaticGroupName(ip)
758 if grpName != "" {
759 return grpName
760 }
761 }
762
763 for _, mvg := range mvp.Groups {
764 if mvg.Wildcard {
765 return mvg.Name
766 }
767 if doesIPMatch(ip, mvg.McIPs) {
768 return mvg.Name
769 }
770 }
771 return ""
772}
773
774// ProcessStaticGroup - Process Static Join/Leave Req for static channels
Tinoj Joseph07cc5372022-07-18 22:53:51 +0530775func (mvp *MvlanProfile) ProcessStaticGroup(cntx context.Context, device string, groupAddresses []net.IP, isJoin bool) {
Tinoj Josephcf161be2022-07-07 19:47:47 +0530776
777 logger.Debugw(ctx, "Received Static Group Request", log.Fields{"Device": device, "Join": isJoin, "Group Address List": groupAddresses})
778
779 mvlan := mvp.Mvlan
780 va := GetApplication()
781
782 //TODO - Handle bulk add of groupAddr
783 for _, groupAddr := range groupAddresses {
784
785 ig := mvp.GetStaticIgmpGroup(groupAddr)
786 if isJoin {
787 vd := va.GetDevice(device)
788 igmpProf, _, _ := getIgmpProxyCfgAndIP(mvlan, vd.SerialNum)
789 ver := igmpProf.IgmpVerToServer
790
791 if ig == nil {
792 // First time group Creation: Create the IGMP group and then add the receiver to the group
793 logger.Infow(ctx, "Static IGMP Add received for new group", log.Fields{"Addr": groupAddr, "Port": StaticPort})
Tinoj Joseph07cc5372022-07-18 22:53:51 +0530794 if ig := GetApplication().AddIgmpGroup(cntx, mvp.Name, groupAddr, device); ig != nil {
Tinoj Josephcf161be2022-07-07 19:47:47 +0530795 ig.IgmpGroupLock.Lock()
Tinoj Joseph07cc5372022-07-18 22:53:51 +0530796 ig.AddReceiver(cntx, device, StaticPort, groupAddr, nil, getVersion(ver),
Tinoj Josephcf161be2022-07-07 19:47:47 +0530797 0, 0, 0xFF)
798 ig.IgmpGroupLock.Unlock()
799 } else {
800 logger.Warnw(ctx, "Static IGMP Group Creation Failed", log.Fields{"Addr": groupAddr})
801 }
802 } else {
803 //Converting existing dynamic group to static group
804 if !mvp.IsStaticGroup(ig.GroupName) {
Tinoj Joseph07cc5372022-07-18 22:53:51 +0530805 ig.updateGroupName(cntx, ig.GroupName)
Tinoj Josephcf161be2022-07-07 19:47:47 +0530806 }
807 // Update case: If the IGMP group is already created. just add the receiver
808 logger.Infow(ctx, "Static IGMP Add received for existing group", log.Fields{"Addr": groupAddr, "Port": StaticPort})
809 ig.IgmpGroupLock.Lock()
Tinoj Joseph07cc5372022-07-18 22:53:51 +0530810 ig.AddReceiver(cntx, device, StaticPort, groupAddr, nil, getVersion(ver),
Tinoj Josephcf161be2022-07-07 19:47:47 +0530811 0, 0, 0xFF)
812 ig.IgmpGroupLock.Unlock()
813 }
814 } else if ig != nil {
815 logger.Infow(ctx, "Static IGMP Del received for existing group", log.Fields{"Addr": groupAddr, "Port": StaticPort})
816
817 if ig.IsChannelBasedGroup {
818 grpName := mvp.GetMvlanGroup(ig.GroupAddr)
819 if grpName != "" {
820 ig.IgmpGroupLock.Lock()
Tinoj Joseph07cc5372022-07-18 22:53:51 +0530821 ig.DelReceiver(cntx, device, StaticPort, groupAddr, nil, 0xFF)
Tinoj Josephcf161be2022-07-07 19:47:47 +0530822 ig.IgmpGroupLock.Unlock()
Tinoj Joseph07cc5372022-07-18 22:53:51 +0530823 ig.updateGroupName(cntx, grpName)
Tinoj Josephcf161be2022-07-07 19:47:47 +0530824 } else {
Tinoj Joseph07cc5372022-07-18 22:53:51 +0530825 ig.DelIgmpGroup(cntx)
Tinoj Josephcf161be2022-07-07 19:47:47 +0530826 }
827 } else {
828 ig.IgmpGroupLock.Lock()
Tinoj Joseph07cc5372022-07-18 22:53:51 +0530829 ig.DelReceiver(cntx, device, StaticPort, groupAddr, nil, 0xFF)
Tinoj Josephcf161be2022-07-07 19:47:47 +0530830 ig.IgmpGroupLock.Unlock()
831 }
832 if ig.NumDevicesActive() == 0 {
Tinoj Joseph07cc5372022-07-18 22:53:51 +0530833 GetApplication().DelIgmpGroup(cntx, ig)
Tinoj Josephcf161be2022-07-07 19:47:47 +0530834 }
835 } else {
836 logger.Warnw(ctx, "Static IGMP Del received for unknown group", log.Fields{"Addr": groupAddr})
837 }
838 }
839}
840
841//getStaticChannelDiff - return the static channel newly added and removed from existing static group
842func (mvp *MvlanProfile) getStaticChannelDiff() (newlyAdded []net.IP, removed []net.IP, common []net.IP) {
843
844 var commonChannels []net.IP
845 newChannelList, _ := mvp.getAllStaticChannels()
846 existingChannelList, _ := mvp.getAllOldGroupStaticChannels()
847 if len(existingChannelList) == 0 {
848 return newChannelList, []net.IP{}, []net.IP{}
849 }
850 for _, newChannel := range append([]net.IP{}, newChannelList...) {
851 for _, existChannel := range append([]net.IP{}, existingChannelList...) {
852
853 //Remove common channels between existing and new list
854 // The remaining in the below slices give the results
855 // Remaining in newChannelList: Newly added
856 // Remaining in existingChannelList: Removed channels
857 if existChannel.Equal(newChannel) {
858 existingChannelList = removeIPFromList(existingChannelList, existChannel)
859 newChannelList = removeIPFromList(newChannelList, newChannel)
860 commonChannels = append(commonChannels, newChannel)
861 logger.Infow(ctx, "#############Channel: "+existChannel.String()+" New: "+newChannel.String(), log.Fields{"Added": newChannelList, "Removed": existingChannelList})
862 break
863 }
864 }
865 }
866 return newChannelList, existingChannelList, commonChannels
867}
868
869//getGroupChannelDiff - return the channel newly added and removed from existing group
870func (mvp *MvlanProfile) getGroupChannelDiff(newGroup *MvlanGroup, oldGroup *MvlanGroup) (newlyAdded []net.IP, removed []net.IP, common []net.IP) {
871
872 var commonChannels []net.IP
873 newChannelList, _ := newGroup.getAllChannels()
874 existingChannelList, _ := oldGroup.getAllChannels()
875 if len(existingChannelList) == 0 {
876 return newChannelList, []net.IP{}, []net.IP{}
877 }
878 for _, newChannel := range append([]net.IP{}, newChannelList...) {
879 for _, existChannel := range append([]net.IP{}, existingChannelList...) {
880
881 //Remove common channels between existing and new list
882 // The remaining in the below slices give the results
883 // Remaining in newChannelList: Newly added
884 // Remaining in existingChannelList: Removed channels
885 if existChannel.Equal(newChannel) {
886 existingChannelList = removeIPFromList(existingChannelList, existChannel)
887 newChannelList = removeIPFromList(newChannelList, newChannel)
888 commonChannels = append(commonChannels, newChannel)
889 logger.Infow(ctx, "#############Channel: "+existChannel.String()+" New: "+newChannel.String(), log.Fields{"Added": newChannelList, "Removed": existingChannelList})
890 break
891 }
892 }
893 }
894 return newChannelList, existingChannelList, commonChannels
895}
896
897// UpdateProfile - Updates the group & member info w.r.t the mvlan profile for the given device
Tinoj Joseph07cc5372022-07-18 22:53:51 +0530898func (mvp *MvlanProfile) UpdateProfile(cntx context.Context, deviceID string) {
Tinoj Josephcf161be2022-07-07 19:47:47 +0530899 logger.Infow(ctx, "Update Mvlan Profile task triggered", log.Fields{"Mvlan": mvp.Mvlan})
900 var removedStaticChannels []net.IP
901 addedStaticChannels := []net.IP{}
902 /* Taking mvpLock to protect the mvp groups and proxy */
903 mvp.mvpLock.RLock()
904 defer mvp.mvpLock.RUnlock()
905
906 serialNo := ""
907 if deviceID != "" {
908 if vd := GetApplication().GetDevice(deviceID); vd != nil {
909 serialNo = vd.SerialNum
910 if mvp.DevicesList[serialNo] != UpdateInProgress {
911 logger.Warnw(ctx, "Exiting Update Task since device not present in MvlanProfile", log.Fields{"Device": deviceID, "SerialNum": vd.SerialNum, "MvlanProfile": mvp})
912 return
913 }
914 } else {
915 logger.Errorw(ctx, "Volt Device not found. Stopping Update Mvlan Profile processing for device", log.Fields{"SerialNo": deviceID, "MvlanProfile": mvp})
916 return
917 }
918 }
919
920 //Update the groups based on static channels added & removed
921 if mvp.containsStaticChannels() {
922 addedStaticChannels, removedStaticChannels, _ = mvp.getStaticChannelDiff()
923 logger.Debugw(ctx, "Update Task - Static Group Changes", log.Fields{"Added": addedStaticChannels, "Removed": removedStaticChannels})
924
925 if len(addedStaticChannels) > 0 || len(removedStaticChannels) > 0 {
Tinoj Joseph07cc5372022-07-18 22:53:51 +0530926 mvp.updateStaticGroups(cntx, deviceID, []net.IP{}, removedStaticChannels)
Tinoj Josephcf161be2022-07-07 19:47:47 +0530927 }
928 }
Tinoj Joseph07cc5372022-07-18 22:53:51 +0530929 mvp.GroupsUpdated(cntx, deviceID)
Tinoj Josephcf161be2022-07-07 19:47:47 +0530930 if len(addedStaticChannels) > 0 {
Tinoj Joseph07cc5372022-07-18 22:53:51 +0530931 mvp.updateStaticGroups(cntx, deviceID, addedStaticChannels, []net.IP{})
Tinoj Josephcf161be2022-07-07 19:47:47 +0530932 }
933
934 /* Need to handle if SSM params are modified for groups */
935 for key := range mvp.Groups {
936 _, _, commonChannels := mvp.getGroupChannelDiff(mvp.Groups[key], mvp.oldGroups[key])
937 if mvp.checkStaticGrpSSMProxyDiff(mvp.oldProxy[key], mvp.Proxy[key]) {
938 if mvp.Groups[key].IsStatic {
939 /* Static group proxy modified, need to trigger membership report with new mode/src-list for existing channels */
Tinoj Joseph07cc5372022-07-18 22:53:51 +0530940 mvp.updateStaticGroups(cntx, deviceID, commonChannels, []net.IP{})
Tinoj Josephcf161be2022-07-07 19:47:47 +0530941 } else {
942 /* Dynamic group proxy modified, need to trigger membership report with new mode/src-list for existing channels */
Tinoj Joseph07cc5372022-07-18 22:53:51 +0530943 mvp.updateDynamicGroups(cntx, deviceID, commonChannels, []net.IP{})
Tinoj Josephcf161be2022-07-07 19:47:47 +0530944 }
945 }
946 }
947
948 mvp.SetUpdateStatus(serialNo, NoOp)
949
950 if deviceID == "" || !mvp.isUpdateInProgress() {
951 mvp.oldGroups = nil
952 }
Tinoj Joseph07cc5372022-07-18 22:53:51 +0530953 if err := mvp.WriteToDb(cntx); err != nil {
Tinoj Josephcf161be2022-07-07 19:47:47 +0530954 logger.Errorw(ctx, "Mvlan profile write to DB failed", log.Fields{"ProfileName": mvp.Name})
955 }
956 logger.Debugw(ctx, "Updated MVLAN Profile", log.Fields{"VLAN": mvp.Mvlan, "Name": mvp.Name, "Grp IPs": mvp.Groups})
957}
958
959//checkStaticGrpSSMProxyDiff- return true if the proxy of oldGroup is modified in newGroup
960func (mvp *MvlanProfile) checkStaticGrpSSMProxyDiff(oldProxy *MCGroupProxy, newProxy *MCGroupProxy) bool {
961
962 if oldProxy == nil && newProxy == nil {
963 return false
964 }
965 if (oldProxy == nil && newProxy != nil) ||
966 (oldProxy != nil && newProxy == nil) {
967 return true
968 }
969
970 if oldProxy.Mode != newProxy.Mode {
971 return true
972 }
973
974 oldSrcLst := oldProxy.SourceList
975 newSrcLst := newProxy.SourceList
976 oLen := len(oldSrcLst)
977 nLen := len(newSrcLst)
978 if oLen != nLen {
979 return true
980 }
981
982 visited := make([]bool, nLen)
983
984 /* check if any new IPs added in the src list, return true if present */
985 for i := 0; i < nLen; i++ {
986 found := false
987 element := newSrcLst[i]
988 for j := 0; j < oLen; j++ {
989 if visited[j] {
990 continue
991 }
992 if element.Equal(oldSrcLst[j]) {
993 visited[j] = true
994 found = true
995 break
996 }
997 }
998 if !found {
999 return true
1000 }
1001 }
1002
1003 visited = make([]bool, nLen)
1004 /* check if any IPs removed from existing src list, return true if removed */
1005 for i := 0; i < oLen; i++ {
1006 found := false
1007 element := oldSrcLst[i]
1008 for j := 0; j < nLen; j++ {
1009 if visited[j] {
1010 continue
1011 }
1012 if element.Equal(newSrcLst[j]) {
1013 visited[j] = true
1014 found = true
1015 break
1016 }
1017 }
1018 if !found {
1019 return true
1020 }
1021 }
1022 return false
1023}
1024
1025
1026//UpdateActiveChannelSubscriberAlarm - Updates the Active Channel Subscriber Alarm
1027func (mvp *MvlanProfile) UpdateActiveChannelSubscriberAlarm() {
1028 va := GetApplication()
1029 logger.Debugw(ctx, "Update of Active Channel Subscriber Alarm", log.Fields{"Mvlan": mvp.Mvlan})
1030 for srNo := range mvp.DevicesList {
1031 d := va.GetDeviceBySerialNo(srNo)
1032 if d == nil {
1033 logger.Warnw(ctx, "Device info not found", log.Fields{"Device_SrNo": srNo, "Mvlan": mvp.Mvlan})
1034 return
1035 }
1036 d.Ports.Range(func(key, value interface{}) bool {
1037 //port := key.(string)
1038 vp := value.(*VoltPort)
1039 if vp.Type != VoltPortTypeAccess {
1040 return true
1041 }
1042 if mvp.MaxActiveChannels > vp.ActiveChannels && vp.ChannelPerSubAlarmRaised {
1043 serviceName := GetMcastServiceForSubAlarm(vp, mvp)
1044 logger.Debugw(ctx, "Clearing-SendActiveChannelPerSubscriberAlarm-due-to-update", log.Fields{"ActiveChannels": vp.ActiveChannels, "ServiceName": serviceName})
1045 vp.ChannelPerSubAlarmRaised = false
1046 } else if mvp.MaxActiveChannels < vp.ActiveChannels && !vp.ChannelPerSubAlarmRaised {
1047 /* When the max active channel count is reduced via update, we raise an alarm.
1048 But the previous excess channels still exist until a leave or expiry */
1049 serviceName := GetMcastServiceForSubAlarm(vp, mvp)
1050 logger.Debugw(ctx, "Raising-SendActiveChannelPerSubscriberAlarm-due-to-update", log.Fields{"ActiveChannels": vp.ActiveChannels, "ServiceName": serviceName})
1051 vp.ChannelPerSubAlarmRaised = true
1052 }
1053 return true
1054 })
1055 }
1056}
1057
1058//TriggerAssociatedFlowDelete - Re-trigger delete for pending delete flows
Tinoj Joseph07cc5372022-07-18 22:53:51 +05301059func (mvp *MvlanProfile) TriggerAssociatedFlowDelete(cntx context.Context, device string) bool {
Tinoj Josephcf161be2022-07-07 19:47:47 +05301060 mvp.mvpFlowLock.Lock()
1061
1062 cookieList := []uint64{}
1063 flowMap := mvp.PendingDeleteFlow[device]
1064
1065 for cookie := range flowMap {
1066 cookieList = append(cookieList, convertToUInt64(cookie))
1067 }
1068 mvp.mvpFlowLock.Unlock()
1069
1070 if len(cookieList) == 0 {
1071 return false
1072 }
1073
1074 for _, cookie := range cookieList {
1075 if vd := GetApplication().GetDevice(device); vd != nil {
1076 flow := &of.VoltFlow{}
1077 flow.SubFlows = make(map[uint64]*of.VoltSubFlow)
1078 subFlow := of.NewVoltSubFlow()
1079 subFlow.Cookie = cookie
1080 flow.SubFlows[cookie] = subFlow
1081 logger.Infow(ctx, "Retriggering Vnet Delete Flow", log.Fields{"Device": device, "Mvlan": mvp.Mvlan.String(), "Cookie": cookie})
Tinoj Joseph07cc5372022-07-18 22:53:51 +05301082 err := mvp.DelFlows(cntx, vd, flow)
Tinoj Josephcf161be2022-07-07 19:47:47 +05301083 if err != nil {
1084 logger.Warnw(ctx, "De-Configuring IGMP Flow for device failed ", log.Fields{"Device": device, "err": err})
1085 }
1086 }
1087 }
1088 return true
1089}
1090
1091// JsonMarshal wrapper function for json Marshal MvlanProfile
1092func (mvp *MvlanProfile) JsonMarshal() ([]byte, error) {
1093 return json.Marshal(MvlanProfile{
1094 Name: mvp.Name,
1095 Mvlan: mvp.Mvlan,
1096 PonVlan: mvp.PonVlan,
1097 Groups: mvp.Groups,
1098 Proxy: mvp.Proxy,
1099 Version: mvp.Version,
1100 IsPonVlanPresent: mvp.IsPonVlanPresent,
1101 IsChannelBasedGroup: mvp.IsChannelBasedGroup,
1102 DevicesList: mvp.DevicesList,
1103 MaxActiveChannels: mvp.MaxActiveChannels,
1104 PendingDeleteFlow: mvp.PendingDeleteFlow,
1105 DeleteInProgress: mvp.DeleteInProgress,
1106 IgmpServVersion: mvp.IgmpServVersion,
1107 })
1108}
1109
1110// removeIPFromList to remove ip from the list
1111func removeIPFromList(s []net.IP, value net.IP) []net.IP {
1112 i := 0
1113 for i = 0; i < len(s); i++ {
1114 if s[i].Equal(value) {
1115 break
1116 }
1117 }
1118 if i != len(s) {
1119 //It means value is found in the slice
1120 return append(s[0:i], s[i+1:]...)
1121 }
1122 return s
1123}
1124
1125// doesIPMatch to check if ip match with any ip from the list
1126func doesIPMatch(ip net.IP, ipsOrRange []string) bool {
1127 for _, ipOrRange := range ipsOrRange {
1128 if strings.Contains(ipOrRange, "-") {
1129 var splits = strings.Split(ipOrRange, "-")
1130 ipStart := util.IP2LongConv(net.ParseIP(splits[0]))
1131 ipEnd := util.IP2LongConv(net.ParseIP(splits[1]))
1132 if ipEnd < ipStart {
1133 return false
1134 }
1135 ipInt := util.IP2LongConv(ip)
1136 if ipInt >= ipStart && ipInt <= ipEnd {
1137 return true
1138 }
1139 } else if ip.Equal(net.ParseIP(ipOrRange)) {
1140 return true
1141 }
1142 }
1143 return false
1144}
1145
1146// IgmpProfile structure
1147type IgmpProfile struct {
1148 ProfileID string
1149 UnsolicitedTimeOut uint32 //In seconds
1150 MaxResp uint32
1151 KeepAliveInterval uint32
1152 KeepAliveCount uint32
1153 LastQueryInterval uint32
1154 LastQueryCount uint32
1155 FastLeave bool
1156 PeriodicQuery bool
1157 IgmpCos uint8
1158 WithRAUpLink bool
1159 WithRADownLink bool
1160 IgmpVerToServer string
1161 IgmpSourceIP net.IP
1162 Version string
1163}
1164
1165func newIgmpProfile(igmpProfileConfig *common.IGMPConfig) *IgmpProfile {
1166 var igmpProfile IgmpProfile
1167 igmpProfile.ProfileID = igmpProfileConfig.ProfileID
1168 igmpProfile.UnsolicitedTimeOut = uint32(igmpProfileConfig.UnsolicitedTimeOut)
1169 igmpProfile.MaxResp = uint32(igmpProfileConfig.MaxResp)
1170
1171 keepAliveInterval := uint32(igmpProfileConfig.KeepAliveInterval)
1172
1173 //KeepAliveInterval should have a min of 10 seconds
1174 if keepAliveInterval < MinKeepAliveInterval {
1175 keepAliveInterval = MinKeepAliveInterval
1176 logger.Infow(ctx, "Auto adjust keepAliveInterval - Value < 10", log.Fields{"Received": igmpProfileConfig.KeepAliveInterval, "Configured": keepAliveInterval})
1177 }
1178 igmpProfile.KeepAliveInterval = keepAliveInterval
1179
1180 igmpProfile.KeepAliveCount = uint32(igmpProfileConfig.KeepAliveCount)
1181 igmpProfile.LastQueryInterval = uint32(igmpProfileConfig.LastQueryInterval)
1182 igmpProfile.LastQueryCount = uint32(igmpProfileConfig.LastQueryCount)
1183 igmpProfile.FastLeave = *igmpProfileConfig.FastLeave
1184 igmpProfile.PeriodicQuery = *igmpProfileConfig.PeriodicQuery
1185 igmpProfile.IgmpCos = uint8(igmpProfileConfig.IgmpCos)
1186 igmpProfile.WithRAUpLink = *igmpProfileConfig.WithRAUpLink
1187 igmpProfile.WithRADownLink = *igmpProfileConfig.WithRADownLink
1188
1189 if igmpProfileConfig.IgmpVerToServer == "2" || igmpProfileConfig.IgmpVerToServer == "v2" {
1190 igmpProfile.IgmpVerToServer = "2"
1191 } else {
1192 igmpProfile.IgmpVerToServer = "3"
1193 }
1194 igmpProfile.IgmpSourceIP = net.ParseIP(igmpProfileConfig.IgmpSourceIP)
1195
1196 return &igmpProfile
1197}
1198
1199// newDefaultIgmpProfile Igmp profiles with default values
1200func newDefaultIgmpProfile() *IgmpProfile {
1201 return &IgmpProfile{
1202 ProfileID: DefaultIgmpProfID,
1203 UnsolicitedTimeOut: 60,
1204 MaxResp: 10, // seconds
1205 KeepAliveInterval: 60, // seconds
1206 KeepAliveCount: 3, // TODO - May not be needed
1207 LastQueryInterval: 0, // TODO - May not be needed
1208 LastQueryCount: 0, // TODO - May not be needed
1209 FastLeave: true,
1210 PeriodicQuery: false, // TODO - May not be needed
1211 IgmpCos: 7, //p-bit value included in the IGMP packet
1212 WithRAUpLink: false, // TODO - May not be needed
1213 WithRADownLink: false, // TODO - May not be needed
1214 IgmpVerToServer: "3",
1215 IgmpSourceIP: net.ParseIP("172.27.0.1"), // This will be replaced by configuration
1216 }
1217}
1218
1219// WriteToDb is utility to write Igmp Config Info to database
Tinoj Joseph07cc5372022-07-18 22:53:51 +05301220func (igmpProfile *IgmpProfile) WriteToDb(cntx context.Context) error {
Tinoj Josephcf161be2022-07-07 19:47:47 +05301221 igmpProfile.Version = database.PresentVersionMap[database.IgmpProfPath]
1222 b, err := json.Marshal(igmpProfile)
1223 if err != nil {
1224 return err
1225 }
Tinoj Joseph07cc5372022-07-18 22:53:51 +05301226 if err1 := db.PutIgmpProfile(cntx, igmpProfile.ProfileID, string(b)); err1 != nil {
Tinoj Josephcf161be2022-07-07 19:47:47 +05301227 return err1
1228 }
1229 return nil
1230}