Linux原始套接字编程简述 (原始套接字编程 linux)

Linux原始套接字编程是指在Linux操作系统中,使用套接字API对网络数据包进行原始访问和操作的编程技术。它给开发者提供了极大的自由度和灵活性,能够实现从网络底层到应用层的各种操作,比如捕获网络数据包、修改数据包内容、发送自定义数据包等。下面将对Linux原始套接字编程进行简单介绍。

1. 原始套接字概述

原始套接字本质上是一种与传输层协议无关的套接字类型,它可以对传输层及其以下的网络协议栈数据报文进行底层的网络数据包操作。在Linux系统中,原始套接字是通过socket()系统调用创建的,如下所示:

“`c

#include

int socket(int domn, int type, int protocol);

“`

其中,domn参数指定套接字的通信协议族,type参数指定套接字类型,protocol参数在原始套接字中一般设置为0。

原始套接字与普通套接字的更大区别在于,它可以让应用程序接收和发送参照数据链路层协议的数据包,而不像TCP、UDP等传输层协议会对数据进行分段、重组和重传等操作。

2. 数据包捕获

原始套接字最常用的功能之一是捕获网络数据包,可以用于网络分析、嗅探等诸多场景。以下是一个简单的代码例子,演示如何使用原始套接字捕获网络数据包:

“`c

#include

#include

#include

#include

#include

#include

#define MAX_BUFFER_SIZE 2023

int mn()

{

int raw_socket;

struct sockaddr_in server;

char buffer[MAX_BUFFER_SIZE] = {0};

// 创建原始套接字

raw_socket = socket(AF_INET, SOCK_RAW, IPPROTO_TCP);

if (raw_socket

perror(“socket”);

return -1;

}

// 填充Server端地址

memset(&server, 0, sizeof(server));

server.sin_family = AF_INET;

server.sin_addr.s_addr = inet_addr(“192.168.1.10”);

// 绑定套接字

if (bind(raw_socket, (struct sockaddr *)&server, sizeof(server))

perror(“bind”);

return -1;

}

// 接收数据包

while (1) {

int size = recvfrom(raw_socket, buffer, MAX_BUFFER_SIZE, 0, NULL, NULL);

if (size

perror(“recvfrom”);

return -1;

}

printf(“Received %d bytes packet\n”, size);

}

// 关闭套接字

close(raw_socket);

return 0;

}

“`

代码中首先使用socket()函数创建了一个原始套接字,然后将其绑定在指定的服务器IP地址上。通过循环调用recvfrom()函数,不断接收从网络中来的数据包,并打印收到的数据包长度。

这个例子仅捕获TCP协议的数据包,可以通过在socket()函数中指定不同的协议参数来实现原始套接字的多种用途。

3. 数据包发送

除了数据包捕获,原始套接字还可以用于自定义的网络数据包发送。以下是一个简单的例子,演示如何使用原始套接字发送一个Ping包:

“`c

#include

#include

#include

#include

#include

#include

#define MAX_BUFFER_SIZE 2023

unsigned short checksum(unsigned short *buf, int n)

{

unsigned long sum = 0;

while (n > 1) {

sum += *buf++;

n -= 2;

}

if (n == 1) {

sum += *(unsigned char *)buf;

}

sum = (sum >> 16) + (sum & 0xffff);

sum += (sum >> 16);

return (unsigned short)(~sum);

}

int mn()

{

int raw_socket;

struct sockaddr_in server;

char buffer[MAX_BUFFER_SIZE] = {0};

struct iphdr *ip_header;

struct icmphdr *icmp_header;

// 创建原始套接字

raw_socket = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP);

if (raw_socket

perror(“socket”);

return -1;

}

// 填充Server端地址

memset(&server, 0, sizeof(server));

server.sin_family = AF_INET;

server.sin_addr.s_addr = inet_addr(“192.168.1.1”);

// 准备IP头和ICMP头

ip_header = (struct iphdr *)buffer;

icmp_header = (struct icmphdr *)(buffer + sizeof(struct iphdr));

ip_header->ihl = 5;

ip_header->version = 4;

ip_header->tos = 0;

ip_header->tot_len = sizeof(struct iphdr) + sizeof(struct icmphdr);

ip_header->id = htons(0x1234);

ip_header->frag_off = 0;

ip_header->ttl = 255;

ip_header->protocol = IPPROTO_ICMP;

ip_header->saddr = INADDR_ANY;

ip_header->daddr = server.sin_addr.s_addr;

icmp_header->type = ICMP_ECHO;

icmp_header->code = 0;

icmp_header->un.echo.id = htons(0x5678);

icmp_header->un.echo.sequence = htons(1);

icmp_header->checksum = checksum((unsigned short *)icmp_header, sizeof(struct icmphdr));

// 发送Ping包

int size = sendto(raw_socket, buffer, ip_header->tot_len, 0, (struct sockaddr *)&server, sizeof(server));

if (size

perror(“sendto”);

return -1;

}

printf(“Sent %d bytes packet\n”, size);

// 关闭套接字

close(raw_socket);

return 0;

}

“`

