SRv6实践项目(六):控制面完成链路和主机的发现

news2024/11/15 7:36:09

在本次实验中,我们需要利用ONOS完成对数据面的控制

1.使能packet的IO功能,验证链路发现

main.p4提供了和P4Runtime的通信的消息的定义格式,分别是PacketInPacketOut,他们都被加上了一个注解,表示这是一个控制器交互的数据包包头格式

@controller_header("packet_in")
header cpu_in_header_t {
    port_num_t ingress_port;
    bit<7> _pad;
}

@controller_header("packet_out")
header cpu_out_header_t {
    port_num_t egress_port;
    bit<7> _pad;
}

这些报头用于携带数据包的初始的交换机入端口,并指定数据包转发的预期出端口。

  • Stratum中的P4Runtime代理从交换机CPU端口接收到数据包时,它希望找到数据包的CPU_in_header_t标头作为帧中的第一个标头,为了解析这个数据包,它去查看P4Info文件的controller_packet_metadata部分,以确定在帧的开头剥离的比特数,并填充PacketIn消息的相应元数据字段,在本例中就是一个入端口。
  • 类似地,当Stratum接收到P4Runtime PacketOut消息时,它使用在PacketOut的元数据字段中找到的值对帧进行序列化,并在将其提供给管道解析器之前将cpu_out_header_t预附加到帧中

因此,在p4代码中,我们应该实现如下几个功能

1.1 解析数据包

这个早在第二部分就讲过了,这里做一个回忆,解析指的是解析来自于控制面的packet_out消息(解析packet_in消息是控制面的工作)

state parse_packet_out {
        packet.extract(hdr.cpu_out);
        transition parse_ethernet;
    }

1.2什么时候反馈控制器

这里设计了两个操作,分别是直接发送给CPU,另一个则是克隆一份发给CPU

CPU_CLONE_SSION_ID:为要克隆到CPU端口的数据包指定镜像会话。与此会话ID相关联的数据包将被克隆到CPU_port,并通过其出口进行传输(由桥接/路由/acl表设置)。要使克隆工作,P4Runtime控制器需要首先插入一个将此会话ID映射到CPU_PORT的CloneSessionEntry。

action send_to_cpu() {
        standard_metadata.egress_spec = CPU_PORT;
    }

action clone_to_cpu() {
        // Cloning is achieved by using a v1model-specific primitive. Here we
        // set the type of clone operation (ingress-to-egress pipeline), the
        // clone session ID (the CPU one), and the metadata fields we want to
        // preserve for the cloned packet replica.
        clone3(CloneType.I2E, CPU_CLONE_SESSION_ID, { standard_metadata.ingress_port });
    }

1.3 收到了Packet OUT消息要怎么做

这里,我们在Ingress控制流的apply中,加入一段代码:

if (hdr.cpu_out.isValid()) {
           
            standard_metadata.egress_spec = hdr.cpu_out.egress_port;
            hdr.cpu_out.setInvalid();
            exit;
        }

这段代码的核心思想就是如果这个包是控制面下来的包,就要把它转发到由控制面指定的位置去

1.4 如何发出一个Packet IN消息

这里,我们在Egress控制流的apply中,加入一段代码:

if (standard_metadata.egress_port == CPU_PORT) {
            
            hdr.cpu_in.setValid();
            hdr.cpu_in.ingress_port = standard_metadata.ingress_port;
            exit;
        }

如果要从CPU_PORT发出去,说明这是一个packetin数据包,把它的进端口封包到它的cpu_in中,这样,等它到控制面的时候,控制面就知道了这个包是来自于交换机的这个端口的,这里设计了一下cpu_in是有效的。

1.5 封装Packetin数据包

如果cpu_invalid的,它就会封包

apply {
        packet.emit(hdr.cpu_in);
        packet.emit(hdr.ethernet);
        packet.emit(hdr.ipv4);
        packet.emit(hdr.ipv6);
        packet.emit(hdr.srv6h);
        packet.emit(hdr.srv6_list);
        packet.emit(hdr.tcp);
        packet.emit(hdr.udp);
        packet.emit(hdr.icmp);
        packet.emit(hdr.icmpv6);
        packet.emit(hdr.ndp);
    }

