💎一站式轻松地调用各大LLM模型接口,支持GPT4、智谱、星火、月之暗面及文生图 广告
这来主要看看ovs从网络接口收到packet后的一系列操作。 在内核模块启动的时候会初始化vport子系统(ovs_vport_init),各种vport类型,那么什么时候会调用相应的函数与实际网络设备建立联系?其实当我们在为网桥增设端口的时候,就会进入ovs_netdev_vport_ops中的create方法,进而 注册网络设备。 看[ovs-vsctl add-port br0 eth1 实际做了什么?](http://blog.csdn.net/vonzhoufz/article/details/19981911) ~~~ struct netdev_vport {     struct rcu_head rcu;    struct net_device *dev; }; const struct vport_ops ovs_netdev_vport_ops = {     .type          = OVS_VPORT_TYPE_NETDEV,     .flags          = VPORT_F_REQUIRED,     .init          = netdev_init,   //之后的内核版本,这里直接return 0;     .exit          = netdev_exit,     .create          = netdev_create,     .destroy     = netdev_destroy,     .set_addr     = ovs_netdev_set_addr,     .get_name     = ovs_netdev_get_name,     .get_addr     = ovs_netdev_get_addr,     .get_kobj     = ovs_netdev_get_kobj,     .get_dev_flags     = ovs_netdev_get_dev_flags,     .is_running     = ovs_netdev_is_running,     .get_operstate     = ovs_netdev_get_operstate,     .get_ifindex     = ovs_netdev_get_ifindex,     .get_mtu     = ovs_netdev_get_mtu,     .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;     int err;     vport = ovs_vport_alloc(sizeof(struct netdev_vport),   &ovs_netdev_vport_ops, parms);    //有ovs_netdev_vport_ops和vport parameters 来构造初始化一个vport;    netdev_vport = netdev_vport_priv(vport);    //获得vport私有区域??     netdev_vport->dev = dev_get_by_name(ovs_dp_get_net(vport->dp), parms->name);    [//通过interface]() name比如eth0 得到具体具体的net_device 结构体,然后下面注册 rx_handler;     if (netdev_vport->dev->flags & IFF_LOOPBACK ||  netdev_vport->dev->type != ARPHRD_ETHER ||         ovs_is_internal_dev(netdev_vport->dev)) {          err = -EINVAL;          goto error_put;     }    //不是环回接口;而且底层链路层是以太网;netdev->netdev_ops == &internal_dev_netdev_ops 显然为false     err = netdev_rx_handler_register(netdev_vport->dev, netdev_frame_hook, vport);     [//核心,收到packet后会调用]() netdev_frame_hook处理;     dev_set_promiscuity(netdev_vport->dev, 1);  //设置为混杂模式;     netdev_vport->dev->priv_flags |= IFF_OVS_DATAPATH;  //设置netdevice私有区域的标识;     return vport; } --datapath/vport.h 创建vport所需要的参数结构 struct vport_parms {     const char *name;         enum ovs_vport_type type;     struct nlattr *options;   [//利于必要的时候从 netlink msg通过属性OVS_VPORT_ATTR_OPTIONS取得 ]()     /* For ovs_vport_alloc(). */     struct datapath *dp;  // 这个vport所从属的datapath     u16 port_no;   //端口号     u32 upcall_portid; // 如果从这个vport收到的包 在flow table没有得到匹配就会从 netlink端口upcall_portid 发送到用户空间; }; ~~~ 函数netdev_rx_handler_register(struct net_device *dev,rx_handler_func_t *rx_handler, void *rx_handler_data)定义在 linux/netdevice.h 实现在 net/core/dev.c 中,为网络设备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) 。   netif_receive_skb(struct sk_buff *skb)从网络中接收数据,它是主要的接收数据处理函数,总是成功,这个buffer在拥塞处理或协议层的时候可能被丢弃。这个函数只能从软中断环境(softirq context)中调用,并且中断允许。返回值 NET_RX_SUCCESS表示没有拥塞,NET_RX_DROP包丢弃。(实现细节暂时没看) 接下来进入我们的钩子函数 netdev_frame_hook(datapath/vport-netdev.c)这里主要看内核版本>=2.6.39的实现。 ~~~    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 = ovs_netdev_get_vport(skb->dev);    //提携出前面存入的那个vport结构体,vport-netdev.c line 401;     netdev_port_receive(vport, skb);     return RX_HANDLER_CONSUMED; } ~~~ 函数 netdev_port_receive 首先得到一个packet的拷贝,否则会损坏先于我们而来的packet使用者 (e.g. tcpdump via AF_PACKET),我们之后没有这种情况,因为会告知handle_bridge()我们获得了那个packet 。 skb_push是将skb的数据区向后移动*_HLEN长度,为了存入帧头;而skb_put是扩展数据区后面为存数据memcpy做准备。 ~~~ static void netdev_port_receive(struct vport *vport, struct sk_buff *skb) {    skb = skb_share_check(skb, GFP_ATOMIC);     //GFP_ATOMIC用于在中断处理例程或其它运行于进程上下文之外的地方分配内存,不会休眠(LDD214)。     skb_push(skb, ETH_HLEN);    //疑问:刚接收到的packet应该是有 ether header的,为何还执行这个操作??     if (unlikely(compute_ip_summed(skb, false)))          goto error;     vlan_copy_skb_tci(skb); // <2.6.27版本的时候才需要VLAN field;     ovs_vport_receive(vport, skb);     return;     ................. } ~~~ 接下来将收到的packet传给datapath处理(datapath/vport.c),参数vport是收到这个包的vport(表征物理接口和datapath),skb是收到的数据。读的时候要用rcu_read_lock,这个包不能被共享而且skb->data 应该指向以太网头域,而且调用者要确保已经执行过 compute_ip_summed() 初始化那些校验和域。 ~~~ 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的状态(包数,字节数),struct vport_percpu_stats定义在vport.h中。     u64_stats_update_begin(&stats->sync);     stats->rx_packets++;     stats->rx_bytes += skb->len;     u64_stats_update_end(&stats->sync);     if (!(vport->ops->flags & VPORT_F_FLOW))          OVS_CB(skb)->flow = NULL;    [//vport->ops->flags]() (VPORT_F_*)影响的是这个通用vport层如何处理这个packet;     if (!(vport->ops->flags & VPORT_F_TUN_ID))          OVS_CB(skb)->tun_key = NULL;     ovs_dp_process_received_packet(vport, skb); } ~~~ 接下来我们的datapath模块来处理传上来的packet(datapath/datapath.c),首先我们要判断如果存在skb->cb域中的OVS  data sw_flow 是空的话,就要从packet中提携构造;函数 ovs_flow_extract 从以太网帧中构造 sw_flow_key,为接下来的流表查询做准备;流表结构struct flow_table定义在flow.h中,流表实在ovs_flow_init的时候初始化的?? 如果没有match成功,就会upcall递交给用户空间处理(见vswitchd模块分析),匹配成功的话执行flow action(接下来就是openflow相关)。 ~~~ void ovs_dp_process_received_packet(struct vport *p, struct sk_buff *skb) {     struct datapath *dp = p->dp;     struct sw_flow *flow;     struct dp_stats_percpu *stats;     u64 *stats_counter;     int error;     stats = per_cpu_ptr(dp->stats_percpu, smp_processor_id());     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;               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);     ovs_execute_actions(dp, skb); out:     /* Update datapath statistics. */     u64_stats_update_begin(&stats->sync);     (*stats_counter)++;     u64_stats_update_end(&stats->sync); } ~~~ 转载地址:http://blog.csdn.net/vonzhoufz/article/details/19840683