OVS datapath流表结构及匹配过程

2023-11-08

datapath流表的查找函数是ovs_flow_tbl_lookup_stats,在此之前,先看下datapath组织流表的方式。
最新2.6的ovs流表,已经不是最早单纯的精确匹配了,而是一种精确匹配+带掩码匹配合并在一起的方式,叫做megaflow,目的是减少datapath里精确流表的条目数。但在我看来,这种方式只是在yy,在大规模生产环境下,不会因为用了megaflow精确流表就会变得可控,反而增加了datapath匹配时的复杂度,降低了性能,使用精确匹配还是掩码匹配还是hybrid的方式,应该留给运维人员去选择。

struct flow_table {
        struct table_instance __rcu *ti;
        struct table_instance __rcu *ufid_ti;
        struct mask_cache_entry __percpu *mask_cache;
        struct mask_array __rcu *mask_array;
        unsigned long last_rehash;
        unsigned int count;
        unsigned int ufid_count;
};

struct table_instance {
        struct flex_array *buckets;
        unsigned int n_buckets;
        struct rcu_head rcu;
        int node_ver;
        u32 hash_seed;
        bool keep_flows;
};

struct table_instance *ti 是传统的桶+链表的哈希表结构,其中桶基于struct flex_array的结构体,该结构体是linux内核定义的,本文先不去分析

/*
 * This is meant to replace cases where an array-like
 * structure has gotten too big to fit into kmalloc()
 * and the developer is getting tempted to use
 * vmalloc().
 */

struct flex_array {
        union {
                struct {
                        int element_size;
                        int total_nr_elements;
                        int elems_per_part;
                        struct reciprocal_value reciprocal_elems;
                        struct flex_array_part *parts[];
                };
                /*
                 * This little trick makes sure that
                 * sizeof(flex_array) == PAGE_SIZE
                 */
                char padding[FLEX_ARRAY_BASE_SIZE];
        };
};

除了struct table_instance之外,struct flow_table还增加了缓存机制,这张表是一张精确匹配和掩码匹配的合体,struct mask_array *mask_array是一个掩码结构体struct sw_flow_mask的数组,可以看到struct sw_flow_mask分为两部分:掩码的范围struct sw_flow_key_range以及流的keystruct sw_flow_key

struct sw_flow_key {
        u8 tun_opts[255];
        u8 tun_opts_len;
        struct ip_tunnel_key tun_key;  /* Encapsulating tunnel key. */
        struct {
                u32     priority;       /* Packet QoS priority. */
                u32     skb_mark;       /* SKB mark. */
                u16     in_port;        /* Input switch port (or DP_MAX_PORTS). */
        } __packed phy; /* Safe when right after 'tun_key'. */
        u8 tun_proto;                   /* Protocol of encapsulating tunnel. */
        u32 ovs_flow_hash;              /* Datapath computed hash value.  */
        u32 recirc_id;                  /* Recirculation ID.  */
        struct {
                u8     src[ETH_ALEN];   /* Ethernet source address. */
                u8     dst[ETH_ALEN];   /* Ethernet destination address. */
                __be16 tci;             /* 0 if no VLAN, VLAN_TAG_PRESENT set otherwise. */
                __be16 type;            /* Ethernet frame type. */
        } eth;
        union {
                struct {
                        __be32 top_lse; /* top label stack entry */
                } mpls;
                struct {
                        u8     proto;   /* IP protocol or lower 8 bits of ARP opcode. */
                        u8     tos;         /* IP ToS. */
                        u8     ttl;         /* IP TTL/hop limit. */
                        u8     frag;    /* One of OVS_FRAG_TYPE_*. */
                } ip;
        };
        struct {
                __be16 src;             /* TCP/UDP/SCTP source port. */
                __be16 dst;             /* TCP/UDP/SCTP destination port. */
                __be16 flags;           /* TCP flags. */
        } tp;
        union {
                struct {
                        struct {
                                __be32 src;     /* IP source address. */
                                __be32 dst;     /* IP destination address. */
                        } addr;
                        struct {
                                u8 sha[ETH_ALEN];       /* ARP source hardware address. */
                                u8 tha[ETH_ALEN];       /* ARP target hardware address. */
                        } arp;
                } ipv4;
                struct {
                        struct {
                                struct in6_addr src;    /* IPv6 source address. */
                                struct in6_addr dst;    /* IPv6 destination address. */
                        } addr;
                        __be32 label;                   /* IPv6 flow label. */
                        struct {
                                struct in6_addr target; /* ND target address. */
                                u8 sll[ETH_ALEN];       /* ND source link layer address. */
                                u8 tll[ETH_ALEN];       /* ND target link layer address. */
                        } nd;
                } ipv6;
        };
        struct {
                /* Connection tracking fields. */
                u16 zone;
                u32 mark;
                u8 state;
                struct ovs_key_ct_labels labels;
        } ct;

} __aligned(BITS_PER_LONG/8); /* Ensure that we can do comparisons as longs. */