总的来说,来讲讲控制面和数据面交互是怎么做的:

  • 我们先只说LLDP和BDDP:控制器会发送一个PACKET_OUT的数据包到数据面,这个PACKET_OUT数据包指定了它的出端口port A
  • 当交换机leaf1收到这个数据包,把它拆开来一看,发现它的出端口port A要去leaf2,于是它拆了包,然后不做任何操作了,直接发出去到leaf2
  • leaf2从port B收到了这个包,此时这个包的格式已经不是PACKET_OUT了,它是一个LLDP协议包或者BDDP协议包,此时leaf2去它的ACL表里检索,发现匹配到了这个包,然后它执行了对应的操作:把它克隆一份
    • 其中,原件按照它原有的逻辑继续走
    • 复印件到管道的元数据的ingress和原件一样,其他不存在,然后复印件把自己的ingress(port B)信息封装在PACKET_IN包头中,并激活这个数据包的PACKET_IN包头,然后发给控制器
  • 控制器收到了一个PACKET_IN的包,它打开这个包一看得知这个包在数据面的ingress是(port B),然后进一步拆开,发现它是一个LLDP或者BDDP数据包,然后控制面回忆起自己曾经向leaf1发过这个包叫它去port A的,这个包路线是控制器->eaf1 port A->leaf2 port B->控制器,控制器就得到了两个交换机相连的端口
  • 但是我们知道,ACL中并不只关注了LLDP和BDDP,它还关注了ARP以及NDP的NS和NA,也就是说,控制器还能跟踪到IPv4和IPv6的主机的信息

2.测试上述功能

在这里,我们用一个代码测试了它的功能:

PacketOutTest

  • runTest函数:生成了一大堆的数据包,然后去testPacket这些数据包的IO的功能
  • testPacket(self, pkt)函数:把packetout消息的cpu_out的出端口分别发出去

PacketInTest

  • runTest函数:生成了一大堆的数据包,然后去testPacket这些数据包的IO的功能
  • testPacket(self, pkt)函数:insert这个数据包对应的表的表项,然后,看每一个端口,它有一个期望的数据包格式类型,testutils发包工具发出数据包,如果符合期望,就ok了

from ptf.testutils import group

from base_test import *

CPU_CLONE_SESSION_ID = 99


@group("packetio")
class PacketOutTest(P4RuntimeTest):
    """Tests controller packet-out capability by sending PacketOut messages and
    expecting a corresponding packet on the output port set in the PacketOut
    metadata.
    """

    def runTest(self):
        for pkt_type in ["tcp", "udp", "icmp", "arp", "tcpv6", "udpv6",
                         "icmpv6"]:
            print_inline("%s ... " % pkt_type)
            pkt = getattr(testutils, "simple_%s_packet" % pkt_type)()
            self.testPacket(pkt)

    def testPacket(self, pkt):
        for outport in [self.port1, self.port2]:
            # Build PacketOut message.
            # Modify metadata names to match the content of your P4Info file
            # ---- START SOLUTION ----
            packet_out_msg = self.helper.build_packet_out(
                payload=str(pkt),
                metadata={
                    "egress_port": outport,
                    "_pad": 0
                })
            
            # Send message and expect packet on the given data plane port.
            self.send_packet_out(packet_out_msg)

            testutils.verify_packet(self, pkt, outport)

        # Make sure packet was forwarded only on the specified ports
        testutils.verify_no_other_packets(self)


@group("packetio")
class PacketInTest(P4RuntimeTest):
    """Tests controller packet-in capability my matching on the packet EtherType
    and cloning to the CPU port.
    """

    def runTest(self):
        for pkt_type in ["tcp", "udp", "icmp", "arp", "tcpv6", "udpv6",
                         "icmpv6"]:
            print_inline("%s ... " % pkt_type)
            pkt = getattr(testutils, "simple_%s_packet" % pkt_type)()
            self.testPacket(pkt)

    @autocleanup
    def testPacket(self, pkt):

        # Insert clone to CPU session
        self.insert_pre_clone_session(
            session_id=CPU_CLONE_SESSION_ID,
            ports=[self.cpu_port])

        # Insert ACL entry to match on the given eth_type and clone to CPU.
        eth_type = pkt[Ether].type
        
        # Modify names to match content of P4Info file (look for the fully
        # qualified name of the ACL table, EtherType match field, and
        # clone_to_cpu action.
        
        self.insert(self.helper.build_table_entry(
            table_name="IngressPipeImpl.acl_table",
            match_fields={
                # Ternary match.
                "hdr.ethernet.ether_type": (eth_type, 0xffff)
            },
            action_name="IngressPipeImpl.clone_to_cpu",
            priority=DEFAULT_PRIORITY
        ))
        

        for inport in [self.port1, self.port2, self.port3]:
            
            # Expected P4Runtime PacketIn message.
            exp_packet_in_msg = self.helper.build_packet_in(
                payload=str(pkt),
                metadata={
                    "ingress_port": inport,
                    "_pad": 0
                })

            # Send packet to given switch ingress port and expect P4Runtime
            # PacketIn message.
            testutils.send_packet(self, inport, str(pkt))
            self.verify_packet_in(exp_packet_in_msg)

看完了代码,开始测试:

make p4-test TEST=packetio

3.开启ONOS完成链路发现

