Declaration: without my consent, all forms of reprint is strictly prohibited!!

1. Network Layer processing framework (Layer 3)

function role
ip_rcv Checking packets (format and whether to send them locally)
ip_rcv_finish Query the routing table to determine whether forwarding is processed locally
dst_input True forward/local processing interface
ip_local_deliver If the packet is fragmented, reassemble the packet and send it to upper-layer for processing
ip_defrag Fragment reassembly
ip_local_deliver_finish The IP header is removed and transferred to the transport layer protocol for further processing
ip_forward Whether to forward and query IPSec policy-based routes (such as IPSec policies) and modify the TTL
ip_forward_finish IP option processing
ip_queue_xmit Check whether the route is valid (if not, query the route again) and add the IP header
dst_output The forwarding operation is performed based on the routing result
ip_output If the IP address exceeds the MTU, the IP address is fragmented
ip_fragment Interface for processing IP packet fragments
ip_finish_output Specifies that the packet type in the Layer 2 header is IP (0x0800), the last function in the IP layer
ip_finish_output2 Filling the Layer 2 header (involving the MAC address of ARP query)
neigh_resolve_output ARP correlation processing

1.1 PRE_ROUTING Describes some functions

1.1.1 ip_rcv ()

This function is the entry function of the IP layer when receiving packets. Very very important. Its main functions are as follows:

  • Indicates whether to send the packet to the local device (== Destination MAC is local device ==).
  • IP Packet Header Format Check (including checksum)
  • Enter the NF_IP_PRE_ROUTING point

The code is as follows:

/* * Main IP Receive routine. */ 
int ip_rcv(struct sk_buff *skb, struct net_device *dev, struct packet_type *pt)
{
	struct iphdr *iph;

	/* When the interface is in promisc. mode, drop all the crap * that it receives, do not try to analyse it. */
	/* Discard all packets???? in promiscuous mode * /
	if (skb->pkt_type == PACKET_OTHERHOST)/* The destination MAC address is set according to the destination MAC address. Only packets destined for the destination MAC address continue to be processed at the IP layer */
		goto drop;

	IP_INC_STATS_BH(IPSTATS_MIB_INRECEIVES);

	if ((skb = skb_share_check(skb, GFP_ATOMIC)) == NULL) {
		IP_INC_STATS_BH(IPSTATS_MIB_INDISCARDS);
		goto out;
	}
	/* The linear area is the area where packets are stored, head~data~tail~end */
	if(! pskb_may_pull(skb,sizeof(struct iphdr)))/* If the length of the SKB linear region is smaller than the standard IPH, the message format is wrong */
		goto inhdr_error;

	iph = skb->nh.iph;

	RFC1122: 3.1.2.2 MUST silently discard any IP frame that fails the checksum. * * Is the datagram acceptable? * * 1. Length at least the size of an ip header * 2. Version of 4 * 3. Checksums correctly. [Speed optimisation for later, skip loopback checksums] * 4. Doesn't have a bogus length */

	if (iph->ihl < 5|| iph->version ! =4)/* Only ipv4*/
		goto inhdr_error; 

	/* * pH ->ihl*4 True header length, because it may contain part of the option field *
	if(! pskb_may_pull(skb, iph->ihl*4))
		goto inhdr_error;

	iph = skb->nh.iph;

	/* * Checksum Indicates whether the checksum is correct. The value 0 indicates that the checksum is correct */
	if(ip_fast_csum((u8 *)iph, iph->ihl) ! =0)/* If the checksum is 0, the checksum is correct */
		goto inhdr_error; 

	{
		__u32 len = ntohs(iph->tot_len); 
		if (skb->len < len || len < (iph->ihl<<2))/* Packet length verification */
			goto inhdr_error;

		/* Our transport medium may have padded the buffer out. Now we know it * is IP we can trim to the true length of the frame. * Note this now means skb->len holds ntohs(iph->tot_len). */
		if (pskb_trim_rcsum(skb, len)) {
			IP_INC_STATS_BH(IPSTATS_MIB_INDISCARDS);
			gotodrop; }}return NF_HOOK(PF_INET, NF_IP_PRE_ROUTING, skb, dev, NULL,
		       ip_rcv_finish);

inhdr_error:
	IP_INC_STATS_BH(IPSTATS_MIB_INHDRERRORS);
drop:
        kfree_skb(skb);
out:
        return NET_RX_DROP;
}
Copy the code

