2

I'm new to C and socket programming, still struggling in sockaddr_in structure:

struct sockaddr_in {
    short            sin_family;   /* Protocol family (always AF_INET) */
    unsigned short   sin_port;     /* Port number in network byte order */
    struct in_addr   sin_addr;     /* IP address in network byte order */
    unsigned char    sin_zero[8];  /* Pad to sizeof(struct sockaddr) */
};

and

struct in_addr {
   uint32_t s_addr; /* Address in network byte order (big-endian) */
};

I'm not sure about the need of sin_zero other than alignment requirement, below is my assumption, please correct me if I was wrong:

Assumption 1-since sin_addr is 32 bit for ipv4 address, if we need a ipv6 address, the the first 4 bytes of sin_zero will be assigned and combined with sin_addr to form a 64 bit ipv6 address.

Assumption 2-If my assumption is correct, then is the reason why we need to specific the length of a socket address such as:

int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);

is because the possibility of ipv6 addresses? so if ipv4 is used, then addrlen should be 8, and if ipv6 is used, then addrlen should be 12?

marc_s
  • 732,580
  • 175
  • 1,330
  • 1,459
  • According to https://stackoverflow.com/questions/18609397/whats-the-difference-between-sockaddr-sockaddr-in-and-sockaddr-in6 `sizeof(sockaddr_in6) > sizeof(sockaddr) == sizeof(sockaddr_in)` The reason sockaddr_in needs to be at least as large as sockaddr is so that you can aallocate a sockaddr_in and then cast it to a sockaddr when passing it to a library function - it's a bit like inheritance for C. sockaddr_in6 is already larger than sockaddr so it doesn't need any padding. – Jerry Jeremiah Oct 08 '20 at 04:31
  • Related: https://stackoverflow.com/questions/21099041/why-do-we-cast-sockaddr-in-to-sockaddr-when-calling-bind/21099196 – Jerry Jeremiah Oct 08 '20 at 04:35

1 Answers1

1

There are multiple protocol families. Each family has its own address structure.

Example: AF_INET uses sockaddr_in, AF_INET6 uses sockaddr_in6, AF_UNIX uses sockaddr_un, etc. But sockaddr is the base structure. All these structures must be type-cast to sockaddr while binding/connecting a socket.

int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);

int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);

Let's look at the structures of sockaddr_in and sockaddr:

struct sockaddr_in {
    short            sin_family;   /* Protocol family (always AF_INET) */
    unsigned short   sin_port;     /* Port number in network byte order */
    struct in_addr   sin_addr;     /* IP address in network byte order */
    unsigned char    sin_zero[8];  /* Pad to sizeof(struct sockaddr) */
};


struct in_addr {
   uint32_t s_addr; /* Address in network byte order (big-endian) */
};

The structure of sockaddr is:

struct sockaddr 
{
    sa_family_t sa_family;
    char        sa_data[14];
}

Look at the sizes of the elements in the two structures sockaddr_in and sockaddr.

The first element in both structures is the same and occupies the same memory.

sin_port --> 2 bytes
sin_addr --> 4 bytes
sin_zero[8] --> 8 bytes
Total = 14 bytes (equal to size of sa_data[14])

We add padding bytes to make their structure sizes equal.

reference:
https://man7.org/linux/man-pages/man2/bind.2.html
https://man7.org/linux/man-pages/man2/connect.2.html

Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
Krishna Kanth Yenumula
  • 2,533
  • 2
  • 14
  • 26
  • 1
    Thanks for your answer. But why we need to specify the length `addrlen` to `bind` functio, if both `sockaddr_in` and `sockaddr` have the same size? –  Oct 08 '20 at 13:01
  • @amjad See this answers: https://stackoverflow.com/questions/30251960/why-socklent-t-is-used-in-accept-in-socket-programming – Krishna Kanth Yenumula Oct 08 '20 at 18:30
  • 1
    @amjad Other types of address structures DO NOT have the same size as `sockaddr`, for instance `sockaddr_in6` and `sockaddr_un` are much larger. – Remy Lebeau Oct 08 '20 at 19:38