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