# OVS原理

ovs的架构如下图所示，主要由内核datapath、vswitchd、ovsdb以及用户空间的ovs-vsctl/ovs-ofctl/ovs-dpctl等组成。

![](/files/-LDZSDIOtTVm0n1Ehhh0)

* vswitchd是一个守护进程，是ovs的管理和控制服务，通过unix socket将配置信息保存到ovsdb，并通过netlink和内核模块交互
* ovsdb则是ovs的数据库，保存了ovs配置信息
* datapath是负责数据交换的内核模块，比如把接受端口收到的包放到流表中进行匹配，并执行匹配后的动作等。它在初始化和port binding的时候注册钩子函数，把端口的报文处理接管到内核模块

## 主要数据结构

![](/files/-LDZSDIVyBlHrLrR4LSs) (图片来自[csdn](http://blog.csdn.net/yuzhihui_no1/article/details/39188373))

## 主要流程

*注：部分转载自*[*OVS 源码分析整理*](http://www.jianshu.com/p/bf112793d658)

### 添加网桥

1. 键入命令ovs-vsctl add-br testBR
2. 内核中的 openvswitch.ko 收到一个添加网桥的命令时候——即收到 OVS\_DATAPATH\_FAMILY通道的 OVS\_DP\_CMD\_NEW命令。该命令绑定的回调函数为 ovs\_dp\_cmd\_new
3. ovs\_dp\_cmd\_new 函数除了初始化 dp 结构外，调用 new\_vport 函数来生成新的 vport
4. new\_vport 函数调用 ovs\_vport\_add()来尝试生成一个新的 vport
5. ovs\_vport\_add()函数会检查 vport 类型（通过 vport\_ops\_list\[]数组），并调用相关的 create()函数来生成 vport 结构
6. 当dp是网络设备时（vport\_netdev.c），最终由 ovs\_vport\_add()函数调用的是 netdev\_create()【在 vport\_ops\_list的ovs\_netdev\_ops 中】
7. netdev\_create()函数最关键的一步是注册了收到网包时的回调函数
8. err=netdev\_rx\_handler\_register(netdev\_vport->dev,netdev\_frame\_hook,vport);
9. 操作是将 netdev\_vport->dev 收到网包时的相关数据由 netdev\_frame\_hook()函数来处理，都是些辅助处理，依次调用各处理函数，在 netdev\_port\_receive()【这里会进行数据包的拷贝，避免损坏】进入 ovs\_vport\_receive()回到 vport.c，从 ovs\_dp\_process\_receive\_packet()回到 datapath.c，进行统一处理
10. 流程：netdev\_frame\_hook()->netdev\_port\_receive->ovs\_vport\_receive->ovs\_dp\_process\_received\_packet()
11. net\_port\_receive()首先检测是否 skb 被共享，若是则得到 packet 的拷贝。
12. net\_port\_receive()其调用ovs\_vport\_receive()，检查包的校验和，然后交付给我们的vport通用层来处理。

![](/files/-LDZSDIaObRb5Ho6Fw06) (图片来自[简书](http://www.jianshu.com/p/bf112793d658))

### 流表匹配

1. flow\_lookup()查找对应的流表项
2. for 循环调用 rcu\_dereference\_ovs 对流表结构体中的 mask\_list 成员遍历，找到对应的的 成员
3. flow=masked\_flow\_lookup()遍历进行下一级 hmap查找，找到为止
4. 进入 包含函数 ovs\_flow\_mask\_key(\&masked\_key,unmasked,mask)，将最开始提取的 Key 值和 mask 的 key 值进行“与”操作，结果存放在 masked\_key 中，用来得到后面的 Hash 值
5. hash=flow\_hash(\&masked\_key,key\_start,key\_end)key 值的匹配字段只有部分
6. ovs\_vport\_add()函数会检查 vport 类型（通过 vport\_ops\_list\[]数组），并调用相关的 create()函数来生成 vport 结构
7. 可见，当 dp 时网络设备时（vport\_netdev.c），最终由 ovs\_vport\_add()函数调用的是 netdev\_create()【在 vport\_ops\_list的ovs\_netdev\_ops 中】
8. netdev\_vport->dev 收到网包时的相关数据由 netdev\_frame\_hook()函数来处理，都是些辅助处理，依次调用各处理函数，在 netdev\_port\_receive()【这里会进行数据包的拷贝，避免损坏】进入 ovs\_vport\_receive()回到 vport.c，从 ovs\_dp\_process\_receive\_packet()回到 datapath.c，进行统一处理

![](/files/-LDZSDIgr_nEsDpuXfe-)

### 收包处理

1. ovs\_vport\_receive\_packets()调用ovs\_flow\_extract基于skb生成key值，并检查是否有错,然后调用ovs\_dp\_process\_packet。交付给datapath处理
2. ovs\_flow\_tbl\_lookup\_stats。基于前面生成的key值进行流表查找，返回匹配的流表项，结构为sw\_flow。&#x20;
3. 若不存在匹配，则调用ovs\_dp\_upcall上传至userspace进行匹配。 (包括包和key都要上传)&#x20;
4. 若存在匹配，则直接调用ovs\_execute\_actions执行对应的action，比如添加vlan头，转发到某个port等。

![](/files/-LDZSDIjoEZRk9Ju-SFU)

### upcall 消息处理

1. ovs\_dp\_upcall()首先调用 err=queue\_userspace\_packet()将信息排队发到用户空间去
2. dp\_ifindex=get\_dpifindex(dp)获取网卡设备索引号
3. 调整 VLAN的 MAC 地址头指针
4. 网络链路属性，如果不需要填充则调用此函数
5. len=upcall\_msg\_size()，获得 upcall 发送消息的大小
6. user\_skb=genlmsg\_new\_unicast，创建一个新的 netlink 消息
7. upcall=genlmsg\_put()增加一个新的 netlink 消息到 skb
8. err=genlmsg\_unicast(),发送消息到用户空间去处理

![](/files/-LDZSDIlKJx05ewhgc_e)

## 参考文档

* [OVS 源码分析整理](http://www.jianshu.com/p/bf112793d658)
* [openvswitch源码分析](http://blog.csdn.net/column/details/openvswitch.html)
* [OVS Deep Dive](http://docs.openvswitch.org/en/latest/topics/)


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://sdn.feisky.xyz/wang-luo-ji-chu/index-2/internal.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
