下面这个函数主要由udp调用, 调用几次ip_append_data之后udp缓存中有一些数据 这时想要将数据发送出去就调用ip_push_pending_frames 这个函数将skb从第一个skb的next指针取下,链入frag_list指针 然后将后面skb的len全部加到第一个skb的len,datalen,truesize上 最后填充ip首部并将包发送出去。 //输入参数 struck sock *sk: 包含待发送数据的sock //局部变量说明 struct sk_buff *skb:指向skb列表的第一个skb指针 struct sk_buff *tmp_skb:循环时当前准备加入链表的skb struct sk_buff **tail_skb:指向当前最后一个链入frag_list的skb的next指针 struct rtable *rt:路由表缓存项
- /*
- * Combined all pending IP fragments on the socket as one IP datagram
- * and push them out.
- */
- int ip_push_pending_frames(struct sock *sk)
- {
- struct sk_buff *skb, *tmp_skb;
- struct sk_buff **tail_skb;
- struct inet_sock *inet = inet_sk(sk);
- struct net *net = sock_net(sk);
- struct ip_options *opt = NULL;
- struct rtable *rt = (struct rtable *)inet->cork.dst;
- struct iphdr *iph;
- __be16 df = 0;
- __u8 ttl;
- int err = 0;
- //从sock的sk_write_queue上取下第一个skbuff,赋给skb
- if ((skb = __skb_dequeue(&sk->sk_write_queue)) == NULL)
- goto out;
- //将tail_skb指向第一个skb的frag_list指针,此步和上一步算是为循环作准备
- tail_skb = &(skb_shinfo(skb)->frag_list);
- /* move skb->data to ip header from ext header */
- //将skb的data指针指向ip首部,越过exthdr
- if (skb->data < skb_network_header(skb))
- __skb_pull(skb, skb_network_offset(skb));
- //主循环开始,每次循环从sock的sk_write_queue中取下一个skbuf,
- //链入上一个skb的frag_list域,直到sk_write_queue为空。两点要注意
- //(1).每次循环会改变第一个skb的三个变量skb->len和skb->data_len会加上tmp_skb->len,
- //skb->truesize加上tmp_skb->truesize,也就是说,所有skb的len和data_len以前是分开算的,
- //现在把所有的长度都算到第一个skb上了。
- //(2).这里会调用__sock_put(tmp_skb->sk)来递减sock的引用计数,
- //这个计数曾在ip_append_data的sock_alloc_send_skb --> sock_alloc_send_pskb --> skb_set_owner_w
- //和sock_wmalloc --> skb_set_owner_w中递增过。同时将tmp_skb->sk设置成NULL,
- //这样一来这个分片就已经完全脱离socket而交给了内核协议栈。
- while ((tmp_skb = __skb_dequeue(&sk->sk_write_queue)) != NULL) {
- __skb_pull(tmp_skb, skb_network_header_len(skb));
- *tail_skb = tmp_skb;
- tail_skb = &(tmp_skb->next);
- skb->len += tmp_skb->len;
- skb->data_len += tmp_skb->len;
- skb->truesize += tmp_skb->truesize;
- __sock_put(tmp_skb->sk);
- tmp_skb->destructor = NULL;
- tmp_skb->sk = NULL;
- }
- /* Unless user demanded real pmtu discovery (IP_PMTUDISC_DO), we allow
- * to fragment the frame generated here. No matter, what transforms
- * how transforms change size of the packet, it will come out.
- */
- //以下条件表示pmtu发现使用WANT或者DONT,即没有强制要求路径MTU发现,则将skb->local_df置1
- if (inet->pmtudisc < IP_PMTUDISC_DO)
- skb->local_df = 1;
- /* DF bit is set when we want to see DF on outgoing frames.
- * If local_df is set too, we still allow to fragment this frame
- * locally. */
- //如果满足以下几个条件之一则将df设置为IP_DF(即把ip首部DF置1),
- //(1)要求了PMTU发现
- //(2)数据包总长度小于目标路由的mtu并且ip_dont_fragment返回真。ip_dont_fragment测试条件如下:
- //(inet_sk(sk)->pmtudisc == IP_PMTUDISC_DO || (inet_sk(sk)->pmtudisc == IP_PMTUDISC_WANT /
- //&& !(dst_metric_locked(dst, RTAX_MTU))));最后一个dst_metric_locked还不明白什么意思。
- if (inet->pmtudisc >= IP_PMTUDISC_DO ||
- (skb->len <= dst_mtu(&rt->u.dst) &&
- ip_dont_fragment(sk, &rt->u.dst)))
- df = htons(IP_DF);
- //如果socket的cork的flags包含IPCORK_OPT则表明在cork中缓存有ip首部选项,这时将选项读入opt变量。
- if (inet->cork.flags & IPCORK_OPT)
- opt = inet->cork.opt;
- //检查目标路由的类型,如果是多播则将ttl设置为inet->mc_ttl(书上说这个值默认是1,因为多播不能夸局域网),
- //单播则用ip_select_ttl来设置ttl
- if (rt->rt_type == RTN_MULTICAST)
- ttl = inet->mc_ttl;
- else
- ttl = ip_select_ttl(inet, &rt->u.dst);
- //设置ip首部,版本号4,首部长度=5+opt->optlen>>2,调用ip_options_build建立ip首部选项。
- //tos=sock->tos,用df设置DF位,调用ip_select_ident设置ip的id字段,用sock->protocol设置protocol,
- //用rt的目的地址和源地址设置ip首部的dstip和srcip。
- iph = (struct iphdr *)skb->data;
- iph->version = 4;
- iph->ihl = 5;
- if (opt) {
- iph->ihl += opt->optlen>>2;
- ip_options_build(skb, opt, inet->cork.addr, rt, 0);
- }
- iph->tos = inet->tos;
- iph->frag_off = df;
- ip_select_ident(iph, &rt->u.dst, sk);
- iph->ttl = ttl;
- iph->protocol = sk->sk_protocol;
- iph->saddr = rt->rt_src;
- iph->daddr = rt->rt_dst;
- skb->priority = sk->sk_priority;
- skb->mark = sk->sk_mark;
- skb->dst = dst_clone(&rt->u.dst);
- //如果ip->protocol是icmp的话,调用icmp_out_count计算icmp的一些统计信息,
- if (iph->protocol == IPPROTO_ICMP)
- icmp_out_count(net, ((struct icmphdr *)
- skb_transport_header(skb))->type);
- /* Netfilter gets whole the not fragmented skb. */
- err = ip_local_out(skb);
- if (err) {
- if (err > 0)
- err = inet->recverr ? net_xmit_errno(err) : 0;
- if (err)
- goto error;
- }
- out:
- //发送完毕,调用ip_cork_release释放sock的cork成员
- //从ip_append_data可以看到cork是在第一个片段被缓存时分配,与这里遥相呼应
- ip_cork_release(inet);
- return err;
- error:
- IP_INC_STATS(net, IPSTATS_MIB_OUTDISCARDS);
- goto out;
- }
|