Open vSwitch 行为匹配的实现

news2024/12/24 9:28:55

 一、Datapath 模块的行为匹配

        在 Open vSwitch 的数据包转发流程中,存在快速路径和慢速路径两种模式,如下图所示:

        其中,快速路径直接在 Datapath 模块完成行为匹配,将数据包转发出去。而慢速路径的数据包无法在 Datapath 模块完全处理,需要通过 upcall 调用将数据包交给用户空间的 vswitchd 守护进程,但是最终守护进程会将处理后的数据包和流表发回 Datapath 模块,然后再次进行行为匹配和数据包的转发。

        对于快速路径而言,行为匹配发生在 Datapath 模块接收和检查筛选之后,即在数据包处理 ovs_dp_process_packet() 函数中调用:

void ovs_dp_process_packet(struct sk_buff *skb, struct sw_flow_key *key) {
    ......

	error = ovs_execute_actions(dp, skb, sf_acts, key);

    ......
}

        对于慢速路径而言,行为匹配发生在流表下发到 Datapath 模块和数据包通过 reinject 发回之后,即在数据包接收和执行 ovs_packet_cmd_execute() 函数中调用:

static int ovs_packet_cmd_execute(struct sk_buff *skb, struct genl_info *info) {
    ......

	err = ovs_execute_actions(dp, packet, sf_acts, &flow->key);

    ......
}

        相应的调用关系如下图所示:

        也就是说,无论是快速路径还是慢速路径,最终都会由 Datapath 模块进行行为匹配,只不过不同的路径需要走不同的流程,会有不同的函数实现。

二、行为匹配 ovs_execute_actions()

        函数 ovs_execute_actions() 是 Datapath 模块的核心部分,主要负责行为的匹配和执行,存储在 ovs-main/datapath/actions.c 文件中:

/* Execute a list of actions against 'skb'. */
int ovs_execute_actions(struct datapath *dp, struct sk_buff *skb, const struct sw_flow_actions *acts, struct sw_flow_key *key) {
	int err, level;

	level = __this_cpu_inc_return(exec_actions_level);
	if (unlikely(level > OVS_RECURSION_LIMIT)) {
		net_crit_ratelimited("ovs: recursion limit reached on datapath %s, probable configuration error\n", ovs_dp_name(dp));
		kfree_skb(skb);
		err = -ENETDOWN;
		goto out;
	}

	OVS_CB(skb)->acts_origlen = acts->orig_len;
	err = do_execute_actions(dp, skb, key, acts->actions, acts->actions_len);

	if (level == 1)
		process_deferred_actions(dp);

out:
	__this_cpu_dec(exec_actions_level);
	return err;
}

        函数的第一个输入参数 struct datapath *dp 表示 datapath 对象(代表一个 Open vSwitch 实例在内核空间的实现),第二个输入参数 struct sk_buff *skb 表示要处理的数据包,第三个输入参数 const struct sw_flow_actions *acts 表示要执行的动作,第四个输入参数 struct sw_flow_key *key 表示数据包的流关键字信息。

        函数首先维护一个当前 CPU 核心的 exec_actions_level 计数器,并进行一些预处理。然后调用 do_execute_actions(dp, skb, key, acts->actions, acts->actions_len) 函数来执行动作列表,并通过判断 exec_actions_level 计数器的值来限制递归次数。最后如果 exec_actions_level 计数器的值为 1 即表示最外层调用,则调用 process_deferred_actions(dp) 函数来处理延迟的动作。

Tips:这里的 exec_actions_level 计数器主要用于递归限制
        Open vSwitch 的 Datapath 模块支持递归执行,以处理嵌套的动作。但为了防止无限递归,代码设置了 OVS_RECURSION_LIMIT 的限制。如果递归深度超过该限制,则会打印警告日志,释放数据包,并返回相应错误码 -ENETDOWN。

Tips:延迟动作处理

        数据包的某些行为在匹配完成后,可能需要在后续处理过程中执行而不是立即执行(比如转发行为)。对于 Datapath 模块而言,这些行为会被推迟到最外层调用时统一处理,由 process_deferred_actions() 函数负责处理这些延迟的动作,该函数存储在 ovs-main/datapath/actions.c 文件中:

