如何在 C++ 中创建 RAW TCP/IP 数据包?

Posted

技术标签:

【中文标题】如何在 C++ 中创建 RAW TCP/IP 数据包?【英文标题】:How do I create RAW TCP/IP packets in C++? 【发布时间】:2010-09-09 20:12:26 【问题描述】:

我是一名初级 C++ 程序员/网络管理员,但我认为如果有人指出我正确的方向,我可以学习如何做到这一点。大多数教程都是使用由于某种原因不再有效的旧代码进行演示的。

由于我在 Linux 上,我只需要解释一下如何编写原始伯克利套接字。谁能帮我快速介绍一下?

【问题讨论】:

Beej's network guide 几乎是 linux 环境中与基本网络相关的任何事物的黄金标准。这很简单,我不能更强烈地推荐它。 RAW 套接字非常棘手,在不同的系统上略有不同。我只知道人们使用它们来编码嗅探器(如wireshark)和一些相当神秘的诊断。他们当然不是开始学习套接字编程的好地方。在大多数情况下,“我是 C++ 初学者/网络管理员”的意思是“远离 SOCK_RAW”。 @ChuckKollars,我不同意。我认为学习原始套接字是学习 C++,尤其是 C 的好方法。使用库是一回事,但是 Linux 的原始套接字是一种非常有见地的体验。 C++ 可以做很多独特的事情。我认为避免你暗示的心痛是一个错误。 你到底有什么系统? “原始”套接字实现从一个系统到下一个系统有很大不同,因此您学到的东西(实际上主要是关于系统,而不是关于 C++)不会在其他地方延续。 @ChuckKollars,这是真的;我更多地指的是结构的大量使用,而不是代码的可移植性。 【参考方案1】:

从阅读 Beej's guide on socket programming 开始。它将带您快速了解如何开始编写网络代码。之后,您应该能够通过阅读手册页获得越来越多的信息。

其中大部分内容包括阅读您喜欢的库的文档,以及对手册页的基本了解。

【讨论】:

您在上面提供的链接已失效。这是新的beej.us/guide/bgnet/html【参考方案2】:

首先,如果您澄清“原始”的含义,将会很有帮助。传统上,这意味着您想要制作所有第 4 层标头(TCP/UDP/ICMP... 标头),也许还需要您自己制作一些 IP 标头。为此,您需要的不仅仅是 beej 的教程,我已经在这里提到了很多。在这种情况下,您希望在对套接字的调用中使用 SOCK_RAW 参数获得原始 IP 套接字(请参阅http://mixter.void.ru/rawip.html 了解一些基础知识)。

如果您真正想要的只是能够与某个远程主机(例如 ***.com 上的 80 端口)建立 TCP 连接,那么您可能不需要“原始”套接字,beej 的指南将为您提供很好的服务。

要获得有关该主题的权威参考资料,您应该阅读 Stevens 等人的 Unix Network Programming, Volume 1: The Sockets Networking API (3rd Edition)。

【讨论】:

【参考方案3】:

对于 TCP 客户端:

使用 gethostbyname 查找 dns 名称到 IP,它会返回一个 hostent 结构。我们称这个返回值为主机。

hostent *host = gethostbyname(HOSTNAME_CSTR);

填充socket地址结构:

sockaddr_in sock;
sock.sin_family = AF_INET;
sock.sin_port = htons(REMOTE_PORT);
sock.sin_addr.s_addr = ((struct in_addr *)(host->h_addr))->s_addr;

创建一个socket并调用connect:

s = socket(AF_INET, SOCK_STREAM, 0); 
connect(s, (struct sockaddr *)&sock, sizeof(sock))

对于 TCP 服务器端:

设置套接字

使用 bind 将您的地址绑定到该套接字。

用listen开始监听那个socket

调用accept来获得一个连接的客户端。

一般交流:

使用 send 和 recv 在客户端和服务器之间进行读写。

BSD套接字源代码示例:

您可以在 wikipedia 找到一些很好的示例代码。

进一步阅读:

我强烈推荐this book 和this online tutorial:

4:

【讨论】:

gethostbyname 不应再用于新项目:它与 IP 版本 (IPv4) 绑定。现在 IPv4 地址的剩余库存为 rapidly depleting,您确实应该使用与版本无关的 getaddrinfo(适用于 v4 和 v6)。【参考方案4】:

使用Asio 是一个不错的起点,它是一个用于网络通信的出色跨平台(包括 Linux)库。

【讨论】:

你永远不能让 UDP 数据包看起来或表现得像 TCP 数据包 - 两者的 IP 数据包头是互斥的。【参考方案5】:

您执行此操作的方式与在常规 C 中完全相同,在标准库中没有特定于 C++ 的方式来执行此操作。

我用来学习这个的教程是http://beej.us/guide/bgnet/。这是我环顾四周后发现的最好的教程,它给出了清晰的示例,并且很好地解释了它所描述的所有功能。

【讨论】:

【参考方案6】:

过去一年我一直在开发libtins。它是一个高级 C++ 数据包制作和嗅探库。

除非您想重新发明***并实现每个协议的内部结构,否则我建议您使用一些已经为您做到这一点的更高级别的库。

【讨论】:

【参考方案7】:

这方面有大量参考资料(当然,我想到了 Stevens 的书),但我发现 Beej guide 对于入门非常有用。它足够丰富,你可以理解真正发生的事情,但它也足够简单,你不需要花费几天时间来编写一个 'hello world' udp 客户端/服务器。

【讨论】:

【参考方案8】:

海报,请澄清你的问题。

几乎所有的回复似乎都认为您在要求套接字教程;我读到您的问题意味着您需要创建一个能够发送任意 IP 数据包的原始套接字。 正如我在之前的回答中所说,一些操作系统限制使用原始套接字。

http://linux.die.net/man/7/raw “只有有效用户 ID 为 0 或具有 CAP_NET_RAW 能力的进程才允许打开原始套接字。”

【讨论】:

【参考方案9】:

阅读理查德史蒂文斯的Unix Network Programming。必须的。它解释了它是如何工作的,给你代码,甚至给你帮助方法。你可能想看看他的其他一些书。 Advanced Programming In The Unix Enviernment 是 Unix 中较低级别编程的必需品,这是通用的。我什至不再在 Unix 堆栈上做任何事情,这些书中的内容仍然有助于我编写代码。

【讨论】:

【参考方案10】:

Libpcap 将让您制作完整的数据包(第 2 层到第 7 层)并通过网络发送出去。听起来很有趣,但有一些警告。您需要创建所有适当的标头并自己进行所有校验和。不过,Libnet 可以提供帮助。

如果你想退出 C++ 编程池,python 有scapy。它使制作和传输 TCP/IP 数据包变得轻而易举。

【讨论】:

【参考方案11】:

简单:

#include <sys/socket.h>
#include <sys/types.h>

int socket(int protocolFamily, int Type, int Protocol)
// returns a socket descriptor

int bind(int socketDescriptor, struct sockaddr* localAddress, unsigned int addressLength)
// returns 0 

...等等。

都在 sys/socket.h 中

【讨论】:

【参考方案12】:

在任何情况下,如果您想使用 tcp/ip 堆栈,它们的标头将被添加到您的数据包中。如果你想控制数据包到字节,我建议你 npcap。不幸的是,Libpcap 已经过时了,尽管它更容易使用。但是,我个人使用过 npcap,它提供了对包的完全控制

【讨论】:

他们说他们“在 Linux 上”,这意味着 WinPcap 和 Npcap 都不可用,因为它们仅适用于 Windows - 相反,libpcap 可用,但使用 Linux 的 PF_PACKET 套接字。在 Linux 和其他 UN*Xes 上,libpcap 使用操作系统的内置捕获机制; Windows 不提供,因此 WinPcap 和 Npcap 都将 libpcap 与内核驱动程序和库结合起来与该驱动程序进行通信。 WinPcap 已过时; libpcap 不是——它是程序员通常使用的 Npcap 的一部分。 据我所知,wireshark 完全可以在 npcap 上运行,而且我经常在 windows 和 linux 上使用它,所以我假设 npcap 在 linux 上可用。对于 Windows,这几乎是唯一的方法。 Wireshark 使用 libpcap API 进行数据包捕获。在 UN*Xes 上,它使用操作系统附带的 libpcap。在 Windows 上,它使用 WinPcap 的 libpcap DLL 或 Npcap 的 libpcap DLL(两者都在运行时加载,因此 Wireshark 二进制文件可以在安装或不安装 WinPcap/Npcap 的情况下运行;没有 WinPcap/Npcap,它支持读取捕获文件但不执行实时捕获)。

以上是关于如何在 C++ 中创建 RAW TCP/IP 数据包?的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 C++ 在 MongoDB 中创建地理空间索引

如何在 C++ 中创建 Lua 表,并将其传递给 Lua 函数?

在 SOCK_RAW 通信中创建以太网帧

如何使用 C++ 在 %appdata% 中创建文件 [重复]

如何防止在 C++ 中创建对象

如何在 C++ 中创建参数化对象数组?