首页 > 编程知识 正文

ovs流表匹配顺序

时间:2023-05-03 12:35:39 阅读:182111 作者:1633

————————————————

版权:本文是从cnblogs lhddy的普通云计算中转用的

原文链接: https://www.cn blogs.com/pop super 1982/p/5870181.html 3359 www.cn blogs.com/pop super 1982/p/58886819.html

每当网卡注册netdev_frame_hook函数且网络包到达网卡时,都会调用openv交换机的内核模块openvswitch.ko。

staticstructsk _ buff * netdev _ frame _ hook (structsk _ buff * skb ) if(unlikely ) sk B- PKT _ type==packet _ lly 返回空值; //上面的port_receive (是netdev_port_receive )函数#defineport_receive ) skb ) netdev_port_receive )和vort=ovs _ netdev _ get _ vport (sk B- dev ); ……skb_push(skb,ETH_HLEN ); OVS_skb_postpush_RCsum(skb,skb-data,ETH_HLEN ); OVS_vport_receive(vport,skb,tun_info ); 返回; error:kfree_skb(skb; }函数into vs _ vport _ receive (struct vport,struct sk_buff skb,conststructip _ tunnel _ info * tun _ info )执行如下

into vs _ vport _ receive (struct vport * vport,struct sk_buff *skb, conststructip _ tunnel _ info * tun _ info ) ) ) ststructip _ tu info . * extractflowfrom ' skb ' into ' key '.* ) unlikely (error ) (kfree_skb ); 返回错误; }OVS_DP_process_packet(skb,key ); 返回0; 在名为ovs_vport_receive ()的函数中,首先将变量struct sw_flow_key key;

看了这个key的定义,就知道这个key里面是大杂烩,数据包里的大部分都能作为key找流量表:

struct SW _ flow _ key { u8tun _ opts [ 255 ]; u8 tun_opts_len; struct ip_tunnel_key tun_key;/* encapsulatingtunnelkey.*/struct { u 32 priority;/* packetqospriority.*/u 32 skb _ hs dxg; /* SKB hsdxg. */u16 in_port;/*输入交换机端口(ordp _ max _ ports ).*/} __packed phy;/* safewhenrightafter ' tun _ key '.*/u32o vs _ flow _ hash;/* datapathcomputedhashvalue.*/u 32 re CIRC _ id;/* recirculation id.*/struct { u8s RC [ eth _ alen ];/* ethernetsourceaddress.*/u8dst [ eth _ alen ];/* ethernetdestinationaddress.*//_ be16t ci; /* 0 if no VLAN,VLAN _ tag _ presentsetotherwise.*//_ be16 type;/*以太网帧类型. */} eth; union { struct { __be32 top_lse; /* top label stack entry */} mpls; struct { u8 proto; /* IP protoco

l or lower 8 bits of ARP opcode. */ u8 tos; /* IP ToS. */ u8 ttl; /* IP TTL/hop limit. */ u8 frag; /* One of OVS_FRAG_TYPE_*. */ } ip; }; struct { __be16 src; /* TCP/UDP/SCTP source port. */ __be16 dst; /* TCP/UDP/SCTP destination port. */ __be16 flags; /* TCP flags. */ } tp; union { struct { struct { __be32 src; /* IP source address. */ __be32 dst; /* IP destination address. */ } addr; struct { u8 sha[ETH_ALEN]; /* ARP source hardware address. */ u8 tha[ETH_ALEN]; /* ARP target hardware address. */ } arp; } ipv4; struct { struct { struct in6_addr src; /* IPv6 source address. */ struct in6_addr dst; /* IPv6 destination address. */ } addr; __be32 label; /* IPv6 flow label. */ struct { struct in6_addr target; /* ND target address. */ u8 sll[ETH_ALEN]; /* ND source link layer address. */ u8 tll[ETH_ALEN]; /* ND target link layer address. */ } nd; } ipv6; }; struct { /* Connection tracking fields. */ u16 zone; u32 hsdxg; u8 state; struct ovs_key_ct_labels labels; } ct; } __aligned(BITS_PER_LONG/8); /* Ensure that we can do comparisons as longs. */

可见这个key里面是一个大杂烩,数据包里面的几乎任何部分都可以作为key来查找flow表

tunnel可以作为key在物理层,in_port即包进入的网口的ID在MAC层,源和目的MAC地址在IP层,源和目的IP地址在传输层,源和目的端口号IPV6

所以,要在内核态匹配流表,首先需要调用ovs_flow_key_extract,从包的正文中提取key的值。

接下来

调用ovs_dp_process_packet()

函数ovs_dp_process_packet()首先在内核里面的流表中查找符合key的flow,也即ovs_flow_tbl_lookup_stats,如果找到了,很好说明用户态的流表已经放入内核,则走fast path就可了。于是直接调用ovs_execute_actions,执行这个key对应的action。

如果不能找到,则只好调用ovs_dp_upcall,让用户态去查找流表。会调用static int queue_userspace_packet(struct datapath dp, struct sk_buff skb, const struct sw_flow_key key, const struct dp_upcall_info upcall_info)

它会调用err = genlmsg_unicast(ovs_dp_get_net(dp), user_skb, upcall_info->portid);通过netlink将消息发送给用户态。在用户态,有线程监听消息,一旦有消息,则触发udpif_upcall_handler。

从Device接收Packet交给事先注册的event handler进行处理;接收Packet后识别是否是unknown packet,是则交由upcall处理;vswitchd对unknown packet找到flow rule进行处理;将Flow rule发送给datapath.

也就是说OVS处理数据包首先会看有没有事先订阅的事件,如果有直接转交给该对应的处理函数,然后匹配流表,最后按照传统网络协议栈进行处理。

void ovs_dp_process_packet(struct sk_buff *skb, struct sw_flow_key *key){ const struct vport *p = OVS_CB(skb)->input_vport; struct datapath *dp = p->dp; 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. */ flow = ovs_flow_tbl_lookup_stats(&dp->table, key, skb_get_hash(skb), &n_mask_hit); 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); if (unlikely(error)) kfree_skb(skb); else consume_skb(skb); stats_counter = &stats->n_missed; goto out; } 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);}

————————————————
版权:本文转自cnblogs lhddy的通俗云计算
原文链接:https://www.cnblogs.com/popsuper1982/p/5870181.html
发布于:2016-9-13

image.png

函数ovs_flow_tbl_lookup()利用OVS流表精确匹配TCP/IP报头五元组,并执行action do_output或consume_skb

OVS定义的流表结构体struct flow_table; 流表索引key: struct sw_flow_key; 掩码: struct sw_flow_mask 流表元素: struct sw_flow 执行体struct sw_flow_actions 在内核中,flow table的数据结构如下图所示:

每个虚拟交换机对应一个datapath,每个datapath有一个flow table,每个flow table分成N个桶bucket,根据key进行哈希,不同的key分布在不同的桶里面。

每个桶的大小是一个内存页的大小,在内存页的头部保存了保存了多少个元素,每个元素的大小。每个元素都是sw_flow,里面有key,也有action。

OVS流表结构示意图 https://www.cnblogs.com/popsuper1982/p/5886819.html 调用ovs_flow_key_extract()取出TCP/IP报头5元组作为key调用ovs_dp_process_packet() ovs_flow_tbl_lookup_stats()查询流表 ovs_execute_actions() 执行ACTION

版权声明:该文观点仅代表作者本人。处理文章:请发送邮件至 三1五14八八95#扣扣.com 举报,一经查实,本站将立刻删除。