static void process_deferred_actions(struct datapath *dp) {
	struct action_fifo *fifo = this_cpu_ptr(action_fifos);

	/* Do not touch the FIFO in case there is no deferred actions. */
	if (action_fifo_is_empty(fifo))
		return;

	/* Finishing executing all deferred actions. */
	do {
		struct deferred_action *da = action_fifo_get(fifo);
		struct sk_buff *skb = da->skb;
		struct sw_flow_key *key = &da->pkt_key;
		const struct nlattr *actions = da->actions;
		int actions_len = da->actions_len;

		if (actions)
			do_execute_actions(dp, skb, key, actions, actions_len);
		else
			ovs_dp_process_packet(skb, key);
	} while (!action_fifo_is_empty(fifo));

	/* Reset FIFO for the next packet.  */
	action_fifo_init(fifo);
}

三、行为执行 do_execute_actions()

        函数 do_execute_actions() 的主要作用是针对不同的数据包类型执行相应的行为,存储在 ovs-main/datapath/actions.c 文件中:

/* Execute a list of actions against 'skb'. */
static int do_execute_actions(struct datapath *dp, struct sk_buff *skb, struct sw_flow_key *key, const struct nlattr *attr, int len) {
	const struct nlattr *a;
	int rem;

	for (a = attr, rem = len; rem > 0; a = nla_next(a, &rem)) {
		int err = 0;

		switch (nla_type(a)) {
    		case OVS_ACTION_ATTR_OUTPUT: {
			    int port = nla_get_u32(a);
			    struct sk_buff *clone;

			    /* Every output action needs a separate clone of 'skb', In case the output action is the last action, cloning can be avoided. */
			    if (nla_is_last(a, rem)) {
				    do_output(dp, skb, port, key);
				    /* 'skb' has been used for output. */
				    return 0;
			    }

			    clone = skb_clone(skb, GFP_ATOMIC);
			    if (clone)
				    do_output(dp, clone, port, key);
			    OVS_CB(skb)->cutlen = 0;
			    break;
		    }

		    case OVS_ACTION_ATTR_TRUNC: {
			    struct ovs_action_trunc *trunc = nla_data(a);

			    if (skb->len > trunc->max_len)
				    OVS_CB(skb)->cutlen = skb->len - trunc->max_len;
			    break;
		    }

		    case OVS_ACTION_ATTR_USERSPACE:
			    output_userspace(dp, skb, key, a, attr, len, OVS_CB(skb)->cutlen);
			    OVS_CB(skb)->cutlen = 0;
			    break;

		    case OVS_ACTION_ATTR_HASH:
			    execute_hash(skb, key, a);
			    break;

		    case OVS_ACTION_ATTR_PUSH_MPLS:
			    err = push_mpls(skb, key, nla_data(a));
			    break;

		    case OVS_ACTION_ATTR_POP_MPLS:
			    err = pop_mpls(skb, key, nla_get_be16(a));
			    break;

		    case OVS_ACTION_ATTR_PUSH_VLAN:
			    err = push_vlan(skb, key, nla_data(a));
			    break;

		    case OVS_ACTION_ATTR_POP_VLAN:
			    err = pop_vlan(skb, key);
			    break;

		    case OVS_ACTION_ATTR_RECIRC: {
			    bool last = nla_is_last(a, rem);

			    err = execute_recirc(dp, skb, key, a, last);
			    if (last) {
				    /* If this is the last action, the skb has been consumed or freed.
				     * Return immediately. */
				    return err;
			    }
			    break;
		    }

		    case OVS_ACTION_ATTR_SET:
			    err = execute_set_action(skb, key, nla_data(a));
			    break;

		    case OVS_ACTION_ATTR_SET_MASKED:
		    case OVS_ACTION_ATTR_SET_TO_MASKED:
			    err = execute_masked_set_action(skb, key, nla_data(a));
			    break;

		    case OVS_ACTION_ATTR_SAMPLE: {
			    bool last = nla_is_last(a, rem);

			    err = sample(dp, skb, key, a, last);
			    if (last)
				    return err;

			    break;
		    }

		    case OVS_ACTION_ATTR_CT:
			    if (!is_flow_key_valid(key)) {
				    err = ovs_flow_key_update(skb, key);
				    if (err)
					    return err;
			    }

			    err = ovs_ct_execute(ovs_dp_get_net(dp), skb, key, nla_data(a));

			    /* Hide stolen IP fragments from user space. */
			    if (err)
				    return err == -EINPROGRESS ? 0 : err;
			    break;

		    case OVS_ACTION_ATTR_CT_CLEAR:
			    err = ovs_ct_clear(skb, key);
			    break;

		    case OVS_ACTION_ATTR_PUSH_ETH:
			    err = push_eth(skb, key, nla_data(a));
			    break;

		    case OVS_ACTION_ATTR_POP_ETH:
			    err = pop_eth(skb, key);
			    break;

		    case OVS_ACTION_ATTR_PUSH_NSH: {
			    u8 buffer[NSH_HDR_MAX_LEN];
			    struct nshhdr *nh = (struct nshhdr *)buffer;

			    err = nsh_hdr_from_nlattr(nla_data(a), nh, NSH_HDR_MAX_LEN);
			    if (unlikely(err))
				    break;
			    err = push_nsh(skb, key, nh);
			    break;
		    }

		    case OVS_ACTION_ATTR_POP_NSH:
			    err = pop_nsh(skb, key);
			    break;

		    case OVS_ACTION_ATTR_METER:
			    if (ovs_meter_execute(dp, skb, key, nla_get_u32(a))) {
				    consume_skb(skb);
				    return 0;
			    }
                break;

		    case OVS_ACTION_ATTR_CLONE: {
			    bool last = nla_is_last(a, rem);

			    err = clone(dp, skb, key, a, last);
			    if (last)
				    return err;
			    break;
		    }

		    case OVS_ACTION_ATTR_CHECK_PKT_LEN: {
                bool last = nla_is_last(a, rem);

                err = execute_check_pkt_len(dp, skb, key, a, last);
                if (last)
                    return err;

                    break;
            }
		}

		if (unlikely(err)) {
			kfree_skb(skb);
			return err;
		}
	}

	consume_skb(skb);
	return 0;
}

        函数的第一个输入参数 struct datapath *dp 表示 datapath 对象(代表一个 Open vSwitch 实例在内核空间的实现),第二个输入参数 struct sk_buff *skb 表示要处理的数据包,第三个输入参数 struct sw_flow_key *key 表示数据包的流关键字信息,第四个输入参数 const struct nlattr *attr 表示要执行的操作列表,第五个输入参数 int len 表示操作列表的长度。

        函数使用 for 循环遍历操作列表,对于每个操作而言,它都会根据 nla_type(a) 类型执行相应的行为。支持的操作类型包括:

  • OVS_ACTION_ATTR_OUTPUT 将数据包发送到指定端口
  • OVS_ACTION_ATTR_TRUNC 截断数据包长度
  • OVS_ACTION_ATTR_USERSPACE 将数据包发送到用户空间
  • OVS_ACTION_ATTR_HASH 计算数据包的哈希值
  • OVS_ACTION_ATTR_PUSH_MPLS 为数据包添加 MPLS 头部
  • OVS_ACTION_ATTR_POP_MPLS 从数据包中删除 MPLS 头部
  • OVS_ACTION_ATTR_PUSH_VLAN 为数据包添加 VLAN 头部
  • OVS_ACTION_ATTR_POP_VLAN 从数据包中删除 VLAN 头部
  • OVS_ACTION_ATTR_RECIRC 重新循环处理数据包
  • OVS_ACTION_ATTR_SET 设置数据包的特定字段
  • OVS_ACTION_ATTR_SET_MASKED 使用掩码设置数据包的特定字段
  • OVS_ACTION_ATTR_SAMPLE 对数据包进行采样操作
  • OVS_ACTION_ATTR_CT 执行连接跟踪操作
  • OVS_ACTION_ATTR_CT_CLEAR 清除数据包的连接跟踪状态
  • OVS_ACTION_ATTR_PUSH_ETH 为数据包添加以太网头部
  • OVS_ACTION_ATTR_POP_ETH 从数据包中删除以太网头部
  • OVS_ACTION_ATTR_PUSH_NSH 为数据包添加 NSH (网络服务头) 头部
  • OVS_ACTION_ATTR_POP_NSH 从数据包中删除 NSH 头部
  • OVS_ACTION_ATTR_METER 执行数据包计量操作
  • OVS_ACTION_ATTR_CLONE 克隆数据包
  • OVS_ACTION_ATTR_CHECK_PKT_LEN 检查数据包长度

        对于每个操作而言,如果执行过程中发生错误,则直接释放数据包并返回错误代码。如果所有操作都执行成功,则最终释放原始数据包。 

