AAA Emulation & BBSim Containerization

VOL-1154, VOL-1168, VOL-1273

Change-Id: Ib0fbbaec897f633601976e8636c218f42375bedd
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
+}