Commit 735cb16b authored by David S. Miller's avatar David S. Miller
Browse files

Merge branch 'ip-ingress-skb-reason'

Menglong Dong says:

====================
net: ip: add skb drop reasons to ip ingress

In the series "net: use kfree_skb_reason() for ip/udp packet receive",
skb drop reasons are added to the basic ingress path of IPv4. And in
the series "net: use kfree_skb_reason() for ip/neighbour", the egress
paths of IPv4 and IPv6 are handled. Related links:

https://lore.kernel.org/netdev/20220205074739.543606-1-imagedong@tencent.com/
https://lore.kernel.org/netdev/20220226041831.2058437-1-imagedong@tencent.com/



Seems we still have a lot work to do with IP layer, including IPv6 basic
ingress path, IPv4/IPv6 forwarding, IPv6 exthdrs, fragment and defrag,
etc.

In this series, skb drop reasons are added to the basic ingress path of
IPv6 protocol and IPv4/IPv6 packet forwarding. Following functions, which
are used for IPv6 packet receiving are handled:

  ip6_pkt_drop()
  ip6_rcv_core()
  ip6_protocol_deliver_rcu()

And following functions that used for IPv6 TLV parse are handled:

  ip6_parse_tlv()
  ipv6_hop_ra()
  ipv6_hop_ioam()
  ipv6_hop_jumbo()
  ipv6_hop_calipso()
  ipv6_dest_hao()

Besides, ip_forward() and ip6_forward(), which are used for IPv4/IPv6
forwarding, are also handled. And following new drop reasons are added:

  /* host unreachable, corresponding to IPSTATS_MIB_INADDRERRORS */
  SKB_DROP_REASON_IP_INADDRERRORS
  /* network unreachable, corresponding to IPSTATS_MIB_INADDRERRORS */
  SKB_DROP_REASON_IP_INNOROUTES
  /* packet size is too big, corresponding to
   * IPSTATS_MIB_INTOOBIGERRORS
   */
  SKB_DROP_REASON_PKT_TOO_BIG

In order to simply the definition and assignment for
'enum skb_drop_reason', some helper functions are introduced in the 1th
patch. I'm not such if this is necessary, but it makes the code simpler.
For example, we can replace the code:

  if (reason == SKB_DROP_REASON_NOT_SPECIFIED)
          reason = SKB_DROP_REASON_IP_INHDR;

with:

  SKB_DR_OR(reason, IP_INHDR);

In the 6th patch, the statistics for skb in ipv6_hop_jum() is removed,
as I think it is redundant. There are two call chains for
ipv6_hop_jumbo(). The first one is:

  ipv6_destopt_rcv() -> ip6_parse_tlv() -> ipv6_hop_jumbo()

On this call chain, the drop statistics will be done in
ipv6_destopt_rcv() with 'IPSTATS_MIB_INHDRERRORS' if ipv6_hop_jumbo()
returns false.

The second call chain is:

  ip6_rcv_core() -> ipv6_parse_hopopts() -> ip6_parse_tlv()

And the drop statistics will also be done in ip6_rcv_core() with
'IPSTATS_MIB_INHDRERRORS' if ipv6_hop_jumbo() returns false.

Therefore, the statistics in ipv6_hop_jumbo() is redundant, which
means the drop is counted twice. The statistics in ipv6_hop_jumbo()
is almost the same as the outside, except the
'IPSTATS_MIB_INTRUNCATEDPKTS', which seems that we have to ignore it.

======================================================================

Here is a basic test for IPv6 forwarding packet drop that monitored by
'dropwatch' tool:

  drop at: ip6_forward+0x81a/0xb70 (0xffffffff86c73f8a)
  origin: software
  input port ifindex: 7
  timestamp: Wed Apr 13 11:51:06 2022 130010176 nsec
  protocol: 0x86dd
  length: 94
  original length: 94
  drop reason: IP_INADDRERRORS

The origin cause of this case is that IPv6 doesn't allow to forward the
packet with LOCAL-LINK saddr, and results the 'IP_INADDRERRORS' drop
reason.
====================

Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents dba47afd eeab7e7f
Loading
Loading
Loading
Loading
+9 −2
Original line number Diff line number Diff line
@@ -79,8 +79,9 @@ extern int icmpv6_init(void);
extern int				icmpv6_err_convert(u8 type, u8 code,
							   int *err);
extern void				icmpv6_cleanup(void);
extern void				icmpv6_param_prob(struct sk_buff *skb,
							  u8 code, int pos);
extern void				icmpv6_param_prob_reason(struct sk_buff *skb,
								 u8 code, int pos,
								 enum skb_drop_reason reason);

struct flowi6;
struct in6_addr;
@@ -91,6 +92,12 @@ extern void icmpv6_flow_init(struct sock *sk,
							 const struct in6_addr *daddr,
							 int oif);

static inline void icmpv6_param_prob(struct sk_buff *skb, u8 code, int pos)
{
	icmpv6_param_prob_reason(skb, code, pos,
				 SKB_DROP_REASON_NOT_SPECIFIED);
}