总结:

        在 Open vSwitch 的 Datapath 模块中,主要通过 ovs_execute_actions() 函数进行相应行为的匹配和执行,这是数据包在 Datapath 模块中必须执行的步骤。

        由于本人水平有限,以上内容如有不足之处欢迎大家指正(评论区/私信均可)。

参考资料:

Open vSwitch 官网

Open vSwitch 源代码 GitHub

Open vSwitch 数据包接收的实现-CSDN博客

Open vSwitch 的 reinject 数据包发回-CSDN博客

Open vSwitch v2.17.10 LTS 源代码

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1877820.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

AI大模型到底有没有智能?一篇文章给你讲明明白白

生成式人工智能 (GenAI[1] ) 和大语言模型 (LLM [2] ),这两个词汇想必已在大家的耳边萦绕多时。它们如惊涛骇浪般席卷了整个科技界,登上了各大新闻头条。ChatGPT,这个神奇的对话助手,也许已成为你形影不离的良师益友。 然而&…

一次可输入多张图像,还能多轮对话!最新开源数据集,让AI聊天更接近现实

大模型对话能更接近现实了! 不仅可以最多输入20张图像,还能支持多达27轮对话。可处理文本图像tokens最多18k。 这就是最新开源的超长多图多轮对话理解数据集MMDU(Multi-Turn Multi-Image Dialog Understanding)。 大型视觉语言模…

