Linux 上IPv6 udp套接字在板子上总是出现网络不可达,在主机上只有发送缓存<40字节才 能发送,41个字节的都会出现网络不可达。
问题描述: 最开始只发现了再主机上可以跑(测试数据<40字节),然后移到板子上(由于需求,在 板子上测试的时候就直接测试60字节数据,然后就发现不通,但是当时没注意到这个差别)而且打印系统返回的错误“网络不可达”,就以为是板子的IPv6配置或是板子对v6套接字的支持问题。
存在问题代码
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <errno.h>
#include <string.h>
#include <stdio.h>
#include <unistd.h>
static char src[] = "fe80::aa5e:45ff:fe32:4cc4";
static char dst[] = "fe80::b934:59c9:1a44:a672";
int main()
{
int udp6_socket,ret,addr_len;
struct sockaddr_in6 saddr;
struct sockaddr_in6 daddr;
u_int32_t buffer[360];
for(size_t i = 0; i < 48; i++)
{
buffer[i] = 'A'+i;
}
if ((inet_pton(AF_INET6,(char *)&src[0], &saddr.sin6_addr) != 1))
{
printf("invalid ipv6 addr \r\n");
return -1;
}
if ((inet_pton(AF_INET6,(char *)&dst[0], &daddr.sin6_addr) != 1))
{
printf("invalid ipv6 adddr \r\n");
return -1;
}
bzero(&saddr,sizeof(saddr));
bzero(&saddr,sizeof(saddr));
saddr.sin6_family = AF_INET6;
saddr.sin6_port = htons(30000);
daddr.sin6_family = AF_INET6;
daddr.sin6_port = htons(40000);
udp6_socket = socket(AF_INET6, SOCK_DGRAM,0);
int result = setsockopt(udp6_socket,SOL_SOCKET,SO_BINDTODEVICE,"eth0",sizeof("eth0"));
if (result == -1)
{
printf("setsockopt fail,errno: %d ,%s \n",errno,strerror(errno));
}
if (udp6_socket == -1)
{
printf("create udp6_socket fail\r\n");
return -1;
}
addr_len = sizeof(struct sockaddr_in6);
int i = bind(udp6_socket,(struct sockaddr*)&saddr, addr_len);
if (i != 0) {
printf("udp6 bind addr fail,errno: %d ,%s \n",errno,strerror(errno));
close(udp6_socket);
return -1;
}
i = 0;
while(1)
{
i++;
ret = sendto(udp6_socket, buffer, 24, 0, (struct sockaddr *)&daddr, addr_len);
if (ret > 0)
{
printf("%d :udp6 send %d bytes success \r\n",i,ret);
}
else
{
printf("send errno: %d ,error: %s \n ",errno,strerror(errno));
sleep(1);
continue;
}
sleep(1);
}
close(udp6_socket);
return 0;
}
解决过程: 1、 查看是否开启IPv6支持—不是 板子和主机都是有链路本地地址的,所以都是开启IPv6的。 2、然后就想着是不是IPv6的路由没配好,结果发现当双方是直连的时候,是本地链路IPv6地址,根本不需要配置理由,而且也是支持v6的
3、后来发现主机上有时候也会出现网络不可达的问题, 再仔细一看,发现发送缓存只要>40字节就会网络不可达,<40是可以正常传输的。
4、一下就蒙了,这是为啥,为啥刚好卡在40字节,就尝试改了改发送缓存数组的的类型
//这是原来定义的以8位位基本单位的缓存数组
unsigned char buf[20];
//改成其他大于8位的类型就都可以发送长度>40的数组就可以成功发送
unsigned u_int16_t buf[60];
5、把之前写的IPv6 原始套接字的代码跑了一下,发现可以跑
综上,说明不是板子对v6机制支持,或者v6路由配置的问题。
然后就重写了一下套接字部分的代码,(区别感觉就是使用的结构体不同了),然后就不存在上述问题了。ok
以下是能正确发送所有合理长度的IPv6 udp 套接字的代码版本
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <linux/sockios.h>
#include <sys/ioctl.h>
#include <net/if.h>
int main()
{
int sd,i;
sd = socket(AF_INET6,SOCK_DGRAM,0);
if (sd < 0) {
perror("socket");
return -1;
}
struct ifreq req;
strcpy(req.ifr_name,"eth0");
if (ioctl(sd,SIOCGIFINDEX, &req) < 0) {
perror("siocgifindex");
return -2;
}
struct sockaddr_in6 to;
to.sin6_family = AF_INET6;
to.sin6_port = htons(10086);
to.sin6_scope_id = req.ifr_ifindex;
if (inet_pton(AF_INET6,"fe80::b934:59c9:1a44:a672", &to.sin6_addr) <= 0) {
perror("pton");
return -3;
}
char data[200];
for(i = 0; i < 10; i++)
{
sprintf(data,"ipv6 udp test %d\n",i);
if (sendto(sd,data,sizeof(data),0,(struct sockaddr *)&to, sizeof(to)) < 0)
{
perror("sendto");
}
}
close(sd);
return 0;
}
总结: 唉,还是对Linux上使用套接字不太熟悉,遇到问题只能去网上找是不是有一样的问题,或者换代码,还是得对自己的代码思路清晰一点,增加解决问题的能力
|