当前位置: 首页 > 工具软件 > IPv6 CARE > 使用案例 >

编写一个简单r程序_编写一个简单的IPv6程序

苏乐
2023-12-01

编写一个简单r程序

在当今的网络世界中,IPv4是网络的基础,但是在最近的十年中,由于以下原因而出现了问题:

  • 担心最快在2002年就用完IPv4地址空间
  • 担心全局路由表中的容量不足

对于这些简单但严重的问题,网络地址转换(NAT)和无类域间路由(CIDR)(请参阅相关概念 )已被用作权宜之计。 IPv6(也称为IPng(新一代IP))已被视为长期解决方案。

还计划对IPv4进行以下增强:

  • 简化标题处理
  • 支持扩展选项
  • 服务质量功能,身份验证和隐私功能,流控制功能和自动配置等增强功能

所有这些更改背后的关键规则是IPv6应用程序应继续与IPv4应用程序一起使用。 底线是IPv6应该支持混合的IPv6和IPv4环境。

本文将帮助您快速理解IPv6的概念,并在其中编写一个简单的程序。 让我们从IPv6寻址开始。

IPv6寻址

IPv6中的地址类型:Anycast是新手

在IPv6中,存在三种类型的地址-单播,多播和任播。 我们在IPv4中拥有单播地址,并且许多系统也支持多播。 Anycast是IPv6定义的一种新型地址。

  1. 单播:这类似于单个接口上的任何常规IP地址(例如, en0上的IPv4地址9.185.101.1)。
  2. 组播:发送到组播地址的数据包将传递到该地址标识的所有接口。 由于多播类型可以处理广播地址类型,因此没有广播地址类型。
  3. 任播:发送到任播地址的数据包将传递到该地址标识的接口之一(根据路由协议对距离的度量,“最接近”的一个)。 让我们考虑一种可以使用任播地址的情况-连接到服务提供商的路由器。 假定服务提供商可以为您提供一组要连接的地址,然后选择其中一个地址。 使用IPv6,服务提供商可以为您提供一个任播地址,您将使用该地址自动连接到“最近的”地址。 这是IPv6中的一项新功能,关于其实现仍然有很多争论。

IPv6地址的写入方式:发生了什么变化

有三种将IPv6地址表示为文本字符串的常规形式:

1.主要形式:首选形式是x:x:x:x:x:x:x:x ,其中“ x”是地址的八个16位块的十六进制值。 两个例子:

fe80:0:0:0:207:30ee:edcb:d05d
	1080:0:0:0:1:700:200B:417C

第一个地址中有八个十六进制字段:

  1. fe80
  2. 0
  3. 0
  4. 0
  5. 207
  6. 30ee
  7. edcb
  8. d05d

在IPv6中,我们不在字段中写入前导零。 也就是说,上面的第二个字段只是写为“ 0”,而不是“ 0000”。 请注意,每个字段中有4个十六进制数字。 每个十六进制数字是4位(并且可以表示0-F的十六进制值)。 这意味着每个字段中有16位(4个十六进制数字x 4个数字/位数)。 IPv6地址的总大小为128位(8个十六进制字段x每个字段16位)。

2.上述地址的另一种表示形式:由于某些分配某些样式的IPv6地址的方法,地址通常包含零位长字符串。 为了使写入包含零位的地址更容易,可以使用特殊语法来压缩零。 ::的使用表示16位零的多个组。 ::只能在地址中出现一次,也可以用于压缩地址中的前导零。 例如:

  • FF01:0:0:0:0:0:0:101是可以写为FF01::101的多播地址。
  • 0:0:0:0:0:0:0:1是可以写为::1的回送地址。

3.对于双重环境:处理IPv4和IPv6节点的混合环境时,有时更方便的另一种形式是x:x:x:x:x:x:dddd ,其中“ x”是十六进制值地址的六个高阶16位元中的一个,“ d”是地址的四个低阶8位元(标准IPv4表示)的十进制值-即前96个这些位表示为6 x 16位十六进制字段,最后32位为4 x 8位十进制数字。 例如:

::9.184.201.1 
      ::ffff:9.184.209.2

