Merge "[SEBA-836] Sending unique transaction id together with the DHCP packets"
diff --git a/build/package/Dockerfile b/build/package/Dockerfile
index 5ede6cc..73f3cca 100644
--- a/build/package/Dockerfile
+++ b/build/package/Dockerfile
@@ -69,6 +69,7 @@
 WORKDIR /app
 COPY --from=builder /go/src/github.com/opencord/bbsim/bbsim /app/bbsim
 COPY --from=builder /go/src/github.com/opencord/bbsim/bbsimctl /usr/bin/bbsimctl
+RUN mv /usr/sbin/tcpdump /usr/bin/tcpdump
 RUN chmod a+x /app/bbsim
 RUN chmod a+x /usr/bin/bbsimctl
 RUN bbsimctl completion bash >> $HOME/.bashrc
diff --git a/cmd/bbsim/bbsim.go b/cmd/bbsim/bbsim.go
index 48b9743..53b6ae0 100644
--- a/cmd/bbsim/bbsim.go
+++ b/cmd/bbsim/bbsim.go
@@ -84,6 +84,7 @@
 		"NumNniPerOlt": options.NumNniPerOlt,
 		"NumPonPerOlt": options.NumPonPerOlt,
 		"NumOnuPerPon": options.NumOnuPerPon,
+		"TotalOnus":    options.NumPonPerOlt * options.NumOnuPerPon,
 	}).Info("BroadBand Simulator is on")
 
 	// control channels, they are only closed when the goroutine needs to be terminated
diff --git a/internal/bbsim/devices/nni.go b/internal/bbsim/devices/nni.go
index f5503cd..3a36287 100644
--- a/internal/bbsim/devices/nni.go
+++ b/internal/bbsim/devices/nni.go
@@ -158,7 +158,7 @@
 	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)
+	cmd := exec.Command(dhcp, "-cf", conf, upstreamVeth, "-tf", logfile, "-4")
 	cmd.Stderr = &stderr
 	err := cmd.Run()
 	if err != nil {
diff --git a/internal/bbsim/devices/onu.go b/internal/bbsim/devices/onu.go
index a5aa208..0f93e0d 100644
--- a/internal/bbsim/devices/onu.go
+++ b/internal/bbsim/devices/onu.go
@@ -113,7 +113,7 @@
 			{Name: "eap_response_success_received", Src: []string{"eap_response_challenge_sent"}, Dst: "eap_response_success_received"},
 			{Name: "auth_failed", Src: []string{"auth_started", "eap_start_sent", "eap_response_identity_sent", "eap_response_challenge_sent"}, Dst: "auth_failed"},
 			// DHCP
-			{Name: "start_dhcp", Src: []string{"eap_response_success_received", "dhcp_ack_received", "dhcp_failed"}, Dst: "dhcp_started"},
+			{Name: "start_dhcp", Src: []string{"eap_response_success_received", "dhcp_discovery_sent", "dhcp_request_sent", "dhcp_ack_received", "dhcp_failed"}, Dst: "dhcp_started"},
 			{Name: "dhcp_discovery_sent", Src: []string{"dhcp_started"}, Dst: "dhcp_discovery_sent"},
 			{Name: "dhcp_request_sent", Src: []string{"dhcp_discovery_sent"}, Dst: "dhcp_request_sent"},
 			{Name: "dhcp_ack_received", Src: []string{"dhcp_request_sent"}, Dst: "dhcp_ack_received"},
diff --git a/internal/bbsim/responders/dhcp/dhcp.go b/internal/bbsim/responders/dhcp/dhcp.go
index 25f5210..ce71069 100644
--- a/internal/bbsim/responders/dhcp/dhcp.go
+++ b/internal/bbsim/responders/dhcp/dhcp.go
@@ -30,6 +30,7 @@
 	log "github.com/sirupsen/logrus"
 	"net"
 	"reflect"
+	"strconv"
 )
 
 var GetGemPortId = omci.GetGemPortId
