這裏主要分析在網絡功能簡單配置,且報文正常的情況下,報文的處理過程。
網卡接收到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層的事了。