Handle flows to trap DHCP and LLDP on NNI port

Change-Id: I1801ff00023f35ad7a8710378667d8462a69af7e
diff --git a/voltha/adapters/openolt/openolt_flow_mgr.py b/voltha/adapters/openolt/openolt_flow_mgr.py
index c984fe0..4c40910 100644
--- a/voltha/adapters/openolt/openolt_flow_mgr.py
+++ b/voltha/adapters/openolt/openolt_flow_mgr.py
@@ -18,8 +18,8 @@
 import grpc
 
 from voltha.protos.openflow_13_pb2 import OFPXMC_OPENFLOW_BASIC, \
-    ofp_flow_stats, ofp_match, OFPMT_OXM, Flows, FlowGroups, \
-    OFPXMT_OFB_IN_PORT, OFPXMT_OFB_VLAN_VID
+    ofp_flow_stats, OFPMT_OXM, Flows, FlowGroups, OFPXMT_OFB_IN_PORT, \
+    OFPXMT_OFB_VLAN_VID
 from voltha.protos.device_pb2 import Port
 import voltha.core.flow_decomposer as fd
 import openolt_platform as platform
@@ -33,9 +33,10 @@
 EAPOL_DOWNLINK_FLOW_INDEX = 3  # FIXME
 EAPOL_DOWNLINK_SECONDARY_FLOW_INDEX = 4  # FIXME
 EAPOL_UPLINK_SECONDARY_FLOW_INDEX = 5  # FIXME
-
+LLDP_FLOW_INDEX = 7  # FIXME
 
 EAP_ETH_TYPE = 0x888e
+LLDP_ETH_TYPE = 0x88cc
 
 # FIXME - see also BRDCM_DEFAULT_VLAN in broadcom_onu.py
 DEFAULT_MGMT_VLAN = 4091
@@ -53,14 +54,12 @@
         self.flows_proxy = registry('core').get_proxy(
             '/devices/{}/flows'.format(self.device_id))
         self.root_proxy = registry('core').get_proxy('/')
-        self.fd_object = fd.FlowDecomposer()
 
     def add_flow(self, flow):
         self.log.debug('add flow', flow=flow)
         classifier_info = dict()
         action_info = dict()
 
-
         for field in fd.get_ofb_fields(flow):
             if field.type == fd.ETH_TYPE:
                 classifier_info['eth_type'] = field.eth_type
@@ -117,12 +116,14 @@
                     self.log.debug('being taken care of by ONU', flow=flow)
                     return
                 action_info['pop_vlan'] = True
-                self.log.debug('action-type-pop-vlan', in_port=classifier_info['in_port'])
+                self.log.debug('action-type-pop-vlan',
+                               in_port=classifier_info['in_port'])
             elif action.type == fd.PUSH_VLAN:
                 action_info['push_vlan'] = True
                 action_info['tpid'] = action.push.ethertype
                 self.log.debug('action-type-push-vlan',
-                               push_tpid=action_info['tpid'], in_port=classifier_info['in_port'])
+                               push_tpid=action_info['tpid'],
+                               in_port=classifier_info['in_port'])
                 if action.push.ethertype != 0x8100:
                     self.log.error('unhandled-tpid',
                                    ethertype=action.push.ethertype)
@@ -131,8 +132,8 @@
                 _field = action.set_field.field.ofb_field
                 assert (action.set_field.field.oxm_class ==
                         OFPXMC_OPENFLOW_BASIC)
-                self.log.debug('action-type-set-field',
-                               field=_field, in_port=classifier_info['in_port'])
+                self.log.debug('action-type-set-field', field=_field,
+                               in_port=classifier_info['in_port'])
                 if _field.type == fd.VLAN_VID:
                     self.log.debug('set-field-type-vlan-vid',
                                    vlan_vid=_field.vlan_vid & 0xfff)
@@ -142,7 +143,8 @@
                                    field_type=_field.type)
             else:
                 self.log.error('unsupported-action-type',
-                               action_type=action.type, in_port=classifier_info['in_port'])
+                               action_type=action.type,
+                               in_port=classifier_info['in_port'])
 
         if fd.get_goto_table_id(flow) is not None and not 'pop_vlan' in \
                 action_info:
@@ -150,7 +152,7 @@
             return
 
         if not 'output' in action_info and 'metadata' in classifier_info:
-            #find flow in the next table
+            # find flow in the next table
             next_flow = self.find_next_flow(flow)
             if next_flow is None:
                 return
@@ -159,24 +161,21 @@
                 if field.type == fd.VLAN_VID:
                     classifier_info['metadata'] = field.vlan_vid & 0xfff
 
-
         (intf_id, onu_id) = platform.extract_access_from_flow(
             classifier_info['in_port'], action_info['output'])
 
-
         self.divide_and_add_flow(intf_id, onu_id, classifier_info,
                                  action_info, flow)
 
     def remove_flow(self, flow):
         self.log.debug('trying to remove flows from logical flow :',
-                   logical_flow=flow)
+                       logical_flow=flow)
         device_flows_to_remove = []
         device_flows = self.flows_proxy.get('/').items
         for f in device_flows:
             if f.cookie == flow.id:
                 device_flows_to_remove.append(f)
 
-
         for f in device_flows_to_remove:
             (id, direction) = self.decode_stored_id(f.id)
             flow_to_remove = openolt_pb2.Flow(flow_id=id, flow_type=direction)
@@ -185,7 +184,8 @@
             except grpc.RpcError as grpc_e:
                 if grpc_e.code() == grpc.StatusCode.NOT_FOUND:
                     self.log.debug('This flow does not exist on the switch, '
-                                   'normal after an OLT reboot', flow=flow_to_remove)
+                                   'normal after an OLT reboot',
+                                   flow=flow_to_remove)
                 else:
                     raise grpc_e
 
@@ -209,7 +209,6 @@
             self.log.debug('no device flow to remove for this flow (normal '
                            'for multi table flows)', flow=flow)
 
-
     def divide_and_add_flow(self, intf_id, onu_id, classifier,
                             action, flow):
 
@@ -225,22 +224,27 @@
                 self.log.warn('igmp flow add ignored, not implemented yet')
             else:
                 self.log.warn("Invalid-Classifier-to-handle",
-                               classifier=classifier,
-                               action=action)
+                              classifier=classifier,
+                              action=action)
         elif 'eth_type' in classifier:
             if classifier['eth_type'] == EAP_ETH_TYPE:
                 self.log.debug('eapol flow add')
                 self.add_eapol_flow(intf_id, onu_id, flow)
                 vlan_id = self.get_subscriber_vlan(fd.get_in_port(flow))
                 if vlan_id is not None:
-                    self.add_eapol_flow(intf_id, onu_id, flow,
+                    self.add_eapol_flow(
+                        intf_id, onu_id, flow,
                         uplink_eapol_id=EAPOL_UPLINK_SECONDARY_FLOW_INDEX,
                         downlink_eapol_id=EAPOL_DOWNLINK_SECONDARY_FLOW_INDEX,
                         vlan_id=vlan_id)
+            if classifier['eth_type'] == LLDP_ETH_TYPE:
+                self.log.debug('lldp flow add')
+                self.add_lldp_flow(intf_id, onu_id, flow, classifier,
+                                   action)
 
         elif 'push_vlan' in action:
             self.add_upstream_data_flow(intf_id, onu_id, classifier, action,
-                                      flow)
+                                        flow)
         elif 'pop_vlan' in action:
             self.add_downstream_data_flow(intf_id, onu_id, classifier,
                                           action, flow)
@@ -249,10 +253,8 @@
                            classifier=classifier,
                            action=action, flow=flow)
 
-
     def add_upstream_data_flow(self, intf_id, onu_id, uplink_classifier,
-                       uplink_action, logical_flow):
-
+                               uplink_action, logical_flow):
 
         uplink_classifier['pkt_tag_type'] = 'single_tag'
 
@@ -263,12 +265,12 @@
         # Secondary EAP on the subscriber vlan
         (eap_active, eap_logical_flow) = self.is_eap_enabled(intf_id, onu_id)
         if eap_active:
