Contents

OVS添加自定义action

版本及环境

ovs 2.13.7

ubuntu 18.04

与前一篇ovs安装的环境一致,里面所提及的脚本本篇也会用到

看这篇需要有一定的OVS基础,基本概念必须要了解。

基本概念

  • 这里讲的action并不是openflow action, 而是数据平面的执行action

  • OVS中流主要从两个部分转发:内核空间和用户空间

    内核空间主要是通过缓存的流表转发流,若在缓存流表中未找到,则到用户空间的完整流表中找。

    用户空间对于匹配的流会添加一份缓存到内核空间的流表中;

    若用户空间的完整流表也没有匹配的,就把这个数据包的相关信息发到控制器。

  • 一个OpenFlow flow有两部分的组成:match, action

    match用于匹配到达的数据包,action是对匹配到的数据包执行相应的动作

    下面这个图是流表的例子

下图为已有的一些action

  • 为什么需要自定义action: 当然是已有的满足不了需求了!!!

基本步骤

  1. 先在OVS社区进行讨论(挺难找的)

  2. 在ofp-action.c中添加新的action代码

  3. 添加parse/encode/decode代码,这是为了使ovs-ofctl和控制器使用新加的action

  4. 在内核datapath部分实现新加的action

  5. 在ovs-vswitchd中添加action的用户层面实现

  6. 将action翻译到datapath

流表的添加过程概述

对于下面这个流表

它的添加过程为:

控制平面(控制器,还有在mininet中的操作命令行中)

  1. 我们的流表字符串会被先编码为ofpacts
  2. 再将ofpacts编码为OpenFlow actions
  3. 将flow_mod 消息发送到ovs-vswitchd

数据平面:

  1. 将OpenFlow actions 解码为 ofpacts
  2. 把flow放到交换机的规则分类器中(这里要表达的意思其实是:把转换后的ofpacts放到流表中,当有数据包来了后就会进行流表匹配)

自定义action代码实现

一、添加 action 定义(OpenFlow)

lib/ofp-actions.c中添加代码

这里添加我们自定义action的名字,这个名字是OpenFlow action的名字,它并不是OVS action的名字。OpenFlow action 需要解码才可转换为 OVS action。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
enum ofp_raw_action_type {
    /* ... */
  /* NX1.3+(47): struct nx_action_decap, ... */
  NXAST_RAW_DECAP,

  /* OF1.0+(30): uint32_t. */
  OFPAT_RAW_PVL,    //这行是我们的,注释删掉

  /* NX1.3+(48): void. */
  NXAST_RAW_DEC_NSH_TTL,
  /* ... */
}

这里的注释 * OF1.0+(30): uint32_t. * 是必要的,它声明了OF消息的类型,序号以及构造OF消息所需要的参数,ovs会根据这里声明的类型在编译时会在/lib/ofp-actions.inc2这个文件里按put_OFAT_##ENUM的形式自动生成函数,这里生成的函数名为put_OFAT_PVL, 这个函数的第一个参数是 ofpbuf *, 其他的参数为注释中指定的参数(指定为void时则只有一个参数),因此未按指定格式注释会出错.

include/openvswitch/ofp-actions.h 中添加定义

1
2
OFPACT(GOTO_TABLE,      ofpact_goto_table,  ofpact, "goto_table")   \
OFPACT(PVL,        ofpact_pvl,    ofpact, "pvl") //这行是我们的,注释删掉

找到文件中代码对应的位置,添加一行我们自定义的pvl

结合/include/openvswitch/ofp-actions.h 中关于`#define OFPACTS` 的宏定义看,每添加一行OFPACT编译器会根据宏定义添加大量的处理函数;结合/include/openvswitch/ofp-actionsh.h中关于enum OVS_PACKED_ENUM ofpact_type来看,每添加一行OFPACT会给这个枚举类型添加一项.

因为C中没有类这个概念,当新添加一个行为是,对应的行为属性其实是通过宏定义这个方式来实现的。

include/openvswitch/ofp-actions.h 中添加代码

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
// 在 struct ofpact_goto_table  后面添加

/* OFPACT_PVL.
 *
 * Used for OFPAT_PVL */
struct ofpact_pvl {
    OFPACT_PADDED_MEMBERS(
            struct ofpact ofpact;
            uint32_t command;           /* command from the controller */
    );
    uint8_t data[];
};

这里面其实定义了新action中所需要的参数。

datapath/linux/compat/include/linux/openvswitch.h:中添加代码:

1
2
3
4
//在 enum ovs_action_attr { 中添加代码
OVS_ACTION_ATTR_PVL = 29,   /* unit32_t */ //这行是我们的,本注释删掉
__OVS_ACTION_ATTR_MAX,	      /* Nothing past this will be accepted
                     * from userspace. */

这里需要对新添加的类型定义显式的值,否则可能会出错

这部份代码是内核层的定义,这个枚举类型里有所有的action,当datapath要执行哪个action时,会有for循环轮每个action, 再用switch case语句直接跳到对应的action中进行执行.

二、parse,encode和decode

这步的目的是为了ovs-ofctl和控制器能够使用我们新添加的action