首先每个packet都会被datapath/vport.c : line 481的ovs_vport_receive函数接受。这个是第一步。
int ovs_vport_receive(struct vport *vport, struct sk_buff *skb,
const struct ip_tunnel_info *tun_info)
{
struct sw_flow_key key; // packet包头信息的提取,便于后面进行流表的匹配
int error;
OVS_CB(skb)->input_vport = vport; // 在skb_buff数据结构里,有个struct cb 用来存放临时信息的。
OVS_CB(skb)->mru = 0;
OVS_CB(skb)->cutlen = 0;
if (unlikely(dev_net(skb->dev) != ovs_dp_get_net(vport->dp))) {
u32 mark;
mark = skb->mark;
skb_scrub_packet(skb, true);
skb->mark = mark;
tun_info = NULL;
}
ovs_skb_init_inner_protocol(skb);
skb_clear_ovs_gso_cb(skb);
/* Extract flow from 'skb' into 'key'. */
error = ovs_flow_key_extract(tun_info, skb, &key);
if (unlikely(error)) {
kfree_skb(skb);
return error;
}
ovs_dp_process_packet(skb, &key); // 处理包。。这是重点。
return 0;
}
然后由datapath/datapath.c中的ovs_dp_process_packet函数处理:
void ovs_dp_process_packet(struct sk_buff *skb, struct sw_flow_key *key)
{
const struct vport *p = OVS_CB(skb)->input_vport; // 获取此packet是从哪个vport进来的。
struct datapath *dp = p->dp; // 从struct vport 中获取vport所属的datapath
struct sw_flow *flow;
struct sw_flow_actions *sf_acts;
struct dp_stats_percpu *stats;
u64 *stats_counter;
u32 n_mask_hit;
stats = this_cpu_ptr(dp->stats_percpu);
/* Look up flow. */
// 在datapath中查找匹配的流表项。
flow = ovs_flow_tbl_lookup_stats(&dp->table, key, skb_get_hash(skb),
&n_mask_hit);
// 如果查找失败,没有匹配的流表,则进行upcall,上报到用户态。
if (unlikely(!flow)) {
struct dp_upcall_info upcall;
int error;
memset(&upcall, 0, sizeof(upcall));
upcall.cmd = OVS_PACKET_CMD_MISS;
upcall.portid = ovs_vport_find_upcall_portid(p, skb);
upcall.mru = OVS_CB(skb)->mru;
error = ovs_dp_upcall(dp, skb, key, &upcall, 0);
if (unlikely(error))
kfree_skb(skb);
else
consume_skb(skb);
stats_counter = &stats->n_missed;
goto out;
}
// 如果匹配到,则执行action
ovs_flow_stats_update(flow, key->tp.flags, skb);
sf_acts = rcu_dereference(flow->sf_acts);
ovs_execute_actions(dp, skb, sf_acts, key);
stats_counter = &stats->n_hit;
out:
/* Update datapath statistics. */
u64_stats_update_begin(&stats->syncp);
(*stats_counter)++;
stats->n_mask_hit += n_mask_hit;
u64_stats_update_end(&stats->syncp);
}
如果流表匹配成功,则执行流表相对应的action。函数是datapath/actions中的ovs_execute_actions函数对数据包执行action。代码如下:
int ovs_execute_actions(struct datapath *dp, struct sk_buff *skb,
const struct sw_flow_actions *acts,
struct sw_flow_key *key)
{
int err, level;
level = __this_cpu_inc_return(exec_actions_level);
if (unlikely(level > OVS_RECURSION_LIMIT)) {
net_crit_ratelimited("ovs: recursion limit reached on datapath %s, probable configuration error\n",
ovs_dp_name(dp));
kfree_skb(skb);
err = -ENETDOWN;
goto out;
}
err = do_execute_actions(dp, skb, key,
acts->actions, acts->actions_len);
if (level == 1)
process_deferred_actions(dp);
out:
__this_cpu_dec(exec_actions_level);
return err;
}
在上面的函数中有个do_execute_actions函数,里面是针对OVS_ACTION_ATTR_*来执行相对应的action函数。最终如果从某个端口转发出去,则会执行do_output函数,通过调用datapath/vport.c/ovs_vport_send函数进行端口转发。这就是一个数据包如果在datapath中匹配到流表的大致流程。
如果没有匹配成功,则执行upcall,函数是datapath/ovs_dp_upcall函数,代码如下:
int ovs_dp_upcall(struct datapath *dp, struct sk_buff *skb,
const struct sw_flow_key *key,
const struct dp_upcall_info *upcall_info,
uint32_t cutlen)
{
struct dp_stats_percpu *stats;
int err;
// 判断pid是否为0,这个是用来NetLink通讯使用的,为0表示传给内核空间
if (upcall_info->portid == 0) {
err = -ENOTCONN;
goto err;
}
if (!skb_is_gso(skb))
// 如果不是gso数据包,则直接发送到用户队列,这个在之前有说明
err = queue_userspace_packet(dp, skb, key, upcall_info, cutlen);
else
// 如果是gso数据包,则先处理gso协议,然后继续发送到用户队列
// 函数里最终会把数据包发送到上面的函数中 queue_userspace_packet
err = queue_gso_packets(dp, skb, key, upcall_info, cutlen);
if (err)
goto err;
return 0;
err:
stats = this_cpu_ptr(dp->stats_percpu);
u64_stats_update_begin(&stats->syncp);
stats->n_lost++;
u64_stats_update_end(&stats->syncp);
return err;
}
点击关注阿里云科技快讯,关注最新大新闻!还有机会获得精美礼品!!