Skip to content

fix: use NetworkManager D-Bus to enable/disable network interfaces#669

Merged
deepin-bot[bot] merged 1 commit into
linuxdeepin:develop/eaglefrom
GongHeng2017:202605271430-eagle-fix
May 27, 2026
Merged

fix: use NetworkManager D-Bus to enable/disable network interfaces#669
deepin-bot[bot] merged 1 commit into
linuxdeepin:develop/eaglefrom
GongHeng2017:202605271430-eagle-fix

Conversation

@GongHeng2017
Copy link
Copy Markdown
Contributor

Replace the previous iw/rfkill/ifconfig and ioctl approach with NetworkManager D-Bus API as the primary method for controlling network interface state. The D-Bus method sets the DeviceEnabled property via GetDeviceByIpIface. Fall back to the ioctl method when the D-Bus approach is unavailable or fails.

  • Add enableNetworkByDBus() as primary method (scheme 1)
  • Add enableNetworkByIoctl() as fallback method (scheme 2)
  • Remove wlan/wlp-specific iw/rfkill/ifconfig logic
  • Refactor ioctlOperateNetworkLogicalName() to try D-Bus first, then fall back to ioctl
  • Add QDBus headers and update SPDX copyright year

Log: fix issue
Bug: https://pms.uniontech.com/bug-view-362409.html

Copy link
Copy Markdown

@sourcery-ai sourcery-ai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry @GongHeng2017, you have reached your weekly rate limit of 500000 diff characters.

Please try again later or upgrade to continue using Sourcery

@GongHeng2017 GongHeng2017 force-pushed the 202605271430-eagle-fix branch 2 times, most recently from 9bc3cf2 to 3ef1ebb Compare May 27, 2026 06:41
Copy link
Copy Markdown
Contributor

@max-lvs max-lvs left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

-1, 注意代码逻辑及安全校验,参考Ai review comments

@GongHeng2017 GongHeng2017 force-pushed the 202605271430-eagle-fix branch from 3ef1ebb to b095a4a Compare May 27, 2026 06:52
Replace the previous iw/rfkill/ifconfig and ioctl approach with
NetworkManager D-Bus API as the primary method for controlling
network interface state. The D-Bus method sets the DeviceEnabled
property via GetDeviceByIpIface. Fall back to the ioctl method
when the D-Bus approach is unavailable or fails.

- Add enableNetworkByDBus() as primary method (scheme 1)
- Add enableNetworkByIoctl() as fallback method (scheme 2)
- Remove wlan/wlp-specific iw/rfkill/ifconfig logic
- Refactor ioctlOperateNetworkLogicalName() to try D-Bus first,
  then fall back to ioctl
- Add QDBus headers and update SPDX copyright year

Log: fix issue
Bug: https://pms.uniontech.com/bug-view-362409.html
@GongHeng2017 GongHeng2017 force-pushed the 202605271430-eagle-fix branch from b095a4a to 7e642b6 Compare May 27, 2026 07:01
@deepin-ci-robot
Copy link
Copy Markdown

deepin pr auto review

这份代码变更将网卡启用/禁用的逻辑从原有的“无线网卡走 iw/rfkill/ifconfig 命令行,有线网卡走 ioctl”的混合方案,重构为了“优先使用 NetworkManager D-Bus,失败后回退到 ioctl”的方案。整体重构方向非常好,减少了对外部进程的依赖,提高了代码的可维护性。

但在语法逻辑、代码质量、性能和安全方面,还有一些需要改进和注意的地方。以下是详细的审查意见:

