VOL-1077 added NNI port and management port distinction

Change-Id: Ie24e05aa927661bb9a227d04e565018ce5523cd6
diff --git a/ponsim/v2/core/ponsim_device.go b/ponsim/v2/core/ponsim_device.go
index 13cae92..3fc52a5 100644
--- a/ponsim/v2/core/ponsim_device.go
+++ b/ponsim/v2/core/ponsim_device.go
@@ -111,7 +111,9 @@
 		forwarded := 0
 		links := o.links[int(egressPort)]
 
-		o.Counter.CountTxFrame(int(egressPort), len(common.GetEthernetLayer(egressFrame).Payload))
+		if int(egressPort) <= 2 && int(egressPort) > 0 {
+			o.Counter.CountTxFrame(int(egressPort), len(common.GetEthernetLayer(egressFrame).Payload))
+		}
 
 		for _, link := range links {
 			forwarded += 1
diff --git a/ponsim/v2/core/ponsim_olt.go b/ponsim/v2/core/ponsim_olt.go
index 34ed68a..15ca2e7 100644
--- a/ponsim/v2/core/ponsim_olt.go
+++ b/ponsim/v2/core/ponsim_olt.go
@@ -24,6 +24,7 @@
 	"github.com/golang/protobuf/ptypes/empty"
 	"github.com/google/gopacket"
 	"github.com/opencord/voltha/ponsim/v2/common"
+	"github.com/opencord/voltha/protos/go/openflow_13"
 	"github.com/opencord/voltha/protos/go/ponsim"
 	"github.com/sirupsen/logrus"
 	"google.golang.org/grpc"
@@ -122,6 +123,33 @@
 }
 
 /*
+forwardToNNI defines function to forward a packet to the NNI interface
+*/
+func (o *PonSimOltDevice) forwardToNNI() func(int, gopacket.Packet) {
+	return func(port int, frame gopacket.Packet) {
+		var err error
+		common.Logger().WithFields(logrus.Fields{
+			"device": o,
+			"port":   port,
+			"frame":  frame,
+		}).Debug("Forwarding packet to NNI")
+		if err = o.egressHandler.WritePacketData(frame.Data()); err != nil {
+			common.Logger().WithFields(logrus.Fields{
+				"device": o,
+				"port":   port,
+				"frame":  frame,
+			}).Fatal("Problem while forwarding packet to NNI")
+		} else {
+			common.Logger().WithFields(logrus.Fields{
+				"device": o,
+				"port":   port,
+				"frame":  frame,
+			}).Debug("Forwarded packet to NNI")
+		}
+	}
+}
+
+/*
 Start performs setup operations for an OLT device
 */
 func (o *PonSimOltDevice) Start(ctx context.Context) {
@@ -134,7 +162,10 @@
 	o.outgoing = make(chan []byte, 1)
 
 	// Add INGRESS operation
-	o.AddLink(2, 0, o.forwardToLAN())
+	o.AddLink(int(openflow_13.OfpPortNo_OFPP_CONTROLLER), 0, o.forwardToLAN())
+
+	// Add Data-Plane Forwarding operation
+	o.AddLink(2, 0, o.forwardToNNI())
 
 	// Start PM counter logging
 	o.counterLoop = common.NewIntervalHandler(90, o.Counter.LogCounts)
@@ -344,6 +375,10 @@
 			o.GetOnus()[portNum] = registree
 
 			o.AddLink(1, int(portNum), o.forwardToONU(portNum))
+			common.Logger().WithFields(logrus.Fields{
+				"port": portNum,
+				"onu":  onu,
+			}).Info("Connected ONU")
 			go o.MonitorOnu(ctx, portNum)
 			go o.Listen(ctx, portNum)
 		}
diff --git a/tests/utests/voltha/core/test_flow_decomposer.py b/tests/utests/voltha/core/test_flow_decomposer.py
index bad3234..bbdc599 100644
--- a/tests/utests/voltha/core/test_flow_decomposer.py
+++ b/tests/utests/voltha/core/test_flow_decomposer.py
@@ -303,7 +303,7 @@
         device_rules = self.decompose_rules([flow], [])
         onu1_flows, onu1_groups = device_rules['onu1']
         olt_flows, olt_groups = device_rules['olt']
