blob: d8a2644794e873e55cb7088626bfd07f11f9bd4b [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 "net"
22
23 "github.com/google/gopacket/layers"
24
25 cntlr "voltha-go-controller/internal/pkg/controller"
Tinoj Josephcf161be2022-07-07 19:47:47 +053026 "voltha-go-controller/internal/pkg/of"
vinokuma926cb3e2023-03-29 11:41:06 +053027 common "voltha-go-controller/internal/pkg/types"
Tinoj Josephcf161be2022-07-07 19:47:47 +053028 "voltha-go-controller/log"
29)
30
31// IgmpGroupChannel structure
32type IgmpGroupChannel struct {
vinokuma926cb3e2023-03-29 11:41:06 +053033 CurReceivers map[string]*IgmpGroupPort `json:"-"`
34 NewReceivers map[string]*IgmpGroupPort `json:"-"`
35 proxyCfg **IgmpProfile
36 IgmpProxyIP **net.IP `json:"-"`
37 ServVersion *uint8
38 Device string
39 GroupName string
40 GroupAddr net.IP
41 ExcludeList []net.IP
42 IncludeList []net.IP `json:"-"`
43 Exclude int
44 GroupID uint32
45 Mvlan of.VlanType
46 Version uint8
Tinoj Josephcf161be2022-07-07 19:47:47 +053047}
48
49// NewIgmpGroupChannel is constructor for a channel. The default IGMP version is set to 3
50// as the protocol defines the way to manage backward compatibility
vinokuma926cb3e2023-03-29 11:41:06 +053051// The implementation handles simultaneous presence of lower versioned
Tinoj Josephcf161be2022-07-07 19:47:47 +053052// receivers
53func NewIgmpGroupChannel(igd *IgmpGroupDevice, groupAddr net.IP, version uint8) *IgmpGroupChannel {
vinokuma926cb3e2023-03-29 11:41:06 +053054 var igc IgmpGroupChannel
55 igc.Device = igd.Device
56 igc.GroupID = igd.GroupID
57 igc.GroupName = igd.GroupName
58 igc.GroupAddr = groupAddr
59 igc.Mvlan = igd.Mvlan
60 igc.Version = version
61 igc.CurReceivers = make(map[string]*IgmpGroupPort)
62 igc.NewReceivers = make(map[string]*IgmpGroupPort)
63 igc.proxyCfg = &igd.proxyCfg
64 igc.IgmpProxyIP = &igd.IgmpProxyIP
65 igc.ServVersion = igd.ServVersion
66 return &igc
Tinoj Josephcf161be2022-07-07 19:47:47 +053067}
68
69// NewIgmpGroupChannelFromBytes create the IGMP group channel from a byte slice
70func NewIgmpGroupChannelFromBytes(b []byte) (*IgmpGroupChannel, error) {
vinokuma926cb3e2023-03-29 11:41:06 +053071 var igc IgmpGroupChannel
72 if err := json.Unmarshal(b, &igc); err != nil {
73 return nil, err
74 }
75 igc.CurReceivers = make(map[string]*IgmpGroupPort)
76 igc.NewReceivers = make(map[string]*IgmpGroupPort)
77 return &igc, nil
Tinoj Josephcf161be2022-07-07 19:47:47 +053078}
79
80// RestorePorts to restore ports
Tinoj Joseph07cc5372022-07-18 22:53:51 +053081func (igc *IgmpGroupChannel) RestorePorts(cntx context.Context) {
vinokuma926cb3e2023-03-29 11:41:06 +053082 igc.migrateIgmpPorts(cntx)
83 ports, _ := db.GetIgmpRcvrs(cntx, igc.Mvlan, igc.GroupAddr, igc.Device)
84 for _, port := range ports {
85 b, ok := port.Value.([]byte)
86 if !ok {
87 logger.Warn(ctx, "The value type is not []byte")
88 continue
89 }
90 if igp, err := NewIgmpGroupPortFromBytes(b); err == nil {
91 igc.NewReceivers[igp.Port] = igp
92 logger.Infow(ctx, "Group Port Restored", log.Fields{"IGP": igp})
93 } else {
94 logger.Warn(ctx, "Failed to decode port from DB")
95 }
96 }
97 if err := igc.WriteToDb(cntx); err != nil {
98 logger.Errorw(ctx, "Igmp group channel Write to DB failed", log.Fields{"mvlan": igc.Mvlan, "GroupAddr": igc.GroupAddr})
99 }
Tinoj Josephcf161be2022-07-07 19:47:47 +0530100}
101
102// WriteToDb is utility to write IGMPGroupChannel Info to database
Tinoj Joseph07cc5372022-07-18 22:53:51 +0530103func (igc *IgmpGroupChannel) WriteToDb(cntx context.Context) error {
vinokuma926cb3e2023-03-29 11:41:06 +0530104 b, err := json.Marshal(igc)
105 if err != nil {
106 return err
107 }
108 if err1 := db.PutIgmpChannel(cntx, igc.Mvlan, igc.GroupName, igc.Device, igc.GroupAddr, string(b)); err1 != nil {
109 return err1
110 }
111 logger.Info(ctx, "IGC Updated")
112 return nil
Tinoj Josephcf161be2022-07-07 19:47:47 +0530113}
114
Tinoj Josephcf161be2022-07-07 19:47:47 +0530115// InclSourceIsIn checks if a source is in include list
116func (igc *IgmpGroupChannel) InclSourceIsIn(src net.IP) bool {
vinokuma926cb3e2023-03-29 11:41:06 +0530117 return IsIPPresent(src, igc.IncludeList)
Tinoj Josephcf161be2022-07-07 19:47:47 +0530118}
119
120// ExclSourceIsIn checks if a source is in exclude list
121func (igc *IgmpGroupChannel) ExclSourceIsIn(src net.IP) bool {
vinokuma926cb3e2023-03-29 11:41:06 +0530122 return IsIPPresent(src, igc.ExcludeList)
Tinoj Josephcf161be2022-07-07 19:47:47 +0530123}
124
125// AddInclSource adds a source is in include list
126func (igc *IgmpGroupChannel) AddInclSource(src net.IP) {
vinokuma926cb3e2023-03-29 11:41:06 +0530127 logger.Debugw(ctx, "Adding Include Source for Channel", log.Fields{"Channel": igc.GroupAddr.String(), "Device": igc.Device, "Src": src})
128 igc.IncludeList = append(igc.IncludeList, src)
Tinoj Josephcf161be2022-07-07 19:47:47 +0530129}
130
131// AddExclSource adds a source is in exclude list
132func (igc *IgmpGroupChannel) AddExclSource(src net.IP) {
vinokuma926cb3e2023-03-29 11:41:06 +0530133 logger.Debugw(ctx, "Adding Exclude Source for Channel", log.Fields{"Channel": igc.GroupAddr.String(), "Device": igc.Device, "Src": src})
134 igc.ExcludeList = append(igc.ExcludeList, src)
Tinoj Josephcf161be2022-07-07 19:47:47 +0530135}
136
137// UpdateExclSource update excl source list for the given channel
138func (igc *IgmpGroupChannel) UpdateExclSource(srcList []net.IP) bool {
vinokuma926cb3e2023-03-29 11:41:06 +0530139 logger.Debugw(ctx, "Updating Exclude Source for Channel", log.Fields{"Channel": igc.GroupAddr.String(), "Device": igc.Device, "Current List": igc.ExcludeList, "Incoming List": srcList})
140 if !igc.IsExclListChanged(srcList) {
141 return false
142 }
Tinoj Josephcf161be2022-07-07 19:47:47 +0530143
vinokuma926cb3e2023-03-29 11:41:06 +0530144 if igc.NumReceivers() == 1 {
145 igc.ExcludeList = srcList
146 } else {
147 igc.ExcludeList = igc.computeExclList(srcList)
148 }
Tinoj Josephcf161be2022-07-07 19:47:47 +0530149
vinokuma926cb3e2023-03-29 11:41:06 +0530150 logger.Debugw(ctx, "Updated Exclude Source for Channel", log.Fields{"Channel": igc.GroupAddr.String(), "Device": igc.Device, "Updated Excl List": igc.ExcludeList})
151 return true
Tinoj Josephcf161be2022-07-07 19:47:47 +0530152}
153
vinokuma926cb3e2023-03-29 11:41:06 +0530154// computeExclList computes intersection of previous & current src list
Tinoj Josephcf161be2022-07-07 19:47:47 +0530155func (igc *IgmpGroupChannel) computeExclList(srcList []net.IP) []net.IP {
vinokuma926cb3e2023-03-29 11:41:06 +0530156 updatedSrcList := []net.IP{}
157 for _, src := range srcList {
158 for _, excl := range igc.ExcludeList {
159 if src.Equal(excl) {
160 updatedSrcList = append(updatedSrcList, src)
161 }
162 }
163 }
164 return updatedSrcList
Tinoj Josephcf161be2022-07-07 19:47:47 +0530165}
166
167// IsExclListChanged checks if excl list has been updated
168func (igc *IgmpGroupChannel) IsExclListChanged(srcList []net.IP) bool {
vinokuma926cb3e2023-03-29 11:41:06 +0530169 srcPresent := false
170 if len(igc.ExcludeList) != len(srcList) {
171 return true
172 }
Tinoj Josephcf161be2022-07-07 19:47:47 +0530173
vinokuma926cb3e2023-03-29 11:41:06 +0530174 for _, src := range srcList {
175 for _, excl := range igc.ExcludeList {
176 srcPresent = false
177 if src.Equal(excl) {
178 srcPresent = true
179 break
180 }
181 }
182 if !srcPresent {
183 return true
184 }
185 }
186 return false
Tinoj Josephcf161be2022-07-07 19:47:47 +0530187}
188
189// DelInclSource deletes a source is in include list
190func (igc *IgmpGroupChannel) DelInclSource(src net.IP) {
vinokuma926cb3e2023-03-29 11:41:06 +0530191 mvp := GetApplication().GetMvlanProfileByTag(igc.Mvlan)
192 /* If the SSM proxy is configured, then we can del the src ip from igc as whatever is in proxy that is final list */
193 if _, ok := mvp.Proxy[igc.GroupName]; !ok {
194 logger.Debugw(ctx, "Deleting Include Source for Channel", log.Fields{"Channel": igc.GroupAddr.String(), "Device": igc.Device, "Src": src})
195 for _, igp := range igc.CurReceivers {
196 if igp.InclSourceIsIn(src) {
197 logger.Infow(ctx, "Skipping deletion: Source Present for another Receiver", log.Fields{"Receiver": igp.Port})
198 return
199 }
200 }
201 for _, igp := range igc.NewReceivers {
202 if igp.InclSourceIsIn(src) {
203 logger.Infow(ctx, "Skipping deletion: Source Present for another Receiver", log.Fields{"Receiver": igp.Port})
204 return
205 }
206 }
207 } else {
208 logger.Debug(ctx, "Proxy configured, not Deleting Include Source for Channel")
209 }
210 for i, addr := range igc.IncludeList {
211 if addr.Equal(src) {
212 igc.IncludeList = append(igc.IncludeList[:i], igc.IncludeList[i+1:]...)
213 return
214 }
215 }
Tinoj Josephcf161be2022-07-07 19:47:47 +0530216}
217
218// DelExclSource deletes a source is in exclude list
219func (igc *IgmpGroupChannel) DelExclSource(src net.IP) {
vinokuma926cb3e2023-03-29 11:41:06 +0530220 logger.Debugw(ctx, "Deleting Exclude Source for Channel", log.Fields{"Channel": igc.GroupAddr.String(), "Device": igc.Device, "Src": src})
Tinoj Josephcf161be2022-07-07 19:47:47 +0530221
vinokuma926cb3e2023-03-29 11:41:06 +0530222 for _, igp := range igc.CurReceivers {
223 if igp.ExclSourceIsIn(src) {
224 logger.Infow(ctx, "Skipping deletion: Source Present for another Receiver", log.Fields{"Receiver": igp.Port})
225 return
226 }
227 }
228 for _, igp := range igc.NewReceivers {
229 if igp.ExclSourceIsIn(src) {
230 logger.Infow(ctx, "Skipping deletion: Source Present for another Receiver", log.Fields{"Receiver": igp.Port})
231 return
232 }
233 }
234 for i, addr := range igc.ExcludeList {
235 if addr.Equal(src) {
236 igc.ExcludeList = append(igc.ExcludeList[:i], igc.ExcludeList[i+1:]...)
237 return
238 }
239 }
Tinoj Josephcf161be2022-07-07 19:47:47 +0530240}
241
242// ProcessSources process the received list of either included sources or the excluded sources
243// The return value indicate sif the group is modified and needs to be informed
244// to the upstream multicast servers
Tinoj Joseph07cc5372022-07-18 22:53:51 +0530245func (igc *IgmpGroupChannel) ProcessSources(cntx context.Context, port string, ip []net.IP, incl bool) (bool, bool) {
vinokuma926cb3e2023-03-29 11:41:06 +0530246 groupChanged := false
247 groupExclUpdated := false
248 receiverSrcListEmpty := false
249 // If the version type is 2, there isn't anything to process here
250 if igc.Version == IgmpVersion2 && *igc.ServVersion == IgmpVersion2 {
251 return false, false
252 }
Tinoj Josephcf161be2022-07-07 19:47:47 +0530253
vinokuma926cb3e2023-03-29 11:41:06 +0530254 igp := igc.GetReceiver(port)
255 if igp == nil {
256 logger.Warnw(ctx, "Receiver not found", log.Fields{"Port": port})
257 return false, false
258 }
259 mvp := GetApplication().GetMvlanProfileByTag(igc.Mvlan)
260 if incl {
261 for _, src := range ip {
262 if igp.ExclSourceIsIn(src) {
263 igp.DelExclSource(src)
264 if igc.ExclSourceIsIn(src) {
265 igc.DelExclSource(src)
266 groupChanged = true
267 }
268 }
Tinoj Josephcf161be2022-07-07 19:47:47 +0530269
vinokuma926cb3e2023-03-29 11:41:06 +0530270 // If the source is not in the list of include sources for the port
271 // add it. If so, check also if it is in list of include sources
272 // at the device level.
273 if !igp.InclSourceIsIn(src) {
274 igp.AddInclSource(src)
275 if !igc.InclSourceIsIn(src) {
276 igc.AddInclSource(src)
277 groupChanged = true
278 }
279 }
280 }
281 /* If any of the existing ip in the source list is removed we need to remove from the list in igp and igc */
282 if _, ok := mvp.Proxy[igc.GroupName]; ok {
283 /* If we get leave message from any subscriber, we do not have to delete the entries in the src list
284 Only if there is any modification in the src list by proxy config update only then we need to update */
285 if len(ip) != 0 && len(ip) != len(igc.IncludeList) {
286 for i := len(igc.IncludeList) - 1; i >= 0; i-- {
287 src := igc.IncludeList[i]
288 if !IsIPPresent(src, ip) {
289 igp.DelInclSource(src)
290 igc.DelInclSource(src)
291 groupChanged = true
292 }
293 }
294 }
295 }
296 } else {
297 for _, src := range ip {
298 if igp.InclSourceIsIn(src) {
299 igp.DelInclSource(src)
300 if igc.InclSourceIsIn(src) {
301 igc.DelInclSource(src)
302 groupChanged = true
303 }
304 if len(igp.IncludeList) == 0 {
305 receiverSrcListEmpty = true
306 }
307 }
Tinoj Josephcf161be2022-07-07 19:47:47 +0530308
vinokuma926cb3e2023-03-29 11:41:06 +0530309 // If the source is not in the list of exclude sources for the port
310 // add it. If so, check also if it is in list of include sources
311 // at the device level.
312 if !igp.ExclSourceIsIn(src) {
313 igp.AddExclSource(src)
314 /* If there is any update in the src list of proxy we need to update the igc */
315 if _, ok := mvp.Proxy[igc.GroupName]; ok {
316 if !igc.ExclSourceIsIn(src) {
317 igc.AddExclSource(src)
318 groupChanged = true
319 }
320 }
321 }
322 }
323 /* If any of the existing ip in the source list is removed we need to remove from the list in igp and igc */
324 if _, ok := mvp.Proxy[igc.GroupName]; ok {
325 if len(ip) != len(igc.ExcludeList) {
326 for i := len(igc.ExcludeList) - 1; i >= 0; i-- {
327 src := igc.ExcludeList[i]
328 if !IsIPPresent(src, ip) {
329 igp.DelExclSource(src)
330 igc.DelExclSource(src)
331 groupChanged = true
332 }
333 }
334 }
335 }
336 groupExclUpdated = igc.UpdateExclSource(ip)
337 }
338 if err := igp.WriteToDb(cntx, igc.Mvlan, igc.GroupAddr, igc.Device); err != nil {
339 logger.Errorw(ctx, "Igmp group port Write to DB failed", log.Fields{"mvlan": igc.Mvlan, "GroupAddr": igc.GroupAddr})
340 }
341 return (groupChanged || groupExclUpdated), receiverSrcListEmpty
Tinoj Josephcf161be2022-07-07 19:47:47 +0530342}
343
344// GetReceiver to get receiver info
345func (igc *IgmpGroupChannel) GetReceiver(port string) *IgmpGroupPort {
vinokuma926cb3e2023-03-29 11:41:06 +0530346 igp := igc.NewReceivers[port]
347 if igp == nil {
348 igp = igc.CurReceivers[port]
349 }
350 return igp
Tinoj Josephcf161be2022-07-07 19:47:47 +0530351}
352
353// AddReceiver add the receiver to the device and perform other actions such as adding the group
354// to the physical device, add members, add flows to point the MC packets to the
355// group. Also, send a IGMP report upstream if there is a change in the group
Tinoj Joseph07cc5372022-07-18 22:53:51 +0530356func (igc *IgmpGroupChannel) AddReceiver(cntx context.Context, port string, group *layers.IGMPv3GroupRecord, cvlan uint16, pbit uint8) bool {
vinokuma926cb3e2023-03-29 11:41:06 +0530357 var igp *IgmpGroupPort
358 var groupModified = false
359 var isNewReceiver = false
Tinoj Josephcf161be2022-07-07 19:47:47 +0530360
vinokuma926cb3e2023-03-29 11:41:06 +0530361 var ip []net.IP
362 incl := false
363 mvp := GetApplication().GetMvlanProfileByTag(igc.Mvlan)
364 if _, ok := mvp.Proxy[igc.GroupName]; ok {
365 if mvp.Proxy[igc.GroupName].Mode == common.Include {
366 incl = true
367 }
368 ip = mvp.Proxy[igc.GroupName].SourceList
369 } else if group != nil {
370 incl = isIncl(group.Type)
371 ip = group.SourceAddresses
372 }
373 logger.Debugw(ctx, "Attempting to add receiver", log.Fields{"Version": igc.Version, "Port": port, "Incl": incl, "srcIp": ip})
Tinoj Josephcf161be2022-07-07 19:47:47 +0530374
vinokuma926cb3e2023-03-29 11:41:06 +0530375 //logger.Infow(ctx, "Receivers", log.Fields{"New": igc.NewReceivers, "Current": igc.CurReceivers})
376 logger.Debugw(ctx, "Receiver Group", log.Fields{"Igd GId": igc.GroupID})
377 logger.Debugw(ctx, "Receiver Channel", log.Fields{"Igd addr": igc.GroupAddr})
378 logger.Debugw(ctx, "Receiver Mvlan", log.Fields{"Igd mvlan": igc.Mvlan})
379 logger.Debugw(ctx, "Receiver Sources", log.Fields{"Igd addr": ip})
Tinoj Josephcf161be2022-07-07 19:47:47 +0530380
vinokuma926cb3e2023-03-29 11:41:06 +0530381 ponPortID := GetApplication().GetPonPortID(igc.Device, port)
Tinoj Josephcf161be2022-07-07 19:47:47 +0530382
vinokuma926cb3e2023-03-29 11:41:06 +0530383 // Process the IGMP receiver. If it is already in, we should only process the changes
384 // to source list.
385 var newRcvExists bool
386 igp, newRcvExists = igc.NewReceivers[port]
387 if !newRcvExists {
388 // Add the receiver to the list of receivers and make the necessary group modification
389 // if this is the first time the receiver is added
390 var curRcvExists bool
391 if igp, curRcvExists = igc.CurReceivers[port]; curRcvExists {
392 logger.Debugw(ctx, "Existing IGMP receiver", log.Fields{"Group": igc.GroupAddr.String(), "Port": port})
393 delete(igc.CurReceivers, port)
394 igp.QueryTimeoutCount = 0
395 igc.NewReceivers[port] = igp
396 } else {
397 // New receiver who wasn't part of earlier list
398 // Need to send out IGMP group modification for this port
399 igp = NewIgmpGroupPort(port, cvlan, pbit, igc.Version, incl, uint32(ponPortID))
400 igc.NewReceivers[port] = igp
401 isNewReceiver = true
402 logger.Debugw(ctx, "New IGMP receiver", log.Fields{"Group": igc.GroupAddr.String(), "Port": port})
403 if len(igc.NewReceivers) == 1 && len(igc.CurReceivers) == 0 {
404 groupModified = true
405 igc.AddMcFlow(cntx)
406 logger.Debugw(ctx, "Added New Flow", log.Fields{"Group": igc.GroupAddr.String(), "Port": port})
407 }
408 if !incl {
409 igc.Exclude++
410 }
411 }
412 }
Tinoj Josephcf161be2022-07-07 19:47:47 +0530413
vinokuma926cb3e2023-03-29 11:41:06 +0530414 // Process the include/exclude list which may end up modifying the group
415 if change, _ := igc.ProcessSources(cntx, port, ip, incl); change {
416 groupModified = true
417 }
418 igc.ProcessMode(port, incl)
Tinoj Josephcf161be2022-07-07 19:47:47 +0530419
vinokuma926cb3e2023-03-29 11:41:06 +0530420 // If the group is modified as this is the first receiver or due to include/exclude list modification
421 // send a report to the upstream multicast servers
422 if groupModified {
423 logger.Debug(ctx, "Group Modified and IGMP report sent to the upstream server")
424 igc.SendReport(false)
425 } else if newRcvExists {
426 return false
427 }
Tinoj Josephcf161be2022-07-07 19:47:47 +0530428
vinokuma926cb3e2023-03-29 11:41:06 +0530429 logger.Debugw(ctx, "Channel Receiver Added", log.Fields{"Group Channel": igc.GroupAddr, "Group Port": igp})
Tinoj Josephcf161be2022-07-07 19:47:47 +0530430
vinokuma926cb3e2023-03-29 11:41:06 +0530431 if err := igc.WriteToDb(cntx); err != nil {
432 logger.Errorw(ctx, "Igmp group channel Write to DB failed", log.Fields{"mvlan": igc.Mvlan, "GroupAddr": igc.GroupAddr})
433 }
434 if err := igp.WriteToDb(cntx, igc.Mvlan, igc.GroupAddr, igc.Device); err != nil {
435 logger.Errorw(ctx, "Igmp group port Write to DB failed", log.Fields{"mvlan": igc.Mvlan, "GroupAddr": igc.GroupAddr})
436 }
437 return isNewReceiver
Tinoj Josephcf161be2022-07-07 19:47:47 +0530438}
439
440// DelReceiver is called when Query expiry happened for a receiver. This removes the receiver from the
441// the group
Tinoj Joseph07cc5372022-07-18 22:53:51 +0530442func (igc *IgmpGroupChannel) DelReceiver(cntx context.Context, port string, incl bool, srcList []net.IP) bool {
vinokuma926cb3e2023-03-29 11:41:06 +0530443 // The receiver may exist either in NewReceiver list or
444 // the CurReceivers list. Find and remove it from either
445 // of the lists.
446 logger.Debugw(ctx, "Deleting Receiver from Channel", log.Fields{"Port": port, "SrcList": srcList, "Incl": incl})
447 logger.Debugw(ctx, "New Receivers", log.Fields{"New": igc.NewReceivers})
448 logger.Debugw(ctx, "Current Receivers", log.Fields{"Current": igc.CurReceivers})
Tinoj Josephcf161be2022-07-07 19:47:47 +0530449
vinokuma926cb3e2023-03-29 11:41:06 +0530450 receiversUpdated := false
451 groupModified, receiverSrcListEmpty := igc.ProcessSources(cntx, port, srcList, incl)
Tinoj Josephcf161be2022-07-07 19:47:47 +0530452
vinokuma926cb3e2023-03-29 11:41:06 +0530453 if len(srcList) == 0 || len(igc.IncludeList) == 0 || receiverSrcListEmpty {
454 if igp, ok := igc.NewReceivers[port]; ok {
455 logger.Debug(ctx, "Deleting from NewReceivers")
456 delete(igc.NewReceivers, port)
457 receiversUpdated = true
458 if igp.Exclude {
459 igc.Exclude--
460 }
461 } else {
462 if igp, ok1 := igc.CurReceivers[port]; ok1 {
463 logger.Debug(ctx, "Deleting from CurReceivers")
464 delete(igc.CurReceivers, port)
465 receiversUpdated = true
466 if igp.Exclude {
467 igc.Exclude--
468 }
469 } else {
470 logger.Debug(ctx, "Receiver doesnot exist. Dropping Igmp leave")
471 return false
472 }
473 }
474 _ = db.DelIgmpRcvr(cntx, igc.Mvlan, igc.GroupAddr, igc.Device, port)
475 }
Tinoj Josephcf161be2022-07-07 19:47:47 +0530476
vinokuma926cb3e2023-03-29 11:41:06 +0530477 if igc.NumReceivers() == 0 {
478 igc.DelMcFlow(cntx)
479 mvp := GetApplication().GetMvlanProfileByTag(igc.Mvlan)
480 /* If proxy is configured and NumReceivers is 0, then we can reset the igc src list so that we send leave */
481 if _, ok := mvp.Proxy[igc.GroupName]; ok {
482 igc.IncludeList = []net.IP{}
483 }
484 igc.SendLeaveToServer()
485 logger.Debugw(ctx, "Deleted the receiver Flow", log.Fields{"Num Receivers": igc.NumReceivers()})
486 return true
487 }
488 if groupModified {
489 igc.SendReport(false)
490 logger.Infow(ctx, "Updated SourceList for Channel", log.Fields{"Current": igc.CurReceivers, "New": igc.NewReceivers})
491 }
492 if err := igc.WriteToDb(cntx); err != nil {
493 logger.Errorw(ctx, "Igmp group channel Write to DB failed", log.Fields{"mvlan": igc.Mvlan, "GroupAddr": igc.GroupAddr})
494 }
495 logger.Infow(ctx, "Updated Receiver info for Channel", log.Fields{"Current": igc.CurReceivers, "New": igc.NewReceivers})
Tinoj Josephcf161be2022-07-07 19:47:47 +0530496
vinokuma926cb3e2023-03-29 11:41:06 +0530497 return receiversUpdated
Tinoj Josephcf161be2022-07-07 19:47:47 +0530498}
499
500// DelAllReceivers deletes all receiver for the provided igmp device
Tinoj Joseph07cc5372022-07-18 22:53:51 +0530501func (igc *IgmpGroupChannel) DelAllReceivers(cntx context.Context) {
vinokuma926cb3e2023-03-29 11:41:06 +0530502 logger.Infow(ctx, "Deleting All Receiver for Channel", log.Fields{"Device": igc.Device, "Channel": igc.GroupAddr.String()})
503 _ = db.DelAllIgmpRcvr(cntx, igc.Mvlan, igc.GroupAddr, igc.Device)
504 igc.Exclude = 0
505 igc.DelMcFlow(cntx)
506 igc.SendLeaveToServer()
507 logger.Infow(ctx, "MC Flow deleted and Leave sent", log.Fields{"Channel": igc.GroupAddr.String(), "Device": igc.Device})
Tinoj Josephcf161be2022-07-07 19:47:47 +0530508}
509
510// Igmpv2ReportPacket build an IGMPv2 Report for the upstream servers
511func (igc *IgmpGroupChannel) Igmpv2ReportPacket() ([]byte, error) {
vinokuma926cb3e2023-03-29 11:41:06 +0530512 logger.Debugw(ctx, "Building IGMP version 2 Report", log.Fields{"Device": igc.Device})
513 return IgmpReportv2Packet(igc.GroupAddr, igc.Mvlan, (*igc.proxyCfg).IgmpCos, **igc.IgmpProxyIP)
Tinoj Josephcf161be2022-07-07 19:47:47 +0530514}
515
516// Igmpv3ReportPacket build an IGMPv3 Report for the upstream servers
517func (igc *IgmpGroupChannel) Igmpv3ReportPacket() ([]byte, error) {
vinokuma926cb3e2023-03-29 11:41:06 +0530518 logger.Debugw(ctx, "Building IGMP version 3 Report", log.Fields{"Device": igc.Device, "Exclude": igc.Exclude})
519 if igc.Exclude > 0 {
520 return Igmpv3ReportPacket(igc.GroupAddr, igc.Mvlan, (*igc.proxyCfg).IgmpCos, **igc.IgmpProxyIP, false, igc.ExcludeList)
521 }
522 return Igmpv3ReportPacket(igc.GroupAddr, igc.Mvlan, (*igc.proxyCfg).IgmpCos, **igc.IgmpProxyIP, true, igc.IncludeList)
Tinoj Josephcf161be2022-07-07 19:47:47 +0530523}
524
525// SendReport send a consolidated report to the server
526func (igc *IgmpGroupChannel) SendReport(isQuery bool) {
vinokuma926cb3e2023-03-29 11:41:06 +0530527 var report []byte
528 var err error
529 logger.Debugw(ctx, "Checking Version", log.Fields{"IGC Version": igc.Version, "Proxy Version": (*igc.proxyCfg).IgmpVerToServer,
530 "Result": (getVersion((*igc.proxyCfg).IgmpVerToServer) == IgmpVersion2)})
Tinoj Josephcf161be2022-07-07 19:47:47 +0530531
vinokuma926cb3e2023-03-29 11:41:06 +0530532 /**
533 +------------------------------------------------------------------------+
534 | IGMP version(towards BNG) Configured at VGC |
535 +-------------------------------+----------------------------------------+
536 | v2 | v3 |
537 +===================+==========+===============================+========================================+
538 | Received From RG | V2 Join | Process and Send as V2 to BNG | Process, Convert to v3 and Send to BNG |
539 | | | | Process, Send as v2, if the BNG is v2 |
540 +===================+----------+-------------------------------+----------------------------------------+
541 | V3 Join | Process and Send as V2 to BNG | Process, Send v3 to BNG |
542 | | | Process, Convert, Send as v2, if the |
543 | | | BNG is v2 |
544 +===================+==========+===============================+========================================+
545 | Received From BNG | V2 Query | V2 response to BNG | V2 response to BNG |
546 +===================+----------+-------------------------------+----------------------------------------+
547 | V3 Query | Discard | V3 response to BNG |
548 +==========+===============================+========================================+
549 */
550 // igc.Version: igmp version received from RG.
551 // igc.ServVersion: igmp version received from BNG or IgmpVerToServer present in proxy igmp conf.
Tinoj Josephcf161be2022-07-07 19:47:47 +0530552
vinokuma926cb3e2023-03-29 11:41:06 +0530553 if isQuery && *igc.ServVersion == IgmpVersion3 && getVersion((*igc.proxyCfg).IgmpVerToServer) == IgmpVersion2 {
554 // This is the last scenario where we must discard the query processing.
555 logger.Debug(ctx, "Dropping query packet since the server verion is v3 but igmp proxy version is v2")
556 return
557 }
Tinoj Josephcf161be2022-07-07 19:47:47 +0530558
vinokuma926cb3e2023-03-29 11:41:06 +0530559 if *igc.ServVersion == IgmpVersion2 || getVersion((*igc.proxyCfg).IgmpVerToServer) == IgmpVersion2 {
560 report, err = igc.Igmpv2ReportPacket()
561 } else {
562 report, err = igc.Igmpv3ReportPacket()
563 }
564 if err != nil {
565 logger.Warnw(ctx, "Error Preparing Report", log.Fields{"Device": igc.Device, "Ver": igc.Version, "Reason": err.Error()})
566 return
567 }
568 nni, err := GetApplication().GetNniPort(igc.Device)
569 if err == nil {
570 _ = cntlr.GetController().PacketOutReq(igc.Device, nni, nni, report, false)
571 } else {
572 logger.Warnw(ctx, "Didn't find NNI port", log.Fields{"Device": igc.Device})
573 }
Tinoj Josephcf161be2022-07-07 19:47:47 +0530574}
575
576// AddMcFlow adds flow to the device when the first receiver joins
Tinoj Joseph07cc5372022-07-18 22:53:51 +0530577func (igc *IgmpGroupChannel) AddMcFlow(cntx context.Context) {
vinokuma926cb3e2023-03-29 11:41:06 +0530578 flow, err := igc.BuildMcFlow()
579 if err != nil {
580 logger.Warnw(ctx, "MC Flow Build Failed", log.Fields{"Reason": err.Error()})
581 return
582 }
583 port, _ := GetApplication().GetNniPort(igc.Device)
584 _ = cntlr.GetController().AddFlows(cntx, port, igc.Device, flow)
Tinoj Josephcf161be2022-07-07 19:47:47 +0530585}
586
587// DelMcFlow deletes flow from the device when the last receiver leaves
Tinoj Joseph07cc5372022-07-18 22:53:51 +0530588func (igc *IgmpGroupChannel) DelMcFlow(cntx context.Context) {
vinokuma926cb3e2023-03-29 11:41:06 +0530589 flow, err := igc.BuildMcFlow()
590 if err != nil {
591 logger.Warnw(ctx, "MC Flow Build Failed", log.Fields{"Reason": err.Error()})
592 return
593 }
594 flow.ForceAction = true
595 device := GetApplication().GetDevice(igc.Device)
Tinoj Josephcf161be2022-07-07 19:47:47 +0530596
vinokuma926cb3e2023-03-29 11:41:06 +0530597 if mvpIntf, _ := GetApplication().MvlanProfilesByTag.Load(igc.Mvlan); mvpIntf != nil {
598 mvp := mvpIntf.(*MvlanProfile)
599 err := mvp.DelFlows(cntx, device, flow)
600 if err != nil {
601 logger.Warnw(ctx, "Delering IGMP Flow for device failed ", log.Fields{"Device": device, "err": err})
602 }
603 }
Tinoj Josephcf161be2022-07-07 19:47:47 +0530604}
605
606// BuildMcFlow builds the flow using which it is added/deleted
607func (igc *IgmpGroupChannel) BuildMcFlow() (*of.VoltFlow, error) {
vinokuma926cb3e2023-03-29 11:41:06 +0530608 flow := &of.VoltFlow{}
609 flow.SubFlows = make(map[uint64]*of.VoltSubFlow)
610 //va := GetApplication()
611 logger.Infow(ctx, "Building Mcast flow", log.Fields{"Mcast Group": igc.GroupAddr.String(), "Mvlan": igc.Mvlan.String()})
612 uintGroupAddr := ipv4ToUint(igc.GroupAddr)
613 subFlow := of.NewVoltSubFlow()
614 subFlow.SetMatchVlan(igc.Mvlan)
615 subFlow.SetIpv4Match()
616 subFlow.SetMatchDstIpv4(igc.GroupAddr)
617 mvp := GetApplication().GetMvlanProfileByTag(igc.Mvlan)
618 //nni, err := va.GetNniPort(igc.Device)
619 //if err != nil {
620 // return nil, err
621 //}
622 //inport, err := va.GetPortID(nni)
623 //if err != nil {
624 // return nil, err
625 //}
626 //subFlow.SetInPort(inport)
627 subFlow.SetOutGroup(igc.GroupID)
628 cookiePort := uintGroupAddr
629 subFlow.Cookie = uint64(cookiePort)<<32 | uint64(igc.Mvlan)
630 subFlow.Priority = of.McFlowPriority
631 metadata := uint64(mvp.PonVlan)
632 subFlow.SetTableMetadata(metadata)
Tinoj Josephcf161be2022-07-07 19:47:47 +0530633
vinokuma926cb3e2023-03-29 11:41:06 +0530634 flow.SubFlows[subFlow.Cookie] = subFlow
635 logger.Infow(ctx, "Built Mcast flow", log.Fields{"cookie": subFlow.Cookie, "subflow": subFlow})
636 return flow, nil
Tinoj Josephcf161be2022-07-07 19:47:47 +0530637}
638
639// IgmpLeaveToServer sends IGMP leave to server. Called when the last receiver leaves the group
640func (igc *IgmpGroupChannel) IgmpLeaveToServer() {
vinokuma926cb3e2023-03-29 11:41:06 +0530641 if leave, err := IgmpLeavePacket(igc.GroupAddr, igc.Mvlan, (*igc.proxyCfg).IgmpCos, **igc.IgmpProxyIP); err == nil {
642 nni, err1 := GetApplication().GetNniPort(igc.Device)
643 if err1 == nil {
644 _ = cntlr.GetController().PacketOutReq(igc.Device, nni, nni, leave, false)
645 }
646 }
Tinoj Josephcf161be2022-07-07 19:47:47 +0530647}
648
649// SendLeaveToServer delete the group when the last receiver leaves the group
650func (igc *IgmpGroupChannel) SendLeaveToServer() {
vinokuma926cb3e2023-03-29 11:41:06 +0530651 /**
652 +-------------------------------------------------------------------------+
653 | IGMP version(towards BNG) Configured at VGC |
654 +-------------------------------+-----------------------------------------+
655 | v2 | v3 |
656 +===================+==========+===============================+=========================================+
657 | Received From RG | V2 Leave | Process and Send as V2 to BNG | Process, Convert to V3 and Send to BNG/ |
658 | | | | Process, Send as V2, if the BNG is V2 |
659 +===================+----------+-------------------------------+-----------------------------------------+
660 | V3 Leave | Process and Send as V2 to BNG | Process, Send V3 to BNG |
661 | | | Process, Convert, Send as V2, if the |
662 | | | BNG is v2 |
663 +==========+===============================+=========================================+
664 */
665 // igc.Version: igmp version received from RG.
666 // igc.ServVersion: igmp version received from BNG or IgmpVerToServer present in proxy igmp conf.
Tinoj Josephcf161be2022-07-07 19:47:47 +0530667
vinokuma926cb3e2023-03-29 11:41:06 +0530668 logger.Debugw(ctx, "Sending IGMP leave upstream", log.Fields{"Device": igc.Device})
669 if *igc.ServVersion == IgmpVersion2 || getVersion((*igc.proxyCfg).IgmpVerToServer) == IgmpVersion2 {
670 igc.IgmpLeaveToServer()
671 } else {
672 igc.SendReport(false)
673 }
Tinoj Josephcf161be2022-07-07 19:47:47 +0530674}
675
676// NumReceivers returns total number of receivers left on the group
677func (igc *IgmpGroupChannel) NumReceivers() uint32 {
vinokuma926cb3e2023-03-29 11:41:06 +0530678 return uint32(len(igc.CurReceivers) + len(igc.NewReceivers))
Tinoj Josephcf161be2022-07-07 19:47:47 +0530679}
680
681// SendQuery sends query to the receivers for counting purpose
682func (igc *IgmpGroupChannel) SendQuery() {
vinokuma926cb3e2023-03-29 11:41:06 +0530683 //var b []byte
684 //var err error
685 for portKey, port := range igc.NewReceivers {
686 igc.CurReceivers[portKey] = port
687 }
Tinoj Josephcf161be2022-07-07 19:47:47 +0530688
vinokuma926cb3e2023-03-29 11:41:06 +0530689 igc.NewReceivers = make(map[string]*IgmpGroupPort)
Tinoj Josephcf161be2022-07-07 19:47:47 +0530690
vinokuma926cb3e2023-03-29 11:41:06 +0530691 logger.Debugw(ctx, "Sending Query to receivers", log.Fields{"Receivers": igc.CurReceivers})
692 for port, groupPort := range igc.CurReceivers {
693 if port == StaticPort {
694 continue
695 }
696 if queryPkt, err := igc.buildQuery(igc.GroupAddr, of.VlanType(groupPort.CVlan), groupPort.Pbit); err == nil {
697 _ = cntlr.GetController().PacketOutReq(igc.Device, port, port, queryPkt, false)
698 logger.Debugw(ctx, "Query Sent", log.Fields{"Device": igc.Device, "Port": port, "Packet": queryPkt})
699 } else {
700 logger.Warnw(ctx, "Query Creation Failed", log.Fields{"Reason": err.Error()})
701 }
702 }
Tinoj Josephcf161be2022-07-07 19:47:47 +0530703}
704
705// buildQuery to build query packet
706func (igc *IgmpGroupChannel) buildQuery(groupAddr net.IP, cVlan of.VlanType, pbit uint8) ([]byte, error) {
vinokuma926cb3e2023-03-29 11:41:06 +0530707 if igc.Version == IgmpVersion2 {
708 return Igmpv2QueryPacket(igc.GroupAddr, cVlan, **igc.IgmpProxyIP, pbit, (*igc.proxyCfg).MaxResp)
709 }
710 return Igmpv3QueryPacket(igc.GroupAddr, cVlan, **igc.IgmpProxyIP, pbit, (*igc.proxyCfg).MaxResp)
Tinoj Josephcf161be2022-07-07 19:47:47 +0530711}
712
713// ProcessMode process the received mode and updated the igp
714func (igc *IgmpGroupChannel) ProcessMode(port string, incl bool) {
vinokuma926cb3e2023-03-29 11:41:06 +0530715 /* Update the mode in igp if the mode has changed */
716 igp := igc.GetReceiver(port)
717 if igp.Exclude && incl {
718 igp.Exclude = !incl
719 if igc.Exclude > 0 {
720 igc.Exclude--
721 }
722 } else if !incl && !igp.Exclude {
723 igp.Exclude = !incl
724 igc.Exclude++
725 }
Tinoj Josephcf161be2022-07-07 19:47:47 +0530726}