[VOL-2778] Handling IGMP

Change-Id: I54c26bc438a144c7d4c64a9b3d543461fc743ab2
diff --git a/internal/bbsim/responders/igmp/igmp.go b/internal/bbsim/responders/igmp/igmp.go
index defbe19..5390347 100644
--- a/internal/bbsim/responders/igmp/igmp.go
+++ b/internal/bbsim/responders/igmp/igmp.go
@@ -15,25 +15,28 @@
 
 import (
 	"encoding/binary"
+	"encoding/hex"
+	"errors"
+	"github.com/opencord/bbsim/internal/bbsim/packetHandlers"
 	"net"
 	"time"
 
 	"github.com/google/gopacket"
 	"github.com/google/gopacket/layers"
 	bbsim "github.com/opencord/bbsim/internal/bbsim/types"
-	omci "github.com/opencord/omci-sim"
-	"github.com/opencord/voltha-protos/v2/go/openolt"
+	"github.com/opencord/voltha-protos/v3/go/openolt"
 	log "github.com/sirupsen/logrus"
 )
 
-func SendIGMPLeaveGroupV2(ponPortId uint32, onuId uint32, serialNumber string, portNo uint32, macAddress net.HardwareAddr, stream bbsim.Stream) error {
+func SendIGMPLeaveGroupV2(ponPortId uint32, onuId uint32, serialNumber string, portNo uint32,
+	gemPortId uint32, macAddress net.HardwareAddr, cTag int, pbit uint8, stream bbsim.Stream) error {
 	log.WithFields(log.Fields{
 		"OnuId":        onuId,
 		"SerialNumber": serialNumber,
 		"PortNo":       portNo,
 	}).Debugf("Entered SendIGMPLeaveGroupV2")
 	igmp := createIGMPV2LeaveRequestPacket()
-	pkt, err := serializeIgmpPacket(ponPortId, onuId, macAddress, igmp)
+	pkt, err := serializeIgmpPacket(ponPortId, onuId, cTag, macAddress, pbit, igmp)
 
 	if err != nil {
 		log.WithFields(log.Fields{
@@ -44,21 +47,11 @@
 		return err
 	}
 
-	gemid, err := omci.GetGemPortId(ponPortId, onuId)
-	if err != nil {
-		log.WithFields(log.Fields{
-			"OnuId":        onuId,
-			"IntfId":       ponPortId,
-			"SerialNumber": serialNumber,
-		}).Errorf("Can't retrieve GemPortId for IGMP: %s", err)
-		return err
-	}
-
 	data := &openolt.Indication_PktInd{
 		PktInd: &openolt.PacketIndication{
 			IntfType:  "pon",
 			IntfId:    ponPortId,
-			GemportId: uint32(gemid),
+			GemportId: gemPortId,
 			Pkt:       pkt,
 			PortNo:    portNo,
 		},
@@ -77,14 +70,11 @@
 	return nil
 }
 
-func SendIGMPMembershipReportV2(ponPortId uint32, onuId uint32, serialNumber string, portNo uint32, macAddress net.HardwareAddr, stream bbsim.Stream) error {
-	log.WithFields(log.Fields{
-		"OnuId":        onuId,
-		"SerialNumber": serialNumber,
-		"PortNo":       portNo,
-	}).Debugf("Entered SendIGMPMembershipReportV2")
+func SendIGMPMembershipReportV2(ponPortId uint32, onuId uint32, serialNumber string, portNo uint32,
+	gemPortId uint32, macAddress net.HardwareAddr, cTag int, pbit uint8, stream bbsim.Stream) error {
+
 	igmp := createIGMPV2MembershipReportPacket()
-	pkt, err := serializeIgmpPacket(ponPortId, onuId, macAddress, igmp)
+	pkt, err := serializeIgmpPacket(ponPortId, onuId, cTag, macAddress, pbit, igmp)
 
 	if err != nil {
 		log.WithFields(log.Fields{
@@ -95,21 +85,11 @@
 		return err
 	}
 
-	gemid, err := omci.GetGemPortId(ponPortId, onuId)
-	if err != nil {
-		log.WithFields(log.Fields{
-			"OnuId":        onuId,
-			"IntfId":       ponPortId,
-			"SerialNumber": serialNumber,
-		}).Errorf("Can't retrieve GemPortId for IGMP: %s", err)
-		return err
-	}
-
 	data := &openolt.Indication_PktInd{
 		PktInd: &openolt.PacketIndication{
 			IntfType:  "pon",
 			IntfId:    ponPortId,
-			GemportId: uint32(gemid),
+			GemportId: gemPortId,
 			Pkt:       pkt,
 			PortNo:    portNo,
 		},
@@ -125,17 +105,24 @@
 		}).Errorf("Fail to send IGMP PktInd indication")
 		return err
 	}
+
+	log.WithFields(log.Fields{
+		"OnuId":        onuId,
+		"SerialNumber": serialNumber,
+		"PortNo":       portNo,
+	}).Debugf("Sent SendIGMPMembershipReportV2")
 	return nil
 }
 
-func SendIGMPMembershipReportV3(ponPortId uint32, onuId uint32, serialNumber string, portNo uint32, macAddress net.HardwareAddr, stream bbsim.Stream) error {
+func SendIGMPMembershipReportV3(ponPortId uint32, onuId uint32, serialNumber string, portNo uint32,
+	gemPortId uint32, macAddress net.HardwareAddr, cTag int, pbit uint8, stream bbsim.Stream) error {
 	log.WithFields(log.Fields{
 		"OnuId":        onuId,
 		"SerialNumber": serialNumber,
 		"PortNo":       portNo,
 	}).Debugf("Entered SendIGMPMembershipReportV3")
 	igmp := createIGMPV3MembershipReportPacket()
-	pkt, err := serializeIgmpPacket(ponPortId, onuId, macAddress, igmp)
+	pkt, err := serializeIgmpPacket(ponPortId, onuId, cTag, macAddress, pbit, igmp)
 
 	if err != nil {
 		log.WithFields(log.Fields{
@@ -146,21 +133,11 @@
 		return err
 	}
 
-	gemid, err := omci.GetGemPortId(ponPortId, onuId)
-	if err != nil {
-		log.WithFields(log.Fields{
-			"OnuId":        onuId,
-			"IntfId":       ponPortId,
-			"SerialNumber": serialNumber,
-		}).Errorf("Can't retrieve GemPortId for IGMP: %s", err)
-		return err
-	}
-
 	data := &openolt.Indication_PktInd{
 		PktInd: &openolt.PacketIndication{
 			IntfType:  "pon",
 			IntfId:    ponPortId,
-			GemportId: uint32(gemid),
+			GemportId: gemPortId,
 			Pkt:       pkt,
 			PortNo:    portNo,
 		},
@@ -178,7 +155,34 @@
 	return nil
 }
 
-func createIGMPV3MembershipReportPacket() IGMP {
+func HandleNextPacket(ponPortId uint32, onuId uint32, serialNumber string, portNo uint32,
+	gemPortId uint32, macAddress net.HardwareAddr, pkt gopacket.Packet, cTag int, pbit uint8, stream bbsim.Stream) error {
+
+	igmpLayer := pkt.Layer(layers.LayerTypeIGMP)
+	if igmpLayer == nil {
+		log.WithFields(log.Fields{
+			"OnuId":        onuId,
+			"SerialNumber": serialNumber,
+			"PortNo":       portNo,
+			"Pkt":          hex.EncodeToString(pkt.Data()),
+		}).Error("This is not an IGMP packet")
+		return errors.New("packet-is-not-igmp")
+	}
+
+	log.WithFields(log.Fields{
+		"Pkt": pkt.Data(),
+	}).Trace("IGMP packet")
+
+	igmp := igmpLayer.(*layers.IGMPv1or2)
+
+	if igmp.Type == layers.IGMPMembershipQuery {
+		_ = SendIGMPMembershipReportV2(ponPortId, onuId, serialNumber, portNo, gemPortId, macAddress, cTag, pbit, stream)
+	}
+
+	return nil
+}
+
+func createIGMPV3MembershipReportPacket() *IGMP {
 
 	groupRecord1 := IGMPv3GroupRecord{
 		Type:             IGMPv3GroupRecordType(IGMPIsIn),
@@ -198,8 +202,8 @@
 		AuxData:          0, // NOT USED
 	}
 
-	igmpDefault := IGMP{
-		Type:                    0x22, //IGMPV3 Membership Report
+	igmpDefault := &IGMP{
+		Type:                    layers.IGMPMembershipReportV3, //IGMPV3 Membership Report
 		MaxResponseTime:         time.Duration(1),
 		Checksum:                0,
 		GroupAddress:            net.IPv4(224, 0, 0, 22),
@@ -216,10 +220,18 @@
 	return igmpDefault
 }
 
-//func serializeIgmpPacket(intfId uint32, onuId uint32, srcMac net.HardwareAddr, igmp *layers.IGMP) ([]byte, error) {
-func createIGMPV2MembershipReportPacket() IGMP {
-	return IGMP{
-		Type:            0x16, //IGMPV2 Membership Report
+func createIGMPV2MembershipReportPacket() *IGMP {
+	return &IGMP{
+		Type:         layers.IGMPMembershipReportV2, //IGMPV2 Membership Report
+		Checksum:     0,
+		GroupAddress: net.IPv4(224, 0, 0, 22),
+		Version:      2,
+	}
+}
+
+func createIGMPV2LeaveRequestPacket() *IGMP {
+	return &IGMP{
+		Type:            layers.IGMPLeaveGroup, //IGMPV2 Leave Group
 		MaxResponseTime: time.Duration(1),
 		Checksum:        0,
 		GroupAddress:    net.IPv4(224, 0, 0, 22),
@@ -227,17 +239,7 @@
 	}
 }
 
-func createIGMPV2LeaveRequestPacket() IGMP {
-	return IGMP{
-		Type:            0x17, //IGMPV2 Leave Group
-		MaxResponseTime: time.Duration(1),
-		Checksum:        0,
-		GroupAddress:    net.IPv4(224, 0, 0, 22),
-		Version:         2,
-	}
-}
-
-func serializeIgmpPacket(intfId uint32, onuId uint32, srcMac net.HardwareAddr, igmp IGMP) ([]byte, error) {
+func serializeIgmpPacket(intfId uint32, onuId uint32, cTag int, srcMac net.HardwareAddr, pbit uint8, igmp *IGMP) ([]byte, error) {
 	buffer := gopacket.NewSerializeBuffer()
 	options := gopacket.SerializeOptions{
 		ComputeChecksums: true,
@@ -265,28 +267,22 @@
 		return nil, err
 	}
 
-	return buffer.Bytes(), nil
+	untaggedPkt := gopacket.NewPacket(buffer.Bytes(), layers.LayerTypeEthernet, gopacket.Default)
+	taggedPkt, err := packetHandlers.PushSingleTag(cTag, untaggedPkt, pbit)
+
+	if err != nil {
+		log.Error("TagPacket")
+		return nil, err
+	}
+
+	return taggedPkt.Data(), nil
 }
 
 //-----------------------------------------***********************---------------------------------
-// BaseLayer is a convenience struct which implements the LayerData and
-// LayerPayload functions of the Layer interface.
-type BaseLayer struct {
-	// Contents is the set of bytes that make up this layer.  IE: for an
-	// Ethernet packet, this would be the set of bytes making up the
-	// Ethernet frame.
-	Contents []byte
-	// Payload is the set of bytes contained by (but not part of) this
-	// Layer.  Again, to take Ethernet as an example, this would be the
-	// set of bytes encapsulated by the Ethernet protocol.
-	Payload []byte
-}
-
-type IGMPType uint8
 
 type IGMP struct {
-	BaseLayer
-	Type                    IGMPType
+	layers.BaseLayer
+	Type                    layers.IGMPType
 	MaxResponseTime         time.Duration
 	Checksum                uint16
 	GroupAddress            net.IP
@@ -343,8 +339,7 @@
 // SerializeTo writes the serialized form of this layer into the
 // SerializationBuffer, implementing gopacket.SerializableLayer.
 // See the docs for gopacket.SerializableLayer for more info.
-func (igmp IGMP) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error {
-	log.Debugf("Serializing IGMP Packet")
+func (igmp *IGMP) SerializeTo(b gopacket.SerializeBuffer, opts gopacket.SerializeOptions) error {
 	data, err := b.PrependBytes(8915)
 	if err != nil {
 		return err
@@ -410,4 +405,6 @@
 	return ^uint16(csum)
 }
 
-func (IGMP) LayerType() gopacket.LayerType { return layers.LayerTypeIGMP }
+func (i *IGMP) LayerType() gopacket.LayerType { return layers.LayerTypeIGMP }
+func (i *IGMP) LayerContents() []byte         { return i.Contents }
+func (i *IGMP) LayerPayload() []byte          { return i.Payload }