VOL-1451 Initial checkin of openonu build

Produced docker container capable of building and running
openonu/brcm_openonci_onu.  Copied over current onu code
and resolved all imports by copying into the local source tree.

Change-Id: Ib9785d37afc65b7d32ecf74aee2456352626e2b6
diff --git a/python/extensions/eoam/EOAM.py b/python/extensions/eoam/EOAM.py
new file mode 100644
index 0000000..935f0e3
--- /dev/null
+++ b/python/extensions/eoam/EOAM.py
@@ -0,0 +1,507 @@
+#!/usr/bin/env python

+# Copyright 2017-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.
+#--------------------------------------------------------------------------#

+# Copyright (C) 2015 - 2016 by Tibit Communications, Inc.                  #

+# All rights reserved.                                                     #

+#                                                                          #

+#    _______ ____  _ ______                                                #

+#   /_  __(_) __ )(_)_  __/                                                #

+#    / / / / __  / / / /                                                   #

+#   / / / / /_/ / / / /                                                    #

+#  /_/ /_/_____/_/ /_/                                                     #

+#                                                                          #

+#--------------------------------------------------------------------------#

+""" EOAM protocol implementation in scapy """

+

+TIBIT_VERSION_NUMBER = '1.1.4'

+

+import argparse

+import logging

+import time

+from hexdump import hexdump

+from datetime import datetime

+

+

+logging.getLogger("scapy.runtime").setLevel(logging.ERROR)

+from scapy.layers.l2 import Ether, Dot1Q

+from scapy.sendrecv import sendp

+from scapy.fields import PacketField

+from scapy.packet import bind_layers

+from scapy.fields import StrField, X3BytesField

+from scapy.packet import Packet

+from scapy.fields import ByteEnumField, XShortField, XByteField, MACField, \

+    ByteField, BitEnumField, BitField, ShortField

+from scapy.fields import XLongField, StrFixedLenField, XIntField, \

+    FieldLenField, StrLenField, IntField

+

+import fcntl, socket, struct # for get hw address

+

+from EOAM_Layers import EOAM_MULTICAST_ADDRESS, IGMP_MULTICAST_ADDRESS, OAM_ETHERTYPE

+from EOAM_Layers import VENDOR_SPECIFIC_OPCODE, CABLELABS_OUI, TIBIT_OUI

+from EOAM_Layers import RxedOamMsgTypeEnum, RxedOamMsgTypes

+from EOAM_Layers import EOAMPayload, EOAM_EventMsg, EOAM_VendSpecificMsg, EOAM_TibitMsg, EOAM_DpoeMsg, EOAM_OmciMsg

+

+# TODO should remove import *

+from EOAM_TLV import *

+

+ADTRAN_SHORTENED_VSSN = u'4144'  # 'AD'

+TIBIT_SHORTENED_VSSN  = u'5442'  # 'TB'

+

+def get_oam_msg_type(log, frame):

+

+    respType = RxedOamMsgTypeEnum["Unknown"]

+    recv_frame = frame

+

+    if recv_frame.haslayer(EOAMPayload):

+        if recv_frame.haslayer(EOAM_EventMsg):

+            respType = RxedOamMsgTypeEnum["Event Notification"]

+        elif recv_frame.haslayer(EOAM_OmciMsg):

+            respType = RxedOamMsgTypeEnum["OMCI Message"]

+        else:

+            dpoeOpcode = 0x00

+            if recv_frame.haslayer(EOAM_TibitMsg):

+                dpoeOpcode = recv_frame.getlayer(EOAM_TibitMsg).dpoe_opcode;

+            elif recv_frame.haslayer(EOAM_DpoeMsg):

+                dpoeOpcode = recv_frame.getlayer(EOAM_DpoeMsg).dpoe_opcode;

+

+            # Get Response

+            if (dpoeOpcode == DPoEOpcodes["Get Response"]):

+                respType = RxedOamMsgTypeEnum["DPoE Get Response"]

+

+            # Set Response

+            elif (dpoeOpcode == DPoEOpcodes["Set Response"]):

+                respType = RxedOamMsgTypeEnum["DPoE Set Response"]

+

+            # File Transfer ACK

+            elif (dpoeOpcode == DPoEOpcodes["File Transfer"]):

+                respType = RxedOamMsgTypeEnum["DPoE File Transfer"]

+            else:

+                log.info("Unsupported DPoE Opcode {:0>2X}".format(dpoeOpcode))

+    else:

+        log.info("Invalid OAM Header")

+

+    log.info('Received OAM Message - %s' % RxedOamMsgTypes[respType])

+

+    return respType

+

+

+def handle_get_value(log, loadstr, startOfTlvs, queryBranch, queryLeaf):

+    retVal = False;

+    value = 0

+    branch = 0

+    leaf = 0

+    bytesRead = startOfTlvs

+    loadstrlen    = len(loadstr)

+

+    while (bytesRead <= loadstrlen):

+        (branch, leaf) = struct.unpack_from('>BH', loadstr, bytesRead)

+

+        if (branch != 0):

+            bytesRead += 3

+            length = struct.unpack_from('>B', loadstr, bytesRead)[0]

+            bytesRead += 1

+

+            if (length == 1):

+                value = struct.unpack_from(">B", loadstr, bytesRead)[0]

+            elif (length == 2):

+                value = struct.unpack_from(">H", loadstr, bytesRead)[0]

+            elif (length == 4):

+                value = struct.unpack_from(">I", loadstr, bytesRead)[0]

+            elif (length == 8):

+                value = struct.unpack_from(">Q", loadstr, bytesRead)[0]

+            else:

+                if (length >= 0x80):

+                    log.info('Branch 0x{:0>2X} Leaf 0x{:0>4X} {}'.format(branch, leaf, DPoEVariableResponseEnum[length]))

+                    # Set length to zero so bytesRead doesn't get mistakenly incremented below

+                    length = 0

+                else:

+                    # Attributes with a length of zero are actually 128 bytes long

+                    if (length == 0):

+                        length = 128;

+                    valStr = ">{}s".format(length)

+                    value = struct.unpack_from(valStr, loadstr, bytesRead)[0]

+

+            if (length > 0):

+                bytesRead += length

+

+            if (branch != OamBranches["DPoE Object"]):

+                if ( ((queryBranch == 0) and (queryLeaf == 0)) or

+                     ((queryBranch == branch) and (queryLeaf == leaf)) ):

+                    # Prevent zero-lengthed values from returning success

+                    if (length > 0):

+                        retVal = True;

+                    break

+        else:

+            break

+

+    if (retVal == False):

+        value = 0

+

+    return retVal,bytesRead,value,branch,leaf

+

+

+def get_value_from_msg(log, frame, branch, leaf):

+    retVal = False

+    value = 0

+    recv_frame = frame

+

+    if recv_frame.haslayer(EOAMPayload):

+        payload = recv_frame.payload

+        if hasattr(payload, 'body'):

+            loadstr = payload.body.load

+            # Get a specific TLV value

+            (retVal,bytesRead,value,retbranch,retleaf) = handle_get_value(log, loadstr, 0, branch, leaf)

+        else:

+            log.info('received frame has no payload')

+    else:

+        log.info('Invalid OAM Header')

+    return retVal,value,

+

+def check_set_resp_attrs(log, loadstr, startOfTlvs):

+    retVal = True;

+    branch = 0

+    leaf = 0

+    length = 0

+    bytesRead = startOfTlvs

+    loadstrlen    = len(loadstr)

+

+    while (bytesRead <= loadstrlen):

+        (branch, leaf) = struct.unpack_from('>BH', loadstr, bytesRead)

+#            print "Branch/Leaf        0x{:0>2X}/0x{:0>4X}".format(branch, leaf)

+

+        if (branch != 0):

+            bytesRead += 3

+            length = struct.unpack_from('>B', loadstr, bytesRead)[0]

+#                print "Length:            0x{:0>2X} ({})".format(length,length)

+            bytesRead += 1

+

+            if (length >= 0x80):

+                log.info('Branch 0x{:0>2X} Leaf 0x{:0>4X} {}'.format(branch, leaf, DPoEVariableResponseEnum[length]))

+                if (length > 0x80):

+                    retVal = False;

+                    break;

+            else:

+                bytesRead += length

+

+        else:

+            break

+

+    return retVal,branch,leaf,length

+

+def check_set_resp(log, frame):

+    rc = False

+    branch = 0

+    leaf = 0

+    status = 0

+    recv_frame = frame

+

+    if recv_frame.haslayer(EOAMPayload):

+        payload = recv_frame.payload

+        if hasattr(payload, 'body'):

+            loadstr = payload.body.load

