2.6.1*Linux核心中TCP的串連跟蹤

來源:互聯網
上載者:User
本文檔的Copyleft歸yfydz所有,使用GPL發布,可以自由拷貝,轉載,轉載時請保持文檔的完整性,嚴禁用於任何商業用途。
msn: yfydz_no1@hotmail.com

來源:http://yfydz.cublog.cn1. 前言在2.6.1*以上的Linux核心中,關於TCP串連跟蹤處理有了比較大的修改,增加了TCP可能標誌位組合的檢查;增加了通過序號、確認號和視窗值來判斷資料包合法性的功能,支援SACK選項;狀態轉換數組也進行了一些修改和完善,相應程式碼量增加不少。以下2.6核心代碼版本為2.6.17.11。2. 通過確認號、序號和視窗判斷資料包合法性該思路提出比較早,最初是在“Real Statefule TCP Packet Filtering in IP FIlter”中提出的( http://www.nluug.nl/events/sane2000/papers.html
),用在FreeBSD,OpenBSD,NetBSD等作業系統的防火牆IP Filter中。原理:TCP串連開始時進行3次握手,交換MSS等資訊,同時在window欄位中告訴對方本方的資料接收緩衝區大小,另一方發送資料時一次不能發送
超過該大小的資料,也就是一方的序號變化值不能超過對方提供的window大小,確認號的變化值是不能超過己方提供的window大小,這是正常TCP
實現都會遵守的,如果不遵守這條件,說明該資料包非法。使用該功能要注意兩個TCP選項,第一,TCP的SACK(選擇性確認)選項,RFC1323,2018,2883,在資料包丟失的情況下,使發送方只重新發送丟失的包而不是全部發送;第二,擴充window選項,該選項可將window值從16位最大擴充到30位。為描述此功能新增加了一個資料結構:/* include/linux/netfilter/nf_conntrack_tcp.h */struct ip_ct_tcp_state {
 u_int32_t td_end;  /* max of seq + len */
 u_int32_t td_maxend; /* max of ack + max(win, 1) */
 u_int32_t td_maxwin; /* max(win) */
 u_int8_t td_scale; /* window scale factor */
 u_int8_t loose;  /* used when connection picked up from the middle */
 u_int8_t flags;  /* per direction options */
};判斷一個TCP包序號和確認號是否在給定window範圍內的函數是tcp_in_window:static int tcp_in_window(struct ip_ct_tcp *state,
                         enum ip_conntrack_dir dir,
                         unsigned int index,
                         const struct sk_buff *skb,
                         struct iphdr *iph,
                         struct tcphdr *tcph)
{
 struct ip_ct_tcp_state *sender = &state->seen[dir];
 struct ip_ct_tcp_state *receiver = &state->seen[!dir];
 __u32 seq, ack, sack, end, win, swin;
 int res;
 
// 用戶端發的第一個SYN包是到不了這個函數的,直接就接受了,
// 是從串連的第2個包以後才進入本函數處理
 /*
  * Get the required data from the packet.
  */
// 序號
 seq = ntohl(tcph->seq);
// 確認號
 ack = sack = ntohl(tcph->ack_seq);
// 本方視窗
 win = ntohs(tcph->window);
// 本資料包結束序號
 end = segment_seq_plus_len(seq, skb->len, iph, tcph);
// 接收方支援SACK的話檢查是否在TCP選項中有SACK 
 if (receiver->flags & IP_CT_TCP_FLAG_SACK_PERM)
  tcp_sack(skb, iph, tcph, &sack);// 省略符號部分是一些調試列印資訊,忽略下同  
...  
 if (sender->td_end == 0) {
// 串連初始情況
  /*
   * Initialize sender data.
   */
  if (tcph->syn && tcph->ack) {
// 伺服器端
   /*
    * Outgoing SYN-ACK in reply to a SYN.
    */
   sender->td_end =
   sender->td_maxend = end;
   sender->td_maxwin = (win == 0 ? 1 : win);
// 檢查TCP選項,判斷接收方是否支援SACK和視窗擴大
   tcp_options(skb, iph, tcph, sender);
   /*
    * RFC 1323:
    * Both sides must send the Window Scale option
    * to enable window scaling in either direction.
    */
   if (!(sender->flags & IP_CT_TCP_FLAG_WINDOW_SCALE
         && receiver->flags & IP_CT_TCP_FLAG_WINDOW_SCALE))
// 不支援視窗擴大
    sender->td_scale =
    receiver->td_scale = 0;
  } else {
   /*
    * We are in the middle of a connection,
    * its history is lost for us.
    * Let's try to use the data from the packet.
     */
   sender->td_end = end;
   sender->td_maxwin = (win == 0 ? 1 : win);
   sender->td_maxend = end + sender->td_maxwin;
  }
 } else if (((state->state == TCP_CONNTRACK_SYN_SENT
       && dir == IP_CT_DIR_ORIGINAL)
      || (state->state == TCP_CONNTRACK_SYN_RECV
          && dir == IP_CT_DIR_REPLY))
      && after(end, sender->td_end)) {
// 發送方重新發包
  /*
   * RFC 793: "if a TCP is reinitialized ... then it need
   * not wait at all; it must only be sure to use sequence
   * numbers larger than those recently used."
   */
  sender->td_end =
  sender->td_maxend = end;
  sender->td_maxwin = (win == 0 ? 1 : win);  tcp_options(skb, iph, tcph, sender);
 }
 
// 非ACK包和RST包,將確認號置為接收方的結束序號
 if (!(tcph->ack)) {
  /*
   * If there is no ACK, just pretend it was set and OK.
   */
  ack = sack = receiver->td_end;
 } else if (((tcp_flag_word(tcph) & (TCP_FLAG_ACK|TCP_FLAG_RST)) ==
      (TCP_FLAG_ACK|TCP_FLAG_RST))
     && (ack == 0)) {
  /*
   * Broken TCP stacks, that set ACK in RST packets as well
   * with zero ack value.
   */
  ack = sack = receiver->td_end;
 }// 無資料包或起始包
 if (seq == end
     && (!tcph->rst
         || (seq == 0 && state->state == TCP_CONNTRACK_SYN_SENT)))
  /*
   * Packets contains no data: we assume it is valid
   * and check the ack value only.
   * However RST segments are always validated by their
   * SEQ number, except when seq == 0 (reset sent answering
   * SYN.
   */
  seq = end = sender->td_end;
  
... // 檢查序號和確認號是否在合法範圍內
 if (sender->loose || receiver->loose ||
     (before(seq, sender->td_maxend + 1) &&
      after(end, sender->td_end - receiver->td_maxwin - 1) &&
      before(sack, receiver->td_end + 1) &&
      after(ack, receiver->td_end - MAXACKWINDOW(sender)))) {
// 合法包
      /*
   * Take into account window scaling (RFC 1323).
   */
// 視窗擴大調整
  if (!tcph->syn)
   win <<= sender->td_scale;
  
  /*
   * Update sender data.
   */
// 發送方視窗調整
  swin = win + (sack - ack);
  if (sender->td_maxwin < swin)
   sender->td_maxwin = swin;
  if (after(end, sender->td_end))
   sender->td_end = end;
  /*
   * Update receiver data.
   */
// 接收方的參數調整
  if (after(end, sender->td_maxend))
   receiver->td_maxwin += end - sender->td_maxend;
  if (after(sack + win, receiver->td_maxend - 1)) {
   receiver->td_maxend = sack + win;
   if (win == 0)
    receiver->td_maxend++;
  }  /*
   * Check retransmissions.
   */
// 判斷是否是重發包
  if (index == TCP_ACK_SET) {
   if (state->last_dir == dir
       && state->last_seq == seq
       && state->last_ack == ack
       && state->last_end == end)
    state->retrans++;
   else {
    state->last_dir = dir;
    state->last_seq = seq;
    state->last_ack = ack;
    state->last_end = end;
    state->retrans = 0;
   }
  }
  /*
   * Close the window of disabled window tracking
   */
  if (sender->loose)
   sender->loose--;
  
  res = 1;
 } else {
...
// 對非法包的預設策略,0拒絕,非0接受.該參數可通過/proc檔案系統設定
  res = ip_ct_tcp_be_liberal;
   }
... 
 return res;
}3. TCP狀態轉換表這是2.6.1*中的新轉換表:static const enum tcp_conntrack tcp_conntracks[2][6][TCP_CONNTRACK_MAX] = {
 {
/* ORIGINAL */
/*       sNO, sSS, sSR, sES, sFW, sCW, sLA, sTW, sCL, sLI */
/*syn*/    { sSS, sSS, sIG, sIG, sIG, sIG, sIG, sSS, sSS, sIV },
/*synack*/ { sIV, sIV, sIV, sIV, sIV, sIV, sIV, sIV, sIV, sIV },
/*fin*/    { sIV, sIV, sFW, sFW, sLA, sLA, sLA, sTW, sCL, sIV },
/*ack*/    { sES, sIV, sES, sES, sCW, sCW, sTW, sTW, sCL, sIV },
/*rst*/    { sIV, sCL, sCL, sCL, sCL, sCL, sCL, sCL, sCL, sIV },
/*none*/   { sIV, sIV, sIV, sIV, sIV, sIV, sIV, sIV, sIV, sIV }
 },
 {
/* REPLY */
/*       sNO, sSS, sSR, sES, sFW, sCW, sLA, sTW, sCL, sLI */
/*syn*/    { sIV, sIV, sIV, sIV, sIV, sIV, sIV, sIV, sIV, sIV },
/*synack*/ { sIV, sSR, sSR, sIG, sIG, sIG, sIG, sIG, sIG, sIV },
/*fin*/    { sIV, sIV, sFW, sFW, sLA, sLA, sLA, sTW, sCL, sIV },
/*ack*/    { sIV, sIG, sSR, sES, sCW, sCW, sTW, sTW, sCL, sIV },
/*rst*/    { sIV, sCL, sCL, sCL, sCL, sCL, sCL, sCL, sCL, sIV },
/*none*/   { sIV, sIV, sIV, sIV, sIV, sIV, sIV, sIV, sIV, sIV }
   }
};這是以前2.4.26中的轉換表
static enum tcp_conntrack tcp_conntracks[2][5][TCP_CONNTRACK_MAX] = {
 {
/* ORIGINAL */
/*       sNO, sES, sSS, sSR, sFW, sTW, sCL, sCW, sLA, sLI  */
/*syn*/ {sSS, sES, sSS, sSR, sSS, sSS, sSS, sSS, sSS, sLI },
/*fin*/ {sTW, sFW, sSS, sTW, sFW, sTW, sCL, sTW, sLA, sLI },
/*ack*/ {sES, sES, sSS, sES, sFW, sTW, sCL, sCW, sLA, sES },
/*rst*/ {sCL, sCL, sSS, sCL, sCL, sTW, sCL, sCL, sCL, sCL },
/*none*/{sIV, sIV, sIV, sIV, sIV, sIV, sIV, sIV, sIV, sIV }
 },
 {
/* REPLY */
/*       sNO, sES, sSS, sSR, sFW, sTW, sCL, sCW, sLA, sLI  */
/*syn*/ {sSR, sES, sSR, sSR, sSR, sSR, sSR, sSR, sSR, sSR },
/*fin*/ {sCL, sCW, sSS, sTW, sTW, sTW, sCL, sCW, sLA, sLI },
/*ack*/ {sCL, sES, sSS, sSR, sFW, sTW, sCL, sCW, sCL, sLI },
/*rst*/ {sCL, sCL, sCL, sCL, sCL, sCL, sCL, sCL, sLA, sLI },
/*none*/{sIV, sIV, sIV, sIV, sIV, sIV, sIV, sIV, sIV, sIV }
 }
};該數組的解讀方法在以前的文章中介紹過,不再贅述。從兩個數組對比可看到,增加了對SYNACK包的判斷,同時數組中的sIV(非法狀態)項也增加了很多,使得狀態跟蹤更加嚴格,但不足的是對ACK包還是太寬容,ACK掃描還是防不住。4. TCP合法標誌位組合TCP的各個標誌位的合法組合方式由下面的數組定義,數組每個元素為一種可能的組合方式,除了專門定義合法組合項為1,其他未定義的都屬於非法項,值為0。static const u8 tcp_valid_flags[(TH_FIN|TH_SYN|TH_RST|TH_PUSH|TH_ACK|TH_URG) + 1] =
{
 [TH_SYN]   = 1,
 [TH_SYN|TH_ACK]   = 1,
 [TH_SYN|TH_PUSH]  = 1,
 [TH_SYN|TH_ACK|TH_PUSH]  = 1,
 [TH_RST]   = 1,
 [TH_RST|TH_ACK]   = 1,
 [TH_RST|TH_ACK|TH_PUSH]  = 1,
 [TH_FIN|TH_ACK]   = 1,
 [TH_ACK]   = 1,
 [TH_ACK|TH_PUSH]  = 1,
 [TH_ACK|TH_URG]   = 1,
 [TH_ACK|TH_URG|TH_PUSH]  = 1,
 [TH_FIN|TH_ACK|TH_PUSH]  = 1,
 [TH_FIN|TH_ACK|TH_URG]  = 1,
 [TH_FIN|TH_ACK|TH_URG|TH_PUSH] = 1,
};5. 與netlink的介面新的協議跟蹤結構struct ip_conntrack_protocol中增加了4個和netlink介面相關函數,用於通過netlink套介面傳遞跟蹤協議相關資訊。 /* convert protoinfo to nfnetink attributes */
 int (*to_nfattr)(struct sk_buff *skb, struct nfattr *nfa,
    const struct ip_conntrack *ct); /* convert nfnetlink attributes to protoinfo */
 int (*from_nfattr)(struct nfattr *tb[], struct ip_conntrack *ct); int (*tuple_to_nfattr)(struct sk_buff *skb,
          const struct ip_conntrack_tuple *t);
 int (*nfattr_to_tuple)(struct nfattr *tb[],
          struct ip_conntrack_tuple *t);在TCP協議中對應函數為: .to_nfattr  = tcp_to_nfattr,
 .from_nfattr  = nfattr_to_tcp,
 .tuple_to_nfattr = ip_ct_port_tuple_to_nfattr,
 .nfattr_to_tuple = ip_ct_port_nfattr_to_tuple,6. 結論2.6.1*的TCP協議跟蹤處理比2.4考慮的因素增加了很多,這些新功能的使用可使系統的安全性進一步有所提高。
相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.