blob: a1d999cb0a3ebdb36bac0621921a1bb5c71fed41 [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.
vinokuma926cb3e2023-03-29 11:41:06 +053014 */
Tinoj Josephcf161be2022-07-07 19:47:47 +053015
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
Tinoj Josephcf161be2022-07-07 19:47:47 +053027 "voltha-go-controller/database"
vinokuma926cb3e2023-03-29 11:41:06 +053028 cntlr "voltha-go-controller/internal/pkg/controller"
Tinoj Josephcf161be2022-07-07 19:47:47 +053029 "voltha-go-controller/internal/pkg/of"
vinokuma926cb3e2023-03-29 11:41:06 +053030 common "voltha-go-controller/internal/pkg/types"
Tinoj Josephcf161be2022-07-07 19:47:47 +053031 "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 {
vinokuma926cb3e2023-03-29 11:41:06 +053048 // Mode represents source list include/exclude
49 Mode common.MulticastSrcListMode
50 // SourceList represents list of multicast server IP addresses.
51 SourceList []net.IP
Tinoj Josephcf161be2022-07-07 19:47:47 +053052}
53
54// MvlanGroup identifies MC group info
55type MvlanGroup struct {
vinokuma926cb3e2023-03-29 11:41:06 +053056 Name string
57 McIPs []string
58 Wildcard bool
59 IsStatic bool
Tinoj Josephcf161be2022-07-07 19:47:47 +053060}
61
62// OperInProgress type
63type OperInProgress uint8
64
65const (
vinokuma926cb3e2023-03-29 11:41:06 +053066 // UpdateInProgress constant
67 UpdateInProgress OperInProgress = 2
68 // NoOp constant
69 NoOp OperInProgress = 1
70 // Nil constant
71 Nil OperInProgress = 0
Tinoj Josephcf161be2022-07-07 19:47:47 +053072)
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 {
vinokuma926cb3e2023-03-29 11:41:06 +053079 Groups map[string]*MvlanGroup
80 Proxy map[string]*MCGroupProxy
81 oldGroups map[string]*MvlanGroup
82 oldProxy map[string]*MCGroupProxy
83 IgmpServVersion map[string]*uint8
84 PendingDeleteFlow map[string]map[string]bool
85 DevicesList map[string]OperInProgress //device serial number //here
86 Version string
87 Name string
88 mvpLock sync.RWMutex
89 mvpFlowLock sync.RWMutex
90 MaxActiveChannels uint32
91 Mvlan of.VlanType
92 PonVlan of.VlanType
93 IsPonVlanPresent bool
94 IsChannelBasedGroup bool
95 DeleteInProgress bool
Tinoj Josephcf161be2022-07-07 19:47:47 +053096}
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 {
vinokuma926cb3e2023-03-29 11:41:06 +0530100 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)
Tinoj Josephcf161be2022-07-07 19:47:47 +0530113
vinokuma926cb3e2023-03-29 11:41:06 +0530114 if (ponVlan != of.VlanNone) && (ponVlan != 0) {
115 mvp.IsPonVlanPresent = true
116 }
117 return &mvp
Tinoj Josephcf161be2022-07-07 19:47:47 +0530118}
119
120// AddMvlanProxy for addition of groups to an MVLAN profile
121func (mvp *MvlanProfile) AddMvlanProxy(name string, proxyInfo common.MulticastGroupProxy) {
vinokuma926cb3e2023-03-29 11:41:06 +0530122 proxy := &MCGroupProxy{}
123 proxy.Mode = proxyInfo.Mode
124 proxy.SourceList = util.GetExpIPList(proxyInfo.SourceList)
Tinoj Josephcf161be2022-07-07 19:47:47 +0530125
vinokuma926cb3e2023-03-29 11:41:06 +0530126 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
Tinoj Josephcf161be2022-07-07 19:47:47 +0530135}
136
137// AddMvlanGroup for addition of groups to an MVLAN profile
138func (mvp *MvlanProfile) AddMvlanGroup(name string, ips []string) {
vinokuma926cb3e2023-03-29 11:41:06 +0530139 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
Tinoj Josephcf161be2022-07-07 19:47:47 +0530150}
151
152// GetUsMatchVlan provides mvlan for US Match parameter
153func (mvp *MvlanProfile) GetUsMatchVlan() of.VlanType {
vinokuma926cb3e2023-03-29 11:41:06 +0530154 if mvp.IsPonVlanPresent {
155 return mvp.PonVlan
156 }
157 return mvp.Mvlan
Tinoj Josephcf161be2022-07-07 19:47:47 +0530158}
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 {
vinokuma926cb3e2023-03-29 11:41:06 +0530162 if mvp.DeleteInProgress {
163 logger.Warnw(ctx, "Skipping Redis Update for MvlanProfile, MvlanProfile delete in progress", log.Fields{"Mvlan": mvp.Mvlan})
164 return nil
165 }
Tinoj Josephcf161be2022-07-07 19:47:47 +0530166
vinokuma926cb3e2023-03-29 11:41:06 +0530167 mvp.Version = database.PresentVersionMap[database.MvlanPath]
168 b, err := json.Marshal(mvp)
169 if err != nil {
170 return err
171 }
172 if err1 := db.PutMvlan(cntx, uint16(mvp.Mvlan), string(b)); err1 != nil {
173 return err1
174 }
175 return nil
Tinoj Josephcf161be2022-07-07 19:47:47 +0530176}
177
vinokuma926cb3e2023-03-29 11:41:06 +0530178// isChannelStatic - Returns true if the given channel is part of static group in the Mvlan Profile
Tinoj Josephcf161be2022-07-07 19:47:47 +0530179func (mvp *MvlanProfile) isChannelStatic(channel net.IP) bool {
vinokuma926cb3e2023-03-29 11:41:06 +0530180 for _, mvg := range mvp.Groups {
181 if mvg.IsStatic {
182 if isChannelStatic := doesIPMatch(channel, mvg.McIPs); isChannelStatic {
183 return true
184 }
185 }
186 }
187 return false
Tinoj Josephcf161be2022-07-07 19:47:47 +0530188}
189
vinokuma926cb3e2023-03-29 11:41:06 +0530190// containsStaticChannels - Returns if any static channels is part of the Mvlan Profile
Tinoj Josephcf161be2022-07-07 19:47:47 +0530191func (mvp *MvlanProfile) containsStaticChannels() bool {
vinokuma926cb3e2023-03-29 11:41:06 +0530192 for _, mvg := range mvp.Groups {
193 if mvg.IsStatic && len(mvg.McIPs) != 0 {
194 return true
195 }
196 }
197 return false
Tinoj Josephcf161be2022-07-07 19:47:47 +0530198}
199
vinokuma926cb3e2023-03-29 11:41:06 +0530200// getAllStaticChannels - Returns all static channels in the Mvlan Profile
Tinoj Josephcf161be2022-07-07 19:47:47 +0530201func (mvp *MvlanProfile) getAllStaticChannels() ([]net.IP, bool) {
vinokuma926cb3e2023-03-29 11:41:06 +0530202 channelList := []net.IP{}
203 containsStatic := false
204 for _, mvg := range mvp.Groups {
205 if mvg.IsStatic {
206 staticChannels, _ := mvg.getAllChannels()
207 channelList = append(channelList, staticChannels...)
208 }
209 }
210 if len(channelList) > 0 {
211 containsStatic = true
212 }
213 return channelList, containsStatic
Tinoj Josephcf161be2022-07-07 19:47:47 +0530214}
215
vinokuma926cb3e2023-03-29 11:41:06 +0530216// getAllOldGroupStaticChannels - Returns all static channels in the Mvlan Profile
Tinoj Josephcf161be2022-07-07 19:47:47 +0530217func (mvp *MvlanProfile) getAllOldGroupStaticChannels() ([]net.IP, bool) {
vinokuma926cb3e2023-03-29 11:41:06 +0530218 channelList := []net.IP{}
219 containsStatic := false
220 for _, mvg := range mvp.oldGroups {
221 if mvg.IsStatic {
222 staticChannels, _ := mvg.getAllChannels()
223 channelList = append(channelList, staticChannels...)
224 }
225 }
226 if len(channelList) > 0 {
227 containsStatic = true
228 }
229 return channelList, containsStatic
Tinoj Josephcf161be2022-07-07 19:47:47 +0530230}
231
vinokuma926cb3e2023-03-29 11:41:06 +0530232// getAllChannels - Returns all channels in the Mvlan Profile
Tinoj Josephcf161be2022-07-07 19:47:47 +0530233func (mvg *MvlanGroup) getAllChannels() ([]net.IP, bool) {
vinokuma926cb3e2023-03-29 11:41:06 +0530234 channelList := []net.IP{}
Tinoj Josephcf161be2022-07-07 19:47:47 +0530235
vinokuma926cb3e2023-03-29 11:41:06 +0530236 if mvg == nil || len(mvg.McIPs) == 0 {
237 return []net.IP{}, false
238 }
Tinoj Josephcf161be2022-07-07 19:47:47 +0530239
vinokuma926cb3e2023-03-29 11:41:06 +0530240 grpChannelOrRange := mvg.McIPs
241 for _, channelOrRange := range grpChannelOrRange {
242 if strings.Contains(channelOrRange, "-") {
243 var splits = strings.Split(channelOrRange, "-")
244 ipStart := util.IP2LongConv(net.ParseIP(splits[0]))
245 ipEnd := util.IP2LongConv(net.ParseIP(splits[1]))
Tinoj Josephcf161be2022-07-07 19:47:47 +0530246
vinokuma926cb3e2023-03-29 11:41:06 +0530247 for i := ipStart; i <= ipEnd; i++ {
248 channelList = append(channelList, util.Long2ipConv(i))
249 }
250 } else {
251 channelList = append(channelList, net.ParseIP(channelOrRange))
252 }
253 }
254 return channelList, true
Tinoj Josephcf161be2022-07-07 19:47:47 +0530255}
256
vinokuma926cb3e2023-03-29 11:41:06 +0530257// SetUpdateStatus - Sets profile update status for devices
Tinoj Josephcf161be2022-07-07 19:47:47 +0530258func (mvp *MvlanProfile) SetUpdateStatus(serialNum string, status OperInProgress) {
vinokuma926cb3e2023-03-29 11:41:06 +0530259 if serialNum != "" {
260 mvp.DevicesList[serialNum] = status
261 return
262 }
Tinoj Josephcf161be2022-07-07 19:47:47 +0530263
vinokuma926cb3e2023-03-29 11:41:06 +0530264 for srNo := range mvp.DevicesList {
265 mvp.DevicesList[srNo] = status
266 }
Tinoj Josephcf161be2022-07-07 19:47:47 +0530267}
268
vinokuma926cb3e2023-03-29 11:41:06 +0530269// isUpdateInProgress - checking is update is in progress for the mvlan profile
Tinoj Josephcf161be2022-07-07 19:47:47 +0530270func (mvp *MvlanProfile) isUpdateInProgress() bool {
vinokuma926cb3e2023-03-29 11:41:06 +0530271 for srNo := range mvp.DevicesList {
272 if mvp.DevicesList[srNo] == UpdateInProgress {
273 return true
274 }
275 }
276 return false
Tinoj Josephcf161be2022-07-07 19:47:47 +0530277}
278
vinokuma926cb3e2023-03-29 11:41:06 +0530279// IsUpdateInProgressForDevice - Checks is Mvlan Profile update is is progress for the given device
Tinoj Josephcf161be2022-07-07 19:47:47 +0530280func (mvp *MvlanProfile) IsUpdateInProgressForDevice(device string) bool {
vinokuma926cb3e2023-03-29 11:41:06 +0530281 if vd := GetApplication().GetDevice(device); vd != nil {
282 if mvp.DevicesList[vd.SerialNum] == UpdateInProgress {
283 return true
284 }
285 }
286 return false
Tinoj Josephcf161be2022-07-07 19:47:47 +0530287}
288
289// DelFromDb to delere mvlan from database
Tinoj Joseph07cc5372022-07-18 22:53:51 +0530290func (mvp *MvlanProfile) DelFromDb(cntx context.Context) {
vinokuma926cb3e2023-03-29 11:41:06 +0530291 _ = db.DelMvlan(cntx, uint16(mvp.Mvlan))
Tinoj Josephcf161be2022-07-07 19:47:47 +0530292}
293
vinokuma926cb3e2023-03-29 11:41:06 +0530294// DelFlows - Triggers flow deletion after registering for flow indication event
Tinoj Joseph07cc5372022-07-18 22:53:51 +0530295func (mvp *MvlanProfile) DelFlows(cntx context.Context, device *VoltDevice, flow *of.VoltFlow) error {
vinokuma926cb3e2023-03-29 11:41:06 +0530296 mvp.mvpFlowLock.Lock()
297 defer mvp.mvpFlowLock.Unlock()
Tinoj Josephcf161be2022-07-07 19:47:47 +0530298
vinokuma926cb3e2023-03-29 11:41:06 +0530299 var flowMap map[string]bool
300 var ok bool
Tinoj Josephcf161be2022-07-07 19:47:47 +0530301
vinokuma926cb3e2023-03-29 11:41:06 +0530302 for cookie := range flow.SubFlows {
303 cookie := strconv.FormatUint(cookie, 10)
304 fe := &FlowEvent{
305 eType: EventTypeMcastFlowRemoved,
306 device: device.Name,
307 cookie: cookie,
308 eventData: mvp,
309 }
310 device.RegisterFlowDelEvent(cookie, fe)
Tinoj Josephcf161be2022-07-07 19:47:47 +0530311
vinokuma926cb3e2023-03-29 11:41:06 +0530312 if flowMap, ok = mvp.PendingDeleteFlow[device.Name]; !ok {
313 flowMap = make(map[string]bool)
314 }
315 flowMap[cookie] = true
316 mvp.PendingDeleteFlow[device.Name] = flowMap
317 }
318 if err := mvp.WriteToDb(cntx); err != nil {
319 logger.Errorw(ctx, "Mvlan profile write to DB failed", log.Fields{"ProfileName": mvp.Name})
320 }
321 return cntlr.GetController().DelFlows(cntx, device.NniPort, device.Name, flow)
Tinoj Josephcf161be2022-07-07 19:47:47 +0530322}
323
vinokuma926cb3e2023-03-29 11:41:06 +0530324// FlowRemoveSuccess - Process flow success indication
Tinoj Joseph07cc5372022-07-18 22:53:51 +0530325func (mvp *MvlanProfile) FlowRemoveSuccess(cntx context.Context, cookie string, device string) {
vinokuma926cb3e2023-03-29 11:41:06 +0530326 mvp.mvpFlowLock.Lock()
327 defer mvp.mvpFlowLock.Unlock()
Tinoj Josephcf161be2022-07-07 19:47:47 +0530328
vinokuma926cb3e2023-03-29 11:41:06 +0530329 logger.Infow(ctx, "Mvlan Flow Remove Success Notification", log.Fields{"MvlanProfile": mvp.Name, "Cookie": cookie, "Device": device})
Tinoj Josephcf161be2022-07-07 19:47:47 +0530330
vinokuma926cb3e2023-03-29 11:41:06 +0530331 if _, ok := mvp.PendingDeleteFlow[device]; ok {
332 delete(mvp.PendingDeleteFlow[device], cookie)
333 }
Tinoj Josephcf161be2022-07-07 19:47:47 +0530334
vinokuma926cb3e2023-03-29 11:41:06 +0530335 if err := mvp.WriteToDb(cntx); err != nil {
336 logger.Errorw(ctx, "Mvlan profile write to DB failed", log.Fields{"ProfileName": mvp.Name})
337 }
Tinoj Josephcf161be2022-07-07 19:47:47 +0530338}
339
vinokuma926cb3e2023-03-29 11:41:06 +0530340// FlowRemoveFailure - Process flow failure indication
Tinoj Josephcf161be2022-07-07 19:47:47 +0530341func (mvp *MvlanProfile) FlowRemoveFailure(cookie string, device string, errorCode uint32, errReason string) {
vinokuma926cb3e2023-03-29 11:41:06 +0530342 mvp.mvpFlowLock.Lock()
343 defer mvp.mvpFlowLock.Unlock()
Tinoj Josephcf161be2022-07-07 19:47:47 +0530344
vinokuma926cb3e2023-03-29 11:41:06 +0530345 if flowMap, ok := mvp.PendingDeleteFlow[device]; ok {
346 if _, ok := flowMap[cookie]; ok {
347 logger.Errorw(ctx, "Mvlan Flow Remove Failure Notification", log.Fields{"MvlanProfile": mvp.Name, "Cookie": cookie, "ErrorCode": errorCode, "ErrorReason": errReason, "Device": device})
348 return
349 }
350 }
351 logger.Errorw(ctx, "Mvlan Flow Del Failure Notification for Unknown cookie", log.Fields{"MvlanProfile": mvp.Name, "Cookie": cookie, "ErrorCode": errorCode, "ErrorReason": errReason})
Tinoj Josephcf161be2022-07-07 19:47:47 +0530352}
353
354// IsStaticGroup to check if group is static
355func (mvp *MvlanProfile) IsStaticGroup(groupName string) bool {
vinokuma926cb3e2023-03-29 11:41:06 +0530356 return mvp.Groups[groupName].IsStatic
Tinoj Josephcf161be2022-07-07 19:47:47 +0530357}
358
359// generateGroupKey to generate group key
360func (mvp *MvlanProfile) generateGroupKey(name string, ipAddr string) string {
vinokuma926cb3e2023-03-29 11:41:06 +0530361 if mvp.IsChannelBasedGroup {
362 return mvp.Mvlan.String() + "_" + ipAddr
363 }
364 return mvp.Mvlan.String() + "_" + name
Tinoj Josephcf161be2022-07-07 19:47:47 +0530365}
366
367// GetStaticGroupName to get static igmp group
368func (mvp *MvlanProfile) GetStaticGroupName(gip net.IP) string {
vinokuma926cb3e2023-03-29 11:41:06 +0530369 for _, mvg := range mvp.Groups {
370 if mvg.IsStatic {
371 if doesIPMatch(gip, mvg.McIPs) {
372 return mvg.Name
373 }
374 }
375 }
376 return ""
Tinoj Josephcf161be2022-07-07 19:47:47 +0530377}
378
379// GetStaticIgmpGroup to get static igmp group
380func (mvp *MvlanProfile) GetStaticIgmpGroup(gip net.IP) *IgmpGroup {
vinokuma926cb3e2023-03-29 11:41:06 +0530381 staticGroupName := mvp.GetStaticGroupName(gip)
382 grpKey := mvp.generateGroupKey(staticGroupName, gip.String())
383 logger.Debugw(ctx, "Get Static IGMP Group", log.Fields{"Group": grpKey})
384 ig, ok := GetApplication().IgmpGroups.Load(grpKey)
385 if ok {
386 logger.Debugw(ctx, "Get Static IGMP Group Success", log.Fields{"Group": grpKey})
387 return ig.(*IgmpGroup)
388 }
389 return nil
Tinoj Josephcf161be2022-07-07 19:47:47 +0530390}
391
vinokuma926cb3e2023-03-29 11:41:06 +0530392// pushIgmpMcastFlows - Adds all IGMP related flows (generic DS flow & static group flows)
Tinoj Joseph07cc5372022-07-18 22:53:51 +0530393func (mvp *MvlanProfile) pushIgmpMcastFlows(cntx context.Context, OLTSerialNum string) {
vinokuma926cb3e2023-03-29 11:41:06 +0530394 mvp.mvpLock.RLock()
395 defer mvp.mvpLock.RUnlock()
Tinoj Josephcf161be2022-07-07 19:47:47 +0530396
vinokuma926cb3e2023-03-29 11:41:06 +0530397 if mvp.DevicesList[OLTSerialNum] == Nil {
398 logger.Infow(ctx, "Mvlan Profile not configure for device", log.Fields{"Device": OLTSerialNum, "Mvlan": mvp.Mvlan})
399 return
400 }
Tinoj Josephcf161be2022-07-07 19:47:47 +0530401
vinokuma926cb3e2023-03-29 11:41:06 +0530402 d, _ := GetApplication().GetDeviceBySerialNo(OLTSerialNum)
403 if d == nil {
404 logger.Warnw(ctx, "Skipping Igmp & Mcast Flow processing: Device Not Found", log.Fields{"Device_SrNo": OLTSerialNum, "Mvlan": mvp.Mvlan})
405 return
406 }
Tinoj Josephcf161be2022-07-07 19:47:47 +0530407
vinokuma926cb3e2023-03-29 11:41:06 +0530408 p := d.GetPort(d.NniPort)
Tinoj Josephcf161be2022-07-07 19:47:47 +0530409
vinokuma926cb3e2023-03-29 11:41:06 +0530410 if p != nil && p.State == PortStateUp {
411 logger.Infow(ctx, "NNI Port Status is: UP & Vlan Enabled", log.Fields{"Device": d, "port": p})
Tinoj Josephcf161be2022-07-07 19:47:47 +0530412
vinokuma926cb3e2023-03-29 11:41:06 +0530413 //Push Igmp DS Control Flows
414 err := mvp.ApplyIgmpDSFlowForMvp(cntx, d.Name)
415 if err != nil {
416 logger.Errorw(ctx, "DS IGMP Flow Add Failed for device",
417 log.Fields{"Reason": err.Error(), "device": d.Name})
418 }
Tinoj Josephcf161be2022-07-07 19:47:47 +0530419
vinokuma926cb3e2023-03-29 11:41:06 +0530420 //Trigger Join for static channels
421 if channelList, containsStatic := mvp.getAllStaticChannels(); containsStatic {
422 mvp.ProcessStaticGroup(cntx, d.Name, channelList, true)
423 } else {
424 logger.Infow(ctx, "No Static Channels Present", log.Fields{"mvp": mvp.Name, "Mvlan": mvp.Mvlan})
425 }
426 }
Tinoj Josephcf161be2022-07-07 19:47:47 +0530427}
vinokuma926cb3e2023-03-29 11:41:06 +0530428
429// removeIgmpMcastFlows - Removes all IGMP related flows (generic DS flow & static group flows)
Tinoj Joseph07cc5372022-07-18 22:53:51 +0530430func (mvp *MvlanProfile) removeIgmpMcastFlows(cntx context.Context, oltSerialNum string) {
vinokuma926cb3e2023-03-29 11:41:06 +0530431 mvp.mvpLock.RLock()
432 defer mvp.mvpLock.RUnlock()
Tinoj Josephcf161be2022-07-07 19:47:47 +0530433
vinokuma926cb3e2023-03-29 11:41:06 +0530434 if d, _ := GetApplication().GetDeviceBySerialNo(oltSerialNum); d != nil {
435 p := d.GetPort(d.NniPort)
436 if p != nil {
437 logger.Infow(ctx, "NNI Port Status is: UP", log.Fields{"Device": d, "port": p})
Tinoj Josephcf161be2022-07-07 19:47:47 +0530438
vinokuma926cb3e2023-03-29 11:41:06 +0530439 // ***Do not change the order***
440 // When Vlan is disabled, the process end is determined by the DS Igmp flag in device
Tinoj Josephcf161be2022-07-07 19:47:47 +0530441
vinokuma926cb3e2023-03-29 11:41:06 +0530442 //Trigger Leave for static channels
443 if channelList, containsStatic := mvp.getAllStaticChannels(); containsStatic {
444 mvp.ProcessStaticGroup(cntx, d.Name, channelList, false)
445 } else {
446 logger.Infow(ctx, "No Static Channels Present", log.Fields{"mvp": mvp.Name, "Mvlan": mvp.Mvlan})
447 }
Tinoj Josephcf161be2022-07-07 19:47:47 +0530448
vinokuma926cb3e2023-03-29 11:41:06 +0530449 //Remove all dynamic members for the Mvlan Profile
450 GetApplication().IgmpGroups.Range(func(key, value interface{}) bool {
451 ig := value.(*IgmpGroup)
452 if ig.Mvlan == mvp.Mvlan {
453 igd := ig.Devices[d.Name]
454 ig.DelIgmpGroupDevice(cntx, igd)
455 if ig.NumDevicesActive() == 0 {
456 GetApplication().DelIgmpGroup(cntx, ig)
457 }
458 }
459 return true
460 })
Tinoj Josephcf161be2022-07-07 19:47:47 +0530461
vinokuma926cb3e2023-03-29 11:41:06 +0530462 //Remove DS Igmp trap flow
463 err := mvp.RemoveIgmpDSFlowForMvp(cntx, d.Name)
464 if err != nil {
465 logger.Errorw(ctx, "DS IGMP Flow Del Failed", log.Fields{"Reason": err.Error(), "device": d.Name})
466 }
467 }
468 }
Tinoj Josephcf161be2022-07-07 19:47:47 +0530469}
470
471// ApplyIgmpDSFlowForMvp to apply Igmp DS flow for mvlan.
Tinoj Joseph07cc5372022-07-18 22:53:51 +0530472func (mvp *MvlanProfile) ApplyIgmpDSFlowForMvp(cntx context.Context, device string) error {
vinokuma926cb3e2023-03-29 11:41:06 +0530473 va := GetApplication()
474 dIntf, ok := va.DevicesDisc.Load(device)
475 if !ok {
476 return errors.New("Device Doesn't Exist")
477 }
478 d := dIntf.(*VoltDevice)
479 mvlan := mvp.Mvlan
Tinoj Josephcf161be2022-07-07 19:47:47 +0530480
vinokuma926cb3e2023-03-29 11:41:06 +0530481 flowAlreadyApplied, ok := d.IgmpDsFlowAppliedForMvlan[uint16(mvlan)]
482 if !ok || !flowAlreadyApplied {
483 flows, err := mvp.BuildIgmpDSFlows(device)
484 if err == nil {
485 err = cntlr.GetController().AddFlows(cntx, d.NniPort, device, flows)
486 if err != nil {
487 logger.Warnw(ctx, "Configuring IGMP Flow for device failed ", log.Fields{"Device": device, "err": err})
488 return err
489 }
490 d.IgmpDsFlowAppliedForMvlan[uint16(mvlan)] = true
491 logger.Infow(ctx, "Updating voltDevice that IGMP DS flow as \"added\" for ",
492 log.Fields{"device": d.SerialNum, "mvlan": mvlan})
493 } else {
494 logger.Errorw(ctx, "DS IGMP Flow Add Failed", log.Fields{"Reason": err.Error(), "Mvlan": mvlan})
495 }
496 }
Tinoj Josephcf161be2022-07-07 19:47:47 +0530497
vinokuma926cb3e2023-03-29 11:41:06 +0530498 return nil
Tinoj Josephcf161be2022-07-07 19:47:47 +0530499}
500
501// RemoveIgmpDSFlowForMvp to remove Igmp DS flow for mvlan.
Tinoj Joseph07cc5372022-07-18 22:53:51 +0530502func (mvp *MvlanProfile) RemoveIgmpDSFlowForMvp(cntx context.Context, device string) error {
vinokuma926cb3e2023-03-29 11:41:06 +0530503 va := GetApplication()
504 mvlan := mvp.Mvlan
Tinoj Josephcf161be2022-07-07 19:47:47 +0530505
vinokuma926cb3e2023-03-29 11:41:06 +0530506 dIntf, ok := va.DevicesDisc.Load(device)
507 if !ok {
508 return errors.New("Device Doesn't Exist")
509 }
510 d := dIntf.(*VoltDevice)
511 /* No need of strict check during DS IGMP deletion
512 flowAlreadyApplied, ok := d.IgmpDsFlowAppliedForMvlan[uint16(mvlan)]
513 if ok && flowAlreadyApplied
514 */
515 flows, err := mvp.BuildIgmpDSFlows(device)
516 if err == nil {
517 flows.ForceAction = true
Tinoj Josephcf161be2022-07-07 19:47:47 +0530518
vinokuma926cb3e2023-03-29 11:41:06 +0530519 err = mvp.DelFlows(cntx, d, flows)
520 if err != nil {
521 logger.Warnw(ctx, "De-Configuring IGMP Flow for device failed ", log.Fields{"Device": device, "err": err})
522 return err
523 }
524 d.IgmpDsFlowAppliedForMvlan[uint16(mvlan)] = false
525 logger.Infow(ctx, "Updating voltDevice that IGMP DS flow as \"removed\" for ",
526 log.Fields{"device": d.SerialNum, "mvlan": mvlan})
527 } else {
528 logger.Errorw(ctx, "DS IGMP Flow Del Failed", log.Fields{"Reason": err.Error()})
529 }
Tinoj Josephcf161be2022-07-07 19:47:47 +0530530
vinokuma926cb3e2023-03-29 11:41:06 +0530531 return nil
Tinoj Josephcf161be2022-07-07 19:47:47 +0530532}
533
534// BuildIgmpDSFlows to build Igmp DS flows for NNI port
535func (mvp *MvlanProfile) BuildIgmpDSFlows(device string) (*of.VoltFlow, error) {
vinokuma926cb3e2023-03-29 11:41:06 +0530536 dIntf, ok := GetApplication().DevicesDisc.Load(device)
537 if !ok {
538 return nil, errors.New("Device Doesn't Exist")
539 }
540 d := dIntf.(*VoltDevice)
Tinoj Josephcf161be2022-07-07 19:47:47 +0530541
vinokuma926cb3e2023-03-29 11:41:06 +0530542 logger.Infow(ctx, "Building DS IGMP Flow for NNI port", log.Fields{"vs": d.NniPort, "Mvlan": mvp.Mvlan})
543 flow := &of.VoltFlow{}
544 flow.SubFlows = make(map[uint64]*of.VoltSubFlow)
545 subFlow := of.NewVoltSubFlow()
546 subFlow.SetTableID(0)
547 subFlow.SetMatchVlan(mvp.Mvlan)
Tinoj Josephcf161be2022-07-07 19:47:47 +0530548
vinokuma926cb3e2023-03-29 11:41:06 +0530549 nniPort, err := GetApplication().GetNniPort(device)
550 if err != nil {
551 return nil, err
552 }
553 nniPortID, err1 := GetApplication().GetPortID(nniPort)
554 if err1 != nil {
555 return nil, errors.New("Unknown NNI outport")
556 }
557 subFlow.SetInPort(nniPortID)
558 subFlow.SetIgmpMatch()
559 subFlow.SetReportToController()
560 subFlow.Cookie = uint64(nniPortID)<<32 | uint64(mvp.Mvlan)
561 subFlow.Priority = of.IgmpFlowPriority
Tinoj Josephcf161be2022-07-07 19:47:47 +0530562
vinokuma926cb3e2023-03-29 11:41:06 +0530563 flow.SubFlows[subFlow.Cookie] = subFlow
564 logger.Infow(ctx, "Built DS IGMP flow", log.Fields{"cookie": subFlow.Cookie, "subflow": subFlow})
565 return flow, nil
Tinoj Josephcf161be2022-07-07 19:47:47 +0530566}
567
vinokuma926cb3e2023-03-29 11:41:06 +0530568// updateStaticGroups - Generates static joins & leaves for newly added and removed static channels respectively
Tinoj Joseph07cc5372022-07-18 22:53:51 +0530569func (mvp *MvlanProfile) updateStaticGroups(cntx context.Context, deviceID string, added []net.IP, removed []net.IP) {
vinokuma926cb3e2023-03-29 11:41:06 +0530570 // Update static group configs for all associated devices
571 updateGroups := func(key interface{}, value interface{}) bool {
572 d := value.(*VoltDevice)
Tinoj Josephcf161be2022-07-07 19:47:47 +0530573
vinokuma926cb3e2023-03-29 11:41:06 +0530574 if mvp.DevicesList[d.SerialNum] == Nil {
575 logger.Infow(ctx, "Mvlan Profile not configure for device", log.Fields{"Device": d, "Profile Device List": mvp.DevicesList})
576 return true
577 }
578 // TODO if mvp.IsChannelBasedGroup {
579 mvp.ProcessStaticGroup(cntx, d.Name, added, true)
580 mvp.ProcessStaticGroup(cntx, d.Name, removed, false)
581 //}
582 return true
583 }
Tinoj Josephcf161be2022-07-07 19:47:47 +0530584
vinokuma926cb3e2023-03-29 11:41:06 +0530585 if deviceID != "" {
586 vd := GetApplication().GetDevice(deviceID)
587 updateGroups(deviceID, vd)
588 } else {
589 GetApplication().DevicesDisc.Range(updateGroups)
590 }
Tinoj Josephcf161be2022-07-07 19:47:47 +0530591}
592
vinokuma926cb3e2023-03-29 11:41:06 +0530593// updateDynamicGroups - Generates joins with updated sources for existing channels
Tinoj Joseph07cc5372022-07-18 22:53:51 +0530594func (mvp *MvlanProfile) updateDynamicGroups(cntx context.Context, deviceID string, added []net.IP, removed []net.IP) {
vinokuma926cb3e2023-03-29 11:41:06 +0530595 //mvlan := mvp.Mvlan
596 va := GetApplication()
Tinoj Josephcf161be2022-07-07 19:47:47 +0530597
vinokuma926cb3e2023-03-29 11:41:06 +0530598 updateGroups := func(key interface{}, value interface{}) bool {
599 d := value.(*VoltDevice)
Tinoj Josephcf161be2022-07-07 19:47:47 +0530600
vinokuma926cb3e2023-03-29 11:41:06 +0530601 if mvp.DevicesList[d.SerialNum] == Nil {
602 logger.Infow(ctx, "Mvlan Profile not configure for device", log.Fields{"Device": d, "Profile Device List": mvp.DevicesList})
603 return true
604 }
605 for _, groupAddr := range added {
606 _, gName := va.GetMvlanProfileForMcIP(mvp.Name, groupAddr)
607 grpKey := mvp.generateGroupKey(gName, groupAddr.String())
608 logger.Debugw(ctx, "IGMP Group", log.Fields{"Group": grpKey, "groupAddr": groupAddr})
609 if igIntf, ok := va.IgmpGroups.Load(grpKey); ok {
610 ig := igIntf.(*IgmpGroup)
611 if igd, ok := ig.getIgmpGroupDevice(cntx, d.Name); ok {
612 if igcIntf, ok := igd.GroupChannels.Load(groupAddr.String()); ok {
613 igc := igcIntf.(*IgmpGroupChannel)
614 incl := false
615 var ip []net.IP
616 var groupModified = false
617 if _, ok := mvp.Proxy[igc.GroupName]; ok {
618 if mvp.Proxy[igc.GroupName].Mode == common.Include {
619 incl = true
620 }
621 ip = mvp.Proxy[igc.GroupName].SourceList
622 }
623 for port, igp := range igc.NewReceivers {
624 // Process the include/exclude list which may end up modifying the group
625 if change, _ := igc.ProcessSources(cntx, port, ip, incl); change {
626 groupModified = true
627 }
628 igc.ProcessMode(port, incl)
Tinoj Josephcf161be2022-07-07 19:47:47 +0530629
vinokuma926cb3e2023-03-29 11:41:06 +0530630 if err := igp.WriteToDb(cntx, igc.Mvlan, igc.GroupAddr, igc.Device); err != nil {
631 logger.Errorw(ctx, "Igmp group port Write to DB failed", log.Fields{"mvlan": igc.Mvlan, "GroupAddr": igc.GroupAddr})
632 }
633 }
634 // If the group is modified as this is the first receiver or due to include/exclude list modification
635 // send a report to the upstream multicast servers
636 if groupModified {
637 logger.Debug(ctx, "Group Modified and IGMP report sent to the upstream server")
638 igc.SendReport(false)
639 }
640 if err := igc.WriteToDb(cntx); err != nil {
641 logger.Errorw(ctx, "Igmp group channel Write to DB failed", log.Fields{"mvlan": igc.Mvlan, "GroupAddr": igc.GroupAddr})
642 }
643 }
644 }
645 }
646 }
Tinoj Josephcf161be2022-07-07 19:47:47 +0530647
vinokuma926cb3e2023-03-29 11:41:06 +0530648 return true
649 }
Tinoj Josephcf161be2022-07-07 19:47:47 +0530650
vinokuma926cb3e2023-03-29 11:41:06 +0530651 if deviceID != "" {
652 vd := GetApplication().GetDevice(deviceID)
653 updateGroups(deviceID, vd)
654 } else {
655 GetApplication().DevicesDisc.Range(updateGroups)
656 }
Tinoj Josephcf161be2022-07-07 19:47:47 +0530657}
658
vinokuma926cb3e2023-03-29 11:41:06 +0530659// GroupsUpdated - Handles removing of Igmp Groups, flows & group table entries for
660// channels removed as part of update
Tinoj Joseph07cc5372022-07-18 22:53:51 +0530661func (mvp *MvlanProfile) GroupsUpdated(cntx context.Context, deviceID string) {
vinokuma926cb3e2023-03-29 11:41:06 +0530662 deleteChannelIfRemoved := func(key interface{}, value interface{}) bool {
663 ig := value.(*IgmpGroup)
Tinoj Josephcf161be2022-07-07 19:47:47 +0530664
vinokuma926cb3e2023-03-29 11:41:06 +0530665 if ig.Mvlan != mvp.Mvlan {
666 return true
667 }
668 grpName := ig.GroupName
669 logger.Infow(ctx, "###Update Cycle", log.Fields{"IG": ig.GroupName, "Addr": ig.GroupAddr})
670 // Check if group exists and remove the entire group object otherwise
671 if currentChannels := mvp.Groups[grpName]; currentChannels != nil {
672 if mvp.IsChannelBasedGroup {
673 channelPresent := doesIPMatch(ig.GroupAddr, currentChannels.McIPs)
674 if channelPresent || mvp.isChannelStatic(ig.GroupAddr) {
675 return true
676 }
677 } else {
678 allExistingChannels := ig.GetAllIgmpChannelForDevice(deviceID)
679 for channel := range allExistingChannels {
680 channelIP := net.ParseIP(channel)
681 channelPresent := mvp.IsChannelPresent(channelIP, currentChannels.McIPs, mvp.IsStaticGroup(ig.GroupName))
682 if channelPresent {
683 staticChannel := mvp.isChannelStatic(channelIP)
684 logger.Infow(ctx, "###Channel Comparison", log.Fields{"staticChannel": staticChannel, "Group": mvp.IsStaticGroup(ig.GroupName), "Channel": channel})
685 // Logic:
686 // If channel is Static & existing Group is also static - No migration required
687 // If channel is not Static & existing Group is also not static - No migration required
Tinoj Josephcf161be2022-07-07 19:47:47 +0530688
vinokuma926cb3e2023-03-29 11:41:06 +0530689 // If channel is Static and existing Group is not static - Migrate (from dynamic to static)
690 // (Channel already part of dynamic, added to static)
Tinoj Josephcf161be2022-07-07 19:47:47 +0530691
vinokuma926cb3e2023-03-29 11:41:06 +0530692 // If channel is not Static but existing Group is static - Migrate (from static to dynamic)
693 // (Channel removed from satic but part of dynamic)
694 if (staticChannel != mvp.IsStaticGroup(ig.GroupName)) || (ig.IsGroupStatic != mvp.IsStaticGroup(ig.GroupName)) { // Equivalent of XOR
695 ig.HandleGroupMigration(cntx, deviceID, channelIP)
696 } else {
697 if (ig.IsGroupStatic) && mvp.IsStaticGroup(ig.GroupName) {
698 if ig.GroupName != mvp.GetStaticGroupName(channelIP) {
699 ig.HandleGroupMigration(cntx, deviceID, channelIP)
700 }
701 }
702 continue
703 }
704 } else {
705 logger.Debugw(ctx, "Channel Removed", log.Fields{"Channel": channel, "Group": grpName})
706 ig.DelIgmpChannel(cntx, deviceID, net.ParseIP(channel))
707 if ig.NumDevicesActive() == 0 {
708 GetApplication().DelIgmpGroup(cntx, ig)
709 }
710 }
711 }
712 ig.IsGroupStatic = mvp.IsStaticGroup(ig.GroupName)
713 if err := ig.WriteToDb(cntx); err != nil {
714 logger.Errorw(ctx, "Igmp group Write to DB failed", log.Fields{"groupName": ig.GroupName})
715 }
716 return true
717 }
718 }
719 logger.Debugw(ctx, "Group Removed", log.Fields{"Channel": ig.GroupAddr, "Group": grpName, "ChannelBasedGroup": ig.IsChannelBasedGroup})
720 ig.DelIgmpGroup(cntx)
721 logger.Debugw(ctx, "Removed Igmp Group", log.Fields{"Channel": ig.GroupAddr, "Group": grpName})
722 return true
723 }
724 GetApplication().IgmpGroups.Range(deleteChannelIfRemoved)
Tinoj Josephcf161be2022-07-07 19:47:47 +0530725}
726
727// IsChannelPresent to check if channel is present
728func (mvp *MvlanProfile) IsChannelPresent(channelIP net.IP, groupChannelList []string, IsStaticGroup bool) bool {
vinokuma926cb3e2023-03-29 11:41:06 +0530729 // Only in case of static group, migration need to be supported.
730 // Dynamic to dynamic group migration not supported currently
731 if doesIPMatch(channelIP, groupChannelList) || mvp.isChannelStatic(channelIP) {
732 return true
733 } else if IsStaticGroup {
734 return (mvp.GetMvlanGroup(channelIP) != "")
735 }
Tinoj Josephcf161be2022-07-07 19:47:47 +0530736
vinokuma926cb3e2023-03-29 11:41:06 +0530737 return false
Tinoj Josephcf161be2022-07-07 19:47:47 +0530738}
739
Tinoj Josephcf161be2022-07-07 19:47:47 +0530740// GetMvlanGroup to get mvlan group
741func (mvp *MvlanProfile) GetMvlanGroup(ip net.IP) string {
vinokuma926cb3e2023-03-29 11:41:06 +0530742 // Check for Static Group First
743 if mvp.containsStaticChannels() {
744 grpName := mvp.GetStaticGroupName(ip)
745 if grpName != "" {
746 return grpName
747 }
748 }
Tinoj Josephcf161be2022-07-07 19:47:47 +0530749
vinokuma926cb3e2023-03-29 11:41:06 +0530750 for _, mvg := range mvp.Groups {
751 if mvg.Wildcard {
752 return mvg.Name
753 }
754 if doesIPMatch(ip, mvg.McIPs) {
755 return mvg.Name
756 }
757 }
758 return ""
Tinoj Josephcf161be2022-07-07 19:47:47 +0530759}
760
761// ProcessStaticGroup - Process Static Join/Leave Req for static channels
Tinoj Joseph07cc5372022-07-18 22:53:51 +0530762func (mvp *MvlanProfile) ProcessStaticGroup(cntx context.Context, device string, groupAddresses []net.IP, isJoin bool) {
vinokuma926cb3e2023-03-29 11:41:06 +0530763 logger.Debugw(ctx, "Received Static Group Request", log.Fields{"Device": device, "Join": isJoin, "Group Address List": groupAddresses})
Tinoj Josephcf161be2022-07-07 19:47:47 +0530764
vinokuma926cb3e2023-03-29 11:41:06 +0530765 mvlan := mvp.Mvlan
766 va := GetApplication()
Tinoj Josephcf161be2022-07-07 19:47:47 +0530767
vinokuma926cb3e2023-03-29 11:41:06 +0530768 // TODO - Handle bulk add of groupAddr
769 for _, groupAddr := range groupAddresses {
770 ig := mvp.GetStaticIgmpGroup(groupAddr)
771 if isJoin {
772 vd := va.GetDevice(device)
773 igmpProf, _, _ := getIgmpProxyCfgAndIP(mvlan, vd.SerialNum)
774 ver := igmpProf.IgmpVerToServer
Tinoj Josephcf161be2022-07-07 19:47:47 +0530775
vinokuma926cb3e2023-03-29 11:41:06 +0530776 if ig == nil {
777 // First time group Creation: Create the IGMP group and then add the receiver to the group
778 logger.Infow(ctx, "Static IGMP Add received for new group", log.Fields{"Addr": groupAddr, "Port": StaticPort})
779 if ig = GetApplication().AddIgmpGroup(cntx, mvp.Name, groupAddr, device); ig != nil {
780 ig.IgmpGroupLock.Lock()
781 ig.AddReceiver(cntx, device, StaticPort, groupAddr, nil, getVersion(ver),
782 0, 0, 0xFF)
783 ig.IgmpGroupLock.Unlock()
784 } else {
785 logger.Warnw(ctx, "Static IGMP Group Creation Failed", log.Fields{"Addr": groupAddr})
786 }
787 } else {
788 // Converting existing dynamic group to static group
789 if !mvp.IsStaticGroup(ig.GroupName) {
790 ig.updateGroupName(cntx, ig.GroupName)
791 }
792 // Update case: If the IGMP group is already created. just add the receiver
793 logger.Infow(ctx, "Static IGMP Add received for existing group", log.Fields{"Addr": groupAddr, "Port": StaticPort})
794 ig.IgmpGroupLock.Lock()
795 ig.AddReceiver(cntx, device, StaticPort, groupAddr, nil, getVersion(ver),
796 0, 0, 0xFF)
797 ig.IgmpGroupLock.Unlock()
798 }
799 } else if ig != nil {
800 logger.Infow(ctx, "Static IGMP Del received for existing group", log.Fields{"Addr": groupAddr, "Port": StaticPort})
Tinoj Josephcf161be2022-07-07 19:47:47 +0530801
vinokuma926cb3e2023-03-29 11:41:06 +0530802 if ig.IsChannelBasedGroup {
803 grpName := mvp.GetMvlanGroup(ig.GroupAddr)
804 if grpName != "" {
805 ig.IgmpGroupLock.Lock()
806 ig.DelReceiver(cntx, device, StaticPort, groupAddr, nil, 0xFF)
807 ig.IgmpGroupLock.Unlock()
808 ig.updateGroupName(cntx, grpName)
809 } else {
810 ig.DelIgmpGroup(cntx)
811 }
812 } else {
813 ig.IgmpGroupLock.Lock()
814 ig.DelReceiver(cntx, device, StaticPort, groupAddr, nil, 0xFF)
815 ig.IgmpGroupLock.Unlock()
816 }
817 if ig.NumDevicesActive() == 0 {
818 GetApplication().DelIgmpGroup(cntx, ig)
819 }
820 } else {
821 logger.Warnw(ctx, "Static IGMP Del received for unknown group", log.Fields{"Addr": groupAddr})
822 }
823 }
Tinoj Josephcf161be2022-07-07 19:47:47 +0530824}
825
vinokuma926cb3e2023-03-29 11:41:06 +0530826// getStaticChannelDiff - return the static channel newly added and removed from existing static group
Tinoj Josephcf161be2022-07-07 19:47:47 +0530827func (mvp *MvlanProfile) getStaticChannelDiff() (newlyAdded []net.IP, removed []net.IP, common []net.IP) {
vinokuma926cb3e2023-03-29 11:41:06 +0530828 var commonChannels []net.IP
829 newChannelList, _ := mvp.getAllStaticChannels()
830 existingChannelList, _ := mvp.getAllOldGroupStaticChannels()
831 if len(existingChannelList) == 0 {
832 return newChannelList, []net.IP{}, []net.IP{}
833 }
834 for _, newChannel := range append([]net.IP{}, newChannelList...) {
835 for _, existChannel := range append([]net.IP{}, existingChannelList...) {
836 // Remove common channels between existing and new list
837 // The remaining in the below slices give the results
838 // Remaining in newChannelList: Newly added
839 // Remaining in existingChannelList: Removed channels
840 if existChannel.Equal(newChannel) {
841 existingChannelList = removeIPFromList(existingChannelList, existChannel)
842 newChannelList = removeIPFromList(newChannelList, newChannel)
843 commonChannels = append(commonChannels, newChannel)
844 logger.Infow(ctx, "#############Channel: "+existChannel.String()+" New: "+newChannel.String(), log.Fields{"Added": newChannelList, "Removed": existingChannelList})
845 break
846 }
847 }
848 }
849 return newChannelList, existingChannelList, commonChannels
Tinoj Josephcf161be2022-07-07 19:47:47 +0530850}
851
vinokuma926cb3e2023-03-29 11:41:06 +0530852// getGroupChannelDiff - return the channel newly added and removed from existing group
Tinoj Josephcf161be2022-07-07 19:47:47 +0530853func (mvp *MvlanProfile) getGroupChannelDiff(newGroup *MvlanGroup, oldGroup *MvlanGroup) (newlyAdded []net.IP, removed []net.IP, common []net.IP) {
vinokuma926cb3e2023-03-29 11:41:06 +0530854 var commonChannels []net.IP
855 newChannelList, _ := newGroup.getAllChannels()
856 existingChannelList, _ := oldGroup.getAllChannels()
857 if len(existingChannelList) == 0 {
858 return newChannelList, []net.IP{}, []net.IP{}
859 }
860 for _, newChannel := range append([]net.IP{}, newChannelList...) {
861 for _, existChannel := range append([]net.IP{}, existingChannelList...) {
862 // Remove common channels between existing and new list
863 // The remaining in the below slices give the results
864 // Remaining in newChannelList: Newly added
865 // Remaining in existingChannelList: Removed channels
866 if existChannel.Equal(newChannel) {
867 existingChannelList = removeIPFromList(existingChannelList, existChannel)
868 newChannelList = removeIPFromList(newChannelList, newChannel)
869 commonChannels = append(commonChannels, newChannel)
870 logger.Infow(ctx, "#############Channel: "+existChannel.String()+" New: "+newChannel.String(), log.Fields{"Added": newChannelList, "Removed": existingChannelList})
871 break
872 }
873 }
874 }
875 return newChannelList, existingChannelList, commonChannels
Tinoj Josephcf161be2022-07-07 19:47:47 +0530876}
877
878// UpdateProfile - Updates the group & member info w.r.t the mvlan profile for the given device
Tinoj Joseph07cc5372022-07-18 22:53:51 +0530879func (mvp *MvlanProfile) UpdateProfile(cntx context.Context, deviceID string) {
vinokuma926cb3e2023-03-29 11:41:06 +0530880 logger.Infow(ctx, "Update Mvlan Profile task triggered", log.Fields{"Mvlan": mvp.Mvlan})
881 var removedStaticChannels []net.IP
882 addedStaticChannels := []net.IP{}
883 /* Taking mvpLock to protect the mvp groups and proxy */
884 mvp.mvpLock.RLock()
885 defer mvp.mvpLock.RUnlock()
Tinoj Josephcf161be2022-07-07 19:47:47 +0530886
vinokuma926cb3e2023-03-29 11:41:06 +0530887 serialNo := ""
888 if deviceID != "" {
889 if vd := GetApplication().GetDevice(deviceID); vd != nil {
890 serialNo = vd.SerialNum
891 if mvp.DevicesList[serialNo] != UpdateInProgress {
892 logger.Warnw(ctx, "Exiting Update Task since device not present in MvlanProfile", log.Fields{"Device": deviceID, "SerialNum": vd.SerialNum, "MvlanProfile": mvp})
893 return
894 }
895 } else {
896 logger.Errorw(ctx, "Volt Device not found. Stopping Update Mvlan Profile processing for device", log.Fields{"SerialNo": deviceID, "MvlanProfile": mvp})
897 return
898 }
899 }
Tinoj Josephcf161be2022-07-07 19:47:47 +0530900
vinokuma926cb3e2023-03-29 11:41:06 +0530901 // Update the groups based on static channels added & removed
902 if mvp.containsStaticChannels() {
903 addedStaticChannels, removedStaticChannels, _ = mvp.getStaticChannelDiff()
904 logger.Debugw(ctx, "Update Task - Static Group Changes", log.Fields{"Added": addedStaticChannels, "Removed": removedStaticChannels})
Tinoj Josephcf161be2022-07-07 19:47:47 +0530905
vinokuma926cb3e2023-03-29 11:41:06 +0530906 if len(addedStaticChannels) > 0 || len(removedStaticChannels) > 0 {
907 mvp.updateStaticGroups(cntx, deviceID, []net.IP{}, removedStaticChannels)
908 }
909 }
910 mvp.GroupsUpdated(cntx, deviceID)
911 if len(addedStaticChannels) > 0 {
912 mvp.updateStaticGroups(cntx, deviceID, addedStaticChannels, []net.IP{})
913 }
Tinoj Josephcf161be2022-07-07 19:47:47 +0530914
vinokuma926cb3e2023-03-29 11:41:06 +0530915 /* Need to handle if SSM params are modified for groups */
916 for key := range mvp.Groups {
917 _, _, commonChannels := mvp.getGroupChannelDiff(mvp.Groups[key], mvp.oldGroups[key])
918 if mvp.checkStaticGrpSSMProxyDiff(mvp.oldProxy[key], mvp.Proxy[key]) {
919 if mvp.Groups[key].IsStatic {
920 /* Static group proxy modified, need to trigger membership report with new mode/src-list for existing channels */
921 mvp.updateStaticGroups(cntx, deviceID, commonChannels, []net.IP{})
922 } else {
923 /* Dynamic group proxy modified, need to trigger membership report with new mode/src-list for existing channels */
924 mvp.updateDynamicGroups(cntx, deviceID, commonChannels, []net.IP{})
925 }
926 }
927 }
Tinoj Josephcf161be2022-07-07 19:47:47 +0530928
vinokuma926cb3e2023-03-29 11:41:06 +0530929 mvp.SetUpdateStatus(serialNo, NoOp)
Tinoj Josephcf161be2022-07-07 19:47:47 +0530930
vinokuma926cb3e2023-03-29 11:41:06 +0530931 if deviceID == "" || !mvp.isUpdateInProgress() {
932 mvp.oldGroups = nil
933 }
934 if err := mvp.WriteToDb(cntx); err != nil {
935 logger.Errorw(ctx, "Mvlan profile write to DB failed", log.Fields{"ProfileName": mvp.Name})
936 }
937 logger.Debugw(ctx, "Updated MVLAN Profile", log.Fields{"VLAN": mvp.Mvlan, "Name": mvp.Name, "Grp IPs": mvp.Groups})
Tinoj Josephcf161be2022-07-07 19:47:47 +0530938}
939
vinokuma926cb3e2023-03-29 11:41:06 +0530940// checkStaticGrpSSMProxyDiff- return true if the proxy of oldGroup is modified in newGroup
Tinoj Josephcf161be2022-07-07 19:47:47 +0530941func (mvp *MvlanProfile) checkStaticGrpSSMProxyDiff(oldProxy *MCGroupProxy, newProxy *MCGroupProxy) bool {
vinokuma926cb3e2023-03-29 11:41:06 +0530942 if oldProxy == nil && newProxy == nil {
943 return false
944 }
945 if (oldProxy == nil && newProxy != nil) ||
946 (oldProxy != nil && newProxy == nil) {
947 return true
948 }
Tinoj Josephcf161be2022-07-07 19:47:47 +0530949
vinokuma926cb3e2023-03-29 11:41:06 +0530950 if oldProxy.Mode != newProxy.Mode {
951 return true
952 }
Tinoj Josephcf161be2022-07-07 19:47:47 +0530953
vinokuma926cb3e2023-03-29 11:41:06 +0530954 oldSrcLst := oldProxy.SourceList
955 newSrcLst := newProxy.SourceList
956 oLen := len(oldSrcLst)
957 nLen := len(newSrcLst)
958 if oLen != nLen {
959 return true
960 }
Tinoj Josephcf161be2022-07-07 19:47:47 +0530961
vinokuma926cb3e2023-03-29 11:41:06 +0530962 visited := make([]bool, nLen)
Tinoj Josephcf161be2022-07-07 19:47:47 +0530963
vinokuma926cb3e2023-03-29 11:41:06 +0530964 /* check if any new IPs added in the src list, return true if present */
965 for i := 0; i < nLen; i++ {
966 found := false
967 element := newSrcLst[i]
968 for j := 0; j < oLen; j++ {
969 if visited[j] {
970 continue
971 }
972 if element.Equal(oldSrcLst[j]) {
973 visited[j] = true
974 found = true
975 break
976 }
977 }
978 if !found {
979 return true
980 }
981 }
Tinoj Josephcf161be2022-07-07 19:47:47 +0530982
vinokuma926cb3e2023-03-29 11:41:06 +0530983 visited = make([]bool, nLen)
984 /* check if any IPs removed from existing src list, return true if removed */
985 for i := 0; i < oLen; i++ {
986 found := false
987 element := oldSrcLst[i]
988 for j := 0; j < nLen; j++ {
989 if visited[j] {
990 continue
991 }
992 if element.Equal(newSrcLst[j]) {
993 visited[j] = true
994 found = true
995 break
996 }
997 }
998 if !found {
999 return true
1000 }
1001 }
1002 return false
Tinoj Josephcf161be2022-07-07 19:47:47 +05301003}
1004
vinokuma926cb3e2023-03-29 11:41:06 +05301005// UpdateActiveChannelSubscriberAlarm - Updates the Active Channel Subscriber Alarm
Tinoj Josephcf161be2022-07-07 19:47:47 +05301006func (mvp *MvlanProfile) UpdateActiveChannelSubscriberAlarm() {
vinokuma926cb3e2023-03-29 11:41:06 +05301007 va := GetApplication()
1008 logger.Debugw(ctx, "Update of Active Channel Subscriber Alarm", log.Fields{"Mvlan": mvp.Mvlan})
1009 for srNo := range mvp.DevicesList {
1010 d, _ := va.GetDeviceBySerialNo(srNo)
1011 if d == nil {
1012 logger.Warnw(ctx, "Device info not found", log.Fields{"Device_SrNo": srNo, "Mvlan": mvp.Mvlan})
1013 return
1014 }
1015 d.Ports.Range(func(key, value interface{}) bool {
1016 //port := key.(string)
1017 vp := value.(*VoltPort)
1018 if vp.Type != VoltPortTypeAccess {
1019 return true
1020 }
1021 if mvp.MaxActiveChannels > vp.ActiveChannels && vp.ChannelPerSubAlarmRaised {
1022 serviceName := GetMcastServiceForSubAlarm(vp, mvp)
1023 logger.Debugw(ctx, "Clearing-SendActiveChannelPerSubscriberAlarm-due-to-update", log.Fields{"ActiveChannels": vp.ActiveChannels, "ServiceName": serviceName})
1024 vp.ChannelPerSubAlarmRaised = false
1025 } else if mvp.MaxActiveChannels < vp.ActiveChannels && !vp.ChannelPerSubAlarmRaised {
1026 /* When the max active channel count is reduced via update, we raise an alarm.
1027 But the previous excess channels still exist until a leave or expiry */
1028 serviceName := GetMcastServiceForSubAlarm(vp, mvp)
1029 logger.Debugw(ctx, "Raising-SendActiveChannelPerSubscriberAlarm-due-to-update", log.Fields{"ActiveChannels": vp.ActiveChannels, "ServiceName": serviceName})
1030 vp.ChannelPerSubAlarmRaised = true
1031 }
1032 return true
1033 })
1034 }
Tinoj Josephcf161be2022-07-07 19:47:47 +05301035}
1036
vinokuma926cb3e2023-03-29 11:41:06 +05301037// TriggerAssociatedFlowDelete - Re-trigger delete for pending delete flows
Tinoj Joseph07cc5372022-07-18 22:53:51 +05301038func (mvp *MvlanProfile) TriggerAssociatedFlowDelete(cntx context.Context, device string) bool {
vinokuma926cb3e2023-03-29 11:41:06 +05301039 mvp.mvpFlowLock.Lock()
Tinoj Josephcf161be2022-07-07 19:47:47 +05301040
vinokuma926cb3e2023-03-29 11:41:06 +05301041 cookieList := []uint64{}
1042 flowMap := mvp.PendingDeleteFlow[device]
Tinoj Josephcf161be2022-07-07 19:47:47 +05301043
vinokuma926cb3e2023-03-29 11:41:06 +05301044 for cookie := range flowMap {
1045 cookieList = append(cookieList, convertToUInt64(cookie))
1046 }
1047 mvp.mvpFlowLock.Unlock()
Tinoj Josephcf161be2022-07-07 19:47:47 +05301048
vinokuma926cb3e2023-03-29 11:41:06 +05301049 if len(cookieList) == 0 {
1050 return false
1051 }
Tinoj Josephcf161be2022-07-07 19:47:47 +05301052
vinokuma926cb3e2023-03-29 11:41:06 +05301053 for _, cookie := range cookieList {
1054 if vd := GetApplication().GetDevice(device); vd != nil {
1055 flow := &of.VoltFlow{}
1056 flow.SubFlows = make(map[uint64]*of.VoltSubFlow)
1057 subFlow := of.NewVoltSubFlow()
1058 subFlow.Cookie = cookie
1059 flow.SubFlows[cookie] = subFlow
1060 logger.Infow(ctx, "Retriggering Vnet Delete Flow", log.Fields{"Device": device, "Mvlan": mvp.Mvlan.String(), "Cookie": cookie})
1061 err := mvp.DelFlows(cntx, vd, flow)
1062 if err != nil {
1063 logger.Warnw(ctx, "De-Configuring IGMP Flow for device failed ", log.Fields{"Device": device, "err": err})
1064 }
1065 }
1066 }
1067 return true
Tinoj Josephcf161be2022-07-07 19:47:47 +05301068}
1069
vinokuma926cb3e2023-03-29 11:41:06 +05301070// JSONMarshal wrapper function for json Marshal MvlanProfile
1071func (mvp *MvlanProfile) JSONMarshal() ([]byte, error) {
1072 return json.Marshal(MvlanProfile{
1073 Name: mvp.Name,
1074 Mvlan: mvp.Mvlan,
1075 PonVlan: mvp.PonVlan,
1076 Groups: mvp.Groups,
1077 Proxy: mvp.Proxy,
1078 Version: mvp.Version,
1079 IsPonVlanPresent: mvp.IsPonVlanPresent,
1080 IsChannelBasedGroup: mvp.IsChannelBasedGroup,
1081 DevicesList: mvp.DevicesList,
1082 MaxActiveChannels: mvp.MaxActiveChannels,
1083 PendingDeleteFlow: mvp.PendingDeleteFlow,
1084 DeleteInProgress: mvp.DeleteInProgress,
1085 IgmpServVersion: mvp.IgmpServVersion,
1086 })
Tinoj Josephcf161be2022-07-07 19:47:47 +05301087}
1088
1089// removeIPFromList to remove ip from the list
1090func removeIPFromList(s []net.IP, value net.IP) []net.IP {
vinokuma926cb3e2023-03-29 11:41:06 +05301091 i := 0
1092 for i = 0; i < len(s); i++ {
1093 if s[i].Equal(value) {
1094 break
1095 }
1096 }
1097 if i != len(s) {
1098 //It means value is found in the slice
1099 return append(s[0:i], s[i+1:]...)
1100 }
1101 return s
Tinoj Josephcf161be2022-07-07 19:47:47 +05301102}
1103
1104// doesIPMatch to check if ip match with any ip from the list
1105func doesIPMatch(ip net.IP, ipsOrRange []string) bool {
vinokuma926cb3e2023-03-29 11:41:06 +05301106 for _, ipOrRange := range ipsOrRange {
1107 if strings.Contains(ipOrRange, "-") {
1108 var splits = strings.Split(ipOrRange, "-")
1109 ipStart := util.IP2LongConv(net.ParseIP(splits[0]))
1110 ipEnd := util.IP2LongConv(net.ParseIP(splits[1]))
1111 if ipEnd < ipStart {
1112 return false
1113 }
1114 ipInt := util.IP2LongConv(ip)
1115 if ipInt >= ipStart && ipInt <= ipEnd {
1116 return true
1117 }
1118 } else if ip.Equal(net.ParseIP(ipOrRange)) {
1119 return true
1120 }
1121 }
1122 return false
Tinoj Josephcf161be2022-07-07 19:47:47 +05301123}
1124
1125// IgmpProfile structure
1126type IgmpProfile struct {
vinokuma926cb3e2023-03-29 11:41:06 +05301127 ProfileID string
1128 IgmpVerToServer string
1129 Version string
1130 IgmpSourceIP net.IP
1131 UnsolicitedTimeOut uint32 //In seconds
1132 MaxResp uint32
1133 KeepAliveInterval uint32
1134 KeepAliveCount uint32
1135 LastQueryInterval uint32
1136 LastQueryCount uint32
1137 IgmpCos uint8
1138 FastLeave bool
1139 PeriodicQuery bool
1140 WithRAUpLink bool
1141 WithRADownLink bool
Tinoj Josephcf161be2022-07-07 19:47:47 +05301142}
1143
1144func newIgmpProfile(igmpProfileConfig *common.IGMPConfig) *IgmpProfile {
vinokuma926cb3e2023-03-29 11:41:06 +05301145 var igmpProfile IgmpProfile
1146 igmpProfile.ProfileID = igmpProfileConfig.ProfileID
1147 igmpProfile.UnsolicitedTimeOut = uint32(igmpProfileConfig.UnsolicitedTimeOut)
1148 igmpProfile.MaxResp = uint32(igmpProfileConfig.MaxResp)
Tinoj Josephcf161be2022-07-07 19:47:47 +05301149
vinokuma926cb3e2023-03-29 11:41:06 +05301150 keepAliveInterval := uint32(igmpProfileConfig.KeepAliveInterval)
Tinoj Josephcf161be2022-07-07 19:47:47 +05301151
vinokuma926cb3e2023-03-29 11:41:06 +05301152 //KeepAliveInterval should have a min of 10 seconds
1153 if keepAliveInterval < MinKeepAliveInterval {
1154 keepAliveInterval = MinKeepAliveInterval
1155 logger.Infow(ctx, "Auto adjust keepAliveInterval - Value < 10", log.Fields{"Received": igmpProfileConfig.KeepAliveInterval, "Configured": keepAliveInterval})
1156 }
1157 igmpProfile.KeepAliveInterval = keepAliveInterval
Tinoj Josephcf161be2022-07-07 19:47:47 +05301158
vinokuma926cb3e2023-03-29 11:41:06 +05301159 igmpProfile.KeepAliveCount = uint32(igmpProfileConfig.KeepAliveCount)
1160 igmpProfile.LastQueryInterval = uint32(igmpProfileConfig.LastQueryInterval)
1161 igmpProfile.LastQueryCount = uint32(igmpProfileConfig.LastQueryCount)
1162 igmpProfile.FastLeave = *igmpProfileConfig.FastLeave
1163 igmpProfile.PeriodicQuery = *igmpProfileConfig.PeriodicQuery
1164 igmpProfile.IgmpCos = uint8(igmpProfileConfig.IgmpCos)
1165 igmpProfile.WithRAUpLink = *igmpProfileConfig.WithRAUpLink
1166 igmpProfile.WithRADownLink = *igmpProfileConfig.WithRADownLink
Tinoj Josephcf161be2022-07-07 19:47:47 +05301167
vinokuma926cb3e2023-03-29 11:41:06 +05301168 if igmpProfileConfig.IgmpVerToServer == "2" || igmpProfileConfig.IgmpVerToServer == "v2" {
1169 igmpProfile.IgmpVerToServer = "2"
1170 } else {
1171 igmpProfile.IgmpVerToServer = "3"
1172 }
1173 igmpProfile.IgmpSourceIP = net.ParseIP(igmpProfileConfig.IgmpSourceIP)
Tinoj Josephcf161be2022-07-07 19:47:47 +05301174
vinokuma926cb3e2023-03-29 11:41:06 +05301175 return &igmpProfile
Tinoj Josephcf161be2022-07-07 19:47:47 +05301176}
1177
1178// newDefaultIgmpProfile Igmp profiles with default values
1179func newDefaultIgmpProfile() *IgmpProfile {
vinokuma926cb3e2023-03-29 11:41:06 +05301180 return &IgmpProfile{
1181 ProfileID: DefaultIgmpProfID,
1182 UnsolicitedTimeOut: 60,
1183 MaxResp: 10, // seconds
1184 KeepAliveInterval: 60, // seconds
1185 KeepAliveCount: 3, // TODO - May not be needed
1186 LastQueryInterval: 0, // TODO - May not be needed
1187 LastQueryCount: 0, // TODO - May not be needed
1188 FastLeave: true,
1189 PeriodicQuery: false, // TODO - May not be needed
1190 IgmpCos: 7, //p-bit value included in the IGMP packet
1191 WithRAUpLink: false, // TODO - May not be needed
1192 WithRADownLink: false, // TODO - May not be needed
1193 IgmpVerToServer: "3",
1194 IgmpSourceIP: net.ParseIP("172.27.0.1"), // This will be replaced by configuration
1195 }
Tinoj Josephcf161be2022-07-07 19:47:47 +05301196}
1197
1198// WriteToDb is utility to write Igmp Config Info to database
Tinoj Joseph07cc5372022-07-18 22:53:51 +05301199func (igmpProfile *IgmpProfile) WriteToDb(cntx context.Context) error {
vinokuma926cb3e2023-03-29 11:41:06 +05301200 igmpProfile.Version = database.PresentVersionMap[database.IgmpProfPath]
1201 b, err := json.Marshal(igmpProfile)
1202 if err != nil {
1203 return err
1204 }
1205 if err1 := db.PutIgmpProfile(cntx, igmpProfile.ProfileID, string(b)); err1 != nil {
1206 return err1
1207 }
1208 return nil
Tinoj Josephcf161be2022-07-07 19:47:47 +05301209}