Socket Address Structure for IPv4 and IPv6

    Most socket functions require a pointer to a socket address structure as parameter, and each protocol family defines its own socket address structure. The names of these structures all start with "sockaddr_" and end with a unique suffix corresponding to each protocol standard.
    The IPv4 socket address structure, also commonly referred to as the "Internet Socket Address Structure", is named "sockaddr_in" and is defined in the <netinet/in.h> header file. Below is its POSIX definition.
#include <netinet/in.h>

struct in_addr{
    in_addr_t    s_addr;          // 32-bit IPv4 address, network byte ordered
    /* Most other history fields are now deprecated */
};

struct sockaddr_in{
    uint8_t      sin_len;      // length of structure (16)
    sa_family_t sin_family; // AF_INET
    in_port_t    sin_port;     // 16-bit TCP or UDP port number, network byte ordered
    struct in_addr sin_addr;   // 32-bit IPv4 address, network byte ordered
    char   sin_zero[8];        // unused
};

    With this definition, a few general remarks about the socket address structure are required.
    1. The length field sin_len is added with 4.3 BSD-Remo to increase the support for the OSI protocol. It is because of this field that the handling of variable-length socket address structures is simplified. Not all manufacturers support this field, and the POSIX specification does not require this member.
    2. Even if there is a length field, we don't need to set and check it (unless routing sockets are involved), it is handled in the kernel by routines (like routing table processing code) that handle socket address structures from different protocol families used (in the Berkeley-derived implementation, the four socket functions (bind, connect, sendto, and sendmsg) that pass the socket address structure from the process to the kernel all call the sockargs function, which copies the socket from the process word address structure, and explicitly set its sin_len field to the length of the address structure passed as an argument to these four functions earlier. The five socket functions that pass the socket address structure from the kernel to the process (accept, recvfrom, recvmsg, getpeername, and getsockname) all set the sin_len field before returning to the process).
    3. The POSIX specification requires only three fields in this structure: sin_family, sin_port, and sin_addr. It is acceptable for POSIX-compliant implementations to define additional struct fields. In addition, almost all implementations increase the sin_zero field, so all socket address structures are at least 16 bytes in size.
    4. In the POSIX data types of the fields s_addr, sin_family and sin_port, the in_addr_t data type must be an unsigned integer type of at least 32 bits, in_port_t must be an unsigned integer type of at least 16 bits, and sa_family_t can be any unsigned integer type Integer type. In implementations that support a length field, sa_family_t is usually an 8-bit unsigned integer, and in implementations that do not support a length field, it is a 16-bit unsigned integer. The following table lists this data type commonly used in POSIX definitions.

    5. The socket address structure is only used on a given host: Although fields such as IP address and port number in the structure are used in communication between different hosts, the structure itself is not passed between hosts.
    The socket address structure is always passed by reference as an argument to any socket function, so this requires that the corresponding socket function must handle the socket address structure from any supported protocol family. To solve the problem of how to declare the data type of the passed pointer, the solution taken before the ANSI C definition (when there was no generic pointer type "void *") was to define a generic socket address as follows structure.
#include <sys/socket.h>

struct sockaddr{
    uint8_t sa_len;
    sa_family_t sa_family; // address family: AF_XXX value
    char            sa_data[14];       // protocol-specific address
};

    The socket function is then defined as follows:
        int bind(int, struct sockaddr *, socklen_t);
    This requires that before passing a pointer to a protocol-specific socket address structure to any socket function must be casted. For example:
            struct sockaddr_in serv; // IPv4 socket address structure.
            /* fill in serv() */
            bind(sockfd, (struct sockaddr *)&serv, sizeof(serv))
    ; Pointers to word address structures have another reason: the kernel must take the caller's pointer, convert it to type "struct sockaddr *", and then check the value of the sa_family field in it to determine the real type of the structure. From an application developer's perspective, the only purpose of these generic socket address structures is to perform casts on pointers to protocol-specific socket address mechanisms. It would be simpler if the "void *" pointer type were available, since no explicit type conversion would have to be done.
   
    After talking about the IPv4 socket address structure, let's talk about the IPv6 socket address structure.
#include <netinet/in.h>

struct in6_addr{
    uint8_t     s6_addr[16];   // 128-bit IPv6 address, network byte ordered.
};

#define SIN6_LEN               // required for compile-time tests

struct sockaddr_in6{
    uint8_t          sin6_len;       // length of this struct (28)
    sa_family_t sin6_family; // AF_INET6
    in_port_t        sin6_port;      // transport layer port, network byte ordered
    uint32_t         sin6_flowinfo;  // flow information, undefined
    struct in6_addr  sin6_addr;      // IPv6 address, network byte ordered
    uint32_t         sin6_scope_id;  // set of interfaces for a scope
};

    The following points should be noted here:
    1. If the system supports the length field in the socket address structure, the SIN6_LEN constant must be defined.
    2. The address family of IPv6 is AF_INET6, while that of IPv4 is AF_INET.
    3. The order of the fields in the structure is arranged so that if the structure of sockaddr_in6 is 64-bit aligned, the 128-bit sin6_addr field is also 64-bit aligned.
    4. The sin6_flowinfo field is divided into two parts: a) the low-order 20 bits are the flow label; b) the high-order 12 bits are reserved.
    5. For addresses with scope, the sin6_scope_id field identifies the scope. The most common is the interface index of the link-local address.
    A new generic socket address structure, sockaddr_storage, is defined in the IPv6 sockets API, which overcomes some of the shortcomings of the existing "struct sockaddr" and is sufficient to accommodate any socket address structure supported by the system.
#include <netinet/in.h>

struct sockaddr_storage{
    uint8_t       ss_len;     // length of this struct (implementation dependent)
    sa_family_t   ss_family;  // address family: AF_xxx value
 /* implementation-dependent elements to provide:
  * a) alignment sufficient to fullfill the alignment requirements of all
  *    socket address types that the system supports.
  * b) enough storage to hold any type of socket address that the system supports.
  */
};

    The difference between sockaddr_storage and sockaddr general socket address structure:
    1. If any socket address structure supported by the system has alignment requirements, then sockaddr_storage can meet the most stringent alignment requirements.
    2. sockaddr_storage is large enough to accommodate any socket address structure supported by the system.
    Note that with the exception of ss_family and ss_len (if any), the other fields in the sockaddr_storage structure are transparent to the user. The sockaddr_storage structure must be type-cast or copied into a socket address structure appropriate for the type of address given by the ss_family field in order to access other fields.

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=326170784&siteId=291194637