[SEBA-836] BBSim Reflector
Change-Id: Ib4ae5a2c24880dc62209bebb81188eca5f57865d
diff --git a/.gitignore b/.gitignore
index d0d8872..c904607 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,3 +1,4 @@
-
+/bbr
/bbsim
/bbsimctl
+logs/*
\ No newline at end of file
diff --git a/Makefile b/Makefile
index 6a69c9f..9ff4ba7 100644
--- a/Makefile
+++ b/Makefile
@@ -31,7 +31,7 @@
dep: # @HELP Download the dependencies to the vendor folder
GO111MODULE=on go mod vendor
-build: dep protos fmt build-bbsim build-bbsimctl# @HELP Build the binary
+build: dep protos fmt build-bbsim build-bbsimctl build-bbr # @HELP Build the binary
test: dep protos fmt # @HELP Execute unit tests
GO111MODULE=on go test -v -mod vendor ./... -covermode count -coverprofile ./tests/results/go-test-coverage.out 2>&1 | tee ./tests/results/go-test-results.out
@@ -50,6 +50,11 @@
docker-run: # @HELP Run the container locally (intended for development purposes: DOCKER_RUN_ARGS="-pon 2 -onu 2" make docker-run)
docker run -p 50070:50070 -p 50060:50060 --privileged --rm --name bbsim ${DOCKER_REGISTRY}${DOCKER_REPOSITORY}bbsim:${DOCKER_TAG} /app/bbsim ${DOCKER_RUN_ARGS}
+.PHONY: docs
+docs: # @HELP Generate docs and serve them in the browser
+ @echo "\033[36m Open your browser at localhost:6060 \033[0m"
+ godoc -http=localhost:6060
+
help: # @HELP Print the command options
@echo
@echo "\033[0;31m BroadBand Simulator (BBSim) \033[0m"
@@ -66,9 +71,17 @@
# Internals
+build-bbr:
+ GO111MODULE=on go build -i -v -mod vendor \
+ -ldflags "-w -X main.buildTime=$(shell date +”%Y/%m/%d-%H:%M:%S”) \
+ -X main.commitHash=$(shell git log --pretty=format:%H -n 1) \
+ -X main.gitStatus=${GIT_STATUS} \
+ -X main.version=${VERSION}" \
+ ./cmd/bbr
+
build-bbsim:
GO111MODULE=on go build -i -v -mod vendor \
- -ldflags "-X main.buildTime=$(shell date +”%Y/%m/%d-%H:%M:%S”) \
+ -ldflags "-w -X main.buildTime=$(shell date +”%Y/%m/%d-%H:%M:%S”) \
-X main.commitHash=$(shell git log --pretty=format:%H -n 1) \
-X main.gitStatus=${GIT_STATUS} \
-X main.version=${VERSION}" \
@@ -76,7 +89,7 @@
build-bbsimctl:
GO111MODULE=on go build -i -v -mod vendor \
- -ldflags "-X github.com/opencord/bbsim/internal/bbsimctl/config.BuildTime=$(shell date +”%Y/%m/%d-%H:%M:%S”) \
+ -ldflags "-w -X github.com/opencord/bbsim/internal/bbsimctl/config.BuildTime=$(shell date +”%Y/%m/%d-%H:%M:%S”) \
-X github.com/opencord/bbsim/internal/bbsimctl/config.CommitHash=$(shell git log --pretty=format:%H -n 1) \
-X github.com/opencord/bbsim/internal/bbsimctl/config.GitStatus=${GIT_STATUS} \
-X github.com/opencord/bbsim/internal/bbsimctl/config.Version=${VERSION}" \
diff --git a/README.md b/README.md
index c63d5d7..b6dee44 100644
--- a/README.md
+++ b/README.md
@@ -112,7 +112,7 @@
3 up
-$ ./bbsimctl onus
+$ ./bbsimctl onu list
PONPORTID ID SERIALNUMBER STAG CTAG OPERSTATE INTERNALSTATE
0 1 BBSM00000001 900 900 up eap_response_identity_sent
0 2 BBSM00000002 900 901 up eap_start_sent
diff --git a/build/package/Dockerfile b/build/package/Dockerfile
index e85888e..5ede6cc 100644
--- a/build/package/Dockerfile
+++ b/build/package/Dockerfile
@@ -47,7 +47,8 @@
# copy and build
COPY . ./
RUN go mod vendor -v # we can't vendor dependencies unless the code is there
-RUN make build
+RUN make build-bbsim
+RUN make build-bbsimctl
# runtime parent
FROM golang:1.12-stretch
@@ -60,7 +61,6 @@
&& apt-get install -y libpcap-dev isc-dhcp-server network-manager tcpdump\
&& ln -s /usr/lib/libpcap.so.1.8.1 /usr/lib/libpcap.so.0.8
-COPY ./configs/isc-dhcp-server /etc/default/
COPY ./configs/dhcpd.conf /etc/dhcp/
RUN mv /usr/sbin/dhcpd /usr/local/bin/ \
&& mv /sbin/dhclient /usr/local/bin/ \
diff --git a/cmd/bbr/bbr.go b/cmd/bbr/bbr.go
new file mode 100644
index 0000000..69b8639
--- /dev/null
+++ b/cmd/bbr/bbr.go
@@ -0,0 +1,80 @@
+/*
+ * 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.
+ */
+
+// BBR (BBSim Reflector) is a tool designed to scale test BBSim
+// It shared most of the code with BBSim itself and replies to messages
+// pretending to be VOLTHA.
+// The idea behind it is that, given that the BBSim and BBR are based on the same
+// codebase, BBR is acting as a wall for BBSim. And you can't beat the wall.
+package main
+
+import (
+ bbrdevices "github.com/opencord/bbsim/internal/bbr/devices"
+ "github.com/opencord/bbsim/internal/bbsim/devices"
+ "github.com/opencord/bbsim/internal/common"
+ log "github.com/sirupsen/logrus"
+ "os"
+ "runtime/pprof"
+ "time"
+)
+
+// usage
+func main() {
+ options := common.GetBBROpts()
+
+ common.SetLogLevel(log.StandardLogger(), options.LogLevel, options.LogCaller)
+
+ if *options.ProfileCpu != "" {
+ // start profiling
+ log.Infof("Creating profile file at: %s", *options.ProfileCpu)
+ f, err := os.Create(*options.ProfileCpu)
+ if err != nil {
+ log.Fatal(err)
+ }
+ pprof.StartCPUProfile(f)
+ }
+
+ log.WithFields(log.Fields{
+ "OltID": options.OltID,
+ "NumNniPerOlt": options.NumNniPerOlt,
+ "NumPonPerOlt": options.NumPonPerOlt,
+ "NumOnuPerPon": options.NumOnuPerPon,
+ }).Info("BroadBand Reflector is on")
+
+ // NOTE this are probably useless in the MockOLT case, check if we can avoid using them in the CreateOlt method
+ oltDoneChannel := make(chan bool)
+ apiDoneChannel := make(chan bool)
+
+ // create the OLT device
+ olt := devices.CreateOLT(options.OltID, options.NumNniPerOlt, options.NumPonPerOlt, options.NumOnuPerPon, options.STag, options.CTagInit, &oltDoneChannel, &apiDoneChannel, true)
+ oltMock := bbrdevices.OltMock{
+ Olt: olt,
+ TargetOnus: options.NumPonPerOlt * options.NumOnuPerPon,
+ CompletedOnus: 0,
+ BBSimIp: options.BBSimIp,
+ BBSimPort: options.BBSimPort,
+ BBSimApiPort: options.BBSimApiPort,
+ }
+
+ // start the enable sequence
+ startTime := time.Now()
+ defer func() {
+ endTime := time.Now()
+ runTime := endTime.Sub(startTime)
+ log.WithField("Duration", runTime).Info("BBR done!")
+ }()
+ oltMock.Start()
+}
diff --git a/cmd/bbsim/bbsim.go b/cmd/bbsim/bbsim.go
index f243801..2ceb50a 100644
--- a/cmd/bbsim/bbsim.go
+++ b/cmd/bbsim/bbsim.go
@@ -17,11 +17,10 @@
package main
import (
- "flag"
"github.com/opencord/bbsim/api/bbsim"
"github.com/opencord/bbsim/internal/bbsim/api"
"github.com/opencord/bbsim/internal/bbsim/devices"
- bbsimLogger "github.com/opencord/bbsim/internal/bbsim/logger"
+ "github.com/opencord/bbsim/internal/common"
log "github.com/sirupsen/logrus"
"google.golang.org/grpc"
"google.golang.org/grpc/reflection"
@@ -31,50 +30,6 @@
"sync"
)
-type CliOptions struct {
- OltID int
- NumNniPerOlt int
- NumPonPerOlt int
- NumOnuPerPon int
- STag int
- CTagInit int
- profileCpu *string
- logLevel string
- logCaller bool
-}
-
-func getOpts() *CliOptions {
-
- olt_id := flag.Int("olt_id", 0, "Number of OLT devices to be emulated (default is 1)")
- nni := flag.Int("nni", 1, "Number of NNI ports per OLT device to be emulated (default is 1)")
- pon := flag.Int("pon", 1, "Number of PON ports per OLT device to be emulated (default is 1)")
- onu := flag.Int("onu", 1, "Number of ONU devices per PON port to be emulated (default is 1)")
-
- s_tag := flag.Int("s_tag", 900, "S-Tag value (default is 900)")
- c_tag_init := flag.Int("c_tag", 900, "C-Tag starting value (default is 900), each ONU will get a sequentail one (targeting 1024 ONUs per BBSim instance the range is bug enough)")
-
- profileCpu := flag.String("cpuprofile", "", "write cpu profile to file")
-
- logLevel := flag.String("logLevel", "debug", "Set the log level (trace, debug, info, warn, error)")
- logCaller := flag.Bool("logCaller", false, "Whether to print the caller filename or not")
-
- flag.Parse()
-
- o := new(CliOptions)
-
- o.OltID = int(*olt_id)
- o.NumNniPerOlt = int(*nni)
- o.NumPonPerOlt = int(*pon)
- o.NumOnuPerPon = int(*onu)
- o.STag = int(*s_tag)
- o.CTagInit = int(*c_tag_init)
- o.profileCpu = profileCpu
- o.logLevel = *logLevel
- o.logCaller = *logCaller
-
- return o
-}
-
func startApiServer(channel chan bool, group *sync.WaitGroup) {
// TODO make configurable
address := "0.0.0.0:50070"
@@ -110,14 +65,14 @@
}
func main() {
- options := getOpts()
+ options := common.GetBBSimOpts()
- bbsimLogger.SetLogLevel(log.StandardLogger(), options.logLevel, options.logCaller)
+ common.SetLogLevel(log.StandardLogger(), options.LogLevel, options.LogCaller)
- if *options.profileCpu != "" {
+ if *options.ProfileCpu != "" {
// start profiling
- log.Infof("Creating profile file at: %s", *options.profileCpu)
- f, err := os.Create(*options.profileCpu)
+ log.Infof("Creating profile file at: %s", *options.ProfileCpu)
+ f, err := os.Create(*options.ProfileCpu)
if err != nil {
log.Fatal(err)
}
@@ -138,7 +93,8 @@
wg := sync.WaitGroup{}
wg.Add(2)
- go devices.CreateOLT(options.OltID, options.NumNniPerOlt, options.NumPonPerOlt, options.NumOnuPerPon, options.STag, options.CTagInit, &oltDoneChannel, &apiDoneChannel, &wg)
+ olt := devices.CreateOLT(options.OltID, options.NumNniPerOlt, options.NumPonPerOlt, options.NumOnuPerPon, options.STag, options.CTagInit, &oltDoneChannel, &apiDoneChannel, false)
+ go devices.StartOlt(olt, &wg)
log.Debugf("Created OLT with id: %d", options.OltID)
go startApiServer(apiDoneChannel, &wg)
log.Debugf("Started APIService")
@@ -147,7 +103,7 @@
defer func() {
log.Info("BroadBand Simulator is off")
- if *options.profileCpu != "" {
+ if *options.ProfileCpu != "" {
log.Info("Stopping profiler")
pprof.StopCPUProfile()
}
diff --git a/configs/dhcpd.conf b/configs/dhcpd.conf
index 06d366b..2ab1fe5 100644
--- a/configs/dhcpd.conf
+++ b/configs/dhcpd.conf
@@ -1,115 +1,17 @@
#
# Sample configuration file for ISC dhcpd for Debian
#
-# Attention: If /etc/ltsp/dhcpd.conf exists, that will be used as
-# configuration file instead of this file.
-#
-#
-# The ddns-updates-style parameter controls whether or not the server will
-# attempt to do a DNS update when a lease is confirmed. We default to the
-# behavior of the version 2 packages ('none', since DHCP v2 didn't
-# have support for DDNS.)
ddns-update-style none;
-# option definitions common to all supported networks...
option domain-name "example.org";
option domain-name-servers ns1.example.org, ns2.example.org;
default-lease-time 600;
max-lease-time 7200;
-# If this DHCP server is the official DHCP server for the local
-# network, the authoritative directive should be uncommented.
-#authoritative;
-
-# Use this to send dhcp log messages to a different log file (you also
-# have to hack syslog.conf to complete the redirection).
-log-facility local7;
-
-# No service will be given on this subnet, but declaring it helps the
-# DHCP server to understand the network topology.
-
-#subnet 10.152.187.0 netmask 255.255.255.0 {
-#}
-
# This is a very basic subnet declaration.
-subnet 182.21.0.0 netmask 255.255.0.0 {
- range 182.21.0.1 182.21.0.128;
- option routers 182.21.0.254;
-}
-
-#subnet 10.254.239.0 netmask 255.255.255.224 {
-# range 10.254.239.10 10.254.239.20;
-# option routers rtr-239-0-1.example.org, rtr-239-0-2.example.org;
-#}
-
-# This declaration allows BOOTP clients to get dynamic addresses,
-# which we don't really recommend.
-
-#subnet 10.254.239.32 netmask 255.255.255.224 {
-# range dynamic-bootp 10.254.239.40 10.254.239.60;
-# option broadcast-address 10.254.239.31;
-# option routers rtr-239-32-1.example.org;
-#}
-
-# A slightly different configuration for an internal subnet.
-#subnet 10.5.5.0 netmask 255.255.255.224 {
-# range 10.5.5.26 10.5.5.30;
-# option domain-name-servers ns1.internal.example.org;
-# option domain-name "internal.example.org";
-# option subnet-mask 255.255.255.224;
-# option routers 10.5.5.1;
-# option broadcast-address 10.5.5.31;
-# default-lease-time 600;
-# max-lease-time 7200;
-#}
-
-# Hosts which require special configuration options can be listed in
-# host statements. If no address is specified, the address will be
-# allocated dynamically (if possible), but the host-specific information
-# will still come from the host declaration.
-
-#host passacaglia {
-# hardware ethernet 0:0:c0:5d:bd:95;
-# filename "vmunix.passacaglia";
-# server-name "toccata.fugue.com";
-#}
-
-# Fixed IP addresses can also be specified for hosts. These addresses
-# should not also be listed as being available for dynamic assignment.
-# Hosts for which fixed IP addresses have been specified can boot using
-# BOOTP or DHCP. Hosts for which no fixed address is specified can only
-# be booted with DHCP, unless there is an address range on the subnet
-# to which a BOOTP client is connected which has the dynamic-bootp flag
-# set.
-#host fantasia {
-# hardware ethernet 08:00:07:26:c0:a5;
-# fixed-address fantasia.fugue.com;
-#}
-
-# You can declare a class of clients and then do address allocation
-# based on that. The example below shows a case where all clients
-# in a certain class get addresses on the 10.17.224/24 subnet, and all
-# other clients get addresses on the 10.0.29/24 subnet.
-
-#class "foo" {
-# match if substring (option vendor-class-identifier, 0, 4) = "SUNW";
-#}
-
-#shared-network 224-29 {
-# subnet 10.17.224.0 netmask 255.255.255.0 {
-# option routers rtr-224.example.org;
-# }
-# subnet 10.0.29.0 netmask 255.255.255.0 {
-# option routers rtr-29.example.org;
-# }
-# pool {
-# allow members of "foo";
-# range 10.17.224.10 10.17.224.250;
-# }
-# pool {
-# deny members of "foo";
-# range 10.0.29.10 10.0.29.230;
-# }
-#}
\ No newline at end of file
+subnet 192.168.0.0 netmask 255.255.0.0 {
+ range 192.168.0.1 192.168.253.254;
+ option routers 192.168.254.254;
+}
\ No newline at end of file
diff --git a/configs/isc-dhcp-server b/configs/isc-dhcp-server
deleted file mode 100644
index 0501355..0000000
--- a/configs/isc-dhcp-server
+++ /dev/null
@@ -1,22 +0,0 @@
-# Defaults for isc-dhcp-server initscript
-# sourced by /etc/init.d/isc-dhcp-server
-# installed at /etc/default/isc-dhcp-server by the maintainer scripts
-
-#
-# This is a POSIX shell fragment
-#
-
-# Path to dhcpd's config file (default: /etc/dhcp/dhcpd.conf).
-#DHCPD_CONF=/etc/dhcp/dhcpd.conf
-
-# Path to dhcpd's PID file (default: /var/run/dhcpd.pid).
-#DHCPD_PID=/var/run/dhcpd.pid
-
-# Additional options to start dhcpd with.
-# Don't use options -cf or -pf here; use DHCPD_CONF/ DHCPD_PID instead
-#OPTIONS=""
-
-# On what interfaces should the DHCP server (dhcpd) serve DHCP requests?
-# Separate multiple interfaces with spaces, e.g. "eth0 eth1".
-INTERFACES="nni_north"
-
diff --git a/docs/README.md b/docs/README.md
index 710182d..6aed37b 100644
--- a/docs/README.md
+++ b/docs/README.md
@@ -1,4 +1,5 @@
# BBSim Documentation
- [ONU State Machine](./onu-state-machine.md)
-- [Using development dependencies](./development-dependencies.md)
\ No newline at end of file
+- [Using development dependencies](./development-dependencies.md)
+- [BBR](./bbr.md)
\ No newline at end of file
diff --git a/docs/bbr.md b/docs/bbr.md
new file mode 100644
index 0000000..fafdf77
--- /dev/null
+++ b/docs/bbr.md
@@ -0,0 +1,54 @@
+# BBR
+
+To run `bbr` you need to have a `bbsim` instance running.
+
+You can start `bbsim` locally with:
+
+```bash
+$ DOCKER_RUN_ARGS="-onu 16 -pon 4" make docker-run
+```
+
+Once `bbsim` is up and running you'll see this on the console:
+
+```bash
+docker run -p 50070:50070 -p 50060:50060 --privileged --rm --name bbsim """"bbsim:0.0.2-dev /app/bbsim -onu 16 -pon 4
+time="2019-10-18T00:24:46Z" level=info msg="BroadBand Simulator is on" NumNniPerOlt=1 NumOnuPerPon=16 NumPonPerOlt=4 OltID=0
+time="2019-10-18T00:24:46Z" level=debug msg=CreateOLT ID=0 NumNni=1 NumOnuPerPon=16 NumPon=4 module=OLT
+time="2019-10-18T00:24:46Z" level=info msg="Successfully activated DHCP Server" module=NNI
+time="2019-10-18T00:24:46Z" level=debug msg="Created OLT with id: 0"
+time="2019-10-18T00:24:46Z" level=debug msg="Started APIService"
+time="2019-10-18T00:24:46Z" level=debug msg="APIServer Listening on: 0.0.0.0:50070"
+time="2019-10-18T00:24:46Z" level=debug msg="OLT Listening on: 0.0.0.0:50060" module=OLT
+```
+
+At this point you can start `bbr` (note that you need to pass the same number of ONUs and PON Ports to the two processes):
+
+```bash
+$ ./bbr -onu 16 -pon 4
+```
+
+`bbr` will run to completion and output the time it took to bring all the ONUs to the `dhcp_ack` state.
+If the `bbr` process doesn't exit, it means something went wrong.
+
+## Debugging and issue reporting
+
+If you are experiencing issues with `bbr` please capture:
+- `bbr` logs
+- `bbsim` logs
+- `bbsimctl onu list` output
+
+You can use these commands to capture `bbsim` and `bbr` logs during execution:
+
+```bash
+$ DOCKER_RUN_ARGS="-onu 16 -pon 4" make docker-run 2>&1 | tee bbsim.logs
+```
+
+```bash
+$ ./bbr -onu 16 -pon 4 2>&1 | tee bbr.logs
+```
+
+And this command to retrieve the list of onus in the system (run this command once `bbr` is hanging but before terminating `bbsim`):
+
+```bash
+docker exec bbsim bbsimctl onu list > onu.list
+```
\ No newline at end of file
diff --git a/go.mod b/go.mod
index a7c7b38..c3882e0 100644
--- a/go.mod
+++ b/go.mod
@@ -3,6 +3,9 @@
go 1.12
require (
+ github.com/aead/cmac v0.0.0-20160719120800-7af84192f0b1 // indirect
+ github.com/cboling/omci v0.1.0
+ github.com/deckarep/golang-set v1.7.1 // indirect
github.com/fullstorydev/grpcurl v1.3.2 // indirect
github.com/golang/protobuf v1.3.2
github.com/google/gopacket v1.1.17
diff --git a/go.sum b/go.sum
index daa619e..e0ebc88 100644
--- a/go.sum
+++ b/go.sum
@@ -1,8 +1,14 @@
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
+github.com/aead/cmac v0.0.0-20160719120800-7af84192f0b1 h1:+JkXLHME8vLJafGhOH4aoV2Iu8bR55nU6iKMVfYVLjY=
+github.com/aead/cmac v0.0.0-20160719120800-7af84192f0b1/go.mod h1:nuudZmJhzWtx2212z+pkuy7B6nkBqa+xwNXZHL1j8cg=
+github.com/cboling/omci v0.1.0 h1:hzsf8oomdIt6IWX6ZVj3p2zIP+GOyEEHD8b47KrA1MY=
+github.com/cboling/omci v0.1.0/go.mod h1:qE+T+qTEh/U1UaMidFdMv1eDOJ45WTKTBp2QmEvsWGQ=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/deckarep/golang-set v1.7.1 h1:SCQV0S6gTtp6itiFrTqI+pfmJ4LN85S1YzhDf9rTHJQ=
+github.com/deckarep/golang-set v1.7.1/go.mod h1:93vsz/8Wt4joVM7c2AVqh+YRMiUSc14yDtF28KmMOgQ=
github.com/fullstorydev/grpcurl v1.3.2 h1:cJKWsBYMocdxXQvgbnhtLG810SL5MhKT4K7BagxRih8=
github.com/fullstorydev/grpcurl v1.3.2/go.mod h1:kvk8xPCXOrwVd9zYdjy+xSOT4YWm6kyth4Y9NMfBns4=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
diff --git a/internal/bbr/devices/olt.go b/internal/bbr/devices/olt.go
new file mode 100644
index 0000000..103be3b
--- /dev/null
+++ b/internal/bbr/devices/olt.go
@@ -0,0 +1,380 @@
+/*
+ * 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"
+ "errors"
+ "fmt"
+ "github.com/google/gopacket"
+ "github.com/google/gopacket/layers"
+ "github.com/opencord/bbsim/internal/bbsim/devices"
+ "github.com/opencord/bbsim/internal/bbsim/packetHandlers"
+ "github.com/opencord/bbsim/internal/common"
+ "github.com/opencord/voltha-protos/go/openolt"
+ log "github.com/sirupsen/logrus"
+ "google.golang.org/grpc"
+ "io"
+ "reflect"
+ "time"
+)
+
+type OltMock struct {
+ Olt *devices.OltDevice
+ BBSimIp string
+ BBSimPort string
+ BBSimApiPort string
+
+ conn *grpc.ClientConn
+
+ TargetOnus int
+ CompletedOnus int // Number of ONUs that have received a DHCPAck
+}
+
+// trigger an enable call and start the same listeners on the gRPC stream that VOLTHA would create
+// this method is blocking
+func (o *OltMock) Start() {
+ log.Info("Starting Mock OLT")
+
+ for _, pon := range o.Olt.Pons {
+ for _, onu := range pon.Onus {
+ log.Debugf("Created ONU: %s (%d:%d)", onu.Sn(), onu.STag, onu.CTag)
+ }
+ }
+
+ client, conn := Connect(o.BBSimIp, o.BBSimPort)
+ o.conn = conn
+ defer conn.Close()
+
+ deviceInfo, err := o.getDeviceInfo(client)
+
+ if err != nil {
+ log.WithFields(log.Fields{
+ "error": err,
+ }).Fatal("Can't read device info")
+ }
+
+ log.WithFields(log.Fields{
+ "Vendor": deviceInfo.Vendor,
+ "Model": deviceInfo.Model,
+ "DeviceSerialNumber": deviceInfo.DeviceSerialNumber,
+ "PonPorts": deviceInfo.PonPorts,
+ }).Info("Retrieved device info")
+
+ o.readIndications(client)
+
+}
+
+func (o *OltMock) getDeviceInfo(client openolt.OpenoltClient) (*openolt.DeviceInfo, error) {
+ ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
+ defer cancel()
+
+ return client.GetDeviceInfo(ctx, new(openolt.Empty))
+}
+
+func (o *OltMock) getOnuByTags(sTag int, cTag int) (*devices.Onu, error) {
+
+ for _, pon := range o.Olt.Pons {
+ for _, onu := range pon.Onus {
+ if onu.STag == sTag && onu.CTag == cTag {
+ return onu, nil
+ }
+ }
+ }
+
+ return nil, errors.New("cant-find-onu-by-c-s-tags")
+}
+
+func (o *OltMock) readIndications(client openolt.OpenoltClient) {
+ defer func() {
+ log.Info("OLT readIndications done")
+ }()
+
+ // Tell the OLT to start sending indications
+ indications, err := client.EnableIndication(context.Background(), new(openolt.Empty))
+ if err != nil {
+ log.WithFields(log.Fields{
+ "error": err,
+ }).Error("Failed to enable indication stream")
+ return
+ }
+
+ // listen for indications
+ for {
+ indication, err := indications.Recv()
+ if err == io.EOF {
+ break
+ }
+ if err != nil {
+
+ // the connection is closed once we have sent the DHCP_ACK packet to all of the ONUs
+ // it means BBR completed, it's not an error
+
+ log.WithFields(log.Fields{
+ "error": err,
+ }).Debug("Failed to read from indications")
+ break
+ }
+
+ o.handleIndication(client, indication)
+ }
+}
+
+func (o *OltMock) handleIndication(client openolt.OpenoltClient, indication *openolt.Indication) {
+ switch indication.Data.(type) {
+ case *openolt.Indication_OltInd:
+ log.Info("Received Indication_OltInd")
+ case *openolt.Indication_IntfInd:
+ log.Info("Received Indication_IntfInd")
+ case *openolt.Indication_IntfOperInd:
+ log.Info("Received Indication_IntfOperInd")
+ case *openolt.Indication_OnuDiscInd:
+ onuDiscInd := indication.GetOnuDiscInd()
+ o.handleOnuDiscIndication(client, onuDiscInd)
+ case *openolt.Indication_OnuInd:
+ onuInd := indication.GetOnuInd()
+ o.handleOnuIndication(client, onuInd)
+ case *openolt.Indication_OmciInd:
+ omciIndication := indication.GetOmciInd()
+ o.handleOmciIndication(client, omciIndication)
+ case *openolt.Indication_PktInd:
+ pktIndication := indication.GetPktInd()
+ o.handlePktIndication(client, pktIndication)
+ case *openolt.Indication_PortStats:
+ case *openolt.Indication_FlowStats:
+ case *openolt.Indication_AlarmInd:
+ default:
+ log.WithFields(log.Fields{
+ "data": indication.Data,
+ "type": reflect.TypeOf(indication.Data),
+ }).Warn("Indication unsupported")
+ }
+}
+
+func (o *OltMock) handleOnuDiscIndication(client openolt.OpenoltClient, onuDiscInd *openolt.OnuDiscIndication) {
+ log.WithFields(log.Fields{
+ "IntfId": onuDiscInd.IntfId,
+ "SerialNumber": common.OnuSnToString(onuDiscInd.SerialNumber),
+ }).Info("Received Onu discovery indication")
+
+ onu, err := o.Olt.FindOnuBySn(common.OnuSnToString(onuDiscInd.SerialNumber))
+
+ if err != nil {
+ log.WithFields(log.Fields{
+ "IntfId": onuDiscInd.IntfId,
+ "SerialNumber": common.OnuSnToString(onuDiscInd.SerialNumber),
+ }).Fatal("Cannot find ONU")
+ }
+
+ var pir uint32 = 1000000
+ Onu := openolt.Onu{
+ IntfId: onu.PonPortID,
+ OnuId: onu.ID,
+ SerialNumber: onu.SerialNumber,
+ Pir: pir,
+ }
+
+ if _, err := client.ActivateOnu(context.Background(), &Onu); err != nil {
+ log.WithFields(log.Fields{
+ "IntfId": onuDiscInd.IntfId,
+ "SerialNumber": common.OnuSnToString(onuDiscInd.SerialNumber),
+ }).Error("Failed to activate ONU")
+ }
+}
+
+func (o *OltMock) handleOnuIndication(client openolt.OpenoltClient, onuInd *openolt.OnuIndication) {
+ log.WithFields(log.Fields{
+ "IntfId": onuInd.IntfId,
+ "SerialNumber": common.OnuSnToString(onuInd.SerialNumber),
+ }).Info("Received Onu indication")
+
+ onu, err := o.Olt.FindOnuBySn(common.OnuSnToString(onuInd.SerialNumber))
+
+ if err != nil {
+ log.WithFields(log.Fields{
+ "IntfId": onuInd.IntfId,
+ "SerialNumber": common.OnuSnToString(onuInd.SerialNumber),
+ }).Fatal("Cannot find ONU")
+ }
+
+ go onu.ProcessOnuMessages(nil, client)
+
+ go func() {
+
+ defer func() {
+ log.WithFields(log.Fields{
+ "onuSn": common.OnuSnToString(onuInd.SerialNumber),
+ "CompletedOnus": o.CompletedOnus,
+ "TargetOnus": o.TargetOnus,
+ }).Debugf("Onu done")
+
+ }()
+
+ for message := range onu.DoneChannel {
+ if message == true {
+ o.CompletedOnus++
+ if o.CompletedOnus == o.TargetOnus {
+ // NOTE once all the ONUs are completed, exit
+ // closing the connection is not the most elegant way,
+ // but I haven't found any other way to stop
+ // the indications.Recv() infinite loop
+ log.Info("Simulation Done")
+ ValidateAndClose(o)
+ }
+
+ break
+ }
+ }
+
+ }()
+
+ // TODO change the state instead of calling an ONU method from here
+ onu.StartOmci(client)
+}
+
+func (o *OltMock) handleOmciIndication(client openolt.OpenoltClient, omciInd *openolt.OmciIndication) {
+
+ pon, err := o.Olt.GetPonById(omciInd.IntfId)
+ if err != nil {
+ log.WithFields(log.Fields{
+ "OnuId": omciInd.OnuId,
+ "IntfId": omciInd.IntfId,
+ "err": err,
+ }).Fatal("Can't find PonPort")
+ }
+ onu, _ := pon.GetOnuById(omciInd.OnuId)
+ if err != nil {
+ log.WithFields(log.Fields{
+ "OnuId": omciInd.OnuId,
+ "IntfId": omciInd.IntfId,
+ "err": err,
+ }).Fatal("Can't find Onu")
+ }
+
+ log.WithFields(log.Fields{
+ "IntfId": onu.PonPortID,
+ "OnuId": onu.ID,
+ "OnuSn": onu.Sn(),
+ "Pkt": omciInd.Pkt,
+ }).Trace("Received Onu omci indication")
+
+ msg := devices.Message{
+ Type: devices.OmciIndication,
+ Data: devices.OmciIndicationMessage{
+ OnuSN: onu.SerialNumber,
+ OnuID: onu.ID,
+ OmciInd: omciInd,
+ },
+ }
+ onu.Channel <- msg
+}
+
+func (o *OltMock) handlePktIndication(client openolt.OpenoltClient, pktIndication *openolt.PacketIndication) {
+
+ pkt := gopacket.NewPacket(pktIndication.Pkt, layers.LayerTypeEthernet, gopacket.Default)
+
+ pktType, err := packetHandlers.IsEapolOrDhcp(pkt)
+
+ if err != nil {
+ log.Warnf("Ignoring packet as it's neither EAPOL or DHCP")
+ return
+ }
+
+ log.WithFields(log.Fields{
+ "IntfType": pktIndication.IntfType,
+ "IntfId": pktIndication.IntfId,
+ "GemportId": pktIndication.GemportId,
+ "FlowId": pktIndication.FlowId,
+ "PortNo": pktIndication.PortNo,
+ "Cookie": pktIndication.Cookie,
+ "pktType": pktType,
+ }).Trace("Received PktIndication")
+
+ msg := devices.Message{}
+ if pktIndication.IntfType == "nni" {
+ // This is an packet that is arriving from the NNI and needs to be sent to an ONU
+ // in this case we need to fin the ONU from the C/S tags
+ // TODO: handle errors in the untagging process
+ sTag, _ := packetHandlers.GetVlanTag(pkt)
+ singleTagPkt, _ := packetHandlers.PopSingleTag(pkt)
+ cTag, _ := packetHandlers.GetVlanTag(singleTagPkt)
+
+ onu, err := o.getOnuByTags(int(sTag), int(cTag))
+
+ if err != nil {
+ log.WithFields(log.Fields{
+ "sTag": sTag,
+ "cTag": cTag,
+ }).Fatalf("Can't find ONU from c/s tags")
+ }
+
+ msg = devices.Message{
+ Type: devices.OnuPacketIn,
+ Data: devices.OnuPacketMessage{
+ IntfId: pktIndication.IntfId,
+ OnuId: onu.ID,
+ Packet: pkt,
+ Type: pktType,
+ },
+ }
+ // NOTE we send it on the ONU channel so that is handled as all the others packets in a separate thread
+ onu.Channel <- msg
+ } else {
+ // TODO a very similar construct is used in many places,
+ // abstract this in an OLT method
+ pon, err := o.Olt.GetPonById(pktIndication.IntfId)
+ if err != nil {
+ log.WithFields(log.Fields{
+ "OnuId": pktIndication.PortNo,
+ "IntfId": pktIndication.IntfId,
+ "err": err,
+ }).Fatal("Can't find PonPort")
+ }
+ onu, err := pon.GetOnuById(pktIndication.PortNo)
+ if err != nil {
+ log.WithFields(log.Fields{
+ "OnuId": pktIndication.PortNo,
+ "IntfId": pktIndication.IntfId,
+ "err": err,
+ }).Fatal("Can't find Onu")
+ }
+ // NOTE when we push the EAPOL flow we set the PortNo = OnuId for convenience sake
+ // BBsim responds setting the port number that was sent with the flow
+ msg = devices.Message{
+ Type: devices.OnuPacketIn,
+ Data: devices.OnuPacketMessage{
+ IntfId: pktIndication.IntfId,
+ OnuId: pktIndication.PortNo,
+ Packet: pkt,
+ Type: pktType,
+ },
+ }
+ onu.Channel <- msg
+ }
+}
+
+// TODO Move in a different file
+func Connect(ip string, port string) (openolt.OpenoltClient, *grpc.ClientConn) {
+ server := fmt.Sprintf("%s:%s", ip, port)
+ conn, err := grpc.Dial(server, grpc.WithInsecure())
+
+ if err != nil {
+ log.Fatalf("did not connect: %v", err)
+ return nil, conn
+ }
+ return openolt.NewOpenoltClient(conn), conn
+}
diff --git a/internal/bbr/devices/validate.go b/internal/bbr/devices/validate.go
new file mode 100644
index 0000000..cb2551f
--- /dev/null
+++ b/internal/bbr/devices/validate.go
@@ -0,0 +1,78 @@
+/*
+ * 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"
+ "fmt"
+ "github.com/opencord/bbsim/api/bbsim"
+ log "github.com/sirupsen/logrus"
+ "google.golang.org/grpc"
+ "time"
+)
+
+func ValidateAndClose(olt *OltMock) {
+
+ // connect to the BBSim control APIs to check that all the ONUs are in the correct state
+ client, conn := ApiConnect(olt.BBSimIp, olt.BBSimApiPort)
+ defer conn.Close()
+
+ ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
+ defer cancel()
+
+ onus, err := client.GetONUs(ctx, &bbsim.Empty{})
+
+ if err != nil {
+ log.WithFields(log.Fields{
+ "error": err,
+ }).Fatalf("Can't reach BBSim API")
+ }
+
+ expectedState := "dhcp_ack_received"
+
+ res := true
+ for _, onu := range onus.Items {
+ if onu.InternalState != expectedState {
+ res = false
+ log.WithFields(log.Fields{
+ "OnuSN": onu.SerialNumber,
+ "OnuId": onu.ID,
+ "InternalState": onu.InternalState,
+ "ExpectedSatte": expectedState,
+ }).Error("Not matching expected state")
+ }
+ }
+
+ if res == true {
+ log.WithFields(log.Fields{
+ "ExpectedState": expectedState,
+ }).Infof("%d ONUs matching expected state", len(onus.Items))
+ }
+
+ olt.conn.Close()
+}
+
+func ApiConnect(ip string, port string) (bbsim.BBSimClient, *grpc.ClientConn) {
+ server := fmt.Sprintf("%s:%s", ip, port)
+ conn, err := grpc.Dial(server, grpc.WithInsecure())
+
+ if err != nil {
+ log.Fatalf("did not connect: %v", err)
+ return nil, conn
+ }
+ return bbsim.NewBBSimClient(conn), conn
+}
diff --git a/internal/bbsim/api/grpc_api_server.go b/internal/bbsim/api/grpc_api_server.go
index caa60e8..0c209f8 100644
--- a/internal/bbsim/api/grpc_api_server.go
+++ b/internal/bbsim/api/grpc_api_server.go
@@ -20,7 +20,7 @@
"context"
"github.com/opencord/bbsim/api/bbsim"
"github.com/opencord/bbsim/internal/bbsim/devices"
- bbsimLogger "github.com/opencord/bbsim/internal/bbsim/logger"
+ "github.com/opencord/bbsim/internal/common"
log "github.com/sirupsen/logrus"
)
@@ -82,7 +82,7 @@
func (s BBSimServer) SetLogLevel(ctx context.Context, req *bbsim.LogLevel) (*bbsim.LogLevel, error) {
- bbsimLogger.SetLogLevel(log.StandardLogger(), req.Level, req.Caller)
+ common.SetLogLevel(log.StandardLogger(), req.Level, req.Caller)
return &bbsim.LogLevel{
Level: log.StandardLogger().Level.String(),
diff --git a/internal/bbsim/devices/helpers.go b/internal/bbsim/devices/helpers.go
index 5a56825..a00931d 100644
--- a/internal/bbsim/devices/helpers.go
+++ b/internal/bbsim/devices/helpers.go
@@ -39,6 +39,7 @@
)
}
+// deprecated
func onuSnToString(sn *openolt.SerialNumber) string {
s := string(sn.VendorId)
for _, i := range sn.VendorSpecific {
diff --git a/internal/bbsim/devices/messageTypes.go b/internal/bbsim/devices/messageTypes.go
index fe6abef..b61ac18 100644
--- a/internal/bbsim/devices/messageTypes.go
+++ b/internal/bbsim/devices/messageTypes.go
@@ -18,6 +18,7 @@
import (
"github.com/google/gopacket"
+ "github.com/opencord/bbsim/internal/bbsim/packetHandlers"
"github.com/opencord/voltha-protos/go/openolt"
)
@@ -35,6 +36,12 @@
StartDHCP MessageType = 8
OnuPacketOut MessageType = 9
DyingGaspIndication MessageType = 10
+
+ // BBR messages
+ OmciIndication MessageType = 11 // this are OMCI messages going from the OLT to VOLTHA
+ SendEapolFlow MessageType = 12
+ SendDhcpFlow MessageType = 13
+ OnuPacketIn MessageType = 14
)
func (m MessageType) String() string {
@@ -50,6 +57,10 @@
"StartDHCP",
"OnuPacketOut",
"DyingGaspIndication",
+ "OmciIndication",
+ "SendEapolFlow",
+ "SendDhcpFlow",
+ "OnuPacketIn",
}
return names[m]
}
@@ -91,6 +102,12 @@
omciMsg *openolt.OmciMsg
}
+type OmciIndicationMessage struct {
+ OnuSN *openolt.SerialNumber
+ OnuID uint32
+ OmciInd *openolt.OmciIndication
+}
+
type OnuFlowUpdateMessage struct {
PonPortID uint32
OnuID uint32
@@ -102,10 +119,11 @@
OnuID uint32
}
-type OnuPacketOutMessage struct {
+type OnuPacketMessage struct {
IntfId uint32
OnuId uint32
Packet gopacket.Packet
+ Type packetHandlers.PacketType
}
type DyingGaspIndicationMessage struct {
diff --git a/internal/bbsim/devices/nni.go b/internal/bbsim/devices/nni.go
index 30104fc..f5503cd 100644
--- a/internal/bbsim/devices/nni.go
+++ b/internal/bbsim/devices/nni.go
@@ -31,7 +31,7 @@
nniLogger = log.WithFields(log.Fields{"module": "NNI"})
nniVeth = "nni"
upstreamVeth = "upstream"
- dhcpServerIp = "182.21.0.128"
+ dhcpServerIp = "192.168.254.1"
)
type Executor interface {
diff --git a/internal/bbsim/devices/olt.go b/internal/bbsim/devices/olt.go
index c3a62ff..27f4e78 100644
--- a/internal/bbsim/devices/olt.go
+++ b/internal/bbsim/devices/olt.go
@@ -63,17 +63,17 @@
return &olt
}
-func CreateOLT(seq int, nni int, pon int, onuPerPon int, sTag int, cTagInit int, oltDoneChannel *chan bool, apiDoneChannel *chan bool, group *sync.WaitGroup) OltDevice {
+func CreateOLT(oltId int, nni int, pon int, onuPerPon int, sTag int, cTagInit int, oltDoneChannel *chan bool, apiDoneChannel *chan bool, isMock bool) *OltDevice {
oltLogger.WithFields(log.Fields{
- "ID": seq,
+ "ID": oltId,
"NumNni": nni,
"NumPon": pon,
"NumOnuPerPon": onuPerPon,
}).Debug("CreateOLT")
olt = OltDevice{
- ID: seq,
- SerialNumber: fmt.Sprintf("BBSIM_OLT_%d", seq),
+ ID: oltId,
+ SerialNumber: fmt.Sprintf("BBSIM_OLT_%d", oltId),
OperState: getOperStateFSM(func(e *fsm.Event) {
oltLogger.Debugf("Changing OLT OperState from %s to %s", e.Src, e.Dst)
}),
@@ -103,15 +103,16 @@
},
)
- // create NNI Port
- nniPort, err := CreateNNI(&olt)
+ if isMock != true {
+ // create NNI Port
+ nniPort, err := CreateNNI(&olt)
+ if err != nil {
+ oltLogger.Fatalf("Couldn't create NNI Port: %v", err)
+ }
- if err != nil {
- oltLogger.Fatalf("Couldn't create NNI Port: %v", err)
+ olt.Nnis = append(olt.Nnis, &nniPort)
}
- olt.Nnis = append(olt.Nnis, &nniPort)
-
// create PON ports
availableCTag := cTagInit
for i := 0; i < pon; i++ {
@@ -137,11 +138,13 @@
olt.Pons = append(olt.Pons, &p)
}
+ return &olt
+}
- newOltServer(olt)
-
+// this function start the OLT gRPC server and blocks until it's done
+func StartOlt(olt *OltDevice, group *sync.WaitGroup) {
+ newOltServer(*olt)
group.Done()
- return olt
}
func newOltServer(o OltDevice) error {
@@ -222,7 +225,7 @@
o.channel <- msg
for _, onu := range pon.Onus {
- go onu.processOnuMessages(stream)
+ go onu.ProcessOnuMessages(stream, nil)
go onu.processOmciMessages(stream)
// FIXME move the message generation in the state transition
// from here only invoke the state transition
@@ -243,7 +246,7 @@
// Helpers method
-func (o OltDevice) getPonById(id uint32) (*PonPort, error) {
+func (o OltDevice) GetPonById(id uint32) (*PonPort, error) {
for _, pon := range o.Pons {
if pon.ID == id {
return pon, nil
@@ -294,7 +297,7 @@
}
func (o OltDevice) sendPonIndication(msg PonIndicationMessage, stream openolt.Openolt_EnableIndicationServer) {
- pon, _ := o.getPonById(msg.PonPortID)
+ pon, _ := o.GetPonById(msg.PonPortID)
pon.OperState.Event("enable")
discoverData := &openolt.Indication_IntfInd{IntfInd: &openolt.IntfIndication{
IntfId: pon.ID,
@@ -410,6 +413,7 @@
"IntfType": data.PktInd.IntfType,
"IntfId": nniId,
"Pkt": doubleTaggedPkt.Data(),
+ "OnuSn": onu.Sn(),
}).Tracef("Sent PktInd indication")
}
}
@@ -451,8 +455,8 @@
"OnuSn": onuSnToString(onu.SerialNumber),
}).Info("Received ActivateOnu call from VOLTHA")
- pon, _ := o.getPonById(onu.IntfId)
- _onu, _ := pon.getOnuBySn(onu.SerialNumber)
+ pon, _ := o.GetPonById(onu.IntfId)
+ _onu, _ := pon.GetOnuBySn(onu.SerialNumber)
if err := _onu.OperState.Event("enable"); err != nil {
oltLogger.WithFields(log.Fields{
@@ -531,7 +535,7 @@
"FlowId": flow.FlowId,
}).Debugf("This is an OLT flow")
} else {
- pon, err := o.getPonById(uint32(flow.AccessIntfId))
+ pon, err := o.GetPonById(uint32(flow.AccessIntfId))
if err != nil {
oltLogger.WithFields(log.Fields{
"OnuId": flow.OnuId,
@@ -539,7 +543,7 @@
"err": err,
}).Error("Can't find PonPort")
}
- onu, err := pon.getOnuById(uint32(flow.OnuId))
+ onu, err := pon.GetOnuById(uint32(flow.OnuId))
if err != nil {
oltLogger.WithFields(log.Fields{
"OnuId": flow.OnuId,
@@ -600,8 +604,8 @@
}
func (o OltDevice) OmciMsgOut(ctx context.Context, omci_msg *openolt.OmciMsg) (*openolt.Empty, error) {
- pon, _ := o.getPonById(omci_msg.IntfId)
- onu, _ := pon.getOnuById(omci_msg.OnuId)
+ pon, _ := o.GetPonById(omci_msg.IntfId)
+ onu, _ := pon.GetOnuById(omci_msg.OnuId)
oltLogger.WithFields(log.Fields{
"IntfId": onu.PonPortID,
"OnuId": onu.ID,
@@ -620,7 +624,7 @@
}
func (o OltDevice) OnuPacketOut(ctx context.Context, onuPkt *openolt.OnuPacket) (*openolt.Empty, error) {
- pon, err := o.getPonById(onuPkt.IntfId)
+ pon, err := o.GetPonById(onuPkt.IntfId)
if err != nil {
oltLogger.WithFields(log.Fields{
"OnuId": onuPkt.OnuId,
@@ -628,7 +632,7 @@
"err": err,
}).Error("Can't find PonPort")
}
- onu, err := pon.getOnuById(onuPkt.OnuId)
+ onu, err := pon.GetOnuById(onuPkt.OnuId)
if err != nil {
oltLogger.WithFields(log.Fields{
"OnuId": onuPkt.OnuId,
@@ -644,13 +648,15 @@
}).Tracef("Received OnuPacketOut")
rawpkt := gopacket.NewPacket(onuPkt.Pkt, layers.LayerTypeEthernet, gopacket.Default)
+ pktType, err := packetHandlers.IsEapolOrDhcp(rawpkt)
msg := Message{
Type: OnuPacketOut,
- Data: OnuPacketOutMessage{
+ Data: OnuPacketMessage{
IntfId: onuPkt.IntfId,
OnuId: onuPkt.OnuId,
Packet: rawpkt,
+ Type: pktType,
},
}
onu.Channel <- msg
diff --git a/internal/bbsim/devices/onu.go b/internal/bbsim/devices/onu.go
index 7d36bfd..f4ef84b 100644
--- a/internal/bbsim/devices/onu.go
+++ b/internal/bbsim/devices/onu.go
@@ -17,13 +17,17 @@
package devices
import (
+ "context"
"fmt"
+ "github.com/cboling/omci"
"github.com/google/gopacket/layers"
"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"
- omci "github.com/opencord/omci-sim"
+ "github.com/opencord/bbsim/internal/common"
+ omcilib "github.com/opencord/bbsim/internal/common/omci"
+ omcisim "github.com/opencord/omci-sim"
"github.com/opencord/voltha-protos/go/openolt"
log "github.com/sirupsen/logrus"
"net"
@@ -50,23 +54,35 @@
SerialNumber *openolt.SerialNumber
Channel chan Message // this Channel is to track state changes OMCI messages, EAPOL and DHCP packets
+
+ // OMCI params
+ tid uint16
+ hpTid uint16
+ seqNumber uint16
+ HasGemPort bool
+
+ DoneChannel chan bool // this channel is used to signal once the onu is complete (when the struct is used by BBR)
}
func (o Onu) Sn() string {
- return onuSnToString(o.SerialNumber)
+ return common.OnuSnToString(o.SerialNumber)
}
func CreateONU(olt OltDevice, pon PonPort, id uint32, sTag int, cTag int) *Onu {
o := Onu{
- ID: id,
- PonPortID: pon.ID,
- PonPort: pon,
- STag: sTag,
- CTag: cTag,
- HwAddress: net.HardwareAddr{0x2e, 0x60, 0x70, 0x13, byte(pon.ID), byte(id)},
- PortNo: 0,
- Channel: make(chan Message, 2048),
+ ID: id,
+ PonPortID: pon.ID,
+ PonPort: pon,
+ STag: sTag,
+ CTag: cTag,
+ HwAddress: net.HardwareAddr{0x2e, 0x60, 0x70, 0x13, byte(pon.ID), byte(id)},
+ PortNo: 0,
+ Channel: make(chan Message, 2048),
+ tid: 0x1,
+ hpTid: 0x8000,
+ seqNumber: 0,
+ DoneChannel: make(chan bool, 1),
}
o.SerialNumber = o.NewSN(olt.ID, pon.ID, o.ID)
@@ -102,6 +118,10 @@
{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{"created"}, Dst: "eapol_flow_sent"},
+ {Name: "send_dhcp_flow", Src: []string{"eapol_flow_sent"}, Dst: "dhcp_flow_sent"},
},
fsm.Callbacks{
"enter_state": func(e *fsm.Event) {
@@ -164,6 +184,18 @@
"OnuSn": o.Sn(),
}).Errorf("ONU failed to DHCP!")
},
+ "enter_eapol_flow_sent": func(e *fsm.Event) {
+ msg := Message{
+ Type: SendEapolFlow,
+ }
+ o.Channel <- msg
+ },
+ "enter_dhcp_flow_sent": func(e *fsm.Event) {
+ msg := Message{
+ Type: SendDhcpFlow,
+ }
+ o.Channel <- msg
+ },
},
)
return &o
@@ -177,7 +209,7 @@
}).Debugf("Changing ONU InternalState from %s to %s", src, dst)
}
-func (o Onu) processOnuMessages(stream openolt.Openolt_EnableIndicationServer) {
+func (o Onu) ProcessOnuMessages(stream openolt.Openolt_EnableIndicationServer, client openolt.OpenoltClient) {
onuLogger.WithFields(log.Fields{
"onuID": o.ID,
"onuSN": o.Sn(),
@@ -211,20 +243,51 @@
// FIXME use id, ponId as SendEapStart
dhcp.SendDHCPDiscovery(o.PonPortID, o.ID, o.Sn(), o.PortNo, o.InternalState, o.HwAddress, o.CTag, stream)
case OnuPacketOut:
- msg, _ := message.Data.(OnuPacketOutMessage)
- pkt := msg.Packet
- etherType := pkt.Layer(layers.LayerTypeEthernet).(*layers.Ethernet).EthernetType
- if etherType == layers.EthernetTypeEAPOL {
- eapol.HandleNextPacket(msg.OnuId, msg.IntfId, o.Sn(), o.PortNo, o.InternalState, msg.Packet, stream)
- } else if packetHandlers.IsDhcpPacket(pkt) {
+ msg, _ := message.Data.(OnuPacketMessage)
+
+ log.WithFields(log.Fields{
+ "IntfId": msg.IntfId,
+ "OnuId": msg.OnuId,
+ "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.ID, o.PonPortID, o.Sn(), o.PortNo, o.HwAddress, o.CTag, o.InternalState, msg.Packet, stream)
}
+ case OnuPacketIn:
+ // NOTE we only receive BBR packets here.
+ // Eapol.HandleNextPacket can handle both BBSim and BBr cases so the call is the same
+ // in the DHCP case VOLTHA only act as a proxy, the behaviour is completely different thus we have a dhcp.HandleNextBbrPacket
+ msg, _ := message.Data.(OnuPacketMessage)
+
+ log.WithFields(log.Fields{
+ "IntfId": msg.IntfId,
+ "OnuId": msg.OnuId,
+ "pktType": msg.Type,
+ }).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)
+ } else if msg.Type == packetHandlers.DHCP {
+ dhcp.HandleNextBbrPacket(o.ID, o.PonPortID, o.Sn(), o.STag, o.HwAddress, o.DoneChannel, msg.Packet, client)
+ }
case DyingGaspIndication:
msg, _ := message.Data.(DyingGaspIndicationMessage)
o.sendDyingGaspInd(msg, stream)
+ case OmciIndication:
+ // TODO handle me!
+ // here https://gerrit.opencord.org/#/c/15521/11/internal/bbr/onu.go in startOmci
+ msg, _ := message.Data.(OmciIndicationMessage)
+ o.handleOmci(msg, client)
+ case SendEapolFlow:
+ o.sendEapolFlow(client)
+ case SendDhcpFlow:
+ o.sendDhcpFlow(client)
default:
onuLogger.Warnf("Received unknown message data %v for type %v in OLT Channel", message.Data, message.Type)
}
@@ -232,7 +295,7 @@
}
func (o Onu) processOmciMessages(stream openolt.Openolt_EnableIndicationServer) {
- ch := omci.GetChannel()
+ ch := omcisim.GetChannel()
onuLogger.WithFields(log.Fields{
"onuID": o.ID,
@@ -241,7 +304,7 @@
for message := range ch {
switch message.Type {
- case omci.GemPortAdded:
+ case omcisim.GemPortAdded:
log.WithFields(log.Fields{
"OnuId": message.Data.OnuId,
"IntfId": message.Data.IntfId,
@@ -359,7 +422,7 @@
}).Tracef("Received OMCI message")
var omciInd openolt.OmciIndication
- respPkt, err := omci.OmciSim(o.PonPortID, o.ID, HexDecode(msg.omciMsg.Pkt))
+ respPkt, err := omcisim.OmciSim(o.PonPortID, o.ID, HexDecode(msg.omciMsg.Pkt))
if err != nil {
onuLogger.WithFields(log.Fields{
"IntfId": o.PonPortID,
@@ -381,7 +444,7 @@
"SerialNumber": o.Sn(),
"omciPacket": omciInd.Pkt,
"msg": msg,
- }).Errorf("send omci indication failed: %v", err)
+ }).Errorf("send omcisim indication failed: %v", err)
return
}
onuLogger.WithFields(log.Fields{
@@ -395,10 +458,10 @@
// FIXME this is a workaround to always use the SN-1 entry in sadis,
// we need to add support for multiple UNIs
// the action plan is:
- // - refactor the omci-sim library to use https://github.com/cboling/omci instead of canned messages
+ // - refactor the omcisim-sim library to use https://github.com/cboling/omci instead of canned messages
// - change the library so that it reports a single UNI and remove this workaroung
// - add support for multiple UNIs in BBSim
- if portNo < o.PortNo {
+ if o.PortNo == 0 || portNo < o.PortNo {
o.PortNo = portNo
}
}
@@ -459,3 +522,193 @@
onuLogger.Tracef("Omci decoded: %x.", p)
return p
}
+
+// BBR methods
+
+func sendOmciMsg(pktBytes []byte, intfId uint32, onuId uint32, sn *openolt.SerialNumber, msgType string, client openolt.OpenoltClient) {
+ omciMsg := openolt.OmciMsg{
+ IntfId: intfId,
+ OnuId: onuId,
+ Pkt: pktBytes,
+ }
+
+ if _, err := client.OmciMsgOut(context.Background(), &omciMsg); err != nil {
+ log.WithFields(log.Fields{
+ "IntfId": intfId,
+ "OnuId": onuId,
+ "SerialNumber": common.OnuSnToString(sn),
+ "Pkt": omciMsg.Pkt,
+ }).Fatalf("Failed to send MIB Reset")
+ }
+ log.WithFields(log.Fields{
+ "IntfId": intfId,
+ "OnuId": onuId,
+ "SerialNumber": common.OnuSnToString(sn),
+ "Pkt": omciMsg.Pkt,
+ }).Tracef("Sent OMCI message %s", msgType)
+}
+
+func (onu *Onu) getNextTid(highPriority ...bool) uint16 {
+ var next uint16
+ if len(highPriority) > 0 && highPriority[0] {
+ next = onu.hpTid
+ onu.hpTid += 1
+ if onu.hpTid < 0x8000 {
+ onu.hpTid = 0x8000
+ }
+ } else {
+ next = onu.tid
+ onu.tid += 1
+ if onu.tid >= 0x8000 {
+ onu.tid = 1
+ }
+ }
+ return next
+}
+
+// TODO move this method in responders/omcisim
+func (o *Onu) StartOmci(client openolt.OpenoltClient) {
+ mibReset, _ := omcilib.CreateMibResetRequest(o.getNextTid(false))
+ sendOmciMsg(mibReset, o.PonPortID, o.ID, o.SerialNumber, "mibReset", client)
+}
+
+func (o *Onu) handleOmci(msg OmciIndicationMessage, client openolt.OpenoltClient) {
+ msgType, packet := omcilib.DecodeOmci(msg.OmciInd.Pkt)
+
+ log.WithFields(log.Fields{
+ "IntfId": msg.OmciInd.IntfId,
+ "OnuId": msg.OmciInd.OnuId,
+ "OnuSn": common.OnuSnToString(o.SerialNumber),
+ "Pkt": msg.OmciInd.Pkt,
+ "msgType": msgType,
+ }).Trace("ONU Receveives OMCI Msg")
+ switch msgType {
+ default:
+ log.Fatalf("unexpected frame: %v", packet)
+ case omci.MibResetResponseType:
+ mibUpload, _ := omcilib.CreateMibUploadRequest(o.getNextTid(false))
+ sendOmciMsg(mibUpload, o.PonPortID, o.ID, o.SerialNumber, "mibUpload", client)
+ case omci.MibUploadResponseType:
+ mibUploadNext, _ := omcilib.CreateMibUploadNextRequest(o.getNextTid(false), o.seqNumber)
+ sendOmciMsg(mibUploadNext, o.PonPortID, o.ID, o.SerialNumber, "mibUploadNext", client)
+ case omci.MibUploadNextResponseType:
+ o.seqNumber++
+
+ if o.seqNumber > 290 {
+ // NOTE we are done with the MIB Upload (290 is the number of messages the omci-sim library will respond to)
+ galEnet, _ := omcilib.CreateGalEnetRequest(o.getNextTid(false))
+ sendOmciMsg(galEnet, o.PonPortID, o.ID, o.SerialNumber, "CreateGalEnetRequest", client)
+ } else {
+ mibUploadNext, _ := omcilib.CreateMibUploadNextRequest(o.getNextTid(false), o.seqNumber)
+ sendOmciMsg(mibUploadNext, o.PonPortID, o.ID, o.SerialNumber, "mibUploadNext", client)
+ }
+ case omci.CreateResponseType:
+ // NOTE Creating a GemPort,
+ // BBsim actually doesn't care about the values, so we can do we want with the parameters
+ // In the same way we can create a GemPort even without setting up UNIs/TConts/...
+ // but we need the GemPort to trigger the state change
+
+ if !o.HasGemPort {
+ // NOTE this sends a CreateRequestType and BBSim replies with a CreateResponseType
+ // thus we send this request only once
+ gemReq, _ := omcilib.CreateGemPortRequest(o.getNextTid(false))
+ sendOmciMsg(gemReq, o.PonPortID, o.ID, o.SerialNumber, "CreateGemPortRequest", client)
+ o.HasGemPort = true
+ } else {
+ if err := o.InternalState.Event("send_eapol_flow"); err != nil {
+ onuLogger.WithFields(log.Fields{
+ "OnuId": o.ID,
+ "IntfId": o.PonPortID,
+ "OnuSn": o.Sn(),
+ }).Errorf("Error while transitioning ONU State %v", err)
+ }
+ }
+
+ }
+}
+
+func (o *Onu) sendEapolFlow(client openolt.OpenoltClient) {
+
+ classifierProto := openolt.Classifier{
+ EthType: uint32(layers.EthernetTypeEAPOL),
+ OVid: 4091,
+ }
+
+ actionProto := openolt.Action{}
+
+ downstreamFlow := openolt.Flow{
+ AccessIntfId: int32(o.PonPortID),
+ OnuId: int32(o.ID),
+ UniId: int32(0x101), // FIXME do not hardcode this
+ FlowId: uint32(o.ID),
+ FlowType: "downstream",
+ AllocId: int32(0),
+ NetworkIntfId: int32(0),
+ GemportId: int32(1), // FIXME use the same value as CreateGemPortRequest PortID, do not hardcode
+ Classifier: &classifierProto,
+ Action: &actionProto,
+ Priority: int32(100),
+ Cookie: uint64(o.ID),
+ PortNo: uint32(o.ID), // NOTE we are using this to map an incoming packetIndication to an ONU
+ }
+
+ if _, err := client.FlowAdd(context.Background(), &downstreamFlow); err != nil {
+ log.WithFields(log.Fields{
+ "IntfId": o.PonPortID,
+ "OnuId": o.ID,
+ "FlowId": downstreamFlow.FlowId,
+ "PortNo": downstreamFlow.PortNo,
+ "SerialNumber": common.OnuSnToString(o.SerialNumber),
+ }).Fatalf("Failed to EAPOL Flow")
+ }
+ log.WithFields(log.Fields{
+ "IntfId": o.PonPortID,
+ "OnuId": o.ID,
+ "FlowId": downstreamFlow.FlowId,
+ "PortNo": downstreamFlow.PortNo,
+ "SerialNumber": common.OnuSnToString(o.SerialNumber),
+ }).Info("Sent EAPOL Flow")
+}
+
+func (o *Onu) sendDhcpFlow(client openolt.OpenoltClient) {
+ classifierProto := openolt.Classifier{
+ EthType: uint32(layers.EthernetTypeIPv4),
+ SrcPort: uint32(68),
+ DstPort: uint32(67),
+ }
+
+ actionProto := openolt.Action{}
+
+ downstreamFlow := openolt.Flow{
+ AccessIntfId: int32(o.PonPortID),
+ OnuId: int32(o.ID),
+ UniId: int32(0x101), // FIXME do not hardcode this
+ FlowId: uint32(o.ID),
+ FlowType: "downstream",
+ AllocId: int32(0),
+ NetworkIntfId: int32(0),
+ GemportId: int32(1), // FIXME use the same value as CreateGemPortRequest PortID, do not hardcode
+ Classifier: &classifierProto,
+ Action: &actionProto,
+ Priority: int32(100),
+ Cookie: uint64(o.ID),
+ PortNo: uint32(o.ID), // NOTE we are using this to map an incoming packetIndication to an ONU
+ }
+
+ if _, err := client.FlowAdd(context.Background(), &downstreamFlow); err != nil {
+ log.WithFields(log.Fields{
+ "IntfId": o.PonPortID,
+ "OnuId": o.ID,
+ "FlowId": downstreamFlow.FlowId,
+ "PortNo": downstreamFlow.PortNo,
+ "SerialNumber": common.OnuSnToString(o.SerialNumber),
+ }).Fatalf("Failed to send DHCP Flow")
+ }
+ log.WithFields(log.Fields{
+ "IntfId": o.PonPortID,
+ "OnuId": o.ID,
+ "FlowId": downstreamFlow.FlowId,
+ "PortNo": downstreamFlow.PortNo,
+ "SerialNumber": common.OnuSnToString(o.SerialNumber),
+ }).Info("Sent DHCP Flow")
+}
diff --git a/internal/bbsim/devices/pon.go b/internal/bbsim/devices/pon.go
index 374b1b9..f27510f 100644
--- a/internal/bbsim/devices/pon.go
+++ b/internal/bbsim/devices/pon.go
@@ -38,7 +38,7 @@
// NOTE do we need a state machine for the PON Ports?
}
-func (p PonPort) getOnuBySn(sn *openolt.SerialNumber) (*Onu, error) {
+func (p PonPort) GetOnuBySn(sn *openolt.SerialNumber) (*Onu, error) {
for _, onu := range p.Onus {
if bytes.Equal(onu.SerialNumber.VendorSpecific, sn.VendorSpecific) {
return onu, nil
@@ -47,7 +47,7 @@
return nil, errors.New(fmt.Sprintf("Cannot find Onu with serial number %d in PonPort %d", sn, p.ID))
}
-func (p PonPort) getOnuById(id uint32) (*Onu, error) {
+func (p PonPort) GetOnuById(id uint32) (*Onu, error) {
for _, onu := range p.Onus {
if onu.ID == id {
return onu, nil
diff --git a/internal/bbsim/packetHandlers/filters.go b/internal/bbsim/packetHandlers/filters.go
index 0748839..b43b677 100644
--- a/internal/bbsim/packetHandlers/filters.go
+++ b/internal/bbsim/packetHandlers/filters.go
@@ -39,14 +39,14 @@
ip, _ := ipLayer.(*layers.IPv4)
// FIXME find a better way to filter outgoing packets
- if ip.SrcIP.Equal(net.ParseIP("182.21.0.128")) {
+ if ip.SrcIP.Equal(net.ParseIP("192.168.254.1")) {
return true
}
}
return false
}
-// returns the Desctination Mac Address contained in the packet
+// returns the Destination Mac Address contained in the packet
func GetDstMacAddressFromPacket(packet gopacket.Packet) (net.HardwareAddr, error) {
if ethLayer := packet.Layer(layers.LayerTypeEthernet); ethLayer != nil {
eth, _ := ethLayer.(*layers.Ethernet)
@@ -57,3 +57,25 @@
}
return nil, errors.New("cant-find-mac-address")
}
+
+// returns the Source Mac Address contained in the packet
+func GetSrcMacAddressFromPacket(packet gopacket.Packet) (net.HardwareAddr, error) {
+ if ethLayer := packet.Layer(layers.LayerTypeEthernet); ethLayer != nil {
+ eth, _ := ethLayer.(*layers.Ethernet)
+
+ if eth.DstMAC != nil {
+ return eth.SrcMAC, nil
+ }
+ }
+ return nil, errors.New("cant-find-mac-address")
+}
+
+// returns wether it's an EAPOL or DHCP packet, error if it's none
+func IsEapolOrDhcp(pkt gopacket.Packet) (PacketType, error) {
+ if pkt.Layer(layers.LayerTypeEAP) != nil || pkt.Layer(layers.LayerTypeEAPOL) != nil {
+ return EAPOL, nil
+ } else if IsDhcpPacket(pkt) {
+ return DHCP, nil
+ }
+ return UNKNOWN, errors.New("packet-is-neither-eapol-or-dhcp")
+}
diff --git a/internal/bbsim/packetHandlers/filters_test.go b/internal/bbsim/packetHandlers/filters_test.go
index 6d82495..6064e68 100644
--- a/internal/bbsim/packetHandlers/filters_test.go
+++ b/internal/bbsim/packetHandlers/filters_test.go
@@ -64,7 +64,7 @@
func Test_IsIncomingPacket_True(t *testing.T) {
eth := &layers.IPv4{
- SrcIP: net.ParseIP("182.21.0.128"),
+ SrcIP: net.ParseIP("192.168.254.1"),
DstIP: net.ParseIP("182.21.0.122"),
}
@@ -84,7 +84,7 @@
func Test_IsIncomingPacket_False(t *testing.T) {
eth := &layers.IPv4{
SrcIP: net.ParseIP("182.21.0.122"),
- DstIP: net.ParseIP("182.21.0.128"),
+ DstIP: net.ParseIP("192.168.254.1"),
}
buffer := gopacket.NewSerializeBuffer()
diff --git a/internal/bbsim/packetHandlers/packetTypes.go b/internal/bbsim/packetHandlers/packetTypes.go
new file mode 100644
index 0000000..fe424f0
--- /dev/null
+++ b/internal/bbsim/packetHandlers/packetTypes.go
@@ -0,0 +1,34 @@
+/*
+ * 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 packetHandlers
+
+type PacketType int
+
+const (
+ UNKNOWN PacketType = iota
+ EAPOL
+ DHCP
+)
+
+func (t PacketType) String() string {
+ names := [...]string{
+ "UNKNOWN",
+ "EAPOL",
+ "DHCP",
+ }
+ return names[t]
+}
diff --git a/internal/bbsim/responders/dhcp/dhcp.go b/internal/bbsim/responders/dhcp/dhcp.go
index c7ae75c..25f5210 100644
--- a/internal/bbsim/responders/dhcp/dhcp.go
+++ b/internal/bbsim/responders/dhcp/dhcp.go
@@ -17,11 +17,13 @@
package dhcp
import (
+ "context"
"errors"
"fmt"
"github.com/google/gopacket"
"github.com/google/gopacket/layers"
"github.com/looplab/fsm"
+ "github.com/opencord/bbsim/internal/bbsim/packetHandlers"
bbsim "github.com/opencord/bbsim/internal/bbsim/types"
omci "github.com/opencord/omci-sim"
"github.com/opencord/voltha-protos/go/openolt"
@@ -52,14 +54,14 @@
layers.DHCPOptNTPServers,
}
-func createDefaultDHCPReq(intfId uint32, onuId uint32) layers.DHCPv4 {
+func createDefaultDHCPReq(intfId uint32, onuId uint32, mac net.HardwareAddr) 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)},
+ ClientHWAddr: mac,
}
}
@@ -85,8 +87,8 @@
return opts
}
-func createDHCPDisc(intfId uint32, onuId uint32) *layers.DHCPv4 {
- dhcpLayer := createDefaultDHCPReq(intfId, onuId)
+func createDHCPDisc(intfId uint32, onuId uint32, macAddress net.HardwareAddr) *layers.DHCPv4 {
+ dhcpLayer := createDefaultDHCPReq(intfId, onuId, macAddress)
defaultOpts := createDefaultOpts()
dhcpLayer.Options = append([]layers.DHCPOption{layers.DHCPOption{
Type: layers.DHCPOptMessageType,
@@ -94,11 +96,19 @@
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)),
+ //})
+
return &dhcpLayer
}
-func createDHCPReq(intfId uint32, onuId uint32) *layers.DHCPv4 {
- dhcpLayer := createDefaultDHCPReq(intfId, onuId)
+func createDHCPReq(intfId uint32, onuId uint32, macAddress net.HardwareAddr, offeredIp net.IP) *layers.DHCPv4 {
+ dhcpLayer := createDefaultDHCPReq(intfId, onuId, macAddress)
defaultOpts := createDefaultOpts()
dhcpLayer.Options = append(defaultOpts, layers.DHCPOption{
@@ -115,18 +125,18 @@
})
data = []byte{0xcd, 0x28, 0xcb, 0xcc, 0x00, 0x01, 0x00, 0x01,
- 0x23, 0xed, 0x11, 0xec, 0x4e, 0xfc, 0xcd, 0x28, 0xcb, 0xcc}
+ 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{182, 21, 0, byte(onuId)}
+ // NOTE we should not request a specific IP, or we should request the one that has been offered
dhcpLayer.Options = append(dhcpLayer.Options, layers.DHCPOption{
Type: layers.DHCPOptRequestIP,
- Data: data,
- Length: uint8(len(data)),
+ Data: offeredIp,
+ Length: uint8(len(offeredIp)),
})
return &dhcpLayer
}
@@ -167,7 +177,7 @@
return bytes, nil
}
-func getDhcpLayer(pkt gopacket.Packet) (*layers.DHCPv4, error) {
+func GetDhcpLayer(pkt gopacket.Packet) (*layers.DHCPv4, error) {
layerDHCP := pkt.Layer(layers.LayerTypeDHCPv4)
dhcp, _ := layerDHCP.(*layers.DHCPv4)
if dhcp == nil {
@@ -176,11 +186,15 @@
return dhcp, nil
}
-func getDhcpMessageType(dhcp *layers.DHCPv4) (layers.DHCPMsgType, error) {
+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)}) {
+ if reflect.DeepEqual(option.Data, []byte{byte(layers.DHCPMsgTypeDiscover)}) {
+ return layers.DHCPMsgTypeDiscover, nil
+ } else if reflect.DeepEqual(option.Data, []byte{byte(layers.DHCPMsgTypeOffer)}) {
return layers.DHCPMsgTypeOffer, nil
+ } else if reflect.DeepEqual(option.Data, []byte{byte(layers.DHCPMsgTypeRequest)}) {
+ return layers.DHCPMsgTypeRequest, 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)}) {
@@ -194,6 +208,20 @@
return 0, errors.New("Failed to extract MsgType from dhcp")
}
+// returns the DHCP Layer type or error if it's not a DHCP Packet
+func GetDhcpPacketType(pkt gopacket.Packet) (string, error) {
+ dhcpLayer, err := GetDhcpLayer(pkt)
+ if err != nil {
+ return "", err
+ }
+ dhcpMessageType, err := GetDhcpMessageType(dhcpLayer)
+ if err != nil {
+ return "", err
+ }
+
+ return dhcpMessageType.String(), nil
+}
+
func sendDHCPPktIn(msg bbsim.ByteMsg, portNo uint32, stream bbsim.Stream) error {
// FIXME unify sendDHCPPktIn and sendEapolPktIn methods
gemid, err := GetGemPortId(msg.IntfId, msg.OnuId)
@@ -219,8 +247,8 @@
return nil
}
-func sendDHCPRequest(ponPortId uint32, onuId uint32, serialNumber string, portNo uint32, onuHwAddress net.HardwareAddr, cTag int, stream openolt.Openolt_EnableIndicationServer) error {
- dhcp := createDHCPReq(ponPortId, onuId)
+func sendDHCPRequest(ponPortId uint32, onuId uint32, serialNumber string, portNo uint32, 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 {
@@ -239,11 +267,13 @@
if err := sendDHCPPktIn(msg, portNo, stream); err != nil {
return err
}
+
dhcpLogger.WithFields(log.Fields{
- "OnuId": onuId,
- "IntfId": ponPortId,
- "OnuSn": serialNumber,
- }).Infof("DHCPDiscovery Sent")
+ "OnuId": onuId,
+ "IntfId": ponPortId,
+ "OnuSn": serialNumber,
+ "OfferedIp": offeredIp.String(),
+ }).Infof("DHCPRequest Sent")
return nil
}
@@ -260,7 +290,7 @@
}
func SendDHCPDiscovery(ponPortId uint32, onuId uint32, serialNumber string, portNo uint32, onuStateMachine *fsm.FSM, onuHwAddress net.HardwareAddr, cTag int, stream bbsim.Stream) error {
- dhcp := createDHCPDisc(ponPortId, onuId)
+ dhcp := createDHCPDisc(ponPortId, onuId, onuHwAddress)
pkt, err := serializeDHCPPacket(ponPortId, onuId, onuHwAddress, dhcp)
if err != nil {
dhcpLogger.Errorf("Cannot serializeDHCPPacket: %s", err)
@@ -299,7 +329,7 @@
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)
+ dhcpLayer, err := GetDhcpLayer(pkt)
if err != nil {
dhcpLogger.WithFields(log.Fields{
"OnuId": onuId,
@@ -311,7 +341,7 @@
}
return err
}
- dhcpMessageType, err := getDhcpMessageType(dhcpLayer)
+ dhcpMessageType, err := GetDhcpMessageType(dhcpLayer)
if err != nil {
dhcpLogger.WithFields(log.Fields{
"OnuId": onuId,
@@ -326,7 +356,8 @@
if dhcpLayer.Operation == layers.DHCPOpReply {
if dhcpMessageType == layers.DHCPMsgTypeOffer {
- if err := sendDHCPRequest(ponPortId, onuId, serialNumber, portNo, onuHwAddress, cTag, stream); err != nil {
+ offeredIp := dhcpLayer.YourClientIP
+ if err := sendDHCPRequest(ponPortId, onuId, serialNumber, portNo, onuHwAddress, offeredIp, stream); err != nil {
dhcpLogger.WithFields(log.Fields{
"OnuId": onuId,
"IntfId": ponPortId,
@@ -370,3 +401,94 @@
}
return nil
}
+
+// This method handle the BBR DHCP Packets
+// BBR does not need to do anything but forward the packets in the correct direction
+func HandleNextBbrPacket(onuId uint32, ponPortId uint32, serialNumber string, sTag int, macAddress net.HardwareAddr, doneChannel chan bool, pkt gopacket.Packet, client openolt.OpenoltClient) error {
+
+ // check if the packet is going:
+ // - outgouing: toward the DHCP
+ // - incoming: toward the ONU
+ isIncoming := packetHandlers.IsIncomingPacket(pkt)
+ log.Tracef("Is Incoming: %t", isIncoming)
+
+ dhcpType, err := GetDhcpPacketType(pkt)
+ if err != nil {
+ log.WithFields(log.Fields{
+ "err": err,
+ }).Fatalf("Can't find DHCP type for packet")
+ }
+
+ srcMac, _ := packetHandlers.GetSrcMacAddressFromPacket(pkt)
+ dstMac, _ := packetHandlers.GetDstMacAddressFromPacket(pkt)
+
+ if isIncoming == true {
+
+ onuPacket := openolt.OnuPacket{
+ IntfId: ponPortId,
+ OnuId: onuId,
+ PortNo: onuId,
+ GemportId: 1,
+ Pkt: pkt.Data(),
+ }
+
+ if _, err := client.OnuPacketOut(context.Background(), &onuPacket); err != nil {
+ log.WithFields(log.Fields{
+ "OnuId": onuId,
+ "IntfId": ponPortId,
+ "OnuSn": serialNumber,
+ "Type": dhcpType,
+ "error": err,
+ }).Error("Failed to send DHCP packet to the ONU")
+ }
+
+ log.WithFields(log.Fields{
+ "OnuId": onuId,
+ "IntfId": ponPortId,
+ "OnuSn": serialNumber,
+ "Type": dhcpType,
+ "DstMac": dstMac,
+ "SrcMac": srcMac,
+ "OnuMac": macAddress,
+ }).Infof("Sent DHCP packet to the ONU")
+
+ // TODO: signal that the ONU has completed
+ dhcpLayer, _ := GetDhcpLayer(pkt)
+ dhcpMessageType, _ := GetDhcpMessageType(dhcpLayer)
+ if dhcpMessageType == layers.DHCPMsgTypeAck {
+ doneChannel <- true
+ }
+
+ } else {
+ // double tag the packet and send it to the NNI
+ // NOTE do we need this in the HandleDHCP Packet?
+ doubleTaggedPkt, err := packetHandlers.PushDoubleTag(sTag, sTag, pkt)
+ if err != nil {
+ log.Error("Failt to add double tag to packet")
+ }
+
+ pkt := openolt.UplinkPacket{
+ IntfId: 0, // BBSim does not care about which NNI, it has only one
+ Pkt: doubleTaggedPkt.Data(),
+ }
+ if _, err := client.UplinkPacketOut(context.Background(), &pkt); err != nil {
+ log.WithFields(log.Fields{
+ "OnuId": onuId,
+ "IntfId": ponPortId,
+ "OnuSn": serialNumber,
+ "Type": dhcpType,
+ "error": err,
+ }).Error("Failed to send DHCP packet out of the NNI Port")
+ }
+ log.WithFields(log.Fields{
+ "OnuId": onuId,
+ "IntfId": ponPortId,
+ "OnuSn": serialNumber,
+ "Type": dhcpType,
+ "DstMac": dstMac,
+ "SrcMac": srcMac,
+ "OnuMac": macAddress,
+ }).Infof("Sent DHCP packet out of the NNI Port")
+ }
+ return nil
+}
diff --git a/internal/bbsim/responders/eapol/eapol.go b/internal/bbsim/responders/eapol/eapol.go
index f31a068..c13aa69 100644
--- a/internal/bbsim/responders/eapol/eapol.go
+++ b/internal/bbsim/responders/eapol/eapol.go
@@ -17,6 +17,7 @@
package eapol
import (
+ "context"
"crypto/md5"
"errors"
"github.com/google/gopacket"
@@ -72,6 +73,17 @@
return ret
}
+func createEAPChallengeRequest(eapId uint8, payload []byte) *layers.EAP {
+ eap := layers.EAP{
+ Code: layers.EAPCodeRequest,
+ Id: eapId,
+ Length: 22,
+ Type: layers.EAPTypeOTP,
+ TypeData: payload,
+ }
+ return &eap
+}
+
func createEAPChallengeResponse(eapId uint8, payload []byte) *layers.EAP {
eap := layers.EAP{
Code: layers.EAPCodeResponse,
@@ -83,6 +95,15 @@
return &eap
}
+func createEAPIdentityRequest(eapId uint8) *layers.EAP {
+ eap := layers.EAP{Code: layers.EAPCodeRequest,
+ Id: eapId,
+ Length: 9,
+ Type: layers.EAPTypeIdentity,
+ TypeData: []byte{0x75, 0x73, 0x65, 0x72}}
+ return &eap
+}
+
func createEAPIdentityResponse(eapId uint8) *layers.EAP {
eap := layers.EAP{Code: layers.EAPCodeResponse,
Id: eapId,
@@ -92,6 +113,16 @@
return &eap
}
+func createEAPSuccess(eapId uint8) *layers.EAP {
+ eap := layers.EAP{
+ Code: layers.EAPCodeSuccess,
+ Id: eapId,
+ Length: 9,
+ Type: layers.EAPTypeNone,
+ TypeData: []byte{0x75, 0x73, 0x65, 0x72}}
+ return &eap
+}
+
func createEAPOLPkt(eap *layers.EAP, onuId uint32, intfId uint32) []byte {
buffer := gopacket.NewSerializeBuffer()
options := gopacket.SerializeOptions{}
@@ -121,6 +152,30 @@
return eap, nil
}
+func extractEAPOL(pkt gopacket.Packet) (*layers.EAPOL, error) {
+ layerEAPOL := pkt.Layer(layers.LayerTypeEAPOL)
+ eap, _ := layerEAPOL.(*layers.EAPOL)
+ if eap == nil {
+ return nil, errors.New("Cannot extract EAPOL")
+ }
+ return eap, nil
+}
+
+func sendEapolPktOut(client openolt.OpenoltClient, intfId uint32, onuId uint32, pkt []byte) error {
+ onuPacket := openolt.OnuPacket{
+ IntfId: intfId,
+ OnuId: onuId,
+ PortNo: onuId,
+ GemportId: 1,
+ Pkt: pkt,
+ }
+
+ if _, err := client.OnuPacketOut(context.Background(), &onuPacket); err != nil {
+ return err
+ }
+ return nil
+}
+
func updateAuthFailed(onuId uint32, ponPortId uint32, serialNumber string, onuStateMachine *fsm.FSM) error {
if err := onuStateMachine.Event("auth_failed"); err != nil {
eapolLogger.WithFields(log.Fields{
@@ -210,22 +265,58 @@
return nil
}
-func HandleNextPacket(onuId uint32, ponPortId uint32, serialNumber string, portNo uint32, onuStateMachine *fsm.FSM, recvpkt gopacket.Packet, stream openolt.Openolt_EnableIndicationServer) {
+func HandleNextPacket(onuId uint32, ponPortId uint32, serialNumber string, portNo uint32, onuStateMachine *fsm.FSM, pkt gopacket.Packet, stream openolt.Openolt_EnableIndicationServer, client openolt.OpenoltClient) {
- eap, err := extractEAP(recvpkt)
- if err != nil {
- eapolLogger.Errorf("%s", err)
+ eap, eapErr := extractEAP(pkt)
+
+ eapol, eapolErr := extractEAPOL(pkt)
+
+ if eapErr != nil && eapolErr != nil {
+ log.Fatalf("Failed to Extract EAP: %v - %v", eapErr, eapolErr)
+ return
}
- log.WithFields(log.Fields{
- "eap.Code": eap.Code,
- "eap.Type": eap.Type,
- "OnuId": onuId,
- "IntfId": ponPortId,
- "OnuSn": serialNumber,
- }).Tracef("HandleNextPacket")
+ fields := log.Fields{}
+ if eap != nil {
+ fields = log.Fields{
+ "Code": eap.Code,
+ "Type": eap.Type,
+ "OnuId": onuId,
+ "IntfId": ponPortId,
+ "OnuSn": serialNumber,
+ }
+ } else if eapol != nil {
+ fields = log.Fields{
+ "Type": eapol.Type,
+ "OnuId": onuId,
+ "IntfId": ponPortId,
+ "OnuSn": serialNumber,
+ }
+ }
- if eap.Code == layers.EAPCodeRequest && eap.Type == layers.EAPTypeIdentity {
+ log.WithFields(fields).Tracef("Handle Next EAPOL Packet")
+
+ if eapol != nil && eapol.Type == layers.EAPOLTypeStart {
+ identityRequest := createEAPIdentityRequest(1)
+ pkt := createEAPOLPkt(identityRequest, onuId, ponPortId)
+
+ if err := sendEapolPktOut(client, ponPortId, onuId, pkt); err != nil {
+ log.WithFields(log.Fields{
+ "OnuId": onuId,
+ "IntfId": ponPortId,
+ "OnuSn": serialNumber,
+ "error": err,
+ }).Errorf("Error while sending EAPIdentityRequest packet")
+ return
+ }
+
+ log.WithFields(log.Fields{
+ "OnuId": onuId,
+ "IntfId": ponPortId,
+ "OnuSn": serialNumber,
+ }).Infof("Sent EAPIdentityRequest packet")
+ return
+ } else if eap.Code == layers.EAPCodeRequest && eap.Type == layers.EAPTypeIdentity {
reseap := createEAPIdentityResponse(eap.Id)
pkt := createEAPOLPkt(reseap, onuId, ponPortId)
@@ -250,6 +341,27 @@
}).Errorf("Error while transitioning ONU State %v", err)
}
+ } else if eap.Code == layers.EAPCodeResponse && eap.Type == layers.EAPTypeIdentity {
+ senddata := getMD5Data(eap)
+ senddata = append([]byte{0x10}, senddata...)
+ challengeRequest := createEAPChallengeRequest(eap.Id, senddata)
+ pkt := createEAPOLPkt(challengeRequest, onuId, ponPortId)
+
+ if err := sendEapolPktOut(client, ponPortId, onuId, pkt); err != nil {
+ log.WithFields(log.Fields{
+ "OnuId": onuId,
+ "IntfId": ponPortId,
+ "OnuSn": serialNumber,
+ "error": err,
+ }).Errorf("Error while sending EAPChallengeRequest packet")
+ return
+ }
+ log.WithFields(log.Fields{
+ "OnuId": onuId,
+ "IntfId": ponPortId,
+ "OnuSn": serialNumber,
+ }).Infof("Sent EAPChallengeRequest packet")
+ return
} else if eap.Code == layers.EAPCodeRequest && eap.Type == layers.EAPTypeOTP {
senddata := getMD5Data(eap)
senddata = append([]byte{0x10}, senddata...)
@@ -276,6 +388,33 @@
"OnuSn": serialNumber,
}).Errorf("Error while transitioning ONU State %v", err)
}
+ } else if eap.Code == layers.EAPCodeResponse && eap.Type == layers.EAPTypeOTP {
+ eapSuccess := createEAPSuccess(eap.Id)
+ pkt := createEAPOLPkt(eapSuccess, onuId, ponPortId)
+
+ if err := sendEapolPktOut(client, ponPortId, onuId, pkt); err != nil {
+ log.WithFields(log.Fields{
+ "OnuId": onuId,
+ "IntfId": ponPortId,
+ "OnuSn": serialNumber,
+ "error": err,
+ }).Errorf("Error while sending EAPSuccess packet")
+ return
+ }
+
+ log.WithFields(log.Fields{
+ "OnuId": onuId,
+ "IntfId": ponPortId,
+ "OnuSn": serialNumber,
+ }).Infof("Sent EAP Success packet")
+
+ if err := onuStateMachine.Event("send_dhcp_flow"); err != nil {
+ eapolLogger.WithFields(log.Fields{
+ "OnuId": onuId,
+ "IntfId": ponPortId,
+ "OnuSn": serialNumber,
+ }).Errorf("Error while transitioning ONU State %v", err)
+ }
} else if eap.Code == layers.EAPCodeSuccess && eap.Type == layers.EAPTypeNone {
eapolLogger.WithFields(log.Fields{
"OnuId": onuId,
diff --git a/internal/bbsimctl/commands/onu.go b/internal/bbsimctl/commands/onu.go
index 4f222f2..716c7d3 100644
--- a/internal/bbsimctl/commands/onu.go
+++ b/internal/bbsimctl/commands/onu.go
@@ -31,7 +31,7 @@
)
const (
- DEFAULT_ONU_DEVICE_HEADER_FORMAT = "table{{ .PonPortID }}\t{{ .ID }}\t{{ .PortNo }}\t{{ .SerialNumber }}\t{{ .STag }}\t{{ .CTag }}\t{{ .OperState }}\t{{ .InternalState }}"
+ DEFAULT_ONU_DEVICE_HEADER_FORMAT = "table{{ .PonPortID }}\t{{ .ID }}\t{{ .PortNo }}\t{{ .SerialNumber }}\t{{ .HwAddress }}\t{{ .STag }}\t{{ .CTag }}\t{{ .OperState }}\t{{ .InternalState }}"
)
type OnuSnString string
diff --git a/internal/common/helpers.go b/internal/common/helpers.go
new file mode 100644
index 0000000..6ba022f
--- /dev/null
+++ b/internal/common/helpers.go
@@ -0,0 +1,30 @@
+/*
+ * 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 common
+
+import (
+ "github.com/opencord/voltha-protos/go/openolt"
+ "strconv"
+)
+
+func OnuSnToString(sn *openolt.SerialNumber) string {
+ s := string(sn.VendorId)
+ for _, i := range sn.VendorSpecific {
+ s = s + strconv.FormatInt(int64(i/16), 16) + strconv.FormatInt(int64(i%16), 16)
+ }
+ return s
+}
diff --git a/internal/bbsim/logger/logger.go b/internal/common/logger.go
similarity index 98%
rename from internal/bbsim/logger/logger.go
rename to internal/common/logger.go
index c6036fc..cc6e48d 100644
--- a/internal/bbsim/logger/logger.go
+++ b/internal/common/logger.go
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package logger
+package common
import log "github.com/sirupsen/logrus"
diff --git a/internal/bbsim/logger/logger_test.go b/internal/common/logger_test.go
similarity index 72%
rename from internal/bbsim/logger/logger_test.go
rename to internal/common/logger_test.go
index d692135..6e9de8a 100644
--- a/internal/bbsim/logger/logger_test.go
+++ b/internal/common/logger_test.go
@@ -14,10 +14,10 @@
* limitations under the License.
*/
-package logger_test
+package common_test
import (
- bbsimLogger "github.com/opencord/bbsim/internal/bbsim/logger"
+ "github.com/opencord/bbsim/internal/common"
"github.com/sirupsen/logrus"
"gotest.tools/assert"
"testing"
@@ -26,31 +26,31 @@
func Test_SetLogLevel(t *testing.T) {
log := logrus.New()
- bbsimLogger.SetLogLevel(log, "trace", false)
+ common.SetLogLevel(log, "trace", false)
assert.Equal(t, log.Level, logrus.TraceLevel)
- bbsimLogger.SetLogLevel(log, "debug", false)
+ common.SetLogLevel(log, "debug", false)
assert.Equal(t, log.Level, logrus.DebugLevel)
- bbsimLogger.SetLogLevel(log, "info", false)
+ common.SetLogLevel(log, "info", false)
assert.Equal(t, log.Level, logrus.InfoLevel)
- bbsimLogger.SetLogLevel(log, "warn", false)
+ common.SetLogLevel(log, "warn", false)
assert.Equal(t, log.Level, logrus.WarnLevel)
- bbsimLogger.SetLogLevel(log, "error", false)
+ common.SetLogLevel(log, "error", false)
assert.Equal(t, log.Level, logrus.ErrorLevel)
- bbsimLogger.SetLogLevel(log, "foobar", false)
+ common.SetLogLevel(log, "foobar", false)
assert.Equal(t, log.Level, logrus.DebugLevel)
}
func Test_SetLogLevelCaller(t *testing.T) {
log := logrus.New()
- bbsimLogger.SetLogLevel(log, "debug", true)
+ common.SetLogLevel(log, "debug", true)
assert.Equal(t, log.ReportCaller, true)
- bbsimLogger.SetLogLevel(log, "debug", false)
+ common.SetLogLevel(log, "debug", false)
assert.Equal(t, log.ReportCaller, false)
}
diff --git a/internal/common/omci/mibpackets.go b/internal/common/omci/mibpackets.go
new file mode 100755
index 0000000..99ebb56
--- /dev/null
+++ b/internal/common/omci/mibpackets.go
@@ -0,0 +1,238 @@
+/*
+ * 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 omci
+
+import (
+ "encoding/hex"
+ "github.com/cboling/omci"
+ me "github.com/cboling/omci/generated"
+ "github.com/google/gopacket"
+ omcisim "github.com/opencord/omci-sim"
+ log "github.com/sirupsen/logrus"
+)
+
+var omciLogger = log.WithFields(log.Fields{
+ "module": "OMCI",
+})
+
+const galEthernetEID = uint16(1)
+const maxGemPayloadSize = uint16(48)
+const gemEID = uint16(1)
+
+type txFrameCreator func() ([]byte, error)
+type rxFrameParser func(gopacket.Packet) error
+
+type ServiceStep struct {
+ MakeTxFrame txFrameCreator
+ RxHandler rxFrameParser
+}
+
+func serialize(msgType omci.MessageType, request gopacket.SerializableLayer, tid uint16) ([]byte, error) {
+ omciLayer := &omci.OMCI{
+ TransactionID: tid,
+ MessageType: msgType,
+ }
+ var options gopacket.SerializeOptions
+ options.FixLengths = true
+
+ buffer := gopacket.NewSerializeBuffer()
+ err := gopacket.SerializeLayers(buffer, options, omciLayer, request)
+ if err != nil {
+ return nil, err
+ }
+ return buffer.Bytes(), nil
+}
+
+func hexEncode(omciPkt []byte) ([]byte, error) {
+ dst := make([]byte, hex.EncodedLen(len(omciPkt)))
+ hex.Encode(dst, omciPkt)
+ return dst, nil
+}
+
+func DecodeOmci(payload []byte) (omci.MessageType, gopacket.Packet) {
+ // Perform base OMCI decode (common to all requests)
+ packet := gopacket.NewPacket(payload, omci.LayerTypeOMCI, gopacket.NoCopy)
+
+ if omciLayer := packet.Layer(omci.LayerTypeOMCI); omciLayer != nil {
+
+ omciObj, omciOk := omciLayer.(*omci.OMCI)
+ if !omciOk {
+ panic("Not Expected") // TODO: Do something better or delete...
+ }
+ if byte(omciObj.MessageType) & ^me.AK == 0 {
+ // Not a response, silently discard
+ return 0, nil
+ }
+ return omciObj.MessageType, packet
+ }
+
+ // FIXME
+ // if we can't properly decode the packet, try using shad helper method
+ // most likely this won't be necessary once we move omci-sim to use cboling/omci
+ // to generate packets
+ _, _, msgType, _, _, _, err := omcisim.ParsePkt(payload)
+ if err != nil {
+ return 0, nil
+ }
+ if msgType == omcisim.MibReset {
+ return omci.MibResetResponseType, nil
+ }
+ if msgType == omcisim.MibUpload {
+ return omci.MibUploadResponseType, nil
+ }
+ if msgType == omcisim.MibUploadNext {
+ return omci.MibUploadNextResponseType, nil
+ }
+ if msgType == omcisim.Create {
+ return omci.CreateResponseType, nil
+ }
+ if msgType == omcisim.Set {
+ return omci.SetResponseType, nil
+ }
+
+ omciLogger.Warnf("omci-sim returns msgType: %d", msgType)
+
+ return 0, nil
+}
+
+func CreateMibResetRequest(tid uint16) ([]byte, error) {
+
+ request := &omci.MibResetRequest{
+ MeBasePacket: omci.MeBasePacket{
+ EntityClass: me.OnuDataClassId,
+ },
+ }
+ pkt, err := serialize(omci.MibResetRequestType, request, tid)
+ if err != nil {
+ omciLogger.WithFields(log.Fields{
+ "Err": err,
+ }).Fatalf("Cannot serialize MibResetRequest")
+ return nil, err
+ }
+ return hexEncode(pkt)
+}
+
+func CreateMibUploadRequest(tid uint16) ([]byte, error) {
+ request := &omci.MibUploadRequest{
+ MeBasePacket: omci.MeBasePacket{
+ EntityClass: me.OnuDataClassId,
+ // Default Instance ID is 0
+ },
+ }
+ pkt, err := serialize(omci.MibUploadRequestType, request, tid)
+ if err != nil {
+ omciLogger.WithFields(log.Fields{
+ "Err": err,
+ }).Fatalf("Cannot serialize MibUploadRequest")
+ return nil, err
+ }
+ return hexEncode(pkt)
+}
+
+func CreateMibUploadNextRequest(tid uint16, seqNumber uint16) ([]byte, error) {
+
+ request := &omci.MibUploadNextRequest{
+ MeBasePacket: omci.MeBasePacket{
+ EntityClass: me.OnuDataClassId,
+ // Default Instance ID is 0
+ },
+ CommandSequenceNumber: seqNumber,
+ }
+ pkt, err := serialize(omci.MibUploadNextRequestType, request, tid)
+
+ if err != nil {
+ omciLogger.WithFields(log.Fields{
+ "Err": err,
+ }).Fatalf("Cannot serialize MibUploadNextRequest")
+ return nil, err
+ }
+ return hexEncode(pkt)
+}
+
+// TODO understand and refactor
+
+func CreateGalEnetRequest(tid uint16) ([]byte, error) {
+ params := me.ParamData{
+ EntityID: galEthernetEID,
+ Attributes: me.AttributeValueMap{"MaximumGemPayloadSize": maxGemPayloadSize},
+ }
+ meDef, _ := me.NewGalEthernetProfile(params)
+ pkt, err := omci.GenFrame(meDef, omci.CreateRequestType, omci.TransactionID(tid))
+ if err != nil {
+ omciLogger.WithField("err", err).Fatalf("Can't generate GalEnetRequest")
+ }
+ return hexEncode(pkt)
+}
+
+func CreateEnableUniRequest(tid uint16, uniId uint16, enabled bool, isPtp bool) ([]byte, error) {
+
+ var _enabled uint8
+ if enabled {
+ _enabled = uint8(1)
+ } else {
+ _enabled = uint8(0)
+ }
+
+ data := me.ParamData{
+ EntityID: uniId,
+ Attributes: me.AttributeValueMap{
+ "AdministrativeState": _enabled,
+ },
+ }
+ var medef *me.ManagedEntity
+ var omciErr me.OmciErrors
+
+ if isPtp {
+ medef, omciErr = me.NewPhysicalPathTerminationPointEthernetUni(data)
+ } else {
+ medef, omciErr = me.NewVirtualEthernetInterfacePoint(data)
+ }
+ if omciErr != nil {
+ return nil, omciErr.GetError()
+ }
+ pkt, err := omci.GenFrame(medef, omci.SetRequestType, omci.TransactionID(tid))
+ if err != nil {
+ omciLogger.WithField("err", err).Fatalf("Can't generate EnableUniRequest")
+ }
+ return hexEncode(pkt)
+}
+
+func CreateGemPortRequest(tid uint16) ([]byte, error) {
+ params := me.ParamData{
+ EntityID: gemEID,
+ Attributes: me.AttributeValueMap{
+ "PortId": 1,
+ "TContPointer": 1,
+ "Direction": 0,
+ "TrafficManagementPointerForUpstream": 0,
+ "TrafficDescriptorProfilePointerForUpstream": 0,
+ "UniCounter": 0,
+ "PriorityQueuePointerForDownStream": 0,
+ "EncryptionState": 0,
+ "TrafficDescriptorProfilePointerForDownstream": 0,
+ "EncryptionKeyRing": 0,
+ },
+ }
+ meDef, _ := me.NewGemPortNetworkCtp(params)
+ pkt, err := omci.GenFrame(meDef, omci.CreateRequestType, omci.TransactionID(tid))
+ if err != nil {
+ omciLogger.WithField("err", err).Fatalf("Can't generate GemPortRequest")
+ }
+ return hexEncode(pkt)
+}
+
+// END TODO
diff --git a/internal/common/options.go b/internal/common/options.go
new file mode 100644
index 0000000..46c8752
--- /dev/null
+++ b/internal/common/options.go
@@ -0,0 +1,88 @@
+/*
+ * 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 common
+
+import "flag"
+
+type BBSimCliOptions struct {
+ OltID int
+ NumNniPerOlt int
+ NumPonPerOlt int
+ NumOnuPerPon int
+ STag int
+ CTagInit int
+ ProfileCpu *string
+ LogLevel string
+ LogCaller bool
+}
+
+type BBRCliOptions struct {
+ *BBSimCliOptions
+ BBSimIp string
+ BBSimPort string
+ BBSimApiPort string
+}
+
+func GetBBSimOpts() *BBSimCliOptions {
+
+ olt_id := flag.Int("olt_id", 0, "Number of OLT devices to be emulated")
+ nni := flag.Int("nni", 1, "Number of NNI ports per OLT device to be emulated")
+ pon := flag.Int("pon", 1, "Number of PON ports per OLT device to be emulated")
+ onu := flag.Int("onu", 1, "Number of ONU devices per PON port to be emulated")
+
+ s_tag := flag.Int("s_tag", 900, "S-Tag value")
+ c_tag_init := flag.Int("c_tag", 900, "C-Tag starting value, each ONU will get a sequential one (targeting 1024 ONUs per BBSim instance the range is big enough)")
+
+ profileCpu := flag.String("cpuprofile", "", "write cpu profile to file")
+
+ logLevel := flag.String("logLevel", "debug", "Set the log level (trace, debug, info, warn, error)")
+ logCaller := flag.Bool("logCaller", false, "Whether to print the caller filename or not")
+
+ flag.Parse()
+
+ o := new(BBSimCliOptions)
+
+ o.OltID = int(*olt_id)
+ o.NumNniPerOlt = int(*nni)
+ o.NumPonPerOlt = int(*pon)
+ o.NumOnuPerPon = int(*onu)
+ o.STag = int(*s_tag)
+ o.CTagInit = int(*c_tag_init)
+ o.ProfileCpu = profileCpu
+ o.LogLevel = *logLevel
+ o.LogCaller = *logCaller
+
+ return o
+}
+
+func GetBBROpts() BBRCliOptions {
+
+ bbsimIp := flag.String("bbsimIp", "127.0.0.1", "BBSim IP")
+ bbsimPort := flag.String("bbsimPort", "50060", "BBSim Port")
+ bbsimApiPort := flag.String("bbsimApiPort", "50070", "BBSim API Port")
+
+ options := GetBBSimOpts()
+
+ bbrOptions := BBRCliOptions{
+ options,
+ *bbsimIp,
+ *bbsimPort,
+ *bbsimApiPort,
+ }
+
+ return bbrOptions
+}