linux socket 发送ip报文,Linux内核协议栈IP报文的上送转发及下发

这里主要分析在网络功能简单配置,且报文正常的情况下,报文的处理过程。

网卡接收到IP报文,经过一些执行路径后,最终进入ip_rcv做处理。

ip_rcv在网络功能简单配置,且报文正常的情况下,就是简单的通过ip_rcv_finish完成后续的全部处理工作。

ip_rcv_finish通过查找路由,为此报文找到一个dst_entry,然后即由skb_dst(skb)->input(skb);完成后续全部工作。

dst_entry来自查找到的路由表目。既然后续的处理,都由此dst_entry完成。那么dst_entry会将报文带到什么处理流程上去呢?

只有找到dst_entry初始化的代码,才能看出线索了。

先来看看普通的路由转发对应的dst_entry是怎么初始化的,代码在net/ipv4/route.c中的__mkroute_input中。

(生成路由条目的完整的函数调用链是ip_route_input_common-)ip_route_input_slow-)ip_mkroute_input-)__mkroute_input),

这里贴出__mkroute_input中的一部分代码。

rth = rt_dst_alloc(IN_DEV_CONF_GET(in_dev, NOPOLICY),

IN_DEV_CONF_GET(out_dev, NOXFRM));

rth->rt_key_dst= daddr;

rth->rt_dst= daddr;

rth->rt_tos= tos;

rth->rt_mark = skb->mark;

rth->rt_key_src= saddr;

rth->rt_src= saddr;

rth->rt_gateway= daddr;

rth->rt_route_iif = in_dev->dev->ifindex;

rth->rt_iif = in_dev->dev->ifindex;

rth->dst.dev= (out_dev)->dev;

dev_hold(rth->dst.dev);

rth->rt_oif = 0;

rth->rt_spec_dst= spec_dst;

rth->dst.input = ip_forward;

rth->dst.output = ip_output;

可见对应的input是ip_forward。

另外,如果报文的目的ip是本机地址,则查路由的过程中,ip_route_input_slow内部会将dst.input修改为ip_local_deliver。

ip_local_deliver的功能是将报文传递给上层协议,如udp、tcp等。

我们这里看转发的这一支。

假设内核没有配置XFRM安全框架,那么ip_forward就是调用ip_forward_finish完成报文转发。

后续代码,就是调用skb_dst(skb)->output(skb)完成转发。

再结合前面贴的__mkroute_input中的代码,得知skb_dst(skb)->output就是ip_output。

接下来,直接给出后续的调用链算了:

ip_finish_output

ip_finish_output2

dst->neighbour->output

dst->neighbour->output会是什么函数呢。这又涉及到dst->neighbour的初始化。

他是通过如下调用链初始化的。

ip_mkroute_input-)rt_intern_hash-)arp_bind_neighbour

neighbour->output又是怎么初始化的呢,具体参考arp_constructor函数。

通过参考net/ethernet/eth.c中的如下代码,可以知道neigh->ops = &arp_hh_ops;

const struct header_ops eth_header_ops ____cacheline_aligned = {

.create= eth_header,

.parse= eth_header_parse,

.rebuild= eth_rebuild_header,

.cache= eth_header_cache,

.cache_update= eth_header_cache_update,

};

/**

* ether_setup - setup Ethernet network device

* @dev: network device

* Fill in the fields of the device structure with Ethernet-generic values.

*/

void ether_setup(struct net_device *dev)

{

dev->header_ops= ð_header_ops;

dev->type= ARPHRD_ETHER;

dev->hard_header_len = ETH_HLEN;

dev->mtu= ETH_DATA_LEN;

dev->addr_len= ETH_ALEN;

dev->tx_queue_len= 1000;/* Ethernet wants good queues */

dev->flags= IFF_BROADCAST|IFF_MULTICAST;

memset(dev->broadcast, 0xFF, ETH_ALEN);

}

好了,接下来的函数,就是

neigh_resolve_output

dev_queue_xmit

__dev_xmit_skb

最后,再来看看本机产生的ip报文的外发。

应用程序的数据,通过系统调用送到socket之后,如何发送出去的呢?

这里从tcp_transmit_skb看起,此函数调用如下的函数进行发送。

icsk->icsk_af_ops->queue_xmit

icsk_af_ops = &ipv4_specific,因此,icsk->icsk_af_ops->queue_xmit就是ip_queue_xmit。

ip_queue_xmit查个路由,为此报文找到一个dst_entry,然后通过skb_dst(skb)->output(skb)将报文发送出去。

从__mkroute_input得知,dst.output = ip_output。

因此,接下来的调用链为

ip_output

ip_finish_output

ip_finish_output2

接下来的事,就和转发基本一样了,都是neighbour层的事了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值