@@ -55,18 +56,27 @@
 }
 
 func createDefaultDHCPReq(intfId uint32, onuId uint32, mac net.HardwareAddr) layers.DHCPv4 {
+	// NOTE we want to generate a unique XID, the easiest way is to concat the PON ID and the ONU ID
+	// we increment them by one otherwise:
+	// - PON: 0 ONU: 62 = 062 -> 62
+	// - PON: 6 ONU: 2 = 62 -> 62
+	xid, err := strconv.Atoi(fmt.Sprintf("%d%d", intfId+1, onuId+1))
+	if err != nil {
+		log.Fatal("Can't generate unique XID for ONU")
+	}
+
 	return layers.DHCPv4{
 		Operation:    layers.DHCPOpRequest,
 		HardwareType: layers.LinkTypeEthernet,
 		HardwareLen:  6,
 		HardwareOpts: 0,
-		Xid:          onuId,
+		Xid:          uint32(xid),
 		ClientHWAddr: mac,
 	}
 }
 
-func createDefaultOpts() []layers.DHCPOption {
-	hostname := []byte("bbsim.onf.org")
+func createDefaultOpts(intfId uint32, onuId uint32) []layers.DHCPOption {
+	hostname := []byte(fmt.Sprintf("%d.%d.bbsim.onf.org", intfId, onuId))
 	opts := []layers.DHCPOption{}
 	opts = append(opts, layers.DHCPOption{
 		Type:   layers.DHCPOptHostname,
@@ -89,27 +99,27 @@
 
 func createDHCPDisc(intfId uint32, onuId uint32, macAddress net.HardwareAddr) *layers.DHCPv4 {
 	dhcpLayer := createDefaultDHCPReq(intfId, onuId, macAddress)
-	defaultOpts := createDefaultOpts()
+	defaultOpts := createDefaultOpts(intfId, onuId)
 	dhcpLayer.Options = append([]layers.DHCPOption{layers.DHCPOption{
 		Type:   layers.DHCPOptMessageType,
 		Data:   []byte{byte(layers.DHCPMsgTypeDiscover)},
 		Length: 1,
 	}}, defaultOpts...)
 
-	//data := []byte{0xcd, 0x28, 0xcb, 0xcc, 0x00, 0x01, 0x00, 0x01,
-	//	0x23, 0xed, 0x11, 0xec, 0x4e, 0xfc, 0xcd, 0x28, byte(intfId), byte(onuId)}
-	//dhcpLayer.Options = append(dhcpLayer.Options, layers.DHCPOption{
-	//	Type:   layers.DHCPOptClientID,
-	//	Data:   data,
-	//	Length: uint8(len(data)),
-	//})
+	data := []byte{0xcd, 0x28, 0xcb, 0xcc, 0x00, 0x01, 0x00, 0x01,
+		0x23, 0xed, 0x11, 0xec, 0x4e, 0xfc, 0xcd, 0x28, byte(intfId), byte(onuId)}
+	dhcpLayer.Options = append(dhcpLayer.Options, layers.DHCPOption{
+		Type:   layers.DHCPOptClientID,
+		Data:   data,
+		Length: uint8(len(data)),
+	})
 
 	return &dhcpLayer
 }
 
 func createDHCPReq(intfId uint32, onuId uint32, macAddress net.HardwareAddr, offeredIp net.IP) *layers.DHCPv4 {
 	dhcpLayer := createDefaultDHCPReq(intfId, onuId, macAddress)
-	defaultOpts := createDefaultOpts()
+	defaultOpts := createDefaultOpts(intfId, onuId)
 
 	dhcpLayer.Options = append(defaultOpts, layers.DHCPOption{
 		Type:   layers.DHCPOptMessageType,
@@ -247,12 +257,20 @@
 	return nil
 }
 
-func sendDHCPRequest(ponPortId uint32, onuId uint32, serialNumber string, portNo uint32, onuHwAddress net.HardwareAddr, offeredIp net.IP, stream openolt.Openolt_EnableIndicationServer) error {
+func sendDHCPRequest(ponPortId uint32, onuId uint32, serialNumber string, portNo uint32, onuStateMachine *fsm.FSM, onuHwAddress net.HardwareAddr, offeredIp net.IP, stream openolt.Openolt_EnableIndicationServer) error {
 	dhcp := createDHCPReq(ponPortId, onuId, onuHwAddress, offeredIp)
 	pkt, err := serializeDHCPPacket(ponPortId, onuId, onuHwAddress, dhcp)
 
 	if err != nil {
-		dhcpLogger.Errorf("Cannot serializeDHCPPacket: %s", err)
+		dhcpLogger.WithFields(log.Fields{
+			"OnuId":     onuId,
+			"IntfId":    ponPortId,
+			"OnuSn":     serialNumber,
+			"OfferedIp": offeredIp.String(),
+		}).Errorf("Cannot serializeDHCPPacket: %s", err)
+		if err := updateDhcpFailed(onuId, ponPortId, serialNumber, onuStateMachine); err != nil {
+			return err
+		}
 		return err
 	}
 	// NOTE I don't think we need to tag the packet
@@ -265,6 +283,14 @@
 	}
 
 	if err := sendDHCPPktIn(msg, portNo, stream); err != nil {
+		dhcpLogger.WithFields(log.Fields{
+			"OnuId":  onuId,
+			"IntfId": ponPortId,
+			"OnuSn":  serialNumber,
+		}).Errorf("Cannot sendDHCPPktIn: %s", err)
+		if err := updateDhcpFailed(onuId, ponPortId, serialNumber, onuStateMachine); err != nil {
+			return err
+		}
 		return err
 	}
 
@@ -293,7 +319,14 @@
 	dhcp := createDHCPDisc(ponPortId, onuId, onuHwAddress)
 	pkt, err := serializeDHCPPacket(ponPortId, onuId, onuHwAddress, dhcp)
 	if err != nil {
-		dhcpLogger.Errorf("Cannot serializeDHCPPacket: %s", err)
+		dhcpLogger.WithFields(log.Fields{
+			"OnuId":  onuId,
+			"IntfId": ponPortId,
+			"OnuSn":  serialNumber,
+		}).Errorf("Cannot serializeDHCPPacket: %s", err)
+		if err := updateDhcpFailed(onuId, ponPortId, serialNumber, onuStateMachine); err != nil {
+			return err
+		}
 		return err
 	}
 	// NOTE I don't think we need to tag the packet
@@ -306,6 +339,11 @@
 	}
 
 	if err := sendDHCPPktIn(msg, portNo, stream); err != nil {
+		dhcpLogger.WithFields(log.Fields{
+			"OnuId":  onuId,
+			"IntfId": ponPortId,
+			"OnuSn":  serialNumber,
+		}).Errorf("Cannot sendDHCPPktIn: %s", err)
 		if err := updateDhcpFailed(onuId, ponPortId, serialNumber, onuStateMachine); err != nil {
 			return err
 		}
@@ -327,6 +365,7 @@
 	return nil
 }
 
+// FIXME cTag is not used here
 func HandleNextPacket(onuId uint32, ponPortId uint32, serialNumber string, portNo uint32, onuHwAddress net.HardwareAddr, cTag int, onuStateMachine *fsm.FSM, pkt gopacket.Packet, stream openolt.Openolt_EnableIndicationServer) error {
 
 	dhcpLayer, err := GetDhcpLayer(pkt)
@@ -357,7 +396,7 @@
 	if dhcpLayer.Operation == layers.DHCPOpReply {
 		if dhcpMessageType == layers.DHCPMsgTypeOffer {
 			offeredIp := dhcpLayer.YourClientIP
-			if err := sendDHCPRequest(ponPortId, onuId, serialNumber, portNo, onuHwAddress, offeredIp, stream); err != nil {
+			if err := sendDHCPRequest(ponPortId, onuId, serialNumber, portNo, onuStateMachine, onuHwAddress, offeredIp, stream); err != nil {
 				dhcpLogger.WithFields(log.Fields{
 					"OnuId":  onuId,
 					"IntfId": ponPortId,
@@ -399,6 +438,7 @@
 			"OnuSn":  serialNumber,
 		}).Warnf("Unsupported DHCP Operation: %s", dhcpLayer.Operation.String())
 	}
+
 	return nil
 }