在平时使用ovs中,常常用到的ovs命令,参数,与举例总结,持续更新中…
进程启动
1.先准备ovs的工做目录,数据库存储路径等
mkdir -p /etc/openvswitch mkdir -p /var/run/openvswitch
2.先启动ovsdb-server
ovsdb-server /etc/openvswitch/conf.db \ -vconsole:emer -vsyslog:err -vfile:info \ --remote=punix:/var/run/openvswitch/db.sock \ --private-key=db:Open_vSwitch,SSL,private_key \ --certificate=db:Open_vSwitch,SSL,certificate \ --bootstrap-ca-cert=db:Open_vSwitch,SSL,ca_cert --no-chdir \ --log-file=/var/log/openvswitch/ovsdb-server.log \ --pidfile=/var/run/openvswitch/ovsdb-server.pid \ --detach --monitor
ps: 若是想清除配置,能够先删除/etc/openvswitch/*,而后再手动建立一个数据库:
ovsdb-tool create /etc/openvswitch/conf.db /usr/share/openvswitch/vswitch.ovsschema
3.初始化ovsdb
针对于新建立的数据库才须要初始化
ovs-vsctl --no-wait init
4.启动vswitchd进程
ovs-vswitchd unix:/var/run/openvswitch/db.sock \ -vconsole:emer -vsyslog:err -vfile:info --mlockall --no-chdir \ --log-file=/var/log/openvswitch/ovs-vswitchd.log \ --pidfile=/var/run/openvswitch/ovs-vswitchd.pid \ --detach --monitor
ovs经常使用命令
控制管理类
1.查看网桥和端口
ovs-vsctl show
2.建立一个网桥
ovs-vsctl add-br br0 ovs-vsctl set bridge br0 datapath_type=netdev
3.添加/删除一个端口
4.设置/清除网桥的openflow协议版本
ovs-vsctl set bridge br0 protocols=OpenFlow13 ovs-vsctl clear bridge br0 protocols
5.查看某网桥当前流表
ovs-ofctl dump-flows br0 ovs-ofctl -O OpenFlow13 dump-flows br0 ovs-appctl bridge/dump-flows br0
6.设置/删除控制器
ovs-vsctl set-controller br0 tcp:1.2.3.4:6633 ovs-vsctl del-controller br0
7.查看控制器列表
ovs-vsctl list controller
8.设置/删除被动链接控制器
ovs-vsctl set-manager tcp:1.2.3.4:6640 ovs-vsctl get-manager ovs-vsctl del-manager
9.设置/移除可选选项
ovs-vsctl set Interface eth0 options:link_speed=1G ovs-vsctl remove Interface eth0 options link_speed
10.设置fail模式,支持standalone或者secure
standalone(default):清除全部控制器下发的流表,ovs本身接管
secure:按照原来流表继续转发
ovs-vsctl del-fail-mode br0 ovs-vsctl set-fail-mode br0 secure ovs-vsctl get-fail-mode br0
11.查看接口id等
ovs-appctl dpif/show
12.查看接口统计
ovs-ofctl dump-ports br0
流表类
流表操做
1.添加普通流表
ovs-ofctl add-flow br0 in_port=1,actions=output:2
2.删除全部流表
ovs-ofctl del-flows br0
3.按匹配项来删除流表
ovs-ofctl del-flows br0 "in_port=1"
匹配项
1.匹配vlan tag,范围为0-4095
ovs-ofctl add-flow br0 priority=401,in_port=1,dl_vlan=777,actions=output:2
2.匹配vlan pcp,范围为0-7
ovs-ofctl add-flow br0 priority=401,in_port=1,dl_vlan_pcp=7,actions=output:2
3.匹配源/目的MAC
ovs-ofctl add-flow br0 in_port=1,dl_src=00:00:00:00:00:01/00:00:00:00:00:01,actions=output:2 ovs-ofctl add-flow br0 in_port=1,dl_dst=00:00:00:00:00:01/00:00:00:00:00:01,actions=output:2
4.匹配以太网类型,范围为0-65535
ovs-ofctl add-flow br0 in_port=1,dl_type=0x0806,actions=output:2
5.匹配源/目的IP
条件:指定dl_type=0x0800,或者ip/tcp
ovs-ofctl add-flow br0 ip,in_port=1,nw_src=10.10.0.0/16,actions=output:2 ovs-ofctl add-flow br0 ip,in_port=1,nw_dst=10.20.0.0/16,actions=output:2
6.匹配协议号,范围为0-255
条件:指定dl_type=0x0800或者ip
7.匹配IP ToS/DSCP,tos范围为0-255,DSCP范围为0-63
条件:指定dl_type=0x0800/0x86dd,而且ToS低2位会被忽略(DSCP值为ToS的高6位,而且低2位为预留位)
ovs-ofctl add-flow br0 ip,in_port=1,nw_tos=68,actions=output:2 ovs-ofctl add-flow br0 ip,in_port=1,ip_dscp=62,actions=output:2
8.匹配IP ecn位,范围为0-3
条件:指定dl_type=0x0800/0x86dd
ovs-ofctl add-flow br0 ip,in_port=1,ip_ecn=2,actions=output:2
9.匹配IP TTL,范围为0-255
ovs-ofctl add-flow br0 ip,in_port=1,nw_ttl=128,actions=output:2
10.匹配tcp/udp,源/目的端口,范围为0-65535
11.匹配tcp flags
tcp flags=fin,syn,rst,psh,ack,urg,ece,cwr,ns
ovs-ofctl add-flow br0 tcp,tcp_flags=ack,actions=output:2
12.匹配icmp code,范围为0-255
条件:指定icmp
ovs-ofctl add-flow br0 icmp,icmp_code=2,actions=output:2
13.匹配vlan TCI
TCI低12位为vlan id,高3位为priority,例如tci=0xf123则vlan_id为0x123和vlan_pcp=7
ovs-ofctl add-flow br0 in_port=1,vlan_tci=0xf123,actions=output:2
14.匹配mpls label
条件:指定dl_type=0x8847/0x8848
ovs-ofctl add-flow br0 mpls,in_port=1,mpls_label=7,actions=output:2
15.匹配mpls tc,范围为0-7
条件:指定dl_type=0x8847/0x8848
ovs-ofctl add-flow br0 mpls,in_port=1,mpls_tc=7,actions=output:2
16.匹配tunnel id,源/目的IP
一些匹配项的速记符
速记符 |
匹配项 |
ip |
dl_type=0x800 |
ipv6 |
dl_type=0x86dd |
icmp |
dl_type=0x0800,nw_proto=1 |
icmp6 |
dl_type=0x86dd,nw_proto=58 |
tcp |
dl_type=0x0800,nw_proto=6 |
tcp6 |
dl_type=0x86dd,nw_proto=6 |
udp |
dl_type=0x0800,nw_proto=17 |
udp6 |
dl_type=0x86dd,nw_proto=17 |
sctp |
dl_type=0x0800,nw_proto=132 |
sctp6 |
dl_type=0x86dd,nw_proto=132 |
arp |
dl_type=0x0806 |
rarp |
dl_type=0x8035 |
mpls |
dl_type=0x8847 |
mplsm |
dl_type=0x8848 |
指令动做
1.动做为出接口
从指定接口转发出去
ovs-ofctl add-flow br0 in_port=1,actions=output:2
2.动做为指定group
group id为已建立的group table
ovs-ofctl add-flow br0 in_port=1,actions=group:666
3.动做为normal
转为L2/L3处理流程
ovs-ofctl add-flow br0 in_port=1,actions=normal
4.动做为flood
从全部物理接口转发出去,除了入接口和已关闭flooding的接口
ovs-ofctl add-flow br0 in_port=1,actions=flood
5.动做为all
从全部物理接口转发出去,除了入接口
ovs-ofctl add-flow br0 in_port=1,actions=all
6.动做为local
通常是转发给本地网桥
ovs-ofctl add-flow br0 in_port=1,actions=local
7.动做为in_port
从入接口转发回去
ovs-ofctl add-flow br0 in_port=1,actions=in_port
8.动做为controller
以packet-in消息上送给控制器
ovs-ofctl add-flow br0 in_port=1,actions=controller
9.动做为drop
丢弃数据包操做
ovs-ofctl add-flow br0 in_port=1,actions=drop
10.动做为mod_vlan_vid
修改报文的vlan id,该选项会使vlan_pcp置为0
ovs-ofctl add-flow br0 in_port=1,actions=mod_vlan_vid:8,output:2
11.动做为mod_vlan_pcp
修改报文的vlan优先级,该选项会使vlan_id置为0
ovs-ofctl add-flow br0 in_port=1,actions=mod_vlan_pcp:7,output:2
12.动做为strip_vlan
剥掉报文内外层vlan tag
ovs-ofctl add-flow br0 in_port=1,actions=strip_vlan,output:2
13.动做为push_vlan
在报文外层压入一层vlan tag,须要使用openflow1.1以上版本兼容
ovs-ofctl add-flow -O OpenFlow13 br0 in_port=1,actions=push_vlan:0x8100,set_field:4097-\>vlan_vid,output:2
ps: set field值为4096+vlan_id,而且vlan优先级为0,即4096-8191,对应的vlan_id为0-4095
14.动做为push_mpls
修改报文的ethertype,而且压入一个MPLS LSE
ovs-ofctl add-flow br0 in_port=1,actions=push_mpls:0x8847,set_field:10-\>mpls_label,output:2
15.动做为pop_mpls
剥掉最外层mpls标签,而且修改ethertype为非mpls类型
ovs-ofctl add-flow br0 mpls,in_port=1,mpls_label=20,actions=pop_mpls:0x0800,output:2
16.动做为修改源/目的MAC,修改源/目的IP
17.动做为修改TCP/UDP/SCTP源目的端口
18.动做为mod_nw_tos
条件:指定dl_type=0x0800
修改ToS字段的高6位,范围为0-255,值必须为4的倍数,而且不会去修改ToS低2位ecn值
ovs-ofctl add-flow br0 ip,in_port=1,actions=mod_nw_tos:68,output:2
19.动做为mod_nw_ecn
条件:指定dl_type=0x0800,须要使用openflow1.1以上版本兼容
修改ToS字段的低2位,范围为0-3,而且不会去修改ToS高6位的DSCP值
ovs-ofctl add-flow br0 ip,in_port=1,actions=mod_nw_ecn:2,output:2
20.动做为mod_nw_ttl
修改IP报文ttl值,须要使用openflow1.1以上版本兼容
ovs-ofctl add-flow -O OpenFlow13 br0 in_port=1,actions=mod_nw_ttl:6,output:2
21.动做为dec_ttl
对IP报文进行ttl自减操做
ovs-ofctl add-flow br0 in_port=1,actions=dec_ttl,output:2
22.动做为set_mpls_label
对报文最外层mpls标签进行修改,范围为20bit值
ovs-ofctl add-flow br0 in_port=1,actions=set_mpls_label:666,output:2
23.动做为set_mpls_tc
对报文最外层mpls tc进行修改,范围为0-7
ovs-ofctl add-flow br0 in_port=1,actions=set_mpls_tc:7,output:2
24.动做为set_mpls_ttl
对报文最外层mpls ttl进行修改,范围为0-255
ovs-ofctl add-flow br0 in_port=1,actions=set_mpls_ttl:255,output:2
25.动做为dec_mpls_ttl
对报文最外层mpls ttl进行自减操做
ovs-ofctl add-flow br0 in_port=1,actions=dec_mpls_ttl,output:2
26.动做为move NXM字段
使用move参数对NXM字段进行操做
ps: 经常使用NXM字段参照表
NXM字段 |
报文字段 |
NXM_OF_ETH_SRC |
源MAC |
NXM_OF_ETH_DST |
目的MAC |
NXM_OF_ETH_TYPE |
以太网类型 |
NXM_OF_VLAN_TCI |
vid |
NXM_OF_IP_PROTO |
IP协议号 |
NXM_OF_IP_TOS |
IP ToS值 |
NXM_NX_IP_ECN |
IP ToS ECN |
NXM_OF_IP_SRC |
源IP |
NXM_OF_IP_DST |
目的IP |
NXM_OF_TCP_SRC |
TCP源端口 |
NXM_OF_TCP_DST |
TCP目的端口 |
NXM_OF_UDP_SRC |
UDP源端口 |
NXM_OF_UDP_DST |
UDP目的端口 |
NXM_OF_SCTP_SRC |
SCTP源端口 |
NXM_OF_SCTP_DST |
SCTP目的端口 |
27.动做为load NXM字段
使用load参数对NXM字段进行赋值操做
28.动做为pop_vlan
弹出报文最外层vlan tag
ovs-ofctl add-flow br0 in_port=1,dl_type=0x8100,dl_vlan=777,actions=pop_vlan,output:2
meter表
经常使用操做
因为meter表是openflow1.3版本之后才支持,因此全部命令须要指定OpenFlow1.3版本以上
ps: 在openvswitch-v2.8以前的版本中,还不支持meter
在v2.8版本以后已经实现,要正常使用的话,须要注意的是datapath类型要指定为netdev,band type暂时只支持drop,还不支持DSCP REMARK
1.查看当前设备对meter的支持
ovs-ofctl -O OpenFlow13 meter-features br0
2.查看meter表
ovs-ofctl -O OpenFlow13 dump-meters br0
3.查看meter统计
ovs-ofctl -O OpenFlow13 meter-stats br0
4.建立meter表
5.删除meter表
6.建立流表
ovs-ofctl -O OpenFlow13 add-flow br0 in_port=1,actions=meter:1,output:2
group表
因为group表是openflow1.1版本之后才支持,因此全部命令须要指定OpenFlow1.1版本以上
经常使用操做
group table支持4种类型
- all:全部buckets都执行一遍
- select: 每次选择其中一个bucket执行,经常使用于负载均衡应用
- ff(FAST FAILOVER):快速故障修复,用于检测解决接口等故障
- indirect:间接执行,相似于一个函数方法,被另外一个group来调用
1.查看当前设备对group的支持
ovs-ofctl -O OpenFlow13 dump-group-features br0
2.查看group表
ovs-ofctl -O OpenFlow13 dump-groups br0
3.建立group表
# 类型为all
ovs-ofctl -O OpenFlow13 add-group br0 group_id=1,type=all,bucket=output:1,bucket=output:2,bucket=output:3
# 类型为select ovs-ofctl -O OpenFlow13 add-group br0 group_id=2,type=select,bucket=output:1,bucket=output:2,bucket=output:3 # 类型为select,指定hash方法(5元组,OpenFlow1.5+) ovs-ofctl -O OpenFlow15 add-group br0 group_id=3,type=select,selection_method=hash,fields=ip_src,bucket=output:2,bucket=output:3
4.删除group表
ovs-ofctl -O OpenFlow13 del-groups br0 group_id=2
5.建立流表
ovs-ofctl -O OpenFlow13 add-flow br0 in_port=1,actions=group:2
goto table配置
数据流先从table0开始匹配,如actions有goto_table,再进行后续table的匹配,实现多级流水线,如需使用goto table,则建立流表时,指定table id,范围为0-255,不指定则默认为table0
1.在table0中添加一条流表条目
ovs-ofctl add-flow br0 table=0,in_port=1,actions=goto_table=1
2.在table1中添加一条流表条目
ovs-ofctl add-flow br0 table=1,ip,nw_dst=10.10.0.0/16,actions=output:2
tunnel配置
如需配置tunnel,必需确保当前系统对各tunnel的remote ip网络可达
gre
1.建立一个gre接口,而且指定端口id=1001
ovs-vsctl add-port br0 gre1 -- set Interface gre1 type=gre options:remote_ip=1.1.1.1 ofport_request=1001
2.可选选项
将tos或者ttl在隧道上继承,并将tunnel id设置成123
ovs-vsctl set Interface gre1 options:tos=inherit options:ttl=inherit options:key=123
3.建立关于gre流表
vxlan
1.建立一个vxlan接口,而且指定端口id=2001
ovs-vsctl add-port br0 vxlan1 -- set Interface vxlan1 type=vxlan options:remote_ip=1.1.1.1 ofport_request=2001
2.可选选项
将tos或者ttl在隧道上继承,将vni设置成123,UDP目的端为设置成8472(默认为4789)
ovs-vsctl set Interface vxlan1 options:tos=inherit options:ttl=inherit options:key=123 options:dst_port=8472
3.建立关于vxlan流表
sflow配置
1.对网桥br0进行sflow监控
- agent: 与collector通讯所在的网口名,一般为管理口
- target: collector监听的IP地址和端口,端口默认为6343
- header: sFlow在采样时截取报文头的长度
- polling: 采样时间间隔,单位为秒
2.查看建立的sflow
ovs-vsctl list sflow
3.删除对应的网桥sflow配置,参数为sFlow UUID
ovs-vsctl remove bridge br0 sflow 7b9b962e-fe09-407c-b224-5d37d9c1f2b3
4.删除网桥下全部sflow配置
QoS配置
ingress policing
1.配置ingress policing,对接口eth0入流限速10Mbps
ovs-vsctl set interface eth0 ingress_policing_rate=10000 ovs-vsctl set interface eth0 ingress_policing_burst=8000
2.清除相应接口的ingress policer配置
ovs-vsctl set interface eth0 ingress_policing_rate=0 ovs-vsctl set interface eth0 ingress_policing_burst=0
3.查看接口ingress policer配置
ovs-vsctl list interface eth0
4.查看网桥支持的Qos类型
ovs-appctl qos/show-types br0
端口镜像配置
1.配置eth0收到/发送的数据包镜像到eth1
ovs-vsctl -- set bridge br0 mirrors=@m \ -- --id=@eth0 get port eth0 \ -- --id=@eth1 get port eth1 \ -- --id=@m create mirror name=mymirror select-dst-port=@eth0 select-src-port=@eth0 output-port=@eth1
2.删除端口镜像配置
3.清除网桥下全部端口镜像配置
ovs-vsctl clear bridge br0 mirrors
4.查看端口镜像配置
ovs-vsctl get bridge br0 mirrors
https://www.sdnlab.com/20966.html
2.设计考虑
2.1.总体数据结构
ovs datapath classifier涉及的数据结构主要有以下。
网桥数据结构
|
struct datapath {
struct rcu_head rcu;
/* 该结构使用双链表组织 */
struct list_head list_node;
/* 从属该datapath的流表项 */
struct flow_table table;
/* 从属该datapath的vport信息 */
struct hlist_head *ports;
…
}
|
流表数据结构
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
struct flow_table {
/* 用key组织的流表实例 */
struct table_instance *ti;
/* 用unique flow identifier(ufid)组织的流表实例 */
struct table_instance *ufid_ti;
/* 掩码信息缓存表 */
struct mask_cache_entry *mask_cache;
/* 掩码信息列表 */
struct mask_array *mask_array;
/* 记录当前节拍数 */
unsigned long last_rehash;
/* ti流表实例中存储的流表项数目 */
unsigned int count;
/* ufid_ti流表实例中存储的流表项数目 */
unsigned int ufid_count;
}
|
流表实例数据结构
|
struct table_instance {
/* 哈希桶,用于组织各个流表项的具体信息. */
struct flex_array *buckets;
/* 哈希桶大小 */
unsigned int n_buckets;
struct rcu_head rcu;
int node_ver;
u32 hash_seed;
bool keep_flows;
}
|
掩码信息列表数据结构
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
struct mask_array {
struct rcu_head rcu;
/* 当前已存储的掩码信息数目 */
int count;
/* 一共能够存储掩码信息的总数目 */
int max;
/* 具体的掩码信息 */
struct sw_flow_mask *masks[];
}
struct sw_flow_mask {
/* 引用计数 */
int ref_count;
struct rcu_head rcu;
/* 匹配关键字的有效范围,下文会具体解释 */
struct sw_flow_key_range range;
/* 匹配关键字信息 */
struct sw_flow_key key;
}
|
掩码信息缓存表数据结构
|
struct mask_cache_entry {
/* hash值 */
u32 skb_hash;
/* 对应哪个掩码信息,索引值 */
u32 mask_index;
}
|
哈希桶数据结构
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
struct flex_array {
union {
struct {
/* 实际等于sizeof(struct hlist_head) */
int element_size;
/* 一共有多少个struct hlist_head指针. */
int total_nr_elements;
/* 每一个part(页)能够存储多少个struct hlist_head指针 */
int elems_per_part;
u32 reciprocal_value reciprocal_elems;
/* 具体存储struct hlist_head指针的缓冲区 */
struct flex_array_part *parts[];
};
/* 填充字段.表明了整个结构的大小=页大小(4096字节) */
char padding[FLEX_ARRAY_BASE_SIZE];
}
}
struct flex_array_part {
/* 缓冲区.一个页大小,即:4096字节 */
char elements[FLEX_ARRAY_PART_SIZE];
};
|
上述这些数据结构是在源码中的ovs_flow_tbl_init函数里面进行初始化操做的。初始化后以及上述这些数据结构之间的关系以下图所示。

