【博客454】OVS网桥如何接管数据

OVS网桥如何接管数据

ovs控制和数据分离

在这里插入图片描述

ovs如何接管host的流量

问题:ovs的datapath如何接收来自网卡的流量

解决:

Datapatch 加载到内核后,会在网卡上注册一个钩子函数,每当有网络包到达网卡时,这个函数就会被调用,将网络包开始层层拆包(MAC 层,IP 层,TCP 层等),然后与流表项匹配,如果找到匹配的流表项则根据既定策略来处理网络包(e.g. 修改 MAC,修改 IP,修改 TCP 端口,从哪个网卡发出去等等),再将网络包从网卡发出。

ovs如何接管attach到ovs网桥的设备的流量

问题:当一块设备(veth/tap等)attach到了ovs的网桥上时,这块设备的流量如何被ovs网桥接管

比如:ovs-vsctl add-port br0 eth1,此时eth1就被attach到ovs网桥上了,但是eth1的流量是如何被ovs网桥接管的呢,因为ovs网桥并不是基于jinux bridge实现

解决:通过为attach上来的设备注册ovs数据处理回调函数,当设备收到数据时会内核会调用ovs数据处理回调函数来处理,从而将数据传到ovs datapath进行处理

源码分析:
// attach一个设备上来时,其实也就是多了一个vport,这里是创建vport
static struct vport * netdev_create(const struct vport_parms *parms)
{
     struct vport *vport;
     struct netdev_vport *netdev_vport;
     int err;
     // 分配空间
     vport = ovs_vport_alloc(sizeof(struct netdev_vport), &ovs_netdev_vport_ops, parms);
       ...
       ...
     // 为attath上来的设备注册回调函数netdev_frame_hook
     err =   netdev_rx_handler_register(netdev_vport->dev, netdev_frame_hook, vport);
     // 设置设备会混杂模式
     dev_set_promiscuity(netdev_vport->dev, 1);
     // 设置datapath
     netdev_vport->dev->priv_flags |= IFF_OVS_DATAPATH;

     return vport;
}

// netdev_rx_handler_register函数解析:

原型:
netdev_rx_handler_register(struct net_device *dev,rx_handler_func_t *rx_handler, void *rx_handler_data)

ovs用例:
err =   netdev_rx_handler_register(netdev_vport->dev, netdev_frame_hook, vport);
为网络设备dev注册一个receive handler,rx_handler_data指向的是这个receive handler是用的内存区域
(这里存的是vport,里面有datapath的相关信息),这个handler 以后会被 __netif_receive_skb() 呼叫,
实际就是更新netdevice中的两个指针域:
rcu_assign_pointer(dev->rx_handler_data, rx_handler_data)
rcu_assign_pointer(dev->rx_handler, rx_handler) 。
  

// netdev_frame_hook钩子函数解析:
     static rx_handler_result_t netdev_frame_hook(struct sk_buff **pskb)
{
     struct sk_buff *skb = *pskb;
     struct vport *vport;

     if (unlikely(skb->pkt_type == PACKET_LOOPBACK))
          return RX_HANDLER_PASS;
          
     // 取出前面存入的那个vport结构体
     vport = ovs_netdev_get_vport(skb->dev);
     netdev_port_receive(vport, skb);
     // RX_HANDLER_CONSUMED:告诉内核这个包已经被我处理了,不用再走TCP/IP协议栈往上传送
     return RX_HANDLER_CONSUMED;
}

// netdev_port_receive接着调用ovs_vport_receive
// 接下来将收到的packet传给datapath处理
// 参数vport是收到这个包的vport(表征物理接口和datapath)
void ovs_vport_receive(struct vport *vport, struct sk_buff *skb)
{
     struct vport_percpu_stats *stats;

     stats = per_cpu_ptr(vport->percpu_stats, smp_processor_id());
     //每当收发数据的时候更新这个vport的状态(包数,字节数)
     u64_stats_update_begin(&stats->sync);
     stats->rx_packets++;
     stats->rx_bytes += skb->len;
     u64_stats_update_end(&stats->sync);
     ...
     ...
     ovs_dp_process_received_packet(vport, skb);
}

// 接下来我们的datapath模块来处理传上来的packet
void ovs_dp_process_received_packet(struct vport *p, struct sk_buff *skb)
{
     // 从vport中取出其所属的datapath对象
     struct datapath *dp = p->dp;
     struct sw_flow *flow;
     struct dp_stats_percpu *stats;
     u64 *stats_counter;
     int error;
   
     if (!OVS_CB(skb)->flow) {
          struct sw_flow_key key;
          int key_len;
 
          /* Extract flow from 'skb' into 'key'. */
          error = ovs_flow_extract(skb, p->port_no, &key, &key_len);
        
          /* Look up flow. */
          // 查流表
          flow = ovs_flow_tbl_lookup(rcu_dereference(dp->table),  &key, key_len);
          if (unlikely(!flow)) {
               struct dp_upcall_info upcall;

               upcall.cmd = OVS_PACKET_CMD_MISS;
               upcall.key = &key;
               upcall.userdata = NULL;
               upcall.portid = p->upcall_portid;
               // 流表无法命中,upcall到用户层的控制器去决策
               ovs_dp_upcall(dp, skb, &upcall);
               consume_skb(skb);
               stats_counter = &stats->n_missed;
               goto out;
          }

          OVS_CB(skb)->flow = flow;
     }

     stats_counter = &stats->n_hit;
     // 使用此流表
     ovs_flow_used(OVS_CB(skb)->flow, skb);
     // 执行流表的action
     ovs_execute_actions(dp, skb);

out:
     /* Update datapath statistics. */
     u64_stats_update_begin(&stats->sync);
     (*stats_counter)++;
     u64_stats_update_end(&stats->sync);
}

结构关联总结:skb->dev关联 vport 关联 datapath

一个包来了之后,执行netdev_frame_hook回调,此时通过skb->dev找到设备,
通过设备找到vport,vport = ovs_netdev_get_vport(skb->dev),然后根据vport找到datapath,
struct datapath *dp = p->dp;最后执行ovs_execute_actions(dp, skb);

当一个设备attatch到ovs网桥会发生什么

Normal Port:

用户可以把操作系统中已有的网卡添加到Open vSwitch上,Open vSwitch会自动生成一个同名的Port(Normal类型)来处理这张网卡进和出的数据报文。

Internal Port:

当Port的类型是时,OVS会自动创建一个虚拟网卡(Interface),此端口收到的数据报文都会转发给这块网卡,从这块网卡发出的数据报文也会通过Port交给OVS处理

最后关于ovs-system网卡

运行ovs后,Linux系统中会多出一块状态是down掉的网卡ovs-system:

ifconfig -a  
...
ovs-system Link encap:Ethernet  HWaddr FA:4F:0D:E4:DA:11  
          BROADCAST MULTICAST  MTU:1500  Metric:1
          RX packets:0 errors:0 dropped:0 overruns:0 frame:0
          TX packets:6 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:0 
          RX bytes:0 (0.0 b)  TX bytes:468 (468.0 b)
...

It’s not used for anything, and not useful for anything. It is mostly historical. A Linux kernel Open vSwitch datapath needs to have a port named the same as the datapath. Open vSwitch needs one datapath, and it
names it ovs-system, so there’s a port with the same name, even though it doesn’t get used for anything.

翻译:

历史原因,Open vSwitch在内核中使用的是datapath,而每个datapath必须有一个端口与之对应,ovs使用的datapath名字是ovs-system,所以系统中就冒出来一个ovs-system的网卡,没有任何用途,忽略它既可。你也根本删不掉它的。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值