P4设计实现链路监控

news2024/11/19 15:20:11

实验要求

  • 在本次实验中,目的是编写一个P4程序,使主机能够监控网络中所有链路的使用情况
  • 本练习基于基本的IPv4转发练习,因此请确保在尝试此练习之前完成此练习(basic.p4)
  • 具体来说,我们将修改基本P4程序以处理源路由探测包,以便它能够在每个跳处获取出口链路利用率,并将其传递给主机以进行监控。 

实验内容

Step1:设计包头 

1.为了获取包经过了多少跳,定义了一个包头 probe_t

header probe_t {
    bit<8> hop_cnt;
}

2.在每一跳的过程中,收集我们想要的信息,比如交换机的端口号,进出时间等

header probe_data_t {
    bit<1>    bos;
    bit<7>    swid;
    bit<8>    port;
    bit<32>   byte_cnt;
    time_t    last_time;
    time_t    cur_time;
}

3.指定交换机应该将这个探测包发往哪里,保证了探测包的确定性转发路径

header probe_fwd_t {
    bit<8>   egress_spec;
}

包头的总体结构如下,但是实际的包头结构要么是ipv4包要么是探测包

struct headers {
    ethernet_t              ethernet;
    ipv4_t                  ipv4;
    probe_t                 probe;
    probe_data_t[MAX_HOPS]  probe_data;
    probe_fwd_t[MAX_HOPS]   probe_fwd;
}

Step2: 设计两个寄存器

`byte_cnt_reg` :统计自最后一个探测数据包从端口发送出去以来从每个端口发送出去的字节数。

 `last_time_reg`:存储上一个探测包从端口出去的最后时间

 Step3: 对照实验

        先把没有完善的p4代码编译并载入交换机创建mininet运行一下,我打开同一个主机的两个终端,一个发送数据包,一个接收数据包,至于数据包的发送原理,就是简单的python网络编程提及的东西,我在以后放在java网络编程一块讲。

        可以看到,接收端收到的域都是0,并没有提现任何信息,这是因为我的监测模块还没实现

 Step4: 控制面逻辑

控制面逻辑的json主要包含4个元素,分别是p4目标,也就是底层实现,以及p4info,这是p4文件编译后生成的信息文件,还有bmv2_json指的是p4文件编译后的json,最后一个是table_entires,描述了table里面每一个键值对,注意到,和以往不同的是,这里有Egress控制流的表了(swid)

{
  "target": "bmv2",
  "p4info": "build/link_monitor.p4.p4info.txt",
  "bmv2_json": "build/link_monitor.json",
  "table_entries": [
    {
      "table": "MyEgress.swid",
      "default_action": true,
      "action_name": "MyEgress.set_swid",
      "action_params": {
         "swid": 2
       }
    },
    {
      "table": "MyIngress.ipv4_lpm",
      "default_action": true,
      "action_name": "MyIngress.drop",
      "action_params": { }
    },
    {
      "table": "MyIngress.ipv4_lpm",
      "match": {
        "hdr.ipv4.dstAddr": ["10.0.1.1", 32]
      },
      "action_name": "MyIngress.ipv4_forward",
      "action_params": {
        "dstAddr": "08:00:00:00:03:00",
        "port": 4
      }
    },
    {
      "table": "MyIngress.ipv4_lpm",
      "match": {
        "hdr.ipv4.dstAddr": ["10.0.2.2", 32]
      },
      "action_name": "MyIngress.ipv4_forward",
      "action_params": {
        "dstAddr": "08:00:00:00:04:00",
        "port": 3
      }
    },
    {
      "table": "MyIngress.ipv4_lpm",
      "match": {
        "hdr.ipv4.dstAddr": ["10.0.3.3", 32]
      },
      "action_name": "MyIngress.ipv4_forward",
      "action_params": {
        "dstAddr": "08:00:00:00:03:33",
        "port": 1
      }
    },
    {
      "table": "MyIngress.ipv4_lpm",
      "match": {
        "hdr.ipv4.dstAddr": ["10.0.4.4", 32]
      },
      "action_name": "MyIngress.ipv4_forward",
      "action_params": {
        "dstAddr": "08:00:00:00:04:44",
        "port": 2
      }
    }
  ]
}