2.2.关键信息范围
从上面2.1节可知,掩码信息(struct sw_flow_mask结构)中记录了一个范围(struct sw_flow_key_range结构),该范围用于标识须要匹配的关键信息最小偏移和最大偏移。为何须要这样作?我的感受和ovs-dpdk datapath classifier中描述的miniflow相似,即:匹配过程当中并不是使用报文的全部字段去匹配,而是使用部分字段,例如使用报文的五元组信息(源IP、目的IP、协议号、源端口、目的端口)。那么使用sw_flow_key_range结构来标识这五元组信息中最小偏移和最大偏移。实际源码中,关键信息是使用struct sw_flow_key结构来描述的。因为该结构字段较多,这里不详细给出。以报文五元组信息为例,这里给出的五元组信息所在sw_flow_key结构的位置和实际源码对应的位置是不相同的,这里只是给出计算最小偏移和最大偏移的概念,以下图所示,关键信息的有效范围为:
最小偏移=M,最大偏移=N

2.3.更新过程
在源码中,对应更新过程的入口函数是:ovs_flow_cmd_new。这个入口函数是处于内核模块中,在接收到报文时,经过下面2.4节所述的查找过程,查找失败时,会将报文的相关信息upcall到用户空间,在用户空间经过查找”慢路径”将对应的actions和mask信息下发到内核空间,在内核空间,处理的入口函数正是ovs_flow_cmd_new。下面将对这个函数的处理过程做详细描述。描述以前,先掌握一些相关的数据结构,以下所示:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
|
struct sw_flow {
struct rcu_head rcu;
/* 哈希桶链表 */
struct {
struct hlist_node node[2];
u32 hash;
} flow_table, ufid_table;
int stats_last_writer;
/* 关键信息.通过掩码运算后的 */
struct sw_flow_key key;
/* 流id信息 */
struct sw_flow_id id;
/* 指向的掩码信息 */
struct sw_flow_mask *mask;
/* 指向的流动做信息 */
struct sw_flow_actions *sf_acts;
struct flow_stats *stats[];
}
struct sw_flow_id {
u32 ufid_len;
union {
/* ufid为128比特,共16字节 */
u32 ufid[4];
struct sw_flow_key *unmasked_key;
}
}
struct sw_flow_actions {
struct rcu_head rcu;
size_t orig_len;
u32 actions_len;
struct nlattr actions[];
}
struct sw_flow_match {
struct sw_flow_key *key;
struct sw_flow_key_range range;
struct sw_flow_mask *mask;
}
|
具体流程以下所示:
1)初始化struct sw_flow *new_flow。分配空间给new_flow,并设置部分的成员值,以下:
new_flow->sf_acts = null;
new_flow->mask = null;
new_flow->id.ufid_len=0;
new_flow->id.unmasked_key=null;
2)构建struct sw_flow_match match信息。根据接收到的关键信息和掩码信息,存储在match.key和match.mask中。

