[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/responders/dhcp/dhcp.go b/internal/bbsim/responders/dhcp/dhcp.go
new file mode 100644
index 0000000..d2a3db7
--- /dev/null
+++ b/internal/bbsim/responders/dhcp/dhcp.go
@@ -0,0 +1,387 @@
+/*
+ * Copyright 2018-present Open Networking Foundation
+
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+
+ * http://www.apache.org/licenses/LICENSE-2.0
+
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dhcp
+
+import (
+ "errors"
+ "fmt"
+ "github.com/google/gopacket"
+ "github.com/google/gopacket/layers"
+ "github.com/looplab/fsm"
+ bbsim "github.com/opencord/bbsim/internal/bbsim/types"
+ omci "github.com/opencord/omci-sim"
+ "github.com/opencord/voltha-protos/go/openolt"
+ log "github.com/sirupsen/logrus"
+ "net"
+ "reflect"
+)
+
+var dhcpLogger = log.WithFields(log.Fields{
+ "module": "DHCP",
+})
+
+var defaultParamsRequestList = []layers.DHCPOpt{
+ layers.DHCPOptSubnetMask,
+ layers.DHCPOptBroadcastAddr,
+ layers.DHCPOptTimeOffset,
+ layers.DHCPOptRouter,
+ layers.DHCPOptDomainName,
+ layers.DHCPOptDNS,
+ layers.DHCPOptDomainSearch,
+ layers.DHCPOptHostname,
+ layers.DHCPOptNetBIOSTCPNS,
+ layers.DHCPOptNetBIOSTCPScope,
+ layers.DHCPOptInterfaceMTU,
+ layers.DHCPOptClasslessStaticRoute,
+ layers.DHCPOptNTPServers,
+}
+
+func createDefaultDHCPReq(intfId uint32, onuId uint32) layers.DHCPv4 {
+ return layers.DHCPv4{
+ Operation: layers.DHCPOpRequest,
+ HardwareType: layers.LinkTypeEthernet,
+ HardwareLen: 6,
+ HardwareOpts: 0,
+ Xid: onuId,
+ ClientHWAddr: net.HardwareAddr{0x2e, 0x60, 0x70, 0x13, byte(intfId), byte(onuId)},
+ }
+}
+
+func createDefaultOpts() []layers.DHCPOption {
+ hostname := []byte("bbsim.onf.org")
+ opts := []layers.DHCPOption{}
+ opts = append(opts, layers.DHCPOption{
+ Type: layers.DHCPOptHostname,
+ Data: hostname,
+ Length: uint8(len(hostname)),
+ })
+
+ bytes := []byte{}
+ for _, option := range defaultParamsRequestList {
+ bytes = append(bytes, byte(option))
+ }
+
+ opts = append(opts, layers.DHCPOption{
+ Type: layers.DHCPOptParamsRequest,
+ Data: bytes,
+ Length: uint8(len(bytes)),
+ })
+ return opts
+}
+
+func createDHCPDisc(intfId uint32, onuId uint32) *layers.DHCPv4 {
+ dhcpLayer := createDefaultDHCPReq(intfId, onuId)
+ defaultOpts := createDefaultOpts()
+ dhcpLayer.Options = append([]layers.DHCPOption{layers.DHCPOption{
+ Type: layers.DHCPOptMessageType,
+ Data: []byte{byte(layers.DHCPMsgTypeDiscover)},
+ Length: 1,
+ }}, defaultOpts...)
+
+ return &dhcpLayer
+}
+
+func createDHCPReq(intfId uint32, onuId uint32) *layers.DHCPv4 {
+ dhcpLayer := createDefaultDHCPReq(intfId, onuId)
+ defaultOpts := createDefaultOpts()
+
+ dhcpLayer.Options = append(defaultOpts, layers.DHCPOption{
+ Type: layers.DHCPOptMessageType,
+ Data: []byte{byte(layers.DHCPMsgTypeRequest)},
+ Length: 1,
+ })
+
+ data := []byte{182, 21, 0, 128}
+ dhcpLayer.Options = append(dhcpLayer.Options, layers.DHCPOption{
+ Type: layers.DHCPOptServerID,
+ Data: data,
+ Length: uint8(len(data)),
+ })
+
+ data = []byte{0xcd, 0x28, 0xcb, 0xcc, 0x00, 0x01, 0x00, 0x01,
+ 0x23, 0xed, 0x11, 0xec, 0x4e, 0xfc, 0xcd, 0x28, 0xcb, 0xcc}
+ dhcpLayer.Options = append(dhcpLayer.Options, layers.DHCPOption{
+ Type: layers.DHCPOptClientID,
+ Data: data,
+ Length: uint8(len(data)),
+ })
+
+ data = []byte{182, 21, 0, byte(onuId)}
+ dhcpLayer.Options = append(dhcpLayer.Options, layers.DHCPOption{
+ Type: layers.DHCPOptRequestIP,
+ Data: data,
+ Length: uint8(len(data)),
+ })
+ return &dhcpLayer
+}
+
+func serializeDHCPPacket(intfId uint32, onuId uint32, srcMac net.HardwareAddr, dhcp *layers.DHCPv4) ([]byte, error) {
+ buffer := gopacket.NewSerializeBuffer()
+ options := gopacket.SerializeOptions{
+ ComputeChecksums: true,
+ FixLengths: true,
+ }
+
+ ethernetLayer := &layers.Ethernet{
+ SrcMAC: srcMac,
+ DstMAC: net.HardwareAddr{0xff, 0xff, 0xff, 0xff, 0xff, 0xff},
+ EthernetType: layers.EthernetTypeIPv4,
+ }
+
+ ipLayer := &layers.IPv4{
+ Version: 4,
+ TOS: 0x10,
+ TTL: 128,
+ SrcIP: []byte{0, 0, 0, 0},
+ DstIP: []byte{255, 255, 255, 255},
+ Protocol: layers.IPProtocolUDP,
+ }
+
+ udpLayer := &layers.UDP{
+ SrcPort: 68,
+ DstPort: 67,
+ }
+
+ udpLayer.SetNetworkLayerForChecksum(ipLayer)
+ if err := gopacket.SerializeLayers(buffer, options, ethernetLayer, ipLayer, udpLayer, dhcp); err != nil {
+ return nil, err
+ }
+
+ bytes := buffer.Bytes()
+ return bytes, nil
+}
+
+func getDhcpLayer(pkt gopacket.Packet) (*layers.DHCPv4, error) {
+ layerDHCP := pkt.Layer(layers.LayerTypeDHCPv4)
+ dhcp, _ := layerDHCP.(*layers.DHCPv4)
+ if dhcp == nil {
+ return nil, errors.New("Failed-to-extract-DHCP-layer")
+ }
+ return dhcp, nil
+}
+
+func getDhcpMessageType(dhcp *layers.DHCPv4) (layers.DHCPMsgType, error) {
+ for _, option := range dhcp.Options {
+ if option.Type == layers.DHCPOptMessageType {
+ if reflect.DeepEqual(option.Data, []byte{byte(layers.DHCPMsgTypeOffer)}) {
+ return layers.DHCPMsgTypeOffer, nil
+ } else if reflect.DeepEqual(option.Data, []byte{byte(layers.DHCPMsgTypeAck)}) {
+ return layers.DHCPMsgTypeAck, nil
+ } else if reflect.DeepEqual(option.Data, []byte{byte(layers.DHCPMsgTypeRelease)}) {
+ return layers.DHCPMsgTypeRelease, nil
+ } else {
+ msg := fmt.Sprintf("This type %x is not supported", option.Data)
+ return 0, errors.New(msg)
+ }
+ }
+ }
+ return 0, errors.New("Failed to extract MsgType from dhcp")
+}
+
+func sendDHCPPktIn(msg bbsim.ByteMsg, stream openolt.Openolt_EnableIndicationServer) error {
+ // FIXME unify sendDHCPPktIn and sendEapolPktIn methods
+ gemid, err := omci.GetGemPortId(msg.IntfId, msg.OnuId)
+ if err != nil {
+ dhcpLogger.WithFields(log.Fields{
+ "OnuId": msg.OnuId,
+ "IntfId": msg.IntfId,
+ }).Errorf("Can't retrieve GemPortId: %s", err)
+ return err
+ }
+ data := &openolt.Indication_PktInd{PktInd: &openolt.PacketIndication{
+ IntfType: "pon", IntfId: msg.IntfId, GemportId: uint32(gemid), Pkt: msg.Bytes,
+ }}
+
+ if err := stream.Send(&openolt.Indication{Data: data}); err != nil {
+ dhcpLogger.Errorf("Fail to send DHCP PktInd indication. %v", err)
+ return err
+ }
+ return nil
+}
+
+func sendDHCPDiscovery(ponPortId uint32, onuId uint32, serialNumber string, onuHwAddress net.HardwareAddr, cTag int, stream openolt.Openolt_EnableIndicationServer) error {
+ dhcp := createDHCPDisc(ponPortId, onuId)
+ pkt, err := serializeDHCPPacket(ponPortId, onuId, onuHwAddress, dhcp)
+ if err != nil {
+ dhcpLogger.Errorf("Cannot serializeDHCPPacket: %s", err)
+ return err
+ }
+ // NOTE I don't think we need to tag the packet
+ //taggedPkt, err := packetHandlers.PushSingleTag(cTag, pkt)
+
+ msg := bbsim.ByteMsg{
+ IntfId: ponPortId,
+ OnuId: onuId,
+ Bytes: pkt,
+ }
+
+ if err := sendDHCPPktIn(msg, stream); err != nil {
+ return err
+ }
+ dhcpLogger.WithFields(log.Fields{
+ "OnuId": onuId,
+ "IntfId": ponPortId,
+ "OnuSn": serialNumber,
+ }).Infof("DHCPDiscovery Sent")
+ return nil
+}
+
+func sendDHCPRequest(ponPortId uint32, onuId uint32, serialNumber string, onuHwAddress net.HardwareAddr, cTag int, stream openolt.Openolt_EnableIndicationServer) error {
+ dhcp := createDHCPReq(ponPortId, onuId)
+ pkt, err := serializeDHCPPacket(ponPortId, onuId, onuHwAddress, dhcp)
+
+ if err != nil {
+ dhcpLogger.Errorf("Cannot serializeDHCPPacket: %s", err)
+ return err
+ }
+ // NOTE I don't think we need to tag the packet
+ //taggedPkt, err := packetHandlers.PushSingleTag(cTag, pkt)
+
+ msg := bbsim.ByteMsg{
+ IntfId: ponPortId,
+ OnuId: onuId,
+ Bytes: pkt,
+ }
+
+ if err := sendDHCPPktIn(msg, stream); err != nil {
+ return err
+ }
+ dhcpLogger.WithFields(log.Fields{
+ "OnuId": onuId,
+ "IntfId": ponPortId,
+ "OnuSn": serialNumber,
+ }).Infof("DHCPDiscovery Sent")
+ return nil
+}
+
+func CreateDHCPClient(onuId uint32, ponPortId uint32, serialNumber string, onuHwAddress net.HardwareAddr, cTag int, onuStateMachine *fsm.FSM, stream openolt.Openolt_EnableIndicationServer, pktOutCh chan *bbsim.ByteMsg) {
+ // NOTE pckOutCh is channel to listen on for packets received by VOLTHA
+ // the OLT device will publish messages on that channel
+
+ dhcpLogger.WithFields(log.Fields{
+ "OnuId": onuId,
+ "IntfId": ponPortId,
+ "OnuSn": serialNumber,
+ }).Infof("DHCP State Machine starting")
+
+ defer dhcpLogger.WithFields(log.Fields{
+ "OnuId": onuId,
+ "IntfId": ponPortId,
+ "OnuSn": serialNumber,
+ }).Infof("DHCP State machine completed")
+
+ // Send DHCP Discovery packet
+ if err := sendDHCPDiscovery(ponPortId, onuId, serialNumber, onuHwAddress, cTag, stream); err != nil {
+ dhcpLogger.WithFields(log.Fields{
+ "OnuId": onuId,
+ "IntfId": ponPortId,
+ "OnuSn": serialNumber,
+ }).Errorf("Can't send DHCP Discovery: %s", err)
+ if err := onuStateMachine.Event("dhcp_failed"); err != nil {
+ dhcpLogger.WithFields(log.Fields{
+ "OnuId": onuId,
+ "IntfId": ponPortId,
+ "OnuSn": serialNumber,
+ }).Errorf("Error while transitioning ONU State %v", err)
+ }
+ return
+ }
+
+ if err := onuStateMachine.Event("dhcp_discovery_sent"); err != nil {
+ dhcpLogger.WithFields(log.Fields{
+ "OnuId": onuId,
+ "IntfId": ponPortId,
+ "OnuSn": serialNumber,
+ }).Errorf("Error while transitioning ONU State %v", err)
+ }
+
+ dhcpLogger.WithFields(log.Fields{
+ "OnuId": onuId,
+ "IntfId": ponPortId,
+ "OnuSn": serialNumber,
+ }).Infof("Listening on dhcpPktOutCh")
+
+ for msg := range pktOutCh {
+ dhcpLogger.Tracef("Received DHCP message %v", msg)
+
+ pkt := gopacket.NewPacket(msg.Bytes, layers.LayerTypeEthernet, gopacket.Default)
+ dhcpLayer, err := getDhcpLayer(pkt)
+ if err != nil {
+ dhcpLogger.WithFields(log.Fields{
+ "OnuId": onuId,
+ "IntfId": ponPortId,
+ "OnuSn": serialNumber,
+ }).Errorf("Can't get DHCP Layer from Packet: %v", err)
+ continue
+ }
+ dhcpMessageType, err := getDhcpMessageType(dhcpLayer)
+ if err != nil {
+ dhcpLogger.WithFields(log.Fields{
+ "OnuId": onuId,
+ "IntfId": ponPortId,
+ "OnuSn": serialNumber,
+ }).Errorf("Can't get DHCP Message Type from DHCP Layer: %v", err)
+ continue
+ }
+
+ if dhcpLayer.Operation == layers.DHCPOpReply {
+ if dhcpMessageType == layers.DHCPMsgTypeOffer {
+ if err := sendDHCPRequest(ponPortId, onuId, serialNumber, onuHwAddress, cTag, stream); err != nil {
+ dhcpLogger.WithFields(log.Fields{
+ "OnuId": onuId,
+ "IntfId": ponPortId,
+ "OnuSn": serialNumber,
+ }).Errorf("Can't send DHCP Request: %s", err)
+ if err := onuStateMachine.Event("dhcp_failed"); err != nil {
+ dhcpLogger.WithFields(log.Fields{
+ "OnuId": onuId,
+ "IntfId": ponPortId,
+ "OnuSn": serialNumber,
+ }).Errorf("Error while transitioning ONU State %v", err)
+ }
+ return
+ }
+ if err := onuStateMachine.Event("dhcp_request_sent"); err != nil {
+ dhcpLogger.WithFields(log.Fields{
+ "OnuId": onuId,
+ "IntfId": ponPortId,
+ "OnuSn": serialNumber,
+ }).Errorf("Error while transitioning ONU State %v", err)
+ }
+
+ } else if dhcpMessageType == layers.DHCPMsgTypeAck {
+ // NOTE once the ack is received we don't need to do anything but change the state
+ if err := onuStateMachine.Event("dhcp_ack_received"); err != nil {
+ dhcpLogger.WithFields(log.Fields{
+ "OnuId": onuId,
+ "IntfId": ponPortId,
+ "OnuSn": serialNumber,
+ }).Errorf("Error while transitioning ONU State %v", err)
+ }
+ return
+ }
+ // NOTE do we need to care about DHCPMsgTypeRelease??
+ } else {
+ dhcpLogger.WithFields(log.Fields{
+ "OnuId": onuId,
+ "IntfId": ponPortId,
+ "OnuSn": serialNumber,
+ }).Warnf("Unsupported DHCP Operation: %s", dhcpLayer.Operation.String())
+ continue
+ }
+ }
+}