[CORD-381] Implement L3McastToL2 tests

- Be careful with 224.1.1.1

Change-Id: I5c561f62956c669d946bf16cc6d09632b89d9688
diff --git a/README.md b/README.md
index 0418cfc..860f71c 100755
--- a/README.md
+++ b/README.md
@@ -90,29 +90,39 @@
 
 The following tests are implemented and these are their results.
 
-Test Results     | i12_1.7 | 2.0 GA | 3.0 EA0
--------          | ------- | ------ | -------
-/0Ucast          | X       | ok     | ok
-/24UnicastTagged | ok      | ok     | ok
-/32UnicastTagged | ok      | ok     | ok
-/24ECMPL3        | ok      | ok     | ok
-/32ECMPL3        | ok      | ok     | ok
-/24ECMPVPN       | ok      | ok     | ok
-/32ECMPVPN       | ok      | ok     | ok
-/32VPN           | ok      | ok     | ok
-/24VPN           | ok      | ok     | ok
-EcmpGroupMod     | X       | X      | ok
-PacketInArp      | ok      | ok     | ok
-MTU1500          | ok      | ok     | ok
-MplsTermination  | ok      | ok     | ok
-MplsFwd          | X       | ok     | ok
-L2FloodQinQ      | ok      | ok     | ok
-L2UnicastTagged  | ok      | ok     | ok
-L3McastToL3      | ok      | X      | ?
-L3McastToL2      | ok      | X      | X
-FloodGroupMod    | X       | X      | ok
-PacketInUDP      | ok      | ok     | ok
-Unfiltered       | X       | ok     | X
-Untagged         | ok      | n/a    | ok
+Test Results       | i12_1.7 | 2.0 GA | 3.0 EA0
+-------            | ------- | ------ | -------
+/0Ucast            | X       | ok     | ok
+/24UnicastTagged   | ok      | ok     | ok
+/32UnicastTagged   | ok      | ok     | ok
+/24ECMPL3          | ok      | ok     | ok
+/32ECMPL3          | ok      | ok     | ok
+/24ECMPVPN         | ok      | ok     | ok
+/32ECMPVPN         | ok      | ok     | ok
+/32VPN             | ok      | ok     | ok
+/24VPN             | ok      | ok     | ok
+EcmpGroupMod       | X       | X      | ok
+PacketInArp        | ok      | ok     | ok
+MTU1500            | ok      | ok     | ok
+MplsTermination    | ok      | ok     | ok
+MplsFwd            | X       | ok     | ok
+L2FloodQinQ        | ok      | ok     | ok
+L2UnicastTagged    | ok      | ok     | ok
+L3McastToL3        | ok      | X      | ?
+L3McastToL2_1*     | ok      | ?      | ?
+L3McastToL2_2**    | ok      | ?      | ?
+L3McastToL2_3***   | ok      | ?      | ?
+L3McastToL2_4****  | ok      | ?      | ?
+L3McastToL2_5***** | ok      | ?      | ok
+FloodGroupMod      | X       | X      | ok
+PacketInUDP        | ok      | ok     | ok
+Unfiltered         | X       | ok     | X
+Untagged           | ok      | n/a    | ok
 
-n/a means test is not available for that version of the pipeline.
+*       Untag -> Untag (4094 as internal vlan)
+**      Untag -> Tag
+***     Tag   -> Untag
+****    Tag   -> Tag
+*****   Tag   -> Tag (Translated)
+
+n/a means test is not available for that version of the pipeline.
\ No newline at end of file
diff --git a/accton/accton_util.py b/accton/accton_util.py
index 22aea23..78d247b 100755
--- a/accton/accton_util.py
+++ b/accton/accton_util.py
@@ -569,6 +569,36 @@
     logging.info("Add allow all vlan on port %d " %(in_port))
     ctrl.message_send(request)
 
