原始套接字報文處理時的結構與原理
一、概述
原始套接字主要應用在底層網路程式設計上,同時也是網路黑客的必備手段。例如sniffer、拒絕服務(DoS)、IP地址欺騙等都需要在原始套接字的基礎上實現。
原始套接字提供以下3種標準套接字不具備的功能。 使用原始套接字可以讀/寫ICMP、IGMP分組。
使用原始套接字可以讀寫特殊的IP資料報,核心不處理這些資料報的協議欄位。
使用原始套接字,可以修改IP資料和IP層之上的各層資料,構造自己的特定型別的TCP或者UDP的分組。
二、原始套接字的建立
建立原始套接字使用socket()函式。下面的程式碼建立一個AF_INET協議族中的原始套接字,協議型別為protocol。
建立:
int rawsock = socket(AF_INET, SOCK_RAW, protocol);
IPPROTO_IP:IP協議,接收或者傳送IP資料包,包含IP頭部。
IPPROTO_ICMP:ICMP協議,接收或者傳送ICMP的資料包,IP的頭部不需要處理,由核心字自己封裝IP包頭。
IPPROTO_TCP:TCP協議,接收或者傳送TCP資料包。 IPPROTO_UDP:UDP協議,接收或者傳送UDP資料包。
IPPROTO_RAW:原始IP包。
IP_HDRINCL
使用套接字選項IP_HDRINCL設定套接字,在之後進行的接收和傳送時,接收到的資料包含IP的頭部。使用者之後需要對IP層相關的資料段進行處理,例如IP頭部資料的設定和分析,校驗和的計算等。設定方法如下:
int set = 1; if(setsockopt(rawsock, IPPROTO_IP, IP_HDRINCL, &set,
sizeof(set))<0){ /錯誤處理/ }
不許要bind函式
原始套接字不需要使用bind()函式,因為進行傳送和接收資料的時候可以指定要傳送和接收的目的地址的IP。當系統對socket進行繫結的時候,傳送和接收的函式可以使用send()和recv()及read()和write()等不需要指定目的地址的函式。
傳送報文
原始套接字傳送報文有如下原則:
通常情況下可以使用sendto()函式並指定傳送目的地址來發送資料,當已經指定了bind()目標地址的時候可以使用write()或者send()傳送資料。
如果使用setsockopt()設定了選項IP_RINCL,則傳送的資料緩衝區指向IP頭部第一個位元組的頭部,使用者傳送的資料包含IP頭部之後的所有資料,需要使用者自己填寫IP頭部和計算校驗和並需要對所包含資料進行處理和計算。
如果沒有設定IP_RINCL,則傳送緩衝區指向IP頭部後面資料區域的第一個位元組,不需要使用者填寫IP頭部,IP頭部的填寫工作由核心進行,核心還進行校驗和的計算。
接收報文
與傳送報文類似,接收報文也有相似的規則: 通常可以使用recvfrom()或者recv()及read()獲得資料。
當設定了IP_RINCL後,接收的緩衝區為IP頭部的第一個位元組。 當沒有設定IP_RINCL的時候,接收的緩衝區為IP資料區域的第一個位元組。
三、報文結構
1. IP頭部的結構
版本:
佔 4 位,指 IP 協議的版本目前的 IP 協議版本號為 4 (即 IPv4)
首部長度:
佔4位,可表示的最大數值是15個單位(一個單位為 4 位元組)因此IP 的首部長度的最大值是 60 位元組
區分服務:
佔8位,用來獲得更好的服務,在舊標準中叫做服務型別,但實際上一直未被使用過.1998 年這個欄位改名為區分服務.只有在使用區分服務(DiffServ)時,這個欄位才起作用.一般的情況下都不使用這個欄位
總長度:
佔16位,指首部和資料之和的長度,單位為位元組,因此資料報的最大長度為 65535 位元組.總長度必須不超過最大傳送單元 MTU
標識:
佔16位,它是一個計數器,用來產生資料報的標識
標誌(flag):
佔3位,目前只有前兩位有意義
MF
標誌欄位的最低位是 MF (More Fragment) MF=1 表示後面“還有分片”。MF=0 表示最後一個分片
DF
標誌欄位中間的一位是 DF (Don’t Fragment) 只有當 DF=0 時才允許分片
片偏移:
佔12位,指較長的分組在分片後某片在原分組中的相對位置.片偏移以 8 個位元組為偏移單位
生存時間:
佔8位,記為TTL (Time To Live) 資料報在網路中可通過的路由器數的最大值,TTL 欄位是由傳送端初始設定一個 8 bit欄位.推薦的初始值由分配數字 RFC 指定,當前值為 64.傳送 ICMP 回顯應答時經常把 TTL 設為最大值 255
協議:
佔8位,指出此資料報攜帶的資料使用何種協議以便目的主機的IP層將資料部分上交給哪個處理過程, 1表示為 ICMP 協議, 2表示為 IGMP 協議, 6表示為 TCP 協議, 17表示為 UDP 協議
首部檢驗和:
佔16位,只檢驗資料報的首部不檢驗資料部分.這裡不採用 CRC 檢驗碼而採用簡單的計算方法
源地址和目的地址:
都各佔 4 位元組,分別記錄源地址和目的地址
/*ip資料結構*/
#include <netinet/ip.h>
struct ip
{
#if __BYTE_ORDER == __LITTLE_ENDIAN
unsigned int ip_hl:4; /* header length */
unsigned int ip_v:4; /* version */
#endif
#if __BYTE_ORDER == __BIG_ENDIAN
unsigned int ip_v:4; /* version */
unsigned int ip_hl:4; /* header length */
#endif
u_int8_t ip_tos; /* type of service */
u_short ip_len; /* 總長度 */
u_short ip_id; /* 標識值 */
u_short ip_off; /* 段偏移值 */
#define IP_RF 0x8000 /* reserved fragment flag */
#define IP_DF 0x4000 /* dont fragment flag */
#define IP_MF 0x2000 /* more fragments flag */
#define IP_OFFMASK 0x1fff /* mask for fragmenting bits */
u_int8_t ip_ttl; /* time to live */
u_int8_t ip_p; /* 協議型別 */
u_short ip_sum; /* checksum */
struct in_addr ip_src, ip_dst; /* source and dest address */
};
對應關係
2. ICMP頭部結構
ICMP的頭部結構比較複雜,主要包含訊息型別icmp_type,訊息程式碼icmp_code、校驗和icmp_cksum等,不同的ICMP型別其他部分有不同的實現。
常用的ICMP報文包括
ECHO-REQUEST(響應請求訊息)
ECHO-REPLY(響應應答訊息)
Destination Unreachable(目標不可到達訊息)
Time Exceeded(超時訊息)
Parameter Problems(引數錯誤訊息)
Source Quenchs(源抑制訊息)
Redirects(重定向訊息)
Timestamps(時間戳訊息)
Timestamp Replies(時間戳響應訊息)
Address Masks(地址掩碼請求訊息)
Address Mask Replies(地址掩碼響應訊息)等
資料結構
#include <netinet/ip_icmp.h>
struct icmp
{
u_int8_t icmp_type; /* 訊息型別 */
u_int8_t icmp_code; /* 程式碼 */
u_int16_t icmp_cksum; /* 校驗和 */
union
{
u_char ih_pptr; /* ICMP_PARAMPROB */
struct in_addr ih_gwaddr; /* gateway address */
struct ih_idseq /* 顯示資料報 */
{
u_int16_t icd_id; /*資料報ID*/
u_int16_t icd_seq;/*資料報序號*/
} ih_idseq;
u_int32_t ih_void;
/* ICMP_UNREACH_NEEDFRAG -- Path MTU Discovery (RFC1191) */
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;
/* options and then 64 bits of data */
} id_ip;
struct icmp_ra_addr id_radv;
u_int32_t id_mask;/*子網掩碼的子網掩碼*/
u_int8_t id_data[1];/*資料*/
} 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
};
常用的回顯應答資料結構如下
3. UDP頭部結構
UDP的頭部結構包含傳送端的源埠號、資料接收端的目的埠號、UDP資料的長度以及UDP的校驗和等資訊。
struct udphdr
{
u_int16_t source;
u_int16_t dest;
u_int16_t len;
u_int16_t check;
};
4. TCP頭部結構
TCP的頭部結構主要包含傳送端的源埠、接收端的目的埠、資料的序列號、上一個資料的確認號、滑動視窗大小、資料的校驗和、緊急資料的偏移指標以及一些控制位等資訊。
/usr/src/linux-2.6.19/include/linux/tcp.h
struct tcphdr {
__be16 source;
__be16 dest;
__be32 seq;/*序列號*/
__be32 ack_seq;/*確認序列號*/
#if defined(__LITTLE_ENDIAN_BITFIELD)
__u16 res1:4,/*保留*/
doff:4,/*偏移*/
fin:1,/*關閉連線標誌*/
syn:1,/*請求連線標誌*/
rst:1,/*重置連線標誌*/
psh:1,/*接收方儘快將資料放到應用層標誌*/
ack:1,/*確認序號標誌*/
urg:1,/*緊急指標標誌*/
ece:1,/*擁塞標誌位*/
cwr:1;/*擁塞標誌位*/
#elif defined(__BIG_ENDIAN_BITFIELD)
__u16 doff:4,
res1:4,
cwr:1,
ece:1,
urg:1,
ack:1,
psh:1,
rst:1,
syn:1,
fin:1;
#else
#error "Adjust your defines"
#endif
__be16 window;/*滑動視窗*/
__be16 check;/*校驗和*/
__be16 urg_ptr;/*緊急欄位指標*/
};