openvswitch vxlan 源码分析_vswitch中的vni-程序员宅基地

技术标签: vxlan  源码分析  OVS  ovs  

ovs对于vxlan的支持依赖datapath的类型,对于kernel space datapath来说,创建vxlan端口后,在将此端口添加到datapath时,会调用kernel自身提供的vxlan.ko模块创建出vxlan_sys_port来,ovs只需要将流表action指向vxlan_sys_port即可,vxlan报文的封装,封装后路由查找和邻居查找都由vxlan模块/kernel来实现;而对于userspace datapath来说,vxlan报文的封装,封装后路由查找和邻居查找都由ovs本身实现。

实验

下面分别实验两种datapath下的vxlan。
kernel space vxlan
拓扑图如下所示

image.png

对应的命令如下

//在vm1上操作,创建一个bridge,添加一个vxlan端口
ovs-vsctl add-br br0
ovs-vsctl add-port br0 vxlan1 -- set interface vxlan1 type=vxlan options:remote_ip=10.10.10.2 options:key=flow options:dst_port=8472
ifconfig br0 1.1.1.1/24
ifconfig ens8 10.10.10.1/24
//在vm2上操作
ovs-vsctl add-br br0
ovs-vsctl add-port br0 vxlan1 -- set interface vxlan1 type=vxlan options:remote_ip=10.10.10.1 options:key=flow options:dst_port=8472
ifconfig br0 1.1.1.2/24
ifconfig ens8 10.10.10.2/24

命令执行成功后,查看基本情况

//创建vlxna端口后会监听端口8472,用来接收vxlan报文
root@master:~# netstat -nap | grep 8472
udp        0      0 0.0.0.0:8472            0.0.0.0:*                           -
udp6       0      0 :::8472                 :::*                                -

//查看vxlan端口驱动类型为vxlan
root@master:~# ethtool -i vxlan_sys_8472
driver: vxlan
version: 0.1
firmware-version:
expansion-rom-version:
bus-info:
supports-statistics: no
supports-test: no
supports-eeprom-access: no
supports-register-dump: no
supports-priv-flags: no

//查看ovs当前配置
root@master:~# ovs-vsctl show
1e633d2b-7a9e-44ba-9c16-89e12912c2d6
    Bridge "br0"
        Port "br0"
            Interface "br0"
                type: internal
        Port "vxlan1"
            Interface "vxlan1"
                type: vxlan
                options: {dst_port="8472", key=flow, remote_ip="10.10.10.2"}

//查看datapath端口
root@master:~# ovs-appctl dpctl/show
system@ovs-system:
        lookups: hit:147 missed:23 lost:0
        flows: 4
        masks: hit:457 total:5 hit/pkt:2.69
        port 0: ovs-system (internal)
        port 1: br0 (internal)
        port 2: vxlan_sys_8472 (vxlan: packet_type=ptap)

在vm1上ping 1.1.1.2,可以ping通,查看流表及抓包情况

root@master:~# ping 1.1.1.2
PING 1.1.1.2 (1.1.1.2) 56(84) bytes of data.
64 bytes from 1.1.1.2: icmp_seq=1 ttl=64 time=4.58 ms
64 bytes from 1.1.1.2: icmp_seq=2 ttl=64 time=0.643 ms
^C
--- 1.1.1.2 ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 1002ms
rtt min/avg/max/mdev = 0.643/2.610/4.578/1.967 ms

//datapath流表信息,有4条流表,两条arp相关的,两条icmp相关的,从1口收到的报文转发给2口,从2口收到的报文转发给1口
root@master:~# ovs-appctl dpctl/dump-flows
recirc_id(0),tunnel(tun_id=0x0,src=10.10.10.2,dst=10.10.10.1,flags(-df-csum+key)),in_port(2),eth(src=d6:5a:9e:97:39:4f,dst=b6:7b:1a:1e:79:44),eth_type(0x0806), packets:1, bytes:42, used:8.528s, actions:1
recirc_id(0),in_port(1),eth(src=b6:7b:1a:1e:79:44,dst=d6:5a:9e:97:39:4f),eth_type(0x0800),ipv4(tos=0/0x3,frag=no), packets:13, bytes:1274, used:0.384s, actions:set(tunnel(tun_id=0x0,dst=10.10.10.2,ttl=64,tp_dst=8472,flags(df|key))),2
recirc_id(0),in_port(1),eth(src=b6:7b:1a:1e:79:44,dst=d6:5a:9e:97:39:4f),eth_type(0x0806), packets:0, bytes:0, used:never, actions:set(tunnel(tun_id=0x0,dst=10.10.10.2,ttl=64,tp_dst=8472,flags(df|key))),2
recirc_id(0),tunnel(tun_id=0x0,src=10.10.10.2,dst=10.10.10.1,flags(-df-csum+key)),in_port(2),eth(src=d6:5a:9e:97:39:4f,dst=b6:7b:1a:1e:79:44),eth_type(0x0800),ipv4(frag=no), packets:13, bytes:1274, used:0.384s, actions:1

