第13章 原始套接字
Socket_RAW元生套接字捕获网卡数据
引言从事网络安全的技术人员和相当一部分准黑客(指那些使用现成的黑客软件进行攻击而不是根据需要去自己编写代码的人)都一定不会对网络嗅探器(sniffer)感到陌生,网络嗅探器无论是在网络安全还是在黑客攻击方面均扮演了很重要的角色。
通过使用网络嗅探器可以把网卡设置于混杂模式,并可实现对网络上传输的数据包的捕获与分析。
此分析结果可供网络安全分析之用,但如为黑客所利用也可以为其发动进一步的攻击提供有价值的信息。
可见,嗅探器实际是一把双刃剑。
虽然网络嗅探器技术被黑客利用后会对网络安全构成一定的威胁,但嗅探器本身的危害并不是很大,主要是用来为其他黑客软件提供网络情报,真正的攻击主要是由其他黑软来完成的。
而在网络安全方面,网络嗅探手段可以有效地探测在网络上传输的数据包信息,通过对这些信息的分析利用是有助于网络安全维护的。
权衡利弊,有必要对网络嗅探器的实现原理进行介绍。
嗅探器设计原理嗅探器作为一种网络通讯程序,也是通过对网卡的编程来实现网络通讯的,对网卡的编程也是使用通常的套接字(socket)方式来进行。
但是,通常的套接字程序只能响应与自己硬件地址相匹配的或是以广播形式发出的数据帧,对于其他形式的数据帧比如已到达网络接口但却不是发给此地址的数据帧,网络接口在验证投递地址并非自身地址之后将不引起响应,也就是说应用程序无法收取到达的数据包。
而网络嗅探器的目的恰恰在于从网卡接收所有经过它的数据包,这些数据包即可以是发给它的也可以是发往别处的。
显然,要达到此目的就不能再让网卡按通常的正常模式工作,而必须将其设置为混杂模式。
具体到编程实现上,这种对网卡混杂模式的设置是通过原始套接字(raw socket)来实现的,这也有别于通常经常使用的数据流套接字和数据报套接字。
在创建了原始套接字后,需要通过setsockopt()函数来设置IP头操作选项,然后再通过bind()函数将原始套接字绑定到本地网卡。
为了让原始套接字能接受所有的数据,还需要通过ioctlsocket()来进行设置,而且还可以指定是否亲自处理IP头。
Windows网络编程基础-习题解答
《Windows网络编程基础》习题解答第一章网络应用程序设计基础习题1.TCP/IP协议栈的五个层次是什么?在这些层次中,每层的主要任务是什么?解答:TCP/IP参考模型分为五个层次:应用层、传输层、网络层、链路层和物理层。
以下分别介绍各层的主要功能。
应用层是网络应用程序及其应用层协议存留的层次。
该层包括了所有与网络相关的高层协议,如文件传输协议(File Transfer Protocol,FTP)、超文本传输协议(Hypertext Transfer Protocol,HTTP)、Telent(远程终端协议)、简单邮件传送协议(Simple Mail Transfer Protocol,SMTP)、因特网中继聊天(Internet Relay Chat,IRC)、网络新闻传输协议(Network News Transfer Protocol,NNTP)等。
传输层的功能是使源端主机和目标端主机上的对等实体可以进行会话。
在传输层定义了两种服务质量不同的协议,即:传输控制协议(Transmission Control Protocol,TCP)和用户数据报协议(User Datagram Protocol,UDP)。
网络层是整个TCP/IP协议栈的核心。
它的功能是通过路径选择把分组发往目标网络或主机,进行网络拥塞控制以及差错控制。
链路层负责物理层和网络层之间的通信,将网络层接收到的数据分割成特定的可被物理层传输的帧,并交付物理层进行实际的数据传送。
物理层的任务是将该帧中的一个一个比特从一个节点移动到下一个节点。
该层中的协议仍然是链路相关的,并且进一步与链路(如双绞线、单模光纤)的实际传输媒体相关。
对应于不同的传输媒体,跨越这些链路移动一个比特的方式不同。
2.请分析路由器、链路层交换机和主机分别处理TCP/IP协议栈中的哪些层次?解答:路由器处理TCP/IP协议栈的物理层、链路层和网络层;链路层交换机处理TCP/IP协议栈的物理层和链路层;主机处理TCP/IP协议栈的物理层、链路层、网络层、传输层和应用层。
《原始套接字编程》课程设计报告
《原始套接字编程》课程设计报告姓名:***班级:9班学号:********《原始套接字编程》课程设计报告班级:11级9班学号:54110904 姓名:王延兴一、设计任务分析(一)实验环境操作系统:Windows编程工具及集成开发环境:VC++(二)实验目的和要求实验目的:掌握原始套接字编程。
实验要求:完成下列功能:(1)利用RAW SOCKET捕获网络数据包的程序模型SOCKET_STREAM 流式套接字SOCKET_DGRAMSOCKET_RAW 原始套接字IPPROTO_IP IP协议IPPROTO_ICMP INTERNET控制消息协议,配合原始套接字可以实现ping的功能IPPROTO_IGMP INTERNET 网关服务协议,在多播中用到在AF_INET地址族下,有SOCK_STREAM、SOCK_DGRAM、SOCK_RAW三种套接字类型。
SOCK_STREAM也就是通常所说的TCP,而SOCK_DGRAM则是通常所说的UDP,而SOCK_RAW 则是用于提供一些较低级的控制的;第3个参数依赖于第2个参数,用于指定套接字所用的特定协议,设为0表示使用默认的协议。
RAW SOCKET能够对较低层次的协议直接访问,网络监听技术很大程度上依赖于它。
(2)能够抓取第二节课的并发服务器程序的服务器端或客户端的应用层数据,即:时间值,打印输出。
二、设计方案同一台主机不同进程可以用进程号来唯一标识,但是在网络环境下进程号并不能唯一标识该进程。
TCP/IP主要引入了网络地址、端口和连接等概念来解决网络间进程标识问题。
套接字(Socket)是一个指向传输提供者的句柄,TCP/IP协议支持3种类型的套接字,分别是流式套接字、数据报式套接字和原始套接字。
流式套接字(SOCKET_STREAM)提供了面向连接、双向可靠的数据流传输服务。
数据报式套接字(SOCKET_ DGRAM)提供了无连接服务,不提供无错保证。
原始套接字tcp包构造
原始套接字tcp包构造想象一下,我们要给远方的小伙伴寄一个超级特别的包裹。
这个包裹就像是tcp 包。
那这个包裹怎么构造呢?我们先来说说包裹里最基本的东西。
就好像我们写信的时候,要有收信人的地址一样,tcp包里面得有目标的地址信息。
比如说,你要把你的小秘密分享给住在隔壁街的好朋友,那你得知道他家的地址才能把你的信送过去。
在tcp包里面,这个地址信息就像是告诉网络这个包裹要送到哪里去。
再说说包裹里的内容。
假如你要给朋友送一个自己画的小画,这个小画就是内容。
在tcp包里面,也有内容部分,这个内容可能是一些文字信息,或者是一些数据。
就像你在纸上写了“今天我看到一只超级可爱的小猫咪”,这就是包裹里的内容。
那这个包裹怎么包起来呢?它有一定的格式。
就像我们折纸盒子包东西的时候,有一定的折法。
tcp包也是这样,它有头部和数据部分。
头部就像是包裹外面写着地址、包裹类型之类信息的地方。
比如说,头部可能会写着这是一个紧急的包裹还是普通的包裹。
如果是紧急的包裹,那网络就会优先把它送到目的地。
我给大家讲个小故事吧。
有一天,小猴子想给小兔子送一个特别的果子。
小猴子把果子放在一个小盒子里,这个小盒子就像是tcp包。
小猴子在盒子上写了小兔子的家的地址,这就是地址信息。
然后小猴子还在盒子上画了一个小标记,表示这个果子是刚摘下来的很新鲜,这就像是tcp包里面的一些特殊标记。
小猴子把盒子包好后,就交给森林邮递员小鸟。
小鸟就根据盒子上的地址把果子送到小兔子家了。
在构造tcp包的时候,还有一些小细节。
比如说,要有一个顺序号。
这个顺序号就像是我们给故事书的每一页标上页码一样。
这样接收的一方就能按照顺序把内容整理好。
就像你把画的好多小画按顺序寄给朋友,朋友就能按照你标的顺序一张一张看,知道这个故事是怎么发展的。
还有一个确认号呢。
这就像是你的朋友收到你的包裹后,给你回了一个小纸条说“我收到包裹啦”。
这个确认号就是告诉发送方,这个包裹已经安全到达啦。
套接字所用的类型
题目:套接字所用的类型摘要:本文探讨了在网络编程中使用的套接字类型,重点介绍了流套接字(SOCK_STREAM)、数据报套接字(SOCK_DGRAM)和原始套接字(SOCK_RAW)三种类型。
文章解释了每种类型的套接字如何工作以及它们在网络通信中的优缺点。
一、引言套接字(Socket)是计算机网络编程中的重要概念,它们被用来实现网络通信的不同层和模型中的数据交互和收发。
套接字类型决定了数据在网络中的传输方式,不同的套接字类型适用于不同的应用场景。
本文将深入探讨网络编程中常见的套接字类型以及它们的适用范围和特点。
二、流套接字(SOCK_STREAM)流套接字是一种面向连接的套接字类型,通常用于TCP协议。
它提供了一种可靠的、双向的、基于字节流的通信方式。
流套接字确保数据的按序交付和错误控制。
因此,它是实现基于TCP的应用层协议(如HTTP、SMTP等)的理想选择。
优点:1. 可靠的数据传输:通过确认机制、重传丢失的数据包和流量控制来确保数据的可靠传输。
2. 按序交付:确保数据包的顺序与发送时的顺序一致。
3. 错误控制:检测并处理数据传输过程中的错误。
缺点:1. 较高的开销:为确保可靠传输,TCP协议需要维护连接状态,这可能导致较高的开销。
2. 可能产生拥塞:在网络拥堵的情况下,流套接字的性能可能受到影响。
三、数据报套接字(SOCK_DGRAM)数据报套接字是一种无连接的套接字类型,通常用于UDP协议。
它提供了一种不可靠的、无连接的通信方式,适用于对实时性要求较高或能容忍数据丢失的应用场景。
数据报套接字以独立的数据包形式发送数据,不保证数据的按序交付或可靠性。
优点:1. 低开销:UDP协议开销小,适合于传输较少数据的场景。
2. 实时性:无需建立连接,传输延迟低,适合于实时应用场景如语音通话或视频流。
3. 灵活性:允许发送不同大小的数据包,且数据包独立传输。
缺点:1. 不可靠的数据传输:不保证数据包的可靠传输,可能会丢失或重复接收数据包。
rawsocket编程讲解
网络协议回顾对TCP/IP、UDP、Socket编程这些词你不会很陌生吧?随着网络技术的发展,这些词充斥着我们的耳朵。
那么我想问:1. 什么是TCP/IP、UDP?2. Socket在哪里呢?3. Socket是什么呢?4. 你会使用它们吗?什么是TCP/IP、UDP?TCP/IP(Transmission Control Protocol/Internet Protocol)即传输控制协议/网间协议,是一个工业标准的协议集,它是为广域网(WANs)设计的。
UDP(User Data Protocol,用户数据报协议)是与TCP 相对应的协议。
它是属于TCP/IP协议族中的一种。
这里有一张图,表明了这些协议的关系。
TCP/IP协议族包括运输层、网络层、链路层。
现在你知道TCP/IP与UDP的关系了吧。
ICMP协议格式ICMP协议有两种类型:查询报文和差错报文。
其格式如下:差错报文中,Type是差错类型;Code是差错类型中的子类型;Checksum是ICMP的校验和(校验和覆盖ICMP的头和数据)。
其数据部分包含出错包的IP头(包括选项)和IP数据的前八个字节。
地址伪装只处理三种协议(ICMP,TCP,UDP)的差错包,并且只处理三种类型的差错报文,如下:ICMP_DEST_UNREACH(目的不可达),ICMP_SOURCE_QUENCH(源端被关闭),ICMP_TIME_EXCEEDED(超时)。
差错报文中,Type是差错类型;Code是差错类型中的子类型;Checksum是ICMP的校验和(校验和覆盖ICMP的头和数据)。
其数据部分包含出错包的IP头(包括选项)和IP数据的前八个字节。
地址伪装只处理三种协议(ICMP,TCP,UDP)的差错包,并且只处理三种类型的差错报文,如下:ICMP_DEST_UNREACH(目的不可达),ICMP_SOURCE_QUENCH(源端被关闭),ICMP_TIME_EXCEEDED(超时)。
原始套接字网络嗅探器的实现与应用
1 . 3 U D P协议结构
U D P 数 据段 头 比较 简单 .由一 个 8 字 节 的头和 数 据部 分 组成 具体 格式 如表 2 所示。
表2 U D P报 文 格 式
源端 口 ( 1 6 位 )I 目的端 口 ( 1 6位)
U D P长度 ( 1 6位 U D P校验和
#d e f i n e I P DAD DR
9 1 2
1 6
g e t h o s t n a me函数获取 本地 主机 的名称 。
( 2) g e t h O S t b Y n a m e( h O S t —n a m e
#d e f i n e H I — I C M P
G MP #d e f i n e H II
—Байду номын сангаас
#d e f i n e H I — T C P
D P #d e f i n e H IU
—
#d e f i n e HI — OS P F
5
1 . 4 T C P协议结构
2 . 3实现的具体 函数
( 1 )g e t h o s t n a me (h o s t _ n a me .s i z e o f ( h o s t _ n a me ))
术
#d e f i n e I P — H I T Y P E #d e f i n e I P — S A D D R
而T C P 数 据 头则 比较 复 杂 ,以 2 0个 固定 字 节开
始 在 固定头 后 面还可 以有一 些长 度不 固定 的可 选项
2 . 2具体实现
根 据 前文 提 到的把 网卡设 置成 混杂模 式 ,进 而实
原始套接字
传输层 (TCP、UDP)
网络互联层 (IP)
主机到网络 (网络接口层)
应用程序
13.1
原始套接字概述
标准套接字
TCP/UDP IP
网络 核心
原始套接字 用户空间 内核空间
ICMP
套接字与内核的访问关系
13.1
原始套接字概述
原始套接字能够提供以下3种标准套接字不具备的功能:
(0 – 40字节)
数据
全长 ip_len
分片偏移 ip_off
首部校验和 ip_cksum
13.3.1 IP首部结构
31
20 字节
最大 65535 字节
Linux中 struct ip 结构体说明:
struct ip {
#if __BYTE_ORDER == __LITTLE_ENDIAN
接收时间戳
发送时间戳
13.3.2ICMP首部结构
类型:13或14,时间戳请求和应答 代码:0
Linux中 struct icmp 结构体说明(BSD):
struct icmp {
u_int8_t
u_int8_t
icmp_type; icmp_code;
u_int16_t
icmp_cksum;
③ 如果IP以分片形式到达,则所有分片都已经接收到并重组后才传给原始 套接字 。
④ 内核不能识别的协议、格式等传给原始套接字,因此,可以使用原始套 接字实现自定义协议格式。
13.2.3
原始套接收报文
⑤ 如果收到的数据中的协议类型与自定义的原始套接字匹配,则将接收到
的数据复制到原始套接字接收缓冲区中。
② 原始套接字创建完成后,一般还需要指定套接字数据格式类型,使得 原始套接字可确定以从网络接收哪种格式的数据。
Winsock通信原理之详解
详析Winsock通信原理1.Windows套接字技术套接字(Socket)是网络通信的基本构件,最初是由加利福尼亚大学Berke ley学院为UNIX开发的网络通信编程接口,它只能运行在UNIX操作系统,不支持DOS和Windows操作系统。
随着Windows操作系统的日益推广,90年代初,微软和第三方厂商共同制定了一套标准,即Windows Socket规范,简称WinSoc k。
套接字的概念与文件句柄类似,一个套接字就是一个通信标识,由一个短整数表示,实际上就是一个句柄,代表网络协议中的一组数据,该数据包含了通信双方的IP地址和当前的连接状态等信息。
我们知道,如果一个文件被打开,可以通过文件句柄对文件进行读写操作,套接字也一样,只不过套接字提供的函数更多一些。
2.WinSock的通信机制套接字存在于通信区域中,由协议、地址、端口来描述并在网络中用一个三元组来全局唯一标志一个进程:(协议,本地地址,本地端口号),这样一个三元组叫做一个半相关(half-association),它指定连接的每半部分。
一个完整的网间进程通信需要由两个进程组成,并且只能使用同一种高层协议。
也就是说,不可能通信的一端用TCP协议,而另一端用UDP协议。
因此一个完整的网间通信需要一个五元组来标识:(协议,本地地址,本地端口号,远地地址,远地端口号)这样一个五元组,叫做一个相关(association),即两个协议相同的半相关才能组合成一个合适的相关,或完全指定组成一连接。
根据传输协议的不同,套接字可分为3种类型:流式套接字、数据报套接字和原始套接字。
流式套接字提供了一个面向连接的、可靠的、数据无错且按顺序接收的服务,这种套接字对应的是面向连接的传输协议,如TCP/IP协议簇中的TCP。
数据报套接字提供了一个无连接服务,不提供无错保证,数据可能丢失或重复,且接受顺序混乱,该套接字所对应的是无连接传输协议,如TCP/IP协议簇中的UDP。
信息安全系统实践第十一次作业原始套接字、
四川大学计算机学院、软件学院
实验报告
学号:姓名:专业:__软件工程__ 班级:第 12 周
{
tcph=(struct tcphdr*)(buffer+sizeof(struct ether_header)+sizeof(struct ip));
printf("Sourport:%d\n",ntohs(tcph->source));
printf("Destport :%d\n",ntohs(tcph->dest));
}
}
}
}
这里主要修改的地方是:
1、原代码问题:在输出ip那部分需要利用inet_ntop函数,不然程序运行出问题。
2、加入了TCP头部解封,输出源端口和目的端口,当然还要把相应的头文件加入。
其实只要把上面这程序和这次的syn flood结合起来再做点修
*((u_char*)&oddbyte)=*(u_char*)ptr;
sum+=oddbyte;
}
sum = (sum>>16)+(sum & 0xffff);
sum = sum + (sum>>16);
answer=(short)~sum;
return(answer);
}
下面让我们看一下运行效果:
运行syn flood程序,使用不同的伪装IP攻击:
然后检验结果:
数据记录
和计算
结论
通过(结果)。
套接字
套接字:通信端点16.2.1 什么是套接字套接字是一种具有之前所说的“通信端点”概念的计算机网络数据结构。
网络化的应用程序在开始任何通讯之前都必需要创建套接字。
就像电话的插口一样,没有它就完全没办法通信。
套接字起源于20世纪70年代加州大学伯克利分校版本的Unix,即人们所说的BSD Unix。
因此,有时人们也把套接字称为“伯克利套接字”或“BSD套接字”。
一开始,套接字被设计用在同一台主机上多个应用程序之间的通讯。
这也被称作进程间通讯,或IPC。
套接字有两种,分别是基于文件型的和基于网络型的。
Unix套接字是我们要介绍的第一个套接字家族。
其“家族名”为AF_UNIX(在POSIX1.g标准中也叫AF_LOCAL),表示“地址家族:UNIX”。
包括Python在内的大多数流行平台上都使用术语“地址家族”及其缩写“AF”。
而老一点的系统中,地址家族被称为“域”或“协议家族”,并使用缩写“PF”而不是“AF”。
同样的,AF_LOCAL(在2000-2001年被列为标准)将会代替AF_UNIX。
不过,为了向后兼容,很多系统上,两者是等价的。
Python自己则仍然使用AF_UNIX。
由于两个进程都运行在同一台机器上,而且这些套接字是基于文件的。
所以,它们的底层结构是由文件系统来支持的。
这样做相当有道理,因为,同一台电脑上,文件系统的确是不同的进程都能访问的。
另一种套接字是基于网络的,它有自己的家族名字:AF_INET,或叫“地址家族:Internet”。
还有一种地址家族AF_INET6被用于网际协议第6版(IPv6)寻址上。
还有一些其他的地址家族,不过,它们要么是只用在某个平台上,要么就是已经被废弃,或是很少被使用,或是根本就还没有实现。
所有地址家族中,AF_INET是使用最广泛的一个。
Python 2.5中加入了一种Linux套接字的支持:AF_NETLINK(无连接(稍后讲解))套接字家族让用户代码与内核代码之间的IPC可以使用标准BSD套接字接口。
uIP原始套接字
UI原始套接字(protosockets)库2016-03-05 11:08:17| 分类:TCP/UDP |详细说明:原始套接字(protosocket)为uIP提供了一个与传统BSD套接字接口类似的接口。
不同于为传统uIP事件驱动接口写的程序,为原始套接字(protosocket)接口写的程序是顺序方式执行的,并且无需以明确的状态机方式实现。
原始套接字(protosocket)只能用于TCP连接。
原始套接字(protosocket)库使用"原始线程(protothreads)"来提供顺序控制流。
这使得原始套接字在内存方面变得轻量型,但也同时意味着原始套接字继承了"原始线程"的功能限制。
每个原始套接字只能生存于单个函数中,自动变量(栈变量)不能跨原始套接字函数调用存在。
注意:由于原始套接字库使用的是"原始线程(protothreads)",在调用原始套接字库函数时,局部变量并不总能得到保存.所以这里建议局部变量的使用要十分小心。
原始套接字库提供了一些无需处理重传和回应的发送数据函数,和一些无需对被分解成多个TCP段的数据进行处理的读取数据函数。
由于每个原始套接字都作为一个“原始线程”来运行,应在使用原始套接字的函数起始处通过调用PSOCK_BEGIN() 的方式启用原始套接字。
与之类似,原始套接字可以通过调用PSOCK_EXIT()结束掉。
相关文件:psock.h 原始套接字库头文件。
相关结构体:struct psock_bufstruct psock 代表一个原始套接字。
#define PSOCK_INIT(psock, buffer, buffersize) 初始化一个原始套接字。
#define PSOCK_BEGIN(psock) 在一个函数中启用一个原始套接字的原始线程。
#define PSOCK_SEND(psock, data, datalen) 发送数据。
原始套接字接收流程
原始套接字接收流程:
原始套接字接收流程主要包括以下几个步骤:
1.创建原始套接字:使用socket(PF_PACKET, SOCK_RAW, protocol)函数创建原始套接
字,其中PF_PACKET表示使用原始套接字协议族,SOCK_RAW表示创建原始套接字,protocol指定可以接收或发送的数据包类型。
2.绑定套接字:使用bind()函数将原始套接字绑定到特定的网络接口和端口上。
3.接收数据包:使用recvfrom()函数从原始套接字接收数据包。
这个函数将接收到的
数据包保存到指定的缓冲区中,并返回数据包的长度。
4.处理数据包:在接收到数据包后,可以对数据包进行进一步的处理。
例如,可以解
析数据包的各个字段,提取有用的信息,或者对数据包进行过滤和转发等操作。
5.关闭套接字:使用close()函数关闭原始套接字。
计算机网络课程设计-编程实现简单的TCP协议分析器
编程实现简单的TCP协议分析器一、问题描述编程实现简单的TCP协议分析器,TCP协议分析器是一种用于监督和跟踪网络活动的诊断工具,它从局域网中抓取IP数据包,并对它进行分析得到相应的头部信息,过滤TCP包进行分析,得到TCP包的相应信息。
二、基本要求1.利用原始套接字实现简单的TCP协议分析器。
2.系统功能包括:2.1 原始套接字与网卡绑定,并接收流经网卡的所有数据包;2.2 对数据包进行分析以获得源IP地址和目的IP地址;2.3 对TCP Segment进行分析以获得其首部详细信息;2.4 显示分析结果。
3 建议使用VC++。
三、设计思想TCP协议的数据传送程序是由二个子程序组成的。
也可以看成是服务器端程序和客户端程序,其中:服务器端程序的功能是侦听端口号,接收远程主要的TCP连接申请,并接收远程主机传送来的文字数据。
另外一个子程序,也就是所谓的客户端程序,主要实现向网络的远程主机提出TCP连接申请。
程序利用原始套接字抓取局域网中的IP包。
TCP协议分析器实现了sniffer的一部分功能。
而sniffer的工作原理是:1. 把网卡置于混杂模式;2. 捕获数据包;3. 分析数据包。
Raw Socket: 原始套接字可以用它来发送和接收 IP 层以上的原始数据包, 如 ICMP, TCP, UDP等。
四、系统结构(1)Pcap_addr描述网络接口地址;(2)pcap_pkthdr用来描述每个捕获到的数据包的基本信息;(3)int_pcaplookupnet获取网络地址和网络掩码;(4)int_pcaploop循环捕获网络数据包,直到遇到错误或满足退出条件;(5)pcap_t* pcap_open_dead构造一个libpcap句柄。
五、程序流程(或模块划分)六、源程序#include "pcap.h"struct ether_header{u_int8_t ether_dhost[6];/* 目的以太网地址*/u_int8_t ether_shost[6];/* 源以太网地址*/u_int16_t ether_type;/* 以太网类型*/};struct arp_header{u_int16_t arp_hardware_type;/* 硬件类型*/u_int16_t arp_protocol_type;/* 协议类型*/u_int8_t arp_hardware_length;/* 硬件地址长度*/u_int8_t arp_protocol_length;/* 协议地址长度*/u_int16_t arp_operation_code;/* 操作码*/u_int8_t arp_source_ethernet_address[6];/* 源以太网地址*/u_int8_t arp_source_ip_address[4];/* 源IP地址*/u_int8_t arp_destination_ethernet_address[6];/* 目的以太网地址*/u_int8_t arp_destination_ip_address[4];/* 目的IP地址*/};struct ip_header{#if defined(WORDS_BIGENDIAN)u_int8_t ip_version: 4,/* 版本*/ip_header_length: 4;/* 首部长度*/#elseu_int8_t ip_header_length: 4, ip_version: 4;#endifu_int8_t ip_tos;/* 服务质量*/u_int16_t ip_length;/* 长度*/u_int16_t ip_id;/* 标识*/u_int16_t ip_off;/* 偏移*/u_int8_t ip_ttl;/* 生存时间*/u_int8_t ip_protocol;/* 协议类型*/u_int16_t ip_checksum;/* 校验和*/struct in_addr ip_souce_address;/* 源IP地址*/struct in_addr ip_destination_address;/* 目的IP地址*/};struct udp_header{u_int16_t udp_source_port;/* 源端口号*/u_int16_t udp_destination_port;/* 目的端口号*/u_int16_t udp_length;/* 长度*/u_int16_t udp_checksum;/* 校验和*/};struct tcp_header{u_int16_t tcp_source_port;/* 源端口号*/u_int16_t tcp_destination_port;/* 目的端口号*/u_int32_t tcp_sequence_liuzhen;/* 序列号*/u_int32_t tcp_acknowledgement;/* 确认序列号*/#ifdef WORDS_BIGENDIANu_int8_t tcp_offset: 4,/* 偏移*/tcp_reserved: 4;/* 未用*/#elseu_int8_t tcp_reserved: 4,/* 未用*/tcp_offset: 4;/* 偏移*/#endifu_int8_t tcp_flags;/* 标记*/u_int16_t tcp_windows;/* 窗口大小*/u_int16_t tcp_checksum;/* 校验和*/u_int16_t tcp_urgent_pointer;/* 紧急指针*/};struct icmp_header{u_int8_t icmp_type;/* ICMP类型*/u_int8_t icmp_code;/* ICMP代码*/u_int16_t icmp_checksum;/* 校验和*/u_int16_t icmp_id;/* 标识符*/u_int16_t icmp_sequence;/* 序列码*/};void tcp_protocol_packet_callback(u_char *argument, const struct pcap_pkthdr *packet_header, const u_char *packet_content){struct tcp_header *tcp_protocol;/* TCP协议变量*/u_char flags;/* 标记*/int header_length;/* 长度*/u_short source_port;/* 源端口*/u_short destination_port;/* 目的端口*/u_short windows;/* 窗口大小*/u_short urgent_pointer;/* 紧急指针*/u_int sequence;/* 序列号*/u_int acknowledgement;/* 确认号*/u_int16_t checksum;/* 校验和*/tcp_protocol = (struct tcp_header*)(packet_content + 14+20);/* 获得TCP协议内容*/source_port = ntohs(tcp_protocol->tcp_source_port);/* 获得源端口*/destination_port = ntohs(tcp_protocol->tcp_destination_port);/* 获得目的端口*/header_length = tcp_protocol->tcp_offset *4;/* 长度*/sequence = ntohl(tcp_protocol->tcp_sequence_liuzhen);/* 序列码*/acknowledgement = ntohl(tcp_protocol->tcp_acknowledgement);/* 确认序列码*/windows = ntohs(tcp_protocol->tcp_windows);/* 窗口大小*/urgent_pointer = ntohs(tcp_protocol->tcp_urgent_pointer);/* 紧急指针*/flags = tcp_protocol->tcp_flags;/* 标识*/checksum = ntohs(tcp_protocol->tcp_checksum);/* 校验和*/printf("------- TCP协议-------\n");printf("源端口号:%d\n", source_port);printf("目的端口号:%d\n", destination_port);switch (destination_port){case 80:printf("上层协议为HTTP协议\n");break;case 21:printf("上层协议为FTP协议\n");break;case 23:printf("上层协议为TELNET协议\n");break;case 25:printf("上层协议为SMTP协议\n");break;case 110:printf("上层协议POP3协议\n");break;default:break;}printf("序列码:%u\n", sequence);printf("确认号:%u\n", acknowledgement);printf("首部长度:%d\n", header_length);printf("保留:%d\n", tcp_protocol->tcp_reserved);printf("标记:");if (flags &0x08)printf("PSH ");if (flags &0x10)printf("ACK ");if (flags &0x02)printf("SYN ");if (flags &0x20)printf("URG ");if (flags &0x01)printf("FIN ");if (flags &0x04)printf("RST ");printf("\n");printf("窗口大小:%d\n", windows);printf("校验和:%d\n", checksum);printf("紧急指针:%d\n", urgent_pointer);}void ip_protocol_packet_callback(u_char *argument, const struct pcap_pkthdr *packet_header, const u_char *packet_content){struct ip_header *ip_protocol;/* IP协议变量*/u_int header_length;/* 长度*/u_int offset;/* 偏移*/u_char tos;/* 服务质量*/u_int16_t checksum;/* 校验和*/ip_protocol = (struct ip_header*)(packet_content + 14);/* 获得IP协议内容*/checksum = ntohs(ip_protocol->ip_checksum);/* 获得校验和*/header_length = ip_protocol->ip_header_length *4;/* 获得长度*/tos = ip_protocol->ip_tos;/* 获得服务质量*/offset = ntohs(ip_protocol->ip_off);/* 获得偏移*/if (ip_protocol->ip_protocol==6){printf("----------- IP协议-----------\n");printf("版本号:%d\n", ip_protocol->ip_version);printf("首部长度:%d\n", header_length);printf("服务质量:%d\n", tos);printf("总长度:%d\n", ntohs(ip_protocol->ip_length));printf("标识:%d\n", ntohs(ip_protocol->ip_id));printf("偏移:%d\n", (offset &0x1fff) *8);printf("生存时间:%d\n", ip_protocol->ip_ttl);printf("协议类型:%d\n", ip_protocol->ip_protocol);printf("上层协议为TCP协议\n");printf("校验和:%d\n", checksum);printf("源IP地址:%s\n", inet_ntoa(ip_protocol->ip_souce_address));/* 获得源IP地址*/printf("目的IP地址:%s\n", inet_ntoa(ip_protocol->ip_destination_address));/* 获得目的IP地址*/}}void ethernet_protocol_packet_callback(u_char *argument, const struct pcap_pkthdr *packet_header, const u_char *packet_content){static int packet_number = 1;/* 数据包个数,静态变量*/u_short ethernet_type;/* 以太网类型*/struct ether_header *ethernet_protocol;struct ip_header *ip_protocol;/* IP协议变量*/u_int header_length;/* 长度*/u_int offset;/* 偏移*/u_char tos;/* 服务质量*/u_int16_t checksum;/* 校验和*/ip_protocol = (struct ip_header*)(packet_content + 14);/* 获得IP协议内容*/checksum = ntohs(ip_protocol->ip_checksum);/* 获得校验和*/header_length = ip_protocol->ip_header_length *4;/* 获得长度*/tos = ip_protocol->ip_tos;/* 获得服务质量*/offset = ntohs(ip_protocol->ip_off);/* 获得偏移*//* 以太网协议变量*/ethernet_protocol = (struct ether_header*)packet_content;ethernet_type = ntohs(ethernet_protocol->ether_type);/* 获得以太网类型*/if(ethernet_type==0x0800 && ip_protocol->ip_protocol==6){u_char *mac_string;/* 以太网地址*/printf("**************************************************\n");printf("捕获第%d个TCP网络数据包\n", packet_number);printf("捕获时间:\n");printf("%s", ctime((const time_t*) &packet_header->_sec));/* 获得捕获数据包的时间*/printf("数据包长度:\n");printf("%d\n", packet_header->len);printf("-------- 以太网协议--------\n");/* 获得以太网协议内容*/printf("类型:\n");printf("%04x\n", ethernet_type);printf("源以太网地址: \n");mac_string = ethernet_protocol->ether_shost;printf("%02x:%02x:%02x:%02x:%02x:%02x\n", *mac_string, *(mac_string + 1), *(mac_string + 2), *(mac_string + 3), *(mac_string + 4), *(mac_string + 5));/* 获得源以太网地址*/printf("目的以太网地址: \n");mac_string = ethernet_protocol->ether_dhost;printf("%02x:%02x:%02x:%02x:%02x:%02x\n", *mac_string, *(mac_string + 1), *(mac_string + 2), *(mac_string + 3), *(mac_string + 4), *(mac_string + 5));/* 获得目的以太网地址*/ip_protocol_packet_callback(argument, packet_header, packet_content);packet_number++;printf("**************************************************\n");}}void main(){pcap_t *pcap_handle;/* Winpcap句柄*/char error_content[PCAP_ERRBUF_SIZE];/* 存储错误信息*/char *net_interface;/* 网络接口*/struct bpf_program bpf_filter;/* BPF过滤规则*/char bpf_filter_string[] = "";/* 过滤规则字符串*/bpf_u_int32 net_mask;/* 掩码*/bpf_u_int32 net_ip;/* 网路地址*/net_interface = pcap_lookupdev(error_content);/* 获得可用的网络接口*/pcap_lookupnet(net_interface, &net_ip, &net_mask, error_content);/* 获得网络地址和掩码地址*/pcap_handle = pcap_open_live(net_interface, BUFSIZ, 1, 1, error_content);/* 打开网路接口*/pcap_compile(pcap_handle, &bpf_filter, bpf_filter_string, 0, net_ip);/* 编译BPF过滤规则*/pcap_setfilter(pcap_handle, &bpf_filter);/* 设置过滤规则*/if (pcap_datalink(pcap_handle) != DLT_EN10MB)return ;pcap_loop(pcap_handle, - 1, ethernet_protocol_packet_callback, NULL);/* 注册回调函数,循环捕获网络数据包,利用回调函数来处理每个数据包*/ pcap_close(pcap_handle);/* 关闭Winpcap操作*/}七、测试数据本地局域网IP数据包八、测试情况程序运行结果图:结论通过两周的课程设计,增强了我的实际动手能力,通过实际的编程整合串联了我所学到的知识。
网络编程--Socket(套接字)
⽹络编程--Socket(套接字)⽹络编程⽹络编程的⽬的就是指直接或间接地通过⽹络协议与其他计算机进⾏通讯。
⽹络编程中有两个主要的问题,⼀个是如何准确的定位⽹络上⼀台或多台主机,另⼀个就是找到主机后如何可靠⾼效的进⾏数据传输。
在TCP/IP协议中IP层主要负责⽹络主机的定位,数据传输的路由,由IP地址可以唯⼀地确定Internet上的⼀台主机。
⽽TCP层则提供⾯向应⽤的可靠的或⾮可靠的数据传输机制,这是⽹络编程的主要对象,⼀般不需要关⼼IP层是如何处理数据的。
⽬前较为流⾏的⽹络编程模型是客户机/服务器(C/S)结构。
即通信双⽅⼀⽅作为服务器等待客户提出请求并予以响应。
客户则在需要服务时向服务器提出申请。
服务器⼀般作为守护进程始终运⾏,监听⽹络端⼝,⼀旦有客户请求,就会启动⼀个服务进程来响应该客户,同时⾃⼰继续监听服务端⼝,使后来的客户也能及时得到服务。
在Internet上IP地址和主机名是⼀⼀对应的,通过域名解析可以由主机名得到机器的IP,由于机器名更接近⾃然语⾔,容易记忆,所以使⽤⽐IP地址⼴泛,但是对机器⽽⾔只有IP地址才是有效的标识符。
通常⼀台主机上总是有很多个进程需要⽹络资源进⾏⽹络通讯。
⽹络通讯的对象准确的讲不是主机,⽽应该是主机中运⾏的进程。
这时候光有主机名或IP地址来标识这么多个进程显然是不够的。
端⼝号就是为了在⼀台主机上提供更多的⽹络资源⽽采取得⼀种⼿段,也是TCP层提供的⼀种机制。
只有通过主机名或IP地址和端⼝号的组合才能唯⼀的确定⽹络通讯中的对象:进程。
套接字所谓socket通常也称作"套接字",⽤于描述IP地址和端⼝,是⼀个通信链的句柄。
应⽤程序通常通过"套接字"向⽹络发出请求或者应答⽹络请求。
套接字可以根据通信性质分类,这种性质对于⽤户是可见的。
应⽤程序⼀般仅在同⼀类的套接字间进⾏通信。
不过只要底层的通信协议允许,不同类型的套接字间也照样可以通信。
linuxsock_raw原始套接字编程
linux sock_raw原始套接字编程sock_raw原始套接字编程能够接收到本机网卡上的数据帧或数据包,对与监听网络的流量和分析是很有作用的.一共能够有3种方式创建这种socket1.socket(AF_INET, SOCK_RAW, IPPROTO_TCP|IPPROTO_UDP|IPPROTO_ICMP)发送接收ip数据包2.socket(PF_PACKET, SOCK_RAW, htons(ETH_P_IP|ETH_P_ARP|ETH_P_ALL))发送接收以太网数据帧3.socket(AF_INET, SOCK_PACKET, htons(ETH_P_IP|ETH_P_ARP|ETH_P_ALL))过时了,不要用啊明白得一下SOCK_RAW的原理, 比如网卡收到了一个14+20+8+100+4 的udp的以太网数据帧.第一,网卡对该数据帧进行硬过滤(依照网卡的模式不同会有不同的动作,若是设置了promisc混杂模式的话,那么不做任何过滤直接交给下一层输入例程,不然非本机mac或广播mac会被直接抛弃).依照上面的例子,若是成功的话,会进入ip 输入例程.可是在进入ip输入例程之前,系统会检查系统中是不是有通过socket(AF_PACKET, SOCK_RAW, ..)创建的套接字.若是有的话而且协议相符,在那个例子中确实是需要ETH_P_IP或ETH_P_ALL类型.系统就给每一个如此的socket接收缓冲区发送一个数据帧拷贝.然后进入下一步.第二,进入了ip输入例程(ip层会对该数据包进行软过滤,确实是检查校验或抛弃非本机ip或广播ip的数据包等,具体要参考源代码),例子中确实是若是成功的话会进入udp输入例程.可是在交给udp输入例程之前,系统会检查系统中是不是有通过socket(AF_INET, SOCK_RAW, ..)创建的套接字.若是有的话而且协议相符,在那个例子中确实是需要IPPROTO_UDP类型.系统就给每一个如此的socket接收缓冲区发送一个数据帧拷贝.然后进入下一步.最后,进入udp输入例程 ...ps:若是校验和犯错的话,内核会直接抛弃该数据包的.而可不能拷贝给sock_raw 的套接字,因为校验和都犯错了,数据确信有问题的包括所有信息都没成心义了.进一步分析他们的能力.1. socket(AF_INET, SOCK_RAW, IPPROTO_UDP);能:该套接字能够接收协议类型为(tcp udp icmp等)发往本机的ip数据包,从上面看的确实是20+8+100.不能:不能收到非发往本地ip的数据包(ip软过滤会抛弃这些不是发往本机ip的数据包).不能:不能收到从本机发送出去的数据包.发送的话需要自己组织tcp udp icmp等头部.能够setsockopt来自己包装ip 头部这种套接字用来写个ping程序比较适合2. socket(PF_PACKET, SOCK_RAW, htons(x));那个套接字比较壮大,创建这种套接字能够监听网卡上的所有数据帧.从上面看确实是20+20+8+100.最后一个以太网crc从来都不算进来的,因为内核已经判定过了,对程序来讲没有任何意义了.能: 接收发往本地mac的数据帧能: 接收从本机发送出去的数据帧(第3个参数需要设置为ETH_P_ALL)能: 接收非发往本地mac的数据帧(网卡需要设置为promisc混杂模式)协议类型一共有四个ETH_P_IP 0x800 只接收发往本机mac的ip类型的数据帧ETH_P_ARP 0x806 只同意发往本机mac的arp类型的数据帧ETH_P_ARP 0x8035 只同意发往本机mac的rarp类型的数据帧ETH_P_ALL 0x3 接收发往本机mac的所有类型ip arp rarp的数据帧, 接收从本机发出的所有类型的数据帧.(混杂模式打开的情形下,会接收到非发往本地mac的数据帧)发送的时候需要自己组织整个以太网数据帧.所有相关的地址利用struct sockaddr_ll 而不是struct sockaddr_in(因为协议簇是PF_PACKET不是AF_INET了),比如发送给某个机械,对方的地址需要利用struct sockaddr_ll.这种socket大小通吃,强悍下面是一段相关的代码:perror("iotcl()");return -1;}return 0;}int unset_promisc(char *interface, int fd) {struct ifreq ifr;strcpy(ifr.ifr_name, interface);if(ioctl(fd, SIOCGIFFLAGS, &ifr) == -1) {perror("iotcl()");return -1;}ifr.ifr_flags &= ~IFF_PROMISC;if(ioctl(fd, SIOCSIFFLAGS, &ifr) == -1) {perror("iotcl()");return -1;}return 0;}3. socket(AF_INET, SOCK_PACKET, htons(ETH_P_ALL))那个最好不要用,终归我不用...总结利用方式: 1.只想收到发往本机某种协议的ip数据包的话用第一种就足够了2. 更多的详细的内容请利用第二种.包括ETH_P_ALL参数和混杂模式都能够使它的能力不断的增强.ps:很多自己的方式.虚拟机测试环境.有错欢迎指出交流qq:110024218我写的ping#include "arpa/inet.h"#include "signal.h"#include "sys/time.h"extern int errno;int sockfd;struct sockaddr_in addr; //peer addrchar straddr[128]; //peer addr ip(char*)char sendbuf[2048];char recvbuf[2048];int sendnum;int recvnum;int datalen = 30;unsigned short my_cksum(unsigned short *data, int len) {int result = 0;for(int i=0; i<len/2; i++) {result += *data;data++;}while(result >> 16)result = (result&0xffff) + (result>>16);return ~result;}void tv_sub(struct timeval* recvtime, const struct timeval* sendtime) { int sec = recvtime->tv_sec - sendtime->tv_sec;int usec = recvtime->tv_usec - sendtime->tv_usec;if(usec >= 0) {recvtime->tv_sec = sec;recvtime->tv_usec = usec;} else {recvtime->tv_sec = sec-1;recvtime->tv_usec = -usec;}}void send_icmp() {struct icmp* icmp = (struct icmp*)sendbuf;icmp->icmp_type = ICMP_ECHO;icmp->icmp_code = 0;icmp->icmp_cksum = 0;icmp->icmp_id = getpid(); //needn't use htons() call, because peer networking kernel didn't handle this data and won't make different meanings(bigdian litteldian)icmp->icmp_seq = ++sendnum; //needn't use hotns() call too.gettimeofday((struct timeval*)icmp->icmp_data, NULL);int len = 8+datalen;icmp->icmp_cksum = my_cksum((unsigned short*)icmp, len);int retval = sendto(sockfd, sendbuf, len, 0, (struct sockaddr*)&addr, sizeof(addr));if(retval == -1){perror("sendto()");exit(-1);} else {// printf("send icmp request to %s(%d) bytes\n", straddr, len);}}void recv_icmp() {struct timeval *sendtime;struct timeval recvtime;for(;;) {int n = recvfrom(sockfd, recvbuf, sizeof(recvbuf), 0, 0, 0);if(n == -1) {if(errno == EINTR)continue;else {perror("recvfrom()");exit(-1);}} else {gettimeofday(&recvtime, NULL);struct ip *ip = (struct ip*)recvbuf;if(ip->ip_src.s_addr != addr.sin_addr.s_addr) {// printf("ip_src is not : %s\n", straddr);continue;}struct icmp *icmp = (struct icmp*)(recvbuf + ((ip->ip_hl)<<2));if(icmp->icmp_id != getpid()) {// printf("icmp_id is not :%d\n", getpid());continue;}recvnum++;sendtime = (structtimeval*)icmp->icmp_data;tv_sub(&recvtime, sendtime);printf("imcp echo from %s(%dbytes)\tttl=%d\tseq=%d\ttime=%d.%06d s\n", straddr, n, ip->ip_ttl, icmp->icmp_seq, _sec, _usec);}}}void catch_sigalrm(int signum) {send_icmp();alarm(1);}void catch_sigint(int signum) {printf("\nPing statics:send %d packets, recv %d packets, %d%% lost...\n", sendnum, recvnum, (int)((float)(sendnum-recvnum)/sendnum)*100);exit(0);}int main(int argc, char **argv) {if(argc != 2) {printf("please use format: ping hostname\n");exit(-1);}sockfd = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP);if(sockfd == -1) {perror("socket()");return -1;}/*int sendbufsize = 180;socklen_t sendbufsizelen = sizeof(sendbufsize);if(setsockopt(sockfd, SOL_SOCKET, SO_RCVBUF, &sendbufsize, sendbufsizelen) == -1)perror("setsockopt()");int recvbufsize;socklen_t recvbufsizelen;if(getsockopt(sockfd, SOL_SOCKET, SO_RCVBUF, &recvbufsize, &recvbufsizelen) == -1)perror("getsockopt()");*/memset(&addr, 0, sizeof(addr));addr.sin_family = AF_INET;int retval = inet_pton(AF_INET, argv[1], &addr.sin_addr);if(retval == -1 || retval == 0) {struct hostent* host = gethostbyname(argv[1]);if(host == NULL) {fprintf(stderr, "gethostbyname(%s):%s\n", argv[1], strerror(errno));exit(-1);}/*if(host->h_name != NULL)printf("hostent.h_name:%s\n", host->h_name);if(host->h_aliases != NULL && *(host->h_aliases) != NULL)printf("hostent.h_aliases:%s\n", *(host->h_aliases));printf("hostent.h_addrtype:%d\n",host->h_addrtype);printf("hostent.h_length:%d\n", host->h_length);*/if(host->h_addr_list != NULL && *(host->h_addr_list) != NULL) {strncpy((char*)&addr.sin_addr,*(host->h_addr_list), 4);inet_ntop(AF_INET, *(host->h_addr_list), straddr, sizeof(straddr));}printf("Ping address:%s(%s)\n\n", host->h_name, straddr);} else {strcpy(straddr, argv[1]);printf("Ping address:%s(%s)\n\n", straddr, straddr);}struct sigaction sa1;memset(&sa1, 0, sizeof(sa1));sa1.sa_handler = catch_sigalrm;sigemptyset(&sa1.sa_mask);sa1.sa_flags = 0;if(sigaction(SIGALRM, &sa1, NULL) == -1)perror("sigaction()");struct sigaction sa2;memset(&sa2, 0, sizeof(sa2));sa2.sa_handler = catch_sigint;sigemptyset(&sa2.sa_mask);sa2.sa_flags = 0;if(sigaction(SIGINT, &sa2, NULL) == -1)perror("sigaction()");。
套接字通信原理
套接字通信原理套接字通信原理套接字(Socket)是一种在计算机网络中进行进程间通信的机制。
套接字通信可以被看做是一种面向流的、双向的、可靠的高层次通信协议。
它的基本原理是通过一个标志符描述对一个网络连接的请求,从而在网络上建立一条连接,并进行数据传输。
套接字通信的原理可以简单地描述为客户端和服务器之间的数据传输。
在套接字系统中,客户端通过IP地址和端口号来连接服务器端。
服务器端监听固定的端口,在服务器端接收到客户端发送的请求后,就可以和客户端进行相应的数据传输。
套接字通信可以使用不同的协议来进行数据传输,其中最常用的是TCP及UDP协议。
套接字通信的实现分为四个步骤,包括初始化、发送数据、接收数据和关闭连接。
在进行初始化时,客户端需要定义一个标识符来标识网络连接。
服务器端需要在进行初始化时激活监听进程,等待客户端的连接请求。
其次,在发送数据时,客户端需要将数据发送给服务器,服务器需要接收并处理这些数据,并向客户端返回结果。
接收数据的过程和发送数据的过程类似,客户端需要接收结果,服务器需要将结果返回给客户端。
最后,在关闭连接时,需要将已经建立的套接字通道断开,释放资源。
套接字通信的优点在于它具有可靠、灵活、可扩展性高等特点。
在进行数据传输时,套接字通信可以通过内存映射和缓存等操作来提高数据传输效率和速度,同时它还可以支持多颗处理器同时工作,以提高系统的并发处理能力。
总的来说,套接字通信是一种高效、可靠、灵活的进程间通信方式。
它广泛应用于各类网络应用程序,例如Web服务器、邮件系统、即时通讯等。
在未来,随着互联网的发展,套接字通信将会成为越来越重要的通信方式。
C++Win32使用原始套接字获取所有ip数据包并分析(包括ping包)
C++Win32使⽤原始套接字获取所有ip数据包并分析(包括ping包)/*页⾯编码:GBK 开发环境 VS2019 */#define _WINSOCK_DEPRECATED_NO_WARNINGS#include <iostream>#include<WinSock2.h>#pragma comment(lib,"ws2_32.lib")#define SIO_RCVALL _WSAIOW(IOC_VENDOR,1)std::string Utf8ToGbk(const char* src_str){int len = MultiByteToWideChar(CP_UTF8, 0, src_str, -1, NULL, 0);wchar_t* wszGBK = new wchar_t[len + 1];memset(wszGBK, 0, len * 2 + 2);MultiByteToWideChar(CP_UTF8, 0, src_str, -1, wszGBK, len);len = WideCharToMultiByte(CP_ACP, 0, wszGBK, -1, NULL, 0, NULL, NULL);char* szGBK = new char[len + 1];memset(szGBK, 0, len + 1);WideCharToMultiByte(CP_ACP, 0, wszGBK, -1, szGBK, len, NULL, NULL);std::string strTemp(szGBK);if (wszGBK) delete[] wszGBK;if (szGBK) delete[] szGBK;return strTemp;}typedef struct _IP_HEADER{char m_cVersionAndHanderLen; //版本信息(前4位),头长度(后4位)char m_cTypeOfService; //服务类型8位short m_sTotalLenOfPacket; //数据包长度short m_sPacketID; //数据包标识short m_sSliceinfo; //分⽚使⽤char m_cTTL; //存活时间char m_cTypeOfProtocol; //协议类型short m_sChechSum; //校验和unsigned int m_uiSourIp; //源IP地址unsigned int m_uiDestIp; //⽬的IP地址}IP_HEADER, * PIP_HEADER;typedef struct _UDP_HEADER{unsigned short m_usSourPort; //源端⼝号16bitunsigned short m_usDestPort; //⽬的端⼝号16bitunsigned short m_usLength; //数据包长度16bitunsigned short m_usCheckSum; //校验和16bit}UDP_HEADER, * PUDP_HEADER;int main(){WSADATA wsadata;int ret = WSAStartup(MAKEWORD(2, 2), &wsadata); if (ret != 0) { printf("初始化winsock2失败!"); return -1; } //if(LOBYTE(wsadata.wVersion) != 2 || HIBYTE(wsadata.wVersion) != 2) { puts("版本号错误!"); return -1; }SOCKADDR_IN saddr;memset(&saddr, 0, sizeof(SOCKADDR_IN));saddr.sin_family = AF_INET;saddr.sin_port = htons(8080);saddr.sin_addr.s_addr = inet_addr("192.168.125.9");SOCKET sockfd = socket(AF_INET, SOCK_RAW, IPPROTO_IP); if (sockfd < 0) { puts("创建套接字失败!"); return system("pause"); }int on = 1;setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, (char*)&on, sizeof(on));ret = bind(sockfd, (SOCKADDR*)&saddr, sizeof(SOCKADDR)); if (ret != 0) { puts("绑定失败!"); return system("pause"); }DWORD dwlen[10], dwlenrtned = 0, optval = 1;WSAIoctl(sockfd, SIO_RCVALL, &optval, sizeof(optval), &dwlen, sizeof(dwlen), &dwlenrtned, NULL, NULL); //允许套接字接收所有经过本机的⽹络数据包char buf[500] = "";int len = sizeof(SOCKADDR);IP_HEADER iph;UDP_HEADER udph;while (1){puts("等待接收数据......");ret = recvfrom(sockfd, buf, 200, 0, (SOCKADDR*)&saddr, &len); if (ret < 0) { puts("接收错误!"); }memcpy(&iph, buf, 20);memcpy(&udph, buf + 20, 8);in_addr ias, iad;ias.S_un.S_addr = iph.m_uiSourIp;iad.S_un.S_addr = iph.m_uiDestIp;printf("协议类型:%d\n", iph.m_cTypeOfProtocol);switch (iph.m_cTypeOfProtocol){case IPPROTO_ICMP:puts("收到ICMP包");break;case IPPROTO_IGMP:puts("收到IGMP包");break;case IPPROTO_UDP:puts("收到UDP包");break;case IPPROTO_TCP:puts("收到TCP包");break;default:puts("未指定协议类型");break;}char str1[20];char str2[20];strcpy_s(str1, inet_ntoa(ias));strcpy_s(str2, inet_ntoa(iad));printf("源地址IP:%s 源地址端⼝:%d \n⽬标地址IP:%s ⽬标地址端⼝:%d \n", str1, ntohs(udph.m_usSourPort), str2,ntohs(udph.m_usDestPort));std::string str = "0";str= Utf8ToGbk(buf+28);std::cout << "接收到的数据段:" << str << std::endl;puts("");}closesocket(sockfd);WSACleanup();return system("pause");}注意:使⽤管理员模式运⾏,因为原始套接字的使⽤是需要管理员权限的。
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
与发送函数一样,接收报文也用一个线程实现,使 用select()轮询等待报文到来。当接收到一个报文后 使用函数icmp_unpack()来解包和查找报文之前发送 时的记录,获取发送时间,计算收发差值并打印信 息。
(1)接收成功后将合法的报文记录重置为没有使 用,flag为0。
(2)接收报文数量增加1。
(1)获得当前的时间值,按照序列号packet_send 将ICMP报文打包到缓冲区send_buff中后,发送到 目的地址。发送成功后,记录发送报文的状态:
(3)在线程开始进入主循环while(alive)之前,将整 个程序的开始发送时间记录下来,用于在程序退出
的时候进行全局统计,即 gettimeofday(&tv_begin,NULL),将时间保存在变量 tv_begin中。
{
struct ih_idseq {
/*显示数据报*/
u_int16_t icd_id;
u_int16_t icd_seq;
}ih_idseq;
/*数据报ID*/ /*数据报的序号*/
}icmp_hun;
#define icmp_id
icmp_hun.ih_idseq.icd_id
}
原始套接字不需要使用bind()函数,因为进行发送 和接收数据的时候可以指定要发送和接收的目的地 址的IP。当系统对socket进行绑定的时候,发送和 接收的函数可以使用send()和recv()及read()和write() 等不需要指定目的地址的函数。
原始套接字发送报文有如下原则:
参数buf为剥去了以太网部分数据的IP数据报文, len为数据长度。可以利用IP头部的参数快速地跳到 ICMP报文部分,IP结构的ip_hl标识IP头部的长度, 由于ip_hl标识的是4字节单位,所以需要乘以4来获 得ICMP段的地址。
由于需要评估网络状况,在发送数据报文的时候保存发送时间,接收到报文后,计算两个时 刻之间的差值,生成了ICMP源主机和目标主机之间的网络状况的时间评估。
(3)为了防止丢包,select()的轮询时间设置的比 较短。
ping程序的实现使用了两个线程,一个线程 icmp_send()用于发送请求,一个线程icmp_recv()用 于接收远程主机的响应。当变量alive为0时,两个 线程退出。
1.ping数据的数据结构
2.信号SIGINT处理函数 3.查找数组中的标识函数icmp_findpacket() 4.统计数据结果函数icmp_statistics()
ping的客户端方式的类型为8,代码值为0,表示 ICMP的回显请求。类型为0,代码为0时,是ICMP 回显应答。校验和为16位的crc16的算法。
TCP/IP协议栈使用的校验算法是比较经典的,对16 位的数据进行累加计算,并返回计算结果。需要注 意的是对奇数个字节数据的计算,是将最后的有效 数据作为最高位的字节,低字节填充了0。
如果没有设置IP_RINCL,则发送缓冲区指向IP头部后 面数据区域的第一个字节,不需要用户填写IP头部,IP 头部的填写工作由内核进行,内核还进行校验和的计算。
与发送报文类似,接收报文也有相似的规则:
通常可以使用recvfrom()或者recv()及read()获得数据。 当设置了IP_RINCL后,接收的缓冲区为IP头部的第
子网掩码请求协议增加了标识符icmp_id、序列号 icmp_seq和掩码icmp_mask。
UDP的头部结构包含发送端的源端口号、数据接收 端的目的端口号、UDP数据的长度以及UDP的校验 和等信息。
TCP的头部结构主要包含发送端的源端口、接收端 的目的端口、数据的序列号、上一个数据的确认号、 滑动窗口大小、数据的校验和、紧急数据的偏移指 针以及一些控制位等信息。
static struct timeval icmp_tvsub(struct timeval end,struct timeval begin)
{
struct timeval tv;
/*计算差值*/
_sec = _sec - _sec;
_usec = _usec - _usec;
使用原始套接字,可以修改IP数据和IP层之上的各层 数据,构造自己的特定类型的TCP或者UDP的分组。
13.2.1 SOCK_RAW选项 13.2.2 IP_HDRINCL套接字选项 13.2.3 不需要bind()函数
创建原始套接字使用socket()函数。下面的代码创建一 个AF_INET协议族中的原始套接字,协议类型为 protocol。
类型为结构struct pingm_packet的变量pingpacket用于保 存发送数据报文的状态。
/*保存已经发送包的状态值*/ typedef struct pingm_pakcet{
struct timeval tv_begin; /*发送的时间*/
struct timeval tv_end;
对于回显请求的ICMP报文,13.5节的ICMP结构可以简化为如下形式:
struct icmp
{
u_int8_t icmp_type;
ቤተ መጻሕፍቲ ባይዱ
u_int8_t icmp_code;
u_int16_t icmp_cksum;
union
/*消息类型*/ /*消息类型的子码*/ /*校验和*/
ttl=128 time=28.2 ms ^C --- ping statistics -- 6 packets transmitted, 6 received, 0% packet loss, time 5009ms rtt min/avg/max/mdev = 28.199/28.763/30.349/0.744 ms
使用套接字选项IP_HDRINCL设置套接字,在之后 进行的接收和发送时,接收到的数据包含IP的头部。 用户之后需要对IP层相关的数据段进行处理,例如 IP头部数据的设置和分析,校验和的计算等。设置 方法如下:
int set = 1;
if(setsockopt(rawsock, IPPROTO_IP, IP_HDRINCL, &set, sizeof(set))<0){ /*错误处理*/
/*接收到的时间*/
short seq;
/*序列号*/
有接i收nt 到fla回g; 应包0,表示接收到/回*1,应表包示*/ 已经发送但没
}pingm_pakcet;
static pingm_pakcet pingpacket[128];
/*终端信号处理函数SIGINT*/ static void icmp_sigint(int signo) { alive = 0; /*告诉接收和发送线程结束程序*/ gettimeofday(&tv_end, NULL); /*读取程序结束时间*/ tv_interval = icmp_tvsub(tv_end, tv_begin); /*计算一
13.1 概述 13.2 原始套接字的创建 13.3 原始套接字发送报文 13.4 原始套接字接收报文 13.5 原始套接字报文处理时的结构 13.6 ping的例子 13.7 洪水攻击 13.8 ICMP洪水攻击 13.9 UDP洪水攻击 13.10 SYN洪水攻击
int rawsock = socket(AF_INET, SOCK_RAW, protocol);
IPPROTO_IP:IP协议,接收或者发送IP数据包,包含 IP头部。
IPPROTO_ICMP:ICMP协议,接收或者发送ICMP的数 据包,IP的头部不需要处理。
IPPROTO_TCP:TCP协议,接收或者发送TCP数据包。 IPPROTO_UDP:UDP协议,接收或者发送UDP数据包。 IPPROTO_RAW:原始IP包。
下总共所用时间*/ return;
}
函数icmp_findpacket()用于在数组pingpacket中查找 一个报文的标识,参数为–1时表示查找一个空包, 存放已经发送成功的数据报文;其他值表示查找seq 匹配的标识。
icmp_statistics()函数用于统计总体的结果,包含成 功发送的报文数量、成功接收的报文数量、丢失报 文的百分比和总共程序运行的时间。
/*如果接收时间的usec值小于发送时的usec值,从usec域借位*/
if(_usec < 0)
{
_sec --;
_usec += 1000000;
}
return tv;
}
发送报文函数是一个线程,每隔1s向目的主机发送 一个ICMP回显请求报文,它在整个程序处于激活 状态(alive为1)时一直发送报文。
通常情况下可以使用sendto()函数并指定发送目的地址 来发送数据,当已经指定了bind()目标地址的时候可以 使用write()或者send()发送数据。
如果使用setsockopt()设置了选项IP_RINCL,则发送的 数据缓冲区指向IP头部第一个字节的头部,用户发送的 数据包含IP头部之后的所有数据,需要用户自己填写IP 头部和计算校验和并需要对所包含数据进行处理和计算。
一个字节。
当没有设置IP_RINCL的时候,接收的缓冲区为IP数 据区域的第一个字节。
13.5.1 IP头部的结构 13.5.2 ICMP头部结构 13.5.3 UDP头部结构 13.5.4 TCP头部结构
ICMP的头部结构比较复杂,主要包含消息类型 icmp_type,消息代码icmp_code、校验和 icmp_cksum等,不同的ICMP类型其他部分有不同 的实现。