raw socket icmp
时间: 2023-08-22 08:11:04 浏览: 214
原始套接字(Raw Socket)是一种网络编程技术,允许程序直接访问网络层的数据。ICMP(Internet Control Message Protocol)是一种网络协议,用于在IP网络中传递错误消息和操作控制消息。
使用原始套接字可以实现对ICMP报文的发送和接收。通过创建一个原始套接字,并指定协议参数为ICMP,可以构建和发送自定义的ICMP报文。同时,也可以通过原始套接字接收和解析接收到的ICMP报文。
使用原始套接字发送和接收ICMP报文需要具备一定的网络编程知识和权限。在使用时需要注意网络安全和合法性,以免滥用或导致网络问题。
相关问题
如何使用 RAW SOCKET 发送 ICMP 消息并解析回应?(
使用 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 消息。
golang raw socket
在 Go 语言中,可以使用 net 包来创建原始套接字(raw socket)。这个套接字允许应用程序直接访问网络层和数据链路层,以便发送和接收原始数据包。下面是一个简单的示例程序,演示如何使用 Go 语言创建原始套接字:
```go
package main
import (
"fmt"
"net"
)
func main() {
// 创建 IPv4 的原始套接字
conn, err := net.ListenPacket("ip4:icmp", "0.0.0.0")
if err != nil {
fmt.Println("Error listening:", err)
return
}
defer conn.Close()
// 从套接字中读取数据包
buffer := make([]byte, 1024)
n, addr, err := conn.ReadFrom(buffer)
if err != nil {
fmt.Println("Error reading:", err)
return
}
// 输出数据包的信息
fmt.Printf("Received %d bytes from %s\n", n, addr.String())
fmt.Printf("% x\n", buffer[:n])
}
```
在这个示例程序中,我们使用 `net.ListenPacket()` 函数创建了一个 IPv4 的原始套接字,它将监听所有的 ICMP 数据包。然后,我们使用 `conn.ReadFrom()` 函数从套接字中读取数据包,并输出它的信息。当程序运行时,它将会一直等待直到收到一个 ICMP 数据包。当收到数据包后,程序将输出数据包的大小和源地址,并将数据包的内容以十六进制形式输出。
需要注意的是,使用原始套接字需要具有管理员权限或特权,因为它可以绕过操作系统的网络协议栈,直接访问硬件。
阅读全文