单机多网卡互通——问题跟踪+工具分析

一、背景

想搭建soft ROCE(RXE)与实体ROCE设备互联的测试环境,为了节省机器以及使用方便,预想在配备ROCE卡的主机上,用另一个网卡绑定soft ROCE,然后互通。

[ETH1 + ROCE] <--------------------> [ETH2 + RXE]

二、问题跟踪

ROCE向RXE发送send only,两端都没有收到cqe。

1、tcpdump抓取ETH2网卡,抓获了ROCE v2的send only报文,没有ack报文。

也就是说数据包已经成功环回,外部链路正常;RXE的接受处理路径出现异常,导致没有响应。

2、使用funcgraph抓取,RXE注册的隧道接收处理函数

perf-tools funcgraph rxe_udp_encap_recv

没有捕获到,这跟预想的不一样,这说明:

数据包在进入RXE的处理之前,已经被丢掉了。需要看数据包的底层接收路径。

3、然后开启了将近一天的四处碰壁阶段。

由于过往对网络的内核处理路径了解很少,理了一下rxe_udp_encap_recv的调用路径,推测应该是通过udp_unicast_rcv_skb接口调用的,其余还有mcast/broadcast两个接口,但我们这个数据包不应该是多播和广播。

然后trace了一下udp_unicast_rcv_skb接口,还是没有捕获到!

这段代码是这个样子的:

int __udp4_lib_rcv(struct sk_buff *skb, struct udp_table *udptable,int proto)
{/* .............*/sk = __udp4_lib_lookup_skb(skb, uh->source, uh->dest, udptable);if (sk)return udp_unicast_rcv_skb(sk, skb, uh);/* .............*/
}

4、本着就近原则,trace了一下udp4_lib_lookup接口,发现可以trace 到!!!那问题不就出在这里了么???

所以接下来用bpftrace跟踪了下udp4_lib_lookup的返回值,确实是0,没有拿到socket,所以报文被丢了!

5、为什么拿不到socket

bpftrace跟踪了upd4_lib_lookup中的入参,源目端口,源目地址,数值都是正确的!那为啥拿不到socket?RXE里监听这个端口的socket创建有问题???

试图梳理一下socket的创建、查找,短时间不好搞定,未果。

6、udp_rcv没有执行

这时候想要么funcgraph抓一下udp报文的处理入口,udp_rcv,看一下他的执行路径,在哪个环节跟正常的报文不一样?也可能前置出错,引发的问题。

结果发现,udp_rcv没有捕获到!!!离了个大谱!!

7、bpftrace跟踪udp_lib_lookup的调用栈

那谁调用了udp_lib_lookup呢?把它的调用栈打印出来看一下。

咦,是在udp_gro_receive接口中调用的。

#!/usr/bin/env bpftrace#include <linux/netdevice.h>
#include <linux/udp.h>
#include <linux/skbuff.h>
#include <linux/netdev_features.h>kprobe:__udp4_lib_lookup {printf("saddr=0x%x,sport=0x%x,daddr=0x%x,dport=0x%x\n",arg1, arg2, arg3, arg4);@[kstack]=count();
}kretprobe:udp4_lib_lookup2 {printf("sock=0x%x\n",retval);if(retval != 0) {printf("encap_recv = 0x%x, gro_receive= 0x%x, gro_enabled=%d\n", ((struct udp_sock *)retval)->encap_rcv, ((struct udp_sock *)retval)->gro_receive, ((struct udp_sock *)retval)->gro_enabled);}
}

看到是udp4_gro_receive接口调用了udp4_lib_lookup,然后没有查询到socket。

