ZijingWu
ZijingWu

Reputation: 3490

What's the difference between sockaddr, sockaddr_in, and sockaddr_in6?

I know that sockaddr_in is for IPv4, and sockaddr_in6 for IPv6. The confusion to me is the difference between sockaddr and sockaddr_in[6].

Some functions accept sockaddr and some functions accept sockaddr_in or sockaddr_in6, so:

And because the sizeof(sockaddr_in6) > sizeof(sockaddr) == sizeof(sockaddr_in).

One example is: we have a socket, and we want to get the string ip address of it (it can be ipv4 or ipv6).

We first call getsockname to get an addr and then call inet_ntop based on the addr.sa_family.

Is there anything wrong with this code snippet?

char ipStr[256];
sockaddr_in6 addr_inv6;
sockaddr* addr = (sockaddr*)&addr_inv6;
sockaddr_in* addr_in = (sockaddr_in*)&addr_inv6;

socklen_t len = sizeof(addr_inv6);
getsockname(_socket, addr, &len);

if (addr->sa_family == AF_INET6) {
    inet_ntop(addr_inv6.sin6_family, &addr_inv6.sin6_addr, ipStr, sizeof(ipStr)); 
    // <<<<<<<<IS THIS LINE VALID, getsockname expected a sockaddr, but we use 
    // it output parameter as sockaddr_in6.
} else {
    inet_ntop(addr_in->sin_family, &addr_in->sin_addr, ipStr, sizeof(ipStr));
}

Upvotes: 58

Views: 52459

Answers (2)

ZijingWu
ZijingWu

Reputation: 3490

In order to give more information other people may find useful, I have decided to answer my question although I initially did not intend to.

After some digging into the linux source code I have found the following : There are multiple protocols and they all implement getsockname. And each one has an underlying address data structure. For example, IPv4 has sockaddr_in, IPV6 has sockaddr_in6, the AF_UNIX socket has sockaddr_un. sockaddr is used as the common data struct in the signature of the linux networking

That API will copy the the socketaddr_inor sockaddr_in6 or sockaddr_un to a sockaddr base on another parameter length by memcpy.

And all those data structures begin with same type field sa_family.

Because of all this, the code snippet is valid, because both sockaddr_in and sockaddr_in6 have a sa_family field and then can be cast into the correct data structure to be used after a check on that sa_family field.

BTW, I'm not sure why the sizeof(sockaddr_in6) > sizeof(sockaddr), which cause allocate memory based on size of sockaddr is not enough for ipv6 (that is error-prone), but I guess it is because of history reason.

Upvotes: 32

linkdd
linkdd

Reputation: 1052

sockaddr_in and sockaddr_in6 are both structures where first member is a sockaddr structure.

According to the C standard, the address of a structure and its first member are the same, so you can cast the pointer to sockaddr_in(6) in a pointer to sockaddr.

Functions taking sockaddr_in(6) as parameter may modify the sockaddr part, and functions taking sockaddr as parameter just care about that part.

It's a bit like inheritance.

Upvotes: 36

Related Questions