如何理解存储在 sockaddr_storage 中的 IP 的 IP 版本
Posted
技术标签:
【中文标题】如何理解存储在 sockaddr_storage 中的 IP 的 IP 版本【英文标题】:How to understand IP version of an IP stored in sockaddr_storage 【发布时间】:2021-08-23 05:05:14 【问题描述】:我是 IPV6 的新手,我需要将我的项目从 IPV4 样式移植到双栈支持样式,但我遇到了困难。我需要了解存储在 sockaddr_storage 中的地址的 IP 版本,而无需检查其系列。
我编写了一个代码并尝试使用“inet_ntop”函数,但它将所有族相互转换而没有任何错误。例如,如果我尝试使用 AF_INET 进行转换,它会将“fe80::9110:403c:1f99:eacb”IP 转换为“0.0.0.0”,并且由于它是有效的 IPV4 地址,因此我的代码会执行错误的操作。
请检查我下面的代码;
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <stdbool.h>
#include <netdb.h>
#include <arpa/inet.h>
#include <errno.h>
#include "ipv6_util.h"
#define SUCCESS (EXIT_SUCCESS)
#define FAIL (EXIT_FAILURE)
#define UNUSED(__val__) (void)(__val__)
#define PORT 9999
#define CONN_COUNT 1
#define BUFFER_SIZE 1024
#define QUIT_MESSAGE "quit"
#define ACK_MESSAGE "ack"
#define IPV6_IP_1 "::1"
#define IPV4_IP_1 "127.0.0.1"
#define IPV4_IP_2 "192.168.2.254"
#define IPV4_IP_3 "100.100.100.100"
#define IPV6_IP_2 "fe80::9110:403c:1f99:eacb"
void ipv6_util_get_ipadd_ver_from_addr2(const struct sockaddr_storage *ipaddress)
char address_buf[128] = 0;
memset(address_buf, 0, 128);
if (inet_ntop(AF_INET, &((struct sockaddr_in *)ipaddress)->sin_addr, address_buf, 128))
printf("it is ipv4\n");
printf("buff: %s %d\n", address_buf, __LINE__);
//return;
memset(address_buf, 0, 128);
if (inet_ntop(AF_INET6, &((struct sockaddr_in6 *)ipaddress)->sin6_addr, address_buf, 128))
printf("it is ipv6\n");
printf("buff: %s %d\n", address_buf, __LINE__);
//return;
int main(void)
struct sockaddr_storage dumm;
memset(&dumm, 0, sizeof(struct sockaddr_storage));
printf("\n");
memset(&dumm, 0, sizeof(struct sockaddr_storage));
if (inet_pton(AF_INET6, IPV6_IP_1, &((struct sockaddr_in6 *)&dumm)->sin6_addr) != 1)
printf("error %d\n", __LINE__);
ipv6_util_get_ipadd_ver_from_addr2(&dumm);
printf("\n\n");
memset(&dumm, 0, sizeof(struct sockaddr_storage));
if (inet_pton(AF_INET, IPV4_IP_1, &((struct sockaddr_in *)&dumm)->sin_addr) != 1)
printf("error %d\n", __LINE__);
ipv6_util_get_ipadd_ver_from_addr2(&dumm);
printf("\n\n");
memset(&dumm, 0, sizeof(struct sockaddr_storage));
if (inet_pton(AF_INET, IPV4_IP_2, &((struct sockaddr_in *)&dumm)->sin_addr) != 1)
printf("error %d\n", __LINE__);
ipv6_util_get_ipadd_ver_from_addr2(&dumm);
printf("\n\n");
memset(&dumm, 0, sizeof(struct sockaddr_storage));
if (inet_pton(AF_INET6, IPV6_IP_2, &((struct sockaddr_in6 *)&dumm)->sin6_addr) != 1)
printf("error %d\n", __LINE__);
ipv6_util_get_ipadd_ver_from_addr2(&dumm);
printf("\n\n");
memset(&dumm, 0, sizeof(struct sockaddr_storage));
if (inet_pton(AF_INET, IPV4_IP_3, &((struct sockaddr_in *)&dumm)->sin_addr) != 1)
printf("error %d\n", __LINE__);
ipv6_util_get_ipadd_ver_from_addr2(&dumm);
printf("\n\n");
return SUCCESS;
return 0;
对于这段代码,我得到了这个输出;
it is ipv4
buff: 0.0.0.0 39
it is ipv6
buff: ::1 46
it is ipv4
buff: 127.0.0.1 39
it is ipv6
buff: :: 46
it is ipv4
buff: 192.168.2.254 39
it is ipv6
buff: :: 46
it is ipv4
buff: 0.0.0.0 39
it is ipv6
buff: fe80::9110:403c:1f99:eacb 46
it is ipv4
buff: 100.100.100.100 39
it is ipv6
buff: :: 46
所以我找不到它的原始版本。我该怎么做?
感谢您的帮助。
【问题讨论】:
我怀疑这可能是 XY 问题。尽管这样做是有正当理由的,但用户空间代码需要解释struct sockaddr
的内容有点不寻常。但是,如果您的程序确实有这样的需求,那么为什么您需要在不检查 sockaddr 的家庭的情况下这样做呢?
似乎这种方式更适合我尽可能少地更改源代码,但我从您的 cmets 了解到,这种方式不应该是一个用例。我得到了它。由于我的问题不是一个有效的问题,我真的很想知道为什么“inet_ntop”会这样,有什么办法可以做我想做的事,也许是一种解决方法?
【参考方案1】:
地址族正好在ss_family
或sa_family
成员中。
使用getaddrinfo
将sockaddr
转换为人类可读的格式。
您不应该使用sockaddr_storage
作为函数的参数,而是使用sockaddr
并将sockaddr_storage
转换为sockaddr
。
【讨论】:
OP 确实说他想做他想做的事情“而不检查它的家庭”。不过,公平地说,这个要求听起来是人为的。 如果我没记错的话,一个IPV6地址有128位,我的意思是16个字节。但是,sockaddr 结构也是 16 字节长,但它还包括一些额外的字段,例如其中的家庭数据。那么,既然没有足够的空间容纳它,它怎么能覆盖 IPV6 地址呢?那时我很困惑。sockaddr_storage
应该足够大以包含任何sockaddr
类型。因此,它是用作字节的 storage 的正确类型。但是,接受该类型作为函数的参数是非常不寻常的。我的意思是,例如看getaddrinfo
和getnameinfo
。由于您传递的是指针,因此实际上并不重要,因为它基本上是一个内存地址。该函数的内部可以根据需要使用它。您可以使用switch (sa->sa_family)
或getnameinfo
就可以了,没有任何问题。【参考方案2】:
我按照Cheatah 的建议尝试了getnameinfo 函数,结果与我原来的帖子相同。我的意思是,我似乎必须在某些时候将 switch-case 与 sa_familiy 一起使用。
谢谢大家。
【讨论】:
以上是关于如何理解存储在 sockaddr_storage 中的 IP 的 IP 版本的主要内容,如果未能解决你的问题,请参考以下文章
从 sockaddr_storage 中提取 IP 地址和端口信息
将 IPv4/IPv6 地址和端口设置为 sockaddr_storage 结构