openVswitch(OVS)原始碼分析 datapath的flow操作
阿新 • • 發佈:2018-12-15
OVS
中, 核心模組datapath
負責報文的處理和轉發, 當它從一個接收埠(vport
)收到報文後, 會提取報文中的欄位, 查詢流表(flow table
)進行流匹配, 如果與其中一條flow
匹配成功, 則執行flow
中規定的動作(action
), 如從另外某個vport
轉發, 這個過程如上面的Fast Path所示; 如果沒有匹配上任何一條flow
, 則將報文上送到使用者空間, 如上圖中的Slow Path所示. 本文關注Fast Path中flow
相關操作
資料結構
flow
相關的資料結構如下(只列出了其中比較重要的欄位),看程式碼的過程中可能需要來回檢視這張圖
[圖]
flow_table
datapath
都有一個流表table_instance
流表例項, 其中的 buckets 用來存放具體的 flow 條目,儲存方式參見FlexArraysw_flow
flow條目, 其中 key 表示報文的特徵, 在進行匹配時, 便是從收到的報文中提取 key , 與flow 條目的 key 進行比較sw_flow_key
報文特徵. 提取報文特徵時,會提取每一層的特徵.mask_array
流表掩碼集合. 老版本OVS只支援exact flow
, 即報文特徵必須和flow中描述完全相同才算匹配,而在較新的版本中,支援wildcarded flow
. 可以為 flowsw_flow_mask
掩碼條目。其中 refcount 表明有多少個 flow 正在關聯它。sw_flow_match
在匹配過程中使用的結構
建立 flow
使用者使用 ovs-dpctl add-flow [DP] FLOW ACTIONS
命令可以建立新的 flow ,核心響應該命令的函式是ovs_flow_cmd_new
static int ovs_flow_cmd_new(struct sk_buff *skb, struct genl_info *info)
{
struct sw_flow_mask mask;
struct sw_flow_key key;
struct sw_flow_match match;
/* 建立新的flow條目 */
struct sw_flow* new_flow = ovs_flow_alloc();
/* 建立一個match結構,並將其與空的key和mask關聯起來 */
ovs_match_init(&match, &key, &mask);
/* 解析使用者下發的命令,使用者的命令以netlink attribute的形式存放, 有key和mask兩條屬性序列 */
error = ovs_nla_get_match(net, &match, a[OVS_FLOW_ATTR_KEY], a[OVS_FLOW_ATTR_MASK], log);
在 ovs_nla_get_match
中, 逐個解析attr
,填充sw_flow_match
結構
int ovs_nla_get_match(struct net *net, struct sw_flow_match *match, // 輸出引數
const struct nlattr *nla_key, // OVS_KEY_ATTR_* attribute sequence
const struct nlattr *nla_mask,// OVS_KEY_ATTR_* attribute sequence
bool log)
{
const struct nlattr *a[OVS_KEY_ATTR_MAX + 1];
u64 key_attrs = 0; /* KEY屬性BITMAP */
u64 mask_attrs = 0; /* MASK屬性BITMAP */
/* 解析nla_key, 將內層attr放到屬性陣列a, 點陣圖放到key_attrs */
err = parse_flow_nlattrs(nla_key, a, &key_attrs, log);
/* 利用點陣圖key_attrs, 屬性陣列a, 填入match->key */
err = ovs_key_from_nlattrs(net, match, key_attrs, a, false, log);
/* 解析nla_mask, 將內層attr放到a, 點陣圖放到mask_attrs */
err = parse_flow_mask_nlattrs(nla_mask, a, &mask_attrs, log);
/* 利用點陣圖mask_attrs, 屬性陣列a, 填入match->mask.key */
err = ovs_key_from_nlattrs(net, match, mask_attrs, a, true, log);
}
接續ovs_flow_cmd_new
static int ovs_flow_cmd_new(struct sk_buff *skb, struct genl_info *info)
{
/* 接上面 */
/* 將key & mask 拷貝到 new_flow->key
eg. 192.168.1.101 & 255.255.255.0 = 192.168.1.0
*/
ovs_flow_mask_key(&new_flow->key, &key, true, &mask);
dp = get_dp(net, ovs_header->dp_ifindex);
/* 將新建立的flow插入dp的流表 */
error = ovs_flow_tbl_insert(&dp->table, new_flow, &mask);
/* 組裝netlink訊息向用戶態回覆 */
}
進入ovs_flow_tbl_insert
int ovs_flow_tbl_insert(struct flow_table *table, struct sw_flow *flow,
const struct sw_flow_mask *mask)
{
/* 插入mask */
flow_mask_insert(table, flow, mask);
/* 插入key */
flow_key_insert(table, flow);
return 0;
}
查詢 flow
datapath
的一個vport
收到包後在進行報文處理
ovs_vport_receive
=> ovs_dp_process_packet
=> ovs_flow_tbl_lookup_stats
/*
@tbl 流表
@key 從skb報文中提取出的報文流特徵
@skb_hash 報文的hash值
@n_mask_hit 輸出引數, 表示嘗試匹配掩碼的次數
*/
struct sw_flow *ovs_flow_tbl_lookup_stats(struct flow_table *tbl,
const struct sw_flow_key *key,
u32 skb_hash,
u32 *n_mask_hit)
{
/* 先嚐試用mask cache進行查詢 */
/* 若沒有找到 則進行完整的查詢 */
flow = flow_lookup(tbl, tbl->ti, tbl->mask_array,key, n_mask_hit, &ce->mask_index);
return flow;
}
進入flow_lookup
static struct sw_flow *flow_lookup(struct flow_table *tbl,
struct table_instance *ti,
const struct mask_array *ma,
const struct sw_flow_key *key,
u32 *n_mask_hit,
u32 *index)
{
struct sw_flow_mask *mask;
struct sw_flow *flow;
int i;
for (i = 0; i < ma->max; i++) {
/* 遍歷每個flow掩碼 */
mask = rcu_dereference_ovsl(ma->masks[i]);
/* 比對每條flow */
flow = masked_flow_lookup(ti, key, mask, n_mask_hit);
if (flow) { /* Found */
*index = i;
return flow;
}
}
}