为什么sockaddr_in需要零填充?

时间:2013-03-25 06:12:12

标签: c linux kernel

我用谷歌搜索了一些人说“要与struct sockaddr保持相同的大小”。但内核不会直接使用sockaddr(对吧?)。使用时。内核会把它重新归结为它。那么为什么需要零填充?

struct sockaddr {
    unsigned short    sa_family;    // address family, AF_xxx
    char              sa_data[14];  // 14 bytes of protocol address
};

struct sockaddr_in {
    short            sin_family;   // e.g. AF_INET, AF_INET6
    unsigned short   sin_port;     // e.g. htons(3490)
    struct in_addr   sin_addr;     // see struct in_addr, below
    char             sin_zero[8];  // zero this if you want to
};

struct in_addr {
    unsigned long s_addr;          // load with inet_pton()
};

4 个答案:

答案 0 :(得分:10)

我能找到的两个更相关的信息是

谈论清除字节

的代码片段
  

这是一个错误。我发现偶尔会发生这种情况。此错误可能导致应用程序中的未定义行为。

接下来的一些说明

  

大多数网络代码不使用sockaddr_in,它使用sockaddr。当您使用像sendto这样的函数时,您必须显式地将sockaddr_in或您使用的任何地址转换为sockaddr。 sockaddr_in与sockaddr的大小相同,但内部大小相同,因为有轻微的黑客攻击。

     

那个黑客是sin_zero。实际上sockaddr_in中有用数据的长度比sockaddr短。但是使用一个小缓冲区在sockaddr_in中填充差异;缓冲区是sin_zero。

最后,可以在各个地方找到的信息

  

在某些架构上,它不会导致任何不清除sin_zero的问题。但在其他架构上它可能会。规范要求清除sin_zero,所以如果你打算现在和将来你的代码都没有bug,你必须这样做。

回答问题

  

为什么我们需要这个8字节的填充?

和答案

  

Unix网络编程3.2章说“POSIX规范   在结构中只需要三个成员:sin_family,sin_addr和   sin_port。符合POSIX的实现可以定义   其他结构成员,这对于Internet套接字地址是正常的   结构体。几乎所有实现都添加了sin_zero成员以便所有套接字   地址结构的大小至少为16个字节。 “

     

它有点像结构填充,可能保留给中的额外字段   未来。你将永远不会使用它,就像评论一样。

与第一个链接一致。清除字节告诉接收者“我们这边没有使用那些字节”。

答案 1 :(得分:2)

由于struct sockaddr_in需要强制转换为struct sockaddr,因此它必须保持相同的大小,sin_zero是一个未使用的成员,其唯一目的是将结构填充到16个字节(sock_addr的大小)。此填充大小可能因地址系列而异。例如;

struct sockaddr_in {
   short int sin_family;     // Address family, AF_INET 
   unsigned short int sin_port;     // Port number 
   struct in_addr sin_addr;     // Internet address 
   unsigned char sin_zero[8];     // For padding, to make it same size as struct sockaddr 
}; 

现在选择具有不同结构成员的Xerox NS系列:

struct sockaddr_ns {
    u_short sns_family;        // Address family, AF_NS 
    struct ns_addr sns_addr;        // the 12-byte XNS address 
    char sns_zero[2];        // unused except for padding 
};

答案 2 :(得分:0)

发生结构填充是因为结构的成员必须出现在Correce字节边界,为此,编译器会填充填充字节(如果正在使用位字段,则为bit),以便结构成员出现在正确的位置。另外,结构的大小必须使得在结构的阵列中所有结构在存储器中正确对准。

因此,可能需要忽略内存泄漏。

答案 3 :(得分:0)

struct sockaddr是此结构的抽象,不完整版本,只有该系列。 struct sockaddr_in是此结构的IPv4版本。它只使用前8个字节。 struct sockaddr_in6是此结构的IPv6版本,并且更大。填充允许较小的结构以适应该结构的最大变化,因此缓冲区不会过小。

当您将地址传递给函数或系统调用时,额外的字节不是必需的。但是检索地址时,您需要为结果提供结构地址。该结构需要是所有可能变化中最大的。如果不是 - 想象你提供了一个IPv4版本,但得到了一个IPv6地址 - 那么结果将超出结构并破坏内存中的隔壁。

为避免此内存损坏,大多数相关函数将结构大小作为参数。但现在,当您传递IPv4版本及其太小的尺寸时,您最终会得到一个未完全填充的IPv6版本的结构。看看这个家庭,你可以看到它是IPv6。但是,如果将结构转换为IPv6并尝试使用它,则内容错误,因为结构太小而无法包含完整有效的数据。

填充较小的结构可以避免这些障碍,并避免任何相关的潜在安全问题。