文档章节

P4教程Flowlet Switching源码分析

wangxuwei
 wangxuwei
发布于 2017/05/23 15:41
字数 3130
阅读 88
收藏 0

作者简介:陈翔,福州大学数计学院2015级计算机科学与技术(实验班)本科生 ,对软件定义网络SDN,特别是对P4语言感兴趣。

前言

为深入研究P4语言相关规范及运行操作使用,本系列文章根据P4.org网站给出的《The P4 Language Specification v1.0.2》[1]内容,并通过我们的运行使用的具体实例和分析汇总,希望能为大家研究P4提供一点参考。作为大二和大三的本科生,水平和经验有限,感谢SDNLAB提供平台,希望能和大家相互学习交流。

本系列文章分为三个部分,系列一翻译和阐述 P4.org网站给出的《The P4 Language Specification v1.0.2》的第二部分首部及字段,见链接:http://www.sdnlab.com/17955.html;系列二是翻译和阐述《The P4 Language Specification v1.0.2》的第三部分解析器,见链接:http://www.sdnlab.com/18021.html;系列三是基于Github开源项目中的P4项目源码分析。

本篇文章是系列文章的收尾篇,除了对于Tutorial中的名为Flowlet Switching项目的源码分析之外,也贴出了我们安装P4相关软件时的步骤与顺序,希望能够帮助到大家在安装相关项目的时候少遇到一些问题。

一、: P4-Related Tools Installation

本章节是安装P4相关工具的步骤和说明。
需要注意的是,本说明只适用于 Ubuntu 14.04 系统。

1.1 推荐安装的其他工具

  • mininet:SDN网络仿真工具
  • vim:编辑器
  • scapy:Python的一个功能十分强大的库,可以用于生成数据报
  • pip:Python包管理工具

1.2 P4Factory

官方README:P4 Model Repository,如果你对P4已经很熟悉并且能够独立搭建环境,这个repo已经不大适合现在的P4开发了(使用bmv1软件交换机),建议是使用bmv2搭建环境并进行相关开发。

1.安装外部引用的库:

 

1

git submodule update --init --recursive

2.安装Ubuntu14.04系统下所需的所有依赖:

 

1

./install_deps.sh

3.在启动模拟器之前,需要创建虚拟的端口:

 

1

sudo p4factory/tools/veth_setup.sh

4.使用autoconf工具生成Makefile,并对工作环境进行配置:

 

1

2

3

cd p4factory

./autogen.sh

./configure

5.验证安装是否成功,并测试一个简单的P4程序:

 

1

2

3

cd p4factory/targets/basic_routing/

make bm

sudo ./behavioral-model

同时新打开一个终端进行测试:

 

1

2

cd p4factory/targets/basic_routing/

sudo python run_tests.py --test-dir tests/ptf-tests/

 

1.3 BMv2

官方README:BEHAVIORAL MODEL REPOSITORY

1.Ubuntu 14.04下要求安装的依赖:

 

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

    automake

    cmake

    libjudy-dev

    libgmp-dev

    libpcap-dev

    libboost-dev

    libboost-test-dev

    libboost-program-options-dev

    libboost-system-dev

    libboost-filesystem-dev

    libboost-thread-dev

    libevent-dev

    libtool

    flex

    bison

    pkg-config

    g++

    libssl-dev

2.使用脚本安装外部依赖库,如thrift。

注意:如果已经安装了P4Factory,请忽略此步,否则会有一系列的版本不匹配问题。

 

1

./install_deps.sh

3.按照以下步骤安装bmv2:

./autogen.sh
./configure
make
sudo make install  # if you need to install bmv2

4.更新Linux库缓存:

1

sudo ldconfig

5.检验:

1

sudo make check

1.4 P4c-bm

官方README:p4c-bm

1.要求安装好pip;

2.安装步骤:

sudo pip install -r requirements.txt
sudo pip install -r requirements_v1_1.txt
sudo python setup.py install


二、 P4 Tutorial Flowlet Switching 源码分析

Exercise 2: Implementing TCP flowlet switching

最近P4社区更新了Tutorial这个Demo的代码,今天偶然发现了它并且觉得很有学习的价值,于是写了一个对源码的简易解读,希望能够帮助大家学习P4。

2.1 源码

