【网络编程开发】15.原始套接字

15.原始套接字


原始套接字(Raw Socket)是一种网络编程接口,它允许开发者访问底层的网络协议,如IP、ICMP、TCP和UDP等。与常规的流式套接字、数据报套接字不同,原始套接字提供了对网络层的直接访问,这意味着开发者可以发送和接收未经操作系统处理的原始数据包。

原始套接字广泛应用于高级网络编程,如网络流量监听、网络数据分析、安全测试等领域。

链路层原始套接字

  1. 创建过程:通过调用socket()函数并指定协议族为PF_PACKET、套接字类型为SOCK_RAW或SOCK_DGRAM,以及协议类型参数,可以创建一个链路层原始套接字。
  2. 数据格式:当使用SOCK_RAW时,套接字接收和发送的数据都是从MAC首部开始。在发送时,开发者需要从MAC首部开始构造和封装报文数据。如果选择SOCK_DGRAM,则系统会处理链路层协议头,开发者只需从IP首部(或ARP首部)开始构造即可。
  3. 注意事项:在使用链路层原始套接字时,需要注意字节序之间的转换,以及如何截取自己想要的数据内容

示例:接收并处理包含TCP协议的IP数据报。

程序首先创建一个链路层原始套接字,然后循环接收数据包。

#include <stdio.h>
#include <sys/socket.h>
#include <netinet/ether.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <linux/ip.h>
#include <linux/tcp.h>
#include <net/ethernet.h>

#define MTU 1500

int main()
{
	/* 定义变量 */
	int sockfd, len;
	uint8_t buf[MTU]={};
	uint16_t ether_type;

	struct iphdr *iph;  //IP包头
	struct tcphdr *tcph;//TCP包头
	struct ether_header *eth;

	/* 创建一个链路层原始套接字 */
	if( (sockfd = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL)) ) < 0){
		perror("socket");
		return 0;
	}
	printf("sockfd = %d\n", sockfd);

	/* 接收(只接收TCP数据协议)并处理IP数据报 */
	while(1)
	{
		/* 接收包含TCP协议的IP数据报 */
		len = recvfrom(sockfd, buf, sizeof(buf),0,NULL,NULL);

		eth = (struct ether_header *)buf;
		ether_type = htons(eth->ether_type);
		switch(ether_type){
		case ETHERTYPE_IP:
			printf("IP协议\n");
			break;
		case ETHERTYPE_ARP:
			printf("ARP协议\n");
			break;
		case ETHERTYPE_LOOPBACK:
			printf("loop back\n");
			break;
		default:
			printf("其他协议 %x\n",eth->ether_type);
		}
		if(ether_type != ETHERTYPE_IP)
			continue;

		/* 打印源IP和目的IP */
		iph = (struct iphdr *)(buf+14);
		if(iph->protocol != IPPROTO_TCP)
			continue;
		printf("源IP:%s\n",inet_ntoa(*(struct in_addr *)&iph->saddr) );
		printf("目的IP%s\n",inet_ntoa(*(struct in_addr *)&iph->daddr) );

		/* 打印TCP包头的源端口号和目的端口号 */
		tcph = (struct tcphdr *)(buf+14+iph->ihl*4);
		printf("%hu--->", ntohs(tcph->source));
		printf("%hu\n", ntohs(tcph->dest));

		/* 打印TCP数据段的长度 */
		printf("TCP首部长度:%d\n", tcph->doff*4);
	}
	//关闭套接字
	close(sockfd);
	return 0;
}

使用ping命令和nc命令模拟通信,实现捕获

在这里插入图片描述

在这里插入图片描述

网络层原始套接字

创建原始套接字

/* 创建一个原始套接字 */
if( (sockfd = socket(AF_INET, SOCK_RAW, IPPROTO_TCP) ) < 0)
{
    perror("socket");
    return 0;
}
  • AF_INET:表示使用IPv4协议族。
  • SOCK_RAW:表示创建一个原始套接字,可以接收所有类型的数据包,包括TCP、UDP等。
  • IPPROTO_TCP:表示使用TCP协议。

示例:接收并处理包含TCP协议的IP数据报

#include <stdio.h>
#include <sys/socket.h>
#include <netinet/ether.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <linux/ip.h>
#include <linux/tcp.h>
#include <sys/ioctl.h>
 
#define MTU 1500
 
int main()
{
    /* 定义变量 */
    int sockfd = -1, len, datalen, i;
    uint8_t buf[MTU]={}, *data;
 
    struct iphdr *iph;  //IP包头
    struct tcphdr *tcph;//TCP包头
    struct winsize size;
 
    /* 创建一个原始套接字 */
    if( (sockfd = socket(AF_INET, SOCK_RAW, IPPROTO_TCP) ) < 0)
    {
        perror("socket");
        return 0;
    }
    printf("sockfd = %d\n", sockfd);
 
    /* 接收(只接收TCP数据协议)并处理IP数据报 */
    while(1)
    {
        /* 接收包含TCP协议的IP数据报 */
        len = recvfrom(sockfd, buf, sizeof(buf),0,NULL,NULL);
        printf("IP数据报长度 = %d\n", len);
 
        /* 打印源IP和目的IP */
        iph = (struct iphdr *)buf;
        printf("源IP:%s",inet_ntoa(*(struct in_addr *)&iph->saddr) );
        printf("目的IP%s\n",inet_ntoa(*(struct in_addr *)&iph->daddr) );
 
        /* 打印TCP包头的源端口号和目的端口号 */
        tcph = (struct tcphdr *)(buf+iph->ihl*4);
        printf("%hu--->", ntohs(tcph->source));
        printf("%hu\n", ntohs(tcph->dest));
 
        /* 打印TCP数据段的长度 */
        printf("TCP首部长度:%d\n", tcph->doff*4);
        if(iph->ihl*4+tcph->doff*4 < len) {
            data = buf + iph->ihl*4 + tcph->doff*4;
            datalen = len - iph->ihl*4 - tcph->doff*4;
            ioctl(STDIN_FILENO,TIOCGWINSZ,&size); //terminal 结构体
            for(i = 0; i < size.ws_col; i++) //显示一行 = 
                putchar('=');
            putchar('\n');
            printf("TCP数据字符:\n");
            for(i = 0; i < size.ws_col; i++)
                putchar('=');
            putchar('\n');
            for(i = 0; i < datalen-1; i++) {
                printf("%c", data[i]);
            }
            for(i = 0; i < size.ws_col; i++)
                putchar('=');
            putchar('\n');
            printf("TCP数据16进制:\n");
            for(i = 0; i < size.ws_col; i++)
                putchar('=');
            putchar('\n');
            for(i = 0; i < datalen-1; i++){
                printf("%x ", data[i]);
            }
            putchar('\n');
            for(i = 0; i < size.ws_col; i++)
                putchar('=');
            putchar('\n');
        }
    }
    //关闭套接字
    close(sockfd);
    return 0;
}
  1. 运行程序 sudo ./tcp
  2. nc 命令模拟tcp传输 nc 110.242.68.4.1
    在这里插入图片描述
  • 13
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值