//在vxlan端口上抓包,此时只能抓到封装前的报文
root@master:~# tcpdump -vne -i vxlan_sys_8472
tcpdump: listening on vxlan_sys_8472, link-type EN10MB (Ethernet), capture size 262144 bytes
23:02:27.662008 0e:ed:bb:33:06:40 > 96:e5:e8:08:63:44, ethertype IPv4 (0x0800), length 98: (tos 0x0, ttl 64, id 23800, offset 0, flags [DF], proto ICMP (1), length 84)
    1.1.1.2 > 1.1.1.1: ICMP echo request, id 9663, seq 89, length 64
23:02:27.662095 96:e5:e8:08:63:44 > 0e:ed:bb:33:06:40, ethertype IPv4 (0x0800), length 98: (tos 0x0, ttl 64, id 64219, offset 0, flags [none], proto ICMP (1), length 84)
    1.1.1.1 > 1.1.1.2: ICMP echo reply, id 9663, seq 89, length 64

//在最终出端口上抓包,可以抓到封装vxlan后的报文
root@master:~# tcpdump -vne -i ens8
tcpdump: listening on ens8, link-type EN10MB (Ethernet), capture size 262144 bytes
23:36:05.294029 52:54:00:9f:e8:0e > 52:54:00:9e:98:20, ethertype IPv4 (0x0800), length 148: (tos 0x0, ttl 64, id 36259, offset 0, flags [DF], proto UDP (17), length 134)
    10.10.10.2.49240 > 10.10.10.1.8472: OTV, flags [I] (0x08), overlay 0, instance 0
0e:ed:bb:33:06:40 > 96:e5:e8:08:63:44, ethertype IPv4 (0x0800), length 98: (tos 0x0, ttl 64, id 11222, offset 0, flags [DF], proto ICMP (1), length 84)
    1.1.1.2 > 1.1.1.1: ICMP echo request, id 9663, seq 2061, length 64
23:36:05.294094 52:54:00:9e:98:20 > 52:54:00:9f:e8:0e, ethertype IPv4 (0x0800), length 148: (tos 0x0, ttl 64, id 55108, offset 0, flags [DF], proto UDP (17), length 134)
    10.10.10.1.51916 > 10.10.10.2.8472: OTV, flags [I] (0x08), overlay 0, instance 0
96:e5:e8:08:63:44 > 0e:ed:bb:33:06:40, ethertype IPv4 (0x0800), length 98: (tos 0x0, ttl 64, id 57149, offset 0, flags [none], proto ICMP (1), length 84)
    1.1.1.1 > 1.1.1.2: ICMP echo reply, id 9663, seq 2061, length 64

ping流程如下

a. vm1上ping 1.1.1.2时,首先查找arp表,找不到后,会发送arp请求报文。arp请求报文从br0发出后,网桥br0收到,查找datapath流表,因为是首包肯定查不到,然后将收包上送用户态的slow-path处理。
b. 在slow-path查找openflow流表,action为 normal,需要查找fdb表。
root@master:~# ovs-ofctl dump-flows br0
 cookie=0x0, duration=62611.119s, table=0, n_packets=21004, n_bytes=1141452, priority=0 actions=NORMAL
