一、通用

首先讨论在使用以下任何API进行开发时必须考虑的一些一般性问题。除了这里引出的问题外,编程者还必须注意他想要使用的网络协议的特殊性。如果没有注意到这种特殊性,例如交换信息的正确顺序,就不能确保我们能够与另一个主机通信。

1.1 字节顺序

在网络通信中,字节顺序(也称为endianness)对于正确设置和解释字段至关重要。每当一个值需要超过1字节的存储空间时,我们就必须在网络字节顺序和主机字节顺序之间进行转换。根据所使用的协议和操作系统,网络字节顺序和主机字节顺序可能不同。对于IP协议,网络字节顺序是大端编址,因此每当主机的字节顺序不是大端编址时,我们就必须进行转换[5]。

对于用于IP协议的转换,Berkeley套接字API提供了一组标准函数,这些函数在头文件<arpa/inet.h>:

Function Description
uint32_t htonl(uint32_t hostlong) host-to-network
uint16_t htons(uint16_t hostshort) host-to-network
uint32_t ntohl(uint32_t netlong) network-to-host
uint16_t ntohs(uint16_t netshort) network-to-host

Linux环境下通过套接字接收/发送的数据总是按网络字节顺序[5]。

1.2 checksum

许多用于网络通信的协议使用校验和来检测传输错误。有不同的方法来计算校验和。如果我们选择完全由自己生成首部,我们必须手动计算和设置适用的头字段。如果我们这样做,有几点需要考虑:

  • 使用哪种算法来计算校验和
  • 首部的哪些字段(有时甚至是多个首部的字段)必须包含在校验和的计算中[5]。当然,用户数据可能也被包含在计算中
  • 如果我们想计算校验和,所有的头信息需要已经经过了正确的设置
  • 我们是否必须计算校验和,或者可以通过网络驱动程序/硬件(例如以太网校验和)、操作系统或预定义函数调用自动计算校验和[5]。

作为计算校验和的一个可能的代码示例,附录中给出了用于计算因特网校验和的listling,这个listing随RFC 1071一起提供的。

1.3 Type-Casting

C语言的一个基本功能是支持类型转换。将二进制数据转换为C中的数据结构的能力对于程序员更容易地处理头数据是至关重要的。要执行类型转换,只需创建指向所需结构的指针:

struct header* protocolHeader;

Then we do need a pointer to the memory space where the binary data of the packet is stored, in our examples mostly a char-Array with a fixed SIZE[5]:

char buffer[SIZE];

Finally we can cast the binary data to our struct and afterwards are able to access the header fields[5]:

protocolHeader = (struct header*) buffer;

在访问header字段时,我们仍然需要处理前面提到的字节顺序。对于大多数众所周知的协议,库中包含的头文件中都有预定义的结构。下表显示了一些可用的头,但它只显示了可用头结构的一部分。根据操作系统和库的不同,可能会提供更多的头文件:

Header-File Description
<netinet/ip.h> Defines macros, variables, and structures for IP.
<netinet/ip_icmp.h> Defines macros, variables, and structures for ICMP.
<netinet/udp.h> Defines macros, variables, and structures for UDP.
<netinet/tcp.h> Defines macros, variables, and structures for TCP
<netns/idp.h> Defines IPX packet headers.
<netns/sp.h> Defines SPX packet header.
<ssl.h> Defines SSL prototypes, macros, variables, and structures.

1.4 Header-Positions

