Implementation of IP protocol (V4) in Linux (2)
This article mainly introduces forward and local delivery.
Last time we mentioned that after ip_rcv_finish is complete, call the relevant sending functions ip_forward or ip_local_deliver. This time we will mainly introduce these two functions.
Let's first look at forward.
Forward generally consists of the following parts:
1. Execute IP Option
2. Make sure this package can be forward.
3. Reduce the TTL. When the TTL is 0, discard the package.
4. If necessary, slice the package.
5 send packets to the output Network Device
Note that if the packet cannot be forward for some reason, the ICMP message must be sent to the sending host.
Java code
Int ip_forward (struct sk_buff * SKB)
{
Struct iphdr * IPH;/* Our header */
Struct rtable * RT;/* route we use */
Struct ip_options * opt = & (IPCB (SKB)-> OPT );
/// GSO settings
If (skb_warn_if_lro (SKB ))
Goto drop;
/// Xfrm (IPSec) Related detection
If (! Xfrm4_policy_check (null, xfrm_policy_fwd, SKB ))
Goto drop;
/// Determine whether router_alter option exists (that is, the IP address of the sender is saved). If yes, call ip_call_ra_chain for processing. If the space is full, false is returned and processing continues.
If (IPCB (SKB)-> opt. router_alert & ip_call_ra_chain (SKB ))
Return net_rx_success;
/// Determine whether the package is accepted by Layer 2 of the local host. Set the frame type at Layer 2. When the destination address of the frame is the Layer 2 address of the Local Machine, SKB-> pkt_type is set to pcaket_host.
If (SKB-> pkt_type! = Packet_host)
Goto drop;
/// Because it is forward, we do not need to care about Layer 4 verification. Set ip_summed to checksum_none.
Skb_forward_csum (SKB );
/*
* According to the RFC, we must first decrease the TTL field. If
* That reaches zero, we must reply an ICMP control message telling
* That the packet's life time expired.
*/
/// If the TTL is less than 1, the package is discarded.
If (ip_hdr (SKB)-> TTL <= 1)
Goto too_many_hops;
/// IPSec Detection
If (! Xfrm4_route_forward (SKB ))
Goto drop;
/// Obtain the route table
RT = SKB-> rtable;
/// Determine whether it is a strict source route option. If yes, check whether the route specified by the source route option matches with rt_gateway (Next Hop.
If (opt-> is_strictroute & RT-> rt_dst! = RT-> rt_gateway)
Goto sr_failed;
/// Detect related domains. If an error occurs, send ICMP and discard the packet.
If (unlikely (SKB-> Len> dst_mtu (& RT-> U. DST )&&! Skb_is_gso (SKB )&&
(Ip_hdr (SKB)-> frag_off & htons (ip_df )))&&! SKB-> local_df ){
Ip_inc_stats (dev_net (RT-> U. dst. Dev), ipstats_mib_fragfails );
Icmp_send (SKB, icmp_dest_unreach, icmp_frag_needed,
Htonl (dst_mtu (& RT-> U. DST )));
Goto drop;
}
/// Because we want to modify some of this SKB (in ip_forward_finish below), we need to copy a copy (mainly to prevent SKB sharing)
If (skb_cow (SKB, ll_reserved_space (RT-> U. dst. Dev) + RT-> U. dst. header_len ))
Goto drop;
IPH = ip_hdr (SKB );
/// Reduce TTL
Ip_decrease_ttl (IPH );
/// If the next hop address we find is better than the request, the source host will now receive an ICMP rediresct message (only when the source host does not request the source routing option)
If (RT-> rt_flags & rtcf_doredirect &&! Opt-> SRR &&! SKB-> SP)
Ip_rt_send_redirect (SKB );
/// QoS priority settings
SKB-> priority = rt_tos2priority (IPH-> ToS );
/// Finally, the netfilter hook is returned. Here we ignore netfilter for the time being and only focus on ip_forward_finish.
Return nf_hook (pf_inet, nf_inet_forward, SKB, SKB-> Dev, RT-> U. dst. Dev,
Ip_forward_finish );
Sr_failed:
/*
* Strict routing permits no gatewaying
*/
Icmp_send (SKB, icmp_dest_unreach, icmp_sr_failed, 0 );
Goto drop;
Too_many_hops:
/* Tell the sender its packet died ...*/
Ip_inc_stats_bh (dev_net (SKB-> DST-> Dev), ipstats_mib_inhdrerrors );
Icmp_send (SKB, icmp_time_exceeded, icmp_exc_ttl, 0 );
Drop:
Kfree_skb (SKB );
Return net_rx_drop;
}
The ip_forward_finish method is mainly used to process some of the remaining options, and ip_forward_options will re-calculate IP Checksum, because it will update some IP header fields:
Java code
Static int ip_forward_finish (struct sk_buff * SKB)
{
Struct ip_options * opt = & (IPCB (SKB)-> OPT );
Ip_inc_stats_bh (dev_net (SKB-> DST-> Dev), ipstats_mib_outforwdatagrams );
If (unlikely (opt-> optlen ))
Ip_forward_options (SKB );
/// Dst_output is returned. This virtual function calls SKB-> dst_output. ip_output is used for unicast and ip_mc_output is used for multicasting. and slice (if needed) will also be performed in this function ). there is also a concept of neighboring subsystem, which will be discussed later.
Return dst_output (SKB );
}
Here's the ip_local_deliver function:
Java code
Int ip_local_deliver (struct sk_buff * SKB)
{
/*
* Reassemble IP fragments.
*/
/// If slice exists, the group package starts.
If (ip_hdr (SKB)-> frag_off & htons (ip_mf | ip_offset )){
If (ip_defrag (SKB, ip_defrag_local_deliver ))
Return 0;
}
/// Return the netfilter hook and call ip_local_deliver_finish. It will send the data packet to Layer 4. Next time, we will introduce this function in detail.
Return nf_hook (pf_inet, nf_inet_local_in, SKB, SKB-> Dev, null,
Ip_local_deliver_finish );
}
Here we can see a comparison, that is, when forward, there is no need for a group of packages, this is because local delivery must send the complete package to Layer 4, while forward does not need to go through Layer 4, directly send the frame.
Finally, let's take a look at the ip_local_deliver_finish function snippet:
Java code
Static int ip_local_deliver_finish (struct sk_buff * SKB)
{
Struct net * Net = dev_net (SKB-> Dev );
_ Skb_pull (SKB, ip_hdrlen (SKB ));
/* Point into the IP datasync, just past the header .*/
Skb_reset_transport_header (SKB );
Rcu_read_lock ();
{
Int protocol = ip_hdr (SKB)-> protocol;
Int hash, raw;
Struct net_protocol * ipprot;
Resubmit:
/// Process raw socket.
Raw = raw_local_deliver (SKB, Protocol );
Hash = protocol & (max_inet_protos-1 );
Ipprot = rcu_dereference (inet_protos [hash]);
If (ipprot! = NULL ){
Int ret;
........................................ ...........
/// Send the data packet to the processing function of the registered high-level protocol.
Ret = ipprot-> handler (SKB );
If (Ret <0 ){
Protocol =-ret;
Goto resubmit;
}
Ip_inc_stats_bh (net, ipstats_mib_indelivers );
}
........................................ ...........
}
Out:
Rcu_read_unlock ();
Return 0;
}