技术周总结2024.06.17~06.23(Doris数据库)

文章目录 一、06.18 周二1.1) 问题01: doris数据表写入使用 stream load好还是 inser into好 一、06.18 周二 1.1) 问题01: doris数据表写入使用 stream load好还是 inser into好 对于Doris数据表的写入操作,通常推荐…

【折腾笔记】兰空图床使用Redis做缓存

前言 最近发现我部署在群晖NAS上的兰空图床程序在高并发的情况下会导致图片加载缓慢或出现图片加载失败的情况,于是我查阅了官方文档资料并进行了一系列的测试,发现兰空图床如果开启了原图保护功能,会非常的吃CPU的性能,尤其是在…

编译工具-Gradle

文章目录 Idea中配置Gradle项目project目录settings.gradlebuild.gradlegradlewgradlew.bat Gradle Build生命周期编写Settings.gradle编写Build.gradleTasksPlugins Idea中配置 配置项:gradle位置 及仓库位置 Gradle项目 Task,settings.gradle,build.…

【Arduino】实验使用ESP32控制可编程继电器制作跑马灯(图文)

今天小飞鱼实验使用ESP控制继电器,为了更好的掌握继电器的使用方法这里实验做了一个跑马灯的效果。 这里用到的可编程继电器,起始原理并不复杂,同样需要ESP32控制针脚输出高电平或低电平给到继电器,继电器使用这个信号控制一个电…

【SpringBoot】SpringBoot核心启动流程源码解析

SpringBoot总体流程 当我们启动一个SpringBoot程序的时候,只需要一个main方法就可以启动,但是对于其中流程时如何执行的,以及如何调用spring的IOC和AOP机制,本篇带着这个问题来整体体系化的梳理下流程。 SpringBootApplication …

哥斯拉短视频:成都柏煜文化传媒有限公司