另一个必须考虑的问题是首部在二进制数据中的位置。必须考虑以下因素:

  • 较低层的头总是封装较高层的头和数据
  • 最低层的首部总是可以在二进制数据包的开头找到。可能不是所有字段都存在,例如数据链路层中的以太网协议就是这种情况。对于以太网协议,前导码、帧起始分隔符和帧校验和由网络驱动程序删除,因为它们更属于物理层(OSI层1)[5]。
  • 如果我们将二进制数据类型转化为结构体,我们必须将最后一个头的长度添加到数据指针中,以获得下一个头的开始[5]
    `next_header = (struct header*) (buffer + sizeof(struct header));
  • 如果最后一个报头具有可变长度和可选字段(例如IP报头或TCP报头),则要找到正确的起始位置可能比上面所示的简单加操作要困难一些。在这种情况下,我们必须首先得到报头的固定部分,然后访问长度字段,最后计算可选报头的长度。只有这样做之后,我们才能找到下一个首部的开始。如果我们对可选字段的内容感兴趣,我们还必须访问包含可选字段类型的标题字段,并相应地将其转换为正确的类型。

二、RAW-sockets

RAW-socket允许访问传输层(OSI第4层)和网络层(OSI第3层)[5]。它的使用仅限于有效用户ID为0或具有CAP_NET_RAW功能的进程,因为它需要root访问权限[1]。

2.1 socket()

对RAW-socket的读写都需要首先创建套接字。创建套接字使用与普通套接字相同的函数。可在<netinet/in.h>中找到,其形式如下[8]

int socket(int family, int type, int protocol)

其中参数如下:


family

family是一个描述所用地址族的常量值,常量在<sys/socket.h>中进行了定义:

Constant Description
AF_LOCAL Local communication
AF_UNIX Unix domain sockets
AF_INET IP version 4
AF_INET6 IP version 6
AF_IPX Novell IPX
AF_NETLINK Kernel user interface device
AF_X25 Reserved for X.25 project
AF_AX25 Amateur Radio AX.25
AF_APPLETALK Appletalk DDP
AF_PACKET Low level packet interface
AF_ALG Interface to kernel crypto API

该函数仅在套接字已经建立连接后将源自网络的错误传递给用户。在这种情况下,只传递EMSGSIZE和EPROTO以实现兼容性。如果启用了IP_RECVERR标志位,则所有网络错误都将保存在错误队列中。如果套接字创建成功,则返回非负的套接字描述符;如果在创建套接字期间发生错误,则返回-1。此外<errno.h>中定义的变量errno可以具有以下值[8]:

使用者必须意识到,设置family会影响以后可以选择哪个协议。与RAW-socket一起使用的通常选项是用于发送和接收IPv4数据包的AF_INET。


type

type定义了套接字类型,如下的值在<sys/socket.h>中进行了定义:

常量 描述
SOCK_STREAM Stream (connection) socket(TCP)
SOCK_DGRAM Datagram (connection-less) socket(UDP)
SOCK_RAW RAW socket
SOCK_RDM Reliably-delivered message
SOCK_SEQPACKET Sequential packet socket
SOCK_PACKET Linux specific way of getting packets at the dev level.


protocol

protocol定义了套接字发送和接收数据包所使用的协议[8]。协议编号由IANA(互联网分配编号管理局)定义,完整列表可在其网站上找到。<netinet/in.h>中定义了一些协议常量:

常量 描述
IPPROTO_IP Dummy protocol.
IPPROTO_HOPOPTS IPv6 Hop-by-Hop options.
IPPROTO_ICMP Internet Control Message Protocol.
IPPROTO_IGMP Internet Group Management Protocol.
IPPROTO_IPIP IPIP tunnels (older KA9Q tunnels use 94).
IPPROTO_TCP Transmission Control Protocol
IPPROTO_EGP Exterior Gateway Protocol.
IPPROTO_PUP PUP protocol.
IPPROTO_UDP User Datagram Protocol.
IPPROTO_IDP XNS IDP protocol.
IPPROTO_TP SO Transport Protocol Class 4.
IPPROTO_IPV6 IPv6 header.
IPPROTO_ROUTING IPv6 routing header.
IPPROTO_FRAGMENT IPv6 fragmentation header.
IPPROTO_RSVP Reservation Protocol.
IPPROTO_GRE General Routing Encapsulation.
IPPROTO_ESP Encapsulating security payload.
IPPROTO_AH Authentication header.
IPPROTO_ICMPV6 ICMPv6.
IPPROTO_NONE IPv6 no next header.
IPPROTO_DSTOPTS IPv6 destination options.
IPPROTO_MTP Multicast Transport Protocol.
IPPROTO_ENCAP Encapsulation Header.
IPPROTO_PIM Protocol Independent Multicast.
IPPROTO_COMP Compression Header Protocol.
IPPROTO_SCTP Stream Control Transmission Protocol.
IPPROTO_RAW Raw IP packets.
IPPROTO_MAX No description.

这里protocol的选择受到第一个选项family的影响,只有该family的协议可以选择为protocol。因此,如果选择了我们AF_INET选项,我们只能使用基于IP的协议[8]。

此外,这里应该注意到,大多数操作系统(取决于Linux/Unix发行版)的IPPROTO_RAW意味着套接字还希望用户手动创建IP首部[8]。如果我们选择这个常数,我们就有第3层写入权限。通常的方法是使用另一个常量,然后使用setsockopt()更改套接字选项

In addition to these constants we could also use constants that are defined for layer 2 (PACKET-sockets) to access their protocol information. These constants are operating system dependent and therefore code that uses them cannot be ported as easy as the general constants from the previous table. As an example, table 48 in the appendix shows the constants in Linux for Ethernet protocols which are defined in the <linux/if_ether.h> header.

2.2 setsockopt()

setsockopt()函数可用于更改套接字的options。在不同网络层次军存在options(例如TCP、IP),they are always present at the uppermost socket level.。函数体在<sys/socket.h>头中定义,如下所示:

int setsockopt(int sockfd, int level, int optname, const void * optval, socklen_t optlen)

其中可以设置如下的参数:

  • sockfd - 指定需要设定哪个socket的选项
  • level - The protocol level of the option we want to set.
  • optname - 我们想要设定的选项名,它与optval和optlen一起被未经解释地传递给协议模块进行处理
  • optval - 我们要设置的选项值所在的缓冲区,通常是一个整数。It should then be non-zero to enable a Boolean option and zero to disable it.
  • optlen - 用字节衡量的optval的缓冲区长度

当修改socket option时,option所在的网络层次及option名需要进行指定。当修改socket API层次的option时,level需要被被设定为SOL_SOCKET。当修改在其他任意层次的option时,the protocol number of the appropriate protocol controlling the option is supplied.例如,为了指明某一个option是被TCP协议使用的,level需要被设定为TCP 的protocol number。

对于以IPPROTO_IP作为参数指定的level,下表显示了<netinet/IP.h>头文件支持并已包含的以下optname:

optname 描述
IP_ADD_MEMBERSHIP Join a multicast group. Argument is an ip_mreqn structure
IP_ADD_SOURCE_MEMBERSHIP Join a multicast group and allow receiving data only from a specified source
IP_BLOCK_SOURCE Stop receiving multicast data from a specific source in a given group
IP_DROP_MEMBERSHIP Leave a multicast group
IP_DROP_SOURCE_MEMBERSHIP Leave a source-specific group-that
IP_FREEBIND If enabled, this boolean option allows binding to an IP address that is non-local/does not exist
IP_HDRINCL If enabled, the user supplies an IP header in front of the user data. Only valid for SOCK_RAW sockets
IP_MSFILTER This option provides access to the advanced full-state filtering API
IP_MTU_DISCOVER Set or receive the Path MTU Discovery setting for a socket
IP_MULTICAST_IF Set the local device for a multicast socket
IP_MULTICAST_LOOP Set or read an argument that determines if multicast packets should be looped back to the local sockets
IP_MULTICAST_TTL Set or read the time-to-live value of outgoing multicast packets for this socket
IP_NODEFRAG If enabled (nonzero), the reassembly of outgoing packets is disabled in the netfilter layer
IP_OPTIONS Set or get the IP options to be sent with every packet from this socket
IP_PKTINFO Pass an IP_PKTINFO ancillary message that supplies information about the incoming packet
IP_RECVERR Enable extended reliable error message passing. On a datagram socket, all generated errors are stored in a per-socket error queue
IP_RECVTOS If enabled the IP_TOS ancillary message is passed with incoming packets
IP_RECVTTL If set, pass a IP_TTL control message with the received packets TTL. Not supported for SOCK_STREAM sockets
IP_RETOPTS Identical to IP_RECVOPTS, but returns raw unprocessed options with timestamp and route record options not filled in for this hop
IP_ROUTER_ALERT Pass all to-be forwarded packets with the IP Router Alert option set to this socket. Only valid for raw sockets.
IP_TOS Set or get the TOS field that is sent with every IP packet originating from this socket.
IP_TRANSPARENT Setting this boolean option enables transparent proxying on this socket.
IP_TTL Set or get the current time-to-live field that is used in every packet sent from this socket.
IP_UNBLOCK_SOURCE Unblock previously blocked multicast source.

函数执行成功时返回0,如果发生错误返回-1。

2.3 getsockopt

2.4 bind

在创建了一个socket后,我们就可以将创建的socket和一个特定的address绑定到一起。通常来讲这个过程叫做assigning a name to a socket。对于RAW-socket和PACKET-socket来讲这个过程是可选的,但是我们通常使用它来定义我们包的source address,同时也用它来定义我们想要从哪个network-interface来读取包。其他类型的socket可能要求在使用前绑定到一个特定的address。具体地,我们使用<sys/socket.h>中定义的bind()函数来将socket绑定到一个特定的IP地址。

函数体如下:

int bind(int sockfd,const struct sockaddr * addr, socklen_t addrlen);

其中参数如下:

  • sockfd:指定需要设定地址的socket
  • addr:需要设定其中地址信息的地址数据结构
  • addrlen:地址数据结构的字节大小

当函数成功执行时返回0,执行失败时返回-1。

2.5 getsockname

2.6 connect

这个函数用于初始化一个有具体目的地址的连接,write、send、read以及recv函数都需要这个函数。

对于connection based的协议,例如SOCK_STREAM这个函数同样会尝试从另一端建立连接。对于datagram based的协议,only the default destination is defined with this function。函数在<sys/socket.h>中进行了定义,函数体如下:

int connect(int sockfd,const struct sockaddr *addr,socklen_t addrlen);

其中参数如下:

  • sockfd:指定进行连接的socket的文件描述符
  • addr:sockaddr*结构体指针,指定了连接的另一方
  • addrlen:addr的长度

注意如果socket sockfd是SOCK_DGRAM类型,那么addr是datagram默认发送的地址,也是接收datagram的唯一地址。如果socket是SOCK_STREAM或SOCK_SEQPACKET类型,那么调用这个函数会尝试和addr指定的地址绑定的socket进行连接。

当函数成功执行时返回0,失败时返回-1。

参考

2.7 read

2.8 write

我们可以用三种不同的方式通过套接字发送数据。write()和send()函数都需要先定义目标地址,这可以通过使用connect()函数实现。sendto()具有定义目标地址的选项,但也可以使用connect()函数设置。对于以上三种方式,我们都必须考虑缓冲区约束,否则在所有数据传输完全之前,写入连接将关闭。

2.8.1 write

2.8.2 send

2.8.3 sendto

sendto()函数允许我们定义一个数据应该发送到的特定地址,而不必首先调用connect()来设置目标地址[8]。需要注意的是,如果在连接模式(connection-mode)套接字上使用sendto()函数,则会忽略额外的地址信息,并在errno中返回EISCONN错误(如果未分别设置为NULL和零)[8]。这是因为对于这样的套接字,连接类型已经指定了目标。要使用此函数,必须包含<sys/types.h>(对于数据类型)和<sys/socket.h>(对于函数)。函数的调用方式如下[8]:

ssize_t sendto(int sockfd, void * buf, size_t len, int flags, struct sockaddr * dest_addr, socklen_t addrlen)

需要设置的参数如下:

  • sockfd - 指定我们通过哪个socket将数据发送出去
  • buf - 指定了我们想要通过socket发送的二进制数据所在的内存空间
  • len - 指定了从给定的内存空间中应当读取多少数据用于传输
  • dest_addr - 该函数使用此参数定义一个结构sockaddr*,其中包含套接字地址族和该族的协议地址。参数可以设置为空,这时不填写
  • addrlen - 提供给socket的dest_addr的长度
  • flags - 可以为这个方程设置的flag如下表所示
Flag 描述
MSG_CONFIRM Only valid on SOCK_DGRAM and SOCK_RAW. Tell the layer 2 that you got a successful reply from the other side
MSG_DONTROUTE Don’t use a gateway to send out the packet, only send to hosts on directly connected networks.
MSG_DONTWAIT Enables non-blocking operation, if the operation would block EAGAIN or EWOULDBLOCK is returned.
MSG_EOR Terminates a record (when this notion is supported).
MSG_MORE The caller has more data to send. This flag is used with UDP/TCP sockets.
MSG_NOSIGNAL Requests not to send SIGPIPE on errors on stream oriented sockets when the other end breaks the connection. The EPIPE error is still returned.
MSG_OOB Sends out-of-band data on sockets that support this notion, the underlying protocol must also support out-of-band data.

2.9 close

2.10 inet_ntop

2.11 inet_pton

2.12

RAW socket使用相关推荐

  1. 用C#的Raw Socket实现网络封包监视

    <script language="javascript" src="/ad/js/edu_left_300-300.js" type="tex ...

  2. python实现syn半扫描_python 使用raw socket进行TCP SYN扫描实例

    1. TCP SYN扫描 端口扫描常用于用于探测服务器或主机开放端口情况,被计算机管理员用于确认安全策略,同时被攻击者用于识别目标主机上的可运作的网络服务.端口扫描是向一定范围的服务器端口发送对应请求 ...

  3. RAW SOCKET探索

    在Linux实现抓取以太网络上的数据包主要有libpcap,raw socket以及从内核中获取的方式. 我尝试选择使用raw socket来抓取以太网络上的数据包. 一.socket地址域(协议簇) ...

  4. 解读Raw Socket

    什么是Raw Socket呢?下面是来自wikipedia的解释: In computer networking, a raw socket is a socket that allows direc ...

  5. 关于windows xp sp2/sp3 中tcpip.sys对于Raw socket的限制

    WINDOWS XP SP2修改了TCPIP.SYS,增加了对于RAW SOCKET的发送和伪造源地的限制,详情如下 : 1) 不能通过raw socket发送TCP报文.做此尝试时会得到10004号 ...

  6. Raw Socket编程

    原始套接字(SOCK_RAW)是一种不同于SOCK_STREAM.SOCK_DGRAM的套接字,它实现于系统核心.然而,原始套接字能做什么呢?首 先来说,普通的套接字无法处理ICMP.IGMP等网络报 ...

  7. linux raw限制端口访出,使用Linux raw socket时需要注意的一些问题

    本文的copyleft归gfree.wind@gmail.com所有,使用GPL发布,可以自由拷贝,转载.但转载请保持文档的完整性,注明原作者及原链接,严禁用于任何商业用途. 作者:gfree.win ...

  8. linux raw socket 例子,raw socket编程例子

    raw socket编程例子内容安排: 1.原始套接字介绍 1.1 原始套接字工作原理与规则 1.2 简单应用 2 FTP密码窃取器实现(简单的rootkit) 2.1 设计思路 2.2 实现 2.3 ...

  9. C#下的Raw Socket编程实现网络封包监视

    谈起socket编程,大家也许会想起QQ和IE,没错.还有许多网络工具如P2P.NetMeeting等在应用层实现的应用程序,也是用socket来实现的.Socket是一个网络编程接口,实现于网络应用 ...

  10. 用C#下的Raw Socket编程实现网络封包监视(摘录)

    谈起socket编程,大家也许会想起QQ和IE,没错.还有许多网络工具如P2P.NetMeeting等在应用层实现的应用程序,也是用 socket来实现的.Socket是一个网络编程接口,实现于网络应 ...

最新文章

  1. RAID和mdadm
  2. Linux Xmanager
  3. ssr pac_阴阳师新SSR千姬什么时候上线 ssr千姬活动上线时间一览
  4. java向指定文件写入内容
  5. 团队组建阶段,项目经理要做些什么?
  6. word List35
  7. 2020年牛客多校第五场C题-easy(纯组合计数不要生成函数的做法)
  8. smarty 简单分页
  9. android文件体系,Android文件体系-基础
  10. 关于程序员面试时的智力题集锦
  11. 李炎恢php视频教程ed2k,李炎恢PHP视频教程第一二三四季,含源码和教课文档从入门到精通...
  12. 骚操作 Python爬你要的网站数据
  13. EDEM资料大全(包含教程、论文、案例、应用)
  14. mediatek无线网卡驱动 linux,MediaTek 高速USB网卡驱动适配
  15. html将四张图片合并,照片拼图在线制作 自带多种照片拼图模板,自由排版将多张照片合成一张...
  16. 电脑C盘满了怎么办?电脑C盘满了怎么清理?
  17. 阶段二第一部分:第2章 Nginx进阶与实战
  18. Djano3.0使用-CBV使用实例
  19. 电脑锁定计算机有没有难度较高的,你对电脑的刷新率了解有多少 锁定刷新率的方法...
  20. vim 无法使用backspace 删除键进行删除

热门文章

  1. Python 读取MODIS叶绿素-a浓度.nc文件
  2. mysql插入时unique字段重复插入失败
  3. mongodb分页优化
  4. oracle 的控制结构
  5. oracle学习笔记之pI/sql视频小结
  6. 阿里巴巴Java 程序员常用的 10 款开源工具!用好了,事半功倍!
  7. Android View框架总结(四)View布局流程之Measure
  8. Python数据库操作——连接MySQL
  9. echarts大屏模板_大屏数据展示模板智慧城市
  10. dotween unity 延时_使用DoTween在Unity中制作队列(Sequence)动画