简答
我相信addrlen 值不会在内核中检查只是为了不浪费一些 CPU 周期,因为它应该始终是已知类型(例如 struct sockaddr),因此它应该始终具有已知和固定大小(为 16 个字节)。所以内核只是将addrlen重写为16,无论如何。
关于您遇到的问题:我不知道为什么会这样,但实际上似乎并不是尺寸不匹配。我很确定内核和用户空间都具有相同的结构大小,应该传递给getsockname() syscall(证明如下)。所以基本上你在这里描述的情况:
...这使得内核在设计地址 (&sa) 复制的数据 (sizeof(sin_addr)) 比该地址堆栈上变量的大小更多
并非如此。我只能想象如果它是真的有多少应用程序会失败。
详细说明
用户空间方面
在 iperf 源中,您有 sockaddr 结构 (/usr/include/bits/socket.h) 的下一个定义:
/* Structure describing a generic socket address. */
struct sockaddr
{
__SOCKADDR_COMMON (sa_); /* Common data: address family and length. */
char sa_data[14]; /* Address data. */
};
而__SOCKADDR_COMMON宏定义如下(/usr/include/bits/sockaddr.h):
/* This macro is used to declare the initial common members
of the data types used for socket addresses, `struct sockaddr',
`struct sockaddr_in', `struct sockaddr_un', etc. */
#define __SOCKADDR_COMMON(sa_prefix) \
sa_family_t sa_prefix##family
而sa_family_t定义为:
/* POSIX.1g specifies this type name for the `sa_family' member. */
typedef unsigned short int sa_family_t;
所以基本上sizeof(struct sockaddr) 总是 16 个字节(= sizeof(char[14]) + sizeof(short))。
内核端
在inet_getname() 函数中,您会看到addrlen 参数被下一个值重写:
*uaddr_len = sizeof(*sin);
sin 在哪里:
DECLARE_SOCKADDR(struct sockaddr_in *, sin, uaddr);
所以你看到sin 的类型是struct sockaddr_in *。这个结构体定义如下(include/uapi/linux/in.h):
/* Structure describing an Internet (IP) socket address. */
#define __SOCK_SIZE__ 16 /* sizeof(struct sockaddr) */
struct sockaddr_in {
__kernel_sa_family_t sin_family; /* Address family */
__be16 sin_port; /* Port number */
struct in_addr sin_addr; /* Internet address */
/* Pad to size of `struct sockaddr'. */
unsigned char __pad[__SOCK_SIZE__ - sizeof(short int) -
sizeof(unsigned short int) - sizeof(struct in_addr)];
};
所以sin 变量也是 16 字节长。
更新
我会尽量回复你的评论:
如果 getsockname 想要分配一个 ipv6 而不是这可能是它溢出缓冲区的原因
当为AF_INET6 套接字调用getsockname() 时,内核将计算(在getsockname() 系统调用中,通过sockfd_lookup_light() 函数)应该调用inet6_getname() 来处理您的请求。在这种情况下,uaddr_len 将被赋予下一个值:
struct sockaddr_in6 *sin = (struct sockaddr_in6 *)uaddr;
...
*uaddr_len = sizeof(*sin);
因此,如果您也在用户空间程序中使用sockaddr_in6 结构,则大小将相同。当然,如果您的用户空间应用程序将sockaddr 结构传递给getsockname 以获得AF_INET6 套接字,则会出现某种溢出(因为sizeof(struct sockaddr_in6) > sizeof(struct sockaddr))。但我相信您使用的iperf3 工具并非如此。如果是——首先应该修复的是iperf,而不是内核。