首先每個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;
}