+def add_one_vlan_table_flow_translation(ctrl, of_port, vlan_id=1, new_vlan_id=-1, vrf=0, flag=VLAN_TABLE_FLAG_ONLY_BOTH, send_barrier=False):
+	# Insert a VLAN translation flow
+	# in VLAN flows table    
+	# table 10: vlan
+    # goto to table 20
+    if (flag == VLAN_TABLE_FLAG_ONLY_TAG) or (flag == VLAN_TABLE_FLAG_ONLY_BOTH) or (flag == 4):
+        match = ofp.match()
+        match.oxm_list.append(ofp.oxm.in_port(of_port))
+        match.oxm_list.append(ofp.oxm.vlan_vid_masked(0x1000+vlan_id,0x1fff))
+
+        actions=[]
+        if vrf!=0:
+            actions.append(ofp.action.set_field(ofp.oxm.exp2ByteValue(exp_type=1, value=vrf)))
+        if new_vlan_id != -1:
+            actions.append(ofp.action.set_field(ofp.oxm.vlan_vid(new_vlan_id)))
+
+        request = ofp.message.flow_add(
+            table_id=10,
+            cookie=42,
+            match=match,
+            instructions=[
+                ofp.instruction.apply_actions(
+                     actions=actions
+                ),
+                ofp.instruction.goto_table(20)
+            ],
+            priority=0)
+        logging.info("Add vlan %d tagged packets on port %d and go to table 20" %( vlan_id, of_port))
+        ctrl.message_send(request)
+
 def add_one_vlan_table_flow(ctrl, of_port, vlan_id=1, vrf=0, flag=VLAN_TABLE_FLAG_ONLY_BOTH, send_barrier=False):
     # table 10: vlan
     # goto to table 20
@@ -580,7 +610,7 @@
         actions=[]
         if vrf!=0:
             actions.append(ofp.action.set_field(ofp.oxm.exp2ByteValue(exp_type=1, value=vrf)))
