blob: 512782a51cfcaf5b56c486c0d368ac6215071899 [file] [log] [blame]
/*
* 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 (
"context"
"testing"
"time"
"github.com/google/gopacket"
"github.com/google/gopacket/layers"
"github.com/opencord/bbsim/internal/bbsim/responders/dhcp"
"github.com/opencord/bbsim/internal/common"
"github.com/opencord/voltha-protos/v5/go/openolt"
"github.com/stretchr/testify/assert"
)
func getTestOlt(t *testing.T, ctx context.Context, services []common.ServiceYaml) (olt *OltDevice, pon *PonPort, onu *Onu, uni *UniPort, stream *mockStream) {
common.Services = services
common.Config = &common.GlobalConfig{
Olt: common.OltConfig{
ID: 1,
NniPorts: 1,
PonPorts: 1,
OnusPonPort: 1,
UniPorts: 1,
},
}
allocIdPerOnu := uint32(common.Config.Olt.UniPorts * uint32(len(common.Services)))
common.PonsConfig = &common.PonPortsConfig{
Number: common.Config.Olt.PonPorts,
Ranges: []common.PonRangeConfig{
{
PonRange: common.IdRange{StartId: 0, EndId: common.Config.Olt.PonPorts - 1},
Technology: common.XGSPON.String(),
OnuRange: common.IdRange{StartId: 1, EndId: 1 + (common.Config.Olt.OnusPonPort - 1)},
AllocIdRange: common.IdRange{StartId: 1024, EndId: 1024 + (common.Config.Olt.OnusPonPort * allocIdPerOnu)},
GemportRange: common.IdRange{StartId: 1024, EndId: 1024 + common.Config.Olt.OnusPonPort*allocIdPerOnu*8},
},
},
}
olt = CreateOLT(*common.Config, common.Services, true)
stream = &mockStream{
Calls: make(map[int]*openolt.Indication),
}
olt.OpenoltStream = stream
olt.enableContext = ctx
if len(olt.Pons) <= 0 {
assert.Fail(t, "No PONs on OLT")
return
}
pon = olt.Pons[0]
if len(pon.Onus) <= 0 {
assert.Fail(t, "No ONUs on PON")
return
}
onu = pon.Onus[0]
if len(onu.UniPorts) <= 0 {
assert.Fail(t, "No UNIs on ONU")
return
}
uni = onu.UniPorts[0].(*UniPort)
_, err := olt.ActivateOnu(ctx, &openolt.Onu{
IntfId: pon.ID,
OnuId: onu.ID,
SerialNumber: onu.SerialNumber,
})
assert.Nil(t, err)
assert.Equal(t, len(services), len(uni.Services))
for i, s := range uni.Services {
service := s.(*Service)
assert.Equal(t, services[i].Name, service.Name)
assert.Equal(t, services[i].NeedsDhcp, service.NeedsDhcp)
}
err = onu.InternalState.Event(OnuTxInitialize)
assert.Nil(t, err)
//A mock ONU won't start processing messages, so we have to call it manually
go onu.ProcessOnuMessages(ctx, stream, nil)
err = onu.InternalState.Event(OnuTxDiscover)
assert.Nil(t, err)
err = onu.InternalState.Event(OnuTxEnable)
assert.Nil(t, err)
err = uni.Enable()
assert.Nil(t, err)
return
}
func addTestFlow(t *testing.T, ctx context.Context, olt *OltDevice, onu *Onu, flow openolt.Flow) {
//Check if the flow is correctly added
_, err := olt.FlowAdd(ctx, &flow)
assert.Nil(t, err)
}
func removeTestFlow(t *testing.T, ctx context.Context, olt *OltDevice, onu *Onu, flow openolt.Flow) {
//Check if the flow is correctly removed
_, err := olt.FlowRemove(ctx, &flow)
assert.Nil(t, err)
}
func Test_Flows_FttbTrapRules(t *testing.T) {
const (
VID_VENDOR_MGMT = 6
VID_NETWORK_DPU_MGMT = 60
allocId = int32(1024)
gemportId = int32(1024)
uniPortNo = uint32(256)
nniId = 0
nniPortNo = 0x1000000
pbitNone = 255
dhcpServiceName = "dpu_dhcp"
hsiaServicename = "hsia"
)
testServices := []common.ServiceYaml{
{Name: dhcpServiceName, CTag: 60, CTagAllocation: common.TagAllocationShared.String(), STagAllocation: common.TagAllocationShared.String(), NeedsDhcp: true, TechnologyProfileID: 64},
{Name: hsiaServicename, UniTagMatch: 4096, CTag: 4096, CTagAllocation: common.TagAllocationShared.String(), STag: 3101, STagAllocation: common.TagAllocationUnique.String(), TechnologyProfileID: 64},
}
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
olt, pon, onu, uni, stream := getTestOlt(t, ctx, testServices)
flows := []openolt.Flow{
{
AccessIntfId: int32(pon.ID),
OnuId: int32(onu.ID),
UniId: int32(uni.ID),
FlowId: 1,
FlowType: flowTypeUpstream,
AllocId: allocId,
NetworkIntfId: nniId,
GemportId: gemportId,
Classifier: &openolt.Classifier{
OVid: VID_NETWORK_DPU_MGMT,
OPbits: pbitNone,
EthType: uint32(layers.EthernetTypeIPv4),
IpProto: uint32(layers.IPProtocolUDP),
SrcPort: 68,
DstPort: 67,
PktTagType: flowTagTypeSingle,
},
Action: &openolt.Action{
Cmd: &openolt.ActionCmd{
TrapToHost: true,
},
},
Priority: 40000,
PortNo: uniPortNo,
},
{
AccessIntfId: -1,
OnuId: -1,
UniId: -1,
FlowId: 2,
FlowType: flowTypeDownstream,
AllocId: -1,
NetworkIntfId: nniId,
GemportId: -1,
Classifier: &openolt.Classifier{
OVid: VID_NETWORK_DPU_MGMT,
OPbits: pbitNone,
EthType: uint32(layers.EthernetTypeIPv4),
IpProto: uint32(layers.IPProtocolUDP),
SrcPort: 67,
PktTagType: flowTagTypeDouble,
},
Action: &openolt.Action{
Cmd: &openolt.ActionCmd{
TrapToHost: true,
},
},
Priority: 40000,
PortNo: nniPortNo,
},
}
for _, f := range flows {
addTestFlow(t, ctx, olt, onu, f)
}
//Wait a bit for messages on various channels to be processed
time.Sleep(time.Second)
//Check if a DHCP request is sent correctly after trap rules have been added
assert.True(t, stream.CallCount > 0)
dhcpCall := stream.Calls[stream.CallCount]
switch ind := dhcpCall.Data.(type) {
case *openolt.Indication_PktInd:
assert.Equal(t, uniPortNo, ind.PktInd.PortNo)
assert.Equal(t, pon.ID, ind.PktInd.IntfId)
assert.Equal(t, "pon", ind.PktInd.IntfType)
assert.Equal(t, uint32(gemportId), ind.PktInd.GemportId)
packet := gopacket.NewPacket(ind.PktInd.Pkt, layers.LayerTypeEthernet, gopacket.Default)
_, err := dhcp.GetDhcpLayer(packet)
assert.Nil(t, err)
default:
assert.Fail(t, "Wrong indication type for DHCP request")
}
for _, f := range flows {
removeTestFlow(t, ctx, olt, onu, f)
}
}