从Openvswitch代码看网络包的旅程
我们知道,Openvwitch可以创建虚拟交换机,而网络包可以通过虚拟交换机进行转发,并通过流表进行处理,具体的过程如何呢?
一、内核模块Openvswitch.ko的加载
OVS是内核态和用户态配合工作的,所以首先要加载内核态模块Openvswitch.ko。
在datapath/datapath.c中会调用module_init(dp_init);来初始化内核模块。
其中比较重要的是调用了dp_register_genl(),这个就是注册netlink函数,从而用户态进程ovs-vswitchd可以通过netlink调用内核。
这里dp_genl_families由四个netlink的family组成
static struct genl_family *dp_genl_families[] = {
&dp_datapath_genl_family,
&dp_vport_genl_family,
&dp_flow_genl_family,
&dp_packet_genl_family,
};
可以看出,在内核中,包含对datapath的操作,例如OVS_DP_CMD_NEW,对虚拟端口vport的操作,例如OVS_VPORT_CMD_NEW,对flow流表的操作,例如OVS_FLOW_CMD_NEW,对packet包的操作,例如OVS_PACKET_CMD_EXECUTE。
二、用户态进程ovs-vswitchd的启动
ovs-vswitchd.c的main函数最终会进入一个while循环,在这个无限循环中,里面最重要的两个函数是bridge_run()和netdev_run()。
Openvswitch主要管理两种类型的设备,一个是创建的虚拟网桥,一个是连接到虚拟网桥上的设备。
其中bridge_run就是初始化数据库中已经创建的虚拟网桥。
虚拟网卡的初始化则靠netdev_run()。
bridge_run会调用static void bridge_reconfigure(const struct ovsrec_open_vswitch *ovs_cfg),其中ovs_cfg是从ovsdb-server里面读取出来的配置。
在这个函数里面,对于每一个网桥,将网卡添加进去。
HMAP_FOR_EACH (br, node, &all_bridges) {
bridge_add_ports(br, &br->wanted_ports);
shash_destroy(&br->wanted_ports);
}
最终会调用dpif_netlink_port_add__在这个函数里面,会调用netlink的API,命令为OVS_VPORT_CMD_NEW。
三、内核模块监听网卡
ovs-vswitchd启动的时候,将虚拟网卡添加到虚拟交换机上的时候,会调用netlink的OVS_VPORT_CMD_NEW命令,因而会调用函数ovs_vport_cmd_new。
它最终会调用ovs_netdev_link,其中有下面的代码:
err = netdev_rx_handler_register(vport->dev, netdev_frame_hook,
vport);
注册一个方法叫做netdev_frame_hook,每当网卡收到包的时候,就调用这个方法。
四、内核态网络包处理
Openvswitch的内核模块openvswitch.ko会在网卡上注册一个函数netdev_frame_hook,每当有网络包到达网卡的时候,这个函数就会被调用。
static struct sk_buff *netdev_frame_hook(struct sk_buff *skb)
{
if (unlikely(skb->pkt_type == PACKET_LOOPBACK))
return skb;
port_receive(skb);
return NULL;
}
调用port_receive即是调用netdev_port_receive
在这个函数里面,首先声明了变量struct sw_flow_key key;
如果我们看这个key的定义,可见这个key里面是一个大杂烩,数据包里面的几乎任何部分都可以作为key来查找flow表
tunnel可以作为key
在物理层,in_port即包进入的网口的ID
在MAC层,源和目的MAC地址
在IP层,源和目的IP地址
在传输层,源和目的端口号
IPV6
所以,要在内核态匹配流表,首先需要调用ovs_flow_key_extract,从包的正文中提取key的值。
接下来就是要调用ovs_dp_process_packet了。
这个函数首先在内核里面的流表中查找符合key的flow,也即ovs_flow_tbl_lookup_stats,如果找到了,很好说明用户态的流表已经放入内核,则走fast
path就可了。于是直接调用ovs_execute_actions,执行这个key对应的action。
如果不能找到,则只好调用ovs_dp_upcall,让用户态去查找流表。会调用static
int queue_userspace_packet(struct datapath *dp, struct sk_buff *skb,
const struct sw_flow_key *key, const struct dp_upcall_info *upcall_info)
它会调用err
= genlmsg_unicast(ovs_dp_get_net(dp), user_skb,
upcall_info->portid);通过netlink将消息发送给用户态。在用户态,有线程监听消息,一旦有消息,则触发udpif_upcall_handler。
Slow Path & Fast Path
Slow Path:
当Datapath找不到flow rule对packet进行处理时
Vswitchd使用flow rule对packet进行处理。
Fast Path:
将slow path的flow rule放在内核态,对packet进行处理
Unknown Packet Processing
Datapath使用flow rule对packet进行处理,如果没有,则有vswitchd使用flow rule进行处理
从Device接收Packet交给事先注册的event handler进行处理
接收Packet后识别是否是unknown packet,是则交由upcall处理
vswitchd对unknown packet找到flow rule进行处理
将Flow rule发送给datapath
五、用户态处理包
当内核无法查找到流表项的时候,则会通过upcall来调用用户态ovs-vswtichd中的flow table。
会调用ofproto-dpif-upcall.c中的udpif_upcall_handler函数。
(1)
首先读取upcall调用static int upcall_receive(struct upcall *upcall, const
struct dpif_backer *backer, const struct dp_packet *packet, enum
dpif_upcall_type type, const struct nlattr *userdata, const struct flow
*flow, const unsigned int mru, const ovs_u128 *ufid, const unsigned
pmd_id)
(2) 其次提取包头调用void flow_extract(struct dp_packet *packet, struct flow *flow),提取出的flow如下:
/* L2, Order the same as in the Ethernet header! (64-bit aligned) */
struct eth_addr dl_dst; /* Ethernet destination address. */
struct eth_addr dl_src; /* Ethernet source address. */
ovs_be16 dl_type; /* Ethernet frame type. */
ovs_be16 vlan_tci; /* If 802.1Q, TCI | VLAN_CFI; otherwise 0. */
ovs_be32 mpls_lse[ROUND_UP(FLOW_MAX_MPLS_LABELS, 2)]; /* MPLS label stack
(with padding). */
/* L3 (64-bit aligned) */
ovs_be32 nw_src; /* IPv4 source address. */
ovs_be32 nw_dst; /* IPv4 destination address. */
struct in6_addr ipv6_src; /* IPv6 source address. */
struct in6_addr ipv6_dst; /* IPv6 destination address. */
ovs_be32 ipv6_label; /* IPv6 flow label. */
uint8_t nw_frag; /* FLOW_FRAG_* flags. */
uint8_t nw_tos; /* IP ToS (including DSCP and ECN). */
uint8_t nw_ttl; /* IP TTL/Hop Limit. */
uint8_t nw_proto; /* IP protocol or low 8 bits of ARP opcode. */
struct in6_addr nd_target; /* IPv6 neighbor discovery (ND) target. */
struct eth_addr arp_sha; /* ARP/ND source hardware address. */
struct eth_addr arp_tha; /* ARP/ND target hardware address. */
ovs_be16 tcp_flags; /* TCP flags. With L3 to avoid matching L4. */
ovs_be16 pad3; /* Pad to 64 bits. */
/* L4 (64-bit aligned) */
ovs_be16 tp_src; /* TCP/UDP/SCTP source port/ICMP type. */
ovs_be16 tp_dst; /* TCP/UDP/SCTP destination port/ICMP code. */
ovs_be32 igmp_group_ip4; /* IGMP group IPv4 address.
* Keep last for BUILD_ASSERT_DECL below. */
(3)
然后调用static int process_upcall(struct udpif *udpif, struct upcall
*upcall, struct ofpbuf *odp_actions, struct flow_wildcards
*wc)来处理upcall。
对于MISS_UPCALL,调用static void
upcall_xlate(struct udpif *udpif, struct upcall *upcall, struct ofpbuf
*odp_actions, struct flow_wildcards *wc)
会调用enum xlate_error xlate_actions(struct xlate_in *xin, struct xlate_out *xout)
在这个函数里面,会在flow table里面查找rule
ctx.rule
= rule_dpif_lookup_from_table( ctx.xbridge->ofproto,
ctx.tables_version, flow, xin->wc, ctx.xin->resubmit_stats,
&ctx.table_id, flow->in_port.ofp_port, true, true);
找到rule之后,调用static
void do_xlate_actions(const struct ofpact *ofpacts, size_t ofpacts_len,
struct xlate_ctx *ctx)在这个函数里面,根据action的不同,修改flow的内容。
(4) 最后调用static void handle_upcalls(struct udpif *udpif, struct upcall *upcalls, size_t n_upcalls)将flow rule添加到内核中的datapath
他会调用void
dpif_operate(struct dpif *dpif, struct dpif_op **ops, size_t
n_ops),他会调用dpif->dpif_class->operate(dpif, ops, chunk);
会调用dpif_netlink_operate()
会调用netlink修改内核中datapath的规则。
case DPIF_OP_FLOW_PUT:
put = &op->u.flow_put;
dpif_netlink_init_flow_put(dpif, put, &flow);
if (put->stats) {
flow.nlmsg_flags |= NLM_F_ECHO;
aux->txn.reply = &aux->reply;
}
dpif_netlink_flow_to_ofpbuf(&flow, &aux->request);
break;
欢迎关注个人公众号
从Openvswitch代码看网络包的旅程的更多相关文章
- Openvswitch原理与代码分析(4):网络包的处理过程
在上一节提到,Openvswitch的内核模块openvswitch.ko会在网卡上注册一个函数netdev_frame_hook,每当有网络包到达网卡的时候,这个函数就会被调用. stati ...
- OceanBase server处理网络包的回调逻辑
OceanBase处理网络包的逻辑还是蛮绕的,这里以UPS为例,作为给自己的备忘. UPS代码的main.cpp中调用ObUpdateServerMain的start启动server.start函数会 ...
- C# 利用SharpPcap实现网络包捕获嗅探
本文是利用SharpPcap实现网络包的捕获的小例子,实现了端口监控,数据包捕获等功能,主要用于学习分享. 什么是SharpPcap? SharpPcap 是一个.NET 环境下的网络包捕获框架,基于 ...
- 不要怂,就是GAN (生成式对抗网络) (五):无约束条件的 GAN 代码与网络的 Graph
GAN 这个领域发展太快,日新月异,各种 GAN 层出不穷,前几天看到一篇关于 Wasserstein GAN 的文章,讲的很好,在此把它分享出来一起学习:https://zhuanlan.zhihu ...
- 科普文:从人人网看网络科学(Network Science)的X个经典问题
转:https://zr9558.wordpress.com/2013/12/05/科普文:从人人网看网络科学(network-science)的x个经典问/ 长文,写了N个小时写完的.你肯定能看懂, ...
- c#基于事件模型的UDP通讯框架(适用于网络包编解码)
之前写过一篇关于c#udp分包发送的文章 这篇文章里面介绍的方法是一种实现,可是存在一个缺点就是一个对象序列化后会增大非常多.不利于在网络中的传输. 我们在网络中的传输是须要尽可能的减小传送的数据包的 ...
- ios 抓取真机的网络包
一直被如何从真机上抓包所困扰!今天偶然看到了最简单有效的方法!分享一下: 原地址链接 http://blog.csdn.net/phunxm/article/details/38590561 通过 R ...
- 好看的IDE配色方案让代码看起来不再那么凶猛了
写这篇小文的初衷是,笔者是原教旨主义者,一直坚持用IDE默认的配色方案.另外也觉得网上黑色系的配色方案太过bling bling了.但今天尝试用新的配色方案后,兴奋地发现对代码的好感度大幅提升. 嗯, ...
- 【原创】使用Fiddler抓取手机网络包
一: 下载安装Fiddler 二: 打开 tools--Telerik Fiddler Options, 进行如下设置
随机推荐
- C++中const对象和非const对象调用成员函数问题
一.类MyClass 二.主函数调用 三.结果
- 前端之CSS内容
一.CSS介绍 CSS(Cascading Style Sheet,层叠样式表)定义如何显示HTML元素. 当浏览器读到一个样式表,它就会按照这个样式表来对文档进行格式化(渲染). 二.CSS语法 1 ...
- maven环境变量的配置及+eclipse的配置使用
1. 环境搭建(Maven+eclipse) 进入CMD 输入: mvn –v 查看是否配置好 输入: mvn -version 可以查看其安装的版本 在eclipse中配置maven: 在h ...
- Oracle处理XML字段时遇到的ORA-31013: XPATH 表达式无效问题
select extractValue(ed.info_id, '/Root/ExpandProfile/PhoneNumber') as phone, extractValue(ed.info_id ...
- IT 必备电脑快捷键
IT 必备电脑快捷键 键盘上除了有字母.数字之外,还有一些特殊的按键:ctrl.shift.alt.tab ● ctrl键是英语control“控制”的意思,这个按键,单独按没有任何作用,都要和其他的 ...
- Python scrapy框架
Scrapy Scrapy是一个为了爬取网站数据,提取结构性数据而编写的应用框架. 其可以应用在数据挖掘,信息处理或存储历史数据等一系列的程序中.其最初是为了页面抓取 (更确切来说, 网络抓取 )所设 ...
- 高下相倾,前后相随——iterator 与 for ... of 循环
iterator 是es6新提供的一种遍历器.本质上是一个接口,为各种不同的数据结构,提供统一的访问机制. 数据只要部署了iterator接口,便是可遍历的数据,标志是具有Symbol.iterato ...
- hdu1072 Nightmare---BFS
题目链接: http://acm.hdu.edu.cn/showproblem.php?pid=1072 题目大意: 在n×m的地图上,0表示墙,1表示空地,2表示人,3表示目的地,4表示有定时炸弹重 ...
- scrapy中response.body 与 response.text区别
scrapy中response.body 与 response.text区别 body http响应正文, byte类型 text 文本形式的http正文,str类型,它是response.body经 ...
- 优易软件-关于click事件在苹果手机失效的问题
因为是动态添加的内容,所以想要使用click事件,需要给他用on绑定一下: $(document).on("click",".next_button",func ...