blob: f26e33b2e7156ba94e0a8e4a825d47e52d2dfe0f [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 }
Sridhar Ravindrab76eb162025-07-02 01:25:10 +0530321 nniPort, err := GetApplication().GetNniPort(device.Name)
322 if err != nil {
323 logger.Errorw(ctx, "Error getting NNI port", log.Fields{"Error": err})
324 }
325 return cntlr.GetController().DelFlows(cntx, nniPort, device.Name, flow, false)
Tinoj Josephcf161be2022-07-07 19:47:47 +0530326}
327
vinokuma926cb3e2023-03-29 11:41:06 +0530328// FlowRemoveSuccess - Process flow success indication
Tinoj Joseph07cc5372022-07-18 22:53:51 +0530329func (mvp *MvlanProfile) FlowRemoveSuccess(cntx context.Context, cookie string, device string) {
vinokuma926cb3e2023-03-29 11:41:06 +0530330 mvp.mvpFlowLock.Lock()
331 defer mvp.mvpFlowLock.Unlock()
Tinoj Josephcf161be2022-07-07 19:47:47 +0530332
vinokuma926cb3e2023-03-29 11:41:06 +0530333 logger.Infow(ctx, "Mvlan Flow Remove Success Notification", log.Fields{"MvlanProfile": mvp.Name, "Cookie": cookie, "Device": device})
Tinoj Josephcf161be2022-07-07 19:47:47 +0530334
vinokuma926cb3e2023-03-29 11:41:06 +0530335 if _, ok := mvp.PendingDeleteFlow[device]; ok {
336 delete(mvp.PendingDeleteFlow[device], cookie)
337 }
Tinoj Josephcf161be2022-07-07 19:47:47 +0530338
vinokuma926cb3e2023-03-29 11:41:06 +0530339 if err := mvp.WriteToDb(cntx); err != nil {
340 logger.Errorw(ctx, "Mvlan profile write to DB failed", log.Fields{"ProfileName": mvp.Name})
341 }
Tinoj Josephcf161be2022-07-07 19:47:47 +0530342}
343
vinokuma926cb3e2023-03-29 11:41:06 +0530344// FlowRemoveFailure - Process flow failure indication
Tinoj Josephcf161be2022-07-07 19:47:47 +0530345func (mvp *MvlanProfile) FlowRemoveFailure(cookie string, device string, errorCode uint32, errReason string) {
vinokuma926cb3e2023-03-29 11:41:06 +0530346 mvp.mvpFlowLock.Lock()
347 defer mvp.mvpFlowLock.Unlock()
Tinoj Josephcf161be2022-07-07 19:47:47 +0530348
vinokuma926cb3e2023-03-29 11:41:06 +0530349 if flowMap, ok := mvp.PendingDeleteFlow[device]; ok {
350 if _, ok := flowMap[cookie]; ok {
351 logger.Errorw(ctx, "Mvlan Flow Remove Failure Notification", log.Fields{"MvlanProfile": mvp.Name, "Cookie": cookie, "ErrorCode": errorCode, "ErrorReason": errReason, "Device": device})
352 return
353 }
354 }
355 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 +0530356}
357
358// IsStaticGroup to check if group is static
359func (mvp *MvlanProfile) IsStaticGroup(groupName string) bool {
vinokuma926cb3e2023-03-29 11:41:06 +0530360 return mvp.Groups[groupName].IsStatic
Tinoj Josephcf161be2022-07-07 19:47:47 +0530361}
362
363// generateGroupKey to generate group key
364func (mvp *MvlanProfile) generateGroupKey(name string, ipAddr string) string {
vinokuma926cb3e2023-03-29 11:41:06 +0530365 if mvp.IsChannelBasedGroup {
366 return mvp.Mvlan.String() + "_" + ipAddr
367 }
368 return mvp.Mvlan.String() + "_" + name
Tinoj Josephcf161be2022-07-07 19:47:47 +0530369}
370
371// GetStaticGroupName to get static igmp group
372func (mvp *MvlanProfile) GetStaticGroupName(gip net.IP) string {
vinokuma926cb3e2023-03-29 11:41:06 +0530373 for _, mvg := range mvp.Groups {
374 if mvg.IsStatic {
375 if doesIPMatch(gip, mvg.McIPs) {
376 return mvg.Name
377 }
378 }
379 }
380 return ""
Tinoj Josephcf161be2022-07-07 19:47:47 +0530381}
382
383// GetStaticIgmpGroup to get static igmp group
384func (mvp *MvlanProfile) GetStaticIgmpGroup(gip net.IP) *IgmpGroup {
vinokuma926cb3e2023-03-29 11:41:06 +0530385 staticGroupName := mvp.GetStaticGroupName(gip)
386 grpKey := mvp.generateGroupKey(staticGroupName, gip.String())
387 logger.Debugw(ctx, "Get Static IGMP Group", log.Fields{"Group": grpKey})
388 ig, ok := GetApplication().IgmpGroups.Load(grpKey)
389 if ok {
390 logger.Debugw(ctx, "Get Static IGMP Group Success", log.Fields{"Group": grpKey})
391 return ig.(*IgmpGroup)
392 }
393 return nil
Tinoj Josephcf161be2022-07-07 19:47:47 +0530394}
395
vinokuma926cb3e2023-03-29 11:41:06 +0530396// pushIgmpMcastFlows - Adds all IGMP related flows (generic DS flow & static group flows)
Tinoj Joseph07cc5372022-07-18 22:53:51 +0530397func (mvp *MvlanProfile) pushIgmpMcastFlows(cntx context.Context, OLTSerialNum string) {
vinokuma926cb3e2023-03-29 11:41:06 +0530398 mvp.mvpLock.RLock()
399 defer mvp.mvpLock.RUnlock()
Tinoj Josephcf161be2022-07-07 19:47:47 +0530400
vinokuma926cb3e2023-03-29 11:41:06 +0530401 if mvp.DevicesList[OLTSerialNum] == Nil {
402 logger.Infow(ctx, "Mvlan Profile not configure for device", log.Fields{"Device": OLTSerialNum, "Mvlan": mvp.Mvlan})
403 return
404 }
Tinoj Josephcf161be2022-07-07 19:47:47 +0530405
vinokuma926cb3e2023-03-29 11:41:06 +0530406 d, _ := GetApplication().GetDeviceBySerialNo(OLTSerialNum)
407 if d == nil {
408 logger.Warnw(ctx, "Skipping Igmp & Mcast Flow processing: Device Not Found", log.Fields{"Device_SrNo": OLTSerialNum, "Mvlan": mvp.Mvlan})
409 return
410 }
Sridhar Ravindrab76eb162025-07-02 01:25:10 +0530411 nniPort, err := GetApplication().GetNniPort(d.Name)
412 if err != nil {
413 logger.Errorw(ctx, "Error getting NNI port", log.Fields{"Error": err})
414 }
415 p := d.GetPort(nniPort)
Tinoj Josephcf161be2022-07-07 19:47:47 +0530416
vinokuma926cb3e2023-03-29 11:41:06 +0530417 if p != nil && p.State == PortStateUp {
418 logger.Infow(ctx, "NNI Port Status is: UP & Vlan Enabled", log.Fields{"Device": d, "port": p})
Tinoj Josephcf161be2022-07-07 19:47:47 +0530419
vinokuma926cb3e2023-03-29 11:41:06 +0530420 //Push Igmp DS Control Flows
421 err := mvp.ApplyIgmpDSFlowForMvp(cntx, d.Name)
422 if err != nil {
423 logger.Errorw(ctx, "DS IGMP Flow Add Failed for device",
424 log.Fields{"Reason": err.Error(), "device": d.Name})
425 }
Tinoj Josephcf161be2022-07-07 19:47:47 +0530426
vinokuma926cb3e2023-03-29 11:41:06 +0530427 //Trigger Join for static channels
428 if channelList, containsStatic := mvp.getAllStaticChannels(); containsStatic {
429 mvp.ProcessStaticGroup(cntx, d.Name, channelList, true)
430 } else {
431 logger.Infow(ctx, "No Static Channels Present", log.Fields{"mvp": mvp.Name, "Mvlan": mvp.Mvlan})
432 }
433 }
Tinoj Josephcf161be2022-07-07 19:47:47 +0530434}
vinokuma926cb3e2023-03-29 11:41:06 +0530435
436// removeIgmpMcastFlows - Removes all IGMP related flows (generic DS flow & static group flows)
Tinoj Joseph07cc5372022-07-18 22:53:51 +0530437func (mvp *MvlanProfile) removeIgmpMcastFlows(cntx context.Context, oltSerialNum string) {
vinokuma926cb3e2023-03-29 11:41:06 +0530438 mvp.mvpLock.RLock()
439 defer mvp.mvpLock.RUnlock()
Tinoj Josephcf161be2022-07-07 19:47:47 +0530440
vinokuma926cb3e2023-03-29 11:41:06 +0530441 if d, _ := GetApplication().GetDeviceBySerialNo(oltSerialNum); d != nil {
Sridhar Ravindrab76eb162025-07-02 01:25:10 +0530442 nniPort, err := GetApplication().GetNniPort(d.Name)
443 if err != nil {
444 logger.Errorw(ctx, "Error getting NNI port", log.Fields{"Error": err})
445 }
446 p := d.GetPort(nniPort)
vinokuma926cb3e2023-03-29 11:41:06 +0530447 if p != nil {
448 logger.Infow(ctx, "NNI Port Status is: UP", log.Fields{"Device": d, "port": p})
Tinoj Josephcf161be2022-07-07 19:47:47 +0530449
vinokuma926cb3e2023-03-29 11:41:06 +0530450 // ***Do not change the order***
451 // When Vlan is disabled, the process end is determined by the DS Igmp flag in device
Tinoj Josephcf161be2022-07-07 19:47:47 +0530452
vinokuma926cb3e2023-03-29 11:41:06 +0530453 //Trigger Leave for static channels
454 if channelList, containsStatic := mvp.getAllStaticChannels(); containsStatic {
455 mvp.ProcessStaticGroup(cntx, d.Name, channelList, false)
456 } else {
457 logger.Infow(ctx, "No Static Channels Present", log.Fields{"mvp": mvp.Name, "Mvlan": mvp.Mvlan})
458 }
Tinoj Josephcf161be2022-07-07 19:47:47 +0530459
vinokuma926cb3e2023-03-29 11:41:06 +0530460 //Remove all dynamic members for the Mvlan Profile
461 GetApplication().IgmpGroups.Range(func(key, value interface{}) bool {
462 ig := value.(*IgmpGroup)
463 if ig.Mvlan == mvp.Mvlan {
464 igd := ig.Devices[d.Name]
465 ig.DelIgmpGroupDevice(cntx, igd)
466 if ig.NumDevicesActive() == 0 {
467 GetApplication().DelIgmpGroup(cntx, ig)
468 }
469 }
470 return true
471 })
Tinoj Josephcf161be2022-07-07 19:47:47 +0530472
vinokuma926cb3e2023-03-29 11:41:06 +0530473 //Remove DS Igmp trap flow
474 err := mvp.RemoveIgmpDSFlowForMvp(cntx, d.Name)
475 if err != nil {
476 logger.Errorw(ctx, "DS IGMP Flow Del Failed", log.Fields{"Reason": err.Error(), "device": d.Name})
477 }
478 }
479 }
Tinoj Josephcf161be2022-07-07 19:47:47 +0530480}
481
482// ApplyIgmpDSFlowForMvp to apply Igmp DS flow for mvlan.
Tinoj Joseph07cc5372022-07-18 22:53:51 +0530483func (mvp *MvlanProfile) ApplyIgmpDSFlowForMvp(cntx context.Context, device string) error {
vinokuma926cb3e2023-03-29 11:41:06 +0530484 va := GetApplication()
485 dIntf, ok := va.DevicesDisc.Load(device)
486 if !ok {
487 return errors.New("Device Doesn't Exist")
488 }
489 d := dIntf.(*VoltDevice)
490 mvlan := mvp.Mvlan
Tinoj Josephcf161be2022-07-07 19:47:47 +0530491
vinokuma926cb3e2023-03-29 11:41:06 +0530492 flowAlreadyApplied, ok := d.IgmpDsFlowAppliedForMvlan[uint16(mvlan)]
493 if !ok || !flowAlreadyApplied {
494 flows, err := mvp.BuildIgmpDSFlows(device)
495 if err == nil {
Sridhar Ravindrab76eb162025-07-02 01:25:10 +0530496 nniPort, err1 := va.GetNniPort(device)
497 if err1 != nil {
498 logger.Errorw(ctx, "Error getting NNI port", log.Fields{"Error": err1})
499 }
500 err = cntlr.GetController().AddFlows(cntx, nniPort, device, flows)
vinokuma926cb3e2023-03-29 11:41:06 +0530501 if err != nil {
502 logger.Warnw(ctx, "Configuring IGMP Flow for device failed ", log.Fields{"Device": device, "err": err})
503 return err
504 }
505 d.IgmpDsFlowAppliedForMvlan[uint16(mvlan)] = true
506 logger.Infow(ctx, "Updating voltDevice that IGMP DS flow as \"added\" for ",
507 log.Fields{"device": d.SerialNum, "mvlan": mvlan})
508 } else {
509 logger.Errorw(ctx, "DS IGMP Flow Add Failed", log.Fields{"Reason": err.Error(), "Mvlan": mvlan})
510 }
511 }
Tinoj Josephcf161be2022-07-07 19:47:47 +0530512
vinokuma926cb3e2023-03-29 11:41:06 +0530513 return nil
Tinoj Josephcf161be2022-07-07 19:47:47 +0530514}
515
516// RemoveIgmpDSFlowForMvp to remove Igmp DS flow for mvlan.
Tinoj Joseph07cc5372022-07-18 22:53:51 +0530517func (mvp *MvlanProfile) RemoveIgmpDSFlowForMvp(cntx context.Context, device string) error {
vinokuma926cb3e2023-03-29 11:41:06 +0530518 va := GetApplication()
519 mvlan := mvp.Mvlan
Tinoj Josephcf161be2022-07-07 19:47:47 +0530520
vinokuma926cb3e2023-03-29 11:41:06 +0530521 dIntf, ok := va.DevicesDisc.Load(device)
522 if !ok {
523 return errors.New("Device Doesn't Exist")
524 }
525 d := dIntf.(*VoltDevice)
526 /* No need of strict check during DS IGMP deletion
527 flowAlreadyApplied, ok := d.IgmpDsFlowAppliedForMvlan[uint16(mvlan)]
528 if ok && flowAlreadyApplied
529 */
530 flows, err := mvp.BuildIgmpDSFlows(device)
531 if err == nil {
532 flows.ForceAction = true
Tinoj Josephcf161be2022-07-07 19:47:47 +0530533
vinokuma926cb3e2023-03-29 11:41:06 +0530534 err = mvp.DelFlows(cntx, d, flows)
535 if err != nil {
536 logger.Warnw(ctx, "De-Configuring IGMP Flow for device failed ", log.Fields{"Device": device, "err": err})
537 return err
538 }
539 d.IgmpDsFlowAppliedForMvlan[uint16(mvlan)] = false
540 logger.Infow(ctx, "Updating voltDevice that IGMP DS flow as \"removed\" for ",
541 log.Fields{"device": d.SerialNum, "mvlan": mvlan})
542 } else {
543 logger.Errorw(ctx, "DS IGMP Flow Del Failed", log.Fields{"Reason": err.Error()})
544 }
Tinoj Josephcf161be2022-07-07 19:47:47 +0530545
vinokuma926cb3e2023-03-29 11:41:06 +0530546 return nil
Tinoj Josephcf161be2022-07-07 19:47:47 +0530547}
548
549// BuildIgmpDSFlows to build Igmp DS flows for NNI port
550func (mvp *MvlanProfile) BuildIgmpDSFlows(device string) (*of.VoltFlow, error) {
vinokuma926cb3e2023-03-29 11:41:06 +0530551 dIntf, ok := GetApplication().DevicesDisc.Load(device)
552 if !ok {
553 return nil, errors.New("Device Doesn't Exist")
554 }
555 d := dIntf.(*VoltDevice)
Tinoj Josephcf161be2022-07-07 19:47:47 +0530556
vinokuma926cb3e2023-03-29 11:41:06 +0530557 logger.Infow(ctx, "Building DS IGMP Flow for NNI port", log.Fields{"vs": d.NniPort, "Mvlan": mvp.Mvlan})
558 flow := &of.VoltFlow{}
559 flow.SubFlows = make(map[uint64]*of.VoltSubFlow)
560 subFlow := of.NewVoltSubFlow()
561 subFlow.SetTableID(0)
562 subFlow.SetMatchVlan(mvp.Mvlan)
Tinoj Josephcf161be2022-07-07 19:47:47 +0530563
vinokuma926cb3e2023-03-29 11:41:06 +0530564 nniPort, err := GetApplication().GetNniPort(device)
565 if err != nil {
566 return nil, err
567 }
568 nniPortID, err1 := GetApplication().GetPortID(nniPort)
569 if err1 != nil {
570 return nil, errors.New("Unknown NNI outport")
571 }
572 subFlow.SetInPort(nniPortID)
573 subFlow.SetIgmpMatch()
574 subFlow.SetReportToController()
575 subFlow.Cookie = uint64(nniPortID)<<32 | uint64(mvp.Mvlan)
576 subFlow.Priority = of.IgmpFlowPriority
Tinoj Josephcf161be2022-07-07 19:47:47 +0530577
vinokuma926cb3e2023-03-29 11:41:06 +0530578 flow.SubFlows[subFlow.Cookie] = subFlow
579 logger.Infow(ctx, "Built DS IGMP flow", log.Fields{"cookie": subFlow.Cookie, "subflow": subFlow})
580 return flow, nil
Tinoj Josephcf161be2022-07-07 19:47:47 +0530581}
582
vinokuma926cb3e2023-03-29 11:41:06 +0530583// updateStaticGroups - Generates static joins & leaves for newly added and removed static channels respectively
Tinoj Joseph07cc5372022-07-18 22:53:51 +0530584func (mvp *MvlanProfile) updateStaticGroups(cntx context.Context, deviceID string, added []net.IP, removed []net.IP) {
vinokuma926cb3e2023-03-29 11:41:06 +0530585 // Update static group configs for all associated devices
586 updateGroups := func(key interface{}, value interface{}) bool {
587 d := value.(*VoltDevice)
Tinoj Josephcf161be2022-07-07 19:47:47 +0530588
vinokuma926cb3e2023-03-29 11:41:06 +0530589 if mvp.DevicesList[d.SerialNum] == Nil {
590 logger.Infow(ctx, "Mvlan Profile not configure for device", log.Fields{"Device": d, "Profile Device List": mvp.DevicesList})
591 return true
592 }
593 // TODO if mvp.IsChannelBasedGroup {
594 mvp.ProcessStaticGroup(cntx, d.Name, added, true)
595 mvp.ProcessStaticGroup(cntx, d.Name, removed, false)
596 //}
597 return true
598 }
Tinoj Josephcf161be2022-07-07 19:47:47 +0530599
vinokuma926cb3e2023-03-29 11:41:06 +0530600 if deviceID != "" {
601 vd := GetApplication().GetDevice(deviceID)
602 updateGroups(deviceID, vd)
603 } else {
604 GetApplication().DevicesDisc.Range(updateGroups)
605 }
Tinoj Josephcf161be2022-07-07 19:47:47 +0530606}
607
vinokuma926cb3e2023-03-29 11:41:06 +0530608// updateDynamicGroups - Generates joins with updated sources for existing channels
Tinoj Joseph07cc5372022-07-18 22:53:51 +0530609func (mvp *MvlanProfile) updateDynamicGroups(cntx context.Context, deviceID string, added []net.IP, removed []net.IP) {
vinokuma926cb3e2023-03-29 11:41:06 +0530610 //mvlan := mvp.Mvlan
611 va := GetApplication()
Tinoj Josephcf161be2022-07-07 19:47:47 +0530612
vinokuma926cb3e2023-03-29 11:41:06 +0530613 updateGroups := func(key interface{}, value interface{}) bool {
614 d := value.(*VoltDevice)
Tinoj Josephcf161be2022-07-07 19:47:47 +0530615
vinokuma926cb3e2023-03-29 11:41:06 +0530616 if mvp.DevicesList[d.SerialNum] == Nil {
617 logger.Infow(ctx, "Mvlan Profile not configure for device", log.Fields{"Device": d, "Profile Device List": mvp.DevicesList})
618 return true
619 }
620 for _, groupAddr := range added {
621 _, gName := va.GetMvlanProfileForMcIP(mvp.Name, groupAddr)
622 grpKey := mvp.generateGroupKey(gName, groupAddr.String())
623 logger.Debugw(ctx, "IGMP Group", log.Fields{"Group": grpKey, "groupAddr": groupAddr})
624 if igIntf, ok := va.IgmpGroups.Load(grpKey); ok {
625 ig := igIntf.(*IgmpGroup)
626 if igd, ok := ig.getIgmpGroupDevice(cntx, d.Name); ok {
627 if igcIntf, ok := igd.GroupChannels.Load(groupAddr.String()); ok {
628 igc := igcIntf.(*IgmpGroupChannel)
629 incl := false
630 var ip []net.IP
631 var groupModified = false
632 if _, ok := mvp.Proxy[igc.GroupName]; ok {
633 if mvp.Proxy[igc.GroupName].Mode == common.Include {
634 incl = true
635 }
636 ip = mvp.Proxy[igc.GroupName].SourceList
637 }
638 for port, igp := range igc.NewReceivers {
639 // Process the include/exclude list which may end up modifying the group
640 if change, _ := igc.ProcessSources(cntx, port, ip, incl); change {
641 groupModified = true
642 }
643 igc.ProcessMode(port, incl)
Tinoj Josephcf161be2022-07-07 19:47:47 +0530644
vinokuma926cb3e2023-03-29 11:41:06 +0530645 if err := igp.WriteToDb(cntx, igc.Mvlan, igc.GroupAddr, igc.Device); err != nil {
646 logger.Errorw(ctx, "Igmp group port Write to DB failed", log.Fields{"mvlan": igc.Mvlan, "GroupAddr": igc.GroupAddr})
647 }
648 }
649 // If the group is modified as this is the first receiver or due to include/exclude list modification
650 // send a report to the upstream multicast servers
651 if groupModified {
652 logger.Debug(ctx, "Group Modified and IGMP report sent to the upstream server")
653 igc.SendReport(false)
654 }
655 if err := igc.WriteToDb(cntx); err != nil {
656 logger.Errorw(ctx, "Igmp group channel Write to DB failed", log.Fields{"mvlan": igc.Mvlan, "GroupAddr": igc.GroupAddr})
657 }
658 }
659 }
660 }
661 }
Tinoj Josephcf161be2022-07-07 19:47:47 +0530662
vinokuma926cb3e2023-03-29 11:41:06 +0530663 return true
664 }
Tinoj Josephcf161be2022-07-07 19:47:47 +0530665
vinokuma926cb3e2023-03-29 11:41:06 +0530666 if deviceID != "" {
667 vd := GetApplication().GetDevice(deviceID)
668 updateGroups(deviceID, vd)
669 } else {
670 GetApplication().DevicesDisc.Range(updateGroups)
671 }
Tinoj Josephcf161be2022-07-07 19:47:47 +0530672}
673
vinokuma926cb3e2023-03-29 11:41:06 +0530674// GroupsUpdated - Handles removing of Igmp Groups, flows & group table entries for
675// channels removed as part of update
Tinoj Joseph07cc5372022-07-18 22:53:51 +0530676func (mvp *MvlanProfile) GroupsUpdated(cntx context.Context, deviceID string) {
vinokuma926cb3e2023-03-29 11:41:06 +0530677 deleteChannelIfRemoved := func(key interface{}, value interface{}) bool {
678 ig := value.(*IgmpGroup)
Tinoj Josephcf161be2022-07-07 19:47:47 +0530679
vinokuma926cb3e2023-03-29 11:41:06 +0530680 if ig.Mvlan != mvp.Mvlan {
681 return true
682 }
683 grpName := ig.GroupName
684 logger.Infow(ctx, "###Update Cycle", log.Fields{"IG": ig.GroupName, "Addr": ig.GroupAddr})
685 // Check if group exists and remove the entire group object otherwise
686 if currentChannels := mvp.Groups[grpName]; currentChannels != nil {
687 if mvp.IsChannelBasedGroup {
688 channelPresent := doesIPMatch(ig.GroupAddr, currentChannels.McIPs)
689 if channelPresent || mvp.isChannelStatic(ig.GroupAddr) {
690 return true
691 }
692 } else {
693 allExistingChannels := ig.GetAllIgmpChannelForDevice(deviceID)
694 for channel := range allExistingChannels {
695 channelIP := net.ParseIP(channel)
696 channelPresent := mvp.IsChannelPresent(channelIP, currentChannels.McIPs, mvp.IsStaticGroup(ig.GroupName))
697 if channelPresent {
698 staticChannel := mvp.isChannelStatic(channelIP)
699 logger.Infow(ctx, "###Channel Comparison", log.Fields{"staticChannel": staticChannel, "Group": mvp.IsStaticGroup(ig.GroupName), "Channel": channel})
700 // Logic:
701 // If channel is Static & existing Group is also static - No migration required
702 // If channel is not Static & existing Group is also not static - No migration required
Tinoj Josephcf161be2022-07-07 19:47:47 +0530703
vinokuma926cb3e2023-03-29 11:41:06 +0530704 // If channel is Static and existing Group is not static - Migrate (from dynamic to static)
705 // (Channel already part of dynamic, added to static)
Tinoj Josephcf161be2022-07-07 19:47:47 +0530706
vinokuma926cb3e2023-03-29 11:41:06 +0530707 // If channel is not Static but existing Group is static - Migrate (from static to dynamic)
708 // (Channel removed from satic but part of dynamic)
709 if (staticChannel != mvp.IsStaticGroup(ig.GroupName)) || (ig.IsGroupStatic != mvp.IsStaticGroup(ig.GroupName)) { // Equivalent of XOR
710 ig.HandleGroupMigration(cntx, deviceID, channelIP)
711 } else {
712 if (ig.IsGroupStatic) && mvp.IsStaticGroup(ig.GroupName) {
713 if ig.GroupName != mvp.GetStaticGroupName(channelIP) {
714 ig.HandleGroupMigration(cntx, deviceID, channelIP)
715 }
716 }
717 continue
718 }
719 } else {
720 logger.Debugw(ctx, "Channel Removed", log.Fields{"Channel": channel, "Group": grpName})
721 ig.DelIgmpChannel(cntx, deviceID, net.ParseIP(channel))
722 if ig.NumDevicesActive() == 0 {
723 GetApplication().DelIgmpGroup(cntx, ig)
724 }
725 }
726 }
727 ig.IsGroupStatic = mvp.IsStaticGroup(ig.GroupName)
728 if err := ig.WriteToDb(cntx); err != nil {
729 logger.Errorw(ctx, "Igmp group Write to DB failed", log.Fields{"groupName": ig.GroupName})
730 }
731 return true
732 }
733 }
734 logger.Debugw(ctx, "Group Removed", log.Fields{"Channel": ig.GroupAddr, "Group": grpName, "ChannelBasedGroup": ig.IsChannelBasedGroup})
735 ig.DelIgmpGroup(cntx)
736 logger.Debugw(ctx, "Removed Igmp Group", log.Fields{"Channel": ig.GroupAddr, "Group": grpName})
737 return true
738 }
739 GetApplication().IgmpGroups.Range(deleteChannelIfRemoved)
Tinoj Josephcf161be2022-07-07 19:47:47 +0530740}
741
742// IsChannelPresent to check if channel is present
743func (mvp *MvlanProfile) IsChannelPresent(channelIP net.IP, groupChannelList []string, IsStaticGroup bool) bool {
vinokuma926cb3e2023-03-29 11:41:06 +0530744 // Only in case of static group, migration need to be supported.
745 // Dynamic to dynamic group migration not supported currently
746 if doesIPMatch(channelIP, groupChannelList) || mvp.isChannelStatic(channelIP) {
747 return true
748 } else if IsStaticGroup {
749 return (mvp.GetMvlanGroup(channelIP) != "")
750 }
Tinoj Josephcf161be2022-07-07 19:47:47 +0530751
vinokuma926cb3e2023-03-29 11:41:06 +0530752 return false
Tinoj Josephcf161be2022-07-07 19:47:47 +0530753}
754
Tinoj Josephcf161be2022-07-07 19:47:47 +0530755// GetMvlanGroup to get mvlan group
756func (mvp *MvlanProfile) GetMvlanGroup(ip net.IP) string {
vinokuma926cb3e2023-03-29 11:41:06 +0530757 // Check for Static Group First
758 if mvp.containsStaticChannels() {
759 grpName := mvp.GetStaticGroupName(ip)
760 if grpName != "" {
761 return grpName
762 }
763 }
Tinoj Josephcf161be2022-07-07 19:47:47 +0530764
vinokuma926cb3e2023-03-29 11:41:06 +0530765 for _, mvg := range mvp.Groups {
766 if mvg.Wildcard {
767 return mvg.Name
768 }
769 if doesIPMatch(ip, mvg.McIPs) {
770 return mvg.Name
771 }
772 }
773 return ""
Tinoj Josephcf161be2022-07-07 19:47:47 +0530774}
775
776// ProcessStaticGroup - Process Static Join/Leave Req for static channels
Tinoj Joseph07cc5372022-07-18 22:53:51 +0530777func (mvp *MvlanProfile) ProcessStaticGroup(cntx context.Context, device string, groupAddresses []net.IP, isJoin bool) {
vinokuma926cb3e2023-03-29 11:41:06 +0530778 logger.Debugw(ctx, "Received Static Group Request", log.Fields{"Device": device, "Join": isJoin, "Group Address List": groupAddresses})
Tinoj Josephcf161be2022-07-07 19:47:47 +0530779
vinokuma926cb3e2023-03-29 11:41:06 +0530780 mvlan := mvp.Mvlan
781 va := GetApplication()
Tinoj Josephcf161be2022-07-07 19:47:47 +0530782
vinokuma926cb3e2023-03-29 11:41:06 +0530783 // TODO - Handle bulk add of groupAddr
784 for _, groupAddr := range groupAddresses {
785 ig := mvp.GetStaticIgmpGroup(groupAddr)
786 if isJoin {
787 vd := va.GetDevice(device)
788 igmpProf, _, _ := getIgmpProxyCfgAndIP(mvlan, vd.SerialNum)
789 ver := igmpProf.IgmpVerToServer
Tinoj Josephcf161be2022-07-07 19:47:47 +0530790
vinokuma926cb3e2023-03-29 11:41:06 +0530791 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})
794 if ig = GetApplication().AddIgmpGroup(cntx, mvp.Name, groupAddr, device); ig != nil {
795 ig.IgmpGroupLock.Lock()
796 ig.AddReceiver(cntx, device, StaticPort, groupAddr, nil, getVersion(ver),
797 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) {
805 ig.updateGroupName(cntx, ig.GroupName)
806 }
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()
810 ig.AddReceiver(cntx, device, StaticPort, groupAddr, nil, getVersion(ver),
811 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})
Tinoj Josephcf161be2022-07-07 19:47:47 +0530816
vinokuma926cb3e2023-03-29 11:41:06 +0530817 if ig.IsChannelBasedGroup {
818 grpName := mvp.GetMvlanGroup(ig.GroupAddr)
819 if grpName != "" {
820 ig.IgmpGroupLock.Lock()
821 ig.DelReceiver(cntx, device, StaticPort, groupAddr, nil, 0xFF)
822 ig.IgmpGroupLock.Unlock()
823 ig.updateGroupName(cntx, grpName)
824 } else {
825 ig.DelIgmpGroup(cntx)
826 }
827 } else {
828 ig.IgmpGroupLock.Lock()
829 ig.DelReceiver(cntx, device, StaticPort, groupAddr, nil, 0xFF)
830 ig.IgmpGroupLock.Unlock()
831 }
832 if ig.NumDevicesActive() == 0 {
833 GetApplication().DelIgmpGroup(cntx, ig)
834 }
835 } else {
836 logger.Warnw(ctx, "Static IGMP Del received for unknown group", log.Fields{"Addr": groupAddr})
837 }
838 }
Tinoj Josephcf161be2022-07-07 19:47:47 +0530839}
840
vinokuma926cb3e2023-03-29 11:41:06 +0530841// getStaticChannelDiff - return the static channel newly added and removed from existing static group
Tinoj Josephcf161be2022-07-07 19:47:47 +0530842func (mvp *MvlanProfile) getStaticChannelDiff() (newlyAdded []net.IP, removed []net.IP, common []net.IP) {
vinokuma926cb3e2023-03-29 11:41:06 +0530843 var commonChannels []net.IP
844 newChannelList, _ := mvp.getAllStaticChannels()
845 existingChannelList, _ := mvp.getAllOldGroupStaticChannels()
846 if len(existingChannelList) == 0 {
847 return newChannelList, []net.IP{}, []net.IP{}
848 }
849 for _, newChannel := range append([]net.IP{}, newChannelList...) {
850 for _, existChannel := range append([]net.IP{}, existingChannelList...) {
851 // Remove common channels between existing and new list
852 // The remaining in the below slices give the results
853 // Remaining in newChannelList: Newly added
854 // Remaining in existingChannelList: Removed channels
855 if existChannel.Equal(newChannel) {
856 existingChannelList = removeIPFromList(existingChannelList, existChannel)
857 newChannelList = removeIPFromList(newChannelList, newChannel)
858 commonChannels = append(commonChannels, newChannel)
859 logger.Infow(ctx, "#############Channel: "+existChannel.String()+" New: "+newChannel.String(), log.Fields{"Added": newChannelList, "Removed": existingChannelList})
860 break
861 }
862 }
863 }
864 return newChannelList, existingChannelList, commonChannels
Tinoj Josephcf161be2022-07-07 19:47:47 +0530865}
866
vinokuma926cb3e2023-03-29 11:41:06 +0530867// getGroupChannelDiff - return the channel newly added and removed from existing group
Tinoj Josephcf161be2022-07-07 19:47:47 +0530868func (mvp *MvlanProfile) getGroupChannelDiff(newGroup *MvlanGroup, oldGroup *MvlanGroup) (newlyAdded []net.IP, removed []net.IP, common []net.IP) {
vinokuma926cb3e2023-03-29 11:41:06 +0530869 var commonChannels []net.IP
870 newChannelList, _ := newGroup.getAllChannels()
871 existingChannelList, _ := oldGroup.getAllChannels()
872 if len(existingChannelList) == 0 {
873 return newChannelList, []net.IP{}, []net.IP{}
874 }
875 for _, newChannel := range append([]net.IP{}, newChannelList...) {
876 for _, existChannel := range append([]net.IP{}, existingChannelList...) {
877 // Remove common channels between existing and new list
878 // The remaining in the below slices give the results
879 // Remaining in newChannelList: Newly added
880 // Remaining in existingChannelList: Removed channels
881 if existChannel.Equal(newChannel) {
882 existingChannelList = removeIPFromList(existingChannelList, existChannel)
883 newChannelList = removeIPFromList(newChannelList, newChannel)
884 commonChannels = append(commonChannels, newChannel)
885 logger.Infow(ctx, "#############Channel: "+existChannel.String()+" New: "+newChannel.String(), log.Fields{"Added": newChannelList, "Removed": existingChannelList})
886 break
887 }
888 }
889 }
890 return newChannelList, existingChannelList, commonChannels
Tinoj Josephcf161be2022-07-07 19:47:47 +0530891}
892
893// UpdateProfile - Updates the group & member info w.r.t the mvlan profile for the given device
Tinoj Joseph07cc5372022-07-18 22:53:51 +0530894func (mvp *MvlanProfile) UpdateProfile(cntx context.Context, deviceID string) {
vinokuma926cb3e2023-03-29 11:41:06 +0530895 logger.Infow(ctx, "Update Mvlan Profile task triggered", log.Fields{"Mvlan": mvp.Mvlan})
896 var removedStaticChannels []net.IP
897 addedStaticChannels := []net.IP{}
898 /* Taking mvpLock to protect the mvp groups and proxy */
899 mvp.mvpLock.RLock()
900 defer mvp.mvpLock.RUnlock()
Tinoj Josephcf161be2022-07-07 19:47:47 +0530901
vinokuma926cb3e2023-03-29 11:41:06 +0530902 serialNo := ""
903 if deviceID != "" {
904 if vd := GetApplication().GetDevice(deviceID); vd != nil {
905 serialNo = vd.SerialNum
906 if mvp.DevicesList[serialNo] != UpdateInProgress {
907 logger.Warnw(ctx, "Exiting Update Task since device not present in MvlanProfile", log.Fields{"Device": deviceID, "SerialNum": vd.SerialNum, "MvlanProfile": mvp})
908 return
909 }
910 } else {
911 logger.Errorw(ctx, "Volt Device not found. Stopping Update Mvlan Profile processing for device", log.Fields{"SerialNo": deviceID, "MvlanProfile": mvp})
912 return
913 }
914 }
Tinoj Josephcf161be2022-07-07 19:47:47 +0530915
vinokuma926cb3e2023-03-29 11:41:06 +0530916 // Update the groups based on static channels added & removed
917 if mvp.containsStaticChannels() {
918 addedStaticChannels, removedStaticChannels, _ = mvp.getStaticChannelDiff()
919 logger.Debugw(ctx, "Update Task - Static Group Changes", log.Fields{"Added": addedStaticChannels, "Removed": removedStaticChannels})
Tinoj Josephcf161be2022-07-07 19:47:47 +0530920
vinokuma926cb3e2023-03-29 11:41:06 +0530921 if len(addedStaticChannels) > 0 || len(removedStaticChannels) > 0 {
922 mvp.updateStaticGroups(cntx, deviceID, []net.IP{}, removedStaticChannels)
923 }
924 }
925 mvp.GroupsUpdated(cntx, deviceID)
926 if len(addedStaticChannels) > 0 {
927 mvp.updateStaticGroups(cntx, deviceID, addedStaticChannels, []net.IP{})
928 }
Tinoj Josephcf161be2022-07-07 19:47:47 +0530929
vinokuma926cb3e2023-03-29 11:41:06 +0530930 /* Need to handle if SSM params are modified for groups */
931 for key := range mvp.Groups {
932 _, _, commonChannels := mvp.getGroupChannelDiff(mvp.Groups[key], mvp.oldGroups[key])
933 if mvp.checkStaticGrpSSMProxyDiff(mvp.oldProxy[key], mvp.Proxy[key]) {
934 if mvp.Groups[key].IsStatic {
935 /* Static group proxy modified, need to trigger membership report with new mode/src-list for existing channels */
936 mvp.updateStaticGroups(cntx, deviceID, commonChannels, []net.IP{})
937 } else {
938 /* Dynamic group proxy modified, need to trigger membership report with new mode/src-list for existing channels */
939 mvp.updateDynamicGroups(cntx, deviceID, commonChannels, []net.IP{})
940 }
941 }
942 }
Tinoj Josephcf161be2022-07-07 19:47:47 +0530943
vinokuma926cb3e2023-03-29 11:41:06 +0530944 mvp.SetUpdateStatus(serialNo, NoOp)
Tinoj Josephcf161be2022-07-07 19:47:47 +0530945
vinokuma926cb3e2023-03-29 11:41:06 +0530946 if deviceID == "" || !mvp.isUpdateInProgress() {
947 mvp.oldGroups = nil
948 }
949 if err := mvp.WriteToDb(cntx); err != nil {
950 logger.Errorw(ctx, "Mvlan profile write to DB failed", log.Fields{"ProfileName": mvp.Name})
951 }
952 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 +0530953}
954
vinokuma926cb3e2023-03-29 11:41:06 +0530955// checkStaticGrpSSMProxyDiff- return true if the proxy of oldGroup is modified in newGroup
Tinoj Josephcf161be2022-07-07 19:47:47 +0530956func (mvp *MvlanProfile) checkStaticGrpSSMProxyDiff(oldProxy *MCGroupProxy, newProxy *MCGroupProxy) bool {
vinokuma926cb3e2023-03-29 11:41:06 +0530957 if oldProxy == nil && newProxy == nil {
958 return false
959 }
960 if (oldProxy == nil && newProxy != nil) ||
961 (oldProxy != nil && newProxy == nil) {
962 return true
963 }
Tinoj Josephcf161be2022-07-07 19:47:47 +0530964
vinokuma926cb3e2023-03-29 11:41:06 +0530965 if oldProxy.Mode != newProxy.Mode {
966 return true
967 }
Tinoj Josephcf161be2022-07-07 19:47:47 +0530968
vinokuma926cb3e2023-03-29 11:41:06 +0530969 oldSrcLst := oldProxy.SourceList
970 newSrcLst := newProxy.SourceList
971 oLen := len(oldSrcLst)
972 nLen := len(newSrcLst)
973 if oLen != nLen {
974 return true
975 }
Tinoj Josephcf161be2022-07-07 19:47:47 +0530976
vinokuma926cb3e2023-03-29 11:41:06 +0530977 visited := make([]bool, nLen)
Tinoj Josephcf161be2022-07-07 19:47:47 +0530978
vinokuma926cb3e2023-03-29 11:41:06 +0530979 /* check if any new IPs added in the src list, return true if present */
980 for i := 0; i < nLen; i++ {
981 found := false
982 element := newSrcLst[i]
983 for j := 0; j < oLen; j++ {
984 if visited[j] {
985 continue
986 }
987 if element.Equal(oldSrcLst[j]) {
988 visited[j] = true
989 found = true
990 break
991 }
992 }
993 if !found {
994 return true
995 }
996 }
Tinoj Josephcf161be2022-07-07 19:47:47 +0530997
vinokuma926cb3e2023-03-29 11:41:06 +0530998 visited = make([]bool, nLen)
999 /* check if any IPs removed from existing src list, return true if removed */
1000 for i := 0; i < oLen; i++ {
1001 found := false
1002 element := oldSrcLst[i]
1003 for j := 0; j < nLen; j++ {
1004 if visited[j] {
1005 continue
1006 }
1007 if element.Equal(newSrcLst[j]) {
1008 visited[j] = true
1009 found = true
1010 break
1011 }
1012 }
1013 if !found {
1014 return true
1015 }
1016 }
1017 return false
Tinoj Josephcf161be2022-07-07 19:47:47 +05301018}
1019
vinokuma926cb3e2023-03-29 11:41:06 +05301020// UpdateActiveChannelSubscriberAlarm - Updates the Active Channel Subscriber Alarm
Tinoj Josephcf161be2022-07-07 19:47:47 +05301021func (mvp *MvlanProfile) UpdateActiveChannelSubscriberAlarm() {
vinokuma926cb3e2023-03-29 11:41:06 +05301022 va := GetApplication()
1023 logger.Debugw(ctx, "Update of Active Channel Subscriber Alarm", log.Fields{"Mvlan": mvp.Mvlan})
1024 for srNo := range mvp.DevicesList {
1025 d, _ := va.GetDeviceBySerialNo(srNo)
1026 if d == nil {
1027 logger.Warnw(ctx, "Device info not found", log.Fields{"Device_SrNo": srNo, "Mvlan": mvp.Mvlan})
1028 return
1029 }
1030 d.Ports.Range(func(key, value interface{}) bool {
1031 //port := key.(string)
1032 vp := value.(*VoltPort)
1033 if vp.Type != VoltPortTypeAccess {
1034 return true
1035 }
1036 if mvp.MaxActiveChannels > vp.ActiveChannels && vp.ChannelPerSubAlarmRaised {
1037 serviceName := GetMcastServiceForSubAlarm(vp, mvp)
1038 logger.Debugw(ctx, "Clearing-SendActiveChannelPerSubscriberAlarm-due-to-update", log.Fields{"ActiveChannels": vp.ActiveChannels, "ServiceName": serviceName})
1039 vp.ChannelPerSubAlarmRaised = false
1040 } else if mvp.MaxActiveChannels < vp.ActiveChannels && !vp.ChannelPerSubAlarmRaised {
1041 /* When the max active channel count is reduced via update, we raise an alarm.
Sridhar Ravindrab76eb162025-07-02 01:25:10 +05301042 But the previous excess channels still exist until a leave or expiry */
vinokuma926cb3e2023-03-29 11:41:06 +05301043 serviceName := GetMcastServiceForSubAlarm(vp, mvp)
1044 logger.Debugw(ctx, "Raising-SendActiveChannelPerSubscriberAlarm-due-to-update", log.Fields{"ActiveChannels": vp.ActiveChannels, "ServiceName": serviceName})
1045 vp.ChannelPerSubAlarmRaised = true
1046 }
1047 return true
1048 })
1049 }
Tinoj Josephcf161be2022-07-07 19:47:47 +05301050}
1051
vinokuma926cb3e2023-03-29 11:41:06 +05301052// TriggerAssociatedFlowDelete - Re-trigger delete for pending delete flows
Tinoj Joseph07cc5372022-07-18 22:53:51 +05301053func (mvp *MvlanProfile) TriggerAssociatedFlowDelete(cntx context.Context, device string) bool {
vinokuma926cb3e2023-03-29 11:41:06 +05301054 mvp.mvpFlowLock.Lock()
Tinoj Josephcf161be2022-07-07 19:47:47 +05301055
vinokuma926cb3e2023-03-29 11:41:06 +05301056 cookieList := []uint64{}
1057 flowMap := mvp.PendingDeleteFlow[device]
Tinoj Josephcf161be2022-07-07 19:47:47 +05301058
vinokuma926cb3e2023-03-29 11:41:06 +05301059 for cookie := range flowMap {
1060 cookieList = append(cookieList, convertToUInt64(cookie))
1061 }
1062 mvp.mvpFlowLock.Unlock()
Tinoj Josephcf161be2022-07-07 19:47:47 +05301063
vinokuma926cb3e2023-03-29 11:41:06 +05301064 if len(cookieList) == 0 {
1065 return false
1066 }
Tinoj Josephcf161be2022-07-07 19:47:47 +05301067
vinokuma926cb3e2023-03-29 11:41:06 +05301068 for _, cookie := range cookieList {
1069 if vd := GetApplication().GetDevice(device); vd != nil {
1070 flow := &of.VoltFlow{}
1071 flow.SubFlows = make(map[uint64]*of.VoltSubFlow)
1072 subFlow := of.NewVoltSubFlow()
1073 subFlow.Cookie = cookie
1074 flow.SubFlows[cookie] = subFlow
1075 logger.Infow(ctx, "Retriggering Vnet Delete Flow", log.Fields{"Device": device, "Mvlan": mvp.Mvlan.String(), "Cookie": cookie})
1076 err := mvp.DelFlows(cntx, vd, flow)
1077 if err != nil {
1078 logger.Warnw(ctx, "De-Configuring IGMP Flow for device failed ", log.Fields{"Device": device, "err": err})
1079 }
1080 }
1081 }
1082 return true
Tinoj Josephcf161be2022-07-07 19:47:47 +05301083}
1084
vinokuma926cb3e2023-03-29 11:41:06 +05301085// JSONMarshal wrapper function for json Marshal MvlanProfile
1086func (mvp *MvlanProfile) JSONMarshal() ([]byte, error) {
1087 return json.Marshal(MvlanProfile{
1088 Name: mvp.Name,
1089 Mvlan: mvp.Mvlan,
1090 PonVlan: mvp.PonVlan,
1091 Groups: mvp.Groups,
1092 Proxy: mvp.Proxy,
1093 Version: mvp.Version,
1094 IsPonVlanPresent: mvp.IsPonVlanPresent,
1095 IsChannelBasedGroup: mvp.IsChannelBasedGroup,
1096 DevicesList: mvp.DevicesList,
1097 MaxActiveChannels: mvp.MaxActiveChannels,
1098 PendingDeleteFlow: mvp.PendingDeleteFlow,
1099 DeleteInProgress: mvp.DeleteInProgress,
1100 IgmpServVersion: mvp.IgmpServVersion,
1101 })
Tinoj Josephcf161be2022-07-07 19:47:47 +05301102}
1103
1104// removeIPFromList to remove ip from the list
1105func removeIPFromList(s []net.IP, value net.IP) []net.IP {
vinokuma926cb3e2023-03-29 11:41:06 +05301106 i := 0
1107 for i = 0; i < len(s); i++ {
1108 if s[i].Equal(value) {
1109 break
1110 }
1111 }
1112 if i != len(s) {
1113 //It means value is found in the slice
1114 return append(s[0:i], s[i+1:]...)
1115 }
1116 return s
Tinoj Josephcf161be2022-07-07 19:47:47 +05301117}
1118
1119// doesIPMatch to check if ip match with any ip from the list
1120func doesIPMatch(ip net.IP, ipsOrRange []string) bool {
vinokuma926cb3e2023-03-29 11:41:06 +05301121 for _, ipOrRange := range ipsOrRange {
1122 if strings.Contains(ipOrRange, "-") {
1123 var splits = strings.Split(ipOrRange, "-")
1124 ipStart := util.IP2LongConv(net.ParseIP(splits[0]))
1125 ipEnd := util.IP2LongConv(net.ParseIP(splits[1]))
1126 if ipEnd < ipStart {
1127 return false
1128 }
1129 ipInt := util.IP2LongConv(ip)
1130 if ipInt >= ipStart && ipInt <= ipEnd {
1131 return true
1132 }
1133 } else if ip.Equal(net.ParseIP(ipOrRange)) {
1134 return true
1135 }
1136 }
1137 return false
Tinoj Josephcf161be2022-07-07 19:47:47 +05301138}
1139
1140// IgmpProfile structure
1141type IgmpProfile struct {
vinokuma926cb3e2023-03-29 11:41:06 +05301142 ProfileID string
1143 IgmpVerToServer string
1144 Version string
1145 IgmpSourceIP net.IP
1146 UnsolicitedTimeOut uint32 //In seconds
1147 MaxResp uint32
1148 KeepAliveInterval uint32
1149 KeepAliveCount uint32
1150 LastQueryInterval uint32
1151 LastQueryCount uint32
1152 IgmpCos uint8
1153 FastLeave bool
1154 PeriodicQuery bool
1155 WithRAUpLink bool
1156 WithRADownLink bool
Tinoj Josephcf161be2022-07-07 19:47:47 +05301157}
1158
1159func newIgmpProfile(igmpProfileConfig *common.IGMPConfig) *IgmpProfile {
vinokuma926cb3e2023-03-29 11:41:06 +05301160 var igmpProfile IgmpProfile
1161 igmpProfile.ProfileID = igmpProfileConfig.ProfileID
1162 igmpProfile.UnsolicitedTimeOut = uint32(igmpProfileConfig.UnsolicitedTimeOut)
1163 igmpProfile.MaxResp = uint32(igmpProfileConfig.MaxResp)
Tinoj Josephcf161be2022-07-07 19:47:47 +05301164
vinokuma926cb3e2023-03-29 11:41:06 +05301165 keepAliveInterval := uint32(igmpProfileConfig.KeepAliveInterval)
Tinoj Josephcf161be2022-07-07 19:47:47 +05301166
vinokuma926cb3e2023-03-29 11:41:06 +05301167 //KeepAliveInterval should have a min of 10 seconds
1168 if keepAliveInterval < MinKeepAliveInterval {
1169 keepAliveInterval = MinKeepAliveInterval
1170 logger.Infow(ctx, "Auto adjust keepAliveInterval - Value < 10", log.Fields{"Received": igmpProfileConfig.KeepAliveInterval, "Configured": keepAliveInterval})
1171 }
1172 igmpProfile.KeepAliveInterval = keepAliveInterval
Tinoj Josephcf161be2022-07-07 19:47:47 +05301173
vinokuma926cb3e2023-03-29 11:41:06 +05301174 igmpProfile.KeepAliveCount = uint32(igmpProfileConfig.KeepAliveCount)
1175 igmpProfile.LastQueryInterval = uint32(igmpProfileConfig.LastQueryInterval)
1176 igmpProfile.LastQueryCount = uint32(igmpProfileConfig.LastQueryCount)
1177 igmpProfile.FastLeave = *igmpProfileConfig.FastLeave
1178 igmpProfile.PeriodicQuery = *igmpProfileConfig.PeriodicQuery
1179 igmpProfile.IgmpCos = uint8(igmpProfileConfig.IgmpCos)
1180 igmpProfile.WithRAUpLink = *igmpProfileConfig.WithRAUpLink
1181 igmpProfile.WithRADownLink = *igmpProfileConfig.WithRADownLink
Tinoj Josephcf161be2022-07-07 19:47:47 +05301182
vinokuma926cb3e2023-03-29 11:41:06 +05301183 if igmpProfileConfig.IgmpVerToServer == "2" || igmpProfileConfig.IgmpVerToServer == "v2" {
1184 igmpProfile.IgmpVerToServer = "2"
1185 } else {
1186 igmpProfile.IgmpVerToServer = "3"
1187 }
1188 igmpProfile.IgmpSourceIP = net.ParseIP(igmpProfileConfig.IgmpSourceIP)
Tinoj Josephcf161be2022-07-07 19:47:47 +05301189
vinokuma926cb3e2023-03-29 11:41:06 +05301190 return &igmpProfile
Tinoj Josephcf161be2022-07-07 19:47:47 +05301191}
1192
1193// newDefaultIgmpProfile Igmp profiles with default values
1194func newDefaultIgmpProfile() *IgmpProfile {
vinokuma926cb3e2023-03-29 11:41:06 +05301195 return &IgmpProfile{
1196 ProfileID: DefaultIgmpProfID,
1197 UnsolicitedTimeOut: 60,
1198 MaxResp: 10, // seconds
1199 KeepAliveInterval: 60, // seconds
1200 KeepAliveCount: 3, // TODO - May not be needed
1201 LastQueryInterval: 0, // TODO - May not be needed
1202 LastQueryCount: 0, // TODO - May not be needed
1203 FastLeave: true,
1204 PeriodicQuery: false, // TODO - May not be needed
1205 IgmpCos: 7, //p-bit value included in the IGMP packet
1206 WithRAUpLink: false, // TODO - May not be needed
1207 WithRADownLink: false, // TODO - May not be needed
1208 IgmpVerToServer: "3",
1209 IgmpSourceIP: net.ParseIP("172.27.0.1"), // This will be replaced by configuration
1210 }
Tinoj Josephcf161be2022-07-07 19:47:47 +05301211}
1212
1213// WriteToDb is utility to write Igmp Config Info to database
Tinoj Joseph07cc5372022-07-18 22:53:51 +05301214func (igmpProfile *IgmpProfile) WriteToDb(cntx context.Context) error {
vinokuma926cb3e2023-03-29 11:41:06 +05301215 igmpProfile.Version = database.PresentVersionMap[database.IgmpProfPath]
1216 b, err := json.Marshal(igmpProfile)
1217 if err != nil {
1218 return err
1219 }
1220 if err1 := db.PutIgmpProfile(cntx, igmpProfile.ProfileID, string(b)); err1 != nil {
1221 return err1
1222 }
1223 return nil
Tinoj Josephcf161be2022-07-07 19:47:47 +05301224}