<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结构体中的出口路由是否?#34892;行?#30340;话可不用再次查询路由表在函数ip_make_skb中直接使用rt并且调用skb_dst_set赋值给skb的_skb_refdst结构体?#21592;?#22312;发送过程中使用

              对于UDP服务端在首次发包检测到rt为空时查询路由表得到出口路由缓存在sock结构中之后发包时rt?#34892;?#30465;去再次查询

              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结构中出口路由缓存如果?#34892;?#35774;置到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做二级路由时发生错误怎么回事
              相关文章
              图文推荐
              点击?#21028;?/dd>

              关于我们 | 联系我们 | 广告服务 | 投资合作 | 版权申明 | 在线帮助 | 网站地图 | 作品发布 | 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>