Starting new repository for ofdpa testing
diff --git a/ofdpa/accton_util.py b/ofdpa/accton_util.py
new file mode 100755
index 0000000..e6b2adc
--- /dev/null
+++ b/ofdpa/accton_util.py
@@ -0,0 +1,1229 @@
+import logging
+
+from oftest import config
+import oftest.base_tests as base_tests
+import ofp
+import time
+from oftest.testutils import *
+
+from ncclient import manager
+import ncclient
+
+OFDPA_GROUP_TYPE_SHIFT=28
+OFDPA_VLAN_ID_SHIFT   =16
+OFDPA_TUNNEL_ID_SHIFT =12
+OFDPA_TUNNEL_SUBTYPE_SHIFT=10
+
+#VLAN_TABLE_FLAGS
+VLAN_TABLE_FLAG_ONLY_UNTAG=1
+VLAN_TABLE_FLAG_ONLY_TAG  =2
+VLAN_TABLE_FLAG_ONLY_BOTH =3
+
+PORT_FLOW_TABLE=0
+VLAN_FLOW_TABLE=10
+TERMINATION_FLOW_TABLE=20
+UCAST_ROUTING_FLOW_TABLE=30
+MCAST_ROUTING_FLOW_TABLE=40
+BRIDGE_FLOW_TABLE=50
+ACL_FLOW_TABLE=60
+
+def convertIP4toStr(ip_addr):
+    a=(ip_addr&0xff000000)>>24
+    b=(ip_addr&0x00ff0000)>>16
+    c=(ip_addr&0x0000ff00)>>8
+    d=(ip_addr&0x000000ff)
+    return str(a)+"."+str(b)+"."+str(c)+"."+str(d)
+
+def convertMACtoStr(mac):
+    if not isinstance(mac, list):
+        assert(0)
+
+    return ':'.join(['%02X' % x for x in mac])
+
+def getSwitchCpuMACFromDPID(dpid):
+    str_datapath_id_f= "{:016x}".format(dpid)
+    str_datapath_id=':'.join([str_datapath_id_f[i:i+2] for i in range(0, len(str_datapath_id_f), 2)])
+    switch_cpu_mac_str=str_datapath_id[6:]
+    switch_cpu_mac = switch_cpu_mac_str.split(":")
+    switch_cpu_mac=[int(switch_cpu_mac[i],16) for i in range(0, len(switch_cpu_mac))]
+
+    return switch_cpu_mac_str, switch_cpu_mac
+        
+def DumpGroup(stats, verify_group_stats, always_show=True):
+    if(len(stats) > len(verify_group_stats)):
+        min_len = len(verify_group_stats)
+        print "Stats Len is not the same, stats>verify_group_stats"
+    if(len(stats)< len(verify_group_stats)):
+        min_len = len(stats)    
+        print "Stats Len is not the same, stats<verify_group_stats"
+    else:   
+        min_len = len(stats)
+
+    print "\r\n"
+    for i in range(min_len):
+        gs = stats[i]
+        gv = verify_group_stats[i]        
+        print "FromSwtich:(GID=%lx, TYPE=%lx)\r\nVerify    :(GID=%lx, TYPE=%lx)"%(gs.group_id, gs.group_type, gv.group_id, gv.group_type)
+        if(len(gs.buckets) != len(gv.buckets)):
+            print "buckets len is not the same gs %lx, gv %lx",(len(gs.buckets), len(gv.buckets))
+
+        for j in range(len(gs.buckets)):
+           b1=gs.buckets[j]
+           b2=gv.buckets[j]           
+           if(len(b1.actions) != len(b2.actions)):
+               print "action len is not the same"
+
+           for k in range(len(b1.actions)):
+               a1=b1.actions[k]
+               a2=b2.actions[k]
+               if(always_show == True):
+                   print "a1:"+a1.show()
+                   print "a2:"+a2.show()               
+
+def AssertGroup(self, stats, verify_group_stats):
+    self.assertTrue(len(stats) ==len(verify_group_stats), "stats len is not the same")
+
+    for i in range(len(stats)):
+        gs = stats[i]
+        gv = verify_group_stats[i]        
+        self.assertTrue(len(gs.buckets) == len(gv.buckets), "buckets len is not the same")
+
+        for j in range(len(gs.buckets)):
+           b1=gs.buckets[j]
+           b2=gv.buckets[j]           
+           self.assertTrue(len(b1.actions) == len(b2.actions), "action len is not the same")
+
+           for k in range(len(b1.actions)):
+               a1=b1.actions[k]
+               a2=b2.actions[k]
+               self.assertEquals(a1, a2, "action is not the same")
+    
+def encode_l2_interface_group_id(vlan, id):
+    return id + (vlan << OFDPA_VLAN_ID_SHIFT)
+
+def encode_l2_rewrite_group_id(id):
+    return id + (1 << OFDPA_GROUP_TYPE_SHIFT)
+
+def encode_l3_unicast_group_id(id):
+    return id + (2 << OFDPA_GROUP_TYPE_SHIFT)
+
+def encode_l2_mcast_group_id(vlan, id):
+    return id + (vlan << OFDPA_VLAN_ID_SHIFT) + (3 << OFDPA_GROUP_TYPE_SHIFT)
+
+def encode_l2_flood_group_id(vlan, id):
+    return id + (vlan << OFDPA_VLAN_ID_SHIFT) + (4 << OFDPA_GROUP_TYPE_SHIFT)
+    
+def encode_l3_interface_group_id(id):
+    return id + (5 << OFDPA_GROUP_TYPE_SHIFT)
+
+def encode_l3_mcast_group_id(vlan, id):
+    return id + (vlan << OFDPA_VLAN_ID_SHIFT)+(6 << OFDPA_GROUP_TYPE_SHIFT)
+
+def encode_l3_ecmp_group_id(id):
+    return id + (7 << OFDPA_GROUP_TYPE_SHIFT)
+
+def encode_l2_overlay_group_id(tunnel_id, subtype, index):
+    tunnel_id=tunnel_id&0xffff #16 bits
+    subtype = subtype&3        #2 bits
+    index = index & 0x3f       #10 bits
+    return index + (tunnel_id << OFDPA_TUNNEL_ID_SHIFT)+ (subtype<<OFDPA_TUNNEL_SUBTYPE_SHIFT)+(8 << OFDPA_GROUP_TYPE_SHIFT)
+
+def add_l2_interface_grouop(ctrl, ports, vlan_id=1, is_tagged=False, send_barrier=False):
+    # group table
+    # set up untag groups for each port
+    group_id_list=[]
+    msgs=[]
+    for of_port in ports:
+        # do stuff
+        group_id = encode_l2_interface_group_id(vlan_id, of_port)
+        group_id_list.append(group_id)
+        if is_tagged:
+            actions = [
+                ofp.action.output(of_port),
+            ]        
+        else:
+            actions = [
+                ofp.action.pop_vlan(),
+                ofp.action.output(of_port),
+            ]
+
+        buckets = [
+            ofp.bucket(actions=actions),
+        ]
+
+        request = ofp.message.group_add(group_type=ofp.OFPGT_INDIRECT,
+                                        group_id=group_id,
+                                        buckets=buckets
+                                       )
+        ctrl.message_send(request)
+        msgs.append(request)
+
+        if send_barrier:
+            do_barrier(ctrl)
+ 
+    return group_id_list, msgs
+
+def add_one_l2_interface_grouop(ctrl, port, vlan_id=1, is_tagged=False, send_barrier=False):
+    # group table
+    # set up untag groups for each port
+    group_id = encode_l2_interface_group_id(vlan_id, port)
+
+    if is_tagged:
+        actions = [
+            ofp.action.output(port),
+        ]        
+    else:
+        actions = [
+            ofp.action.pop_vlan(),
+            ofp.action.output(port),
+        ]
+
+    buckets = [
+        ofp.bucket(actions=actions),
+    ]
+
+    request = ofp.message.group_add(group_type=ofp.OFPGT_INDIRECT,
+                                    group_id=group_id,
+                                    buckets=buckets
+                                   )
+    ctrl.message_send(request)
+
+    if send_barrier:
+        do_barrier(ctrl)
+ 
+    return group_id, request
+    
+def add_l2_mcast_group(ctrl, ports, vlanid, mcast_grp_index):
+    buckets=[]
+    for of_port in ports:
+        group_id = encode_l2_interface_group_id(vlanid, of_port)
+        action=[ofp.action.group(group_id)]
+        buckets.append(ofp.bucket(actions=action))
+
+    group_id =encode_l2_mcast_group_id(vlanid, mcast_grp_index)
+    request = ofp.message.group_add(group_type=ofp.OFPGT_ALL,
+                                    group_id=group_id,
+                                    buckets=buckets
+                                   )
+    ctrl.message_send(request)
+    return request
+
+def add_l2_flood_group(ctrl, ports, vlanid, id):
+    buckets=[]
+    for of_port in ports:
+        group_id = encode_l2_interface_group_id(vlanid, of_port)
+        action=[ofp.action.group(group_id)]
+        buckets.append(ofp.bucket(actions=action))
+
+    group_id =encode_l2_flood_group_id(vlanid, id)
+    request = ofp.message.group_add(group_type=ofp.OFPGT_ALL,
+                                    group_id=group_id,
+                                    buckets=buckets
+                                   )
+    ctrl.message_send(request)
+    return request
+
+def add_l2_rewrite_group(ctrl, port, vlanid, id, src_mac, dst_mac):
+    group_id = encode_l2_interface_group_id(vlanid, port)
+
+    action=[]
+    if src_mac is not None:
+        action.append(ofp.action.set_field(ofp.oxm.eth_src(src_mac)))
+
+    if dst_mac is not None:
+        action.append(ofp.action.set_field(ofp.oxm.eth_dst(dst_mac)))
+
+    action.append(ofp.action.set_field(ofp.oxm.vlan_vid(vlanid)))
+        
+    action.append(ofp.action.group(group_id))
+    
+    buckets = [ofp.bucket(actions=action)]
+
+    group_id =encode_l2_rewrite_group_id(id)
+    request = ofp.message.group_add(group_type=ofp.OFPGT_INDIRECT,
+                                    group_id=group_id,
+                                    buckets=buckets
+                                   )
+    ctrl.message_send(request)
+    return request
+    
+def add_l3_unicast_group(ctrl, port, vlanid, id, src_mac, dst_mac):
+    group_id = encode_l2_interface_group_id(vlanid, port)
+
+    action=[]
+    if src_mac is not None:
+        action.append(ofp.action.set_field(ofp.oxm.eth_src(src_mac)))
+
+    if dst_mac is not None:
+        action.append(ofp.action.set_field(ofp.oxm.eth_dst(dst_mac)))
+
+    action.append(ofp.action.set_field(ofp.oxm.vlan_vid(vlanid)))
+        
+    action.append(ofp.action.group(group_id))
+    
+    buckets = [ofp.bucket(actions=action)]
+
+    group_id =encode_l3_unicast_group_id(id)
+    request = ofp.message.group_add(group_type=ofp.OFPGT_INDIRECT,
+                                    group_id=group_id,
+                                    buckets=buckets
+                                   )
+    ctrl.message_send(request)
+    return request
+    
+def add_l3_interface_group(ctrl, port, vlanid, id, src_mac):
+    group_id = encode_l2_interface_group_id(vlanid, port)
+
+    action=[]
+    action.append(ofp.action.set_field(ofp.oxm.eth_src(src_mac)))
+    action.append(ofp.action.set_field(ofp.oxm.vlan_vid(vlanid)))       
+    action.append(ofp.action.group(group_id))
+    
+    buckets = [ofp.bucket(actions=action)]
+
+    group_id =encode_l3_interface_group_id(id)
+    request = ofp.message.group_add(group_type=ofp.OFPGT_INDIRECT,
+                                    group_id=group_id,
+                                    buckets=buckets
+                                   )
+    ctrl.message_send(request)
+    return request
+
+def add_l3_ecmp_group(ctrl, id, l3_ucast_groups):
+    buckets=[]
+    for group in l3_ucast_groups:
+        buckets.append(ofp.bucket(actions=[ofp.action.group(group)]))
+
+    group_id =encode_l3_ecmp_group_id(id)
+    request = ofp.message.group_add(group_type=ofp.OFPGT_SELECT,
+                                    group_id=group_id,
+                                    buckets=buckets
+                                   )
+    ctrl.message_send(request)
+    return request
+        
+def add_l3_mcast_group(ctrl, vid,  mcast_group_id, groups_on_buckets):
+    buckets=[]
+    for group in groups_on_buckets:
+        buckets.append(ofp.bucket(actions=[ofp.action.group(group)]))
+    
+    group_id =encode_l3_mcast_group_id(vid, mcast_group_id)
+    request = ofp.message.group_add(group_type=ofp.OFPGT_ALL,
+                                    group_id=group_id,
+                                    buckets=buckets
+                                   )
+    ctrl.message_send(request)
+    return request
+
+def add_l2_overlay_flood_over_unicast_tunnel_group(ctrl, tunnel_id, ports, index):
+    buckets=[]
+    for port in ports:
+        buckets.append(ofp.bucket(actions=[ofp.action.output(port)]))
+
+    group_id=encode_l2_overlay_group_id(tunnel_id, 0, index)
+    request = ofp.message.group_add(group_type=ofp.OFPGT_ALL,
+                                    group_id=group_id,
+                                    buckets=buckets
+                                   )
+    ctrl.message_send(request)
+    return request
+
+def add_l2_overlay_flood_over_mcast_tunnel_group(ctrl, tunnel_id, ports, index):
+    buckets=[]
+    for port in ports:
+        buckets.append(ofp.bucket(actions=[ofp.action.output(port)]))
+
+    group_id=encode_l2_overlay_group_id(tunnel_id, 1, index)
+    request = ofp.message.group_add(group_type=ofp.OFPGT_ALL,
+                                    group_id=group_id,
+                                    buckets=buckets
+                                   )
+    ctrl.message_send(request)
+    return request
+
+def add_l2_overlay_mcast_over_unicast_tunnel_group(ctrl, tunnel_id, ports, index):
+    buckets=[]
+    for port in ports:
+        buckets.append(ofp.bucket(actions=[ofp.action.output(port)]))
+
+    group_id=encode_l2_overlay_group_id(tunnel_id, 2, index)
+    request = ofp.message.group_add(group_type=ofp.OFPGT_ALL,
+                                    group_id=group_id,
+                                    buckets=buckets
+                                   )
+    ctrl.message_send(request)
+    return request
+
+def add_l2_overlay_mcast_over_mcast_tunnel_group(ctrl, tunnel_id, ports, index):
+    buckets=[]
+    for port in ports:
+        buckets.append(ofp.bucket(actions=[ofp.action.output(port)]))
+
+    group_id=encode_l2_overlay_group_id(tunnel_id, 3, index)
+    request = ofp.message.group_add(group_type=ofp.OFPGT_ALL,
+                                    group_id=group_id,
+                                    buckets=buckets
+                                   )
+    ctrl.message_send(request)
+    return request
+	
+def add_port_table_flow(ctrl, is_overlay=True):
+    match = ofp.match()
+
+    if is_overlay == True:
+       match.oxm_list.append(ofp.oxm.in_port(0x10000))
+       NEXT_TABLE=50
+    else:
+       match.oxm_list.append(ofp.oxm.in_port(0))
+       NEXT_TABLE=10       
+
+    request = ofp.message.flow_add(
+		table_id=0,
+		cookie=42,
+		match=match,
+		instructions=[
+		  ofp.instruction.goto_table(NEXT_TABLE)
+		],
+		priority=0)
+    logging.info("Add port table, match port %lx" % 0x10000)
+    ctrl.message_send(request)
+    
+    
+
+def add_vlan_table_flow(ctrl, ports, vlan_id=1, flag=VLAN_TABLE_FLAG_ONLY_BOTH, send_barrier=False):
+    # table 10: vlan
+    # goto to table 20
+    msgs=[]
+    for of_port in ports:
+        if (flag == VLAN_TABLE_FLAG_ONLY_TAG) or (flag == VLAN_TABLE_FLAG_ONLY_BOTH):
+            match = ofp.match()
+            match.oxm_list.append(ofp.oxm.in_port(of_port))
+            match.oxm_list.append(ofp.oxm.vlan_vid(0x1000+vlan_id))
+            request = ofp.message.flow_add(
+                table_id=10,
+                cookie=42,
+                match=match,
+                instructions=[
+                  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)
+            
+        if (flag == VLAN_TABLE_FLAG_ONLY_UNTAG) or (flag == VLAN_TABLE_FLAG_ONLY_BOTH):
+            match = ofp.match()
+            match.oxm_list.append(ofp.oxm.in_port(of_port))
+            match.oxm_list.append(ofp.oxm.vlan_vid_masked(0, 0xfff))
+            request = ofp.message.flow_add(
+                table_id=10,
+                cookie=42,
+                match=match,
+                instructions=[
+                  ofp.instruction.apply_actions(
+                    actions=[
+                      ofp.action.set_field(ofp.oxm.vlan_vid(0x1000+vlan_id))
+                    ]
+                  ),
+                  ofp.instruction.goto_table(20)
+                ],
+                priority=0)
+            logging.info("Add vlan %d untagged packets on port %d and go to table 20" % (vlan_id, of_port))
+            ctrl.message_send(request)
+            msgs.append(request)
+
+    if send_barrier:
+        do_barrier(ctrl)
+
+    return msgs
+
+def add_vlan_table_flow_pvid(ctrl, in_port, match_vid=None, pvid=1, send_barrier=False):
+    """it will tag pack as untagged packet wether it has tagg or not"""
+    match = ofp.match()
+    match.oxm_list.append(ofp.oxm.in_port(in_port))
+    actions=[]
+    if match_vid == None:
+        match.oxm_list.append(ofp.oxm.vlan_vid(0))    
+        actions.append(ofp.action.set_field(ofp.oxm.vlan_vid(0x1000+pvid)))
+        goto_table=20
+    else:
+        match.oxm_list.append(ofp.oxm.vlan_vid_masked(0x1000+match_vid, 0x1fff))
+        actions.append(ofp.action.push_vlan(0x8100))
+        actions.append(ofp.action.set_field(ofp.oxm.vlan_vid(0x1000+pvid)))        
+        goto_table=20
+        
+    request = ofp.message.flow_add(
+        table_id=10,
+        cookie=42,
+        match=match,
+        instructions=[
+             ofp.instruction.apply_actions(actions=actions)
+            ,ofp.instruction.goto_table(goto_table)
+        ],
+        priority=0)
+    logging.info("Add PVID %d on port %d and go to table %ld" %( pvid, in_port, goto_table))
+    ctrl.message_send(request)   
+    
+    if send_barrier:
+        do_barrier(ctrl)
+
+def add_vlan_table_flow_allow_all_vlan(ctrl, in_port, send_barrier=False):
+    """it st flow allow all vlan tag on this port"""
+    match = ofp.match()
+    match.oxm_list.append(ofp.oxm.in_port(in_port))
+    match.oxm_list.append(ofp.oxm.vlan_vid_masked(0x1000, 0x1000))
+    request = ofp.message.flow_add(
+        table_id=10,
+        cookie=42,
+        match=match,
+        instructions=[
+            ofp.instruction.goto_table(20) 
+        ],
+        priority=0)
+    logging.info("Add allow all vlan on port %d " %(in_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
+    if (flag == VLAN_TABLE_FLAG_ONLY_TAG) or (flag == VLAN_TABLE_FLAG_ONLY_BOTH):
+        match = ofp.match()
+        match.oxm_list.append(ofp.oxm.in_port(of_port))
+        match.oxm_list.append(ofp.oxm.vlan_vid(0x1000+vlan_id))
+
+        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(
+            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)
+        
+    if (flag == VLAN_TABLE_FLAG_ONLY_UNTAG) or (flag == VLAN_TABLE_FLAG_ONLY_BOTH):
+        match = ofp.match()
+        match.oxm_list.append(ofp.oxm.in_port(of_port))
+        match.oxm_list.append(ofp.oxm.vlan_vid(0))
+        
+        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(0x1000+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 untagged packets on port %d and go to table 20" % (vlan_id, of_port))
+        ctrl.message_send(request)
+
+    if send_barrier:
+        do_barrier(ctrl)
+
+    return request
+    
+def add_bridge_flow(ctrl, dst_mac, vlanid, group_id, send_barrier=False):
+    match = ofp.match()
+    if dst_mac!=None:
+        match.oxm_list.append(ofp.oxm.eth_dst(dst_mac))
+
+    match.oxm_list.append(ofp.oxm.vlan_vid(0x1000+vlanid))
+
+    request = ofp.message.flow_add(
+            table_id=50,
+            cookie=42,
+            match=match,
+            instructions=[
+                ofp.instruction.write_actions(
+                    actions=[
+                        ofp.action.group(group_id)]),
+                    ofp.instruction.goto_table(60)
+                ],
+            buffer_id=ofp.OFP_NO_BUFFER,
+            priority=1000) 
+
+    logging.info("Inserting Brdige flow vlan %d, mac %s", vlanid, dst_mac)
+    ctrl.message_send(request)
+
+    if send_barrier:
+        do_barrier(ctrl)   
+
+    return request        
+
+def add_overlay_bridge_flow(ctrl, dst_mac, vnid, group_id, is_group=True, send_barrier=False):
+    match = ofp.match()
+    if dst_mac!=None:
+        match.oxm_list.append(ofp.oxm.eth_dst(dst_mac))
+
+    match.oxm_list.append(ofp.oxm.tunnel_id(vnid))
+    if is_group == True:
+        actions=[ofp.action.group(group_id)]
+    else:
+        actions=[ofp.action.output(group_id)]
+
+    request = ofp.message.flow_add(
+            table_id=50,
+            cookie=42,
+            match=match,
+            instructions=[
+                ofp.instruction.write_actions(
+                    actions=actions),
+                    ofp.instruction.goto_table(60)
+                ],
+            buffer_id=ofp.OFP_NO_BUFFER,
+            priority=1000) 
+
+    logging.info("Inserting Brdige flow vnid %d, mac %s", vnid, dst_mac)
+    ctrl.message_send(request)
+
+    if send_barrier:
+        do_barrier(ctrl)   
+
+    return request        
+    
+def add_termination_flow(ctrl, in_port, eth_type, dst_mac, vlanid, goto_table=None, send_barrier=False):
+    match = ofp.match()
+    match.oxm_list.append(ofp.oxm.eth_type(eth_type))
+    if dst_mac[0]&0x01 == 0x01:
+       match.oxm_list.append(ofp.oxm.eth_dst_masked(dst_mac, [0xff, 0xff, 0xff, 0x80, 0x00, 0x00]))
+       goto_table=40
+    else:
+       if in_port!=0:
+           match.oxm_list.append(ofp.oxm.in_port(in_port))
+       match.oxm_list.append(ofp.oxm.eth_dst(dst_mac))
+       match.oxm_list.append(ofp.oxm.vlan_vid(0x1000+vlanid))
+       if goto_table == None:
+           goto_table=30
+
+    request = ofp.message.flow_add(
+            table_id=20,
+            cookie=42,
+            match=match,
+            instructions=[
+                    ofp.instruction.goto_table(goto_table)
+                ],
+            buffer_id=ofp.OFP_NO_BUFFER,
+            priority=1) 
+
+    logging.info("Inserting termination flow inport %d, eth_type %lx, vlan %d, mac %s", in_port, eth_type, vlanid, dst_mac)
+    ctrl.message_send(request)
+
+    if send_barrier:
+        do_barrier(ctrl)   
+
+    return request    
+    
+def add_unicast_routing_flow(ctrl, eth_type, dst_ip, mask, action_group_id, vrf=0, send_barrier=False):
+    match = ofp.match()
+    match.oxm_list.append(ofp.oxm.eth_type(eth_type))
+    if vrf != 0:
+        match.oxm_list.append(ofp.oxm.exp2ByteValue(ofp.oxm.OFDPA_EXP_TYPE_VRF, vrf))
+    
+    if mask!=0:
+        match.oxm_list.append(ofp.oxm.ipv4_dst_masked(dst_ip, mask))
+    else:
+        match.oxm_list.append(ofp.oxm.ipv4_dst(dst_ip))
+
+
+    request = ofp.message.flow_add(
+            table_id=30,
+            cookie=42,
+            match=match,
+            instructions=[
+                    ofp.instruction.write_actions(
+                        actions=[ofp.action.group(action_group_id)]),
+                    ofp.instruction.goto_table(60)
+                ],
+            buffer_id=ofp.OFP_NO_BUFFER,
+            priority=1) 
+
+    logging.info("Inserting unicast routing flow eth_type %lx, dip %ld",eth_type, dst_ip)
+    ctrl.message_send(request)
+
+    if send_barrier:
+        do_barrier(ctrl)   
+
+    return request        
+        
+def add_mcast4_routing_flow(ctrl, vlan_id, src_ip, src_ip_mask, dst_ip, action_group_id, send_barrier=False):
+    match = ofp.match()
+    match.oxm_list.append(ofp.oxm.eth_type(0x0800))
+    match.oxm_list.append(ofp.oxm.vlan_vid(vlan_id))    
+    if src_ip_mask!=0:
+        match.oxm_list.append(ofp.oxm.ipv4_src_masked(src_ip, src_ip_mask))
+    else:
+        match.oxm_list.append(ofp.oxm.ipv4_src(src_ip))
+        
+    match.oxm_list.append(ofp.oxm.ipv4_dst(dst_ip))
+    
+    request = ofp.message.flow_add(
+            table_id=40,
+            cookie=42,
+            match=match,
+            instructions=[
+                    ofp.instruction.write_actions(
+                        actions=[ofp.action.group(action_group_id)]),
+                    ofp.instruction.goto_table(60)
+                ],
+            buffer_id=ofp.OFP_NO_BUFFER,
+            priority=1) 
+
+    logging.info("Inserting mcast routing flow eth_type %lx, dip %lx, sip %lx, sip_mask %lx",0x0800, dst_ip, src_ip, src_ip_mask)
+    ctrl.message_send(request)
+
+    if send_barrier:
+        do_barrier(ctrl)   
+
+    return request            
+
+#dpctl tcp:192.168.1.1:6633 flow-mod table=28,cmd=add,prio=281 eth_type=0x800,ip_dst=100.0.0.1,ip_proto=6,tcp_dst=5000 write:set_field=ip_dst:10.0.0.1,set_field=tcp_dst:2000,group=0x71000001 goto:60
+def add_dnat_flow(ctrl, eth_type, ip_dst, ip_proto, tcp_dst, set_ip_dst, set_tcp_dst, action_group_id):
+    match = ofp.match()
+    match.oxm_list.append(ofp.oxm.eth_type(eth_type))
+    match.oxm_list.append(ofp.oxm.ipv4_dst(ip_dst))
+    match.oxm_list.append(ofp.oxm.ip_proto(ip_proto))
+    match.oxm_list.append(ofp.oxm.tcp_dst(tcp_dst))
+    
+    request = ofp.message.flow_add(
+            table_id=28,
+            cookie=42,
+            match=match,
+            instructions=[
+                    ofp.instruction.write_actions(
+                        actions=[ofp.action.set_field(ofp.oxm.ipv4_dst(set_ip_dst)),
+                                 ofp.action.set_field(ofp.oxm.tcp_dst(set_tcp_dst)),
+                                 ofp.action.group(action_group_id)]),
+                    ofp.instruction.goto_table(60)
+                ],
+            buffer_id=ofp.OFP_NO_BUFFER,
+            priority=1) 
+    logging.info("Inserting DNAT flow eth_type %lx, dip %lx, ip_proto %ld, tcp_dst %ld, SetFeild: Dip %lx, tcp_dst %ld, action_gorup=%lx",eth_type, ip_dst, ip_proto, tcp_dst, set_ip_dst, set_tcp_dst, action_group_id)
+    ctrl.message_send(request)
+    return request
+
+#dpctl tcp:192.168.1.1:6633 flow-mod table=29,cmd=add,prio=291 eth_type=0x800,ip_src=10.0.0.1,ip_proto=6,tcp_src=2000 write:set_field=ip_src:100.0.0.1,set_field=tcp_src:5000 goto:30
+def add_snat_flow(ctrl, eth_type, ip_src, ip_proto, tcp_src, set_ip_src, set_tcp_src):
+    match = ofp.match()
+    match.oxm_list.append(ofp.oxm.eth_type(eth_type))
+    match.oxm_list.append(ofp.oxm.ipv4_src(ip_src))
+    match.oxm_list.append(ofp.oxm.ip_proto(ip_proto))
+    match.oxm_list.append(ofp.oxm.tcp_src(tcp_src))
+    
+    request = ofp.message.flow_add(
+            table_id=29,
+            cookie=42,
+            match=match,
+            instructions=[
+                    ofp.instruction.write_actions(
+                        actions=[ofp.action.set_field(ofp.oxm.ipv4_src(set_ip_src)),
+                                 ofp.action.set_field(ofp.oxm.tcp_src(set_tcp_src))]),
+                    ofp.instruction.goto_table(30)
+                ],
+            buffer_id=ofp.OFP_NO_BUFFER,
+            priority=1) 
+    logging.info("Inserting DNAT flow eth_type %lx, sip %lx, ip_proto %ld, tcp_src %ld, SetFeild: sip %lx, tcp_src %ld",eth_type, ip_src, ip_proto, tcp_src, set_ip_src, set_tcp_src)
+    ctrl.message_send(request)
+    return request
+    
+def get_vtap_lport_config_xml(dp_id, lport, phy_port, vlan, vnid, operation='merge'):  
+    """
+    Command Example:
+    of-agent vtap 10001 ethernet 1/1 vid 1
+    of-agent vtp 10001 vni 10
+    """
+    if vlan != 0:
+        config_vtap_xml="""
+        <config>
+            <capable-switch xmlns="urn:onf:of111:config:yang" xmlns:xc="urn:ietf:params:xml:ns:netconf:base:1.0">
+                <id>capable-switch-1</id>
+                <resources>
+                    <port xc:operation="OPERATION">
+                        <resource-id >LPORT</resource-id>     
+                        <features>
+                            <current>
+                              <rate>10Gb</rate>
+                              <medium>fiber</medium>
+                              <pause>symmetric</pause>      
+                            </current>
+                            <advertised>
+                              <rate>10Gb</rate>
+                              <rate>100Gb</rate>
+                              <medium>fiber</medium>
+                              <pause>symmetric</pause>
+                            </advertised>    
+                            <supported>
+                              <rate>10Gb</rate>
+                              <rate>100Gb</rate>
+                              <medium>fiber</medium>
+                              <pause>symmetric</pause>
+                            </supported> 
+                            <advertised-peer>
+                              <rate>10Gb</rate>
+                              <rate>100Gb</rate>
+                              <medium>fiber</medium>
+                              <pause>symmetric</pause>
+                            </advertised-peer>        
+                        </features>
+                        <ofdpa10:vtap xmlns:ofdpa10="urn:bcm:ofdpa10:accton01" xc:operation="OPERATION">
+                            <ofdpa10:phy-port>PHY_PORT</ofdpa10:phy-port>
+                            <ofdpa10:vid>VLAN_ID</ofdpa10:vid>
+                            <ofdpa10:vni>VNID</ofdpa10:vni>
+                        </ofdpa10:vtap>
+                    </port> 
+              </resources>
+              <logical-switches>
+                  <switch>
+                    <id>DATAPATH_ID</id>
+                    <datapath-id>DATAPATH_ID</datapath-id>
+                    <resources>
+                      <port xc:operation="OPERATION">LPORT</port>
+                    </resources>
+                  </switch>
+              </logical-switches>
+            </capable-switch>
+          </config>
+        """    
+    else:
+        config_vtap_xml="""
+        <config>
+            <capable-switch xmlns="urn:onf:of111:config:yang" xmlns:xc="urn:ietf:params:xml:ns:netconf:base:1.0">
+                <id>capable-switch-1</id>
+                <resources>
+                    <port xc:operation="OPERATION">
+                        <resource-id >LPORT</resource-id>     
+                        <features>
+                            <current>
+                              <rate>10Gb</rate>
+                              <medium>fiber</medium>
+                              <pause>symmetric</pause>      
+                            </current>
+                            <advertised>
+                              <rate>10Gb</rate>
+                              <rate>100Gb</rate>
+                              <medium>fiber</medium>
+                              <pause>symmetric</pause>
+                            </advertised>    
+                            <supported>
+                              <rate>10Gb</rate>
+                              <rate>100Gb</rate>
+                              <medium>fiber</medium>
+                              <pause>symmetric</pause>
+                            </supported> 
+                            <advertised-peer>
+                              <rate>10Gb</rate>
+                              <rate>100Gb</rate>
+                              <medium>fiber</medium>
+                              <pause>symmetric</pause>
+                            </advertised-peer>        
+                        </features>
+                        <ofdpa10:vtap xmlns:ofdpa10="urn:bcm:ofdpa10:accton01" xc:operation="OPERATION">
+                            <ofdpa10:phy-port>PHY_PORT</ofdpa10:phy-port>
+                            <ofdpa10:vni>VNID</ofdpa10:vni>
+                        </ofdpa10:vtap>
+                    </port> 
+              </resources>
+              <logical-switches>
+                  <switch>
+                    <id>DATAPATH_ID</id>
+                    <datapath-id>DATAPATH_ID</datapath-id>
+                    <resources>
+                      <port xc:operation="OPERATION">LPORT</port>
+                    </resources>
+                  </switch>
+              </logical-switches>
+            </capable-switch>
+          </config>
+        """        
+    str_datapath_id_f= "{:016x}".format(dp_id)        
+    str_datapath_id=':'.join([str_datapath_id_f[i:i+2] for i in range(0, len(str_datapath_id_f), 2)])	
+    config_vtap_xml=config_vtap_xml.replace("DATAPATH_ID", str_datapath_id)      
+    config_vtap_xml=config_vtap_xml.replace("LPORT", str(int(lport)))         
+    config_vtap_xml=config_vtap_xml.replace("PHY_PORT", str(phy_port))       
+    config_vtap_xml=config_vtap_xml.replace("VLAN_ID", str(vlan))     
+    config_vtap_xml=config_vtap_xml.replace("VNID", str(vnid))
+    config_vtap_xml=config_vtap_xml.replace("OPERATION", str(operation))
+    return config_vtap_xml
+      
+def get_vtep_lport_config_xml(dp_id, lport, src_ip, dst_ip, next_hop_id, vnid, udp_src_port=6633, ttl=25, operation='merge'): 
+    """
+    Command Example:
+    of-agent vtep 10002 source user-input-src-ip destination user-input-dst-ip udp-source-port 6633 nexthop 2 ttl 25
+    of-agent vtp 10001 vni 10    
+    """
+
+    config_vtep_xml="""
+        <config>
+          <capable-switch xmlns="urn:onf:of111:config:yang" xmlns:xc="urn:ietf:params:xml:ns:netconf:base:1.0">
+            <id>capable-switch-1</id>
+            <resources>
+             <port xc:operation="OPERATION">
+               <resource-id>LPORT</resource-id>     
+                 <features>
+                   <current>
+                     <rate>10Gb</rate>
+                     <medium>fiber</medium>
+                     <pause>symmetric</pause>      
+                   </current>
+                   <advertised>
+                     <rate>10Gb</rate>
+                     <rate>100Gb</rate>
+                     <medium>fiber</medium>
+                     <pause>symmetric</pause>
+                   </advertised>    
+                   <supported>
+                     <rate>10Gb</rate>
+                     <rate>100Gb</rate>
+                     <medium>fiber</medium>
+                     <pause>symmetric</pause>
+                   </supported> 
+                   <advertised-peer>
+                     <rate>10Gb</rate>
+                     <rate>100Gb</rate>
+                     <medium>fiber</medium>
+                     <pause>symmetric</pause>
+                   </advertised-peer>        
+                </features>
+			  <ofdpa10:vtep xmlns:ofdpa10="urn:bcm:ofdpa10:accton01">
+				<ofdpa10:src-ip>SRC_IP</ofdpa10:src-ip>
+				<ofdpa10:dest-ip>DST_IP</ofdpa10:dest-ip>
+				<ofdpa10:udp-src-port>UDP_SRC_PORT</ofdpa10:udp-src-port>
+				<ofdpa10:vni xc:operation="OPERATION">
+                    <ofdpa10:id>VNID</ofdpa10:id>
+                </ofdpa10:vni>
+				<ofdpa10:nexthop-id>NEXT_HOP_ID</ofdpa10:nexthop-id>
+				<ofdpa10:ttl>TTL</ofdpa10:ttl>
+			  </ofdpa10:vtep>
+             </port> 
+            </resources>
+            <logical-switches>
+                <switch>
+                  <id>DATAPATH_ID</id>
+                  <datapath-id>DATAPATH_ID</datapath-id>
+                  <resources>
+                    <port xc:operation="OPERATION">LPORT</port>
+                  </resources>
+                </switch>
+            </logical-switches>
+          </capable-switch>
+        </config>  
+    """
+    str_datapath_id_f= "{:016x}".format(dp_id)        
+    str_datapath_id=':'.join([str_datapath_id_f[i:i+2] for i in range(0, len(str_datapath_id_f), 2)])	
+    config_vtep_xml=config_vtep_xml.replace("DATAPATH_ID", str_datapath_id)      
+    config_vtep_xml=config_vtep_xml.replace("LPORT", str(int(lport)))
+    config_vtep_xml=config_vtep_xml.replace("SRC_IP", str(src_ip))            
+    config_vtep_xml=config_vtep_xml.replace("DST_IP", str(dst_ip))                 
+    config_vtep_xml=config_vtep_xml.replace("UDP_SRC_PORT", str(udp_src_port))                      
+    config_vtep_xml=config_vtep_xml.replace("NEXT_HOP_ID", str(next_hop_id))                           
+    config_vtep_xml=config_vtep_xml.replace("TTL", str(ttl))                           
+    config_vtep_xml=config_vtep_xml.replace("VNID", str(vnid))
+    config_vtep_xml=config_vtep_xml.replace("OPERATION", str(operation))		
+
+    return config_vtep_xml   
+      
+def get_next_hop_config_xml(next_hop_id, dst_mac, phy_port, vlan, operation='merge'): 
+    #of-agent nexthop 2 destination user-input-dst-mac ethernet 1/2 vid 2
+    config_nexthop_xml="""
+      <config>
+          <of11-config:capable-switch xmlns:of11-config="urn:onf:of111:config:yang" xmlns:xc="urn:ietf:params:xml:ns:netconf:base:1.0">
+            <ofdpa10:next-hop xmlns:ofdpa10="urn:bcm:ofdpa10:accton01"  xc:operation="OPERATION">
+              <ofdpa10:id>NEXT_HOP_ID</ofdpa10:id>
+              <ofdpa10:dest-mac>DST_MAC</ofdpa10:dest-mac>
+              <ofdpa10:phy-port>PHY_PORT</ofdpa10:phy-port>
+              <ofdpa10:vid>VLAN_ID</ofdpa10:vid>
+            </ofdpa10:next-hop>
+          </of11-config:capable-switch>
+      </config>
+      """
+    config_nexthop_xml=config_nexthop_xml.replace("VLAN_ID", str(vlan))
+    config_nexthop_xml=config_nexthop_xml.replace("PHY_PORT", str(phy_port))   
+    config_nexthop_xml=config_nexthop_xml.replace("NEXT_HOP_ID", str(next_hop_id))   
+    config_nexthop_xml=config_nexthop_xml.replace("DST_MAC", str(dst_mac))   
+    config_nexthop_xml=config_nexthop_xml.replace("OPERATION", str(operation))	
+    return config_nexthop_xml   
+
+def get_vni_config_xml(vni_id, mcast_ipv4, next_hop_id, operation='merge'):  
+    #of-agent vni 10 multicast 224.1.1.1 nexthop 20
+    if mcast_ipv4!=None:    
+        config_vni_xml="""
+          <config>
+              <of11-config:capable-switch xmlns:of11-config="urn:onf:of111:config:yang" xmlns:xc="urn:ietf:params:xml:ns:netconf:base:1.0">
+                <ofdpa10:vni xmlns:ofdpa10="urn:bcm:ofdpa10:accton01" xc:operation="OPERATION">
+                  <ofdpa10:id>VNID</ofdpa10:id>
+                  <ofdpa10:vni-multicast-group>MCAST_IP</ofdpa10:vni-multicast-group>
+                  <ofdpa10:multicast-group-nexthop-id>NEXT_HOP_ID</ofdpa10:multicast-group-nexthop-id>
+                </ofdpa10:vni>
+              </of11-config:capable-switch>
+          </config>
+          """   
+        config_vni_xml=config_vni_xml.replace("NEXT_HOP_ID", str(next_hop_id))   
+        config_vni_xml=config_vni_xml.replace("MCAST_IP", str(mcast_ipv4))             
+    else:
+        config_vni_xml="""
+          <config>
+              <of11-config:capable-switch xmlns:of11-config="urn:onf:of111:config:yang" xmlns:xc="urn:ietf:params:xml:ns:netconf:base:1.0">
+                <ofdpa10:vni xmlns:ofdpa10="urn:bcm:ofdpa10:accton01" xc:operation="OPERATION">
+                  <ofdpa10:id>VNID</ofdpa10:id>
+                </ofdpa10:vni>
+              </of11-config:capable-switch>
+          </config>
+          """   
+          
+    config_vni_xml=config_vni_xml.replace("VNID", str(vni_id))            
+    config_vni_xml=config_vni_xml.replace("OPERATION", str(operation))	
+    return config_vni_xml
+	
+def get_featureReplay(self):    
+    req = ofp.message.features_request()
+    res, raw = self.controller.transact(req)
+    self.assertIsNotNone(res, "Did not receive a response from the DUT.")        
+    self.assertEqual(res.type, ofp.OFPT_FEATURES_REPLY,
+                 ("Unexpected packet type %d received in response to "
+                  "OFPT_FEATURES_REQUEST") % res.type)
+    return res		
+	
+def send_edit_config(switch_ip, xml, target='runing'):
+    NETCONF_ACCOUNT="netconfuser"
+    NETCONF_PASSWD="netconfuser"
+    with manager.connect_ssh(host=switch_ip, port=830, username=NETCONF_ACCOUNT, password=NETCONF_PASSWD, hostkey_verify=False ) as m:
+        try:
+            m.edit_config(target='running', 
+                      config=xml, 
+                      default_operation='merge', 
+                      error_option='stop-on-error')
+
+        except Exception as e:
+            logging.info("Fail to set xml %s", xml)
+            return False
+
+	#return m.get_config(source='running').data_xml
+    return True
+
+def send_delete_config(switch_ip, xml, target='runing'):
+    NETCONF_ACCOUNT="netconfuser"
+    NETCONF_PASSWD="netconfuser"
+    with manager.connect_ssh(host=switch_ip, port=830, username=NETCONF_ACCOUNT, password=NETCONF_PASSWD, hostkey_verify=False ) as m:
+        try:
+            m.edit_config(target='running', 
+                      config=xml, 
+                      default_operation='delete', 
+                      error_option='stop-on-error')
+
+        except Exception as e:
+            logging.info("Fail to set xml %s", xml)
+            return False
+
+	#return m.get_config(source='running').data_xml
+    return True
+    
+def get_edit_config(switch_ip, target='runing'):
+    NETCONF_ACCOUNT="netconfuser"
+    NETCONF_PASSWD="netconfuser"
+    with manager.connect_ssh(host=switch_ip, port=830, username=NETCONF_ACCOUNT, password=NETCONF_PASSWD, hostkey_verify=False ) as m:
+	    return m.get_config(source='running').data_xml
+
+
+"""
+MPLS
+"""
+
+OFDPA_MPLS_SUBTYPE_SHIFT=24
+OFDPA_MPLS_GROUP_SUBTYPE_L2_VPN_LABEL=1 
+OFDPA_MPLS_GROUP_SUBTYPE_L3_VPN_LABEL=2
+OFDPA_MPLS_GROUP_SUBTYPE_TUNNEL_LABEL1=3
+OFDPA_MPLS_GROUP_SUBTYPE_TUNNEL_LABEL2=4
+OFDPA_MPLS_GROUP_SUBTYPE_SWAP_LABEL=5
+OFDPA_MPLS_GROUP_SUBTYPE_FAST_FAILOVER_GROUP=6
+OFDPA_MPLS_GROUP_SUBTYPE_ECMP=8
+OFDPA_MPLS_GROUP_SUBTYPE_L2_TAG=10
+
+def encode_mpls_interface_group_id(subtype, index):
+    index=index&0x00ffffff
+    assert(subtype==0)
+    return index + (9 << OFDPA_GROUP_TYPE_SHIFT)+(subtype<<OFDPA_MPLS_SUBTYPE_SHIFT)
+
+def encode_mpls_label_group_id(subtype, index):
+    index=index&0x00ffffff
+    assert(subtype <=5 or subtype==0)
+    #1: l2 vpn label
+    #2: l3 vpn label
+    #3: mpls tunnel label 1
+    #4: mpls tunnel lable 2
+    #5: mpls swap label
+    return index + (9 << OFDPA_GROUP_TYPE_SHIFT)+(subtype<<OFDPA_MPLS_SUBTYPE_SHIFT)         
+
+def encode_mpls_forwarding_group_id(subtype, index):
+    index=index&0x00ffffff
+    assert(subtype==6 or subtype==8 or subtype==10)
+    return index + (10 << OFDPA_GROUP_TYPE_SHIFT)+(subtype<<OFDPA_MPLS_SUBTYPE_SHIFT)         
+
+
+def add_mpls_intf_group(ctrl, ref_gid, dst_mac, src_mac, vid, index, subtype=0):
+    action=[]
+    action.append(ofp.action.set_field(ofp.oxm.eth_src(src_mac)))
+    action.append(ofp.action.set_field(ofp.oxm.eth_dst(dst_mac)))
+    action.append(ofp.action.set_field(ofp.oxm.vlan_vid(vid)))    
+    action.append(ofp.action.group(ref_gid))
+    
+    buckets = [ofp.bucket(actions=action)]
+
+    mpls_group_id =encode_mpls_interface_group_id(subtype, index)
+    request = ofp.message.group_add(group_type=ofp.OFPGT_INDIRECT,
+                                    group_id=mpls_group_id,
+                                    buckets=buckets
+                                   )
+    ctrl.message_send(request)
+    return mpls_group_id, request
+
+def add_mpls_label_group(ctrl, subtype, index, ref_gid, 
+                         lmep_id=-1,
+                         qos_index=-1,
+                         push_l2_header=False,
+                         push_vlan=False,
+                         push_mpls_header=False,
+                         push_cw=False,
+                         set_mpls_label=None,
+                         set_bos=None,
+                         set_tc=None,
+                         set_tc_from_table=False,
+                         cpy_tc_outward=False,
+                         set_ttl=None,
+                         cpy_ttl_outward=False,
+                         oam_lm_tx_count=False,
+                         set_pri_from_table=False
+                         ):
+    """
+    @ref_gid: only can be mpls intf group or mpls tunnel label 1/2 group
+    """      
+    action=[]
+
+    if push_vlan== True:
+        action.append(ofp.action.push_vlan(0x8100))
+    if push_mpls_header== True:
+        action.append(ofp.action.push_mpls(0x8847))
+    if set_mpls_label != None:
+        action.append(ofp.action.set_field(ofp.oxm.mpls_label(set_mpls_label)))
+    if set_bos != None:
+        action.append(ofp.action.set_field(ofp.oxm.mpls_bos(set_bos)))
+    if set_tc != None:
+        assert(set_tc_from_table==False)
+        action.append(ofp.action.set_field(ofp.oxm.mpls_tc(set_tc)))
+    if set_ttl != None:
+        action.append(ofp.action.set_mpls_ttl(set_ttl))  
+    if cpy_ttl_outward == True:
+        action.append(ofp.action.copy_ttl_out())  
+    """
+    ofdpa experimenter
+    """    
+    if push_l2_header== True:
+        action.append(ofp.action.ofdpa_push_l2_header())          
+    if set_tc_from_table== True:
+        assert(qos_index>=0)
+        assert(set_tc == None)
+        action.append(ofp.action.ofdpa_set_tc_from_table(qos_index))        
+    if cpy_tc_outward == True:
+        action.append(ofp.action.ofdpa_copy_tc_out())	
+    if oam_lm_tx_count == True:
+        assert(qos_index>=0 and lmep_id>=0)	
+        action.append(ofp.action.ofdpa_oam_lm_tx_count(lmep_id, qos_index))  
+    if set_pri_from_table == True:
+        assert(qos_index>=0)	
+        action.append(ofp.action.ofdpa_set_qos_from_table(qos_index))  
+    if push_cw == True:
+        action.append(ofp.action.ofdpa_push_cw())
+       
+    action.append(ofp.action.group(ref_gid))    
+    buckets = [ofp.bucket(actions=action)]
+    
+    mpls_group_id = encode_mpls_label_group_id(subtype, index)
+    request = ofp.message.group_add(group_type=ofp.OFPGT_INDIRECT,
+                                    group_id=mpls_group_id,
+                                    buckets=buckets
+                                   )
+    ctrl.message_send(request)
+    
+    return mpls_group_id, request    
+    
+def add_mpls_forwarding_group(ctrl, subtype, index, ref_gids, 
+                              watch_port=None, 
+							  watch_group=ofp.OFPP_ANY, 
+							  push_vlan=None,
+                              pop_vlan=None,
+                              set_vid=None):
+    assert(subtype == OFDPA_MPLS_GROUP_SUBTYPE_FAST_FAILOVER_GROUP
+	       or subtype == OFDPA_MPLS_GROUP_SUBTYPE_ECMP
+		   or subtype == OFDPA_MPLS_GROUP_SUBTYPE_L2_TAG)
+
+    buckets=[]
+    if subtype == OFDPA_MPLS_GROUP_SUBTYPE_FAST_FAILOVER_GROUP:
+        group_type = ofp.OFPGT_FF
+        for gid in ref_gids:
+            action=[]
+            action.append(ofp.action.group(gid)) 
+            buckets.append(ofp.bucket(watch_port=watch_port, watch_group=watch_group,actions=action))
+
+    elif subtype == OFDPA_MPLS_GROUP_SUBTYPE_ECMP:
+        group_type = ofp.OFPGT_SELECT
+        for gid in ref_gids:
+            action=[]
+            action.append(ofp.action.group(gid))    
+            buckets.append(ofp.bucket(actions=action))
+
+    elif subtype == OFDPA_MPLS_GROUP_SUBTYPE_L2_TAG:
+        group_type = ofp.OFPGT_INDIRECT
+        action=[]
+        if set_vid!=None:
+            action.append(ofp.action.set_field(ofp.oxm.vlan_vid(set_vid)))
+        if push_vlan!=None:
+            action.append(ofp.action.push_vlan(push_vlan))		
+        if pop_vlan!=None:
+            action.append(ofp.action.pop_vlan())		
+            action.append(ofp.action.group(ref_gids[0]))    
+            buckets.append(ofp.bucket(actions=action))
+
+    mpls_group_id = encode_mpls_forwarding_group_id(subtype, index)
+    request = ofp.message.group_add(group_type=group_type,
+                                    group_id=mpls_group_id,
+                                    buckets=buckets
+                                   )
+    ctrl.message_send(request)
+    return mpls_group_id, request    
+
+
+"""
+dislay
+"""   
+def print_current_table_flow_stat(ctrl, table_id=0xff):
+    stat_req=ofp.message.flow_stats_request()
+    response, pkt = ctrl.transact(stat_req)
+    if response == None:
+        print "no response"
+        return None
+    print len(response.entries)
+    for obj in response.entries:
+        print "match ", obj.match
+        print "cookie", obj.cookie
+        print "priority", obj.priority
+        print "idle_timeout", obj.idle_timeout
+        print "hard_timeout", obj.hard_timeout
+        #obj.actions
+        print "packet count: %lx"%obj.packet_count
\ No newline at end of file
diff --git a/ofdpa/all.py b/ofdpa/all.py
new file mode 100644
index 0000000..c8b00b1
--- /dev/null
+++ b/ofdpa/all.py
@@ -0,0 +1,598 @@
+# Distributed under the OpenFlow Software License (see LICENSE)
+# Copyright (c) 2010 The Board of Trustees of The Leland Stanford Junior University
+# Copyright (c) 2012, 2013 Big Switch Networks, Inc.
+# Copyright (c) 2012, 2013 CPqD
+# Copyright (c) 2012, 2013 Ericsson
+"""
+Basic test cases
+
+Test cases in other modules depend on this functionality.
+"""
+
+import logging
+
+from oftest import config
+import oftest.base_tests as base_tests
+import ofp
+import ofdpa_utils
+
+from oftest.testutils import *
+
+@group('smoke')
+class Echo(base_tests.SimpleProtocol):
+    """
+    Test echo response with no data
+    """
+    def runTest(self):
+        request = ofp.message.echo_request()
+        response, pkt = self.controller.transact(request)
+        self.assertTrue(response is not None,
+                        "Did not get echo reply")
+        self.assertEqual(response.type, ofp.OFPT_ECHO_REPLY,
+                         'response is not echo_reply')
+        self.assertEqual(request.xid, response.xid,
+                         'response xid != request xid')
+        self.assertEqual(len(response.data), 0, 'response data non-empty')
+
+class EchoWithData(base_tests.SimpleProtocol):
+    """
+    Test echo response with short string data
+    """
+    def runTest(self):
+        data = 'OpenFlow Will Rule The World'
+        request = ofp.message.echo_request(data=data)
+        response, _ = self.controller.transact(request)
+        self.assertTrue(response is not None,
+                        "Did not get echo reply")
+        self.assertEqual(response.type, ofp.OFPT_ECHO_REPLY,
+                         'response is not echo_reply')
+        self.assertEqual(request.xid, response.xid,
+                         'response xid != request xid')
+        self.assertEqual(request.data, response.data,
+                         'response data != request data')
+
+class FeaturesRequest(base_tests.SimpleProtocol):
+    """
+    Test features_request to make sure we get a response
+
+    Does NOT test the contents; just that we get a response
+    """
+    def runTest(self):
+        request = ofp.message.features_request()
+        response,_ = self.controller.transact(request)
+        self.assertTrue(response is not None,
+                        'Did not get features reply')
+
+class DefaultDrop(base_tests.SimpleDataPlane):
+    """
+    Check that an empty flowtable results in drops
+    """
+    def runTest(self):
+        in_port, = openflow_ports(1)
+        delete_all_flows(self.controller)
+
+        pkt = str(simple_tcp_packet())
+        self.dataplane.send(in_port, pkt)
+        verify_no_packet_in(self, pkt, None)
+        verify_packets(self, pkt, [])
+
+class OutputExact(base_tests.SimpleDataPlane):
+    """
+    Test output function for an exact-match flow
+
+    For each port A, adds a flow directing matching packets to that port.
+    Then, for all other ports B != A, verifies that sending a matching packet
+    to B results in an output to A.
+    """
+    def runTest(self):
+        ports = sorted(config["port_map"].keys())
+
+        delete_all_flows(self.controller)
+
+        parsed_pkt = simple_tcp_packet()
+        pkt = str(parsed_pkt)
+        match = packet_to_flow_match(self, parsed_pkt)
+
+        for out_port in ports:
+            request = ofp.message.flow_add(
+                    table_id=test_param_get("table", 0),
+                    cookie=42,
+                    match=match,
+                    instructions=[
+                        ofp.instruction.apply_actions(
+                            actions=[
+                                ofp.action.output(
+                                    port=out_port,
+                                    max_len=ofp.OFPCML_NO_BUFFER)])],
+                    buffer_id=ofp.OFP_NO_BUFFER,
+                    priority=1000)
+
+            logging.info("Inserting flow sending matching packets to port %d", out_port)
+            self.controller.message_send(request)
+            do_barrier(self.controller)
+
+            for in_port in ports:
+                if in_port == out_port:
+                    continue
+                logging.info("OutputExact test, ports %d to %d", in_port, out_port)
+                self.dataplane.send(in_port, pkt)
+                verify_packets(self, pkt, [out_port])
+
+class OutputWildcard(base_tests.SimpleDataPlane):
+    """
+    Test output function for a match-all (but not table-miss) flow
+
+    For each port A, adds a flow directing all packets to that port.
+    Then, for all other ports B != A, verifies that sending a packet
+    to B results in an output to A.
+    """
+    def runTest(self):
+        ports = sorted(config["port_map"].keys())
+
+        delete_all_flows(self.controller)
+
+        pkt = str(simple_tcp_packet())
+
+        for out_port in ports:
+            request = ofp.message.flow_add(
+                    table_id=test_param_get("table", 0),
+                    cookie=42,
+                    instructions=[
+                        ofp.instruction.apply_actions(
+                            actions=[
+                                ofp.action.output(
+                                    port=out_port,
+                                    max_len=ofp.OFPCML_NO_BUFFER)])],
+                    buffer_id=ofp.OFP_NO_BUFFER,
+                    priority=1000)
+
+            logging.info("Inserting flow sending all packets to port %d", out_port)
+            self.controller.message_send(request)
+            do_barrier(self.controller)
+
+            for in_port in ports:
+                if in_port == out_port:
+                    continue
+                logging.info("OutputWildcard test, ports %d to %d", in_port, out_port)
+                self.dataplane.send(in_port, pkt)
+                verify_packets(self, pkt, [out_port])
+
+class PacketInExact(base_tests.SimpleDataPlane):
+    """
+    Test packet in function for an exact-match flow
+
+    Send a packet to each dataplane port and verify that a packet
+    in message is received from the controller for each
+    """
+    def runTest(self):
+        delete_all_flows(self.controller)
+
+        # required for OF-DPA to not drop packets
+        ofdpa_utils.installDefaultVlan(self.controller)
+
+        parsed_pkt = simple_tcp_packet()
+        pkt = str(parsed_pkt)
+
+        #  NOTE: interally the switch adds a VLAN so the match needs to be with an explicit VLAN
+        parsed_vlan_pkt = simple_tcp_packet(dl_vlan_enable=True, 
+                            vlan_vid=ofdpa_utils.DEFAULT_VLAN, 
+                            vlan_pcp=0,
+                            pktlen=104) # 4 less than we started with, because the way simple_tcp calc's length
+        match = packet_to_flow_match(self, parsed_vlan_pkt)
+        vlan_pkt = str(parsed_vlan_pkt)
+
+        request = ofp.message.flow_add(
+            table_id=ofdpa_utils.ACL_TABLE.table_id,
+            cookie=42,
+            match=match,
+            instructions=[
+                ofp.instruction.apply_actions(
+                    actions=[
+                        ofp.action.output(
+                            port=ofp.OFPP_CONTROLLER,
+                            max_len=ofp.OFPCML_NO_BUFFER)])],
+            buffer_id=ofp.OFP_NO_BUFFER,
+            priority=1000)
+
+        logging.info("Inserting flow sending matching packets to controller")
+        self.controller.message_send(request)
+        do_barrier(self.controller)
+
+        for of_port in config["port_map"].keys():
+            logging.info("PacketInExact test, port %d", of_port)
+            self.dataplane.send(of_port, pkt)
+            verify_packet_in(self, vlan_pkt, of_port, ofp.OFPR_ACTION)
+            verify_packets(self, pkt, [])
+
+class PacketInWildcard(base_tests.SimpleDataPlane):
+        #  NOTE: interally the switch adds a VLAN so the match needs to be with an explicit VLAN
+    """
+    Test packet in function for a match-all flow
+
+    Send a packet to each dataplane port and verify that a packet
+    in message is received from the controller for each
+    """
+    def runTest(self):
+        delete_all_flows(self.controller)
+
+        # required for OF-DPA to not drop packets
+        ofdpa_utils.installDefaultVlan(self.controller)
+
+        pkt = str(simple_tcp_packet())
+
+        #  NOTE: interally the switch adds a VLAN so the match needs to be with an explicit VLAN
+        parsed_vlan_pkt = simple_tcp_packet(dl_vlan_enable=True, 
+                            vlan_vid=ofdpa_utils.DEFAULT_VLAN, 
+                            vlan_pcp=0,
+                            pktlen=104) # 4 less than we started with, because the way simple_tcp calc's length
+        vlan_pkt = str(parsed_vlan_pkt)
+
+
+        request = ofp.message.flow_add(
+            table_id=ofdpa_utils.ACL_TABLE.table_id,
+            cookie=42,
+            instructions=[
+                ofp.instruction.apply_actions(
+                    actions=[
+                        ofp.action.output(
+                            port=ofp.OFPP_CONTROLLER,
+                            max_len=ofp.OFPCML_NO_BUFFER)])],
+            buffer_id=ofp.OFP_NO_BUFFER,
+            priority=1000)
+
+        logging.info("Inserting flow sending all packets to controller")
+        self.controller.message_send(request)
+        do_barrier(self.controller)
+
+        for of_port in config["port_map"].keys():
+            logging.info("PacketInWildcard test, port %d", of_port)
+            self.dataplane.send(of_port, pkt)
+            verify_packet_in(self, vlan_pkt, of_port, ofp.OFPR_ACTION)
+            verify_packets(self, pkt, [])
+
+class PacketInMiss(base_tests.SimpleDataPlane):
+    """
+    Test packet in function for a table-miss flow
+
+    Send a packet to each dataplane port and verify that a packet
+    in message is received from the controller for each
+    """
+    def runTest(self):
+        delete_all_flows(self.controller)
+
+        # required for OF-DPA to not drop packets
+        ofdpa_utils.installDefaultVlan(self.controller)
+
+        parsed_pkt = simple_tcp_packet()
+        pkt = str(parsed_pkt)
+
+        #  NOTE: interally the switch adds a VLAN so the match needs to be with an explicit VLAN
+        parsed_vlan_pkt = simple_tcp_packet(dl_vlan_enable=True, 
+                            vlan_vid=ofdpa_utils.DEFAULT_VLAN, 
+                            vlan_pcp=0,
+                            pktlen=104) # 4 less than we started with, because the way simple_tcp calc's length
+        vlan_pkt = str(parsed_vlan_pkt)
+
+        request = ofp.message.flow_add(
+            table_id=ofdpa_utils.ACL_TABLE.table_id,
+            cookie=42,
+            instructions=[
+                ofp.instruction.apply_actions(
+                    actions=[
+                        ofp.action.output(
+                            port=ofp.OFPP_CONTROLLER,
+                            max_len=ofp.OFPCML_NO_BUFFER)])],
+            buffer_id=ofp.OFP_NO_BUFFER,
+            priority=0)
+
+        logging.info("Inserting table-miss flow sending all packets to controller")
+        self.controller.message_send(request)
+        do_barrier(self.controller)
+
+        for of_port in config["port_map"].keys():
+            logging.info("PacketInMiss test, port %d", of_port)
+            self.dataplane.send(of_port, pkt)
+            verify_packet_in(self, vlan_pkt, of_port, ofp.OFPR_NO_MATCH)
+            verify_packets(self, pkt, [])
+
+class PacketOut(base_tests.SimpleDataPlane):
+    """
+    Test packet out function
+
+    Send packet out message to controller for each dataplane port and
+    verify the packet appears on the appropriate dataplane port
+    """
+    def runTest(self):
+        pkt = str(simple_tcp_packet())
+
+        for of_port in config["port_map"].keys():
+            msg = ofp.message.packet_out(
+                in_port=ofp.OFPP_CONTROLLER,
+                actions=[ofp.action.output(port=of_port)],
+                buffer_id=ofp.OFP_NO_BUFFER,
+                data=pkt)
+
+            logging.info("PacketOut test, port %d", of_port)
+            self.controller.message_send(msg)
+            verify_packets(self, pkt, [of_port])
+
+class FlowRemoveAll(base_tests.SimpleProtocol):
+    """
+    Remove all flows; required for almost all tests
+
+    Add a bunch of flows, remove them, and then make sure there are no flows left
+    This is an intentionally naive test to see if the baseline functionality works
+    and should be a precondition to any more complicated deletion test (e.g.,
+    delete_strict vs. delete)
+    """
+    def runTest(self):
+        for i in range(1,5):
+            logging.debug("Adding flow %d", i)
+            request = ofp.message.flow_add(
+                buffer_id=ofp.OFP_NO_BUFFER,
+                priority=i*1000)
+            self.controller.message_send(request)
+        do_barrier(self.controller)
+
+        delete_all_flows(self.controller)
+
+        logging.info("Sending flow stats request")
+        stats = get_flow_stats(self, ofp.match())
+        self.assertEqual(len(stats), 0, "Expected empty flow stats reply")
+
+
+## Multipart messages
+
+class DescStats(base_tests.SimpleProtocol):
+    """
+    Switch description multipart transaction
+
+    Only verifies we get a single reply.
+    """
+    def runTest(self):
+        request = ofp.message.desc_stats_request()
+        logging.info("Sending desc stats request")
+        response, _ = self.controller.transact(request)
+        self.assertTrue(response != None, "No response to desc stats request")
+        logging.info(response.show())
+        self.assertEquals(response.flags, 0, "Unexpected bit set in desc stats reply flags")
+
+class FlowStats(base_tests.SimpleProtocol):
+    """
+    Flow stats multipart transaction
+
+    Only verifies we get a reply.
+    """
+    def runTest(self):
+        logging.info("Sending flow stats request")
+        stats = get_flow_stats(self, ofp.match())
+        logging.info("Received %d flow stats entries", len(stats))
+        for entry in stats:
+            logging.info(entry.show())
+
+class AggregateStats(base_tests.SimpleProtocol):
+    """
+    Aggregate flow stats multipart transaction
+
+    Only verifies we get a single reply.
+    """
+    def runTest(self):
+        request = ofp.message.aggregate_stats_request(
+            table_id=ofp.OFPTT_ALL,
+            out_port=ofp.OFPP_ANY,
+            out_group=ofp.OFPG_ANY,
+            cookie=0,
+            cookie_mask=0)
+        logging.info("Sending aggregate flow stats request")
+        response, _ = self.controller.transact(request)
+        self.assertTrue(response != None, "No response to aggregate stats request")
+        logging.info(response.show())
+        self.assertEquals(response.flags, 0, "Unexpected bit set in aggregate stats reply flags")
+
+class TableStats(base_tests.SimpleProtocol):
+    """
+    Table stats multipart transaction
+
+    Only verifies we get a reply.
+    """
+    def runTest(self):
+        logging.info("Sending table stats request")
+        stats = get_stats(self, ofp.message.table_stats_request())
+        logging.info("Received %d table stats entries", len(stats))
+        for entry in stats:
+            logging.info(entry.show())
+
+class PortStats(base_tests.SimpleProtocol):
+    """
+    Port stats multipart transaction
+
+    Only verifies we get a reply.
+    """
+    def runTest(self):
+        request = ofp.message.port_stats_request(port_no=ofp.OFPP_ANY)
+        logging.info("Sending port stats request")
+        stats = get_stats(self, request)
+        logging.info("Received %d port stats entries", len(stats))
+        for entry in stats:
+            logging.info(entry.show())
+
+class QueueStats(base_tests.SimpleProtocol):
+    """
+    Queue stats multipart transaction
+
+    Only verifies we get a reply.
+    """
+    def runTest(self):
+        request = ofp.message.queue_stats_request(port_no=ofp.OFPP_ANY,
+                                                  queue_id=ofp.OFPQ_ALL)
+        logging.info("Sending queue stats request")
+        stats = get_stats(self, request)
+        logging.info("Received %d queue stats entries", len(stats))
+        for entry in stats:
+            logging.info(entry.show())
+
+class GroupStats(base_tests.SimpleProtocol):
+    """
+    Group stats multipart transaction
+
+    Only verifies we get a reply.
+    """
+    def runTest(self):
+        request = ofp.message.group_stats_request(group_id=ofp.OFPG_ALL)
+        logging.info("Sending group stats request")
+        stats = get_stats(self, request)
+        logging.info("Received %d group stats entries", len(stats))
+        for entry in stats:
+            logging.info(entry.show())
+
+class GroupDescStats(base_tests.SimpleProtocol):
+    """
+    Group description multipart transaction
+
+    Only verifies we get a reply.
+    """
+    def runTest(self):
+        request = ofp.message.group_desc_stats_request()
+        logging.info("Sending group desc stats request")
+        stats = get_stats(self, request)
+        logging.info("Received %d group desc stats entries", len(stats))
+        for entry in stats:
+            logging.info(entry.show())
+
+class GroupFeaturesStats(base_tests.SimpleProtocol):
+    """
+    Group features multipart transaction
+
+    Only verifies we get a single reply.
+    """
+    def runTest(self):
+        request = ofp.message.group_features_stats_request()
+        logging.info("Sending group features stats request")
+        response, _ = self.controller.transact(request)
+        self.assertTrue(response != None, "No response to group features stats request")
+        logging.info(response.show())
+        self.assertEquals(response.flags, 0, "Unexpected bit set in group features stats reply flags")
+
+class MeterStats(base_tests.SimpleProtocol):
+    """
+    Meter stats multipart transaction
+
+    Only verifies we get a reply.
+    """
+    def runTest(self):
+        request = ofp.message.meter_stats_request(meter_id=ofp.OFPM_ALL)
+        logging.info("Sending meter stats request")
+        stats = get_stats(self, request)
+        logging.info("Received %d meter stats entries", len(stats))
+        for entry in stats:
+            logging.info(entry.show())
+
+class MeterConfigStats(base_tests.SimpleProtocol):
+    """
+    Meter config multipart transaction
+
+    Only verifies we get a reply.
+    """
+    def runTest(self):
+        request = ofp.message.meter_config_stats_request(meter_id=ofp.OFPM_ALL)
+        logging.info("Sending meter config stats request")
+        stats = get_stats(self, request)
+        logging.info("Received %d meter config stats entries", len(stats))
+        for entry in stats:
+            logging.info(entry.show())
+
+class MeterFeaturesStats(base_tests.SimpleProtocol):
+    """
+    Meter features multipart transaction
+
+    Only verifies we get a single reply.
+    """
+    def runTest(self):
+        request = ofp.message.meter_features_stats_request()
+        logging.info("Sending meter features stats request")
+        response, _ = self.controller.transact(request)
+        self.assertTrue(response != None, "No response to meter features stats request")
+        logging.info(response.show())
+        self.assertEquals(response.flags, 0, "Unexpected bit set in meter features stats reply flags")
+
+@disabled # pyloxi does not yet support table features
+class TableFeaturesStats(base_tests.SimpleProtocol):
+    """
+    Table features multipart transaction
+
+    Only verifies we get a reply.
+    """
+    def runTest(self):
+        logging.info("Sending table features stats request")
+        stats = get_stats(self, ofp.message.table_features_stats_request())
+        logging.info("Received %d table features stats entries", len(stats))
+        for entry in stats:
+            logging.info(entry.show())
+
+class PortDescStats(base_tests.SimpleProtocol):
+    """
+    Port description multipart transaction
+
+    Only verifies we get a reply.
+    """
+    def runTest(self):
+        logging.info("Sending port desc stats request")
+        stats = get_stats(self, ofp.message.port_desc_stats_request())
+        logging.info("Received %d port desc stats entries", len(stats))
+        for entry in stats:
+            logging.info(entry.show())
+
+class PortConfigMod(base_tests.SimpleProtocol):
+    """
+    Modify a bit in port config and verify changed
+
+    Get the switch configuration, modify the port configuration
+    and write it back; get the config again and verify changed.
+    Then set it back to the way it was.
+    """
+
+    def runTest(self):
+        logging.info("Running " + str(self))
+        for of_port, _ in config["port_map"].items(): # Grab first port
+            break
+
+        (_, config1, _) = \
+            port_config_get(self.controller, of_port)
+        self.assertTrue(config is not None, "Did not get port config")
+
+        logging.debug("OFPPC_NO_PACKET_IN bit port " + str(of_port) + " is now " +
+                      str(config1 & ofp.OFPPC_NO_PACKET_IN))
+
+        rv = port_config_set(self.controller, of_port,
+                             config1 ^ ofp.OFPPC_NO_PACKET_IN,
+                             ofp.OFPPC_NO_PACKET_IN)
+        self.assertTrue(rv != -1, "Error sending port mod")
+
+        # Verify change took place with same feature request
+        (_, config2, _) = port_config_get(self.controller, of_port)
+        self.assertTrue(config2 is not None, "Did not get port config2")
+        logging.debug("OFPPC_NO_PACKET_IN bit port " + str(of_port) + " is now " +
+                      str(config2 & ofp.OFPPC_NO_PACKET_IN))
+        self.assertTrue(config2 & ofp.OFPPC_NO_PACKET_IN !=
+                        config1 & ofp.OFPPC_NO_PACKET_IN,
+                        "Bit change did not take")
+        # Set it back
+        rv = port_config_set(self.controller, of_port, config1,
+                             ofp.OFPPC_NO_PACKET_IN)
+        self.assertTrue(rv != -1, "Error sending port mod")
+
+class AsyncConfigGet(base_tests.SimpleProtocol):
+    """
+    Verify initial async config
+
+    Other tests rely on connections starting with these values.
+    """
+
+    def runTest(self):
+        logging.info("Sending get async config request")
+        response, _ = self.controller.transact(ofp.message.async_get_request())
+        self.assertTrue(response != None, "No response to get async config request")
+        logging.info(response.show())
+        self.assertEquals(response.packet_in_mask_equal_master & 0x07, 0x07)
+        self.assertEquals(response.port_status_mask_equal_master & 0x07, 0x07)
+        self.assertEquals(response.flow_removed_mask_equal_master & 0x0f, 0x0f)
diff --git a/ofdpa/basic.py b/ofdpa/basic.py
new file mode 100644
index 0000000..b2392a7
--- /dev/null
+++ b/ofdpa/basic.py
@@ -0,0 +1,608 @@
+# Distributed under the OpenFlow Software License (see LICENSE)
+# Copyright (c) 2010 The Board of Trustees of The Leland Stanford Junior University
+# Copyright (c) 2012, 2013 Big Switch Networks, Inc.
+# Copyright (c) 2012, 2013 CPqD
+# Copyright (c) 2012, 2013 Ericsson
+"""
+Basic test cases
+
+Test cases in other modules depend on this functionality.
+"""
+
+import logging
+
+from oftest import config
+import oftest.base_tests as base_tests
+import ofp
+import ofdpa_utils
+
+from oftest.testutils import *
+
+@group('smoke')
+class Echo(base_tests.SimpleProtocol):
+    """
+    Test echo response with no data
+    """
+    def runTest(self):
+        request = ofp.message.echo_request()
+        response, pkt = self.controller.transact(request)
+        self.assertTrue(response is not None,
+                        "Did not get echo reply")
+        self.assertEqual(response.type, ofp.OFPT_ECHO_REPLY,
+                         'response is not echo_reply')
+        self.assertEqual(request.xid, response.xid,
+                         'response xid != request xid')
+        self.assertEqual(len(response.data), 0, 'response data non-empty')
+
+class EchoWithData(base_tests.SimpleProtocol):
+    """
+    Test echo response with short string data
+    """
+    def runTest(self):
+        data = 'OpenFlow Will Rule The World'
+        request = ofp.message.echo_request(data=data)
+        response, _ = self.controller.transact(request)
+        self.assertTrue(response is not None,
+                        "Did not get echo reply")
+        self.assertEqual(response.type, ofp.OFPT_ECHO_REPLY,
+                         'response is not echo_reply')
+        self.assertEqual(request.xid, response.xid,
+                         'response xid != request xid')
+        self.assertEqual(request.data, response.data,
+                         'response data != request data')
+
+class FeaturesRequest(base_tests.SimpleProtocol):
+    """
+    Test features_request to make sure we get a response
+
+    Does NOT test the contents; just that we get a response
+    """
+    def runTest(self):
+        request = ofp.message.features_request()
+        response,_ = self.controller.transact(request)
+        self.assertTrue(response is not None,
+                        'Did not get features reply')
+
+class DefaultDrop(base_tests.SimpleDataPlane):
+    """
+    Check that an empty flowtable results in drops
+    """
+    def runTest(self):
+        in_port, = openflow_ports(1)
+        delete_all_flows(self.controller)
+
+        pkt = str(simple_tcp_packet())
+        self.dataplane.send(in_port, pkt)
+        verify_no_packet_in(self, pkt, None)
+        verify_packets(self, pkt, [])
+
+@disabled   #FIXME ofdpa 
+class OutputExact(base_tests.SimpleDataPlane):
+    """
+    Test output function for an exact-match flow
+
+    For each port A, adds a flow directing matching packets to that port.
+    Then, for all other ports B != A, verifies that sending a matching packet
+    to B results in an output to A.
+    """
+    def runTest(self):
+        ports = sorted(config["port_map"].keys())
+
+        delete_all_flows(self.controller)
+
+        parsed_pkt = simple_tcp_packet()
+        pkt = str(parsed_pkt)
+        match = packet_to_flow_match(self, parsed_pkt)
+
+        for out_port in ports:
+            request = ofp.message.flow_add(
+                    table_id=test_param_get("table", 0),
+                    cookie=42,
+                    match=match,
+                    instructions=[
+                        ofp.instruction.apply_actions(
+                            actions=[
+                                ofp.action.output(
+                                    port=out_port,
+                                    max_len=ofp.OFPCML_NO_BUFFER)])],
+                    buffer_id=ofp.OFP_NO_BUFFER,
+                    priority=1000)
+
+            logging.info("Inserting flow sending matching packets to port %d", out_port)
+            self.controller.message_send(request)
+            do_barrier(self.controller)
+
+            for in_port in ports:
+                if in_port == out_port:
+                    continue
+                logging.info("OutputExact test, ports %d to %d", in_port, out_port)
+                self.dataplane.send(in_port, pkt)
+                verify_packets(self, pkt, [out_port])
+
+@disabled   #FIXME ofdpa 
+class OutputWildcard(base_tests.SimpleDataPlane):
+    """
+    Test output function for a match-all (but not table-miss) flow
+
+    For each port A, adds a flow directing all packets to that port.
+    Then, for all other ports B != A, verifies that sending a packet
+    to B results in an output to A.
+    """
+    def runTest(self):
+        ports = sorted(config["port_map"].keys())
+
+        delete_all_flows(self.controller)
+
+        pkt = str(simple_tcp_packet())
+
+        for out_port in ports:
+            request = ofp.message.flow_add(
+                    table_id=test_param_get("table", 0),
+                    cookie=42,
+                    instructions=[
+                        ofp.instruction.apply_actions(
+                            actions=[
+                                ofp.action.output(
+                                    port=out_port,
+                                    max_len=ofp.OFPCML_NO_BUFFER)])],
+                    buffer_id=ofp.OFP_NO_BUFFER,
+                    priority=1000)
+
+            logging.info("Inserting flow sending all packets to port %d", out_port)
+            self.controller.message_send(request)
+            do_barrier(self.controller)
+
+            for in_port in ports:
+                if in_port == out_port:
+                    continue
+                logging.info("OutputWildcard test, ports %d to %d", in_port, out_port)
+                self.dataplane.send(in_port, pkt)
+                verify_packets(self, pkt, [out_port])
+
+@disabled   #FIXME ofdpa 
+class PacketInExact(base_tests.SimpleDataPlane):
+    """
+    Test packet in function for an exact-match flow
+
+    Send a packet to each dataplane port and verify that a packet
+    in message is received from the controller for each
+    """
+    def runTest(self):
+        delete_all_flows(self.controller)
+
+        # required for OF-DPA to not drop packets
+        ofdpa_utils.installDefaultVlan(self.controller)
+
+        parsed_pkt = simple_tcp_packet()
+        pkt = str(parsed_pkt)
+
+        #  NOTE: interally the switch adds a VLAN so the match needs to be with an explicit VLAN
+        parsed_vlan_pkt = simple_tcp_packet(dl_vlan_enable=True, 
+                            vlan_vid=ofdpa_utils.DEFAULT_VLAN, 
+                            vlan_pcp=0,
+                            pktlen=104) # 4 less than we started with, because the way simple_tcp calc's length
+        match = packet_to_flow_match(self, parsed_vlan_pkt)
+        vlan_pkt = str(parsed_vlan_pkt)
+
+        request = ofp.message.flow_add(
+            table_id=ofdpa_utils.ACL_TABLE.table_id,
+            cookie=42,
+            match=match,
+            instructions=[
+                ofp.instruction.apply_actions(
+                    actions=[
+                        ofp.action.output(
+                            port=ofp.OFPP_CONTROLLER,
+                            max_len=ofp.OFPCML_NO_BUFFER)])],
+            buffer_id=ofp.OFP_NO_BUFFER,
+            priority=1000)
+
+        logging.info("Inserting flow sending matching packets to controller")
+        self.controller.message_send(request)
+        do_barrier(self.controller)
+
+        for of_port in config["port_map"].keys():
+            logging.info("PacketInExact test, port %d", of_port)
+            self.dataplane.send(of_port, pkt)
+            verify_packet_in(self, vlan_pkt, of_port, ofp.OFPR_ACTION)
+            verify_packets(self, pkt, [])
+
+@disabled   #FIXME ofdpa 
+class PacketInWildcard(base_tests.SimpleDataPlane):
+        #  NOTE: interally the switch adds a VLAN so the match needs to be with an explicit VLAN
+    """
+    Test packet in function for a match-all flow
+
+    Send a packet to each dataplane port and verify that a packet
+    in message is received from the controller for each
+    """
+    def runTest(self):
+        delete_all_flows(self.controller)
+
+        # required for OF-DPA to not drop packets
+        ofdpa_utils.installDefaultVlan(self.controller)
+
+        pkt = str(simple_tcp_packet())
+
+        #  NOTE: interally the switch adds a VLAN so the match needs to be with an explicit VLAN
+        parsed_vlan_pkt = simple_tcp_packet(dl_vlan_enable=True, 
+                            vlan_vid=ofdpa_utils.DEFAULT_VLAN, 
+                            vlan_pcp=0,
+                            pktlen=104) # 4 less than we started with, because the way simple_tcp calc's length
+        vlan_pkt = str(parsed_vlan_pkt)
+
+
+        request = ofp.message.flow_add(
+            table_id=ofdpa_utils.ACL_TABLE.table_id,
+            cookie=42,
+            instructions=[
+                ofp.instruction.apply_actions(
+                    actions=[
+                        ofp.action.output(
+                            port=ofp.OFPP_CONTROLLER,
+                            max_len=ofp.OFPCML_NO_BUFFER)])],
+            buffer_id=ofp.OFP_NO_BUFFER,
+            priority=1000)
+
+        logging.info("Inserting flow sending all packets to controller")
+        self.controller.message_send(request)
+        do_barrier(self.controller)
+
+        for of_port in config["port_map"].keys():
+            logging.info("PacketInWildcard test, port %d", of_port)
+            self.dataplane.send(of_port, pkt)
+            verify_packet_in(self, vlan_pkt, of_port, ofp.OFPR_ACTION)
+            verify_packets(self, pkt, [])
+
+@disabled   #FIXME ofdpa 
+class PacketInMiss(base_tests.SimpleDataPlane):
+    """
+    Test packet in function for a table-miss flow
+
+    Send a packet to each dataplane port and verify that a packet
+    in message is received from the controller for each
+    """
+    def runTest(self):
+        delete_all_flows(self.controller)
+
+        # required for OF-DPA to not drop packets
+        ofdpa_utils.installDefaultVlan(self.controller)
+
+        parsed_pkt = simple_tcp_packet()
+        pkt = str(parsed_pkt)
+
+        #  NOTE: interally the switch adds a VLAN so the match needs to be with an explicit VLAN
+        parsed_vlan_pkt = simple_tcp_packet(dl_vlan_enable=True, 
+                            vlan_vid=ofdpa_utils.DEFAULT_VLAN, 
+                            vlan_pcp=0,
+                            pktlen=104) # 4 less than we started with, because the way simple_tcp calc's length
+        vlan_pkt = str(parsed_vlan_pkt)
+
+        request = ofp.message.flow_add(
+            table_id=ofdpa_utils.ACL_TABLE.table_id,
+            cookie=42,
+            instructions=[
+                ofp.instruction.apply_actions(
+                    actions=[
+                        ofp.action.output(
+                            port=ofp.OFPP_CONTROLLER,
+                            max_len=ofp.OFPCML_NO_BUFFER)])],
+            buffer_id=ofp.OFP_NO_BUFFER,
+            priority=0)
+
+        logging.info("Inserting table-miss flow sending all packets to controller")
+        self.controller.message_send(request)
+        do_barrier(self.controller)
+
+        for of_port in config["port_map"].keys():
+            logging.info("PacketInMiss test, port %d", of_port)
+            self.dataplane.send(of_port, pkt)
+            verify_packet_in(self, vlan_pkt, of_port, ofp.OFPR_NO_MATCH)
+            verify_packets(self, pkt, [])
+
+class PacketOut(base_tests.SimpleDataPlane):
+    """
+    Test packet out function
+
+    Send packet out message to controller for each dataplane port and
+    verify the packet appears on the appropriate dataplane port
+    """
+    def runTest(self):
+        pkt = str(simple_tcp_packet())
+
+        for of_port in config["port_map"].keys():
+            msg = ofp.message.packet_out(
+                in_port=ofp.OFPP_CONTROLLER,
+                actions=[ofp.action.output(port=of_port)],
+                buffer_id=ofp.OFP_NO_BUFFER,
+                data=pkt)
+
+            logging.info("PacketOut test, port %d", of_port)
+            self.controller.message_send(msg)
+            verify_packets(self, pkt, [of_port])
+
+class FlowRemoveAll(base_tests.SimpleProtocol):
+    """
+    Remove all flows; required for almost all tests
+
+    Add a bunch of flows, remove them, and then make sure there are no flows left
+    This is an intentionally naive test to see if the baseline functionality works
+    and should be a precondition to any more complicated deletion test (e.g.,
+    delete_strict vs. delete)
+    """
+    def runTest(self):
+        for i in range(1,5):
+            logging.debug("Adding flow %d", i)
+            request = ofp.message.flow_add(
+                buffer_id=ofp.OFP_NO_BUFFER,
+                priority=i*1000)
+            self.controller.message_send(request)
+        do_barrier(self.controller)
+
+        delete_all_flows(self.controller)
+
+        logging.info("Sending flow stats request")
+        stats = get_flow_stats(self, ofp.match())
+        self.assertEqual(len(stats), 0, "Expected empty flow stats reply")
+
+
+## Multipart messages
+
+class DescStats(base_tests.SimpleProtocol):
+    """
+    Switch description multipart transaction
+
+    Only verifies we get a single reply.
+    """
+    def runTest(self):
+        request = ofp.message.desc_stats_request()
+        logging.info("Sending desc stats request")
+        response, _ = self.controller.transact(request)
+        self.assertTrue(response != None, "No response to desc stats request")
+        logging.info(response.show())
+        self.assertEquals(response.flags, 0, "Unexpected bit set in desc stats reply flags")
+
+class FlowStats(base_tests.SimpleProtocol):
+    """
+    Flow stats multipart transaction
+
+    Only verifies we get a reply.
+    """
+    def runTest(self):
+        logging.info("Sending flow stats request")
+        stats = get_flow_stats(self, ofp.match())
+        logging.info("Received %d flow stats entries", len(stats))
+        for entry in stats:
+            logging.info(entry.show())
+
+class AggregateStats(base_tests.SimpleProtocol):
+    """
+    Aggregate flow stats multipart transaction
+
+    Only verifies we get a single reply.
+    """
+    def runTest(self):
+        request = ofp.message.aggregate_stats_request(
+            table_id=ofp.OFPTT_ALL,
+            out_port=ofp.OFPP_ANY,
+            out_group=ofp.OFPG_ANY,
+            cookie=0,
+            cookie_mask=0)
+        logging.info("Sending aggregate flow stats request")
+        response, _ = self.controller.transact(request)
+        self.assertTrue(response != None, "No response to aggregate stats request")
+        logging.info(response.show())
+        self.assertEquals(response.flags, 0, "Unexpected bit set in aggregate stats reply flags")
+
+class TableStats(base_tests.SimpleProtocol):
+    """
+    Table stats multipart transaction
+
+    Only verifies we get a reply.
+    """
+    def runTest(self):
+        logging.info("Sending table stats request")
+        stats = get_stats(self, ofp.message.table_stats_request())
+        logging.info("Received %d table stats entries", len(stats))
+        for entry in stats:
+            logging.info(entry.show())
+
+class PortStats(base_tests.SimpleProtocol):
+    """
+    Port stats multipart transaction
+
+    Only verifies we get a reply.
+    """
+    def runTest(self):
+        request = ofp.message.port_stats_request(port_no=ofp.OFPP_ANY)
+        logging.info("Sending port stats request")
+        stats = get_stats(self, request)
+        logging.info("Received %d port stats entries", len(stats))
+        for entry in stats:
+            logging.info(entry.show())
+
+class QueueStats(base_tests.SimpleProtocol):
+    """
+    Queue stats multipart transaction
+
+    Only verifies we get a reply.
+    """
+    def runTest(self):
+        request = ofp.message.queue_stats_request(port_no=ofp.OFPP_ANY,
+                                                  queue_id=ofp.OFPQ_ALL)
+        logging.info("Sending queue stats request")
+        stats = get_stats(self, request)
+        logging.info("Received %d queue stats entries", len(stats))
+        for entry in stats:
+            logging.info(entry.show())
+
+class GroupStats(base_tests.SimpleProtocol):
+    """
+    Group stats multipart transaction
+
+    Only verifies we get a reply.
+    """
+    def runTest(self):
+        request = ofp.message.group_stats_request(group_id=ofp.OFPG_ALL)
+        logging.info("Sending group stats request")
+        stats = get_stats(self, request)
+        logging.info("Received %d group stats entries", len(stats))
+        for entry in stats:
+            logging.info(entry.show())
+
+class GroupDescStats(base_tests.SimpleProtocol):
+    """
+    Group description multipart transaction
+
+    Only verifies we get a reply.
+    """
+    def runTest(self):
+        request = ofp.message.group_desc_stats_request()
+        logging.info("Sending group desc stats request")
+        stats = get_stats(self, request)
+        logging.info("Received %d group desc stats entries", len(stats))
+        for entry in stats:
+            logging.info(entry.show())
+
+@disabled   #FIXME ofdpa 
+class GroupFeaturesStats(base_tests.SimpleProtocol):
+    """
+    Group features multipart transaction
+
+    Only verifies we get a single reply.
+    """
+    def runTest(self):
+        request = ofp.message.group_features_stats_request()
+        logging.info("Sending group features stats request")
+        response, _ = self.controller.transact(request)
+        self.assertTrue(response != None, "No response to group features stats request")
+        logging.info(response.show())
+        self.assertEquals(response.flags, 0, "Unexpected bit set in group features stats reply flags")
+
+@disabled   #FIXME ofdpa
+class MeterStats(base_tests.SimpleProtocol):
+    """
+    Meter stats multipart transaction
+
+    Only verifies we get a reply.
+    """
+    def runTest(self):
+        request = ofp.message.meter_stats_request(meter_id=ofp.OFPM_ALL)
+        logging.info("Sending meter stats request")
+        stats = get_stats(self, request)
+        logging.info("Received %d meter stats entries", len(stats))
+        for entry in stats:
+            logging.info(entry.show())
+                                                                                   
+@disabled   #FIXME ofdpa  
+class MeterConfigStats(base_tests.SimpleProtocol):
+    """
+    Meter config multipart transaction
+
+    Only verifies we get a reply.
+    """
+    def runTest(self):
+        request = ofp.message.meter_config_stats_request(meter_id=ofp.OFPM_ALL)
+        logging.info("Sending meter config stats request")
+        stats = get_stats(self, request)
+        logging.info("Received %d meter config stats entries", len(stats))
+        for entry in stats:
+            logging.info(entry.show())
+
+@disabled   #FIXME ofdpa 
+class MeterFeaturesStats(base_tests.SimpleProtocol):
+    """
+    Meter features multipart transaction
+
+    Only verifies we get a single reply.
+    """
+    def runTest(self):
+        request = ofp.message.meter_features_stats_request()
+        logging.info("Sending meter features stats request")
+        response, _ = self.controller.transact(request)
+        self.assertTrue(response != None, "No response to meter features stats request")
+        logging.info(response.show())
+        self.assertEquals(response.flags, 0, "Unexpected bit set in meter features stats reply flags")
+
+@disabled # pyloxi does not yet support table features
+class TableFeaturesStats(base_tests.SimpleProtocol):
+    """
+    Table features multipart transaction
+
+    Only verifies we get a reply.
+    """
+    def runTest(self):
+        logging.info("Sending table features stats request")
+        stats = get_stats(self, ofp.message.table_features_stats_request())
+        logging.info("Received %d table features stats entries", len(stats))
+        for entry in stats:
+            logging.info(entry.show())
+
+class PortDescStats(base_tests.SimpleProtocol):
+    """
+    Port description multipart transaction
+
+    Only verifies we get a reply.
+    """
+    def runTest(self):
+        logging.info("Sending port desc stats request")
+        stats = get_stats(self, ofp.message.port_desc_stats_request())
+        logging.info("Received %d port desc stats entries", len(stats))
+        for entry in stats:
+            logging.info(entry.show())
+
+class PortConfigMod(base_tests.SimpleProtocol):
+    """
+    Modify a bit in port config and verify changed
+
+    Get the switch configuration, modify the port configuration
+    and write it back; get the config again and verify changed.
+    Then set it back to the way it was.
+    """
+
+    def runTest(self):
+        logging.info("Running " + str(self))
+        for of_port, _ in config["port_map"].items(): # Grab first port
+            break
+
+        (_, config1, _) = \
+            port_config_get(self.controller, of_port)
+        self.assertTrue(config is not None, "Did not get port config")
+
+        logging.debug("OFPPC_NO_PACKET_IN bit port " + str(of_port) + " is now " +
+                      str(config1 & ofp.OFPPC_NO_PACKET_IN))
+
+        rv = port_config_set(self.controller, of_port,
+                             config1 ^ ofp.OFPPC_NO_PACKET_IN,
+                             ofp.OFPPC_NO_PACKET_IN)
+        self.assertTrue(rv != -1, "Error sending port mod")
+
+        # Verify change took place with same feature request
+        (_, config2, _) = port_config_get(self.controller, of_port)
+        self.assertTrue(config2 is not None, "Did not get port config2")
+        logging.debug("OFPPC_NO_PACKET_IN bit port " + str(of_port) + " is now " +
+                      str(config2 & ofp.OFPPC_NO_PACKET_IN))
+        self.assertTrue(config2 & ofp.OFPPC_NO_PACKET_IN !=
+                        config1 & ofp.OFPPC_NO_PACKET_IN,
+                        "Bit change did not take")
+        # Set it back
+        rv = port_config_set(self.controller, of_port, config1,
+                             ofp.OFPPC_NO_PACKET_IN)
+        self.assertTrue(rv != -1, "Error sending port mod")
+
+@disabled #FIXME ofdpa
+class AsyncConfigGet(base_tests.SimpleProtocol):
+    """
+    Verify initial async config
+
+    Other tests rely on connections starting with these values.
+    """
+
+    def runTest(self):
+        logging.info("Sending get async config request")
+        response, _ = self.controller.transact(ofp.message.async_get_request())
+        self.assertTrue(response != None, "No response to get async config request")
+        logging.info(response.show())
+        self.assertEquals(response.packet_in_mask_equal_master & 0x07, 0x07)
+        self.assertEquals(response.port_status_mask_equal_master & 0x07, 0x07)
+        self.assertEquals(response.flow_removed_mask_equal_master & 0x0f, 0x0f)
diff --git a/ofdpa/dev.py b/ofdpa/dev.py
new file mode 100644
index 0000000..1a93957
--- /dev/null
+++ b/ofdpa/dev.py
@@ -0,0 +1,206 @@
+"""
+Flow Test
+Test each flow table can set entry, and packet rx correctly.
+1) L3UcastRoute
+2) QinQ
+"""
+
+import logging
+
+from oftest import config
+import oftest.base_tests as base_tests
+import ofp
+from oftest.testutils import *
+from accton_util import *
+
+class L3UcastRoute(base_tests.SimpleDataPlane):
+    """
+    Port1(vlan1, 0x00, 0x00, 0x00, 0x22, 0x22, 0x01, 192.168.1.1) , 
+    Port2(vlan2, 0x00, 0x00, 0x00, 0x22, 0x22, 0x02, 19.168.2.1)
+    """
+    def runTest(self):          
+        delete_all_flows(self.controller)
+        delete_all_groups(self.controller)
+
+        if len(config["port_map"]) <2:
+            logging.info("Port count less than 2, can't run this case")
+            return
+        
+        vlan_id=1
+        intf_src_mac=[0x00, 0x00, 0x00, 0xcc, 0xcc, 0xcc]
+        dst_mac=[0x00, 0x00, 0x00, 0x22, 0x22, 0x00]
+        dip=0xc0a80001
+        for port in config["port_map"].keys():
+            #add l2 interface group
+            add_one_l2_interface_grouop(self.controller, port, vlan_id=vlan_id, is_tagged=True, send_barrier=False)
+            dst_mac[5]=vlan_id
+            l3_msg=add_l3_unicast_group(self.controller, port, vlanid=vlan_id, id=vlan_id, src_mac=intf_src_mac, dst_mac=dst_mac)
+            #add vlan flow table
+            add_one_vlan_table_flow(self.controller, port, vlan_id, flag=VLAN_TABLE_FLAG_ONLY_BOTH)
+            #add termination flow
+            add_termination_flow(self.controller, port, 0x0800, intf_src_mac, vlan_id)           
+            #add unicast routing flow
+            dst_ip = dip + (vlan_id<<8)           
+            add_unicast_routing_flow(self.controller, 0x0800, dst_ip, 0, l3_msg.group_id)            
+            vlan_id += 1
+        
+        do_barrier(self.controller)  
+        
+        port1=config["port_map"].keys()[0]
+        port2=config["port_map"].keys()[1]
+        #port 1 to port 2        
+        switch_mac = ':'.join(['%02X' % x for x in intf_src_mac])
+        dst_mac[5]=1
+        port1_mac=':'.join(['%02X' % x for x in dst_mac])
+
+        parsed_pkt = simple_tcp_packet(pktlen=100, 
+                                       dl_vlan_enable=True,
+                                       vlan_vid=1,
+                                       eth_dst=switch_mac,
+                                       eth_src=port1_mac,
+                                       ip_ttl=64,
+                                       ip_src="192.168.1.1",
+                                       ip_dst='192.168.2.1')
+        pkt=str(parsed_pkt)
+        self.dataplane.send(port1, pkt)
+        #build expect packet
+        dst_mac[5]=2
+        port2_mac=':'.join(['%02X' % x for x in dst_mac])  
+        exp_pkt = simple_tcp_packet(pktlen=100,
+                                       dl_vlan_enable=True,
+                                       vlan_vid=2, 
+                                       eth_dst=port2_mac,
+                                       eth_src=switch_mac,
+                                       ip_ttl=63,
+                                       ip_src="192.168.1.1",
+                                       ip_dst='192.168.2.1')        
+        pkt=str(exp_pkt)
+        verify_packet(self, pkt, port2)
+        verify_no_other_packets(self)
+
+        #port 2 to port 1
+        switch_mac = ':'.join(['%02X' % x for x in intf_src_mac])
+        dst_mac[5]=2
+        port2_mac=':'.join(['%02X' % x for x in dst_mac])  
+
+        parsed_pkt = simple_tcp_packet(pktlen=100,
+                                       dl_vlan_enable=True,
+                                       vlan_vid=2, 
+                                       eth_dst=switch_mac,
+                                       eth_src=port2_mac,
+                                       ip_ttl=64,
+                                       ip_src="192.168.2.1",
+                                       ip_dst='192.168.1.1')
+        pkt=str(parsed_pkt)                                       
+        self.dataplane.send(port2, pkt)
+        #build expect packet
+        dst_mac[5]=1
+        port1_mac=':'.join(['%02X' % x for x in dst_mac])  
+        exp_pkt = simple_tcp_packet(pktlen=100,
+                                       dl_vlan_enable=True,
+                                       vlan_vid=1,
+                                       eth_dst=port1_mac,
+                                       eth_src=switch_mac,
+                                       ip_ttl=63,
+                                       ip_src="192.168.2.1",
+                                       ip_dst='192.168.1.1')        
+        pkt=str(exp_pkt) 
+        verify_packet(self, pkt, port1)
+        verify_no_other_packets(self)    
+
+
+class qinq(base_tests.SimpleDataPlane):
+    def runTest(self):
+        delete_all_flows(self.controller)
+        delete_all_groups(self.controller)   
+        
+        in_port = config["port_map"].keys()[0]
+        out_port = config["port_map"].keys()[1]        
+        out_vlan=10
+        #add_vlan_table_flow_pvid(self.controller, in_port, None, out_vlan, False)
+        add_vlan_table_flow_pvid(self.controller, in_port, 1,out_vlan, False)        
+        group_id, msg=add_one_l2_interface_grouop(self.controller, out_port, out_vlan,  True, False) 
+        #add acl 
+        match = ofp.match()
+        match.oxm_list.append(ofp.oxm.in_port(in_port))    
+        request = ofp.message.flow_add(
+                table_id=60,
+                cookie=42,
+                match=match,
+                instructions=[
+                    ofp.instruction.write_actions(
+                        actions=[
+                            ofp.action.group(msg.group_id)])
+                    ],
+                buffer_id=ofp.OFP_NO_BUFFER,
+                priority=1000) 
+        self.controller.message_send(request)  
+
+        #input tag packet
+        parsed_pkt = simple_tcp_packet(pktlen=104, dl_vlan_enable=True, vlan_vid=1)
+        pkt = str(parsed_pkt)
+        self.dataplane.send(in_port, pkt)
+    
+        parsed_pkt = simple_tcp_packet_two_vlan(pktlen=108, out_dl_vlan_enable=True, out_vlan_vid=10,
+                                                in_dl_vlan_enable=True, in_vlan_vid=1)
+        verify_packet(self, str(parsed_pkt), out_port)
+
+
+class FlowStats(base_tests.SimpleProtocol):
+    """
+    Flow stats multipart transaction
+    Only verifies we get a reply.
+    """
+    def runTest(self):
+        logging.info("Sending flow stats request")
+        stats = get_flow_stats(self, ofp.match())
+        logging.info("Received %d flow stats entries", len(stats))
+        for entry in stats:
+            logging.info(entry.show())
+
+
+class ACLStats(base_tests.SimpleProtocol):
+    """
+    Flow stats multipart transaction
+    Only verifies we get a reply.
+    """
+    def runTest(self):
+        logging.info("Installing ACL rule")
+        #delete_all_flows(self.controller)
+        #delete_all_groups(self.controller)
+
+        in_port = config["port_map"].keys()[0]
+        out_port=config["port_map"].keys()[1]
+        out_vlan=10
+        #add_vlan_table_flow_pvid(self.controller, in_port, None, out_vlan, False)
+        #add_vlan_table_flow_pvid(self.controller, in_port, 1,out_vlan, False)
+        group_id, msg=add_one_l2_interface_grouop(self.controller, out_port, out_vlan,  True, False)
+        inst=[ofp.instruction.write_actions(
+                        actions=[
+                            ofp.action.group(msg.group_id)])
+                    ],
+
+        #add acl
+        match = ofp.match()
+        match.oxm_list.append(ofp.oxm.in_port(in_port))
+        request = ofp.message.flow_add(
+                table_id=60,
+                cookie=42,
+                match=match,
+                instructions=inst,
+                buffer_id=ofp.OFP_NO_BUFFER,
+                priority=1000)
+        #self.controller.message_send(request)
+
+        logging.info("Sending flow stats request")
+        stats = get_flow_stats(self, match)
+        logging.info("Received %d flow stats entries", len(stats))
+        verify_flow_stats=[ofp.flow_stats_entry(
+                               table_id=60
+                               #cookie=42,
+                               #match=match,
+                               #instructions=inst,
+                               #priority=1000
+)]
+        self.assertEquals(stats, verify_flow_stats)
+
diff --git a/ofdpa/ofdpa_utils.py b/ofdpa/ofdpa_utils.py
new file mode 100644
index 0000000..4fa4d0a
--- /dev/null
+++ b/ofdpa/ofdpa_utils.py
@@ -0,0 +1,156 @@
+"""
+
+A set of inter-test utility functions for dealing with OF-DPA
+
+
+"""
+
+import logging
+import ofp
+
+from oftest import config
+from oftest.testutils import *
+
+
+
+class table(object):
+    """ Metadata on each OFDPA table """
+    def __init__(self, table_id, table_name):
+        self.table_id = table_id
+        self.table_name = table_name
+    # TODO consider adding type checking verification here
+
+INGRESS_TABLE           = table(0, "Ingress")
+VLAN_TABLE              = table(10, "VLAN")
+MACTERM_TABLE           = table(20, "MacTerm")
+UNICAST_ROUTING_TABLE   = table(30, "Unicast Routing")
+MULTICAST_ROUTING_TABLE = table(40, "Multicast Routing")
+BRIDGING_TABLE          = table(50, "Bridging Table")
+ACL_TABLE               = table(60, "ACL Policy Table")
+#.... FIXME add all tables
+
+DEFAULT_VLAN = 1
+
+def enableVlanOnPort(controller, vlan, port=ofp.OFPP_ALL, priority=0):
+    if port == ofp.OFPP_ALL:
+        ports = sorted(config["port_map"].keys())
+    else:
+        ports = [port]
+    for port in ports:
+        tagged_match = ofp.match([
+                ofp.oxm.in_port(port),
+                ofp.oxm.vlan_vid(vlan | ofp.OFPVID_PRESENT)
+                ])
+
+        request = ofp.message.flow_add(
+            table_id = VLAN_TABLE.table_id,
+            cookie = 0xdead,
+            match = tagged_match,
+            instructions = [
+                ofp.instruction.goto_table(MACTERM_TABLE.table_id),
+#                 ofp.instruction.apply_actions(
+#                     actions=[
+#                         ofp.action.push_vlan(ethertype=0x8100), # DO NOT PUT THIS FOR OF-DPA 2.0 EA1 - seems to not matter for EA2
+#                         ofp.action.set_field(ofp.oxm.vlan_vid( ofp.OFPVID_PRESENT | vlan))
+#                     ]),
+                    ],
+            buffer_id = ofp.OFP_NO_BUFFER,
+            priority = priority)
+
+        logging.info("Inserting vlan rule allowing tagged vlan %d on port %d" % (vlan, port))
+        controller.message_send(request)
+        do_barrier(controller)
+        verify_no_errors(controller)
+
+
+def installDefaultVlan(controller, vlan=DEFAULT_VLAN, port=ofp.OFPP_ALL, priority=0):
+    """ Insert a rule that maps all untagged traffic to vlan $vlan
+
+    In OFDPA, table 10 (the vlan table) requires that all traffic be
+    mapped to an internal vlan else the packets be dropped.  This function
+    sets up a default vlan mapping all untagged traffic to an internal VLAN.
+
+    With OF-DPA, before you can insert a 'untagged to X' rule on a
+    port, you must first insert a 'X --> X' rule for the same port.
+
+    Further, the 'X --> X' rule must set ofp.OFPVID_PRESENT even
+    though 'X' is non-zero.
+
+    The 'controller' variable is self.controller from a test
+    """
+    # OFDPA seems to be dumb and wants each port set individually
+    #       Can't set all ports by using OFPP_ALL
+    if port == ofp.OFPP_ALL:
+        ports = sorted(config["port_map"].keys())
+    else:
+        ports = [port]
+
+    for port in ports:
+        # enable this vlan on this port before we can map untagged packets to the vlan
+        enableVlanOnPort(controller, vlan, port)
+
+        untagged_match = ofp.match([
+                ofp.oxm.in_port(port),
+                # OFDPA 2.0 says untagged is vlan_id=0, mask=ofp.OFPVID_PRESENT
+                ofp.oxm.vlan_vid_masked(0,ofp.OFPVID_PRESENT)    # WTF OFDPA 2.0EA2 -- really!?
+                ])
+
+        request = ofp.message.flow_add(
+            table_id = VLAN_TABLE.table_id,
+            cookie = 0xbeef,
+            match = untagged_match,
+            instructions = [
+                ofp.instruction.apply_actions(
+                    actions=[
+                        #ofp.action.push_vlan(ethertype=0x8100),
+                        ofp.action.set_field(ofp.oxm.vlan_vid(ofp.OFPVID_PRESENT | vlan))
+                    ]),
+                ofp.instruction.goto_table(MACTERM_TABLE.table_id)   
+                    ],
+            buffer_id = ofp.OFP_NO_BUFFER,
+            priority = priority)
+
+        logging.info("Inserting default vlan sending all untagged traffic to vlan %d on port %d" % (vlan, port))
+        controller.message_send(request)
+        do_barrier(controller)
+        verify_no_errors(controller)
+
+
+_group_types = {
+    "L2 Interface": 0,
+    "L2 Rewrite" : 1,
+    "L3 Unicast" : 2,
+    "L2 Multicast" : 3,
+    "L2 Flood" : 4,
+    "L3 Interface" : 5,
+    "L3 Multicast": 6,
+    "L3 ECMP": 7,
+    "L2 Data Center Overlay": 8,
+    "MPLS Label" : 9,
+    "MPLS Forwarding" :10,
+    "L2 Unfiltered Interface": 11,
+    "L2 Loopback": 12,
+}
+
+
+def makeGroupID(groupType, local_id):
+    """ Group IDs in OF-DPA have rich meaning
+
+    @param groupType is a key in _group_types
+    @param local_id is an integer 0<= local_id < 2**27,
+        but it may have more semantic meaning depending on the
+        groupType
+
+
+    Read Section 4.3 of the OF-DPA manual on groups for 
+    details
+    """
+    if groupType not in _group_types:
+        raise KeyError("%s not a valid OF-DPA group type" % groupType)
+    if local_id < 0 or local_id >=134217728:
+        raise ValueError("local_id %d must be  0<= local_id < 2**27" % local_id)
+    return (_group_types[groupType] << 28) + local_id
+
+
+def delete_all_recursive_groups(controller):
+    pass