Linux下IPV6通信

1. IPv6简介

1.1 简介

    通常我们在Linux下使用的都是IPv4的方式来进行网络编程通信,本次介绍Linux下IPv6方式通信方式。
    IPv6是英文“Internet Protocol Version 6”(互联网协议第6版)的缩写,是互联网工程任务组(IETF)设计的用于替代IPv4的下一代IP协议,其地址数量号称可以为全世界的每一粒沙子编上一个地址。由于IPv4最大的问题在于网络地址资源不足,严重制约了互联网的应用和发展。IPv6的使用,不仅能解决网络地址资源数量的问题,而且也解决了多种接入设备连入互联网的障碍。

1.2 IPv6地址类型和格式

    IPv6地址由被划分为8个16位块的128位组成。 然后将每个块转换为由冒号符号分隔的4位十六进制数字。
    例如,下面给出的是以二进制格式表示并被划分为八个16位块的128位IPv6地址:

0010000000000001 0000000000000000 0011001000111000 1101111111100001 0000000001100011 0000000000000000 000000000000000

    每个块被转换为十六进制并由":"符号分隔,即使在转换为十六进制格式后,IPv6地址仍然很长。
    2001:0000:3238:DFE1:0063:0000:0000:FEFB

    地址的使用规则如下:

    规则1:丢弃前导零:

    在块5,0063中,可以省略前导的两个0,例如(第五块):
    2001:0000:3238:DFE1:63:0000:0000:FEFB

    规则2:如果两个或多个块包含连续零,则省略它们并用双冒号"::"替换:

    例如(第6和第7块):
    2001:0000:3238:DFE1:63::FEFB

    连续的零块只能被::替换一次。如果地址中仍有零块,它们可以缩小到一个零:
    例如(第二块):
    2001:0:3238:DFE1:63::FEFB

2. Linux系统中添加IPv6模块

    确定开发板运行Linux内核是否支持Ipv6协议栈,可通过对网卡添加Ipv6地址是否被允许来判断。当对网卡添加IPv6地址不被允许时,会出现“ifconfig: socket: Address family not supported by protocol”这个错误信息。如下图所示。
Linux下IPV6通信_第1张图片
    以Linux 4.9.16内核版本为例,可以2种方法来使内核支持Ipv6协议栈:
    1)重新配置内核,编译镜像,使之支持IPv6;
    2)编译IPv6协议栈模块,利用insmod方式加载模块。

    运行命令“make ARCH=arm menuconfig”,依次在内核配置单选择Networking support -> Networking options -> The IPv6 protocol
Linux下IPV6通信_第2张图片
    由于需要将IPv6协议栈编译进内核,所以选择“*”的方式。
    以AM5728开发板添加IPv6配置为例:
    网卡eth0没有配置ipv6地址之前:
Linux下IPV6通信_第3张图片
为网卡eth0配置ipv6地址后:
Linux下IPV6通信_第4张图片
    如上图所示,我们使用命令“ifconfig eth0 add 1::1”添加了Ipv6地址。inet6中有两个ipv6地址,这两个地址是有区别的,就是尾部的Global和Link两个关键字。要想使用ipv6进行网络通信,不能使用Link标识的inet6字段,因为该地址私有,供给内核使用。所以需要我们使用手动添加相应的ipv6地址且标识为Global inet6地址来进行通信。如果网卡没有该标识的inet6地址,可手动添加。

3. Linux系统中ping命令测试IPv6通信

    局域网内以PC机和AM5728开发板为例。
    AM5728开发板 Linux下ping一下网卡的私有Ipv6地址,验证Ipv6协议栈的支持。

Linux下IPV6通信_第5张图片
打开自己的PC机,进行开发板与PC机之间的ipv6通信测试。windows下查看ipv6地址:

Linux下IPV6通信_第6张图片
    windows下通过ipconfig看到的ipv6地址如下:

fe80::44ac:4b43:a82e:e5dd%18 

%后面是本ipv6地址对应的网络接口的index,windows术语叫scope id,可理解为一个接口序号。

在开发板侧ping PC机的ipv6地址。

Linux下IPV6通信_第7张图片
    直接使用“ping6 fe80::34c7:7536:f8f1:4de6”命令测试也可以。

