1.发送IPv4数据包

IPv4层为它上面的层-----传输层(L4)提供了将数据包交给数据链路层(L2)来发送的方式。本节讨论该过程是如何实现的。你将看到在IPv4中传输TCPv4数据包和UDPv4数据包的一些差别。从第4层(传输层)发送IPv4数据包的主要方法有两个。一个是方法ip_queue_xmit(),供那些由自己处理分段的传输协议(如TCPv4)使用。TCPv4并非只使用传输方法ip_queue_xmit(),它还使用方法ip_build_and_send_pkt()来发送SYNACK消息(参见net/ipv4/tcp_ipv4.c中方法tcp_v4_send_synack()的实现)。另一个方法是ip_append_data(),供不处理分段的传输协议(如UDPv4和ICMPv4)使用。方法ip_append_data()并不发送数据包,而只准备数据包。实际发送数据包的方法是ip_push_pending_frames(),被ICMPv4和原始套接字使用。调用ip_push_pending_frames()后,它将调用方法ip_send_skd()来开始实际的传输过程,而方法ip_send_skb()最终将调用方法ip_local_out()。在2.6.39版之前的内核中,UDPv4使用方法ip_push_pending_frames()来传输数据包,但2.6.39引入新的APIip_finish_skb后,转而使用的是方法ip_sned_skb()。这两个方法都是在net/ipv4/ip_output.c中实现的。

有时候直接调用方法dst_output(),而不是用方法ip_queue_xmit()或ip_append_data()。例如,利用使用套接字选项IP_HDRINCL的原始套接字发送数据包时,不需要准备IPv4报头。自己创建IPv4报头的用户控件应用程序使用IPv4套接字选项IP_HDRINCL。

arp --- ARP table(arp创建的过程)2(IPv4)-LMLPHP
-----摘自《精通Linux内核网络.(以)罗森著
2.dst_output()
/*
static struct rtable *__mkroute_output(){
rth->dst.output = ip_output;
}
*/
/* Output packet to network from transport.  */
static inline int dst_output(struct sk_buff *skb)
{
return skb_dst(skb)->output(skb);
}

3.ip_output()
int ip_output(struct sk_buff *skb){
struct net_device *dev = skb_dst(skb)->dev;

IP_UPD_PO_STATS(dev_net(dev), IPSTATS_MIB_OUT, skb->len);

skb->dev = dev;
skb->protocol = htons(ETH_P_IP);

return NF_HOOK_COND(NFPROTO_IPV4, NF_INET_POST_ROUTING, skb, NULL, dev,
    ip_finish_output,
    !(IPCB(skb)->flags & IPSKB_REROUTED));
}

4.ip_finish_output()
static int ip_finish_output(struct sk_buff *skb)
{
#if defined(CONFIG_NETFILTER) && defined(CONFIG_XFRM)
/* Policy lookup after SNAT yielded a new policy */
if (skb_dst(skb)->xfrm != NULL) {
IPCB(skb)->flags |= IPSKB_REROUTED;
return dst_output(skb);
}
#endif
if (skb->len > ip_skb_dst_mtu(skb) && !skb_is_gso(skb))
return ip_fragment(skb, ip_finish_output2);
else
return ip_finish_output2(skb);
}

5.ip_finish_output2()
static inline int ip_finish_output2(struct sk_buff *skb)
{
 ......
nexthop = (__force u32) rt_nexthop(rt, ip_hdr(skb)->daddr);
neigh = __ipv4_neigh_lookup_noref(dev, nexthop);
if (unlikely(!neigh))
neigh = __neigh_create(&arp_tbl, &nexthop, dev, false);
if (!IS_ERR(neigh)) {
int res = dst_neigh_output(dst, neigh, skb);
......
}

11-06 19:01