[root@localhost leiyanjie]# bpftrace udp.bt
Attaching 3 probes...
saddr=0x3231a8c0,sport=0x8,daddr=0x7e33a8c0,dport=0xb712
sock=0x0
sock=0xa7ef9b00
encap_recv = 0xc071a520, gro_receive= 0x0, gro_enabled=0
skb->dev=0xffff9280187d7000, skb->dev->features = 0x10000134829, GRO_FEATURE_BIT=57
^C@[__udp4_lib_lookup+1udp4_gro_receive+402inet_gro_receive+675dev_gro_receive+1574napi_gro_receive+96receive_buf+911virtnet_poll+331net_rx_action+309__softirqentry_text_start+188asm_call_sysvec_on_stack+15do_softirq_own_stack+55irq_exit_rcu+208common_interrupt+120asm_common_interrupt+30cpuidle_enter_state+214cpuidle_enter+41do_idle+452cpu_startup_entry+25start_secondary+276secondary_startup_64_no_verify+176
]: 1

8、跟踪udp4_gro_receive的执行路径

发现,怎么这个接口接口这么简单就退出了呀,是不是没有接收完,把报文丢掉了?!!

# ad perf-tools funcgraph udp4_gro_receive
Tracing "udp4_gro_receive"... Ctrl-C to end.1)               |  udp4_gro_receive() {1)   0.197 us    |    irq_enter_rcu();1)               |    __sysvec_irq_work() {1)               |      __wake_up() {1)               |        __wake_up_common_lock() {1)   0.644 us    |          _raw_spin_lock_irqsave();1)               |          __wake_up_common() {1)               |            autoremove_wake_function() {1)               |              default_wake_function() {1)               |                try_to_wake_up() {1)   0.801 us    |                  _raw_spin_lock_irqsave();1)               |                  select_task_rq_fair() {1)   0.365 us    |                    available_idle_cpu();1)   0.364 us    |                    cpus_share_cache();1)   1.268 us    |                    update_cfs_rq_h_load();1)               |                    select_idle_sibling() {1)   0.374 us    |                      available_idle_cpu();1)   0.560 us    |                    }1)   0.086 us    |                    rcu_read_unlock_strict();1)   3.776 us    |                  }1)               |                  ttwu_queue_wakelist() {1)               |                    __smp_call_single_queue() {1)   0.569 us    |                      send_call_function_single_ipi();1)   1.396 us    |                    }1)   1.776 us    |                  }1)   0.097 us    |                  _raw_spin_unlock_irqrestore();1)   7.576 us    |                }1)   8.340 us    |              }1)   8.544 us    |            }1)   9.832 us    |          }1)   0.094 us    |          _raw_spin_unlock_irqrestore();1) + 11.127 us   |        }1) + 11.301 us   |      }1) + 12.007 us   |    }1)               |    irq_exit_rcu() {1)   0.087 us    |      idle_cpu();1)               |      tick_nohz_irq_exit() {1)   0.102 us    |        ktime_get();1)   0.287 us    |      }1)   0.647 us    |    }1)               |    __udp4_lib_lookup() {1)   0.700 us    |      udp4_lib_lookup2();1)               |      udp4_lib_lookup2() {1)   0.227 us    |        compute_score();1)   0.252 us    |        compute_score();1)   1.306 us    |      }1)   2.482 us    |    }1)   0.243 us    |    udp_gro_receive();1)   0.086 us    |    rcu_read_unlock_strict();1) + 20.713 us   |  }
^C
Ending tracing...

然后掉进了一个大坑,查看udp_gro_receive的代码如下:

	if (!sk || !udp_sk(sk)->gro_receive) {if (skb->dev->features & NETIF_F_GRO_FRAGLIST)NAPI_GRO_CB(skb)->is_flist = sk ? !udp_sk(sk)->gro_enabled : 1;if ((!sk && (skb->dev->features & NETIF_F_GRO_UDP_FWD)) ||(sk && udp_sk(sk)->gro_enabled) || NAPI_GRO_CB(skb)->is_flist)return call_gro_receive(udp_gro_receive_segment, head, skb);/* no GRO, be sure flush the current packet */goto out;}

那么udp_gro_receive_segment应该是要被执行的,可实际没有执行。为什么?

因为我们的网卡使能了GRO,而RXE只提供了encap_rcv接口,实际也需要注册自己的gro_receive么?!

	tnl_cfg.encap_type = 1;tnl_cfg.encap_rcv = rxe_udp_encap_recv;/* Setup UDP tunnel */setup_udp_tunnel_sock(net, sock, &tnl_cfg);

void setup_udp_tunnel_sock(struct net *net, struct socket *sock,struct udp_tunnel_sock_cfg *cfg)
{struct sock *sk = sock->sk;/* Disable multicast loopback */inet_sk(sk)->mc_loop = 0;/* Enable CHECKSUM_UNNECESSARY to CHECKSUM_COMPLETE conversion */inet_inc_convert_csum(sk);rcu_assign_sk_user_data(sk, cfg->sk_user_data);udp_sk(sk)->encap_type = cfg->encap_type;udp_sk(sk)->encap_rcv = cfg->encap_rcv;udp_sk(sk)->encap_err_rcv = cfg->encap_err_rcv;udp_sk(sk)->encap_err_lookup = cfg->encap_err_lookup;udp_sk(sk)->encap_destroy = cfg->encap_destroy;udp_sk(sk)->gro_receive = cfg->gro_receive;udp_sk(sk)->gro_complete = cfg->gro_complete;udp_tunnel_encap_enable(sock);
}

RXE是标准内核驱动随着内核版本更新升级的,理论上不应该不支持开启了GRO的网卡?

9、bpfrrace跟踪这段代码的参数

因为不熟悉,所以全都靠猜:是不是我们的网卡置位NETIF_F_GRO_FRAGLIST这个标识,就可以执行后续的call_gro_receive?

# bpftrace udp.bt
Attaching 3 probes...
saddr=0x3231a8c0,sport=0x8,daddr=0x7e33a8c0,dport=0xb712
sock=0x0
sock=0xa7ef9b00
encap_recv = 0xc071a520, gro_receive= 0x0, gro_enabled=0
skb->dev=0xffff9280187d7000, skb->dev->features = 0x10000134829, GRO_FEATURE_BIT=57

10、增加GRO_FRAGLIST属性

man ethtool

ethtool -k eth2 查询网卡属性

ethtool -K eth2 **** on/off 设置网卡属性

# ethtool -k eth2 | grep gro
rx-gro-hw: off [fixed]
rx-gro-list: off# thtool -K eth2 rx-gro-list on 

然后bpftrace发现,这个标识确实在netdev->features开启成功了,但并还是没有进入后续的segment收包流程。

再次进入死胡同。

三、问题跟踪2

网络搜索了一下udp_gro_receive的相关流程,也不能够帮助在短时间内建立非常清晰的理解。

比较幸运的是,udp的报文并不多,所以我们在udp_gro_receive接口中轻易的抓到了报文,而且从sk_buff结构中拿到了netdev的地址。

1、bpftrace根据参数过滤eth2的ip_rcv接口

所以,尝试抓取ip_rcv接口,数据有没有进入ip_rcv接口呢?

eth0的IP报文是很多的,咋筛选出eth2的报文呢?既然我们拿到了netdev的地址,就可以根据netdev精确过滤出eth2的报文。

kprobe:ip_rcv {if(arg1 == 0xffff9ab4a371a000) {printf("get eth2 packet\n");@[kstack]=count();}
}

捕获到了get eth2 packet!

这说明之前推测的GRO丢包是错误的,其实报文已经送到了IP层,没有进入UDP层。是在IP层丢掉的。

saddr=0x3231a8c0,sport=0x8,daddr=0x8734a8c0,dport=0xb712
sock=0x0
sock=0x1680bf00
encap_recv = 0xc055d520, gro_receive= 0x0, gro_enabled=0
skb->dev=0xffff9ab4a371a000, skb->dev->features = 0x200010000134829, GRO_FEATURE_BIT=57
get eth2 packet

本来用funcgraph跟踪一下ip_rcv接口会更直观,看报文丢弃在了哪个环节,但因为funcgraph无法传入参数过滤,eth0的IP报文也比较多,就没有采用这种方式。

2、ip_rcv_finish接口返回失败

ip_rcv接口比较简单,很容易就跟踪到是ip_rcv_finish接口返回失败,导致报文没有进一步上送。

kretprobe:ip_rcv_finish {printf("ret=0x%x\n",retval);
}

3、tracepoint跟踪kfree_skb_reason

分析了ip_rcv_finish_core接口,看到有多个位置可能导致异常返回,异常返回都会调用kfree_skb_reason这个接口传递释放sk_buff,而且会把错误码(drop_reason)传入。

kfree_skb_reason在我安装的5.10内核版本上实际测试,没有查询到bpftrace入口。

但是在我5.15的内核代码中,是有trace点的,可以把reason打印出来。

void kfree_skb_reason(struct sk_buff *skb, enum skb_drop_reason reason)
{if (!skb_unref(skb))return;trace_kfree_skb(skb, __builtin_return_address(0), reason);__kfree_skb(skb);
}

所以开始研究使用tracepoint。/sys/kernel/debug/tracing/available_events中记录了所有的可trace事件。可以看到

/sys/kernel/debug/tracing # cat available_events | grep kfree_skb
skb:kfree_skb

那么如何使能这个事件?进入events/skb/kfree_skb目录,有如下接口文件:

/sys/kernel/debug/tracing/events/skb/kfree_skb # ls
enable  filter  format  id  trigger

enable输入1,就可以使能trace,不需要其他开关。

filter是可以输入过滤条件的,比如过滤eth2的报文?这个需要后面学习一下。

format是显示trace日志格式的。

实际的trace日志,还可以显示pid、cpu和时间,挺好的。结果如下:

因为我的机器上的内核版本是5.10,所以看不到reason,这是个让人伤心的故事。从某个patch来看,确实是后面的版本合入的kfree_skb_reason。原来正常的skb释放也是走这个路径,所以trace kfree_skb有特别多的日志,无法区分。

4、tracepoint跟踪kfree_skb_reasonconsume_skb

在高版本内核中,正常的skb释放走consume_skb路径,drop的skb释放走kfree_skb_reason路径,就可以区分出来了。

5、bpftrace打桩跟踪执行路径

既然看不出来drop reason,那其实直接用bpftrace在ip_recv_finish_core的各个接口中打桩是可以看到执行路径的。

因为我们也不是大流量的环境,网络报文的密度比较小。所以抓到udp报文后,紧跟着的打印可以认为是同一条报文的处理路径中的。

最终确认是在ip_route_input_slow接口,路由失败引发丢包的。

kprobe:ip_rcv {if(arg1 == 0xffff9ab4a371a000) {printf("get eth2 packet\n");@[kstack]=count();}
}kprobe:ip_rcv_finish {if(((struct sk_buff *)arg2)->dev == 0xffff9ab4a371a000) {printf("get eth2 packet\n");@[kstack]=count();}
}kretprobe:ip_route_use_hint {printf("ip_route_use_hind, ret=0x%x\n", retval);
}kretprobe:udp_v4_early_demux {printf("udp_v4_early_demux, ret=0x%x\n", retval);
}kretprobe:ip_route_input_noref {printf("ip_route_input_noref, ret=%d\n", retval);
}kretprobe:ip_rcv_finish {printf("ret=0x%x\n",retval);
}kretprobe:ip_route_input_slow {printf("ip_route_input_slow, ret=%d\n", retval);
}

6、为什么会路由失败??

我简单尝试了一下,用eth1 ping eth2,发现也是无法ping通的。这台机器上的3个网卡都无法互相ping通。

所以此时意识到,同一台机器上的两个网卡之间是不是就是不能通信的?

然后开始资料查询,重点针对一台机器上的多个网卡。

有几篇文章都作了说明,但原理又都不清楚,只是说linux对此的支持不友好。

反而是最终在自己疯狂的尝试中,误打误撞解决了问题。

sysctl -w /proc/sys/net/ipv4/conf/all/accep_local=1
sysctl -w /proc/sys/net/ipv4/conf/all/rp_filter=2

IP路由的相关知识后续学习补充。

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

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

相关文章

Appium元素定位(全网详细讲解)(二)

1.appium inspector&#xff08;定位元素的工具&#xff09;使用方法 详细介绍&#xff1a; 详细解释&#xff1a; 图标名称说明1Show Element Handles是否显示元素句柄2Select Elements选择元素定位3Tap/Swipe By Coordinates按坐标点击/滑动4Download Screenshot下载屏幕截…

2024机器遗忘(Machine Unlearning)技术分类-思维导图

1 介绍 机器遗忘&#xff08;Machine Unlearning&#xff09;是指从机器学习模型中安全地移除或"遗忘"特定的数据点或信息。这个概念源于数据隐私保护的需求&#xff0c;尤其是在欧盟通用数据保护条例&#xff08;GDPR&#xff09;等法规中提出的"被遗忘的权利…

【漏洞复现】飞企互联-FE企业运营管理平台——SQL注入

声明&#xff1a;本文档或演示材料仅供教育和教学目的使用&#xff0c;任何个人或组织使用本文档中的信息进行非法活动&#xff0c;均与本文档的作者或发布者无关。 文章目录 漏洞描述漏洞复现测试工具 漏洞描述 飞企互联-FE企业运营管理平台是一个基于云计算、智能化、大数据…

【8】相关补充

【8】相关补充 文章目录 前言一、不同模型在测试集上的精度二、实验记录三、SNP位点筛选及其它python脚本四、总结五、后续安排总结 前言 存放一些有关这个项目研究的补充。 三叶青图像识别研究简概 一、不同模型在测试集上的精度 存放了不同识别模型在测试集上精度评估展示…

Android HWASAN使用与实现原理

一、背景 为了提前检测出Android User Sapce的app或native进程的内存错误问题&#xff0c;帮助研发定位与分析这些问题&#xff0c;基于Android 14版本上对HWASAN做了调研分析。 二、ASAN介绍 HWASAN是在ASAN的基础上做了拓展&#xff0c;因此在介绍HWASAN之前先了解下ASAN.…

WBCE CMS v1.5.2 远程命令执行漏洞(CVE-2022-25099)

前言 CVE-2022-25099 是一个影响 WBCE CMS v1.5.2 的严重安全漏洞&#xff0c;具体存在于 /languages/index.php 组件中。该漏洞允许攻击者通过上传精心构造的 PHP 文件在受影响的系统上执行任意代码。 技术细节 受影响组件&#xff1a;/languages/index.php受影响版本&…

如何在 Odoo 16 中向新视图添加字段

例如,让我们看看如何在新视图或新操作窗口中创建“many2one”字段。 请考虑下面的屏幕截图,它表示不包含任何字段的新视图类型或客户端操作窗口。 我们现在可以将与“res.partner”关联的“多对一”字段引入到我们的新视图或客户端操作窗口中。 为了实现这一点,在 XML 模板…

Using a text embedding model locally with semantic kernel

题意&#xff1a;在本地使用带有语义核&#xff08;Semantic Kernel&#xff09;的文本嵌入模型 问题背景&#xff1a; Ive been reading Stephen Toubs blog post about building a simple console-based .NET chat application from the ground up with semantic-kernel. Im…

idea中maven全局配置

配置了就不需要每次创建项目都来设置maven仓库了。 1.先把项目全关了 2. 进入全局设置 3.设置maven的仓库就可以了

SpringBoot实现多数据源切换

1. 概述 随着项目规模的扩大和业务需求的复杂化&#xff0c;单一数据源已经不能满足实际开发中的需求。在许多情况下&#xff0c;我们需要同时操作多个数据库&#xff0c;或者需要将不同类型的数据存储在不同的数据库中。这时&#xff0c;多数据源场景成为必不可少的解决方案。…

【CentOS7.6】docker部署EMQX教程,本地镜像直接导入(附下载链接),没法在云服务器上魔法拉取镜像的快来

总览 先把下载链接放在这里吧&#xff0c;这是 EMQX 的 tar 包&#xff0c;能够直接导入 CentOS 的 docker&#xff1a; 链接&#xff1a;https://pan.baidu.com/s/1rSGSLoVvj83ai6d5oolg8Q?pwd0108 提取码&#xff1a;0108 一、安装配置教程 1.将 EMQX-latest.tar 包导入…

[图解]企业应用架构模式2024新译本讲解19-数据映射器1

1 00:00:01,720 --> 00:00:03,950 下一个我们要讲的就是 2 00:00:04,660 --> 00:00:07,420 数据映射器这个模式 3 00:00:09,760 --> 00:00:13,420 这个也是在数据源模式里面 4 00:00:13,430 --> 00:00:14,820 用得最广泛的 5 00:00:16,250 --> 00:00:19,170…

链篦机回转窑球团生产工艺

生球在回转窑氧化焙烧&#xff0c;回转窑头部设有燃烧器&#xff0c;燃料可以采用气体、固体、液体。 来自环冷机一冷却段的高温废气作为二次风进入窑内参与燃烧&#xff0c;烧成成品球进入环冷机。 环冷机采用鼓风冷却&#xff0c;热风风箱分为四段&#xff1a; 一段气体引至…

无人机有哪些关键技术?

一、控制技术 无人机的核心还是在控制上&#xff0c;飞控系统的可靠性、稳定性及可扩展性是其中重要的指标。可靠性上&#xff0c;除了器件选型之外&#xff0c;目前主要靠多余度来增加&#xff1b;稳定性主要体现在多场景下仍能保持良好的工作状态&#xff0c;主要靠算法来进…

【Nginx】docker运行Nginx及配置

Nginx镜像的获取 直接从Docker Hub拉取Nginx镜像通过Dockerfile构建Nginx镜像后拉取 二者区别 主要区别在于定制化程度和构建过程的控制&#xff1a; 直接拉取Nginx镜像&#xff1a; 简便性&#xff1a;直接使用docker pull nginx命令可以快速拉取官方的Nginx镜像。这个过程…

通透!手把教你如何从头构建一个机器学习模型

目录 1.业务理解 2.数据收集和准备 数据采集 探索性数据分析 (EDA) 和数据清理 特征选择 3.建立机器学习模型 选择正确的模型 分割数据 训练模型 模型评估 4.模型优化 5.部署模型 今天我将带领大家一步步的来构建一个机器学习模型。 我们将按照以下步骤开发客户流失…

【基础算法】UE中实现轮播

本期作者&#xff1a;尼克 易知微3D引擎技术负责人 当前N 总数M 从0到M-1 从1到M 感谢阅读&#xff0c;以上内容均由易知微3D引擎团队原创设计&#xff0c;以及易知微版权所有&#xff0c;转载请注明出处&#xff0c;违者必究&#xff0c;谢谢您的合作。申请转载授权后台回复【…

掌握XD数字设计:打造令人惊艳的用户体验

xd是adobe旗下一款主打UI界面设计-建立原型的软件&#xff0c;它可以将wireframe、design、以及prototype等UI/UX设计流程整合到一个软件中&#xff0c;算是一款与sketch对打的软件。 与PS相比&#xff0c;在UI设计方面&#xff0c;Adobe XD有非常突出的3个优点&#xff1a;能…

从0到1手写vue源码

模版引擎 数组join法&#xff08;字符串&#xff09; es6反引号法&#xff08;模版字符串换行&#xff09; mustache (小胡子) 引入mustache 模版引擎的使用 mustache.render(templatestr,data) mustache.render 循环简单数组 循环复杂数组 循环单项数组 数组的嵌套 musta…

江苏徐州SAP代理商有哪些?怎么选择?

在数字化浪潮席卷全球的今天&#xff0c;企业对于高效、智能的管理系统需求日益迫切。SAP作为全球领先的企业管理软件解决方案提供商&#xff0c;其产品在市场上享有极高的声誉。而在江苏徐州&#xff0c;哲讯智能科技作为SAP的代理商&#xff0c;以其专业的技术实力和优质的服…