IPv6地址前缀
IPv6地址前缀表示地址的网络部分,并用符号ipv6-address/prefix-length

举个例子:

fe80::206:29ff:fedc:e06e/64

在这种情况下, fe80::206:29ff:fedc:e06e是地址,而64是前缀长度。 这两个一起为我们提供了地址前缀。 在示例中,指定64表示我们采用上述128位地址的前64位来标识该地址的网络部分。

这就提出了几个问题:

  1. 以上表示形式如何解决我们要解决的两个主要问题:
    • 可用地址空间是否有限?
    • 大型全局路由表?
  2. 如何在IPv4地址中识别网络?
  3. 为什么应允许在IPv6地址中指定前缀长度?
  4. 如何在IPv4地址中指定前缀?
  5. 造成什么问题呢?

这里是答案:

地址空间:关于地址空间问题,IPv6工作中的关键人物之一Robert M Hinden解释说:

IPV6支持的地址是IPv4地址的位数的四倍(128对32)。 这是IPv4地址空间大小(2 ^ 32)的40亿乘以40亿乘以40亿(2 ^ 96)倍。 结果是:

340,282,366,920,938,463,463,374,607,431,768,211,456

这是一个非常大的地址空间。 从理论上讲,这大约是地球每平方米表面的665,570,793,348,866,943,898,599个地址(假设地球表面为511,263,971,197,990平方米)。

阶级敌人:现在让我们来解决有关IPv4和IPv6中的地址前缀的问题。 将IPv4地址空间划分为A,B,C和D类网络引起了一些问题。 在IPv4中,网络部分由地址类别固定。 让我们用一个例子来说明我们的观点。 A类地址可以在其128个网络中的每个网络上支持1600万个主机(因为在A类地址中,最高位设置为0;后7位用于网络部分;其余24位用于用于本地地址)。 现在,如果给一个组织分配了A类地址,并且没有1600万个主机,则剩余的地址空间将被浪费。 还要注意,不能给每个人A类地址,因为只有127类。必须引入CIDR才能解决此问题并延长IP寿命。 这意味着地址的网络部分不应固定。 显然需要特定于组织的网络规模。 这意味着地址的网络部分不应固定。 通过允许用户在地址前缀中指定网络位,可以在IPv6中实现此可变前缀长度。 例如,在地址fe80::206:29ff:fedc:e06e/64 - ,数字64表示网络部分,可以更改。 在这里,我们可以选择网络部分。 这是灵活的,与IPv4始终固定的不同。

路由表: Internet中的路由随时间增长。 骨干路由器在1984年接近其极限。如果不引入CIDR来解决全球骨干路由器的空间问题,它们将被终止。

CIDR技术:那么IPv6如何解决这个问题? 解决此问题的技术是允许满足特定组织需求的地址前缀。 此技术基本上是在CIDR中引入的。 在IPv6中,前缀或网络部分也由用户指定的网络前缀指定。 这有助于聚合大量IP地址并为组织指定一条路由。 如果组织具有多个网络,则在IPv4的情况下,将在全局路由表中指定许多网络前缀。 对于IPv6,我们可以简单地给出一个更高级别的路由来代表整个组织,因为我们可以通过改变它来缩小和扩展网络前缀。 这有助于使全局表保持较小。 这种设置在IPv4中不存在。 (有关CIDR的更多信息,请参阅相关概念 )。

IPv6中的自动配置:即插即用

什么是自动配置? 首先要做的是用IPv6地址设置一台机器。 有一个在IPv6的一个有趣的功能,称为无状态自动配置多数民众赞成由RFC 2462定义(参见相关主题 )。 该RFC声明您的主机应该能够为您提供一个自动的,全球唯一的IPv6地址。

例如,在AIX中,只需启动机器并从#提示符下键入autoconf6 -v ,您将看到机器自动检测子网并为您分配有效的IPng地址。

我运行了ifconfig来查看IPv6地址。 这是我的AIX机器上ifconfig -a的部分输出:

inet 9.184.209.3 netmask 0xffffff00 broadcast 9.184.209.255
	inet6 fe80::207:30ee:edcb:d05d/64

