OpenBSD 上原始套接字 icmp 的协议族不支持 sendto 地址族
sendto Address family not supported by protocol family for raw socket icmp on OpenBSD
我正在尝试为 ICMP 库编写一个 ping 函数。一切似乎都在 sendto
之前正常工作,那时它 returns Address family not supported by protocol family
。我不明白这个错误。我做错了什么?
#include <netinet/in.h>
#include <netinet/ip.h>
#include <netinet/ip_icmp.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <errno.h>
#include <netdb.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
static u_int16_t
checksum(u_int16_t *arr, size_t bytes)
{
u_int32_t sum = 0;
u_int16_t *ptr = arr;
while (bytes > 1) {
sum += *ptr++;
bytes -= 2;
}
if (bytes == 1) {
*(u_int8_t *)&sum += *(u_int8_t *)ptr;
}
sum = (sum >> 16) + (sum & 0xFFFF);
sum += (sum >> 16);
return (u_int16_t)(~sum);
}
ssize_t
icmp_send(const char *host, const char *data, const size_t datalen)
{
int s, error;
struct addrinfo hints;
struct addrinfo *res = NULL;
bzero(&hints, sizeof(hints));
hints.ai_flags = AI_CANONNAME;
hints.ai_family = AF_INET;
hints.ai_socktype = SOCK_RAW;
hints.ai_protocol = IPPROTO_ICMP;
if ((error = getaddrinfo(host, NULL, &hints, &res))) {
fprintf(stderr, "ping: getaddrinfo: %s\n", gai_strerror(error));
return -1;
}
struct protoent *proto = getprotobyname("icmp");
if ((s = socket(AF_INET, SOCK_RAW, proto->p_proto)) == -1) {
fprintf(stderr, "ping: socket: %s\n", strerror(errno));
return -1;
}
int on = 1;
if (setsockopt(s, IPPROTO_IP, IP_HDRINCL, &on, sizeof(on)) < 0) {
fprintf(stderr, "ping: setsockopt: %s\n", strerror(errno));
return -1;
}
struct sockaddr_in to;
bzero(&to, sizeof(to));
to.sin_family = AF_INET;
struct ip ip;
bzero(&ip, sizeof(ip));
ip.ip_v = IPVERSION;
ip.ip_hl = sizeof(struct ip) << 2;
ip.ip_id = 0;
ip.ip_ttl = 255;
ip.ip_p = IPPROTO_ICMP;
ip.ip_src.s_addr = INADDR_ANY;
ip.ip_dst = ((struct sockaddr_in *)res)->sin_addr;
ip.ip_sum = checksum((u_int16_t *)&ip, sizeof(ip));
struct icmp icp;
bzero(&icp, sizeof(icp));
icp.icmp_type = ICMP_ECHOREPLY;
icp.icmp_code = 0;
icp.icmp_cksum = checksum((u_int16_t *)&icp, sizeof(icp));
size_t packetlen = sizeof(ip) + sizeof(icp) + datalen;
char packet[packetlen];
memset(packet, 0, packetlen);
memcpy((char *)packet, &ip, sizeof(ip));
memcpy((char *)packet + sizeof(ip), &icp, sizeof(icp));
memcpy((char *)packet + sizeof(ip) + sizeof(icp), data, packetlen);
ssize_t snd_ret = sendto(s, packet, packetlen, 0, (const struct sockaddr *)&to, sizeof(to));
if (errno) {
fprintf(stderr, "ping: sendto: %s\n", strerror(errno));
return -1;
}
return snd_ret;
}
res
是指向 addrinfo
结构的指针,而不是 sockaddr
。 sockaddr
在 ai_addr
成员中,它的长度在 ai_addrlen
成员中。这些应该传递给 sendto()
.
ssize_t snd_ret = sendto(s, packet, packetlen, 0, res->ai_addr, res->ai_addrlen);
我正在尝试为 ICMP 库编写一个 ping 函数。一切似乎都在 sendto
之前正常工作,那时它 returns Address family not supported by protocol family
。我不明白这个错误。我做错了什么?
#include <netinet/in.h>
#include <netinet/ip.h>
#include <netinet/ip_icmp.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <errno.h>
#include <netdb.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
static u_int16_t
checksum(u_int16_t *arr, size_t bytes)
{
u_int32_t sum = 0;
u_int16_t *ptr = arr;
while (bytes > 1) {
sum += *ptr++;
bytes -= 2;
}
if (bytes == 1) {
*(u_int8_t *)&sum += *(u_int8_t *)ptr;
}
sum = (sum >> 16) + (sum & 0xFFFF);
sum += (sum >> 16);
return (u_int16_t)(~sum);
}
ssize_t
icmp_send(const char *host, const char *data, const size_t datalen)
{
int s, error;
struct addrinfo hints;
struct addrinfo *res = NULL;
bzero(&hints, sizeof(hints));
hints.ai_flags = AI_CANONNAME;
hints.ai_family = AF_INET;
hints.ai_socktype = SOCK_RAW;
hints.ai_protocol = IPPROTO_ICMP;
if ((error = getaddrinfo(host, NULL, &hints, &res))) {
fprintf(stderr, "ping: getaddrinfo: %s\n", gai_strerror(error));
return -1;
}
struct protoent *proto = getprotobyname("icmp");
if ((s = socket(AF_INET, SOCK_RAW, proto->p_proto)) == -1) {
fprintf(stderr, "ping: socket: %s\n", strerror(errno));
return -1;
}
int on = 1;
if (setsockopt(s, IPPROTO_IP, IP_HDRINCL, &on, sizeof(on)) < 0) {
fprintf(stderr, "ping: setsockopt: %s\n", strerror(errno));
return -1;
}
struct sockaddr_in to;
bzero(&to, sizeof(to));
to.sin_family = AF_INET;
struct ip ip;
bzero(&ip, sizeof(ip));
ip.ip_v = IPVERSION;
ip.ip_hl = sizeof(struct ip) << 2;
ip.ip_id = 0;
ip.ip_ttl = 255;
ip.ip_p = IPPROTO_ICMP;
ip.ip_src.s_addr = INADDR_ANY;
ip.ip_dst = ((struct sockaddr_in *)res)->sin_addr;
ip.ip_sum = checksum((u_int16_t *)&ip, sizeof(ip));
struct icmp icp;
bzero(&icp, sizeof(icp));
icp.icmp_type = ICMP_ECHOREPLY;
icp.icmp_code = 0;
icp.icmp_cksum = checksum((u_int16_t *)&icp, sizeof(icp));
size_t packetlen = sizeof(ip) + sizeof(icp) + datalen;
char packet[packetlen];
memset(packet, 0, packetlen);
memcpy((char *)packet, &ip, sizeof(ip));
memcpy((char *)packet + sizeof(ip), &icp, sizeof(icp));
memcpy((char *)packet + sizeof(ip) + sizeof(icp), data, packetlen);
ssize_t snd_ret = sendto(s, packet, packetlen, 0, (const struct sockaddr *)&to, sizeof(to));
if (errno) {
fprintf(stderr, "ping: sendto: %s\n", strerror(errno));
return -1;
}
return snd_ret;
}
res
是指向 addrinfo
结构的指针,而不是 sockaddr
。 sockaddr
在 ai_addr
成员中,它的长度在 ai_addrlen
成员中。这些应该传递给 sendto()
.
ssize_t snd_ret = sendto(s, packet, packetlen, 0, res->ai_addr, res->ai_addrlen);