由OpenFlowJ源码学习OpenFlow协议V1.0(流表:动作)

流表(Flow Table):动作(Actions)

OpenFlow流表(Flow Table)
流表记录(Flow Table Entry)应当包含三个部分:

  1. 头部字段(header fields);
  2. 计数器(counters);
  3. 动作(actions)。
头部字段计数器动作
用于匹配包通过统计来更新已匹配的包应用已匹配的包

其中,要求&可选实现动作操作中如下的端口功能:

必须动作

  1. 转发动作中的:
    1. ALL:从所有非接收端口发包
    2. LOCAL:发送包给交换机本地网络栈(本地端口)
    3. TABLE:在流表中实施动作
    4. IN_PORT:从输入端口发包
  2. 丢弃动作

可选动作

  1. 转发动作中的:
    1. NOEMAL:以寻常的L2/L3转发方式处理
    2. FLOOD:沿着最小生成树来泛洪包(不包括输入端口)
  2. 入队动作
  3. 修改字段动作包含以下子项:
    1. 设置 VLAN ID
    2. 设置 VLAN 优先级
    3. 去除 VLAN 头部
    4. 修改以太网源MAC地址
    5. 修改以太网目的MAC地址
    6. 修改IPv4源地址
    7. 修改IPv4目的地址
    8. 修改IPv4 ToS位
    9. 修改传输源端口
    10. 修改传输目的端口

其对应协议5.2.1的端口结构一节,其定义的端口动作类型如下:

enum ofp_port {
	OFPP_MAX = 0xff00,/* 物理端口的最大数量 */
	OFPP_IN_PORT = 0xfff8, 
	OFPP_TABLE = 0xfff9, 
	OFPP_NORMAL = 0xfffa, 
	OFPP_FLOOD = 0xfffb, 
	OFPP_ALL = 0xfffc, 
	OFPP_CONTROLLER = 0xfffd, /* 发送给控制器 */
	OFPP_LOCAL = 0xfffe, 
	OFPP_NONE = 0xffff
};

在对应的OFPort.java源文件中,仅实现获取获取端口动作类型值的功能,其代码如下所示:

package org.openflow.protocol;

public enum OFPort {
    OFPP_MAX                ((short)0xff00),
    OFPP_IN_PORT            ((short)0xfff8),
    OFPP_TABLE              ((short)0xfff9),
    OFPP_NORMAL             ((short)0xfffa),
    OFPP_FLOOD              ((short)0xfffb),
    OFPP_ALL                ((short)0xfffc),
    OFPP_CONTROLLER         ((short)0xfffd),
    OFPP_LOCAL              ((short)0xfffe),
    OFPP_NONE               ((short)0xffff);

    protected short value;

    private OFPort(short value) {
        this.value = value;
    }

    // 获取端口动作类型值
    public short getValue() {
        return value;
    }
}

同时在OpenFlow 1.0协议中,定义了动作消息头部结构(合计为64位数据):

  1. 16位的类型值;
  2. 16位的长度值;
  3. 8 * 4 位的填充值。
struct ofp_action_header {
	uint16_t type; 
	uint16_t len; 
	uint8_t pad[4];
};
OFP_ASSERT(sizeof(struct ofp_action_header) == 8);

在下方OpenFlowJ项目的OFAction.java源文件中实现可克隆接口,并定义以下功能:

  1. 初始化:最小长度为4字节(代码实现仅读取前4字节做处理);偏移长度为2;偏移类型为0;
  2. 获取&设置动作消息头部长度(以有&无符号形式);
  3. 获取&设置动作类型;
  4. 由动作获取消息概述(toString方法);由信消息概述获取动作对象(fromString);
  5. 从ByteBuffer中读取数据(类型&长度);
  6. 将数据(类型&长度)写入到ByteBuffer当中;
  7. 重写hashCode与equals函数。
  8. 浅克隆
package org.openflow.protocol.action;

import java.nio.ByteBuffer;

import org.openflow.util.U16;

// OFAction.java文件
// 所有OpenFLow动作的基类
public class OFAction implements Cloneable {
    /** 
     * 注意:实际的头部最小长为8字节(包含了为了对齐64位的填充值),
     * 由于此基类被用于复用于传入的动作消息,仅需要读取首位4个字节。
     * 所有动作都扩展此类去负责读取/写入首位8字节(包含所需的填充)。
     */
    public static int MINIMUM_LENGTH = 4;
    public static int OFFSET_LENGTH = 2;
    public static int OFFSET_TYPE = 0;

    protected OFActionType type;
    protected short length;

	// 获取&设置消息长度(以有&无符号形式)
    public short getLength() {
        return length;
    }
    public int getLengthU() {
        return U16.f(length);
    }
    public OFAction setLength(short length) {
        this.length = length;
        return this;
    }

