[VOL-2778] Introducing Service definition in order to support the TT workflow
Change-Id: Ib171502e8940b5d0b219620a4503f7095d376d7a
diff --git a/internal/bbsim/devices/messageTypes.go b/internal/bbsim/devices/messageTypes.go
index f59ea5c..d9ed620 100644
--- a/internal/bbsim/devices/messageTypes.go
+++ b/internal/bbsim/devices/messageTypes.go
@@ -20,6 +20,7 @@
"github.com/google/gopacket"
"github.com/opencord/bbsim/internal/bbsim/packetHandlers"
"github.com/opencord/voltha-protos/v2/go/openolt"
+ "net"
)
type MessageType int
@@ -33,8 +34,6 @@
OMCI MessageType = 5
FlowAdd MessageType = 6
FlowRemoved MessageType = 18
- StartEAPOL MessageType = 7
- StartDHCP MessageType = 8
OnuPacketOut MessageType = 9
// BBR messages
@@ -130,10 +129,12 @@
}
type OnuPacketMessage struct {
- IntfId uint32
- OnuId uint32
- Packet gopacket.Packet
- Type packetHandlers.PacketType
+ IntfId uint32
+ OnuId uint32
+ Packet gopacket.Packet
+ Type packetHandlers.PacketType
+ MacAddress net.HardwareAddr
+ GemPortId uint32 // this is used by BBR
}
type OperState int
diff --git a/internal/bbsim/devices/nni.go b/internal/bbsim/devices/nni.go
index e74e97b..fe092e8 100644
--- a/internal/bbsim/devices/nni.go
+++ b/internal/bbsim/devices/nni.go
@@ -18,6 +18,7 @@
import (
"bytes"
+ "encoding/hex"
"os/exec"
"github.com/google/gopacket"
@@ -110,7 +111,9 @@
return err
}
- nniLogger.Infof("Sent packet out of NNI")
+ 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")
diff --git a/internal/bbsim/devices/olt.go b/internal/bbsim/devices/olt.go
index 21b57f2..eff9c86 100644
--- a/internal/bbsim/devices/olt.go
+++ b/internal/bbsim/devices/olt.go
@@ -18,6 +18,7 @@
import (
"context"
+ "encoding/hex"
"fmt"
"net"
"sync"
@@ -73,7 +74,7 @@
enableContext context.Context
enableContextCancel context.CancelFunc
- OpenoltStream *openolt.Openolt_EnableIndicationServer
+ OpenoltStream openolt.Openolt_EnableIndicationServer
enablePerf bool
}
@@ -84,7 +85,7 @@
return &olt
}
-func CreateOLT(options common.BBSimYamlConfig, isMock bool) *OltDevice {
+func CreateOLT(options common.GlobalConfig, services []common.ServiceYaml, isMock bool) *OltDevice {
oltLogger.WithFields(log.Fields{
"ID": options.Olt.ID,
"NumNni": options.Olt.NniPorts,
@@ -146,44 +147,61 @@
olt.Nnis = append(olt.Nnis, &nniPort)
}
+ // Create device and Services
+
+ nextCtag := map[string]int{}
+ nextStag := map[string]int{}
+
// create PON ports
+ for i := 0; i < olt.NumPon; i++ {
+ p := CreatePonPort(&olt, uint32(i))
- if options.BBSim.STagAllocation == common.TagAllocationShared && options.BBSim.CTagAllocation == common.TagAllocationShared {
- oltLogger.Fatalf("This configuration will result in duplicate C/S tags combination")
- } else if options.BBSim.STagAllocation == common.TagAllocationUnique && options.BBSim.CTagAllocation == common.TagAllocationUnique {
- oltLogger.Fatalf("This configuration is not supported yet")
- } else if options.BBSim.STagAllocation == common.TagAllocationShared && options.BBSim.CTagAllocation == common.TagAllocationUnique {
- // ATT case
- availableCTag := options.BBSim.CTag
- for i := 0; i < olt.NumPon; i++ {
- p := CreatePonPort(&olt, uint32(i))
+ // create ONU devices
+ for j := 0; j < olt.NumOnuPerPon; j++ {
+ delay := time.Duration(olt.Delay*j) * time.Millisecond
+ o := CreateONU(&olt, p, uint32(j+1), delay, isMock)
- // create ONU devices
- for j := 0; j < olt.NumOnuPerPon; j++ {
- delay := time.Duration(olt.Delay*j) * time.Millisecond
- o := CreateONU(&olt, p, uint32(j+1), options.BBSim.STag, availableCTag, options.BBSim.EnableAuth, options.BBSim.EnableDhcp, delay, isMock)
- p.Onus = append(p.Onus, o)
- availableCTag = availableCTag + 1
+ for k, s := range common.Services {
+
+ // find the correct cTag for this service
+ if _, ok := nextCtag[s.Name]; !ok {
+ // it's the first time we iterate over this service,
+ // so we start from the config value
+ nextCtag[s.Name] = s.CTag
+ } else {
+ // we have a previous value, so we check it
+ // if Allocation is unique, we increment,
+ // otherwise (shared) we do nothing
+ if s.CTagAllocation == common.TagAllocationUnique.String() {
+ nextCtag[s.Name] = nextCtag[s.Name] + 1
+ }
+ }
+
+ // find the correct sTag for this service
+ if _, ok := nextStag[s.Name]; !ok {
+ nextStag[s.Name] = s.STag
+ } else {
+ if s.STagAllocation == common.TagAllocationUnique.String() {
+ nextStag[s.Name] = nextStag[s.Name] + 1
+ }
+ }
+
+ mac := net.HardwareAddr{0x2e, 0x60, byte(olt.ID), byte(p.ID), byte(o.ID), byte(k)}
+ service, err := NewService(s.Name, mac, o, nextCtag[s.Name], nextStag[s.Name],
+ s.NeedsEapol, s.NeedsDchp, s.NeedsIgmp, s.TechnologyProfileID, s.UniTagMatch,
+ s.ConfigureMacAddress, s.UsPonCTagPriority, s.UsPonSTagPriority, s.DsPonCTagPriority, s.DsPonSTagPriority)
+
+ if err != nil {
+ oltLogger.WithFields(log.Fields{
+ "Err": err.Error(),
+ }).Fatal("Can't create Service")
+ }
+
+ o.Services = append(o.Services, service)
}
-
- olt.Pons = append(olt.Pons, p)
+ p.Onus = append(p.Onus, o)
}
- } else if options.BBSim.STagAllocation == common.TagAllocationUnique && options.BBSim.CTagAllocation == common.TagAllocationShared {
- // DT case
- availableSTag := options.BBSim.STag
- for i := 0; i < olt.NumPon; i++ {
- p := CreatePonPort(&olt, uint32(i))
-
- // create ONU devices
- for j := 0; j < olt.NumOnuPerPon; j++ {
- delay := time.Duration(olt.Delay*j) * time.Millisecond
- o := CreateONU(&olt, p, uint32(j+1), availableSTag, options.BBSim.CTag, options.BBSim.EnableAuth, options.BBSim.EnableDhcp, delay, isMock)
- p.Onus = append(p.Onus, o)
- availableSTag = availableSTag + 1
- }
-
- olt.Pons = append(olt.Pons, p)
- }
+ olt.Pons = append(olt.Pons, p)
}
if !isMock {
@@ -238,7 +256,7 @@
func (o *OltDevice) RestartOLT() error {
- rebootDelay := common.Options.Olt.OltRebootDelay
+ rebootDelay := common.Config.Olt.OltRebootDelay
oltLogger.WithFields(log.Fields{
"oltId": o.ID,
@@ -298,7 +316,7 @@
// newOltServer launches a new grpc server for OpenOLT
func (o *OltDevice) newOltServer() (*grpc.Server, error) {
- address := common.Options.BBSim.OpenOltAddress
+ address := common.Config.BBSim.OpenOltAddress
lis, err := net.Listen("tcp", address)
if err != nil {
oltLogger.Fatalf("OLT failed to listen: %v", err)
@@ -351,7 +369,7 @@
wg := sync.WaitGroup{}
wg.Add(3)
- o.OpenoltStream = &stream
+ o.OpenoltStream = stream
// create Go routine to process all OLT events
go o.processOltMessages(o.enableContext, stream, &wg)
@@ -444,7 +462,7 @@
"messageType": message.Type,
"OnuId": message.Data.OnuId,
"IntfId": message.Data.IntfId,
- }).Info("Received message on OMCI Sim channel")
+ }).Debug("Received message on OMCI Sim channel")
onuId := message.Data.OnuId
intfId := message.Data.IntfId
@@ -577,7 +595,7 @@
func (o *OltDevice) sendPonIndication(ponPortID uint32) {
- stream := *o.OpenoltStream
+ stream := o.OpenoltStream
pon, _ := o.GetPonById(ponPortID)
// Send IntfIndication for PON port
discoverData := &openolt.Indication_IntfInd{IntfInd: &openolt.IntfIndication{
@@ -624,7 +642,7 @@
data := &openolt.Indication_PortStats{
PortStats: stats,
}
- stream := *o.OpenoltStream
+ stream := o.OpenoltStream
if err := stream.Send(&openolt.Indication{Data: data}); err != nil {
oltLogger.Errorf("Failed to send PortStats: %v", err)
return
@@ -743,7 +761,7 @@
return
}
- onu, err := o.FindOnuByMacAddress(onuMac)
+ s, err := o.FindServiceByMacAddress(onuMac)
if err != nil {
log.WithFields(log.Fields{
"IntfType": "nni",
@@ -754,7 +772,9 @@
return
}
- doubleTaggedPkt, err := packetHandlers.PushDoubleTag(onu.STag, onu.CTag, message.Pkt)
+ service := s.(*Service)
+
+ doubleTaggedPkt, err := packetHandlers.PushDoubleTag(service.STag, service.CTag, message.Pkt)
if err != nil {
log.Error("Fail to add double tag to packet")
}
@@ -773,9 +793,9 @@
oltLogger.WithFields(log.Fields{
"IntfType": data.PktInd.IntfType,
"IntfId": nniId,
- "Pkt": doubleTaggedPkt.Data(),
- "OnuSn": onu.Sn(),
- }).Tracef("Sent PktInd indication")
+ "Pkt": hex.EncodeToString(doubleTaggedPkt.Data()),
+ "OnuSn": service.Onu.Sn(),
+ }).Trace("Sent PktInd indication (from NNI to VOLTHA)")
}
}
wg.Done()
@@ -815,19 +835,20 @@
return &Onu{}, fmt.Errorf("cannot-find-onu-by-id-%v-%v", intfId, onuId)
}
-// returns an ONU with a given Mac Address
-func (o *OltDevice) FindOnuByMacAddress(mac net.HardwareAddr) (*Onu, error) {
+// returns a Service with a given Mac Address
+func (o *OltDevice) FindServiceByMacAddress(mac net.HardwareAddr) (ServiceIf, error) {
// TODO this function can be a performance bottleneck when we have many ONUs,
// memoizing it will remove the bottleneck
for _, pon := range o.Pons {
for _, onu := range pon.Onus {
- if onu.HwAddress.String() == mac.String() {
- return onu, nil
+ s, err := onu.findServiceByMacAddress(mac)
+ if err == nil {
+ return s, nil
}
}
}
- return &Onu{}, fmt.Errorf("cannot-find-onu-by-mac-address-%s", mac)
+ return nil, fmt.Errorf("cannot-find-service-by-mac-address-%s", mac)
}
// GRPC Endpoints
@@ -963,7 +984,7 @@
OnuID: onu.ID,
OnuSN: onu.SerialNumber,
}
- onu.sendOnuIndication(onuIndication, *o.OpenoltStream)
+ onu.sendOnuIndication(onuIndication, o.OpenoltStream)
}
@@ -999,7 +1020,7 @@
OnuID: onu.ID,
OnuSN: onu.SerialNumber,
}
- onu.sendOnuIndication(onuIndication, *o.OpenoltStream)
+ onu.sendOnuIndication(onuIndication, o.OpenoltStream)
}
@@ -1174,11 +1195,11 @@
"PonPorts": o.NumPon,
}).Info("OLT receives GetDeviceInfo call from VOLTHA")
devinfo := new(openolt.DeviceInfo)
- devinfo.Vendor = common.Options.Olt.Vendor
- devinfo.Model = common.Options.Olt.Model
- devinfo.HardwareVersion = common.Options.Olt.HardwareVersion
- devinfo.FirmwareVersion = common.Options.Olt.FirmwareVersion
- devinfo.Technology = common.Options.Olt.Technology
+ devinfo.Vendor = common.Config.Olt.Vendor
+ devinfo.Model = common.Config.Olt.Model
+ devinfo.HardwareVersion = common.Config.Olt.HardwareVersion
+ devinfo.FirmwareVersion = common.Config.Olt.FirmwareVersion
+ devinfo.Technology = common.Config.Olt.Technology
devinfo.PonPorts = uint32(o.NumPon)
devinfo.OnuIdStart = 1
devinfo.OnuIdEnd = 255
@@ -1189,7 +1210,7 @@
devinfo.FlowIdStart = 1
devinfo.FlowIdEnd = 16383
devinfo.DeviceSerialNumber = o.SerialNumber
- devinfo.DeviceId = common.Options.Olt.DeviceId
+ devinfo.DeviceId = common.Config.Olt.DeviceId
return devinfo, nil
}
@@ -1254,20 +1275,34 @@
"IntfId": onu.PonPortID,
"OnuId": onu.ID,
"OnuSn": onu.Sn(),
- }).Tracef("Received OnuPacketOut")
+ }).Info("Received OnuPacketOut")
rawpkt := gopacket.NewPacket(onuPkt.Pkt, layers.LayerTypeEthernet, gopacket.Default)
pktType, _ := packetHandlers.IsEapolOrDhcp(rawpkt)
+ pktMac, err := packetHandlers.GetDstMacAddressFromPacket(rawpkt)
+
+ if err != nil {
+ log.WithFields(log.Fields{
+ "IntfId": onu.PonPortID,
+ "OnuId": onu.ID,
+ "OnuSn": onu.Sn(),
+ "Pkt": rawpkt.Data(),
+ }).Error("Can't find Dst MacAddress in packet, droppint it")
+ return new(openolt.Empty), nil
+ }
+
msg := Message{
Type: OnuPacketOut,
Data: OnuPacketMessage{
- IntfId: onuPkt.IntfId,
- OnuId: onuPkt.OnuId,
- Packet: rawpkt,
- Type: pktType,
+ IntfId: onuPkt.IntfId,
+ OnuId: onuPkt.OnuId,
+ Packet: rawpkt,
+ Type: pktType,
+ MacAddress: pktMac,
},
}
+
onu.Channel <- msg
return new(openolt.Empty), nil
diff --git a/internal/bbsim/devices/olt_test.go b/internal/bbsim/devices/olt_test.go
index 8aa0ae6..53f9f44 100644
--- a/internal/bbsim/devices/olt_test.go
+++ b/internal/bbsim/devices/olt_test.go
@@ -17,6 +17,7 @@
package devices
import (
+ "github.com/opencord/bbsim/internal/common"
"net"
"testing"
@@ -24,7 +25,7 @@
"gotest.tools/assert"
)
-func createMockOlt(numPon int, numOnu int) *OltDevice {
+func createMockOlt(numPon int, numOnu int, services []ServiceIf) *OltDevice {
olt := &OltDevice{
ID: 0,
}
@@ -40,8 +41,15 @@
ID: onuId,
PonPort: &pon,
PonPortID: pon.ID,
- HwAddress: net.HardwareAddr{0x2e, 0x60, 0x70, 0x13, byte(pon.ID), byte(onuId)},
}
+
+ for k, s := range services {
+ service := s.(*Service)
+ service.HwAddress = net.HardwareAddr{0x2e, 0x60, byte(olt.ID), byte(pon.ID), byte(onuId), byte(k)}
+ service.Onu = &onu
+ onu.Services = append(onu.Services, service)
+ }
+
onu.SerialNumber = onu.NewSN(olt.ID, pon.ID, onu.ID)
pon.Onus = append(pon.Onus, &onu)
}
@@ -50,12 +58,76 @@
return olt
}
+// check the creation of an OLT with a single Service
+func TestCreateOLT(t *testing.T) {
+
+ common.Services = []common.ServiceYaml{
+ {Name: "hsia", CTag: 900, CTagAllocation: common.TagAllocationUnique.String(), STag: 900, STagAllocation: common.TagAllocationShared.String(), NeedsEapol: true, NeedsDchp: true, NeedsIgmp: true},
+ }
+
+ common.Config = &common.GlobalConfig{
+ Olt: common.OltConfig{
+ ID: 1,
+ PonPorts: 2,
+ OnusPonPort: 2,
+ },
+ }
+
+ olt := CreateOLT(*common.Config, common.Services, true)
+
+ assert.Equal(t, len(olt.Pons), int(common.Config.Olt.PonPorts))
+
+ // count the ONUs
+ onus := 0
+ for _, p := range olt.Pons {
+ onus = onus + len(p.Onus)
+ }
+
+ assert.Equal(t, onus, int(common.Config.Olt.PonPorts*common.Config.Olt.OnusPonPort))
+
+ // count the services
+ services := 0
+ for _, p := range olt.Pons {
+ for _, o := range p.Onus {
+ services = services + len(o.Services)
+ }
+ }
+
+ assert.Equal(t, services, int(common.Config.Olt.PonPorts)*int(common.Config.Olt.OnusPonPort)*len(common.Services))
+
+ s1 := olt.Pons[0].Onus[0].Services[0].(*Service)
+
+ assert.Equal(t, s1.Name, "hsia")
+ assert.Equal(t, s1.CTag, 900)
+ assert.Equal(t, s1.STag, 900)
+ assert.Equal(t, s1.HwAddress.String(), "2e:60:01:00:01:00")
+ assert.Equal(t, olt.Pons[0].Onus[0].ID, uint32(1))
+
+ s2 := olt.Pons[0].Onus[1].Services[0].(*Service)
+ assert.Equal(t, s2.CTag, 901)
+ assert.Equal(t, s2.STag, 900)
+ assert.Equal(t, s2.HwAddress.String(), "2e:60:01:00:02:00")
+ assert.Equal(t, olt.Pons[0].Onus[1].ID, uint32(2))
+
+ s3 := olt.Pons[1].Onus[0].Services[0].(*Service)
+ assert.Equal(t, s3.CTag, 902)
+ assert.Equal(t, s3.STag, 900)
+ assert.Equal(t, s3.HwAddress.String(), "2e:60:01:01:01:00")
+ assert.Equal(t, olt.Pons[1].Onus[0].ID, uint32(1))
+
+ s4 := olt.Pons[1].Onus[1].Services[0].(*Service)
+ assert.Equal(t, s4.CTag, 903)
+ assert.Equal(t, s4.STag, 900)
+ assert.Equal(t, s4.HwAddress.String(), "2e:60:01:01:02:00")
+ assert.Equal(t, olt.Pons[1].Onus[1].ID, uint32(2))
+}
+
func Test_Olt_FindOnuBySn_Success(t *testing.T) {
numPon := 4
numOnu := 4
- olt := createMockOlt(numPon, numOnu)
+ olt := createMockOlt(numPon, numOnu, []ServiceIf{})
onu, err := olt.FindOnuBySn("BBSM00000303")
@@ -70,7 +142,7 @@
numPon := 1
numOnu := 4
- olt := createMockOlt(numPon, numOnu)
+ olt := createMockOlt(numPon, numOnu, []ServiceIf{})
_, err := olt.FindOnuBySn("BBSM00000303")
@@ -78,20 +150,30 @@
}
func Test_Olt_FindOnuByMacAddress_Success(t *testing.T) {
-
numPon := 4
numOnu := 4
- olt := createMockOlt(numPon, numOnu)
+ services := []ServiceIf{
+ &Service{Name: "hsia"},
+ &Service{Name: "voip"},
+ &Service{Name: "vod"},
+ }
- mac := net.HardwareAddr{0x2e, 0x60, 0x70, 0x13, byte(3), byte(3)}
+ olt := createMockOlt(numPon, numOnu, services)
- onu, err := olt.FindOnuByMacAddress(mac)
+ mac := net.HardwareAddr{0x2e, 0x60, byte(olt.ID), byte(3), byte(6), byte(1)}
+ s, err := olt.FindServiceByMacAddress(mac)
+
+ assert.NilError(t, err)
+
+ service := s.(*Service)
assert.Equal(t, err, nil)
- assert.Equal(t, onu.Sn(), "BBSM00000303")
- assert.Equal(t, onu.ID, uint32(3))
- assert.Equal(t, onu.PonPortID, uint32(3))
+ assert.Equal(t, service.Onu.Sn(), "BBSM00000306")
+ assert.Equal(t, service.Onu.ID, uint32(6))
+ assert.Equal(t, service.Onu.PonPortID, uint32(3))
+
+ assert.Equal(t, service.Name, "voip")
}
func Test_Olt_FindOnuByMacAddress_Error(t *testing.T) {
@@ -99,20 +181,20 @@
numPon := 1
numOnu := 4
- olt := createMockOlt(numPon, numOnu)
+ olt := createMockOlt(numPon, numOnu, []ServiceIf{})
mac := net.HardwareAddr{0x2e, 0x60, 0x70, 0x13, byte(3), byte(3)}
- _, err := olt.FindOnuByMacAddress(mac)
+ _, err := olt.FindServiceByMacAddress(mac)
- assert.Equal(t, err.Error(), "cannot-find-onu-by-mac-address-2e:60:70:13:03:03")
+ assert.Equal(t, err.Error(), "cannot-find-service-by-mac-address-2e:60:70:13:03:03")
}
func Test_Olt_GetOnuByFlowId(t *testing.T) {
numPon := 4
numOnu := 4
- olt := createMockOlt(numPon, numOnu)
+ olt := createMockOlt(numPon, numOnu, []ServiceIf{})
// Add the flows to onus (to be found)
onu1, _ := olt.FindOnuBySn("BBSM00000303")
diff --git a/internal/bbsim/devices/onu.go b/internal/bbsim/devices/onu.go
index 6c53b68..0f78107 100644
--- a/internal/bbsim/devices/onu.go
+++ b/internal/bbsim/devices/onu.go
@@ -18,8 +18,10 @@
import (
"context"
- "errors"
"fmt"
+ "github.com/opencord/bbsim/internal/bbsim/packetHandlers"
+ "github.com/opencord/bbsim/internal/bbsim/responders/dhcp"
+ "github.com/opencord/bbsim/internal/bbsim/responders/eapol"
"net"
"time"
@@ -28,10 +30,6 @@
"github.com/google/gopacket/layers"
"github.com/jpillora/backoff"
"github.com/looplab/fsm"
- "github.com/opencord/bbsim/internal/bbsim/packetHandlers"
- "github.com/opencord/bbsim/internal/bbsim/responders/dhcp"
- "github.com/opencord/bbsim/internal/bbsim/responders/eapol"
- "github.com/opencord/bbsim/internal/bbsim/responders/igmp"
"github.com/opencord/bbsim/internal/common"
omcilib "github.com/opencord/bbsim/internal/common/omci"
omcisim "github.com/opencord/omci-sim"
@@ -53,31 +51,26 @@
ID uint32
PonPortID uint32
PonPort *PonPort
- STag int
- CTag int
- Auth bool // automatically start EAPOL if set to true
- Dhcp bool // automatically start DHCP if set to true
- HwAddress net.HardwareAddr
InternalState *fsm.FSM
DiscoveryRetryDelay time.Duration // this is the time between subsequent Discovery Indication
DiscoveryDelay time.Duration // this is the time to send the first Discovery Indication
- Backoff *backoff.Backoff
+
+ Services []ServiceIf
+
+ Backoff *backoff.Backoff
// ONU State
// PortNo comes with flows and it's used when sending packetIndications,
// There is one PortNo per UNI Port, for now we're only storing the first one
// FIXME add support for multiple UNIs (each UNI has a different PortNo)
- PortNo uint32
- GemPortAdded bool
- EapolFlowReceived bool
- DhcpFlowReceived bool
- Flows []FlowKey
- FlowIds []uint32 // keep track of the flows we currently have in the ONU
+ PortNo uint32
+ GemPortAdded bool
+ Flows []FlowKey
+ FlowIds []uint32 // keep track of the flows we currently have in the ONU
OperState *fsm.FSM
SerialNumber *openolt.SerialNumber
- Channel chan Message // this Channel is to track state changes OMCI messages, EAPOL and DHCP packets
- GemPortChannels []chan bool // this channels are used to notify everyone that is interested that a GemPort has been added
+ Channel chan Message // this Channel is to track state changes OMCI messages, EAPOL and DHCP packets
// OMCI params
tid uint16
@@ -92,13 +85,7 @@
return common.OnuSnToString(o.SerialNumber)
}
-func (o *Onu) GetGemPortChan() chan bool {
- listener := make(chan bool, 1)
- o.GemPortChannels = append(o.GemPortChannels, listener)
- return listener
-}
-
-func CreateONU(olt *OltDevice, pon *PonPort, id uint32, sTag int, cTag int, auth bool, dhcp bool, delay time.Duration, isMock bool) *Onu {
+func CreateONU(olt *OltDevice, pon *PonPort, id uint32, delay time.Duration, isMock bool) *Onu {
b := &backoff.Backoff{
//These are the defaults
Min: 5 * time.Second,
@@ -108,21 +95,14 @@
}
o := Onu{
- ID: 0,
+ ID: id,
PonPortID: pon.ID,
PonPort: pon,
- STag: sTag,
- CTag: cTag,
- Auth: auth,
- Dhcp: dhcp,
- HwAddress: net.HardwareAddr{0x2e, 0x60, 0x70, byte(olt.ID), byte(pon.ID), byte(id)},
PortNo: 0,
tid: 0x1,
hpTid: 0x8000,
seqNumber: 0,
DoneChannel: make(chan bool, 1),
- DhcpFlowReceived: false,
- EapolFlowReceived: false,
GemPortAdded: false,
DiscoveryRetryDelay: 60 * time.Second, // this is used to send OnuDiscoveryIndications until an activate call is received
Flows: []FlowKey{},
@@ -152,28 +132,10 @@
{Name: "disable", Src: []string{"enabled", "eap_response_success_received", "auth_failed", "dhcp_ack_received", "dhcp_failed", "pon_disabled"}, Dst: "disabled"},
// ONU state when PON port is disabled but ONU is power ON(more states should be added in src?)
{Name: "pon_disabled", Src: []string{"enabled", "eap_response_success_received", "auth_failed", "dhcp_ack_received", "dhcp_failed"}, Dst: "pon_disabled"},
- // EAPOL
- {Name: "start_auth", Src: []string{"enabled", "eap_start_sent", "eap_response_identity_sent", "eap_response_challenge_sent", "eap_response_success_received", "auth_failed", "dhcp_ack_received", "dhcp_failed", "igmp_join_started", "igmp_left", "igmp_join_error"}, Dst: "auth_started"},
- {Name: "eap_start_sent", Src: []string{"auth_started"}, Dst: "eap_start_sent"},
- {Name: "eap_response_identity_sent", Src: []string{"eap_start_sent"}, Dst: "eap_response_identity_sent"},
- {Name: "eap_response_challenge_sent", Src: []string{"eap_response_identity_sent"}, Dst: "eap_response_challenge_sent"},
- {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{"enabled", "eap_response_success_received", "dhcp_discovery_sent", "dhcp_request_sent", "dhcp_ack_received", "dhcp_failed", "igmp_join_started", "igmp_left", "igmp_join_error"}, 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"},
- {Name: "dhcp_failed", Src: []string{"dhcp_started", "dhcp_discovery_sent", "dhcp_request_sent"}, Dst: "dhcp_failed"},
// BBR States
// TODO add start OMCI state
{Name: "send_eapol_flow", Src: []string{"initialized"}, Dst: "eapol_flow_sent"},
{Name: "send_dhcp_flow", Src: []string{"eapol_flow_sent"}, Dst: "dhcp_flow_sent"},
- // IGMP
- {Name: "igmp_join_start", Src: []string{"eap_response_success_received", "dhcp_ack_received", "igmp_left", "igmp_join_error", "igmp_join_started"}, Dst: "igmp_join_started"},
- {Name: "igmp_join_startv3", Src: []string{"eap_response_success_received", "dhcp_ack_received", "igmp_left", "igmp_join_error", "igmp_join_started"}, Dst: "igmp_join_started"},
- {Name: "igmp_join_error", Src: []string{"igmp_join_started"}, Dst: "igmp_join_error"},
- {Name: "igmp_leave", Src: []string{"igmp_join_started", "eap_response_success_received", "dhcp_ack_received"}, Dst: "igmp_left"},
},
fsm.Callbacks{
"enter_state": func(e *fsm.Event) {
@@ -193,7 +155,7 @@
if !isMock {
// start ProcessOnuMessages Go routine
- go o.ProcessOnuMessages(olt.enableContext, *olt.OpenoltStream, nil)
+ go o.ProcessOnuMessages(olt.enableContext, olt.OpenoltStream, nil)
}
},
"enter_discovered": func(e *fsm.Event) {
@@ -220,8 +182,6 @@
"enter_disabled": func(event *fsm.Event) {
// clean the ONU state
- o.DhcpFlowReceived = false
- o.EapolFlowReceived = false
o.GemPortAdded = false
o.PortNo = 0
o.Flows = []FlowKey{}
@@ -252,75 +212,7 @@
close(o.Channel)
}
},
- "before_start_auth": func(e *fsm.Event) {
- if !o.EapolFlowReceived {
- e.Cancel(errors.New("cannot-go-to-auth-started-as-eapol-flow-is-missing"))
- return
- }
- if !o.GemPortAdded {
- e.Cancel(errors.New("cannot-go-to-auth-started-as-gemport-is-missing"))
- return
- }
- },
- "enter_auth_started": func(e *fsm.Event) {
- o.logStateChange(e.Src, e.Dst)
- msg := Message{
- Type: StartEAPOL,
- Data: PacketMessage{
- PonPortID: o.PonPortID,
- OnuID: o.ID,
- },
- }
- o.Channel <- msg
- },
- "enter_eap_response_success_received": func(e *fsm.Event) {
- publishEvent("ONU-authentication-done", int32(o.PonPortID), int32(o.ID), o.Sn())
- },
- "enter_auth_failed": func(e *fsm.Event) {
- onuLogger.WithFields(log.Fields{
- "OnuId": o.ID,
- "IntfId": o.PonPortID,
- "OnuSn": o.Sn(),
- }).Errorf("ONU failed to authenticate!")
- },
- "before_start_dhcp": func(e *fsm.Event) {
-
- // we allow transition from eanbled to dhcp_started only if auth was set to false
- if o.InternalState.Current() == "enabled" && o.Auth {
- e.Cancel(errors.New("cannot-go-to-dhcp-started-as-authentication-is-required"))
- return
- }
-
- if !o.DhcpFlowReceived {
- e.Cancel(errors.New("cannot-go-to-dhcp-started-as-dhcp-flow-is-missing"))
- return
- }
-
- if !o.GemPortAdded {
- e.Cancel(errors.New("cannot-go-to-dhcp-started-as-gemport-is-missing"))
- return
- }
- },
- "enter_dhcp_started": func(e *fsm.Event) {
- msg := Message{
- Type: StartDHCP,
- Data: PacketMessage{
- PonPortID: o.PonPortID,
- OnuID: o.ID,
- },
- }
- o.Channel <- msg
- },
- "enter_dhcp_ack_received": func(e *fsm.Event) {
- publishEvent("ONU-DHCP-ACK-received", int32(o.PonPortID), int32(o.ID), o.Sn())
- },
- "enter_dhcp_failed": func(e *fsm.Event) {
- onuLogger.WithFields(log.Fields{
- "OnuId": o.ID,
- "IntfId": o.PonPortID,
- "OnuSn": o.Sn(),
- }).Errorf("ONU failed to DHCP!")
- },
+ // BBR states
"enter_eapol_flow_sent": func(e *fsm.Event) {
msg := Message{
Type: SendEapolFlow,
@@ -413,10 +305,6 @@
case FlowRemoved:
msg, _ := message.Data.(OnuFlowUpdateMessage)
o.handleFlowRemove(msg)
- case StartEAPOL:
- o.handleEAPOLStart(stream)
- case StartDHCP:
- o.handleDHCPStart(stream)
case OnuPacketOut:
msg, _ := message.Data.(OnuPacketMessage)
@@ -427,13 +315,20 @@
"pktType": msg.Type,
}).Trace("Received OnuPacketOut Message")
- if msg.Type == packetHandlers.EAPOL {
- eapol.HandleNextPacket(msg.OnuId, msg.IntfId, o.Sn(), o.PortNo, o.InternalState, msg.Packet, stream, client)
- } else if msg.Type == packetHandlers.DHCP {
- // 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
- _ = dhcp.HandleNextPacket(o.PonPort.Olt.ID, o.ID, o.PonPortID, o.Sn(), o.PortNo, o.HwAddress, o.CTag, o.InternalState, msg.Packet, stream)
+ service, err := o.findServiceByMacAddress(msg.MacAddress)
+ if err != nil {
+ onuLogger.WithFields(log.Fields{
+ "IntfId": msg.IntfId,
+ "OnuId": msg.OnuId,
+ "pktType": msg.Type,
+ "MacAddress": msg.MacAddress,
+ "OnuSn": o.Sn(),
+ }).Error("Cannot find Service associated with packet")
+ return
}
+
+ service.PacketCh <- msg
+
case OnuPacketIn:
// NOTE we only receive BBR packets here.
// Eapol.HandleNextPacket can handle both BBSim and BBr cases so the call is the same
@@ -447,9 +342,9 @@
}).Trace("Received OnuPacketIn Message")
if msg.Type == packetHandlers.EAPOL {
- eapol.HandleNextPacket(msg.OnuId, msg.IntfId, o.Sn(), o.PortNo, o.InternalState, msg.Packet, stream, client)
+ eapol.HandleNextPacket(msg.OnuId, msg.IntfId, msg.GemPortId, o.Sn(), o.PortNo, o.InternalState, msg.Packet, stream, client)
} else if msg.Type == packetHandlers.DHCP {
- _ = dhcp.HandleNextBbrPacket(o.ID, o.PonPortID, o.Sn(), o.STag, o.HwAddress, o.DoneChannel, msg.Packet, client)
+ _ = dhcp.HandleNextBbrPacket(o.ID, o.PonPortID, o.Sn(), o.DoneChannel, msg.Packet, client)
}
case OmciIndication:
msg, _ := message.Data.(OmciIndicationMessage)
@@ -458,15 +353,6 @@
o.sendEapolFlow(client)
case SendDhcpFlow:
o.sendDhcpFlow(client)
- case IGMPMembershipReportV2:
- log.Infof("Recieved IGMPMembershipReportV2 message on ONU channel")
- _ = igmp.SendIGMPMembershipReportV2(o.PonPortID, o.ID, o.Sn(), o.PortNo, o.HwAddress, stream)
- case IGMPLeaveGroup:
- log.Infof("Recieved IGMPLeaveGroupV2 message on ONU channel")
- _ = igmp.SendIGMPLeaveGroupV2(o.PonPortID, o.ID, o.Sn(), o.PortNo, o.HwAddress, stream)
- case IGMPMembershipReportV3:
- log.Infof("Recieved IGMPMembershipReportV3 message on ONU channel")
- _ = igmp.SendIGMPMembershipReportV3(o.PonPortID, o.ID, o.Sn(), o.PortNo, o.HwAddress, stream)
default:
onuLogger.Warnf("Received unknown message data %v for type %v in OLT Channel", message.Data, message.Type)
}
@@ -485,7 +371,7 @@
"OnuId": message.Data.OnuId,
"IntfId": message.Data.IntfId,
"Type": message.Type,
- }).Infof("UNI Link Alarm")
+ }).Debug("UNI Link Alarm")
// TODO send to OLT
omciInd := openolt.OmciIndication{
@@ -510,60 +396,10 @@
"SerialNumber": o.Sn(),
"Type": message.Type,
"omciPacket": omciInd.Pkt,
- }).Info("UNI Link alarm sent")
-
- case omcisim.GemPortAdded:
- log.WithFields(log.Fields{
- "OnuId": message.Data.OnuId,
- "IntfId": message.Data.IntfId,
- "OnuSn": o.Sn(),
- }).Infof("GemPort Added")
-
- o.GemPortAdded = true
-
- // broadcast the change to all listeners
- // and close the channels as once the GemPort is set
- // it won't change anymore
- for _, ch := range o.GemPortChannels {
- ch <- true
- close(ch)
- }
- o.GemPortChannels = []chan bool{}
+ }).Debug("UNI Link alarm sent")
}
}
-func (o *Onu) handleEAPOLStart(stream openolt.Openolt_EnableIndicationServer) {
- log.Infof("Receive StartEAPOL message on ONU Channel")
- _ = eapol.SendEapStart(o.ID, o.PonPortID, o.Sn(), o.PortNo, o.HwAddress, o.InternalState, stream)
- go func(delay time.Duration) {
- time.Sleep(delay)
- if (o.InternalState.Current() == "eap_start_sent" ||
- o.InternalState.Current() == "eap_response_identity_sent" ||
- o.InternalState.Current() == "eap_response_challenge_sent" ||
- o.InternalState.Current() == "auth_failed") && common.Options.BBSim.AuthRetry {
- _ = o.InternalState.Event("start_auth")
- } else if o.InternalState.Current() == "eap_response_success_received" {
- o.Backoff.Reset()
- }
- }(o.Backoff.Duration())
-}
-
-func (o *Onu) handleDHCPStart(stream openolt.Openolt_EnableIndicationServer) {
- log.Infof("Receive StartDHCP message on ONU Channel")
- // FIXME use id, ponId as SendEapStart
- _ = dhcp.SendDHCPDiscovery(o.PonPort.Olt.ID, o.PonPortID, o.ID, o.Sn(), o.PortNo, o.InternalState, o.HwAddress, o.CTag, stream)
- go func(delay time.Duration) {
- time.Sleep(delay)
- if (o.InternalState.Current() == "dhcp_discovery_sent" ||
- o.InternalState.Current() == "dhcp_request_sent" ||
- o.InternalState.Current() == "dhcp_failed") && common.Options.BBSim.DhcpRetry {
- _ = o.InternalState.Event("start_dhcp")
- } else if o.InternalState.Current() == "dhcp_ack_received" {
- o.Backoff.Reset()
- }
- }(o.Backoff.Duration())
-}
-
func (o Onu) NewSN(oltid int, intfid uint32, onuid uint32) *openolt.SerialNumber {
sn := new(openolt.SerialNumber)
@@ -627,6 +463,10 @@
"OnuSn": o.Sn(),
}).Debug("Sent Indication_OnuInd")
+ for _, s := range o.Services {
+ go s.HandlePackets(stream)
+ }
+
}
func (o *Onu) publishOmciEvent(msg OmciMessage) {
@@ -790,83 +630,22 @@
}
o.FlowIds = append(o.FlowIds, msg.Flow.FlowId)
+ o.addGemPortToService(uint32(msg.Flow.GemportId), msg.Flow.Classifier.EthType, msg.Flow.Classifier.OVid, msg.Flow.Classifier.IVid)
if msg.Flow.Classifier.EthType == uint32(layers.EthernetTypeEAPOL) && msg.Flow.Classifier.OVid == 4091 {
// NOTE storing the PortNO, it's needed when sending PacketIndications
o.storePortNumber(uint32(msg.Flow.PortNo))
- o.EapolFlowReceived = true
- // if authentication is not enabled, do nothing
- if o.Auth {
- // NOTE if we receive the EAPOL flows but we don't have GemPorts
- // wait for it before starting auth
- if !o.GemPortAdded {
- // wait for Gem and then start auth
- go func() {
- for v := range o.GetGemPortChan() {
- if v {
- if err := o.InternalState.Event("start_auth"); err != nil {
- onuLogger.Warnf("Can't go to auth_started: %v", err)
- }
- }
- }
- onuLogger.Trace("GemPortChannel closed")
- }()
- } else {
- // start the EAPOL state machine
- if err := o.InternalState.Event("start_auth"); err != nil {
- onuLogger.Warnf("Can't go to auth_started: %v", err)
- }
- }
- } else {
- onuLogger.WithFields(log.Fields{
- "IntfId": o.PonPortID,
- "OnuId": o.ID,
- "SerialNumber": o.Sn(),
- }).Warn("Not starting authentication as Auth bit is not set in CLI parameters")
+
+ for _, s := range o.Services {
+ s.HandleAuth(o.PonPort.Olt.OpenoltStream)
}
} else if msg.Flow.Classifier.EthType == uint32(layers.EthernetTypeIPv4) &&
msg.Flow.Classifier.SrcPort == uint32(68) &&
msg.Flow.Classifier.DstPort == uint32(67) &&
(msg.Flow.Classifier.OPbits == 0 || msg.Flow.Classifier.OPbits == 255) {
- if o.Dhcp {
- if !o.DhcpFlowReceived {
- // keep track that we received the DHCP Flows
- // so that we can transition the state to dhcp_started
- // this is needed as a check in case someone trigger DHCP from the CLI
- o.DhcpFlowReceived = true
-
- if !o.GemPortAdded {
- // wait for Gem and then start DHCP
- go func() {
- for v := range o.GetGemPortChan() {
- if v {
- if err := o.InternalState.Event("start_dhcp"); err != nil {
- log.Errorf("Can't go to dhcp_started: %v", err)
- }
- }
- }
- }()
- } else {
- // start the DHCP state machine
- if err := o.InternalState.Event("start_dhcp"); err != nil {
- log.Errorf("Can't go to dhcp_started: %v", err)
- }
- }
- } else {
- onuLogger.WithFields(log.Fields{
- "IntfId": o.PonPortID,
- "OnuId": o.ID,
- "SerialNumber": o.Sn(),
- "DhcpFlowReceived": o.DhcpFlowReceived,
- }).Warn("DHCP already started")
- }
- } else {
- onuLogger.WithFields(log.Fields{
- "IntfId": o.PonPortID,
- "OnuId": o.ID,
- "SerialNumber": o.Sn(),
- }).Warn("Not starting DHCP as Dhcp bit is not set in CLI parameters")
+ for _, s := range o.Services {
+ s.HandleDhcp(o.PonPort.Olt.OpenoltStream, int(msg.Flow.Classifier.OVid))
}
}
}
@@ -896,11 +675,6 @@
}).Info("Resetting GemPort")
o.GemPortAdded = false
- // TODO ideally we should keep track of the flow type (and not only the ID)
- // so that we can properly set these two flag when the flow is removed
- o.EapolFlowReceived = false
- o.DhcpFlowReceived = false
-
// check if ONU delete is performed and
// terminate the ONU's ProcessOnuMessages Go routine
if o.InternalState.Current() == "disabled" {
@@ -1075,10 +849,15 @@
}
func (o *Onu) sendDhcpFlow(client openolt.OpenoltClient) {
+
+ // BBR only works with a single service (ATT HSIA)
+ hsia := o.Services[0].(*Service)
+
classifierProto := openolt.Classifier{
EthType: uint32(layers.EthernetTypeIPv4),
SrcPort: uint32(68),
DstPort: uint32(67),
+ OVid: uint32(hsia.CTag),
}
actionProto := openolt.Action{}
@@ -1158,3 +937,36 @@
}).Infof("Failed to transition ONU to discovered state: %s", err.Error())
}
}
+
+func (onu *Onu) addGemPortToService(gemport uint32, ethType uint32, oVlan uint32, iVlan uint32) {
+ for _, s := range onu.Services {
+ if service, ok := s.(*Service); ok {
+ // EAPOL is a strange case, as packets are untagged
+ // but we assume we will have a single service requiring EAPOL
+ if ethType == uint32(layers.EthernetTypeEAPOL) && service.NeedsEapol {
+ service.GemPort = gemport
+ }
+
+ // For DHCP services we single tag the outgoing packets,
+ // thus the flow only contains the CTag and we can use that to match the service
+ if ethType == uint32(layers.EthernetTypeIPv4) && service.NeedsDhcp && service.CTag == int(oVlan) {
+ service.GemPort = gemport
+ }
+
+ // for dataplane services match both C and S tags
+ if service.CTag == int(iVlan) && service.STag == int(oVlan) {
+ service.GemPort = gemport
+ }
+ }
+ }
+}
+
+func (onu *Onu) findServiceByMacAddress(macAddress net.HardwareAddr) (*Service, error) {
+ for _, s := range onu.Services {
+ service := s.(*Service)
+ if service.HwAddress.String() == macAddress.String() {
+ return service, nil
+ }
+ }
+ return nil, fmt.Errorf("cannot-find-service-with-mac-address-%s", macAddress.String())
+}
diff --git a/internal/bbsim/devices/onu_flow_test.go b/internal/bbsim/devices/onu_flow_test.go
index 4ff33a7..3f0d702 100644
--- a/internal/bbsim/devices/onu_flow_test.go
+++ b/internal/bbsim/devices/onu_flow_test.go
@@ -21,13 +21,12 @@
"github.com/looplab/fsm"
"github.com/opencord/voltha-protos/v2/go/openolt"
"gotest.tools/assert"
- "sync"
"testing"
- "time"
)
+// test that BBR correctly sends the EAPOL Flow
func Test_Onu_SendEapolFlow(t *testing.T) {
- onu := createMockOnu(1, 1, 900, 900, false, false)
+ onu := createMockOnu(1, 1)
client := &mockClient{
FlowAddSpy: FlowAddSpy{
@@ -49,7 +48,7 @@
// checks that the FlowId is added to the list
func Test_HandleFlowAddFlowId(t *testing.T) {
- onu := createMockOnu(1, 1, 900, 900, true, false)
+ onu := createMockOnu(1, 1)
flow := openolt.Flow{
FlowId: 64,
@@ -65,12 +64,110 @@
assert.Equal(t, onu.FlowIds[0], uint32(64))
}
+// checks that we only remove the correct flow
+func Test_HandleFlowRemoveFlowId(t *testing.T) {
+ onu := createMockOnu(1, 1)
+
+ onu.FlowIds = []uint32{1, 2, 34, 64, 92}
+
+ flow := openolt.Flow{
+ FlowId: 64,
+ Classifier: &openolt.Classifier{},
+ }
+ msg := OnuFlowUpdateMessage{
+ OnuID: onu.ID,
+ PonPortID: onu.PonPortID,
+ Flow: &flow,
+ }
+ onu.handleFlowRemove(msg)
+ assert.Equal(t, len(onu.FlowIds), 4)
+ assert.Equal(t, onu.FlowIds[0], uint32(1))
+ assert.Equal(t, onu.FlowIds[1], uint32(2))
+ assert.Equal(t, onu.FlowIds[2], uint32(34))
+ assert.Equal(t, onu.FlowIds[3], uint32(92))
+}
+
+// checks that when the last flow is removed we reset the stored flags in the ONU
+func Test_HandleFlowRemoveFlowId_LastFlow(t *testing.T) {
+ onu := createMockOnu(1, 1)
+
+ onu.InternalState = fsm.NewFSM(
+ "enabled",
+ fsm.Events{
+ {Name: "disable", Src: []string{"enabled"}, Dst: "disabled"},
+ },
+ fsm.Callbacks{},
+ )
+
+ onu.GemPortAdded = true
+
+ onu.FlowIds = []uint32{64}
+
+ flow := openolt.Flow{
+ FlowId: 64,
+ Classifier: &openolt.Classifier{},
+ }
+ msg := OnuFlowUpdateMessage{
+ OnuID: onu.ID,
+ PonPortID: onu.PonPortID,
+ Flow: &flow,
+ }
+ onu.handleFlowRemove(msg)
+ assert.Equal(t, len(onu.FlowIds), 0)
+ assert.Equal(t, onu.GemPortAdded, false)
+}
+
+func TestOnu_HhandleEAPOLStart(t *testing.T) {
+ onu := createMockOnu(1, 1)
+ hsia := mockService{Name: "hsia"}
+ voip := mockService{Name: "voip"}
+
+ onu.Services = []ServiceIf{&hsia, &voip}
+
+ stream := mockStream{
+ Calls: make(map[int]*openolt.Indication),
+ }
+
+ onu.PonPort.Olt.OpenoltStream = &stream
+
+ flow := openolt.Flow{
+ AccessIntfId: int32(onu.PonPortID),
+ OnuId: int32(onu.ID),
+ UniId: int32(0),
+ FlowId: uint32(onu.ID),
+ FlowType: "downstream",
+ AllocId: int32(0),
+ NetworkIntfId: int32(0),
+ Classifier: &openolt.Classifier{
+ EthType: uint32(layers.EthernetTypeEAPOL),
+ OVid: 4091,
+ },
+ Action: &openolt.Action{},
+ Priority: int32(100),
+ PortNo: uint32(onu.ID), // NOTE we are using this to map an incoming packetIndication to an ONU
+ }
+
+ msg := OnuFlowUpdateMessage{
+ PonPortID: 1,
+ OnuID: 1,
+ Flow: &flow,
+ }
+
+ onu.handleFlowAdd(msg)
+
+ // check that we call HandleAuth on all the services
+ assert.Equal(t, hsia.HandleAuthCallCount, 1)
+ assert.Equal(t, voip.HandleAuthCallCount, 1)
+}
+
+// TODO all the following tests needs to be moved in the Service model
+
// validates that when an ONU receives an EAPOL flow for UNI 0
// and the GemPort has already been configured
// it transition to auth_started state
func Test_HandleFlowAddEapolWithGem(t *testing.T) {
-
- onu := createMockOnu(1, 1, 900, 900, true, false)
+ t.Skip("Needs to be moved in the Service struct")
+ onu := createMockOnu(1, 1)
onu.InternalState = fsm.NewFSM(
"enabled",
@@ -110,8 +207,8 @@
// validates that when an ONU receives an EAPOL flow for UNI that is not 0
// no action is taken (this is independent of GemPort status
func Test_HandleFlowAddEapolWrongUNI(t *testing.T) {
-
- onu := createMockOnu(1, 1, 900, 900, true, false)
+ t.Skip("Needs to be moved in the Service struct")
+ onu := createMockOnu(1, 1)
onu.InternalState = fsm.NewFSM(
"enabled",
@@ -148,110 +245,12 @@
assert.Equal(t, onu.InternalState.Current(), "enabled")
}
-// validates that when an ONU receives an EAPOL flow for UNI 0
-// and the GemPort has not yet been configured
-// it transition to auth_started state
-func Test_HandleFlowAddEapolWithoutGem(t *testing.T) {
-
- onu := createMockOnu(1, 1, 900, 900, true, false)
- onu.GemPortAdded = false
-
- onu.InternalState = fsm.NewFSM(
- "enabled",
- fsm.Events{
- {Name: "start_auth", Src: []string{"enabled"}, Dst: "auth_started"},
- },
- fsm.Callbacks{},
- )
-
- flow := openolt.Flow{
- AccessIntfId: int32(onu.PonPortID),
- OnuId: int32(onu.ID),
- UniId: int32(0),
- FlowId: uint32(onu.ID),
- FlowType: "downstream",
- AllocId: int32(0),
- NetworkIntfId: int32(0),
- Classifier: &openolt.Classifier{
- EthType: uint32(layers.EthernetTypeEAPOL),
- OVid: 4091,
- },
- Action: &openolt.Action{},
- Priority: int32(100),
- PortNo: uint32(onu.ID), // NOTE we are using this to map an incoming packetIndication to an ONU
- }
-
- msg := OnuFlowUpdateMessage{
- PonPortID: 1,
- OnuID: 1,
- Flow: &flow,
- }
-
- onu.handleFlowAdd(msg)
-
- wg := sync.WaitGroup{}
- wg.Add(1)
- go func(wg *sync.WaitGroup) {
- defer wg.Done()
- time.Sleep(100 * time.Millisecond)
-
- // emulate the addition of a GemPort
- for _, ch := range onu.GemPortChannels {
- ch <- true
- }
-
- time.Sleep(100 * time.Millisecond)
- assert.Equal(t, onu.InternalState.Current(), "auth_started")
- }(&wg)
- wg.Wait()
-
-}
-
-// validates that when an ONU receives an EAPOL flow for UNI 0
-// but the noAuth bit is set no action is taken
-func Test_HandleFlowAddEapolNoAuth(t *testing.T) {
- onu := createMockOnu(1, 1, 900, 900, false, false)
-
- onu.InternalState = fsm.NewFSM(
- "enabled",
- fsm.Events{
- {Name: "start_auth", Src: []string{"enabled"}, Dst: "auth_started"},
- },
- fsm.Callbacks{},
- )
-
- flow := openolt.Flow{
- AccessIntfId: int32(onu.PonPortID),
- OnuId: int32(onu.ID),
- UniId: int32(0),
- FlowId: uint32(onu.ID),
- FlowType: "downstream",
- AllocId: int32(0),
- NetworkIntfId: int32(0),
- Classifier: &openolt.Classifier{
- EthType: uint32(layers.EthernetTypeEAPOL),
- OVid: 4091,
- },
- Action: &openolt.Action{},
- Priority: int32(100),
- PortNo: uint32(onu.ID), // NOTE we are using this to map an incoming packetIndication to an ONU
- }
-
- msg := OnuFlowUpdateMessage{
- PonPortID: 1,
- OnuID: 1,
- Flow: &flow,
- }
-
- onu.handleFlowAdd(msg)
- assert.Equal(t, onu.InternalState.Current(), "enabled")
-}
-
// validates that when an ONU receives a DHCP flow for UNI 0 and pbit 0
// and the GemPort has already been configured
// it transition to dhcp_started state
func Test_HandleFlowAddDhcp(t *testing.T) {
- onu := createMockOnu(1, 1, 900, 900, false, true)
+ t.Skip("Needs to be moved in the Service struct")
+ onu := createMockOnu(1, 1)
onu.InternalState = fsm.NewFSM(
"eap_response_success_received",
@@ -288,14 +287,14 @@
onu.handleFlowAdd(msg)
assert.Equal(t, onu.InternalState.Current(), "dhcp_started")
- assert.Equal(t, onu.DhcpFlowReceived, true)
}
// validates that when an ONU receives a DHCP flow for UNI 0 and pbit 255
// and the GemPort has already been configured
// it transition to dhcp_started state
func Test_HandleFlowAddDhcpPBit255(t *testing.T) {
- onu := createMockOnu(1, 1, 900, 900, false, true)
+ t.Skip("Needs to be moved in the Service struct")
+ onu := createMockOnu(1, 1)
onu.InternalState = fsm.NewFSM(
"eap_response_success_received",
@@ -332,14 +331,14 @@
onu.handleFlowAdd(msg)
assert.Equal(t, onu.InternalState.Current(), "dhcp_started")
- assert.Equal(t, onu.DhcpFlowReceived, true)
}
// validates that when an ONU receives a DHCP flow for UNI 0 and pbit not 0 or 255
// and the GemPort has already been configured
// it ignores the message
func Test_HandleFlowAddDhcpIgnoreByPbit(t *testing.T) {
- onu := createMockOnu(1, 1, 900, 900, false, true)
+ t.Skip("Needs to be moved in the Service struct")
+ onu := createMockOnu(1, 1)
onu.InternalState = fsm.NewFSM(
"eap_response_success_received",
@@ -376,13 +375,13 @@
onu.handleFlowAdd(msg)
assert.Equal(t, onu.InternalState.Current(), "eap_response_success_received")
- assert.Equal(t, onu.DhcpFlowReceived, false)
}
// validates that when an ONU receives a DHCP flow for UNI 0
// but the noDchp bit is set no action is taken
func Test_HandleFlowAddDhcpNoDhcp(t *testing.T) {
- onu := createMockOnu(1, 1, 900, 900, false, false)
+ t.Skip("Needs to be moved in the Service struct")
+ onu := createMockOnu(1, 1)
onu.InternalState = fsm.NewFSM(
"eap_response_success_received",
@@ -418,16 +417,16 @@
onu.handleFlowAdd(msg)
assert.Equal(t, onu.InternalState.Current(), "eap_response_success_received")
- assert.Equal(t, onu.DhcpFlowReceived, false)
}
// validates that when an ONU receives a DHCP flow for UNI 0 and pbit not 0 or 255
// and the GemPort has not already been configured
// it transition to dhcp_started state
func Test_HandleFlowAddDhcpWithoutGem(t *testing.T) {
+ t.Skip("Needs to be moved in the Service struct")
// NOTE that this feature is required as there is no guarantee that the gemport is the same
// one we received with the EAPOL flow
- onu := createMockOnu(1, 1, 900, 900, false, true)
+ onu := createMockOnu(1, 1)
onu.GemPortAdded = false
@@ -465,78 +464,4 @@
}
onu.handleFlowAdd(msg)
-
- wg := sync.WaitGroup{}
- wg.Add(1)
- go func(wg *sync.WaitGroup) {
- defer wg.Done()
- time.Sleep(100 * time.Millisecond)
-
- // emulate the addition of a GemPort
- for _, ch := range onu.GemPortChannels {
- ch <- true
- }
-
- time.Sleep(100 * time.Millisecond)
- assert.Equal(t, onu.InternalState.Current(), "dhcp_started")
- assert.Equal(t, onu.DhcpFlowReceived, true)
- }(&wg)
- wg.Wait()
-}
-
-// checks that we only remove the correct flow
-func Test_HandleFlowRemoveFlowId(t *testing.T) {
- onu := createMockOnu(1, 1, 900, 900, true, false)
-
- onu.FlowIds = []uint32{1, 2, 34, 64, 92}
-
- flow := openolt.Flow{
- FlowId: 64,
- Classifier: &openolt.Classifier{},
- }
- msg := OnuFlowUpdateMessage{
- OnuID: onu.ID,
- PonPortID: onu.PonPortID,
- Flow: &flow,
- }
- onu.handleFlowRemove(msg)
- assert.Equal(t, len(onu.FlowIds), 4)
- assert.Equal(t, onu.FlowIds[0], uint32(1))
- assert.Equal(t, onu.FlowIds[1], uint32(2))
- assert.Equal(t, onu.FlowIds[2], uint32(34))
- assert.Equal(t, onu.FlowIds[3], uint32(92))
-}
-
-// checks that when the last flow is removed we reset the stored flags in the ONU
-func Test_HandleFlowRemoveFlowId_LastFlow(t *testing.T) {
- onu := createMockOnu(1, 1, 900, 900, true, false)
-
- onu.InternalState = fsm.NewFSM(
- "enabled",
- fsm.Events{
- {Name: "disable", Src: []string{"enabled"}, Dst: "disabled"},
- },
- fsm.Callbacks{},
- )
-
- onu.GemPortAdded = true
- onu.DhcpFlowReceived = true
- onu.EapolFlowReceived = true
-
- onu.FlowIds = []uint32{64}
-
- flow := openolt.Flow{
- FlowId: 64,
- Classifier: &openolt.Classifier{},
- }
- msg := OnuFlowUpdateMessage{
- OnuID: onu.ID,
- PonPortID: onu.PonPortID,
- Flow: &flow,
- }
- onu.handleFlowRemove(msg)
- assert.Equal(t, len(onu.FlowIds), 0)
- assert.Equal(t, onu.GemPortAdded, false)
- assert.Equal(t, onu.DhcpFlowReceived, false)
- assert.Equal(t, onu.EapolFlowReceived, false)
}
diff --git a/internal/bbsim/devices/onu_indications_test.go b/internal/bbsim/devices/onu_indications_test.go
index 433b776..76c7a7d 100644
--- a/internal/bbsim/devices/onu_indications_test.go
+++ b/internal/bbsim/devices/onu_indications_test.go
@@ -30,7 +30,7 @@
type mockStream struct {
grpc.ServerStream
CallCount int
- Calls map[int]*openolt.OnuDiscIndication
+ Calls map[int]*openolt.Indication
channel chan int
fail bool
}
@@ -40,8 +40,10 @@
if s.fail {
return errors.New("fake-error")
}
- s.Calls[s.CallCount] = ind.GetOnuDiscInd()
- s.channel <- s.CallCount
+ s.Calls[s.CallCount] = ind
+ go func() {
+ s.channel <- s.CallCount
+ }()
return nil
}
@@ -50,7 +52,7 @@
onu := createTestOnu()
stream := &mockStream{
CallCount: 0,
- Calls: make(map[int]*openolt.OnuDiscIndication),
+ Calls: make(map[int]*openolt.Indication),
fail: false,
channel: make(chan int, 10),
}
@@ -62,9 +64,10 @@
select {
default:
case <-time.After(90 * time.Millisecond):
+ call := stream.Calls[1].GetOnuDiscInd()
assert.Equal(t, stream.CallCount, 1)
- assert.Equal(t, stream.Calls[1].IntfId, onu.PonPortID)
- assert.Equal(t, stream.Calls[1].SerialNumber, onu.SerialNumber)
+ assert.Equal(t, call.IntfId, onu.PonPortID)
+ assert.Equal(t, call.SerialNumber, onu.SerialNumber)
}
cancel()
}
@@ -74,7 +77,7 @@
onu := createTestOnu()
stream := &mockStream{
CallCount: 0,
- Calls: make(map[int]*openolt.OnuDiscIndication),
+ Calls: make(map[int]*openolt.Indication),
fail: false,
channel: make(chan int, 10),
}
@@ -97,7 +100,7 @@
onu.DiscoveryRetryDelay = 500 * time.Millisecond
stream := &mockStream{
CallCount: 0,
- Calls: make(map[int]*openolt.OnuDiscIndication),
+ Calls: make(map[int]*openolt.Indication),
fail: false,
channel: make(chan int, 10),
}
diff --git a/internal/bbsim/devices/onu_state_machine_test.go b/internal/bbsim/devices/onu_state_machine_test.go
index fdecc3f..b48b286 100644
--- a/internal/bbsim/devices/onu_state_machine_test.go
+++ b/internal/bbsim/devices/onu_state_machine_test.go
@@ -37,8 +37,6 @@
assert.Equal(t, onu.InternalState.Current(), "enabled")
onu.PortNo = 16
- onu.DhcpFlowReceived = true
- onu.EapolFlowReceived = true
onu.GemPortAdded = true
onu.Flows = []FlowKey{
{ID: 1, Direction: "upstream"},
@@ -48,8 +46,6 @@
_ = onu.InternalState.Event("disable")
assert.Equal(t, onu.InternalState.Current(), "disabled")
- assert.Equal(t, onu.DhcpFlowReceived, false)
- assert.Equal(t, onu.EapolFlowReceived, false)
assert.Equal(t, onu.GemPortAdded, false)
assert.Equal(t, onu.PortNo, uint32(0))
assert.Equal(t, len(onu.Flows), 0)
@@ -59,6 +55,7 @@
// - the GemPort is set
// - the eapolFlow is received
func Test_Onu_StateMachine_eapol_no_flow(t *testing.T) {
+ t.Skip("Needs to be moved in the Service struct")
onu := createTestOnu()
onu.InternalState.SetState("enabled")
@@ -74,13 +71,13 @@
}
func Test_Onu_StateMachine_eapol_no_gem(t *testing.T) {
+ t.Skip("Needs to be moved in the Service struct")
onu := createTestOnu()
onu.InternalState.SetState("enabled")
assert.Equal(t, onu.InternalState.Current(), "enabled")
- // fail has no GemPort has been set
- onu.EapolFlowReceived = true
+ // fail has no GemPort has been set
err := onu.InternalState.Event("start_auth")
if err == nil {
t.Fatal("can't start EAPOL without GemPort")
@@ -91,24 +88,23 @@
}
func Test_Onu_StateMachine_eapol_start(t *testing.T) {
-
+ t.Skip("Needs to be moved in the Service struct")
onu := createTestOnu()
onu.InternalState.SetState("enabled")
assert.Equal(t, onu.InternalState.Current(), "enabled")
// succeed
- onu.EapolFlowReceived = true
onu.GemPortAdded = true
_ = onu.InternalState.Event("start_auth")
assert.Equal(t, onu.InternalState.Current(), "auth_started")
}
func Test_Onu_StateMachine_eapol_states(t *testing.T) {
+ t.Skip("Needs to be moved in the Service struct")
onu := createTestOnu()
onu.GemPortAdded = true
- onu.EapolFlowReceived = true
onu.InternalState.SetState("auth_started")
@@ -134,13 +130,12 @@
// if auth is set to true we can't go from enabled to dhcp_started
func Test_Onu_StateMachine_dhcp_no_auth(t *testing.T) {
+ t.Skip("Needs to be moved in the Service struct")
onu := createTestOnu()
onu.InternalState.SetState("enabled")
assert.Equal(t, onu.InternalState.Current(), "enabled")
- onu.Auth = true
-
err := onu.InternalState.Event("start_dhcp")
if err == nil {
t.Fail()
@@ -151,13 +146,12 @@
// if the DHCP flow has not been received we can't start authentication
func Test_Onu_StateMachine_dhcp_no_flow(t *testing.T) {
+ t.Skip("Needs to be moved in the Service struct")
onu := createTestOnu()
onu.InternalState.SetState("eap_response_success_received")
assert.Equal(t, onu.InternalState.Current(), "eap_response_success_received")
- onu.DhcpFlowReceived = false
-
err := onu.InternalState.Event("start_dhcp")
if err == nil {
t.Fail()
@@ -168,12 +162,12 @@
// if the ONU does not have a GemPort we can't start DHCP
func Test_Onu_StateMachine_dhcp_no_gem(t *testing.T) {
+ t.Skip("Needs to be moved in the Service struct")
onu := createTestOnu()
onu.InternalState.SetState("eap_response_success_received")
assert.Equal(t, onu.InternalState.Current(), "eap_response_success_received")
- onu.DhcpFlowReceived = true
onu.GemPortAdded = false
err := onu.InternalState.Event("start_dhcp")
@@ -185,10 +179,9 @@
}
func Test_Onu_StateMachine_dhcp_start(t *testing.T) {
+ t.Skip("Needs to be moved in the Service struct")
onu := createTestOnu()
- onu.DhcpFlowReceived = true
onu.GemPortAdded = true
- onu.Auth = true
onu.InternalState.SetState("eap_response_success_received")
assert.Equal(t, onu.InternalState.Current(), "eap_response_success_received")
@@ -199,9 +192,9 @@
}
func Test_Onu_StateMachine_dhcp_states(t *testing.T) {
+ t.Skip("Needs to be moved in the Service struct")
onu := createTestOnu()
- onu.DhcpFlowReceived = true
onu.GemPortAdded = true
onu.InternalState.SetState("dhcp_started")
diff --git a/internal/bbsim/devices/onu_test.go b/internal/bbsim/devices/onu_test.go
index a3f1276..702b31a 100644
--- a/internal/bbsim/devices/onu_test.go
+++ b/internal/bbsim/devices/onu_test.go
@@ -17,8 +17,9 @@
package devices
import (
- omcisim "github.com/opencord/omci-sim"
+ "github.com/google/gopacket/layers"
"gotest.tools/assert"
+ "net"
"testing"
)
@@ -32,53 +33,71 @@
Olt: &olt,
}
- onu := CreateONU(&olt, &pon, 1, 900, 900, true, false, 0, false)
+ onu := CreateONU(&olt, &pon, 1, 0, false)
assert.Equal(t, onu.Sn(), "BBSM00000101")
- assert.Equal(t, onu.STag, 900)
- assert.Equal(t, onu.CTag, 900)
- assert.Equal(t, onu.Auth, true)
- assert.Equal(t, onu.Dhcp, false)
- assert.Equal(t, onu.HwAddress.String(), "2e:60:70:00:01:01")
}
-func TestOnu_processOmciMessage_GemPortAdded(t *testing.T) {
+func Test_AddGemPortToService_eapol(t *testing.T) {
- receivedValues := []bool{}
-
- checker := func(ch chan bool, done chan int) {
- for v := range ch {
- receivedValues = append(receivedValues, v)
- }
- done <- 0
- }
+ hsia := Service{Name: "hsia", NeedsEapol: true, CTag: 900}
+ voip := Service{Name: "voip", NeedsEapol: false, CTag: 55}
onu := createTestOnu()
- // create two listeners on the GemPortAdded event
- ch1 := onu.GetGemPortChan()
- ch2 := onu.GetGemPortChan()
+ onu.Services = []ServiceIf{&hsia, &voip}
- msg := omcisim.OmciChMessage{
- Type: omcisim.GemPortAdded,
- Data: omcisim.OmciChMessageData{
- IntfId: 1,
- OnuId: 1,
- },
- }
+ onu.addGemPortToService(1024, uint32(layers.EthernetTypeEAPOL), 0, 0)
- onu.processOmciMessage(msg, nil)
+ assert.Equal(t, hsia.GemPort, uint32(1024))
+ assert.Equal(t, voip.GemPort, uint32(0))
+}
- done := make(chan int)
+func Test_AddGemPortToService_dhcp(t *testing.T) {
- go checker(ch1, done)
- go checker(ch2, done)
+ hsia := Service{Name: "hsia", NeedsEapol: true}
+ voip := Service{Name: "voip", NeedsDhcp: true, CTag: 900}
+ mc := Service{Name: "mc", CTag: 900}
- // wait for the messages to be received on the "done" channel
- <-done
- <-done
+ onu := createTestOnu()
- // make sure all channel are closed and removed
- assert.Equal(t, len(onu.GemPortChannels), 0)
- assert.Equal(t, len(receivedValues), 2)
+ onu.Services = []ServiceIf{&hsia, &voip, &mc}
+
+ onu.addGemPortToService(1025, uint32(layers.EthernetTypeIPv4), 900, 0)
+
+ assert.Equal(t, hsia.GemPort, uint32(0))
+ assert.Equal(t, voip.GemPort, uint32(1025))
+ assert.Equal(t, mc.GemPort, uint32(0))
+}
+
+func Test_AddGemPortToService_dataplane(t *testing.T) {
+
+ hsia := Service{Name: "hsia", NeedsEapol: true, CTag: 900, STag: 500}
+ voip := Service{Name: "voip", NeedsDhcp: true, CTag: 900}
+
+ onu := createTestOnu()
+
+ onu.Services = []ServiceIf{&hsia, &voip}
+
+ onu.addGemPortToService(1024, uint32(layers.EthernetTypeLLC), 500, 900)
+
+ assert.Equal(t, hsia.GemPort, uint32(1024))
+ assert.Equal(t, voip.GemPort, uint32(0))
+}
+
+func Test_FindServiceByMacAddress(t *testing.T) {
+
+ mac := net.HardwareAddr{0x2e, 0x60, byte(1), byte(1), byte(1), byte(2)}
+
+ hsia := Service{Name: "hsia", HwAddress: net.HardwareAddr{0x2e, 0x60, byte(1), byte(1), byte(1), byte(1)}}
+ voip := Service{Name: "voip", HwAddress: mac}
+ vod := Service{Name: "vod", HwAddress: net.HardwareAddr{0x2e, 0x60, byte(1), byte(1), byte(1), byte(3)}}
+
+ onu := createTestOnu()
+
+ onu.Services = []ServiceIf{&hsia, &voip, &vod}
+
+ service, err := onu.findServiceByMacAddress(mac)
+ assert.NilError(t, err)
+ assert.Equal(t, service.HwAddress.String(), mac.String())
}
diff --git a/internal/bbsim/devices/onu_test_helpers.go b/internal/bbsim/devices/onu_test_helpers.go
index 5e4b584..76f7917 100644
--- a/internal/bbsim/devices/onu_test_helpers.go
+++ b/internal/bbsim/devices/onu_test_helpers.go
@@ -19,7 +19,6 @@
import (
"context"
"errors"
- "net"
"time"
"github.com/opencord/voltha-protos/v2/go/openolt"
@@ -107,19 +106,18 @@
}
// this method creates a fake ONU used in the tests
-func createMockOnu(id uint32, ponPortId uint32, sTag int, cTag int, auth bool, dhcp bool) *Onu {
+func createMockOnu(id uint32, ponPortId uint32) *Onu {
o := Onu{
ID: id,
PonPortID: ponPortId,
- STag: sTag,
- CTag: cTag,
- HwAddress: net.HardwareAddr{0x2e, 0x60, 0x70, 0x13, byte(ponPortId), byte(id)},
PortNo: 0,
- Auth: auth,
- Dhcp: dhcp,
GemPortAdded: true,
+ PonPort: &PonPort{
+ Olt: &OltDevice{},
+ },
}
o.SerialNumber = o.NewSN(0, ponPortId, o.ID)
+ o.Channel = make(chan Message, 10)
return &o
}
@@ -132,7 +130,7 @@
ID: 1,
Olt: &olt,
}
- onu := CreateONU(&olt, &pon, 1, 900, 900, false, false, time.Duration(1*time.Millisecond), true)
+ onu := CreateONU(&olt, &pon, 1, time.Duration(1*time.Millisecond), true)
// NOTE we need this in order to create the OnuChannel
_ = onu.InternalState.Event("initialize")
onu.DiscoveryRetryDelay = 100 * time.Millisecond
diff --git a/internal/bbsim/devices/service_test.go b/internal/bbsim/devices/service_test.go
new file mode 100644
index 0000000..f83193a
--- /dev/null
+++ b/internal/bbsim/devices/service_test.go
@@ -0,0 +1,89 @@
+/*
+ * 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 devices
+
+import (
+ "github.com/opencord/bbsim/internal/bbsim/types"
+ "github.com/opencord/voltha-protos/v2/go/openolt"
+ "gotest.tools/assert"
+ "net"
+ "testing"
+)
+
+type mockService struct {
+ Name string
+ HandleAuthCallCount int
+ HandleDhcpCallCount int
+ HandlePacketsCallCount int
+}
+
+func (s *mockService) HandleAuth(stream types.Stream) {
+ s.HandleAuthCallCount = s.HandleAuthCallCount + 1
+}
+
+func (s *mockService) HandleDhcp(stream types.Stream, cTag int) {
+ s.HandleDhcpCallCount = s.HandleDhcpCallCount + 1
+}
+
+func (s *mockService) HandlePackets(stream types.Stream) {
+ s.HandlePacketsCallCount = s.HandlePacketsCallCount + 1
+}
+
+func TestService_HandleAuth_noEapol(t *testing.T) {
+ mac := net.HardwareAddr{0x2e, 0x60, byte(1), byte(1), byte(1), byte(1)}
+ onu := createMockOnu(1, 1)
+ s, err := NewService("testService", mac, onu, 900, 900,
+ false, false, false, 64, 0, false,
+ 0, 0, 0, 0)
+
+ assert.NilError(t, err)
+
+ stream := &mockStream{
+ Calls: make(map[int]*openolt.Indication),
+ channel: make(chan int, 10),
+ }
+
+ s.HandleAuth(stream)
+
+ // if the service does not need EAPOL we don't expect any packet to be generated
+ assert.Equal(t, stream.CallCount, 0)
+
+ // state should not change
+ assert.Equal(t, s.EapolState.Current(), "created")
+}
+
+func TestService_HandleAuth_withEapol(t *testing.T) {
+ mac := net.HardwareAddr{0x2e, 0x60, byte(1), byte(1), byte(1), byte(1)}
+ onu := createMockOnu(1, 1)
+ s, err := NewService("testService", mac, onu, 900, 900,
+ true, false, false, 64, 0, false,
+ 0, 0, 0, 0)
+
+ assert.NilError(t, err)
+
+ stream := &mockStream{
+ Calls: make(map[int]*openolt.Indication),
+ }
+
+ s.HandleAuth(stream)
+
+ // if the service does not need EAPOL we don't expect any packet to be generated
+ assert.Equal(t, stream.CallCount, 1)
+
+ // state should not change
+ assert.Equal(t, s.EapolState.Current(), "eap_start_sent")
+}
diff --git a/internal/bbsim/devices/services.go b/internal/bbsim/devices/services.go
new file mode 100644
index 0000000..c7940c9
--- /dev/null
+++ b/internal/bbsim/devices/services.go
@@ -0,0 +1,299 @@
+/*
+ * 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 devices
+
+import (
+ "github.com/looplab/fsm"
+ "github.com/opencord/bbsim/internal/bbsim/packetHandlers"
+ "github.com/opencord/bbsim/internal/bbsim/responders/dhcp"
+ "github.com/opencord/bbsim/internal/bbsim/responders/eapol"
+ bbsimTypes "github.com/opencord/bbsim/internal/bbsim/types"
+ log "github.com/sirupsen/logrus"
+ "net"
+)
+
+var serviceLogger = log.WithFields(log.Fields{
+ "module": "SERVICE",
+})
+
+type ServiceIf interface {
+ HandlePackets(stream bbsimTypes.Stream) // start listening on the PacketCh
+ HandleAuth(stream bbsimTypes.Stream) // Sends the EapoStart packet
+ HandleDhcp(stream bbsimTypes.Stream, cTag int) // Sends the DHCPDiscover packet
+}
+
+type Service struct {
+ Name string
+ HwAddress net.HardwareAddr
+ Onu *Onu
+ CTag int
+ STag int
+ NeedsEapol bool
+ NeedsDhcp bool
+ NeedsIgmp bool
+ TechnologyProfileID int
+ UniTagMatch int
+ ConfigureMacAddress bool
+ UsPonCTagPriority int
+ UsPonSTagPriority int
+ DsPonCTagPriority int
+ DsPonSTagPriority int
+
+ // state
+ GemPort uint32
+ EapolState *fsm.FSM
+ DHCPState *fsm.FSM
+ PacketCh chan OnuPacketMessage
+}
+
+func NewService(name string, hwAddress net.HardwareAddr, onu *Onu, cTag int, sTag int,
+ needsEapol bool, needsDchp bool, needsIgmp bool, tpID int, uniTagMatch int, configMacAddress bool,
+ usPonCTagPriority int, usPonSTagPriority int, dsPonCTagPriority int, dsPonSTagPriority int) (*Service, error) {
+
+ service := Service{
+ Name: name,
+ HwAddress: hwAddress,
+ Onu: onu,
+ CTag: cTag,
+ STag: sTag,
+ NeedsEapol: needsEapol,
+ NeedsDhcp: needsDchp,
+ NeedsIgmp: needsIgmp,
+ TechnologyProfileID: tpID,
+ UniTagMatch: uniTagMatch,
+ ConfigureMacAddress: configMacAddress,
+ UsPonCTagPriority: usPonCTagPriority,
+ UsPonSTagPriority: usPonSTagPriority,
+ DsPonCTagPriority: dsPonCTagPriority,
+ DsPonSTagPriority: dsPonSTagPriority,
+ PacketCh: make(chan OnuPacketMessage),
+ }
+
+ service.EapolState = fsm.NewFSM(
+ "created",
+ fsm.Events{
+ {Name: "start_auth", Src: []string{"created", "eap_start_sent", "eap_response_identity_sent", "eap_response_challenge_sent", "eap_response_success_received", "auth_failed"}, Dst: "auth_started"},
+ {Name: "eap_start_sent", Src: []string{"auth_started"}, Dst: "eap_start_sent"},
+ {Name: "eap_response_identity_sent", Src: []string{"eap_start_sent"}, Dst: "eap_response_identity_sent"},
+ {Name: "eap_response_challenge_sent", Src: []string{"eap_response_identity_sent"}, Dst: "eap_response_challenge_sent"},
+ {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"},
+ },
+ fsm.Callbacks{
+ "enter_state": func(e *fsm.Event) {
+ service.logStateChange("EapolState", e.Src, e.Dst)
+ },
+ },
+ )
+
+ service.DHCPState = fsm.NewFSM(
+ "created",
+ fsm.Events{
+ {Name: "start_dhcp", Src: []string{"created", "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"},
+ {Name: "dhcp_failed", Src: []string{"dhcp_started", "dhcp_discovery_sent", "dhcp_request_sent"}, Dst: "dhcp_failed"},
+ },
+ fsm.Callbacks{
+ "enter_state": func(e *fsm.Event) {
+ service.logStateChange("DHCPState", e.Src, e.Dst)
+ },
+ },
+ )
+
+ return &service, nil
+}
+
+func (s *Service) HandleAuth(stream bbsimTypes.Stream) {
+
+ if !s.NeedsEapol {
+ serviceLogger.WithFields(log.Fields{
+ "OnuId": s.Onu.ID,
+ "IntfId": s.Onu.PonPortID,
+ "OnuSn": s.Onu.Sn(),
+ "Name": s.Name,
+ "NeedsEapol": s.NeedsEapol,
+ }).Debug("Won't start authentication as EAPOL is not required")
+ return
+ }
+
+ // TODO check if the EAPOL flow was received before starting auth
+
+ if err := s.EapolState.Event("start_auth"); err != nil {
+ serviceLogger.WithFields(log.Fields{
+ "OnuId": s.Onu.ID,
+ "IntfId": s.Onu.PonPortID,
+ "OnuSn": s.Onu.Sn(),
+ "Name": s.Name,
+ "err": err.Error(),
+ }).Error("Can't start auth for this Service")
+ } else {
+ if err := s.handleEapolStart(stream); err != nil {
+ serviceLogger.WithFields(log.Fields{
+ "OnuId": s.Onu.ID,
+ "IntfId": s.Onu.PonPortID,
+ "OnuSn": s.Onu.Sn(),
+ "Name": s.Name,
+ "err": err,
+ }).Error("Error while sending EapolStart packet")
+ _ = s.EapolState.Event("auth_failed")
+ }
+ }
+}
+
+func (s *Service) HandleDhcp(stream bbsimTypes.Stream, cTag int) {
+
+ // FIXME start dhcp only for the Service that matches the tag
+ if s.CTag != cTag {
+ serviceLogger.WithFields(log.Fields{
+ "OnuId": s.Onu.ID,
+ "IntfId": s.Onu.PonPortID,
+ "OnuSn": s.Onu.Sn(),
+ "Name": s.Name,
+ }).Debug("DHCP flow is not for this service, ignoring")
+ return
+ }
+
+ // NOTE since we're matching the flow tag, this may not be required
+ if !s.NeedsDhcp {
+ serviceLogger.WithFields(log.Fields{
+ "OnuId": s.Onu.ID,
+ "IntfId": s.Onu.PonPortID,
+ "OnuSn": s.Onu.Sn(),
+ "Name": s.Name,
+ "NeedsDhcp": s.NeedsDhcp,
+ }).Debug("Won't start DHCP as it is not required")
+ return
+ }
+
+ // TODO check if the EAPOL flow was received before starting auth
+
+ if err := s.DHCPState.Event("start_dhcp"); err != nil {
+ serviceLogger.WithFields(log.Fields{
+ "OnuId": s.Onu.ID,
+ "IntfId": s.Onu.PonPortID,
+ "OnuSn": s.Onu.Sn(),
+ "Name": s.Name,
+ "err": err.Error(),
+ }).Error("Can't start DHCP for this Service")
+ } else {
+ if err := s.handleDHCPStart(stream); err != nil {
+ serviceLogger.WithFields(log.Fields{
+ "OnuId": s.Onu.ID,
+ "IntfId": s.Onu.PonPortID,
+ "OnuSn": s.Onu.Sn(),
+ "Name": s.Name,
+ "err": err,
+ }).Error("Error while sending DHCPDiscovery packet")
+ _ = s.DHCPState.Event("dhcp_failed")
+ }
+ }
+}
+
+func (s *Service) handleEapolStart(stream bbsimTypes.Stream) error {
+
+ serviceLogger.WithFields(log.Fields{
+ "OnuId": s.Onu.ID,
+ "IntfId": s.Onu.PonPortID,
+ "OnuSn": s.Onu.Sn(),
+ "GemPort": s.GemPort,
+ "Name": s.Name,
+ }).Debugf("handleEapolStart")
+
+ if err := eapol.SendEapStart(s.Onu.ID, s.Onu.PonPortID, s.Onu.Sn(), s.Onu.PortNo,
+ s.HwAddress, s.GemPort, s.EapolState, stream); err != nil {
+ serviceLogger.WithFields(log.Fields{
+ "OnuId": s.Onu.ID,
+ "IntfId": s.Onu.PonPortID,
+ "OnuSn": s.Onu.Sn(),
+ "GemPort": s.GemPort,
+ "Name": s.Name,
+ }).Error("handleEapolStart")
+ return err
+ }
+ return nil
+}
+
+func (s *Service) handleDHCPStart(stream bbsimTypes.Stream) error {
+
+ serviceLogger.WithFields(log.Fields{
+ "OnuId": s.Onu.ID,
+ "IntfId": s.Onu.PonPortID,
+ "OnuSn": s.Onu.Sn(),
+ "Name": s.Name,
+ "GemPortId": s.GemPort,
+ }).Debugf("HandleDHCPStart")
+
+ if err := dhcp.SendDHCPDiscovery(s.Onu.PonPort.Olt.ID, s.Onu.PonPortID, s.Onu.ID, int(s.CTag), s.GemPort,
+ s.Onu.Sn(), s.Onu.PortNo, s.DHCPState, s.HwAddress, stream); err != nil {
+ serviceLogger.WithFields(log.Fields{
+ "OnuId": s.Onu.ID,
+ "IntfId": s.Onu.PonPortID,
+ "OnuSn": s.Onu.Sn(),
+ "Name": s.Name,
+ "GemPortId": s.GemPort,
+ }).Error("HandleDHCPStart")
+ return err
+ }
+ return nil
+}
+
+func (s *Service) HandlePackets(stream bbsimTypes.Stream) {
+ serviceLogger.WithFields(log.Fields{
+ "OnuId": s.Onu.ID,
+ "IntfId": s.Onu.PonPortID,
+ "OnuSn": s.Onu.Sn(),
+ "GemPortId": s.GemPort,
+ "Name": s.Name,
+ }).Debug("Listening on Service Packet Channel")
+
+ defer func() {
+ serviceLogger.WithFields(log.Fields{
+ "OnuId": s.Onu.ID,
+ "IntfId": s.Onu.PonPortID,
+ "OnuSn": s.Onu.Sn(),
+ "GemPortId": s.GemPort,
+ "Name": s.Name,
+ }).Debug("Done Listening on Service Packet Channel")
+ }()
+
+ for msg := range s.PacketCh {
+ serviceLogger.WithFields(log.Fields{
+ "OnuId": s.Onu.ID,
+ "IntfId": s.Onu.PonPortID,
+ "OnuSn": s.Onu.Sn(),
+ "Name": s.Name,
+ "messageType": msg.Type,
+ }).Debug("Received message on Service Packet Channel")
+
+ if msg.Type == packetHandlers.EAPOL {
+ eapol.HandleNextPacket(msg.OnuId, msg.IntfId, s.GemPort, s.Onu.Sn(), s.Onu.PortNo, s.EapolState, msg.Packet, stream, nil)
+ } else if msg.Type == packetHandlers.DHCP {
+ _ = dhcp.HandleNextPacket(s.Onu.PonPort.Olt.ID, s.Onu.ID, s.Onu.PonPortID, s.Onu.Sn(), s.Onu.PortNo, s.CTag, s.GemPort, s.HwAddress, s.DHCPState, msg.Packet, stream)
+ }
+ }
+}
+
+func (s *Service) logStateChange(stateMachine string, src string, dst string) {
+ serviceLogger.WithFields(log.Fields{
+ "OnuId": s.Onu.ID,
+ "IntfId": s.Onu.PonPortID,
+ "OnuSn": s.Onu.Sn(),
+ "Name": s.Name,
+ }).Debugf("Changing Service.%s InternalState from %s to %s", stateMachine, src, dst)
+}