POF技术分享(三):Packet处理流程

前言:

之前对POF基本原理、POF交换机源码结构进行解读,但是,要想完成POF交换机的二次开发和拓展,有必要对POF交换机特有的数据包处理流程、POF交换机和控制器交互模式进行了解,本节对数据包处理流程进行分析。

0 基础

在开始分析之前,先看看一个基本结构体和流程图:

1)结构体:pofdp_packet

这里需要明确一个重要结构体pofdp_packet(pof_datapath.h),变量名一般为dpp。它是整个匹配处理的对象,集合了数据包相关内容、当前表和表项、待执行的instruction相关内容等,后面过程会用到。这里示意其中部分属性:

结构体pofdp_packet成员属性有很多,其中与packet内容相的属性非常重要,关系较为复杂。则下图示意出相关关系,黄色部分为转发出口待输出域:

2)流程图

数据包进入POF交换机后,会进行一系列匹配处理过程,为了方便后面分析和对照,这里先给出整体流程图:

一 Packet处理入口

有了以上基础,现在开始进入分析流程。pofdp_main_task()(pof_datapath.c)函数是POF开启的一个线程任务,对进入pof交换机的packet进行循环、逐一的处理,主要代码如下:

这里主要做了这么几件事情:

1、设定默认instruction,即goto第一张表0(set_goto_first_table_instruction()),为了每个进入交换机的packet都可以从表0开始匹配处理。如果想改变数据包处理流程,那就需要从这里入手。此后,采用while开始每个packet的循环处理;

2、从端口的packet接收队列中读取packet数据(pofdp_recv_raw);

3、packet长度检测、第一个表存在性检测,如果检测出错则释放此packet缓存;

4、packet转发处理(pofdp_forward函数)。

二 Packet的instruction集中处理

接上面,进入pofdp_forward()函数(pof_datapath.c),主要代码:

这里进行两件事:

1、初始化dpp(pofdp_packet结构体)的一些属性:初始化dpp的元数据区metadata(初始化为0)、初始化dpp的instruction为firs_ins(即goto第一张表0)

2、调用pofdp_instruction_execute()函数,对dpp执行其instruction,这也是packet进入pof交换机第一次执行的instruction,即跳转到表0进行匹配。

在pofdp_instruction_execute()函数中(pof_instruction.c),通过while循环,检测dpp的所有instruction是否执行完,如果没有,则调用excute_##INSTRUCTIONS_NAME()函数,执行相应instruction。当然,这里对于进入POF交换机的每一个packet来说,默认第一个动作是跳转表0,即调用execute_GOTO_TABLE()函数,执行其跳转表0的动作。

三 跳转表instruction和匹配处理

对于数据包,默认的第一个动作就是跳转表0动作的执行,这里以这个为例分析。execute_GOTO_TABLE()函数(pof_instruction.c)主要实现跳转到指定表,并且用跳转前生成的key,方便跳转后进行跳转表的匹配。

因为此节包含着数据包跳转前的处理、key的生成、表如何匹配等多项关键性内容,因此需要逐条分析。

3.1 基础内容

先来看看跳转的instruction结构体:

1)next_table_id:待跳转表id,具有全局性,之后会分析

2)match_field_num:待跳转表的匹配域数量

3)packet_offset:Byte类型,即跳转后packet指针的需要的偏移量,如果不偏移则为0

4)match:待跳转表的匹配域内容,匹配域结构体为pof_match(包含field_id、offset和len)

3.2 packet偏移

execute_GOTO_TABLE()函数开始,将dpp中跳转instruction赋给p,然后对dpp进行偏移(由于是packet初次跳转,之前默认值为0,即不偏移)。当然,有必要看看如何偏移,进入函数move_packet_offset_forward():

这里dpp的四个属性需要说明:

buf: 原始packet的指针初始位置

buf_offset:目前packet指针偏移到的位置,这是匹配和action执行的基准。

offset:从起始位置buf到目前位置buf_offfset的偏移量

left_len:剩余packet的的内容长度,即从buf_offset到packet末端的内容长度。

3.3 跳转表id和类型获取

首先需要清楚,pof中表的类型type定义了四种,如下:

每张表有俩编号,在相同类型表中有编号id(相对的),在所有表(全部类型表的集合)也有一个全局编号id(绝对的),跳转instruction指定的ID为表的全局编号(大写ID区分)。要找到全局ID的表对应哪种类型中的哪个相对id的表,需要进行转化映射:

其实映射法则很简单,就是查看每种类型表有很几个,进行累加,看看加到哪一种类型表的第几张表时恰好等于全局ID,则找到全局ID表对应的表类型(table_type)和表的相对id(table_id)。

3.4 待跳转表获取

所有表在一个全局的二维数组poflr_table_ptr中存储(维度即类型和相对id),知道了类型和相对id,很容易得到具体跳转的表:

table_vhal_ptr(poflr_flow_table结构体)即返回得到的跳转表,poflr_flow_table结构体是具体一张表内容的集合,定义如下:

tbl_base_info:此表基本信息(结构体pof_flow_table),包含表名字,表全局ID,表类型,匹配域数量和所含有的匹配域基本信息等(结构体pof_match,这点和OF表不同,后面会具体分析)

poflr_flow_entry:此表含有的所有流表项,后面会具体分析

entry_num:流表项数量

state:此表状态,用True 或False表示此表是否存在。

3.5 查找key构造

要完成POF的GOTO_Table后的表的匹配,是需要根据key来对表的各个表项匹配域进行查找匹配的,因此需要先构造key。这里key用变量key_ptr(二维指针数组,存储key的各个匹配字段)表示,先对其分配空间初始化:

key_ptr(查找key)直接分配最大数量的匹配字段(8个),每个匹配字段长度为最大(16Byte),然后调用函数pofdp_find_key()构造key。

pofdp_find_key()输入为dpp->buf_offset(即指针packet,示意原数据包packet偏移到buf_offset处为基准的剩下数据),metadata,此表的匹配域数量match_field_num和匹配域数组match。通过这些来构造key的match_field_num个待匹配字段。

每个match即结构体pof_match,有必要看其结构体:

其属性field_id指示待匹配字段由packet还是metadata(field_id=0xFFFF)构成,还定义了偏移量(offset,单位bit)和长度(len,单位bit)。当field_id不是0xFFFF时,即意味着packet的buf_offset位置再偏移offset长度后的位置a处,a向后len长度的数据需要构成key的一个待匹配字段。代码如下:

可以看到,每个key的待匹配字段都由offset、len来从packet(或metadata)中取出构造,见函数 pofdp_copy_bit():

key的每个匹配域字段长度为1Byte(uint8_t,char类型),while循环把刚刚大于len的整数个Byte先赋值填充到匹配域字段,然后再把多于出来的bit位置0。其过程见示意图:

图例解释:pof_match的offset为9,len为14(即意味着取出偏移量为9长度为14个bit作为key的一个待匹待域),14位不足两个字节,用0填充。

首先明确,匹配域按字节算,且标记匹配域每个字节的data_res也是字节。可以得到offset_b_x=9%8=1,*ptr即字符“1”。

通过第一次while循环取出第一个字符‘1’的后七位和第二个字符‘!’的前一位,构成待匹配字段的第一个字节‘b’(方法:即第一个字节左移1位,即0(0110001)->(0110001)0,第二个字节右移7位,即(0)0100001 -> 0000000(0),然后做逻辑或即可得。注意无符号数移位不足的用0补),此时process_len_b=0+8=8<14,需要进行第二次while循环,同样方法得到第二个字节‘c’,由于取出了两个字节(16位),比len要求的14位多两位,因此需要在while循环外对最后一个字节(即第二个字节)进行作与(后两位为0)。这样,则可以得到packet中指定的偏移量为9长度为14的所有14个bit值,后两位用0填充,这16位成为key的匹配域的第一个字段,其他字段构造同理。

当然,一般情况pof_match的offset和len都为8bit(1个字节)的整数倍,但不一定(如匹配字段是是IP层的一些位字段)。这里只是为了示意通用性,例子才这么设计。

3.6 流表项匹配

流表项匹配的主要过程代码如下:

没有匹配到,则调用函数pofdp_entry_nomatch(),根据配置情况选择丢弃包或长传控制器;匹配到则将流表项instruction填充入dpp中,待执行。

主要看看POF匹配机制:在POF中,每张流表的pof_match结构体(之前提到)相对于流表的匹配域资源(基准),其定义了这张流表应该含有的所有匹配字段。同一张流表中的流表项的匹配域的匹配字段个数要和流表定义的相同,且又因key_ptr(待查询key)是按照流表匹配域基准构造的,则每条流表项的匹配域部分,其匹配字段的个数、顺序、匹配字段字节长度都是一致的。

因此,POF流表项查询机制很简单,就是逐条取出流表项匹配域,按照匹配字段顺序,然后一个个字节掩码后进行对比即可,遇到重复匹配到的流表项,考察优先级大的记录采用即可。

逐条匹配过程见pofdp_lookup_in_table()(pof_lookup.c):

每一条流表项其匹配域匹配细节见函数pofdp_lookup_in_table()->pofdp_match_per_entry():

可以看到,就是按照匹配字段的个数match_field_num,对每一个字段的每个字节,掩码后比对。所以,比对过程中,已经不需要偏移量和偏移长度了。

这里基本分析完数据包进入POF之后匹配处理等全部过程,接下来简单说明几个重要instruction的执行。

四 Instruction是APPLY_ACTIONS

APPLY_ACTIONS这种Instruaction,即可以包含很多立即执行的动作actions,执行函数为execute_APPLY_ACTIONS()->pofdp_action_execute()(pof_action.c),循环执行完包含的各个action,这里根据action不同逐一简单说明:

execute_OUTPUT:packet/metadata端口发出。给定packet偏移量packet_offset(bit,相对于packet其实位dpp->buf的)、metadata偏移量metadata_offset和metadata偏移长度metadata_len,将metadata指定偏移量和产度的值放入data,再将packet指定偏移之后的值放入data(metadata放入之后),然后将data赋给dpp->buf_out,从指定端口发出。

execute_ADD_FIELD:添加packet字段。顾名思义,在packet相应位置之后添加一个字段,修改dpp->metadata->len。(packet的实时长度由metadata->len记录)。

execute_DELETE_FIELD:删除packet字段。与execute_ADD_FIELD同理,只是对packet删除一个字段。

execute_SET_FIELD:设置packet字段。给出值、偏移量和长度(pof_match_x),对packet的相应偏移量和长度的相应位置的值用给出的值进行修改。

execute_MODIFY_FIELD:修改packet/metadata字段。给出一个increment数值、偏移量和长度(pof_match),取出packet相应偏移量和长度位置处的值,强制加increment后,然后再填充到刚才偏移量和长度位置处;同样有可能是对metadata的某个字段进行强制加increment后修改。(场景,比如IPV4中TTL增加)

execute_SET_FIELD_FROM_METADATA:通过metadata修改packet字段。给出metadata偏移量metadata_offset、packet偏移量offset和偏移长度len,然后用metadata的metadata_offset处长度为len的值覆盖packet偏移量为offset长度为len的值。

五 Instruction是execute_WRITE_METADATA

WRITE_METADATA和APPLY_ACTIONS的execute_SET_FIELD类似,功能是设置metadata字段。给出值value、metadata偏移位置metadata_offset和长度len,即对metadata的metadata_offset位置处长度为len的值写入value。

六 Instruction是execute_WRITE_METADATA_FROM_PACKET

WRITE_METADATA_FROM_PACKET和APPLY_ACTIONS的execute_SET_FIELD_FROM_METADATA是相反过程,通过packet设置metadata字段。给出metadata偏移量metadata_offset、packet偏移量offset和偏移长度len,然后将packet的metadata_offset处长度为len的值写入到metadata偏移量为offset长度为len处。

应用场景:将原packet字段值记录入metadata,后期可以通过metadata对原packet相应字段进行恢复或是直接通过metadata字段进行匹配操作。

后记

虽然POF和OVS等OpenFlow交换机大同小异,但是对于数据包的匹配处理流程还是有一些独有的特色。本文着重对数据包进入POF交换机后数据包处理、匹配、instruction执行等做了细节介绍,在学习POF交换机的过程中,感受到OpenFlow交换机在匹配处理过程中无法体现出来的灵活性,基于此可以拓展研究的地方也相对会多很多。

原文发布于微信公众号 - SDNLAB(SDNLAB)

原文发表时间:2016-04-18

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏jeremy的技术点滴

JVM的Finalization Delay引起的OOM

42280
来自专栏芋道源码1024

Spring Webflux —— 源码阅读之 handler 包

查找给定请求的handler,如果找不到特定的请求,则返回一个空的Mono。这个方法被getHandler(org.springframework.web.se...

40050
来自专栏对角另一面

lodash源码分析之数组的差集

外部世界那些破旧与贫困的样子,可以使我内心世界得到平衡。 ——卡尔维诺《烟云》 本文为读 lodash 源码的第十七篇,后续文章会更新到这个仓库中,欢迎 s...

454140
来自专栏祝威廉

ElasticSearch Aggregations GroupBy 实现源码分析

也就是按newtype 字段进行group by,然后对num求平均值。在我们实际的业务系统中,这种统计需求也是最多的。

50830
来自专栏尾尾部落

[剑指offer] 替换空格

请实现一个函数,将一个字符串中的每个空格替换成“%20”。例如,当字符串为We Are Happy.则经过替换之后的字符串为We%20Are%20Happy。

18810
来自专栏androidBlog

java 解决文件名重复问题的两种方法

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/gdutxiaoxu/article/de...

44610
来自专栏耕耘实录

Linux三大剑客之awk

版权声明:本文为耕耘实录原创文章,各大自媒体平台同步更新。欢迎转载,转载请注明出处,谢谢

18140
来自专栏大内老A

采用一个自创的"验证框架"实现对数据实体的验证[编程篇]

昨天晚上突发奇想,弄了一个简易版的验证框架,用于进行数据实体的验证。目前仅仅实现基于属性的声明式的验证,即通过自定义特性(Custom Attribute)的方...

26860
来自专栏菩提树下的杨过

rpc框架之 thrift 学习 2 - 基本概念

thrift的基本构架: ? 上图源自:http://jnb.ociweb.com/jnb/jnbJun2009.html 底层Underlying I/O以上...

27270
来自专栏coder修行路

Go实现短url项目

首先说一下这种业务的应用场景: 把一个长url转换为一个短url网址 主要用于微博,二维码,等有字数限制的场景 主要实现的功能分析: 把长url的地址转换为短u...

45850

扫码关注云+社区

领取腾讯云代金券