因为目的mac为广播,所以需要广播到网桥br0上其他所有端口,目前只有端口vxlan_sys_8472。
c. 上一步查找流表结果是将报文从vxlan_sys_8472发出,因为vxlan_sys_8472为vxlan端口,所以会将vxlan相关配置一同下发到datapath流表
root@master:~# ovs-appctl dpctl/dump-flows
recirc_id(0),in_port(1),eth(src=b6:7b:1a:1e:79:44,dst=d6:5a:9e:97:39:4f),eth_type(0x0806), packets:0, bytes:0, used:never, actions:set(tunnel(tun_id=0x0,dst=10.10.10.2,ttl=64,tp_dst=8472,flags(df|key))),2
d. 除了下发流表到datapath外,还会将上送的首包下发到datapath,根据最新的流表执行。即将首包发给vxlan_sys_8472处理,同时携带vxlan相关配置(比如remote_ip,dst_port, tunnle id等信息)
e. 报文到达vxlan_sys_8472后根据vxlan配置,封装成vxlan报文,根据外层目的ip查找路由表可知,需要从ens8网卡发出
root@master:~# ip r
default via 192.168.122.1 dev ens3 proto static
1.1.1.0/24 dev br0 proto kernel scope link src 1.1.1.1
10.10.10.0/24 dev ens8 proto kernel scope link src 10.10.10.1
f. 封装后的vxlan报文经过底层网络转发到vm2,根据目的ip发现需要本机处理,经过协议栈处理后,相当于解封装vxlan报文,再根据目的端口号8472查找对应的socket可知,需要将udp的payload,即内层arp请求报文发给vm2上vxlan端口处理。
g. 因为vxlan端口在网桥上,也需要查找vm2上datapath流表,当然也会失败,将arp请求报文上送vm2上slow-path处理,广播报文发给网桥上其他端口,目前只有br0,最后将流表信息下发到datapath,并且将arp请求报文下发到datapath,最终发给端口br0。
h. 上一步在slow-path收到arp请求报文时,也会学到对端的mac,如下
root@node1:~# ovs-appctl fdb/show br0
 port  VLAN  MAC                Age
    1     0  b6:7b:1a:1e:79:44    1
LOCAL     0  d6:5a:9e:97:39:4f    1
vm2回复arp响应报文时,目的mac为单播地址。查找datapath流表失败,上送slow-path,查找openflow流表,查找fdb表成功找到出端口1。注意fdb表的出端口对应的是openflow端口,可通过ovs-ofctl show br0查看,不要和datapath端口号混淆了。
i. 将流表信息下发到datapath,并将报文发给vxlan端口,封装vxlan报文,查找路由表,发送出去。
j. vm1收到后,解封装vxlan报文,查找datapath流表失败,上送slow-path,查找fdb表成功找到出端口,同时学习到对端的mac。将流表下发到datapath,报文发给vm1上的br0。至此vm1学到了1.1.1.2的mac地址。
k. vm1上学到mac地址后发出ping报文,网桥br1收到ping包,查找datapath流表,因为是首包肯定查不到,上送用户态的slow-path处理,查找fdb表成功找到出端口,将流表下发到datapath,将报文发给vxlan端口,封装vxlan后发出。
l. 后续的流程都是类似的,不再赘述。后续的报文可以直接查找datapath流表进行转发即可。

userspace vxlan
拓扑图如下所示

image.png


注意:br1上通往外部的端口即可以是绑定到dpdk driver的(由pmd线程处理收发包),也可以是绑定到kernel driver的(由ovs的non-pmd线程(vswitchd主线程)处理它的收发包)。上图中的ens8端口是绑定在kernel driver的。

对应的命令如下

//以下命令在vm1上执行
ovs-vsctl add-br br0 -- set bridge br0 datapath_type=netdev
ovs-vsctl add-port br0 vxlan1 -- set interface vxlan1 type=vxlan options:remote_ip=10.10.10.2 options:key=flow options:dst_port=8472
ifconfig br0 1.1.1.1/24

ovs-vsctl add-br br1 -- set bridge br1 datapath_type=netdev
ovs-vsctl add-port br1 ens8
ip link set dev br1 up
ip addr add dev br1 10.10.10.1/24
ip link set dev ens8 up

//以下命令在vm2上执行
ovs-vsctl add-br br0 -- set bridge br0 datapath_type=netdev
ovs-vsctl add-port br0 vxlan1 -- set interface vxlan1 type=vxlan options:remote_ip=10.10.10.1 options:key=flow options:dst_port=8472
ifconfig br0 1.1.1.2/24

ovs-vsctl add-br br1 -- set bridge br1 datapath_type=netdev
ovs-vsctl add-port br1 ens8
ip link set dev br1 up
ip addr add dev br1 10.10.10.2/24
ip link set dev ens8 up

查看下ovs配置,因为userspace vxlan,所以此时在vm上通过ifconfig是看不到类似vxlan_sys_8472的vxlan端口的。

root@master:~# ovs-vsctl show
163a03bf-8b1b-4043-8d37-8b2287bf94fe
    Bridge "br1"
        Port "br1"
            Interface "br1"
                type: internal
        Port "ens8"
            Interface "ens8"
    Bridge "br0"
        Port "vxlan1"
            Interface "vxlan1"
                type: vxlan
                options: {dst_port="8472", key=flow, remote_ip="10.10.10.2"}
        Port "br0"
            Interface "br0"
                type: internal