+            # Get a specific TLV value

+            (rc,branch,leaf,status) = check_set_resp_attrs(log, loadstr, 0)

+        else:

+            log.info('received frame has no payload')

+    else:

+        log.info('Invalid OAM Header')

+    return rc,branch,leaf,status

+

+

+def handle_get_event_context(log, loadstr, startOfTlvs, queryType):

+    retVal = False;

+    value = 0

+    objType = 0

+    bytesRead = startOfTlvs

+    loadstrlen    = len(loadstr)

+

+    while (bytesRead <= loadstrlen):

+        objType = struct.unpack_from('>H', loadstr, bytesRead)[0]

+#            print "Branch/Leaf        0x{:0>2X}/0x{:0>4X}".format(branch, leaf)

+

+        if (objType != 0):

+            bytesRead += 2

+            length = struct.unpack_from('>B', loadstr, bytesRead)[0]

+#                print "Length:            0x{:0>2X} ({})".format(length,length)

+            bytesRead += 1

+

+            if (length == 1):

+                value = struct.unpack_from(">B", loadstr, bytesRead)[0]

+            elif (length == 2):

+                value = struct.unpack_from(">H", loadstr, bytesRead)[0]

+            elif (length == 4):

+                value = struct.unpack_from(">I", loadstr, bytesRead)[0]

+            elif (length == 8):

+                value = struct.unpack_from(">Q", loadstr, bytesRead)[0]

+            else:

+                valStr = ">{}s".format(length)

+                value = struct.unpack_from(valStr, loadstr, bytesRead)[0]

+

+#                print "Value:             {}".format(value)

+

+            if (length > 0):

+                bytesRead += length

+

+            if ( (queryType == 0) or (queryType == objType) ):

+                # Prevent zero-lengthed values from returning success

+                if (length > 0):

+                    retVal = True;

+                break

+        else:

+            break

+

+    if (retVal == False):

+        value = 0

+

+    return retVal,bytesRead,value,objType

+

+

+def handle_tibit_oam_event(log, loadstr):

+    bytesRead = 0

+    loadstrlen = len(loadstr)

+    if loadstrlen > 0:

+        rc = True

+        num_iters = 0

+        bytesRead = 0

+        link_mac = ""

+        msg = ""

+        # Theare are two contexts in a Tibit-specific event - Source & Reference Contexts

+        while(rc == True and num_iters < 2):

+            objType = 0

+            (rc,bytesRead,value,objType) = handle_get_event_context(log, loadstr, bytesRead, objType)

+            if (rc == True):

+                if objType == 0x0001:

+#                        print "PON Object 0x{:0>4X}  Value = {}".format(objType, value)

+                    pass

+                elif objType == 0x000A:

+                    # This is a Unicast Logical Link context. Determine if this a GPON or EPON link

+                    if value[1:5] == "TBIT":

+                        #

+                        link_mac = ''.join(s.encode('hex') for s in value[1:3])

+                        link_mac += ''.join(s.encode('hex') for s in value[5:9])

+                    else:

+                        link_mac = ''.join(s.encode('hex') for s in value[1:7])

+

+#                        print "Unicast Logical Link Object 0x{:0>4X}  Value = {}".format(objType, link_mac)

+                else:

+                    log.info("Object Type 0x{:0>4X}  value = {}".format(objType, value))

+            elif (branch != 0):

+                log.error("Object Type 0x{:0>4X}  no value".format(objType))

+            num_iters += 1

+

+        # Pull the Event Code and Event Length out of the event

+        (evtCode, evtLen) = struct.unpack_from('>HB', loadstr, bytesRead)

+        bytesRead += 3

+

+#            print "Event Code  : 0x{:0>4X}".format(evtCode)

+#            print "Event Len   : 0x{:0>4X}".format(evtLen)

+

+        # Tibit Registration Event

+        if (evtCode == 0x0001):

+            # Handle Registration Status attribute

+            regStatus = struct.unpack_from('>B', loadstr, bytesRead)[0]

+            if regStatus == 1:

+                msg = "Link {} Registered".format(link_mac)

+            else:

+                msg = "Link {} Deregistered".format(link_mac)

+

+    return objType,evtCode,msg

+

+

+def handle_dpoe_oam_event(log, loadstr):

+    bytesRead = 0

+    loadstrlen = len(loadstr)

+    if loadstrlen > 0:

