http 重定向 302報文
我這裡的重定向環境是對客戶端的請求直接截斷重定向到其他頁面。
以太頭和Ip頭想應資料對調。
Tcp頭flags標記和序號問題。主要問題
資料內容主要的就兩條:
"HTTP/1.1 302 Moved Temporarily"
"Location: 路徑"
一、以太頭結構
在幀結構裡我用到的有MAC源和目的地址,以太型別。至於802.1Q標籤,如果加了,對以太型別想對地址要向生移4個位元組。源來的地址值則為0x8100。
用到的以太型別是 0x800 表以後一個負載段的內容是一個IP包。這個巨集定義在if_ether.h標頭檔案裡。
#define ETH_P_PUPAT 0x0201 /*Xerox PUP Addr Trans packet */
#define ETH_P_IP 0x0800 /*Internet Protocol packet */
#define ETH_P_X25 0x0805 /* CCITTX.25 */
#define ETH_P_ARP 0x0806 /*Address Resolution packet */
先看是不是IP包
是:對換MAC地址,複製以太頭結構到buffer
不是:這個就直接轉發的轉發,丟包的丟包了不管了。
unsignedshort *port = NULL; unsignedshort h_poto = 0; if(pkt->eth_hdr_ptr->h_proto== htons(ETH_P_8021Q)) { h_poto= *((unsigned short *) pkt ->eth_hdr_ptr + 8); } else { h_poto= pkt ->eth_hdr_ptr->h_proto; } if( h_poto != htons(ETH_P_IP) ) { returnERROR_FAIL; } port= (unsigned short *) pkt->l4_hdr_ptr; if(port== NULL) { returnERROR_FAIL; }
二、IP報文頭結構的話
struct iphdr { #if defined(__LITTLE_ENDIAN_BITFIELD) __u8 ihl:4, version:4; #elif defined (__BIG_ENDIAN_BITFIELD) __u8 version:4, ihl:4; #else #error "Pleasefix <asm/byteorder.h>" #endif __u8 tos; __be16 tot_len; __be16 id; __be16 frag_off; __u8 ttl; __u8 protocol; __sum16 check; __be32 saddr; __be32 daddr; /*Theoptions start here. */ };
Version = 4;
Ihl 一般情況下都是5。我在重定向的時候直接 new_ipv4_hdr->ihl = old_ipv4_hdr->ihl;因為長度使用收到報文頭長度,所以如果後還有選項的話是直接複製到新的頭結構中。
Tos兩樣複製接收到的報文資料。現在都不怎麼用這個欄位了。
Tot_len是指從IP頭結構到資料的總長度,自己算。我在算你在算,在校驗之前算好。
Id我比較簡單直接用0。這個會用於分片和重新組裝資料報。我也沒用到只是發一個302重定向報文。
Frag_off直接0。我也沒有分片,用不到偏移。
Ttl一般不是32就是64。
Protoco 根據自己的實際情況為6。
Check 去網上找個現成的就可以。也可以找找lib庫介面函式
Saddr daddr 直接對換了就可以。
要是有大小端的情況下,在這些頭結構中如果欄位是32位的就用 htonl 或是 ntohl;要是16位的話就用 htons 或是 ntohs。這個問題當時還糾結了好幾天。唉
int reply_packet_iphdr_data(pkt_info_s *pkt,struct iphdr *ipv4_hdr)
{
//其他沒有填寫欄位的內容需要在算校驗之前填好
ipv4_hdr->id=0;
ipv4_hdr->version=4;
ipv4_hdr->ihl=pkt->ipv4_hdr_ptr->ihl;
ipv4_hdr->ttl=64;
ipv4_hdr->protocol=6; //tcp 6
ipv4_hdr->frag_off=ntohs(0x4000);
ipv4_hdr->tos=pkt ->ipv4_hdr_ptr->tos;
ipv4_hdr->daddr=pkt ->ipv4_hdr_ptr->saddr;
ipv4_hdr->saddr=pkt ->ipv4_hdr_ptr->daddr;
ipv4_hdr->check= 0;
returnERROR_OK;
}
三、tcp頭結構
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 "Adjustyour <asm/byteorder.h> defines"
#endif
__be16 window;
__sum16 check;
__be16 urg_ptr;
};
TCP協議頭最少20個位元組,和IP頭到是一樣
TCP源埠(Source Port),TCP目的埠(Destination port) 對換
TCP序列號(序列碼,Sequence Number):32位
TCP應答號(Acknowledgment Number):32位
資料偏移量(HLEN):4位包括TCP頭大小,指示何處資料開始。其實也是tcp頭部長度。
保留(Reserved):6位值域,這些位必須是0。為了將來定義新的用途所保留。
標誌(Code Bits):6位標誌域:
1.URG:緊急標誌
緊急(The urgent pointer)標誌有效。緊急標誌置位,
2.ACK:確認標誌
確認編號(Acknowledgement Number)欄有效。大多數情況下該標誌位是置位的。TCP報頭內的確認編號欄內包含的確認編號(w+1,Figure:1)為下一個預期的序列編號,同時提示遠端系統已經成功接收所有資料。
3.PSH:推標誌
該標誌置位時,接收端不將該資料進行佇列處理,而是儘可能快將資料轉由應用處理。在處理telnet或rlogin等互動模式的連線時,該標誌總是置位的。
4.RST:復位標誌
復位標誌有效。用於復位相應的TCP連線。
5.SYN:同步標誌
同步序列編號(SynchronizeSequence Numbers)欄有效。該標誌僅在三次握手建立TCP連線時有效。它提示TCP連線的服務端檢查序列編號,該序列編號為TCP連線初始端(一般是客戶端)的初始序列編號。在這裡,可以把TCP序列編號看作是一個範圍從0到4,294,967,295的32位計數器。通過TCP連線交換的資料中每一個位元組都經過序列編號。在TCP報頭中的序列編號欄包括了TCP分段中第一個位元組的序列編號。
6.FIN:結束標誌
帶有該標誌置位的資料包用來結束一個TCP回話,但對應埠仍處於開放狀態,準備接收後續資料。
視窗(Window):16位,用來表示想收到的每個TCP資料段的大小。隨便寫個就好不要太小了。
校驗位(Checksum):16位TCP頭。源機器基於資料內容計算一個數值,收資訊機要與源機器數值 結果完全一樣,從而證明資料的有效性。
優先指標(緊急,Urgent Pointer):16位,指向後面是優先資料的位元組,在URG標誌設定了時才有效。如果URG標誌沒有被設定,緊急域作為填充。加快處理標示為緊急的資料段。
選項(Option):長度不定,但長度必須以位元組。如果沒有 選項就表示這個一位元組的域等於0。 一般只用出到在syn包中
資料(Date):應用程式的資料。
從三次握手開始。
C -------> S: syn 1; ack 0;客戶端seq,ack_seq會有值。可能是一個隨機值,寫一個就可以
C <------- S: syn 1; ack 1; 捎帶ack。服務端回 seq = 隨機值。Ack_seq = old_seq + 1
C -------> S: syn 0; ack 1; 端回 seq = old_ack_seq; Ack_seq= old_seq + 1
C -------> S: get psh1; ack 1; 端回seq Ack_seq 和上一個ack包的一樣。
C <------- S: 302 fin 1;psh 0; ack 1; 端回 seq= old_ack_seq; Ack_seq = old_seq + data_len
對於客戶端發過來的seq和ack_seq 值直接取就可以,為了給回包用。之後就直接丟了。
在頭兩個syn包裡會有選項欄位內容。要注意MSS
重定向主要就回了兩個包:一個是syn_ack包。另一個是http 302包。
注意get 報文可能會進行tcp分片:發兩個ack資料包等一個ack確認回包。真到psh,ack包發之後才算是一個完整的get 報文。
//這些資料基本不用變。
tcp_hdr->dest= pdesc->tcp_hdr_ptr->source;
tcp_hdr->source= pdesc->tcp_hdr_ptr->dest;
tcp_hdr->doff= pdesc->tcp_hdr_ptr->doff;
tcp_hdr->window= pdesc->tcp_hdr_ptr->window;
tcp_hdr->check= 0;
tcp_hdr->seq= pdesc->tcp_hdr_ptr->ack_seq;
tcp_hdr->ack= 1;
// ack_seq欄位的兩種情況
if(send_falg!= 0)
{
tcp_hdr->ack_seq= htonl(ntohl(pdesc->tcp_hdr_ptr->seq)+ data_len);
}
else
{
tcp_hdr->ack_seq= htonl(ntohl(pdesc->tcp_hdr_ptr->seq)+ 1);
}
//剩下的就是根據下邊四個狀態進行處理了。
fin:1,
syn:1,
rst:1,
psh:1,
四、http 302報文最小頭部
在組過302報文頭內容之後需要自己組http302報文內容 1,最小可用http 302報文頭 HTTP/1.1 302 Moved TemporarilyServer: JSP2/1.0.26
Location: www.hao123.com
這裡需要注意server: 的內容必須是location認識的伺服器型別,不知道的話可以去抓個包看下。要是不對的話302報文不認。 2,自組302報文不認, 在進行三次握手重定向時自組syn_ack報文復回正確,到了302報文客戶端接收了,wirshark抓包也沒錯但就是不認的情況,可以除錯下http302頭部欄位內容。 eg: 如果有Content-Length:欄位但是值不對,對於有的伺服器來就不認。這是我碰到的一個問題,除錯了好幾天MD。而且這個值還不怎麼好新增,直接幹掉OK。