lv8 嵌入式开发-网络编程开发 19 原始套接字

news2024/11/17 13:39:10

目录

1 链路层原始套接字用法

1.1 利用原始套接字实现类似wireshark的功能

1.2 利用原始套接字实现ping命令

2 网络层原始套接字用法

2.1 TCP原始套接字用法


1 链路层原始套接字用法

Linux中的原始套接字(Raw Socket)是一种高级套接字类型,允许应用程序直接访问网络协议栈,发送和接收自定义的网络数据包。使用原始套接字,你可以实现各种网络工具、网络协议分析和网络攻防等功能。

下面是使用原始套接字的一般步骤:

1 创建原始套接字:通过调用socket()函数创建一个原始套接字。指定参数AF_PACKET表示使用Packet套接字族,参数SOCK_RAW表示使用原始套接字类型。

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

2 
绑定套接字到网络设备:使用bind()函数将套接字绑定到特定的网络设备上。你需要指定网络设备的名称或索引。可以使用ifconfig命令或者ioctl()函数来获取设备名称和索引。

struct sockaddr_ll sa;
memset(&sa, 0, sizeof(sa));
sa.sll_family = AF_PACKET;
sa.sll_protocol = htons(ETH_P_ALL);
sa.sll_ifindex = if_nametoindex("eth0");
bind(sockfd, (struct sockaddr*)&sa, sizeof(sa));

3 构造自定义数据包:使用结构体来构造自定义的网络数据包,如以太网帧(struct ethhdr)和IP数据报头(struct iphdr)。在构造数据包时,你需要设置正确的协议头信息和有效载荷。

struct ethhdr ether_header;
struct iphdr ip_header;
char payload[100];

// 构建以太网帧、IP数据报头和有效载荷
// ...

4 发送和接收数据包:使用sendto()函数发送数据包,指定目标地址和端口。使用recvfrom()函数接收数据包,获取发送者的信息。

// 发送数据包
sendto(sockfd, &ether_header, sizeof(ether_header) + sizeof(ip_header) + payload_length, 0, (struct sockaddr*)&sa, sizeof(sa));

// 接收数据包
struct sockaddr_ll sa_recv;
socklen_t sa_len = sizeof(sa_recv);
recvfrom(sockfd, recv_buffer, sizeof(recv_buffer), 0, (struct sockaddr *)&sa_recv, &sa_len);

5 关闭套接字:当你完成使用原始套接字时,记得使用close()函数关闭套接字。

close(sockfd);

请注意,在使用原始套接字时需要具有足够的权限,通常需要以root用户身份运行程序。此外,使用原始套
接字需要对网络协议有深入的了解,并且要小心操作,以避免对网络造成不良影响。

1.1 利用原始套接字实现类似wireshark的功能

tcp_all.c 接收所用链路层的数据包,但是以TCP为主(实现功能类似wireshark)

#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是一个定义在<netinet/ip.h>中的IP包头结构体,用于表示IPv4协议的包头信息。
    struct tcphdr是一个定义在<netinet/tcp.h>中的TCP包头结构体,用于表示TCP协议的包头信息。
    struct ether_header是一个定义在<net/ethernet.h>中的以太网帧头结构体,用于表示以太网帧头的信息。
    */
	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)  //如果是IP包才往下执行
			continue;

		/* 打印源IP和目的IP */
		iph = (struct iphdr *)(buf+14);
		if(iph->protocol != IPPROTO_TCP)  //如果是ip包并且是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;
}

运行效果: 

后期改进,也可以更细的去打印各类协议,详细包内容。

1.2 利用原始套接字实现ping命令

send_ping.c

#include <unistd.h>
#include <arpa/inet.h>
#include <linux/ip.h>
#include <linux/tcp.h>
#include <stdlib.h>
#include <linux/icmp.h>
#include <stdio.h>

uint16_t SetChetSum(uint16_t *buf, int size);
void Set_IcmpH(struct icmphdr *icmph, int size);

#define MSG_SIZE 40