    // 获取&设置动作类型
    public OFActionType getType() {
        return this.type;
    }
    public OFAction setType(OFActionType type) {
        this.type = type;
        return this;
    }

    // 由动作获取消息概述
    public String toString() {
        return "ofaction" +
            ";t=" + this.getType() +
            ";l=" + this.getLength();
    }
    
    // 由信消息概述获取动作对象
    public static OFAction fromString(String val) {
        String tokens[] = val.split(";");
        if (!tokens[0].equals("ofaction"))
            throw new IllegalArgumentException("expected 'ofaction' but got '" + 
                    tokens[0] + "'");
        String type_tokens[] = tokens[1].split("="); 
        String len_tokens[] = tokens[2].split("=");
        OFAction action = new OFAction();
        action.setLength(Short.valueOf(len_tokens[1]));
        action.setType(OFActionType.valueOf(type_tokens[1]));
        return action;
    }

	// 读取:字节缓冲区(ByteBuffer)-->数据
    // 写入:数据-->字节缓冲区(ByteBuffer)
    // 忽略填充值
    public void readFrom(ByteBuffer data) {
        this.type = OFActionType.valueOf(data.getShort());
        this.length = data.getShort();
    }
    public void writeTo(ByteBuffer data) {
        data.putShort(type.getTypeValue());
        data.putShort(length);
    }

	// 重写hashCode函数与equals函数
    @Override
    public int hashCode() {
        final int prime = 347;
        int result = 1;
        result = prime * result + length;
        result = prime * result + ((type == null) ? 0 : type.hashCode());
        return result;
    }
    @Override
    public boolean equals(Object obj) {
        if (this == obj) {
            return true;
        }
        if (obj == null) {
            return false;
        }
        if (!(obj instanceof OFAction)) {
            return false;
        }
        OFAction other = (OFAction) obj;
        if (length != other.length) {
            return false;
        }
        if (type == null) {
            if (other.type != null) {
                return false;
            }
        } else if (!type.equals(other.type)) {
            return false;
        }
        return true;
    }

    // 浅克隆
    @Override
    public OFAction clone() throws CloneNotSupportedException {
        return (OFAction) super.clone();
    }
    
}

同时OpenFlow 1.0协议中定义了动作类型:

  1. 发出动作
  2. 设置VLAN VID
  3. 设置VLAN PCP(设置802.1q优先级)
  4. 剥离VLAN操作(剥离802.1q头部)
  5. 设置以太网源地址
  6. 设置以太网目的地址
  7. 设置IP源地址
  8. 设置IP目的地址
  9. 设置IP ToS(6位)
  10. 设置TCP/IP源端口
  11. 设置TCP/IP目的端口
  12. 输出到队列
  13. 供应商(默认为0xffff,16位)
enum ofp_action_type {
	OFPAT_OUTPUT, 
	OFPAT_SET_VLAN_VID, 
	OFPAT_SET_VLAN_PCP, 
	OFPAT_STRIP_VLAN, 
	OFPAT_SET_DL_SRC, 
	OFPAT_SET_DL_DST, 
	OFPAT_SET_NW_SRC, 
	OFPAT_SET_NW_DST, 
	OFPAT_SET_NW_TOS, 
	OFPAT_SET_TP_SRC, 
	OFPAT_SET_TP_DST, 
	OFPAT_ENQUEUE, 
	OFPAT_VENDOR = 0xffff
};

对应的OFActionType.java源文件结构与前两篇所描述的类型文件结构类似:

  1. 枚举所有类型名称,及其对应子类
  2. 初始化(存储基本信息、添加映射操作)
  3. 映射操作(添加映射)
  4. 查询操作(根据类型值查询类型、查询此类型名、查询此类型类、获取此类构造器)
  5. 实例化操作(实例化接口获取&设置、新建此类型实例)
package org.openflow.protocol.action;

import java.lang.reflect.Constructor;
import org.openflow.protocol.Instantiable;

