blob: ed39d235eca0797a7921e2c932ee7d9739dce105 [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 "net"
21
22 "github.com/google/gopacket/layers"
23
24 cntlr "voltha-go-controller/internal/pkg/controller"
25 "voltha-go-controller/internal/pkg/types"
26 "voltha-go-controller/internal/pkg/of"
27 "voltha-go-controller/log"
28)
29
30// IgmpGroupChannel structure
31type IgmpGroupChannel struct {
32 Device string
33 GroupID uint32
34 GroupName string
35 GroupAddr net.IP
36 Mvlan of.VlanType
37 Exclude int
38 ExcludeList []net.IP
39 IncludeList []net.IP
40 Version uint8
41 ServVersion *uint8 `json:"-"`
42 CurReceivers map[string]*IgmpGroupPort `json:"-"`
43 NewReceivers map[string]*IgmpGroupPort `json:"-"`
44 proxyCfg **IgmpProfile
45 IgmpProxyIP **net.IP `json:"-"`
46}
47
48// NewIgmpGroupChannel is constructor for a channel. The default IGMP version is set to 3
49// as the protocol defines the way to manage backward compatibility
50// The implementation handles simultaneous presense of lower versioned
51// receivers
52func NewIgmpGroupChannel(igd *IgmpGroupDevice, groupAddr net.IP, version uint8) *IgmpGroupChannel {
53 var igc IgmpGroupChannel
54 igc.Device = igd.Device
55 igc.GroupID = igd.GroupID
56 igc.GroupName = igd.GroupName
57 igc.GroupAddr = groupAddr
58 igc.Mvlan = igd.Mvlan
59 igc.Version = version
60 igc.CurReceivers = make(map[string]*IgmpGroupPort)
61 igc.NewReceivers = make(map[string]*IgmpGroupPort)
62 igc.proxyCfg = &igd.proxyCfg
63 igc.IgmpProxyIP = &igd.IgmpProxyIP
64 igc.ServVersion = igd.ServVersion
65 return &igc
66}
67
68// NewIgmpGroupChannelFromBytes create the IGMP group channel from a byte slice
69func NewIgmpGroupChannelFromBytes(b []byte) (*IgmpGroupChannel, error) {
70 var igc IgmpGroupChannel
71 if err := json.Unmarshal(b, &igc); err != nil {
72 return nil, err
73 }
74 igc.CurReceivers = make(map[string]*IgmpGroupPort)
75 igc.NewReceivers = make(map[string]*IgmpGroupPort)
76 return &igc, nil
77}
78
79// RestorePorts to restore ports
80func (igc *IgmpGroupChannel) RestorePorts() {
81
82 igc.migrateIgmpPorts()
83 ports, _ := db.GetIgmpRcvrs(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(); err != nil {
98 logger.Errorw(ctx, "Igmp group channel Write to DB failed", log.Fields{"mvlan": igc.Mvlan, "GroupAddr": igc.GroupAddr})
99 }
100}
101
102// WriteToDb is utility to write IGMPGroupChannel Info to database
103func (igc *IgmpGroupChannel) WriteToDb() error {
104 b, err := json.Marshal(igc)
105 if err != nil {
106 return err
107 }
108 if err1 := db.PutIgmpChannel(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
113}
114
115
116// InclSourceIsIn checks if a source is in include list
117func (igc *IgmpGroupChannel) InclSourceIsIn(src net.IP) bool {
118 return IsIPPresent(src, igc.IncludeList)
119}
120
121// ExclSourceIsIn checks if a source is in exclude list
122func (igc *IgmpGroupChannel) ExclSourceIsIn(src net.IP) bool {
123 return IsIPPresent(src, igc.ExcludeList)
124}
125
126// AddInclSource adds a source is in include list
127func (igc *IgmpGroupChannel) AddInclSource(src net.IP) {
128 logger.Debugw(ctx, "Adding Include Source for Channel", log.Fields{"Channel": igc.GroupAddr.String(), "Device": igc.Device, "Src": src})
129 igc.IncludeList = append(igc.IncludeList, src)
130}
131
132// AddExclSource adds a source is in exclude list
133func (igc *IgmpGroupChannel) AddExclSource(src net.IP) {
134 logger.Debugw(ctx, "Adding Exclude Source for Channel", log.Fields{"Channel": igc.GroupAddr.String(), "Device": igc.Device, "Src": src})
135 igc.ExcludeList = append(igc.ExcludeList, src)
136}
137
138// UpdateExclSource update excl source list for the given channel
139func (igc *IgmpGroupChannel) UpdateExclSource(srcList []net.IP) bool {
140
141 logger.Debugw(ctx, "Updating Exclude Source for Channel", log.Fields{"Channel": igc.GroupAddr.String(), "Device": igc.Device, "Current List": igc.ExcludeList, "Incoming List": srcList})
142 if !igc.IsExclListChanged(srcList) {
143 return false
144 }
145
146 if igc.NumReceivers() == 1 {
147 igc.ExcludeList = srcList
148 } else {
149 igc.ExcludeList = igc.computeExclList(srcList)
150 }
151
152 logger.Debugw(ctx, "Updated Exclude Source for Channel", log.Fields{"Channel": igc.GroupAddr.String(), "Device": igc.Device, "Updated Excl List": igc.ExcludeList})
153 return true
154}
155
156// computeExclList computes intersection of pervious & current src list
157func (igc *IgmpGroupChannel) computeExclList(srcList []net.IP) []net.IP {
158
159 updatedSrcList := []net.IP{}
160 for _, src := range srcList {
161 for _, excl := range igc.ExcludeList {
162 if src.Equal(excl) {
163 updatedSrcList = append(updatedSrcList, src)
164 }
165 }
166 }
167 return updatedSrcList
168}
169
170// IsExclListChanged checks if excl list has been updated
171func (igc *IgmpGroupChannel) IsExclListChanged(srcList []net.IP) bool {
172
173 srcPresent := false
174 if len(igc.ExcludeList) != len(srcList) {
175 return true
176 }
177
178 for _, src := range srcList {
179 for _, excl := range igc.ExcludeList {
180 srcPresent = false
181 if src.Equal(excl) {
182 srcPresent = true
183 break
184 }
185 }
186 if !srcPresent {
187 return true
188 }
189 }
190 return false
191}
192
193// DelInclSource deletes a source is in include list
194func (igc *IgmpGroupChannel) DelInclSource(src net.IP) {
195 mvp := GetApplication().GetMvlanProfileByTag(igc.Mvlan)
196 /* If the SSM proxy is configured, then we can del the src ip from igc as whatever is in proxy that is final list */
197 if _, ok := mvp.Proxy[igc.GroupName]; !ok {
198 logger.Debugw(ctx, "Deleting Include Source for Channel", log.Fields{"Channel": igc.GroupAddr.String(), "Device": igc.Device, "Src": src})
199 for _, igp := range igc.CurReceivers {
200 if igp.InclSourceIsIn(src) {
201 logger.Infow(ctx, "Skipping deletion: Source Present for another Receiver", log.Fields{"Receiver": igp.Port})
202 return
203 }
204 }
205 for _, igp := range igc.NewReceivers {
206 if igp.InclSourceIsIn(src) {
207 logger.Infow(ctx, "Skipping deletion: Source Present for another Receiver", log.Fields{"Receiver": igp.Port})
208 return
209 }
210 }
211 } else {
212 logger.Debug(ctx, "Proxy configured, not Deleting Include Source for Channel")
213 }
214 for i, addr := range igc.IncludeList {
215 if addr.Equal(src) {
216 igc.IncludeList = append(igc.IncludeList[:i], igc.IncludeList[i+1:]...)
217 return
218 }
219 }
220}
221
222// DelExclSource deletes a source is in exclude list
223func (igc *IgmpGroupChannel) DelExclSource(src net.IP) {
224 logger.Debugw(ctx, "Deleting Exclude Source for Channel", log.Fields{"Channel": igc.GroupAddr.String(), "Device": igc.Device, "Src": src})
225
226 for _, igp := range igc.CurReceivers {
227 if igp.ExclSourceIsIn(src) {
228 logger.Infow(ctx, "Skipping deletion: Source Present for another Receiver", log.Fields{"Receiver": igp.Port})
229 return
230 }
231 }
232 for _, igp := range igc.NewReceivers {
233 if igp.ExclSourceIsIn(src) {
234 logger.Infow(ctx, "Skipping deletion: Source Present for another Receiver", log.Fields{"Receiver": igp.Port})
235 return
236 }
237 }
238 for i, addr := range igc.ExcludeList {
239 if addr.Equal(src) {
240 igc.ExcludeList = append(igc.ExcludeList[:i], igc.ExcludeList[i+1:]...)
241 return
242 }
243 }
244}
245
246// ProcessSources process the received list of either included sources or the excluded sources
247// The return value indicate sif the group is modified and needs to be informed
248// to the upstream multicast servers
249func (igc *IgmpGroupChannel) ProcessSources(port string, ip []net.IP, incl bool) (bool, bool) {
250 groupChanged := false
251 groupExclUpdated := false
252 receiverSrcListEmpty := false
253 // If the version type is 2, there isn't anything to process here
254 if igc.Version == IgmpVersion2 && *igc.ServVersion == IgmpVersion2 {
255 return false, false
256 }
257
258 igp := igc.GetReceiver(port)
259 if igp == nil {
260 logger.Warnw(ctx, "Receiver not found", log.Fields{"Port": port})
261 return false, false
262 }
263 mvp := GetApplication().GetMvlanProfileByTag(igc.Mvlan)
264 if incl {
265 for _, src := range ip {
266
267 if igp.ExclSourceIsIn(src) {
268 igp.DelExclSource(src)
269 if igc.ExclSourceIsIn(src) {
270 igc.DelExclSource(src)
271 groupChanged = true
272 }
273 }
274
275 // If the source is not in the list of include sources for the port
276 // add it. If so, check also if it is in list of include sources
277 // at the device level.
278 if !igp.InclSourceIsIn(src) {
279 igp.AddInclSource(src)
280 if !igc.InclSourceIsIn(src) {
281 igc.AddInclSource(src)
282 groupChanged = true
283 }
284 }
285 }
286 /* If any of the existing ip in the source list is removed we need to remove from the list in igp and igc */
287 if _, ok := mvp.Proxy[igc.GroupName]; ok {
288 /* If we get leave message from any subscriber, we do not have to delete the entries in the src list
289 Only if ther is any modification in the src list by proxy config update only then we need to update */
290 if len(ip) != 0 && len(ip) != len(igc.IncludeList) {
291 for i := len(igc.IncludeList) - 1; i >= 0; i-- {
292 src := igc.IncludeList[i]
293 if !IsIPPresent(src, ip) {
294 igp.DelInclSource(src)
295 igc.DelInclSource(src)
296 groupChanged = true
297 }
298 }
299 }
300 }
301 } else {
302 for _, src := range ip {
303
304 if igp.InclSourceIsIn(src) {
305 igp.DelInclSource(src)
306 if igc.InclSourceIsIn(src) {
307 igc.DelInclSource(src)
308 groupChanged = true
309 }
310 if len(igp.IncludeList) == 0 {
311 receiverSrcListEmpty = true
312 }
313 }
314
315 // If the source is not in the list of exclude sources for the port
316 // add it. If so, check also if it is in list of include sources
317 // at the device level.
318 if !igp.ExclSourceIsIn(src) {
319 igp.AddExclSource(src)
320 /* If there is any update in the src list of proxy we need to update the igc */
321 if _, ok := mvp.Proxy[igc.GroupName]; ok {
322 if !igc.ExclSourceIsIn(src) {
323 igc.AddExclSource(src)
324 groupChanged = true
325 }
326 }
327 }
328 }
329 /* If any of the existing ip in the source list is removed we need to remove from the list in igp and igc */
330 if _, ok := mvp.Proxy[igc.GroupName]; ok {
331 if len(ip) != len(igc.ExcludeList) {
332 for i := len(igc.ExcludeList) - 1; i >= 0; i-- {
333 src := igc.ExcludeList[i]
334 if !IsIPPresent(src, ip) {
335 igp.DelExclSource(src)
336 igc.DelExclSource(src)
337 groupChanged = true
338 }
339 }
340 }
341 }
342 groupExclUpdated = igc.UpdateExclSource(ip)
343 }
344 if err := igp.WriteToDb(igc.Mvlan, igc.GroupAddr, igc.Device); err != nil {
345 logger.Errorw(ctx, "Igmp group port Write to DB failed", log.Fields{"mvlan": igc.Mvlan, "GroupAddr": igc.GroupAddr})
346 }
347 return (groupChanged || groupExclUpdated), receiverSrcListEmpty
348}
349
350// GetReceiver to get receiver info
351func (igc *IgmpGroupChannel) GetReceiver(port string) *IgmpGroupPort {
352 igp := igc.NewReceivers[port]
353 if igp == nil {
354 igp = igc.CurReceivers[port]
355 }
356 return igp
357}
358
359// AddReceiver add the receiver to the device and perform other actions such as adding the group
360// to the physical device, add members, add flows to point the MC packets to the
361// group. Also, send a IGMP report upstream if there is a change in the group
362func (igc *IgmpGroupChannel) AddReceiver(port string, group *layers.IGMPv3GroupRecord, cvlan uint16, pbit uint8) bool {
363
364 var igp *IgmpGroupPort
365 var groupModified = false
366 var isNewReceiver = false
367
368 var ip []net.IP
369 incl := false
370 mvp := GetApplication().GetMvlanProfileByTag(igc.Mvlan)
371 if _, ok := mvp.Proxy[igc.GroupName]; ok {
372 if mvp.Proxy[igc.GroupName].Mode == common.Include {
373 incl = true
374 }
375 ip = mvp.Proxy[igc.GroupName].SourceList
376 } else if group != nil {
377 incl = isIncl(group.Type)
378 ip = group.SourceAddresses
379 }
380 logger.Debugw(ctx, "Attempting to add receiver", log.Fields{"Version": igc.Version, "Port": port, "Incl": incl, "srcIp": ip})
381
382 //logger.Infow(ctx, "Receivers", log.Fields{"New": igc.NewReceivers, "Current": igc.CurReceivers})
383 logger.Debugw(ctx, "Receiver Group", log.Fields{"Igd GId": igc.GroupID})
384 logger.Debugw(ctx, "Receiver Channel", log.Fields{"Igd addr": igc.GroupAddr})
385 logger.Debugw(ctx, "Receiver Mvlan", log.Fields{"Igd mvlan": igc.Mvlan})
386 logger.Debugw(ctx, "Receiver Sources", log.Fields{"Igd addr": ip})
387
388 ponPortID := GetApplication().GetPonPortID(igc.Device, port)
389
390 // Process the IGMP receiver. If it is already in, we should only process the changes
391 // to source list.
392 var newRcvExists bool
393 igp, newRcvExists = igc.NewReceivers[port]
394 if !newRcvExists {
395 // Add the receiver to the list of receivers and make the necessary group modification
396 // if this is the first time the receiver is added
397 var curRcvExists bool
398 if igp, curRcvExists = igc.CurReceivers[port]; curRcvExists {
399 logger.Debugw(ctx, "Existing IGMP receiver", log.Fields{"Group": igc.GroupAddr.String(), "Port": port})
400 delete(igc.CurReceivers, port)
401 igp.QueryTimeoutCount = 0
402 igc.NewReceivers[port] = igp
403 } else {
404 // New receiver who wasn't part of earlier list
405 // Need to send out IGMP group modification for this port
406 igp = NewIgmpGroupPort(port, cvlan, pbit, igc.Version, incl, uint32(ponPortID))
407 igc.NewReceivers[port] = igp
408 isNewReceiver = true
409 logger.Debugw(ctx, "New IGMP receiver", log.Fields{"Group": igc.GroupAddr.String(), "Port": port})
410 if len(igc.NewReceivers) == 1 && len(igc.CurReceivers) == 0 {
411 groupModified = true
412 igc.AddMcFlow()
413 logger.Debugw(ctx, "Added New Flow", log.Fields{"Group": igc.GroupAddr.String(), "Port": port})
414 }
415 if !incl {
416 igc.Exclude++
417 }
418 }
419 }
420
421 // Process the include/exclude list which may end up modifying the group
422 if change, _ := igc.ProcessSources(port, ip, incl); change {
423 groupModified = true
424 }
425 igc.ProcessMode(port, incl)
426
427 // If the group is modified as this is the first receiver or due to include/exclude list modification
428 // send a report to the upstream multicast servers
429 if groupModified {
430 logger.Debug(ctx, "Group Modified and IGMP report sent to the upstream server")
431 igc.SendReport(false)
432 } else if newRcvExists {
433 return false
434 }
435
436 logger.Debugw(ctx, "Channel Receiver Added", log.Fields{"Group Channel": igc.GroupAddr, "Group Port": igp})
437
438 if err := igc.WriteToDb(); err != nil {
439 logger.Errorw(ctx, "Igmp group channel Write to DB failed", log.Fields{"mvlan": igc.Mvlan, "GroupAddr": igc.GroupAddr})
440 }
441 if err := igp.WriteToDb(igc.Mvlan, igc.GroupAddr, igc.Device); err != nil {
442 logger.Errorw(ctx, "Igmp group port Write to DB failed", log.Fields{"mvlan": igc.Mvlan, "GroupAddr": igc.GroupAddr})
443 }
444 return isNewReceiver
445}
446
447// DelReceiver is called when Query expiry happened for a receiver. This removes the receiver from the
448// the group
449func (igc *IgmpGroupChannel) DelReceiver(port string, incl bool, srcList []net.IP) bool {
450 // The receiver may exist either in NewReceiver list or
451 // the CurReceivers list. Find and remove it from either
452 // of the lists.
453 logger.Debugw(ctx, "Deleting Receiver from Channel", log.Fields{"Port": port, "SrcList": srcList, "Incl": incl})
454 logger.Debugw(ctx, "New Receivers", log.Fields{"New": igc.NewReceivers})
455 logger.Debugw(ctx, "Current Receivers", log.Fields{"Current": igc.CurReceivers})
456
457 receiversUpdated := false
458 groupModified, receiverSrcListEmpty := igc.ProcessSources(port, srcList, incl)
459
460 if len(srcList) == 0 || len(igc.IncludeList) == 0 || receiverSrcListEmpty {
461 if igp, ok := igc.NewReceivers[port]; ok {
462 logger.Debug(ctx, "Deleting from NewReceivers")
463 delete(igc.NewReceivers, port)
464 receiversUpdated = true
465 if igp.Exclude {
466 igc.Exclude--
467 }
468 } else {
469 if igp, ok1 := igc.CurReceivers[port]; ok1 {
470 logger.Debug(ctx, "Deleting from CurReceivers")
471 delete(igc.CurReceivers, port)
472 receiversUpdated = true
473 if igp.Exclude {
474 igc.Exclude--
475 }
476 } else {
477 logger.Debug(ctx, "Receiver doesnot exist. Dropping Igmp leave")
478 return false
479 }
480 }
481 _ = db.DelIgmpRcvr(igc.Mvlan, igc.GroupAddr, igc.Device, port)
482 }
483
484 if igc.NumReceivers() == 0 {
485 igc.DelMcFlow()
486 mvp := GetApplication().GetMvlanProfileByTag(igc.Mvlan)
487 /* If proxy is configured and NumReceivers is 0, then we can reset the igc src list so that we send leave */
488 if _, ok := mvp.Proxy[igc.GroupName]; ok {
489 igc.IncludeList = []net.IP{}
490 }
491 igc.SendLeaveToServer()
492 logger.Debugw(ctx, "Deleted the receiver Flow", log.Fields{"Num Receivers": igc.NumReceivers()})
493 return true
494 }
495 if groupModified {
496 igc.SendReport(false)
497 logger.Infow(ctx, "Updated SourceList for Channel", log.Fields{"Current": igc.CurReceivers, "New": igc.NewReceivers})
498 }
499 if err := igc.WriteToDb(); err != nil {
500 logger.Errorw(ctx, "Igmp group channel Write to DB failed", log.Fields{"mvlan": igc.Mvlan, "GroupAddr": igc.GroupAddr})
501 }
502 logger.Infow(ctx, "Updated Receiver info for Channel", log.Fields{"Current": igc.CurReceivers, "New": igc.NewReceivers})
503
504 return receiversUpdated
505}
506
507// DelAllReceivers deletes all receiver for the provided igmp device
508func (igc *IgmpGroupChannel) DelAllReceivers() {
509 logger.Infow(ctx, "Deleting All Receiver for Channel", log.Fields{"Device": igc.Device, "Channel": igc.GroupAddr.String()})
510 _ = db.DelAllIgmpRcvr(igc.Mvlan, igc.GroupAddr, igc.Device)
511 igc.Exclude = 0
512 igc.DelMcFlow()
513 igc.SendLeaveToServer()
514 logger.Infow(ctx, "MC Flow deleted and Leave sent", log.Fields{"Channel": igc.GroupAddr.String(), "Device": igc.Device})
515}
516
517// Igmpv2ReportPacket build an IGMPv2 Report for the upstream servers
518func (igc *IgmpGroupChannel) Igmpv2ReportPacket() ([]byte, error) {
519 logger.Debugw(ctx, "Buidling IGMP version 2 Report", log.Fields{"Device": igc.Device})
520 return IgmpReportv2Packet(igc.GroupAddr, igc.Mvlan, (*igc.proxyCfg).IgmpCos, **igc.IgmpProxyIP)
521}
522
523// Igmpv3ReportPacket build an IGMPv3 Report for the upstream servers
524func (igc *IgmpGroupChannel) Igmpv3ReportPacket() ([]byte, error) {
525 logger.Debugw(ctx, "Buidling IGMP version 3 Report", log.Fields{"Device": igc.Device, "Exclude": igc.Exclude})
526 if igc.Exclude > 0 {
527 return Igmpv3ReportPacket(igc.GroupAddr, igc.Mvlan, (*igc.proxyCfg).IgmpCos, **igc.IgmpProxyIP, false, igc.ExcludeList)
528 }
529 return Igmpv3ReportPacket(igc.GroupAddr, igc.Mvlan, (*igc.proxyCfg).IgmpCos, **igc.IgmpProxyIP, true, igc.IncludeList)
530}
531
532// SendReport send a consolidated report to the server
533func (igc *IgmpGroupChannel) SendReport(isQuery bool) {
534 var report []byte
535 var err error
536 logger.Debugw(ctx, "Checking Version", log.Fields{"IGC Version": igc.Version, "Proxy Version": (*igc.proxyCfg).IgmpVerToServer,
537 "Result": (getVersion((*igc.proxyCfg).IgmpVerToServer) == IgmpVersion2)})
538
539 /**
540 +------------------------------------------------------------------------+
541 | IGMP version(towards BNG) Configured at VGC |
542 +-------------------------------+----------------------------------------+
543 | v2 | v3 |
544 +===================+==========+===============================+========================================+
545 | Received From RG | V2 Join | Process and Send as V2 to BNG | Process, Convert to v3 and Send to BNG |
546 | | | | Process, Send as v2, if the BNG is v2 |
547 +===================+----------+-------------------------------+----------------------------------------+
548 | V3 Join | Process and Send as V2 to BNG | Process, Send v3 to BNG |
549 | | | Process, Convert, Send as v2, if the |
550 | | | BNG is v2 |
551 +===================+==========+===============================+========================================+
552 | Received From BNG | V2 Query | V2 response to BNG | V2 response to BNG |
553 +===================+----------+-------------------------------+----------------------------------------+
554 | V3 Query | Discard | V3 response to BNG |
555 +==========+===============================+========================================+
556 */
557 // igc.Version: igmp version received from RG.
558 // igc.ServVersion: igmp version received from BNG or IgmpVerToServer present in proxy igmp conf.
559
560 if isQuery && *igc.ServVersion == IgmpVersion3 && getVersion((*igc.proxyCfg).IgmpVerToServer) == IgmpVersion2 {
561 // This is the last scenario where we must discard the query processing.
562 logger.Debug(ctx, "Dropping query packet since the server verion is v3 but igmp proxy version is v2")
563 return
564 }
565
566 if *igc.ServVersion == IgmpVersion2 || getVersion((*igc.proxyCfg).IgmpVerToServer) == IgmpVersion2 {
567 report, err = igc.Igmpv2ReportPacket()
568 } else {
569 report, err = igc.Igmpv3ReportPacket()
570 }
571 if err != nil {
572 logger.Warnw(ctx, "Error Preparing Report", log.Fields{"Device": igc.Device, "Ver": igc.Version, "Reason": err.Error()})
573 return
574 }
575 nni, err := GetApplication().GetNniPort(igc.Device)
576 if err == nil {
577 _ = cntlr.GetController().PacketOutReq(igc.Device, nni, nni, report, false)
578 } else {
579 logger.Warnw(ctx, "Didn't find NNI port", log.Fields{"Device": igc.Device})
580 }
581}
582
583// AddMcFlow adds flow to the device when the first receiver joins
584func (igc *IgmpGroupChannel) AddMcFlow() {
585 flow, err := igc.BuildMcFlow()
586 if err != nil {
587 logger.Warnw(ctx, "MC Flow Build Failed", log.Fields{"Reason": err.Error()})
588 return
589 }
590 port, _ := GetApplication().GetNniPort(igc.Device)
591 _ = cntlr.GetController().AddFlows(port, igc.Device, flow)
592}
593
594// DelMcFlow deletes flow from the device when the last receiver leaves
595func (igc *IgmpGroupChannel) DelMcFlow() {
596 flow, err := igc.BuildMcFlow()
597 if err != nil {
598 logger.Warnw(ctx, "MC Flow Build Failed", log.Fields{"Reason": err.Error()})
599 return
600 }
601 flow.ForceAction = true
602 device := GetApplication().GetDevice(igc.Device)
603
604 if mvpIntf, _ := GetApplication().MvlanProfilesByTag.Load(igc.Mvlan); mvpIntf != nil {
605 mvp := mvpIntf.(*MvlanProfile)
606 err := mvp.DelFlows(device, flow)
607 if err != nil {
608 logger.Warnw(ctx, "Delering IGMP Flow for device failed ", log.Fields{"Device": device, "err": err})
609 }
610 }
611}
612
613// BuildMcFlow builds the flow using which it is added/deleted
614func (igc *IgmpGroupChannel) BuildMcFlow() (*of.VoltFlow, error) {
615 flow := &of.VoltFlow{}
616 flow.SubFlows = make(map[uint64]*of.VoltSubFlow)
617 //va := GetApplication()
618 logger.Infow(ctx, "Building Mcast flow", log.Fields{"Mcast Group": igc.GroupAddr.String(), "Mvlan": igc.Mvlan.String()})
619 uintGroupAddr := ipv4ToUint(igc.GroupAddr)
620 subFlow := of.NewVoltSubFlow()
621 subFlow.SetMatchVlan(igc.Mvlan)
622 subFlow.SetIpv4Match()
623 subFlow.SetMatchDstIpv4(igc.GroupAddr)
624 mvp := GetApplication().GetMvlanProfileByTag(igc.Mvlan)
625 //nni, err := va.GetNniPort(igc.Device)
626 //if err != nil {
627 // return nil, err
628 //}
629 //inport, err := va.GetPortID(nni)
630 //if err != nil {
631 // return nil, err
632 //}
633 //subFlow.SetInPort(inport)
634 subFlow.SetOutGroup(igc.GroupID)
635 cookiePort := uintGroupAddr
636 subFlow.Cookie = uint64(cookiePort)<<32 | uint64(igc.Mvlan)
637 subFlow.Priority = of.McFlowPriority
638 metadata := uint64(mvp.PonVlan)
639 subFlow.SetTableMetadata(metadata)
640
641 flow.SubFlows[subFlow.Cookie] = subFlow
642 logger.Infow(ctx, "Built Mcast flow", log.Fields{"cookie": subFlow.Cookie, "subflow": subFlow})
643 return flow, nil
644}
645
646// IgmpLeaveToServer sends IGMP leave to server. Called when the last receiver leaves the group
647func (igc *IgmpGroupChannel) IgmpLeaveToServer() {
648 if leave, err := IgmpLeavePacket(igc.GroupAddr, igc.Mvlan, (*igc.proxyCfg).IgmpCos, **igc.IgmpProxyIP); err == nil {
649 nni, err1 := GetApplication().GetNniPort(igc.Device)
650 if err1 == nil {
651 _ = cntlr.GetController().PacketOutReq(igc.Device, nni, nni, leave, false)
652 }
653 }
654}
655
656// SendLeaveToServer delete the group when the last receiver leaves the group
657func (igc *IgmpGroupChannel) SendLeaveToServer() {
658 /**
659 +-------------------------------------------------------------------------+
660 | IGMP version(towards BNG) Configured at VGC |
661 +-------------------------------+-----------------------------------------+
662 | v2 | v3 |
663 +===================+==========+===============================+=========================================+
664 | Received From RG | V2 Leave | Process and Send as V2 to BNG | Process, Convert to V3 and Send to BNG/ |
665 | | | | Process, Send as V2, if the BNG is V2 |
666 +===================+----------+-------------------------------+-----------------------------------------+
667 | V3 Leave | Process and Send as V2 to BNG | Process, Send V3 to BNG |
668 | | | Process, Convert, Send as V2, if the |
669 | | | BNG is v2 |
670 +==========+===============================+=========================================+
671 */
672 // igc.Version: igmp version received from RG.
673 // igc.ServVersion: igmp version received from BNG or IgmpVerToServer present in proxy igmp conf.
674
675 logger.Debugw(ctx, "Sending IGMP leave upstream", log.Fields{"Device": igc.Device})
676 if *igc.ServVersion == IgmpVersion2 || getVersion((*igc.proxyCfg).IgmpVerToServer) == IgmpVersion2 {
677 igc.IgmpLeaveToServer()
678 } else {
679 igc.SendReport(false)
680 }
681}
682
683// NumReceivers returns total number of receivers left on the group
684func (igc *IgmpGroupChannel) NumReceivers() uint32 {
685 return uint32(len(igc.CurReceivers) + len(igc.NewReceivers))
686}
687
688// SendQuery sends query to the receivers for counting purpose
689func (igc *IgmpGroupChannel) SendQuery() {
690 //var b []byte
691 //var err error
692 for portKey, port := range igc.NewReceivers {
693 igc.CurReceivers[portKey] = port
694 }
695
696 igc.NewReceivers = make(map[string]*IgmpGroupPort)
697
698 logger.Debugw(ctx, "Sending Query to receivers", log.Fields{"Receivers": igc.CurReceivers})
699 for port, groupPort := range igc.CurReceivers {
700 if port == StaticPort {
701 continue
702 }
703 if queryPkt, err := igc.buildQuery(igc.GroupAddr, of.VlanType(groupPort.CVlan), groupPort.Pbit); err == nil {
704 _ = cntlr.GetController().PacketOutReq(igc.Device, port, port, queryPkt, false)
705 logger.Debugw(ctx, "Query Sent", log.Fields{"Device": igc.Device, "Port": port, "Packet": queryPkt})
706 } else {
707 logger.Warnw(ctx, "Query Creation Failed", log.Fields{"Reason": err.Error()})
708 }
709 }
710
711}
712
713// buildQuery to build query packet
714func (igc *IgmpGroupChannel) buildQuery(groupAddr net.IP, cVlan of.VlanType, pbit uint8) ([]byte, error) {
715 if igc.Version == IgmpVersion2 {
716 return Igmpv2QueryPacket(igc.GroupAddr, cVlan, **igc.IgmpProxyIP, pbit, (*igc.proxyCfg).MaxResp)
717 }
718 return Igmpv3QueryPacket(igc.GroupAddr, cVlan, **igc.IgmpProxyIP, pbit, (*igc.proxyCfg).MaxResp)
719}
720
721// ProcessMode process the received mode and updated the igp
722func (igc *IgmpGroupChannel) ProcessMode(port string, incl bool) {
723 /* Update the mode in igp if the mode has changed */
724 igp := igc.GetReceiver(port)
725 if igp.Exclude && incl {
726 igp.Exclude = !incl
727 if igc.Exclude > 0 {
728 igc.Exclude--
729 }
730 } else if !incl && !igp.Exclude {
731 igp.Exclude = !incl
732 igc.Exclude++
733 }
734}
735