在上一节提到,Openvswitch的内核模块openvswitch.ko会在网卡上注册一个函数netdev_frame_hook,每当有网络包到达网卡的时候,这个函数就会被调用。

 

  1. static
    struct sk_buff *netdev_frame_hook(struct sk_buff *skb)
  2. {
  3.    if (unlikely(skb->pkt_type == PACKET_LOOPBACK))
  4.       return skb;
  5.  
  6.    port_receive(skb);
  7.    return NULL;
  8. }

 

调用port_receive即是调用netdev_port_receive

#define port_receive(skb) netdev_port_receive(skb, NULL)

 

  1. void netdev_port_receive(struct sk_buff *skb, struct ip_tunnel_info *tun_info)
  2. {
  3.    struct vport *vport;
  4.  
  5.    vport = ovs_netdev_get_vport(skb->dev);
  6. ……
  7.    skb_push(skb, ETH_HLEN);
  8.    ovs_skb_postpush_rcsum(skb, skb->data, ETH_HLEN);
  9.    ovs_vport_receive(vport, skb, tun_info);
  10.    return;
  11. error:
  12.    kfree_skb(skb);
  13. }

 

在函数int ovs_vport_receive(struct vport *vport, struct sk_buff *skb, const struct ip_tunnel_info *tun_info)实现如下

  1. int ovs_vport_receive(struct vport *vport, struct sk_buff *skb,
  2.             const
    struct ip_tunnel_info *tun_info)
  3. {
  4.    struct sw_flow_key key;
  5.    ......
  6.    /* Extract flow from 'skb' into 'key'. */
  7.    error = ovs_flow_key_extract(tun_info, skb, &key);
  8.    if (unlikely(error)) {
  9.       kfree_skb(skb);
  10.       return error;
  11.    }
  12.    ovs_dp_process_packet(skb, &key);
  13.    return 0;
  14. }

 

在这个函数里面,首先声明了变量struct sw_flow_key key;