//查看userspace datapath端口信息
root@master:~# ovs-appctl dpctl/show
netdev@ovs-netdev:
        lookups: hit:27 missed:26 lost:0
        flows: 1
        port 0: ovs-netdev (tap)
        port 1: br0 (tap)
        port 2: vxlan_sys_8472 (vxlan: packet_type=ptap)
        port 3: br1 (tap)
        port 4: ens8

//查看路由信息,封装vxlan后,根据外层ip查找路由表,寻找出接口
root@master:~# ovs-appctl ovs/route/show
Route Table:
Cached: 1.1.1.1/32 dev br0 SRC 1.1.1.1
Cached: 10.10.10.1/32 dev br1 SRC 10.10.10.1
Cached: 127.0.0.1/32 dev lo SRC 127.0.0.1
Cached: 172.17.0.1/32 dev docker0 SRC 172.17.0.1
Cached: 192.168.122.20/32 dev ens3 SRC 192.168.122.20
Cached: ::1/128 dev lo SRC ::1
...
Cached: 1.1.1.0/24 dev br0 SRC 1.1.1.1
Cached: 10.10.10.0/24 dev br1 SRC 10.10.10.1
Cached: 192.168.122.0/24 dev ens3 SRC 192.168.122.20
Cached: 172.17.0.0/16 dev docker0 SRC 172.17.0.1
Cached: 127.0.0.0/8 dev lo SRC 127.0.0.1
Cached: 0.0.0.0/0 dev ens3 GW 192.168.122.1 SRC 192.168.122.20
Cached: fe80::/64 dev br1 SRC fe80::1031:66ff:fe3c:9547

在vm1上ping 1.1.1.2,可以ping通,查看流表及抓包情况

root@master:~# ping 1.1.1.2
PING 1.1.1.2 (1.1.1.2) 56(84) bytes of data.
64 bytes from 1.1.1.2: icmp_seq=1 ttl=64 time=1.85 ms
^C
--- 1.1.1.2 ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 1.848/1.848/1.848/0.000 ms

//datapath流表信息(先忽略arp流表),出方向流表只有一条,封装vxlan后,发给出端口即可,但是入方向需要两条,一条用于查找tunnel信息,解封装后将报文送到vxlan端口,第二条根据内层报文信息转到正确的端口
root@master:~# ovs-appctl dpctl/dump-flows
flow-dump from non-dpdk interfaces:
recirc_id(0),in_port(4),packet_type(ns=0,id=0),eth(src=2e:ab:5f:92:6c:47,dst=12:31:66:3c:95:47),eth_type(0x0800),ipv4(dst=10.10.10.1,proto=17,frag=no),udp(dst=8472), packets:919, bytes:90148, used:0.409s, actions:tnl_pop(2)
recirc_id(0),in_port(1),packet_type(ns=0,id=0),eth(src=6a:91:a4:4f:78:45,dst=b2:42:ef:84:79:4b),eth_type(0x0800),ipv4(tos=0/0x3,frag=no), packets:267, bytes:26166, used:0.410s, actions:clone(tnl_push(tnl_port(2),header(size=50,type=4,eth(dst=52:54:00:9f:e8:0e,src=12:31:66:3c:95:47,dl_type=0x0800),ipv4(src=10.10.10.1,dst=10.10.10.2,proto=17,tos=0,ttl=64,frag=0x4000),udp(src=0,dst=8472,csum=0x0),vxlan(flags=0x8000000,vni=0x0)),out_port(3)),4)
tunnel(tun_id=0x0,src=10.10.10.2,dst=10.10.10.1,flags(-df-csum+key)),recirc_id(0),in_port(2),packet_type(ns=0,id=0),eth(src=b2:42:ef:84:79:4b,dst=6a:91:a4:4f:78:45),eth_type(0x0800),ipv4(dst=1.1.1.1,proto=1,frag=no), packets:100, bytes:9800, used:0.409s, actions:1

//neigh信息
root@master:~# ovs-appctl tnl/neigh/show
IP                                            MAC                 Bridge
==========================================================================
1.1.1.1                                       6a:91:a4:4f:78:45   br0
1.1.1.2                                       b2:42:ef:84:79:4b   br0
10.10.10.1                                    12:31:66:3c:95:47   br1
10.10.10.2                                    52:54:00:9f:e8:0e   br1

ping流程如下(暂时忽略arp学习过程)