int main(int argc, char *argv[])
{
	/* 定义变量 */
	int fd;
	char buf[MSG_SIZE]={};

	if(argc < 3)
	{
		printf("%s <addr> <port>\n", argv[0]);
		exit(EXIT_FAILURE);
	}

	/* 创建原始套接字 */
	if((fd = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP)) < 0)
	{
		perror("socket");
		return 0;
	}
	struct sockaddr_in sin;
	sin.sin_family = AF_INET;
	sin.sin_port = htons(atoi(argv[2]));
	if(inet_aton(argv[1], &sin.sin_addr) == 0)
	{
		printf("Invalid address\n");
		exit(EXIT_FAILURE);
	}
	Set_IcmpH((struct icmphdr *)&buf, MSG_SIZE);
	sendto(fd, buf, MSG_SIZE, 0, (struct sockaddr *)&sin, sizeof(sin));
	close(fd);
	return 0;


}

void Set_IcmpH(struct icmphdr * icmph, int size)
{
	static int seq;
	icmph->type = ICMP_ECHO; //icmp宏定义
	icmph->code = 0;        //每个icmp的编号,发一次,简化逻辑
	icmph->un.echo.id = getpid();
	icmph->un.echo.sequence = seq++;
	icmph->checksum = SetChetSum((uint16_t *)icmph, MSG_SIZE);//校验和

}

uint16_t SetChetSum(uint16_t *buf, int size)
{
	uint32_t checksum = 0;
	while(size > 1)
	{
		checksum += *buf++;
		size -= 16;
	}

	if(size)
		checksum += *(unsigned char *)buf;
	checksum = (checksum >> 16) + (checksum & 0xffff);
	checksum += (checksum >> 16);
	return (uint16_t)(~checksum);

}

实验效果

2 网络层原始套接字用法

原始套接字(Raw Socket)和标准套接字(Standard Socket)是在网络编程中使用的两种不同类型的套接字。

  1. 原始套接字(Raw Socket): 原始套接字允许应用程序直接访问网络协议栈,可以发送和接收原始的网络数据包。使用原始套接字,开发者可以自定义协议头部信息,以及对底层网络数据包进行更底层的控制和处理。它提供了更高级别的网络访问权限,但需要更高的权限级别(如管理员或特权用户)来使用。(整个数据包,包括IP的包头、TCP包头)

  2. 标准套接字(Standard Socket): 标准套接字是通过操作系统提供的网络套接字API进行通信的一种方式,使用TCP(Transmission Control Protocol)或UDP(User Datagram Protocol)等传输协议。标准套接字隐藏了底层网络协议的细节,提供了更高层次的抽象和简化的网络编程接口,使得开发人员可以更方便地进行网络通信和应用程序开发。

区别:

  • 功能差异:原始套接字允许直接处理和操作底层的网络数据包,可以实现更高级别的网络控制和定制化,而标准套接字则提供了更高层的、抽象化的网络编程接口,适合日常的网络通信需求。
  • 权限要求:原始套接字需要更高的权限级别来访问和使用,通常需要管理员或特权用户的权限。而标准套接字可以由一般用户进行操作。
  • 使用场景:原始套接字通常用于进行网络嗅探、网络扫描、包分析等高级网络操作,以及特定网络协议的开发和研究。标准套接字则广泛应用于一般的网络通信场景,如客户端和服务器之间的数据传输。

在实际应用中,一般情况下使用标准套接字就能满足大部分的网络通信需求,而原始套接字主要由网络工具和专业开发者使用。

2.1 TCP原始套接字用法

tcp.c (主要用于网络工具的开发)

#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;  //首地址是一样的,强转成ip数据报结构体
		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);  //强转buf中tcp部分
		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) {     //tcp数据报里面有没有放数据
			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;
}

执行效果

 IP协议包头结构体

/usr/include/linux/ip.h

struct iphdr {
#if defined(__LITTLE_ENDIAN_BITFIELD) //小段模式
	__u8	ihl:4,                    //位域操作,两个合起来占8个字节
		version:4;
#elif defined (__BIG_ENDIAN_BITFIELD) //大端模式
	__u8	version:4,
  		ihl:4;
#else
#error	"Please fix <asm/byteorder.h>"
#endif

    unsigned char  tos;              // 服务类型
    unsigned short tot_len;          // 总长度
    unsigned short id;               // 标识
    unsigned short frag_off;         // 分片偏移
    unsigned char  ttl;              // 存活时间
    unsigned char  protocol;         // 协议类型
    unsigned short check;            // 校验和
    unsigned int   saddr;            // 源IP地址
    unsigned int   daddr;            // 目标IP地址
    // 其他字段...
};

 /usr/include/linux/in.h

