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