1.1.2 ip_rcv_finish ()

The functions of this function include:

  • Querying a Routing Table
  • Option field parsing in IP header
  • Enter thedst_input()Function, this function will, according to the results of the query routing decision message is forward “= = = =”, “= = to the upper processing = =”, “= = = =”.

The code is as follows:

/* * ip_rcv_finish: the main job is to query the routing table; Parses the option field * and decides according to the route that * 1. Passes * 2 to the upper layer. Forward ** 3. Discard **/
static inline int ip_rcv_finish(struct sk_buff *skb)
{
	struct net_device *dev = skb->dev;
	struct iphdr *iph = skb->nh.iph;

	/* * Initialise the virtual path cache for the packet. It describes * how the packet travels inside Linux networking. */ 
	 /* All received packets do not contain routing information */
	if (skb->dst == NULL) {/* If the destination route entry is empty and the route query fails, the packet is discarded */
		if (ip_route_input(skb, iph->daddr, iph->saddr, iph->tos, dev))/ *!!!!!!!!!!!!!!!!!!!!!!! core opterations!!!!!!!!! * /
			goto drop; 
	}

#ifdef CONFIG_NET_CLS_ROUTE
	if (skb->dst->tclassid) {
		struct ip_rt_acct *st = ip_rt_acct + 256*smp_processor_id();
		u32 idx = skb->dst->tclassid;
		st[idx&0xFF].o_packets++;
		st[idx&0xFF].o_bytes+=skb->len;
		st[(idx>>16) &0xFF].i_packets++;
		st[(idx>>16) &0xFF].i_bytes+=skb->len;
	}
#endif

	if (iph->ihl > 5) {/* The IP header contains the option field */
		struct ip_options *opt;

		/* It looks as overkill, because not all IP options require packet mangling. But it is the easiest for now, especially taking into account that combination of IP options and running sniffer is extremely rare condition. --ANK (980813) * /

		if (skb_cow(skb, skb_headroom(skb))) {/* If headroom space is insufficient, expand the header */
			IP_INC_STATS_BH(IPSTATS_MIB_INDISCARDS);
			goto drop;
		}
		iph = skb->nh.iph;

		if (ip_options_compile(NULL, skb))/* Parse the opt field */
			goto inhdr_error;

		opt = &(IPCB(skb)->opt);
		if (opt->srr) {
			struct in_device *in_dev = in_dev_get(dev);
			if (in_dev) {
				if(! IN_DEV_SOURCE_ROUTE(in_dev)) {if (IN_DEV_LOG_MARTIANS(in_dev) && net_ratelimit())
						printk(KERN_INFO "source route option %u.%u.%u.%u -> %u.%u.%u.%u\n",
						       NIPQUAD(iph->saddr), NIPQUAD(iph->daddr));
					in_dev_put(in_dev);
					goto drop;
				}
				in_dev_put(in_dev);
			}
			if (ip_options_rcv_srr(skb))
				gotodrop; }}return dst_input(skb);

inhdr_error:
	IP_INC_STATS_BH(IPSTATS_MIB_INHDRERRORS);
drop:
        kfree_skb(skb);
        return NET_RX_DROP;
}
Copy the code

1.1.2 dst_input ()

Dst_input () calls the corresponding function pointer based on the route result queried in ip_rcv_finish(== The route information is stored in SKB -> DST ==). Processing operations include:

  • Upper management (ip_local_deliver())
  • Forwarding (ip_forward()),
  • Also includes multicast forwarding processing (ip_mr_input()). The concrete implementation is as follows:
/* Input packet from network to transport. */
static inline int dst_input(struct sk_buff *skb)
{
	int err;
	/* * Select different processing paths according to the routing situation */
	for (;;) {
		err = skb->dst->input(skb);
		/* * SKB -> DST -> INPUT Usually has three types: * 1. ip_local_deliver * 2. ip_forward * 3. ip_MR_input **/
		if (likely(err == 0))
			return err;
		/* Oh, Jamal... Seems, I will not forgive you this mess. :-) */
		if(unlikely(err ! = NET_XMIT_BYPASS))returnerr; }}Copy the code

1.2. Some functions of LOCAL_IN are introduced

1.2.1 ip_local_deliver ()

The core function of this function is:

  • == Fragments are reassembled. If ==, the reassembled packets are forwarded to the upper layer for processing
  • Enter the NF_IP_LOCAL_IN point

The code is as follows (excluding fragment reassembly) :

/* * Deliver IP Packets to the higher protocol layers. */ 
int ip_local_deliver(struct sk_buff *skb)
{
	/* * Reassemble IP fragments. */

	if (skb->nh.iph->frag_off & htons(IP_MF|IP_OFFSET)) {
		skb = ip_defrag(skb, IP_DEFRAG_LOCAL_DELIVER);/* Reassembled packets */
		if(! skb)return 0;
	}

	return NF_HOOK(PF_INET, NF_IP_LOCAL_IN, skb, skb->dev, NULL,
		       ip_local_deliver_finish);
}
Copy the code

1.2.2 ip_defrag ()

This function does:

  • Fragment reassembly
/* Process an incoming IP datagram fragment. */
struct sk_buff *ip_defrag(struct sk_buff *skb, u32 user)
{
	struct iphdr *iph = skb->nh.iph;
	struct ipq *qp;
	struct net_device *dev;
	
	IP_INC_STATS_BH(IPSTATS_MIB_REASMREQDS);

	/* Start by cleaning up the memory. */
	if (atomic_read(&ip_frag_mem) > sysctl_ipfrag_high_thresh)/* Release a portion of the cache when the number of cached packets exceeds the system fragment threshold */
		ip_evictor();

	dev = skb->dev;

	/* Lookup (or create) queue header */
	if((qp = ip_find(iph, user)) ! =NULL) {/* Find shard nodes, only null*/ if error
		struct sk_buff *ret = NULL;

		spin_lock(&qp->lock);

		ip_frag_queue(qp, skb);/* Insert the fragment into the fragment queue according to the sequence number */

		if (qp->last_in == (FIRST_IN|LAST_IN) &&
		    qp->meat == qp->len)
			ret = ip_frag_reasm(qp, dev);/* Reassemble fragments */

		spin_unlock(&qp->lock);
		ipq_put(qp, NULL);/ * * /
		return ret;
	}

	IP_INC_STATS_BH(IPSTATS_MIB_REASMFAILS);
	kfree_skb(skb);
	return NULL;
}
Copy the code

1.2.3 ip_local_deliver_finish ()

The function does:

  • Remove the IP header
  • Gets the transport layer protocol and checks whether the original socket is monitoring this protocol
    • If the original socket is monitored, copy a copy of the SKB to the original socket
  • If this transport layer protocol is supported
    • Submit to transport layer for processing (such as ICMP,TCP, UDP,IGMP,AH,ESP,GRE,IPIP, etc.)
  • This transport layer protocol is not supported and there is no raw socket monitoring
    • ICMP returns “Protocol unreachable”

The code is as follows:

static inline int ip_local_deliver_finish(struct sk_buff *skb)/* Fragment reassembled packets */
{
	int ihl = skb->nh.iph->ihl*4;

#ifdef CONFIG_NETFILTER_DEBUG
	nf_debug_ip_local_deliver(skb);
#endif /*CONFIG_NETFILTER_DEBUG*/

	__skb_pull(skb, ihl);/* Remove the IP header */

	/* Free reference early: we don't need it any more, and it may hold ip_conntrack module loaded indefinitely. */
	nf_reset(skb);

        /* Point into the IP datagram, just past the header. */
        skb->h.raw = skb->data;/*IP data */

	rcu_read_lock();
	{
		/* Note: See raw.c and net/raw.h, RAWV4_HTABLE_SIZE==MAX_INET_PROTOS */
		int protocol = skb->nh.iph->protocol;/* Get layer 4 protocol */
		int hash;
		struct sock *raw_sk;
		struct net_protocol *ipprot;

	resubmit:
		hash = protocol & (MAX_INET_PROTOS - 1);/* Retrieve the raw socket with the protocol as a hash and transport layer processing interface */
		raw_sk = sk_head(&raw_v4_htable[hash]);/* Get the first SOCK structure in the original socket list */

		/* If there maybe a raw socket we must check - if not we * don't care less */
		if (raw_sk)/* If the original socket is monitoring this protocol message, copy a SKB to the original socket */
			raw_v4_input(skb, skb->nh.iph, hash);

		
		if((ipprot = rcu_dereference(inet_protos[hash])) ! =NULL) {/* Get the transport layer protocol operation structure */
			int ret;

			if(! ipprot->no_policy && ! xfrm4_policy_check(NULL, XFRM_POLICY_IN, skb)) {
				kfree_skb(skb);
				goto out;
			}
			/* The transport layer continues to process the packet. Note that the IP header has been removed */
			/* #ifdef CONFIG_IP_MULTICAST static struct net_protocol igmp_protocol = { .handler = igmp_rcv, }; #endif static struct net_protocol tcp_protocol = { .handler = tcp_v4_rcv, .err_handler = tcp_v4_err, .no_policy = 1, }; static struct net_protocol udp_protocol = { .handler = udp_rcv, .err_handler = udp_err, .no_policy = 1, }; static struct net_protocol icmp_protocol = { .handler = icmp_rcv, }; * /
			ret = ipprot->handler(skb);UDP,TCP, ICMP,ESP, AH, IGMP... . * /
			if (ret < 0) {
				protocol = -ret;
				goto resubmit;
			}
			IP_INC_STATS_BH(IPSTATS_MIB_INDELIVERS);
		} else {
			/* The socket is unreachable when there is no original socket. * Some packets can be received only through the original socket, for example, OSPF Hello packets, but no interface information is received. * Some packets are directly captured through the original socket and processed at the application layer. This is the case that should be handled here!! * * /
			if(! raw_sk) {/* This protocol is not recognized, and no original socket is monitoring, return protocol unreachable */
				if (xfrm4_policy_check(NULL, XFRM_POLICY_IN, skb)) {
					IP_INC_STATS_BH(IPSTATS_MIB_INUNKNOWNPROTOS);
					icmp_send(skb, ICMP_DEST_UNREACH,
						  ICMP_PROT_UNREACH, 0); }}else
				IP_INC_STATS_BH(IPSTATS_MIB_INDELIVERS);
			kfree_skb(skb);
		}
	}
 out:
	rcu_read_unlock();

	return 0;
}
Copy the code

1.3. IP_FORWARD functions are introduced

1.3.1 ip_forward ()

The function does:

  • Whether the forwarding switch is enabled/proc/sys/net/ipv4/ip_forward=1?
  • Whether the packet is sent to the local (indicates whether the Layer-2 destination MAC address is local)
  • Whether the TTL has timed out
  • == Query IPSEC policy-based routes ==
  • Modify the TTL in the PACKET IP header and recalculate the checksum
  • Enter theNF_IP_FORWARDpoint

Here is a special function: xfrm4_route_forward. Legend has it that the packet is used to match the IPsec policy. If the match succeeds, the packet is encrypted by IPsec.

