<dl id="opymh"></dl>

<div id="opymh"></div>
      <div id="opymh"><tr id="opymh"></tr></div>

        <em id="opymh"><ins id="opymh"><mark id="opymh"></mark></ins></em><sup id="opymh"><menu id="opymh"></menu></sup>

        <em id="opymh"></em>

        <em id="opymh"><ol id="opymh"></ol></em>

              頻道欄目
              首頁 > 網絡 > 路由器 > 正文
              SKB路由緩存與SOCK路由緩存交互知識講解
              2018-05-10 10:07:08         來源:redwings的博客  
              收藏   我要投稿

              skb結構體中的成員_skb_refdst與sock結構體中成員sk_rx_dst(緩存入口路由)和sk_dst_cache(緩存出口路由)成員之間的交互操作。

              SOCK入口路由與SKB路由緩存

              內核在接收流程中,調用early_demux函數提前在IP層做established狀態的sock查找,并負責將sock結構體成員sk_rx_dst的路由緩存賦值給skb成員_skb_refdst,對于UDP協議,先判斷DST_NOCACHE標志,如果成立,增加dst引用計數,設置skb的dst;否則,調用skb_dst_set_noref直接進行設置。

              void udp_v4_early_demux(struct sk_buff *skb)
              {
                  dst = READ_ONCE(sk->sk_rx_dst);
              
                  if (dst)
                      dst = dst_check(dst, 0);
                  if (dst) {
                      /* DST_NOCACHE can not be used without taking a reference */
                      if (dst->flags & DST_NOCACHE) {
                          if (likely(atomic_inc_not_zero(&dst->__refcnt)))
                              skb_dst_set(skb, dst);
                      } else {
                          skb_dst_set_noref(skb, dst);
                      }
                  }
              }

              對于TCP協議,直接調用skb_dst_set_noref函數,將sock結構體成員sk_rx_dst緩存到skb結構體中。

              void tcp_v4_early_demux(struct sk_buff *skb)
              {
                  if (sk->sk_state != TCP_TIME_WAIT) {
                      struct dst_entry *dst = sk->sk_rx_dst;
              
                      if (dst)
                          dst = dst_check(dst, 0);
                      if (dst &&
                          inet_sk(sk)->rx_dst_ifindex == skb->skb_iif)
                          skb_dst_set_noref(skb, dst);
                  }
              }

              同樣都為early_demux函數,都是從sk->sk_rx_dst獲取路由緩存,tcp和udp的存在明顯差別。TCP直接賦值,UDP需要先判斷DST_NOCACHE標志。此情況是由UDP與TCP在sock中緩存dst時的處理不同造成的,TCP預先調用了dst_hold_safe函數,進行了DST_NOCACHE標志的判斷處理,如未緩存則增加了引用計數。然而,UDP在緩存路由dst時,使用xchg函數,未判斷也未增加引用計數,所以需要在后續判斷處理。

              static inline bool dst_hold_safe(struct dst_entry *dst)
              {
                  if (dst->flags & DST_NOCACHE)
                      return atomic_inc_not_zero(&dst->__refcnt);
                  dst_hold(dst);
              }
              
              void inet_sk_rx_dst_set(struct sock *sk, const struct sk_buff *skb)
              {
                  struct dst_entry *dst = skb_dst(skb);
              
                  if (dst && dst_hold_safe(dst)) {
                      sk->sk_rx_dst = dst;
                      inet_sk(sk)->rx_dst_ifindex = skb->skb_iif;
                  }
              }
              
              static void udp_sk_rx_dst_set(struct sock *sk, struct dst_entry *dst)
              {
                  struct dst_entry *old;
              
                  dst_hold(dst);
                  old = xchg(&sk->sk_rx_dst, dst);
                  dst_release(old);
              }


              SOCK出口路由與SKB路由緩存

              對于UDP協議客戶端,其在connect時(UDP客戶端connect不同于TCP,僅綁定通信端地址),查詢路由,緩存到sock結構體的sk_dst_cache中。

              int ip4_datagram_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len)
              {
                  rt = ip_route_connect(...);
                  sk_dst_set(sk, &rt->dst);
              }
              之后,發送UDP數據包時,檢查sock結構體中的出口路由是否有效,有效的話可不用再次查詢路由表,在函數ip_make_skb中直接使用rt,并且調用skb_dst_set賦值給skb的_skb_refdst結構體,以便在發送過程中使用。

              對于UDP服務端,在首次發包檢測到rt為空時,查詢路由表得到出口路由,緩存在sock結構中,之后發包時rt有效,省去再次查詢。

              struct sk_buff *__ip_make_skb(...)
              {
                  skb_dst_set(skb, &rt->dst);
              }
              
              int udp_sendmsg(...)
              {
                  if (connected)
                      rt = (struct rtable *)sk_dst_check(sk, 0);
                  if (rt == NULL) {
                      rt = ip_route_output_flow(net, fl4, sk);
                      if (connected)
                          sk_dst_set(sk, dst_clone(&rt->dst));
                  }
              
                  skb = ip_make_skb(sk, fl4, getfrag, msg->msg_iov, ulen,
                          sizeof(struct udphdr), &ipc, &rt,
                          msg->msg_flags);
              }

              IP層發送數據包時(調用ip_queue_xmit),檢測sock結構中出口路由緩存,如果有效,設置到skb結構體中。否則重新進行出口路由查找。

              int ip_queue_xmit(struct sk_buff *skb, struct flowi *fl)
              {
                  rt = (struct rtable *)__sk_dst_check(sk, 0);
                  if (rt == NULL) {
                      rt = ip_route_output_ports(...);
                      sk_setup_caps(sk, &rt->dst);
                  }
                  skb_dst_set_noref(skb, &rt->dst);
              }

              內核版本

              linux-3.10.0

              點擊復制鏈接 與好友分享!回本站首頁
              上一篇:在家中同時安裝兩個無線路由器的設置方法
              下一篇:openwrt做二級路由時發生錯誤,怎么回事?
              相關文章
              圖文推薦
              點擊排行

              關于我們 | 聯系我們 | 廣告服務 | 投資合作 | 版權申明 | 在線幫助 | 網站地圖 | 作品發布 | Vip技術培訓 | 舉報中心

              版權所有: 紅黑聯盟--致力于做實用的IT技術學習網站

              极速飞艇好假
              <dl id="opymh"></dl>

              <div id="opymh"></div>
                  <div id="opymh"><tr id="opymh"></tr></div>

                    <em id="opymh"><ins id="opymh"><mark id="opymh"></mark></ins></em><sup id="opymh"><menu id="opymh"></menu></sup>

                    <em id="opymh"></em>

                    <em id="opymh"><ol id="opymh"></ol></em>

                          <dl id="opymh"></dl>

                          <div id="opymh"></div>
                              <div id="opymh"><tr id="opymh"></tr></div>

                                <em id="opymh"><ins id="opymh"><mark id="opymh"></mark></ins></em><sup id="opymh"><menu id="opymh"></menu></sup>

                                <em id="opymh"></em>

                                <em id="opymh"><ol id="opymh"></ol></em>