P4的exercises实现原理

news2024/9/23 1:34:17

今天介绍一下p4的tutorials-master\utils如何将tutorials-master\exercises下各个小实验的实现,有助于以后自己构建mininet并配置p4交换机,通过本博客可以简单了解:

  • Makefile是如何实现相关的实验的
  • 基于p4的mininet如何搭建

1.Makefile干了什么?

1.1 实验文件夹下的makefile

我们这里以防火墙为例,当在实验文件夹下的命令行使用run的时候,真正make的文件其实是工具文件夹下的Makefile
BMV2_SWITCH_EXE = simple_switch_grpc
TOPO = pod-topo/topology.json
DEFAULT_PROG = basic.p4

include ../../utils/Makefile

 1.2 build创建文件夹

在run的时候,可以看到,先是build了一下,而在build中,创建了一个名称为build文件夹,以供未来编译p4程序使用
run: build
	sudo python3 $(RUN_SCRIPT) -t $(TOPO) $(run_args)

build: dirs $(compiled_json)

dirs:
	mkdir -p $(BUILD_DIR) $(PCAP_DIR) $(LOG_DIR)

 1.3 编译p4文件成信息文件与json

build结束以后,进入了p4编译阶段,将make工作目录下的所有p4程序编译一下,生成的信息文件以及json文件都存在了build文件夹下,其中P4C_ARGS += --p4runtime-files $(BUILD_DIR)/$(basename $@).p4.p4info.txt
%.json: %.p4
	$(P4C) --p4v 16 $(P4C_ARGS) -o $(BUILD_DIR)/$@ $<

1.4 run 

 从下面的make代码可以看到,其实run调用的是工具文件夹下的run_exercise.py的文件,python运行传递的参数绕来绕去,实际上很简单,这里以防火墙为例,用到的就是basic.p4文件编译的json用到的交换机是simple_switch_grpc,创建的拓扑是pod-topo/topology.json

run_exercise.py究竟是干嘛的,在后面说。

run: build
	sudo python3 $(RUN_SCRIPT) -t $(TOPO) $(run_args)

#run的一些参数
RUN_SCRIPT = ../../utils/run_exercise.py
TOPO = pod-topo/topology.json

ifndef NO_P4
run_args += -j $(DEFAULT_JSON)
endif

# Set BMV2_SWITCH_EXE to override the BMv2 target
ifdef BMV2_SWITCH_EXE
run_args += -b $(BMV2_SWITCH_EXE)
endif

DEFAULT_JSON = $(BUILD_DIR)/$(DEFAULT_PROG:.p4=.json)
DEFAULT_PROG = basic.p4
BMV2_SWITCH_EXE = simple_switch_grpc

最后,附上一张图可以很直观的看到,makefile做了什么

 2.run_exercise.py是怎么创建的基于p4的mininet网络的?

我想,如果是要自己玩一些花样了话,这些多少是要了解一些的。。。

这里继续以防火墙小实验为例,防火墙小实验中,有4台交换机,但是3台交换机的配置是basic.p4有一台是firewall.p4,这个差异化配置是怎么做到的呢?

2.1 run_exercise.py大致流程

从下面的代码看出,简单来说,获取一堆参数,然后创建网络,然后运行起来,就这么简单

if __name__ == '__main__':
    # from mininet.log import setLogLevel
    # setLogLevel("info")

    args = get_args()
    exercise = ExerciseRunner(args.topo, args.log_dir, args.pcap_dir,
                              args.switch_json, args.behavioral_exe, args.quiet)

    exercise.run_exercise()

看了一下参数的获取函数get_args(),结合在make run中涉及到的

sudo python3 ../../utils/run_exercise.py -t pod-topo/topology.json -j build/basic.json -b simple_switch_grpc

的指令,就是把拓扑的json,和p4编译后的json还有行为模型传进来

def get_args():
    cwd = os.getcwd()
    default_logs = os.path.join(cwd, 'logs')
    default_pcaps = os.path.join(cwd, 'pcaps')
    parser = argparse.ArgumentParser()
    parser.add_argument('-q', '--quiet', help='Suppress log messages.',
                        action='store_true', required=False, default=False)
    parser.add_argument('-t', '--topo', help='Path to topology json',
                        type=str, required=False, default='./topology.json')
    parser.add_argument('-l', '--log-dir', type=str, required=False, default=default_logs)
    parser.add_argument('-p', '--pcap-dir', type=str, required=False, default=default_pcaps)
    parser.add_argument('-j', '--switch_json', type=str, required=False)
    parser.add_argument('-b', '--behavioral-exe', help='Path to behavioral executable',
                                type=str, required=False, default='simple_switch')
    return parser.parse_args()

