[SEBA-836] BBSim Reflector

Change-Id: Ib4ae5a2c24880dc62209bebb81188eca5f57865d
diff --git a/internal/bbsim/responders/eapol/eapol.go b/internal/bbsim/responders/eapol/eapol.go
index f31a068..c13aa69 100644
--- a/internal/bbsim/responders/eapol/eapol.go
+++ b/internal/bbsim/responders/eapol/eapol.go
@@ -17,6 +17,7 @@
 package eapol
 
 import (
+	"context"
 	"crypto/md5"
 	"errors"
 	"github.com/google/gopacket"
@@ -72,6 +73,17 @@
 	return ret
 }
 
+func createEAPChallengeRequest(eapId uint8, payload []byte) *layers.EAP {
+	eap := layers.EAP{
+		Code:     layers.EAPCodeRequest,
+		Id:       eapId,
+		Length:   22,
+		Type:     layers.EAPTypeOTP,
+		TypeData: payload,
+	}
+	return &eap
+}
+
 func createEAPChallengeResponse(eapId uint8, payload []byte) *layers.EAP {
 	eap := layers.EAP{
 		Code:     layers.EAPCodeResponse,
@@ -83,6 +95,15 @@
 	return &eap
 }
 
+func createEAPIdentityRequest(eapId uint8) *layers.EAP {
+	eap := layers.EAP{Code: layers.EAPCodeRequest,
+		Id:       eapId,
+		Length:   9,
+		Type:     layers.EAPTypeIdentity,
+		TypeData: []byte{0x75, 0x73, 0x65, 0x72}}
+	return &eap
+}
+
 func createEAPIdentityResponse(eapId uint8) *layers.EAP {
 	eap := layers.EAP{Code: layers.EAPCodeResponse,
 		Id:       eapId,
@@ -92,6 +113,16 @@
 	return &eap
 }
 
+func createEAPSuccess(eapId uint8) *layers.EAP {
+	eap := layers.EAP{
+		Code:     layers.EAPCodeSuccess,
+		Id:       eapId,
+		Length:   9,
+		Type:     layers.EAPTypeNone,
+		TypeData: []byte{0x75, 0x73, 0x65, 0x72}}
+	return &eap
+}
+
 func createEAPOLPkt(eap *layers.EAP, onuId uint32, intfId uint32) []byte {
 	buffer := gopacket.NewSerializeBuffer()
 	options := gopacket.SerializeOptions{}
@@ -121,6 +152,30 @@
 	return eap, nil
 }
 
+func extractEAPOL(pkt gopacket.Packet) (*layers.EAPOL, error) {
+	layerEAPOL := pkt.Layer(layers.LayerTypeEAPOL)
+	eap, _ := layerEAPOL.(*layers.EAPOL)
+	if eap == nil {
+		return nil, errors.New("Cannot extract EAPOL")
+	}
+	return eap, nil
+}
+
+func sendEapolPktOut(client openolt.OpenoltClient, intfId uint32, onuId uint32, pkt []byte) error {
+	onuPacket := openolt.OnuPacket{
+		IntfId:    intfId,
+		OnuId:     onuId,
+		PortNo:    onuId,
+		GemportId: 1,
+		Pkt:       pkt,
+	}
+
+	if _, err := client.OnuPacketOut(context.Background(), &onuPacket); err != nil {
+		return err
+	}
+	return nil
+}
+
 func updateAuthFailed(onuId uint32, ponPortId uint32, serialNumber string, onuStateMachine *fsm.FSM) error {
 	if err := onuStateMachine.Event("auth_failed"); err != nil {
 		eapolLogger.WithFields(log.Fields{
@@ -210,22 +265,58 @@
 	return nil
 }
 
-func HandleNextPacket(onuId uint32, ponPortId uint32, serialNumber string, portNo uint32, onuStateMachine *fsm.FSM, recvpkt gopacket.Packet, stream openolt.Openolt_EnableIndicationServer) {
+func HandleNextPacket(onuId uint32, ponPortId uint32, serialNumber string, portNo uint32, onuStateMachine *fsm.FSM, pkt gopacket.Packet, stream openolt.Openolt_EnableIndicationServer, client openolt.OpenoltClient) {
 
-	eap, err := extractEAP(recvpkt)
-	if err != nil {
-		eapolLogger.Errorf("%s", err)
+	eap, eapErr := extractEAP(pkt)
+
+	eapol, eapolErr := extractEAPOL(pkt)
+
+	if eapErr != nil && eapolErr != nil {
+		log.Fatalf("Failed to Extract EAP: %v - %v", eapErr, eapolErr)
+		return
 	}
 
-	log.WithFields(log.Fields{
-		"eap.Code": eap.Code,
-		"eap.Type": eap.Type,
-		"OnuId":    onuId,
-		"IntfId":   ponPortId,
-		"OnuSn":    serialNumber,
-	}).Tracef("HandleNextPacket")
+	fields := log.Fields{}
+	if eap != nil {
+		fields = log.Fields{
+			"Code":   eap.Code,
+			"Type":   eap.Type,
+			"OnuId":  onuId,
+			"IntfId": ponPortId,
+			"OnuSn":  serialNumber,
+		}
+	} else if eapol != nil {
+		fields = log.Fields{
+			"Type":   eapol.Type,
+			"OnuId":  onuId,
+			"IntfId": ponPortId,
+			"OnuSn":  serialNumber,
+		}
+	}
 
-	if eap.Code == layers.EAPCodeRequest && eap.Type == layers.EAPTypeIdentity {
+	log.WithFields(fields).Tracef("Handle Next EAPOL Packet")
+
+	if eapol != nil && eapol.Type == layers.EAPOLTypeStart {
+		identityRequest := createEAPIdentityRequest(1)
+		pkt := createEAPOLPkt(identityRequest, onuId, ponPortId)
+
+		if err := sendEapolPktOut(client, ponPortId, onuId, pkt); err != nil {
+			log.WithFields(log.Fields{
+				"OnuId":  onuId,
+				"IntfId": ponPortId,
+				"OnuSn":  serialNumber,
+				"error":  err,
+			}).Errorf("Error while sending EAPIdentityRequest packet")
+			return
+		}
+
+		log.WithFields(log.Fields{
+			"OnuId":  onuId,
+			"IntfId": ponPortId,
+			"OnuSn":  serialNumber,
+		}).Infof("Sent EAPIdentityRequest packet")
+		return
+	} else if eap.Code == layers.EAPCodeRequest && eap.Type == layers.EAPTypeIdentity {
 		reseap := createEAPIdentityResponse(eap.Id)
 		pkt := createEAPOLPkt(reseap, onuId, ponPortId)
 
@@ -250,6 +341,27 @@
 			}).Errorf("Error while transitioning ONU State %v", err)
 		}
 
+	} else if eap.Code == layers.EAPCodeResponse && eap.Type == layers.EAPTypeIdentity {
+		senddata := getMD5Data(eap)
+		senddata = append([]byte{0x10}, senddata...)
+		challengeRequest := createEAPChallengeRequest(eap.Id, senddata)
+		pkt := createEAPOLPkt(challengeRequest, onuId, ponPortId)
+
+		if err := sendEapolPktOut(client, ponPortId, onuId, pkt); err != nil {
+			log.WithFields(log.Fields{
+				"OnuId":  onuId,
+				"IntfId": ponPortId,
+				"OnuSn":  serialNumber,
+				"error":  err,
+			}).Errorf("Error while sending EAPChallengeRequest packet")
+			return
+		}
+		log.WithFields(log.Fields{
+			"OnuId":  onuId,
+			"IntfId": ponPortId,
+			"OnuSn":  serialNumber,
+		}).Infof("Sent EAPChallengeRequest packet")
+		return
 	} else if eap.Code == layers.EAPCodeRequest && eap.Type == layers.EAPTypeOTP {
 		senddata := getMD5Data(eap)
 		senddata = append([]byte{0x10}, senddata...)
@@ -276,6 +388,33 @@
 				"OnuSn":  serialNumber,
 			}).Errorf("Error while transitioning ONU State %v", err)
 		}
+	} else if eap.Code == layers.EAPCodeResponse && eap.Type == layers.EAPTypeOTP {
+		eapSuccess := createEAPSuccess(eap.Id)
+		pkt := createEAPOLPkt(eapSuccess, onuId, ponPortId)
+
+		if err := sendEapolPktOut(client, ponPortId, onuId, pkt); err != nil {
+			log.WithFields(log.Fields{
+				"OnuId":  onuId,
+				"IntfId": ponPortId,
+				"OnuSn":  serialNumber,
+				"error":  err,
+			}).Errorf("Error while sending EAPSuccess packet")
+			return
+		}
+
+		log.WithFields(log.Fields{
+			"OnuId":  onuId,
+			"IntfId": ponPortId,
+			"OnuSn":  serialNumber,
+		}).Infof("Sent EAP Success packet")
+
+		if err := onuStateMachine.Event("send_dhcp_flow"); err != nil {
+			eapolLogger.WithFields(log.Fields{
+				"OnuId":  onuId,
+				"IntfId": ponPortId,
+				"OnuSn":  serialNumber,
+			}).Errorf("Error while transitioning ONU State %v", err)
+		}
 	} else if eap.Code == layers.EAPCodeSuccess && eap.Type == layers.EAPTypeNone {
 		eapolLogger.WithFields(log.Fields{
 			"OnuId":  onuId,