3)计算new_flow->key = match.key & match.mask。
4)将ufid信息提取出来存储在new_flow->id.ufid_len和new_flow->id.ufid中。若是未提供任何的UFID信息(此时的new_flow->id.ufid_len为0),则将match.key信息拷贝一份到new_flow->id.unmasked_key中存储。
5)将actions信息存储在变量struct sw_flow_actions acts中。
6)根据接收到的dp_ifindex获取struct datapath dp信息。
7)若是new_flow->id.ufid_len不为0,则使用new_flow->id.ufid信息去dp->table.ufid_ti指向的哈希桶中找到对应的struct sw_flow信息。假设找不到。
8)而若是new_flow->id.ufid_len为0,则须要在dp->table.ti,同时配合在2)中构建出来的match.key和dp->table.mask_array配合找出对应的struct sw_flow信息。这个详细过程能够参考下面的2.4节。这里也假设找不到。
9)通过上面7)和8)步都没找到对应的struct sw_flow信息。首先设置new_flow->sf_acts=acts(在5)步中获取)。而后使用match.mask去dp->table.mask_array中查找是否已存在该match.mask信息。若是不存在则接着分配一个struct sw_flow_mask *mask。设置mask->key=match.mask->key, mask->range=match.mask->range, mask->ref_count=1。而且在dp->table.mask_array.masks[i]中存储该mask的地址。添加完毕以后,以下图所示。变化的地方用绿色标注出来了。同时new_flow->mask=mask。