2.2 ExerciseRunner

先看构造函数,其实就是把上面的拓扑json提取出来存进来

def __init__(self, topo_file, log_dir, pcap_dir,
                       switch_json, bmv2_exe='simple_switch', quiet=False):
        """ Initializes some attributes and reads the topology json. Does not
            actually run the exercise. Use run_exercise() for that.

            Arguments:
                topo_file : string    // A json file which describes the exercise's
                                         mininet topology.
                log_dir  : string     // Path to a directory for storing exercise logs
                pcap_dir : string     // Ditto, but for mininet switch pcap files
                switch_json : string  // Path to a compiled p4 json for bmv2
                bmv2_exe    : string  // Path to the p4 behavioral binary
                quiet : bool          // Enable/disable script debug messages
        """

        self.quiet = quiet
        self.logger('Reading topology file.')
        with open(topo_file, 'r') as f:
            topo = json.load(f)
        self.hosts = topo['hosts']
        self.switches = topo['switches']
        self.links = self.parse_links(topo['links'])

        # Ensure all the needed directories exist and are directories
        for dir_name in [log_dir, pcap_dir]:
            if not os.path.isdir(dir_name):
                if os.path.exists(dir_name):
                    raise Exception("'%s' exists and is not a directory!" % dir_name)
                os.mkdir(dir_name)
        self.log_dir = log_dir
        self.pcap_dir = pcap_dir
        self.switch_json = switch_json
        self.bmv2_exe = bmv2_exe

拓扑的json格式,可以看到,每一个主机都会有一个commands确保以后可以通过arp发现一下对方,在交换机中,具有一个runtime_json,这个是运行时的控制面文件,里面存一些table信息,此外,默认情况下,交换机都会采用默认的p4配置,如果有"program"就会用里面的p4编译的json配置

{
    "hosts": {
        "h1": {"ip": "10.0.1.1/24", "mac": "08:00:00:00:01:11",
               "commands":["route add default gw 10.0.1.10 dev eth0",
                           "arp -i eth0 -s 10.0.1.10 08:00:00:00:01:00"]},
        "h2": {"ip": "10.0.2.2/24", "mac": "08:00:00:00:02:22",
               "commands":["route add default gw 10.0.2.20 dev eth0",
                           "arp -i eth0 -s 10.0.2.20 08:00:00:00:02:00"]},
        "h3": {"ip": "10.0.3.3/24", "mac": "08:00:00:00:03:33",
               "commands":["route add default gw 10.0.3.30 dev eth0",
                           "arp -i eth0 -s 10.0.3.30 08:00:00:00:03:00"]},
        "h4": {"ip": "10.0.4.4/24", "mac": "08:00:00:00:04:44",
               "commands":["route add default gw 10.0.4.40 dev eth0",
                           "arp -i eth0 -s 10.0.4.40 08:00:00:00:04:00"]}
    },
    "switches": {
        "s1": { "runtime_json" : "pod-topo/s1-runtime.json",
                "program" : "build/firewall.json" },
        "s2": { "runtime_json" : "pod-topo/s2-runtime.json" },
        "s3": { "runtime_json" : "pod-topo/s3-runtime.json" },
        "s4": { "runtime_json" : "pod-topo/s4-runtime.json" }
    },
    "links": [
        ["h1", "s1-p1"], ["h2", "s1-p2"], ["s1-p3", "s3-p1"], ["s1-p4", "s4-p2"],
        ["h3", "s2-p1"], ["h4", "s2-p2"], ["s2-p3", "s4-p1"], ["s2-p4", "s3-p2"]
    ]
}

2.3 create_nework() 

  • 先看一眼大概流程,可以看到,首先定义一下交换机的默认配置,重点就是这个p4编译的json,json_path=self.switch_json,其实具体来说在这里传进来的参数就是在刚才命令行里看到的-j build/basic.json 
def create_network(self):
        """ Create the mininet network object, and store it as self.net.

            Side effects:
                - Mininet topology instance stored as self.topo
                - Mininet instance stored as self.net
        """
        self.logger("Building mininet topology.")

        defaultSwitchClass = configureP4Switch(
                                sw_path=self.bmv2_exe,
                                json_path=self.switch_json,
                                log_console=True,
                                pcap_dump=self.pcap_dir)

        self.topo = ExerciseTopo(self.hosts, self.switches, self.links, self.log_dir, self.bmv2_exe, self.pcap_dir)

        self.net = Mininet(topo = self.topo,
                      link = TCLink,
                      host = P4Host,
                      switch = defaultSwitchClass,
                      controller = None)
  •  在上面的代码中,看到创建了一个拓扑用到了ExerciseTopo,里面就是根据链路、交换机、主机进行一个创建,这里主要看交换机,如果交换机的program字段存在,就比如防火墙的配置存在,就会把这个交换机配置成具有防火墙功能的交换机,
