来看接收数据。如果支持GRO,此函数在napi_gro_complete函数中和deliver_skb函数中调用。
[ net/ipv4/ip_input.c ]
/*
* Main IP Receive routine.
*
* The main IPv4 receive method is the ip_rcv() method, which is the handler for all IPv4 packets (including multicasts and broadcasts).
* In fact, this method consists mostly of sanity checks. The real work is done in the ip_rcv_finish() method it invokes.
*
*
* pt : 包的类型
PACKET_HOST : To us
PACKET_BROADCAST : To all
PACKET_MULTICAST : To group
PACKET_OTHERHOST : To someone else
PACKET_OUTGOING : Outgoing of any type
PACKET_LOOPBACK : MC/BRD frame looped back
PACKET_USER : To user space
PACKET_KERNEL : To kernel space
// Unused, PACKET_FASTROUTE and PACKET_LOOPBACK are invisible to user space
PACKET_FASTROUTE : Fastrouted frame
*/
int ip_rcv(struct sk_buff *skb, struct net_device *dev, struct packet_type *pt, struct net_device *orig_dev)
{
const struct iphdr *iph;
u32 len;
/* When the interface is in promisc. mode, drop all the crap
* that it receives, do not try to analyse it.
*/
if (skb->pkt_type == PACKET_OTHERHOST)
goto drop;
// add SNMP IPSTATS_MIB_IN
IP_UPD_PO_STATS_BH(dev_net(dev), IPSTATS_MIB_IN, skb->len);
/* 如果skb是共享的,clone一个新的,此时skb指向新clone出来的
*/
if ((skb = skb_share_check(skb, GFP_ATOMIC)) == NULL) {
IP_INC_STATS_BH(dev_net(dev), IPSTATS_MIB_INDISCARDS);
goto out;
}
/* 检测skb中的主buffer长度大于iphdr(20字节)的长度
*/
if (!pskb_may_pull(skb, sizeof(struct iphdr)))
goto inhdr_error;
iph = ip_hdr(skb); // IP头部
/*
* RFC1122: 3.2.1.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
*/
/* The length of the IPv4 header (ihl) is measured in multiples of 4 bytes.
* The IPv4 header must be at least 20 bytes in size, which means that the ihl size must be at least 5.
* The version should be 4 (for IPv4). If one of these conditions is not met, the packet is dropped and the statistics (IPSTATS_MIB_INHDRERRORS) are updated.
*/
if (iph->ihl < 5 || iph->version != 4)
goto inhdr_error;
BUILD_BUG_ON(IPSTATS_MIB_ECT1PKTS != IPSTATS_MIB_NOECTPKTS + INET_ECN_ECT_1);
BUILD_BUG_ON(IPSTATS_MIB_ECT0PKTS != IPSTATS_MIB_NOECTPKTS + INET_ECN_ECT_0);
BUILD_BUG_ON(IPSTATS_MIB_CEPKTS != IPSTATS_MIB_NOECTPKTS + INET_ECN_CE);
IP_ADD_STATS_BH(dev_net(dev),
IPSTATS_MIB_NOECTPKTS + (iph->tos & INET_ECN_MASK),
max_t(unsigned short, 1, skb_shinfo(skb)->gso_segs));
/* 检测skb中的主buffer长度大于ip头部长度(ihl*4,The length of the IPv4 header (ihl) is measured in multiples of 4 bytes )
*/
if (!pskb_may_pull(skb, iph->ihl*4))
goto inhdr_error;
iph = ip_hdr(skb); // IP头部
/* According RFC 1122, a host must verify the IPv4 header checksum on every received datagram and silently discard every datagram that has a bad checksum.
* This is done by calling the ip_fast_csum() method, which should return 0 on success. The IPv4 header checksum is calculated only over the IPv4 header bytes:
*/
if (unlikely(ip_fast_csum((u8 *)iph, iph->ihl)))
goto csum_error;
len = ntohs(iph->tot_len); // IP数据包的长度
if (skb->len < len) {
IP_INC_STATS_BH(dev_net(dev), IPSTATS_MIB_INTRUNCATEDPKTS);
goto drop;
} else if (len < (iph->ihl*4))
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(dev_net(dev), IPSTATS_MIB_INDISCARDS);
goto drop;
}
skb->transport_header = skb->network_header + iph->ihl*4; // 设置传输层头部的位置
/* Remove any debris in the socket control block
* 将skb->cb初使化为inet_skb_parm
*/
memset(IPCB(skb), 0, sizeof(struct inet_skb_parm));
/* Must drop socket now because of tproxy.
* 调用skb->destructor,和对应的socket断开
*/
skb_orphan(skb);
/* The NF_INET_PRE_ROUTING netfilter hook invoked by calling the NF_HOOK macro.
* The netfilter subsystem allows you to register callbacks in five points along the journey of a packet in the network stack.
* The reason for adding the netfilter hooks is to enable loading the netfilter kernel modules at runtime.
* The NF_HOOK macro invokes the callbacks of a specified point, if such callbacks were registered.
* The netfilter hooks can discard the packet and in such a case it will not continue on its ordinary path.
*
* The packet can either be delivered to the local machine or be forwarded to another host.
* It is the lookup in the routing table that determines which of these two options will take place.
*
* When the registered netfilter hook method returns NF_DROP, it means that the packet should be dropped, and the packet traversal does not continue.
* When the registered netfilter hook returns NF_ST