sw_flow_key相当于流表的索引,对其哈希后可以找到对应的flow entry,从而找到action list。构建key是由 ovs_flow_extract函数完成的,它从以太网帧中提携相关信息构造sw_flow_key ,参数中skb->data指向的是以太帧头ether header,in_port是收到skb的端口号,最后俩是值-结果参数。
int ovs_flow_extract(struct sk_buff *skb, u16 in_port, struct sw_flow_key *key, int *key_lenp)
{
int error = 0;
int key_len = SW_FLOW_KEY_OFFSET(eth);
struct ethhdr *eth;
//key_len 存在的意义是每次填充一个域的时候才增加实际长度(offset+fieldsize);
memset(key, 0, sizeof(*key));
key->phy.priority = skb->priority; //QoS相关的;
if (OVS_CB(skb)->tun_key) //如果skb->cb 域中有tunnel的信息;
memcpy(&key->phy.tun.tun_key, OVS_CB(skb)->tun_key, sizeof(key->phy.tun.tun_key));
key->phy.in_port = in_port;
key->phy.skb_mark = skb_get_mark(skb); //这是防火墙(NetFilter)功能专用参数;
skb_reset_mac_header(skb);
//解析的同时也更新skb里面的头指针 skb->mac_header = skb->data;
//链路层,这里要保证至少有14B的ether header;
eth = eth_hdr(skb); //得到链路层协议头;
memcpy(key->eth.src, eth->h_source, ETH_ALEN);
memcpy(key->eth.dst, eth->h_dest, ETH_ALEN);
__skb_pull(skb, 2 * ETH_ALEN);
//向前推进skb->data 接下来的2B是以太类型;
if (vlan_tx_tag_present(skb))
key->eth.tci = htons(vlan_get_tci(skb));
else if (eth->h_proto == htons(ETH_P_8021Q))
if (unlikely(parse_vlan(skb, key)))
return -ENOMEM;
key->eth.type = parse_ethertype(skb); //从SNAP OUI后的16bit得到以太类型;
skb_reset_network_header(skb);
//skb->nh.raw = skb->data;在skb中代表L2,L3,L4的字段mac,nh,h都实现为联合,每个联合中的raw成员用于初始化;
__skb_push(skb, skb->data - skb_mac_header(skb));
//和前面的__skb_pull相反,缓存头增加12B的空间(skb->data - skb_mac_header(skb)=12),有啥作用??
//网络层-->
if (key->eth.type == htons(ETH_P_IP)) {
struct iphdr *nh;
__be16 offset;
key_len = SW_FLOW_KEY_OFFSET(ipv4.addr);
error = check_iphdr(skb);//在里面同时会设置 transport header;
nh = ip_hdr(skb);
//得到IP头结构体(struct iphdr *)skb_network_header(skb);iphdr具体细节看A1;