a. vm1上ping 1.1.1.2后,br0端口收到icmp报文,查找fast path失败,上送slow-path处理,广播到vxlan1端口。
b. 在vxlan1端口根据配置的remote_ip 10.10.10.2查找路由表,找到出接口br1和源ip。
Cached: 10.10.10.0/24 dev br1 SRC 10.10.10.1
再根据neigh表找到目的mac
10.10.10.2                                    52:54:00:9f:e8:0e   br1
c. 此时外层mac,ip和udp端口号都已知,根据这些信息构造外层报文信息。然后假装报文从br1收到,查找br1上的openflow流表,action为normal,需要查找fdb表,未知报文flood到br1上所有端口,当前只有ens8。所以最后的流表信息如下,会将此流表下发到datapath。
recirc_id(0),in_port(1),packet_type(ns=0,id=0),eth(src=6a:91:a4:4f:78:45,dst=b2:42:ef:84:79:4b),eth_type(0x0800),ipv4(tos=0/0x3,frag=no), packets:267, bytes:26166, used:0.410s, actions:clone(tnl_push(tnl_port(2),header(size=50,type=4,eth(dst=52:54:00:9f:e8:0e,src=12:31:66:3c:95:47,dl_type=0x0800),ipv4(src=10.10.10.1,dst=10.10.10.2,proto=17,tos=0,ttl=64,frag=0x4000),udp(src=0,dst=8472,csum=0x0),vxlan(flags=0x8000000,vni=0x0)),out_port(3)),4)
d. 流表下发后,会将报文按照流表action封装成vxlan报文,从ens8发送出去。
e. vm2上的ens8收到vxlan报文后,查找fast path失败,上送slow-path,在函数terminate_native_tunnel中根据外层报文信息查找是否有匹配的tunnel,如果有将设置action为OVS_ACTION_ATTR_TUNNEL_POP,出端口为tunnel口。
f. 下发上面的流表到datapath,同时执行action处理报文。将外层封装去除,以出端口vxlan1为入端口,重新走slow-path,找到br0,将这次流表也下发到datapath。所以vxlan报文接收方向有两条流表。
g. 后续流程类型,不再赘述。

源码分析

创建vxlan端口流程

main -> bridge_run -> bridge_add_ports -> iface_create -> iface_create -> iface_do_create
在 iface_do_create函数中
  iface_set_netdev_config 将vxlan配置更新到 dev->tnl_cfg
  ofproto_port_add 
    port_add将vxlan端口添加到datapath中
    update_port ->ofport_install -> port_construct保存tunnel port到全局变量

在函数type_run中,将ofport->is_tunnel 转换到 xport->is_tunnel。后面流表转发只会用到xport相关。

发送vxlan报文流程
a. slow-path
slow-path处理流程,如果找到或者flood到vxlan端口后,调用compose_output_action__

upcall_cb -> process_upcall -> upcall_xlate -> xlate_actions -> do_xlate_actions -> xlate_output_action -> xlate_normal -> xlate_normal_flood -> output_normal -> compose_output_action -> compose_output_action__

static void
compose_output_action__(struct xlate_ctx *ctx, ofp_port_t ofp_port, const struct xlate_bond_recirc *xr, bool check_stp)
    if (xport->is_tunnel) {
        //userspace vxlan 这里主要设置is_native_tunnel为true,后面才会真正设置action
        if (ovs_native_tunneling_is_on(ctx->xbridge->ofproto)) {
            xlate_report(ctx, OFT_DETAIL, "output to native tunnel");
            is_native_tunnel = true;
        } else {
            //kernelspace vxlan 这里主要添加两个action set和tunnel
            xlate_report(ctx, OFT_DETAIL, "output to kernel tunnel");
            commit_odp_tunnel_action(flow, &ctx->base_flow, ctx->odp_actions);
                nl_msg_start_nested(odp_actions, OVS_ACTION_ATTR_SET);
                nl_msg_start_nested(a, OVS_KEY_ATTR_TUNNEL);
    }

    //userspace vxlan发送流程,调用build_tunnel_send根据remote_ip查找路由表,查找mac表,并且添加三个action:  
    //OVS_ACTION_ATTR_CLONE,OVS_ACTION_ATTR_TUNNEL_PUSH,OVS_ACTION_ATTR_OUTPUT
    if (is_native_tunnel) {
        /* Output to native tunnel port. */
        build_tunnel_send(ctx, xport, flow, odp_port);
    } else if (terminate_native_tunnel(ctx, ofp_port, flow, wc, &odp_tnl_port)) {
        //这个判断是接收到vxlan报文的处理
        //terminate_native_tunnel 根据外层flow查找tunnel信息,找到 odp 端口,即在datapath的端口号
        /* Intercept packet to be received on native tunnel port. */
        nl_msg_put_odp_port(ctx->odp_actions, OVS_ACTION_ATTR_TUNNEL_POP, odp_tnl_port);
    } else {
        //对于在kernelspace转发的vxlan来说,只需要把action的出端口设置为vxlan即可,不用ovs来封装vxlan报文
        nl_msg_put_odp_port(ctx->odp_actions, OVS_ACTION_ATTR_OUTPUT, out_port);
    }
}