static inline bool icmpv6_is_err(int type)
{
	switch (type) {
+21 −0
Original line number Diff line number Diff line
@@ -447,9 +447,30 @@ enum skb_drop_reason {
					 * 2211, such as a broadcasts
					 * ICMP_TIMESTAMP
					 */
	SKB_DROP_REASON_IP_INADDRERRORS,	/* host unreachable, corresponding
						 * to IPSTATS_MIB_INADDRERRORS
						 */
	SKB_DROP_REASON_IP_INNOROUTES,	/* network unreachable, corresponding
					 * to IPSTATS_MIB_INADDRERRORS
					 */
	SKB_DROP_REASON_PKT_TOO_BIG,	/* packet size is too big (maybe exceed
					 * the MTU)
					 */
	SKB_DROP_REASON_MAX,
};

#define SKB_DR_INIT(name, reason)				\
	enum skb_drop_reason name = SKB_DROP_REASON_##reason
#define SKB_DR(name)						\
	SKB_DR_INIT(name, NOT_SPECIFIED)
#define SKB_DR_SET(name, reason)				\
	(name = SKB_DROP_REASON_##reason)
#define SKB_DR_OR(name, reason)					\
	do {							\
		if (name == SKB_DROP_REASON_NOT_SPECIFIED)	\
			SKB_DR_SET(name, reason);		\
	} while (0)

/* To allow 64K frame to be packed as single skb without frag_list we
 * require 64K/PAGE_SIZE pages plus 1 additional page to allow for
 * buffers which do not start on a page boundary.
+3 −0
Original line number Diff line number Diff line
@@ -63,6 +63,9 @@
	EM(SKB_DROP_REASON_TAP_TXFILTER, TAP_TXFILTER)		\
	EM(SKB_DROP_REASON_ICMP_CSUM, ICMP_CSUM)		\
	EM(SKB_DROP_REASON_INVALID_PROTO, INVALID_PROTO)	\
	EM(SKB_DROP_REASON_IP_INADDRERRORS, IP_INADDRERRORS)	\
	EM(SKB_DROP_REASON_IP_INNOROUTES, IP_INNOROUTES)	\
	EM(SKB_DROP_REASON_PKT_TOO_BIG, PKT_TOO_BIG)		\
	EMe(SKB_DROP_REASON_MAX, MAX)

#undef EM
+10 −3
Original line number Diff line number Diff line
@@ -90,6 +90,7 @@ int ip_forward(struct sk_buff *skb)
	struct rtable *rt;	/* Route we use */
	struct ip_options *opt	= &(IPCB(skb)->opt);
	struct net *net;
	SKB_DR(reason);

	/* that should never happen */
	if (skb->pkt_type != PACKET_HOST)
@@ -101,8 +102,10 @@ int ip_forward(struct sk_buff *skb)
	if (skb_warn_if_lro(skb))
		goto drop;

	if (!xfrm4_policy_check(NULL, XFRM_POLICY_FWD, skb))
	if (!xfrm4_policy_check(NULL, XFRM_POLICY_FWD, skb)) {
		SKB_DR_SET(reason, XFRM_POLICY);
		goto drop;
	}

	if (IPCB(skb)->opt.router_alert && ip_call_ra_chain(skb))
		return NET_RX_SUCCESS;
@@ -118,8 +121,10 @@ int ip_forward(struct sk_buff *skb)
	if (ip_hdr(skb)->ttl <= 1)
		goto too_many_hops;

	if (!xfrm4_route_forward(skb))
	if (!xfrm4_route_forward(skb)) {
		SKB_DR_SET(reason, XFRM_POLICY);
		goto drop;
	}

	rt = skb_rtable(skb);

@@ -132,6 +137,7 @@ int ip_forward(struct sk_buff *skb)
		IP_INC_STATS(net, IPSTATS_MIB_FRAGFAILS);
		icmp_send(skb, ICMP_DEST_UNREACH, ICMP_FRAG_NEEDED,
			  htonl(mtu));
		SKB_DR_SET(reason, PKT_TOO_BIG);
		goto drop;
	}

@@ -169,7 +175,8 @@ int ip_forward(struct sk_buff *skb)
	/* Tell the sender its packet died... */
	__IP_INC_STATS(net, IPSTATS_MIB_INHDRERRORS);
	icmp_send(skb, ICMP_TIME_EXCEEDED, ICMP_EXC_TTL, 0);
	SKB_DR_SET(reason, IP_INHDR);
drop:
	kfree_skb(skb);
	kfree_skb_reason(skb, reason);
	return NET_RX_DROP;
}
+5 −1
Original line number Diff line number Diff line
@@ -945,6 +945,7 @@ static int ip_error(struct sk_buff *skb)
	struct inet_peer *peer;
	unsigned long now;
	struct net *net;
	SKB_DR(reason);
	bool send;
	int code;

@@ -964,10 +965,12 @@ static int ip_error(struct sk_buff *skb)
	if (!IN_DEV_FORWARD(in_dev)) {
		switch (rt->dst.error) {
		case EHOSTUNREACH:
			SKB_DR_SET(reason, IP_INADDRERRORS);
			__IP_INC_STATS(net, IPSTATS_MIB_INADDRERRORS);
			break;

		case ENETUNREACH:
			SKB_DR_SET(reason, IP_INNOROUTES);
			__IP_INC_STATS(net, IPSTATS_MIB_INNOROUTES);
			break;
		}
@@ -983,6 +986,7 @@ static int ip_error(struct sk_buff *skb)
		break;
	case ENETUNREACH:
		code = ICMP_NET_UNREACH;
		SKB_DR_SET(reason, IP_INNOROUTES);
		__IP_INC_STATS(net, IPSTATS_MIB_INNOROUTES);
		break;
	case EACCES:
@@ -1009,7 +1013,7 @@ static int ip_error(struct sk_buff *skb)
	if (send)
		icmp_send(skb, ICMP_DEST_UNREACH, code, 0);

out:	kfree_skb(skb);
out:	kfree_skb_reason(skb, reason);
	return 0;
}

Loading