流表分為兩大類:
1、 內核中flow table 也稱為fast path
2、找用戶態中flow table被稱為slow path
一個數據報文接收后,會經過多個流表,每個流表負責特定的功能,
ovs中的多級流表存放在用戶空間,內核態存放的是流表的緩存。
網卡收到報文時,Openvswitch.ko是這么處理數據報文的:
1、通過key查找內核中的flow table,得到action,然后執行action之后,直接發送這個包;
2、在內核無法查找到流表項的時候,通過netlink的方式發送到用戶空間,接着會去查找用戶態的流表。 真個過程是通過upcall來調用用戶態ovs-vswtichd中的flow table實現的
3、如果用戶態命中則將對應的信息丟給內核態進行緩存。用戶空間沒有查到,用戶態還要繼續將報文的信息丟給控制器,由控制器下發對應的規則
key的組成
MAC層的key :key->eth
網絡層的key:key->ip
傳輸層的key:key->tp
流表發送
流表下發一般是通過以下兩種方式:
controller通過openflow協議下發FLOW_MOD命令給ovs的Userspace流表。
ovs-ofctl通過openflow協議下發FLOW_MOD給ovs的Userspace流表。ovs-ofctl add-flow最終調用 ofctl_flow_mod(ctx->argc, ctx->argv, OFPFC_ADD);
ofctl_flow_mod
handle_openflow
handle_flow_mod
ovs-vsctl add-port br0 eth1實現
linux 中調用ip link set eht1 master br0會調用netdev_rx_handler_register注冊溝子函數,這樣當網卡收到報文時,會走鈎子函數
struct netdev_vport {
struct rcu_head rcu;
struct net_device *dev;
};
const struct vport_ops ovs_netdev_vport_ops = {
.create = netdev_create,
.send = netdev_send,
};
--datapath/vport-netdev.c
static struct vport *netdev_create(const struct vport_parms *parms)
{
struct vport *vport;
struct netdev_vport *netdev_vport;
vport = ovs_vport_alloc(sizeof(struct netdev_vport), &ovs_netdev_vport_ops, parms);
netdev_vport = netdev_vport_priv(vport);
netdev_vport->dev = dev_get_by_name(ovs_dp_get_net(vport->dp), parms->name);
//通過interface name比如eth0 得到eht0 net_device 結構體
err = netdev_rx_handler_register(netdev_vport->dev, netdev_frame_hook, vport);
//核心,收到packet后會調用 netdev_frame_hook處理;
dev_set_promiscuity(netdev_vport->dev, 1); //設置為混雜模式;
return vport;
}
netdev_frame_hook最終會調用ovs_dp_process_packet, ovs_dp_process_packet是一個非常重要的函數
用戶態處理流表
內核差找不到失敗時會通過upcall來調用用戶態ovs-vswtichd中的flow table,
用戶態找到找到rule之后,會通過handle_upcalls將flow rule添加到內核中的datapath
handle_upcalls
dpif_operate
dpif->dpif_class->operate(dpif, ops, chunk);
dpif_netlink_operate()會調用netlink修改內核中datapath的規則。
ovs system接口類型
當接口類型為system
時,vport->ops->send
函數為netdev_send
/*此函數即為OVS流表output action 發送數據包時的函數*/
static int
netdev_send(struct vport *vport, struct sk_buff *skb)
{
struct netdev_vport *netdev_vport = netdev_vport_priv(vport);
int mtu = netdev_vport->dev->mtu;
int len;
/*如果未開啟gso且數據包長度大於MTU,則釋放數據包*/
if (unlikely(packet_length(skb) > mtu && !skb_is_gso(skb))) {
net_warn_ratelimited("%s: dropped over-mtu packet: %d > %d\n",
netdev_vport->dev->name,
packet_length(skb), mtu);
goto drop;
}
/*設置skb->dev為output action網口*/
skb->dev = netdev_vport->dev;
len = skb->len;
/*最后調用dev_queue_xmit發送數據包*/
dev_queue_xmit(skb);
return len;
drop:
kfree_skb(skb);
return 0;
}