-
Notifications
You must be signed in to change notification settings - Fork 920
Description
#!/usr/bin/env python3
SPDX-License-Identifier: Apache-2.0
import argparse
import os
import sys
from time import sleep
import datetime
import json
import grpc
Import P4Runtime lib from parent utils dir
sys.path.append(
os.path.join(os.path.dirname(os.path.abspath(file)),
'../../utils/'))
import p4runtime_lib.bmv2
import p4runtime_lib.helper
from p4runtime_lib.error_utils import printGrpcError
from p4runtime_lib.switch import ShutdownAllSwitchConnections
SWITCH_TO_HOST_PORT = 1
SWITCH_TO_SWITCH_PORT_1 = 2
SWITCH_TO_SWITCH_PORT_2 = 3
SWITCH_TO_SWITCH_PORT_3 = 3
COUNTER_FILES = {
's1_ingress': 'logs/s1_ingress_counters.json',
's1_egress': 'logs/s1_egress_counters.json',
's2_ingress': 'logs/s2_ingress_counters.json',
's2_egress': 'logs/s2_egress_counters.json',
's3_ingress': 'logs/s3_ingress_counters.json',
's3_egress': 'logs/s3_egress_counters.json'
}
def ensure_log_directory():
"""确保日志目录存在"""
os.makedirs('logs', exist_ok=True)
def write_counter_to_file(counter_data, filename):
"""将计数器数据写入文件"""
try:
with open(filename, 'w') as f:
json.dump(counter_data, f, indent=2)
except Exception as e:
print(f"Error writing counter data to {filename}: {e}")
def writeTunnelRules(p4info_helper, ingress_sw, egress_sw, tunnel_id,
dst_eth_addr, dst_ip_addr, transit_port):
"""
安装完整的隧道规则
"""
table_entry = p4info_helper.buildTableEntry(
table_name="MyIngress.ipv4_lpm",
match_fields={
"hdr.ipv4.dstAddr": (dst_ip_addr, 32)
},
action_name="MyIngress.myTunnel_ingress",
action_params={
"dst_id": tunnel_id,
})
ingress_sw.WriteTableEntry(table_entry)
print(f"Installed ingress tunnel rule on {ingress_sw.name} for {dst_ip_addr} -> tunnel {tunnel_id}")
table_entry = p4info_helper.buildTableEntry(
table_name="MyIngress.myTunnel_exact",
match_fields={
"hdr.myTunnel.dst_id": tunnel_id
},
action_name="MyIngress.myTunnel_forward",
action_params={
"port": transit_port
})
ingress_sw.WriteTableEntry(table_entry)
print(f"Installed transit tunnel rule on {ingress_sw.name} for tunnel {tunnel_id} -> port {transit_port}")
table_entry = p4info_helper.buildTableEntry(
table_name="MyIngress.myTunnel_exact",
match_fields={
"hdr.myTunnel.dst_id": tunnel_id
},
action_name="MyIngress.myTunnel_egress",
action_params={
"dstAddr": dst_eth_addr,
"port": SWITCH_TO_HOST_PORT
})
egress_sw.WriteTableEntry(table_entry)
print(f"Installed egress tunnel rule on {egress_sw.name} for tunnel {tunnel_id} -> {dst_eth_addr}")
def writeDirectRoutingRules(p4info_helper, sw, host_ip, host_mac, port):
"""
安装直接路由规则,用于非隧道通信
"""
table_entry = p4info_helper.buildTableEntry(
table_name="MyIngress.ipv4_lpm",
match_fields={
"hdr.ipv4.dstAddr": (host_ip, 32)
},
action_name="MyIngress.ipv4_forward",
action_params={
"dstAddr": host_mac,
"port": port
})
sw.WriteTableEntry(table_entry)
print(f"Installed direct routing rule on {sw.name} for {host_ip} -> port {port}")
def readTableRules(p4info_helper, sw):
"""
读取交换机上的所有表规则
"""
print(f'\n----- Reading tables rules for {sw.name} -----')
try:
for response in sw.ReadTableEntries():
for entity in response.entities:
entry = entity.table_entry
table_name = p4info_helper.get_tables_name(entry.table_id)
print(f"Table: {table_name}")
print(f"Match: {entry.match}")
print(f"Action: {entry.action}")
print('-----')
except Exception as e:
print(f"Error reading table rules from {sw.name}: {e}")
def readTunnelCounter(p4info_helper, sw, counter_name, index):
"""
读取指定计数器的值
"""
try:
counter_id = p4info_helper.get_counters_id(counter_name)
for response in sw.ReadCounters(counter_id, index):
for entity in response.entities:
counter = entity.counter_entry
return counter.data.packet_count, counter.data.byte_count
except Exception as e:
print(f"Error reading counter {counter_name} from {sw.name}: {e}")
return 0, 0
def printTunnelCounters(p4info_helper, s1, s2, s3):
"""
按照指定格式打印隧道计数器
"""
print("\nReading tunnel counters ---")
s1_ingress_packets, s1_ingress_bytes = readTunnelCounter(p4info_helper, s1, "MyIngress.ingressTunnelCounter", 100)
s2_egress_packets, s2_egress_bytes = readTunnelCounter(p4info_helper, s2, "MyIngress.egressTunnelCounter", 100)
print(f"--- s1 -> s2 ---")
print(f"s1 MyIngress.ingressTunnelCounter 100: {s1_ingress_packets} packets ({s1_ingress_bytes} bytes)")
print(f"s2 MyIngress.egressTunnelCounter 100: {s2_egress_packets} packets ({s2_egress_bytes} bytes)")
print()
s2_ingress_packets, s2_ingress_bytes = readTunnelCounter(p4info_helper, s2, "MyIngress.ingressTunnelCounter", 101)
s1_egress_packets, s1_egress_bytes = readTunnelCounter(p4info_helper, s1, "MyIngress.egressTunnelCounter", 101)
print(f"--- s2 -> s1 ---")
print(f"s2 MyIngress.ingressTunnelCounter 101: {s2_ingress_packets} packets ({s2_ingress_bytes} bytes)")
print(f"s1 MyIngress.egressTunnelCounter 101: {s1_egress_packets} packets ({s1_egress_bytes} bytes)")
print()
s1_ingress_packets, s1_ingress_bytes = readTunnelCounter(p4info_helper, s1, "MyIngress.ingressTunnelCounter", 200)
s3_egress_packets, s3_egress_bytes = readTunnelCounter(p4info_helper, s3, "MyIngress.egressTunnelCounter", 200)
print(f"--- s1 -> s3 ---")
print(f"s1 MyIngress.ingressTunnelCounter 200: {s1_ingress_packets} packets ({s1_ingress_bytes} bytes)")
print(f"s3 MyIngress.egressTunnelCounter 200: {s3_egress_packets} packets ({s3_egress_bytes} bytes)")
print()
s3_ingress_packets, s3_ingress_bytes = readTunnelCounter(p4info_helper, s3, "MyIngress.ingressTunnelCounter", 201)
s1_egress_packets, s1_egress_bytes = readTunnelCounter(p4info_helper, s1, "MyIngress.egressTunnelCounter", 201)
print(f"--- s3 -> s1 ---")
print(f"s3 MyIngress.ingressTunnelCounter 201: {s3_ingress_packets} packets ({s3_ingress_bytes} bytes)")
print(f"s1 MyIngress.egressTunnelCounter 201: {s1_egress_packets} packets ({s1_egress_bytes} bytes)")
print()
s2_ingress_packets, s2_ingress_bytes = readTunnelCounter(p4info_helper, s2, "MyIngress.ingressTunnelCounter", 300)
s3_egress_packets, s3_egress_bytes = readTunnelCounter(p4info_helper, s3, "MyIngress.egressTunnelCounter", 300)
print(f"--- s2 -> s3 ---")
print(f"s2 MyIngress.ingressTunnelCounter 300: {s2_ingress_packets} packets ({s2_ingress_bytes} bytes)")
print(f"s3 MyIngress.egressTunnelCounter 300: {s3_egress_packets} packets ({s3_egress_bytes} bytes)")
print()
s3_ingress_packets, s3_ingress_bytes = readTunnelCounter(p4info_helper, s3, "MyIngress.ingressTunnelCounter", 301)
s2_egress_packets, s2_egress_bytes = readTunnelCounter(p4info_helper, s2, "MyIngress.egressTunnelCounter", 301)
print(f"--- s3 -> s2 ---")
print(f"s3 MyIngress.ingressTunnelCounter 301: {s3_ingress_packets} packets ({s3_ingress_bytes} bytes)")
print(f"s2 MyIngress.egressTunnelCounter 301: {s2_egress_packets} packets ({s2_egress_bytes} bytes)")
def install_complete_routing(p4info_helper, s1, s2, s3):
"""
安装完整的路由规则,确保所有主机互通
"""
print("\n" + "="*60)
print("INSTALLING COMPLETE ROUTING RULES")
print("="*60)
print("\n=== Installing Direct Routing Rules ===")
writeDirectRoutingRules(p4info_helper, s1, "10.0.1.1", "08:00:00:00:01:11", 1) # h1
writeDirectRoutingRules(p4info_helper, s1, "10.0.2.2", "08:00:00:00:02:22", 2) # h2 via s2
writeDirectRoutingRules(p4info_helper, s1, "10.0.3.3", "08:00:00:00:03:33", 3) # h3 via s3
writeDirectRoutingRules(p4info_helper, s2, "10.0.2.2", "08:00:00:00:02:22", 1) # h2
writeDirectRoutingRules(p4info_helper, s2, "10.0.1.1", "08:00:00:00:01:11", 2) # h1 via s1
writeDirectRoutingRules(p4info_helper, s2, "10.0.3.3", "08:00:00:00:03:33", 3) # h3 via s3
writeDirectRoutingRules(p4info_helper, s3, "10.0.3.3", "08:00:00:00:03:33", 1) # h3
writeDirectRoutingRules(p4info_helper, s3, "10.0.1.1", "08:00:00:00:01:11", 2) # h1 via s1
writeDirectRoutingRules(p4info_helper, s3, "10.0.2.2", "08:00:00:00:02:22", 3) # h2 via s2
print("\n=== Installing Tunnel Rules ===")
writeTunnelRules(p4info_helper, s1, s2, 100, "08:00:00:00:02:22", "10.0.2.2", 2)
writeTunnelRules(p4info_helper, s2, s1, 101, "08:00:00:00:01:11", "10.0.1.1", 2)
writeTunnelRules(p4info_helper, s1, s3, 200, "08:00:00:00:03:33", "10.0.3.3", 3)
writeTunnelRules(p4info_helper, s3, s1, 201, "08:00:00:00:01:11", "10.0.1.1", 2)
writeTunnelRules(p4info_helper, s2, s3, 300, "08:00:00:00:03:33", "10.0.3.3", 3)
writeTunnelRules(p4info_helper, s3, s2, 301, "08:00:00:00:02:22", "10.0.2.2", 3)
print("\n✓ All routing rules installed successfully!")
print(" You should now be able to pingall and get 6/6")
def install_rules_gradually(p4info_helper, s1, s2, s3):
"""
逐步安装路由规则,模拟网络建立过程
"""
print("\n" + "="*60)
print("STARTING GRADUAL RULE INSTALLATION")
print("="*60)
print("\n=== Phase 1: Direct host connections only ===")
print("Expected pingall result: 0/6 (no inter-switch connectivity)")
sleep(3)
writeDirectRoutingRules(p4info_helper, s1, "10.0.1.1", "08:00:00:00:01:11", 1)
writeDirectRoutingRules(p4info_helper, s2, "10.0.2.2", "08:00:00:00:02:22", 1)
writeDirectRoutingRules(p4info_helper, s3, "10.0.3.3", "08:00:00:00:03:33", 1)
print("\n✓ Phase 1 complete - Only direct host connections installed")
print(" You can now test: mininet> pingall (should show 0/6)")
print(" Waiting 10 seconds for testing...")
sleep(10)
print("\n=== Phase 4: Adding s2-s3 connectivity ===")
print("Expected pingall result: 6/6 (full connectivity)")
sleep(3)
writeDirectRoutingRules(p4info_helper, s2, "10.0.3.3", "08:00:00:00:03:33", 3)
writeDirectRoutingRules(p4info_helper, s3, "10.0.2.2", "08:00:00:00:02:22", 3)
print("\n✓ Phase 4 complete - Full network connectivity achieved!")
print(" You can now test: mininet> pingall (should show 6/6)")
print(" All rules installed successfully!")
print("\n" + "="*60)
print("GRADUAL INSTALLATION COMPLETE")
print("="*60)
def readAllCounters(p4info_helper, sw):
"""
读取交换机的所有计数器并返回结构化数据
"""
counter_data = {
'timestamp': datetime.datetime.now().isoformat(),
'switch': sw.name,
'counters': {}
}
try:
for response in sw.ReadCounters(p4info_helper.get_counters_id("MyIngress.ingressTunnelCounter"), 0):
for entity in response.entities:
counter = entity.counter_entry
counter_name = f"ingress_tunnel_{counter.index}"
counter_data['counters'][counter_name] = {
'packets': counter.data.packet_count,
'bytes': counter.data.byte_count,
'index': counter.index
}
for response in sw.ReadCounters(p4info_helper.get_counters_id("MyIngress.egressTunnelCounter"), 0):
for entity in response.entities:
counter = entity.counter_entry
counter_name = f"egress_tunnel_{counter.index}"
counter_data['counters'][counter_name] = {
'packets': counter.data.packet_count,
'bytes': counter.data.byte_count,
'index': counter.index
}
except Exception as e:
print(f"Error reading counters from {sw.name}: {e}")
return counter_data
def printAndSaveCounters(p4info_helper, s1, s2, s3):
"""
读取所有计数器并打印,同时保存到文件
"""
print('\n----- Reading all counters -----')
s1_counters = readAllCounters(p4info_helper, s1)
s2_counters = readAllCounters(p4info_helper, s2)
s3_counters = readAllCounters(p4info_helper, s3)
print(f"\n--- {s1.name} Counters ---")
for counter_name, counter_data in s1_counters['counters'].items():
print(f"{counter_name}: {counter_data['packets']} packets, {counter_data['bytes']} bytes")
print(f"\n--- {s2.name} Counters ---")
for counter_name, counter_data in s2_counters['counters'].items():
print(f"{counter_name}: {counter_data['packets']} packets, {counter_data['bytes']} bytes")
print(f"\n--- {s3.name} Counters ---")
for counter_name, counter_data in s3_counters['counters'].items():
print(f"{counter_name}: {counter_data['packets']} packets, {counter_data['bytes']} bytes")
write_counter_to_file(s1_counters, COUNTER_FILES['s1_ingress'])
write_counter_to_file(s2_counters, COUNTER_FILES['s2_ingress'])
write_counter_to_file(s3_counters, COUNTER_FILES['s3_ingress'])
print("Counter data saved to log files")
def main(p4info_file_path, bmv2_file_path):
ensure_log_directory()
p4info_helper = p4runtime_lib.helper.P4InfoHelper(p4info_file_path)
try:
s1 = p4runtime_lib.bmv2.Bmv2SwitchConnection(
name='s1',
address='127.0.0.1:50051',
device_id=0,
proto_dump_file='logs/s1-p4runtime-requests.txt')
s2 = p4runtime_lib.bmv2.Bmv2SwitchConnection(
name='s2',
address='127.0.0.1:50052',
device_id=1,
proto_dump_file='logs/s2-p4runtime-requests.txt')
s3 = p4runtime_lib.bmv2.Bmv2SwitchConnection(
name='s3',
address='127.0.0.1:50053',
device_id=2,
proto_dump_file='logs/s3-p4runtime-requests.txt')
s1.MasterArbitrationUpdate()
s2.MasterArbitrationUpdate()
s3.MasterArbitrationUpdate()
s1.SetForwardingPipelineConfig(p4info=p4info_helper.p4info,
bmv2_json_file_path=bmv2_file_path)
print("Installed P4 Program using SetForwardingPipelineConfig on s1")
s2.SetForwardingPipelineConfig(p4info=p4info_helper.p4info,
bmv2_json_file_path=bmv2_file_path)
print("Installed P4 Program using SetForwardingPipelineConfig on s2")
s3.SetForwardingPipelineConfig(p4info=p4info_helper.p4info,
bmv2_json_file_path=bmv2_file_path)
print("Installed P4 Program using SetForwardingPipelineConfig on s3")
print("\nSelect installation mode:")
print("1. Complete installation (immediate 6/6 connectivity)")
print("2. Gradual installation (0/6 -> 2/6 -> 4/6 -> 6/6)")
choice = input("Enter choice (1 or 2, default 1): ").strip()
if choice == "2":
install_rules_gradually(p4info_helper, s1, s2, s3)
else:
install_complete_routing(p4info_helper, s1, s2, s3)
print("\n----- Reading final table rules -----")
readTableRules(p4info_helper, s1)
readTableRules(p4info_helper, s2)
readTableRules(p4info_helper, s3)
# 持续监控计数器
print("\n----- Starting counter monitoring -----")
counter = 0
while True:
sleep(5)
counter += 1
print(f'\n----- Counter reading #{counter} -----')
printTunnelCounters(p4info_helper, s1, s2, s3)
if counter % 5 == 0:
print("\n----- Periodic table rules check -----")
readTableRules(p4info_helper, s1)
readTableRules(p4info_helper, s2)
readTableRules(p4info_helper, s3)
except KeyboardInterrupt:
print("\nFininshed ---")
printTunnelCounters(p4info_helper, s1, s2, s3)
except grpc.RpcError as e:
printGrpcError(e)
ShutdownAllSwitchConnections()
if name == 'main':
parser = argparse.ArgumentParser(description='P4Runtime Controller')
parser.add_argument('--p4info', help='p4info proto in text format from p4c',
type=str, action="store", required=False,
default='./build/advanced_tunnel.p4.p4info.txt')
parser.add_argument('--bmv2-json', help='BMv2 JSON file from p4c',
type=str, action="store", required=False,
default='./build/advanced_tunnel.json')
args = parser.parse_args()
if not os.path.exists(args.p4info):
parser.print_help()
print("\np4info file not found: %s\nHave you run 'make'?" % args.p4info)
parser.exit(1)
if not os.path.exists(args.bmv2_json):
parser.print_help()
print("\nBMv2 JSON file not found: %s\nHave you run 'make'?" % args.bmv2_json)
parser.exit(1)
main(args.p4info, args.bmv2_json)