-        self.assertEqual(len(onu1_flows), 1)
+        self.assertEqual(len(onu1_flows), 2)
         self.assertEqual(len(onu1_groups), 0)
         self.assertEqual(len(olt_flows), 2)
         self.assertEqual(len(olt_groups), 0)
@@ -361,7 +361,7 @@
         device_rules = self.decompose_rules([flow], [])
         onu1_flows, onu1_groups = device_rules['onu1']
         olt_flows, olt_groups = device_rules['olt']
-        self.assertEqual(len(onu1_flows), 1)
+        self.assertEqual(len(onu1_flows), 2)
         self.assertEqual(len(onu1_groups), 0)
         self.assertEqual(len(olt_flows), 2)
         self.assertEqual(len(olt_groups), 0)
@@ -420,7 +420,7 @@
         device_rules = self.decompose_rules([flow], [])
         onu1_flows, onu1_groups = device_rules['onu1']
         olt_flows, olt_groups = device_rules['olt']
-        self.assertEqual(len(onu1_flows), 1)
+        self.assertEqual(len(onu1_flows), 2)
         self.assertEqual(len(onu1_groups), 0)
         self.assertEqual(len(olt_flows), 2)
         self.assertEqual(len(olt_groups), 0)
diff --git a/tests/utests/voltha/core/test_logical_device_agent.py b/tests/utests/voltha/core/test_logical_device_agent.py
index 368cadc..c77b633 100644
--- a/tests/utests/voltha/core/test_logical_device_agent.py
+++ b/tests/utests/voltha/core/test_logical_device_agent.py
@@ -449,7 +449,7 @@
         ))
         self.lda._flow_table_updated(self.flows)
         self.assertEqual(len(self.device_flows['olt'].items), 2)
-        self.assertEqual(len(self.device_flows['onu1'].items), 3)
+        self.assertEqual(len(self.device_flows['onu1'].items), 4)
         self.assertEqual(len(self.device_flows['onu2'].items), 3)
         self.assertEqual(len(self.device_groups['olt'].items), 0)
         self.assertEqual(len(self.device_groups['onu1'].items), 0)
@@ -770,8 +770,8 @@
 
         # now check device level flows
         self.assertEqual(len(self.device_flows['olt'].items), 16)
-        self.assertEqual(len(self.device_flows['onu1'].items), 5)
-        self.assertEqual(len(self.device_flows['onu2'].items), 5)
+        self.assertEqual(len(self.device_flows['onu1'].items), 8)
+        self.assertEqual(len(self.device_flows['onu2'].items), 8)
         self.assertEqual(len(self.device_groups['olt'].items), 0)
         self.assertEqual(len(self.device_groups['onu1'].items), 0)
         self.assertEqual(len(self.device_groups['onu2'].items), 0)
@@ -879,12 +879,12 @@
             actions=[
                 set_field(vlan_vid(4096 + 101)), output(1)]
         ))
