1、地址转换
1、网路地址转化函数inet_pton和inet_ntop
这两个函数是随IPv6出现的函数,对于IPv4地址和IPv6地址都适用,函数中p和n分别代表表达(presentation)和数值(numeric)。地址的表达格式通常是ASCII字符串,数值格式则是存放到套接字地址结构的二进制值。
#include <arpe/inet.h>
int inet_pton(int family, const char *strptr, void *addrptr); //将点分十进制的ip地址转化为用于网络传输的数值格式
返回值:若成功则为1,若输入不是有效的表达式则为0,若出错则为-1
const char * inet_ntop(int family, const void *addrptr, char *strptr, size_t len); //将数值格式转化为点分十进制的ip地址格式
返回值:若成功则为指向结构的指针,若出错则为NULL
(1)这两个函数的family参数既可以是AF_INET(ipv4)也可以是AF_INET6(ipv6)。如果,以不被支持的地址族作为family参数,这两个函数都返回一个错误,并将errno置为EAFNOSUPPORT.
(2)第一个函数尝试转换由strptr指针所指向的字符串,并通过addrptr指针存放二进制结果,若成功则返回值为1,否则如果所指定的family而言输入字符串不是有效的表达式格式,那么返回值为0.
(3)inet_ntop进行相反的转换,从数值格式(addrptr)转换到表达式(strptr)。inet_ntop函数的strptr参数不可以是一个空指针。调用者必须为目标存储单元分配内存并指定其大小,调用成功时,这个指针就是该函数的返回值。len参数是目标存储单元的大小,以免该函数溢出其调用者的缓冲区。如果len太小,不足以容纳表达式结果,那么返回一个空指针,并置为errno为ENOSPC。
int getsockname(int sockfd, struct sockaddr *localaddr,socklen_t *addrlen);
int getpeername(int sockfd, struct sockaddr *localaddr,socklen_t *addrlen);
getsockname可以获得一个与sockfd相关的本地地址。
getpeername可以获得一个与sockfd相关的对端地址。
2、地址格式
sa_family_t 通常为8位无符号整数
in_port_t TCP或UDP端口,一般为uint16_t
sockaddr和sockaddr_in开头一样,内存大小也一样,可以互相转换。
在使用bind或accept时,虽然参数为struct sockaddr *addr,但是可以取sockaddr_in6类型的地址,通过传递地址和长度socklen_t addrlen来进行处理。如果是IPV6操作,则socklen_t addrlen长度为sizeof(struct sockaddr_in6)。在使用inet_pton和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、IPV6绑定时报错处理
local_addr和local_port确定本地的IPV6地址和端口号,在bind时报错,检查IPV6地址和端口号均无问题,后来检查发现绑定IPV6地址时,需要指定sin6_scope_id,可以通过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():指定网络接口名称字符串作为参数;若该接口存在,则返回相应的索引,否则返回0
if_indextoname():指定网络接口索引以及一块长度至少为IF_NAMESIZE(16)字节的内存区域作为参数;若索引对应的网络接口存在,则在内存区域中返回该接口的名称字符串,否则返回NULL,并将errno设置为相应的值
if_nameindex():返回动态分配的struct if_nameindex结构数组,数组中的每一个元素分别对应一个本地网络接口;struct if_nameindex结构的if_index字段为接口索引,if_name字段为接口名称字符串;索引为0且名称字符串为NULL表示结构数组的末尾;调用出错时,返回NULL,并将errno设置为相应的值
if_freenameindex():通过if_nameindex()获取完毕接口名称与索引后,调用该函数以释放动态分配的内存区域
以上4个函数在系统的man文档中都可以查看相应的描述,且都是POSIX标准支持的,Linux内核可能未实现这些函数,或已实现但不同于POSIX标准。这些函数的原型声明与定义并未出现在CentOS 6.7的定制内核2.6.32-573.26.1.el6.x86_64以及原版内核2.6.32.5中,而是由系统的glibc-2.12实现:在glibc-2.12.2源码树中,函数的原型声明位于sysdeps/gnu/net/if.h与sysdeps/generic/net/if.h,函数的定义位于sysdeps/unix/sysv/linux/if_index.c中,本质上是对ioctl(2)的SIOCGIFNAME,SIOCGIFCONF,SIOCGIFINDEX等操作以及netlink套接字进行了封装
*/