ip_rcv(在 ip_input.c for ipv4)环回模式下的行为
ip_rcv (in ip_input.c for ipv4) behaviour in loopback mode
所以我想在ip_rcv函数里放几条printk信息,看看是否收到来自特定 IP 会打印一条消息。我附上了整个 ip_rcv 函数,它具有 printk 修改:
int ip_rcv(struct sk_buff *skb, struct net_device *dev, struct packet_type *pt, struct net_device *orig_dev)
{
const struct iphdr *iph;
struct net *net;
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;
net = dev_net(dev);
__IP_UPD_PO_STATS(net, IPSTATS_MIB_IN, skb->len);
skb = skb_share_check(skb, GFP_ATOMIC);
if (!skb) {
__IP_INC_STATS(net, IPSTATS_MIB_INDISCARDS);
goto out;
}
if (!pskb_may_pull(skb, sizeof(struct iphdr)))
goto inhdr_error;
iph = ip_hdr(skb);
//**PSK's Modification**
if (iph->saddr == 0x08080808)
printk("\n***PSK: %x IP's message recieved: Google***\n", iph->saddr);
if (iph->saddr == 0x0202000A)
printk("\n***PSK: %x IP's message recieved: Gateway***\n", iph->saddr);
if (iph->saddr == 0x010000FF)
printk("\n***PSK: %x IP's message recieved : Home***\n", iph->saddr);
/* 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
*/
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(net,
IPSTATS_MIB_NOECTPKTS + (iph->tos & INET_ECN_MASK),
max_t(unsigned short, 1, skb_shinfo(skb)->gso_segs));
if (!pskb_may_pull(skb, iph->ihl*4))
goto inhdr_error;
iph = ip_hdr(skb);
if (unlikely(ip_fast_csum((u8 *)iph, iph->ihl)))
goto csum_error;
len = ntohs(iph->tot_len);
if (skb->len < len) {
__IP_INC_STATS(net, 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(net, IPSTATS_MIB_INDISCARDS);
goto drop;
}
iph = ip_hdr(skb);
skb->transport_header = skb->network_header + iph->ihl*4;
/* Remove any debris in the socket control block */
memset(IPCB(skb), 0, sizeof(struct inet_skb_parm));
IPCB(skb)->iif = skb->skb_iif;
/* Must drop socket now because of tproxy. */
skb_orphan(skb);
return NF_HOOK(NFPROTO_IPV4, NF_INET_PRE_ROUTING,
net, NULL, skb, dev, NULL,
ip_rcv_finish);
csum_error:
__IP_INC_STATS(net, IPSTATS_MIB_CSUMERRORS);
inhdr_error:
__IP_INC_STATS(net, IPSTATS_MIB_INHDRERRORS);
drop:
kfree_skb(skb);
out:
return NET_RX_DROP;
}
在从 Google DNS、我的本地网关 (10.0.2.2) 或环回地址 (127.0.0.1) 获取数据包后,我应该将一条消息打印到内核缓冲区中。这对 DNS 和网关工作正常,但当我 ping 本地主机或尝试 运行 一个涉及从本地主机来回的程序时,它就不行了。是否有其他一些内核函数调用专门处理到本地主机的数据包,或者我是否遗漏了一些非常基本的东西?我认为至少在 L3 之前,环回数据包应该与任何其他数据包一样通过堆栈跟踪相同的路径。如果有人简要解释环回流量的处理以及答案,我将不胜感激。
系统规格:
虚拟机上的系统- Ubuntu
内核- 4.15.0-70 通用
我觉得这里有问题:
if (iph->saddr == 0x010000FF)
也许你的意思是:
if (iph->saddr == 0x0100007F)
loopback packets should trace the same path
是的,一般来说。
进一步调查 loopback device 的详细信息。
此外,您始终可以使用一些有用的工具进行操作,例如 trace-cmd
。 F.e。要查看功能图,您可以执行以下操作:
trace-cmd record -p function_graph -g net_rx_action
然后开始ping 127.0.0.1
,然后停止跟踪并观察报告
trace-cmd report | vim -
在这里你可以看到 "path" 并确保你的 localhost ping 最终落入 ip_rcv()
.
所以我想在ip_rcv函数里放几条printk信息,看看是否收到来自特定 IP 会打印一条消息。我附上了整个 ip_rcv 函数,它具有 printk 修改:
int ip_rcv(struct sk_buff *skb, struct net_device *dev, struct packet_type *pt, struct net_device *orig_dev)
{
const struct iphdr *iph;
struct net *net;
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;
net = dev_net(dev);
__IP_UPD_PO_STATS(net, IPSTATS_MIB_IN, skb->len);
skb = skb_share_check(skb, GFP_ATOMIC);
if (!skb) {
__IP_INC_STATS(net, IPSTATS_MIB_INDISCARDS);
goto out;
}
if (!pskb_may_pull(skb, sizeof(struct iphdr)))
goto inhdr_error;
iph = ip_hdr(skb);
//**PSK's Modification**
if (iph->saddr == 0x08080808)
printk("\n***PSK: %x IP's message recieved: Google***\n", iph->saddr);
if (iph->saddr == 0x0202000A)
printk("\n***PSK: %x IP's message recieved: Gateway***\n", iph->saddr);
if (iph->saddr == 0x010000FF)
printk("\n***PSK: %x IP's message recieved : Home***\n", iph->saddr);
/* 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
*/
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(net,
IPSTATS_MIB_NOECTPKTS + (iph->tos & INET_ECN_MASK),
max_t(unsigned short, 1, skb_shinfo(skb)->gso_segs));
if (!pskb_may_pull(skb, iph->ihl*4))
goto inhdr_error;
iph = ip_hdr(skb);
if (unlikely(ip_fast_csum((u8 *)iph, iph->ihl)))
goto csum_error;
len = ntohs(iph->tot_len);
if (skb->len < len) {
__IP_INC_STATS(net, 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(net, IPSTATS_MIB_INDISCARDS);
goto drop;
}
iph = ip_hdr(skb);
skb->transport_header = skb->network_header + iph->ihl*4;
/* Remove any debris in the socket control block */
memset(IPCB(skb), 0, sizeof(struct inet_skb_parm));
IPCB(skb)->iif = skb->skb_iif;
/* Must drop socket now because of tproxy. */
skb_orphan(skb);
return NF_HOOK(NFPROTO_IPV4, NF_INET_PRE_ROUTING,
net, NULL, skb, dev, NULL,
ip_rcv_finish);
csum_error:
__IP_INC_STATS(net, IPSTATS_MIB_CSUMERRORS);
inhdr_error:
__IP_INC_STATS(net, IPSTATS_MIB_INHDRERRORS);
drop:
kfree_skb(skb);
out:
return NET_RX_DROP;
}
在从 Google DNS、我的本地网关 (10.0.2.2) 或环回地址 (127.0.0.1) 获取数据包后,我应该将一条消息打印到内核缓冲区中。这对 DNS 和网关工作正常,但当我 ping 本地主机或尝试 运行 一个涉及从本地主机来回的程序时,它就不行了。是否有其他一些内核函数调用专门处理到本地主机的数据包,或者我是否遗漏了一些非常基本的东西?我认为至少在 L3 之前,环回数据包应该与任何其他数据包一样通过堆栈跟踪相同的路径。如果有人简要解释环回流量的处理以及答案,我将不胜感激。
系统规格: 虚拟机上的系统- Ubuntu 内核- 4.15.0-70 通用
我觉得这里有问题:
if (iph->saddr == 0x010000FF)
也许你的意思是:
if (iph->saddr == 0x0100007F)
loopback packets should trace the same path
是的,一般来说。
进一步调查 loopback device 的详细信息。
此外,您始终可以使用一些有用的工具进行操作,例如 trace-cmd
。 F.e。要查看功能图,您可以执行以下操作:
trace-cmd record -p function_graph -g net_rx_action
然后开始ping 127.0.0.1
,然后停止跟踪并观察报告
trace-cmd report | vim -
在这里你可以看到 "path" 并确保你的 localhost ping 最终落入 ip_rcv()
.