-        self.assertFlowsEqual(self.device_flows['onu1'].items[3], mk_flow_stat(
+        self.assertFlowsEqual(self.device_flows['onu1'].items[6], mk_flow_stat(
             priority=1000,
             match_fields=[in_port(1), eth_type(0x800), ipv4_dst(0xe4010102)],
             actions=[output(0)]
         ))
-        self.assertFlowsEqual(self.device_flows['onu1'].items[4], mk_flow_stat(
+        self.assertFlowsEqual(self.device_flows['onu1'].items[7], mk_flow_stat(
             priority=1000,
             match_fields=[in_port(1), eth_type(0x800), ipv4_dst(0xe4010104)],
             actions=[output(0)]
@@ -908,12 +908,12 @@
             actions=[
                 set_field(vlan_vid(4096 + 102)), output(1)]
         ))
-        self.assertFlowsEqual(self.device_flows['onu2'].items[3], mk_flow_stat(
+        self.assertFlowsEqual(self.device_flows['onu2'].items[6], mk_flow_stat(
             priority=1000,
             match_fields=[in_port(1), eth_type(0x800), ipv4_dst(0xe4010103)],
             actions=[output(0)]
         ))
-        self.assertFlowsEqual(self.device_flows['onu2'].items[4], mk_flow_stat(
+        self.assertFlowsEqual(self.device_flows['onu2'].items[7], mk_flow_stat(
             priority=1000,
             match_fields=[in_port(1), eth_type(0x800), ipv4_dst(0xe4010104)],
             actions=[output(0)]
diff --git a/tests/utests/voltha/core/test_multipon_lda.py b/tests/utests/voltha/core/test_multipon_lda.py
index 0859b4f..469a945 100644
--- a/tests/utests/voltha/core/test_multipon_lda.py
+++ b/tests/utests/voltha/core/test_multipon_lda.py
@@ -227,7 +227,7 @@
         self.lda._flow_table_updated(self.flows)
         self.assertEqual(len(self.device_flows['olt'].items), 2)
         self.assertEqual(len(self.device_flows['onu1'].items), 3)
-        self.assertEqual(len(self.device_flows['onu2'].items), 3)
+        self.assertEqual(len(self.device_flows['onu2'].items), 4)
         self.assertEqual(len(self.device_groups['olt'].items), 0)
         self.assertEqual(len(self.device_groups['onu1'].items), 0)
         self.assertEqual(len(self.device_groups['onu2'].items), 0)
@@ -490,8 +490,8 @@
 
         # now check device level flows
         self.assertEqual(len(self.device_flows['olt'].items), 18)
-        self.assertEqual(len(self.device_flows['onu1'].items), 5)
-        self.assertEqual(len(self.device_flows['onu2'].items), 5)
+        self.assertEqual(len(self.device_flows['onu1'].items), 8)
+        self.assertEqual(len(self.device_flows['onu2'].items), 8)
         self.assertEqual(len(self.device_groups['olt'].items), 0)
         self.assertEqual(len(self.device_groups['onu1'].items), 0)
         self.assertEqual(len(self.device_groups['onu2'].items), 0)
@@ -609,12 +609,12 @@
             actions=[
                 set_field(vlan_vid(4096 + 101)), output(1)]
         ))
-        self.assertFlowsEqual(self.device_flows['onu1'].items[3], mk_flow_stat(
+        self.assertFlowsEqual(self.device_flows['onu1'].items[6], mk_flow_stat(
             priority=1000,
             match_fields=[in_port(1), eth_type(0x800), ipv4_dst(0xe4010102)],
             actions=[output(0)]
         ))
-        self.assertFlowsEqual(self.device_flows['onu1'].items[4], mk_flow_stat(
+        self.assertFlowsEqual(self.device_flows['onu1'].items[7], mk_flow_stat(
             priority=1000,
             match_fields=[in_port(1), eth_type(0x800), ipv4_dst(0xe4010104)],
             actions=[output(0)]
@@ -638,12 +638,12 @@
             actions=[
                 set_field(vlan_vid(4096 + 201)), output(1)]
         ))
-        self.assertFlowsEqual(self.device_flows['onu2'].items[3], mk_flow_stat(
+        self.assertFlowsEqual(self.device_flows['onu2'].items[6], mk_flow_stat(
             priority=1000,
             match_fields=[in_port(1), eth_type(0x800), ipv4_dst(0xe4010103)],
             actions=[output(0)]
         ))
-        self.assertFlowsEqual(self.device_flows['onu2'].items[4], mk_flow_stat(
+        self.assertFlowsEqual(self.device_flows['onu2'].items[7], mk_flow_stat(
             priority=1000,
             match_fields=[in_port(1), eth_type(0x800), ipv4_dst(0xe4010104)],
             actions=[output(0)]
diff --git a/voltha/adapters/broadcom_onu/broadcom_onu.py b/voltha/adapters/broadcom_onu/broadcom_onu.py
index c787aa3..75e825d 100644
--- a/voltha/adapters/broadcom_onu/broadcom_onu.py
+++ b/voltha/adapters/broadcom_onu/broadcom_onu.py
@@ -646,6 +646,9 @@
                         self.log.error('unsupported-action-type',
                                   action_type=action.type, in_port=_in_port)
 
+                if _type is not None:
+                    continue
+
                 #
                 # All flows created from ONU adapter should be OMCI based
                 #
@@ -1819,4 +1822,3 @@
         for port in ports:
             port_id = 'uni-{}'.format(port.port_no)
             self.update_logical_port(logical_device_id, port_id, OFPPS_LIVE)
-
diff --git a/voltha/adapters/ponsim_olt/ponsim_olt.py b/voltha/adapters/ponsim_olt/ponsim_olt.py
index 64357df..b84209c 100644
--- a/voltha/adapters/ponsim_olt/ponsim_olt.py
+++ b/voltha/adapters/ponsim_olt/ponsim_olt.py
@@ -21,6 +21,7 @@
 from uuid import uuid4
 
 import arrow
+import voltha.core.flow_decomposer as fd
 import grpc
 import json
 import structlog
@@ -36,6 +37,7 @@
 from voltha.adapters.iadapter import OltAdapter
 from voltha.core.logical_device_agent import mac_str_to_tuple
 from voltha.protos import third_party
+from voltha.protos import openflow_13_pb2 as ofp
 from voltha.protos import ponsim_pb2
 from voltha.protos.common_pb2 import OperStatus, ConnectStatus, AdminState
 from voltha.protos.device_pb2 import Port, Device, PmConfig, PmConfigs
@@ -638,9 +640,34 @@
                       frame_len=len(frame))
         self._rcv_frame(frame)
 
+    # VOLTHA's flow decomposition removes the information about which flows
+    # are trap flows where traffic should be forwarded to the controller.
+    # We'll go through the flows and change the output port of flows that we
+    # know to be trap flows to the OF CONTROLLER port.
     def update_flow_table(self, flows):
         stub = ponsim_pb2.PonSimStub(self.get_channel())
         self.log.info('pushing-olt-flow-table')
+        for flow in flows:
+            classifier_info = {}
+            for field in fd.get_ofb_fields(flow):
+                if field.type == fd.ETH_TYPE:
+                    classifier_info['eth_type'] = field.eth_type
+                    self.log.debug('field-type-eth-type',
+                                eth_type=classifier_info['eth_type'])
+                elif field.type == fd.IP_PROTO:
+                    classifier_info['ip_proto'] = field.ip_proto
+                    self.log.debug('field-type-ip-proto',
+                                ip_proto=classifier_info['ip_proto'])
+            if ('ip_proto' in classifier_info and (
+                classifier_info['ip_proto'] == 17 or
+                classifier_info['ip_proto'] == 2)) or (
+                      'eth_type' in classifier_info and
+                      classifier_info['eth_type'] == 0x888e):
+                for action in fd.get_actions(flow):
+                    if action.type == ofp.OFPAT_OUTPUT:
+                        action.output.port = ofp.OFPP_CONTROLLER
+            self.log.info('out_port', out_port=fd.get_out_port(flow))
+
         stub.UpdateFlowTable(FlowTable(
             port=0,
             flows=flows
diff --git a/voltha/core/flow_decomposer.py b/voltha/core/flow_decomposer.py
index f4d978e..1d918f7 100644
--- a/voltha/core/flow_decomposer.py
+++ b/voltha/core/flow_decomposer.py
@@ -592,6 +592,28 @@
                         pop_vlan(),
                         output(egress_hop.ingress_port.port_no)]
                 ))
+
+            if in_port_no is not None:
+                # Only handle the non-wildcard case on the ONU
+                onu_fl_lst, _ = device_rules.setdefault(
+                    ingress_hop.device.id, ([], []))
+
+                onu_fl_lst.append(mk_flow_stat(        # Onu upstream flow
+                    priority=flow.priority + 1000,
+                    cookie=flow.cookie,
+                    match_fields= [
+                        in_port(ingress_hop.ingress_port.port_no),
+                        vlan_vid(0)
+                    ] + [
+                        field for field in get_ofb_fields(flow)
+                        if field.type not in (IN_PORT, VLAN_VID)
+                    ],
+                    actions=[
+                        push_vlan(0x8100),
+                        set_field(vlan_vid(ofp.OFPVID_PRESENT | input_port)),
+                        output(ingress_hop.egress_port.port_no)
+                    ]
+                ))
         else:
             # NOT A CONTROLLER-BOUND FLOW
             if is_upstream():
@@ -784,4 +806,3 @@
 
     def flow_delete(self, mod):
         raise NotImplementedError('derived class must provide')
-