如果我们看这个key的定义

  1. struct sw_flow_key {
  2.    u8 tun_opts[255];
  3.    u8 tun_opts_len;
  4.    struct ip_tunnel_key tun_key; /* Encapsulating tunnel key. */
  5.    struct {
  6.       u32 priority; /* Packet QoS priority. */
  7.       u32 skb_mark; /* SKB mark. */
  8.       u16 in_port; /* Input switch port (or DP_MAX_PORTS). */
  9.    } __packed phy; /* Safe when right after 'tun_key'. */
  10.    u32 ovs_flow_hash; /* Datapath computed hash value. */
  11.    u32 recirc_id; /* Recirculation ID. */
  12.    struct {
  13.       u8 src[ETH_ALEN]; /* Ethernet source address. */
  14.       u8 dst[ETH_ALEN]; /* Ethernet destination address. */
  15.       __be16 tci; /* 0 if no VLAN, VLAN_TAG_PRESENT set otherwise. */
  16.       __be16 type; /* Ethernet frame type. */
  17.    } eth;
  18.    union {
  19.       struct {
  20.          __be32 top_lse; /* top label stack entry */
  21.       } mpls;
  22.       struct {
  23.          u8 proto; /* IP protocol or lower 8 bits of ARP opcode. */
  24.          u8 tos; /* IP ToS. */
  25.          u8 ttl; /* IP TTL/hop limit. */
  26.          u8 frag; /* One of OVS_FRAG_TYPE_*. */
  27.       } ip;
  28.    };
  29.    struct {
  30.       __be16 src; /* TCP/UDP/SCTP source port. */
  31.       __be16 dst; /* TCP/UDP/SCTP destination port. */
  32.       __be16 flags; /* TCP flags. */
  33.    } tp;
  34.    union {
  35.       struct {
  36.          struct {
  37.             __be32 src; /* IP source address. */
  38.             __be32 dst; /* IP destination address. */
  39.          } addr;
  40.          struct {
  41.             u8 sha[ETH_ALEN]; /* ARP source hardware address. */
  42.             u8 tha[ETH_ALEN]; /* ARP target hardware address. */
  43.          } arp;
  44.       } ipv4;
  45.       struct {
  46.          struct {
  47.             struct
    in6_addr src; /* IPv6 source address. */
  48.             struct
    in6_addr dst; /* IPv6 destination address. */
  49.          } addr;
  50.          __be32 label; /* IPv6 flow label. */
  51.          struct {
  52.             struct
    in6_addr target; /* ND target address. */
  53.             u8 sll[ETH_ALEN]; /* ND source link layer address. */
  54.             u8 tll[ETH_ALEN]; /* ND target link layer address. */
  55.          } nd;
  56.       } ipv6;
  57.    };
  58.    struct {
  59.       /* Connection tracking fields. */
  60.       u16 zone;
  61.       u32 mark;
  62.       u8 state;
  63.       struct ovs_key_ct_labels labels;
  64.    } ct;
  65.  
  66. } __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了。

  1. void ovs_dp_process_packet(struct sk_buff *skb, struct sw_flow_key *key)
  2. {
  3.    const
    struct vport *p = OVS_CB(skb)->input_vport;
  4.    struct datapath *dp = p->dp;
  5.    struct sw_flow *flow;
  6.    struct sw_flow_actions *sf_acts;
  7.    struct dp_stats_percpu *stats;
  8.    u64 *stats_counter;
  9.    u32 n_mask_hit;
  10.  
  11.    stats = this_cpu_ptr(dp->stats_percpu);
  12.  
  13.    /* Look up flow. */
  14.    flow = ovs_flow_tbl_lookup_stats(&dp->table, key, skb_get_hash(skb),
  15.                 &n_mask_hit);
  16.    if (unlikely(!flow)) {
  17.       struct dp_upcall_info upcall;
  18.       int error;
  19.  
  20.       memset(&upcall, 0, sizeof(upcall));
  21.       upcall.cmd = OVS_PACKET_CMD_MISS;
  22.       upcall.portid = ovs_vport_find_upcall_portid(p, skb);
  23.       upcall.mru = OVS_CB(skb)->mru;
  24.       error = ovs_dp_upcall(dp, skb, key, &upcall);
  25.       if (unlikely(error))
  26.          kfree_skb(skb);
  27.       else
  28.          consume_skb(skb);
  29.       stats_counter = &stats->n_missed;
  30.       goto
    out;
  31.    }
  32.  
  33.    ovs_flow_stats_update(flow, key->tp.flags, skb);
  34.    sf_acts = rcu_dereference(flow->sf_acts);
  35.    ovs_execute_actions(dp, skb, sf_acts, key);
  36.  
  37.    stats_counter = &stats->n_hit;
  38.  
  39. out:
  40.    /* Update datapath statistics. */
  41.    u64_stats_update_begin(&stats->syncp);
  42.    (*stats_counter)++;
  43.    stats->n_mask_hit += n_mask_hit;
  44.    u64_stats_update_end(&stats->syncp);
  45. }

 

这个函数首先在内核里面的流表中查找符合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。

 

Slow Path & Fast Path

Slow Path:

当Datapath找不到flow rule对packet进行处理时

Vswitchd使用flow rule对packet进行处理。

 

Fast Path:

将slow path的flow rule放在内核态,对packet进行处理

 

Unknown Packet Processing

Datapath使用flow rule对packet进行处理,如果没有,则有vswitchd使用flow rule进行处理

 

 

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

 

