ovs流表
流表分为两大类:
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;
}