哥斯拉短视频:巨兽传奇的视听盛宴 在短视频的海洋中,成都柏煜文化传媒有限公司 有一种特殊的存在总能吸引人们的目光,那就是以哥斯拉为主题的短视频。这些视频以震撼的视觉效果、扣人 ​心弦的剧情和独特的怪兽文化,为我们呈现了…

mysql5.7安装使用

mysql5.7安装包:百度网盘 提取码: 0000 一、 安装步骤 双击安装文件 选择我接受许可条款–Next 选择自定义安装,下一步 选择电脑对应的系统版本后(我的系统是64位),点击中间的右箭头,选择Next 选择安装路径–Next 执行…

Android隐藏状态栏和修改状态栏颜色_亲测有效

本文记录了隐藏状态栏和修改状态栏颜色以及电量、WiFi标志等内容的模式显示,亲测有效。 1、隐藏屏幕状态栏 public void hideStatusBar(BaseActivity activity) {Window window activity.getWindow();//没有这一行无效window.addFlags(WindowManager.LayoutParam…

瑞数(rs6)接口以及源码

测试代码截图如下:调用接口即可直接用 需要dd 有想要学习教程的也能够找我。 如有需求,欢迎+我绿泡泡。 期待你的加入!

Java | Leetcode Java题解之第189题轮转数组

题目: 题解: class Solution {public void rotate(int[] nums, int k) {k % nums.length;reverse(nums, 0, nums.length - 1);reverse(nums, 0, k - 1);reverse(nums, k, nums.length - 1);}public void reverse(int[] nums, int start, int end) {whil…

ZYNQ MPSOC烧写问题记录

1、如果带有ARM代码,则ZYNQ MPSOC烧写的烧写是通过ViTIS进行的,通过JTAG模式进行烧写,如下图的PS_MODEx配置成0000,这个只与硬件相关,硬件拉高拉低。 2、如果不带ARM代码,则烧写过程与前版本一致。

PaddleOCR的快速使用

一、简介 PaddleOCR 旨在打造一套丰富、领先、且实用的 OCR 工具库,助力开发者训练出更好的模型,并应用落地。 支持多种 OCR 相关前沿算法,在此基础上打造产业级特色模型PP-OCR、PP-Structure和PP-ChatOCRv2,并打通数据生产、模型…

vue-cli 搭建项目

创建 router 目录 在一个.js文件中添加 打开外部命令 打开外部命令后,在指令栏输入npm i vue-router3.5.3 ,等待下载 下载完成后 在 main.js 中配置路由 输入这些后,基本的配置就实现了 最后进行测试,验证是否配置 或者打开外部命…

解决指南:如何应对错误代码 0x80070643

在使用Windows操作系统过程中,用户可能会遭遇各种错误代码,其中错误 0x80070643是比较常见的一种。这个错误通常在安装更新或某些软件时发生,尤其是在微软的Windows Defender或其他Microsoft安全产品以及.NET Framework更新过程中更为常见。本…

Linux的fwrite函数

函数原型: 向文件fp中写入writeBuff里面的内容 int fwrite(void*buffer,intsize,intcount,FILE*fp) /* * description : 对已打开的流进行写入数据块 * param ‐ ptr :指向 数据块的指针 * param ‐ size :指定…

19.《C语言》——【如何理解static和extern?】

🎇开场语 亲爱的读者,大家好!我是一名正在学习编程的高校生。在这个博客里,我将和大家一起探讨编程技巧、分享实用工具,并交流学习心得。希望通过我的博客,你能学到有用的知识,提高自己的技能&a…

操作系统速成笔记五

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录 一、前言🚀🚀🚀二、正文☀️☀️☀️1.内存映射文件(1)方便程序员访问文件数据(2&#xff09…

小红书多账号管理平台哪个好用?可以快速监测多个小红书账号的数据吗?

随着品牌营销战线的不断扩展,小红书已经成为企业和个人品牌竞相展示的舞台。但是,随之而来的多账号管理问题也让众多运营者头疼不已。一个优秀的多账号管理平台,能让你事半功倍,轻松监控和分析账号数据。 如今,市面上出…