对于userspace vxlan来说,主要下发三个action: OVS_ACTION_ATTR_CLONE,OVS_ACTION_ATTR_TUNNEL_PUSH,OVS_ACTION_ATTR_OUTPUT。其中OVS_ACTION_ATTR_TUNNEL_PUSH和OVS_ACTION_ATTR_OUTPUT嵌套在OVS_ACTION_ATTR_CLONE中。对应的流表如下

recirc_id(0),in_port(1),packet_type(ns=0,id=0),eth(src=6a:91:a4:4f:78:45,dst=b2:42:ef:84:79:4b),eth_type(0x0800),ipv4(tos=0/0x3,frag=no), packets:267, bytes:26166, used:0.410s, actions:clone(tnl_push(tnl_port(2),header(size=50,type=4,eth(dst=52:54:00:9f:e8:0e,src=12:31:66:3c:95:47,dl_type=0x0800),ipv4(src=10.10.10.1,dst=10.10.10.2,proto=17,tos=0,ttl=64,frag=0x4000),udp(src=0,dst=8472,csum=0x0),vxlan(flags=0x8000000,vni=0x0)),out_port(3)),4)

actions:clone() 对应OVS_ACTION_ATTR_CLONE。
tnl_push()对应OVS_ACTION_ATTR_TUNNEL_PUSH。括号中的tnl_port(2)表示端口2是vxlan口,header()中的内容包含外层源目的mac,源目的ip和源目的端口号和vxlan头部的flag和vni,out_port(3)表示vxlan报文的出端口,但实际上报文不会直接从此端口发出,而是以它为入端口,再次走slow-path查找实际的出端口。
最后的数字4对应出端口OVS_ACTION_ATTR_OUTPUT。

对于kernel space vxlan来说,也会下发三个action: OVS_ACTION_ATTR_SET,OVS_KEY_ATTR_TUNNEL,OVS_ACTION_ATTR_OUTPUT。其中OVS_KEY_ATTR_TUNNEL嵌套在OVS_ACTION_ATTR_SET中。对应的流表如下

recirc_id(0),in_port(1),eth(src=96:e5:e8:08:63:44,dst=0e:ed:bb:33:06:40),eth_type(0x0800),ipv4(tos=0/0x3,frag=no), packets:400, bytes:39200, used:0.476s, actions:set(tunnel(tun_id=0x0,dst=10.10.10.2,ttl=64,tp_dst=8472,flags(df|key))),2

actions:set() 对应OVS_ACTION_ATTR_SET。
tunnel()对应OVS_KEY_ATTR_TUNNEL。这个action主要保存封装vxlan需要的信息。
最后的数字2对应出端口 OVS_ACTION_ATTR_OUTPUT。这个action主要是将未封装的报文发送给vxlan口,由vxlan kernel模块来封装。

需要注意的是必须安装actions的顺序执行,否则vxlan封装失败。因为首先通过OVS_KEY_ATTR_TUNNEL设置remote_ip等信息到skb的私有数据中,再执行OVS_ACTION_ATTR_OUTPUT时,从skb的私有数据中取出remote_ip等信息才能完成封装。

b. fast path
slow-path下发流表后,后续报文都可以在datapath找到流表直接转发。
对于userspace vxlan流程如下

dp_netdev_execute_actions -> odp_execute_actions -> odp_execute_clone执行clone action,这个函数里又依次执行tunnel push和output action 
  OVS_ACTION_ATTR_TUNNEL_PUSH -> push_tnl_action
  OVS_ACTION_ATTR_OUTPUT -> netdev_send

对于kernel space vxlan流程如下

ovs_vport_receive -> ovs_dp_process_packet -> ovs_execute_actions
  OVS_ACTION_ATTR_SET -> ovs_skb_dst_set(skb, (struct dst_entry *)tun->tun_dst); 保存tunnel信息到skb
  OVS_ACTION_ATTR_OUTPUT -> vxlan_xmit 转发给vxlan模块处理