enum {
  IPPROTO_IP = 0,		/* Dummy protocol for TCP		*/
#define IPPROTO_IP		IPPROTO_IP
  IPPROTO_ICMP = 1,		/* Internet Control Message Protocol	*/
#define IPPROTO_ICMP		IPPROTO_ICMP
  IPPROTO_IGMP = 2,		/* Internet Group Management Protocol	*/
#define IPPROTO_IGMP		IPPROTO_IGMP
  IPPROTO_IPIP = 4,		/* IPIP tunnels (older KA9Q tunnels use 94) */
#define IPPROTO_IPIP		IPPROTO_IPIP
  IPPROTO_TCP = 6,		/* Transmission Control Protocol	*/
#define IPPROTO_TCP		IPPROTO_TCP
  IPPROTO_EGP = 8,		/* Exterior Gateway Protocol		*/
#define IPPROTO_EGP		IPPROTO_EGP
  IPPROTO_PUP = 12,		/* PUP protocol		

...
}

如何找到这些结构体

grep -irn "xxx" /usr/inclue/linux

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1118674.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

YOLOv5涨点必备!改进损失函数EIoU,SIoU,AlphaIoU,FocalEIoU,Wise-IoU

目录 一&#xff0c;改进损失函数的作用 二&#xff0c;具体实现 一&#xff0c;改进损失函数的作用 YOLOv5损失函数的作用是衡量预测框与真实框之间的差异&#xff0c;并根据这些差异来更新模型的参数。它帮助模型学习如何准确地检测和定位目标物体&#xff0c;从而提高检测…

【分类讨论】CF1747D

Problem - D - Codeforces 题意 思路 一看这个做法一定就是分类讨论 先判无解 显然&#xff0c;如果区间异或和不是0一定无解 如果区间内全是0&#xff0c;答案一定是0 之后怎么讨论 注意到需要讨论区间长度 如果长度是奇数&#xff0c;那么直接操作即可&#xff0c;答…

【PyTorch】深度学习实践 1. Overview

目录 人工智能概述 课程前置知识 人工智能 问题分类 推理类 预测类 算法分类 传统算法与智能算法 人工智能领域细分 学习系统的发展 基于规则的系统 经典机器学习算法 表示学习方法 维度诅咒 说明 解决方法 第一代 第二代&#xff08;深度学习&#xff09; 传统…

数据结构: 红黑树

目录​​​​​​​ 1.红黑树概念 2.红黑树性质 3.调整 1.如果p和u都是红色&#xff0c;将其都改为黑色即可,然后向上调整 2.如果p红&#xff08;u黑/u不在&#xff09;&#xff0c;这时候左子树两红&#xff0c;于是给右子树一个红&#xff08;旋转变色&#xff09; 2.1…

华为ICT——第四章深度学习和积卷神经

接第三章的末尾&#xff1a; 目录 接第三章的末尾&#xff1a; 1&#xff1a;自适应阈值分割&#xff1a; 2&#xff1a;形态处理&#xff1a; 4&#xff1a;膨胀&#xff1a; 5&#xff1a;腐蚀 6&#xff1a;开运算 7&#xff1a;闭运算 8&#xff1a;特征描述子 9&#xf…

看《软能力》的读后感

最近在图书大夏看了一本书的一部分&#xff0c;书名是《软能力》。本人看到了几个有意思的观点。一是接一些兼职项目。 二是分享自己的技术&#xff0c;让同事能干自己的工作&#xff0c;让自己的工作变得别人也能干&#xff0c;才有机会让自己的职位提升。 三是让手动操作变…

Linux实战——网络连接模式的三种模式

Linux可以分为三种网络模式&#xff1a; 桥接模式 &#xff08;vmnet0) 仅主机模式 (vmnet1) NAT模式 (vmnet8) 当我们下载了vmware之后&#xff0c;在电脑会出现两个虚拟网卡&#xff0c;VMware Network Adapter VMnet1、VMware Network Adapter VMnet8。 可以通过查找 控…

巡检管理系统哪一款简单实用?如何解决传统巡检难题,实现高效监管?

在电力、燃气、水务等公共服务领域&#xff0c;线路巡检工作是保障公众安全、避免事故发生的重要环节。然而&#xff0c;传统的巡检方式存在一些显著的问题&#xff0c;可能会对公共安全和稳定运行产生不利的影响。为了解决这些问题&#xff0c;需要一种能够实现高效、精准的线…

多线程环境下如何安全的使用线性表, 队列, 哈希表

小王学习录 今日鸡汤安全使用ArrayList安全使用队列安全使用HashMap 今日鸡汤 安全使用ArrayList 使用synchronized锁或者reentrantLock锁使用CopyOnWriteArrayList(COW写时拷贝)类来代替ArrayList类. 多个线程对CopyOnWriteArrayList里面的ArrayList进行读操作, 不会发生线程…

原子核内的相互作用

原子核内的相互作用 氘核基态 和态的混合 核子-核子散射 低能核子-核子散射 n-p散射&#xff1a;只有核力 p-p散射&#xff1a;较复杂 n-n散射&#xff1a;n-n散射没有直接实验 低能 p-p 散射和核力的电荷无关性 高能核子-核子散射 核力的主要性质 核力主要性质 核力是短程力…

Qt第六十五章:自定义菜单栏的隐藏、弹出

目录 一、效果图 二、qtDesigner 三、ui文件如下&#xff1a; 四、代码 一、效果图 二、qtDesigner 原理是利用属性动画来控制QFrame的minimumWidth属性。 ①先拖出相应的控件 ②布局一下 ③填上一些样式 相关QSS background-color: rgb(238, 242, 255); border:2px sol…

【uniapp/uView】解决消息提示框悬浮在下拉框之上

需要实现这样的效果&#xff0c;即 toast 消息提示框在 popup 下拉框之上&#xff1a; 解决方法&#xff0c;把 <u-toast ref"uToast" /> 放在 u-popup 里面即可&#xff0c;这样就可以提升 toast 的优先级&#xff1a; <!-- 弹出下拉框 --><u-popu…

第三章 内存管理 十二、请求分页管理方式

目录 一、页表机制 1、页表结构 二、缺页中断机制 1、有如下例子 2、根据要访问的逻辑地址的页号2&#xff0c;找到该页的状态是没有放入内存&#xff0c;所以会产生缺页中断&#xff0c;将缺页进程堵塞&#xff0c;放入堵塞队列&#xff0c;调页完成后再将其唤醒&#xf…

SAP MM学习笔记37 - 请求书照合中的配送费用

上一次学习了请求书照合中的 追加请求&#xff0c;追加Credit&#xff0c;请求书取消等知识&#xff0c;这一章来学习请求书中的配送费用处理。 SAP MM学习笔记37 - 请求书照合中的 追加请求/追加Credit 等概念/ 请求书的取消-CSDN博客 如下图所示&#xff0c;配送费用分以下两…

ROS功能包编译报错fatal error: xxxxConfig.h: 没有那个文件或目录的解决方法及原理介绍

在ROS中&#xff0c;我们常使用动态调参工具或参数配置文件来进行参数调节&#xff0c;在编译时会生成对应的Config.h文件&#xff0c;如本文例子中的MPCPlannerConfig.h文件 一、报错原因及解决方法 在编译时报以下错误的原因是在编译生成可执行文件mpc_ros的过程中需要使用MP…

【(数据结构)—— 基于单链表实现通讯录】

&#xff08;数据结构&#xff09;—— 基于单链表实现通讯录 一.通讯录的功能介绍1.基于单链表实现通讯录(1). 知识要求(2). 功能要求 二.通讯录的代码实现1.通讯录的底层结构(单链表)(1).思路展示(2).底层代码实现(单链表)1.单链表头文件 —— &#xff08;函数的定义&#x…

国旗升降系统程序及原理图资料

本文主要介绍国旗升降系统设计程序及原理图&#xff08;完整资料见文末链接&#xff09; 系统原理图如下&#xff0c;程序资料见文末 附完整资料链接 百度网盘链接: https://pan.baidu.com/s/1Q5J2J8LgVJ-hoeTSVP95_g?pwd3qkw 提取码: 3qkw

TypeScript 安装

TypeScript 的安装 在电脑上全局安装typescript 在确保电脑上已经安装了node.js的前提下&#xff0c;使用npm工具进行安装。 执行如下命令即可&#xff1a; (执行成功会&#xff0c;会安装当前发布的最新版本的typescript) npm install -g typescript如果是Linux or Mac 系统&…

2023年“绿盟杯”四川省大学生信息安全技术大赛

findme 下载附件打开无法正常显示 使用010editor打开发现CRC报错&#xff0c;很可能是高度被修改了 使用工具爆破图片正确的宽度和高度 这里工具自动修复的依旧不能正常打开显示 我们先对原来图片的高度进行修改 之后再使用工具进行修复&#xff0c;即可正常显示&#xff0c;…