The code implementation is as follows:

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);

	if(! xfrm4_policy_check(NULL, XFRM_POLICY_FWD, skb))/* Forwarding switch /proc/sys/net/ipv4/ip_forward=1 */
		goto drop;

	if (IPCB(skb)->opt.router_alert && ip_call_ra_chain(skb))
		return NET_RX_SUCCESS;

	if(skb->pkt_type ! = PACKET_HOST)/* Layer 2 packets are discarded if they are not sent locally. Reasonable * /
		goto drop;

	skb->ip_summed = CHECKSUM_NONE;
	
	/* * 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 lifetime expired. */

	iph = skb->nh.iph;

	if (iph->ttl <= 1)/*TTL times out. The packet is discarded and an ICMP timeout packet is sent */
                goto too_many_hops;

	if(! xfrm4_route_forward(skb))/* Check XFRM--IPSEC PBR?? * /
		goto drop;

	iph = skb->nh.iph;
	rt = (struct rtable*)skb->dst;/* Information about the routing table */

	if(opt->is_strictroute && rt->rt_dst ! = rt->rt_gateway)goto sr_failed;

	/* We are about to mangle packet. Copy it! * /
	/* If there is enough space to fill the layer 2 header, create a new SKB */
	if (skb_cow(skb, LL_RESERVED_SPACE(rt->u.dst.dev)+rt->u.dst.header_len))
		goto drop;
	iph = skb->nh.iph;

	/* Decrease ttl after skb cow done */
	ip_decrease_ttl(iph);/*TTL - 1*/

	/* * We now generate an ICMP HOST REDIRECT giving the route * we calculated. */
	 /* Route redirection packet */
	if(rt->rt_flags&RTCF_DOREDIRECT && ! opt->srr) ip_rt_send_redirect(skb); skb->priority = rt_tos2priority(iph->tos);return NF_HOOK(PF_INET, NF_IP_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... * /
        icmp_send(skb, ICMP_TIME_EXCEEDED, ICMP_EXC_TTL, 0);
drop:
	kfree_skb(skb);
	return NET_RX_DROP;
}
Copy the code

1.3.2 ip_forward_finish ()

This function does:

  • Packet forwarding IP option processing
  • The packet sending process is displayed

The code implementation is as follows:

static inline int ip_forward_finish(struct sk_buff *skb)
{
	struct ip_options * opt	= &(IPCB(skb)->opt);

	IP_INC_STATS_BH(IPSTATS_MIB_OUTFORWDATAGRAMS);

	if (unlikely(opt->optlen))/* Process the option field */
		ip_forward_options(skb);

	return dst_output(skb);/* Packet sending process */
}
Copy the code

1.4. Some functions of LOCAL_OUT are introduced

1.4.1 ip_queue_xmit ()

The function does:

  • Check whether directory routes are valid.
  • If the route is invalid, query the route again (including matching IPSEC policy routes, == for example, IPSEC policy ==).
  • Build the IP header +IP option
  • Enter theNF_IP_LOCAL_OUTpoint

The code implementation is as follows:

int ip_queue_xmit(struct sk_buff *skb, int ipfragok)
{
	struct sock *sk = skb->sk;/* SKB socket */
	struct inet_sock *inet = inet_sk(sk);
	struct ip_options *opt = inet->opt;
	struct rtable *rt;
	struct iphdr *iph;

	/* Skip all of this if the packet is already routed, * f.e. by something like SCTP. */
	rt = (struct rtable *) skb->dst;/* Whether the route query is complete */
	if(rt ! =NULL)
		goto packet_routed;

	/* Make sure we can route this packet. */
	rt = (struct rtable *)__sk_dst_check(sk, 0);/* Check whether the route is valid */
	if (rt == NULL) {/* This route is invalid. No route has been queried */
		u32 daddr;

		/* Use correct destination address if we have options. */
		daddr = inet->daddr;
		if(opt && opt->srr)
			daddr = opt->faddr;

		{	/* Instantiate a structure */
			struct flowi fl = { .oif = sk->sk_bound_dev_if,
					    .nl_u = { .ip4_u =
						      { .daddr = daddr,
							.saddr = inet->saddr,
							.tos = RT_CONN_FLAGS(sk) } },
					    .proto = sk->sk_protocol,
					    .uli_u = { .ports =
						       { .sport = inet->sport,
							 .dport = inet->dport } } };

			/* If this fails, retransmit mechanism of transport layer will * keep trying until route appears or the connection times * itself out. */
			if (ip_route_output_flow(&rt, &fl, sk, 0))
				goto no_route;
		}
		__sk_dst_set(sk, &rt->u.dst);/* Fill the sock sk_dst_cache with routing information */
		tcp_v4_setup_caps(sk, &rt->u.dst);
	}
	skb->dst = dst_clone(&rt->u.dst);/* Route reference count +1 to prevent deletion during trial */

packet_routed:
	if(opt && opt->is_strictroute && rt->rt_dst ! = rt->rt_gateway)goto no_route;

	/* OK, we know where to send it, allocate and build IP header. */
	/* Fill in the IP header */
	iph = (struct iphdr *) skb_push(skb, sizeof(struct iphdr) + (opt ? opt->optlen : 0));
	*((__u16 *)iph)	= htons((4 << 12) | (5 << 8) | (inet->tos & 0xff));
	iph->tot_len = htons(skb->len);
	if(ip_dont_fragment(sk, &rt->u.dst) && ! ipfragok) iph->frag_off = htons(IP_DF);else
		iph->frag_off = 0;
	iph->ttl      = ip_select_ttl(inet, &rt->u.dst);
	iph->protocol = sk->sk_protocol;
	iph->saddr    = rt->rt_src;
	iph->daddr    = rt->rt_dst;
	skb->nh.iph   = iph;
	/* Transport layer set skb->h.foo itself. */

	if (opt && opt->optlen) {
		iph->ihl += opt->optlen >> 2;
		ip_options_build(skb, opt, inet->daddr, rt, 0);/* Build the IP option field */
	}

	/* Combine socket to generate IP ID field */
	ip_select_ident_more(iph, &rt->u.dst, sk, skb_shinfo(skb)->tso_segs);

	/* Add an IP checksum. */
	ip_send_check(iph);/* Computes the IP header checksum */

	skb->priority = sk->sk_priority;

	return NF_HOOK(PF_INET, NF_IP_LOCAL_OUT, skb, NULL, rt->u.dst.dev,
		       dst_output);

no_route:
	IP_INC_STATS(IPSTATS_MIB_OUTNOROUTES);
	kfree_skb(skb);
	return -EHOSTUNREACH;
}
Copy the code

1.4.2 dst_output ()

The function does:

  • According to the routing result, the corresponding forwarding process is performedip_output). And the acceptance processdst_inputCorresponding to the
/* Output packet to network from transport. */
static inline int dst_output(struct sk_buff *skb)
{
	int err;

	for (;;) {
		err = skb->dst->output(skb);/* Usually ip_output*/

		if (likely(err == 0))
			return err;
		if(unlikely(err ! = NET_XMIT_BYPASS))returnerr; }}Copy the code

1.4.3 ip_output ()

This function is simple and may not have been known before, but it does baffle many programmers.

  • Determine whether fragmentation is required based on the MTU and packet size. If you need to callip_fragmentFragment processing is performed.

The code implementation is as follows:

int ip_output(struct sk_buff *skb)
{
	IP_INC_STATS(IPSTATS_MIB_OUTREQUESTS);

	if(skb->len > dst_mtu(skb->dst) && ! skb_shinfo(skb)->tso_size)return ip_fragment(skb, ip_finish_output);
	else
		return ip_finish_output(skb);
}
Copy the code

1.4.4 ip_fragment ()

This function is used to fragment IP packets that exceed the MTU. At present, there is no serious analysis of learning, temporarily not introduced. A point to make:

Only the peer device can reassemble fragments, but the intermediate router cannot. Cause: ==1. There is no need to regroup == ==2. The intermediate device may fail to receive all fragments and cannot regroup

