Rawsocket
用途
读写除了TCP、UDP以外的协议(ICMP,IGMP, OSPF),还可以收发自己构造的IP包(IP_HDRINCL选项),TCP包和UDP包。
创建
int sockfd = socket(AF_INET, SOCK_RAW, protocal);
//protocal可以是IPPROTO_ICMP。(是的,不再是IPPROTO_TCP UDP SCTP了)
const int on = 1;
setsockopt(sockfd,IPPROTO_IP,IP_HDLINCL,&on,sizeof(on));
之后可以bind和connect。分别决定源IP和目的IP。(没有端口号这个概念)
输出
如果没connect,就用sendto sendmsg
connect了,就确定了目的ip,可以用write writev send
如果在setsockopt的时候,没给IP_HDLINCL这个选项,则内核自己生成IP数据包包头。
如果给了,用户自己构造包头。
IP头以外的校验和需要用户自己计算。
输入
没有TCP和UDP包,有绝大部分ICMP和所有IGMP,以及内核不知道是什么协议的包。
包发给套接字的规则
1 如果创建时给出的协议值不是0,则包的协议必须符合套接字协议
2 如果bind了,则包目的IP必须符合套接字目的IP
3 如果connect了,则包源IP必须符合套接字源IP
也就是说,如果一个套接字创建的时候没有指定协议(协议值是0),且不绑定,不连接,他就可以收到所有包。
IPV4能收到包的全部数据,IPV6只能收到除了包头以外的数据。
数据链路层
Unix下访问数据链路层
BSD的BPF设备(文件),SVR4的DLPI(不讲),Linux SOCK_PACKET
抓包库
libpcap
BSD packet filter (BPF)
每个进程都可以打开BPF设备(/dev/bpf0)
每个设备都可以加载一个自定义的filter
BPF的优势
BPF在内核,直接过滤,可以不用把所有包都传送到进程。
当缓冲区满或者超时再拷贝,减少拷贝次数
BPF不把所有数据都拷贝到进程,只拷贝固定长度的一小段。
(以上都是为了加速)
SOCK_PACKET和PF_PACKET
fd = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL));
fd = socket(AF_INET,SOCK_PACKET,htons(ETH_P_ALL));
第二种方法是老方法。
ETH_P_ALL是所有包,ETH_P_IP只接收IP包。