Openvswitch原理与代码分析(4):网络包的处理过程的更多相关文章

  1. Openvswitch原理与代码分析(3): openvswitch内核模块的加载

      上一节我们讲了ovs-vswitchd,其中虚拟网桥初始化的时候,对调用内核模块来添加虚拟网卡.   我们从openvswitch内核模块的加载过程,来看这个过程.   在datapath/dat ...

  2. Openvswitch原理与代码分析(8): 修改Openvswitch代码添加自定义action

    有时候我们需要自定义一些自己的action,根据包头里面的信息,做一些自己的操作.   例如添加一个action名为handle_example   第一.修改ofp-actions.c文件   首先 ...

  3. Openvswitch原理与代码分析(1):总体架构

      一.Opevswitch总体架构   Openvswitch的架构网上有如下的图表示:       每个模块都有不同的功能 ovs-vswitchd 为主要模块,实现交换机的守护进程daemon ...

  4. Openvswitch原理与代码分析(5): 内核中的流表flow table操作

      当一个数据包到达网卡的时候,首先要经过内核Openvswitch.ko,流表Flow Table在内核中有一份,通过key查找内核中的flow table,即可以得到action,然后执行acti ...

  5. Openvswitch原理与代码分析(2): ovs-vswitchd的启动

    ovs-vswitchd.c的main函数最终会进入一个while循环,在这个无限循环中,里面最重要的两个函数是bridge_run()和netdev_run().     Openvswitch主要 ...

  6. Openvswitch原理与代码分析(7): 添加一条流表flow

    添加一个flow,调用的命令为 ovs-ofctl add-flow hello "hard_timeout=0 idle_timeout=0 priority=1 table=21 pkt ...

  7. Openvswitch原理与代码分析(6):用户态流表flow table的操作

    当内核无法查找到流表项的时候,则会通过upcall来调用用户态ovs-vswtichd中的flow table. 会调用ofproto-dpif-upcall.c中的udpif_upcall_hand ...

  8. 免费的Lucene 原理与代码分析完整版下载

    Lucene是一个基于Java的高效的全文检索库.那么什么是全文检索,为什么需要全文检索?目前人们生活中出现的数据总的来说分为两类:结构化数据和非结构化数据.很容易理解,结构化数据是有固定格式和结构的 ...

  9. OpenStack 虚拟机冷/热迁移的实现原理与代码分析

    目录 文章目录 目录 前文列表 冷迁移代码分析(基于 Newton) Nova 冷迁移实现原理 热迁移代码分析 Nova 热迁移实现原理 向 libvirtd 发出 Live Migration 指令 ...

随机推荐

  1. PHP程序设计经典300例

    不知道怎么转载,原文源自:http://bbs.php100.com/u-htm-uid-330857.html 来自:php100钟泽锋 第一例<?php $s_html="< ...

  2. Google Chrome: Make the Bookmarks Bar Display as Icons Only

    By reducing your bookmarks to show only the icons, you can access more of them from the Bookmarks ba ...

  3. GNU libc (Glibc) 2.18 发布

    标准C库Glibc发布2.18正式版.2013-08-12 上一个版本是2012-12-25的2.17. 详细改进:Version 2.18 * The following bugs are reso ...

  4. Web 开发中 20 个很有用的 CSS 库

    转自:http://www.oschina.net/translate/css-libraries-for-developers 在过去的几年中,CSS已经成为一大部分开发者和设计者的最爱,因为它提供 ...

  5. 12 个 Web 设计师必备的 Bootstrap 工具

    转自:http://www.oschina.net/translate/12-best-bootstrap-tools-for-web-designers Bootstrap是一个非常棒的前端网站开发 ...

  6. 跟我一起学WCF(12)——WCF中Rest服务入门

    一.引言 要将Rest与.NET Framework 3.0配合使用,还需要构建基础架构的一些部件.在.NET Framework 3.5中,WCF在System.ServiceModel.Web组件 ...

  7. C#设计模式总结

    一.引言 经过这段时间对设计模式的学习,自己的感触还是很多的,因为我现在在写代码的时候,经常会想想这里能不能用什么设计模式来进行重构.所以,学完设计模式之后,感觉它会慢慢地影响到你写代码的思维方式.这 ...

  8. JS代码风格指南

    一.基本格式 缩进 建议每级4个空格,可以给编辑器设置tab = 4个空格,自动转换 分号 不要省略分号,防止ASI(自动插入分号)错误 行宽 每行代码不超过80个字符,过长应该用操作符手动断行 断行 ...

  9. [Java Web] 1、Web开发初识——一大堆历史和技术名词

    LZ前言 LZ最近发现网络真是个神奇的东西,以前做的好玩的只能自娱自乐(或者说顾影自怜),现在只要发一个帖子,写一个博客,很快能引来一大群小伙伴的围观(有时候还能遇见几个大牛给个战略性的指导)...L ...

  10. EF Code First Migration总结

    开启Migration 1. 通过 Tools->Nuget Package Manager->Package Manager Console 打开Package Manager Cons ...