注意:
    如果在同一网段中的不同机器上进行通信连接,需注意防火墙是否关闭,针对inet6地址的防火墙它有自己的防火墙名为ip6tables,关闭即可,负责客户端进行连接是会出现connect:: Permission denied错误,或者在ip6tables防火墙配置中配置对应的端口放行即可,在udt封装的协议中,如果防火墙未关闭,会出现Connection setup failure: connection time out.

查看防火墙状态:service ip6tables status
关闭防火墙:service ip6tables stop

4. Linux下IPv6 UDP/TCP socket编程举例

参考:https://blog.csdn.net/zhengxianghdu/article/details/14106167

准备两台Linux开发板,进行配置。
1)两台linux用网线直接相连
2)手动配置两台linux的ipv6地址为:
ifconfig eth0 add 2001:da8:e000::1:1:1
ifconfig eth0 add 2001:da8:e000::1:1:2

4.1 UDP通信

1)server端:

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
 
int main(int argc, char **argv)
{
    struct sockaddr_in6 s_addr;
    struct sockaddr_in6 c_addr;
    int sock;
    socklen_t addr_len;
    int len;
    char buff[128];
    char buf_ip[128];
 
    if ((sock = socket(AF_INET6, SOCK_DGRAM, 0)) == -1) {
        perror("socket");
        exit(errno);
    } else
        printf("create socket.\n\r");
 
    memset(&s_addr, 0, sizeof(struct sockaddr_in6));
    s_addr.sin6_family = AF_INET6;
 
    if (argv[2])
        s_addr.sin6_port = htons(atoi(argv[2]));
    else
        s_addr.sin6_port = htons(4444);
 
    if (argv[1])
        inet_pton(AF_INET6, argv[1], &s_addr.sin6_addr);
    else
        s_addr.sin6_addr = in6addr_any;
 
    if ((bind(sock, (struct sockaddr *) &s_addr, sizeof(s_addr))) == -1) {
        perror("bind");
        exit(errno);
    } else
        printf("bind address to socket.\n\r");
 
    addr_len = sizeof(c_addr);
    while (1) {
        len = recvfrom(sock, buff, sizeof(buff) - 1, 0,
                       (struct sockaddr *) &c_addr, &addr_len);
        if (len < 0) {
            perror("recvfrom");
            exit(errno);
        }
 
        buff[len] = '\0';
        printf("receive from %s: buffer:%s\n\r",
                inet_ntop(AF_INET6, &c_addr.sin6_addr, buf_ip, sizeof(buf_ip)), 
                buff);
    }
    return 0;
}

2)client端:

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
 
int main(int argc, char **argv)
{
    struct sockaddr_in6 s_addr;
    int sock;
    int addr_len;
    int len;
    char buff[128];
 
    if ((sock = socket(AF_INET6, SOCK_DGRAM, 0)) == -1) {
        perror("socket");
        exit(errno);
    } else
        printf("create socket.\n\r");
 
    s_addr.sin6_family = AF_INET6;
    if (argv[2])
        s_addr.sin6_port = htons(atoi(argv[2]));
    else
        s_addr.sin6_port = htons(4444);
 
    if (argv[1])
        inet_pton(AF_INET6, argv[1], &s_addr.sin6_addr);
    else {
        printf("usage:./command ip port\n");
        exit(0);
    }
 
    addr_len = sizeof(s_addr);
    strcpy(buff, "hello i'm here");
    len = sendto(sock, buff, strlen(buff), 0,
                 (struct sockaddr *) &s_addr, addr_len);
    if (len < 0) {
        printf("\n\rsend error.\n\r");
        return 3;
    }
 
    printf("send success.\n\r");
    return 0;
}

Linux下IPV6通信_第8张图片

4.2 TCP/IP通信

1)server端:

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
 
#define MAXBUF 1024
 
