当内核无法查找到流表项的时候,则会通过upcall来调用用户态ovs-vswtichd中的flow table。
会调用ofproto-dpif-upcall.c中的udpif_upcall_handler函数。
static
void *
udpif_upcall_handler(void *arg)
{
struct handler *handler = arg;
struct udpif *udpif = handler->udpif;
while (!latch_is_set(&handler->udpif->exit_latch)) {
if (recv_upcalls(handler)) {
poll_immediate_wake();
} else {
dpif_recv_wait(udpif->dpif, handler->handler_id);
latch_wait(&udpif->exit_latch);
}
poll_block();
}
return NULL;
}
|
会调用static size_t recv_upcalls(struct handler *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如下:
struct flow {
/* Metadata */
struct flow_tnl tunnel; /* Encapsulating tunnel parameters. */
ovs_be64 metadata; /* OpenFlow Metadata. */
uint32_t regs[FLOW_N_REGS]; /* Registers. */
uint32_t skb_priority; /* Packet priority for QoS. */
uint32_t pkt_mark; /* Packet mark. */
uint32_t dp_hash; /* Datapath computed hash value. The exact
* computation is opaque to the user space. */
union flow_in_port in_port; /* Input port.*/
uint32_t recirc_id; /* Must be exact match. */
uint16_t ct_state; /* Connection tracking state. */
uint16_t ct_zone; /* Connection tracking zone. */
uint32_t ct_mark; /* Connection mark.*/
uint8_t pad1[4]; /* Pad to 64 bits. */
ovs_u128 ct_label; /* Connection label. */
uint32_t conj_id; /* Conjunction ID. */
ofp_port_t actset_output; /* Output port in action set. */
uint8_t pad2[2]; /* Pad to 64 bits. */
/* 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)
switch (classify_upcall(upcall->type, userdata)) {
case MISS_UPCALL:
upcall_xlate(udpif, upcall, odp_actions, wc);
return 0;
|
会调用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的内容。
switch (a->type) {
case OFPACT_OUTPUT:
xlate_output_action(ctx, ofpact_get_OUTPUT(a)->port,
ofpact_get_OUTPUT(a)->max_len, true);
break;
case OFPACT_SET_VLAN_VID:
wc->masks.vlan_tci |= htons(VLAN_VID_MASK | VLAN_CFI);
if (flow->vlan_tci & htons(VLAN_CFI) ||
ofpact_get_SET_VLAN_VID(a)->push_vlan_if_needed) {
flow->vlan_tci &= ~htons(VLAN_VID_MASK);
flow->vlan_tci |= (htons(ofpact_get_SET_VLAN_VID(a)->vlan_vid)
| htons(VLAN_CFI));
}
break;
case OFPACT_SET_ETH_SRC:
WC_MASK_FIELD(wc, dl_src);
flow->dl_src = ofpact_get_SET_ETH_SRC(a)->mac;
break;
case OFPACT_SET_ETH_DST:
WC_MASK_FIELD(wc, dl_dst);
flow->dl_dst = ofpact_get_SET_ETH_DST(a)->mac;
break;
case OFPACT_SET_IPV4_SRC:
CHECK_MPLS_RECIRCULATION();
if (flow->dl_type == htons(ETH_TYPE_IP)) {
memset(&wc->masks.nw_src, 0xff, sizeof wc->masks.nw_src);
flow->nw_src = ofpact_get_SET_IPV4_SRC(a)->ipv4;
}
break;
case OFPACT_SET_IPV4_DST:
CHECK_MPLS_RECIRCULATION();
if (flow->dl_type == htons(ETH_TYPE_IP)) {
memset(&wc->masks.nw_dst, 0xff, sizeof wc->masks.nw_dst);
flow->nw_dst = ofpact_get_SET_IPV4_DST(a)->ipv4;
}
break;
case OFPACT_SET_L4_SRC_PORT:
CHECK_MPLS_RECIRCULATION();
if (is_ip_any(flow) && !(flow->nw_frag & FLOW_NW_FRAG_LATER)) {
memset(&wc->masks.nw_proto, 0xff, sizeof wc->masks.nw_proto);
memset(&wc->masks.tp_src, 0xff, sizeof wc->masks.tp_src);
flow->tp_src = htons(ofpact_get_SET_L4_SRC_PORT(a)->port);
}
break;
case OFPACT_SET_L4_DST_PORT:
CHECK_MPLS_RECIRCULATION();
if (is_ip_any(flow) && !(flow->nw_frag & FLOW_NW_FRAG_LATER)) {
memset(&wc->masks.nw_proto, 0xff, sizeof wc->masks.nw_proto);
memset(&wc->masks.tp_dst, 0xff, sizeof wc->masks.tp_dst);
flow->tp_dst = htons(ofpact_get_SET_L4_DST_PORT(a)->port);
}
break;
|
(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()
static
void
dpif_netlink_operate(struct dpif *dpif_, struct dpif_op **ops, size_t n_ops)
{
struct dpif_netlink *dpif = dpif_netlink_cast(dpif_);
while (n_ops > 0) {
size_t chunk = dpif_netlink_operate__(dpif, ops, n_ops);
ops += chunk;
n_ops -= chunk;
}
}
|
在static size_t dpif_netlink_operate__(struct dpif_netlink *dpif, struct dpif_op **ops, size_t n_ops)中,有以下的代码:
switch (op->type) {
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;
case DPIF_OP_FLOW_DEL:
del = &op->u.flow_del;
dpif_netlink_init_flow_del(dpif, del, &flow);
if (del->stats) {
flow.nlmsg_flags |= NLM_F_ECHO;
aux->txn.reply = &aux->reply;
}
dpif_netlink_flow_to_ofpbuf(&flow, &aux->request);
break;
|
会调用netlink修改内核中datapath的规则。
- Openvswitch原理与代码分析(5): 内核中的流表flow table操作
当一个数据包到达网卡的时候,首先要经过内核Openvswitch.ko,流表Flow Table在内核中有一份,通过key查找内核中的flow table,即可以得到action,然后执行acti ...
- Openvswitch原理与代码分析(1):总体架构
一.Opevswitch总体架构 Openvswitch的架构网上有如下的图表示: 每个模块都有不同的功能 ovs-vswitchd 为主要模块,实现交换机的守护进程daemon ...
- Openvswitch原理与代码分析(4):网络包的处理过程
在上一节提到,Openvswitch的内核模块openvswitch.ko会在网卡上注册一个函数netdev_frame_hook,每当有网络包到达网卡的时候,这个函数就会被调用. stati ...
- Openvswitch原理与代码分析(3): openvswitch内核模块的加载
上一节我们讲了ovs-vswitchd,其中虚拟网桥初始化的时候,对调用内核模块来添加虚拟网卡. 我们从openvswitch内核模块的加载过程,来看这个过程. 在datapath/dat ...
- Openvswitch原理与代码分析(2): ovs-vswitchd的启动
ovs-vswitchd.c的main函数最终会进入一个while循环,在这个无限循环中,里面最重要的两个函数是bridge_run()和netdev_run(). Openvswitch主要 ...
- Openvswitch原理与代码分析(8): 修改Openvswitch代码添加自定义action
有时候我们需要自定义一些自己的action,根据包头里面的信息,做一些自己的操作. 例如添加一个action名为handle_example 第一.修改ofp-actions.c文件 首先 ...
- Openvswitch原理与代码分析(7): 添加一条流表flow
添加一个flow,调用的命令为 ovs-ofctl add-flow hello "hard_timeout=0 idle_timeout=0 priority=1 table=21 pkt ...
- 免费的Lucene 原理与代码分析完整版下载
Lucene是一个基于Java的高效的全文检索库.那么什么是全文检索,为什么需要全文检索?目前人们生活中出现的数据总的来说分为两类:结构化数据和非结构化数据.很容易理解,结构化数据是有固定格式和结构的 ...
- OpenStack 虚拟机冷/热迁移的实现原理与代码分析
目录 文章目录 目录 前文列表 冷迁移代码分析(基于 Newton) Nova 冷迁移实现原理 热迁移代码分析 Nova 热迁移实现原理 向 libvirtd 发出 Live Migration 指令 ...
随机推荐
- Unrecognized Windows Sockets error: 0: JVM_Bind 异常怎么办
Unrecognized Windows Sockets error: 0: JVM_Bind 异常解决办法 java.net.SocketException: Unrecognized Window ...
- [Xamarin] 使用LayoutInflater.Inflate載入預先設計好的Layout並使用 (转帖)
開發的時候,一定會把一些東西設計成元件,並且可以多次使用,今天紀錄一篇比較簡單的方法,可以載入事先做好的Layout 並且給予事件 介紹一下範例: Main.axml: <?xml versio ...
- C#设计模式(19)——状态者模式(State Pattern)
一.引言 在上一篇文章介绍到可以使用状态者模式和观察者模式来解决中介者模式存在的问题,在本文中将首先通过一个银行账户的例子来解释状态者模式,通过这个例子使大家可以对状态者模式有一个清楚的认识,接着,再 ...
- 设计模式之美:Prototype(原型)
索引 别名 意图 结构 参与者 适用性 缺点 效果 相关模式 命名约定 实现 实现方式(一):使用一个原型管理器. 实现方式(二):使用浅拷贝实现克隆(Clone)操作. 实现方式(三):使用深拷贝实 ...
- [51单片机] SPI nRF24L01 无线简单程序 1
main.c #include <reg51.h> #include <api.h> #define uchar unsigned char /**************** ...
- 无线客户端框架设计(3):基类的设计(iOS篇)
本文代码:YoungHeart-Chapter-03.zip 没有基类的App都不是好App. 因为iOS使用的是mvc模式的开发模式,所以,业务逻辑基本都在每个页面相应的ViewController ...
- VS2013编译经常卡在正在从以下位置加载xxx.dll的符号
换了系统后,重新下载了一个vs2013 with update2安装,编译的时候总是卡在 正在从以下位置加载xxx.dll的符号 如图: 解决方法: 进入VS---工具---选项----调试----符 ...
- 虚拟机下samba简单安装配置
系统是Win7 虚拟机是CenterOS6.5 1.关闭防火墙以及关闭SELINUX的强制模式(重要): service iptables stop//关闭防火墙 setenforce 0 //关闭S ...
- spring源码 — 二、从容器中获取Bean
getBean 上一节中说明了容器的初始化,也就是把Bean的定义GenericBeanDefinition放到了容器中,但是并没有初始化这些Bean.那么Bean什么时候会初始化呢? 在程序第一个主 ...
- GO語言基礎教程:流程控制
在開始一個新的章節之前先來回顧上一篇文章的部份,首先我們來看這段代碼: package main import ( "fmt" ) func main(){ var x,y int ...