simple_router.p4:Github Source Code

可以从 solution.tar.gz 解压获得。

元数据intrinsic.p4:


header_type intrinsic_metadata_t {
    fields {
        ingress_global_timestamp : 48;
        lf_field_list : 32;
        mcast_grp : 16;
        egress_rid : 16;
    }
}

metadata intrinsic_metadata_t intrinsic_metadata;


2.2 intrinsic.p4

这个文件是对固有元数据intrinsic_metadata的声明,在P414标准中有对该元数据进行相关解释:

Metadata is state associated with each packet…some metadata has special significance to the operation of the switch. This is called Intrinsic Metadata as it has semantics intrinsic to the operation of the machine.

也就是说,Intrinsic Metadata是用来存数据报中某些特殊状态的元数据,本实例使用的是该元数据中的第一个字段ingress_global_timestamp来表示和记录ingress阶段的时间戳。

2.3 主P4程序做了什么?

在对P4程序进行解读之前,先对整个P4干嘛的做个介绍。

这些P4程序是用来做Flowlet Switching的,什么是Flowlet Switching呢?

Flowlet switching leverages the burstiness of TCP flows to achieve better load balancing of TCP traffic. In this exercise, you will start from a program that load-balances based on layer 4 flows: this is generally considered “classic” ECMP. To do this, we compute a hash over the 5-tuple and use this value to choose from a set of possible next hops. This means that all packets belonging to the same flow (i.e. with the same 5-tuple) will be routed to the same nexthop.

简单的来说,Flowlet Switching是用来对TCP突发流量做一个负载均衡的工具,通过ECMP做一个四层的LB。做法是通过算法计算数据报五元组的hash值,然后用这个hash值选择下一跳。意味着同一个流的所有数据报都会被转发到同样的下一跳路由。

那么这个Demo是怎么实现的呢?

1.用modify_field_with_hash_based_offset()元动作和crc16算法计算五元组的hash值。这个hash值用来区别不同的流量。注意这里不考虑hash碰撞。

2.对于每条流,需要存两个状态:a)记录这条流上一个数据报的时间戳 b)flowlet_id,来区别不同的TCP流量;对于数据流中的每一个数据报都更新它的时间戳,如果上一个数据报的时间戳与当前数据报的时间戳超过了一定阈值(50ms,但是对于数据中心中的高突发性的快速流量而言,这个阈值需要设置的非常小),那么就给flowlet_id+1,以区别不同的TCP流量;同样的一条流中的数据报拥有相同的id,不同的流量之间的id是不一样的。不过这样就意味着我们需要在数据报的处理过程中维持必要的状态信息,可以通过P4程序中的寄存器来实现。时间戳则由P4软件交换机进行计算,并存储在字段intrinsic_metadata.ingress_global_timestamp中,该字段宽度为32bit,以亚微秒为单位,且只读。

3.有这个flowlet_id之后,即可进行ecmp的hash计算了,用来选择下一跳,在P4的Tutorial中有关于ECMP的实现,参考repo:Action Profile

对于ECMP的介绍:ECMP Wikipedia

“等价多路径路由(英文:Equal-cost multi-path routing,缩写 ECMP),是一个在next-hop封包传送到一个单一目的所产生在多个最佳路径并列时的首要路由权重计算路由策略。多路径路由能被应用于首要路由协定同时发生的状况,因为它是一个受限于单一路由的per-hop决策,它有可能借由在多路径负载平衡流量下提供大幅增加的带宽,然而,它可能在实际部署时发生重大问题。在RFC2991中讨论了一般的多路径路由。”

2.4 simple_router.p4

2.4.1 一些声明和宏定义

#include "includes/headers.p4"
#include "includes/parser.p4"
#include "includes/intrinsic.p4"

#define FLOWLET_MAP_BITS 13
#define FLOWLET_MAP_SIZE 8192  // 2^13
#define FLOWLET_INACTIVE_TOUT 50000 // usec -> 50ms

header_type ingress_metadata_t {
    fields {
        flow_ipg : 48; // inter-packet gap
        flowlet_map_index : FLOWLET_MAP_BITS; // flowlet map index
        flowlet_id : 16; // flowlet id
        flowlet_lasttime : 48; // flowlet's last reference time

        ecmp_offset : 14; // offset into the ecmp table

        nhop_ipv4 : 32;
    }
}

