Socket建立
为了建立Socket,程序可以调用Socket函数,该函数返回一个类似于文件描述符的句柄。socket函数原型为:
int socket(int domain, int type, int protocol);
1,domain指明所使用的协议族,
通常为PF_INET,表示互联网协议族(TCP/IP协议族);或者PF_PACKET协议栈( 底层数据包接口)
所以在windows中AF_INET与PF_INET完全一样. 而在Unix/Linux系统中,在不同的版本中这两者有微小差别.对于BSD,是AF,对于POSIX是PF.
理论上建立socket时是指定协议,应该用PF_xxxx,设置地址时应该用AF_xxxx。当然AF_INET和PF_INET的值是相同的,混用也不会有太大的问题。
在函数socketpair与socket的domain参数中有AF_UNIX,AF_LOCAL,AF_INET,PF_UNIX,PF_LOCAL,PF_INET.
这几个参数有AF_UNIX=AF_LOCAL, PF_UNIX=PF_LOCAL, AF_LOCAL=PF_LOCAL, AF_INET=PF_INET.
Socket数据结构中包含这五种信息。
第一个参数family指的是协议族(或者说协议栈)。主要的协议族有以下:
PF_INET 互联网IPv4
PF_IPX IPX协议
PF_NETLINK 一个用户空间和内核的接口
PF_PACKET 底层数据包接口
源代码有所有协议栈库在 系统函数库
2,type参数指定socket的类型:
SOCK_STREAM(tcp) 或SOCK_DGRAM(udp),Socket接口还定义了原始Socket(SOCK_RAW),允许程序使用低层协议;
linux2.6内核
3,protocol通常赋值"0"或者IPPROTO_IP。IPPROTO_IP=0
Socket()调用返回一个整型socket描述符,你可以在后面的调用使用它。
Socket描述符是一个指向内部数据结构的指针,它指向描述符表入口。调用Socket函数时,socket执行体将建立一个Socket,实际上"建立一个Socket"意味着为一个Socket数据结构分配存储空间。Socket执行体为你管理描述符表。
协议类型常用有四个在socket(PF_PACKET, SOCK_RAW, htons(ETH_P_IP|ETH_P_ARP|ETH_P_ALL))
ETH_P_IP 0x0800 只接收发往本机mac的ip类型的数据帧
ETH_P_ARP 0x0806 只接受发往本机mac的arp类型的数据帧
ETH_P_RARP 0x8035 只接受发往本机mac的rarp类型的数据帧
ETH_P_ALL 0x0003 接收发往本机mac的所有类型ip arp rarp的数据帧, 接收从本机发出的所有类型的数据帧.(混杂模式打开的情况下,会接收到非发往本地mac的数据帧)
在socket(AF_INET, SOCK_RAW,IPPROTO_TCP|IPPROTO_UDP|IPPROTO_ICMP)下有
/* Standard well-defined IP protocols. */
enum
{
IPPROTO_IP = 0, /* Dummy protocol for TCP. */
IPPROTO_HOPOPTS = 0, /* IPv6 Hop-by-Hop options. */
IPPROTO_ICMP = 1, /* Internet Control Message Protocol. */
IPPROTO_IGMP = 2, /* Internet Group Management Protocol. */
IPPROTO_IPIP = 4, /* IPIP tunnels (older KA9Q tunnels use 94). */
IPPROTO_TCP = 6, /* Transmission Control Protocol. */
IPPROTO_EGP = 8, /* Exterior Gateway Protocol. */
IPPROTO_PUP = 12, /* PUP protocol. */
IPPROTO_UDP = 17, /* User Datagram Protocol. */
IPPROTO_IDP = 22, /* XNS IDP protocol. */
IPPROTO_TP = 29, /* SO Transport Protocol Class 4. */
IPPROTO_DCCP = 33, /* Datagram Congestion Control Protocol. */
IPPROTO_IPV6 = 41, /* IPv6 header. */
IPPROTO_ROUTING = 43, /* IPv6 routing header. */
IPPROTO_FRAGMENT = 44, /* IPv6 fragmentation header. */
IPPROTO_RSVP = 46, /* Reservation Protocol. */
IPPROTO_GRE = 47, /* General Routing Encapsulation. */#define IPPROTO_GRE IPPROTO_GRE
IPPROTO_ESP = 50, /* encapsulating security payload. */
IPPROTO_AH = 51, /* authentication header. */
IPPROTO_ICMPV6 = 58, /* ICMPv6. */
IPPROTO_NONE = 59, /* IPv6 no next header. */#define IPPROTO_NONE IPPROTO_NONE
IPPROTO_DSTOPTS = 60, /* IPv6 destination options. */
IPPROTO_MTP = 92, /* Multicast Transport Protocol. */
IPPROTO_ENCAP = 98, /* Encapsulation Header. */
IPPROTO_PIM = 103, /* Protocol Independent Multicast. */
eg
sock_raw原始套接字编程可以接收到本机网卡上的数据帧或者数据包,对与监听网络的流量和分析是很有作用的.一共可以有3种方式创建这种socket
1.socket(AF_INET, SOCK_RAW, IPPROTO_TCP|IPPROTO_UDP|IPPROTO_ICMP)发送接收ip数据包
能:该套接字可以接收和发送协议类型为(tcp udp icmp等)发往本机的ip数据包,不包括mac地址.
不能:不能收到非发往本地ip的数据包(ip软过滤会丢弃这些不是发往本机ip的数据包).
不能:不能收到从本机发送出去的数据包.
不能:不能修改mac地址。数据包是网络层不等于数据帧,不包括mac地址,数据帧是链路层的,包括mac地址。
发送的话需要自己组织tcp udp icmp等头部.可以setsockopt来自己包装ip头部,这种套接字用来写个ping程序比较适合
2.socket(PF_PACKET, SOCK_RAW, htons(ETH_P_IP|ETH_P_ARP|ETH_P_ALL))发送接收以太网数据帧
能: 接收发往本地mac的数据帧
能: 接收从本机发送出去的数据帧(第3个参数需要设置为ETH_P_ALL)
能: 接收非发往本地mac的数据帧(网卡需要设置为promisc混杂模式)
3.socket(AF_INET, SOCK_PACKET, htons(ETH_P_IP|ETH_P_ARP|ETH_P_ALL))发送接收以太网数据帧(过时了,不要用啊)
(SOCK_PACKET 方式使用结构 sockaddr_pkt来保存数据链路层信息,但该结构缺乏包类型信息;其次,如果参数 MSG_TRUNC 传递给读包函数recvmsg()、recv()、recvfrom()等,则函数返回的数据包长度是实际读到的包数据长度,而不是数据包真正的长度。Libpcap 的开发者在源代码中明确建议不使用 2.0,http://www.cnblogs.com/huyc/archive/2011/10/19/2217134.html)
两个网络程序之间的一个网络连接包括五种信息:
通信协议、
本地协议地址、
本地主机端口、
远端主机地址
远端协议端口。
想想会有哪种协议类型是IPPROTO_IP =0的呢?内核使用IPPROTO_IP仅仅是为了在某些情况下默认匹配协议类型而已。例如SOCK_DGRAM协议族默认的协议是
IPPROTO_UDP,SOCK_STREAM默认的是IPPROTO_TCP。即
socket(PF_INET,SOCK_DGRAM,IPPROTO_IP) 和
socket(PF_INET,SOCK_DGRAM,IPPROTO_UDP)是一样的,
socket(PF_INET,SOCK_STREAM,IPPROTO_IP) 和
socket(PF_INET,SOCK_DGRAM,IPPROTO_TCP)一样。
但是
socket(PF_INET,SOCK_RAW,IPPROTO_IP)让SOCK_RAW如何选择默认的协议类型呢?
SOCK_RAW是网络层(IP层)的,而SOCK_PACKET是数据链路层的.一般的UNIX标准(POSIX)
只有SOCK_RAW,SOCK_PACKET是linux专有的,并不通用.SOCK_PACKET和同类实现相比,比较
底层但不实用.具体的请参考<<UNIX Network Programming>>一书
AF_PACKET是专门用来处理raw socket的,符合我上面列举的规则
是否可以这样总结raw socket的用法:
(1):如果想单独处理TCP,UDP或者ICMP,可用AF_INET, 第三个参数用IPPROTO_TCP, IPPROTO_UDP等
socket(AF_INET, SOCK_RAW, IPPROTO_TCP|IPPROTO_UDP|IPPROTO_ICMP)
(2):如果想对各种IP包进行处理,则用AF_PACKET/PF_PACKET,第三个参数用htons(ETH_P_IP)
socket(PF_PACKET, SOCK_RAW, htons(ETH_P_IP|ETH_P_ARP|ETH_P_ALL))
(3):如果想处理一切链路层上的包(IP,RARP,ARP等),则用AF_PACKET/PF_PACKET,第三个参数用 htons(ETH_P_ALL)
socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL))
1,能接收和发送带mac地址的数据包
socket(PF_PACKET,SOCK_DGRAM,htons(ETH_P_IP))
2,能接收和发送不带mac地址的数据包
(1)udp包
1)socket(PF_INET,SOCK_DGRAM,IPPROTO_IP) 和
socket(PF_INET,SOCK_DGRAM,IPPROTO_UDP)是一样的,
socket(AF_INET,SOCK_DGRAM,0);也是一样的
3)socket(AF_INET, SOCK_RAW, IPPROTO_UDP);
(2)tcp包
socket(PF_INET,SOCK_STREAM,IPPROTO_IP) 和
socket(PF_INET,SOCK_DGRAM,IPPROTO_TCP)一样。
socket(PF_PACKET,SOCK_DGRAM,htons(ETH_P_IP))
PF_PACKET介绍:
在LINUX内核版本中(2.0 releases),一个名为PF_PACKET的协议簇被加了进来!这个簇允许应用程序直接利用网络驱动程序发送和接收报文,避免了原来的协议栈处理过程,在这种情况下,所有SOCKET发出的报文直接送到以太网卡接口,而接口收到的任何报文将直接送到应用程序
The PF_PACKET协议簇支持两个稍微有点不同的SOCKET类型,SOCK_DGRAM和SOCK_RAW。前者让内核处理添加或者去除以太网报文头部工作,而后者则让应用程序对以太网报文头部有完全的控制!在SOCKET调用中的协议类型必须符合/usr/include/linux/if_ether.h中定义的以太网IDs中的一个,除非遇到特别声明的协议,一般你可以用ETH_P_IP来处理IP的一组协议(TCP,UDP,ICMP,raw IP等等)因为它们容易受到一些很严重的安全问题的牵连(比如你可以伪造一个MAC地址),所以只有具有root权限才可以使用PF_PACKET-family socket.这也就是为什么只有具有root权限后才能运行嗅探器的原因!
http://www.cnblogs.com/jinrize/archive/2009/11/24/1609902.html
LPF介绍
LINUX的内核允许我们把一个名为LPF(Linux Packet Filter)的过滤器直接放到PF_PACKET protocol-processing routines(在网卡接收中断执行后立即执行)中,过滤器将决定哪些包被送到应用程序,哪些包被丢弃!这个过滤程序可以根据使用者的定义来运行,由BPF伪机器码写成的。
这个过滤器也不是总是有效的,因为它产生于一般的使用BPF的机器,没考虑到一些特殊结构的机器!在一些特殊情况下,过滤器由PF_PACKET进程运行,也许已经检查过以太协议了!这个根据你在socket()调用初使化的时候指定的那些协议!如果不是ETH_P_ALL(抓所有的报文),那么只有那些符合指定的协议类型的报文会流过过滤器! LPF和BPF的实现不一样。不是一个程序。
BPF介绍:
BPF(BSD Packet Filter)包过滤机制,BPF 于 1992 年被设计出来,其设计目的主要是解决当时已存在的过滤机制效率低下的问题。BPF的工作步骤如下:当一个数据包到达网络接口时,数据链路层的驱动会把它向系统的协议栈传送。但如果 BPF 监听接口,驱动首先调用 BPF。BPF 首先进行过滤操作,然后把数据包存放在过滤器相关的缓冲区中,最后设备驱动再次获得控制。注意到BPF是先对数据包过滤再缓冲,避免了类似 sun 的 NIT 过滤机制先缓冲每个数据包直到用户读数据时再过滤所造成的效率问题。参考资料D是关于 BPF 设计思想最重要的文献。
libpcap提供的一个最有用的函数是pcap_compile(), 它可以把一个输入输出的逻辑表达式变为BPF代码!tcpdump利用这个函数完成在用户输入的命令行和BPF代码之间的转换!tcpdump有个我们很感兴趣但是很少使用的参数 ,-d,可以输出BPF代码!把tcpdump -d中的-d换成-dd,将显示出一段C代码,可以把它复制到自己的程序中,可以通过调用setsockopt()来过滤端口