[SEBA-817][SEBA-821]
Adding c/s tags and hw address in the onu struct
DHCP State machine completed
Cleaned up logs

Change-Id: Iadb1d3967befe1c402e302a552b67faa2701f5c5
diff --git a/internal/bbsim/devices/olt.go b/internal/bbsim/devices/olt.go
index 6ce09a1..c75f2d1 100644
--- a/internal/bbsim/devices/olt.go
+++ b/internal/bbsim/devices/olt.go
@@ -20,10 +20,10 @@
 	"context"
 	"errors"
 	"fmt"
-	bbsim "github.com/opencord/bbsim/internal/bbsim/types"
 	"github.com/google/gopacket"
 	"github.com/google/gopacket/layers"
 	"github.com/looplab/fsm"
+	bbsim "github.com/opencord/bbsim/internal/bbsim/types"
 	"github.com/opencord/voltha-protos/go/openolt"
 	"github.com/opencord/voltha-protos/go/tech_profile"
 	log "github.com/sirupsen/logrus"
@@ -43,32 +43,33 @@
 
 var olt = OltDevice{}
 
-func GetOLT() OltDevice  {
+func GetOLT() OltDevice {
 	return olt
 }
 
-func CreateOLT(seq int, nni int, pon int, onuPerPon int, oltDoneChannel *chan bool, apiDoneChannel *chan bool, group *sync.WaitGroup) OltDevice {
+func CreateOLT(seq int, nni int, pon int, onuPerPon int, sTag int, cTagInit int, oltDoneChannel *chan bool, apiDoneChannel *chan bool, group *sync.WaitGroup) OltDevice {
 	oltLogger.WithFields(log.Fields{
-		"ID": seq,
-		"NumNni":nni,
-		"NumPon":pon,
-		"NumOnuPerPon":onuPerPon,
+		"ID":           seq,
+		"NumNni":       nni,
+		"NumPon":       pon,
+		"NumOnuPerPon": onuPerPon,
 	}).Debug("CreateOLT")
 
 	olt = OltDevice{
-		ID: seq,
+		ID:           seq,
 		SerialNumber: fmt.Sprintf("BBSIM_OLT_%d", seq),
 		OperState: getOperStateFSM(func(e *fsm.Event) {
 			oltLogger.Debugf("Changing OLT OperState from %s to %s", e.Src, e.Dst)
 		}),
-		NumNni:nni,
-		NumPon:pon,
-		NumOnuPerPon:onuPerPon,
-		Pons: []PonPort{},
-		Nnis: []NniPort{},
-		channel: make(chan Message),
-		oltDoneChannel: oltDoneChannel,
-		apiDoneChannel: apiDoneChannel,
+		NumNni:          nni,
+		NumPon:          pon,
+		NumOnuPerPon:    onuPerPon,
+		Pons:            []PonPort{},
+		Nnis:            []NniPort{},
+		channel:         make(chan Message),
+		oltDoneChannel:  oltDoneChannel,
+		apiDoneChannel:  apiDoneChannel,
+		nniPktInChannel: make(chan *bbsim.PacketMsg, 1024), // packets coming in from the NNI and going to VOLTHA
 	}
 
 	// OLT State machine
@@ -87,22 +88,21 @@
 	)
 
 	// create NNI Port
-	nniPort := NniPort{
-		ID: uint32(0),
-		OperState: getOperStateFSM(func(e *fsm.Event) {
-			oltLogger.Debugf("Changing NNI OperState from %s to %s", e.Src, e.Dst)
-		}),
-		Type: "nni",
+	nniPort, err := CreateNNI(&olt)
+
+	if err != nil {
+		oltLogger.Fatalf("Couldn't create NNI Port: %v", err)
 	}
+
 	olt.Nnis = append(olt.Nnis, nniPort)
 
 	// create PON ports
-	//onuId := 1
+	availableCTag := cTagInit
 	for i := 0; i < pon; i++ {
 		p := PonPort{
 			NumOnu: olt.NumOnuPerPon,
-			ID: uint32(i),
-			Type: "pon",
+			ID:     uint32(i),
+			Type:   "pon",
 		}
 		p.OperState = getOperStateFSM(func(e *fsm.Event) {
 			oltLogger.WithFields(log.Fields{
@@ -113,9 +113,9 @@
 		// create ONU devices
 		for j := 0; j < onuPerPon; j++ {
 			//o := CreateONU(olt, p, uint32(onuId))
-			o := CreateONU(olt, p, uint32(j + 1))
+			o := CreateONU(olt, p, uint32(j+1), sTag, availableCTag)
 			p.Onus = append(p.Onus, o)
-			//onuId = onuId + 1
+			availableCTag = availableCTag + 1
 		}
 
 		olt.Pons = append(olt.Pons, p)
@@ -129,7 +129,7 @@
 
 func newOltServer(o OltDevice) error {
 	// TODO make configurable
-	address :=  "0.0.0.0:50060"
+	address := "0.0.0.0:50060"
 	lis, err := net.Listen("tcp", address)
 	if err != nil {
 		oltLogger.Fatalf("OLT failed to listen: %v", err)
@@ -144,7 +144,7 @@
 	oltLogger.Debugf("OLT Listening on: %v", address)
 
 	for {
-		_, ok := <- *o.oltDoneChannel
+		_, ok := <-*o.oltDoneChannel
 		if !ok {
 			// if the olt channel is closed, stop the gRPC server
 			log.Warnf("Stopping OLT gRPC server")
@@ -161,15 +161,16 @@
 
 // Device Methods
 
-func (o OltDevice) Enable (stream openolt.Openolt_EnableIndicationServer) error {
+func (o OltDevice) Enable(stream openolt.Openolt_EnableIndicationServer) error {
 
 	oltLogger.Debug("Enable OLT called")
 
 	wg := sync.WaitGroup{}
-	wg.Add(1)
+	wg.Add(2)
 
 	// create a channel for all the OLT events
 	go o.processOltMessages(stream)
+	go o.processNniPacketIns(stream)
 
 	// enable the OLT
 	olt_msg := Message{
@@ -207,9 +208,9 @@
 			go onu.processOnuMessages(stream)
 			go onu.processOmciMessages(stream)
 			msg := Message{
-				Type:      OnuDiscIndication,
+				Type: OnuDiscIndication,
 				Data: OnuDiscIndicationMessage{
-					Onu:     onu,
+					Onu:       onu,
 					OperState: UP,
 				},
 			}
@@ -257,8 +258,8 @@
 	nni.OperState.Event("enable")
 	// NOTE Operstate may need to be an integer
 	operData := &openolt.Indication_IntfOperInd{IntfOperInd: &openolt.IntfOperIndication{
-		Type: nni.Type,
-		IntfId: nni.ID,
+		Type:      nni.Type,
+		IntfId:    nni.ID,
 		OperState: nni.OperState.Current(),
 	}}
 
@@ -267,8 +268,8 @@
 	}
 
 	oltLogger.WithFields(log.Fields{
-		"Type": nni.Type,
-		"IntfId": nni.ID,
+		"Type":      nni.Type,
+		"IntfId":    nni.ID,
 		"OperState": nni.OperState.Current(),
 	}).Debug("Sent Indication_IntfOperInd for NNI")
 }
@@ -277,7 +278,7 @@
 	pon, _ := o.getPonById(msg.PonPortID)
 	pon.OperState.Event("enable")
 	discoverData := &openolt.Indication_IntfInd{IntfInd: &openolt.IntfIndication{
-		IntfId: pon.ID,
+		IntfId:    pon.ID,
 		OperState: pon.OperState.Current(),
 	}}
 
@@ -286,13 +287,13 @@
 	}
 
 	oltLogger.WithFields(log.Fields{
-		"IntfId": pon.ID,
+		"IntfId":    pon.ID,
 		"OperState": pon.OperState.Current(),
 	}).Debug("Sent Indication_IntfInd")
 
 	operData := &openolt.Indication_IntfOperInd{IntfOperInd: &openolt.IntfOperIndication{
-		Type: pon.Type,
-		IntfId: pon.ID,
+		Type:      pon.Type,
+		IntfId:    pon.ID,
 		OperState: pon.OperState.Current(),
 	}}
 
@@ -301,8 +302,8 @@
 	}
 
 	oltLogger.WithFields(log.Fields{
-		"Type": pon.Type,
-		"IntfId": pon.ID,
+		"Type":      pon.Type,
+		"IntfId":    pon.ID,
 		"OperState": pon.OperState.Current(),
 	}).Debug("Sent Indication_IntfOperInd for PON")
 }
@@ -311,9 +312,8 @@
 	oltLogger.Debug("Started OLT Indication Channel")
 	for message := range o.channel {
 
-
 		oltLogger.WithFields(log.Fields{
-			"oltId": o.ID,
+			"oltId":       o.ID,
 			"messageType": message.Type,
 		}).Trace("Received message")
 
@@ -341,11 +341,32 @@
 	}
 }
 
+func (o OltDevice) processNniPacketIns(stream openolt.Openolt_EnableIndicationServer) {
+	oltLogger.WithFields(log.Fields{
+		"nniChannel": o.nniPktInChannel,
+	}).Debug("Started NNI Channel")
+	nniId := o.Nnis[0].ID // FIXME we are assuming we have only one NNI
+	for message := range o.nniPktInChannel {
+		oltLogger.Debug("Received packets on NNI Channel")
+		data := &openolt.Indication_PktInd{PktInd: &openolt.PacketIndication{
+			IntfType: "nni",
+			IntfId:   nniId,
+			Pkt:      message.Pkt.Data()}}
+		if err := stream.Send(&openolt.Indication{Data: data}); err != nil {
+			oltLogger.WithFields(log.Fields{
+				"IntfType": data.PktInd.IntfType,
+				"IntfId":   nniId,
+				"Pkt":      message.Pkt.Data(),
+			}).Errorf("Fail to send PktInd indication: %v", err)
+		}
+	}
+}
+
 // GRPC Endpoints
 
-func (o OltDevice) ActivateOnu(context context.Context, onu *openolt.Onu) (*openolt.Empty, error)  {
+func (o OltDevice) ActivateOnu(context context.Context, onu *openolt.Onu) (*openolt.Empty, error) {
 	oltLogger.WithFields(log.Fields{
-		"onuSerialNumber": onu.SerialNumber,
+		"OnuSn": onuSnToString(onu.SerialNumber),
 	}).Info("Received ActivateOnu call from VOLTHA")
 
 	pon, _ := o.getPonById(onu.IntfId)
@@ -353,28 +374,28 @@
 
 	// NOTE we need to immediately activate the ONU or the OMCI state machine won't start
 	msg := Message{
-		Type:      OnuIndication,
-		Data:      OnuIndicationMessage{
+		Type: OnuIndication,
+		Data: OnuIndicationMessage{
 			OnuSN:     onu.SerialNumber,
 			PonPortID: onu.IntfId,
 			OperState: UP,
 		},
 	}
 	_onu.channel <- msg
-	return new(openolt.Empty) , nil
+	return new(openolt.Empty), nil
 }
 
-func (o OltDevice) DeactivateOnu(context.Context, *openolt.Onu) (*openolt.Empty, error)  {
+func (o OltDevice) DeactivateOnu(context.Context, *openolt.Onu) (*openolt.Empty, error) {
 	oltLogger.Error("DeactivateOnu not implemented")
-	return new(openolt.Empty) , nil
+	return new(openolt.Empty), nil
 }
 
-func (o OltDevice) DeleteOnu(context.Context, *openolt.Onu) (*openolt.Empty, error)  {
+func (o OltDevice) DeleteOnu(context.Context, *openolt.Onu) (*openolt.Empty, error) {
 	oltLogger.Error("DeleteOnu not implemented")
-	return new(openolt.Empty) , nil
+	return new(openolt.Empty), nil
 }
 
-func (o OltDevice) DisableOlt(context.Context, *openolt.Empty) (*openolt.Empty, error)  {
+func (o OltDevice) DisableOlt(context.Context, *openolt.Empty) (*openolt.Empty, error) {
 	// NOTE when we disable the OLT should we disable NNI, PONs and ONUs altogether?
 	olt_msg := Message{
 		Type: OltIndication,
@@ -383,37 +404,37 @@
 		},
 	}
 	o.channel <- olt_msg
-	return new(openolt.Empty) , nil
+	return new(openolt.Empty), nil
 }
 
-func (o OltDevice) DisablePonIf(context.Context, *openolt.Interface) (*openolt.Empty, error)  {
+func (o OltDevice) DisablePonIf(context.Context, *openolt.Interface) (*openolt.Empty, error) {
 	oltLogger.Error("DisablePonIf not implemented")
-	return new(openolt.Empty) , nil
+	return new(openolt.Empty), nil
 }
 
-func (o OltDevice) EnableIndication(_ *openolt.Empty, stream openolt.Openolt_EnableIndicationServer) error  {
+func (o OltDevice) EnableIndication(_ *openolt.Empty, stream openolt.Openolt_EnableIndicationServer) error {
 	oltLogger.WithField("oltId", o.ID).Info("OLT receives EnableIndication call from VOLTHA")
 	o.Enable(stream)
 	return nil
 }
 
-func (o OltDevice) EnablePonIf(context.Context, *openolt.Interface) (*openolt.Empty, error)  {
+func (o OltDevice) EnablePonIf(context.Context, *openolt.Interface) (*openolt.Empty, error) {
 	oltLogger.Error("EnablePonIf not implemented")
-	return new(openolt.Empty) , nil
+	return new(openolt.Empty), nil
 }
 
-func (o OltDevice) FlowAdd(ctx context.Context, flow *openolt.Flow) (*openolt.Empty, error)  {
+func (o OltDevice) FlowAdd(ctx context.Context, flow *openolt.Flow) (*openolt.Empty, error) {
 	oltLogger.WithFields(log.Fields{
-		"IntfId": flow.AccessIntfId,
-		"OnuId": flow.OnuId,
-		"EthType": fmt.Sprintf("%x", flow.Classifier.EthType),
+		"IntfId":    flow.AccessIntfId,
+		"OnuId":     flow.OnuId,
+		"EthType":   fmt.Sprintf("%x", flow.Classifier.EthType),
 		"InnerVlan": flow.Classifier.IVid,
 		"OuterVlan": flow.Classifier.OVid,
-		"FlowType": flow.FlowType,
-		"FlowId": flow.FlowId,
-		"UniID": flow.UniId,
-		"PortNo": flow.PortNo,
-	}).Infof("OLT receives Flow")
+		"FlowType":  flow.FlowType,
+		"FlowId":    flow.FlowId,
+		"UniID":     flow.UniId,
+		"PortNo":    flow.PortNo,
+	}).Tracef("OLT receives Flow")
 	// TODO optionally store flows somewhere
 
 	if flow.AccessIntfId == -1 {
@@ -425,34 +446,34 @@
 		onu, _ := pon.getOnuById(uint32(flow.OnuId))
 
 		msg := Message{
-			Type:      FlowUpdate,
-			Data:      OnuFlowUpdateMessage{
-				PonPortID:  pon.ID,
-				OnuID: 	onu.ID,
-				Flow: 	flow,
+			Type: FlowUpdate,
+			Data: OnuFlowUpdateMessage{
+				PonPortID: pon.ID,
+				OnuID:     onu.ID,
+				Flow:      flow,
 			},
 		}
 		onu.channel <- msg
 	}
 
-	return new(openolt.Empty) , nil
+	return new(openolt.Empty), nil
 }
 
-func (o OltDevice) FlowRemove(context.Context, *openolt.Flow) (*openolt.Empty, error)  {
-	oltLogger.Info("received FlowRemove")
+func (o OltDevice) FlowRemove(context.Context, *openolt.Flow) (*openolt.Empty, error) {
+	oltLogger.Tracef("received FlowRemove")
 	// TODO store flows somewhere
-	return new(openolt.Empty) , nil
+	return new(openolt.Empty), nil
 }
 
-func (o OltDevice) HeartbeatCheck(context.Context, *openolt.Empty) (*openolt.Heartbeat, error)  {
+func (o OltDevice) HeartbeatCheck(context.Context, *openolt.Empty) (*openolt.Heartbeat, error) {
 	oltLogger.Error("HeartbeatCheck not implemented")
-	return new(openolt.Heartbeat) , nil
+	return new(openolt.Heartbeat), nil
 }
 
-func (o OltDevice) GetDeviceInfo(context.Context, *openolt.Empty) (*openolt.DeviceInfo, error)  {
+func (o OltDevice) GetDeviceInfo(context.Context, *openolt.Empty) (*openolt.DeviceInfo, error) {
 
 	oltLogger.WithFields(log.Fields{
-		"oltId": o.ID,
+		"oltId":    o.ID,
 		"PonPorts": o.NumPon,
 	}).Info("OLT receives GetDeviceInfo call from VOLTHA")
 	devinfo := new(openolt.DeviceInfo)
@@ -475,67 +496,76 @@
 	return devinfo, nil
 }
 
-func (o OltDevice) OmciMsgOut(ctx context.Context, omci_msg *openolt.OmciMsg) (*openolt.Empty, error)  {
+func (o OltDevice) OmciMsgOut(ctx context.Context, omci_msg *openolt.OmciMsg) (*openolt.Empty, error) {
 	pon, _ := o.getPonById(omci_msg.IntfId)
 	onu, _ := pon.getOnuById(omci_msg.OnuId)
 	msg := Message{
-		Type:      OMCI,
-		Data:      OmciMessage{
-			OnuSN:  onu.SerialNumber,
-			OnuID: 	onu.ID,
-			omciMsg: 	omci_msg,
+		Type: OMCI,
+		Data: OmciMessage{
+			OnuSN:   onu.SerialNumber,
+			OnuID:   onu.ID,
+			omciMsg: omci_msg,
 		},
 	}
 	onu.channel <- msg
-	return new(openolt.Empty) , nil
+	return new(openolt.Empty), nil
 }
 
-func (o OltDevice) OnuPacketOut(ctx context.Context, onuPkt *openolt.OnuPacket) (*openolt.Empty, error)  {
+func (o OltDevice) OnuPacketOut(ctx context.Context, onuPkt *openolt.OnuPacket) (*openolt.Empty, error) {
 	pon, _ := o.getPonById(onuPkt.IntfId)
 	onu, _ := pon.getOnuById(onuPkt.OnuId)
 
 	rawpkt := gopacket.NewPacket(onuPkt.Pkt, layers.LayerTypeEthernet, gopacket.Default)
 
-	// NOTE is this the best way to the to the ethertype?
 	etherType := rawpkt.Layer(layers.LayerTypeEthernet).(*layers.Ethernet).EthernetType
 
 	if etherType == layers.EthernetTypeEAPOL {
 		eapolPkt := bbsim.ByteMsg{IntfId: onuPkt.IntfId, OnuId: onuPkt.OnuId, Bytes: rawpkt.Data()}
 		onu.eapolPktOutCh <- &eapolPkt
+	} else if layerDHCP := rawpkt.Layer(layers.LayerTypeDHCPv4); layerDHCP != nil {
+		// TODO use IsDhcpPacket
+		// TODO we need to untag the packets
+		// NOTE here we receive packets going from the DHCP Server to the ONU
+		// for now we expect them to be double-tagged, but ideally the should be single tagged
+		dhcpPkt := bbsim.ByteMsg{IntfId: onuPkt.IntfId, OnuId: onuPkt.OnuId, Bytes: rawpkt.Data()}
+		onu.dhcpPktOutCh <- &dhcpPkt
 	}
-	return new(openolt.Empty) , nil
+	return new(openolt.Empty), nil
 }
 
-func (o OltDevice) Reboot(context.Context, *openolt.Empty) (*openolt.Empty, error)  {
+func (o OltDevice) Reboot(context.Context, *openolt.Empty) (*openolt.Empty, error) {
 	oltLogger.Info("Shutting Down")
 	close(*o.oltDoneChannel)
 	close(*o.apiDoneChannel)
-	return new(openolt.Empty) , nil
+	return new(openolt.Empty), nil
 }
 
 func (o OltDevice) ReenableOlt(context.Context, *openolt.Empty) (*openolt.Empty, error) {
 	oltLogger.Error("ReenableOlt not implemented")
-	return new(openolt.Empty) , nil
+	return new(openolt.Empty), nil
 }
 
 func (o OltDevice) UplinkPacketOut(context context.Context, packet *openolt.UplinkPacket) (*openolt.Empty, error) {
-	oltLogger.Warn("UplinkPacketOut not implemented")
-	return new(openolt.Empty) , nil
+	pkt := gopacket.NewPacket(packet.Pkt, layers.LayerTypeEthernet, gopacket.Default)
+
+	sendNniPacket(pkt)
+	// NOTE should we return an error if sendNniPakcet fails?
+	return new(openolt.Empty), nil
 }
 
-func (o OltDevice) CollectStatistics(context.Context, *openolt.Empty) (*openolt.Empty, error)  {
+func (o OltDevice) CollectStatistics(context.Context, *openolt.Empty) (*openolt.Empty, error) {
 	oltLogger.Error("CollectStatistics not implemented")
-	return new(openolt.Empty) , nil
+	return new(openolt.Empty), nil
 }
 
 func (o OltDevice) GetOnuInfo(context context.Context, packet *openolt.Onu) (*openolt.OnuIndication, error) {
 	oltLogger.Error("GetOnuInfo not implemented")
-	return new(openolt.OnuIndication) , nil
+	return new(openolt.OnuIndication), nil
 }
 
 func (o OltDevice) GetPonIf(context context.Context, packet *openolt.Interface) (*openolt.IntfIndication, error) {
 	oltLogger.Error("GetPonIf not implemented")
-	return new(openolt.IntfIndication) , nil
+	return new(openolt.IntfIndication), nil
 }
 
 func (s OltDevice) CreateTrafficQueues(context.Context, *tech_profile.TrafficQueues) (*openolt.Empty, error) {
@@ -556,4 +586,4 @@
 func (s OltDevice) RemoveTrafficSchedulers(context.Context, *tech_profile.TrafficSchedulers) (*openolt.Empty, error) {
 	oltLogger.Info("received RemoveTrafficSchedulers")
 	return new(openolt.Empty), nil
-}
\ No newline at end of file
+}