metadata ingress_metadata_t ingress_metadata;

action _drop() {
    drop();
}

field_list l3_hash_fields {
    ipv4.srcAddr;
    ipv4.dstAddr;
    ipv4.protocol;
    tcp.srcPort;
    tcp.dstPort;
}

field_list_calculation flowlet_map_hash {
    input {
        l3_hash_fields;
    }
    algorithm : crc16;
    output_width : FLOWLET_MAP_BITS;
}

register flowlet_lasttime {
    width : 48;
    instance_count : 8192;
}

register flowlet_id {
    width : 16;
    instance_count : 8192;
}

一个名为ingress_metadata的元数据,存储状态信息。声明的内容包括:

  • 一个hash字段列表和hash计算器flowlet_map_hash,用来计算flowlet map index,这个偏移位用于确定寄存器实例,查找相应的数据报状态。
  • 两个寄存器,flowlet_lasttime用来记录timestamp,flowlet_id记录id。

名为ingress_metadata的元数据保存有以下信息:

  • flow_ipg: In computer networking, a minimal pause may be required between network packets or network frames. Wikipedia IPG
  • flowlet_map_index: flowlet map index, 映射至寄存器的偏移量。
  • flowlet_id: 区别不同流量的flowlet id。
  • flowlet_lasttime: 上一次使用flowlet的时间,用于记录时间戳。
  • ecmp_offset: ecmp的偏移量。
  • nhop_ipv4: 下一跳的ipv4地址。

2.4.2 流控程序

control ingress {

    apply(flowlet); // flowlet

    if (ingress_metadata.flow_ipg > FLOWLET_INACTIVE_TOUT) { // 时间戳超过阈值

        apply(new_flowlet); // flowlet id+1

    }

    apply(ecmp_group); // ECMP等价路由

    apply(ecmp_nhop);

    apply(forward); // 三层路由

}



control egress {

    apply(send_frame); //用于改写帧头的MAC地址

}

 

2.4.3 Flowlet这里着重对相对来说比较重要的flowlet流表、new_flow流表进行介绍。

这个P4程序中最主要的部分是Flowlet流表:

table flowlet {

    actions { lookup_flowlet_map; }

}

只有一个动作,跑demo的时候需要通过运行时命令将其设置为默认动作。lookup_flowlet_map动作是该流表的主体:

action lookup_flowlet_map() {

    // l3 hash => flowlet map index

    modify_field_with_hash_based_offset(ingress_metadata.flowlet_map_index, 0,

                                        flowlet_map_hash, FLOWLET_MAP_SIZE);



    // flowlet map index + flowlet_id register => flowlet_id

    register_read(ingress_metadata.flowlet_id,

                  flowlet_id, ingress_metadata.flowlet_map_index);



    // ingress_global_timestamp => flow_ipg

    modify_field(ingress_metadata.flow_ipg,

                 intrinsic_metadata.ingress_global_timestamp);



    // flowlet map index + flowlet_lasttime register => flowlet lasttime

    register_read(ingress_metadata.flowlet_lasttime,

    flowlet_lasttime, ingress_metadata.flowlet_map_index);



    // update IPG: flow ipg = flow ipg - flowlet lasttime

    subtract_from_field(ingress_metadata.flow_ipg,

                        ingress_metadata.flowlet_lasttime);



    // write flowlet lasttime into register cell flowlet_lasttime[flowlet_map_index]

    register_write(flowlet_lasttime, ingress_metadata.flowlet_map_index,

                   intrinsic_metadata.ingress_global_timestamp);

}

步骤一:首先通过l3 hash(flowlet_map_hash)计算出当前数据报flowlet的偏移量flowlet_map_index并记录至元数据中,以确定两个寄存器实例flowlet_lasttime[flowlet_map_index](存放当前数据报的时间戳)和flowlet_id[flowlet_map_index](存放当前数据报的flowlet id)以存储数据报状态信息:

// l3 hash => flowlet map index

modify_field_with_hash_based_offset(ingress_metadata.flowlet_map_index, 0,

                                    flowlet_map_hash, FLOWLET_MAP_SIZE);


步骤二:通过步骤一所述偏移量访问寄存器flowlet_id实例,获取当前数据报的flowlet id,并存放至元数据中:

// flowlet map index + flowlet_id register => flowlet_id

register_read(ingress_metadata.flowlet_id,

              flowlet_id, ingress_metadata.flowlet_map_index);

步骤三:将bmv2交换机计算得到的ingress处理时间记录至数据报对应的寄存器flowlet_lasttime实例中:

// ingress_global_timestamp => flow_ipg

modify_field(ingress_metadata.flow_ipg,

             intrinsic_metadata.ingress_global_timestamp);

 步骤四:通过步骤一所述偏移量访问寄存器flowlet_lasttime实例,获取当前数据报的时间戳(通过步骤三得到),并记录至元数据中:

// flowlet map index + flowlet_lasttime register => flowlet lasttime

register_read(ingress_metadata.flowlet_lasttime,

flowlet_lasttime, ingress_metadata.flowlet_map_index);

步骤五:更新 flowlet IPG,用于后续流程判断是否超过阈值:

// update IPG: flow ipg = flow ipg - flowlet lasttime

subtract_from_field(ingress_metadata.flow_ipg,

                    ingress_metadata.flowlet_lasttime);

在Flowlet流表处理完数据报之后,主控程序对数据报进行一个逻辑判断:

if (ingress_metadata.flow_ipg > FLOWLET_INACTIVE_TOUT) { // 时间戳超过阈值

    apply(new_flowlet); // flowlet id+1

}

对数据报的时间戳进行判断,如果元数据(存放数据报状态信息,包括在执行存放的lookup_flowlet_map动作时获取的状态信息)中的记录的flowlet IPG值(存放在ingress_metadata.flow_ipg)大于我们设定的阈值(本实验是50ms),就通过new_flowlet流表更新该数据报的flowlet id:

action update_flowlet_id() {

    add_to_field(ingress_metadata.flowlet_id, 1);

    register_write(flowlet_id, ingress_metadata.flowlet_map_index,

                   ingress_metadata.flowlet_id);

}



table new_flowlet {

    actions { update_flowlet_id; }

}


可以看到,new_flowlet流表只有一个动作update_flowlet_id,该动作更新了这个数据报的flowlet_id,以表明这个数据报是属于一条新的流量。

2.4.4 ECMP

在接下来的处理流程中,我们就可以通过flowlet id来区别不同的流量,做ECMP等价路由。P4程序片段如下:

action set_nhop(nhop_ipv4, port) {

    modify_field(ingress_metadata.nhop_ipv4, nhop_ipv4);

    modify_field(standard_metadata.egress_spec, port);

    add_to_field(ipv4.ttl, -1);

}



···



field_list flowlet_l3_hash_fields { // 用于ECMP计算的字段列表

    ipv4.srcAddr;

    ipv4.dstAddr;

    ipv4.protocol;

    tcp.srcPort;

    tcp.dstPort;

    ingress_metadata.flowlet_id;

}



#define ECMP_BIT_WIDTH 10           // ECMP Hash Value 的位宽

#define ECMP_GROUP_TABLE_SIZE 1024  // ECMP ecmp_group流表的大小

#define ECMP_NHOP_TABLE_SIZE 16384  // ECMP ecmp_nhop流表的大小



field_list_calculation flowlet_ecmp_hash { // ECMP hash

    input {

        flowlet_l3_hash_fields;

    }

    algorithm : crc16;

    output_width : ECMP_BIT_WIDTH;

}



action set_ecmp_select(ecmp_base, ecmp_count) { // 将 计算得到的 ECMP hash 值记录到元数据中

    modify_field_with_hash_based_offset(ingress_metadata.ecmp_offset, ecmp_base,

                                        flowlet_ecmp_hash, ecmp_count);

}



table ecmp_group {

    reads {

        ipv4.dstAddr : lpm;

    }

    actions {

        _drop;

        set_ecmp_select;

    }

    size : ECMP_GROUP_TABLE_SIZE;

}



table ecmp_nhop {

    reads {

        ingress_metadata.ecmp_offset : exact;

    }

    actions {

        _drop;

        set_nhop;

    }

    size : ECMP_NHOP_TABLE_SIZE;}

 步骤和思路很清晰:


步骤1.对数据报的ipv4目的地址进行lpm匹配(最长前缀匹配),若匹配,则进行步骤2;

