打开APP
userphoto
未登录

开通VIP,畅享免费电子书等14项超值服

开通VIP
ip_queue_xmit简单注释

在ULNI上图中写的清楚,此函数一般由tcp或sctp调用
上层工作都已经做好了,只差ip头及以下部分的填充
tcp可以做到这一点,因为有mss的限制以及自己的一些控制包大小的算法

而udp不一样,它的数据包分段没有完成需要下层帮忙,
因此udp使用的ip_append_data函数要复杂得多。
下面简单注释ip_queue_xmit函数

 
  1. int ip_queue_xmit(struct sk_buff *skb, int ipfragok)  
  2. {  
  3.     struct sock *sk = skb->sk;  
  4.     struct inet_sock *inet = inet_sk(sk);  
  5.     struct ip_options *opt = inet->opt;  
  6.     struct rtable *rt;  
  7.     struct iphdr *iph;  
  8.     /* Skip all of this if the packet is already routed, 
  9.      * f.e. by something like SCTP. 
  10.      */  
  11.     //首先检测skb->rtable是否为空,不为空说明已经指定了路由,跳到packet_routed继续执行   
  12.     //根据上面注释,似乎sctp可能提前指定路由   
  13.     rt = skb->rtable;  
  14.     if (rt != NULL)  
  15.         goto packet_routed;  
  16.     /* Make sure we can route this packet. */  
  17.     //检测socket路由合法性,如果不合法也需要重新查找路由   
  18.     rt = (struct rtable *)__sk_dst_check(sk, 0);  
  19.     if (rt == NULL) {  
  20.         __be32 daddr;  
  21.         /* Use correct destination address if we have options. */  
  22.         daddr = inet->daddr;  
  23.         if(opt && opt->srr)  
  24.             daddr = opt->faddr;  
  25.         {  
  26.             struct flowi fl = { .oif = sk->sk_bound_dev_if,  
  27.                         .nl_u = { .ip4_u =  
  28.                               { .daddr = daddr,  
  29.                             .saddr = inet->saddr,  
  30.                             .tos = RT_CONN_FLAGS(sk) } },  
  31.                         .proto = sk->sk_protocol,  
  32.                         .flags = inet_sk_flowi_flags(sk),  
  33.                         .uli_u = { .ports =  
  34.                                { .sport = inet->sport,  
  35.                              .dport = inet->dport } } };  
  36.             /* If this fails, retransmit mechanism of transport layer will 
  37.              * keep trying until route appears or the connection times 
  38.              * itself out. 
  39.              */  
  40.             security_sk_classify_flow(sk, &fl);  
  41.             //下面是主要的出口路由查找函数,等看完路由这一章再回来补充   
  42.             if (ip_route_output_flow(sock_net(sk), &rt, &fl, sk, 0))  
  43.                 goto no_route;  
  44.         }  
  45.         //下面函数做的其中一件事是sk->sk_dst_cache = dst;并释放旧的dst缓存   
  46.         sk_setup_caps(sk, &rt->u.dst);  
  47.     }  
  48.     //增加路由缓存引用计数   
  49.     skb->dst = dst_clone(&rt->u.dst);  
  50. packet_routed:  
  51.     //如果sk_buff指向的sock的opt中包含严格源站路由选项,   
  52.     //而刚刚查找到的路由项目标地址又不等于网关地址的话前往no_route   
  53.     //说明严格源站路由无法满足   
  54.     if (opt && opt->is_strictroute && rt->rt_dst != rt->rt_gateway)  
  55.         goto no_route;  
  56.     /* OK, we know where to send it, allocate and build IP header. */  
  57.     //在skb的数据中预留出ip首部包括选项的空间给ip报头,并将   
  58.     //skb->network_header指向它   
  59.     skb_push(skb, sizeof(struct iphdr) + (opt ? opt->optlen : 0));  
  60.     skb_reset_network_header(skb);  
  61.     iph = ip_hdr(skb);  
  62.     //在ip首部填入版本号4,ip首部长度5(20字节,这个值在后面要根据选项   
  63.     //的长度增加),以及服务类型   
  64.     *((__be16 *)iph) = htons((4 << 12) | (5 << 8) | (inet->tos & 0xff));  
  65.     //如果socket要求ip不分片(这是通过检测sock->pmtudisc做到的,   
  66.     //如果使用路径mtu发现则说明要求不分片,否则允许分片)并且参数ipfragok等于0,   
  67.     //那么将DF标志置1,否则清0   
  68.     if (ip_dont_fragment(sk, &rt->u.dst) && !ipfragok)  
  69.         iph->frag_off = htons(IP_DF);  
  70.     else  
  71.         iph->frag_off = 0;  
  72.     //设置ip首部的ttl(从sock的uc_ttl获得,如果小于0则从路由项的metrics获得),   
  73.     //protocol(从sock->sk_protocol),源地址,目标地址(两者都从路由项获得)   
  74.     iph->ttl      = ip_select_ttl(inet, &rt->u.dst);  
  75.     iph->protocol = sk->sk_protocol;  
  76.     iph->saddr    = rt->rt_src;  
  77.     iph->daddr    = rt->rt_dst;  
  78.     /* Transport layer set skb->h.foo itself. */  
  79.     //若opt不为NULL,则在ip首部长度中加上选项长度,   
  80.     //并且调用ip_options_build向IP首部中写入ip选项   
  81.     if (opt && opt->optlen) {  
  82.         iph->ihl += opt->optlen >> 2;  
  83.         //这个函数值得一看,opt是从inet_sock中获得的   
  84.         ip_options_build(skb, opt, inet->daddr, rt, 0);  
  85.     }  
  86.     //调用ip_select_ident_more填入IP首部的id字段   
  87.     //关于ip的id在ULNI上讲得很清楚,Linux为了防止id回绕采取的策略是对于每一个ip   
  88.     //分配一个inet_peer结构,在这个inet_peer中记录针对这个ip的id号,   
  89.     //这样可以很大程度上减缓id回绕的速度,但是仍不能完全避免   
  90.     ip_select_ident_more(iph, &rt->u.dst, sk,  
  91.                  (skb_shinfo(skb)->gso_segs ?: 1) - 1);  
  92.     skb->priority = sk->sk_priority;  
  93.     skb->mark = sk->sk_mark;  
  94.     return ip_local_out(skb);  
  95. no_route:  
  96.     IP_INC_STATS(sock_net(sk), IPSTATS_MIB_OUTNOROUTES);  
  97.     kfree_skb(skb);  
  98.     return -EHOSTUNREACH;  
  99. }  
本站仅提供存储服务,所有内容均由用户发布,如发现有害或侵权内容,请点击举报
打开APP,阅读全文并永久保存 查看更多类似文章
猜你喜欢
类似文章
【热】打开小程序,算一算2024你的财运
Linux网络
Linux-2.6.21.1 网络函数调用流程
25 张图,一万字,拆解 Linux 网络包发送过程
初探Linux网络协议栈 -- 网络系统 -- IT技术博客大学习 -- 共学习 共进步!
理解 Linux 网络栈(1):Linux 网络协议栈简单总结
TCP/IP UDP
更多类似文章 >>
生活服务
热点新闻
分享 收藏 导长图 关注 下载文章
绑定账号成功
后续可登录账号畅享VIP特权!
如果VIP功能使用有故障,
可点击这里联系客服!

联系客服