+

+        (evtCode, raised, objType) = struct.unpack_from('>BBH', loadstr, bytesRead)

+        bytesRead += 4

+

+#            print "Event Code  : 0x{:0>4X}".format(evtCode)

+#            print "Event Len   : 0x{:0>4X}".format(evtLen)

+

+        if ((loadstrlen - bytesRead) == 2):

+            objInst = struct.unpack_from(">H", loadstr, bytesRead)[0]

+        elif ((loadstrlen - bytesRead) == 4):

+            objInst = struct.unpack_from(">I", loadstr, bytesRead)[0]

+

+        objTypeStr = ObjectContextEnum[objType]

+        evtCodeStr = DPoEEventCodeEnum[evtCode]

+

+        raisedStr = "Raised"

+        if (raised):

+            rasiedStr = "Cleared"

+

+        #print "{} : {} - {} {}".format(objTypeStr, objInst, evtCodeStr, raisedStr)

+        return objType,evtCode,objTypeStr+":"+evtCodeStr

+

+

+def handle_oam_event(log, frame):

+    recv_frame = frame

+    if recv_frame.haslayer(EOAM_EventMsg):

+        now = datetime.now().strftime('%Y-%m-%f %H:%M:%S.%f')

+        event = recv_frame.getlayer(EOAM_EventMsg)

+        if hasattr(event, 'body'):

+            loadstr = event.body.load

+

+            if (event.tlv_type != VENDOR_SPECIFIC_OPCODE):

+                log.error("unexpected tlv_type 0x%x (expected 0xFE)" % event.tlv_type)

+            elif (event.oui == CABLELABS_OUI):

+                log.info("DPoE Event")

+                objType,eventCode,msg = handle_dpoe_oam_event(log, loadstr)

+            elif (event.oui == TIBIT_OUI):

+                log.info("Tibit-specific Event")

+                objType,eventCode,msg = handle_tibit_oam_event(log, loadstr)

+

+            log.info("Description:    %s" % msg)

+            log.info("sequence:       0x%04x" % event.sequence)

+            log.info("tlv_type:       0x%x" % event.tlv_type)

+            log.info("length:         0x%x" % event.length)

+            log.info("oui:            0x%06x" % event.oui)

+            log.info("time_stamp:     %s" % now)

+            log.info("obj_type:       "+hex(objType))

+            log.info("event_code:     "+hex(eventCode))

+

+    # TODO - Store the event for future use or generate alarm

+    #event_data = [msg, event.sequence, objType, eventCode, now]

+

+def handle_omci(log, frame):

+    recv_frame = frame

+    if recv_frame.haslayer(EOAM_OmciMsg):

+        omci = recv_frame.getlayer(EOAM_OmciMsg)

+        if hasattr(omci, 'body'):

+            loadstr = omci.body.load

+

+            #log.info("trans_id:  0x%04x" % omci.trans_id)

+            #log.info("msg_type:  0x%x" % omci.msg_type)

+            #log.info("dev_id:    0x%x" % omci.dev_id)

+            #log.info("me_class:  0x%04x" % omci.me_class)

+            #log.info("me_inst:   0x%04x" % omci.me_inst)

+

+            bytesRead = 0

+

+    # TODO - Handle OMCI message

+

+def handle_fx_ack(log, loadstr):

+    response_code = Dpoe_FileAckRspOpcodes["OK"]

+

+    (fx_opcode, acked_block, response_code) = struct.unpack('>BHB', loadstr[0:4])

+

+    if (fx_opcode == Dpoe_FileXferOpcodes["File Transfer Ack"]):

+        pass

+        #log.info("   Acked_block: {} Code: {}".format(acked_block, DPoEFileAckRespCodeEnum[response_code]))

+    else:

+        log.error("Unexpected File Transfer Opcode {} when expecting ACK".format(DPoEFileXferOpcodeEnum[fx_opcode]))

+

+    return response_code,acked_block

+

+

+def check_resp(log, frame):

+    respType = RxedOamMsgTypeEnum["Unknown"]

+    recv_frame = frame

+    if recv_frame.haslayer(EOAMPayload):

+

+        if recv_frame.haslayer(EOAM_EventMsg):

+            handle_oam_event(log, recv_frame)

+        elif recv_frame.haslayer(EOAM_OmciMsg):

+            handle_omci(log, recv_frame)

+        else:

+            dpoeOpcode = 0x00