当我运行autoconf -v6时,我得到了inet6地址( inet6en0上定义)。 现在,该计算机在同一物理以太网接口上同时具有IPv6和IPv4。

怎么做? 用非常简单的术语来说,链路层地址用作获取IPv6地址以及主机和路由器进行通信的基础,以便主机可以了解有关子网的信息。 (有关更详细的讨论,请参考RFC。)

其他操作系统怎么样? 其他UNIX实现具有类似AIX的类似IPv6自动配置命令。 还有许多IPv6的自由软件实现(请参阅参考资料 )。

我可以手动配置吗? 是。 您还可以使用ifconfig配置IPv6地址。 计划网络以分配网络前缀很重要。

隧道和映射的IPng地址:过渡应平稳

过渡问题的例子
考虑这种情况。 我们有一个现有的IPv4环境,其中只有IPv4主机和路由器。 现在,我们在网络中添加了一些IPv6路由器和主机。 这些主机中的一些具有处理IPv6和IPv4地址的能力,其中一些是纯IPv6或纯IPv4。 如果我们必须编写在此环境中运行的应用程序,则该应用程序的客户端和服务器应该能够处理所有可能的客户端-服务器对。 也就是说,客户端或服务器可以完全是IPv4,完全是IPv6,或者既支持IPv6又支持IPv4。 (有关详细说明,请阅读RFC 2893:“主机和路由器的转换机制” -请参阅参考资料 。)

什么是隧道技术? 再次,让我们举个例子。 我们需要在IPv4网络上传送IPv6数据包。 我们该如何进行? 简单-我们只是将IPv6数据包封装在IPv4数据包中,然后通过IPv4网络发送它。 这称为隧道 。

配置的隧道:我们需要配置位于IPv4网络入口点的主机,以便它可以将IPv6数据包转换为IPv4数据包。 另外,需要配置作为IPv4网络出口点的节点,以便它可以将数据包转换回IPv6数据包。 这称为配置隧道 。

自动隧道:如果主机具有动态进行此转换的能力,则称为自动隧道 。

协议中对自动隧道的支持:为使用此技术的节点分配了特殊的IPv6单播地址。 这些地址以低阶32位传送IPv4地址。 这种类型的地址称为IPv4兼容IPv6地址 ,其格式如下:

|                80 bits               | 16 |      32 bits        |
     +--------------------------------------+--------------------------+
     |0000..............................0000|0000|    IPV4 ADDRESS     |
     +--------------------------------------+----+---------------------+

还定义了保存嵌入式IPv4地址的第二种IPv6地址。 此地址用于表示仅IPv4节点( 不支持IPv6的节点)的地址作为IPv6地址。 这种类型的地址称为“ IPv4映射的IPv6地址”,其格式为:

|                80 bits               | 16 |      32 bits        |
     +--------------------------------------+--------------------------+
     |0000..............................0000|FFFF|    IPV4 ADDRESS     |
     +--------------------------------------+----+---------------------+

映射地址的用法
如果要编写支持IPv6的客户端,则会遇到此问题:是发送IPv6数据包还是发送IPv4数据包? 您不能保证有关基础网络。 您与之建立连接的下一台计算机可以是IPv6计算机,IPv4计算机或双主机。

假设负责路由连接的应用程序能够知道下一台机器是IPv6机器还是IPv4机器。 在这种情况下,如果我们可以在内部包含IPv4地址的IPv6地址,那将是非常有用的。 最好有一种机制(映射的v4地址中的ffff. )来告诉我们该地址是否指向纯IPv4节点。 这将有助于我们对要发送哪种类型的数据包做出适当的决定。 我们在最后一节中的讨论应该使这一点更加清楚。

将IPv4应用程序移植到IPv6