代码中首先使用socket()函数创建了一个原始套接字,然后准备了IP头和ICMP头,填充后使用sendto()函数发送到目标服务器上。

对于Sendto()函数来说,发送的数据长度应该是网络协议下应有的长度,所以在IP头中需要设置IP报文的总长度。另外,ICMP报文中的字段un.echo.sequence是由发送端的序列号,因此它应该发送该字段的反向字节序。

4.

相关问题拓展阅读:

linux下IP地址欺骗(利用原始套接字)

发了你一个ARP包构建的,你自己可以填充IP。数据包都是可以自己随意伪造的:)。

linux socket 如何发现主机是否活着

挨个telnet +端口 如果能进去说明活着,否则就死了

使用基本socket函数来检测。

Linux系统是通过提供套接字(socket)来进行网络编程的。网络的socket数据传输是一种特殊的I/O,socket也是一种文件描述符。socket也有一个类似于打

开文件的函数:socket(),调用socket(),该函数返回一个巧神整型的socket的描述符,随后的连接建立、数据传输等操作也都是通过该socket实现。

1、socket函数

syntax:

int socket(int domain, int type, int protocol);

功能说明:

调用成功,返回socket文件描述符;失败,返回-1,并设置errno

参数说明:

  domain指明所使用的协议族,通常为PF_INET,表示TCP/IP协议;

  type参数指定socket的类型,基本上有三谈姿种:数据流套接字、数据报套接字、原始套接字

  protocol通常赋值”0″。

  两个网络程序之间的一个网络连接包括五种信息:通信协议、本地协议地址、本地主机端口、远端主机地址和远端协议端口。socket数据结构中包含这五种信息。

2、bind函数

syntax:

int bind(int sock_fd,struct sockaddr_in *my_addr, int addrlen);

功能说明:

将套接字和指定的端口相连。成功返回0,否则,返回-1,并置errno.

参数说明:

sock_fd是调用socket函数返回值,

  my_addr是一个指向包含有本机IP地址及端口号等信息的sockaddr类型的指针;

  struct sockaddr_in结构类型是用来保存socket信息的:

  struct sockaddr_in {

  short int sin_family;

  unsigned short int sin_port;

  struct in_addr sin_addr;

  unsigned char sin_zero;

  };

addrlen为sockaddr的长度。

3、connect函数

syntax:

int connect(int sock_fd, struct sockaddr *serv_addr,int addrlen);

功能说明:

客户端发送服务请求。成功返回0,否则返回-1,并置errno。

参数说明:

sock_fd 是socket函数返回的socket描述符;serv_addr是包含远端主机IP地址和端口号的指针;addrlen是结构sockaddr_in的长度。

4、listen函数

syntax:

int listen(int sock_fd, int backlog);

功能说明:

等待指定的端口的出现客户端连接。调用成功返回0,否则,返回-1,并置errno.

参数说明:

sock_fd 是socket()函数返回值;

backlog指定在请求队列中允许的更大请求数

5、accecpt函数

syntax:

int accept(int sock_fd, struct sockadd_in* addr, int addrlen);

功能说明:

用于接受客户端的服务请求,成功返回新的套接字描述符,失败返回-1,并置errno。

参数说明:

sock_fd是被监听的socket描述符,

addr通常是一个指向sockaddr_in变量的指针,

addrlen是结构sockaddr_in的长度。

6、write函数

syntax:

ssize_t write(int fd,const void *buf,size_t nbytes)

功能说明:

write函数将buf中的nbytes字节内容写入文件描述符fd.成功时返回写的字节数.失败时返回-1. 并设置errno变量.

在网络程序中,当我们向套接字文件描述符写时有俩种可能:

)write的返回值大于0,表示写了部分或者是全部的数据.

)返回的值小于0,此时出现了错误.需要根据错误类型来处理.

如果错误为EINTR表示在写的时候出现了中断错误含宽绝.

如果错误为EPIPE表示网络连接出现了问题.

7、read函数

syntax:

ssize_t read(int fd,void *buf,size_t nbyte)

函数说明:

read函数是负责从fd中读取内容.当读成功时,read返回实际所读的字节数,如果返回的值是0 表示已经读到文件的结束了,小于0表示出现了错误.

如果错误为EINTR说明读是由中断引起的,

如果错误是ECONNREST表示网络连接出了问题.

8、close函数

syntax:

int close(sock_fd);

说明:

当所有的数据操作结束以后,你可以调用close()函数来释放该socket,从而停止在该socket上的任何数据操作:

函数运行成功返回0,否则返回-1

你可以用扫描端口的方式,做个循环

socket

connect

原始套接字编程 linux的介绍就聊到这里吧,感谢你花时间阅读本站内容,更多关于原始套接字编程 linux,Linux原始套接字编程简述,linux下IP地址欺骗(利用原始套接字),linux socket 如何发现主机是否活着的信息别忘了在本站进行查找喔。


数据运维技术 » Linux原始套接字编程简述 (原始套接字编程 linux)