//ip协议与l4协议接口,l4通过此接口向下l3传递数据帧 //函数主要任务: // 1.通过路由子系统路由封包 // 2.填充l3报头 // 3.ip分片 // 4.计算校验和 // 5.衔接邻居子系统,向下层传送封包。 1.1 int ip_queue_xmit(struct sk_buff *skb, int ipfragok) { struct sock *sk = skb->sk; struct inet_sock *inet = inet_sk(sk); struct ip_options *opt = inet->opt;//选项与sock相关 struct rtable *rt; struct iphdr *iph; //检查skb是否已经被路由 rt = (struct rtable *) skb->dst; if (rt != NULL) goto packet_routed; //检测ip的可用性 //dst->obsolete // 0 默认值,该结构有效可以被使用 // 2 表示该结构将被删除因而不能被使用 // -1 表示该结构被IPsec和IPv6使用但不被IPv4使用 rt = (struct rtable *)__sk_dst_check(sk, 0); if (rt == NULL) {//没有可用的路由信息,重新路由封包 u32 daddr; daddr = inet->daddr;//sock中指定的目标ip地址 if(opt && opt->srr)//源路由选项 daddr = opt->faddr;//将第一跳地址作为目标地址 { struct flowi fl = { .oif = sk->sk_bound_dev_if,//出口设备 .nl_u = { .ip4_u = { .daddr = daddr,//对目标地址进行路由,为sock中指定的目的地址,或者在源路由选项时,使用的第一跳地址 .saddr = inet->saddr,//源地址 .tos = RT_CONN_FLAGS(sk) } },//sock中指定的tos .proto = sk->sk_protocol,//l4协议 .uli_u = { .ports = { .sport = inet->sport, .dport = inet->dport } } };//源端口,目的端口 if (ip_route_output_flow(&rt, &fl, sk, 0))//对出口skb进行路由 goto no_route; } __sk_dst_set(sk, &rt->u.dst); tcp_v4_setup_caps(sk, &rt->u.dst);//根据网卡对tso的支持,更新sk的tso } skb->dst = dst_clone(&rt->u.dst);//增加dst的引用计数 packet_routed: if (opt && opt->is_strictroute && rt->rt_dst != rt->rt_gateway)//处理严路由选项,下一跳地址必须为网管地址 goto no_route; iph = (struct iphdr *) skb_push(skb, sizeof(struct iphdr) + (opt ? opt->optlen : 0));//移动skb->data向上,准备填写ip报头 *((__u16 *)iph) = htons((4 << 12) | (5 << 8) | (inet->tos & 0xff));//设置报头长度为20字节,选项的长度,在ip_options_build中增加 iph->tot_len = htons(skb->len);//总长度,skb中所有数据的长度,包括报头,选项,有效载荷 if (ip_dont_fragment(sk, &rt->u.dst) && !ipfragok)//检查禁止了分片 iph->frag_off = htons(IP_DF);//设置报头指示禁止分片 else iph->frag_off = 0;//分片中的偏移量 iph->ttl = ip_select_ttl(inet, &rt->u.dst);//生存周期 iph->protocol = sk->sk_protocol;//指示ip上层的l4协议类型 iph->saddr = rt->rt_src;//源地址,目的地址 iph->daddr = rt->rt_dst; skb->nh.iph = iph; if (opt && opt->optlen) {//sock中设置了选项 iph->ihl += opt->optlen >> 2; ip_options_build(skb, opt, inet->daddr, rt, 0);//处理选项 } ip_select_ident_more(iph, &rt->u.dst, sk, skb_shinfo(skb)->tso_segs);//为ip选择id ip_send_check(iph);//ip报头校验和 skb->priority = sk->sk_priority;//skb的优先级为sock的优先级,在dev_queue_xmit中用于在规则队列中排队 return NF_HOOK(PF_INET, NF_IP_LOCAL_OUT, skb, NULL, rt->u.dst.dev, dst_output);//调用dst->output,由ip_route_output_flow设置 no_route: IP_INC_STATS(IPSTATS_MIB_OUTNOROUTES); kfree_skb(skb); return -EHOSTUNREACH; } //调用路径ip_queue_xmit->ip_output或ip_forward_finish->ip_output 1.2 int ip_output(struct sk_buff *skb) { IP_INC_STATS(IPSTATS_MIB_OUTREQUESTS); if (skb->len > dst_pmtu(skb->dst) && !skb_shinfo(skb)->tso_size) return ip_fragment(skb, ip_finish_output);//ip分片 else return ip_finish_output(skb); } //调用路径ip_output->ip_finish_output 1.3 int ip_finish_output(struct sk_buff *skb) { struct net_device *dev = skb->dst->dev; skb->dev = dev;//出口设备 skb->protocol = htons(ETH_P_IP);//skb准备向l2层传送,在skb->protocol中告诉l2层,上层l3的协议类型 return NF_HOOK(PF_INET, NF_IP_POST_ROUTING, skb, NULL, dev, ip_finish_output2);//skb被路由之后的netfilter拦截点 } //调用路径ip_output->ip_finish_output->ip_finish_output2 //函数主要任务: // 1.保证skb头部足够空间容纳l2帧头 // 2.通过l2帧头缓存,拷贝l2帧头,完成向下层的传输 // 3.没有可供使用的l2帧头缓存,则衔接邻居子系统,交由邻居子系统处理skb 1.4 static inline int ip_finish_output2(struct sk_buff *skb) { struct dst_entry *dst = skb->dst; struct hh_cache *hh = dst->hh; struct net_device *dev = dst->dev; int hh_len = LL_RESERVED_SPACE(dev); if (unlikely(skb_headroom(skb) < hh_len && dev->hard_header)) {//skb头空间不足l2头,设备驱动提供了在skb中填充l2头的功能 struct sk_buff *skb2; skb2 = skb_realloc_headroom(skb, LL_RESERVED_SPACE(dev));//重新分配skb头来容纳l2头 if (skb2 == NULL) { kfree_skb(skb); return -ENOMEM; } if (skb->sk) skb_set_owner_w(skb2, skb->sk); kfree_skb(skb); skb = skb2; } if (hh) {//l2帧头缓存 int hh_alen; read_lock_bh(&hh->hh_lock); hh_alen = HH_DATA_ALIGN(hh->hh_len); memcpy(skb->data - hh_alen, hh->hh_data, hh_alen);//拷贝帧头缓存中的l2帧头到skb中 read_unlock_bh(&hh->hh_lock); skb_push(skb, hh->hh_len); return hh->hh_output(skb);//通过l2帧头缓存的output函数,完成skb向下的传递 } else if (dst->neighbour)//没有l2帧头缓存,则需要邻居子系统,获取数据帧的l2地址 return dst->neighbour->output(skb); if (net_ratelimit()) printk(KERN_DEBUG "ip_finish_output2: No header cache and no neighbour! "); kfree_skb(skb); return -EINVAL; }