接收vxlan报文流程
slow-path接收vxlan报文流程和发送报文流程前面函数调用都相同,只不过在函数compose_output_action__中区分开来。

compose_output_action__
  //xport为出端口(flood到所有端口),一般不会是tunnel端口
  if (xport->is_tunnel) {
  }
  if (is_native_tunnel) {
  } else if (terminate_native_tunnel(ctx, ofp_port, flow, wc, &odp_tnl_port)) {
    //terminate_native_tunnel 根据外层flow查找tunnel信息,找到 odp 端口,即在datapath的端口号。添加出端口为 odp_tnl_port。
    /* Intercept packet to be received on native tunnel port. */
    nl_msg_put_odp_port(ctx->odp_actions, OVS_ACTION_ATTR_TUNNEL_POP, odp_tnl_port);
  } else {
  }

这一步会下发一条流表

recirc_id(0),in_port(4),packet_type(ns=0,id=0),eth(src=2e:ab:5f:92:6c:47,dst=12:31:66:3c:95:47),eth_type(0x0800),ipv4(dst=10.10.10.1,proto=17,frag=no),udp(dst=8472), packets:919, bytes:90148, used:0.409s, actions:tnl_pop(2)

接着执行这条流表(以userspace vxlan为例)

dp_netdev_execute_actions -> odp_execute_actions -> dp_execute_cb -> OVS_ACTION_ATTR_TUNNEL_POP
struct dp_packet_batch *orig_packets_ = packets_;
odp_port_t portno = nl_attr_get_odp_port(a);
struct tx_port *p;

p = pmd_tnl_port_cache_lookup(pmd, portno);
if (p) {
    //去掉外层头
    netdev_pop_header(p->port->netdev, packets_);
    
    struct dp_packet *packet;
    DP_PACKET_BATCH_FOR_EACH (packet, packets_) {
        //重新设置 in_port 为 portno,portno为vxlan端口号
        packet->md.in_port.odp_port = portno;
    }

    (*depth)++;
    //以vxlan端口为入端口,报文只有内层,重新走slow-path流程,添加第二条流表
    dp_netdev_recirculate(pmd, packets_);
        dp_netdev_input__(pmd, packets, true, 0);
    (*depth)--;
}

第二条流表如下

tunnel(tun_id=0x0,src=10.10.10.2,dst=10.10.10.1,flags(-df-csum+key)),recirc_id(0),in_port(2),packet_type(ns=0,id=0),eth(src=b2:42:ef:84:79:4b,dst=6a:91:a4:4f:78:45),eth_type(0x0800),ipv4(dst=1.1.1.1,proto=1,frag=no), packets:100, bytes:9800, used:0.409s, actions:1

fast path就是按照datapath流表执行,不再赘述。

也可参考:openvswitch vxlan 源码分析 - 简书 (jianshu.com)

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/fengcai_ke/article/details/125656806

智能推荐

oracle 12c 集群安装后的检查_12c查看crs状态-程序员宅基地

文章浏览阅读1.6k次。安装配置gi、安装数据库软件、dbca建库见下:http://blog.csdn.net/kadwf123/article/details/784299611、检查集群节点及状态:[root@rac2 ~]# olsnodes -srac1 Activerac2 Activerac3 Activerac4 Active[root@rac2 ~]_12c查看crs状态

解决jupyter notebook无法找到虚拟环境的问题_jupyter没有pytorch环境-程序员宅基地

文章浏览阅读1.3w次,点赞45次,收藏99次。我个人用的是anaconda3的一个python集成环境,自带jupyter notebook,但在我打开jupyter notebook界面后,却找不到对应的虚拟环境,原来是jupyter notebook只是通用于下载anaconda时自带的环境,其他环境要想使用必须手动下载一些库:1.首先进入到自己创建的虚拟环境(pytorch是虚拟环境的名字)activate pytorch2.在该环境下下载这个库conda install ipykernelconda install nb__jupyter没有pytorch环境

国内安装scoop的保姆教程_scoop-cn-程序员宅基地

文章浏览阅读5.2k次,点赞19次,收藏28次。选择scoop纯属意外,也是无奈,因为电脑用户被锁了管理员权限,所有exe安装程序都无法安装,只可以用绿色软件,最后被我发现scoop,省去了到处下载XXX绿色版的烦恼,当然scoop里需要管理员权限的软件也跟我无缘了(譬如everything)。推荐添加dorado这个bucket镜像,里面很多中文软件,但是部分国外的软件下载地址在github,可能无法下载。以上两个是官方bucket的国内镜像,所有软件建议优先从这里下载。上面可以看到很多bucket以及软件数。如果官网登陆不了可以试一下以下方式。_scoop-cn