struct sw_flow_key_range {
        unsigned short int start;
        unsigned short int end;
};

struct mask_array {
        struct rcu_head rcu;
        int count, max;
        struct sw_flow_mask __rcu *masks[];
};

struct mask_cache_entry结构体包含了hash值和mask_array数组的索引的key pair,在struct flow_table中,struct mask_cache_entry __percpu *mask_cache实际是一个cuckoo hash表,对应有256条表项,总共有4组key用于计算哈希值(这里用4个不同的key值,同一个哈希函数来计算哈希值,而不是像cuckoo哈希要求的那样提供四个不同的哈希函数,但效果基本是一样的,而对于实际的生产环境,需要更大的cuckoo哈希表项)。在cuckoo哈希查找时,每次key值都会右移8个bit,基于key计算cuckoo hash的索引,即mask_cache的索引,并调用flow_lookup查看flow是否在table_instace里,如果cache未命中,则做一次全量查找,即遍历mask_array查找flow是否在表项中(代价非常大)。

/*
 * mask_cache maps flow to probable mask. This cache is not tightly
 * coupled cache, It means updates to  mask list can result in inconsistent
 * cache entry in mask cache.
 * This is per cpu cache and is divided in MC_HASH_SEGS segments.
 * In case of a hash collision the entry is hashed in next segment.
 */
struct sw_flow *ovs_flow_tbl_lookup_stats(struct flow_table *tbl,
                                          const struct sw_flow_key *key,
                                          u32 skb_hash,
                                          u32 *n_mask_hit)
{
        struct mask_array *ma = rcu_dereference(tbl->mask_array);
        struct table_instance *ti = rcu_dereference(tbl->ti);
        struct mask_cache_entry *entries, *ce;
        struct sw_flow *flow;
        u32 hash;
        int seg;

        *n_mask_hit = 0;
        if (unlikely(!skb_hash)) {
                u32 mask_index = 0;
                return flow_lookup(tbl, ti, ma, key, n_mask_hit, &mask_index);
        }

        /* Pre and post recirulation flows usually have the same skb_hash
         * value. To avoid hash collisions, rehash the 'skb_hash' with
         * 'recirc_id'.  */
        if (key->recirc_id)
                skb_hash = jhash_1word(skb_hash, key->recirc_id);

        ce = NULL;
        hash = skb_hash;
        entries = this_cpu_ptr(tbl->mask_cache);

        /* Find the cache entry 'ce' to operate on. */
        for (seg = 0; seg < MC_HASH_SEGS; seg++) {
                int index = hash & (MC_HASH_ENTRIES - 1);
                struct mask_cache_entry *e;

                e = &entries[index];
                if (e->skb_hash == skb_hash) {
                        flow = flow_lookup(tbl, ti, ma, key, n_mask_hit,
                                           &e->mask_index);
                        if (!flow)
                                e->skb_hash = 0;
                        return flow;
                }

                if (!ce || e->skb_hash < ce->skb_hash)
                        ce = e;  /* A better replacement cache candidate. */

                hash >>= MC_HASH_SHIFT;
        }

        /* Cache miss, do full lookup. */
        flow = flow_lookup(tbl, ti, ma, key, n_mask_hit, &ce->mask_index);
        if (flow)
                ce->skb_hash = skb_hash;

        return flow;
}