-            self.add_eapol_flow(intf_id, onu_id, eap_logical_flow,
+            self.add_eapol_flow(
+                intf_id, onu_id, eap_logical_flow,
                 uplink_eapol_id=EAPOL_UPLINK_SECONDARY_FLOW_INDEX,
                 downlink_eapol_id=EAPOL_DOWNLINK_SECONDARY_FLOW_INDEX,
                 vlan_id=uplink_classifier['vlan_vid'])
 
-
     def add_downstream_data_flow(self, intf_id, onu_id, downlink_classifier,
                                  downlink_action, flow):
         downlink_classifier['pkt_tag_type'] = 'double_tag'
@@ -321,7 +323,6 @@
 
         self.add_flow_to_device(upstream_flow, logical_flow)
 
-
         # FIXME - ONOS should send explicit upstream and downstream
         #         exact dhcp trap flow.
 
@@ -346,7 +347,6 @@
                 classifier),
             action=self.mk_action(action))
 
-
         self.add_flow_to_device(downstream_flow, downstream_logical_flow)
 
     def add_eapol_flow(self, intf_id, onu_id, logical_flow,
@@ -393,6 +393,7 @@
             downlink_classifier['vlan_vid'] = 4000 - onu_id
 
 
+
             downlink_action = {}
             downlink_action['push_vlan'] = True
             downlink_action['vlan_vid'] = vlan_id
@@ -438,6 +439,29 @@
     def reset_flows(self):
         self.flows_proxy.update('/', Flows())
 
+    def add_lldp_flow(self, intf_id, onu_id, logical_flow, classifier, action):
+
+        self.log.debug('add lldp downstream trap', classifier=classifier,
+                       action=action)
+
+        action.clear()
+        action['trap_to_host'] = True
+        classifier['pkt_tag_type'] = 'untagged'
+
+        gemport_id = platform.mk_gemport_id(onu_id)
+        flow_id = platform.mk_flow_id(intf_id, onu_id, LLDP_FLOW_INDEX)
+
+        downstream_flow = openolt_pb2.Flow(
+            onu_id=onu_id, flow_id=flow_id, flow_type="downstream",
+            access_intf_id=3, network_intf_id=0, gemport_id=gemport_id,
+            priority=logical_flow.priority,
+            classifier=self.mk_classifier(classifier),
+            action=self.mk_action(action))
+
+        self.log.debug('add lldp downstream trap', access_intf_id=intf_id,
+                       onu_id=onu_id, flow_id=flow_id)
+        self.stub.FlowAdd(downstream_flow)
+
     def mk_classifier(self, classifier_info):
 
         classifier = openolt_pb2.Classifier()
@@ -523,7 +547,6 @@
 
             if in_port == port and \
                 platform.intf_id_to_port_type_name(out_port) == Port.ETHERNET_NNI:
-
                 fields = fd.get_ofb_fields(flow)
                 self.log.debug('subscriber flow found', fields=fields)
                 for field in fields:
@@ -553,7 +576,6 @@
         flows.items.extend([stored_flow])
         self.flows_proxy.update('/', flows)
 
-
     def find_next_flow(self, flow):
         table_id = fd.get_goto_table_id(flow)
         metadata = 0
@@ -566,20 +588,19 @@
         next_flows = []
         for f in flows:
             if f.table_id == table_id:
-                #FIXME:
+                # FIXME
                 if fd.get_in_port(f) == fd.get_in_port(flow) and \
                         fd.get_out_port(f) == metadata:
 
                     next_flows.append(f)
 
-
         if len(next_flows) == 0:
             self.log.warning('no next flow found, it may be a timing issue',
                              flow=flow, number_of_flows=len(flows))
             reactor.callLater(5, self.add_flow, flow)
             return None
 
-        next_flows.sort(key=lambda f:f.priority, reverse=True)
+        next_flows.sort(key=lambda f: f.priority, reverse=True)
 
         return next_flows[0]
 
@@ -607,4 +628,4 @@
         if id >> 15 == 0x1:
             return (id & 0x7fff, 'upstream')
         else:
-            return (id, 'downstream')
\ No newline at end of file
+            return (id, 'downstream')