Element ui colorpicker在Vue中的使用_vue el-color-picker-程序员宅基地

文章浏览阅读4.5k次,点赞2次,收藏3次。首先要有一个color-picker组件 <el-color-picker v-model="headcolor"></el-color-picker>在data里面data() { return {headcolor: ’ #278add ’ //这里可以选择一个默认的颜色} }然后在你想要改变颜色的地方用v-bind绑定就好了,例如:这里的:sty..._vue el-color-picker

迅为iTOP-4412精英版之烧写内核移植后的镜像_exynos 4412 刷机-程序员宅基地

文章浏览阅读640次。基于芯片日益增长的问题,所以内核开发者们引入了新的方法,就是在内核中只保留函数,而数据则不包含,由用户(应用程序员)自己把数据按照规定的格式编写,并放在约定的地方,为了不占用过多的内存,还要求数据以根精简的方式编写。boot启动时,传参给内核,告诉内核设备树文件和kernel的位置,内核启动时根据地址去找到设备树文件,再利用专用的编译器去反编译dtb文件,将dtb还原成数据结构,以供驱动的函数去调用。firmware是三星的一个固件的设备信息,因为找不到固件,所以内核启动不成功。_exynos 4412 刷机

Linux系统配置jdk_linux配置jdk-程序员宅基地

文章浏览阅读2w次,点赞24次,收藏42次。Linux系统配置jdkLinux学习教程,Linux入门教程(超详细)_linux配置jdk

随便推点

matlab(4):特殊符号的输入_matlab微米怎么输入-程序员宅基地

文章浏览阅读3.3k次,点赞5次,收藏19次。xlabel('\delta');ylabel('AUC');具体符号的对照表参照下图:_matlab微米怎么输入

C语言程序设计-文件(打开与关闭、顺序、二进制读写)-程序员宅基地

文章浏览阅读119次。顺序读写指的是按照文件中数据的顺序进行读取或写入。对于文本文件,可以使用fgets、fputs、fscanf、fprintf等函数进行顺序读写。在C语言中,对文件的操作通常涉及文件的打开、读写以及关闭。文件的打开使用fopen函数,而关闭则使用fclose函数。在C语言中,可以使用fread和fwrite函数进行二进制读写。‍ Biaoge 于2024-03-09 23:51发布 阅读量:7 ️文章类型:【 C语言程序设计 】在C语言中,用于打开文件的函数是____,用于关闭文件的函数是____。

Touchdesigner自学笔记之三_touchdesigner怎么让一个模型跟着鼠标移动-程序员宅基地

文章浏览阅读3.4k次,点赞2次,收藏13次。跟随鼠标移动的粒子以grid(SOP)为partical(SOP)的资源模板,调整后连接【Geo组合+point spirit(MAT)】,在连接【feedback组合】适当调整。影响粒子动态的节点【metaball(SOP)+force(SOP)】添加mouse in(CHOP)鼠标位置到metaball的坐标,实现鼠标影响。..._touchdesigner怎么让一个模型跟着鼠标移动

【附源码】基于java的校园停车场管理系统的设计与实现61m0e9计算机毕设SSM_基于java技术的停车场管理系统实现与设计-程序员宅基地

文章浏览阅读178次。项目运行环境配置:Jdk1.8 + Tomcat7.0 + Mysql + HBuilderX(Webstorm也行)+ Eclispe(IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持)。项目技术:Springboot + mybatis + Maven +mysql5.7或8.0+html+css+js等等组成,B/S模式 + Maven管理等等。环境需要1.运行环境:最好是java jdk 1.8,我们在这个平台上运行的。其他版本理论上也可以。_基于java技术的停车场管理系统实现与设计

Android系统播放器MediaPlayer源码分析_android多媒体播放源码分析 时序图-程序员宅基地

文章浏览阅读3.5k次。前言对于MediaPlayer播放器的源码分析内容相对来说比较多,会从Java-&amp;amp;gt;Jni-&amp;amp;gt;C/C++慢慢分析,后面会慢慢更新。另外,博客只作为自己学习记录的一种方式,对于其他的不过多的评论。MediaPlayerDemopublic class MainActivity extends AppCompatActivity implements SurfaceHolder.Cal..._android多媒体播放源码分析 时序图

java 数据结构与算法 ——快速排序法-程序员宅基地

文章浏览阅读2.4k次,点赞41次,收藏13次。java 数据结构与算法 ——快速排序法_快速排序法