flow_lookup会基于mask_cache_entry缓存的mask_array表项或者通过全量遍历mask_array的表项来查找流

/* Flow lookup does full lookup on flow table. It starts with
 * mask from index passed in *index.
 */
static struct sw_flow *flow_lookup(struct flow_table *tbl,
                                   struct table_instance *ti,
                                   const struct mask_array *ma,
                                   const struct sw_flow_key *key,
                                   u32 *n_mask_hit,
                                   u32 *index)
{
        struct sw_flow_mask *mask;
        struct sw_flow *flow;
        int i;

        if (*index < ma->max) {
                mask = rcu_dereference_ovsl(ma->masks[*index]);
                if (mask) {
                        flow = masked_flow_lookup(ti, key, mask, n_mask_hit);
                        if (flow)
                                return flow;
                }
        }
        for (i = 0; i < ma->max; i++)  {

                if (i == *index)
                        continue;

                mask = rcu_dereference_ovsl(ma->masks[i]);
                if (!mask)
                        continue;

                flow = masked_flow_lookup(ti, key, mask, n_mask_hit);
                if (flow) { /* Found */
                        *index = i;
                        return flow;
                }
        }

        return NULL;
}

最后masked_flow_lookup基于掩码查找流,除了哈希值要相同,流的key要相同,掩码也必须相同

static struct sw_flow *masked_flow_lookup(struct table_instance *ti,
                                          const struct sw_flow_key *unmasked,
                                          const struct sw_flow_mask *mask,
                                          u32 *n_mask_hit)
{
        struct sw_flow *flow;
        struct hlist_head *head;
        u32 hash;
        struct sw_flow_key masked_key;

        ovs_flow_mask_key(&masked_key, unmasked, false, mask);
        hash = flow_hash(&masked_key, &mask->range);
        head = find_bucket(ti, hash);
        (*n_mask_hit)++;
        hlist_for_each_entry_rcu(flow, head, flow_table.node[ti->node_ver]) {
                if (flow->mask == mask && flow->flow_table.hash == hash &&
                    flow_cmp_masked_key(flow, &masked_key, &mask->range))
                        return flow;
        }
        return NULL;
}

下图是从http://vinllen.com/中找到的,可以参考下整个流匹配的流程
附上一张从别处看到的流匹配流程图

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

