1. 程式人生 > >openVswitch(OVS)原始碼分析 datapath的flow操作

openVswitch(OVS)原始碼分析 datapath的flow操作

在這裡插入圖片描述

OVS中, 核心模組datapath負責報文的處理和轉發, 當它從一個接收埠(vport)收到報文後, 會提取報文中的欄位, 查詢流表(flow table)進行流匹配, 如果與其中一條flow匹配成功, 則執行flow中規定的動作(action), 如從另外某個vport轉發, 這個過程如上面的Fast Path所示; 如果沒有匹配上任何一條flow, 則將報文上送到使用者空間, 如上圖中的Slow Path所示. 本文關注Fast Pathflow相關操作

資料結構

flow相關的資料結構如下(只列出了其中比較重要的欄位),看程式碼的過程中可能需要來回檢視這張圖 [圖]

  • flow_table
    流表結構, 每個datapath都有一個流表
  • table_instance 流表例項, 其中的 buckets 用來存放具體的 flow 條目,儲存方式參見FlexArray
  • sw_flow flow條目, 其中 key 表示報文的特徵, 在進行匹配時, 便是從收到的報文中提取 key , 與flow 條目的 key 進行比較
  • sw_flow_key 報文特徵. 提取報文特徵時,會提取每一層的特徵.
  • mask_array 流表掩碼集合. 老版本OVS只支援exact flow, 即報文特徵必須和flow中描述完全相同才算匹配,而在較新的版本中,支援wildcarded flow. 可以為 flow
    中的特徵新增掩碼。最常見的例子,可以設定 flow條目中源 IPIP 掩碼, 只要進行匹配的報文 IP 在掩碼作用後的網段內,就認為是通過匹配的。
  • sw_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;
		}
	} 
}