Linux网络命名空间下:C语言ioctl设置MTU与ip工具设置MTU的差异排查(大以太网帧场景)
问题根源与内核变化分析
你遇到的问题本质是Linux内核对RAW套接字绑定后MTU缓存机制的行为变更,以及近年内核对网络接口配置操作的验证逻辑增强,具体来说有以下几个关键变化:
1. RAW套接字绑定后的MTU缓存机制(5.4+内核引入)
在5.4版本之后的Linux内核中,当你调用bind()将RAW套接字关联到特定网络接口时,内核会缓存此时该接口的MTU值,后续发送数据包时,内核会直接使用这个缓存值进行大小校验,而不是实时读取接口当前的MTU。
你的代码顺序是:
创建RAW套接字 → 获取接口索引 → bind到接口 → 设置MTU
这就导致:当bind()执行时,接口的MTU还是默认的1500,内核将这个值缓存到套接字中;之后你用ioctl(SIOCSIFMTU)修改了接口的MTU,但套接字的缓存值不会自动更新,所以发送大于1500字节的帧时,内核依然用缓存的1500进行校验,返回EINVAL(错误码90)或EMSGSIZE(错误码105)。
而五年前的内核(比如4.x系列)不会缓存这个MTU值,发送时会实时读取接口的当前MTU,所以当时的代码顺序是可行的。
2. ip工具的操作逻辑差异
ip link set mtu命令是直接修改网络接口的MTU属性,不会涉及任何套接字绑定操作,之后当你创建并绑定套接字时,内核会读取已经更新后的MTU值并缓存,自然可以发送巨帧。
代码修复方案
调整操作顺序,先设置MTU,再将套接字绑定到接口,这样内核缓存的就是修改后的MTU值。修改后的RawSocket_new函数如下:
int RawSocket_new(char* device, unsigned mtu, int protocol) { int rawsock = 0; struct sockaddr_ll sll = { 0 }; struct ifreq ifr = { 0 }; if((rawsock = socket(PF_PACKET, SOCK_RAW, htons(protocol)))== -1) { DBGMSG("NETP Could not create socket for device '%s'\n", device); return -1; } // 第一步:先设置MTU,再绑定接口 strncpy((char *)ifr.ifr_name, device, IFNAMSIZ - 1); ifr.ifr_mtu = mtu; if (ioctl(rawsock, SIOCSIFMTU, &ifr) < 0) { DBGMSG("NETP Could not set MTU %u to device '%s'\n", mtu, device); close(rawsock); // 记得关闭套接字再返回 return -1; } // 第二步:获取接口索引 if((ioctl(rawsock, SIOCGIFINDEX, &ifr)) == -1) { DBGMSG("NETP Device '%s' not found\n", device); close(rawsock); return -1; /* device not found */ } // 第三步:绑定套接字到接口 sll.sll_family = AF_PACKET; sll.sll_ifindex = ifr.ifr_ifindex; sll.sll_protocol = htons(protocol); if((bind(rawsock, (struct sockaddr *)&sll, sizeof(sll)))== -1) { DBGMSG("NETP Could not bind socket to device '%s'\n", device); close(rawsock); return -1; } DBGMSG("Setting PACKET_AUXDATA on socket.\n"); int option = 1; if (setsockopt(rawsock, SOL_PACKET, PACKET_AUXDATA, &option, sizeof(option)) == -1) { DBGMSG("Setting PACKET_AUXDATA on socket failed.\n"); } return rawsock; }
额外注意事项
- 确保你的进程拥有
CAP_NET_ADMIN权限(通常以root身份运行),这是修改网络接口MTU的必要条件,虽然你的代码中ioctl没有返回错误,但权限不足可能导致MTU设置未实际生效(部分内核版本会静默失败)。 - 如果你无法调整代码顺序,可以尝试在设置MTU后,调用
setsockopt设置SO_SNDBUF为MTU值,但这只能缓解部分场景,最可靠的还是调整操作顺序。
内容的提问来源于stack exchange,提问作者Kees-Jan Hermans
相关产品推荐
相关产品推荐