+            if recv_frame.haslayer(EOAM_TibitMsg):

+                dpoeOpcode = recv_frame.getlayer(EOAM_TibitMsg).dpoe_opcode;

+            elif recv_frame.haslayer(EOAM_DpoeMsg):

+                dpoeOpcode = recv_frame.getlayer(EOAM_DpoeMsg).dpoe_opcode;

+

+            if hasattr(recv_frame, 'body'):

+                payload = recv_frame.payload

+                loadstr = payload.body.load

+

+            # Get Response

+            if (dpoeOpcode == DPoEOpcodes["Get Response"]):

+                bytesRead = 0

+                rc = True

+                while(rc == True):

+                    branch = 0

+                    leaf = 0

+                    (rc,bytesRead,value,branch,leaf) = handle_get_value(log, loadstr, bytesRead, branch, leaf)

+                    if (rc == True):

+                        log.info('Branch 0x{:0>2X} Leaf 0x{:0>4X}  value = {}'.format(branch, leaf, value))

+                    elif (branch != 0):

+                        log.info('Branch 0x{:0>2X} Leaf 0x{:0>4X}  no value'.format(branch, leaf))

+

+            # Set Response

+            elif (dpoeOpcode == DPoEOpcodes["Set Response"]):

+                (rc,branch,leaf,status) = check_set_resp_attrs(loadstr, 0)

+                if (rc == True):

+                    log.info('Set Response had no errors')

+                else:

+                    log.info('Branch 0x{:X} Leaf 0x{:0>4X} {}'.format(branch, leaf, DPoEVariableResponseEnum[status]))

+

+            # File Transfer ACK

+            elif (dpoeOpcode == DPoEOpcodes["File Transfer"]):

+                (rc,block) = handle_fx_ack(log, loadstr)

+            else:

+                log.info('Unsupported DPoE Opcode {:0>2X}'.format(dpoeOpcode))

+    else:

+        log.info('Invalid OAM Header')

+

+    return respType

+

+

+def mcastIp2McastMac(ip):

+    """ Convert a dot-notated IPv4 multicast address string into an multicast MAC address"""

+    digits = [int(d) for d in ip.split('.')]

+    return '01:00:5e:%02x:%02x:%02x' % (digits[1] & 0x7f, digits[2] & 0xff, digits[3] & 0xff)

+

+def get_olt_queue(mac, mode = None):

+    resultOltQueue = ""

+    if mode:

+        # If the MAC is the Multicast LLID, then use EPON encoding regardless of the actual

+        # mode we are in.

+        if (mac == "FFFFFFFFFFFF"):

+            mode = "EPON"

+

+        if mode.upper()[0] == "G":  #GPON

+            if mac[:4].upper() == ADTRAN_SHORTENED_VSSN:

+                vssn = "ADTN"

+            else:

+                vssn = "TBIT"

+            link = int(mac[4:12], 16)

+            resultOltQueue = "PortIngressRuleResultOLTQueue(unicastvssn=\"" + vssn + "\", unicastlink=" + str(link) + ")"

+        else:                       #EPON

+            vssn = int(mac[0:8].rjust(8,"0"), 16)

+            link = int((mac[8:12]).ljust(8,"0"), 16)

+            resultOltQueue = "PortIngressRuleResultOLTEPONQueue(unicastvssn=" + str(vssn) + ", unicastlink=" + str(link) + ")"

+    return resultOltQueue

+

+

+def get_unicast_logical_link(mac, mode = None):

+    unicastLogicalLink = ""

+    if mode:

+        if mode.upper()[0] == "G":  #GPON

+            if mac[:4].upper() == ADTRAN_SHORTENED_VSSN:

+                vssn = "ADTN"

+            else:

+                vssn = "TBIT"

+            link = int(mac[4:12], 16)

+            unicastLogicalLink = "OLTUnicastLogicalLink(unicastvssn=\"" + vssn + "\", unicastlink=" + str(link) + ")"

+        else:                       #EPON

+            vssn = int(mac[0:8].rjust(8,"0"), 16)

+            link = int((mac[8:12]).ljust(8,"0"), 16)

+            unicastLogicalLink = "OLTEPONUnicastLogicalLink(unicastvssn=" + str(vssn) + ", unicastlink=" + str(link) +")"

+    return unicastLogicalLink

+

+

+if __name__ == "__main__":

+    pass