一、 语法与逻辑问题

  1. ioctl 方案中未关闭 socket 描述符
    enableNetworkByIoctl 函数中,当 ioctl 设置成功时,直接 return true;,但忘记了调用 close(fd);,这会导致文件描述符泄漏。
    修改建议:在 return true; 前添加 close(fd);,或使用 RAII 机制管理 fd。

  2. ioctl 方案中 IFF_PROMISC(混杂模式)逻辑丢失
    旧代码中,禁用网卡时会清除 IFF_UPIFF_PROMISC 标志。新代码只操作了 IFF_UP。如果业务上不再需要控制混杂模式,这是合理的简化;但如果业务仍需在禁用时剥离混杂模式,则属于逻辑遗漏。
    修改建议:确认业务需求。如果不需要控制混杂模式,当前逻辑是正确的;如果需要,应当补充 ifr.ifr_flags &= ~IFF_PROMISC;

  3. D-Bus 设置 DeviceEnabled 的异步生效问题
    NetworkManager 的 Device.Enabled 属性在设为 false 时,NM 会异步执行断开和关闭操作。D-Bus 的 Set 调用成功返回,仅代表 NM 接收了该请求,并不代表网卡已经完全 down 掉。如果后续逻辑依赖网卡状态的真实改变,可能会有时序问题。
    修改建议:如果后续逻辑依赖网卡真实状态,建议在 D-Bus 设置后,通过监听 NM 的 StateChanged 信号或轮询网卡状态来确认操作完成。

二、 代码质量与可读性

  1. ioctl 方案中的 toStdString() 临时对象生命周期
    std::string nameStr = logicalName.toStdString();strncpy(ifr.ifr_name, nameStr.c_str(), IFNAMSIZ - 1); 的写法是正确的(旧代码中直接用 logicalName.toStdString().c_str() 是危险的,因为临时对象可能被销毁)。新代码在这里做了很好的改进,建议保持现在的写法。

  2. 头文件顺序调整
    新代码将 <unistd.h> 移到了 Qt 头文件和 <QCryptographicHash> 之间,打破了原有的“Qt 头文件 -> 空行 -> C/C++ 系统头文件”的规范格式。
    修改建议:将 <unistd.h> 移到 <net/if.h> 的上方,保持分类清晰:

    #include <unistd.h>
    #include <net/if.h>
    #include <sys/ioctl.h>
  3. D-Bus 调用方式优化
    当前使用 QDBusInterface::call() 来设置属性,这需要实例化 org.freedesktop.DBus.Properties 接口。虽然可行,但 Qt 提供了更直观的属性设置方法。
    修改建议:可以直接使用 QDBusInterfacesetProperty 方法,代码更简洁:

    QDBusInterface deviceInterface("org.freedesktop.NetworkManager",
                                   devicePath,
                                   "org.freedesktop.NetworkManager.Device",
                                   QDBusConnection::systemBus());
    if (!deviceInterface.isValid()) {
        // 错误处理...
    }
    deviceInterface.setProperty("DeviceEnabled", QVariant::fromValue(enable));

三、 代码性能

  1. D-Bus 同步调用阻塞问题
    nmInterface.call(...)deviceInterface.call(...) 都是同步阻塞调用。如果在主线程(UI线程)中执行,会导致界面卡顿,尤其是在 NM 响应较慢或总线繁忙时。
    修改建议:如果此代码在主线程运行,强烈建议改用异步调用 QDBusPendingCall async = nmInterface.asyncCall(...),或者将整个操作移到工作线程中执行。

  2. ioctl 方案中 memset 的冗余
    memset(&ifr, 0, sizeof(ifr)); 紧接着又使用了 strncpy 填充 ifr_name,并在下方使用 ioctl 获取 flags(这会覆盖 ifr_ifru)。虽然 memset 增加了安全性,但属于微小的性能冗余。不过出于安全编码规范,保留 memset 是良好的习惯,此处无需强制修改。

四、 代码安全

  1. 版权年份格式修改
    2019 ~ 2023 改为 2019 - 2026。注意 ~ 替换为了 -,请确保这符合你们项目的 SPDX 版权声明规范,通常 SPDX 推荐使用 -,这是合理的。

  2. D-Bus 权限校验缺失
    通过 D-Bus 修改网络状态属于高危操作,通常需要 PolicyKit 授权。当前代码直接调用,如果运行在普通用户权限下,可能会被 NM 拒绝并返回 D-Bus 错误。
    修改建议:确认调用此代码的进程是否具有足够的权限(如以 root 运行,或已经通过 polkit 获取授权)。错误处理中已经包含了 reply.error().message() 的打印,这很好,有助于排查权限问题。

  3. logicalName 注入风险
    虽然 logicalName 来自内部逻辑,但传入 D-Bus 时仍需谨慎。NetworkManager 的 GetDeviceByIpIface 会验证接口是否存在,因此不存在严重的注入风险,但 ioctl 方案中的长度校验 logicalName.length() >= IFNAMSIZ 是非常棒的安全加固,有效防止了栈溢出。


