如何使用 RAW SOCKET 发送 ICMP 消息并解析回应?(
时间: 2023-07-16 22:17:05 浏览: 299
raw socket
5星 · 资源好评率100%
使用 RAW SOCKET 可以直接访问网络层,因此可以发送和接收 ICMP 消息。以下是一个 C 语言示例代码,演示如何使用 RAW SOCKET 发送 ICMP 消息并解析回应。
首先,我们需要包含必要的头文件:
```c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <netinet/ip_icmp.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <errno.h>
```
接下来,我们定义一些常量和结构体:
```c
// ICMP 包头部长度
#define ICMP_HDRLEN 8
// IP 数据包头部长度
#define IP4_HDRLEN 20
// ICMP 报文类型
#define ICMP_ECHO_REQUEST 8
// 定义 ICMP 报文结构体
struct icmp_packet {
struct icmphdr hdr;
char msg[64 - ICMP_HDRLEN];
};
// 定义 IP 数据包结构体
struct ip_packet {
struct iphdr hdr;
struct icmp_packet icmp;
};
```
然后,我们定义一些辅助函数:
```c
// 计算校验和
unsigned short checksum(void *b, int len) {
unsigned short *buf = b;
unsigned int sum = 0;
unsigned short result;
for (sum = 0; len > 1; len -= 2) {
sum += *buf++;
}
if (len == 1) {
sum += *(unsigned char *) buf;
}
sum = (sum >> 16) + (sum & 0xffff);
sum += (sum >> 16);
result = ~sum;
return result;
}
// 发送 ICMP 消息
int send_icmp(int sock_fd, struct sockaddr_in *dest_addr) {
struct ip_packet packet;
// 设置 ICMP 包头部
packet.icmp.hdr.type = ICMP_ECHO_REQUEST;
packet.icmp.hdr.code = 0;
packet.icmp.hdr.checksum = 0;
packet.icmp.hdr.un.echo.id = getpid();
packet.icmp.hdr.un.echo.sequence = 1;
memset(packet.icmp.msg, 'A', sizeof(packet.icmp.msg));
// 计算 ICMP 包校验和
packet.icmp.hdr.checksum = checksum(&packet.icmp, sizeof(packet.icmp));
// 设置 IP 数据包头部
packet.hdr.version = 4;
packet.hdr.ihl = 5;
packet.hdr.tos = 0;
packet.hdr.id = htons(getpid());
packet.hdr.ttl = 64;
packet.hdr.protocol = IPPROTO_ICMP;
packet.hdr.saddr = INADDR_ANY;
packet.hdr.daddr = dest_addr->sin_addr.s_addr;
packet.hdr.tot_len = htons(IP4_HDRLEN + ICMP_HDRLEN);
// 发送 ICMP 消息
if (sendto(sock_fd, &packet, sizeof(packet), 0, (struct sockaddr *) dest_addr, sizeof(*dest_addr)) < 0) {
perror("sendto");
return -1;
}
return 0;
}
// 接收 ICMP 消息
int recv_icmp(int sock_fd, struct sockaddr_in *src_addr) {
char buf[2048];
// 接收 ICMP 消息
socklen_t len = sizeof(*src_addr);
int ret = recvfrom(sock_fd, buf, sizeof(buf), 0, (struct sockaddr *) src_addr, &len);
if (ret < 0) {
perror("recvfrom");
return -1;
}
// 解析 ICMP 消息
struct ip_packet *ip_pkt = (struct ip_packet *) buf;
struct icmphdr *icmp_hdr = (struct icmphdr *) (buf + sizeof(struct iphdr));
if (icmp_hdr->type == ICMP_ECHO_REQUEST && icmp_hdr->code == 0) {
printf("Received echo reply from %s\n", inet_ntoa(src_addr->sin_addr));
return 0;
}
return -1;
}
```
最后,我们可以使用这些函数来发送和接收 ICMP 消息:
```c
int main(int argc, char *argv[]) {
if (argc != 2) {
printf("Usage: %s <IP address>\n", argv[0]);
return -1;
}
// 创建 RAW SOCKET
int sock_fd = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP);
if (sock_fd < 0) {
perror("socket");
return -1;
}
// 设置目标地址
struct sockaddr_in dest_addr;
memset(&dest_addr, 0, sizeof(dest_addr));
dest_addr.sin_family = AF_INET;
dest_addr.sin_port = 0;
if (inet_pton(AF_INET, argv[1], &dest_addr.sin_addr) <= 0) {
perror("inet_pton");
return -1;
}
// 发送 ICMP 消息
if (send_icmp(sock_fd, &dest_addr) < 0) {
return -1;
}
// 接收 ICMP 消息
struct sockaddr_in src_addr;
memset(&src_addr, 0, sizeof(src_addr));
if (recv_icmp(sock_fd, &src_addr) < 0) {
return -1;
}
close(sock_fd);
return 0;
}
```
注意,使用 RAW SOCKET 需要 root 权限。此外,防火墙可能会阻止 ICMP 消息。
阅读全文