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