Step5:数据平面逻辑设计(解析器)

解析器设计,我们先要解析这些包,在设计解析器逻辑的时候,可以看出:

  • 我们的包要么是普通的ipv4包,要么是探测包
  • 探测包经历的跳数被赋值给了 meta.parser_metadata.remaining也就是元数据中的解析器剩余要解析的探测信息
  • 如果已经剥开到第0条的数据了,那就解析它的转发包头,这里注意,next指针指向当前的包的转发包头的数组的当前位置,每次调用完都会指向下一个,last指的是上一次提取出来的包头信息,指的就是上一次next指示的东西。如果解析到remaining=0就accept
  • 如果剥开还不是第0条,就去循环剥开它的探测包的data,知道bos=1也就是剥到了最后一个probe_data的包头了,就去剥fwd包头,这个时候就不用去设置什么bos了,因为fwd每次循环剥开就倒扣remaining就可以了,(那为啥不在data包头就扣掉呢?因为这个remaining要在fwd用,先用了话就是0了,根本不会解析fwd包头,那如果再设置一个remaing来剥开这个data包头不就好了。。。这样还可以节省一个bos位😂)

 

/*************************************************************************
*********************** P A R S E R  ***********************************
*************************************************************************/

parser MyParser(packet_in packet,
                out headers hdr,
                inout metadata meta,
                inout standard_metadata_t standard_metadata) {

    state start {
        transition parse_ethernet;
    }

    state parse_ethernet {
        packet.extract(hdr.ethernet);
        transition select(hdr.ethernet.etherType) {
            TYPE_IPV4: parse_ipv4;
            TYPE_PROBE: parse_probe;
            default: accept;
        }
    }

    state parse_ipv4 {
        packet.extract(hdr.ipv4);
        transition accept;
    }

    state parse_probe {
        packet.extract(hdr.probe);
        meta.parser_metadata.remaining = hdr.probe.hop_cnt + 1;
        transition select(hdr.probe.hop_cnt) {
            0: parse_probe_fwd;
            default: parse_probe_data;
        }
    }

    state parse_probe_data {
        packet.extract(hdr.probe_data.next);
        transition select(hdr.probe_data.last.bos) {
            1: parse_probe_fwd;
            default: parse_probe_data;
        }
    }

    state parse_probe_fwd {
        packet.extract(hdr.probe_fwd.next);
        meta.parser_metadata.remaining = meta.parser_metadata.remaining - 1;
        // extract the forwarding data
        meta.egress_spec = hdr.probe_fwd.last.egress_spec;
        transition select(meta.parser_metadata.remaining) {
            0: accept;
            default: parse_probe_fwd;
        }
    }
}

Step6:数据平面的Ingress控制流

 Ingress控制流设计,在这个入口设计中可以看到,和basic.p4差不多,除了这个apply中,增加了probe包头的操作,定义一下要出去的端口是哪个,然后去给它加一跳