OVS datapath流表结构及匹配过程 的相关文章

  • 【Linux入门教程】1 简介、文件管理、目录

    Linux入门教程 Linux是一个多用户多任务操作系统 不但被很多开发者用作个人操作系统 还大量运行在Web服务器上 该教程将带你快速了解Linux系统 包括基本概念 Linux命令 Shell脚本 常用工具等 该教程可以让你快速入门快速
  • 渗透测试之三:几款小工具

    1 BurpSuite 参考地址 https www cnblogs com qmfsun p 5458707 html https www jianshu com p 50e496737c80 BurpSuite 是一款使用Java编写的
  • @SuppressWarnings注解详解

    SupperessWarnings 一 简介 java lang SupperessWarnings是J2SE5 0标准的Annotation之一 可以标注在类 字段 方法 参数 构造方法 局部变量上 二 作用 该注解的作用是给编译器一条指
  • 【专题5: 硬件设计】 之 【61.案例四:简易空气净化器,使用硬件产生PWM波并对马达调速】

    嵌入式工程师成长之路 系列文章 总目录 系列文章总目录 希望本是无所谓有 无所谓无的 这正如脚下的路 其实地上本没有路 走的人多了 也便成了路 原创不易 文章会持续更新 欢迎微信扫码关注公众号 承接 小程序 嵌入式 PC端项目开发 联系作者
  • ssh: Could not resolve hostname d: Name or service not known

    ssh Could not resolve hostname d Name or service not known Windows下载Linux服务器文件 除了使用XShell中Xftp或者winscp等其他图形化界面软件外 还可使用类似
  • ST表初识(C++)

    ST表 Sparse Table 稀疏表 一种数据结构 主要用来解决静态的区间最大 最小值问题 主要思想 倍增思想 在看ST表之前 先看一个问题 2 4 1 5 3 在这个序列中找出区间 1 3 3 5 1 5 max 1 3 4 max
  • Krpano全景制作使用笔记

    目录 一 前言 二 软件下载安装 三 软件使用 1 软件文件夹说明 1 docu文件夹 2 templates文件夹 3 viewer文件夹 4 droplet bat文件 a MAKE PANO NORMAL Droplet bat b
  • 5.docker可视化工具(Portainer)

    本文操作 在 192 168 204 102 机器执行 安装最新版 portainer 请使用 portainer portainer ce 镜像 图片来源 https hub docker com r portainer portaine
  • 02搭建Spark单机环境2

    目录 一 在三台虚拟机上面安装lrzsz 二 在三台虚拟机上安装配置jdk 三 配置完全分布模式Hadoop 配置文件 hdfs site xml 配置文件 mapred site xml 配置文件 yarn site xml 四 格式化与
  • [Java初学] 第一次作业 hello.java直接调用同根目录下的其他类 A.java 、B.java、C.java

    hello java public class hello public static void main String args System out println 您好 只需编译我 A a new A a fA B b new B b
  • Zookeeper与kafka

    zookeeper概述 Zookeeperl是一个开源的分布式的 为分布式框架提供协调服务的Apache项目 Zookeeper 工作机制 zookeeper从设计模式角度来理解 是一个基于观察者模式设计的分布式服务管理框架 它负责存储和管
  • win8下找到计算器并转换为程序员模式

    最近想用计算器的十进制和十六进制转化的功能 发现win8没有开始菜单了 从网上查了查 原来指令如此简单 特此做笔记 谨防忘记 操作 win r打开运行 输入calc 确定就出来了 首先看到的界面是 然后我们点击查看 程序员 就变成了 这样我