// 列出OpenFLow动作类型,并映射到类型值与派生类
public enum OFActionType {
    OUTPUT              (0, OFActionOutput.class, new Instantiable<OFAction>() {
                            @Override
                            public OFAction instantiate() {
                                return new OFActionOutput();
                            }}),
    SET_VLAN_VID        (1, OFActionVirtualLanIdentifier.class, new Instantiable<OFAction>() {
                            @Override
                            public OFAction instantiate() {
                                return new OFActionVirtualLanIdentifier();
                            }}),
    SET_VLAN_PCP        (2, OFActionVirtualLanPriorityCodePoint.class, new Instantiable<OFAction>() {
                            @Override
                            public OFAction instantiate() {
                                return new OFActionVirtualLanPriorityCodePoint();
                            }}),
    STRIP_VLAN          (3, OFActionStripVirtualLan.class, new Instantiable<OFAction>() {
                            @Override
                            public OFAction instantiate() {
                                return new OFActionStripVirtualLan();
                            }}),
    SET_DL_SRC          (4, OFActionDataLayerSource.class, new Instantiable<OFAction>() {
                            @Override
                            public OFAction instantiate() {
                                return new OFActionDataLayerSource();
                            }}),
    SET_DL_DST          (5, OFActionDataLayerDestination.class, new Instantiable<OFAction>() {
                            @Override
                            public OFAction instantiate() {
                                return new OFActionDataLayerDestination();
                            }}),
    SET_NW_SRC          (6, OFActionNetworkLayerSource.class, new Instantiable<OFAction>() {
                            @Override
                            public OFAction instantiate() {
                                return new OFActionNetworkLayerSource();
                            }}),
    SET_NW_DST          (7, OFActionNetworkLayerDestination.class, new Instantiable<OFAction>() {
                            @Override
                            public OFAction instantiate() {
                                return new OFActionNetworkLayerDestination();
                            }}),
    SET_NW_TOS          (8, OFActionNetworkTypeOfService.class, new Instantiable<OFAction>() {
                            @Override
                            public OFAction instantiate() {
                                return new OFActionNetworkTypeOfService();
                            }}),
    SET_TP_SRC          (9, OFActionTransportLayerSource.class, new Instantiable<OFAction>() {
                            @Override
                            public OFAction instantiate() {
                                return new OFActionTransportLayerSource();
                            }}),
    SET_TP_DST          (10, OFActionTransportLayerDestination.class, new Instantiable<OFAction>() {
                            @Override
                            public OFAction instantiate() {
                                return new OFActionTransportLayerDestination();
                            }}),
    OPAQUE_ENQUEUE      (11, OFActionEnqueue.class, new Instantiable<OFAction>() {
                            @Override
                            public OFAction instantiate() {
                                return new OFActionEnqueue();
                            }}),
    VENDOR              (0xffff, OFActionVendor.class, new Instantiable<OFAction>() {
                            @Override
                            public OFAction instantiate() {
                                return new OFActionVendor();
                            }});

    protected static OFActionType[] mapping;

    protected Class<? extends OFAction> clazz;
    protected Constructor<? extends OFAction> constructor;
    protected Instantiable<OFAction> instantiable;
    protected int minLen;
    protected short type;

    // 初始化OFActionType类,初始化数据包括类型值、长度与派生类
    OFActionType(int type, Class<? extends OFAction> clazz, Instantiable<OFAction> instantiable) {
        this.type = (short) type;
        this.clazz = clazz;
        this.instantiable = instantiable;
        try {
            this.constructor = clazz.getConstructor(new Class[]{});
        } catch (Exception e) {
            throw new RuntimeException(
                    "Failure getting constructor for class: " + clazz, e);
        }
        OFActionType.addMapping(this.type, this);
    }

    // 添加:一个从类型值到OPActionType的枚举映射
    static public void addMapping(short i, OFActionType t) {
        if (mapping == null)
            mapping = new OFActionType[16];
        // bring higher mappings down to the edge of our array
        if (i < 0)
            i = (short) (16 + i);
        OFActionType.mapping[i] = t;
    }

    // 通过类型值获取OPActionType枚举类型
    static public OFActionType valueOf(short i) {
        if (i < 0)
            i = (short) (16+i);
        return OFActionType.mapping[i];
    }

    // 获取与此OFActionType对应的类型值
    public short getTypeValue() {
        return this.type;
    }

    // 获取与此OFActionType对应的OFAction子类
    public Class<? extends OFAction> toClass() {
        return clazz;
    }

    // 获取此OFType实现类的无参构造函数
    public Constructor<? extends OFAction> getConstructor() {
        return constructor;
    }

    // 获取一个通过此OFType表示的OFMessage的新实例
    public OFAction newInstance() {
        return instantiable.instantiate();
    }

    // 获取&设置:可实例化接口
    public Instantiable<OFAction> getInstantiable() {
        return instantiable;
    }
    public void setInstantiable(Instantiable<OFAction> instantiable) {
        this.instantiable = instantiable;
    }
}

总的来说OPAction的操作仅包含头部64位中的前32位(包含16位类型值与16位长度值),同时实现了可克隆接口,在之后的源码详细文件解析中再来看其实现拷贝的作用,同时对于协议定义的类型而言,其对应的枚举类结构大同小异,与之前两篇关于流表的头部字段、计数器的枚举类结构相同。

  • 10
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

枫零NET

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值