-
+            
         #actions.append(ofp.action.set_field(ofp.oxm.vlan_vid(value=vlan_id)))
 
         request = ofp.message.flow_add(
diff --git a/ofdpa/flows.py b/ofdpa/flows.py
index 7d2a6c2..5844090 100755
--- a/ofdpa/flows.py
+++ b/ofdpa/flows.py
@@ -984,81 +984,312 @@
         delete_groups(self.controller, Groups)
 
 
-class L3McastToL2(base_tests.SimpleDataPlane):
+class L3McastToL2UntagToUntag( base_tests.SimpleDataPlane ):
     """
-    Mcast routing to L2
+    Mcast routing, in this test case the traffic is untagged.
+    4094 is used as internal vlan_id. The packet goes out
+    untagged.
     """
+    def runTest( self ):
+        Groups = Queue.LifoQueue( )
+        try:
+            if len( config[ "port_map" ] ) < 2:
+                logging.info( "Port count less than 2, can't run this case" )
+                assert (False)
+                return
 
-    def runTest(self):
-        """
-        port1 (vlan 300)-> All Ports (vlan 300)
-        """
-        if len(config["port_map"]) < 3:
-            logging.info("Port count less than 3, can't run this case")
-            assert (False)
-            return
-        Groups = Queue.LifoQueue()
-        vlan_id = 300
-        intf_src_mac = [0x00, 0x00, 0x00, 0xcc, 0xcc, 0xcc]
-        intf_src_mac_str = ':'.join(['%02X' % x for x in intf_src_mac])
-        dst_mac = [0x01, 0x00, 0x5e, 0x01, 0x01, 0x01]
-        dst_mac_str = ':'.join(['%02X' % x for x in dst_mac])
-        port1_mac = [0x00, 0x11, 0x11, 0x11, 0x11, 0x11]
-        port1_mac_str = ':'.join(['%02X' % x for x in port1_mac])
-        src_ip = 0xc0a80101
-        src_ip_str = "192.168.1.1"
-        dst_ip = 0xe0010101
-        dst_ip_str = "224.1.1.1"
+            ports      = config[ "port_map" ].keys( )
+            dst_ip_str = "224.0.0.1"
+            (port_to_in_vlan, port_to_out_vlan, port_to_src_mac_str, port_to_dst_mac_str, port_to_src_ip_str, Groups) = fill_mcast_pipeline_L3toL2(
+                self.controller,
+                logging,
+                ports,
+                is_ingress_tagged   = False,
+                is_egress_tagged    = False,
+                is_vlan_translated  = False,
+                is_max_vlan         = True
+                )
 
-        port1 = config["port_map"].keys()[0]
-        port2 = config["port_map"].keys()[1]
+            for in_port in ports:
 
-        switch_mac = [0x01, 0x00, 0x5e, 0x00, 0x00, 0x00]
+                parsed_pkt = simple_udp_packet(
+                    pktlen  = 96,
+                    eth_dst = port_to_dst_mac_str[in_port],
+                    eth_src = port_to_src_mac_str[in_port],
+                    ip_ttl  = 64,
+                    ip_src  = port_to_src_ip_str[in_port],
+                    ip_dst  = dst_ip_str
+                    )
+                pkt = str( parsed_pkt )
+                self.dataplane.send( in_port, pkt )
 
-        # add l2 interface group
-        l2_intf_group_list = []
-        for port in config["port_map"].keys():
-            add_one_vlan_table_flow(self.controller, port, vlan_id,
-                                    flag=VLAN_TABLE_FLAG_ONLY_TAG)
-            if port == port2:
-                continue
-            l2_intf_gid, msg = add_one_l2_interface_group(self.controller, port,
-                                                          vlan_id=vlan_id,
-                                                          is_tagged=True,
-                                                          send_barrier=False)
-            l2_intf_group_list.append(l2_intf_gid)
-            Groups.put(l2_intf_gid)
+                for out_port in ports:
 
-        # add termination flow
-        add_termination_flow(self.controller, port1, 0x0800, switch_mac,
-                             vlan_id)
+                    parsed_pkt = simple_udp_packet(
+                        pktlen  = 96,
+                        eth_dst = port_to_dst_mac_str[in_port],
+                        eth_src = port_to_src_mac_str[in_port],
+                        ip_ttl  = 64,
+                        ip_src  = port_to_src_ip_str[in_port],
+                        ip_dst  = dst_ip_str
+                        )
+                    pkt = str( parsed_pkt )
+                    if out_port == in_port:
+                        verify_no_packet( self, pkt, in_port )
+                        continue
+                    verify_packet( self, pkt, out_port )
+                    verify_no_other_packets( self )
+        finally:
+            delete_all_flows( self.controller )
+            delete_groups( self.controller, Groups )
+            delete_all_groups( self.controller )
 
-        # add l3 interface group
-        mcat_group_msg = add_l3_mcast_group(self.controller, vlan_id, 2,
-                                            l2_intf_group_list)
-        add_mcast4_routing_flow(self.controller, vlan_id, src_ip, 0, dst_ip,
-                                mcat_group_msg.group_id)
-        Groups._put(mcat_group_msg.group_id)
+class L3McastToL2UntagToTag( base_tests.SimpleDataPlane ):
+    """
+    Mcast routing, in this test case the traffic is untagged.
+    300 is used as vlan_id. The packet goes out
+    tagged.
+    """
+    def runTest( self ):
+        Groups = Queue.LifoQueue( )
+        try:
+            if len( config[ "port_map" ] ) < 2:
+                logging.info( "Port count less than 2, can't run this case" )
+                assert (False)
+                return
+            ports      = config[ "port_map" ].keys( )
+            dst_ip_str = "224.0.0.1"
+            (port_to_in_vlan, port_to_out_vlan, port_to_src_mac_str, port_to_dst_mac_str, port_to_src_ip_str, Groups) = fill_mcast_pipeline_L3toL2(
+                self.controller,
+                logging,
+                ports,
+                is_ingress_tagged   = False,
+                is_egress_tagged    = True,
+                is_vlan_translated  = False,
+                is_max_vlan         = False
+                )
 
-        parsed_pkt = simple_udp_packet(pktlen=100,
-                                       dl_vlan_enable=True,
-                                       vlan_vid=vlan_id,
-                                       eth_dst=dst_mac_str,
-                                       eth_src=port1_mac_str,
-                                       ip_ttl=64,
-                                       ip_src=src_ip_str,
-                                       ip_dst=dst_ip_str)
-        pkt = str(parsed_pkt)
-        self.dataplane.send(port1, pkt)
-        for port in config["port_map"].keys():
-            if port == port2 or port == port1:
-                verify_no_packet(self, pkt, port)
-                continue
-            verify_packet(self, pkt, port)
-        verify_no_other_packets(self)
-        delete_all_flows(self.controller)
-        delete_groups(self.controller, Groups)
+            for in_port in ports:
 
+                parsed_pkt = simple_udp_packet(
+                    pktlen  = 96,
+                    eth_dst = port_to_dst_mac_str[in_port],
+                    eth_src = port_to_src_mac_str[in_port],
+                    ip_ttl  = 64,
+                    ip_src  = port_to_src_ip_str[in_port],
+                    ip_dst  = dst_ip_str
+                    )
+                pkt = str( parsed_pkt )
+                self.dataplane.send( in_port, pkt )
+
+                for out_port in ports:
+
+                    parsed_pkt = simple_udp_packet(
+                        pktlen          = 100,
+                        dl_vlan_enable  = True,
+                        vlan_vid        = port_to_out_vlan[in_port],
+                        eth_dst         = port_to_dst_mac_str[in_port],
+                        eth_src         = port_to_src_mac_str[in_port],
+                        ip_ttl          = 64,
+                        ip_src          = port_to_src_ip_str[in_port],
+                        ip_dst          = dst_ip_str
+                        )
+                    pkt = str( parsed_pkt )
+                    if out_port == in_port:
+                        verify_no_packet( self, pkt, in_port )
+                        continue
+                    verify_packet( self, pkt, out_port )
+                    verify_no_other_packets( self )
+        finally:
+            delete_all_flows( self.controller )
+            delete_groups( self.controller, Groups )
+            delete_all_groups( self.controller )
+
+class L3McastToL2TagToUntag( base_tests.SimpleDataPlane ):
+    """
+    Mcast routing, in this test case the traffic is tagged.
+    300 is used as vlan_id. The packet goes out
+    untagged.
+    """
+    def runTest( self ):
+        Groups = Queue.LifoQueue( )
+        try:
+            if len( config[ "port_map" ] ) < 2:
+                logging.info( "Port count less than 2, can't run this case" )
+                assert (False)
+                return
+            ports      = config[ "port_map" ].keys( )
+            dst_ip_str = "224.0.0.1"
+            (port_to_in_vlan, port_to_out_vlan, port_to_src_mac_str, port_to_dst_mac_str, port_to_src_ip_str, Groups) = fill_mcast_pipeline_L3toL2(
+                self.controller,
+                logging,
+                ports,
+                is_ingress_tagged   = True,
+                is_egress_tagged    = False,
+                is_vlan_translated  = False,
+                is_max_vlan         = False
+                )
+
+            for in_port in ports:
+
+                parsed_pkt = simple_udp_packet(
+                    pktlen         = 100,
+                    dl_vlan_enable = True,
+                    vlan_vid       = port_to_in_vlan[in_port],
+                    eth_dst        = port_to_dst_mac_str[in_port],
+                    eth_src        = port_to_src_mac_str[in_port],
+                    ip_ttl         = 64,
+                    ip_src         = port_to_src_ip_str[in_port],
+                    ip_dst         = dst_ip_str
+                    )
+                pkt = str( parsed_pkt )
+                self.dataplane.send( in_port, pkt )
+
+                for out_port in ports:
+
+                    parsed_pkt = simple_udp_packet(
+                        pktlen          = 96,
+                        eth_dst         = port_to_dst_mac_str[in_port],
+                        eth_src         = port_to_src_mac_str[in_port],
+                        ip_ttl          = 64,
+                        ip_src          = port_to_src_ip_str[in_port],
+                        ip_dst          = dst_ip_str
+                        )
+                    pkt = str( parsed_pkt )
+                    if out_port == in_port:
+                        verify_no_packet( self, pkt, in_port )
+                        continue
+                    verify_packet( self, pkt, out_port )
+                    verify_no_other_packets( self )
+        finally:
+            delete_all_flows( self.controller )
+            delete_groups( self.controller, Groups )
+            delete_all_groups( self.controller )
+
+class L3McastToL2TagToTag( base_tests.SimpleDataPlane ):
+    """
+    Mcast routing, in this test case the traffic is tagged.
+    300 is used as vlan_id. The packet goes out tagged.
+    """
+    def runTest( self ):
+        Groups = Queue.LifoQueue( )
+        try:
+            if len( config[ "port_map" ] ) < 2:
+                logging.info( "Port count less than 2, can't run this case" )
+                assert (False)
+                return
+            ports      = config[ "port_map" ].keys( )
+            dst_ip_str = "224.0.0.1"
+            (port_to_in_vlan, port_to_out_vlan, port_to_src_mac_str, port_to_dst_mac_str, port_to_src_ip_str, Groups) = fill_mcast_pipeline_L3toL2(
+                self.controller,
+                logging,
+                ports,
+                is_ingress_tagged   = True,
+                is_egress_tagged    = True,
+                is_vlan_translated  = False,
+                is_max_vlan         = False
+                )
+
+            for in_port in ports:
+
+                parsed_pkt = simple_udp_packet(
+                    pktlen         = 100,
+                    dl_vlan_enable = True,
+                    vlan_vid       = port_to_in_vlan[in_port],
+                    eth_dst        = port_to_dst_mac_str[in_port],
+                    eth_src        = port_to_src_mac_str[in_port],
+                    ip_ttl         = 64,
+                    ip_src         = port_to_src_ip_str[in_port],
+                    ip_dst         = dst_ip_str
+                    )
+                pkt = str( parsed_pkt )
+                self.dataplane.send( in_port, pkt )
+
+                for out_port in ports:
+
+                    parsed_pkt = simple_udp_packet(
+                        pktlen         = 100,
+                        dl_vlan_enable = True,
+                        vlan_vid       = port_to_in_vlan[in_port],
+                        eth_dst        = port_to_dst_mac_str[in_port],
+                        eth_src        = port_to_src_mac_str[in_port],
+                        ip_ttl         = 64,
+                        ip_src         = port_to_src_ip_str[in_port],
+                        ip_dst         = dst_ip_str
+                        )
+                    pkt = str( parsed_pkt )
+                    if out_port == in_port:
+                        verify_no_packet( self, pkt, in_port )
+                        continue
+                    verify_packet( self, pkt, out_port )
+                    verify_no_other_packets( self )
+        finally:
+            delete_all_flows( self.controller )
+            delete_groups( self.controller, Groups )
+            delete_all_groups( self.controller )
+
+class L3McastToL2TagToTagTranslated( base_tests.SimpleDataPlane ):
+    """
+    Mcast routing, in this test case the traffic is tagged.
+    port+1 is used as ingress vlan_id. The packet goes out
+    tagged. 4094-port is used as egress vlan_id
+    """
+    def runTest( self ):
+        Groups = Queue.LifoQueue( )
+        try:
+            if len( config[ "port_map" ] ) < 2:
+                logging.info( "Port count less than 2, can't run this case" )
+                assert (False)
+                return
+            ports      = config[ "port_map" ].keys( )
+            dst_ip_str = "224.0.0.1"
+            (port_to_in_vlan, port_to_out_vlan, port_to_src_mac_str, port_to_dst_mac_str, port_to_src_ip_str, Groups) = fill_mcast_pipeline_L3toL2(
+                self.controller,
+                logging,
+                ports,
+                is_ingress_tagged   = True,
+                is_egress_tagged    = True,
+                is_vlan_translated  = True,
+                is_max_vlan         = False
+                )
+
+            for in_port in ports:
+
+                parsed_pkt = simple_udp_packet(
+                    pktlen         = 100,
+                    dl_vlan_enable = True,
+                    vlan_vid       = port_to_in_vlan[in_port],
+                    eth_dst        = port_to_dst_mac_str[in_port],
+                    eth_src        = port_to_src_mac_str[in_port],
+                    ip_ttl         = 64,
+                    ip_src         = port_to_src_ip_str[in_port],
+                    ip_dst         = dst_ip_str
+                    )
+                pkt = str( parsed_pkt )
+                self.dataplane.send( in_port, pkt )
+
+                for out_port in ports:
+
+                    parsed_pkt = simple_udp_packet(
+                        pktlen         = 100,
+                        dl_vlan_enable = True,
+                        vlan_vid       = port_to_out_vlan[in_port],
+                        eth_dst        = port_to_dst_mac_str[in_port],
+                        eth_src        = port_to_src_mac_str[in_port],
+                        ip_ttl         = 64,
+                        ip_src         = port_to_src_ip_str[in_port],
+                        ip_dst         = dst_ip_str
+                        )
+                    pkt = str( parsed_pkt )
+                    if out_port == in_port:
+                        verify_no_packet( self, pkt, in_port )
+                        continue
+                    verify_packet( self, pkt, out_port )
+                    verify_no_other_packets( self )
+        finally:
+            delete_all_flows( self.controller )
+            delete_groups( self.controller, Groups )
+            delete_all_groups( self.controller )
 
 class L3McastToL3( base_tests.SimpleDataPlane ):
     """
diff --git a/ofdpa/utils.py b/ofdpa/utils.py
index 5bbce7f..0b8ab77 100644
--- a/ofdpa/utils.py
+++ b/ofdpa/utils.py
@@ -3,6 +3,126 @@
 from oftest.testutils import *
 from accton_util import *
 
+def fill_mcast_pipeline_L3toL2(
+	controller,
+	logging,
+	ports,
+	is_ingress_tagged,
+	is_egress_tagged,
+	is_vlan_translated,
+	is_max_vlan
+	):
+	"""
+	This method, according to the scenario, fills properly
+	the pipeline. The method generates using ports data the
+	necessary information to fill the multicast pipeline and
+	fills properly the pipeline which consists in this scenario:
+
+	i) to create l2 interface groups;
+	ii) to create l3 multicast groups;
+	iii) to add multicast flows;
+	iv) to add termination; flows;
+	v) to add vlan flows
+
+	Scenarios:
+	1) ingress untagged, egress untagged
+	2) ingress untagged, egress tagged
+	3) ingress tagged, egress untagged
+	4) ingress tagged, egress tagged, no translation
+	5) ingress tagged, egress tagged, translation
+	"""
+
+	MAX_INTERNAL_VLAN           = 4094
+	# Used for no translation
+	FIXED_VLAN                  = 300
+	Groups                      = Queue.LifoQueue( )
+	L2_Groups                   = []
+	port_to_in_vlan             = {}
+	port_to_out_vlan            = {}
+	port_to_src_mac             = {}
+	port_to_src_mac_str         = {}
+	port_to_dst_mac             = {}
+	port_to_dst_mac_str         = {}
+	port_to_src_ip              = {}
+	port_to_src_ip_str          = {}
+	src_ip_0                    = 0xc0a80100
+	src_ip_0_str                = "192.168.1.%s"
+	dst_ip                      = 0xe0000001
+	switch_mac                  = [ 0x01, 0x00, 0x5e, 0x00, 0x00, 0x00 ]
+
+	for port in ports:
+		in_vlan_id  = port + 1
+		out_vlan_id = MAX_INTERNAL_VLAN - port
+		if is_max_vlan and not is_vlan_translated:
+			in_vlan_id  = MAX_INTERNAL_VLAN
+			out_vlan_id = MAX_INTERNAL_VLAN
+		elif not is_max_vlan and not is_vlan_translated:
+			in_vlan_id  = FIXED_VLAN
+			out_vlan_id = FIXED_VLAN
+		src_mac                     = [ 0x00, 0x11, 0x11, 0x11, 0x11, port ]
+		src_mac_str                 = ':'.join( [ '%02X' % x for x in src_mac ] )
+		dst_mac                     = [ 0x01, 0x00, 0x5e, 0x01, 0x01, port ]
+		dst_mac_str                 = ':'.join( [ '%02X' % x for x in dst_mac ] )
+		src_ip                      = src_ip_0 + port
+		src_ip_str                  = src_ip_0_str % port
+		port_to_in_vlan[port]       = in_vlan_id
+		port_to_out_vlan[port]      = out_vlan_id
+		port_to_src_mac[port]       = src_mac
+		port_to_src_mac_str[port]   = src_mac_str
+		port_to_dst_mac[port]       = dst_mac
+		port_to_dst_mac_str[port]   = dst_mac_str
+		port_to_src_ip[port]        = src_ip
+		port_to_src_ip_str[port]    = src_ip_str
+
+	for in_port in ports:
+
+		L2_Groups = []
+		# add vlan flows table
+		add_one_vlan_table_flow( controller, in_port, port_to_in_vlan[in_port], flag=VLAN_TABLE_FLAG_ONLY_TAG )
+		if not is_ingress_tagged:
+			add_one_vlan_table_flow( controller, in_port, port_to_in_vlan[in_port], flag=VLAN_TABLE_FLAG_ONLY_UNTAG )
+		elif is_vlan_translated:
+			add_one_vlan_table_flow_translation( controller, in_port, port_to_in_vlan[in_port], port_to_out_vlan[in_port], flag=VLAN_TABLE_FLAG_ONLY_TAG)
+		# add termination flow
+		if not is_vlan_translated:
+			add_termination_flow( controller, in_port, 0x0800, switch_mac, port_to_in_vlan[in_port] )
+		else:
+			add_termination_flow( controller, in_port, 0x0800, switch_mac, port_to_out_vlan[in_port] )
+
+		for out_port in ports:
+			if out_port == in_port:
+				continue
+			# add l2 interface group, vlan_id equals for each port and must coincide with mcast_group vlan_id
+			if not is_vlan_translated:
+				l2gid, msg = add_one_l2_interface_group( controller, out_port, vlan_id=port_to_in_vlan[in_port],
+					is_tagged=is_egress_tagged, send_barrier=True )
+			else:
+				l2gid, msg = add_one_l2_interface_group( controller, out_port, vlan_id=port_to_out_vlan[in_port],
+					is_tagged=is_egress_tagged, send_barrier=True )
+			Groups._put( l2gid )
+			L2_Groups.append( l2gid )
+
+		# add l3 mcast group
+		if not is_vlan_translated:
+			mcat_group_msg = add_l3_mcast_group( controller, port_to_in_vlan[in_port], in_port, L2_Groups )
+		else:
+			mcat_group_msg = add_l3_mcast_group( controller, port_to_out_vlan[in_port], in_port, L2_Groups )
+		Groups._put( mcat_group_msg.group_id )
+		# add mcast routing flow
+		if not is_vlan_translated:
+			add_mcast4_routing_flow( controller, port_to_in_vlan[in_port], port_to_src_ip[in_port], 0, dst_ip, mcat_group_msg.group_id )
+		else:
+			add_mcast4_routing_flow( controller, port_to_out_vlan[in_port], port_to_src_ip[in_port], 0, dst_ip, mcat_group_msg.group_id )
+
+	return (
+		port_to_in_vlan,
+		port_to_out_vlan,
+		port_to_src_mac_str,
+		port_to_dst_mac_str,
+		port_to_src_ip_str,
+		Groups
+		)
+
 def fill_mcast_pipeline_L3toL3(
     controller,
     logging,
@@ -99,4 +219,4 @@
         port_to_src_ip_str,
         port_to_intf_src_mac_str,
         Groups
-        )
\ No newline at end of file
+        )