随机推荐

  • 关于哈工大操作系统实验三出现 task[0] trying to sleep 的解决方法

    问题 最近做哈工大操作系统实验三时明明代码没写错但是执行 run后出现以下情况 原因和解决方法 再各种比对和研究下发现这个问题原因在于加载文件系统的setup不能放在进程0里 不然会导致进程0休眠 可是别人并没有出现这个情况 尝试了网上很多
  • Kafka C++客户端库librdkafka笔记

    目录 目录 1 1 前言 2 2 缩略语 2 3 配置和主题 3 3 1 配置和主题结构 3 3 1 1 Conf 3 3 1 2 ConfImpl 3 3 1 3 Topic 3 3 1 4 TopicImpl 3 4 线程 4 5 消费
  • 工具URL

    流程图 画图工具 https www processon com
  • java 实现TCP和UDP的底层

    JAVA Socket 底层是怎样基于TCP IP 实现的 图片 2012 08 09 22 54 35 标签 java socket连接分类 JavaSE 首先必须明确 TCP IP模型中有四层结构 应用层 Application Lay
  • 在CentOS8中安装PHP8.0

    我的系统版本 cat etc redhat release 1 下载PHP安装文件 网址 https downloads php net pollita wget https downloads php net pollita php 8
  • 论文笔记:Temporal Regularized Matrix Factorization forHigh-dimensional Time Series Prediction

    0 摘要 时间序列预测问题在现代应用中变得越来越高维 如气候学和需求预测 例如 在需求预测中 项目数量可能高达50 000个 此外 数据通常是嘈杂的 充满缺失值 因此 现代应用程序需要高度可伸缩的方法 并且能够处理损坏或丢失值的噪声数据 然
  • 小程序下拉加载更多数据

    1 功能介绍 1 1 简单列表分页 功能描述 拖动下拉条 可以加载更多内容 1 1 1 实现步骤 1 1 1 1 配置 json文件 1 在app json页面配置 enablePullDownRefresh true 这会让下拉刷新效果适
  • STL 中查找算法使用总结

    顺序查找元素 find 头文件 包含在头文件 include 中 算法作用 使用 操作符 从给定的区间中查找和指定元素值相等的第一个元素 返回其迭代器 适用容器 适用于所有的序列容器 代码示例 vector
  • java 使用RabbitMQ示例

    RabbitMQ简介 RabbitMQ是一个受欢迎的消息代理 通常用于应用程序之间或者程序的不同组件之间通过消息来进行集成 具有高可用高并发的优点 适合集群服务器 采用 Erlang实现 对主要的编程语言都有客户端支持 RabbitMQ环境
  • java的JVM与垃圾回收机制

    核心机制 Java虚拟机 JVM是一个虚拟的计算机 具有指令集并使用不同的存储区域 负责执行指 令 管理数据 内存 寄存器 对于不同的平台 有不同的虚拟机 只有某平台提供了对应的java虚拟机 java程序才可在此平台运行 Java虚拟机机
  • python web自动化实现登录多次 利用ddt数据驱动读取账号信息

    web自动化实现多个用户登录某系统 使用python代码实现 首先创建读取测试数据的方法 代码如下 import unittest from time import sleep from ddt import ddt data 引入ddt驱
  • 【笔记】对称密码之分组密码的工作模式

    目录 一 前言 二 分组密码概述 2 1分组密码的设计原则 2 2混淆和扩散 三 分组加密法的模式 3 1电码本ECB Electronic Code Book 模式 3 2密码分组链接CBC Cipher Block Chaining 模
  • HTTP3

    当我对HTTP的认知还停留在HTTP2 0时 HTTP协议已经发展3 0了 参考下知乎 HTTP 3 原理实战 知乎 大厂对于新技术的追求总是处于行业前列 HTTP3就是其中之一 既然大厂都逐渐在使用了 那说明它经过了一系列的实践的考验 具
  • 二分搜索——分治思想

    二分查找 二分查找是一种在每次比较之后将查找空间一分为二的算法 每次需要查找集合中的索引或元素时 都应该考虑二分查找 如果集合是无序的 我们可以总是在应用二分查找之前先对其进行排序 时间复杂度是 log N 因为 二分查找是通过将现有数组一
  • 数据结构C语言 单链表(插入、删除、查找)

    数据结构C语言 单链表 插入 删除 查找 1 插入 假设 A 的临时指针为 p C 的临时指针为 q 步骤1 删除这条连接线 步骤2 将p gt next给q gt next 步骤3 将q给p gt next 插入代码 q gt next
  • ubuntu18.04+cuda10.2+cudnn7.6.5,并使用CUDA自动安装NVIDIA驱动而非手动。

    一 CUDA和NVIDIA显卡驱动安装 cuda的安装选项中其实包含了nvidia驱动的安装选项 不过网上好多资料都说不要再cuda中勾选nvidia驱动 而要自己去nvidia官网自己查好型号下载安装文件 手动安装nvidia驱动 其实主
  • 字体格式:ttf,woff,eot

    生成网页字体 https onlinefontconverter com eot IE onetype是微软和Adobe共同开发的字体 IE浏览器全部采用这种字体 woff 其它浏览器 woff web开发字体格式 是一种专门为web而设计
  • 信号延迟仿真的 Matlab 源码实现

    信号延迟仿真的 Matlab 源码实现 信号的延迟是数字信号处理中的一个重要概念 本文将介绍如何使用 Matlab 实现信号的延迟仿真 并给出相应的源代码实现 首先 我们需要定义一个信号并进行时域分析 在 Matlab 中 我们可以使用 t
  • Ubuntu下卸载Qt

    卸载有2种办法 1 进入qt的安装目录下卸载 一般ubuntu软件是安装在opt目录下 如果不在就需要找找了 进入安装目录下 sudo MaintenanceTool 选择remove all 就可以完全删除qt了 2 命令行安装的卸载 s
  • OVS datapath流表结构及匹配过程

    datapath流表的查找函数是ovs flow tbl lookup stats 在此之前 先看下datapath组织流表的方式 最新2 6的ovs流表 已经不是最早单纯的精确匹配了 而是一种精确匹配 带掩码匹配合并在一起的方式 叫做me