10)根据new_flow->key和new_flow->mask.range计算出new_flow->flow_table.hash值。
11)在dp->table.ti->buckets指向的哈希桶中插入new_flow。以下图所示(假设插入的位置为part[0]对应的第0个位置)。dp->table.count。
12)若是new_flow->id.ufid_len不为0,则使用new_flow->id.ufid信息计算出new_flow->ufid_table.hash。而后根据这个hash值在dp->table.ufid_ti->buckets中找到合适的哈希桶存储对应的new_flow信息。这里假设找到的位置为parts[0]的第0个位置。以下图所示。插入new_flow信息以后,dp->table.ufid_count。

到此为止,更新过程完毕。
2.4.查找过程
在源码中,对应查找过程的入口函数是:ovs_vport_receive。具体过程以下:
1)从接收到的报文信息中提取出关键信息。存储到struct sw_flow_key key。
2)从接收到的报文信息中获取struct vport信息,再从vport信息获取struct datapath信息,假设为dp。
3)若是接收到的报文中未带有skb_hash值信息,则执行:
遍历每个掩码,即:dp->table.mask_array->masks[i]。这里简单记为mask。将1)提取出来的关键新key与这个掩码mask作逻辑与运算,得出掩码过的信息,记为masked_key。经过masked_key和mask.range信息计算出hash值在dp->table.ti->buckets中找到对应的哈希桶,并遍历该桶上的全部struct sw_flow信息,记为flow。执行比较:若是flow->mask指向的是mask,flow->flow_table.hash值和计算出的hash值相等,且flow->key和masked_key信息在mask.range指定的范围内彻底相同,则认为成功匹配,返回flow信息。不然匹配失败,返回NULL。
4)若是接收到的报文中带有skb_hash值信息,则执行:
4)-1:根据旧的skb_hash值和key->recirc_id值从新计算出新的skb_hash值。
4)-2:将skb_hash值分红4段,每段1字节,记为hash。遍历每一段的hash值,执行:获取dp->table.mask_cache[hash]表项,记为entry,若是entry.skb_hash值和skb_hash值相等,则使用entry.mask_index指向的掩码信息去dp->table.ti->buckets中找到对应的struct sw_flow信息(过程和上述第3)步相同)。若是找到对应的flow,则返回flow,过程当中会更新entry.mask_index值来实际指向具体的掩码信息索引。不然,匹配失败,将entry.skb_hash值置0,返回NULL。而若是每个entry.skb_hash值和skb_hash值不相等,则遍历完每一段hash值以后,从中选择最佳候选entry,最佳候选的条件是:其skb_hash值最小者,假设记为ce。最后使用ce.mask_index指向的掩码信息去dp->table.ti->buckets中找到对应的struct sw_flow信息(过程和上述第3)步相同)。若是找到对应的flow,则返回flow,同时将ce->skb_hash值更新为skb_hash值。固然在查找的过程当中(上述第3)步),也会更新ce->mask_index来指向实际的掩码信息索引。
5) 若是在上述第3)和第4)步中都匹配失败了,则须要将报文的信息upcall到用户空间,由用户空间负责查找对应的流动做信息,返回给内核。这个过程具体请参考上述2.3节。
到此为止,查找过程结束。
2.5.执行过程
在2.4节查找过程当中,若是查找成功,则须要执行对应的流动做。入口函数是:ovs_execute_actions。
而若是查找失败,upcall到用户空间,找到对应的流动做以后,调用执行,到内核空间,入口函数对应的是:ovs_packet_cmd_execute。该函数最终会调用到ovs_execute_actions。
2.6.掩码信息比较
在上述2.3和2.4节所述的更新过程和查找过程当中都会碰见用新构造的掩码信息与datapath结构的table.mask_array做比较,以检测掩码信息是否须要新增。而比较的方法以下:
假设新构造的掩码信息为mask,与table.mask_array->masksi比较:
1)mask.range.start和exist_mask.range.start相等。
2)mask.range.end和exist_mask.range.end相等。
3)mask和exist_mask在range范围内彻底相同。
同时符合上面三个条件才认为两个掩码彻底相同。
2.7.流信息比较
在上述2.4节所述的查找过程当中,对接收到的报文进行匹配表项时,须要对流信息做比较。假设接收到的报文提取出来的关键信息为key,匹配的掩码信息为mask,经过mask & key计算出掩码后的报文关键信息,记为masked_key,经过masked_key和mask.range计算出hash值。根据这个hash值找到对应的哈希桶,遍历这个哈希桶中存储的每个流信息,记为flow。如今须要比较flow信息进而找到匹配的流表项。比较以下:
1)flow->mask指向的是mask。
2)flow->flow_table.hash和计算出的hash值相等。
3)flow->key和masked_key在mask.range范围内彻底相同。
同时符合上面三个条件才认为找到匹配的流表项。
2.8.mask_cache表项
在ovs_dp_process_packet函数中查找匹配的流表项时,若是报文的skb中已经带有skb_hash值,则将这个skb_hash值(32比特)分为4段,每段8比特,每段的哈希值暂记为hash,用这个hash值去datapath结构中的table.mask_cache缓存表中查找对应的掩码信息索引。每一个表项(struct mask_cache_entry结构)存储了skb_hash和mask_index信息。初始化的时候,这个缓存表中全部表项都置0,所以,用报文的skb_hash值,分4段去查找,都没法找到合适的表项。这时须要从中选出最佳候选的表项,而最佳候选的表项为其skb_hash值最小。所以,初始化的时候,最终会选择table.mask_cache[0]为最佳候选表项。
接着根据上述2.4节所述的查找过程,找到合适的流表项信息。若是匹配成功,则最佳候选表项table.mask_cache[0].mask_index记录了掩码信息索引(index),即:table.mask_array->masks[index]。同时table.mask_cache[0].skb_hash赋值为skb_hash。假设index为0,则对应以下图所示。
后续若是收到的报文带的skb_hash值与table.mask_cache[0].skb_hash值相等时,则首先使用table.mask_cache[0].mask_index索引的掩码信息去找匹配的流表信息。固然,若是匹配成功,table.mask_cache[0].mask_index可能并不是为0(以前存储的值),有可能更新为其余值。而若是匹配失败,则table.mask_cache[0].skb_hash置0。