链路发现和主机发现的功能已经实现在ONOS中了,在控制器中的应用程序的解释器PipelineInterpreter中让他能匹配P4Info,这样,就能完成对数据包IO的真正实现了,记得make netcfg把网络配置推送到ONOS中,你发现了两个主机。

make app-reload,加载一下应用程序,现在,你能实现h1a和h1b互ping的功能了

在这里,我们先做一个小结: 

你会发现一个现象,h1a和h1b能互相ping,但是h2就不行了,这是为什么呢?

h1a在一开始,它要找主机h1b,它发出了对h1b的组播,ipv6组播的mac地址也是个组播,是0x333300000..所以,当组播请求经过leaf1的时候,leaf1根据自己身上的2层组播地址转发,转发的出端口是主机向的端口,也就是3456,所以,理论上h1c,h1b,h2都会收到它的组播,但是实际上

  • 同一个广播域内的h1c,它不知道h1a的地址,h1a虽然要组播他要去的地方,但是h1c收到了发现h1a的3层的目的地址不是它,h1c就不再打开看了,在同一广播域内,leaf1呈现l2功能,是一个交换机
  • 同一个leaf的h2,虽然与h1a和h1b,h1c都是与leaf1相连,但是,h1a为了要找h2,它知道h2不是这个子网的,在这个子网内别说组播了,广播都没用,所以h1a要找到与外网相连的leaf1,它要知道路由器怎么走(router的mac),才能知道h2的MEC,然而我们没有实现第三层,所以它连路由器在哪都不知道,leaf1在h1a的眼中,只不过是一个交换机而已。
  • 不同的leaf的h3和h4,他们根本就收不到信息,因为我们还没有实现3层转发功能

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

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

相关文章

c++篇---缺省参数

文章目录 一、缺省参数概念二、缺省参数实例三、缺省参数声明和定义四、全缺省和半缺省 一、缺省参数概念 缺省参数 在调用该函数时&#xff0c;如果实参没有指定传内容&#xff0c;那么在函数中用形参时&#xff0c;就采用为函数参数指定的这个缺省值 但是如果在调用该函数时…

【Qt 实现一个画板,基于QWidget,可以绘制直线和矩形】

【Qt 实现一个画板&#xff0c;基于QWidget,可以绘制直线和矩形】 简介效果展示源码mainwindow.hmainwindow.cpppainterwidget.hpainterwidget.cppshape.h &#xff08;管理&#xff09;line.hline.cpprect.hrect.cpp 结 &#x1f649;&#x1f649;更多内容 点击&#xff1a;Q…

力扣sql中等篇练习(七)

力扣sql中等篇练习(七) 1 查询活跃业务 1.1 题目内容 1.1.1 基本题目信息 1.1.2 示例输入输出 1.2 示例sql语句 # Write your MySQL query statement below # 先求出所有业务的平均发生次数 SELECT t2.business_id FROM (SELECT e.*,IF(e.occurences>t1.A_NUM,1,0) tota…

【C++】vector的简化模拟实现

文章目录 1. 主要结构2. 默认成员函数3. 迭代器4. 容量相关1. size和capacity2. reserve3. resize 5. 数据访问6. 数据修改1. push_back2.pop_back3. insert4.erase5.swap6.clear 1. 主要结构 参照SGI版本的vector实现&#xff0c;使用三个指针来维护这样一段内存空间 templa…

ACL访问控制列表简介和配置演示

一.ACL功能和特点 1.功能 2.特点 二.ACL种类 1.基础ACL&#xff1a; 2.增强ACL&#xff1a; 三.配置演示 1.基础ACL&#xff1a; 2.增强ACL&#xff1a; 一.ACL功能和特点 1.功能 对感兴趣的路由 (控制层面)进行设置策略 对感兴趣的流量 (数据层面)进行设置策略 2.…

Activity启动模式的生命周期

四种启动模式 1.standard android:launchMode"standard" 默认的标准启动模式&#xff0c;每次启动当前Activity&#xff0c;任务栈中都添加一个当前Activity的实例。按返回键时&#xff0c;表现出退出多个当前Activity的现象。 MainActivityOne和MainActivityTwo都…

DPText-DETR原理及源码解读

一、原理 发展脉络&#xff1a;DETR是FACEBOOK基于transformer做检测开山之作&#xff0c;Deformable DETR加速收敛并对小目标改进&#xff0c;TESTR实现了端到端的文本检测识别&#xff0c;DPText-DETR做了精度更高的文字检测。 DETR 2020 FACEBOOK&#xff1a; 原理 https://…

c/c++:函数的作用,分类,随机数,函数定义,调用,申明,exit()函数,多文件编程,防止头文件重复

c/c&#xff1a;函数的作用&#xff0c;分类&#xff0c;随机数&#xff0c;函数定义&#xff0c;调用&#xff0c;申明&#xff0c;exit()函数&#xff0c;多文件编程&#xff0c;防止头文件重复 2022找工作是学历、能力和运气的超强结合体&#xff0c;遇到寒冬&#xff0c;大…

Spring启动及Bean实例化过程来看经典扩展接口

目录 一、Spring启动及Bean实例化过程 二、分析其对应经典扩展接口 三、对开发的指导意义 备注&#xff1a;以下总结只是一些基本的总结思路&#xff0c;具体每个扩展接口的应用后续进行分析总结。 一、Spring启动及Bean实例化过程 Spring启动及Bean实例化的过程&#xff0…

6 款顶级 Android 数据恢复软件列表

数据丢失可能会扰乱您的个人/企业生活&#xff0c;如果手动完成&#xff0c;可能很难恢复丢失的数据。Android 数据恢复软件是解决此问题的完美解决方案。这些工具可帮助您快速轻松地从 Android 设备恢复丢失的数据。它可以帮助您恢复照片、视频、笔记、联系人等。 我研究了市…

1. C++使用Thread类创建多线程的三种方式

1. 说明 使用Thread类创建线程对象的同时就会立刻启动线程&#xff0c;线程启动之后就要明确是等待线程结束&#xff08;join&#xff09;还是让其自主运行&#xff08;detach&#xff09;&#xff0c;如果在线程销毁前还未明确上面的问题&#xff0c;程序就会报错。一般都会选…

webserve简介

目录 I/O分类I/O模型阻塞blocking非阻塞 non-blocking&#xff08;NIO&#xff09;IO复用信号驱动异步 webServerHTTP简介概述工作原理HTTP请求头格式HTTP请求方法HTTP状态码 服务器编程基本框架两种高效的事件处理模式Reactor模式Proactor模拟 Proactor 模式 线程池 I/O分类 …

字节岗位薪酬体系曝光,看完感叹:不服真不行

曾经的互联网是PC的时代&#xff0c;随着智能手机的普及&#xff0c;移动互联网开始飞速崛起。而字节跳动抓住了这波机遇&#xff0c;2015年&#xff0c;字节跳动全面加码短视频&#xff0c;从那以后&#xff0c;抖音成为了字节跳动用户、收入和估值的最大增长引擎。 自从字节…

最全MySQL8.0实战教程

文章目录 最全MySQL8.0实战教程一.前言1.计算机语言概述2.SQL的概述2.1 语法特点2.2 MySQL的安装2.2.1 方式1&#xff1a;解压配置方式2.2.2 方式2&#xff1a;步骤安装方式 二. 数据库DDL操作1.DDL概念2.对数据库常用操作 最全MySQL8.0实战教程 长路漫漫&#xff0c;键盘为伴&…

【Linux进阶篇】启动流程和服务管理

目录 &#x1f341;系统启动 &#x1f343;Init和Systemd的区别 &#x1f343;运行级别和说明 &#x1f341;Systemd服务管理 &#x1f343;6和7命令区别 &#x1f343;systemd常用命令 &#x1f341;系统计划调度任务 &#x1f343;一次性任务-at &#x1f343;batch &#x1…

论文 : Multi-Channel EEG Based Emotion Recognition Using TCNBLS

Multi-Channel EEG Based Emotion Recognition Using Temporal Convolutional Network and Broad Learning System 本文设计了一种基于多通道脑电信号的端到端情绪识别模型——时域卷积广义学习系统(TCBLS)。TCBLS以一维脑电信号为输入&#xff0c;自动提取脑电信号的情绪相关…

自然语言处理 —— 01概述

什么是自然语言处理呢? 自然语言处理就是NLP,全名为Natural Language Processing。 一、NLP的困难 1. 歧义 (1)注音歧义 (2)分词歧义 (3)结构歧义 (4)指代歧义 (5)语义歧义 (6)短语歧义

javascript简单学习

简介&#xff1a; javascript 是脚本语言 javascript是轻量级的语言 javascript是可插入html页面的编程代码 javascript插入html页面后&#xff0c;可由所有现代浏览器执行 以下是JavaScript的一些基本概念&#xff1a; 1. 变量&#xff1a;变量用于存储数据值&#xff0…

每日学术速递4.13

CV - 计算机视觉 | ML - 机器学习 | RL - 强化学习 | NLP 自然语言处理 Subjects: cs.CV 1.Slide-Transformer: Hierarchical Vision Transformer with Local Self-Attention(CVPR 2023) 标题&#xff1a;Slide-Transformer&#xff1a;具有局部自注意力的分层视觉变换器 …

一、vue之初体验-两种方式引入vue

一、Vue引入方式-CDN <html lang"en"><head><meta charset"UTF-8" /><meta http-equiv"X-UA-Compatible" content"IEedge" /><meta name"viewport" content"widthdevice-width, initial-s…