Completed the EAPOL State machine
Stopping BBSim in the proper way when OLT-Reboot is received
Change-Id: I142eaa70e329121a5331f1a5706ed864fcf5d993
diff --git a/build/package/Dockerfile b/build/package/Dockerfile
index c3c0c87..977af52 100644
--- a/build/package/Dockerfile
+++ b/build/package/Dockerfile
@@ -31,7 +31,7 @@
&& go get -v github.com/grpc-ecosystem/grpc-gateway/protoc-gen-grpc-gateway \
&& go get -v github.com/golang/protobuf/protoc-gen-go
-WORKDIR /go/src/gerrit.opencord.org/bbsim
+WORKDIR /go/src/github.com/opencord/bbsim
ENV GO111MODULE=on
ENV GOPROXY=https://proxy.golang.org
@@ -73,6 +73,6 @@
&& touch /var/lib/dhcp/dhcpd.leases
WORKDIR /app
-COPY --from=builder /go/src/gerrit.opencord.org/bbsim/cmd/bbsim /app/bbsim
+COPY --from=builder /go/src/github.com/opencord/bbsim/cmd/bbsim /app/bbsim
RUN chmod a+x /app/bbsim
ENTRYPOINT [ '/app/bbsim' ]
diff --git a/go.sum b/go.sum
index 0cfff06..4ce9f98 100644
--- a/go.sum
+++ b/go.sum
@@ -2,12 +2,9 @@
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
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 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
-github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
-github.com/golang/protobuf v1.2.0 h1:P3YflyNX/ehuJFLhxviNdFxQPkGK5cDcApsge1SqnvM=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.2 h1:6nsPYzhq5kReh6QImI3k5qWzO4PEbvbIW2cwSfR/6xs=
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
@@ -29,22 +26,17 @@
github.com/opencord/voltha-protos v0.0.0-20190813191205-792553b747df/go.mod h1:MDGL9ai3XOPbiZ0tA8U7k4twK/T/P0Hh4gtjNxNk/qY=
github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
-github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/sirupsen/logrus v1.4.2 h1:SPIRibHv4MatM3XXNO2BJeFLZwZ2LvZgfQ5+UNI2im4=
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
-github.com/stretchr/objx v0.1.1 h1:2vfRuCMp5sSVIDSqO8oNnWJq7mPa6KVP3iPIwFBuy8A=
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
-github.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
-github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/t-yuki/gocover-cobertura v0.0.0-20180217150009-aaee18c8195c h1:+aPplBwWcHBo6q9xrfWdMrT9o4kltkmmvpemgIjep/8=
github.com/t-yuki/gocover-cobertura v0.0.0-20180217150009-aaee18c8195c/go.mod h1:SbErYREK7xXdsRiigaQiQkI9McGRzYMvlKYaP3Nimdk=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
-golang.org/x/net v0.0.0-20190311183353-d8887717615a h1:oWX7TPOiFAMXLq8o0ikBYfCJVlRHBcsciT5bXOrH628=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3 h1:0GoQqolDA55aaLxZyTzK/Y2ePZzZTUrRacwib7cNsYQ=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
@@ -64,7 +56,6 @@
google.golang.org/grpc v1.22.1 h1:/7cs52RnTJmD43s3uxzlq2U7nqVTd/37viQwMrMNlOM=
google.golang.org/grpc v1.22.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
-gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gotest.tools v2.2.0+incompatible h1:VsBPFP1AI068pPrMxtb/S8Zkgf9xEmTLJjfM+P5UIEo=
gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw=
diff --git a/internal/bbsim/bbsim.go b/internal/bbsim/bbsim.go
index 268be8e..943c021 100644
--- a/internal/bbsim/bbsim.go
+++ b/internal/bbsim/bbsim.go
@@ -24,15 +24,19 @@
"google.golang.org/grpc"
"google.golang.org/grpc/reflection"
"net"
+ "os"
+ "runtime/pprof"
"sync"
)
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)")
+ 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)")
+ profileCpu := flag.String("cpuprofile", "", "write cpu profile to file")
+
flag.Parse()
o := new(CliOptions)
@@ -41,11 +45,12 @@
o.NumNniPerOlt = int(*nni)
o.NumPonPerOlt = int(*pon)
o.NumOnuPerPon = int(*onu)
+ o.profileCpu = profileCpu
return o
}
-func startApiServer() {
+func startApiServer(channel chan bool, group *sync.WaitGroup) {
// TODO make configurable
address := "0.0.0.0:50070"
log.Debugf("APIServer Listening on: %v", address)
@@ -58,19 +63,47 @@
reflection.Register(grpcServer)
+ wg := sync.WaitGroup{}
+ wg.Add(1)
+
go grpcServer.Serve(lis)
+
+ for {
+ _, ok := <- channel
+ if !ok {
+ // if the olt channel is closed, stop the gRPC server
+ log.Warnf("Stopping API gRPC server")
+ grpcServer.Stop()
+ wg.Done()
+ break
+ }
+ }
+
+ wg.Wait()
+ group.Done()
+ return
}
func init() {
+ // TODO make configurable both via CLI and via ENV (for the tests)
log.SetLevel(log.DebugLevel)
//log.SetLevel(log.TraceLevel)
//log.SetReportCaller(true)
}
func main() {
-
options := getOpts()
+ 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,
@@ -78,18 +111,26 @@
"NumOnuPerPon": options.NumOnuPerPon,
}).Info("BroadBand Simulator is on")
+ // control channels, they are only closed when the goroutine needs to be terminated
+ oltDoneChannel := make(chan bool)
+ apiDoneChannel := make(chan bool)
+
wg := sync.WaitGroup{}
wg.Add(2)
- go devices.CreateOLT(options.OltID, options.NumNniPerOlt, options.NumPonPerOlt, options.NumOnuPerPon)
+ go devices.CreateOLT(options.OltID, options.NumNniPerOlt, options.NumPonPerOlt, options.NumOnuPerPon, &oltDoneChannel, &apiDoneChannel, &wg)
log.Debugf("Created OLT with id: %d", options.OltID)
- go startApiServer()
+ go startApiServer(apiDoneChannel, &wg)
log.Debugf("Started APIService")
wg.Wait()
defer func() {
log.Info("BroadBand Simulator is off")
+ if *options.profileCpu != "" {
+ log.Info("Stopping profiler")
+ pprof.StopCPUProfile()
+ }
}()
}
\ No newline at end of file
diff --git a/internal/bbsim/devices/helpers_test.go b/internal/bbsim/devices/helpers_test.go
index 2d01c1a..7b3bf71 100644
--- a/internal/bbsim/devices/helpers_test.go
+++ b/internal/bbsim/devices/helpers_test.go
@@ -19,6 +19,7 @@
import (
"github.com/looplab/fsm"
"gotest.tools/assert"
+ "os"
"testing"
)
@@ -26,7 +27,7 @@
originalNewFSM func(initial string, events []fsm.EventDesc, callbacks map[string]fsm.Callback) *fsm.FSM
)
-func setUp(t *testing.T) {
+func setUp() {
originalNewFSM = newFSM
}
@@ -35,6 +36,13 @@
newFSM = originalNewFSM
}
+func TestMain(m *testing.M) {
+ setUp()
+ code := m.Run()
+ tearDown()
+ os.Exit(code)
+}
+
func Test_Helpers(t *testing.T) {
// feedback values for the mock
diff --git a/internal/bbsim/devices/olt.go b/internal/bbsim/devices/olt.go
index 3b50584..66abe14 100644
--- a/internal/bbsim/devices/olt.go
+++ b/internal/bbsim/devices/olt.go
@@ -20,13 +20,15 @@
"context"
"errors"
"fmt"
- "github.com/opencord/voltha-protos/go/openolt"
+ bbsim "github.com/opencord/bbsim/internal/bbsim/types"
+ "github.com/google/gopacket"
+ "github.com/google/gopacket/layers"
"github.com/looplab/fsm"
+ "github.com/opencord/voltha-protos/go/openolt"
"github.com/opencord/voltha-protos/go/tech_profile"
log "github.com/sirupsen/logrus"
"google.golang.org/grpc"
"net"
- "os"
"sync"
)
@@ -45,7 +47,7 @@
return olt
}
-func CreateOLT(seq int, nni int, pon int, onuPerPon int) OltDevice {
+func CreateOLT(seq int, nni int, pon int, onuPerPon int, oltDoneChannel *chan bool, apiDoneChannel *chan bool, group *sync.WaitGroup) OltDevice {
oltLogger.WithFields(log.Fields{
"ID": seq,
"NumNni":nni,
@@ -64,6 +66,8 @@
Pons: []PonPort{},
Nnis: []NniPort{},
channel: make(chan Message),
+ oltDoneChannel: oltDoneChannel,
+ apiDoneChannel: apiDoneChannel,
}
// OLT State machine
@@ -116,11 +120,9 @@
olt.Pons = append(olt.Pons, p)
}
- wg := sync.WaitGroup{}
+ newOltServer(olt)
- wg.Add(1)
- go newOltServer(olt)
- wg.Wait()
+ group.Done()
return olt
}
@@ -134,9 +136,25 @@
grpcServer := grpc.NewServer()
openolt.RegisterOpenoltServer(grpcServer, o)
+ wg := sync.WaitGroup{}
+ wg.Add(1)
+
go grpcServer.Serve(lis)
oltLogger.Debugf("OLT Listening on: %v", address)
+ for {
+ _, ok := <- *o.oltDoneChannel
+ if !ok {
+ // if the olt channel is closed, stop the gRPC server
+ log.Warnf("Stopping OLT gRPC server")
+ grpcServer.Stop()
+ wg.Done()
+ break
+ }
+ }
+
+ wg.Wait()
+
return nil
}
@@ -471,14 +489,26 @@
return new(openolt.Empty) , nil
}
-func (o OltDevice) OnuPacketOut(context.Context, *openolt.OnuPacket) (*openolt.Empty, error) {
- oltLogger.Error("OnuPacketOut not implemented")
+func (o OltDevice) OnuPacketOut(ctx context.Context, onuPkt *openolt.OnuPacket) (*openolt.Empty, error) {
+ pon, _ := o.getPonById(onuPkt.IntfId)
+ onu, _ := pon.getOnuById(onuPkt.OnuId)
+
+ rawpkt := gopacket.NewPacket(onuPkt.Pkt, layers.LayerTypeEthernet, gopacket.Default)
+
+ // NOTE is this the best way to the to the ethertype?
+ etherType := rawpkt.Layer(layers.LayerTypeEthernet).(*layers.Ethernet).EthernetType
+
+ if etherType == layers.EthernetTypeEAPOL {
+ eapolPkt := bbsim.ByteMsg{IntfId: onuPkt.IntfId, OnuId: onuPkt.OnuId, Bytes: rawpkt.Data()}
+ onu.eapolPktOutCh <- &eapolPkt
+ }
return new(openolt.Empty) , nil
}
func (o OltDevice) Reboot(context.Context, *openolt.Empty) (*openolt.Empty, error) {
- oltLogger.Info("Shutting Down, hope you're running in K8s...")
- os.Exit(0)
+ oltLogger.Info("Shutting Down")
+ close(*o.oltDoneChannel)
+ close(*o.apiDoneChannel)
return new(openolt.Empty) , nil
}
diff --git a/internal/bbsim/devices/onu.go b/internal/bbsim/devices/onu.go
index 94e0705..fac7ce1 100644
--- a/internal/bbsim/devices/onu.go
+++ b/internal/bbsim/devices/onu.go
@@ -18,7 +18,7 @@
import (
"fmt"
- "github.com/opencord/bbsim/internal/bbsim/responders"
+ "github.com/opencord/bbsim/internal/bbsim/responders/eapol"
"github.com/google/gopacket/layers"
"github.com/looplab/fsm"
omci "github.com/opencord/omci-sim"
@@ -59,6 +59,11 @@
{Name: "receive_eapol_flow", Src: []string{"enabled", "gem_port_added"}, Dst: "eapol_flow_received"},
{Name: "add_gem_port", Src: []string{"enabled", "eapol_flow_received"}, Dst: "gem_port_added"},
{Name: "start_auth", Src: []string{"eapol_flow_received", "gem_port_added"}, Dst: "auth_started"},
+ {Name: "eap_start_sent", Src: []string{"auth_started"}, Dst: "eap_start_sent"},
+ {Name: "eap_resonse_identity_sent", Src: []string{"eap_start_sent"}, Dst: "eap_resonse_identity_sent"},
+ {Name: "eap_resonse_challenge_sent", Src: []string{"eap_resonse_identity_sent"}, Dst: "eap_resonse_challenge_sent"},
+ {Name: "eap_resonse_success_received", Src: []string{"eap_resonse_challenge_sent"}, Dst: "eap_resonse_success_received"},
+ {Name: "auth_failed", Src: []string{"auth_started", "eap_start_sent", "eap_resonse_identity_sent", "eap_resonse_challenge_sent"}, Dst: "auth_failed"},
},
fsm.Callbacks{
"enter_state": func(e *fsm.Event) {
@@ -105,6 +110,14 @@
}(msg)
},
+ "enter_eap_resonse_success_received": func(e *fsm.Event) {
+ o.logStateChange(e.Src, e.Dst)
+ onuLogger.WithFields(log.Fields{
+ "OnuId": o.ID,
+ "IntfId": o.PonPortID,
+ "OnuSn": o.SerialNumber,
+ }).Warnf("TODO start DHCP request")
+ },
},
)
return o
@@ -147,8 +160,8 @@
case StartEAPOL:
log.Infof("Receive StartEAPOL message on ONU channel")
go func() {
-
- responders.StartWPASupplicant(o.ID, o.PonPortID, o.SerialNumber, stream, o.eapolPktOutCh)
+ // TODO kill this thread
+ eapol.CreateWPASupplicant(o.ID, o.PonPortID, o.SerialNumber, o.InternalState, stream, o.eapolPktOutCh)
}()
default:
onuLogger.Warnf("Received unknown message data %v for type %v in OLT channel", message.Data, message.Type)
@@ -191,7 +204,7 @@
sn := new(openolt.SerialNumber)
- sn = new(openolt.SerialNumber)
+ //sn = new(openolt.SerialNumber)
sn.VendorId = []byte("BBSM")
sn.VendorSpecific = []byte{0, byte(oltid % 256), byte(intfid), byte(onuid)}
diff --git a/internal/bbsim/devices/types.go b/internal/bbsim/devices/types.go
index 4c92849..e3439f5 100644
--- a/internal/bbsim/devices/types.go
+++ b/internal/bbsim/devices/types.go
@@ -94,6 +94,8 @@
NumOnuPerPon int
InternalState *fsm.FSM
channel chan Message
+ oltDoneChannel *chan bool
+ apiDoneChannel *chan bool
Pons []PonPort
Nnis []NniPort
diff --git a/internal/bbsim/responders/eapol.go b/internal/bbsim/responders/eapol.go
deleted file mode 100644
index 4e7cd34..0000000
--- a/internal/bbsim/responders/eapol.go
+++ /dev/null
@@ -1,82 +0,0 @@
-/*
- * 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 responders
-
-import (
- bbsim "github.com/opencord/bbsim/internal/bbsim/types"
- "github.com/google/gopacket"
- "github.com/google/gopacket/layers"
- omci "github.com/opencord/omci-sim"
- "github.com/opencord/voltha-protos/go/openolt"
- log "github.com/sirupsen/logrus"
- "net"
-)
-
-var eapolLogger = log.WithFields(log.Fields{
- "module": "EAPOL",
-})
-
-func StartWPASupplicant(onuId uint32, ponPortId uint32, serialNumber *openolt.SerialNumber, stream openolt.Openolt_EnableIndicationServer, pktOutCh chan *bbsim.ByteMsg) {
- // NOTE pckOutCh is channel to listen on for packets received by VOLTHA
- // the ONU device will publish messages on that channel
-
- eapolLogger.WithFields(log.Fields{
- "OnuId": onuId,
- "IntfId": ponPortId,
- "OnuSn": serialNumber,
- }).Infof("EAPOL State Machine starting")
-
- // send the packet (hacked together)
- gemid, err := omci.GetGemPortId(ponPortId, onuId)
- if err != nil {
- eapolLogger.WithFields(log.Fields{
- "OnuId": onuId,
- "IntfId": ponPortId,
- "OnuSn": serialNumber,
- }).Errorf("Can't retrieve GemPortId: %s", err)
- return
- }
-
- buffer := gopacket.NewSerializeBuffer()
- options := gopacket.SerializeOptions{}
-
- ethernetLayer := &layers.Ethernet{
- SrcMAC: net.HardwareAddr{0x2e, 0x60, 0x70, 0x13, 0x07, byte(onuId)},
- DstMAC: net.HardwareAddr{0x01, 0x80, 0xC2, 0x00, 0x00, 0x03},
- EthernetType: layers.EthernetTypeEAPOL,
- }
-
- gopacket.SerializeLayers(buffer, options,
- ethernetLayer,
- &layers.EAPOL{Version: 1, Type: 1, Length: 0},
- )
-
- msg := buffer.Bytes()
-
- data := &openolt.Indication_PktInd{
- PktInd: &openolt.PacketIndication{
- IntfType: "pon",
- IntfId: ponPortId,
- GemportId: uint32(gemid),
- Pkt: msg,
- },
- }
- // end of hacked (move in an EAPOL state machine)
- if err := stream.Send(&openolt.Indication{Data: data}); err != nil {
- eapolLogger.Error("Fail to send EAPOL PktInd indication. %v", err)
- }
-}
\ No newline at end of file
diff --git a/internal/bbsim/responders/eapol/eapol.go b/internal/bbsim/responders/eapol/eapol.go
new file mode 100644
index 0000000..d32999a
--- /dev/null
+++ b/internal/bbsim/responders/eapol/eapol.go
@@ -0,0 +1,287 @@
+/*
+ * 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 eapol
+
+import (
+ "crypto/md5"
+ "errors"
+ bbsim "github.com/opencord/bbsim/internal/bbsim/types"
+ "github.com/google/gopacket"
+ "github.com/google/gopacket/layers"
+ "github.com/looplab/fsm"
+ omci "github.com/opencord/omci-sim"
+ "github.com/opencord/voltha-protos/go/openolt"
+ log "github.com/sirupsen/logrus"
+ "net"
+ "sync"
+)
+
+var eapolLogger = log.WithFields(log.Fields{
+ "module": "EAPOL",
+})
+
+var eapolVersion uint8 = 1
+
+func CreateWPASupplicant(onuId uint32, ponPortId uint32, serialNumber *openolt.SerialNumber, onuStateMachine *fsm.FSM, stream openolt.Openolt_EnableIndicationServer, pktOutCh chan *bbsim.ByteMsg) {
+ // NOTE pckOutCh is channel to listen on for packets received by VOLTHA
+ // the OLT device will publish messages on that channel
+
+ eapolLogger.WithFields(log.Fields{
+ "OnuId": onuId,
+ "IntfId": ponPortId,
+ "OnuSn": serialNumber,
+ }).Infof("EAPOL State Machine starting")
+
+ var wg sync.WaitGroup
+ wg.Add(1)
+
+ if err := sendEapStart(onuId, ponPortId, serialNumber, stream); err != nil {
+ eapolLogger.WithFields(log.Fields{
+ "OnuId": onuId,
+ "IntfId": ponPortId,
+ "OnuSn": serialNumber,
+ }).Errorf("Can't retrieve GemPortId: %s", err)
+ if err := onuStateMachine.Event("auth_failed"); err != nil {
+ eapolLogger.WithFields(log.Fields{
+ "OnuId": onuId,
+ "IntfId": ponPortId,
+ "OnuSn": serialNumber,
+ }).Errorf("Error while transitioning ONU State %v", err)
+ }
+ return
+ }
+ if err := onuStateMachine.Event("eap_start_sent"); err != nil {
+ eapolLogger.WithFields(log.Fields{
+ "OnuId": onuId,
+ "IntfId": ponPortId,
+ "OnuSn": serialNumber,
+ }).Errorf("Error while transitioning ONU State %v", err)
+ }
+
+ // NOTE do we really need a thread here?
+ go func() {
+ eapolLogger.WithFields(log.Fields{
+ "OnuId": onuId,
+ "IntfId": ponPortId,
+ "OnuSn": serialNumber,
+ }).Infof("Listening on eapolPktOutCh")
+ for msg := range pktOutCh {
+ recvpkt := gopacket.NewPacket(msg.Bytes, layers.LayerTypeEthernet, gopacket.Default)
+ eap, err := extractEAP(recvpkt)
+ if err != nil {
+ eapolLogger.Errorf("%s", err)
+ }
+
+ if eap.Code == layers.EAPCodeRequest && eap.Type == layers.EAPTypeIdentity {
+ reseap := createEAPIdentityResponse(eap.Id)
+ pkt := createEAPOLPkt(reseap, onuId, ponPortId)
+
+ msg := bbsim.ByteMsg{
+ IntfId: ponPortId,
+ OnuId: onuId,
+ Bytes: pkt,
+ }
+
+ sendEapolPktIn(msg, stream)
+ eapolLogger.WithFields(log.Fields{
+ "OnuId": onuId,
+ "IntfId": ponPortId,
+ "OnuSn": serialNumber,
+ }).Infof("Sent EAPIdentityResponse packet")
+ if err := onuStateMachine.Event("eap_resonse_identity_sent"); 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.EAPCodeRequest && eap.Type == layers.EAPTypeOTP {
+ senddata := getMD5Data(eap)
+ senddata = append([]byte{0x10}, senddata...)
+ sendeap := createEAPChallengeResponse(eap.Id, senddata)
+ pkt := createEAPOLPkt(sendeap, onuId, ponPortId)
+
+ msg := bbsim.ByteMsg{
+ IntfId: ponPortId,
+ OnuId: onuId,
+ Bytes: pkt,
+ }
+
+ sendEapolPktIn(msg, stream)
+ eapolLogger.WithFields(log.Fields{
+ "OnuId": onuId,
+ "IntfId": ponPortId,
+ "OnuSn": serialNumber,
+ }).Infof("Sent EAPChallengeResponse packet")
+ if err := onuStateMachine.Event("eap_resonse_challenge_sent"); 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,
+ "IntfId": ponPortId,
+ "OnuSn": serialNumber,
+ }).Infof("Received EAPSuccess packet")
+ if err := onuStateMachine.Event("eap_resonse_success_received"); err != nil {
+ eapolLogger.WithFields(log.Fields{
+ "OnuId": onuId,
+ "IntfId": ponPortId,
+ "OnuSn": serialNumber,
+ }).Errorf("Error while transitioning ONU State %v", err)
+ }
+ wg.Done()
+ }
+
+ }
+
+
+ }()
+
+ defer eapolLogger.WithFields(log.Fields{
+ "OnuId": onuId,
+ "IntfId": ponPortId,
+ "OnuSn": serialNumber,
+ }).Infof("EAPOL State machine completed")
+
+ wg.Wait()
+}
+
+func sendEapolPktIn(msg bbsim.ByteMsg, stream openolt.Openolt_EnableIndicationServer) {
+ gemid, err := omci.GetGemPortId(msg.IntfId, msg.OnuId)
+ if err != nil {
+ eapolLogger.WithFields(log.Fields{
+ "OnuId": msg.OnuId,
+ "IntfId": msg.IntfId,
+ }).Errorf("Can't retrieve GemPortId: %s", err)
+ return
+ }
+ data := &openolt.Indication_PktInd{PktInd: &openolt.PacketIndication{
+ IntfType: "pon", IntfId: msg.IntfId, GemportId: uint32(gemid), Pkt: msg.Bytes,
+ }}
+
+ if err := stream.Send(&openolt.Indication{Data: data}); err != nil {
+ eapolLogger.Errorf("Fail to send EAPOL PktInd indication. %v", err)
+ return
+ }
+}
+
+func getMD5Data(eap *layers.EAP) []byte {
+ i := byte(eap.Id)
+ C := []byte(eap.BaseLayer.Contents)[6:]
+ P := []byte{i, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64} // "password"
+ data := md5.Sum(append(P, C...))
+ ret := make([]byte, 16)
+ for j := 0; j < 16; j++ {
+ ret[j] = data[j]
+ }
+ return ret
+}
+
+func createEAPChallengeResponse(eapId uint8, payload []byte) *layers.EAP {
+ eap := layers.EAP{
+ Code: layers.EAPCodeResponse,
+ Id: eapId,
+ Length: 22,
+ Type: layers.EAPTypeOTP,
+ TypeData: payload,
+ }
+ return &eap
+}
+
+func createEAPIdentityResponse(eapId uint8) *layers.EAP {
+ eap := layers.EAP{Code: layers.EAPCodeResponse,
+ Id: eapId,
+ Length: 9,
+ Type: layers.EAPTypeIdentity,
+ TypeData: []byte{0x75, 0x73, 0x65, 0x72}}
+ return &eap
+}
+
+func createEAPOLPkt(eap *layers.EAP, onuId uint32, intfId uint32) []byte {
+ buffer := gopacket.NewSerializeBuffer()
+ options := gopacket.SerializeOptions{}
+
+ ethernetLayer := &layers.Ethernet{
+ SrcMAC: net.HardwareAddr{0x2e, 0x60, 0x70, 0x13, byte(intfId), byte(onuId)},
+ DstMAC: net.HardwareAddr{0x01, 0x80, 0xC2, 0x00, 0x00, 0x03},
+ EthernetType: layers.EthernetTypeEAPOL,
+ }
+
+
+ gopacket.SerializeLayers(buffer, options,
+ ethernetLayer,
+ &layers.EAPOL{Version: eapolVersion, Type: 0, Length: eap.Length},
+ eap,
+ )
+
+ bytes := buffer.Bytes()
+ return bytes
+}
+
+func extractEAP(pkt gopacket.Packet) (*layers.EAP, error) {
+ layerEAP := pkt.Layer(layers.LayerTypeEAP)
+ eap, _ := layerEAP.(*layers.EAP)
+ if eap == nil {
+ return nil, errors.New("Cannot extract EAP")
+ }
+ return eap, nil
+}
+
+var sendEapStart = func(onuId uint32, ponPortId uint32, serialNumber *openolt.SerialNumber, stream openolt.Openolt_EnableIndicationServer) error {
+
+ // send the packet (hacked together)
+ gemid, err := omci.GetGemPortId(ponPortId, onuId)
+ if err != nil {
+ return err
+ }
+
+ buffer := gopacket.NewSerializeBuffer()
+ options := gopacket.SerializeOptions{}
+
+ ethernetLayer := &layers.Ethernet{
+ SrcMAC: net.HardwareAddr{0x2e, 0x60, 0x70, 0x13, byte(ponPortId), byte(onuId)}, // TODO move the SrcMAC in the ONU Device
+ DstMAC: net.HardwareAddr{0x01, 0x80, 0xC2, 0x00, 0x00, 0x03},
+ EthernetType: layers.EthernetTypeEAPOL,
+ }
+
+ gopacket.SerializeLayers(buffer, options,
+ ethernetLayer,
+ &layers.EAPOL{Version: eapolVersion, Type: 1, Length: 0},
+ )
+
+ msg := buffer.Bytes()
+
+ data := &openolt.Indication_PktInd{
+ PktInd: &openolt.PacketIndication{
+ IntfType: "pon",
+ IntfId: ponPortId,
+ GemportId: uint32(gemid),
+ Pkt: msg,
+ },
+ }
+ // end of hacked (move in an EAPOL state machine)
+ if err := stream.Send(&openolt.Indication{Data: data}); err != nil {
+ eapolLogger.Errorf("Fail to send EAPOL PktInd indication. %v", err)
+ return err
+ }
+ return nil
+}
\ No newline at end of file
diff --git a/internal/bbsim/responders/eapol/eapol_test.go b/internal/bbsim/responders/eapol/eapol_test.go
new file mode 100644
index 0000000..2d8a06c
--- /dev/null
+++ b/internal/bbsim/responders/eapol/eapol_test.go
@@ -0,0 +1,117 @@
+/*
+ * 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 eapol
+
+import (
+ bbsim "github.com/opencord/bbsim/internal/bbsim/types"
+ "github.com/looplab/fsm"
+ "github.com/opencord/voltha-protos/go/openolt"
+ "google.golang.org/grpc"
+ "gotest.tools/assert"
+ "os"
+ "sync"
+ "testing"
+ "time"
+)
+
+var (
+ originalSendEapStart func(onuId uint32, ponPortId uint32, serialNumber *openolt.SerialNumber, stream openolt.Openolt_EnableIndicationServer) error
+)
+
+type fakeStream struct {
+ calledSend int
+ grpc.ServerStream
+}
+
+func (s fakeStream) Send(flow *openolt.Indication) error {
+ s.calledSend++
+ return nil
+}
+
+func setUp() {
+ originalSendEapStart = sendEapStart
+}
+
+
+func tearDown() {
+ sendEapStart = originalSendEapStart
+}
+
+func TestMain(m *testing.M) {
+ setUp()
+ code := m.Run()
+ tearDown()
+ os.Exit(code)
+}
+
+func TestCreateWPASupplicant(t *testing.T) {
+
+ // mocks
+ mockSendEapStartCalled := 0
+ mockSendEapStartArgs := struct {
+ onuId uint32
+ ponPortId uint32
+ serialNumber *openolt.SerialNumber
+ stream openolt.Openolt_EnableIndicationServer
+ }{}
+ mockSendEapStart := func(onuId uint32, ponPortId uint32, serialNumber *openolt.SerialNumber, stream openolt.Openolt_EnableIndicationServer) error {
+ mockSendEapStartCalled++
+ mockSendEapStartArgs.onuId = onuId
+ mockSendEapStartArgs.ponPortId = ponPortId
+ return nil
+ }
+ sendEapStart = mockSendEapStart
+
+ // params for the function under test
+ var onuId uint32 = 1
+ var ponPortId uint32 = 0
+ var serialNumber = new(openolt.SerialNumber)
+
+ serialNumber.VendorId = []byte("BBSM")
+ serialNumber.VendorSpecific = []byte{0, byte(0 % 256), byte(ponPortId), byte(onuId)}
+
+ eapolStateMachine := fsm.NewFSM(
+ "auth_started",
+ fsm.Events{
+ {Name: "eap_start_sent", Src: []string{"auth_started"}, Dst: "eap_start_sent"},
+ {Name: "eap_resonse_identity_sent", Src: []string{"eap_start_sent"}, Dst: "eap_resonse_identity_sent"},
+ {Name: "eap_resonse_challenge_sent", Src: []string{"eap_resonse_identity_sent"}, Dst: "eap_resonse_challenge_sent"},
+ {Name: "eap_resonse_success_received", Src: []string{"eap_resonse_challenge_sent"}, Dst: "eap_resonse_success_received"},
+ },
+ fsm.Callbacks{},
+ )
+
+ pktOutCh := make(chan *bbsim.ByteMsg, 1024)
+
+ stream := fakeStream{}
+
+ wg := sync.WaitGroup{}
+ wg.Add(1)
+
+ go CreateWPASupplicant(onuId, ponPortId, serialNumber, eapolStateMachine, stream, pktOutCh)
+ go func(){
+ time.Sleep(1 * time.Second)
+ close(pktOutCh)
+ wg.Done()
+ }()
+
+ wg.Wait()
+
+ assert.Equal(t, mockSendEapStartCalled, 1)
+ assert.Equal(t, mockSendEapStartArgs.onuId, onuId)
+ assert.Equal(t, mockSendEapStartArgs.ponPortId, ponPortId)
+}
\ No newline at end of file
diff --git a/internal/bbsim/types.go b/internal/bbsim/types.go
index 6a6ce76..0da944a 100644
--- a/internal/bbsim/types.go
+++ b/internal/bbsim/types.go
@@ -19,8 +19,10 @@
// General
type CliOptions struct {
- OltID int
- NumNniPerOlt int
- NumPonPerOlt int
- NumOnuPerPon int
-}
\ No newline at end of file
+ OltID int
+ NumNniPerOlt int
+ NumPonPerOlt int
+ NumOnuPerPon int
+ profileCpu *string
+}
+
diff --git a/internal/bbsim/types/types.go b/internal/bbsim/types/types.go
index 3245add..1a1788c 100644
--- a/internal/bbsim/types/types.go
+++ b/internal/bbsim/types/types.go
@@ -19,5 +19,5 @@
type ByteMsg struct {
IntfId uint32
OnuId uint32
- Byte []byte
+ Bytes []byte
}
\ No newline at end of file