1.5. POST_ROUTING functions are introduced

1.5.1 ip_finish_output

The last handler for the IP layer.

  • The packet type is IP packet and is used to fill the protocol type field (0x0800) in the Layer 2 header.
  • After enteringNF_IP_POST_ROUTING
int ip_finish_output(struct sk_buff *skb)/* The last interface in the IP layer */
{
	struct net_device *dev = skb->dst->dev;/* Outbound interface DEV*/

	skb->dev = dev;
	skb->protocol = htons(ETH_P_IP);/* Fill the layer 2 type of packets with IP type 0x0800*/

	return NF_HOOK(PF_INET, NF_IP_POST_ROUTING, skb, NULL, dev,
		       ip_finish_output2);
}
Copy the code

1.5.2 ip_finish_output2

A function that’s also important, why is it important? == because the famous ARP protocol is on this interface ==.

  • Make sure SKB space is sufficient to fill layer 2 headers
  • If the layer 2 header is already constructed, it is directly filled into the packet and then calleddev_queue_xmitSend a message
  • If no layer-2 header is configured, use ARP to query the destination MAC address and store the packet to the ARP sending queue. Send ARP packets after the ARP query is complete.

The code is as follows:

static inline int ip_finish_output2(struct sk_buff *skb)/* The IP layer has entered the data link layer, and the ARP protocol is working */
{
	struct dst_entry *dst = skb->dst; /* Routing information */
	struct hh_cache *hh = dst->hh;   /* Layer-2 header MAC address */
	struct net_device *dev = dst->dev;/* Outbound interface */
	int hh_len = LL_RESERVED_SPACE(dev);

	/* Be paranoid, rather than too clever. */
	if (unlikely(skb_headroom(skb) < hh_len && dev->hard_header)) {/* The remaining space is too small to fill the layer 2 header, then apply another SKB to replace the old SKB */
		struct sk_buff *skb2;

		skb2 = skb_realloc_headroom(skb, LL_RESERVED_SPACE(dev));
		if (skb2 == NULL) {
			kfree_skb(skb);
			return -ENOMEM;
		}
		if (skb->sk)
			skb_set_owner_w(skb2, skb->sk);
		kfree_skb(skb);
		skb = skb2;
	}

#ifdef CONFIG_NETFILTER_DEBUG
	nf_debug_ip_finish_output2(skb);
#endif /*CONFIG_NETFILTER_DEBUG*/

/* static struct neigh_ops arp_hh_ops = { .family = AF_INET, .solicit = arp_solicit, .error_report = arp_error_report, .output = neigh_resolve_output, .connected_output = neigh_resolve_output, .hh_output = dev_queue_xmit, .queue_xmit = dev_queue_xmit, }; static struct neigh_ops arp_direct_ops = { .family = AF_INET, .output = dev_queue_xmit, .connected_output = dev_queue_xmit, .hh_output = dev_queue_xmit, .queue_xmit = dev_queue_xmit, }; * /

	if (hh) {/* The layer-2 header already exists. Copy the MAC header directly before the IP header
		int hh_alen;

		read_lock_bh(&hh->hh_lock);
		hh_alen = HH_DATA_ALIGN(hh->hh_len);
  		memcpy(skb->data - hh_alen, hh->hh_data, hh_alen);/* MAC header copy */
		read_unlock_bh(&hh->hh_lock);
	        skb_push(skb, hh->hh_len);
		return hh->hh_output(skb);/* Dev_queue_xmit */ is normally used
	} else if (dst->neighbour)/* If the ARP entry is empty, use ARP to query the MAC address corresponding to the destination IP address */
		return dst->neighbour->output(skb);	Neigh_resolve_output */

	if (net_ratelimit())
		printk(KERN_DEBUG "ip_finish_output2: No header cache and no neighbour! \n");
	kfree_skb(skb);
	return -EINVAL;
}
Copy the code

1.5.3 neigh_resolve_output

ARP operations. A little.