searchusermenu
  • 发布文章
  • 消息中心
点赞
收藏
评论
分享
原创

OpenvSwitch 用户态热补丁制作指导

2023-04-28 02:11:49
186
0

一、libcare开源使用指导
https://blog.csdn.net/Rong_Toa/article/details/120294038
二、热补丁制作
1.工具准备
   可以下载https://gitee.com/openeuler/libcareplus.git libcareplus代码,此代码是openeuler社区维护的libcare代码,基于开源libcare代码做了部分修改,修改了libcare部分bug。
2.制作热补丁
下载源代码
本文档以centos openvswitch-2.9.0-3 版本源代码为例进行说明,代码路径https://mirrors.aliyun.com/centos-vault/7.4.1708/cloud/Source/openstack-queens/openvswitch-2.9.0-3.el7.src.rpm。
编译源代码
安装src源码包后,直接用spec文件编译源代码,
rpmbuild -D “_topdir /builddir/build”-bb openvswitch.spec
我们就以这个编译出来的版本为基版本,来制作热补丁。
准备patch文件。
    在上述编译路径/builddir/build/BUILD/openvswitch-2.9.0下,先验证patch文件,patch -p1 < flows.diff,测试执行这个patch命令是否能够顺利将patch应用到代码中。另外撤销patch修改命令,patch -p1 -R < flows.diff.
   此次案例的patch文件内容如下:
commit b09574d56073e9774b8ff58f8fada30e364d81df
Author: yangchang <yangchang@chinatelecom.cn>
Date:   Mon Jan 10 03:29:33 2022 -0500

    upcall: considering dataofs when parsing tcp pkt

    dataofs field of tcp header indicates the tcp header len. The len
    should be >= 20 bytes/4 and be <= tcp data len. This patch is to
    test dataofs, and don't parse layer 4 fields when meet bad dataofs.
    This behave is consistent with openvswitch kenrel module.

