Added Set Response Handling

Change-Id: I6d5b9af6df83b044a11b5da140eeaf1de39be657
diff --git a/voltha/adapters/tibit_olt/tibit_olt.py b/voltha/adapters/tibit_olt/tibit_olt.py
index 36ab71d..22ca111 100644
--- a/voltha/adapters/tibit_olt/tibit_olt.py
+++ b/voltha/adapters/tibit_olt/tibit_olt.py
@@ -18,7 +18,9 @@
 Tibit OLT device adapter
 """
 import json
+import time
 from uuid import uuid4
+import struct
 
 import arrow
 import structlog
@@ -46,6 +48,8 @@
 from voltha.extensions.eoam.EOAM_TLV import PortIngressRuleHeader
 from voltha.extensions.eoam.EOAM_TLV import ClauseSubtypeEnum
 from voltha.extensions.eoam.EOAM_TLV import RuleOperatorEnum
+from voltha.extensions.eoam.EOAM_TLV import DPoEVariableResponseCodes
+from voltha.extensions.eoam.EOAM_TLV import TibitOUI
 from voltha.extensions.eoam.EOAM import EOAMPayload, CablelabsOUI
 from voltha.core.flow_decomposer import *
 from voltha.core.logical_device_agent import mac_str_to_tuple
@@ -78,9 +82,25 @@
 
 TIBIT_PACKET_OUT_VLAN = 4000
 
+TIBIT_MSG_WAIT_TIME = 3
+
 is_tibit_frame = BpfProgramFilter('{} or {}'.format(
     frame_match_case1, frame_match_case2))
 
+### Received OAM Message Types
+RxedOamMsgTypeEnum = {
+    "Unknown": 0x00,
+    # Info PDU - not currently used
+    "Info": 0x01,
+    # Event Notification - Tibit or DPoE Event
+    "Event Notification": 0x02,
+    "DPoE Get Response": 0x03,
+    "DPoE Set Response": 0x04,
+    # Specifically - a File Transfer ACK
+    "DPoE File Transfer": 0x05,
+    # Contains an embedded OMCI message
+    "OMCI Message": 0x06,
+    }
 
 # TODO: This information should be conveyed to the adapter
 # from a higher level.
@@ -343,7 +363,7 @@
                 Ether(dst=device.mac_address) /
                 Dot1Q(vlan=TIBIT_MGMT_VLAN, prio=TIBIT_MGMT_PRIORITY) /
                 EOAMPayload(
-                    body=CablelabsOUI() / DPoEOpcode_SetRequest() /
+                    body=TibitOUI() / DPoEOpcode_SetRequest() /
                     NetworkToNetworkPortObject()/
                     PortIngressRuleHeader(precedence=13)/
                     PortIngressRuleClauseMatchLength02(fieldcode=Clause['C-VLAN Tag'], fieldinstance=0,
@@ -358,11 +378,36 @@
                     AddPortIngressRule()))
 
             self.io_port.send(str(packet_out_rule))
-            while True:
-                response = yield self.incoming_queues[olt_mac].get()
-                # verify response and if not the expected response
-                if 1: # TODO check if it is really what we expect, and wait if not
-                    break
+            
+            # Get and process the Set Response
+            ack = False
+            start_time = time.time()
+
+            # Loop until we have a set response or timeout
+            while not ack:
+                frame = yield self.incoming_queues[olt_mac].get()
+                #TODO - Need to add propoer timeout functionality
+                #if (time.time() - start_time) > TIBIT_MSG_WAIT_TIME or (frame is None):
+                #    break  # don't wait forever
+
+                respType = self._voltha_get_oam_msg_type(frame)
+                log.info('Received OAM Message 0x %s' % str(respType))
+
+                #Check that the message received is a Set Response
+                if (respType == RxedOamMsgTypeEnum["DPoE Set Response"]):
+                    ack = True
+                else:
+                    # Handle unexpected events/OMCI messages
+                    self._voltha_check_resp(frame)
+
+            # Verify Set Response
+            if ack:
+                (rc,branch,leaf,status) = self._voltha_check_set_resp(frame)
+                if (rc == True):
+                    log.info('Set Response had no errors')
+                else:
+                    raise Exception('Set Respose had errors')
+                    log.info('Branch 0x{:X} Leaf 0x{:0>4X} {}'.format(branch, leaf, DPoEVariableResponseCodes[status]))
 
             # also record the vlan_id -> (device_id, logical_device_id, linkid) for
             # later use.  The linkid is the macid returned.
@@ -482,6 +527,7 @@
     def get_device_details(self, device):
         raise NotImplementedError()
 
+    @inlineCallbacks
     def update_flows_bulk(self, device, flows, groups):
         log.info('########################################')
         log.info('bulk-flow-update', device_id=device.id,
@@ -492,6 +538,7 @@
         # vid_from_device_id = {v[0]: k for k,v in self.vlan_to_device_ids.iteritems()}
         # ONU_VID = vid_from_device_id[device.id]
         _inner_vid = None
+        olt_mac = device.mac_address
 
         Clause = {v: k for k, v in ClauseSubtypeEnum.iteritems()}
         Operator = {v: k for k, v in RuleOperatorEnum.iteritems()}
@@ -614,11 +661,41 @@
                         Ether(dst=device.mac_address) /
                         Dot1Q(vlan=TIBIT_MGMT_VLAN, prio=TIBIT_MGMT_PRIORITY) /
                         EOAMPayload(
-                            body=CablelabsOUI() / DPoEOpcode_SetRequest() / dn_req)
+                            body=TibitOUI() / DPoEOpcode_SetRequest() / dn_req)
                     )
 
                     self.io_port.send(str(msg))
 
+                    # Get and process the Set Response
+                    ack = False
+                    start_time = time.time()
+
+                    # Loop until we have a set response or timeout
+                    while not ack:
+                        frame = yield self.incoming_queues[olt_mac].get()
+                        #TODO - Need to add propoer timeout functionality
+                        #if (time.time() - start_time) > TIBIT_MSG_WAIT_TIME or (frame is None):
+                        #    break  # don't wait forever
+
+                        respType = self._voltha_get_oam_msg_type(frame)
+                        log.info('Received OAM Message 0x %s' % str(respType))
+
+                        #Check that the message received is a Set Response
+                        if (respType == RxedOamMsgTypeEnum["DPoE Set Response"]):
+                            ack = True
+                        else:
+                            # Handle unexpected events/OMCI messages
+                            self._voltha_check_resp(frame)
+
+                    # Verify Set Response
+                    if ack:
+                        (rc,branch,leaf,status) = self._voltha_check_set_resp(frame)
+                        if (rc == True):
+                            log.info('Set Response had no errors')
+                        else:
+                            raise Exception('Set Respose had errors')
+                            log.info('Branch 0x{:X} Leaf 0x{:0>4X} {}'.format(branch, leaf, DPoEVariableResponseCodes[status]))
+
                 elif in_port == 1:
                     # Upstream rule
                     log.info('#### Upstream Rule ####')
@@ -755,11 +832,41 @@
                         Ether(dst=device.mac_address) /
                         Dot1Q(vlan=TIBIT_MGMT_VLAN, prio=TIBIT_MGMT_PRIORITY) /
                         EOAMPayload(
-                            body=CablelabsOUI() / DPoEOpcode_SetRequest() / up_req)
+                            body=TibitOUI() / DPoEOpcode_SetRequest() / up_req)
                     )
 
                     self.io_port.send(str(msg))
 
+                    # Get and process the Set Response
+                    ack = False
+                    start_time = time.time()
+
+                    # Loop until we have a set response or timeout
+                    while not ack:
+                        frame = yield self.incoming_queues[olt_mac].get()
+                        #TODO - Need to add propoer timeout functionality
+                        #if (time.time() - start_time) > TIBIT_MSG_WAIT_TIME or (frame is None):
+                        #    break  # don't wait forever
+
+                        respType = self._voltha_get_oam_msg_type(frame)
+                        log.info('Received OAM Message 0x %s' % str(respType))
+
+                        #Check that the message received is a Set Response
+                        if (respType == RxedOamMsgTypeEnum["DPoE Set Response"]):
+                            ack = True
+                        else:
+                            # Handle unexpected events/OMCI messages
+                            self._voltha_check_resp(frame)
+
+                    # Verify Set Response
+                    if ack:
+                        (rc,branch,leaf,status) = self._voltha_check_set_resp(frame)
+                        if (rc == True):
+                            log.info('Set Response had no errors')
+                        else:
+                            raise Exception('Set Respose had errors')
+                            log.info('Branch 0x{:X} Leaf 0x{:0>4X} {}'.format(branch, leaf, DPoEVariableResponseCodes[status]))
+
                 else:
                     raise Exception('Port should be 1 or 2 by our convention')
 
@@ -892,3 +999,282 @@
         prefix = 'voltha.{}.{}'.format(self.name, device_id)
         lc = LoopingCall(_collect, device_id, prefix)
         lc.start(interval=15)  # TODO make this configurable
+
+    def _voltha_get_oam_msg_type(self, frame):
+        respType = RxedOamMsgTypeEnum["Unknown"]
+        recv_frame = frame
+        payload = recv_frame.payload
+        if hasattr(payload, 'body'):
+            loadstr       = payload.body.load
+            bytesRead = 0;
+            if (payload.opcode == 0xFE):
+
+                # Extract the OUI
+                (oui_hi, oui_lo) = struct.unpack_from('>BH', loadstr, bytesRead)
+                oui = (oui_hi << 16) | oui_lo
+                log.debug('oui: 0x %06x' % oui)
+                bytesRead += 3
+
+                # If this is the ITU OUI, then there is an embedded OMCI message
+                if (oui == 0x0019A7):
+                    respType = RxedOamMsgTypeEnum["OMCI Message"]
+
+                # Treat Cablelabs OUI and Tibit OUI as the same
+                elif ((oui == 0x001000) or (oui == 0x2AEA15)):
+
+                    (dpoeOpcode) = struct.unpack_from('>B', loadstr, bytesRead)[0]
+#                    log.info('DPoE Opcode:    {} ({:0>2X})'.format(DPoEOpcodeEnum[dpoeOpcode], dpoeOpcode))
+                    bytesRead += 1
+
+                    # Get Response
+                    if (dpoeOpcode == 0x02):
+                        respType = RxedOamMsgTypeEnum["DPoE Get Response"]
+
+                    # Set Response
+                    elif (dpoeOpcode == 0x04):
+                        respType = RxedOamMsgTypeEnum["DPoE Set Response"]
+
+                    # File Transfer ACK
+                    elif (dpoeOpcode == 0x09):
+                        respType = RxedOamMsgTypeEnum["DPoE File Transfer"]
+
+                else:
+                    log.info('Unsupported OAM OUI 0x{:0>6X}'.format(oui))
+
+            # Handle OAM Event Notification
+            elif (payload.opcode == 0x01):
+                respType = RxedOamMsgTypeEnum["Event Notification"]
+            else:
+                log.info('Unsupported OAM Opcode {}'.format(payload.opcode))
+        else:
+            log.debug('received frame has no payload')
+
+        return respType
+
+
+    def _voltha_check_set_resp(self, frame):
+        rc = False
+        branch = 0
+        leaf = 0
+        status = 0
+
+        recv_frame = frame
+        payload = recv_frame.payload
+        if hasattr(payload, 'body'):
+            loadstr   = payload.body.load
+            bytesRead = 0;
+            #if self.report_obj is not None:
+            #    self.report_obj.log_result(data="OAM Opcode", actual=hex(payload.opcode), expected=hex(0xFE), criteria="==")
+            if (payload.opcode == 0xFE):
+
+                # Extract the OUI
+                (oui_hi, oui_lo) = struct.unpack_from('>BH', loadstr, bytesRead)
+                oui = (oui_hi << 16) | oui_lo
+                log.info('oui: 0x %06x' % oui)
+                bytesRead += 3
+
+                # Treat Cablelabs OUI and Tibit OUI as the same
+                if ((oui == 0x001000) or (oui == 0x2AEA15)):
+                    (dpoeOpcode) = struct.unpack_from('>B', loadstr, bytesRead)[0]
+                    bytesRead += 1
+
+                    startOfTlvs = bytesRead
+                    # Set Response
+                    if (dpoeOpcode == 0x04):
+                        test =1
+                        (rc,branch,leaf,status) = self._voltha_check_set_resp_attrs(loadstr, startOfTlvs)
+                        if (rc == True):
+                            log.info('Set Response had no errors')
+                        else:
+                            log.debug('Branch 0x{:X} Leaf 0x{:0>4X} {}'.format(branch, leaf, DPoEVariableResponseCodes[status]))
+                    else:
+                        log.info('Unsupported DPoE Opcode: {} ({:0>2X})'.format(DPoEOpcodeEnum[dpoeOpcode], dpoeOpcode))
+                else:
+                    log.info('Unsupported OAM OUI 0x{:0>6X}'. format(oui))
+            else:
+                log.info('Unsupported OAM Opcode {}'.format(payload.opcode))
+        else:
+            log.debug('received frame has no payload')
+
+        return rc,branch,leaf,status
+    
+
+    def _voltha_check_resp(self, frame):
+        recv_frame = frame
+        payload = recv_frame.payload
+        if hasattr(payload, 'body'):
+            loadstr   = payload.body.load
+            bytesRead = 0;
+            if (payload.opcode == 0xFE):
+                
+                # Extract the OUI
+                (oui_hi, oui_lo) = struct.unpack_from('>BH', loadstr, bytesRead)
+                oui = (oui_hi << 16) | oui_lo
+                log.info('oui: 0x %06x' % oui)
+                bytesRead += 3
+
+                # If this is the ITU OUI, then there is an embedded OMCI message
+                if (oui == 0x0019A7):
+                    self._voltha_handle_omci(loadstr,bytesRead)
+
+                # Treat Cablelabs OUI and Tibit OUI as the same
+                elif ((oui == 0x001000) or (oui == 0x2AEA15)):
+                    log.debug('Recieved Response OUI 0x{:0>6X}'. format(oui))
+                else:
+                    log.info('Unsupported OAM OUI 0x{:0>6X}'. format(oui))
+
+            # Handle OAM Event Notification
+            elif (payload.opcode == 0x01):
+                self._voltha_handle_oam_event(loadstr, bytesRead)
+            else:
+                log.info('Unsupported OAM Opcode {}'.format(payload.opcode))
+
+        else:
+            log.debug('received frame has no payload')
+
+
+    def _voltha_handle_oam_event(self, loadstr, startOfEvent):
+        bytesRead = startOfEvent
+        (seq_num, tlv_type, ev_len, oui_hi, oui_lo) = struct.unpack_from('>HBBBH', loadstr, bytesRead)
+        oui = (oui_hi << 16) | oui_lo
+
+        log.info('seq_num:        0x%04x' % seq_num)
+        log.info('tlv_type:       0x%' % tlv_type)
+        log.info('ev_len:         0x%x' % ev_len)
+        log.info('oui:            0x%06x"'% oui)
+
+        if (tlv_type != 0xFE):
+            log.debug('unexpected tlv_type 0x%x (expected 0xFE)' % tlv_type)
+        elif (oui == 0x001000):
+            log.debug('DPoE Event')
+            ## TODO - Handle DPoE Event/Alarm
+        elif (oui == 0x2AEA15):
+            log.debug('Tibit-specific Event')
+
+            # TODO - Handle addition/removal of links
+
+            bytesRead = 7
+
+            # TODO - Check OUI and parse Source and Reference Object Contexts
+    
+
+    def _voltha_handle_omci(self, loadstr, startOfEvent):
+        bytesRead = startOfEvent
+#        (seq_num, tlv_type, ev_len, oui_hi, oui_lo) = struct.unpack_from('>BBBBBH', loadstr, bytesRead)
+
+        log.debug('OMCI Message')
+
+        # TODO - Handle OMCI message
+
+
+
+    def _voltha_handle_get_value(self, 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)
+#            log.info('Branch/Leaf        0x{:0>2X}/0x{:0>4X}'.format(branch, leaf))
+
+            if (branch != 0):
+                bytesRead += 3
+                length = struct.unpack_from('>B', loadstr, bytesRead)[0]
+#                log.info('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:
+                    if (length >= 0x80):
+                        log.info('Branch 0x{:0>2X} Leaf 0x{:0>4X} {}'.format(branch, leaf, DPoEVariableResponseCodes[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]
+
+#                log.info('Value:             {}'.format(value))
+
+                if (length > 0):
+                    bytesRead += length
+
+                if (branch != 0xD6):
+                    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 _voltha_check_set_resp_attrs(self, 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)
+#            log.info('Branch/Leaf        0x{:0>2X}/0x{:0>4X}'.format(branch, leaf))
+
+            if (branch != 0):
+                bytesRead += 3
+                length = struct.unpack_from('>B', loadstr, bytesRead)[0]
+#                log.info('Length:            0x{:0>2X} ({})'.format(length,length))
+                bytesRead += 1
+
+                if (length >= 0x80):
+                    log.debug('Branch 0x{:0>2X} Leaf 0x{:0>4X} {}'.format(branch, leaf, DPoEVariableResponseCodes[length]))
+                    if (length > 0x80):
+                        retVal = False;
+                        break;
+                else:
+                    bytesRead += length
+
+            else:
+                break
+
+        return retVal,branch,leaf,length
+
+
+
+    def _voltha_handle_fx_ack(self, loadstr, startOfXfer, block_number):
+        retVal = False
+        (fx_opcode, acked_block, response_code) = struct.unpack_from('>BHB', loadstr, startOfXfer)
+
+        log.debug('fx_opcode:      0x%x' % fx_opcode)
+        log.debug('acked_block:    0x%x' % acked_block)
+        log.debug('response_code:  0x%x' % response_code)
+
+
+
+        if (fx_opcode != 0x03):
+            log.debug('unexpected fx_opcode 0x%x (expected 0x03)' % fx_opcode)
+        elif (acked_block != block_number):
+            log.debug('unexpected acked_block 0x%x (expected 0x%x)' % (acked_block, block_number))
+        elif (response_code != 0):
+            log.debug('unexpected response_code 0x%x (expected 0x00)' % response_code)
+        else:
+            retVal = True;
+
diff --git a/voltha/extensions/eoam/EOAM_TLV.py b/voltha/extensions/eoam/EOAM_TLV.py
index 41d55d3..f0861cd 100644
--- a/voltha/extensions/eoam/EOAM_TLV.py
+++ b/voltha/extensions/eoam/EOAM_TLV.py
@@ -12,8 +12,9 @@
 
 from scapy.packet import Packet
 from scapy.fields import ByteEnumField, XShortField, XByteField, MACField, \
-    ByteField, BitEnumField, BitField
-from scapy.fields import XLongField, StrField, XIntField
+    ByteField, BitEnumField, BitField, ShortField
+from scapy.fields import XLongField, StrField, StrFixedLenField, XIntField, \
+    FieldLenField, StrLenField, IntField
 
 # This library strives to be an implementation of the following standard:
 
@@ -52,20 +53,21 @@
     0x05: "Dynamic IP Multicast Control",
     0x06: "Multicast Register",
     0x07: "Multicast Register Response",
+    0x09: "File Transfer",
     }
 
 ### Table 20 - DPoE Variable Response Codes
 DPoEVariableResponseCodes = {
-    0x80, "No Error",
-    0x81, "Too Long",
-    0x86, "Bad Parameters",
-    0x87, "No Resources",
-    0x88, "System Busy",
-    0xa0, "Undetermined Error",
-    0xa1, "Unsupported",
-    0xa2, "May Be Corrupted",
-    0xa3, "Hardware Failure",
-    0xa4, "Overflow",
+    0x80: "No Error",
+    0x81: "Too Long",
+    0x86: "Bad Parameters",
+    0x87: "No Resources",
+    0x88: "System Busy",
+    0xa0: "Undetermined Error",
+    0xa1: "Unsupported",
+    0xa2: "May Be Corrupted",
+    0xa3: "Hardware Failure",
+    0xa4: "Overflow",
     }
 
 class SlowProtocolsSubtype(Packet):
@@ -97,6 +99,13 @@
                     XByteField("oui1", 0x0D),
                     XByteField("oui2", 0xB6)]
 
+class TibitOUI(Packet):
+    """ Organizationally Unique Identifier (Tibit)"""
+    name = "Organizationally Unique Identifier (Tibit)"
+    fields_desc  = [XByteField("oui0", 0x2A),
+                    XByteField("oui1", 0xEA),
+                    XByteField("oui2", 0x15)]
+
 class ItuOUI(Packet):
     """ Organizationally Unique Identifier (Tibit)"""
     name = "Organizationally Unique Identifier (ITU)"
@@ -147,6 +156,12 @@
                    XShortField("MulticastLink", 0x0000),
                    XShortField("UnicastLink", 0x0000),
                    ]
+
+class TibitOpcode_OmciMessage(Packet):
+    """ DPoE Opcode"""
+    name = "Tibit Opcode"
+    fields_desc  = [ByteEnumField("opcode", 0xA0, DPoEOpcodeEnum)]
+
 ####
 #### PORT OBJECTS
 ####
@@ -210,6 +225,19 @@
                    XByteField("pad", 0),
                    ]
 
+class OLTEPONUnicastLogicalLink(Packet):
+    """ Object Context: OLT Unicast Logical Link """
+    name = "Object Context: OLT Unicast Logical Link"
+    fields_desc = [XByteField("branch", 0xD6),
+                   XShortField("leaf", 0x000a),
+                   XByteField("length", 10),
+                   XByteField("pon", 0),
+                   XIntField("unicastvssn", 0x00000000),
+                   XIntField("unicastlink", 0x00000000),
+                   XByteField("pad", 0),
+                   ]
+
+
 # __TIBIT_OLT_OAM__: Defined by Tibit
 class NetworkToNetworkPortObject(Packet):
     """ Object Context: Network-to-Network (NNI) Port Object """
@@ -239,6 +267,14 @@
                    XByteField("number", 0)
                    ]
 
+class ONUObject(Packet):
+    """ Object Context: ONU Object """
+    name = "Object Context: ONU Object"
+    fields_desc = [XByteField("branch", 0xD6),
+                   XShortField("leaf", 0x0009),
+                   XByteField("length", 6),
+                   MACField("mac", "54:42:e2:22:11:00")
+                   ]
 
 ####
 #### 0x09 - BRANCH ATTRIBUTES
@@ -295,6 +331,12 @@
     fields_desc = [XByteField("branch", 0xD7),
                    XShortField("leaf", 0x0005)]
 
+class ManufacturerInfo(Packet):
+    """ Variable Descriptor: ManufacturerInfo """
+    name = "Variable Descriptor: ManufacturerInfo"
+    fields_desc = [XByteField("branch", 0xD7),
+                   XShortField("leaf", 0x0006)]
+
 class MaxLogicalLinks(Packet):
     """ Variable Descriptor: Max Logical Links """
     name = "Variable Descriptor: Max Logical Links"
@@ -573,6 +615,8 @@
     fields_desc = [XByteField("branch", 0xD7),
                    XShortField("leaf", 0x010e),
                    ]
+
+
 ####
 #### 0xD9 - MAC Table Operations - Dynamic and Static
 ####
@@ -627,6 +671,48 @@
 #### 0xd7 - STATISTICS
 ####
 
+class RxFramesGreen(Packet):
+    """ Variable Descriptor: RxFramesGreen """
+    name = "Variable Descriptor: RxFramesGreen"
+    fields_desc = [XByteField("branch", 0xD7),
+                   XShortField("leaf", 0x0201),
+                   ]
+
+class TxFramesGreen(Packet):
+    """ Variable Descriptor: TxFramesGreen """
+    name = "Variable Descriptor: TxFramesGreen"
+    fields_desc = [XByteField("branch", 0xD7),
+                   XShortField("leaf", 0x0202),
+                   ]
+
+class RxFrame_64(Packet):
+    """ Variable Descriptor: RxFrame_64 """
+    name = "Variable Descriptor: RxFrame_64"
+    fields_desc = [XByteField("branch", 0xD7),
+                   XShortField("leaf", 0x0204),
+                   ]
+
+class RxFrame_65_127(Packet):
+    """ Variable Descriptor: RxFrame_65_127 """
+    name = "Variable Descriptor: RxFrame_65_127"
+    fields_desc = [XByteField("branch", 0xD7),
+                   XShortField("leaf", 0x0205),
+                   ]
+
+class RxFrame_128_255(Packet):
+    """ Variable Descriptor: RxFrame_128_255 """
+    name = "Variable Descriptor: RxFrame_128_255"
+    fields_desc = [XByteField("branch", 0xD7),
+                   XShortField("leaf", 0x0206),
+                   ]
+
+class RxFrame_256_511(Packet):
+    """ Variable Descriptor: RxFrame_256_511 """
+    name = "Variable Descriptor: RxFrame_256_511"
+    fields_desc = [XByteField("branch", 0xD7),
+                   XShortField("leaf", 0x0207),
+                   ]
+
 class RxFrame_512_1023(Packet):
     """ Variable Descriptor: RxFrame_512_1023 """
     name = "Variable Descriptor: RxFrame_512_1023"
@@ -634,11 +720,67 @@
                    XShortField("leaf", 0x0208),
                    ]
 
+class RxFrame_1024_1518(Packet):
+    """ Variable Descriptor: RxFrame_1024_1518 """
+    name = "Variable Descriptor: RxFrame_1024_1518"
+    fields_desc = [XByteField("branch", 0xD7),
+                   XShortField("leaf", 0x0209),
+                   ]
+
+class RxFrame_1519Plus(Packet):
+    """ Variable Descriptor: RxFrame_1024_1518 """
+    name = "Variable Descriptor: RxFrame_1024_1518"
+    fields_desc = [XByteField("branch", 0xD7),
+                   XShortField("leaf", 0x020A),
+                   ]
+
+class TxFrame_64(Packet):
+    """ Variable Descriptor: TxFrame_64 """
+    name = "Variable Descriptor: TxFrame_64"
+    fields_desc = [XByteField("branch", 0xD7),
+                   XShortField("leaf", 0x020B),
+                   ]
+
+class TxFrame_65_127(Packet):
+    """ Variable Descriptor: TxFrame_65_127 """
+    name = "Variable Descriptor: TxFrame_65_127"
+    fields_desc = [XByteField("branch", 0xD7),
+                   XShortField("leaf", 0x020C),
+                   ]
+
+class TxFrame_128_255(Packet):
+    """ Variable Descriptor: TxFrame_128_255 """
+    name = "Variable Descriptor: TxFrame_128_255"
+    fields_desc = [XByteField("branch", 0xD7),
+                   XShortField("leaf", 0x020D),
+                   ]
+
+class TxFrame_256_511(Packet):
+    """ Variable Descriptor: TxFrame_256_511 """
+    name = "Variable Descriptor: TxFrame_256_511"
+    fields_desc = [XByteField("branch", 0xD7),
+                   XShortField("leaf", 0x020E),
+                   ]
+
 class TxFrame_512_1023(Packet):
     """ Variable Descriptor: TxFrame_512_1023 """
     name = "Variable Descriptor: TxFrame_512_1023"
     fields_desc = [XByteField("branch", 0xD7),
-                   XShortField("leaf", 0x020f),
+                   XShortField("leaf", 0x020F),
+                   ]
+
+class TxFrame_1024_1518(Packet):
+    """ Variable Descriptor: TxFrame_1024_1518 """
+    name = "Variable Descriptor: TxFrame_1024_1518"
+    fields_desc = [XByteField("branch", 0xD7),
+                   XShortField("leaf", 0x0210),
+                   ]
+
+class TxFrame_1519Plus(Packet):
+    """ Variable Descriptor: TxFrame_1024_1518 """
+    name = "Variable Descriptor: TxFrame_1024_1518"
+    fields_desc = [XByteField("branch", 0xD7),
+                   XShortField("leaf", 0x0211),
                    ]
 
 class FramesDropped(Packet):
@@ -1109,6 +1251,143 @@
                    ]
 
 ####
+#### 0xb7 - TIBIT ATTRIBUTES
+####
+class OltMode(Packet):
+    """ Variable Descriptor: OLT Mode """
+    name = "Variable Descriptor: "
+    fields_desc = [XByteField("branch", 0xB7),
+                   XShortField("leaf", 0x0101),
+                   ]
+
+class OltPonAdminState(Packet):
+    """ Variable Descriptor: OLT PON Admin State """
+    name = "Variable Descriptor: "
+    fields_desc = [XByteField("branch", 0xB7),
+                   XShortField("leaf", 0x0102),
+                   ]
+
+class OltPonAdminStateSet(Packet):
+    """ Variable Container: OLT PON Admin State """
+    name = "Variable Container: "
+    fields_desc = [XByteField("branch", 0xB7),
+                   XShortField("leaf", 0x0102),
+                   XByteField("length", 1),
+                   XByteField("value", 0),
+                   ]
+
+class TibitLinkMacTable(Packet):
+    """ Variable Descriptor: Link MAC Table """
+    name = "Variable Descriptor: "
+    fields_desc = [XByteField("branch", 0xB7),
+                   XShortField("leaf", 0x0103),
+                   ]
+
+class TibitKeyExchange(Packet):
+    """ Variable Descriptor: Key Exchange """
+    name = "Variable Descriptor: "
+    fields_desc = [XByteField("branch", 0xB7),
+                   XShortField("leaf", 0x0104),
+                   ]
+
+class TibitKeyExchangeSet(Packet):
+    """ Variable Descriptor: Key Exchange Set"""
+    name = "Variable Container: "
+    fields_desc = [XByteField("branch", 0xB7),
+                   XShortField("leaf", 0x0104),
+                   XByteField("length", 2),
+                   XShortField("value", 0x1234),
+                  ]
+
+UpstreamSlaSubtypeEnum = { 0x00: "Terminator",
+                           0x01: "Header",
+                           0x02: "Max Grant Period",
+                           0x03: "Min Grant Period",
+                           0x04: "Service Limit",
+                           0x05: "Fixed Rate",
+                           0x06: "Guaranteed Rate",
+                           0x07: "Best Effort Rate",
+                           0x08: "Max Burst Size",
+                           0x09: "Priority",
+                         }
+
+class UpstreamSla(Packet):
+    """ Variable Descriptor: Upstream SLA """
+    name = "Variable Descriptor: Upstream SLA"
+    fields_desc = [XByteField("branch", 0xB7),
+                   XShortField("leaf", 0x0621),
+                   ]
+
+class UpstreamSlaHeader(Packet):
+    """ Variable Descriptor: Upstream SLA Header """
+    name = "Variable Descriptor: Upstream SLA Header"
+    fields_desc = [XByteField("branch", 0xB7),
+                   XShortField("leaf", 0x0621),
+                   ByteField("length", 1),
+                   XByteField("subtype", 1),
+                   ]
+
+class UpstreamSlaTerminator(Packet):
+    """ Variable Descriptor: Upstream SLA Terminator """
+    name = "Variable Descriptor: Upstream SLA Terminator"
+    fields_desc = [XByteField("branch", 0xB7),
+                   XShortField("leaf", 0x0621),
+                   ByteField("length", 1),
+                   XByteField("subtype", 0),
+                   ]
+
+class UpstreamSlaSettingLength01(Packet):
+    """ Variable Descriptor: Upstream SLA Setting """
+    name = "Variable Descriptor: Upstream SLA Setting"
+    fields_desc = [XByteField("branch", 0xB7),
+                   XShortField("leaf", 0x0621),
+                   ByteField("length", 3),
+                   XByteField("subtype", 0),
+                   XByteField("setting_len", 1),
+                   ByteField("setting_val", 0),
+                   ]
+
+class UpstreamSlaSettingLength02(Packet):
+    """ Variable Descriptor: Upstream SLA Setting """
+    name = "Variable Descriptor: Upstream SLA Setting"
+    fields_desc = [XByteField("branch", 0xB7),
+                   XShortField("leaf", 0x0621),
+                   ByteField("length", 4),
+                   XByteField("subtype", 0),
+                   XByteField("setting_len", 2),
+                   ShortField("setting_val", 0),
+                   ]
+
+class UpstreamSlaSettingLength04(Packet):
+    """ Variable Descriptor: Upstream SLA Setting """
+    name = "Variable Descriptor: Upstream SLA Setting"
+    fields_desc = [XByteField("branch", 0xB7),
+                   XShortField("leaf", 0x0621),
+                   ByteField("length", 6),
+                   XByteField("subtype", 0),
+                   XByteField("setting_len", 4),
+                   IntField("setting_val", 0),
+                   ]
+
+class SlaPriorityType(Packet):
+    """ Variable Descriptor: SLA Priority Type """
+    name = "Variable Descriptor: "
+    fields_desc = [XByteField("branch", 0xB7),
+                   XShortField("leaf", 0x0622),
+                   ]
+
+class SlaPriorityTypeSet(Packet):
+    """ Variable Container: SLA Priority Type """
+    name = "Variable Container: "
+    fields_desc = [XByteField("branch", 0xB7),
+                   XShortField("leaf", 0x0622),
+                   XByteField("length", 1),
+                   XByteField("value", 1),
+                   ]
+
+
+
+####
 #### 0xd9 - BRANCH ATTRIBUTES
 ####
 
@@ -1151,6 +1430,31 @@
     fields_desc = [XByteField("branch", 0xD9),
                    XShortField("leaf", 0x0301)]
 
+class DeviceReset(Packet):
+    """ Variable Descriptor: Device Reset """
+    name = "Variable Descriptor: Device Reset"
+    fields_desc = [XByteField("branch", 0xD9),
+                   XShortField("leaf", 0x0001),
+                   XByteField("length", 0x80),
+                   ]
+
+class TibitDeviceReset(Packet):
+    """ Variable Descriptor: Tibit Device Reset """
+    name = "Variable Descriptor: Tibit Device Reset"
+    fields_desc = [XByteField("branch", 0xB9),
+                   XShortField("leaf", 0x0001),
+                   XByteField("length", 0x80),
+                   ]
+
+class TibitApplySla(Packet):
+    """ Variable Descriptor: Apply SLA """
+    name = "Variable Descriptor: Apply Sla"
+    fields_desc = [XByteField("branch", 0xB9),
+                   XShortField("leaf", 0x0601),
+                   XByteField("length", 0x80),
+                   XByteField("value", 0),
+                   ]
+
 
 ##
 ## Broadcom TLVs
@@ -1218,6 +1522,15 @@
                    XByteField("value", 1),
                    ]
 
+class GenericTLV(Packet):
+    """ Variable Descriptor: Generic TLV """
+    name = "Variable Descriptor: Generic TLV"
+    fields_desc = [XByteField("branch", 0x00),
+                   XShortField("leaf", 0x0000),
+                   FieldLenField("length", None, length_of="value", fmt="B"),
+                   StrLenField("value", "", length_from=lambda x:x.length),
+                   ]
+
 class EndOfPDU(Packet):
     name = "End of EOAM PDU"
     fields_desc = [BitEnumField("type", 0x00, 7, TLV_dictionary),