一、前言
在linux之网络子系统-网络协议栈 发包收包详解-CSDN博客 文章中,详细介绍了跨主机之间的数据包发送的源码流程。除了跨主机,还有本机发包到本机是如何实现的?就是 saddr ip地址为 127.0.0.1 .
二、发送数据包到 127.0.0.1
首先,127.0.0.1 就是指 lo (本地回环网络设备):
这个lo设备是在 网络设备子系统 初始化时注册的:
static int __init net_dev_init(void)
{
.....
if (register_pernet_device(&loopback_net_ops))
goto out;
...
}
接着看 loopback_net_ops:
/* Registered in net/core/dev.c */
struct pernet_operations __net_initdata loopback_net_ops = {
.init = loopback_net_init,
};
接着 loopback_net_init:
/* Setup and register the loopback device. */
static __net_init int loopback_net_init(struct net *net)
{
struct net_device *dev;
int err;
err = -ENOMEM;
//初始化 lo 网络设备
dev = alloc_netdev(0, "lo", NET_NAME_UNKNOWN, loopback_setup);
if (!dev)
goto out;
dev_net_set(dev, net);
//注册lo 网络设备
err = register_netdev(dev);
if (err)
goto out_free_netdev;
BUG_ON(dev->ifindex != LOOPBACK_IFINDEX);
net->loopback_dev = dev;
return 0;
out_free_netdev:
free_netdev(dev);
out:
if (net_eq(net, &init_net))
panic("loopback: Failed to register netdevice: %d\n", err);
return err;
}
上面是 网络设备子系统初始化完成,那么如何被调用到?
根据之前的文章中,描述了跨主机的发包过程:
发包过程中,到达 网络设备子系统 dev_queue_xmit 时,会选择 lo 网络设备驱动的ndo_start_xmit 回调函数。这个驱动没有实际的硬件设备,纯属软件意义上的驱动。
lo 驱动是如何定义 ndo_start_xmit 回调函数?
函数调用链:
loopback_net_init ->
loopback_setup->
gen_lo_setup
static void loopback_setup(struct net_device *dev){
gen_lo_setup(dev, (64 * 1024), &loopback_ethtool_ops, ð_header_ops,
&loopback_ops, loopback_dev_free);
}
loopback_ops 定义如下:
static const struct net_device_ops loopback_ops = {
.ndo_init = loopback_dev_init,
.ndo_start_xmit = loopback_xmit,
.ndo_get_stats64 = loopback_get_stats64,
.ndo_set_mac_address = eth_mac_addr,
};
本机发送数据包时,调用 loopback_xmit 函数:
/* The higher levels take care of making this non-reentrant (it's
* called with bh's disabled).
*/
static netdev_tx_t loopback_xmit(struct sk_buff *skb,
struct net_device *dev)
{
int len;
skb_tx_timestamp(skb);
/* do not fool net_timestamp_check() with various clock bases */
skb->tstamp = 0;
skb_orphan(skb);
/* Before queueing this packet to netif_rx(),
* make sure dst is refcounted.
*/
skb_dst_force(skb);
skb->protocol = eth_type_trans(skb, dev);
len = skb->len;
if (likely(netif_rx(skb) == NET_RX_SUCCESS)) // lo 驱动中直接调用 接收数据包的软中断。
dev_lstats_add(dev, len);
return NETDEV_TX_OK;
}
看到了驱动与网络设备子系统的接口函数netif_rx ,然后会触发软中断,接收数据包。
上面已经把主机发包到本机的流程已经分析完毕,和跨主机的流程基本一致,都是到驱动层。
本机网络 IO 需要进行 IP 分片吗?
因为和正常的网络层处理过程一样,如果 skb 大于 MTU 的话,仍然会进行分片。
只不过 lo 的 MTU 比 Ethernet 要大很多。
通过 ifconfig 命令就可以查到,普通网卡一般为 1500,而 lO 虚拟接口能有 65535。
在内核源码中, 65535 是用 (64*1024)表示的,单位是字节。