流表(Flow Table):头部字段(Header Fields)
OpenFlow流表(Flow Table)
流表记录(Flow Table Entry)应当包含三个部分:
- 头部字段(header fields);
- 计数器(counters);
- 动作(actions)。
头部字段 | 计数器 | 动作 |
---|---|---|
用于匹配包 | 通过统计来更新已匹配的包 | 应用已匹配的包 |
其中,在包的头部字段中,合规的12个元组名称及其属性如下所示:
元组名 | 位数 | 使用场景 |
---|---|---|
进入端口(Ingress Port) | -(依赖于实现) | 所有包 |
以太网源地址(Ethernet source address) | 48 | 所有已开启端口的包 |
以太网目的地址(Ethernet destination address) | 48 | 所有已开启端口的包 |
以太网类型(Ethernet type) | 16 | 所有已开启端口的包 |
VLAN ID(VLAN id) | 12 | 所有以太网类型为0x8100的包 |
VLAN优先级(VLAN priority) | 3 | 所有以太网类型为0x8100的包 |
IP源地址(IP source address) | 32 | 所有IP和ARP包 |
IP目的地址(IP destination address) | 32 | 所有IP和ARP包 |
IP协议(IP protocol) | 8 | 所有IP、IPoE、ARP包 |
IP ToS位(IP ToS bits) | 6 | 所有IP包 |
传输源端口/ICMP类型(Transport source port / ICMP Type) | 16 | 所有TCP、UDP和ICMP包 |
传输目的端口/ICMP码值(Transport destination port / ICMP Code) | 16 | 所有TCP、UDP和ICMP包 |
所有的OpenFlow消息头部都具备以下头部结构,合计8字节,即64位:
- version:8位,OpenFlow版本号
- type:8位,OpenFlow消息类型,包含三种:
- Controller-to-Switch(控制器对交换机)
- Asynchronous(异步信息)
- Symmetric(对称信息)
- length:16位,包含头部在内的消息长度
- xid:32位,事务ID,使用请求时相同的事务ID作为回复ID,方便配对
// OpenFlow v1.0规定结构
struct ofp_header {
uint8_t version;
uint8_t type;
uint16_t length;
uint32_t xid;
};
OFP_ASSERT(sizeof(struct ofp_header) == 8);
在下方OpenFlowJ项目的OFMessage.java源文件中可看出,对于通用头部结构的操作可归纳为以下几点:
- 四个通用头部字段的获取&设置函数(version、length、type、xid)
- 计算长度用于写入数据(computeLength)
- 转换为字符串(toString)
- 重写hashCode函数
- 重写equals函数
package org.openflow.protocol;
import java.nio.ByteBuffer;
import org.openflow.util.U16;
import org.openflow.util.U32;
import org.openflow.util.U8;
// OFMessage.java文件
public class OFMessage {
// 定义静态初始值
public static byte OFP_VERSION = 0x01; // OpenFlow版本:1.0
public static int MINIMUM_LENGTH = 8; // 最短长度(包含头部字段在内):8
// 通用OpenFlow头部字段结构
protected byte version;
protected OFType type;
protected short length;
protected int xid;
// 初始化OpenFlow版本
public OFMessage() {
this.version = OFP_VERSION;
}
// 获取&设置:有、无符号的消息长度
public short getLength() {
return length;
}
public int getLengthU() {
return U16.f(length);
}
public OFMessage setLength(short length) {
this.length = length;
return this;
}
public OFMessage setLengthU(int length) {
this.length = U16.t(length);
return this;
}
// 获取&设置:消息类型
public OFType getType() {
return type;
}
public OFMessage setType(OFType type) {
this.type = type;
return this;
}
// 获取&设置:OpenFlow版本号
public byte getVersion() {
return version;
}
public OFMessage setVersion(byte version) {
this.version = version;
return this;
}
// 获取&设置:事务ID
public int getXid() {
return xid;
}
public OFMessage setXid(int xid) {
this.xid = xid;
return this;
}
// 读取:字节缓冲区(ByteBuffer)-->数据
// 写入:数据-->字节缓冲区(ByteBuffer)
public void readFrom(ByteBuffer data) {
this.version = data.get();
this.type = OFType.valueOf(data.get()); // OPType.valueOf()函数:将编号翻译成对应的类型
this.length = data.getShort();
this.xid = data.getInt();
}
public void writeTo(ByteBuffer data) {
computeLength();
data.put(version);
data.put(type.getTypeValue()); // type.getTypeValue()函数:获取类型值
data.putShort(length);
data.putInt(xid);
}
// 工具:计算长度&转换成字符串
public void computeLength() {
this.length = (short) MINIMUM_LENGTH;
}
public String toString() {
return "ofmsg" +
":v=" + U8.f(this.getVersion()) +
";t=" + this.getType() +
";l=" + this.getLengthU() +
";x=" + U32.f(this.getXid());
}
// 重写hashCode函数与equals函数
@Override
public int hashCode() {
final int prime = 97;
int result = 1;
result = prime * result + length;
result = prime * result + ((type == null) ? 0 : type.hashCode());
result = prime * result + version;
result = prime * result + xid;
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (!(obj instanceof OFMessage)) {
return false;
}
OFMessage other = (OFMessage) obj;
if (length != other.length) {
return false;
}
if (type == null) {
if (other.type != null) {
return false;
}
} else if (!type.equals(other.type)) {
return false;
}
if (version != other.version) {
return false;
}
if (xid != other.xid) {
return false;
}
return true;
}
}
在解析完头部通用字段操作后,补充一些OpenFlow的类型介绍
类型名 | 消息分类 | 类型值 |
---|---|---|
HELLO | 对称消息 | 0 |
ERROR | 对称消息 | 1 |
ECHO_REQUEST | 对称消息 | 2 |
ECHO_REPLY | 对称消息 | 3 |
VENDOR | 对称消息 | 4 |
FEATURES_REQUEST | 控制器/交换机消息 | 5 |
FEATURES_REPLY | 控制器/交换机消息 | 6 |
GET_CONFIG_REQUEST | 控制器/交换机消息 | 7 |
GET_CONFIG_REPLY | 控制器/交换机消息 | 8 |
SET_CONFIG | 控制器/交换机消息 | 9 |
PACKET_IN | 异步消息 | 10 |
FLOW_REMOVED | 异步消息 | 11 |
PORT_STATUS | 异步消息 | 12 |
PACKET_OUT | 控制器/交换机消息 | 13 |
FLOW_MOD | 控制器/交换机消息 | 14 |
PORT_MOD | 控制器/交换机消息 | 15 |
STATS_REQUEST | 控制器/交换机消息 | 16 |
STATS_REPLY | 控制器/交换机消息 | 17 |
BARRIER_REQUEST | 控制器/交换机消息 | 18 |
BARRIER_REPLY | 控制器/交换机消息 | 19 |
QUEUE_CONFIG_REQUEST | 控制器/交换机消息 | 20 |
QUEUE_CONFIG_REPLY | 控制器/交换机消息 | 21 |
UNKNOWN | 控制器/交换机消息 | 0xff |
// OpenFlow v1.0规定类型
enum ofp_type {
/* 不可变消息 */
OFPT_HELLO,
OFPT_ERROR,
OFPT_ECHO_REQUEST,
OFPT_ECHO_REPLY,
OFPT_VENDOR,
/* 交换机配置消息 */
OFPT_FEATURES_REQUEST,
OFPT_FEATURES_REPLY,
OFPT_GET_CONFIG_REQUEST,
OFPT_GET_CONFIG_REPLY,
OFPT_SET_CONFIG,
/* 异步消息 */
OFPT_PACKET_IN,
OFPT_FLOW_REMOVED,
OFPT_PORT_STATUS,
/* 控制器命令消息 */
OFPT_PACKET_OUT,
OFPT_FLOW_MOD,
OFPT_PORT_MOD,
/* 统计消息 */
OFPT_STATS_REQUEST,
OFPT_STATS_REPLY,
/* 栅栏消息 */
OFPT_BARRIER_REQUEST,
OFPT_BARRIER_REPLY,
/* 队列配置消息 */
OFPT_QUEUE_GET_CONFIG_REQUEST,
OFPT_QUEUE_GET_CONFIG_REPLY
};
在下方OpenFlowJ项目的OFType.java源文件中对每个类型字段做了类型值定义,可归纳为以下几个功能点:
- 枚举OpenFlow类型
- 添加&删除一个类型值到OFType枚举的映射
- 通过类型值获取OPType枚举类型
- 获取与此OFType对应的类型值
- 获取与此OFType对应的OFMessage子类
- 获取此OFType实现类的无参构造函数
- 获取一个通过此OFType表示的OFMessage的新实例
- 获取&设置可实例化接口
package org.openflow.protocol;
import java.lang.reflect.Constructor;
import org.openflow.util.U8;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
// OFType.java
// OpenFlow v1.0中,type字段占8位
public enum OFType {
HELLO (0, OFHello.class, new Instantiable<OFMessage>() {
@Override
public OFMessage instantiate() {
return new OFHello();
}}),
ERROR (1, OFError.class, new Instantiable<OFMessage>() {
@Override
public OFMessage instantiate() {
return new OFError();
}}),
ECHO_REQUEST (2, OFEchoRequest.class, new Instantiable<OFMessage>() {
@Override
public OFMessage instantiate() {
return new OFEchoRequest();
}}),
ECHO_REPLY (3, OFEchoReply.class, new Instantiable<OFMessage>() {
@Override
public OFMessage instantiate() {
return new OFEchoReply();
}}),
VENDOR (4, OFVendor.class, new Instantiable<OFMessage>() {
@Override
public OFMessage instantiate() {
return new OFVendor();
}}),
FEATURES_REQUEST (5, OFFeaturesRequest.class, new Instantiable<OFMessage>() {
@Override
public OFMessage instantiate() {
return new OFFeaturesRequest();
}}),
FEATURES_REPLY (6, OFFeaturesReply.class, new Instantiable<OFMessage>() {
@Override
public OFMessage instantiate() {
return new OFFeaturesReply();
}}),
GET_CONFIG_REQUEST (7, OFGetConfigRequest.class, new Instantiable<OFMessage>() {
@Override
public OFMessage instantiate() {
return new OFGetConfigRequest();
}}),
GET_CONFIG_REPLY (8, OFGetConfigReply.class, new Instantiable<OFMessage>() {
@Override
public OFMessage instantiate() {
return new OFGetConfigReply();
}}),
SET_CONFIG (9, OFSetConfig.class, new Instantiable<OFMessage>() {
@Override
public OFMessage instantiate() {
return new OFSetConfig();
}}),
PACKET_IN (10, OFPacketIn.class, new Instantiable<OFMessage>() {
@Override
public OFMessage instantiate() {
return new OFPacketIn();
}}),
FLOW_REMOVED (11, OFFlowRemoved.class, new Instantiable<OFMessage>() {
@Override
public OFMessage instantiate() {
return new OFFlowRemoved();
}}),
PORT_STATUS (12, OFPortStatus.class, new Instantiable<OFMessage>() {
@Override
public OFMessage instantiate() {
return new OFPortStatus();
}}),
PACKET_OUT (13, OFPacketOut.class, new Instantiable<OFMessage>() {
@Override
public OFMessage instantiate() {
return new OFPacketOut();
}}),
FLOW_MOD (14, OFFlowMod.class, new Instantiable<OFMessage>() {
@Override
public OFMessage instantiate() {
return new OFFlowMod();
}}),
PORT_MOD (15, OFPortMod.class, new Instantiable<OFMessage>() {
@Override
public OFMessage instantiate() {
return new OFPortMod();
}}),
STATS_REQUEST (16, OFStatisticsRequest.class, new Instantiable<OFMessage>() {
@Override
public OFMessage instantiate() {
return new OFStatisticsRequest();
}}),
STATS_REPLY (17, OFStatisticsReply.class, new Instantiable<OFMessage>() {
@Override
public OFMessage instantiate() {
return new OFStatisticsReply();
}}),
BARRIER_REQUEST (18, OFBarrierRequest.class, new Instantiable<OFMessage>() {
@Override
public OFMessage instantiate() {
return new OFBarrierRequest();
}}),
BARRIER_REPLY (19, OFBarrierReply.class, new Instantiable<OFMessage>() {
@Override
public OFMessage instantiate() {
return new OFBarrierReply();
}}),
QUEUE_CONFIG_REQUEST (20, OFMessage.class, new Instantiable<OFMessage>() {
@Override
public OFMessage instantiate() {
return new OFQueueConfigRequest();
}}),
QUEUE_CONFIG_REPLY (21, OFMessage.class, new Instantiable<OFMessage>() {
@Override
public OFMessage instantiate() {
return new OFQueueConfigReply();
}}),
UNKNOWN (0xff, OFUnknownMessage.class, new Instantiable<OFMessage>() {
@Override
public OFMessage instantiate() {
return new OFUnknownMessage();
}});
protected static Logger log = LoggerFactory.getLogger(OFType.class);
static OFType[] mapping;
protected Class<? extends OFMessage> clazz;
protected Constructor<? extends OFMessage> constructor;
protected Instantiable<OFMessage> instantiable;
protected byte type;
// 初始化OFType
OFType(int type, Class<? extends OFMessage> clazz, Instantiable<OFMessage> instantiator) {
this.type = (byte) type;
this.clazz = clazz;
this.instantiable = instantiator;
try {
this.constructor = clazz.getConstructor(new Class[]{}); // clazz.getConstructor()函数:获取有参数构造函数
} catch (Exception e) {
throw new RuntimeException(
"Failure getting constructor for class: " + clazz, e);
}
OFType.addMapping(this.type, this);
}
// 添加&移除:一个从类型值到OPType的枚举映射
static public void addMapping(byte i, OFType t) {
if (mapping == null)
mapping = new OFType[32];
if (i >= 0 && i < 32)
OFType.mapping[i] = t;
}
static public void removeMapping(byte i) {
if (i >= 0 && i < 32)
OFType.mapping[i] = null;
}
// 通过类型值获取OPType枚举类型
static public OFType valueOf(Byte i) {
try {
return OFType.mapping[i];
} catch (ArrayIndexOutOfBoundsException e) {
log.warn("Unknown message type requested: {}", U8.f(i));
return UNKNOWN;
}
}
// 获取与此OFType对应的类型值
public byte getTypeValue() {
return this.type;
}
// 获取与此OFType对应的OFMessage子类
public Class<? extends OFMessage> toClass() {
return clazz;
}
// 获取此OFType实现类的无参构造函数
public Constructor<? extends OFMessage> getConstructor() {
return constructor;
}
// 获取一个通过此OFType表示的OFMessage的新实例
public OFMessage newInstance() {
return instantiable.instantiate();
}
// 获取&设置:可实例化接口
public Instantiable<OFMessage> getInstantiable() {
return instantiable;
}
public void setInstantiable(Instantiable<OFMessage> instantiable) {
this.instantiable = instantiable;
}
}