综合改进后的代码参考

针对以上意见,以下是修改后的核心函数参考:

bool EnableUtils::ioctlOperateNetworkLogicalName(const QString &logicalName, bool enable)
{
    // 方案一:通过NetworkManager D-Bus设置DeviceEnabled属性
    if (enableNetworkByDBus(logicalName, enable))
        return true;

    // 方案二:方案一失败,回退到ioctl方式
    qWarning() << "Fallback to ioctl method for" << logicalName;
    return enableNetworkByIoctl(logicalName, enable);
}

bool EnableUtils::enableNetworkByDBus(const QString &logicalName, bool enable)
{
    QDBusInterface nmInterface("org.freedesktop.NetworkManager",
                              "/org/freedesktop/NetworkManager",
                              "org.freedesktop.NetworkManager",
                              QDBusConnection::systemBus());
    if (!nmInterface.isValid()) {
        qCritical() << "Failed to connect to NetworkManager:" << nmInterface.lastError().message();
        return false;
    }

    QDBusReply<QDBusObjectPath> reply = nmInterface.call("GetDeviceByIpIface", logicalName);
    if (!reply.isValid()) {
        qCritical() << "Failed to GetDeviceByIpIface for" << logicalName << ":" << reply.error().message();
        return false;
    }

    QString devicePath = reply.value().path();
    if (devicePath.isEmpty()) {
        qCritical() << "Got empty devicePath for interface:" << logicalName;
        return false;
    }

    // 优化:直接通过 Device 接口设置属性,代码更简洁
    QDBusInterface deviceInterface("org.freedesktop.NetworkManager",
                                  devicePath,
                                  "org.freedesktop.NetworkManager.Device",
                                  QDBusConnection::systemBus());
    if (!deviceInterface.isValid()) {
        qCritical() << "Failed to connect to device interface:" << deviceInterface.lastError().message();
        return false;
    }

    // 使用 setProperty 替代底层 Properties.Set 调用
    if (!deviceInterface.setProperty("DeviceEnabled", QVariant::fromValue(enable))) {
        qCritical() << "Failed to set DeviceEnabled for" << logicalName << ":" << deviceInterface.lastError().message();
        return false;
    }

    return true;
}

bool EnableUtils::enableNetworkByIoctl(const QString &logicalName, bool enable)
{
    if (logicalName.isEmpty() || logicalName.length() >= IFNAMSIZ) {
        qCritical() << "Invalid logicalName length for ioctl";
        return false;
    }

    int fd = socket(AF_INET, SOCK_STREAM, 0);
    if (fd < 0)
        return false;

    struct ifreq ifr;
    memset(&ifr, 0, sizeof(ifr));

    std::string nameStr = logicalName.toStdString();
    strncpy(ifr.ifr_name, nameStr.c_str(), IFNAMSIZ - 1);
    ifr.ifr_name[IFNAMSIZ - 1] = '\0';

    if (ioctl(fd, SIOCGIFFLAGS, &ifr) < 0) {
        close(fd);
        return false;
    }

    if (enable) {
        ifr.ifr_flags |= IFF_UP;
    } else {
        ifr.ifr_flags &= ~IFF_UP;
    }

    if (ioctl(fd, SIOCSIFFLAGS, &ifr) < 0) {
        close(fd);
        return false;
    }

    // 修复:操作成功后必须关闭 fd
    close(fd);
    return true;
}

@deepin-ci-robot
Copy link
Copy Markdown

[APPROVALNOTIFIER] This PR is NOT APPROVED

This pull-request has been approved by: GongHeng2017, max-lvs

The full list of commands accepted by this bot can be found here.

Details Needs approval from an approver in each of these files:

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

@GongHeng2017
Copy link
Copy Markdown
Contributor Author

/forcemerge

@deepin-bot deepin-bot Bot merged commit 5dbefea into linuxdeepin:develop/eagle May 27, 2026
22 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants