ipsec.c 编译问题修复记录

一、原始编译错误

source/ipsec.c:37:17: error: field 'conn' has incomplete type 'vici_conn_t'

二、根因分析

include/libvici.h 中 vici_conn_t 是不透明类型(opaque type),只有前向声明,没有结构体定义:

typedef struct vici_conn_t vici_conn_t; // 仅声明,无定义

编译器不知道 vici_conn_t 的大小,所以不能在结构体中按值(by value)嵌入 vici_conn_t conn,必须用指针。

libvici 的 API 全部通过指针操作:

  • vici_connect() 返回 vici_conn_t *(堆上分配)
  • vici_disconnect() 接受 vici_conn_t *
  • vici_register()、vici_submit() 等也都接受 vici_conn_t *

三、修复内容(source/ipsec.c)

3.1 结构体字段改为指针

// 修改前
typedef struct {
    vici_conn_t conn;   // 编译错误:不完整类型
    int connected;
    int handleRegister;
    int hbStatus;
    uint32_t hbCnt;
} oam_ipsec_t;
// 修改后
typedef struct {
    vici_conn_t *conn;  // 改为指针
    int handleRegister;
    int hbStatus;
    uint32_t hbCnt;
} oam_ipsec_t;

3.2 修复错误的类型转换(第136行)

// 修改前
oam_ipsec_t *oamIpsec = (vici_conn_t *)sc_ipsec->pdata;

// 修改后
oam_ipsec_t *oamIpsec = (oam_ipsec_t *)sc_ipsec->pdata;

3.3 memcpy 改为直接指针赋值

// 修改前(两处)
memcpy(&oamIpsec->conn, viciConn, sizeof(vici_conn_t));

// 修改后
oamIpsec->conn = viciConn;

sizeof(vici_conn_t) 对不透明类型也编译不过,所以 memcpy 方式根本行不通。

3.4 去掉取地址符 &

oamIpsec->conn 已是指针,不再需要 &。

// 修改前                    // 修改后
sync_existing(&oamIpsec->conn);  sync_existing(oamIpsec->conn);
vici_register(&oamIpsec->conn, ...)  vici_register(oamIpsec->conn, ...)
vici_submit(req, &oamIpsec->conn)    vici_submit(req, oamIpsec->conn)
vici_disconnect(&oamIpsec->conn)     vici_disconnect(oamIpsec->conn)

3.5 删除冗余的 connected 字段

connected 和 conn == NULL 完全等价,删掉用一个状态即可。

// 删除前                    // 删除后
if (oamIpsec->connected == 0) {  if (oamIpsec->conn == NULL) {
    oamIpsec->connected = 1;     // 删除
    ...
    oamIpsec->connected = 0;     oamIpsec->conn = NULL;
    ...
    oamIpsec->connected = 0;     oamIpsec->conn = NULL;

每次 vici_disconnect() 后必须将 conn 置 NULL,否则 conn 变为悬空指针(dangling pointer)。

四、关于局部变量 viciConn 的说明

launch_ipsec_mod_unix_listener() 和 ipsec_timeout() 函数中 viciConn 确实是栈变量,但:

vici_conn_t *viciConn = vici_connect(...);  // viciConn = 栈上的指针变量
                                             // 指向的对象 = vici_connect() 内部在堆上分配
oamIpsec->conn = viciConn;                   // 复制地址到 oamIpsec->conn
// 函数结束:viciConn 栈变量销毁
// 但 oamIpsec->conn 仍然指向堆上的 vici_conn_t 对象,有效!

指针变量在栈上,但指针指向的 vici_conn_t 对象在堆上,函数返回后依然有效,无悬空指针问题。

五、后续链接错误(独立于 ipsec.c 修改)

../conf/lib/ipsec/libstrongswan.so: undefined reference to `gettid@GLIBC_2.30'

原因

libstrongswan.so 在 glibc >= 2.30 的环境下编译,依赖 gettid@GLIBC_2.30。但交叉编译环境的 rootfs(/home/xx/rootfs_lsdk2004_bsp1.9_dpdk)的 glibc 版本低于 2.30,不提供此符号。

为什么 Python 3.8 配置能过?

查看 Makefile:

# Python 3.6 的 LIBS 配置(不含 --allow-shlib-undefined)
LIBS += -lpython3.6m
Python 3.8 的 LIBS 配置(含 --allow-shlib-undefined)
LIBS += -L../conf/lib/python3.8.10/lib/python3.8/config-3.8-aarch64-linux-gnu 

-lpython3.8 ... -Wl,--allow-shlib-undefined

-Wl,--allow-shlib-undefined 让链接器在链接共享库时不检查未定义符号,所以 Python 3.8 配置下链接可以通过。但运行时如果实际调用了 gettid,程序启动会崩溃(undefined symbol: gettid)。

解决方案(三选一)

方案说明优缺点
① 重新编译 strongSwan用当前交叉编译器(rootfs glibc < 2.30)重新编译 libstrongswan.so最彻底的解决,但需要 strongSwan 源码
② 升级 rootfs glibc将 rootfs 的 glibc 升级到 2.30+可能影响整个系统
③ 加 --allow-shlib-undefined链接时跳过检查只是绕过,运行时可能有风险

更多推荐