| 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, 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)) |
| 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 |
| |
| 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 |