for sw, params in switches.items():
            if "program" in params:
                switchClass = configureP4Switch(
                        sw_path=bmv2_exe,
                        json_path=params["program"],
                        log_console=True,
                        pcap_dump=pcap_dir)
            else:
                # add default switch
                switchClass = None
            self.addSwitch(sw, log_file="%s/%s.log" %(log_dir, sw), cls=switchClass)

具体的配置函数看一下就好了,这里不深究,因为就算以后自己写一个p4文件,写一个topo.json也不会改写这个函数,除非你想要调整一下next_thrift_port = xxx:

def configureP4Switch(**switch_args):
    """ Helper class that is called by mininet to initialize
        the virtual P4 switches. The purpose is to ensure each
        switch's thrift server is using a unique port.
    """
    if "sw_path" in switch_args and 'grpc' in switch_args['sw_path']:
        # If grpc appears in the BMv2 switch target, we assume will start P4Runtime
        class ConfiguredP4RuntimeSwitch(P4RuntimeSwitch):
            def __init__(self, *opts, **kwargs):
                kwargs.update(switch_args)
                P4RuntimeSwitch.__init__(self, *opts, **kwargs)

            def describe(self):
                print("%s -> gRPC port: %d" % (self.name, self.grpc_port))

        return ConfiguredP4RuntimeSwitch
    else:
        class ConfiguredP4Switch(P4Switch):
            next_thrift_port = 9090
            def __init__(self, *opts, **kwargs):
                global next_thrift_port
                kwargs.update(switch_args)
                kwargs['thrift_port'] = ConfiguredP4Switch.next_thrift_port
                ConfiguredP4Switch.next_thrift_port += 1
                P4Switch.__init__(self, *opts, **kwargs)

            def describe(self):
                print("%s -> Thrift port: %d" % (self.name, self.thrift_port))

        return ConfiguredP4Switch
  • 在执行完下面的代码以后,就已经有一个拓扑大概的样子了,连接,主机,交换机及其类型都配置好了(这里只配置好了防火墙的交换机),因为别的交换机program不存在,所以默认的配置还没有配置,从上面的代码段中看到是None,它们的默认配置在mininet中配置的
self.topo = ExerciseTopo(self.hosts, self.switches, self.links, self.log_dir, self.bmv2_exe, self.pcap_dir)

去看一眼mininet的源码,就可以很清楚的发现,带着defaultSwitchClass去配置Mininet,就会把还没没有配置的交换机用defaultSwitchClass配置一下,初始化的时候,传defaultSwitchClass的参进去,而且默认build是True

def __init__( self, topo=None, switch=OVSKernelSwitch, host=Host,
                  controller=DefaultController, link=Link, intf=Intf,
                  build=True, xterms=False, cleanup=False, ipBase='10.0.0.0/8',
                  inNamespace=False,
                  autoSetMacs=False, autoStaticArp=False, autoPinCpus=False,
                  listenPort=None, waitConnected=False ):


        ...
        
        ...
        
        ...

        if topo and build:
            self.build()

所以在初始化最后一行有一个代码执行了build方法,只看拓扑相关的,build函数实际上调用了buildFromTopo( self.topo ),拓扑就是我们早在ExerciseTopo阶段就已经配置好的拓扑了,但是那个时候,我们只配置了防火墙到s1交换机中,仔细看一下buildFromTopo创建交换机的时候,它的addSwitch方法

def build( self ):
        "Build mininet."
        if self.topo:
            self.buildFromTopo( self.topo )

def buildFromTopo( self, topo=None ):
        """Build mininet from a topology object
           At the end of this function, everything should be connected
           and up."""

        # Possibly we should clean up here and/or validate
        # the topo
        if self.cleanup:
            pass

        info( '*** Creating network\n' )

        if not self.controllers and self.controller:
            # Add a default controller
            info( '*** Adding controller\n' )
            classes = self.controller
            if not isinstance( classes, list ):
                classes = [ classes ]
            for i, cls in enumerate( classes ):
                # Allow Controller objects because nobody understands partial()
                if isinstance( cls, Controller ):
                    self.addController( cls )
                else:
                    self.addController( 'c%d' % i, cls )

        info( '*** Adding hosts:\n' )
        for hostName in topo.hosts():
            self.addHost( hostName, **topo.nodeInfo( hostName ) )
            info( hostName + ' ' )

        info( '\n*** Adding switches:\n' )
        for switchName in topo.switches():
            # A bit ugly: add batch parameter if appropriate
            params = topo.nodeInfo( switchName)
            cls = params.get( 'cls', self.switch )
            if hasattr( cls, 'batchStartup' ):
                params.setdefault( 'batch', True )
            self.addSwitch( switchName, **params )
            info( switchName + ' ' )

        info( '\n*** Adding links:\n' )
        for srcName, dstName, params in topo.links(
                sort=True, withInfo=True ):
            self.addLink( **params )
            info( '(%s, %s) ' % ( srcName, dstName ) )

        info( '\n' )

方法里面写到,里面说到,如果交换机没有cls,也就是cls是None,那就把传参的配置配给它,这里传参的配置就是basic.p4进行配置以后生成的类

def addSwitch( self, name, cls=None, **params ):
        """Add switch.
           name: name of switch to add
           cls: custom switch class/constructor (optional)
           returns: added switch
           side effect: increments listenPort ivar ."""
        defaults = { 'listenPort': self.listenPort,
                     'inNamespace': self.inNamespace }
        defaults.update( params )
        if not cls:
            cls = self.switch
        sw = cls( name, **defaults )
        if not self.inNamespace and self.listenPort:
            self.listenPort += 1
        self.switches.append( sw )
        self.nameToNode[ name ] = sw
        return sw

 小结

  • 通过阅读makefile文件,了解到如何通过make来运行基于p4的mininet小实验
  • 通过阅读run_exercise等源码,了解到以后要如何设置相关的json文件,用来创建mininet,而这个网络创建的代码其实如果不是特定需求了话,也不需要我们去改

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

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

相关文章

python-(6-4-3)爬虫---re解析案例

文章目录一 需求二 总体思路1&#xff09;从网址定位到2022必看热片2&#xff09;从2022必看热片定位到子页面的链接地址3&#xff09;请求子页面的链接地址&#xff0c;拿到我们想要的下载地址三 分析步骤1&#xff09;从网址定位到2022必看热片2&#xff09;从2022必看热片定…

开关电源环路稳定性分析(05)-传递函数

大家好&#xff0c;这里是大话硬件。 经过前面4篇文章的梳理&#xff0c;估计很多人已经等不及了&#xff0c;什么时候可以开始环路的分析。为了尽快进入到大家关心的部分&#xff0c;这一讲我们正式进入环路分析的第一部分——传递函数。 传递函数&#xff0c;简单的理解就是…

立体车库管理系统

1、立体车库主界面 左侧为菜单栏中间为监视页面上方为实时报警页面&#xff0c;上方第二页为设备指令&#xff0c;第三页为升降机监测&#xff0c;第三页为穿梭车监测2、立体车库设备状态显示 通用状态显示升降机1#2#和穿梭车1#2#3#的缓存指令编号&#xff0c;点击按钮可以清楚…

【设计模式】32.结构型模式-组合模式(Composite)

一、描述 首先&#xff0c;看一个数据结构&#xff1a; 在平时开发过程中&#xff0c;我们的菜单目录、文件夹目录等都有类似如上的实体结构&#xff0c;其中composite代表父级节点&#xff0c;leaf代表叶子节点&#xff0c;composite可以有子节点&#xff0c;但是leaf下没有…

一文读懂ChatGPT模型原理

&#xff08;本文是ChatGPT原理介绍&#xff0c;但没有任何数学公式&#xff0c;可以放心食用&#xff09; 前言 这两天&#xff0c;ChatGPT模型真可谓称得上是狂拽酷炫D炸天的存在了。一度登上了知乎热搜&#xff0c;这对科技类话题是非常难的存在。不光是做人工智能、机器学习…

ORB-SLAM2 ---- Tracking::TrackWithMotionModel函数

目录 1.函数作用 2.步骤 3.code 4.函数解释 4.1 更新上一帧的位姿&#xff1b;对于双目或RGB-D相机&#xff0c;还会根据深度值生成临时地图点 4.2 根据之前估计的速度&#xff0c;用恒速模型得到当前帧的初始位姿。 4.3 用上一帧地图点进行投影匹配&#xff0c;如果匹…

JDK版本对应其major.minor version,看这一篇就够啦(附java历史版本下载地址)

文章目录前言JDK版本对应其major versionJDK历史版本下载地址前言 今天博主在学习SpringBoot&#xff0c;启动项目时遇到这样一个问题 [ERROR] Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.10.1:compile (default-compile) on project springbo…

[附源码]JAVA毕业设计企业信息安全评价系统(系统+LW)

[附源码]JAVA毕业设计企业信息安全评价系统&#xff08;系统LW&#xff09; 项目运行 环境项配置&#xff1a; Jdk1.8 Tomcat8.5 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目…

[附源码]Python计算机毕业设计Django小区疫情事件处理系统

项目运行 环境配置&#xff1a; Pychram社区版 python3.7.7 Mysql5.7 HBuilderXlist pipNavicat11Djangonodejs。 项目技术&#xff1a; django python Vue 等等组成&#xff0c;B/S模式 pychram管理等等。 环境需要 1.运行环境&#xff1a;最好是python3.7.7&#xff0c;…

Git学习笔记 Git Gitee GitHub GitLab

目录 Git GitHub Gitee码云 GitLab Git 概述 何为版本控制 为什么需要版本控制 集中式版本控制工具 分布式版本控制工具 Git和代码托管中心 Git命令 git init命令 git config git status 查看 git 状态 git rm --cached xx git commit -m “备注” xx git reflo…

PHP代码审计系列(二)

PHP代码审计系列&#xff08;二&#xff09; 本系列将收集多个PHP代码安全审计项目从易到难&#xff0c;并加入个人详细的源码解读。此系列将进行持续更新。 strcmp比较字符串 源码如下 <?php $flag "flag"; if (isset($_GET[a])) { if (strcmp($_GET[a], …

DBCO-PEG3-Maleimide,Mal-PEG3-DBCO,二苯并环辛炔-三聚乙二醇-马来酰亚胺

​ 中英文名&#xff1a; CAS号&#xff1a;N/A| 英文名&#xff1a;DBCO-PEG3-Maleimide&#xff0c;Mal-PEG3-DBCO |中文名&#xff1a;二苯并环辛炔-三聚乙二醇-马来酰亚胺物理参数&#xff1a; CASNumber&#xff1a;N/A Molecular formula&#xff1a;C34H38N4O8 Molecul…

[附源码]计算机毕业设计绿色生鲜Springboot程序

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybatis Maven Vue 等等组成&#xff0c;B/S模式 M…

一键免密登录云平台!ZStack Cloud 4.5.0等你来解锁……

近日&#xff0c;ZStack Cloud 4.5.0发布&#xff0c;新增支持多种标准单点登录&#xff08;SSO&#xff09;协议。云平台现可对接OIDC/OAuth2/CAS三种协议的统一身份认证系统&#xff0c;使认证系统中的用户可一键免密登录云平台&#xff0c;大大提高了云平台的访问效率和安全…

在 Git Bash 中为 vim 设置 Dracula 配色

因为本人一直习惯了用 Jetbrains 家的产品&#xff0c;而且一直独钟 Dracula 配色&#xff0c;因此想要在 Git Bash 中为 vim 配一款 Dracula 配色。本博客将详细介绍如何为 vim 配置暗系主题 Dracula。 一、安装 Dracula 在 Dracula 的官网其实已经给出了关于如何在 vim 中配…

Pygame入门

目录一、初始化pygame环境&#xff08;一&#xff09;在pycharm中新建一个工程&#xff08;二&#xff09;安装pygame&#xff08;三&#xff09;测试安装是否成功二、pygame基础&#xff08;一&#xff09;pygame初始化&#xff08;二&#xff09;设置显示界面&#xff08;三&…

[附源码]计算机毕业设计JAVA在线二手车交易信息管理系统

[附源码]计算机毕业设计JAVA在线二手车交易信息管理系统 项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; S…

《PyInstaller打包实战指南》第二十二节 单文件模式打包Playwright

第二十二节 单文件模式打包Playwright 打包示例源码下载: 请看文章末尾 版本信息: playwright==1.28.0 pyinstaller==5.3 打包系统: Windows 10 打包前我们要先运行下代码,确保没有报错: # belle.py from playwright.sync_api import sync_playwrightwith sync_playw…

【Android】 android | as | android studio 安装与使用

一、说明 1、本文重点介绍android studio的使用&#xff0c;做Android应用开发 2、主要包括&#xff1a; 下载&#xff0c;安装&#xff0c;sdk配置&#xff0c;工具使用说明 二、下载Android Studio与配置 1、下载链接 https://developer.android.google.cn/studio 2、安装 参…

特征工程特征预处理归一化与标准化、鸢尾花种类预测代码实现

一、特征预处理 特征工程目的&#xff1a;把数据转换成机器更容易识别的数据 scikit-learn&#xff1a;提供几个常见的实用程序函数和转换器类&#xff0c;以将原始特征向量更改为更适合下游估计器的表示形式。即通过一些转换函数将特征数据转换成更加适合算法模型的特征数据…