[VOL-4754] Igmp code re-organization
Change-Id: Ib34ea7af0b445e0091593bca6882412fea220fb1
diff --git a/internal/pkg/application/igmpprofiles.go b/internal/pkg/application/igmpprofiles.go
new file mode 100644
index 0000000..b5b24d4
--- /dev/null
+++ b/internal/pkg/application/igmpprofiles.go
@@ -0,0 +1,1229 @@
+/*
+* Copyright 2022-present Open Networking Foundation
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+package application
+
+import (
+ "encoding/json"
+ "errors"
+ "net"
+ "strconv"
+ "strings"
+ "sync"
+
+ cntlr "voltha-go-controller/internal/pkg/controller"
+ "voltha-go-controller/internal/pkg/types"
+ "voltha-go-controller/database"
+ "voltha-go-controller/internal/pkg/of"
+ "voltha-go-controller/internal/pkg/util"
+ "voltha-go-controller/log"
+)
+
+// ------------------------------------------------------------
+// MVLAN related implemnetation
+//
+// Each MVLAN is configured with groups of multicast IPs. The idea of
+// groups is to be able to group some multicast channels into an individual
+// PON group and have a unique multicast GEM port for that set. However, in
+// the current implementation, the concept of grouping is not fully utilized.
+
+// MvlanGroup structure
+// A set of MC IPs form a group
+
+// MCGroupProxy identifies source specific multicast(SSM) config.
+type MCGroupProxy struct {
+ // Mode represents source list include/exclude
+ Mode common.MulticastSrcListMode
+ // SourceList represents list of multicast server IP addresses.
+ SourceList []net.IP
+}
+
+// MvlanGroup identifies MC group info
+type MvlanGroup struct {
+ Name string
+ Wildcard bool
+ McIPs []string
+ IsStatic bool
+}
+
+// OperInProgress type
+type OperInProgress uint8
+
+const (
+ // UpdateInProgress constant
+ UpdateInProgress OperInProgress = 2
+ // NoOp constant
+ NoOp OperInProgress = 1
+ // Nil constant
+ Nil OperInProgress = 0
+)
+
+// MvlanProfile : A set of groups of MC IPs for a MVLAN profile. It is assumed that
+// the MVLAN IP is not repeated within multiples groups and across
+// MVLAN profiles. The first match is used up on search to lcoate the
+// MVLAN profile for an MC IP
+type MvlanProfile struct {
+ Name string
+ Mvlan of.VlanType
+ PonVlan of.VlanType
+ Groups map[string]*MvlanGroup
+ Proxy map[string]*MCGroupProxy
+ Version string
+ IsPonVlanPresent bool
+ IsChannelBasedGroup bool
+ DevicesList map[string]OperInProgress //device serial number //here
+ oldGroups map[string]*MvlanGroup
+ oldProxy map[string]*MCGroupProxy
+ MaxActiveChannels uint32
+ PendingDeleteFlow map[string]map[string]bool
+ DeleteInProgress bool
+ IgmpServVersion map[string]*uint8
+ mvpLock sync.RWMutex
+ mvpFlowLock sync.RWMutex
+}
+
+// NewMvlanProfile is constructor for MVLAN profile.
+func NewMvlanProfile(name string, mvlan of.VlanType, ponVlan of.VlanType, isChannelBasedGroup bool, OLTSerialNums []string, actChannelPerPon uint32) *MvlanProfile {
+ var mvp MvlanProfile
+ mvp.Name = name
+ mvp.Mvlan = mvlan
+ mvp.PonVlan = ponVlan
+ mvp.mvpLock = sync.RWMutex{}
+ mvp.Groups = make(map[string]*MvlanGroup)
+ mvp.Proxy = make(map[string]*MCGroupProxy)
+ mvp.DevicesList = make(map[string]OperInProgress)
+ mvp.PendingDeleteFlow = make(map[string]map[string]bool)
+ mvp.IsChannelBasedGroup = isChannelBasedGroup
+ mvp.MaxActiveChannels = actChannelPerPon
+ mvp.DeleteInProgress = false
+ mvp.IgmpServVersion = make(map[string]*uint8)
+
+ if (ponVlan != of.VlanNone) && (ponVlan != 0) {
+ mvp.IsPonVlanPresent = true
+ }
+ return &mvp
+}
+
+// AddMvlanProxy for addition of groups to an MVLAN profile
+func (mvp *MvlanProfile) AddMvlanProxy(name string, proxyInfo common.MulticastGroupProxy) {
+ proxy := &MCGroupProxy{}
+ proxy.Mode = proxyInfo.Mode
+ proxy.SourceList = util.GetExpIPList(proxyInfo.SourceList)
+
+ if _, ok := mvp.Proxy[name]; !ok {
+ logger.Debugw(ctx, "Added MVLAN Proxy", log.Fields{"Name": name, "Proxy": proxy})
+ } else {
+ logger.Debugw(ctx, "Updated MVLAN Proxy", log.Fields{"Name": name, "Proxy": proxy})
+ }
+ if proxyInfo.IsStatic == common.IsStaticYes {
+ mvp.Groups[name].IsStatic = true
+ }
+ mvp.Proxy[name] = proxy
+}
+
+// AddMvlanGroup for addition of groups to an MVLAN profile
+func (mvp *MvlanProfile) AddMvlanGroup(name string, ips []string) {
+ mvg := &MvlanGroup{}
+ mvg.Name = name
+ mvg.Wildcard = len(ips) == 0
+ mvg.McIPs = ips
+ mvg.IsStatic = false
+ if _, ok := mvp.Groups[name]; !ok {
+ logger.Debugw(ctx, "Added MVLAN Group", log.Fields{"VLAN": mvp.Mvlan, "Name": name, "mvg": mvg, "IPs": mvg.McIPs})
+ } else {
+ logger.Debugw(ctx, "Updated MVLAN Group", log.Fields{"VLAN": mvp.Mvlan, "Name": name})
+ }
+ mvp.Groups[name] = mvg
+}
+
+// GetUsMatchVlan provides mvlan for US Match parameter
+func (mvp *MvlanProfile) GetUsMatchVlan() of.VlanType {
+ if mvp.IsPonVlanPresent {
+ return mvp.PonVlan
+ }
+ return mvp.Mvlan
+}
+
+// WriteToDb is utility to write Mvlan Profile Info to database
+func (mvp *MvlanProfile) WriteToDb() error {
+
+ if mvp.DeleteInProgress {
+ logger.Warnw(ctx, "Skipping Redis Update for MvlanProfile, MvlanProfile delete in progress", log.Fields{"Mvlan": mvp.Mvlan})
+ return nil
+ }
+
+ mvp.Version = database.PresentVersionMap[database.MvlanPath]
+ b, err := json.Marshal(mvp)
+ if err != nil {
+ return err
+ }
+ if err1 := db.PutMvlan(uint16(mvp.Mvlan), string(b)); err1 != nil {
+ return err1
+ }
+ return nil
+}
+
+//isChannelStatic - Returns true if the given channel is part of static group in the Mvlan Profile
+func (mvp *MvlanProfile) isChannelStatic(channel net.IP) bool {
+ for _, mvg := range mvp.Groups {
+ if mvg.IsStatic {
+ if isChannelStatic := doesIPMatch(channel, mvg.McIPs); isChannelStatic {
+ return true
+ }
+ }
+ }
+ return false
+}
+
+//containsStaticChannels - Returns if any static channels is part of the Mvlan Profile
+func (mvp *MvlanProfile) containsStaticChannels() bool {
+ for _, mvg := range mvp.Groups {
+ if mvg.IsStatic && len(mvg.McIPs) != 0 {
+ return true
+ }
+ }
+ return false
+}
+
+//getAllStaticChannels - Returns all static channels in the Mvlan Profile
+func (mvp *MvlanProfile) getAllStaticChannels() ([]net.IP, bool) {
+ channelList := []net.IP{}
+ containsStatic := false
+ for _, mvg := range mvp.Groups {
+ if mvg.IsStatic {
+ staticChannels, _ := mvg.getAllChannels()
+ channelList = append(channelList, staticChannels...)
+ }
+ }
+ if len(channelList) > 0 {
+ containsStatic = true
+ }
+ return channelList, containsStatic
+}
+
+//getAllOldGroupStaticChannels - Returns all static channels in the Mvlan Profile
+func (mvp *MvlanProfile) getAllOldGroupStaticChannels() ([]net.IP, bool) {
+ channelList := []net.IP{}
+ containsStatic := false
+ for _, mvg := range mvp.oldGroups {
+ if mvg.IsStatic {
+ staticChannels, _ := mvg.getAllChannels()
+ channelList = append(channelList, staticChannels...)
+ }
+ }
+ if len(channelList) > 0 {
+ containsStatic = true
+ }
+ return channelList, containsStatic
+}
+
+//getAllChannels - Returns all channels in the Mvlan Profile
+func (mvg *MvlanGroup) getAllChannels() ([]net.IP, bool) {
+ channelList := []net.IP{}
+
+ if mvg == nil || len(mvg.McIPs) == 0 {
+ return []net.IP{}, false
+ }
+
+ grpChannelOrRange := mvg.McIPs
+ for _, channelOrRange := range grpChannelOrRange {
+ if strings.Contains(channelOrRange, "-") {
+ var splits = strings.Split(channelOrRange, "-")
+ ipStart := util.IP2LongConv(net.ParseIP(splits[0]))
+ ipEnd := util.IP2LongConv(net.ParseIP(splits[1]))
+
+ for i := ipStart; i <= ipEnd; i++ {
+ channelList = append(channelList, util.Long2ipConv(i))
+ }
+ } else {
+ channelList = append(channelList, net.ParseIP(channelOrRange))
+ }
+ }
+ return channelList, true
+}
+
+//SetUpdateStatus - Sets profile update status for devices
+func (mvp *MvlanProfile) SetUpdateStatus(serialNum string, status OperInProgress) {
+ if serialNum != "" {
+ mvp.DevicesList[serialNum] = status
+ return
+ }
+
+ for srNo := range mvp.DevicesList {
+ mvp.DevicesList[srNo] = status
+ }
+}
+
+//isUpdateInProgress - checking is update is in progress for the mvlan profile
+func (mvp *MvlanProfile) isUpdateInProgress() bool {
+
+ for srNo := range mvp.DevicesList {
+ if mvp.DevicesList[srNo] == UpdateInProgress {
+ return true
+ }
+ }
+ return false
+}
+
+//IsUpdateInProgressForDevice - Checks is Mvlan Profile update is is progress for the given device
+func (mvp *MvlanProfile) IsUpdateInProgressForDevice(device string) bool {
+ if vd := GetApplication().GetDevice(device); vd != nil {
+ if mvp.DevicesList[vd.SerialNum] == UpdateInProgress {
+ return true
+ }
+ }
+ return false
+}
+
+// DelFromDb to delere mvlan from database
+func (mvp *MvlanProfile) DelFromDb() {
+ _ = db.DelMvlan(uint16(mvp.Mvlan))
+}
+
+//DelFlows - Triggers flow deletion after registering for flow indication event
+func (mvp *MvlanProfile) DelFlows(device *VoltDevice, flow *of.VoltFlow) error {
+ mvp.mvpFlowLock.Lock()
+ defer mvp.mvpFlowLock.Unlock()
+
+ var flowMap map[string]bool
+ var ok bool
+
+ for cookie := range flow.SubFlows {
+ cookie := strconv.FormatUint(cookie, 10)
+ fe := &FlowEvent{
+ eType: EventTypeMcastFlowRemoved,
+ device: device.Name,
+ cookie: cookie,
+ eventData: mvp,
+ }
+ device.RegisterFlowDelEvent(cookie, fe)
+
+ if flowMap, ok = mvp.PendingDeleteFlow[device.Name]; !ok {
+ flowMap = make(map[string]bool)
+ }
+ flowMap[cookie] = true
+ mvp.PendingDeleteFlow[device.Name] = flowMap
+ }
+ if err := mvp.WriteToDb(); err != nil {
+ logger.Errorw(ctx, "Mvlan profile write to DB failed", log.Fields{"ProfileName": mvp.Name})
+ }
+ return cntlr.GetController().DelFlows(device.NniPort, device.Name, flow)
+}
+
+//FlowRemoveSuccess - Process flow success indication
+func (mvp *MvlanProfile) FlowRemoveSuccess(cookie string, device string) {
+ mvp.mvpFlowLock.Lock()
+ defer mvp.mvpFlowLock.Unlock()
+
+ logger.Infow(ctx, "Mvlan Flow Remove Success Notification", log.Fields{"MvlanProfile": mvp.Name, "Cookie": cookie, "Device": device})
+
+ if _, ok := mvp.PendingDeleteFlow[device]; ok {
+ delete(mvp.PendingDeleteFlow[device], cookie)
+ }
+
+ if err := mvp.WriteToDb(); err != nil {
+ logger.Errorw(ctx, "Mvlan profile write to DB failed", log.Fields{"ProfileName": mvp.Name})
+ }
+}
+
+//FlowRemoveFailure - Process flow failure indication
+func (mvp *MvlanProfile) FlowRemoveFailure(cookie string, device string, errorCode uint32, errReason string) {
+
+ mvp.mvpFlowLock.Lock()
+ defer mvp.mvpFlowLock.Unlock()
+
+ if flowMap, ok := mvp.PendingDeleteFlow[device]; ok {
+ if _, ok := flowMap[cookie]; ok {
+ logger.Errorw(ctx, "Mvlan Flow Remove Failure Notification", log.Fields{"MvlanProfile": mvp.Name, "Cookie": cookie, "ErrorCode": errorCode, "ErrorReason": errReason, "Device": device})
+ return
+ }
+ }
+ logger.Errorw(ctx, "Mvlan Flow Del Failure Notification for Unknown cookie", log.Fields{"MvlanProfile": mvp.Name, "Cookie": cookie, "ErrorCode": errorCode, "ErrorReason": errReason})
+
+}
+
+// IsStaticGroup to check if group is static
+func (mvp *MvlanProfile) IsStaticGroup(groupName string) bool {
+ return mvp.Groups[groupName].IsStatic
+}
+
+// generateGroupKey to generate group key
+func (mvp *MvlanProfile) generateGroupKey(name string, ipAddr string) string {
+ if mvp.IsChannelBasedGroup {
+ return mvp.Mvlan.String() + "_" + ipAddr
+ }
+ return mvp.Mvlan.String() + "_" + name
+}
+
+// GetStaticGroupName to get static igmp group
+func (mvp *MvlanProfile) GetStaticGroupName(gip net.IP) string {
+ for _, mvg := range mvp.Groups {
+ if mvg.IsStatic {
+ if doesIPMatch(gip, mvg.McIPs) {
+ return mvg.Name
+ }
+ }
+ }
+ return ""
+}
+
+// GetStaticIgmpGroup to get static igmp group
+func (mvp *MvlanProfile) GetStaticIgmpGroup(gip net.IP) *IgmpGroup {
+
+ staticGroupName := mvp.GetStaticGroupName(gip)
+ grpKey := mvp.generateGroupKey(staticGroupName, gip.String())
+ logger.Debugw(ctx, "Get Static IGMP Group", log.Fields{"Group": grpKey})
+ ig, ok := GetApplication().IgmpGroups.Load(grpKey)
+ if ok {
+ logger.Debugw(ctx, "Get Static IGMP Group Success", log.Fields{"Group": grpKey})
+ return ig.(*IgmpGroup)
+ }
+ return nil
+}
+
+//pushIgmpMcastFlows - Adds all IGMP related flows (generic DS flow & static group flows)
+func (mvp *MvlanProfile) pushIgmpMcastFlows(OLTSerialNum string) {
+
+ mvp.mvpLock.RLock()
+ defer mvp.mvpLock.RUnlock()
+
+ if mvp.DevicesList[OLTSerialNum] == Nil {
+ logger.Infow(ctx, "Mvlan Profile not configure for device", log.Fields{"Device": OLTSerialNum, "Mvlan": mvp.Mvlan})
+ return
+ }
+
+ d := GetApplication().GetDeviceBySerialNo(OLTSerialNum)
+ if d == nil {
+ logger.Warnw(ctx, "Skipping Igmp & Mcast Flow processing: Device Not Found", log.Fields{"Device_SrNo": OLTSerialNum, "Mvlan": mvp.Mvlan})
+ return
+ }
+
+ p := d.GetPort(d.NniPort)
+
+ if p != nil && p.State == PortStateUp {
+ logger.Infow(ctx, "NNI Port Status is: UP & Vlan Enabled", log.Fields{"Device": d, "port": p})
+
+ //Push Igmp DS Control Flows
+ err := mvp.ApplyIgmpDSFlowForMvp(d.Name)
+ if err != nil {
+ logger.Errorw(ctx, "DS IGMP Flow Add Failed for device",
+ log.Fields{"Reason": err.Error(), "device": d.Name})
+ }
+
+ //Trigger Join for static channels
+ if channelList, containsStatic := mvp.getAllStaticChannels(); containsStatic {
+ mvp.ProcessStaticGroup(d.Name, channelList, true)
+ } else {
+ logger.Infow(ctx, "No Static Channels Present", log.Fields{"mvp": mvp.Name, "Mvlan": mvp.Mvlan})
+ }
+ }
+}
+//removeIgmpMcastFlows - Removes all IGMP related flows (generic DS flow & static group flows)
+func (mvp *MvlanProfile) removeIgmpMcastFlows(oltSerialNum string) {
+
+ mvp.mvpLock.RLock()
+ defer mvp.mvpLock.RUnlock()
+
+ if d := GetApplication().GetDeviceBySerialNo(oltSerialNum); d != nil {
+ p := d.GetPort(d.NniPort)
+ if p != nil {
+ logger.Infow(ctx, "NNI Port Status is: UP", log.Fields{"Device": d, "port": p})
+
+ // ***Do not change the order***
+ // When Vlan is disabled, the process end is determined by the DS Igmp flag in device
+
+ //Trigger Leave for static channels
+ if channelList, containsStatic := mvp.getAllStaticChannels(); containsStatic {
+ mvp.ProcessStaticGroup(d.Name, channelList, false)
+ } else {
+ logger.Infow(ctx, "No Static Channels Present", log.Fields{"mvp": mvp.Name, "Mvlan": mvp.Mvlan})
+ }
+
+ //Remove all dynamic members for the Mvlan Profile
+ GetApplication().IgmpGroups.Range(func(key, value interface{}) bool {
+ ig := value.(*IgmpGroup)
+ if ig.Mvlan == mvp.Mvlan {
+ igd := ig.Devices[d.Name]
+ ig.DelIgmpGroupDevice(igd)
+ if ig.NumDevicesActive() == 0 {
+ GetApplication().DelIgmpGroup(ig)
+ }
+ }
+ return true
+ })
+
+ //Remove DS Igmp trap flow
+ err := mvp.RemoveIgmpDSFlowForMvp(d.Name)
+ if err != nil {
+ logger.Errorw(ctx, "DS IGMP Flow Del Failed", log.Fields{"Reason": err.Error(), "device": d.Name})
+ }
+ }
+ }
+}
+
+// ApplyIgmpDSFlowForMvp to apply Igmp DS flow for mvlan.
+func (mvp *MvlanProfile) ApplyIgmpDSFlowForMvp(device string) error {
+ va := GetApplication()
+ dIntf, ok := va.DevicesDisc.Load(device)
+ if !ok {
+ return errors.New("Device Doesn't Exist")
+ }
+ d := dIntf.(*VoltDevice)
+ mvlan := mvp.Mvlan
+
+ flowAlreadyApplied, ok := d.IgmpDsFlowAppliedForMvlan[uint16(mvlan)]
+ if !ok || !flowAlreadyApplied {
+ flows, err := mvp.BuildIgmpDSFlows(device)
+ if err == nil {
+ err = cntlr.GetController().AddFlows(d.NniPort, device, flows)
+ if err != nil {
+ logger.Warnw(ctx, "Configuring IGMP Flow for device failed ", log.Fields{"Device": device, "err": err})
+ return err
+ }
+ d.IgmpDsFlowAppliedForMvlan[uint16(mvlan)] = true
+ logger.Infow(ctx, "Updating voltDevice that IGMP DS flow as \"added\" for ",
+ log.Fields{"device": d.SerialNum, "mvlan": mvlan})
+ } else {
+ logger.Errorw(ctx, "DS IGMP Flow Add Failed", log.Fields{"Reason": err.Error(), "Mvlan": mvlan})
+ }
+ }
+
+ return nil
+}
+
+// RemoveIgmpDSFlowForMvp to remove Igmp DS flow for mvlan.
+func (mvp *MvlanProfile) RemoveIgmpDSFlowForMvp(device string) error {
+
+ va := GetApplication()
+ mvlan := mvp.Mvlan
+
+ dIntf, ok := va.DevicesDisc.Load(device)
+ if !ok {
+ return errors.New("Device Doesn't Exist")
+ }
+ d := dIntf.(*VoltDevice)
+ /* No need of strict check during DS IGMP deletion
+ flowAlreadyApplied, ok := d.IgmpDsFlowAppliedForMvlan[uint16(mvlan)]
+ if ok && flowAlreadyApplied
+ */
+ flows, err := mvp.BuildIgmpDSFlows(device)
+ if err == nil {
+ flows.ForceAction = true
+
+ err = mvp.DelFlows(d, flows)
+ if err != nil {
+ logger.Warnw(ctx, "De-Configuring IGMP Flow for device failed ", log.Fields{"Device": device, "err": err})
+ return err
+ }
+ d.IgmpDsFlowAppliedForMvlan[uint16(mvlan)] = false
+ logger.Infow(ctx, "Updating voltDevice that IGMP DS flow as \"removed\" for ",
+ log.Fields{"device": d.SerialNum, "mvlan": mvlan})
+ } else {
+ logger.Errorw(ctx, "DS IGMP Flow Del Failed", log.Fields{"Reason": err.Error()})
+ }
+
+ return nil
+}
+
+// BuildIgmpDSFlows to build Igmp DS flows for NNI port
+func (mvp *MvlanProfile) BuildIgmpDSFlows(device string) (*of.VoltFlow, error) {
+ dIntf, ok := GetApplication().DevicesDisc.Load(device)
+ if !ok {
+ return nil, errors.New("Device Doesn't Exist")
+ }
+ d := dIntf.(*VoltDevice)
+
+ logger.Infow(ctx, "Building DS IGMP Flow for NNI port", log.Fields{"vs": d.NniPort, "Mvlan": mvp.Mvlan})
+ flow := &of.VoltFlow{}
+ flow.SubFlows = make(map[uint64]*of.VoltSubFlow)
+ subFlow := of.NewVoltSubFlow()
+ subFlow.SetTableID(0)
+ subFlow.SetMatchVlan(mvp.Mvlan)
+
+ nniPort, err := GetApplication().GetNniPort(device)
+ if err != nil {
+ return nil, err
+ }
+ nniPortID, err1 := GetApplication().GetPortID(nniPort)
+ if err1 != nil {
+ return nil, errors.New("Unknown NNI outport")
+ }
+ subFlow.SetInPort(nniPortID)
+ subFlow.SetIgmpMatch()
+ subFlow.SetReportToController()
+ subFlow.Cookie = uint64(nniPortID)<<32 | uint64(mvp.Mvlan)
+ subFlow.Priority = of.IgmpFlowPriority
+
+ flow.SubFlows[subFlow.Cookie] = subFlow
+ logger.Infow(ctx, "Built DS IGMP flow", log.Fields{"cookie": subFlow.Cookie, "subflow": subFlow})
+ return flow, nil
+}
+
+//updateStaticGroups - Generates static joins & leaves for newly added and removed static channels respectively
+func (mvp *MvlanProfile) updateStaticGroups(deviceID string, added []net.IP, removed []net.IP) {
+
+ //Update static group configs for all associated devices
+ updateGroups := func(key interface{}, value interface{}) bool {
+ d := value.(*VoltDevice)
+
+ if mvp.DevicesList[d.SerialNum] == Nil {
+ logger.Infow(ctx, "Mvlan Profile not configure for device", log.Fields{"Device": d, "Profile Device List": mvp.DevicesList})
+ return true
+ }
+ //TODO if mvp.IsChannelBasedGroup {
+ mvp.ProcessStaticGroup(d.Name, added, true)
+ mvp.ProcessStaticGroup(d.Name, removed, false)
+ //}
+ return true
+ }
+
+ if deviceID != "" {
+ vd := GetApplication().GetDevice(deviceID)
+ updateGroups(deviceID, vd)
+ } else {
+ GetApplication().DevicesDisc.Range(updateGroups)
+ }
+}
+
+//updateDynamicGroups - Generates joins with updated sources for existing channels
+func (mvp *MvlanProfile) updateDynamicGroups(deviceID string, added []net.IP, removed []net.IP) {
+
+ //mvlan := mvp.Mvlan
+ va := GetApplication()
+
+ updateGroups := func(key interface{}, value interface{}) bool {
+ d := value.(*VoltDevice)
+
+ if mvp.DevicesList[d.SerialNum] == Nil {
+ logger.Infow(ctx, "Mvlan Profile not configure for device", log.Fields{"Device": d, "Profile Device List": mvp.DevicesList})
+ return true
+ }
+ for _, groupAddr := range added {
+
+ _, gName := va.GetMvlanProfileForMcIP(mvp.Name, groupAddr)
+ grpKey := mvp.generateGroupKey(gName, groupAddr.String())
+ logger.Debugw(ctx, "IGMP Group", log.Fields{"Group": grpKey, "groupAddr": groupAddr})
+ if igIntf, ok := va.IgmpGroups.Load(grpKey); ok {
+ ig := igIntf.(*IgmpGroup)
+ if igd, ok := ig.getIgmpGroupDevice(d.Name); ok {
+ if igcIntf, ok := igd.GroupChannels.Load(groupAddr.String()); ok {
+ igc := igcIntf.(*IgmpGroupChannel)
+ incl := false
+ var ip []net.IP
+ var groupModified = false
+ if _, ok := mvp.Proxy[igc.GroupName]; ok {
+ if mvp.Proxy[igc.GroupName].Mode == common.Include {
+ incl = true
+ }
+ ip = mvp.Proxy[igc.GroupName].SourceList
+ }
+ for port, igp := range igc.NewReceivers {
+ // Process the include/exclude list which may end up modifying the group
+ if change, _ := igc.ProcessSources(port, ip, incl); change {
+ groupModified = true
+ }
+ igc.ProcessMode(port, incl)
+
+ if err := igp.WriteToDb(igc.Mvlan, igc.GroupAddr, igc.Device); err != nil {
+ logger.Errorw(ctx, "Igmp group port Write to DB failed", log.Fields{"mvlan": igc.Mvlan, "GroupAddr": igc.GroupAddr})
+ }
+ }
+ // If the group is modified as this is the first receiver or due to include/exclude list modification
+ // send a report to the upstream multicast servers
+ if groupModified {
+ logger.Debug(ctx, "Group Modified and IGMP report sent to the upstream server")
+ igc.SendReport(false)
+ }
+ if err := igc.WriteToDb(); err != nil {
+ logger.Errorw(ctx, "Igmp group channel Write to DB failed", log.Fields{"mvlan": igc.Mvlan, "GroupAddr": igc.GroupAddr})
+ }
+ }
+ }
+ }
+ }
+
+ return true
+ }
+
+ if deviceID != "" {
+ vd := GetApplication().GetDevice(deviceID)
+ updateGroups(deviceID, vd)
+ } else {
+ GetApplication().DevicesDisc.Range(updateGroups)
+ }
+}
+
+//GroupsUpdated - Handles removing of Igmp Groups, flows & group table entries for
+//channels removed as part of update
+func (mvp *MvlanProfile) GroupsUpdated(deviceID string) {
+
+ deleteChannelIfRemoved := func(key interface{}, value interface{}) bool {
+ ig := value.(*IgmpGroup)
+
+ if ig.Mvlan != mvp.Mvlan {
+ return true
+ }
+ grpName := ig.GroupName
+ logger.Infow(ctx, "###Update Cycle", log.Fields{"IG": ig.GroupName, "Addr": ig.GroupAddr})
+ //Check if group exists and remove the entire group object otherwise
+ if currentChannels := mvp.Groups[grpName]; currentChannels != nil {
+
+ if mvp.IsChannelBasedGroup {
+ channelPresent := doesIPMatch(ig.GroupAddr, currentChannels.McIPs)
+ if channelPresent || mvp.isChannelStatic(ig.GroupAddr) {
+ return true
+ }
+ } else {
+ allExistingChannels := ig.GetAllIgmpChannelForDevice(deviceID)
+ for channel := range allExistingChannels {
+ channelIP := net.ParseIP(channel)
+ channelPresent := mvp.IsChannelPresent(channelIP, currentChannels.McIPs, mvp.IsStaticGroup(ig.GroupName))
+ if channelPresent {
+ staticChannel := mvp.isChannelStatic(channelIP)
+ logger.Infow(ctx, "###Channel Comparision", log.Fields{"staticChannel": staticChannel, "Group": mvp.IsStaticGroup(ig.GroupName), "Channel": channel})
+ // Logic:
+ // If channel is Static & existing Group is also static - No migration required
+ // If channel is not Static & existing Group is also not static - No migration required
+
+ // If channel is Static and existing Group is not static - Migrate (from dynamic to static)
+ // (Channel already part of dynamic, added to static)
+
+ // If channel is not Static but existing Group is static - Migrate (from static to dynamic)
+ // (Channel removed from satic but part of dynamic)
+ if (staticChannel != mvp.IsStaticGroup(ig.GroupName)) || (ig.IsGroupStatic != mvp.IsStaticGroup(ig.GroupName)) { // Equivalent of XOR
+ ig.HandleGroupMigration(deviceID, channelIP)
+ } else {
+ if (ig.IsGroupStatic) && mvp.IsStaticGroup(ig.GroupName) {
+ if ig.GroupName != mvp.GetStaticGroupName(channelIP) {
+ ig.HandleGroupMigration(deviceID, channelIP)
+ }
+ }
+ continue
+ }
+ } else {
+ logger.Debugw(ctx, "Channel Removed", log.Fields{"Channel": channel, "Group": grpName})
+ ig.DelIgmpChannel(deviceID, net.ParseIP(channel))
+ if ig.NumDevicesActive() == 0 {
+ GetApplication().DelIgmpGroup(ig)
+ }
+ }
+ }
+ ig.IsGroupStatic = mvp.IsStaticGroup(ig.GroupName)
+ if err := ig.WriteToDb(); err != nil {
+ logger.Errorw(ctx, "Igmp group Write to DB failed", log.Fields{"groupName": ig.GroupName})
+ }
+ return true
+ }
+ }
+ logger.Debugw(ctx, "Group Removed", log.Fields{"Channel": ig.GroupAddr, "Group": grpName, "ChannelBasedGroup": ig.IsChannelBasedGroup})
+ ig.DelIgmpGroup()
+ logger.Debugw(ctx, "Removed Igmp Group", log.Fields{"Channel": ig.GroupAddr, "Group": grpName})
+ return true
+ }
+ GetApplication().IgmpGroups.Range(deleteChannelIfRemoved)
+}
+
+// IsChannelPresent to check if channel is present
+func (mvp *MvlanProfile) IsChannelPresent(channelIP net.IP, groupChannelList []string, IsStaticGroup bool) bool {
+ // Only in case of static group, migration need to be supported.
+ // Dynamic to dynamic group migration not supported currently
+ if doesIPMatch(channelIP, groupChannelList) || mvp.isChannelStatic(channelIP) {
+ return true
+ } else if IsStaticGroup {
+ return (mvp.GetMvlanGroup(channelIP) != "")
+ }
+
+ return false
+}
+
+
+// GetMvlanGroup to get mvlan group
+func (mvp *MvlanProfile) GetMvlanGroup(ip net.IP) string {
+ //Check for Static Group First
+ if mvp.containsStaticChannels() {
+ grpName := mvp.GetStaticGroupName(ip)
+ if grpName != "" {
+ return grpName
+ }
+ }
+
+ for _, mvg := range mvp.Groups {
+ if mvg.Wildcard {
+ return mvg.Name
+ }
+ if doesIPMatch(ip, mvg.McIPs) {
+ return mvg.Name
+ }
+ }
+ return ""
+}
+
+// ProcessStaticGroup - Process Static Join/Leave Req for static channels
+func (mvp *MvlanProfile) ProcessStaticGroup(device string, groupAddresses []net.IP, isJoin bool) {
+
+ logger.Debugw(ctx, "Received Static Group Request", log.Fields{"Device": device, "Join": isJoin, "Group Address List": groupAddresses})
+
+ mvlan := mvp.Mvlan
+ va := GetApplication()
+
+ //TODO - Handle bulk add of groupAddr
+ for _, groupAddr := range groupAddresses {
+
+ ig := mvp.GetStaticIgmpGroup(groupAddr)
+ if isJoin {
+ vd := va.GetDevice(device)
+ igmpProf, _, _ := getIgmpProxyCfgAndIP(mvlan, vd.SerialNum)
+ ver := igmpProf.IgmpVerToServer
+
+ if ig == nil {
+ // First time group Creation: Create the IGMP group and then add the receiver to the group
+ logger.Infow(ctx, "Static IGMP Add received for new group", log.Fields{"Addr": groupAddr, "Port": StaticPort})
+ if ig := GetApplication().AddIgmpGroup(mvp.Name, groupAddr, device); ig != nil {
+ ig.IgmpGroupLock.Lock()
+ ig.AddReceiver(device, StaticPort, groupAddr, nil, getVersion(ver),
+ 0, 0, 0xFF)
+ ig.IgmpGroupLock.Unlock()
+ } else {
+ logger.Warnw(ctx, "Static IGMP Group Creation Failed", log.Fields{"Addr": groupAddr})
+ }
+ } else {
+ //Converting existing dynamic group to static group
+ if !mvp.IsStaticGroup(ig.GroupName) {
+ ig.updateGroupName(ig.GroupName)
+ }
+ // Update case: If the IGMP group is already created. just add the receiver
+ logger.Infow(ctx, "Static IGMP Add received for existing group", log.Fields{"Addr": groupAddr, "Port": StaticPort})
+ ig.IgmpGroupLock.Lock()
+ ig.AddReceiver(device, StaticPort, groupAddr, nil, getVersion(ver),
+ 0, 0, 0xFF)
+ ig.IgmpGroupLock.Unlock()
+ }
+ } else if ig != nil {
+ logger.Infow(ctx, "Static IGMP Del received for existing group", log.Fields{"Addr": groupAddr, "Port": StaticPort})
+
+ if ig.IsChannelBasedGroup {
+ grpName := mvp.GetMvlanGroup(ig.GroupAddr)
+ if grpName != "" {
+ ig.IgmpGroupLock.Lock()
+ ig.DelReceiver(device, StaticPort, groupAddr, nil, 0xFF)
+ ig.IgmpGroupLock.Unlock()
+ ig.updateGroupName(grpName)
+ } else {
+ ig.DelIgmpGroup()
+ }
+ } else {
+ ig.IgmpGroupLock.Lock()
+ ig.DelReceiver(device, StaticPort, groupAddr, nil, 0xFF)
+ ig.IgmpGroupLock.Unlock()
+ }
+ if ig.NumDevicesActive() == 0 {
+ GetApplication().DelIgmpGroup(ig)
+ }
+ } else {
+ logger.Warnw(ctx, "Static IGMP Del received for unknown group", log.Fields{"Addr": groupAddr})
+ }
+ }
+}
+
+//getStaticChannelDiff - return the static channel newly added and removed from existing static group
+func (mvp *MvlanProfile) getStaticChannelDiff() (newlyAdded []net.IP, removed []net.IP, common []net.IP) {
+
+ var commonChannels []net.IP
+ newChannelList, _ := mvp.getAllStaticChannels()
+ existingChannelList, _ := mvp.getAllOldGroupStaticChannels()
+ if len(existingChannelList) == 0 {
+ return newChannelList, []net.IP{}, []net.IP{}
+ }
+ for _, newChannel := range append([]net.IP{}, newChannelList...) {
+ for _, existChannel := range append([]net.IP{}, existingChannelList...) {
+
+ //Remove common channels between existing and new list
+ // The remaining in the below slices give the results
+ // Remaining in newChannelList: Newly added
+ // Remaining in existingChannelList: Removed channels
+ if existChannel.Equal(newChannel) {
+ existingChannelList = removeIPFromList(existingChannelList, existChannel)
+ newChannelList = removeIPFromList(newChannelList, newChannel)
+ commonChannels = append(commonChannels, newChannel)
+ logger.Infow(ctx, "#############Channel: "+existChannel.String()+" New: "+newChannel.String(), log.Fields{"Added": newChannelList, "Removed": existingChannelList})
+ break
+ }
+ }
+ }
+ return newChannelList, existingChannelList, commonChannels
+}
+
+//getGroupChannelDiff - return the channel newly added and removed from existing group
+func (mvp *MvlanProfile) getGroupChannelDiff(newGroup *MvlanGroup, oldGroup *MvlanGroup) (newlyAdded []net.IP, removed []net.IP, common []net.IP) {
+
+ var commonChannels []net.IP
+ newChannelList, _ := newGroup.getAllChannels()
+ existingChannelList, _ := oldGroup.getAllChannels()
+ if len(existingChannelList) == 0 {
+ return newChannelList, []net.IP{}, []net.IP{}
+ }
+ for _, newChannel := range append([]net.IP{}, newChannelList...) {
+ for _, existChannel := range append([]net.IP{}, existingChannelList...) {
+
+ //Remove common channels between existing and new list
+ // The remaining in the below slices give the results
+ // Remaining in newChannelList: Newly added
+ // Remaining in existingChannelList: Removed channels
+ if existChannel.Equal(newChannel) {
+ existingChannelList = removeIPFromList(existingChannelList, existChannel)
+ newChannelList = removeIPFromList(newChannelList, newChannel)
+ commonChannels = append(commonChannels, newChannel)
+ logger.Infow(ctx, "#############Channel: "+existChannel.String()+" New: "+newChannel.String(), log.Fields{"Added": newChannelList, "Removed": existingChannelList})
+ break
+ }
+ }
+ }
+ return newChannelList, existingChannelList, commonChannels
+}
+
+// UpdateProfile - Updates the group & member info w.r.t the mvlan profile for the given device
+func (mvp *MvlanProfile) UpdateProfile(deviceID string) {
+ logger.Infow(ctx, "Update Mvlan Profile task triggered", log.Fields{"Mvlan": mvp.Mvlan})
+ var removedStaticChannels []net.IP
+ addedStaticChannels := []net.IP{}
+ /* Taking mvpLock to protect the mvp groups and proxy */
+ mvp.mvpLock.RLock()
+ defer mvp.mvpLock.RUnlock()
+
+ serialNo := ""
+ if deviceID != "" {
+ if vd := GetApplication().GetDevice(deviceID); vd != nil {
+ serialNo = vd.SerialNum
+ if mvp.DevicesList[serialNo] != UpdateInProgress {
+ logger.Warnw(ctx, "Exiting Update Task since device not present in MvlanProfile", log.Fields{"Device": deviceID, "SerialNum": vd.SerialNum, "MvlanProfile": mvp})
+ return
+ }
+ } else {
+ logger.Errorw(ctx, "Volt Device not found. Stopping Update Mvlan Profile processing for device", log.Fields{"SerialNo": deviceID, "MvlanProfile": mvp})
+ return
+ }
+ }
+
+ //Update the groups based on static channels added & removed
+ if mvp.containsStaticChannels() {
+ addedStaticChannels, removedStaticChannels, _ = mvp.getStaticChannelDiff()
+ logger.Debugw(ctx, "Update Task - Static Group Changes", log.Fields{"Added": addedStaticChannels, "Removed": removedStaticChannels})
+
+ if len(addedStaticChannels) > 0 || len(removedStaticChannels) > 0 {
+ mvp.updateStaticGroups(deviceID, []net.IP{}, removedStaticChannels)
+ }
+ }
+ mvp.GroupsUpdated(deviceID)
+ if len(addedStaticChannels) > 0 {
+ mvp.updateStaticGroups(deviceID, addedStaticChannels, []net.IP{})
+ }
+
+ /* Need to handle if SSM params are modified for groups */
+ for key := range mvp.Groups {
+ _, _, commonChannels := mvp.getGroupChannelDiff(mvp.Groups[key], mvp.oldGroups[key])
+ if mvp.checkStaticGrpSSMProxyDiff(mvp.oldProxy[key], mvp.Proxy[key]) {
+ if mvp.Groups[key].IsStatic {
+ /* Static group proxy modified, need to trigger membership report with new mode/src-list for existing channels */
+ mvp.updateStaticGroups(deviceID, commonChannels, []net.IP{})
+ } else {
+ /* Dynamic group proxy modified, need to trigger membership report with new mode/src-list for existing channels */
+ mvp.updateDynamicGroups(deviceID, commonChannels, []net.IP{})
+ }
+ }
+ }
+
+ mvp.SetUpdateStatus(serialNo, NoOp)
+
+ if deviceID == "" || !mvp.isUpdateInProgress() {
+ mvp.oldGroups = nil
+ }
+ if err := mvp.WriteToDb(); err != nil {
+ logger.Errorw(ctx, "Mvlan profile write to DB failed", log.Fields{"ProfileName": mvp.Name})
+ }
+ logger.Debugw(ctx, "Updated MVLAN Profile", log.Fields{"VLAN": mvp.Mvlan, "Name": mvp.Name, "Grp IPs": mvp.Groups})
+}
+
+//checkStaticGrpSSMProxyDiff- return true if the proxy of oldGroup is modified in newGroup
+func (mvp *MvlanProfile) checkStaticGrpSSMProxyDiff(oldProxy *MCGroupProxy, newProxy *MCGroupProxy) bool {
+
+ if oldProxy == nil && newProxy == nil {
+ return false
+ }
+ if (oldProxy == nil && newProxy != nil) ||
+ (oldProxy != nil && newProxy == nil) {
+ return true
+ }
+
+ if oldProxy.Mode != newProxy.Mode {
+ return true
+ }
+
+ oldSrcLst := oldProxy.SourceList
+ newSrcLst := newProxy.SourceList
+ oLen := len(oldSrcLst)
+ nLen := len(newSrcLst)
+ if oLen != nLen {
+ return true
+ }
+
+ visited := make([]bool, nLen)
+
+ /* check if any new IPs added in the src list, return true if present */
+ for i := 0; i < nLen; i++ {
+ found := false
+ element := newSrcLst[i]
+ for j := 0; j < oLen; j++ {
+ if visited[j] {
+ continue
+ }
+ if element.Equal(oldSrcLst[j]) {
+ visited[j] = true
+ found = true
+ break
+ }
+ }
+ if !found {
+ return true
+ }
+ }
+
+ visited = make([]bool, nLen)
+ /* check if any IPs removed from existing src list, return true if removed */
+ for i := 0; i < oLen; i++ {
+ found := false
+ element := oldSrcLst[i]
+ for j := 0; j < nLen; j++ {
+ if visited[j] {
+ continue
+ }
+ if element.Equal(newSrcLst[j]) {
+ visited[j] = true
+ found = true
+ break
+ }
+ }
+ if !found {
+ return true
+ }
+ }
+ return false
+}
+
+
+//UpdateActiveChannelSubscriberAlarm - Updates the Active Channel Subscriber Alarm
+func (mvp *MvlanProfile) UpdateActiveChannelSubscriberAlarm() {
+ va := GetApplication()
+ logger.Debugw(ctx, "Update of Active Channel Subscriber Alarm", log.Fields{"Mvlan": mvp.Mvlan})
+ for srNo := range mvp.DevicesList {
+ d := va.GetDeviceBySerialNo(srNo)
+ if d == nil {
+ logger.Warnw(ctx, "Device info not found", log.Fields{"Device_SrNo": srNo, "Mvlan": mvp.Mvlan})
+ return
+ }
+ d.Ports.Range(func(key, value interface{}) bool {
+ //port := key.(string)
+ vp := value.(*VoltPort)
+ if vp.Type != VoltPortTypeAccess {
+ return true
+ }
+ if mvp.MaxActiveChannels > vp.ActiveChannels && vp.ChannelPerSubAlarmRaised {
+ serviceName := GetMcastServiceForSubAlarm(vp, mvp)
+ logger.Debugw(ctx, "Clearing-SendActiveChannelPerSubscriberAlarm-due-to-update", log.Fields{"ActiveChannels": vp.ActiveChannels, "ServiceName": serviceName})
+ vp.ChannelPerSubAlarmRaised = false
+ } else if mvp.MaxActiveChannels < vp.ActiveChannels && !vp.ChannelPerSubAlarmRaised {
+ /* When the max active channel count is reduced via update, we raise an alarm.
+ But the previous excess channels still exist until a leave or expiry */
+ serviceName := GetMcastServiceForSubAlarm(vp, mvp)
+ logger.Debugw(ctx, "Raising-SendActiveChannelPerSubscriberAlarm-due-to-update", log.Fields{"ActiveChannels": vp.ActiveChannels, "ServiceName": serviceName})
+ vp.ChannelPerSubAlarmRaised = true
+ }
+ return true
+ })
+ }
+}
+
+//TriggerAssociatedFlowDelete - Re-trigger delete for pending delete flows
+func (mvp *MvlanProfile) TriggerAssociatedFlowDelete(device string) bool {
+ mvp.mvpFlowLock.Lock()
+
+ cookieList := []uint64{}
+ flowMap := mvp.PendingDeleteFlow[device]
+
+ for cookie := range flowMap {
+ cookieList = append(cookieList, convertToUInt64(cookie))
+ }
+ mvp.mvpFlowLock.Unlock()
+
+ if len(cookieList) == 0 {
+ return false
+ }
+
+ for _, cookie := range cookieList {
+ if vd := GetApplication().GetDevice(device); vd != nil {
+ flow := &of.VoltFlow{}
+ flow.SubFlows = make(map[uint64]*of.VoltSubFlow)
+ subFlow := of.NewVoltSubFlow()
+ subFlow.Cookie = cookie
+ flow.SubFlows[cookie] = subFlow
+ logger.Infow(ctx, "Retriggering Vnet Delete Flow", log.Fields{"Device": device, "Mvlan": mvp.Mvlan.String(), "Cookie": cookie})
+ err := mvp.DelFlows(vd, flow)
+ if err != nil {
+ logger.Warnw(ctx, "De-Configuring IGMP Flow for device failed ", log.Fields{"Device": device, "err": err})
+ }
+ }
+ }
+ return true
+}
+
+// JsonMarshal wrapper function for json Marshal MvlanProfile
+func (mvp *MvlanProfile) JsonMarshal() ([]byte, error) {
+ return json.Marshal(MvlanProfile{
+ Name: mvp.Name,
+ Mvlan: mvp.Mvlan,
+ PonVlan: mvp.PonVlan,
+ Groups: mvp.Groups,
+ Proxy: mvp.Proxy,
+ Version: mvp.Version,
+ IsPonVlanPresent: mvp.IsPonVlanPresent,
+ IsChannelBasedGroup: mvp.IsChannelBasedGroup,
+ DevicesList: mvp.DevicesList,
+ MaxActiveChannels: mvp.MaxActiveChannels,
+ PendingDeleteFlow: mvp.PendingDeleteFlow,
+ DeleteInProgress: mvp.DeleteInProgress,
+ IgmpServVersion: mvp.IgmpServVersion,
+ })
+}
+
+// removeIPFromList to remove ip from the list
+func removeIPFromList(s []net.IP, value net.IP) []net.IP {
+ i := 0
+ for i = 0; i < len(s); i++ {
+ if s[i].Equal(value) {
+ break
+ }
+ }
+ if i != len(s) {
+ //It means value is found in the slice
+ return append(s[0:i], s[i+1:]...)
+ }
+ return s
+}
+
+// doesIPMatch to check if ip match with any ip from the list
+func doesIPMatch(ip net.IP, ipsOrRange []string) bool {
+ for _, ipOrRange := range ipsOrRange {
+ if strings.Contains(ipOrRange, "-") {
+ var splits = strings.Split(ipOrRange, "-")
+ ipStart := util.IP2LongConv(net.ParseIP(splits[0]))
+ ipEnd := util.IP2LongConv(net.ParseIP(splits[1]))
+ if ipEnd < ipStart {
+ return false
+ }
+ ipInt := util.IP2LongConv(ip)
+ if ipInt >= ipStart && ipInt <= ipEnd {
+ return true
+ }
+ } else if ip.Equal(net.ParseIP(ipOrRange)) {
+ return true
+ }
+ }
+ return false
+}
+
+// IgmpProfile structure
+type IgmpProfile struct {
+ ProfileID string
+ UnsolicitedTimeOut uint32 //In seconds
+ MaxResp uint32
+ KeepAliveInterval uint32
+ KeepAliveCount uint32
+ LastQueryInterval uint32
+ LastQueryCount uint32
+ FastLeave bool
+ PeriodicQuery bool
+ IgmpCos uint8
+ WithRAUpLink bool
+ WithRADownLink bool
+ IgmpVerToServer string
+ IgmpSourceIP net.IP
+ Version string
+}
+
+func newIgmpProfile(igmpProfileConfig *common.IGMPConfig) *IgmpProfile {
+ var igmpProfile IgmpProfile
+ igmpProfile.ProfileID = igmpProfileConfig.ProfileID
+ igmpProfile.UnsolicitedTimeOut = uint32(igmpProfileConfig.UnsolicitedTimeOut)
+ igmpProfile.MaxResp = uint32(igmpProfileConfig.MaxResp)
+
+ keepAliveInterval := uint32(igmpProfileConfig.KeepAliveInterval)
+
+ //KeepAliveInterval should have a min of 10 seconds
+ if keepAliveInterval < MinKeepAliveInterval {
+ keepAliveInterval = MinKeepAliveInterval
+ logger.Infow(ctx, "Auto adjust keepAliveInterval - Value < 10", log.Fields{"Received": igmpProfileConfig.KeepAliveInterval, "Configured": keepAliveInterval})
+ }
+ igmpProfile.KeepAliveInterval = keepAliveInterval
+
+ igmpProfile.KeepAliveCount = uint32(igmpProfileConfig.KeepAliveCount)
+ igmpProfile.LastQueryInterval = uint32(igmpProfileConfig.LastQueryInterval)
+ igmpProfile.LastQueryCount = uint32(igmpProfileConfig.LastQueryCount)
+ igmpProfile.FastLeave = *igmpProfileConfig.FastLeave
+ igmpProfile.PeriodicQuery = *igmpProfileConfig.PeriodicQuery
+ igmpProfile.IgmpCos = uint8(igmpProfileConfig.IgmpCos)
+ igmpProfile.WithRAUpLink = *igmpProfileConfig.WithRAUpLink
+ igmpProfile.WithRADownLink = *igmpProfileConfig.WithRADownLink
+
+ if igmpProfileConfig.IgmpVerToServer == "2" || igmpProfileConfig.IgmpVerToServer == "v2" {
+ igmpProfile.IgmpVerToServer = "2"
+ } else {
+ igmpProfile.IgmpVerToServer = "3"
+ }
+ igmpProfile.IgmpSourceIP = net.ParseIP(igmpProfileConfig.IgmpSourceIP)
+
+ return &igmpProfile
+}
+
+// newDefaultIgmpProfile Igmp profiles with default values
+func newDefaultIgmpProfile() *IgmpProfile {
+ return &IgmpProfile{
+ ProfileID: DefaultIgmpProfID,
+ UnsolicitedTimeOut: 60,
+ MaxResp: 10, // seconds
+ KeepAliveInterval: 60, // seconds
+ KeepAliveCount: 3, // TODO - May not be needed
+ LastQueryInterval: 0, // TODO - May not be needed
+ LastQueryCount: 0, // TODO - May not be needed
+ FastLeave: true,
+ PeriodicQuery: false, // TODO - May not be needed
+ IgmpCos: 7, //p-bit value included in the IGMP packet
+ WithRAUpLink: false, // TODO - May not be needed
+ WithRADownLink: false, // TODO - May not be needed
+ IgmpVerToServer: "3",
+ IgmpSourceIP: net.ParseIP("172.27.0.1"), // This will be replaced by configuration
+ }
+}
+
+// WriteToDb is utility to write Igmp Config Info to database
+func (igmpProfile *IgmpProfile) WriteToDb() error {
+ igmpProfile.Version = database.PresentVersionMap[database.IgmpProfPath]
+ b, err := json.Marshal(igmpProfile)
+ if err != nil {
+ return err
+ }
+ if err1 := db.PutIgmpProfile(igmpProfile.ProfileID, string(b)); err1 != nil {
+ return err1
+ }
+ return nil
+}