C - recvfrom unexpected 的结果
C - result of recvfrom unexpected
我有一个简单的代码来发送和接收 ICMP 数据包,如 ping,一切正常:我的数据包已发送,我收到了结果。
我在 recvfrom 结果中遇到问题,缓冲区中的 ip dest/src 不是服务器目标 ip。
结果
$> ./my_ping qwant.com
IPv4: hdr-size=20 pkt-size=56 protocol=1 TTL=254 src=10.0.2.2 dest=172.17.0.2
但是用真实的ping:
$> ping qwant.com
PING qwant.com (194.187.168.99): 48 data bytes
56 bytes from 194.187.168.99: icmp_seq=0 ttl=61 time=101.742 ms
TTL和ip不一样
代码
初始化结构地址信息:
struct addrinfo hints;
struct addrinfo *a_info;
bzero(&hints, sizeof(hints));
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_DGRAM;
hints.ai_protocol = 0;
hints.ai_flags = AI_ADDRCONFIG;
getaddrinfo(host, NULL, &hints, &a_info)
初始化套接字:
sock = socket(PF_INET, SOCK_RAW, IPPROTO_ICMP);
setsockopt(sock, SOL_IP, IP_TTL, (void *)&val, sizeof(val);
同时发送和接收:
size_t i;
t_ping pack;
int count = 1;
struct iphdr buff;
while (TRUE)
{
bzero(&pack, sizeof(pack));
pack.head.type = 8;
pack.head.code = getpid();
pack.id = count++;
while (i < 15)
{
pack.seq[i] = i + '0';
i++;
}
pack.seq[i] = 0;
pack.head.chk = checksum(&pack, sizeof(pack));
ft_bzero((void *)&buff, sizeof(buff));
if (sendto(sock, &pack, sizeof(pack), 0, a_info->ai_addr, a_info->ai_addrlen) < 0)
perror("sendto");
if (recvfrom(sock, (void *)&buff, sizeof(buff), 0, a_info->ai_addr, &a_info->ai_addrlen) < 0)
perror("recvfrom");
display((void *)&buff);
sleep(1);
}
最后是我的显示功能:
struct iphdr *ip = buff;
char src[INET6_ADDRSTRLEN];
char dest[INET6_ADDRSTRLEN];
inet_ntop( AF_INET, (void *)&ip->saddr, src, sizeof(src) );
inet_ntop( AF_INET, (void *)&ip->daddr, dest, sizeof(dest) );
printf("IPv%d: hdr-size=%d pkt-size=%d protocol=%d TTL=%d src=%s dest=%s\n",
ip->version,
ip->ihl*4,
ntohs(ip->tot_len),
ip->protocol,
ip->ttl,
src,
dest);
在我看来,我的缓冲区包含有关最后一个数据包步骤(路由器 -> 我的家)的信息,这解释了为什么 TTL 值为 254 以及为什么我用 traceroute 找到了相同的一对 IP:
$> traceroute qwant.com
traceroute to qwant.com (194.187.168.99), 30 hops max, 60 byte packets
172.17.0.1 (172.17.0.1) 0.026 ms 0.011 ms 0.010 ms
10.0.2.2 (10.0.2.2) 0.149 ms 0.160 ms 0.156 ms
[...]
194.187.168.99 (194.187.168.99) 147.634 ms 147.506 ms 147.540 ms
为什么收到的信息不是关于我的服务器目标的?我怎样才能收到这些信息?
The Internet Assigned Numbers Authority (IANA) has reserved the
following three blocks of the IP address space for private networks:
10.0.0.0 - 10.255.255.255
172.16.0.0 - 172.31.255.255
192.168.0.0 - 192.168.255.255
172.17.0.2 是私有网络中的一个地址。我猜你的机器在这个网络里面,服务器也在里面。
getaddrinfo()
可以return你多一个地址。尝试 运行 查看所有回复并检查您是否也获得了互联网地址。
/* getaddrinfo() returns a list of address structures.
Try each address until we successfully bind(2).
If socket(2) (or bind(2)) fails, we (close the socket
and) try the next address. */
for (rp = result; rp != NULL; rp = rp->ai_next) {
sfd = socket(rp->ai_family, rp->ai_socktype,
rp->ai_protocol);
if (sfd == -1)
continue;
if (bind(sfd, rp->ai_addr, rp->ai_addrlen) == 0)
break; /* Success */
close(sfd);
}
解决方案:
我更改了 getaddrinfo
调用和套接字初始化 :
getaddrinfo(stats.host, NULL, NULL, &addrinfo);
sd = socket(PF_INET, SOCK_RAW, IPPROTO_ICMP);
setsockopt(sd, SOL_IP, IP_TTL, &val, sizeof(val));
接下来我创建了 2 个 struct sockaddr_in
:一个用于 sendto
,第二个用于 recvfrom
。
struct sockaddr_in send;
struct sockaddr_in recv;
现在初始化发送结构以在 sendto
指定目的地,但 recv 没有变化:它是在第一次 recvfrom
调用期间自动分配的。
bzero(&send, sizeof(send));
send.sin_family = addrinfo->ai_family;
send.sin_port = 0;
send.sin_addr.s_addr = ((struct sockaddr_in *)addrinfo->ai_addr)->sin_addr.s_addr;
最后我改变了我的数据包结构
struct packet
{
struct icmphdr hdr;
char msg[PACKETSIZE-sizeof(struct icmphdr)];
};
现在,只需调用 sendto
和 recvfrom
即可:
socklen_t len = sizeof(recv);
sendto(sd, &pckt, sizeof(pckt), 0, (struct sockaddr*)&send, sizeof(send));
recvfrom(sd, &pckt, sizeof(pckt), 0, (struct sockaddr*)&recv, &len);
我有一个简单的代码来发送和接收 ICMP 数据包,如 ping,一切正常:我的数据包已发送,我收到了结果。 我在 recvfrom 结果中遇到问题,缓冲区中的 ip dest/src 不是服务器目标 ip。
结果
$> ./my_ping qwant.com
IPv4: hdr-size=20 pkt-size=56 protocol=1 TTL=254 src=10.0.2.2 dest=172.17.0.2
但是用真实的ping:
$> ping qwant.com
PING qwant.com (194.187.168.99): 48 data bytes
56 bytes from 194.187.168.99: icmp_seq=0 ttl=61 time=101.742 ms
TTL和ip不一样
代码
初始化结构地址信息:
struct addrinfo hints;
struct addrinfo *a_info;
bzero(&hints, sizeof(hints));
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_DGRAM;
hints.ai_protocol = 0;
hints.ai_flags = AI_ADDRCONFIG;
getaddrinfo(host, NULL, &hints, &a_info)
初始化套接字:
sock = socket(PF_INET, SOCK_RAW, IPPROTO_ICMP);
setsockopt(sock, SOL_IP, IP_TTL, (void *)&val, sizeof(val);
同时发送和接收:
size_t i;
t_ping pack;
int count = 1;
struct iphdr buff;
while (TRUE)
{
bzero(&pack, sizeof(pack));
pack.head.type = 8;
pack.head.code = getpid();
pack.id = count++;
while (i < 15)
{
pack.seq[i] = i + '0';
i++;
}
pack.seq[i] = 0;
pack.head.chk = checksum(&pack, sizeof(pack));
ft_bzero((void *)&buff, sizeof(buff));
if (sendto(sock, &pack, sizeof(pack), 0, a_info->ai_addr, a_info->ai_addrlen) < 0)
perror("sendto");
if (recvfrom(sock, (void *)&buff, sizeof(buff), 0, a_info->ai_addr, &a_info->ai_addrlen) < 0)
perror("recvfrom");
display((void *)&buff);
sleep(1);
}
最后是我的显示功能:
struct iphdr *ip = buff;
char src[INET6_ADDRSTRLEN];
char dest[INET6_ADDRSTRLEN];
inet_ntop( AF_INET, (void *)&ip->saddr, src, sizeof(src) );
inet_ntop( AF_INET, (void *)&ip->daddr, dest, sizeof(dest) );
printf("IPv%d: hdr-size=%d pkt-size=%d protocol=%d TTL=%d src=%s dest=%s\n",
ip->version,
ip->ihl*4,
ntohs(ip->tot_len),
ip->protocol,
ip->ttl,
src,
dest);
在我看来,我的缓冲区包含有关最后一个数据包步骤(路由器 -> 我的家)的信息,这解释了为什么 TTL 值为 254 以及为什么我用 traceroute 找到了相同的一对 IP:
$> traceroute qwant.com
traceroute to qwant.com (194.187.168.99), 30 hops max, 60 byte packets
172.17.0.1 (172.17.0.1) 0.026 ms 0.011 ms 0.010 ms
10.0.2.2 (10.0.2.2) 0.149 ms 0.160 ms 0.156 ms
[...]
194.187.168.99 (194.187.168.99) 147.634 ms 147.506 ms 147.540 ms
为什么收到的信息不是关于我的服务器目标的?我怎样才能收到这些信息?
The Internet Assigned Numbers Authority (IANA) has reserved the following three blocks of the IP address space for private networks:
10.0.0.0 - 10.255.255.255 172.16.0.0 - 172.31.255.255 192.168.0.0 - 192.168.255.255
172.17.0.2 是私有网络中的一个地址。我猜你的机器在这个网络里面,服务器也在里面。
getaddrinfo()
可以return你多一个地址。尝试 运行 查看所有回复并检查您是否也获得了互联网地址。
/* getaddrinfo() returns a list of address structures. Try each address until we successfully bind(2). If socket(2) (or bind(2)) fails, we (close the socket and) try the next address. */
for (rp = result; rp != NULL; rp = rp->ai_next) { sfd = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol); if (sfd == -1) continue; if (bind(sfd, rp->ai_addr, rp->ai_addrlen) == 0) break; /* Success */ close(sfd); }
解决方案:
我更改了 getaddrinfo
调用和套接字初始化 :
getaddrinfo(stats.host, NULL, NULL, &addrinfo);
sd = socket(PF_INET, SOCK_RAW, IPPROTO_ICMP);
setsockopt(sd, SOL_IP, IP_TTL, &val, sizeof(val));
接下来我创建了 2 个 struct sockaddr_in
:一个用于 sendto
,第二个用于 recvfrom
。
struct sockaddr_in send;
struct sockaddr_in recv;
现在初始化发送结构以在 sendto
指定目的地,但 recv 没有变化:它是在第一次 recvfrom
调用期间自动分配的。
bzero(&send, sizeof(send));
send.sin_family = addrinfo->ai_family;
send.sin_port = 0;
send.sin_addr.s_addr = ((struct sockaddr_in *)addrinfo->ai_addr)->sin_addr.s_addr;
最后我改变了我的数据包结构
struct packet
{
struct icmphdr hdr;
char msg[PACKETSIZE-sizeof(struct icmphdr)];
};
现在,只需调用 sendto
和 recvfrom
即可:
socklen_t len = sizeof(recv);
sendto(sd, &pckt, sizeof(pckt), 0, (struct sockaddr*)&send, sizeof(send));
recvfrom(sd, &pckt, sizeof(pckt), 0, (struct sockaddr*)&recv, &len);