You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Linux内核中通过sock_sendmsg()设置UDP报文的TTL值?

内核UDP报文发送时设置TTL的方法

我正在Linux内核模块(字符设备驱动)中使用sock_sendmsg()发送UDP报文,想设置TTL值但找不到实现方法,求建议。

以下是我的发送代码:

static int
sendUdpPkt(struct TClientInfo *client, char *udpPayload, size_t udpLen)
{
    if (verbose)
        printk(KERN_INFO MODULE_NAME": [Client %d] sendUdpPkt - WILL SEND %zu bytes to 0x%X : %hd\n",
            client->clientId, udpLen, client->txAddr.sin_addr.s_addr, ntohs(client->txAddr.sin_port));

    mm_segment_t   oldfs;
    int            bytesSent = 0;
    struct msghdr        msg = {};
    struct iovec      ioData = {};

    if (client->txSock->sk == NULL)
    {
        printk(KERN_INFO MODULE_NAME": [Client %d] sendUdpPkt - Error 'sk' is null\n", client->clientId);
        return 0;
    }

    ioData.iov_base = udpPayload;
    ioData.iov_len  = udpLen;

    iov_iter_init(&msg.msg_iter, WRITE, &ioData, 1, udpLen);

    msg.msg_flags   = 0;
    msg.msg_name    = &client->txAddr;
    msg.msg_namelen = sizeof(struct sockaddr_in);
    msg.msg_control = NULL;
    msg.msg_controllen = 0;
    msg.msg_control = NULL;

    oldfs = get_fs();
    set_fs(KERNEL_DS);
    bytesSent = sock_sendmsg(client->txSock, &msg);
    set_fs(oldfs);

    if (verbose)
        printk(KERN_INFO MODULE_NAME": [Client %d] sendUdpPkt - Sent %d bytes to 0x%X : %hd. TS FIFO %d PKTS\n",
            client->clientId, bytesSent, client->txAddr.sin_addr.s_addr, ntohs(client->txAddr.sin_port), kfifo_len(&client->pktFifo) );

    return bytesSent;
}

两种设置TTL的方案:

1. 设置Socket全局默认TTL

如果所有发送的UDP报文都用同一个TTL,可以在创建socket后调用sock_setsockopt设置IP_TTL选项,该设置会对后续所有发送的报文生效:

int ttl = 64; // 自定义TTL值
int ret = sock_setsockopt(client->txSock, IPPROTO_IP, IP_TTL, (char *)&ttl, sizeof(ttl));
if (ret < 0) {
    printk(KERN_ERR MODULE_NAME": Failed to set TTL: %d\n", ret);
}

这段代码需在socket初始化完成后调用,比如在创建txSock的逻辑中添加。

2. 单包指定TTL(通过控制消息cmsg)

如果需要针对单个UDP报文设置不同的TTL,可以通过msghdr的控制消息(msg_control)传递IP_TTL选项。修改后的发送代码如下:

static int
sendUdpPkt(struct TClientInfo *client, char *udpPayload, size_t udpLen)
{
    if (verbose)
        printk(KERN_INFO MODULE_NAME": [Client %d] sendUdpPkt - WILL SEND %zu bytes to 0x%X : %hd\n",
            client->clientId, udpLen, client->txAddr.sin_addr.s_addr, ntohs(client->txAddr.sin_port));

    mm_segment_t   oldfs;
    int            bytesSent = 0;
    struct msghdr        msg = {};
    struct iovec      ioData = {};
    struct cmsghdr *cmsg;
    char cmsg_buf[CMSG_SPACE(sizeof(int))]; // 分配控制消息缓冲区

    if (client->txSock->sk == NULL)
    {
        printk(KERN_INFO MODULE_NAME": [Client %d] sendUdpPkt - Error 'sk' is null\n", client->clientId);
        return 0;
    }

    ioData.iov_base = udpPayload;
    ioData.iov_len  = udpLen;

    iov_iter_init(&msg.msg_iter, WRITE, &ioData, 1, udpLen);

    msg.msg_flags   = 0;
    msg.msg_name    = &client->txAddr;
    msg.msg_namelen = sizeof(struct sockaddr_in);
    // 初始化控制消息
    msg.msg_control = cmsg_buf;
    msg.msg_controllen = sizeof(cmsg_buf);

    // 填充TTL控制消息
    cmsg = CMSG_FIRSTHDR(&msg);
    cmsg->cmsg_level = IPPROTO_IP;
    cmsg->cmsg_type = IP_TTL;
    cmsg->cmsg_len = CMSG_LEN(sizeof(int));
    *(int *)CMSG_DATA(cmsg) = 64; // 设置你需要的TTL值

    oldfs = get_fs();
    set_fs(KERNEL_DS);
    bytesSent = sock_sendmsg(client->txSock, &msg);
    set_fs(oldfs);

    if (verbose)
        printk(KERN_INFO MODULE_NAME": [Client %d] sendUdpPkt - Sent %d bytes to 0x%X : %hd. TS FIFO %d PKTS\n",
            client->clientId, bytesSent, client->txAddr.sin_addr.s_addr, ntohs(client->txAddr.sin_port), kfifo_len(&client->pktFifo) );

    return bytesSent;
}

注意事项:

  • 控制消息缓冲区大小需用CMSG_SPACE计算,确保足够容纳数据
  • 填充cmsg时要正确设置cmsg_level、cmsg_type和cmsg_len字段
  • 内核中控制消息的内存需分配在栈或内核空间,不能使用用户态内存

内容的提问来源于stack exchange,提问作者Danny

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.30 02:05:39