将IPv4应用程序移植到IPv6时,需要考虑以下事项:

  • 已经定义了可以容纳128位地址的sockaddr_in6结构和in6_addr结构。 检查您是否正在使用相关的IPv6结构。
  • 必须将INADDR_ANYINADDR_LOOPBACK修改为in6addr_anyin6addr_loopback进行分配。 IN6ADDR_ANY_INITIN6ADDR_LOOPBACK_INIT宏可能会有所帮助。
  • 使用AF_INET6而不是AF_INET
  • 请注意,有些结构和程序适用于IPv6和IPv4。 其中一个链接点到移植例子,这个链接可以被称为(见“移动到IPv6” 相关主题 )。
  • 请注意,将某些功能用于IPv6时,不需要更改语法。 使用这些函数的唯一区别是必须将sockaddr_in6sockaddr*结构。

以下宏和函数用于编写支持IPv6的应用程序:

  • IN6_IS_ADDR_V4MAPPED可用于确定IPv6地址是否为IPv4映射的地址。
  • gethostbyname通过其名称和地址族检索网络主机条目。
  • getaddrinfo返回与指定服务位置有关的地址信息。
  • getnameinfo返回与提供的IP地址和端口号关联的文本字符串。
  • inet_pton将文本形式的指定地址转换为其等效的二进制地址。
  • inet_ntop将指定的二进制地址转换为适合表示的文本等效项。
  • getaddrinfogetnameinfo均可用于检索与IPv4和IPv6地址有关的信息。 inet_ptoninet_ntop都可以转换IPv4和IPv6地址。 这意味着,在“支持IPv6的”应用程序,你不需要为使用inet_addrinet_ntoa
  • 以下功能在用于IPv6时不需要更改语法: bindconnectsendmsgsendtoacceptrecvfromrecvmsggetpeernamegetsockname ,尽管这些函数的代码已被修改。

编写一个简单的IPv6客户端

现在让我们看一下编写支持IPv6的客户端背后的逻辑。 我相信我们具备基本知识。 我们知道IPv6地址。 如果我们看到它们以不同的表示形式,我们将能够识别它们。 我们将能够使用autoconf在我们的计算机上自动配置IPv6地址。 我们还了解映射的地址转换机制,并对使用的功能有所了解。 考虑以下IPv4客户端:

#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <stdio.h>
#include <netdb.h>
   	...
main(argc, argv) /* client side */
int argc;
char *argv[];
{
   	struct sockaddr_in server;
   	struct servent *sp;
   	struct hostent *hp;
   	int s;
   	...
   	sp = getservbyname("login", "tcp");
   	if (sp == NULL) {
   		fprintf(stderr, "rlogin: tcp/login: unknown service\n");
   		exit(1);
   	}
   	hp = gethostbyname(argv[1]);
   	if (hp == NULL) {
   		fprintf(stderr, "rlogin: %s: unknown host\n", argv[1]);
   		exit(2);
   	}
   	memset((char *)&server, 0, sizeof(server));
   	memcpy((char *)&server.sin_addr, hp->h_addr, hp->h_length);
   	server.sin_len = sizeof(server);
   	server.sin_family = hp->h_addrtype;
   	server.sin_port = sp->s_port;
   	s = socket(AF_INET, SOCK_STREAM, 0);
   	if (s < 0) {
   		perror("rlogin: socket");
   		exit(3);
   	}
   	...
   	/* Connect does the bind for us */
   	if (connect(s, (struct sockaddr *)&server, sizeof(server)) < 0) {
   		perror("rlogin: connect");
   		exit(5);
   	}

我们将研究将其转换为支持IPv6的客户端的逻辑。

如何使上述客户端启用IPv6

sockaddr_in6结构:我们正在使用struct sockaddr_in结构。 我们不能使用相同的结构,因为成员sin_addr只能容纳32位。 在将客户端移植到IPv6客户端时,我们需要使用sockaddr_in6 ,它可以容纳128位地址。

struct sockaddr_in6  {
        	u_char          sin6_len;
        	u_char          sin6_family;
        	u_int16_t       sin6_port;
        	u_int32_t       sin6_flowinfo;
        	struct          in6_addr        sin6_addr;
		};

家庭: sin6_family在我们的程序中将具有AF_INET6而不是AF_INET

sin6_flowinfo字段:应用程序可以通过设置目标地址sockaddr_in6结构的sin6_flowinfo字段来指定流标签和优先级。 我们现在可以将其设置为0。

客户将处理哪种类型的地址? 我们有三种情况。 用户可以通过:

  1. 以冒号分隔的IPv6地址
  2. 以点分隔的IPv4地址
  3. 只是一个主机名

IPv6地址:如果它是用冒号分隔的IPv6地址,那么我们可以将其复制到结构中。

IPv4地址:如果是IPv4地址,我们需要将其复制到最后32位,并用0xffff标记这32位之​​前的16位。

主机名:如果是主机名,那么我们使用gethostbyname来获取地址。 默认情况下, gethostbyname选择一个IPv4地址。

如果在AIX中setting _res.optionsresolv.h )后调用gethostbyname ,则可以强制它进行IPv6查找:

_res.options |= ~RES_USE_INET6

请注意,如果主机名不存在IPv6地址(在UNIX或DNS中的/ etc / hosts中),则通过gethostbyname进行IPv6查找将返回一个IPv4地址,但是我们仍然需要进行映射(填充位) 81-96与0xffff )。 此外,某些实现还对IPv6查找进行了另一个gethostbyname2调用。

为什么要做映射? 我们进行此映射是为了在connect调用中使用sockadd_in6结构,无论我们尝试发送到IPv6地址还是IPv4地址。 如果没有,我们将需要进行两个连接调用-一个使用IPv6地址,而一个使用IPv4地址。 另一种技术是使用sockaddr_insockaddr_in6结构的并集。 程序员也可以设计自己的技术,这不是强制性的。

// use the isinet_addr call to find out whether its a valid
 // dotted ipv4 address

if (isinet_addr(hostname )) {
    ......

//now you might wonder what s6_addr16[5] is - this is basically a union member normally 
 //defined in in.h which will point to bits 81-96

 ip6.sin6_addr.s6_addr16[5] = 0xffff;

 //now we are copying the ipv4 address in the last 32 bits
bcopy(address, &ip6.sin6_addr.s6_addr16[6], sizeof(struct in_addr));
              
ip6.sin6_len = sizeof(struct in6_addr);
ip6.sin6_family = AF_INET6;
        ......               
} 

//check if its is an IPv6 : separated address - inet_pton is used for this

else if (inet_pton(AF_INET6, hostname, &ip6.sin6_addr) > 0) {

            //note inet_pton will take care of setting the address	

            .....
            ip6.sin6_family = AF_INET6;
            ip6.sin6_len = sizeof(struct sockaddr_in6);
            .....
}



else {
//now its not a v6 address or a v4 address so it should be host name
//do a v6 lookup , note that a v6 lookup will look for a v6 address if not 
//present it can pick up a v4 address

//res init is defined in resolv.h 
res_init();
_res.options |= RES_USE_INET6;
 hptr  = gethostbyname(name);
             .....
//check hptr->h_addrtype if its AF_INET6 you can copy the address directly
//if not you need to map it.
.....
.....

 if (connect(sd, &ip6, sizeof (ip6 < 0)
{
	//connect failure
	....
}
else
{
	//continue with the program.
}

以上逻辑总结

总结一下逻辑,我们检查是否有点IPv4地址要处理。 如果是这样,我们继续对其进行映射并填写一个IPv6结构,以供稍后的connect调用使用。 如果它是IPv6地址,我们将其直接复制到IPv6结构。 如果是主机名,我们尝试进行IPv6查找。 我们可以获取一个IPv4或IPv6地址。 我们从家庭领域知道这一点。 因此,我们将其映射或复制,然后进行单个connect调用,而不管它是IPv4还是IPv6地址,然后继续执行我们的程序。

结论

我们仅查看了编写上述程序所需的概念。 还有许多有趣的概念将很快成为日常生活的一部分。 关于诸如IPv6的DNS和IPv6(DHCP)的有状态自动配置之类的争论和建设性辩论。 这些主题以及其他主题(例如其他层的实现,如何进行路由以及如何实现自动配置)将引起有趣的讨论。 我希望能在更加激动人心的IPv6世界中与您见面!


翻译自: https://www.ibm.com/developerworks/web/library/wa-ipv6.html

编写一个简单r程序

 类似资料: