如何在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
相关产品推荐
相关产品推荐

