前面主要介绍了流式套接字(SOCK_STREAM),数据报套接字(SOCK_DGRAM),基本上能够满足TCP与UDP的应用。但一些问题,我们仍然无法解决,如:
(1)发送一个自定义的IP包
(2)发送ICMP包
(3)侦听网络上的数据包
(4)伪装IP地址
(5)实现自定义的协议
究其原因,标准的套接字与TCP,UDP层打交道,而原始套接字只与IP层,MAC层打交道。
2.原始套接字的类型
(1)socket(AF_INET,SOCK_RAW,IPPROTO_TCP|IPPROTO_UDP|IPPROTO_ICMP);
(2)socket(PF_PACKET,SOCK_RAW,htons(ETH_P_IP|ETH_P_ARP|ETH_P_RAP|ETH_P_ALL));
第一种套接字类型可得到原始的IP包,这样就可以自己组织TCP,UDP,ICMP包了.
第二种套接字能收到发往本地的MAC帧,也能收到从本机发出去的MAC帧(第3个参数为ETH_P_ALL).也能接收到非发住本地的MAC帧(网卡设置为promisc混杂模式)
ETH_P_IP 0X800 只接收发往本机的mac的ip类型的数据帧
ETH_P_ARP 0X806 只接收发往本机的arp类型的数据帧
ETH_P_RARP 0x8035 只接受发往本机的rarp类型的数据帧
ETH_P_ALL 0X3 接收发往本机的MAC所有类型ip,arp,rarp数据帧,接收从本机发出去的数据帧,混杂模式打开的情况下,会接收到非发往本地的MAC数据帧
此时设备无关的物理地址使用struct sockaddr_ll
所以第二种套接字的功能特别强大.
如果设置了IP_HDRINCL套接字选项,那么需要手动填写IP首部.
3. IP,TCP(首部),UDP(首部),ICMP
IP结构体:
struct iphdr { #if __BYTE_ORDER == __LITTLE_ENDIAN unsigned int ihl:4; unsigned int version:4; #elif __BYTE_ORDER == __BIG_ENDIAN unsigned int version:4; //版本号 unsigned int ihl:4; //首部长度(4字节为单位) #else # error "Please fix <bits/endian.h>" #endif u_int8_t tos; //服务类型 u_int16_t tot_len; //IP包总长度 u_int16_t id; //标识 u_int16_t frag_off; //是否分片 u_int8_t ttl; //生存时间 u_int8_t protocol; //上层协议 u_int16_t check; //校验和 u_int32_t saddr; //源IP地址 u_int32_t daddr; //目的IP地址 };
UDP首部:
#ifdef __FAVOR_BSD struct udphdr { u_int16_t uh_sport; u_int16_t uh_dport; u_int16_t uh_ulen; u_int16_t uh_sum; }; else struct{ u_int16_t source; //源端口 u_int16_t dest; //目的端口 u_int16_t len; //UDP包的长度 u_int16_t check; //校验和 }; #endif
TCP首部:
struct tcphdr { u_int16_t source; //TCP源端口 u_int16_t dest; //TCP目的端口 u_int32_t seq; //序列号 u_int32_t ack_seq; //确认序列号 # if __BYTE_ORDER == __LITTLE_ENDIAN u_int16_t res1:4; u_int16_t doff:4; u_int16_t fin:1; u_int16_t syn:1; u_int16_t rst:1; u_int16_t psh:1; u_int16_t ack:1; u_int16_t urg:1; u_int16_t res2:2; # elif __BYTE_ORDER == __BIG_ENDIAN u_int16_t doff:4; u_int16_t res1:4; u_int16_t res2:2; u_int16_t urg:1; u_int16_t ack:1; u_int16_t psh:1; u_int16_t rst:1; u_int16_t syn:1; //请求连接标志 u_int16_t fin:1; # else # error "Adjust your <bits/endian.h> defines" # endif u_int16_t window; //滑动窗口的大小 u_int16_t check; //校验和 u_int16_t urg_ptr; //紧急字段指针 };
ICMP:
struct icmp { u_int8_t icmp_type; u_int8_t icmp_code; u_int16_t icmp_cksum; union { u_char ih_pptr; struct in_addr ih_gwaddr; struct ih_idseq { u_int16_t icd_id; u_int16_t icd_seq; } ih_idseq; u_int32_t ih_void; struct ih_pmtu { u_int16_t ipm_void; u_int16_t ipm_nextmtu; } ih_pmtu; struct ih_rtradv { u_int8_t irt_num_addrs; u_int8_t irt_wpa; u_int16_t irt_lifetime; } ih_rtradv; } icmp_hun; #define icmp_pptr icmp_hun.ih_pptr #define icmp_gwaddr icmp_hun.ih_gwaddr #define icmp_id icmp_hun.ih_idseq.icd_id #define icmp_seq icmp_hun.ih_idseq.icd_seq #define icmp_void icmp_hun.ih_void #define icmp_pmvoid icmp_hun.ih_pmtu.ipm_void #define icmp_nextmtu icmp_hun.ih_pmtu.ipm_nextmtu #define icmp_num_addrs icmp_hun.ih_rtradv.irt_num_addrs #define icmp_wpa icmp_hun.ih_rtradv.irt_wpa #define icmp_lifetime icmp_hun.ih_rtradv.irt_lifetime union { struct { u_int32_t its_otime; u_int32_t its_rtime; u_int32_t its_ttime; } id_ts; struct { struct ip idi_ip; } id_ip; struct icmp_ra_addr id_radv; u_int32_t id_mask; u_int8_t id_data[1]; //icmp数据 } icmp_dun; #define icmp_otime icmp_dun.id_ts.its_otime #define icmp_rtime icmp_dun.id_ts.its_rtime #define icmp_ttime icmp_dun.id_ts.its_ttime #define icmp_ip icmp_dun.id_ip.idi_ip #define icmp_radv icmp_dun.id_radv #define icmp_mask icmp_dun.id_mask #define icmp_data icmp_dun.id_data };
IP首部长度为20个字节,TCP首部长度为20个字节,UDP首部长度为8个字节, ICMP的首部长度为8个字节。