2.9.mask_array扩充
当datapath结构中的table.mask_array->count >= table.mask_array->max时,则须要扩充mask_array空间。已当前table.mask_array->max * 2的大小进行扩充。扩充先后的效果以下图所示。橙色线指向的掩码信息在扩充以后,会释放掉old的掩码信息空间。扩充老是按照当前max数值的2倍大小进行扩充,例如:16 -> 32 -> 64 -> 128 -> …。从源码中暂时未看到这个扩充的最大值。

2.10.table_instance扩充
table_instance扩充有两个条件触发:
1)当datapath结构中的table.count > table.ti->n_buckets时,触发扩充。
2)datapath结构中的table.last_rehash记录了上次执行扩充或初始化table时系统的jiffies值。若是超过10分钟,则须要从新扩充,只是此次的扩充并不是增大空间,而是以相同的大小从新分配空间。以为这样作的意义是从新生成table_instance结构中的hash_seed值,从新安排哈希桶的链表长度,分散存储,减小匹配的比较次数。
咱们主要以第一种条件为例,描述扩充的过程。这种扩充是在原有的哈希桶数目(n_buckets)基础之上,以2倍的大小进行扩充。扩充先后,table_instance结构的变化以下图所示。

根据上图可知,扩充以后,table_instance中的hash_seed更新为新的随机数了。这样,在从旧的table_instance将哈希桶中对应的各个流信息拷贝到新的table_instance时,须要从新计算哈希桶的位置,从新安排了。这样作的好处时:能够从新分散每一个哈希桶中流信息链表的长度,减小在匹配时流的比较次数。以下图所示,以前Flow_A和Flow_B都位于第0个哈希桶,扩充以后,Flow_A处于了第0个哈希桶,而Flow_B则处于第1025个哈希桶了。这样在查找Flow_B的时候,比较的次数就减小了一次。在大规模查找的过程当中,这种改变能够大大提升查找的效率。

2.11.示例
假设ovs用户空间的”慢路径”存储的流表信息以下所示:
|
table=0, src_ip=11.11.11.0/24, dst_ip=192.0.0.0/8, actions=output:1
table=0, src_ip=2.2.2.0/24, dst_ip=2.0.0.0/8, actions=output:2
table=0, dst_ip=8.0.0.0/8, actions=drop
|
初始化的时候,ovs内核空间的”快路径”没有存储任何的流路径信息。以下图所示:

收到第一个报文:src_ip=11.11.11.25, dst_ip=192.1.1.1
收到第一个报文,匹配结果Miss,upcall到用户空间,查表将结果发送回内核空间的datapath。根据上述2.3节所述的更新过程,会新增相应的掩码信息(mask_A)和流信息(flow_A)。以下图所示。

收到第二个报文:src_ip=11.11.11.63, dst_ip=192.168.7.8
根据上述2.4节所述的查找过程,匹配成功。
收到第三个报文:src_ip=2.2.2.4, dst_ip=2.7.7.7
根据上述2.4节所述的查找过程,匹配失败。由于掩码信息和mask_A彻底相同,所以无需新增掩码信息,只须要将mask_A的ref_count引用计数加1便可。可是须要新增相应的流信息(flow_B),以下图所示。

收到第四个报文:dst_ip=8.12.34.56
根据上述2.4节所述的查找过程,匹配失败。须要新增相应的掩码信息(mask_B)和流信息(flow_C)。以下图所示