diff --git a/lib/flow.c b/lib/flow.c
index f9d7c2a..5ea016a 100644
--- a/lib/flow.c
+++ b/lib/flow.c
@@ -893,14 +893,18 @@ miniflow_extract(struct dp_packet *packet, struct miniflow *dst)
         if (OVS_LIKELY(nw_proto == IPPROTO_TCP)) {
             if (OVS_LIKELY(size >= TCP_HEADER_LEN)) {
                 const struct tcp_header *tcp = data;
-
-                miniflow_push_be32(mf, arp_tha.ea[2], 0);
-                miniflow_push_be32(mf, tcp_flags,
-                                   TCP_FLAGS_BE32(tcp->tcp_ctl));
-                miniflow_push_be16(mf, tp_src, tcp->tcp_src);
-                miniflow_push_be16(mf, tp_dst, tcp->tcp_dst);
-                miniflow_push_be16(mf, ct_tp_src, ct_tp_src);
-                miniflow_push_be16(mf, ct_tp_dst, ct_tp_dst);
+                size_t tcp_hdr_len = TCP_OFFSET(tcp->tcp_ctl) * 4;
+
+                if (OVS_LIKELY(tcp_hdr_len >= TCP_HEADER_LEN)
+                    && OVS_LIKELY(size >= tcp_hdr_len)) {
+                    miniflow_push_be32(mf, arp_tha.ea[2], 0);
+                    miniflow_push_be32(mf, tcp_flags,
+                                       TCP_FLAGS_BE32(tcp->tcp_ctl));
+                    miniflow_push_be16(mf, tp_src, tcp->tcp_src);
+                    miniflow_push_be16(mf, tp_dst, tcp->tcp_dst);
+                    miniflow_push_be16(mf, ct_tp_src, ct_tp_src);
+                    miniflow_push_be16(mf, ct_tp_dst, ct_tp_dst);
+               }
             }
         } else if (OVS_LIKELY(nw_proto == IPPROTO_UDP)) {
             if (OVS_LIKELY(size >= UDP_HEADER_LEN)) {
4)执行制作热补丁脚本。
   bash libcare-opatch-make -o openvswitch-2.9.0-3.el7.x86_64.rpm flows.diff

补丁所在路径
/builddir/build/BUILD/openvswitch-2.9.0/patchroot/2d1101bac2e9e9903434bd498d992a4ef105a5e2.kpatch

3.补丁操作
打补丁
libcare-ctl patch -p $(pidof ovs-vswitchd) ovs_hp1_gb09574d5.kpatch

查看补丁
libcare-ctl  info -p $(pidof ovs-vswitchd)

卸载补丁
libcare-ctl unpatch -p $(pidof ovs-vswitchd)

三、常见问题
1.无法制作出补丁
   这种问题需要查看制作热补丁过程中的报错信息,比如这种报错,

这种错误涉及TLS(进程私有变量),解决起来比较困难,最好更改修改方案,分析修改其他函数来修复问题。
2.打补丁不成功
   查看具体报错信息,报错如下:

   如果是打热补丁报这种错误,可以查看selinux是否开启了,如果开启,关闭selinux即可打上了。
3.打上补丁不生效
   打上补丁之后测试,发现并没有生效。这个时候需要查看修改函数的汇编代码,
gdb --pid=$(pidof ovs-vswitchd) attach上具体进程,反汇编修改的函数

正常打上补丁之后,目标函数的第一行汇编代码应该会被修改成jump指令,如果指令没有被修改,说明补丁修改位置不对。
这个时候就要查看补丁过程中的打印信息,注意查看此处第一行打印,如果第一个地址0x562a2628cb20这个地址跟miniflow_extract首地址相同,那么补丁就可以打到指定位置。这个补丁很明显地址跟函数首地址不相同,所以补丁打不到正确位置,补丁也就无法生效。   
                 
这种情况大概率就是因为制作补丁的编译环境编译出来的版本跟目标版本的代码段不一样。制作热补丁的编译环境,最好能编译出跟目标版本一模一样的版本,如果做不到,能编译出代码段一样的版本也是可以勉强制作的。在制作热补丁的操作中,我们会基于原始代码编译一个基础版本,我们查看这个基础版本的各个段,对比目标文件的各个段,
readelf -S lpmake/usr/sbin/ovs-vswitchd

对比这个可以看到各个段偏移地址基本不同,重点是.text段不同,补丁文件涉及的函数以及其他符号信息记录的都是符号本身相对于.text的偏移地址,如果制作热补丁过程中的.text偏移地址跟目标文件的.text偏移地址不一致,补丁文件是无法正确修改到对应的函数。
如何保证补丁编译环境能编译出跟目标版本一样代码段的版本呢?如果能找到目标版本当时的编译环境是最好了,除了相同编译环境外,还要注意编译路径是否保持一致。如何查看编译路径,可以直接strings可以查看一下可执行文件,这个就可以看到编译路径是在/builddir/build。

strings /usr/sbin/ovs-vswitchd | grep BUILD

如果调整编译路径,还是编译不出相同的代码的版本,就需要找提供版本的人询问编译环境是如何搭建的;如果目标版本是centos开源版本,就需要查这个版本在centos那个系统中首次出现的,参考这个链接https://mirrors.aliyun.com/centos-vault/,然后用这个系统镜像安装编译环境,所有依赖包也都从系统镜像中获取或者从对应系统镜像源中获取,这样基本就可以编出相同版本。

4.打上补丁,程序跑飞
成功打上补丁之后,跑到对应补丁代码,进程就挂掉,这个时候就要查看打上补丁之后的代码。
gdb --pid=$(pidof ovs-vswitchd)  attach上具体进程,反汇编修改的函数

然后查看跳转目标处代码,

可以看到代码有明显错误,且跟原始代码完全不一样。这个时候,我们就要查看制作补丁的过程

从ovs-vswitchd.stripped到kpatch文件,没有做太多的内容修改,我们可以直接通过查看stripped文件来查看kpatch文件的补丁代码段,

objdump -S /root/rpmbuild/BUILD/openvswitch-2.9.0/.lpmaketmp/patched/usr/sbin/ovs-vswitchd.stripped 

可以看到在stripped文件中补丁代码就出错了,这个时候可以进一步查看打patch之后编译的ovs-vswtichd代码段,

objdump -S /root/rpmbuild/BUILD/openvswitch-2.9.0/.lpmaketmp/patched/usr/sbin/ovs-vswitchd


发现打patch之后的代码段就是错误的,然后再查看汇编代码是否有问题,
        #vim ./lib/.kpatch_flow.o.s

 

可以看到汇编代码是没有问题的,也就是说在汇编代码转二进制出错了,这个问题最终发现是编译方式有问题,更换编译后就没有问题了。讲述这个查看过程,只是为了说明,出现问题后,我们能查看对应中间过程,看是哪个过程出现问题了,然后仔细排查出现问题的原因。

0条评论
0 / 1000
杨****昌
3文章数
1粉丝数
杨****昌
3 文章 | 1 粉丝
杨****昌
3文章数
1粉丝数
杨****昌
3 文章 | 1 粉丝
原创

OpenvSwitch 用户态热补丁制作指导

2023-04-28 02:11:49
186
0

一、libcare开源使用指导
https://blog.csdn.net/Rong_Toa/article/details/120294038
二、热补丁制作
1.工具准备
   可以下载https://gitee.com/openeuler/libcareplus.git libcareplus代码,此代码是openeuler社区维护的libcare代码,基于开源libcare代码做了部分修改,修改了libcare部分bug。
2.制作热补丁
下载源代码
本文档以centos openvswitch-2.9.0-3 版本源代码为例进行说明,代码路径https://mirrors.aliyun.com/centos-vault/7.4.1708/cloud/Source/openstack-queens/openvswitch-2.9.0-3.el7.src.rpm。
编译源代码
安装src源码包后,直接用spec文件编译源代码,
rpmbuild -D “_topdir /builddir/build”-bb openvswitch.spec
我们就以这个编译出来的版本为基版本,来制作热补丁。
准备patch文件。
    在上述编译路径/builddir/build/BUILD/openvswitch-2.9.0下,先验证patch文件,patch -p1 < flows.diff,测试执行这个patch命令是否能够顺利将patch应用到代码中。另外撤销patch修改命令,patch -p1 -R < flows.diff.
   此次案例的patch文件内容如下:
commit b09574d56073e9774b8ff58f8fada30e364d81df
Author: yangchang <yangchang@chinatelecom.cn>
Date:   Mon Jan 10 03:29:33 2022 -0500

    upcall: considering dataofs when parsing tcp pkt

    dataofs field of tcp header indicates the tcp header len. The len
    should be >= 20 bytes/4 and be <= tcp data len. This patch is to
    test dataofs, and don't parse layer 4 fields when meet bad dataofs.
    This behave is consistent with openvswitch kenrel module.

diff --git a/lib/flow.c b/lib/flow.c
index f9d7c2a..5ea016a 100644
--- a/lib/flow.c
+++ b/lib/flow.c
@@ -893,14 +893,18 @@ miniflow_extract(struct dp_packet *packet, struct miniflow *dst)
         if (OVS_LIKELY(nw_proto == IPPROTO_TCP)) {
             if (OVS_LIKELY(size >= TCP_HEADER_LEN)) {
                 const struct tcp_header *tcp = data;
-
-                miniflow_push_be32(mf, arp_tha.ea[2], 0);
-                miniflow_push_be32(mf, tcp_flags,
-                                   TCP_FLAGS_BE32(tcp->tcp_ctl));
-                miniflow_push_be16(mf, tp_src, tcp->tcp_src);
-                miniflow_push_be16(mf, tp_dst, tcp->tcp_dst);
-                miniflow_push_be16(mf, ct_tp_src, ct_tp_src);
-                miniflow_push_be16(mf, ct_tp_dst, ct_tp_dst);
+                size_t tcp_hdr_len = TCP_OFFSET(tcp->tcp_ctl) * 4;
+
+                if (OVS_LIKELY(tcp_hdr_len >= TCP_HEADER_LEN)
+                    && OVS_LIKELY(size >= tcp_hdr_len)) {
+                    miniflow_push_be32(mf, arp_tha.ea[2], 0);
+                    miniflow_push_be32(mf, tcp_flags,
+                                       TCP_FLAGS_BE32(tcp->tcp_ctl));
+                    miniflow_push_be16(mf, tp_src, tcp->tcp_src);
+                    miniflow_push_be16(mf, tp_dst, tcp->tcp_dst);
+                    miniflow_push_be16(mf, ct_tp_src, ct_tp_src);
+                    miniflow_push_be16(mf, ct_tp_dst, ct_tp_dst);
+               }
             }
         } else if (OVS_LIKELY(nw_proto == IPPROTO_UDP)) {
             if (OVS_LIKELY(size >= UDP_HEADER_LEN)) {
4)执行制作热补丁脚本。
   bash libcare-opatch-make -o openvswitch-2.9.0-3.el7.x86_64.rpm flows.diff

补丁所在路径
/builddir/build/BUILD/openvswitch-2.9.0/patchroot/2d1101bac2e9e9903434bd498d992a4ef105a5e2.kpatch

3.补丁操作
打补丁
libcare-ctl patch -p $(pidof ovs-vswitchd) ovs_hp1_gb09574d5.kpatch

查看补丁
libcare-ctl  info -p $(pidof ovs-vswitchd)

卸载补丁
libcare-ctl unpatch -p $(pidof ovs-vswitchd)

三、常见问题
1.无法制作出补丁
   这种问题需要查看制作热补丁过程中的报错信息,比如这种报错,

这种错误涉及TLS(进程私有变量),解决起来比较困难,最好更改修改方案,分析修改其他函数来修复问题。
2.打补丁不成功
   查看具体报错信息,报错如下:

   如果是打热补丁报这种错误,可以查看selinux是否开启了,如果开启,关闭selinux即可打上了。
3.打上补丁不生效
   打上补丁之后测试,发现并没有生效。这个时候需要查看修改函数的汇编代码,
gdb --pid=$(pidof ovs-vswitchd) attach上具体进程,反汇编修改的函数

正常打上补丁之后,目标函数的第一行汇编代码应该会被修改成jump指令,如果指令没有被修改,说明补丁修改位置不对。
这个时候就要查看补丁过程中的打印信息,注意查看此处第一行打印,如果第一个地址0x562a2628cb20这个地址跟miniflow_extract首地址相同,那么补丁就可以打到指定位置。这个补丁很明显地址跟函数首地址不相同,所以补丁打不到正确位置,补丁也就无法生效。   
                 
这种情况大概率就是因为制作补丁的编译环境编译出来的版本跟目标版本的代码段不一样。制作热补丁的编译环境,最好能编译出跟目标版本一模一样的版本,如果做不到,能编译出代码段一样的版本也是可以勉强制作的。在制作热补丁的操作中,我们会基于原始代码编译一个基础版本,我们查看这个基础版本的各个段,对比目标文件的各个段,
readelf -S lpmake/usr/sbin/ovs-vswitchd

对比这个可以看到各个段偏移地址基本不同,重点是.text段不同,补丁文件涉及的函数以及其他符号信息记录的都是符号本身相对于.text的偏移地址,如果制作热补丁过程中的.text偏移地址跟目标文件的.text偏移地址不一致,补丁文件是无法正确修改到对应的函数。
如何保证补丁编译环境能编译出跟目标版本一样代码段的版本呢?如果能找到目标版本当时的编译环境是最好了,除了相同编译环境外,还要注意编译路径是否保持一致。如何查看编译路径,可以直接strings可以查看一下可执行文件,这个就可以看到编译路径是在/builddir/build。

strings /usr/sbin/ovs-vswitchd | grep BUILD

如果调整编译路径,还是编译不出相同的代码的版本,就需要找提供版本的人询问编译环境是如何搭建的;如果目标版本是centos开源版本,就需要查这个版本在centos那个系统中首次出现的,参考这个链接https://mirrors.aliyun.com/centos-vault/,然后用这个系统镜像安装编译环境,所有依赖包也都从系统镜像中获取或者从对应系统镜像源中获取,这样基本就可以编出相同版本。

4.打上补丁,程序跑飞
成功打上补丁之后,跑到对应补丁代码,进程就挂掉,这个时候就要查看打上补丁之后的代码。
gdb --pid=$(pidof ovs-vswitchd)  attach上具体进程,反汇编修改的函数

然后查看跳转目标处代码,

可以看到代码有明显错误,且跟原始代码完全不一样。这个时候,我们就要查看制作补丁的过程

从ovs-vswitchd.stripped到kpatch文件,没有做太多的内容修改,我们可以直接通过查看stripped文件来查看kpatch文件的补丁代码段,

objdump -S /root/rpmbuild/BUILD/openvswitch-2.9.0/.lpmaketmp/patched/usr/sbin/ovs-vswitchd.stripped 

可以看到在stripped文件中补丁代码就出错了,这个时候可以进一步查看打patch之后编译的ovs-vswtichd代码段,

objdump -S /root/rpmbuild/BUILD/openvswitch-2.9.0/.lpmaketmp/patched/usr/sbin/ovs-vswitchd


发现打patch之后的代码段就是错误的,然后再查看汇编代码是否有问题,
        #vim ./lib/.kpatch_flow.o.s

 

可以看到汇编代码是没有问题的,也就是说在汇编代码转二进制出错了,这个问题最终发现是编译方式有问题,更换编译后就没有问题了。讲述这个查看过程,只是为了说明,出现问题后,我们能查看对应中间过程,看是哪个过程出现问题了,然后仔细排查出现问题的原因。

文章来自个人专栏
文章 | 订阅
0条评论
0 / 1000
请输入你的评论
1
0