control MyIngress(inout headers hdr,
                  inout metadata meta,
                  inout standard_metadata_t standard_metadata) {

    action drop() {
        mark_to_drop(standard_metadata);
    }

    action ipv4_forward(macAddr_t dstAddr, egressSpec_t port) {
        standard_metadata.egress_spec = port;
        hdr.ethernet.srcAddr = hdr.ethernet.dstAddr;
        hdr.ethernet.dstAddr = dstAddr;
        hdr.ipv4.ttl = hdr.ipv4.ttl - 1;
    }

    table ipv4_lpm {
        key = {
            hdr.ipv4.dstAddr: lpm;
        }
        actions = {
            ipv4_forward;
            drop;
            NoAction;
        }
        size = 1024;
        default_action = drop();
    }

    apply {
        if (hdr.ipv4.isValid()) {
            ipv4_lpm.apply();
        }
        else if (hdr.probe.isValid()) {
            standard_metadata.egress_spec = (bit<9>)meta.egress_spec;
            hdr.probe.hop_cnt = hdr.probe.hop_cnt + 1;
        }
    }

Step7:数据平面的Egress控制流

在出口控制流这边,我们需要给包附上它的包头信息

  • 在这里就用上了最早提到了两个寄存器,分别是字节计数器和时间寄存器
  • 定义了一个设置交换机id的操作,给探测包的data字段中的swid设置值
  • 在table swid中,并没有设置任何的键,这是因为对于每一个探测包我们都这么做
  • 定义最后时间,设置当前时间,从standard_metadata.egress_global_timestamp就可以拿到当前时间
  • 把寄存器里面的计数器的值,读取到byte_cnt中,然后对byte_cnt = byte_cnt + standard_metadata.packet_length;
  • 如果来了个探测包,计数器刷新,但是计数器的值存在了byte_cnt总给未来赋上包头用
  • 探测包进入这个交换机中,就头插一个data包头进去,如果经历跳数是1(跳数的计算早在解析器的时候就弄了),说明这个是第一跳,就把这个包头设为最后一个data包头
  • 运用swid表,也就等于是指定了下一跳的方向
/*************************************************************************
****************  E G R E S S   P R O C E S S I N G   ********************
*************************************************************************/

control MyEgress(inout headers hdr,
                 inout metadata meta,
                 inout standard_metadata_t standard_metadata) {

    // count the number of bytes seen since the last probe
    register<bit<32>>(MAX_PORTS) byte_cnt_reg;
    // remember the time of the last probe
    register<time_t>(MAX_PORTS) last_time_reg;

    action set_swid(bit<7> swid) {
        hdr.probe_data[0].swid = swid;
    }

    table swid {
        actions = {
            set_swid;
            NoAction;
        }
        default_action = NoAction();
    }

    apply {
        bit<32> byte_cnt;
        bit<32> new_byte_cnt;
        time_t last_time;
        time_t cur_time = standard_metadata.egress_global_timestamp;
        // increment byte cnt for this packet's port
        byte_cnt_reg.read(byte_cnt, (bit<32>)standard_metadata.egress_port);
        byte_cnt = byte_cnt + standard_metadata.packet_length;
        // reset the byte count when a probe packet passes through
        new_byte_cnt = (hdr.probe.isValid()) ? 0 : byte_cnt;
        byte_cnt_reg.write((bit<32>)standard_metadata.egress_port, new_byte_cnt);

        if (hdr.probe.isValid()) {
            // fill out probe fields
            hdr.probe_data.push_front(1);
            hdr.probe_data[0].setValid();
            if (hdr.probe.hop_cnt == 1) {
                hdr.probe_data[0].bos = 1;
            }
            else {
                hdr.probe_data[0].bos = 0;
            }
            // set switch ID field
            swid.apply();
            // TODO: fill out the rest of the probe packet fields
            // hdr.probe_data[0].port = ...
            // hdr.probe_data[0].byte_cnt = ...
            // TODO: read / update the last_time_reg
            // last_time_reg.read(<val>, <index>);
            // last_time_reg.write(<index>, <val>);
            // hdr.probe_data[0].last_time = ...
            // hdr.probe_data[0].cur_time = ...
        }
    }
}

注:在源码中提到寄存器的read函数会把存储在指定索引中的寄存器数组的状态,并将其作为写入结果参数的值返回,索引的值就是standard_metadata.egress_port

register(bit<32> size);  // FIXME -- arg should be `int` but that breaks typechecking
    /***
     * read() reads the state of the register array stored at the
     * specified index, and returns it as the value written to the
     * result parameter.
     *
     * @param index The index of the register array element to be
     *              read, normally a value in the range [0, size-1].
     * @param result Only types T that are bit<W> are currently
     *              supported.  When index is in range, the value of
     *              result becomes the value read from the register
     *              array element.  When index >= size, the final
     *              value of result is not specified, and should be
     *              ignored by the caller.
     */

 Step8:实验

实验结果显示,我们的包头有新的信息了!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/69795.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

使用ChatGPT完成分类、检测、分割等计算机视觉任务(Pytorch)

前言 ChatGPT是一个由OpenAI训练的大型语言模型&#xff0c;其知识涵盖了很多领域。 虽然ChatGPT表示它不能用于写代码&#xff0c;但是万一是它太谦虚了呢&#xff1f; 下面的文字均为ChatGPT给出的回答。 使用ChatGPT解决图像分类任务 我们需要一个PyTorch模型&#xff0…

11-18-kafka-生产者理论

11-kafka-理论&#xff1a; Kafka 工作流程及文件存储机制 Kafka 工作流程 Kafka 中消息是以 topic 进行分类的&#xff0c;生产者生产消息&#xff0c;消费者消费消息&#xff0c;都是面向 topic的。 topic 是逻辑上的概念&#xff0c;而 partition 是物理上的概念&#xf…

【MySQL】MySQL数据库结构与操作

✨个人主页&#xff1a;bit me ✨当前专栏&#xff1a;MySQL数据库 ✨每日一语&#xff1a;自从厌倦于追寻&#xff0c;我已学会一觅即中&#xff0c;自从一股逆风袭来&#xff0c;我已能抗御八面来风&#xff0c;驾舟而行。 目 录 &#x1f334;一. 数据库介绍&#x1f33f;1.…

工业物联网解决方案:地下水实时监测系统

地下水是水资源的重要组成部分&#xff0c;它具备水量稳定、水质好等特点&#xff0c;是农业灌溉、工矿和城市的重要水源之一&#xff0c;但同时也会出现沼泽化、地面沉降、滑坡等问题&#xff0c;影响当地自然环境和生活用水。 随着物联网通信技术的发展以及国家水资源管理的…

JavaScript (WebAPI)

目录 一、DOM 1. DOM树结构&#xff1a; 2. 重要概念 3. DOM 的工作流程 二、获取/操作元素 1. 获取 ① 获取单个元素 ② 获取所有元素 2. 操作 1. 获取/修改元素内容 3. 案例 三、新增元素 1. 创建元素节点 2. 插入节点到 DOM树 四、删除元素 一、DOM DOM 全…

Photoshop简单案例(8)——利用文字工具修改图片上文字

目录一、项目介绍二、原图上有要替换的文字2.1 方法1——利用修补工具修改2.2 方法2——利用移动工具&#xff08;推荐&#xff09;2.3 效果演示三、原图上没有要替换的文字一、项目介绍 本文介绍利用PhotoShop修改图片上的文字。修改下图图片中的学号。 二、原图上有要替换…

【HBU】2022秋线上作业-第五次-有关树的判断选择

判断题&#xff1a; 1. 一棵有124个结点的完全二叉树&#xff0c;其叶结点个数是确定的。 √ 高度为n的完全二叉树的结点数为2ⁿ-1 124位于64-1~128-1之间&#xff0c;所以这棵树的高度是7&#xff0c;前六层是满的有63个&#xff0c;第7层有124-6361个 64-613 第6层有…

使用Electron打包React项目

1、下载Electron Electron中文官网地址&#xff1a;https://www.electronjs.org/zh/docs/latest/ Electron下载如果单纯使用npm的话&#xff0c;会出现n多问题 所以建议使用国内的淘宝镜像 npm config set registry https://registry.npmmirror.com/然后下载 cnpm install -…

LeetCode 81. 搜索旋转排序数组 II

&#x1f308;&#x1f308;&#x1f604;&#x1f604; 欢迎来到茶色岛独家岛屿&#xff0c;本期将为大家揭晓LeetCode 81. 搜索旋转排序数组 II&#xff0c;做好准备了么&#xff0c;那么开始吧。 &#x1f332;&#x1f332;&#x1f434;&#x1f434; 一、题目名称 LeetC…

年后准备换工作的软件测试工程师们,你准备好了吗?

需要严肃说明的是&#xff1a;面试题库作为帮助大家准备面试的辅助资料&#xff0c;但是绝对不能作为备考唯一途径&#xff0c;因为面试是一个考察真实水平的&#xff0c;不是背会了答案就可以的&#xff0c;需要你透彻理解的&#xff0c;否则追问问题答不出来反而减分&#xf…

什么是dapp?如何在web中开发dapp?

web3 “Web3.0”是对“Web2.0”的改进&#xff0c;在此环境下&#xff0c;用户不必在不同中心化的平台创建多种身份&#xff0c;而是能打造一个去中心化的通用数字身份体系&#xff0c;通行各个平台。更像是一种概念吧。 区块链 区块链&#xff08;Blockchain&#xff09;是由…

54-64-k8s-集群监控-高可以用集群-交付部署

54-k8s-集群监控-高可以用集群-交付部署 k8s集群监控 1、概述 一个好的系统&#xff0c;主要监控以下内容 集群监控 节点资源利用率节点数运行Pods Pod监控 容器指标应用程序【程序占用多少CPU、内存】 2、监控平台 使用普罗米修斯【prometheus】 Grafana 搭建监控平台…

JS实现鼠标悬停变色

JS实现鼠标悬停变色 案例池子&#xff1a; JS实现鼠标悬停变色 JavaScript中的排他算法实现按钮单选 JavaScript中的localStorage JavaScript中的sessionStorage JavaScript实现网页关灯效果 JavaScript实现一段时间之后关闭广告 JavaScript实现按键快速获取输入框光标 …

第二证券|紧盯“有诺不行”隐疾 补齐上市公司高质量发展“短板”

有诺不可”的典型事例 “言而有信”是上市公司高质量开展的重要环节。近日&#xff0c;证监会印发的《推动提高上市公司质量三年举动计划&#xff08;2022-2025&#xff09;》&#xff08;下称《举动计划》&#xff09;提出&#xff0c;将着力处理管理领域杰出问题&#xff0c…

(Java)Thymeleaf学习笔记——动力节点

前言 学SpringMVC找课程时就了解到要学习thymeleaf这种Java模板引擎&#xff0c;但本着不用不学的原则就直接跳过&#xff0c;当实践第一个SpringMVC程序helloworld&#xff0c;遇见了thymeleaf&#xff0c;那么就先解决 &#x1f4a1;thymeleaf 知识点&#xff0c;再来学习Sp…

CSS新增样式----圆角边框、盒子阴影、文字阴影

在CSS中新增了三个样式&#xff0c;分别是圆角边框&#xff0c;盒子阴影&#xff0c;文字阴影。 初识圆角边框&#xff1a; border-radius属性用于设置元素的外边框圆角 语法如下&#xff1a; border-radius:length;原理如下&#xff1a; [椭]圆与边框的交集形成圆角效果。…

档案知识:声像档案的数字化处理与保存

声像档案是档案家族中较晚出现的成员&#xff0c;主要包括音像资料、图像资料、图片等&#xff0c;伴随着当今科技的发展进步&#xff0c;声像档案的数字化管理正逐步替代传统声像档案的管理方式&#xff0c;成为当前的发展主流。 虽然声像档案相较于传统文字档案&#xff0c;…

GFS分布式文件系统

GFS分布式文件系统 什么是GFS分布式文件系统 GFS&#xff08;GlusterFS&#xff09; 是一个开源的分布式文件系统。 由存储服务器、客户端以及NFS/Samba 存储网关&#xff08;可选&#xff0c;根据需要选择使用&#xff09;组成。 没有元数据服务器组件&#xff0c;这有助于提…

黑金ZYNQ7100配置HDMI驱动并测试

Linux系统移植系列 Linux系统移植专栏地址 https://blog.csdn.net/qq_41873311/category_12127932.html 一个写了五篇博客&#xff0c;成功在黑金ZYNQ7100搭建了属于我自己的Linux系统 但是缺少显示模块&#xff0c;所以本文就配置个HDMI驱动来完成图像的显示 环境 hdf设计…

N年后牛的数量

1、题目 第一年农场有 1 只成熟的母牛 A&#xff0c;往后的每年&#xff1a; 1&#xff09;每一只成熟的母牛都会生一只母牛 2&#xff09;每一只新出生的母牛都在出生的第三年成熟 3&#xff09;每一只母牛永远不会死 2、思路 举例说明&#xff1a; 可得到递推式为&…