vim /usr/lib/python2.7/site-packages/nova/network/linux_net.py
|
图1
查看虚拟机2网卡:
查看宿主机linux网桥:
分别查看经过修改图1中nova/network代码设置mtu哪些设备生效:
这样说明,若是图1中代码设置了mtu值,则在tap、qbr、qvb、qvo上的mtu值都会被同时设置成相应的值。
验证结束。
www.isjian.com/openstack/openstack-base-use-openvswitch
openstack底层技术-使用openvswitch
Posted on January 23, 2017 by opengers in openstack
Open vSwitch介绍
在过去,数据中心的服务器是直接连在硬件交换机上,后来VMware实现了服务器虚拟化技术,使虚拟服务器(VMs)可以链接在虚拟交换机上,借助这个虚拟交换机,能够为服务器上运行的VMs或容器提供逻辑的虚拟的以太网接口,这些逻辑接口都链接到虚拟交换机上,有三种比较流行的虚拟交换机: VMware virtual switch, Cisco Nexus 1000V,和Open vSwitch
Open vSwitch(OVS)是运行在虚拟化平台上的虚拟交换机,其支持OpenFlow协议,也支持gre/vxlan/IPsec等隧道技术。在OVS以前,基于Linux的虚拟化平台好比KVM或Xen上,缺乏一个功能丰富的虚拟交换机,所以OVS迅速崛起并开始在Xen/KVM中流行起来,而且应用于愈来愈多的开源项目,好比openstack neutron中的网络解决方案
在虚拟交换机的Flow控制器或管理工具方面,一些商业产品都集成有控制器或管理工具,好比Cisco 1000V的Virtual Supervisor Manager(VSM)
,VMware的分布式交换机中的vCenter
。而OVS则须要借助第三方控制器或管理工具实现复杂的转发策略。例如OVS支持OpenFlow 协议,咱们就可使用任何支持OpenFlow协议的控制器来对OVS进行远程管理。OpenStack Neutron中的ML2插件也可以实现对OVS的管理。但这并不意味着OVS必需要有一个控制器才能工做。在不链接外部控制器状况下,OVS自身能够依靠MAC地址学习实现二层数据包转发功能,就像Linux Bridge
在基于Linux内核的系统上,应用最普遍的仍是系统自带的虚拟交换机Linux Bridge
,它是一个单纯的基于MAC地址学习的二层交换机,简单高效,但同时缺少一些高级特性,好比OpenFlow,VLAN tag,QOS,ACL,Flow等,并且在隧道协议支持上,Linux Bridge只支持vxlan,OVS支持gre/vxlan/IPsec等,这也决定了OVS更适用于实现SDN技术
OVS支持如下features
- 支持NetFlow, IPFIX, sFlow, SPAN/RSPAN等流量监控协议
- 精细的ACL和QoS策略
- 可使用OpenFlow和OVSDB协议进行集中控制
- Port bonding,LACP,tunneling(vxlan/gre/Ipsec)
- 适用于Xen,KVM,VirtualBox等hypervisors
- 支持标准的802.1Q VLAN协议
- 基于VM interface的流量管理策略
- 支持组播功能
- flow-caching engine(datapath模块)
文章使用环境
centos7 openvswitch 2.5 OpenFlow 1.4`
OVS架构
先看下OVS总体架构,用户空间主要组件有数据库服务ovsdb-server和守护进程ovs-vswitchd。kernel中是datapath内核模块。最上面的Controller表示OpenFlow控制器,控制器与OVS是经过OpenFlow协议进行链接,控制器不必定位于OVS主机上,下面分别介绍图中各组件

ovs-vswitchd
ovs-vswitchd
守护进程是OVS的核心部件,它和datapath
内核模块一块儿实现OVS基于流的数据交换。做为核心组件,它使用openflow协议与上层OpenFlow控制器通讯,使用OVSDB协议与ovsdb-server
通讯,使用netlink
和datapath
内核模块通讯。ovs-vswitchd
在启动时会读取ovsdb-server
中配置信息,而后配置内核中的datapaths
和全部OVS switches,当ovsdb中的配置信息改变时(例如使用ovs-vsctl工具),ovs-vswitchd
也会自动更新其配置以保持与数据库同步
ovs-vswitchd
须要加载datapath
内核模块才能正常运行。它会自动配置datapath
flows,所以咱们没必要再使用ovs-dpctl
去手动操做datapath
,但ovs-dpctl
仍可用于调试场合
在OVS中,ovs-vswitchd
从OpenFlow控制器获取流表规则,而后把从datapath
中收到的数据包在流表中进行匹配,找到匹配的flows并把所需应用的actions返回给datapath
,同时做为处理的一部分,ovs-vswitchd
会在datapath
中设置一条datapath flows用于后续相同类型的数据包能够直接在内核中执行动做,此datapath flows至关于OpenFlow flows的缓存。对于datapath
来讲,其并不知道用户空间OpenFlow的存在,datapath内核模块信息以下
# modinfo openvswitch filename: /lib/modules/3.10.0-327.el7.x86_64/kernel/net/openvswitch/openvswitch.ko license: GPL description: Open vSwitch switching datapath rhelversion: 7.2 srcversion: F75F2B83324DCC665887FD5 depends: libcrc32c intree: Y ...
ovsdb-server
ovsdb-server
是OVS轻量级的数据库服务,用于整个OVS的配置信息,包括接口/交换内容/VLAN等,OVS主进程ovs-vswitchd
根据数据库中的配置信息工做,下面是ovsdb-server
进程详细信息
ps -ef |grep ovsdb-server root 22166 22165 0 Jan17 ? 00:02:32 ovsdb-server /etc/openvswitch/conf.db -vconsole:emer -vsyslog:err -vfile:info --remote=punix:/var/run/openvswitch/db.sock --private-key=db:Open_vSwitch,SSL,private_key --certificate=db:Open_vSwitch,SSL,certificate --bootstrap-ca-cert=db:Open_vSwitch,SSL,ca_cert --no-chdir --log-file=/var/log/openvswitch/ovsdb-server.log --pidfile=/var/run/openvswitch/ovsdb-server.pid --detach --monitor
/etc/openvswitch/conf.db
是数据库文件存放位置,文件形式存储保证了服务器重启不会影响其配置信息,ovsdb-server
须要文件才能启动,可使用ovsdb-tool create
命令建立并初始化此数据库文件
--remote=punix:/var/run/openvswitch/db.sock
实现了一个Unix sockets链接,OVS主进程ovs-vswitchd
或其它命令工具(ovsdb-client)经过此socket链接管理ovsdb
/var/log/openvswitch/ovsdb-server.log
是日志记录
OpenFlow
OpenFlow是开源的用于管理交换机流表的协议,OpenFlow在OVS中的地位能够参考上面架构图,它是Controller和ovs-vswitched间的通讯协议。须要注意的是,OpenFlow是一个独立的完整的流表协议,不依赖于OVS,OVS只是支持OpenFlow协议,有了支持,咱们可使用OpenFlow控制器来管理OVS中的流表,OpenFlow不只仅支持虚拟交换机,某些硬件交换机也支持OpenFlow协议
OVS经常使用做SDN交换机(OpenFlow交换机),其中控制数据转发策略的就是OpenFlow flow。OpenStack Neutron中实现了一个OpenFlow控制器用于向OVS下发OpenFlow flows控制虚拟机间的访问或隔离。本文讨论的默认是做为SDN交换机场景下
OpenFlow flow的流表项存放于用户空间主进程ovs-vswitchd
中,OVS除了链接OpenFlow控制器获取这种flow,文章后面会提到的命令行工具ovs-ofctl
工具也能够手动管理OVS中的OpenFlow flow,能够查看man ovs-ofctl
了解
在OVS中,OpenFlow flow是最重要的一种flow, 然而还有其它几种flows存在,文章下面OVS概念部分会提到
Controller
Controller指OpenFlow控制器。OpenFlow控制器能够经过OpenFlow协议链接到任何支持OpenFlow的交换机,好比OVS。控制器经过向交换机下发流表规则来控制数据流向。除了能够经过OpenFlow控制器配置OVS中flows,也可使用OVS提供的ovs-ofctl
命令经过OpenFlow协议去链接OVS,从而配置flows,命令也可以对OVS的运行情况进行动态监控。
Kernel Datapath
下面讨论场景是OVS做为一个OpenFlow交换机
datapath是一个Linux内核模块,它负责执行数据交换。关于datapath,The Design and Implementation of Open vSwitch中有描述
The datapath module in the kernel receives the packets first, from a physical NIC or a VM’s virtual NIC. Either ovs-vswitchd has instructed the datapath how to handle packets of this type, or it has not. In the former case, the datapath module simply follows the instructions, called actions, given by ovs-vswitchd, which list physical ports or tunnels on which to transmit the packet. Actions may also specify packet modifications, packet sampling, or instructions to drop the packet. In the other case, where the datapath has not been told what to do with the packet, it delivers it to ovs-vswitchd. In userspace, ovs-vswitchd determines how the packet should be handled, then it passes the packet back to the datapath with the desired handling. Usually, ovs-vswitchd also tells the datapath to cache the actions, for handling similar future packets.
为了说明datapath,来看一张更详细的架构图,图中的大部分组件上面都有提到

用户空间ovs-vswitchd
和内核模块datapath
决定了数据包的转发,首先,datapath
内核模块收到进入数据包(物理网卡或虚拟网卡),而后查找其缓存(datapath flows),当有一个匹配的flow时它执行对应的操做,不然datapath
会把该数据包送入用户空间由ovs-vswitchd
负责在其OpenFlow flows中查询(图1中的First Packet),ovs-vswitchd
查询后把匹配的actions返回给datapath
并设置一条datapath flows到datapath
中,这样后续进入的同类型的数据包(图1中的Subsequent Packets)由于缓存匹配会被datapath
直接处理,不用再次进入用户空间。
datapath
专一于数据交换,它不须要知道OpenFlow的存在。与OpenFlow打交道的是ovs-vswitchd
,ovs-vswitchd
存储全部Flow规则供datapath
查询或缓存.
虽然有ovs-dpctl
管理工具的存在,但咱们不必去手动管理datapath
,这是用户空间ovs-vswitchd
的工做
OVS概念
这部分说下OVS中的重要概念,使用OpenStack neutron+vxlan部署模式下网络节点OVS网桥做为例子
# ovs-vsctl show e44abab7-2f65-4efd-ab52-36e92d9f0200 Manager "ptcp:6640:127.0.0.1" is_connected: true Bridge br-ext Controller "tcp:127.0.0.1:6633" is_connected: true fail_mode: secure Port br-ext Interface br-ext type: internal Port "eth1" Interface "eth1" Port phy-br-ext Interface phy-br-ext type: patch options: {peer=int-br-ext} Bridge br-tun Controller "tcp:127.0.0.1:6633" is_connected: true fail_mode: secure Port br-tun Interface br-tun type: internal Port patch-int Interface patch-int type: patch options: {peer=patch-tun} Port "vxlan-080058ca" Interface "vxlan-080058ca" type: vxlan options: {df_default="true", in_key=flow, local_ip="8.0.88.201", out_key=flow, remote_ip="8.0.88.202"} Bridge br-int Controller "tcp:127.0.0.1:6633" is_connected: true fail_mode: secure Port "qr-11591618-c4" tag: 3 Interface "qr-11591618-c4" type: internal Port patch-tun Interface patch-tun type: patch options: {peer=patch-int} Port int-br-ext Interface int-br-ext type: patch options: {peer=phy-br-ext}
Bridge
Bridge表明一个以太网交换机(Switch),一个主机中能够建立一个或者多个Bridge。Bridge的功能是根据必定规则,把从端口收到的数据包转发到另外一个或多个端口,上面例子中有三个Bridge,br-tun
,br-int
,br-ext
添加一个网桥br0
Port
端口Port与物理交换机的端口概念相似,Port是OVS Bridge上建立的一个虚拟端口,每一个Port都隶属于一个Bridge。Port有如下几种类型
能够把操做系统中已有的网卡(物理网卡em1/eth0,或虚拟机的虚拟网卡tapxxx)挂载到ovs上,ovs会生成一个同名Port处理这块网卡进出的数据包。此时端口类型为Normal。
以下,主机中有一块物理网卡eth1
,把其挂载到OVS网桥br-ext
上,OVS会自动建立同名Port eth1
。
ovs-vsctl add-port br-ext eth1
#Bridge br-ext中出现Port "eth1"
有一点要注意的是,挂载到OVS上的网卡设备不支持分配IP地址,所以若以前eth1
配置有IP地址,挂载到OVS以后IP地址将不可访问。这里的网卡设备不仅包括物理网卡,也包括主机上建立的虚拟网卡
Internal类型是OVS内部建立的虚拟网卡接口,每建立一个Port,OVS会自动建立一个同名接口(Interface)挂载到新建立的Port上。接口的概念下面会提到。
下面建立一个网桥br0,并建立一个Internal类型的Port p0
ovs-vsctl add-br br0 ovs-vsctl add-port br0 p0 -- set Interface p0 type=internal #查看网桥br0 ovs-vsctl show br0 Bridge "br0" fail_mode: secure Port "p0" Interface "p0" type: internal Port "br0" Interface "br0" type: internal
能够看到有两个Port。当ovs建立一个新网桥时,默认会建立一个与网桥同名的Internal Port。在OVS中,只有”internal”类型的设备才支持配置IP地址信息,所以咱们能够为br0
接口配置一个IP地址,固然p0
也能够配置IP地址
ip addr add 192.168.10.11/24 dev br0 ip link set br0 up #添加默认路由 ip route add default via 192.168.10.1 dev br0
上面两种Port类型区别在于,Internal类型会自动建立接口(Interface),而Normal类型是把主机中已有的网卡接口添加到OVS中
当主机中有多个ovs网桥时,可使用Patch Port把两个网桥连起来。Patch Port老是成对出现,分别链接在两个网桥上,从一个Patch Port收到的数据包会被转发到另外一个Patch Port,相似于Linux系统中的veth
。使用Patch链接的两个网桥跟一个网桥没什么区别,OpenStack Neutron中使用到了Patch Port。上面网桥br-ext
中的Port phy-br-ext
与br-int
中的Port int-br-ext
是一对Patch Port
可使用ovs-vsctl
建立patch设备,以下建立两个网桥br0,br1
,而后使用一对Patch Port
链接它们
ovs-vsctl add-br br0 ovs-vsctl add-br br1 ovs-vsctl \ -- add-port br0 patch0 -- set interface patch0 type=patch options:peer=patch1 \ -- add-port br1 patch1 -- set interface patch1 type=patch options:peer=patch0 #结果以下 #ovs-vsctl show Bridge "br0" Port "br0" Interface "br0" type: internal Port "patch0" Interface "patch0" type: patch options: {peer="patch1"} Bridge "br1" Port "br1" Interface "br1" type: internal Port "patch1" Interface "patch1" type: patch options: {peer="patch0"}
链接两个网桥不止上面一种方法,linux中支持建立Veth
设备对,咱们能够首先建立一对Veth
设备对,而后把这两个Veth
分别添加到两个网桥上,其效果跟OVS中建立Patch Port同样,只是性能会有差异
OVS中支持添加隧道(Tunnel)端口,常见隧道技术有两种gre
或vxlan
。隧道技术是在现有的物理网络之上构建一层虚拟网络,上层应用只与虚拟网络相关,以此实现的虚拟网络比物理网络配置更加灵活,并可以实现跨主机的L2通讯以及必要的租户隔离。不一样隧道技术其大致思路均是将以太网报文使用隧道协议封装,而后使用底层IP网络转发封装后的数据包,其差别性在于选择和构造隧道的协议不一样。Tunnel在OpenStack中用做实现大二层网络以及租户隔离,以应对公有云大规模,多租户的复杂网络环境。
OpenStack是多节点结构,同一子网的虚拟机可能被调度到不一样计算节点上,所以须要有隧道技术来保证这些同子网不一样节点上的虚拟机可以二层互通,就像他们链接在同一个交换机上,同时也要保证能与其它子网隔离。
OVS在计算和网络节点上创建隧道Port来链接各节点上的网桥br-int
,这样全部网络和计算节点上的br-int
互联造成了一个大的虚拟的跨全部节点的逻辑网桥(内部靠tunnel id或VNI隔离不一样子网),这个逻辑网桥对虚拟机和qrouter是透明的,它们以为本身链接到了一个大的br-int
上。从某个计算节点虚拟机发出的数据包会被封装进隧道经过底层网络传输到目的主机而后解封装。
上面网桥br-tun
中Port "vxlan-080058ca"
就是一个vxlan
类型tunnel端口。下面使用两台主机测试建立vxlan隧道
#主机192.168.7.21上 ovs-vsctl add-br br-vxlan #主机192.168.7.23上 ovs-vsctl add-br br-vxlan #主机192.168.7.21上添加链接到7.23的Tunnel Port ovs-vsctl add-port br-vxlan tun0 -- set Interface tun0 type=vxlan options:remote_ip=192.168.7.23 #主机192.168.7.23上添加链接到7.21的Tunnel Port ovs-vsctl add-port br-vxlan tun0 -- set Interface tun0 type=vxlan options:remote_ip=192.168.7.21
而后,两个主机上桥接到br-vxlan
的虚拟机就像链接到同一个交换机同样,能够实现跨主机的L2链接,同时又彻底与物理网络隔离。
Interface
Interface是链接到Port的网络接口设备,是OVS与外部交换数据包的组件,在一般状况下,Port和Interface是一对一的关系,只有在配置Port为 bond模式后,Port和Interface是一对多的关系。这个网络接口设备多是建立Internal
类型Port时OVS自动生成的虚拟网卡,也多是系统的物理网卡或虚拟网卡(TUN/TAP)挂载在ovs上。 OVS中只有”Internal”类型的网卡接口才支持配置IP地址
Interface
是一块网络接口设备,负责接收或发送数据包,Port是OVS网桥上创建的一个虚拟端口,Interface
挂载在Port上。
Controller
OpenFlow控制器。OVS能够同时接受一个或者多个OpenFlow控制器的管理。主要做用是下发流表(Flow Tables)到OVS,控制OVS数据包转发规则。控制器与OVS经过网络链接,不必定要在同一主机上
能够看到上面实例中三个网桥br-int
,br-ext
,br-tun
都链接到控制器Controller "tcp:127.0.0.1:6633
上
datapath
OVS内核模块,负责执行数据交换。其内部有做为缓存使用的flows,关于datapath,下面会细说
流(flows)
flows是OVS进行数据转发策略控制的核心数据结构,区别于Linux Bridge是个单纯基于MAC地址学习的二层交换机,flows的存在使OVS做为一款SDN交换机成为云平台网络虚拟机化主要组件
OVS中有多种flows存在,用于不一样目的,但最主要的仍是OpenFlow flows这种,文中未明确说明的flows都是指OpenFlow flows
OVS中最重要的一种flows,Controller控制器下发的就是这种flows,OVS架构部分已经简单介绍过,关于openflow的具体使用,会在另外一篇文章中说明
OVS在使用OpenFlow flow时,须要与OpenFlow控制器创建TCP链接,若此TCP链接不依赖OVS,即没有OVS依然能够创建链接,此时就是out-of-band control
模式,这种模式下不须要”hidden” flows
可是在in-band control
模式下,TCP链接的创建依赖OVS控制的网络,但此时OVS依赖OpenFLow控制器下发的flows才能正常工做,无法创建TCP链接也就没法下发flows,这就产生矛盾了,所以须要存在一些”hidden” flows,这些”hidden” flows保证了TCP链接可以正常创建。关于in-band control
详细介绍,参考OVS官方文档Design Decisions In Open vSwitch 中In-Band Control部分
“hidden” flows优先级高于OpenFlow flows,它们不须要手动设置。可使用ovs-appctl
查看这些flows,下面命令输出内容包括OpenFlow flows
,"hidden" flows
ovs-appctl bridge/dump-flows <br>
datapath flows是datapath
内核模块维护的flows,由内核模块维护意味着咱们并不须要去修改管理它。与OpenFlow flows不一样的是,它不支持优先级,而且只有一个表,这些特色使它很是适合作缓存。与OpenFlow同样的是它支持通配符,也支持指令集(多个action)
datapath flows能够来自用户空间ovs-vswitchd
缓存,也能够是datapath内核模块进行MAC地址学习到的flows,这取决与OVS是做为SDN交换机,仍是像Linux Bridge那样只是一个简单基于MAC地址学习的二层交换机
几种flows对比
咱们能够修改和配置的是OpenFlow flows。datapath flow和”hidden” flows由OVS自身管理,咱们没必要去修改它。固然,调试场景下仍是可使用工具修改的
flows命令行工具
介绍下上面三种flows管理工具,不具体说明,具体使用能够查看相关man手册
-
ovs-ofctl dump-flows <br>
打印指定网桥内的全部OpenFlow flows,能够存在多个流表(flow tables),按表顺序显示。不包括”hidden” flows。这是最经常使用的查看flows命令,固然这条命令对全部OpenFlow交换机都有效,不仅仅是OVS
-
ovs-appctl bridge/dump-flows <br>
打印指定网桥内全部OpenFlow flows,包括”hidden” flows,in-band control
模式下排错能够用到
-
ovs-dpctl dump-flows [dp]
打印内核模块中datapath flows,[dp]
能够省略,默认主机中只有一个datapath system@ovs-system
man手册能够找到很是详细的用法说明,注意ovs-ofctl
管理的是OpenFlow flows
OVS中管理工具的使用及区别
上面介绍了OVS用户空间进程以及控制器和OpenFlow协议,这里说下相关的命令行工具的使用及区别
ovs-vsctl
ovs-vsctl
是一个管理或配置ovs-vswitchd
的高级命令行工具,高级是说其操做对用户友好,封装了对数据库的操做细节。它是管理OVS最经常使用的命令,除了配置flows以外,其它大部分操做好比Bridge/Port/Interface/Controller/Database/Vlan等均可以完成
#添加网桥br0 ovs-vsctl add-br br0 #列出全部网桥 ovs-vsctl list-br #添加一个Port p1到网桥br0 ovs-vsctl add-port br0 p1 #查看网桥br0上全部Port ovs-vsctl list-ports br0 #获取br0网桥的OpenFlow控制器地址,没有控制器则返回空 ovs-vsctl get-controller br0 #设置OpenFlow控制器,控制器地址为192.168.1.10,端口为6633 ovs-vsctl set-controller br0 tcp:192.168.1.10:6633 #移除controller ovs-vsctl del-controller br0 #删除网桥br0 ovs-vsctl del-br br0 #设置端口p1的vlan tag为100 ovs-vsctl set Port p1 tag=100 #设置Port p0类型为internal ovs-vsctl set Interface p0 type=internal #添加vlan10端口,并设置vlan tag为10,Port类型为Internal ovs-vsctl add-port br0 vlan10 tag=10 -- set Interface vlan10 type=internal #添加隧道端口gre0,类型为gre,远端IP为1.2.3.4 ovs-vsctl add-port br0 gre0 -- set Interface gre0 type=gre options:remote_ip=1.2.3.4
ovsdb-tool
ovsdb-tool
是一个专门管理OVS数据库文件的工具,不经常使用,它不直接与ovsdb-server
进程通讯
#可使用此工具建立并初始化database文件 ovsdb-tool create [db] [schema] #可使用ovsdb-client get-schema [database]获取某个数据库的schema(json格式) #能够查看数据库更改记录,具体到操做命令,这个比较有用 ovsdb-tool show-log -m record 48: 2017-01-07 03:34:15.147 "ovs-vsctl: ovs-vsctl --timeout=5 -- --if-exists del-port tapcea211ae-10" table Interface row "tapcea211ae-10" (151f66b6): delete row table Port row "tapcea211ae-10" (cc9898cd): delete row table Bridge row "br-int" (fddd5e27): table Open_vSwitch row a9fc1666 (a9fc1666): record 49: 2017-01-07 04:18:23.671 "ovs-vsctl: ovs-vsctl --timeout=5 -- --if-exists del-port tap5b4345ea-d5 -- add-port br-int tap5b4345ea-d5 -- set Interface tap5b4345ea-d5 "external-ids:attached-mac=\"fa:16:3e:50:1b:5b\"" -- set Interface tap5b4345ea-d5 "external-ids:iface-id=\"5b4345ea-d5ea-4285-be99-0e4cadf1600a\"" -- set Interface tap5b4345ea-d5 "external-ids:vm-id=\"0aa2d71e-9b41-4c88-9038-e4d042b6502a\"" -- set Interface tap5b4345ea-d5 external-ids:iface-status=active" table Port insert row "tap5b4345ea-d5" (4befd532): table Interface insert row "tap5b4345ea-d5" (b8a5e830): table Bridge row "br-int" (fddd5e27): table Open_vSwitch row a9fc1666 (a9fc1666): ...
ovsdb-client
ovsdb-client
是ovsdb-server
进程的命令行工具,主要是从正在运行的ovsdb-server
中查询信息,操做的是数据库相关
ovs-ofctl
ovs-ofctl
是专门管理配置OpenFlow交换机的命令行工具,咱们能够用它手动配置OVS中的OpenFlow flows,注意其不能操做datapath flows和”hidden” flows
#查看br-tun中OpenFlow flows ovs-ofctl dump-flows br-tun #查看br-tun端口信息 ovs-ofctl show br-tun #添加新的flow:对于从端口p0进入交换机的数据包,若是它不包含任何VLAN tag,则自动为它添加VLAN tag 101 ovs-ofctl add-flow br0 "priority=3,in_port=100,dl_vlan=0xffff,actions=mod_vlan_vid:101,normal" #对于从端口3进入的数据包,若其vlan tag为100,去掉其vlan tag,并从端口1发出 ovs-ofctl add-flow br0 in_port=3,dl_vlan=101,actions=strip_vlan,output:1 #添加新的flow: 修改从端口p1收到的数据包的源地址为9.181.137.1,show 查看p1端口ID为100 ovs-ofctl add-flow br0 "priority=1 idle_timeout=0,in_port=100,actions=mod_nw_src:9.181.137.1,normal" #添加新的flow: 重定向全部的ICMP数据包到端口 p2 ovs-ofctl add-flow br0 idle_timeout=0,dl_type=0x0800,nw_proto=1,actions=output:102 #删除编号为 100 的端口上的全部流表项 ovs-ofctl del-flows br0 "in_port=100"
ovs-vsctl
是一个综合的配置管理工具,ovsdb-client
倾向于从数据库中查询某些信息,而ovsdb-tool
是维护数据库文件工具
文章地址http://www.isjian.com/openstack/openstack-base-use-openvswitch/
参考文章
https://www.sdxcentral.com/cloud/open-source/definitions/what-is-open-vswitch/ http://openvswitch.org/features/ https://www.ibm.com/developerworks/cn/cloud/library/1401_zhaoyi_openswitch/ http://openvswitch.org/slides/OpenStack-131107.pdf http://horms.net/projects/openvswitch/2010-10/openvswitch.en.pdf http://benpfaff.org/papers/ovs.pdf https://networkheresy.com/category/open-vswitch/