int main(int argc, char **argv)
{
    int sockfd, new_fd;
    socklen_t len;
 
    /* struct sockaddr_in my_addr, their_addr; */ // IPv4
    struct sockaddr_in6 my_addr, their_addr; // IPv6
 
    unsigned int myport, lisnum;
    char buf[MAXBUF + 1];
 
    if (argv[1])
        myport = atoi(argv[1]);
    else
        myport = 7838;
 
    if (argv[2])
        lisnum = atoi(argv[2]);
    else
        lisnum = 2;
 
    /* if ((sockfd = socket(PF_INET, SOCK_STREAM, 0)) == -1) { */ // IPv4
    if ((sockfd = socket(PF_INET6, SOCK_STREAM, 0)) == -1) { // IPv6
        perror("socket");
        exit(1);
    } else
        printf("socket created\n");
 
    bzero(&my_addr, sizeof(my_addr));
    /* my_addr.sin_family = PF_INET; */ // IPv4
    my_addr.sin6_family = PF_INET6;    // IPv6
    /* my_addr.sin_port = htons(myport); */ // IPv4
    my_addr.sin6_port = htons(myport);   // IPv6
    if (argv[3])
        /* my_addr.sin_addr.s_addr = inet_addr(argv[3]); */ // IPv4
        inet_pton(AF_INET6, argv[3], &my_addr.sin6_addr);  // IPv6
    else
        /* my_addr.sin_addr.s_addr = INADDR_ANY; */ // IPv4
        my_addr.sin6_addr = in6addr_any;            // IPv6
 
    /* if (bind(sockfd, (struct sockaddr *) &my_addr, sizeof(struct sockaddr)) */ // IPv4
    if (bind(sockfd, (struct sockaddr *) &my_addr, sizeof(struct sockaddr_in6))  // IPv6
        == -1) {
        perror("bind");
        exit(1);
    } else
        printf("binded\n");
 
    if (listen(sockfd, lisnum) == -1) {
        perror("listen");
        exit(1);
    } else
        printf("begin listen\n");
 
    while (1) {
        len = sizeof(struct sockaddr);
        if ((new_fd =
             accept(sockfd, (struct sockaddr *) &their_addr,
                    &len)) == -1) {
            perror("accept");
            exit(errno);
        } else
            printf("server: got connection from %s, port %d, socket %d\n",
                   /* inet_ntoa(their_addr.sin_addr), */ // IPv4
                   inet_ntop(AF_INET6, &their_addr.sin6_addr, buf, sizeof(buf)), // IPv6
                   /* ntohs(their_addr.sin_port), new_fd); */ // IPv4
                   their_addr.sin6_port, new_fd); // IPv6
 
        bzero(buf, MAXBUF + 1);
        strcpy(buf, "This is sever to client!\n");
        len = send(new_fd, buf, strlen(buf), 0);
        if (len < 0) {
            printf
                ("message '%s' error number:%d,error mes:'%s'\n", buf, errno, strerror(errno));
        } else
            printf("message'%s'success length: %d\n", buf, len);
 
        bzero(buf, MAXBUF + 1);
        len = recv(new_fd, buf, MAXBUF, 0);
        if (len > 0)
            printf("receive message:'%s',length:%d\n", buf, len);
        else
            printf
                ("message error number:%d,error mes:'%s'\n", errno, strerror(errno));
    }
 
    close(sockfd);
    return 0;
}

2) client端:

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
 
#define MAXBUF 1024
 
int main(int argc, char **argv)
{
    int sockfd, len;
    /* struct sockaddr_in dest; */ // IPv4
    struct sockaddr_in6 dest;      // IPv6
    char buffer[MAXBUF + 1];
 
    if (argc != 3) {
        printf
            ("参数格式错误!正确用法如下:\n\t\t%s IP地址 端口\n\t比如:\t%s 127.0.0.1 80\n此程序用来从某个 IP 地址的服务器某个端口接收最多 MAXBUF 个字节的消息",
             argv[0], argv[0]);
        exit(0);
    }
    /* socket for tcp */
    /* if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) { */ // IPv4
    if ((sockfd = socket(AF_INET6, SOCK_STREAM, 0)) < 0) {      // IPv6
        perror("Socket");
        exit(errno);
    }
    printf("socket created\n");
 
    /* 初始化服务器端(对方)的地址和端口信息 */
    bzero(&dest, sizeof(dest));
    /* dest.sin_family = AF_INET; */  // IPv4
    dest.sin6_family = AF_INET6;     // IPv6
    /* dest.sin_port = htons(atoi(argv[2])); */ // IPv4
    dest.sin6_port = htons(atoi(argv[2]));     // IPv6
    /* if (inet_aton(argv[1], (struct in_addr *) &dest.sin_addr.s_addr) == 0) { */ // IPv4
    if ( inet_pton(AF_INET6, argv[1], &dest.sin6_addr) < 0 ) {                 // IPv6
        perror(argv[1]);
        exit(errno);
    }
    printf("address created\n");
 
    /* 连接服务器 */
    if (connect(sockfd, (struct sockaddr *) &dest, sizeof(dest)) != 0) {
        perror("Connect ");
        exit(errno);
    }
    printf("server connected\n");
 
    /* 接收对方发过来的消息,最多接收 MAXBUF 个字节 */
    bzero(buffer, MAXBUF + 1);
    /* 接收服务器来的消息 */
    len = recv(sockfd, buffer, MAXBUF, 0);
    if (len > 0)
        printf("接收消息成功:'%s',共%d个字节的数据\n",
               buffer, len);
    else
        printf
            ("消息接收失败!错误代码是%d,错误信息是'%s'\n",
             errno, strerror(errno));
 
    bzero(buffer, MAXBUF + 1);
    strcpy(buffer, "这是客户端发给服务器端的消息\n");
    /* 发消息给服务器 */
    len = send(sockfd, buffer, strlen(buffer), 0);
    if (len < 0)
        printf
            ("消息'%s'发送失败!错误代码是%d,错误信息是'%s'\n",
             buffer, errno, strerror(errno));
    else
        printf("消息'%s'发送成功,共发送了%d个字节!\n",
               buffer, len);
 
    /* 关闭连接 */
    close(sockfd);
    return 0;
}

