[VOL-3610] implementing a fake DHCP server within BBSim

Change-Id: If291a0ca7f78909c3713ef0e6831e381304fc2c9
diff --git a/internal/bbsim/devices/nni.go b/internal/bbsim/devices/nni.go
index fe092e8..32fffaa 100644
--- a/internal/bbsim/devices/nni.go
+++ b/internal/bbsim/devices/nni.go
@@ -17,68 +17,42 @@
 package devices
 
 import (
-	"bytes"
 	"encoding/hex"
-	"os/exec"
-
 	"github.com/google/gopacket"
-	"github.com/google/gopacket/pcap"
 	"github.com/looplab/fsm"
 	"github.com/opencord/bbsim/internal/bbsim/packetHandlers"
-	"github.com/opencord/bbsim/internal/bbsim/types"
+	"github.com/opencord/voltha-protos/v4/go/openolt"
 	log "github.com/sirupsen/logrus"
 )
 
-var (
-	nniLogger    = log.WithFields(log.Fields{"module": "NNI"})
-	dhcpServerIp = "192.168.254.1"
-)
-
-type Executor interface {
-	Command(name string, arg ...string) Runnable
-}
-
-type DefaultExecutor struct{}
-
-func (d DefaultExecutor) Command(name string, arg ...string) Runnable {
-	return exec.Command(name, arg...)
-}
-
-type Runnable interface {
-	Run() error
-}
-
-var executor = DefaultExecutor{}
+var nniLogger = log.WithFields(log.Fields{"module": "NNI"})
 
 type NniPort struct {
 	// BBSIM Internals
-	ID           uint32
-	nniVeth      string
-	upstreamVeth string
-	PacketCount  uint64
+	ID  uint32
+	Olt *OltDevice
 
 	// PON Attributes
-	OperState *fsm.FSM
-	Type      string
+	OperState   *fsm.FSM
+	Type        string
+	PacketCount uint64 // dummy value for the stats
 }
 
 func CreateNNI(olt *OltDevice) (NniPort, error) {
 	nniPort := NniPort{
-		ID:           uint32(0),
-		nniVeth:      "nni",
-		upstreamVeth: "upstream",
+		ID: uint32(0),
 		OperState: getOperStateFSM(func(e *fsm.Event) {
 			oltLogger.Debugf("Changing NNI OperState from %s to %s", e.Src, e.Dst)
 		}),
 		Type: "nni",
+		Olt:  olt,
 	}
-	_ = createNNIPair(executor, olt, &nniPort)
+
 	return nniPort, nil
 }
 
-// sendNniPacket will send a packet out of the NNI interface.
-// We will send upstream only DHCP packets and drop anything else
-func (n *NniPort) sendNniPacket(packet gopacket.Packet) error {
+// handleNniPacket will send a packet to a fake DHCP server implementation
+func (n *NniPort) handleNniPacket(packet gopacket.Packet) error {
 	isDhcp := packetHandlers.IsDhcpPacket(packet)
 	isLldp := packetHandlers.IsLldpPacket(packet)
 
@@ -90,152 +64,33 @@
 	}
 
 	if isDhcp {
-		packet, err := packetHandlers.PopDoubleTag(packet)
+
+		// get a response packet from the DHCP server
+		pkt, err := n.Olt.dhcpServer.HandleServerPacket(packet)
 		if err != nil {
 			nniLogger.WithFields(log.Fields{
-				"packet": packet,
-			}).Errorf("Can't remove double tags from packet: %v", err)
+				"SourcePkt": hex.EncodeToString(packet.Data()),
+				"Err":       err,
+			}).Error("DHCP Server can't handle packet")
 			return err
 		}
 
-		handle, err := getVethHandler(n.nniVeth)
-		if err != nil {
+		// send packetIndication to VOLTHA
+		data := &openolt.Indication_PktInd{PktInd: &openolt.PacketIndication{
+			IntfType: "nni",
+			IntfId:   n.ID,
+			Pkt:      pkt.Data()}}
+		if err := n.Olt.OpenoltStream.Send(&openolt.Indication{Data: data}); err != nil {
+			oltLogger.WithFields(log.Fields{
+				"IntfType": data.PktInd.IntfType,
+				"IntfId":   n.ID,
+				"Pkt":      hex.EncodeToString(pkt.Data()),
+			}).Errorf("Fail to send PktInd indication: %v", err)
 			return err
 		}
-
-		err = handle.WritePacketData(packet.Data())
-		if err != nil {
-			nniLogger.WithFields(log.Fields{
-				"packet": packet,
-			}).Errorf("Failed to send packet out of the NNI: %s", err)
-			return err
-		}
-
-		nniLogger.WithFields(log.Fields{
-			"packet": hex.EncodeToString(packet.Data()),
-		}).Trace("Sent packet out of NNI")
 	} else if isLldp {
 		// TODO rework this when BBSim supports data-plane packets
 		nniLogger.Trace("Received LLDP Packet, ignoring it")
 	}
 	return nil
 }
-
-//createNNIBridge will create a veth bridge to fake the connection between the NNI port
-//and something upstream, in this case a DHCP server.
-//It is also responsible to start the DHCP server itself
-func createNNIPair(executor Executor, olt *OltDevice, nniPort *NniPort) error {
-
-	if err := executor.Command("ip", "link", "add", nniPort.nniVeth, "type", "veth", "peer", "name", nniPort.upstreamVeth).Run(); err != nil {
-		nniLogger.Errorf("Couldn't create veth pair between %s and %s", nniPort.nniVeth, nniPort.upstreamVeth)
-		return err
-	}
-
-	if err := setVethUp(executor, nniPort.nniVeth); err != nil {
-		return err
-	}
-
-	if err := setVethUp(executor, nniPort.upstreamVeth); err != nil {
-		return err
-	}
-
-	// TODO should be moved out of this function in case there are multiple NNI interfaces.
-	// Only one DHCP server should be running and listening on all NNI interfaces
-	if err := startDHCPServer(nniPort.upstreamVeth, dhcpServerIp); err != nil {
-		return err
-	}
-
-	return nil
-}
-
-// NewVethChan returns a new channel for receiving packets over the NNI interface
-func (n *NniPort) NewVethChan() (chan *types.PacketMsg, *pcap.Handle, error) {
-	ch, handle, err := listenOnVeth(n.nniVeth)
-	if err != nil {
-		return nil, nil, err
-	}
-	return ch, handle, err
-}
-
-// setVethUp is responsible to activate a virtual interface
-func setVethUp(executor Executor, vethName string) error {
-	if err := executor.Command("ip", "link", "set", vethName, "up").Run(); err != nil {
-		nniLogger.Errorf("Couldn't change interface %s state to up: %v", vethName, err)
-		return err
-	}
-	return nil
-}
-
-var startDHCPServer = func(upstreamVeth string, dhcpServerIp string) error {
-	// TODO the DHCP server should support multiple interfaces
-	if err := exec.Command("ip", "addr", "add", dhcpServerIp, "dev", upstreamVeth).Run(); err != nil {
-		nniLogger.Errorf("Couldn't assing ip %s to interface %s: %v", dhcpServerIp, upstreamVeth, err)
-		return err
-	}
-
-	if err := setVethUp(executor, upstreamVeth); err != nil {
-		return err
-	}
-
-	dhcp := "/usr/local/bin/dhcpd"
-	conf := "/etc/dhcp/dhcpd.conf" // copied in the container from configs/dhcpd.conf
-	logfile := "/tmp/dhcplog"
-	var stderr bytes.Buffer
-	cmd := exec.Command(dhcp, "-cf", conf, upstreamVeth, "-tf", logfile, "-4")
-	cmd.Stderr = &stderr
-	err := cmd.Run()
-	if err != nil {
-		nniLogger.Errorf("Fail to start DHCP Server: %s, %s", err, stderr.String())
-		return err
-	}
-	nniLogger.Info("Successfully activated DHCP Server")
-	return nil
-}
-
-func getVethHandler(vethName string) (*pcap.Handle, error) {
-	var (
-		device            = vethName
-		snapshotLen int32 = 1518
-		promiscuous       = false
-		timeout           = pcap.BlockForever
-	)
-	handle, err := pcap.OpenLive(device, snapshotLen, promiscuous, timeout)
-	if err != nil {
-		nniLogger.Errorf("Can't retrieve handler for interface %s", vethName)
-		return nil, err
-	}
-	return handle, nil
-}
-
-var listenOnVeth = func(vethName string) (chan *types.PacketMsg, *pcap.Handle, error) {
-
-	handle, err := getVethHandler(vethName)
-	if err != nil {
-		return nil, nil, err
-	}
-
-	channel := make(chan *types.PacketMsg, 1024)
-
-	go func() {
-		nniLogger.Info("Start listening on NNI for packets")
-		packetSource := gopacket.NewPacketSource(handle, handle.LinkType())
-		for packet := range packetSource.Packets() {
-
-			if !packetHandlers.IsIncomingPacket(packet) {
-				nniLogger.Tracef("Ignoring packet as it's going out")
-				continue
-			}
-
-			nniLogger.WithFields(log.Fields{
-				"packet": packet.Dump(),
-			}).Tracef("Received packet on NNI Port")
-			pkt := types.PacketMsg{
-				Pkt: packet,
-			}
-			channel <- &pkt
-		}
-		nniLogger.Info("Stop listening on NNI for packets")
-	}()
-
-	return channel, handle, nil
-}