AAA Emulation & BBSim Containerization

VOL-1154, VOL-1168, VOL-1273

Change-Id: Ib0fbbaec897f633601976e8636c218f42375bedd
diff --git a/Dockerfile b/Dockerfile
index 97b715a..3d95450 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -1,10 +1,10 @@
-# Copyright 2018-present Open Networking Foundation
+# Copyright 2018 the original author or authors.
 #
 # 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
+#      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,
@@ -14,36 +14,49 @@
 
 # bbsim dockerfile
 
-# builder parent
-FROM golang:1.10-stretch as builder
+ARG TAG=latest
+ARG REGISTRY=
+ARG REPOSITORY=
 
-# install prereqs
-ENV PROTOC_VERSION 3.6.1
-ENV PROTOC_SHA256SUM 6003de742ea3fcf703cfec1cd4a3380fd143081a2eb0e559065563496af27807
+#builder parent
+FROM ubuntu:16.04
 
-RUN apt-get update \
- && apt-get install unzip \
- && curl -L -o /tmp/protoc-${PROTOC_VERSION}-linux-x86_64.zip https://github.com/google/protobuf/releases/download/v${PROTOC_VERSION}/protoc-${PROTOC_VERSION}-linux-x86_64.zip \
- && echo "$PROTOC_SHA256SUM  /tmp/protoc-${PROTOC_VERSION}-linux-x86_64.zip" | sha256sum -c - \
- && unzip /tmp/protoc-${PROTOC_VERSION}-linux-x86_64.zip -d /tmp/protoc3 \
- && mv /tmp/protoc3/bin/* /usr/local/bin/ \
- && mv /tmp/protoc3/include/* /usr/local/include/ \
- && go get -v github.com/grpc-ecosystem/grpc-gateway/protoc-gen-grpc-gateway \
- && go get -v github.com/golang/protobuf/protoc-gen-go
+MAINTAINER Voltha Community <info@opennetworking.org>
 
-# copy and build
-WORKDIR /go/src/gerrit.opencord.org/voltha-bbsim
-COPY . /go/src/gerrit.opencord.org/voltha-bbsim
+# Install required packages
+RUN apt-get update && apt-get install -y wget git make libpcap-dev gcc unzip
+ARG version="1.9.3."
+RUN wget https://storage.googleapis.com/golang/go${version}linux-amd64.tar.gz -P /tmp \
+    && tar -C /usr/local -xzf /tmp/go${version}linux-amd64.tar.gz \
+    && rm /tmp/go${version}linux-amd64.tar.gz
 
+# Set PATH
+ENV GOPATH $HOME/go
+ENV PATH /usr/local/go/bin:/go/bin:$PATH
+
+# Copy source code
+RUN mkdir -p $GOPATH/src/gerrit.opencord.org/voltha-bbsim
+COPY . $GOPATH/src/gerrit.opencord.org/voltha-bbsim
+
+# Install golang protobuf and pcap support
+RUN wget https://github.com/google/protobuf/releases/download/v3.6.0/protoc-3.6.0-linux-x86_64.zip -P /tmp/ \
+&& unzip /tmp/protoc-3.6.0-linux-x86_64.zip -d /tmp/ \
+&& mv /tmp/bin/* /usr/local/bin/ \
+&& mv /tmp/include/* /usr/local/include/ \
+&& go get -u github.com/grpc-ecosystem/grpc-gateway/protoc-gen-grpc-gateway \
+&& go get -u github.com/golang/protobuf/protoc-gen-go \
+&& go get -u github.com/google/gopacket/pcap \
+&& go get -u golang.org/x/net/context \
+&& go get -u google.golang.org/grpc
+
+# ... Install utilities & config
+RUN apt-get update && apt-get install -y wpasupplicant isc-dhcp-server
+COPY ./config/wpa_supplicant.conf /etc/wpa_supplicant/
+COPY ./config/isc-dhcp-server /etc/default/
+COPY ./config/dhcpd.conf /etc/dhcp/
+RUN mv /usr/sbin/dhcpd /usr/local/bin/ \
+&& mv /sbin/dhclient /usr/local/bin/
+
+WORKDIR $GOPATH/src/gerrit.opencord.org/voltha-bbsim
 RUN make bbsim
 
-# runtime parent
-FROM alpine:3.8
-
-# required for binaries to run
-RUN apk add --update libc6-compat
-
-WORKDIR /app
-COPY --from=builder /go/src/gerrit.opencord.org/voltha-bbsim/bbsim /app/bbsim
-
-CMD [ '/app/bbsim' ]
diff --git a/Makefile b/Makefile
index 0ec079c..56bbcb2 100644
--- a/Makefile
+++ b/Makefile
@@ -17,25 +17,25 @@
 
 .PHONY: dep test clean docker
 
-bbsim: $(BBSIM_DEPS) openolt/openolt.pb.go dep
+bbsim: protos/openolt.pb.go dep
 	go build -i -v -o $@
 
-dep: openolt/openolt.pb.go
+dep: protos/openolt.pb.go
 	@go get -v -d ./...
 
-openolt/openolt.pb.go: openolt.proto
+protos/openolt.pb.go: openolt.proto
 	@protoc -I . \
 	-I${GOPATH}/src \
 	-I${GOPATH}/src/github.com/grpc-ecosystem/grpc-gateway/third_party/googleapis \
-	--go_out=plugins=grpc:openolt \
-	$<
+	--go_out=plugins=grpc:protos/ \
+	openolt.proto
 
 test:
 	@go test -v ./...
 	@go test -v ./... -cover
 
 clean:
-	@rm bbsim openolt/openolt.pb.go
+	@rm bbsim protos/openolt.pb.go
 
 docker:
 	@docker build -t voltha/voltha-bbsim:${DOCKERTAG} .
diff --git a/README b/README
deleted file mode 100644
index 0b186af..0000000
--- a/README
+++ /dev/null
@@ -1,85 +0,0 @@
-# 1. Overview
-
-The BBSim (Broadband Simulator) is for emulating the control message response sent from OLTs and ONUs which are connected to VOLTHA Adapter.
-It is implemetend as a software process which runs outside VOLTHA, and acts as if it was multiple OLTs and ONUs.
-This enables the scalability test of VOLTHA without actual hardware OLTs / ONUs.
-The difference from the existing PONsim is to focus on emulating control messages, not data-path traffic which PONsim targets.
-
-It currently supports the following VOLTHA mode:
-(Reference to https://github.com/opencord/voltha/blob/master/BUILD.md)
-* Run in stand-alone mode
-* Run with the "voltha ensamble"
-
-**Please note: Kubernetes mode is NOT supported by the current version.**
-
-==============
-VOLTHA
-OpenOLT Adapter
-==============
-|||
-|||  gRPC connections
-|||
-==============
-BBSim
-==============
-
-# 2. PON Simulator Usage
-
-```
-Usage of ./bbsim:
-  -H
-    	IP and Port number which BBSim listens (default 172.17.0.1:50060)
-  -N
-        Number of OLTs
-  -i
-    	Number of PON interfaces provided per OLT
-  -n
-    	Number of ONUs per PON interface
-```
-
-# 3. Requirements
-
-# Golang Installation
-
-If you plan on running the simulator locally, i.e. not in a container, you will need to first 
-install setup Golang on your system.  Install using existing packages for your operating system 
-or issue the following commands (Linux).
-
-```
-cd /tmp
-wget https://storage.googleapis.com/golang/go1.9.3.linux-amd64.tar.gz
-tar -C /usr/local -xzf /tmp/go1.9.3.linux-amd64.tar.gz
-rm -f /tmp/go1.9.3.linux-amd64.tar.gz
-mkdir ~/go
-```
-
-Edit your profile (e.g. .bashrc) and add the following configuration
-
-```
-export GOROOT=/usr/local/go
-export GOPATH=~/go
-export PATH=$PATH:$GOROOT/bin:$GOPATH/bin
-```
-
-# 4. Run BBSim and VOLTHA-CLI commands
-
-```
-# in VOLTHA docker
-cd /cord/incubator/voltha/voltha/bbsim
-./bbsim -H 172.17.0.1:50060 -N 1 -i 1 -n 16
-
-# After this, execute the following commands from VOLTHA-CLI
-(voltha) health
-{
-    "state": "HEALTHY"
-}
-(voltha) preprovision_olt -t openolt -H 172.17.0.1:50060
-success (device id = <deviceid>)
-(voltha) enable
-enabling <deviceid>
-waiting for device to be enabled...
-success (device id = <deviceid>)
-(voltha) devices
-## You can see the list of devices (OLT/ONUs) ##
-```
-
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..516c61f
--- /dev/null
+++ b/README.md
@@ -0,0 +1,65 @@
+# 1. Overview
+
+The BBSim (Broadband Simulator) is for emulating the control message response sent from OLT and ONUs which are connected to VOLTHA Adapter.
+It is implemetend as a software process which runs outside VOLTHA, and acts as if it was a OLT connected to multiple ONUs.
+This enables the scalability test of VOLTHA without actual hardware OLT / ONUs.
+The difference from the existing PONsim is to focus on emulating control messages, not data-path traffic which PONsim targets.
+
+The BBSim container contains wpa_supplicants and dhcp clients for supporting AAA client (wpa_supplicant) emulation & DHCP client emulation.
+
+==============
+VOLTHA
+OpenOLT Adapter
+==============
+ |
+ |  gRPC connections
+ |
+==============
+BBSim container
+==============
+
+# 2. Build, Run BBSim and VOLTHA-CLI commands
+```
+# Build and Run Docker container
+git clone https://github.com/opencord/voltha-bbsim
+cd voltha-bbsim
+make docker
+docker run -it --rm --privileged=true --expose=50060 --network=compose_default voltha/voltha-bbsim /go/src/gerrit.opencord.org/voltha-bbsim/bbsim -n 16
+
+# After this, execute the following commands from VOLTHA-CLI
+(voltha) health
+{
+    "state": "HEALTHY"
+}
+(voltha) preprovision_olt -t openolt -H <BBSim Docker container IP>
+success (device id = <deviceid>)
+(voltha) enable
+enabling <deviceid>
+waiting for device to be enabled...
+success (device id = <deviceid>)
+(voltha) devices
+## You can see the list of devices (OLT/ONUs) ##
+```
+
+# 3. How to use BBSim
+
+```
+# Note: 2018/10/8 The current version only supports AAA emulations.
+Usage of ./bbsim:
+  -H string
+    	IP address:port (default ":50060")
+  -a int
+    	Wait time (sec) for activation WPA supplicants (default 30)
+  -d int
+    	Wait time (sec) for activation DHCP clients (default 10)
+  -i int
+    	Number of PON-IF ports (default 1)
+  -id int
+    	OLT-ID (default: 0)
+  -m string
+    	Emulation mode (default, aaa, both (aaa & dhcp)) (default "default")
+  -n int
+    	Number of ONUs per PON-IF port (default 1)
+  -s string
+    	DHCP Server IP Address (default "182.21.0.1")
+```
diff --git a/bbsim.go b/bbsim.go
index 28ab99f..27373f8 100755
--- a/bbsim.go
+++ b/bbsim.go
@@ -17,266 +17,20 @@
 package main
 
 import (
-	"log"
-	"net"
-	"gerrit.opencord.org/voltha-bbsim/openolt"
-	"google.golang.org/grpc"
-	"fmt"
+	"gerrit.opencord.org/voltha-bbsim/protos"
+	"gerrit.opencord.org/voltha-bbsim/core"
 	"flag"
-	"reflect"
-	"strings"
+	"fmt"
+	"log"
+	"os"
+	"os/signal"
 	"strconv"
+	"strings"
 	"sync"
+	"time"
 )
 
-type server struct{
-	olt olt
-	onus map[uint32]map[uint32]*onu
-}
-
-
-type oltState int
-type onuState int
-
-const(
-	PRE_ENABLE oltState = iota
-	OLT_UP
-	PONIF_UP
-	ONU_DISCOVERED
-)
-
-const(
-	ONU_PRE_ACTIVATED onuState = iota
-	ONU_ACTIVATED
-)
-
-type olt struct {
-	ID			         uint32
-	NumPonIntf           uint32
-	NumNniIntf           uint32
-	Mac                  string
-	SerialNumber         string
-	Manufacture          string
-	Name                 string
-	internalState        oltState
-	OperState            string
-	Intfs                []intf
-	HeartbeatSignature   uint32
-}
-
-type intf struct{
-	Type                string
-	IntfID              uint32
-	OperState           string
-}
-
-type onu struct{
-	internalState       onuState
-	IntfID              uint32
-	OperState           string
-	SerialNumber        *openolt.SerialNumber
-}
-
-func createOlt(oltid uint32, npon uint32, nnni uint32) olt{
-	olt := olt {}
-	olt.ID              = oltid
-	olt.NumPonIntf      = npon
-	olt.NumNniIntf      = nnni
-	olt.Name          = "BBSIM OLT"
-	olt.internalState = PRE_ENABLE
-	olt.OperState     = "up"
-	olt.Intfs = make([]intf, olt.NumPonIntf + olt.NumNniIntf)
-	olt.HeartbeatSignature = oltid
-	for i := uint32(0); i < olt.NumNniIntf; i ++ {
-		olt.Intfs[i].IntfID = i
-		olt.Intfs[i].OperState = "up"
-		olt.Intfs[i].Type      = "nni"
-	}
-	for i := uint32(olt.NumNniIntf); i <  olt.NumPonIntf + olt.NumNniIntf; i ++ {
-		olt.Intfs[i].IntfID = i
-		olt.Intfs[i].OperState = "up"
-		olt.Intfs[i].Type      = "pon"
-	}
-	return olt
-}
-
-func createSN(oltid uint32, intfid uint32, onuid uint32) string{
-	sn := fmt.Sprintf("%X%X%02X", oltid, intfid, onuid)
-	return sn
-}
-
-func createOnus(oltid uint32, intfid uint32, nonus uint32, nnni uint32) map[uint32] *onu {
-	onus := make(map[uint32] *onu ,nonus)
-	for onuid := uint32(1 + (intfid - nnni) * nonus); onuid <= (intfid - nnni + 1) * nonus; onuid ++ {
-		onu := onu{}
-		onu.internalState = ONU_PRE_ACTIVATED
-		onu.IntfID        = intfid
-		onu.OperState     = "up"
-		onu.SerialNumber  = new(openolt.SerialNumber)
-		onu.SerialNumber.VendorId = []byte("BRCM")
-		onu.SerialNumber.VendorSpecific = []byte(createSN(oltid, intfid, uint32(onuid))) //FIX
-		onus[onuid] = &onu
-	}
-	return onus
-}
-
-func validateONU(targetonu openolt.Onu, regonus map[uint32]map[uint32] *onu) bool{
-	for _, onus := range regonus{
-		for _, onu := range onus{
-			if validateSN(*targetonu.SerialNumber, *onu.SerialNumber){
-				return true
-			}
-		}
-	}
-	return false
-}
-
-func validateSN(sn1 openolt.SerialNumber, sn2 openolt.SerialNumber) bool{
-	return reflect.DeepEqual(sn1.VendorId, sn2.VendorId) && reflect.DeepEqual(sn1.VendorSpecific, sn2.VendorSpecific)
-}
-
-func isAllONUActive(regonus map[uint32]map[uint32] *onu ) bool{
-	for _, onus := range regonus{
-		for _, onu := range onus{
-			if onu.internalState != ONU_ACTIVATED{
-				return false
-			}
-		}
-	}
-	return true
-}
-
-func updateOnusOpStatus(ponif uint32, onus map[uint32] *onu, opstatus string){
-	for i, onu := range onus{
-		onu.OperState = "up"
-		log.Printf("(PONIF:%d) ONU [%d] discovered.\n", ponif, i)
-	}
-}
-
-func activateOLT(s *server, stream openolt.Openolt_EnableIndicationServer) error{
-	// Activate OLT
-	olt  := &s.olt
-	onus := s.onus
-	if err := sendOltInd(stream, olt); err != nil {
-		return err
-	}
-	olt.OperState = "up"
-	olt.internalState = OLT_UP
-	log.Printf("OLT %s sent OltInd.\n", olt.Name)
-
-
-	// OLT sends Interface Indication to Adapter
-	if err := sendIntfInd(stream, olt); err != nil {
-		return err
-	}
-	log.Printf("OLT %s sent IntfInd.\n", olt.Name)
-
-	// OLT sends Operation Indication to Adapter after activating each interface
-	//time.Sleep(IF_UP_TIME * time.Second)
-	olt.internalState = PONIF_UP
-	if err := sendOperInd(stream, olt); err != nil {
-		return err
-	}
-	log.Printf("OLT %s sent OperInd.\n", olt.Name)
-
-	// OLT sends ONU Discover Indication to Adapter after ONU discovery
-	for intfid := uint32(olt.NumNniIntf); intfid < olt.NumNniIntf + olt.NumPonIntf; intfid ++ {
-		updateOnusOpStatus(intfid, onus[intfid], "up")
-	}
-
-	for intfid := uint32(olt.NumNniIntf); intfid < olt.NumNniIntf + olt.NumPonIntf; intfid ++ {
-		sendOnuDiscInd(stream, onus[intfid])
-		log.Printf("OLT id:%d sent ONUDiscInd.\n", olt.ID)
-	}
-
-	for{
-		//log.Printf("stop %v\n", s.onus)
-		if isAllONUActive(s.onus){
-			//log.Printf("break! %v\n", s.onus)
-			break
-		}
-	}
-	for intfid := uint32(olt.NumNniIntf); intfid < olt.NumNniIntf + olt.NumPonIntf; intfid ++ {
-		sendOnuInd(stream, onus[intfid])
-		log.Printf("OLT id:%d sent ONUInd.\n", olt.ID)
-	}
-	return nil
-}
-
-func sendOltInd(stream openolt.Openolt_EnableIndicationServer, olt *olt) error{
-	data := &openolt.Indication_OltInd{OltInd: &openolt.OltIndication{OperState: "up"}}
-	if err := stream.Send(&openolt.Indication{Data: data}); err != nil {
-		log.Printf("Failed to send OLT indication: %v\n", err)
-		return err
-	}
-	return nil
-}
-
-func sendIntfInd(stream openolt.Openolt_EnableIndicationServer, olt *olt) error{
-	for i := uint32(0); i < olt.NumPonIntf + olt.NumNniIntf; i ++ {
-		intf := olt.Intfs[i]
-		data := &openolt.Indication_IntfInd{&openolt.IntfIndication{IntfId: intf.IntfID, OperState: intf.OperState}}
-		if err := stream.Send(&openolt.Indication{Data: data}); err != nil {
-			log.Printf("Failed to send Intf [id: %d] indication : %v\n", i, err)
-			return err
-		}
-		log.Printf("SendIntfInd olt:%d intf:%d (%s)\n", olt.ID, intf.IntfID, intf.Type)
-	}
-	return nil
-}
-
-func sendOperInd(stream openolt.Openolt_EnableIndicationServer, olt *olt) error{
-	for i := uint32(0); i < olt.NumPonIntf + olt.NumNniIntf; i ++ {
-		intf := olt.Intfs[i]
-		data := &openolt.Indication_IntfOperInd{&openolt.IntfOperIndication{Type: intf.Type, IntfId: intf.IntfID, OperState: intf.OperState}}
-		if err := stream.Send(&openolt.Indication{Data: data}); err != nil {
-			log.Printf("Failed to send IntfOper [id: %d] indication : %v\n", i, err)
-			return err
-		}
-		log.Printf("SendOperInd olt:%d intf:%d (%s)\n", olt.ID, intf.IntfID, intf.Type)
-	}
-	return nil
-}
-
-func sendOnuDiscInd(stream openolt.Openolt_EnableIndicationServer, onus map[uint32] *onu) error{
-	for i, onu := range onus {
-		data := &openolt.Indication_OnuDiscInd{&openolt.OnuDiscIndication{IntfId: onu.IntfID, SerialNumber:onu.SerialNumber}}
-		log.Printf("sendONUDiscInd Onuid: %d\n", i)
-		if err := stream.Send(&openolt.Indication{Data: data}); err != nil {
-			log.Printf("Failed to send ONUDiscInd [id: %d]: %v\n", i, err)
-			return err
-		}
-	}
-	return nil
-}
-
-func sendOnuInd(stream openolt.Openolt_EnableIndicationServer, onus map[uint32] *onu) error{
-	for i, onu := range onus {
-		data := &openolt.Indication_OnuInd{&openolt.OnuIndication{IntfId: onu.IntfID, OnuId: uint32(i), OperState: "up", AdminState: "up", SerialNumber:onu.SerialNumber}}
-		log.Printf("sendONUInd Onuid: %d\n", i)
-		if err := stream.Send(&openolt.Indication{Data: data}); err != nil {
-			log.Printf("Failed to send ONUInd [id: %d]: %v\n", i, err)
-			return err
-		}
-	}
-	return nil
-}
-
-func newServer(oltid uint32, npon uint32, nonus uint32) *server{
-	s := new(server)
-	s.olt  = createOlt(oltid, npon, 1)
-	nnni := s.olt.NumNniIntf
-	log.Printf("OLT ID: %d was retrieved.\n", s.olt.ID)
-
-	s.onus = make(map[uint32]map[uint32] *onu)
-	for intfid := nnni; intfid < npon + nnni; intfid ++ {
-		s.onus[intfid] = createOnus(oltid, intfid, nonus, nnni)
-	}
-	//log.Printf("s.onus: %v\n", s.onus)
-	return s
-}
-
-func printBanner(){
+func printBanner() {
 	log.Println("     ________    _______   ________                 ")
 	log.Println("    / ____   | / ____   | / ______/  __            ")
 	log.Println("   / /____/ / / /____/ / / /_____   /_/            ")
@@ -285,38 +39,63 @@
 	log.Println("/________/ /________/ /________/ /_/ /_/ /_/ /_/  ")
 }
 
-func getOptions()(string, uint32, uint32, uint32, uint32){
-	addressport := flag.String("H","127.0.0.1:50060","IP address:port")
-	nolts    := flag.Int("N", 1, "Number of OLTs")
-	nports   := flag.Int("i", 1, "Number of PON-IF ports")
-	nonus    := flag.Int("n", 1, "Number of ONUs per PON-IF port")
+func getOptions() (uint32, string, uint32, uint32, uint32, int, int, string, core.Mode) {
+	addressport := flag.String("H", ":50060", "IP address:port")
+	oltid := flag.Int("id", 0, "OLT-ID")
+	nintfs := flag.Int("i", 1, "Number of PON-IF ports")
+	nonus := flag.Int("n", 1, "Number of ONUs per PON-IF port")
+	modeopt := flag.String("m", "default", "Emulation mode (default, aaa, both (aaa & dhcp))")
+	aaawait := flag.Int("a", 30, "Wait time (sec) for activation WPA supplicants")
+	dhcpwait := flag.Int("d", 10, "Wait time (sec) for activation DHCP clients")
+	dhcpservip := flag.String("s", "182.21.0.1", "DHCP Server IP Address")
+	mode := core.DEFAULT
 	flag.Parse()
-	fmt.Printf("%v\n", *addressport)
-	//fmt.Println("nports:", *nports, "nonus:", *nonus)
-	address  := strings.Split(*addressport, ":")[0]
-	port,_ := strconv.Atoi(strings.Split(*addressport, ":")[1])
-	return address, uint32(port), uint32(*nolts), uint32(*nports), uint32(*nonus)
+	if *modeopt == "aaa" {
+		mode = core.AAA
+	} else if *modeopt == "both" {
+		mode = core.BOTH
+	}
+	address := strings.Split(*addressport, ":")[0]
+	tmp, _ := strconv.Atoi(strings.Split(*addressport, ":")[1])
+	port := uint32(tmp)
+	return uint32(*oltid), address, port, uint32(*nintfs), uint32(*nonus), *aaawait, *dhcpwait, *dhcpservip, mode
 }
 
-
 func main() {
+	// CLI Shows up
 	printBanner()
-	ipaddress, baseport, nolts, npon, nonus := getOptions()
-	log.Printf("ip:%s, baseport:%d, nolts:%d, npon:%d, nonus:%d\n", ipaddress, baseport, nolts, npon, nonus)
-	servers := make([] *server, nolts)
-	grpcservers := make([] *grpc.Server, nolts)
-	lis         := make([] net.Listener, nolts)
+	oltid, ip, port, npon, nonus, aaawait, dhcpwait, dhcpservip, mode := getOptions()
+	log.Printf("ip:%s, baseport:%d, npon:%d, nonus:%d, mode:%d\n", ip, port, npon, nonus, mode)
+
+	// Set up gRPC Server
 	var wg sync.WaitGroup
-	wg.Add(int(nolts))
-	for oltid := uint32(0); oltid < nolts; oltid ++ {
-		portnum := baseport + oltid
-		addressport := ipaddress + ":" + strconv.Itoa(int(portnum))
-		log.Printf("Listening %s ...", addressport)
-		lis[oltid], _ = net.Listen("tcp", addressport)
-		servers[oltid] = newServer(oltid, npon, nonus)
-		grpcservers[oltid] = grpc.NewServer()
-		openolt.RegisterOpenoltServer(grpcservers[oltid], servers[oltid])
-		go grpcservers[oltid].Serve(lis[oltid])
+
+	addressport := ip + ":" + strconv.Itoa(int(port))
+	endchan := make(chan int, 1)
+	listener, gserver, err := core.CreateGrpcServer(oltid, npon, nonus, addressport)
+	server := core.CreateServer(oltid, npon, nonus, aaawait, dhcpwait, dhcpservip, gserver, mode, endchan)
+	if err != nil {
+		log.Println(err)
 	}
+	openolt.RegisterOpenoltServer(gserver, server)
+
+	wg.Add(1)
+	go func() {
+		defer wg.Done()
+		gserver.Serve(listener)
+	}()
+
+	c := make(chan os.Signal, 1)
+	signal.Notify(c, os.Interrupt)
+	go func() {
+		for sig := range c {
+			fmt.Println("SIGINT", sig)
+			close(c)
+			close(server.Endchan)
+			gserver.Stop()
+		}
+	}()
 	wg.Wait()
+	time.Sleep(5 * time.Second)
+	log.Println("Reach to the end line")
 }
diff --git a/config/dhcpd.conf b/config/dhcpd.conf
new file mode 100644
index 0000000..9f36163
--- /dev/null
+++ b/config/dhcpd.conf
@@ -0,0 +1,125 @@
+#
+# 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.32;
+  option routers 182.21.0.254;
+}
+
+#subnet 182.22.0.0 netmask 255.255.0.0 {
+#  range 182.22.0.1 182.22.0.240;
+#  option routers 182.22.0.254;
+#}
+
+#subnet 172.20.0.0 netmask 255.255.0.0 {
+#  range 172.20.0.1 172.20.0.240;
+#  option routers 172.20.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
diff --git a/config/isc-dhcp-server b/config/isc-dhcp-server
new file mode 100644
index 0000000..440f7b3
--- /dev/null
+++ b/config/isc-dhcp-server
@@ -0,0 +1,22 @@
+# 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="sim_nu0"
+
diff --git a/config/wpa_supplicant.conf b/config/wpa_supplicant.conf
new file mode 100644
index 0000000..1f0a99c
--- /dev/null
+++ b/config/wpa_supplicant.conf
@@ -0,0 +1,15 @@
+ctrl_interface=/var/run/wpa_supplicant
+eapol_version=1
+ap_scan=0
+fast_reauth=1
+network={
+        key_mgmt=WPA-EAP
+        eap=MD5
+        identity="user"
+        password="password"
+        ca_cert="/etc/cert/cacert.pem"
+        client_cert="/etc/cert/client.pem"
+        private_key="/etc/cert/client.key"
+        private_key_passwd="whatever"
+        eapol_flags=3
+}
diff --git a/core/core_server.go b/core/core_server.go
new file mode 100644
index 0000000..39f4ca7
--- /dev/null
+++ b/core/core_server.go
@@ -0,0 +1,460 @@
+/*
+ * 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 core
+
+import (
+	"gerrit.opencord.org/voltha-bbsim/device"
+	"gerrit.opencord.org/voltha-bbsim/protos"
+	"gerrit.opencord.org/voltha-bbsim/setup"
+	"errors"
+	"github.com/google/gopacket"
+	"github.com/google/gopacket/layers"
+	"github.com/google/gopacket/pcap"
+	"google.golang.org/grpc"
+	"log"
+	"strconv"
+	"sync"
+	"time"
+)
+
+type Mode int
+
+const MAX_ONUS_PER_PON = 64 // This value should be the same with the value in AdapterPlatrorm class
+
+const (
+	DEFAULT Mode = iota
+	AAA
+	BOTH
+)
+
+type Server struct {
+	Olt          *device.Olt
+	Onumap       map[uint32][]*device.Onu
+	Ioinfos      []*Ioinfo
+	Endchan      chan int
+	Mode         Mode
+	AAAWait      int
+	DhcpWait     int
+	DhcpServerIP string
+	gRPCserver   *grpc.Server
+}
+
+type Packet struct {
+	Info *Ioinfo
+	Pkt  gopacket.Packet
+}
+
+func CreateServer(oltid uint32, npon uint32, nonus uint32, aaawait int, dhcpwait int, ip string, g *grpc.Server, mode Mode, e chan int) *Server {
+	s := new(Server)
+	s.Olt = device.CreateOlt(oltid, npon, 1)
+	nnni := s.Olt.NumNniIntf
+	log.Printf("OLT ID: %d was retrieved.\n", s.Olt.ID)
+	s.Onumap = make(map[uint32][]*device.Onu)
+	s.AAAWait = aaawait
+	s.DhcpWait = dhcpwait
+	s.DhcpServerIP = ip
+	s.gRPCserver = g
+	s.Mode = mode
+	s.Endchan = e
+	for intfid := nnni; intfid < npon+nnni; intfid++ {
+		s.Onumap[intfid] = device.CreateOnus(oltid, intfid, nonus, nnni)
+	}
+	return s
+}
+
+func (s *Server) activateOLT(stream openolt.Openolt_EnableIndicationServer) error {
+	// Activate OLT
+	olt := s.Olt
+	oltid := olt.ID
+	vethenv := []string{}
+	wg := &sync.WaitGroup{}
+
+	if err := sendOltInd(stream, olt); err != nil {
+		return err
+	}
+	olt.OperState = "up"
+	olt.InternalState = device.OLT_UP
+	log.Printf("OLT %s sent OltInd.\n", olt.Name)
+
+	// OLT sends Interface Indication to Adapter
+	if err := sendIntfInd(stream, olt); err != nil {
+		log.Printf("[ERROR] Fail to sendIntfInd: %v\n", err)
+		return err
+	}
+	log.Printf("OLT %s sent IntfInd.\n", olt.Name)
+
+	// OLT sends Operation Indication to Adapter after activating each interface
+	//time.Sleep(IF_UP_TIME * time.Second)
+	olt.InternalState = device.PONIF_UP
+	if err := sendOperInd(stream, olt); err != nil {
+		log.Printf("[ERROR] Fail to sendOperInd: %v\n", err)
+		return err
+	}
+	log.Printf("OLT %s sent OperInd.\n", olt.Name)
+
+	// OLT sends ONU Discover Indication to Adapter after ONU discovery
+	for intfid, _ := range s.Onumap {
+		device.UpdateOnusOpStatus(intfid, s.Onumap[intfid], "up")
+	}
+
+	for intfid, _ := range s.Onumap {
+		sendOnuDiscInd(stream, s.Onumap[intfid])
+		log.Printf("OLT id:%d sent ONUDiscInd.\n", olt.ID)
+	}
+
+	// OLT Sends OnuInd after waiting all of those ONUs up
+	for {
+		if s.IsAllONUActive() {
+			break
+		}
+	}
+	for intfid, _ := range s.Onumap {
+		sendOnuInd(stream, s.Onumap[intfid])
+		log.Printf("OLT id:%d sent ONUInd.\n", olt.ID)
+	}
+
+	if s.Mode == DEFAULT {
+		//EnableIndication's stream should be kept even after activateOLT() is finished.
+		//Otherwise, OpenOLT adapter sends EnableIndication again.
+		<-s.Endchan
+		log.Println("core server thread receives close !")
+	} else if s.Mode == AAA || s.Mode == BOTH {
+		var err error
+		s.Ioinfos = []*Ioinfo{}
+		for intfid, _ := range s.Onumap {
+			for i := 0; i < len(s.Onumap[intfid]); i++ {
+				var handler *pcap.Handle
+				onuid := s.Onumap[intfid][i].OnuID
+				uniup, unidw := makeUniName(oltid, intfid, onuid)
+				if handler, vethenv, err = setupVethHandler(uniup, unidw, vethenv); err != nil {
+					return err
+				}
+				iinfo := Ioinfo{name: uniup, iotype: "uni", ioloc: "inside", intfid: intfid, onuid: onuid, handler: handler}
+				s.Ioinfos = append(s.Ioinfos, &iinfo)
+				oinfo := Ioinfo{name: unidw, iotype: "uni", ioloc: "outside", intfid: intfid, onuid: onuid, handler: nil}
+				s.Ioinfos = append(s.Ioinfos, &oinfo)
+			}
+		}
+		var handler *pcap.Handle
+		nniup, nnidw := makeNniName(oltid)
+		if handler, vethenv, err = setupVethHandler(nniup, nnidw, vethenv); err != nil {
+			return err
+		}
+		iinfo := Ioinfo{name: nnidw, iotype: "nni", ioloc: "inside", intfid: 1, handler: handler}
+		s.Ioinfos = append(s.Ioinfos, &iinfo)
+		oinfo := Ioinfo{name: nnidw, iotype: "nni", ioloc: "outside", intfid: 1, handler: nil}
+		s.Ioinfos = append(s.Ioinfos, &oinfo)
+
+		errchan := make(chan error)
+		go func() {
+			<-errchan
+			close(s.Endchan)
+		}()
+
+		wg.Add(1)
+		go func() {
+			defer func() {
+				log.Println("runPacketInDaemon Done")
+				wg.Done()
+			}()
+			err := s.runPacketInDaemon(stream)
+			if err != nil {
+				errchan <- err
+				return
+			}
+		}()
+
+		wg.Add(1)
+		go func() {
+			defer func() {
+				log.Println("exeAAATest Done")
+				wg.Done()
+			}()
+			infos, err := s.getUniIoinfos("outside")
+			if err != nil {
+				errchan <- err
+				return
+			}
+			univeths := []string{}
+			for _, info := range infos {
+				univeths = append(univeths, info.name)
+			}
+			err = s.exeAAATest(univeths)
+			if err != nil {
+				errchan <- err
+				return
+			}
+		}()
+		wg.Wait()
+		tearDown(vethenv) // Grace teardown
+		log.Println("Grace shutdown down")
+	}
+	return nil
+}
+
+func (s *Server) runPacketInDaemon(stream openolt.Openolt_EnableIndicationServer) error {
+	log.Println("runPacketInDaemon Start")
+	unichannel := make(chan Packet, 2048)
+	flag := false
+
+	for intfid, _ := range s.Onumap {
+		for _, onu := range s.Onumap[intfid] { //TODO: should be updated for multiple-Interface
+			onuid := onu.OnuID
+			ioinfo, err := s.identifyUniIoinfo("inside", intfid, onuid)
+			if err != nil {
+				log.Printf("[ERROR] Fail to identifyUniIoinfo (onuid: %d): %v\n", onuid, err)
+				return err
+			}
+			uhandler := ioinfo.handler
+			defer uhandler.Close()
+			go RecvWorker(ioinfo, uhandler, unichannel)
+		}
+	}
+
+	ioinfo, err := s.identifyNniIoinfo("inside")
+	if err != nil {
+		return err
+	}
+	nhandler := ioinfo.handler
+	defer nhandler.Close()
+	nnichannel := make(chan Packet, 32)
+	go RecvWorker(ioinfo, nhandler, nnichannel)
+
+	data := &openolt.Indication_PktInd{}
+	for {
+		select {
+		case unipkt := <-unichannel:
+			log.Println("Received packet in grpc Server from UNI.")
+			if unipkt.Info == nil || unipkt.Info.iotype != "uni" {
+				log.Println("[WARNING] This packet does not come from UNI !")
+				continue
+			}
+			intfid := unipkt.Info.intfid
+			onuid := unipkt.Info.onuid
+			gemid, _ := getGemPortID(intfid, onuid)
+			pkt := unipkt.Pkt
+			layerEth := pkt.Layer(layers.LayerTypeEthernet)
+			le, _ := layerEth.(*layers.Ethernet)
+			ethtype := le.EthernetType
+			if ethtype == 0x888e {
+				log.Printf("Received upstream packet is EAPOL.")
+				log.Println(unipkt.Pkt.Dump())
+				log.Println(pkt.Dump())
+			} else if layerDHCP := pkt.Layer(layers.LayerTypeDHCPv4); layerDHCP != nil {
+				log.Printf("Received upstream packet is DHCP.")
+				log.Println(unipkt.Pkt.Dump())
+				log.Println(pkt.Dump())
+			} else {
+				continue
+			}
+			log.Printf("sendPktInd intfid:%d (onuid: %d) gemid:%d\n", intfid, onuid, gemid)
+			data = &openolt.Indication_PktInd{PktInd: &openolt.PacketIndication{IntfType: "pon", IntfId: intfid, GemportId: gemid, Pkt: pkt.Data()}}
+			if err := stream.Send(&openolt.Indication{Data: data}); err != nil {
+				log.Printf("[ERROR] Failed to send PktInd indication. %v\n", err)
+				return err
+			}
+		case nnipkt := <-nnichannel:
+			if nnipkt.Info == nil || nnipkt.Info.iotype != "nni" {
+				log.Println("[WARNING] This packet does not come from NNI !")
+				continue
+			}
+			log.Println("Received packet in grpc Server from NNI.")
+			intfid := nnipkt.Info.intfid
+			pkt := nnipkt.Pkt
+			log.Printf("sendPktInd intfid:%d\n", intfid)
+			data = &openolt.Indication_PktInd{PktInd: &openolt.PacketIndication{IntfType: "nni", IntfId: intfid, Pkt: pkt.Data()}}
+			if err := stream.Send(&openolt.Indication{Data: data}); err != nil {
+				log.Printf("[ERROR] Failed to send PktInd indication. %v\n", err)
+				return err
+			}
+		case <-s.Endchan:
+			if flag == false {
+				log.Println("PacketInDaemon thread receives close !")
+				close(unichannel)
+				log.Println("Closed unichannel !")
+				close(nnichannel)
+				log.Println("Closed nnichannel !")
+				flag = true
+				return nil
+			}
+		}
+	}
+	return nil
+}
+
+func (s *Server) exeAAATest(vethenv []string) error {
+	log.Println("exeAAATest Start")
+	for i := 0; i < s.AAAWait; i++ {
+		select {
+		case <-s.Endchan:
+			log.Println("exeAAATest thread receives close !")
+			return nil
+		default:
+			log.Println("exeAAATest is now sleeping....")
+			time.Sleep(time.Second)
+		}
+	}
+	err := setup.ActivateWPASups(vethenv)
+	if err != nil {
+		return err
+	}
+	return nil
+}
+
+func (s *Server) onuPacketOut(intfid uint32, onuid uint32, rawpkt gopacket.Packet) error {
+	layerEth := rawpkt.Layer(layers.LayerTypeEthernet)
+	if layerEth != nil {
+		pkt, _ := layerEth.(*layers.Ethernet)
+		ethtype := pkt.EthernetType
+		if ethtype == 0x888e {
+			log.Printf("Received downstream packet is EAPOL.")
+			log.Println(rawpkt.Dump())
+		} else if layerDHCP := rawpkt.Layer(layers.LayerTypeDHCPv4); layerDHCP != nil {
+			log.Printf("Received downstream packet is DHCP.")
+			log.Println(rawpkt.Dump())
+			rawpkt, _, _ = PopVLAN(rawpkt)
+			rawpkt, _, _ = PopVLAN(rawpkt)
+		} else {
+			return nil
+		}
+		ioinfo, err := s.identifyUniIoinfo("inside", intfid, onuid)
+		if err != nil {
+			return err
+		}
+		handle := ioinfo.handler
+		SendUni(handle, rawpkt)
+		return nil
+	}
+	log.Printf("[WARNING] Received packet is not supported")
+	return nil
+}
+
+func (s *Server) uplinkPacketOut(rawpkt gopacket.Packet) error {
+	log.Println("")
+	poppkt, _, err := PopVLAN(rawpkt)
+	poppkt, _, err = PopVLAN(poppkt)
+	if err != nil {
+		log.Println(err)
+		return err
+	}
+	ioinfo, err := s.identifyNniIoinfo("inside")
+	if err != nil {
+		return err
+	}
+	handle := ioinfo.handler
+	SendNni(handle, poppkt)
+	return nil
+}
+
+func (s *Server) IsAllONUActive() bool {
+	for _, onus := range s.Onumap {
+		for _, onu := range onus {
+			if onu.InternalState != device.ONU_ACTIVATED {
+				return false
+			}
+		}
+	}
+	return true
+}
+
+func getVID(onuid uint32) (uint16, error) {
+	return uint16(onuid), nil
+}
+
+func getGemPortID(intfid uint32, onuid uint32) (uint32, error) {
+	idx := uint32(0)
+	return 1024 + (((MAX_ONUS_PER_PON*intfid + onuid - 1) * 7) + idx), nil
+	//return uint32(1032 + 8 * (vid - 1)), nil
+}
+
+func (s *Server) getOnuBySN(sn *openolt.SerialNumber) (*device.Onu, error) {
+	for _, onus := range s.Onumap {
+		for _, onu := range onus {
+			if device.ValidateSN(*sn, *onu.SerialNumber) {
+				return onu, nil
+			}
+		}
+	}
+	err := errors.New("No mathced SN is found !")
+	log.Println(err)
+	return nil, err
+}
+
+func (s *Server) getOnuByID(onuid uint32) (*device.Onu, error) {
+	for _, onus := range s.Onumap {
+		for _, onu := range onus {
+			if onu.OnuID == onuid {
+				return onu, nil
+			}
+		}
+	}
+	err := errors.New("No matched OnuID is found !")
+	log.Println(err)
+	return nil, err
+}
+
+func makeUniName(oltid uint32, intfid uint32, onuid uint32) (upif string, dwif string) {
+	upif = setup.UNI_VETH_UP_PFX + strconv.Itoa(int(oltid)) + "_" + strconv.Itoa(int(intfid)) + "_" + strconv.Itoa(int(onuid))
+	dwif = setup.UNI_VETH_DW_PFX + strconv.Itoa(int(oltid)) + "_" + strconv.Itoa(int(intfid)) + "_" + strconv.Itoa(int(onuid))
+	return
+}
+
+func makeNniName(oltid uint32) (upif string, dwif string) {
+	upif = setup.NNI_VETH_UP_PFX + strconv.Itoa(int(oltid))
+	dwif = setup.NNI_VETH_DW_PFX + strconv.Itoa(int(oltid))
+	return
+}
+
+func tearDown(vethenv []string) error {
+	log.Println("tearDown()")
+	setup.KillAllWPASups()
+	setup.KillAllDHCPClients()
+	setup.TearVethDown(vethenv)
+	return nil
+}
+
+func setupVethHandler(inveth string, outveth string, vethenv []string) (*pcap.Handle, []string, error) {
+	log.Printf("setupVethHandler: %s and %s\n", inveth, outveth)
+	err1 := setup.CreateVethPairs(inveth, outveth)
+	vethenv = append(vethenv, inveth)
+	if err1 != nil {
+		setup.RemoveVeths(vethenv)
+		return nil, vethenv, err1
+	}
+	handler, err2 := getVethHandler(inveth)
+	if err2 != nil {
+		setup.RemoveVeths(vethenv)
+		return nil, vethenv, err2
+	}
+	return handler, vethenv, nil
+}
+
+func getVethHandler(vethname string) (*pcap.Handle, error) {
+	var (
+		device       string = vethname
+		snapshot_len int32  = 1518
+		promiscuous  bool   = false
+		err          error
+		timeout      time.Duration = pcap.BlockForever
+	)
+	handle, err := pcap.OpenLive(device, snapshot_len, promiscuous, timeout)
+	if err != nil {
+		return nil, err
+	}
+	log.Printf("Server handle created for %s\n", vethname)
+	return handle, nil
+}
diff --git a/core/grpc_service.go b/core/grpc_service.go
new file mode 100644
index 0000000..a5bc3e5
--- /dev/null
+++ b/core/grpc_service.go
@@ -0,0 +1,154 @@
+/*
+ * 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 core
+
+import (
+	"gerrit.opencord.org/voltha-bbsim/device"
+	"gerrit.opencord.org/voltha-bbsim/protos"
+	"github.com/google/gopacket"
+	"github.com/google/gopacket/layers"
+	"golang.org/x/net/context"
+	"google.golang.org/grpc"
+	"log"
+	"net"
+)
+
+// gRPC Service
+func (s *Server) DisableOlt(c context.Context, empty *openolt.Empty) (*openolt.Empty, error) {
+	log.Printf("OLT receives DisableOLT()\n")
+	return new(openolt.Empty), nil
+}
+
+func (s *Server) ReenableOlt(c context.Context, empty *openolt.Empty) (*openolt.Empty, error) {
+	log.Printf("OLT receives Reenable()\n")
+	return new(openolt.Empty), nil
+}
+
+func (s *Server) CollectStatistics(c context.Context, empty *openolt.Empty) (*openolt.Empty, error) {
+	log.Printf("OLT receives CollectStatistics()\n")
+	return new(openolt.Empty), nil
+}
+
+func (s *Server) GetDeviceInfo(c context.Context, empty *openolt.Empty) (*openolt.DeviceInfo, error) {
+	log.Printf("OLT receives GetDeviceInfo()\n")
+	return new(openolt.DeviceInfo), nil
+}
+
+func (s *Server) ActivateOnu(c context.Context, onu *openolt.Onu) (*openolt.Empty, error) {
+	log.Printf("OLT receives ActivateONU()\n")
+	result := device.ValidateONU(*onu, s.Onumap)
+
+	if result == true {
+		matched, error := s.getOnuBySN(onu.SerialNumber)
+		if error != nil {
+			log.Fatalf("%s\n", error)
+		}
+		onuid := onu.OnuId
+		matched.OnuID = onuid
+		matched.InternalState = device.ONU_ACTIVATED
+		log.Printf("ONU IntfID: %d OnuID: %d activated succesufully.\n", onu.IntfId, onu.OnuId)
+	}
+	return new(openolt.Empty), nil
+}
+
+func (s *Server) DeactivateOnu(c context.Context, onu *openolt.Onu) (*openolt.Empty, error) {
+	log.Printf("OLT receives DeactivateONU()\n")
+	return new(openolt.Empty), nil
+}
+
+func (s *Server) DeleteOnu(c context.Context, onu *openolt.Onu) (*openolt.Empty, error) {
+	log.Printf("OLT receives DeleteONU()\n")
+	return new(openolt.Empty), nil
+}
+
+func (s *Server) OmciMsgOut(c context.Context, msg *openolt.OmciMsg) (*openolt.Empty, error) {
+	log.Printf("OLT %d receives OmciMsgOut to IF %v (ONU-ID: %v) pkt:%x.\n", s.Olt.ID, msg.IntfId, msg.OnuId, msg.Pkt)
+	//s.olt.Queue = append(s.olt.Queue, *msg)
+	return new(openolt.Empty), nil
+}
+
+func (s *Server) OnuPacketOut(c context.Context, packet *openolt.OnuPacket) (*openolt.Empty, error) {
+	log.Printf("OLT %d receives OnuPacketOut () to IF-ID:%d ONU-ID %d.\n", s.Olt.ID, packet.IntfId, packet.OnuId)
+	onuid := packet.OnuId
+	intfid := packet.IntfId
+	rawpkt := gopacket.NewPacket(packet.Pkt, layers.LayerTypeEthernet, gopacket.Default)
+	if err := s.onuPacketOut(intfid, onuid, rawpkt); err != nil {
+		return new(openolt.Empty), err
+	}
+	return new(openolt.Empty), nil
+}
+
+func (s *Server) UplinkPacketOut(c context.Context, packet *openolt.UplinkPacket) (*openolt.Empty, error) {
+	log.Printf("OLT %d receives UplinkPacketOut().\n", s.Olt.ID)
+	rawpkt := gopacket.NewPacket(packet.Pkt, layers.LayerTypeEthernet, gopacket.Default)
+	if err := s.uplinkPacketOut(rawpkt); err != nil {
+		return new(openolt.Empty), err
+	}
+	return new(openolt.Empty), nil
+}
+
+func (s *Server) FlowAdd(c context.Context, flow *openolt.Flow) (*openolt.Empty, error) {
+	log.Printf("OLT %d receives FlowAdd().\n", s.Olt.ID)
+	return new(openolt.Empty), nil
+}
+
+func (s *Server) FlowRemove(c context.Context, flow *openolt.Flow) (*openolt.Empty, error) {
+	log.Printf("OLT %d receives FlowRemove().\n", s.Olt.ID)
+	return new(openolt.Empty), nil
+}
+
+func (s *Server) HeartbeatCheck(c context.Context, empty *openolt.Empty) (*openolt.Heartbeat, error) {
+	log.Printf("OLT %d receives HeartbeatCheck().\n", s.Olt.ID)
+	signature := new(openolt.Heartbeat)
+	signature.HeartbeatSignature = s.Olt.HeartbeatSignature
+	return signature, nil
+}
+
+func (s *Server) EnablePonIf(c context.Context, intf *openolt.Interface) (*openolt.Empty, error) {
+	log.Printf("OLT %d receives EnablePonIf().\n", s.Olt.ID)
+	return new(openolt.Empty), nil
+}
+
+func (s *Server) DisablePonIf(c context.Context, intf *openolt.Interface) (*openolt.Empty, error) {
+	log.Printf("OLT %d receives DisablePonIf().\n", s.Olt.ID)
+	return new(openolt.Empty), nil
+}
+
+func (s *Server) Reboot(c context.Context, empty *openolt.Empty) (*openolt.Empty, error) {
+	log.Printf("OLT %d receives Reboot ().\n", s.Olt.ID)
+	return new(openolt.Empty), nil
+}
+
+func (s *Server) EnableIndication(empty *openolt.Empty, stream openolt.Openolt_EnableIndicationServer) error {
+	defer func() {
+		s.gRPCserver.Stop()
+	}()
+	log.Printf("OLT receives EnableInd.\n")
+	if err := s.activateOLT(stream); err != nil {
+		log.Printf("Failed to activate OLT: %v\n", err)
+		return err
+	}
+	log.Println("Core server down.")
+	return nil
+}
+
+func CreateGrpcServer(oltid uint32, npon uint32, nonus uint32, addrport string) (l net.Listener, g *grpc.Server, e error) {
+	log.Printf("Listening %s ...", addrport)
+	g = grpc.NewServer()
+	l, e = net.Listen("tcp", addrport)
+	return
+}
diff --git a/core/io_info.go b/core/io_info.go
new file mode 100644
index 0000000..4252509
--- /dev/null
+++ b/core/io_info.go
@@ -0,0 +1,69 @@
+/*
+ * 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 core
+
+import (
+	"errors"
+	"github.com/google/gopacket/pcap"
+	"log"
+)
+
+type Ioinfo struct {
+	name    string
+	iotype  string //nni or uni
+	ioloc   string //inside or outsode
+	intfid  uint32
+	onuid   uint32
+	handler *pcap.Handle
+}
+
+func (s *Server) identifyUniIoinfo(ioloc string, intfid uint32, onuid uint32) (*Ioinfo, error) {
+	for _, ioinfo := range s.Ioinfos {
+		if ioinfo.iotype == "uni" && ioinfo.intfid == intfid && ioinfo.onuid == onuid && ioinfo.ioloc == ioloc {
+			return ioinfo, nil
+		}
+	}
+	err := errors.New("No matched Ioinfo is found !")
+	log.Println(err)
+	return nil, err
+}
+
+func (s *Server) identifyNniIoinfo(ioloc string) (*Ioinfo, error) {
+	for _, ioinfo := range s.Ioinfos {
+		if ioinfo.iotype == "nni" && ioinfo.ioloc == ioloc {
+			return ioinfo, nil
+		}
+	}
+	err := errors.New("No matched Ioinfo is found !")
+	log.Println(err)
+	return nil, err
+}
+
+func (s *Server) getUniIoinfos(ioloc string) ([]*Ioinfo, error) {
+	ioinfos := []*Ioinfo{}
+	for _, ioinfo := range s.Ioinfos {
+		if ioinfo.iotype == "uni" && ioinfo.ioloc == ioloc {
+			ioinfos = append(ioinfos, ioinfo)
+		}
+	}
+	if len(ioinfos) == 0 {
+		err := errors.New("No matched Ioinfo is found !")
+		log.Println(err)
+		return nil, err
+	}
+	return ioinfos, nil
+}
diff --git a/core/io_worker.go b/core/io_worker.go
new file mode 100644
index 0000000..a535869
--- /dev/null
+++ b/core/io_worker.go
@@ -0,0 +1,135 @@
+/*
+ * 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 core
+
+import (
+	"errors"
+	"github.com/google/gopacket"
+	"github.com/google/gopacket/layers"
+	"github.com/google/gopacket/pcap"
+	"log"
+	"net"
+)
+
+func RecvWorker(io *Ioinfo, handler *pcap.Handle, r chan Packet) {
+	log.Printf("recvWorker runs. handler: %v", *handler)
+	packetSource := gopacket.NewPacketSource(handler, handler.LinkType())
+	for packet := range packetSource.Packets() {
+		log.Printf("recv packet from IF: %v \n", *handler)
+		//log.Println(packet.Dump())
+		pkt := Packet{}
+		pkt.Info = io
+		pkt.Pkt = packet
+		r <- pkt
+	}
+}
+
+func SendUni(handle *pcap.Handle, packet gopacket.Packet) {
+	handle.WritePacketData(packet.Data())
+	log.Printf("send packet to UNI-IF: %v \n", *handle)
+	//log.Println(packet.Dump())
+}
+
+func SendNni(handle *pcap.Handle, packet gopacket.Packet) {
+	handle.WritePacketData(packet.Data())
+	log.Printf("send packet to NNI-IF: %v \n", *handle)
+	//log.Println(packet.Dump())
+}
+
+func PopVLAN(pkt gopacket.Packet) (gopacket.Packet, uint16, error) {
+	if layer := getDot1QLayer(pkt); layer != nil {
+		if eth := getEthernetLayer(pkt); eth != nil {
+			ethernetLayer := &layers.Ethernet{
+				SrcMAC:       eth.SrcMAC,
+				DstMAC:       eth.DstMAC,
+				EthernetType: layer.Type,
+			}
+			buffer := gopacket.NewSerializeBuffer()
+			gopacket.SerializeLayers(buffer, gopacket.SerializeOptions{},
+				ethernetLayer,
+				gopacket.Payload(layer.Payload),
+			)
+			retpkt := gopacket.NewPacket(
+				buffer.Bytes(),
+				layers.LayerTypeEthernet,
+				gopacket.Default,
+			)
+			vid := uint16(4095 & layer.VLANIdentifier)
+			log.Printf("Pop the 802.1Q header (VID: %d)", vid)
+			return retpkt, vid, nil
+		}
+	}
+	//return pkt, 1, nil
+	return nil, 0, errors.New("failed to pop vlan")
+}
+
+func PushVLAN(pkt gopacket.Packet, vid uint16) (gopacket.Packet, error) {
+	if eth := getEthernetLayer(pkt); eth != nil {
+		ethernetLayer := &layers.Ethernet{
+			SrcMAC:       eth.SrcMAC,
+			DstMAC:       eth.DstMAC,
+			EthernetType: 0x8100,
+		}
+		dot1qLayer := &layers.Dot1Q{
+			Type:           eth.EthernetType,
+			VLANIdentifier: uint16(vid),
+		}
+
+		buffer := gopacket.NewSerializeBuffer()
+		gopacket.SerializeLayers(
+			buffer,
+			gopacket.SerializeOptions{
+				FixLengths: false,
+			},
+			ethernetLayer,
+			dot1qLayer,
+			gopacket.Payload(eth.Payload),
+		)
+		ret := gopacket.NewPacket(
+			buffer.Bytes(),
+			layers.LayerTypeEthernet,
+			gopacket.Default,
+		)
+		log.Printf("Push the 802.1Q header (VID: %d)", vid)
+		return ret, nil
+	}
+	return nil, errors.New("failed to push vlan")
+}
+
+func getEthernetLayer(pkt gopacket.Packet) *layers.Ethernet {
+	eth := &layers.Ethernet{}
+	if ethLayer := pkt.Layer(layers.LayerTypeEthernet); ethLayer != nil {
+		eth, _ = ethLayer.(*layers.Ethernet)
+	}
+	return eth
+}
+func getDot1QLayer(pkt gopacket.Packet) (dot1q *layers.Dot1Q) {
+	if dot1qLayer := pkt.Layer(layers.LayerTypeDot1Q); dot1qLayer != nil {
+		dot1q = dot1qLayer.(*layers.Dot1Q)
+	}
+	return dot1q
+}
+
+func getMacAddress(ifName string) net.HardwareAddr {
+	var err error
+	var netIf *net.Interface
+	var hwAddr net.HardwareAddr
+	if netIf, err = net.InterfaceByName(ifName); err == nil {
+		hwAddr = netIf.HardwareAddr
+	}
+	return hwAddr
+}
diff --git a/core/openolt_service.go b/core/openolt_service.go
new file mode 100644
index 0000000..3f0c311
--- /dev/null
+++ b/core/openolt_service.go
@@ -0,0 +1,82 @@
+/*
+ * 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 core
+
+import (
+	"gerrit.opencord.org/voltha-bbsim/protos"
+	"gerrit.opencord.org/voltha-bbsim/device"
+	"log"
+)
+
+func sendOltInd(stream openolt.Openolt_EnableIndicationServer, olt *device.Olt) error {
+	data := &openolt.Indication_OltInd{OltInd: &openolt.OltIndication{OperState: "up"}}
+	if err := stream.Send(&openolt.Indication{Data: data}); err != nil {
+		log.Printf("Failed to send OLT indication: %v\n", err)
+		return err
+	}
+	return nil
+}
+
+func sendIntfInd(stream openolt.Openolt_EnableIndicationServer, olt *device.Olt) error {
+	for i := uint32(0); i < olt.NumPonIntf+olt.NumNniIntf; i++ {
+		intf := olt.Intfs[i]
+		data := &openolt.Indication_IntfInd{&openolt.IntfIndication{IntfId: intf.IntfID, OperState: intf.OperState}}
+		if err := stream.Send(&openolt.Indication{Data: data}); err != nil {
+			log.Printf("Failed to send Intf [id: %d] indication : %v\n", i, err)
+			return err
+		}
+		log.Printf("SendIntfInd olt:%d intf:%d (%s)\n", olt.ID, intf.IntfID, intf.Type)
+	}
+	return nil
+}
+
+func sendOperInd(stream openolt.Openolt_EnableIndicationServer, olt *device.Olt) error {
+	for i := uint32(0); i < olt.NumPonIntf+olt.NumNniIntf; i++ {
+		intf := olt.Intfs[i]
+		data := &openolt.Indication_IntfOperInd{&openolt.IntfOperIndication{Type: intf.Type, IntfId: intf.IntfID, OperState: intf.OperState}}
+		if err := stream.Send(&openolt.Indication{Data: data}); err != nil {
+			log.Printf("Failed to send IntfOper [id: %d] indication : %v\n", i, err)
+			return err
+		}
+		log.Printf("SendOperInd olt:%d intf:%d (%s)\n", olt.ID, intf.IntfID, intf.Type)
+	}
+	return nil
+}
+
+func sendOnuDiscInd(stream openolt.Openolt_EnableIndicationServer, onus []*device.Onu) error {
+	for i, onu := range onus {
+		data := &openolt.Indication_OnuDiscInd{&openolt.OnuDiscIndication{IntfId: onu.IntfID, SerialNumber: onu.SerialNumber}}
+		log.Printf("sendONUDiscInd Onuid: %d\n", i)
+		if err := stream.Send(&openolt.Indication{Data: data}); err != nil {
+			log.Printf("Failed to send ONUDiscInd [id: %d]: %v\n", i, err)
+			return err
+		}
+	}
+	return nil
+}
+
+func sendOnuInd(stream openolt.Openolt_EnableIndicationServer, onus []*device.Onu) error {
+	for i, onu := range onus {
+		data := &openolt.Indication_OnuInd{&openolt.OnuIndication{IntfId: onu.IntfID, OnuId: onu.OnuID, OperState: "up", AdminState: "up", SerialNumber: onu.SerialNumber}}
+		log.Printf("sendONUInd Onuid: %d\n", i)
+		if err := stream.Send(&openolt.Indication{Data: data}); err != nil {
+			log.Printf("Failed to send ONUInd [id: %d]: %v\n", i, err)
+			return err
+		}
+	}
+	return nil
+}
diff --git a/device/device_olt.go b/device/device_olt.go
new file mode 100644
index 0000000..a01dda1
--- /dev/null
+++ b/device/device_olt.go
@@ -0,0 +1,69 @@
+/*
+ * 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 device
+
+type oltState int
+
+type Olt struct {
+	ID                 uint32
+	NumPonIntf         uint32
+	NumNniIntf         uint32
+	Mac                string
+	SerialNumber       string
+	Manufacture        string
+	Name               string
+	InternalState      oltState
+	OperState          string
+	Intfs              []intf
+	HeartbeatSignature uint32
+}
+
+type intf struct {
+	Type      string
+	IntfID    uint32
+	OperState string
+}
+
+const (
+	PRE_ENABLE oltState = iota
+	OLT_UP
+	PONIF_UP
+	ONU_DISCOVERED
+)
+
+func CreateOlt(oltid uint32, npon uint32, nnni uint32) *Olt {
+	olt := Olt{}
+	olt.ID = oltid
+	olt.NumPonIntf = npon
+	olt.NumNniIntf = nnni
+	olt.Name = "BBSIM OLT"
+	olt.InternalState = PRE_ENABLE
+	olt.OperState = "up"
+	olt.Intfs = make([]intf, olt.NumPonIntf+olt.NumNniIntf)
+	olt.HeartbeatSignature = oltid
+	for i := uint32(0); i < olt.NumNniIntf; i++ {
+		olt.Intfs[i].IntfID = i
+		olt.Intfs[i].OperState = "up"
+		olt.Intfs[i].Type = "nni"
+	}
+	for i := uint32(olt.NumNniIntf); i < olt.NumPonIntf+olt.NumNniIntf; i++ {
+		olt.Intfs[i].IntfID = i
+		olt.Intfs[i].OperState = "up"
+		olt.Intfs[i].Type = "pon"
+	}
+	return &olt
+}
diff --git a/device/device_onu.go b/device/device_onu.go
new file mode 100644
index 0000000..6d3df40
--- /dev/null
+++ b/device/device_onu.go
@@ -0,0 +1,81 @@
+/*
+ * 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 device
+
+import (
+	"fmt"
+	"log"
+	"gerrit.opencord.org/voltha-bbsim/protos"
+	"reflect"
+)
+
+type onuState int
+
+const (
+	ONU_PRE_ACTIVATED onuState = iota
+	ONU_ACTIVATED
+)
+
+type Onu struct {
+	InternalState onuState
+	IntfID        uint32
+	OperState     string
+	SerialNumber  *openolt.SerialNumber
+	OnuID         uint32
+}
+
+func createSN(oltid uint32, intfid uint32, onuid uint32) string {
+	sn := fmt.Sprintf("%X%X%02X", oltid, intfid, onuid)
+	return sn
+}
+
+func CreateOnus(oltid uint32, intfid uint32, nonus uint32, nnni uint32) []*Onu {
+	onus := []*Onu{}
+	for i := 0; i < int(nonus); i++ {
+		onu := Onu{}
+		onu.InternalState = ONU_PRE_ACTIVATED
+		onu.IntfID = intfid
+		onu.OperState = "up"
+		onu.SerialNumber = new(openolt.SerialNumber)
+		onu.SerialNumber.VendorId = []byte("NONE")
+		onu.SerialNumber.VendorSpecific = []byte(createSN(oltid, intfid, uint32(i))) //FIX
+		onus = append(onus, &onu)
+	}
+	return onus
+}
+
+func ValidateONU(targetonu openolt.Onu, regonus map[uint32][]*Onu) bool {
+	for _, onus := range regonus {
+		for _, onu := range onus {
+			if ValidateSN(*targetonu.SerialNumber, *onu.SerialNumber) {
+				return true
+			}
+		}
+	}
+	return false
+}
+
+func ValidateSN(sn1 openolt.SerialNumber, sn2 openolt.SerialNumber) bool {
+	return reflect.DeepEqual(sn1.VendorId, sn2.VendorId) && reflect.DeepEqual(sn1.VendorSpecific, sn2.VendorSpecific)
+}
+
+func UpdateOnusOpStatus(ponif uint32, onus []*Onu, opstatus string) {
+	for i, onu := range onus {
+		onu.OperState = "up"
+		log.Printf("(PONIF:%d) ONU [%d] discovered.\n", ponif, i)
+	}
+}
diff --git a/grpc_service.go b/grpc_service.go
deleted file mode 100644
index 33993f6..0000000
--- a/grpc_service.go
+++ /dev/null
@@ -1,112 +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 main
-
-import (
-	"log"
-  "gerrit.opencord.org/voltha-bbsim/openolt"
-	"golang.org/x/net/context"
-	"time"
-)
-
-// gRPC Service
-func (s *server) DisableOlt(c context.Context, empty *openolt.Empty) (*openolt.Empty, error){
-	log.Printf("OLT receives DisableOLT()\n")
-	return new(openolt.Empty), nil
-}
-
-func (s *server) ReenableOlt(c context.Context, empty *openolt.Empty)(*openolt.Empty, error){
-	log.Printf("OLT receives Reenable()\n")
-	return new(openolt.Empty), nil
-}
-
-func (s *server) ActivateOnu(c context.Context, onu *openolt.Onu) (*openolt.Empty, error){
-	log.Printf("OLT receives ActivateONU()\n")
-	result := validateONU(*onu, s.onus)
-	if result == true {
-		log.Printf("ONU %d activated succesufully.\n", onu.OnuId)
-		s.onus[onu.IntfId][onu.OnuId].internalState = ONU_ACTIVATED
-		//log.Printf("ActivateOnu(): %v", s.onus)
-	}
-	return new(openolt.Empty), nil
-}
-
-func (s *server) DeactivateOnu(c context.Context, onu *openolt.Onu) (*openolt.Empty, error){
-	log.Printf("OLT receives DeactivateONU()\n")
-	return new(openolt.Empty), nil
-}
-
-func (s *server) DeleteOnu(c context.Context, onu *openolt.Onu) (*openolt.Empty, error){
-	log.Printf("OLT receives DeleteONU()\n")
-	return new(openolt.Empty), nil
-}
-
-func (s *server)OmciMsgOut(c context.Context, msg *openolt.OmciMsg)(*openolt.Empty, error){
-	//log.Printf("OLT %d receives OmciMsgOut to IF %v (ONU-ID: %v): %v.\n", s.olt.ID, msg.IntfId, msg.OnuId, msg.Pkt)
-	log.Printf("OLT %d receives OmciMsgOut to IF %v (ONU-ID: %v).\n", s.olt.ID, msg.IntfId, msg.OnuId)
-	return new(openolt.Empty), nil
-}
-
-func (s *server) OnuPacketOut(c context.Context, packet *openolt.OnuPacket)(*openolt.Empty, error){
-	log.Printf("OLT %d receives OnuPacketOut ().\n", s.olt.ID)
-	return new(openolt.Empty), nil
-}
-
-func (s *server) UplinkPacketOut(c context.Context, packet *openolt.UplinkPacket)(*openolt.Empty, error){
-	log.Printf("OLT %d receives UplinkPacketOut().\n", s.olt.ID)
-	return new(openolt.Empty), nil
-}
-
-func (s *server) FlowAdd(c context.Context, flow *openolt.Flow)(*openolt.Empty, error){
-	log.Printf("OLT %d receives FlowAdd().\n", s.olt.ID)
-	return new(openolt.Empty), nil
-}
-
-func (s *server) HeartbeatCheck(c context.Context, empty *openolt.Empty) (*openolt.Heartbeat, error){
-	log.Printf("OLT %d receives HeartbeatCheck().\n", s.olt.ID)
-	signature := new(openolt.Heartbeat)
-	signature.HeartbeatSignature = s.olt.HeartbeatSignature
-	return signature, nil
-}
-
-func (s *server) EnablePonIf(c context.Context, intf *openolt.Interface) (*openolt.Empty, error){
-	log.Printf("OLT %d receives EnablePonIf().\n", s.olt.ID)
-	return new(openolt.Empty), nil
-}
-
-func (s *server) DisablePonIf(c context.Context, intf *openolt.Interface) (*openolt.Empty, error){
-	log.Printf("OLT %d receives DisablePonIf().\n", s.olt.ID)
-	return new(openolt.Empty), nil
-}
-
-func (s *server) Reboot(c context.Context, empty *openolt.Empty,) (*openolt.Empty, error){
-	log.Printf("OLT %d receives Reboot ().\n", s.olt.ID)
-	return new(openolt.Empty), nil
-}
-
-func (s *server) EnableIndication(empty *openolt.Empty, stream openolt.Openolt_EnableIndicationServer) error {
-	log.Printf("OLT receives EnableInd.\n")
-	if err := activateOLT(s, stream); err != nil {
-		log.Printf("Failed to activate OLT: %v\n", err)
-		return err
-	}
-	// Infinite loop TODO: This should be a queue processing.
-	for {
-		time.Sleep(1 * time.Second)
-	}
-	return nil
-}
diff --git a/openolt.proto b/openolt.proto
index c4d4fd0..ed69539 100644
--- a/openolt.proto
+++ b/openolt.proto
@@ -81,6 +81,13 @@
         };
     }
 
+    rpc FlowRemove(Flow) returns (Empty) {
+        option (google.api.http) = {
+          post: "/v1/FlowRemove"
+          body: "*"
+        };
+    }
+
     rpc HeartbeatCheck(Empty) returns (Heartbeat) {
         option (google.api.http) = {
           post: "/v1/HeartbeatCheck"
@@ -102,6 +109,13 @@
         };
     }
 
+    rpc GetDeviceInfo(Empty) returns (DeviceInfo) {
+        option (google.api.http) = {
+            post: "/v1/GetDeviceInfo"
+            body: "*"
+        };
+    }
+
     rpc Reboot(Empty) returns (Empty) {
          option (google.api.http) = {
             post: "/v1/Reboot"
@@ -109,6 +123,13 @@
         };
     }
 
+    rpc CollectStatistics(Empty) returns (Empty) {
+        option (google.api.http) = {
+            post: "/v1/CollectStatistics"
+            body: "*"
+        };
+    }
+
     rpc EnableIndication(Empty) returns (stream Indication) {}
 }
 
@@ -178,6 +199,7 @@
 }
 
 message PacketIndication {
+    string intf_type = 5;		// nni, pon, unknown
     fixed32 intf_id = 1;
     fixed32 gemport_id = 2;
     fixed32 flow_id = 3;
@@ -197,6 +219,7 @@
     fixed32 onu_id = 2;
     SerialNumber serial_number = 3;
     fixed32 pir = 4;   // peak information rate assigned to onu
+    fixed32 alloc_id = 5;
 }
 
 message OmciMsg {
@@ -216,6 +239,21 @@
     bytes pkt = 2;
 }
 
+message DeviceInfo {
+    string vendor = 1;
+    string model = 2;
+    string hardware_version = 3;
+    string firmware_version = 4;
+    string technology = 5;
+    fixed32 onu_id_start = 6;
+    fixed32 onu_id_end = 7;
+    fixed32 alloc_id_start = 8;
+    fixed32 alloc_id_end = 9;
+    fixed32 gemport_id_start = 10;
+    fixed32 gemport_id_end = 11;
+    fixed32 pon_ports = 12;
+}
+
 message Classifier {
     fixed32 o_tpid = 1;
     fixed32 o_vid = 2;
@@ -251,15 +289,16 @@
 }
 
 message Flow {
-    fixed32 access_intf_id = 1;
-    fixed32 onu_id = 2;
+    sfixed32 access_intf_id = 1;
+    sfixed32 onu_id = 2;
     fixed32 flow_id = 3;
     string flow_type = 4;	// upstream, downstream, broadcast, multicast
-    fixed32 network_intf_id = 5;
-    fixed32 gemport_id = 6;
+    sfixed32 alloc_id = 10;
+    sfixed32 network_intf_id = 5;
+    sfixed32 gemport_id = 6;
     Classifier classifier = 7;
     Action action = 8;
-    fixed32 priority = 9;
+    sfixed32 priority = 9;
 }
 
 message SerialNumber {
@@ -367,4 +406,4 @@
 }
 
 
-message Empty {}
+message Empty {}
\ No newline at end of file
diff --git a/openolt/.gitignore b/openolt/.gitignore
deleted file mode 100644
index 8b13789..0000000
--- a/openolt/.gitignore
+++ /dev/null
@@ -1 +0,0 @@
-
diff --git a/protos/.gitignore b/protos/.gitignore
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/protos/.gitignore
diff --git a/setup/setup_env.go b/setup/setup_env.go
new file mode 100644
index 0000000..a4210f5
--- /dev/null
+++ b/setup/setup_env.go
@@ -0,0 +1,110 @@
+/*
+ * 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 setup
+
+import (
+	"log"
+	"os/exec"
+)
+
+const (
+	UNI_VETH_UP_PFX = "sim_uu"
+	UNI_VETH_DW_PFX = "sim_ud"
+	NNI_VETH_UP_PFX = "sim_nu"
+	NNI_VETH_DW_PFX = "sim_nd"
+)
+
+func ActivateWPASups(vethnames []string) error {
+	for _, vethname := range vethnames {
+		if err := activateWPASupplicant(vethname); err != nil {
+			return err
+		}
+	}
+	return nil
+}
+
+func KillAllWPASups() error {
+	err := exec.Command("pkill", "wpa_supplicant").Run()
+	if err != nil {
+		log.Printf("[ERROR] Fail to pkill wpa_supplicant: %v\n", err)
+		return err
+	}
+	return nil
+}
+
+func KillAllDHCPClients() error {
+	err := exec.Command("pkill", "dhclient").Run()
+	if err != nil {
+		log.Printf("[ERROR] Fail to pkill dhclient: %v\n", err)
+		return err
+	}
+	return nil
+}
+
+func TearVethDown(veths []string) {
+	for _, veth := range veths {
+		RemoveVeth(veth)
+	}
+}
+
+func CreateVethPairs(name1 string, name2 string) (err error) {
+	err = exec.Command("ip", "link", "add", name1, "type", "veth", "peer", "name", name2).Run()
+	if err != nil {
+		log.Printf("[ERROR] Fail to createVeth() for %s and %s veth creation error: %s\n", name1, name2, err.Error())
+		return
+	}
+	log.Printf("%s & %s was created.", name1, name2)
+	err = exec.Command("ip", "link", "set", name1, "up").Run()
+	if err != nil {
+		log.Println("[ERROR] Fail to createVeth() veth1 up", err)
+		return
+	}
+	err = exec.Command("ip", "link", "set", name2, "up").Run()
+	if err != nil {
+		log.Println("[ERROR] Fail to createVeth() veth2 up", err)
+		return
+	}
+	log.Printf("%s & %s was up.", name1, name2)
+	return
+}
+
+func RemoveVeth(name string) {
+	err := exec.Command("ip", "link", "del", name).Run()
+	if err != nil {
+		log.Println("[ERROR] Fail to removeVeth()", err)
+	}
+	log.Printf("%s was removed.", name)
+}
+
+func RemoveVeths(names []string) {
+	log.Printf("RemoveVeths() :%s\n", names)
+	for _, name := range names {
+		RemoveVeth(name)
+	}
+}
+
+func activateWPASupplicant(vethname string) (err error) {
+	cmd := "/sbin/wpa_supplicant"
+	conf := "/etc/wpa_supplicant/wpa_supplicant.conf"
+	err = exec.Command(cmd, "-D", "wired", "-i", vethname, "-c", conf).Start()
+	if err != nil {
+		log.Printf("[ERROR] Fail to activateWPASupplicant() for :%s %v\n", vethname, err)
+		return
+	}
+	log.Printf("activateWPASupplicant() for :%s\n", vethname)
+	return
+}
\ No newline at end of file