5. 参考

1) https://blog.csdn.net/qq_26105397/article/details/80929087
ipv6网络通信方式
2) https://blog.csdn.net/zhengxianghdu/article/details/17392849
Linux系统中添加IPv6模块(TQ2440开发板)
3) https://www.cnblogs.com/lfxiao/p/10102294.html
局域网内使用ipv6 通信
4) https://blog.csdn.net/zhengxianghdu/article/details/14106167
简单的IPv6 UDP/TCP socket编程

6. Linux下通过DHCP服务器获取IPv6地址

适用于:Debian / Ubuntu

开启IPv6功能
有些镜像是禁用了IPv6功能的,因此需要开启。首先查看一下是否被禁用了:

root@HIzcND1000044:~# sysctl -a | grep ipv6.*disable
sysctl: reading key "net.ipv6.conf.all.stable_secret"
net.ipv6.conf.all.disable_ipv6 = 1
sysctl: reading key "net.ipv6.conf.default.stable_secret"
net.ipv6.conf.default.disable_ipv6 = 1
sysctl: reading key "net.ipv6.conf.eth0.stable_secret"
net.ipv6.conf.eth0.disable_ipv6 = 1
sysctl: reading key "net.ipv6.conf.lo.stable_secret"
net.ipv6.conf.lo.disable_ipv6 = 1

disable=1说明被禁用了,因此需要去修改,配置文件为/etc/sysctl.conf:

vi /etc/sysctl.conf
#把ipv6 disable的参数都改为0,然后保存,重载服务后生效
[root@hSxavP1000044 ~]# sysctl -p
root@HIzcND1000044:~# sysctl -p
vm.swappiness = 0
net.ipv4.neigh.default.gc_stale_time = 120
net.ipv4.conf.all.rp_filter = 0
net.ipv4.conf.default.rp_filter = 0
net.ipv4.conf.default.arp_announce = 2
net.ipv4.conf.lo.arp_announce = 2
net.ipv4.conf.all.arp_announce = 2
net.ipv4.tcp_max_tw_buckets = 5000
net.ipv4.tcp_syncookies = 1
net.ipv4.tcp_max_syn_backlog = 1024
net.ipv4.tcp_synack_retries = 2
net.ipv6.conf.all.disable_ipv6 = 0
net.ipv6.conf.default.disable_ipv6 = 0
net.ipv6.conf.lo.disable_ipv6 = 0

设置网卡,启用DHCP

nano /etc/network/interfaces

添加一行iface eth0 inet6 dhcp,举例示意:

# This file describes the network interfaces available on your system
# and how to activate them. For more information, see interfaces(5).

source /etc/network/interfaces.d/*

# The loopback network interface
auto lo
iface lo inet loopback

# The primary network interface
auto eth0
iface eth0 inet static
address 172.16.2.X
netmask 255.240.0.0
gateway 172.16.2.1

iface eth0 inet6 dhcp

重启网络,检查是否正常

service networking restart

你可能感兴趣的:(Linux基础)