基于Mininet和P4实现节点间的“秘密盒子”协议

ChatGPT4o 使用 ChatGPT 4o 回答

问题:

在图 1所示的网络拓扑结构中(一个s1连着4个host),假设现在节点 h1 与节点 h3,节点 h2 与节点 h4 之间,采用“秘密盒子”的协议通信,1 个秘密盒子的形式为 key:value,value 为秘密的具体内容 (比如,数值 1),key 为秘密的钥匙。例 如,现在节点 h1 和节点 h3 已经协商好通信秘密盒子的 key 为 1,节点 h1 可以使用 key 打开秘密盒子,放入秘密内容 10(写入 key=1 的秘密盒子, value=10);在下一时刻,节点 h3 想要知道节点 1 给他发送了什么秘密, 于是使用 key 打开秘密盒子,获取秘密内容 10(读取 key=1 的秘密盒子, value=10)。 搭建图 1所示的网络拓扑结构 • 实现节点 h1 和节点 h3、节点 h2 和节点 h4 之间的秘密盒子协议,key 和 value 值可以自定义,假设节点之间已经协商好秘密盒子 key • 支持从控制平面使用 key 读取数据平面对应秘密盒子的 value 使用 register 数据结构

回答:

为了实现图1所示的网络拓扑结构,并在节点之间实现“秘密盒子”协议,我们可以使用Mininet来模拟网络拓扑,并使用P4语言来编写数据平面的行为。以下是详细的步骤和代码示例:

1. 搭建网络拓扑

首先,我们需要使用Mininet来创建一个包含一个交换机(s1)和四个主机(h1, h2, h3, h4)的网络拓扑。

from mininet.net import Mininet
from mininet.topo import Topo
from mininet.cli import CLI
from mininet.link import TCLink

class SimpleTopo(Topo):
    def build(self):
        switch = self.addSwitch('s1')
        h1 = self.addHost('h1')
        h2 = self.addHost('h2')
        h3 = self.addHost('h3')
        h4 = self.addHost('h4')
        
        self.addLink(h1, switch)
        self.addLink(h2, switch)
        self.addLink(h3, switch)
        self.addLink(h4, switch)

if __name__ == '__main__':
    topo = SimpleTopo()
    net = Mininet(topo=topo, link=TCLink)
    net.start()
    CLI(net)
    net.stop()

2. 编写P4程序

接下来,我们需要编写P4程序来实现“秘密盒子”协议。我们将使用P4的register数据结构来存储秘密盒子的内容。

// secret_box.p4
#include <core.p4>

const bit<32> SECRET_KEY_1 = 1;
const bit<32> SECRET_KEY_2 = 2;

register<bit<32>>(2) secret_box;

control ingress {
    apply {
        if (hdr.ipv4.isValid()) {
            if (hdr.ipv4.srcAddr == 10.0.0.1 && hdr.ipv4.dstAddr == 10.0.0.3) {
                // h1 to h3
                if (hdr.ipv4.protocol == 1) { // ICMP
                    secret_box.write(SECRET_KEY_1, hdr.ipv4.totalLen);
                }
            } else if (hdr.ipv4.srcAddr == 10.0.0.2 && hdr.ipv4.dstAddr == 10.0.0.4) {
                // h2 to h4
                if (hdr.ipv4.protocol == 1) { // ICMP
                    secret_box.write(SECRET_KEY_2, hdr.ipv4.totalLen);
                }
            }
        }
    }
}

control egress {
    apply {
        if (hdr.ipv4.isValid()) {
            if (hdr.ipv4.srcAddr == 10.0.0.3 && hdr.ipv4.dstAddr == 10.0.0.1) {
                // h3 to h1
                if (hdr.ipv4.protocol == 1) { // ICMP
                    hdr.ipv4.totalLen = secret_box.read(SECRET_KEY_1);
                }
            } else if (hdr.ipv4.srcAddr == 10.0.0.4 && hdr.ipv4.dstAddr == 10.0.0.2) {
                // h4 to h2
                if (hdr.ipv4.protocol == 1) { // ICMP
                    hdr.ipv4.totalLen = secret_box.read(SECRET_KEY_2);
                }
            }
        }
    }
}

control MyIngress(inout headers hdr, inout metadata meta, inout standard_metadata_t standard_metadata) {
    apply {
        ingress.apply();
        egress.apply();
    }
}

control MyEgress(inout headers hdr, inout metadata meta, inout standard_metadata_t standard_metadata) {
    apply {
        // No egress processing
    }
}

control MyDeparser(packet_out pkt, in headers hdr) {
    apply {
        pkt.emit(hdr.ethernet);
        pkt.emit(hdr.ipv4);
    }
}

control MyParser(packet_in pkt, out headers hdr, inout metadata meta) {
    apply {
        pkt.extract(hdr.ethernet);
        pkt.extract(hdr.ipv4);
    }
}

package MySwitch(MyParser(), MyIngress(), MyEgress(), MyDeparser());

3. 运行P4程序

将P4程序编译并加载到交换机上。可以使用BMv2(Behavioral Model version 2)作为P4的运行时环境。

p4c-bm2-ss --arch v1model --std p4-16 secret_box.p4 -o secret_box.json
sudo simple_switch_grpc --log-console -i 0@eth0 -i 1@eth1 -i 2@eth2 -i 3@eth3 secret_box.json

4. 控制平面读取数据平面秘密盒子的值

我们可以使用P4Runtime API从控制平面读取数据平面中register的值。

from p4runtime_lib.switch import ShutdownAllSwitchConnections
from p4runtime_lib.helper import P4InfoHelper

p4info_helper = P4InfoHelper('build/secret_box.p4info')

def read_register(p4info_helper, sw, register_name, index):
    for response in sw.ReadRegister(p4info_helper.get_register_id(register_name), index):
        for entity in response.entities:
            register_entry = entity.register_entry
            print(f"Register {register_name}[{index}] = {register_entry.data}")

if __name__ == '__main__':
    try:
        p4info_helper = P4InfoHelper('build/secret_box.p4info')
        s1 = p4runtime_lib.bmv2.Bmv2SwitchConnection(
            name='s1',
            address='127.0.0.1:50051',
            device_id=0,
            proto_dump_file='logs/s1-p4runtime-requests.txt'
        )
        s1.MasterArbitrationUpdate()
        s1.SetForwardingPipelineConfig(p4info=p4info_helper.p4info, bmv2_json_file_path='build/secret_box.json')

        read_register(p4info_helper, s1, 'secret_box', 0)
        read_register(p4info_helper, s1, 'secret_box', 1)

    except KeyboardInterrupt:
        print("Shutting down.")
    except grpc.RpcError as e:
        print(f"gRPC Error: {e.details()} ({e.code()})")
    finally:
        ShutdownAllSwitchConnections()

通过以上步骤,我们可以搭建一个包含一个交换机和四个主机的网络拓扑,并实现节点之间的“秘密盒子”协议。控制平面可以使用P4Runtime API读取数据平面中register的值。