Linux基础(11)原始套接字

  • Post category:other

当然,我可以为您提供“Linux基础(11)原始套接字的完整攻略”,包括过程中的两个示例。以下是详细步骤:

Linux基础(11)原始套接字

原始套接字是Linux中的一种特殊类型的套接字,它可以让我们直接访问网络层和传输层的数据包,从而实现更加灵活和高效的网络编程。

创建原始套接字

我们可以使用socket()调用来创建原始套接字。在创建原始套接字时,需要指定协议族为AF_PACKET,协议类型为SOCK_RAW,并且需要指定网络接口的名称。

#include <sys/socket.h>
#include <linux/if_packet.h>
#include <net/ethernet.h>

int sockfd = socket(AF_PACKET, SOCK_RAW, htons(ETH_P_ALL));

在上面的示例中,我们使用socket()系统调用创建了一个原始套接字,并指定协议族为AF_PACKET,协议类型为SOCK_RAW,并且使用htons()函数将协议类型转换为网络字节序。

发送数据包

我们可以使用sendto()系统调用来发送数据包。在发送数据包时,需要指定目标地址、目标端口、数据包的内容等信息。

#include <sys/socket.h>
#include <linux/if_packet.h>
#include <net/ethernet.h>

char buffer[1024];
struct sockaddr_ll dest_addr;

memset(&dest_addr, 0, sizeof(dest_addr));
dest_addr.sll_family = AF_PACKET;
dest_addr.sll_ifindex = if_nametoindex("eth0");
dest_addr.sll_halen = ETH_ALEN;
dest_addr.sll_addr[0] = 0x00;
dest_addr.sll_addr[1] = 0x11;
dest_addr.sll_addr[2] = 0x22;
dest_addr.sll_addr[3] = 0x33;
dest_addr.sll_addr[4] = 0x44;
dest_addr.sll_addr[5] = 0x55;

sendto(sockfd, buffer, sizeof(buffer), 0, (struct sockaddr *)&dest_addr, sizeof(dest_addr));

在上面的示例中,我们使用sendto()系统调用发送了一个数据包,并指定了目标地址、目标端口、数据包的内容等信息。

接收数据包

我们可以使用recvfrom()系统调用来接收数据包。在接收数据包时,需要指定缓冲区的大小、源地址、源端口等信息。

#include <sys/socket.h>
#include <linux/if_packet.h>
#include <net/ethernet.h>

char buffer[1024];
struct sockaddr_ll src_addr;
socklen_t addrlen = sizeof(src_addr);

recvfrom(sockfd, buffer, sizeof(buffer), 0, (struct sockaddr *)&src_addr, &addrlen);

在上面的示例中,我们使用recvfrom()系统调用接收了一个数据包,并指定了缓冲区的大小、源地址、源端口等信息。

示例1:发送ARP请求

以下是发送ARP请求的示例:

#include <sys/socket.h>
#include <linux/if_packet.h>
#include <net/ethernet.h>
#include <arpa/inet.h>

char buffer[1024];
struct sockaddr_ll dest_addr;

memset(&dest_addr, 0, sizeof(dest_addr));
dest_addr.sll_family = AF_PACKET;
dest_addr.sll_ifindex = if_nametoindex("eth0");
dest_addr.sll_halen = ETH_ALEN;
dest_addr.sll_addr[0] = 0xff;
dest_addr.sll_addr[1] = 0xff;
dest_addr.sll_addr[2] = 0xff;
dest_addr.sll_addr[3] = 0xff;
dest_addr.sll_addr[4] = 0xff;
dest_addr.sll_addr[5] = 0xff;

struct ether_header *eth = (struct ether_header *)buffer;
eth->ether_dhost[0] = 0xff;
eth->ether_dhost[1] = 0xff;
eth->ether_dhost[2] = 0xff;
eth->ether_dhost[3] = 0xff;
eth->ether_dhost[4] = 0xff;
eth->ether_dhost[5] = 0xff;
eth->ether_shost[0] = 0x00;
eth->ether_shost[1] = 0x11;
eth->ether_shost[2] = 0x22;
eth->ether_shost[3] = 0x33;
eth->ether_shost[4] = 0x44;
eth->ether_shost[5] = 0x55;
eth->ether_type = htons(ETHERTYPE_ARP);

struct arphdr *arp = (struct arphdr *)(buffer + sizeof(struct ether_header));
arp->ar_hrd = htons(ARPHRD_ETHER);
arp->ar_pro = htons(ETHERTYPE_IP);
arp->ar_hln = 6;
arp->ar_pln = 4;
arp->ar_op = htons(ARPOP_REQUEST);

struct in_addr src_ip, dest_ip;
inet_pton(AF_INET, "192.168.1.1", &src_ip);
inet_pton(AF_INET, "192.168.1.2", &dest_ip);

memcpy(arp->ar_sha, eth->ether_shost, ETH_ALEN);
memcpy(arp->ar_sip, &src_ip, sizeof(src_ip));
memcpy(arp->ar_tha, "\x00\x00\x00\x00\x00\x00", ETH_ALEN);
memcpy(arp->ar_tip, &dest_ip, sizeof(dest_ip));

sendto(sockfd, buffer, sizeof(struct ether_header) + sizeof(struct arphdr), 0, (struct sockaddr *)&dest_addr, sizeof(dest_addr));

在上面的示例中,我们使用原始套接字发送了一个ARP请求,并指定了目标地址、目标端口、数据包的内容等信息。

示例2:接收ICMP数据包

以下是接收ICMP数据包的示例:

#include <sys/socket.h>
#include <linux/if_packet.h>
#include <net/ethernet.h>
#include <netinet/ip.h>
#include <netinet/ip_icmp.h>

char buffer[1024];
struct sockaddr_ll src_addr;
socklen_t addrlen = sizeof(src_addr);

recvfrom(sockfd, buffer, sizeof(buffer), 0, (struct sockaddr *)&src_addr, &addrlen);

struct iphdr *ip = (struct iphdr *)buffer;
struct icmphdr *icmp = (struct icmphdr *)(buffer + sizeof(struct iphdr));

if (ip->protocol == IPPROTO_ICMP && icmp->type == ICMP_ECHO) {
  printf("Received ICMP echo request\n");
}

在上面的示例中,我们使用原始套接字接收了一个ICMP数据包,并解析了其中的IP头和ICMP头,判断是否为ICMP echo请求。