[VOL-1349] EPON OLT adapter (package B)

Change-Id: I634ef62c53813dcf4456f54948f13e06358e263c
diff --git a/internal/pkg/core/l2oam_handle.go b/internal/pkg/core/l2oam_handle.go
new file mode 100644
index 0000000..eb8ebb3
--- /dev/null
+++ b/internal/pkg/core/l2oam_handle.go
@@ -0,0 +1,204 @@
+/*
+ * Copyright 2020-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 (
+	"context"
+	"encoding/binary"
+	"encoding/hex"
+	"fmt"
+	"time"
+
+	"github.com/google/gopacket"
+	"github.com/google/gopacket/layers"
+	"github.com/google/gopacket/pcap"
+	oop "github.com/opencord/voltha-protos/v3/go/openolt"
+)
+
+const vlanEnable = true
+const vlanIdManagement = 4090
+
+var svlanTagAuth = []byte{0x88, 0xa8, 0x0f, 0xfb}
+
+// IfName is a Interface Name of this device
+var IfName string
+
+// SrcMac is a MAC address of this device
+var SrcMac string
+
+var l2oamHandle *L2oamHandle
+
+// GetL2oamHandle returns my handle
+func GetL2oamHandle() *L2oamHandle {
+	return l2oamHandle
+}
+
+// L2oamHandle contains handle information
+type L2oamHandle struct {
+	Handle *pcap.Handle
+	ReqCh  chan []byte
+}
+
+// NewL2oamHandle L2oamHandle created
+func NewL2oamHandle(ctx context.Context, ifname string, srcMac string) {
+	logger.Debug(ctx, fmt.Sprintf("L2oamHandle start. ifname=%s, srcMac=%s", ifname, srcMac))
+	IfName = ifname
+	SrcMac = srcMac
+	if GetL2oamHandle() == nil {
+		handle, _ := pcap.OpenLive(IfName, int32(1500), false, time.Millisecond*1000)
+		l2oamHandle = &L2oamHandle{
+			Handle: handle,
+			ReqCh:  make(chan []byte),
+		}
+		if handle == nil {
+			logger.Debug(ctx, "could not get pcap handle.")
+		} else {
+			go l2oamHandle.startThread(context.Background())
+		}
+	}
+}
+
+// because of each handle object that is provided for a device receive messages respectively,
+// call this method using goroutine
+func (h *L2oamHandle) startThread(ctx context.Context) {
+	logger.Debug(ctx, "L2oamHandle thread start. ")
+
+	var filter string = "ether proto 0xa8c8 or 0x888e or 0x8100 or 0x88a8"
+	err := h.Handle.SetBPFFilter(filter)
+	if err != nil {
+		logger.Error(ctx, "Error: Handle.SetBPFFilter")
+	}
+
+	packetSource := gopacket.NewPacketSource(h.Handle, h.Handle.LinkType())
+
+	for {
+		select {
+		// send a message directly using send method, not sending it via channels
+		case message := <-h.ReqCh:
+			logger.Debug(ctx, fmt.Sprintf("gopacket send. %x", message))
+			if err := h.Handle.WritePacketData(message); err != nil {
+				logger.Debug(ctx, "write-packet-error")
+			}
+		// Packets are received
+		case packet := <-packetSource.Packets():
+			etherLayer := packet.Layer(layers.LayerTypeEthernet)
+			logger.Debug(ctx, fmt.Sprintf("gopacket receive. %x", etherLayer))
+			if etherLayer != nil {
+				// type assertion
+				etherPacket, _ := etherLayer.(*layers.Ethernet)
+				packetBytes := append(etherPacket.Contents, etherPacket.Payload...)
+				logger.Debug(ctx, fmt.Sprintf("gopacket ether receive. %x", packetBytes))
+				if vlanEnable {
+					h.dispatchVlan(ctx, etherPacket)
+				} else {
+					h.dispatch(ctx, etherPacket)
+				}
+			}
+		}
+	}
+}
+func (h *L2oamHandle) dispatch(ctx context.Context, etherPacket *layers.Ethernet) {
+	logger.Debug(ctx, fmt.Sprintf("dispatch(). %x", etherPacket))
+
+	etherType := uint16(etherPacket.EthernetType)
+	if etherType == uint16(layers.EthernetTypeEAPOL) {
+		logger.Debug(ctx, fmt.Sprintf("receive message = EAPOL, Contents=%x, Payload=%x", etherPacket.Contents, etherPacket.Payload))
+		device := FindL2oamDevice(hex.EncodeToString(etherPacket.SrcMAC))
+		if device == nil {
+			// unregisterd device
+			logger.Error(ctx, fmt.Sprintf("Received from an unregistered device. macAddr=%s", etherPacket.SrcMAC))
+			h.packetIn(etherPacket)
+		} else {
+			device.receiveEapol(etherPacket)
+		}
+
+	} else if etherType == 0xa8c8 {
+		bytes := etherPacket.Payload
+		opcode := bytes[0]
+		oampduCode := bytes[3]
+		logger.Debug(ctx, fmt.Sprintf("receive message = 1904.2 OAM opcode=%v oampduCode=%v,  %x", opcode, oampduCode, bytes))
+
+		device := FindL2oamDevice(hex.EncodeToString(etherPacket.SrcMAC))
+		if device == nil {
+			// unregisterd device
+			logger.Error(ctx, fmt.Sprintf("Received from an unregistered device. macAddr=%s", etherPacket.SrcMAC))
+			return
+		}
+
+		// OAM: keep-alive message
+		if opcode == 0x03 && oampduCode == 0x00 {
+			device.recieveKeepAlive(etherPacket)
+
+		} else {
+			device.recieve(etherPacket)
+		}
+	} else if etherType == uint16(layers.EthernetTypeDot1Q) {
+		device := FindL2oamDevice(hex.EncodeToString(etherPacket.SrcMAC))
+		if device == nil {
+			// unregisterd device
+			logger.Error(ctx, fmt.Sprintf("Received from an unregistered device. macAddr=%s", etherPacket.SrcMAC))
+			h.packetIn(etherPacket)
+		} else {
+			device.receiveEapol(etherPacket)
+		}
+
+	} else {
+		logger.Error(ctx, fmt.Sprintf("unknown etherType = 0x%X", etherType))
+	}
+}
+func (h *L2oamHandle) dispatchVlan(ctx context.Context, etherPacket *layers.Ethernet) {
+	logger.Debug(ctx, fmt.Sprintf("dispatchVlan(). %x", etherPacket))
+
+	etherType := uint16(etherPacket.EthernetType)
+	if etherType == uint16(layers.EthernetTypeQinQ) {
+		// Remove the VLAN tag
+		vlanPacket := &layers.Dot1Q{}
+		if err := vlanPacket.DecodeFromBytes(etherPacket.Payload, nil); err != nil {
+			logger.Debug(ctx, fmt.Sprintf("error:%v", err))
+		}
+		etherBytes := etherPacket.Contents
+		binary.BigEndian.PutUint16(etherBytes[12:], uint16(vlanPacket.Type))
+		packetBytes := append(etherBytes, vlanPacket.Payload...)
+		etherPacket = &layers.Ethernet{}
+		if err := etherPacket.DecodeFromBytes(packetBytes, nil); err != nil {
+			logger.Debug(ctx, fmt.Sprintf("error:%v", err))
+		}
+		h.dispatch(ctx, etherPacket)
+	} else {
+		logger.Error(ctx, fmt.Sprintf("Discard unsupported etherType. 0x%X", etherType))
+	}
+}
+
+func (h *L2oamHandle) send(message []byte) {
+	if h.Handle != nil {
+		h.ReqCh <- message
+	}
+}
+func (h *L2oamHandle) packetIn(etherPacket *layers.Ethernet) {
+	packetBytes := append(etherPacket.Contents, etherPacket.Payload...)
+	pktInd := &oop.PacketIndication{
+		IntfId:    0,
+		GemportId: 1,
+		PortNo:    16,
+		Pkt:       packetBytes,
+		IntfType:  "pon",
+	}
+	logger.Info(context.Background(), fmt.Sprintf("L2oamHandle.packetIn() pktInd=%x", pktInd))
+
+	if err := l2oamDeviceHandler.handlePacketIndication(context.Background(), pktInd); err != nil {
+		logger.Error(context.Background(), "L2oamHandle.packetIn() error. ")
+	}
+}