Summary of IPV6 Address Processing

1. Address translation

1. Network address conversion functions inet_pton and inet_ntop
These two functions are functions that appear with IPv6, and are applicable to both IPv4 addresses and IPv6 addresses. In the functions, p and n represent expression (presentation) and value (numeric) respectively. The expression format of the address is usually an ASCII string, and the numerical format is the binary value stored in the socket address structure.

#include <arpe/inet.h>
int inet_pton(int family, const char *strptr, void *addrptr);     //Convert the dotted decimal ip address into a numerical format for network transmission
        Return value: 1 if successful, 0 if the input is not a valid expression, or 0 if an error occurs-1
 
const char * inet_ntop(int family, const void *addrptr, char *strptr, size_t len);     //Convert the numeric format to the dotted decimal ip address format

        Return value: pointer to structure on success, or on error NULL

(1) The family parameter of these two functions can be either AF_INET (ipv4) or AF_INET6 (ipv6). If an unsupported address family is given as the family argument, both functions return an error and set errno to EAFNOSUPPORT.
(2) The first function tries to convert the string pointed to by the strptr pointer, and stores the binary result through the addrptr pointer. If successful, the return value is 1, otherwise, if the specified family is concerned, the input string is not a valid expression format, then the return value is 0.

(3) inet_ntop performs the opposite conversion, from the numerical format (addrptr) to the expression (strptr). The strptr parameter of the inet_ntop function cannot be a null pointer. The caller must allocate memory for the target storage unit and specify its size. When the call is successful, this pointer is the return value of the function. The len argument is the size of the destination memory unit, lest the function overflow its caller's buffer. If len is too small to hold the result of the expression, a null pointer is returned and errno is set to ENOSPC.

int getsockname(int sockfd, struct sockaddr *localaddr,socklen_t *addrlen);
int getpeername(int sockfd, struct sockaddr *localaddr,socklen_t *addrlen);

getsockname can get a local address related to sockfd.

getpeername can get a peer address related to sockfd.

2. Address format

sa_family_t is usually an 8-bit unsigned integer

in_port_t TCP or UDP port, usually uint16_t

sockaddr and sockaddr_in have the same beginning and the same memory size, and can be converted to each other.

When using bind or accept, although the parameter is struct sockaddr *addr, you can take the address of sockaddr_in6 type, and process it by passing the address and length socklen_t addrlen. If it is an IPV6 operation, the length of socklen_t addrlen is sizeof(struct sockaddr_in6). There are similar problems when using inet_pton and inet_ntop.

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

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

#define INET_ADDRSTRLEN 16
#define INET6_ADDRSTRLEN 46
typedef unsigned short int sa_family_t

/* Structure describing a generic socket address.  */
struct sockaddr
{
 uint8_t sa_len;
 sa_family_t sa_family;    /* Common data: address family and length.  */
 char sa_data[14];           /* Address data.  */
};

/* /usr/include/netinet/in.h */
/* Structure describing an Internet socket address.  */
struct sockaddr_in
{
 uint8_t sin_len;
 sa_family_t sin_family; 
 in_port_t sin_port;         /* Port number.  */
 struct in_addr sin_addr;    /* Internet address.  */

 /* Pad to size of `struct sockaddr'.  */
 unsigned char sin_zero[8];
};

typedef uint32_t in_addr_t;
struct in_addr
  {
    in_addr_t s_addr;
  };



struct sockaddr_in6
{
 uint8_t sin6_len;
 sa_family_t sin6_family; 
 in_port_t sin6_port;        /* Transport layer port # */
 uint32_t sin6_flowinfo;     /* IPv6 flow information */
 struct in6_addr sin6_addr;  /* IPv6 address */
 uint32_t sin6_scope_id;     /* IPv6 scope-id */
};

struct in6_addr
  {
	uint8_t	s6_addr[16];
  };

3. Error handling when IPV6 binding is reported

local_addr and local_port determine the local IPV6 address and port number, an error is reported in bind, and there is no problem in checking the IPV6 address and port number. Later, it is found that when binding the IPV6 address, sin6_scope_id needs to be specified, and the index corresponding to the port name can be queried through if_nametoindex .

Cannot bind socket (Cannot assign requested address)
 

struct sockaddr_in6 src;
int sock;
int opt = 1;
struct in6_addr ipv6_inaddr;
bzero(&src, sizeof(src));


if ((sock = socket(AF_INET6, SOCK_STREAM, 0)) <= 0)
{
	printf("open Socket error (%s)\n", strerror(errno));
    return 0;
}
  
src.sin6_family = AF_INET6;
ret = setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
if(ret == -1)
{
    printf("setsockopt fail %s\n", strerror(errno));
    return 0;
}
  
/* bind socket */
src.sin6_family = AF_INET6;
src.sin6_port = htons((unsigned short) local_port); /* short, network byte order */

if(inet_pton(AF_INET6,local_addr,(void *)&ipv6_inaddr)!=1)
{
    printf("Cannot conver local_addr %s, error %s\n", local_addr, strerror(errno));
    src.sin6_addr = in6addr_any;  //
}
else
{
    src.sin6_addr = ipv6_inaddr;
}

src.sin6_scope_id = interface;
  
if (bind(sock, (struct sockaddr *) &src, sizeof(struct sockaddr_in6)) == -1)
{
    printf("Cannot bind socket (%s)\n", strerror(errno));	
    return 0;
}
#include <net/if.h>

unsigned if_nametoindex(const char *ifname);
char *if_indextoname(unsigned ifindex, char *ifname);
struct if_nameindex *if_nameindex(void);
void if_freenameindex(struct if_nameindex *ptr);

/*
if_nametoindex(): Specify the network interface name string as a parameter; if the interface exists, return the corresponding index, otherwise return 0

if_indextoname(): Specify the network interface index and a memory area with a length of at least IF_NAMESIZE(16) bytes as parameters; if the network interface corresponding to the index exists, return the name string of the interface in the memory area, otherwise return NULL and set errno for the corresponding value

if_nameindex(): Returns a dynamically allocated struct if_nameindex structure array, each element in the array corresponds to a local network interface; the if_index field of the struct if_nameindex structure is the interface index, and the if_name field is the interface name string; the index is 0 and the name string is NULL The end of the structure array; when the call fails, return NULL and set errno to the corresponding value

if_freenameindex(): After obtaining the interface name and index through if_nameindex(), call this function to release the dynamically allocated memory area

The above four functions can be viewed in the corresponding descriptions in the system man file, and they are all supported by the POSIX standard. The Linux kernel may not implement these functions, or they may have been implemented but different from the POSIX standard. The prototype declaration and definition of these functions do not appear in the custom kernel 2.6.32-573.26.1.el6.x86_64 of CentOS 6.7 and the original kernel 2.6.32.5, but are implemented by the system's glibc-2.12: in glibc-2.12.  2 In the source tree, the prototype declaration of the function is located in sysdeps/gnu/net/if.h and sysdeps/generic/net/if.h, and the definition of the function is located in sysdeps/unix/sysv/linux/if_index.c, which is essentially Encapsulate ioctl(2) operations such as SIOCGIFNAME, SIOCGIFCONF, SIOCGIFINDEX and netlink sockets
*/


 

Tags: Linux network server

Posted by CG-Sodar on Sat, 17 Dec 2022 00:51:48 +0300