步骤2.根据匹配的表项执行丢包动作,或执行set_ecmp_select动作(转至步骤3);

步骤3.执行set_ecmp_select动作:根据flowlet id和相关数据报信息(源目IP等)计算得到ECMP hash值,存至元数据中;

步骤4.流表ecmp_nhop根据元数据中存储的ecmp hash值(ingress_metadata.ecmp_offset)进行下一跳路由的选择。

三、结语

本系列的文章至此就结束了,不过我们会持续关注P4社区的动态,跟进最新的P4讯息。此外,SDNLAB有一个非常棒的SDN技能图谱,链接:https://github.com/PONOUBA/opensdn_engineer_skill_map,我们也在Github整合了一个P4资源的仓库,链接:https://github.com/Wasdns/The-Road-to-P4,希望能够和大家多多交流和沟通,为数据平面可编程和P4的发展做出努力!

本文转载自:http://www.sdnlab.com/19040.html

共有 人打赏支持
wangxuwei
粉丝 25
博文 335
码字总数 117394
作品 0
杭州
其他
私信 提问
工作流建模插件--Flow4J

Flow4J是一个可在Eclipse平台下以拖放的方式进行工作流建模的插件.一个工作流程可包含许多流程步骤(在该项目中叫作flowlet),然后这些步骤可接连在一起组合成复杂的流程.所有流程将包含以下两...

匿名
2008/09/10
5.7K
0
Android 项目开发必备-为你的项目选择优质框架

如何快速提升自己的代码能力? 初入职场的你可能已经问一些行业前辈了,他们给你的答复无外乎都是阅读好的开源框架,但是,什么是好的开源框架呢?下面就由我来给你们介绍一下。 本着与时俱进...

PaperKite
2018/01/10
0
0
.pgm图片简介以及Python读取.pgm图片的方法

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/quiet_girl/article/details/80904471 一、什么是.pgm图片? .pgm文件由于模式的不同其数据存储方式也有所不同...

nana-li
2018/07/03
0
0
CNN Network quantization 阶段总结

CNN Network quantization 阶段总结 提纲 Tensorflow quantization & 8bit quantization 简介 实验结果 8bit 计算的有效性 NVIDIA GPU P4 and P40 性能 Tensorflow quantization 简介 开发人......

cptn3m0
2017/07/21
0
0
【C++_typedef_疑难定义收集整理】

(注:文章谨代表个人理解,如有错误,还望尽快指正,大家都能获益,我定感激不尽^^) 至今已遇到两个typedef诡异的定义,因此决定下笔记之,以备后用。 【typedef之const指针】 【案例】 type...

技术小胖子
2017/11/10
0
0

没有更多内容

加载失败,请刷新页面

加载更多

Confluence 6 升级中的一些常见问题

升级的时候遇到了问题了吗? 如果你想尝试重新进行升级的话,你需要首先重新恢复老的备份。不要尝试再次对 Confluence 进行升级或者在升级失败后重新启动老的 Confluence。 在升级过程中的一...

honeymoose
55分钟前
2
0
C++随笔(四)Nuget打包

首先把自己编译好的包全部准备到一个文件夹 像这样 接下来新建一个文本文档,后缀名叫.nuspec 填写内容 <?xml version="1.0"?><package xmlns="http://schemas.microsoft.com/packaging/201......

Pulsar-V
今天
2
0
再谈使用开源软件搭建数据分析平台

三年前,我写了这篇博客使用开源软件快速搭建数据分析平台, 当时收到了许多的反馈,有50个点赞和300+的收藏。到现在我还能收到一些关于dataplay2的问题。在过去的三年,开源社区和新技术的发...

naughty
今天
3
0
Python3的日期和时间

python 中处理日期时间数据通常使用datetime和time库 因为这两个库中的一些功能有些重复,所以,首先我们来比较一下这两个库的区别,这可以帮助我们在适当的情况下时候合适的库。 在Python文...

编程老陆
今天
2
0
分布式面试整理

并发和并行 并行是两个任务同时进行,而并发呢,则是一会做一个任务一会又切换做另一个任务。 临界区 临界区用来表示一种公共资源或者说是共享数据,可以被多个线程使用,但是每一次,只能有...

群星纪元
今天
3
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部