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

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
...@@ -79,8 +79,9 @@ extern int icmpv6_init(void); ...@@ -79,8 +79,9 @@ extern int icmpv6_init(void);
extern int icmpv6_err_convert(u8 type, u8 code, extern int icmpv6_err_convert(u8 type, u8 code,
int *err); int *err);
extern void icmpv6_cleanup(void); extern void icmpv6_cleanup(void);
extern void icmpv6_param_prob(struct sk_buff *skb, extern void icmpv6_param_prob_reason(struct sk_buff *skb,
u8 code, int pos); u8 code, int pos,
enum skb_drop_reason reason);
struct flowi6; struct flowi6;
struct in6_addr; struct in6_addr;
...@@ -91,6 +92,12 @@ extern void icmpv6_flow_init(struct sock *sk, ...@@ -91,6 +92,12 @@ extern void icmpv6_flow_init(struct sock *sk,
const struct in6_addr *daddr, const struct in6_addr *daddr,
int oif); 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) static inline bool icmpv6_is_err(int type)
{ {
switch (type) { switch (type) {
......
...@@ -447,9 +447,30 @@ enum skb_drop_reason { ...@@ -447,9 +447,30 @@ enum skb_drop_reason {
* 2211, such as a broadcasts * 2211, such as a broadcasts
* ICMP_TIMESTAMP * 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, 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 /* 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 * require 64K/PAGE_SIZE pages plus 1 additional page to allow for
* buffers which do not start on a page boundary. * buffers which do not start on a page boundary.
......
...@@ -63,6 +63,9 @@ ...@@ -63,6 +63,9 @@
EM(SKB_DROP_REASON_TAP_TXFILTER, TAP_TXFILTER) \ EM(SKB_DROP_REASON_TAP_TXFILTER, TAP_TXFILTER) \
EM(SKB_DROP_REASON_ICMP_CSUM, ICMP_CSUM) \ EM(SKB_DROP_REASON_ICMP_CSUM, ICMP_CSUM) \
EM(SKB_DROP_REASON_INVALID_PROTO, INVALID_PROTO) \ 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) EMe(SKB_DROP_REASON_MAX, MAX)
#undef EM #undef EM
......
...@@ -90,6 +90,7 @@ int ip_forward(struct sk_buff *skb) ...@@ -90,6 +90,7 @@ int ip_forward(struct sk_buff *skb)
struct rtable *rt; /* Route we use */ struct rtable *rt; /* Route we use */
struct ip_options *opt = &(IPCB(skb)->opt); struct ip_options *opt = &(IPCB(skb)->opt);
struct net *net; struct net *net;
SKB_DR(reason);
/* that should never happen */ /* that should never happen */
if (skb->pkt_type != PACKET_HOST) if (skb->pkt_type != PACKET_HOST)
...@@ -101,8 +102,10 @@ int ip_forward(struct sk_buff *skb) ...@@ -101,8 +102,10 @@ int ip_forward(struct sk_buff *skb)
if (skb_warn_if_lro(skb)) if (skb_warn_if_lro(skb))
goto drop; 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; goto drop;
}
if (IPCB(skb)->opt.router_alert && ip_call_ra_chain(skb)) if (IPCB(skb)->opt.router_alert && ip_call_ra_chain(skb))
return NET_RX_SUCCESS; return NET_RX_SUCCESS;
...@@ -118,8 +121,10 @@ int ip_forward(struct sk_buff *skb) ...@@ -118,8 +121,10 @@ int ip_forward(struct sk_buff *skb)
if (ip_hdr(skb)->ttl <= 1) if (ip_hdr(skb)->ttl <= 1)
goto too_many_hops; goto too_many_hops;
if (!xfrm4_route_forward(skb)) if (!xfrm4_route_forward(skb)) {
SKB_DR_SET(reason, XFRM_POLICY);
goto drop; goto drop;
}
rt = skb_rtable(skb); rt = skb_rtable(skb);
...@@ -132,6 +137,7 @@ int ip_forward(struct sk_buff *skb) ...@@ -132,6 +137,7 @@ int ip_forward(struct sk_buff *skb)
IP_INC_STATS(net, IPSTATS_MIB_FRAGFAILS); IP_INC_STATS(net, IPSTATS_MIB_FRAGFAILS);
icmp_send(skb, ICMP_DEST_UNREACH, ICMP_FRAG_NEEDED, icmp_send(skb, ICMP_DEST_UNREACH, ICMP_FRAG_NEEDED,
htonl(mtu)); htonl(mtu));
SKB_DR_SET(reason, PKT_TOO_BIG);
goto drop; goto drop;
} }
...@@ -169,7 +175,8 @@ int ip_forward(struct sk_buff *skb) ...@@ -169,7 +175,8 @@ int ip_forward(struct sk_buff *skb)
/* Tell the sender its packet died... */ /* Tell the sender its packet died... */
__IP_INC_STATS(net, IPSTATS_MIB_INHDRERRORS); __IP_INC_STATS(net, IPSTATS_MIB_INHDRERRORS);
icmp_send(skb, ICMP_TIME_EXCEEDED, ICMP_EXC_TTL, 0); icmp_send(skb, ICMP_TIME_EXCEEDED, ICMP_EXC_TTL, 0);
SKB_DR_SET(reason, IP_INHDR);
drop: drop:
kfree_skb(skb); kfree_skb_reason(skb, reason);
return NET_RX_DROP; return NET_RX_DROP;
} }
...@@ -945,6 +945,7 @@ static int ip_error(struct sk_buff *skb) ...@@ -945,6 +945,7 @@ static int ip_error(struct sk_buff *skb)
struct inet_peer *peer; struct inet_peer *peer;
unsigned long now; unsigned long now;
struct net *net; struct net *net;
SKB_DR(reason);
bool send; bool send;
int code; int code;
...@@ -964,10 +965,12 @@ static int ip_error(struct sk_buff *skb) ...@@ -964,10 +965,12 @@ static int ip_error(struct sk_buff *skb)
if (!IN_DEV_FORWARD(in_dev)) { if (!IN_DEV_FORWARD(in_dev)) {
switch (rt->dst.error) { switch (rt->dst.error) {
case EHOSTUNREACH: case EHOSTUNREACH:
SKB_DR_SET(reason, IP_INADDRERRORS);
__IP_INC_STATS(net, IPSTATS_MIB_INADDRERRORS); __IP_INC_STATS(net, IPSTATS_MIB_INADDRERRORS);
break; break;
case ENETUNREACH: case ENETUNREACH:
SKB_DR_SET(reason, IP_INNOROUTES);
__IP_INC_STATS(net, IPSTATS_MIB_INNOROUTES); __IP_INC_STATS(net, IPSTATS_MIB_INNOROUTES);
break; break;
} }
...@@ -983,6 +986,7 @@ static int ip_error(struct sk_buff *skb) ...@@ -983,6 +986,7 @@ static int ip_error(struct sk_buff *skb)
break; break;
case ENETUNREACH: case ENETUNREACH:
code = ICMP_NET_UNREACH; code = ICMP_NET_UNREACH;
SKB_DR_SET(reason, IP_INNOROUTES);
__IP_INC_STATS(net, IPSTATS_MIB_INNOROUTES); __IP_INC_STATS(net, IPSTATS_MIB_INNOROUTES);
break; break;
case EACCES: case EACCES:
...@@ -1009,7 +1013,7 @@ static int ip_error(struct sk_buff *skb) ...@@ -1009,7 +1013,7 @@ static int ip_error(struct sk_buff *skb)
if (send) if (send)
icmp_send(skb, ICMP_DEST_UNREACH, code, 0); icmp_send(skb, ICMP_DEST_UNREACH, code, 0);
out: kfree_skb(skb); out: kfree_skb_reason(skb, reason);
return 0; return 0;
} }
......
...@@ -90,12 +90,13 @@ static bool ip6_tlvopt_unknown(struct sk_buff *skb, int optoff, ...@@ -90,12 +90,13 @@ static bool ip6_tlvopt_unknown(struct sk_buff *skb, int optoff,
break; break;
fallthrough; fallthrough;
case 2: /* send ICMP PARM PROB regardless and drop packet */ case 2: /* send ICMP PARM PROB regardless and drop packet */
icmpv6_param_prob(skb, ICMPV6_UNK_OPTION, optoff); icmpv6_param_prob_reason(skb, ICMPV6_UNK_OPTION, optoff,
SKB_DROP_REASON_UNHANDLED_PROTO);
return false; return false;
} }
drop: drop:
kfree_skb(skb); kfree_skb_reason(skb, SKB_DROP_REASON_UNHANDLED_PROTO);
return false; return false;
} }
...@@ -218,7 +219,7 @@ static bool ip6_parse_tlv(bool hopbyhop, ...@@ -218,7 +219,7 @@ static bool ip6_parse_tlv(bool hopbyhop,
if (len == 0) if (len == 0)
return true; return true;
bad: bad:
kfree_skb(skb); kfree_skb_reason(skb, SKB_DROP_REASON_IP_INHDR);
return false; return false;
} }
...@@ -232,6 +233,7 @@ static bool ipv6_dest_hao(struct sk_buff *skb, int optoff) ...@@ -232,6 +233,7 @@ static bool ipv6_dest_hao(struct sk_buff *skb, int optoff)
struct ipv6_destopt_hao *hao; struct ipv6_destopt_hao *hao;
struct inet6_skb_parm *opt = IP6CB(skb); struct inet6_skb_parm *opt = IP6CB(skb);
struct ipv6hdr *ipv6h = ipv6_hdr(skb); struct ipv6hdr *ipv6h = ipv6_hdr(skb);
SKB_DR(reason);
int ret; int ret;
if (opt->dsthao) { if (opt->dsthao) {
...@@ -246,19 +248,23 @@ static bool ipv6_dest_hao(struct sk_buff *skb, int optoff) ...@@ -246,19 +248,23 @@ static bool ipv6_dest_hao(struct sk_buff *skb, int optoff)
if (hao->length != 16) { if (hao->length != 16) {
net_dbg_ratelimited("hao invalid option length = %d\n", net_dbg_ratelimited("hao invalid option length = %d\n",
hao->length); hao->length);
SKB_DR_SET(reason, IP_INHDR);
goto discard; goto discard;
} }
if (!(ipv6_addr_type(&hao->addr) & IPV6_ADDR_UNICAST)) { if (!(ipv6_addr_type(&hao->addr) & IPV6_ADDR_UNICAST)) {
net_dbg_ratelimited("hao is not an unicast addr: %pI6\n", net_dbg_ratelimited("hao is not an unicast addr: %pI6\n",
&hao->addr); &hao->addr);
SKB_DR_SET(reason, INVALID_PROTO);
goto discard; goto discard;
} }
ret = xfrm6_input_addr(skb, (xfrm_address_t *)&ipv6h->daddr, ret = xfrm6_input_addr(skb, (xfrm_address_t *)&ipv6h->daddr,
(xfrm_address_t *)&hao->addr, IPPROTO_DSTOPTS); (xfrm_address_t *)&hao->addr, IPPROTO_DSTOPTS);
if (unlikely(ret < 0)) if (unlikely(ret < 0)) {
SKB_DR_SET(reason, XFRM_POLICY);
goto discard; goto discard;
}
if (skb_cloned(skb)) { if (skb_cloned(skb)) {
if (pskb_expand_head(skb, 0, 0, GFP_ATOMIC)) if (pskb_expand_head(skb, 0, 0, GFP_ATOMIC))
...@@ -281,7 +287,7 @@ static bool ipv6_dest_hao(struct sk_buff *skb, int optoff) ...@@ -281,7 +287,7 @@ static bool ipv6_dest_hao(struct sk_buff *skb, int optoff)
return true; return true;
discard: discard:
kfree_skb(skb); kfree_skb_reason(skb, reason);
return false; return false;
} }
#endif #endif
...@@ -931,7 +937,7 @@ static bool ipv6_hop_ra(struct sk_buff *skb, int optoff) ...@@ -931,7 +937,7 @@ static bool ipv6_hop_ra(struct sk_buff *skb, int optoff)
} }
net_dbg_ratelimited("ipv6_hop_ra: wrong RA length %d\n", net_dbg_ratelimited("ipv6_hop_ra: wrong RA length %d\n",
nh[optoff + 1]); nh[optoff + 1]);
kfree_skb(skb); kfree_skb_reason(skb, SKB_DROP_REASON_IP_INHDR);
return false; return false;
} }
...@@ -985,7 +991,7 @@ static bool ipv6_hop_ioam(struct sk_buff *skb, int optoff) ...@@ -985,7 +991,7 @@ static bool ipv6_hop_ioam(struct sk_buff *skb, int optoff)
return true; return true;
drop: drop:
kfree_skb(skb); kfree_skb_reason(skb, SKB_DROP_REASON_IP_INHDR);
return false; return false;
} }
...@@ -994,31 +1000,30 @@ static bool ipv6_hop_ioam(struct sk_buff *skb, int optoff) ...@@ -994,31 +1000,30 @@ static bool ipv6_hop_ioam(struct sk_buff *skb, int optoff)
static bool ipv6_hop_jumbo(struct sk_buff *skb, int optoff) static bool ipv6_hop_jumbo(struct sk_buff *skb, int optoff)
{ {
const unsigned char *nh = skb_network_header(skb); const unsigned char *nh = skb_network_header(skb);
struct inet6_dev *idev = __in6_dev_get_safely(skb->dev); SKB_DR(reason);
struct net *net = ipv6_skb_net(skb);
u32 pkt_len; u32 pkt_len;
if (nh[optoff + 1] != 4 || (optoff & 3) != 2) { if (nh[optoff + 1] != 4 || (optoff & 3) != 2) {
net_dbg_ratelimited("ipv6_hop_jumbo: wrong jumbo opt length/alignment %d\n", net_dbg_ratelimited("ipv6_hop_jumbo: wrong jumbo opt length/alignment %d\n",
nh[optoff+1]); nh[optoff+1]);
__IP6_INC_STATS(net, idev, IPSTATS_MIB_INHDRERRORS); SKB_DR_SET(reason, IP_INHDR);
goto drop; goto drop;
} }
pkt_len = ntohl(*(__be32 *)(nh + optoff + 2)); pkt_len = ntohl(*(__be32 *)(nh + optoff + 2));
if (pkt_len <= IPV6_MAXPLEN) { if (pkt_len <= IPV6_MAXPLEN) {
__IP6_INC_STATS(net, idev, IPSTATS_MIB_INHDRERRORS); icmpv6_param_prob_reason(skb, ICMPV6_HDR_FIELD, optoff + 2,
icmpv6_param_prob(skb, ICMPV6_HDR_FIELD, optoff+2); SKB_DROP_REASON_IP_INHDR);
return false; return false;
} }
if (ipv6_hdr(skb)->payload_len) { if (ipv6_hdr(skb)->payload_len) {
__IP6_INC_STATS(net, idev, IPSTATS_MIB_INHDRERRORS); icmpv6_param_prob_reason(skb, ICMPV6_HDR_FIELD, optoff,
icmpv6_param_prob(skb, ICMPV6_HDR_FIELD, optoff); SKB_DROP_REASON_IP_INHDR);
return false; return false;
} }
if (pkt_len > skb->len - sizeof(struct ipv6hdr)) { if (pkt_len > skb->len - sizeof(struct ipv6hdr)) {
__IP6_INC_STATS(net, idev, IPSTATS_MIB_INTRUNCATEDPKTS); SKB_DR_SET(reason, PKT_TOO_SMALL);
goto drop; goto drop;
} }
...@@ -1029,7 +1034,7 @@ static bool ipv6_hop_jumbo(struct sk_buff *skb, int optoff) ...@@ -1029,7 +1034,7 @@ static bool ipv6_hop_jumbo(struct sk_buff *skb, int optoff)
return true; return true;
drop: drop:
kfree_skb(skb); kfree_skb_reason(skb, reason);
return false; return false;
} }
...@@ -1051,7 +1056,7 @@ static bool ipv6_hop_calipso(struct sk_buff *skb, int optoff) ...@@ -1051,7 +1056,7 @@ static bool ipv6_hop_calipso(struct sk_buff *skb, int optoff)
return true; return true;
drop: drop:
kfree_skb(skb); kfree_skb_reason(skb, SKB_DROP_REASON_IP_INHDR);
return false; return false;
} }
......
...@@ -629,12 +629,13 @@ void icmp6_send(struct sk_buff *skb, u8 type, u8 code, __u32 info, ...@@ -629,12 +629,13 @@ void icmp6_send(struct sk_buff *skb, u8 type, u8 code, __u32 info,
} }
EXPORT_SYMBOL(icmp6_send); EXPORT_SYMBOL(icmp6_send);
/* Slightly more convenient version of icmp6_send. /* Slightly more convenient version of icmp6_send with drop reasons.
*/ */
void icmpv6_param_prob(struct sk_buff *skb, u8 code, int pos) void icmpv6_param_prob_reason(struct sk_buff *skb, u8 code, int pos,
enum skb_drop_reason reason)
{ {
icmp6_send(skb, ICMPV6_PARAMPROB, code, pos, NULL, IP6CB(skb)); icmp6_send(skb, ICMPV6_PARAMPROB, code, pos, NULL, IP6CB(skb));
kfree_skb(skb); kfree_skb_reason(skb, reason);
} }
/* Generate icmpv6 with type/code ICMPV6_DEST_UNREACH/ICMPV6_ADDR_UNREACH /* Generate icmpv6 with type/code ICMPV6_DEST_UNREACH/ICMPV6_ADDR_UNREACH
......
...@@ -145,13 +145,14 @@ static void ip6_list_rcv_finish(struct net *net, struct sock *sk, ...@@ -145,13 +145,14 @@ static void ip6_list_rcv_finish(struct net *net, struct sock *sk,
static struct sk_buff *ip6_rcv_core(struct sk_buff *skb, struct net_device *dev, static struct sk_buff *ip6_rcv_core(struct sk_buff *skb, struct net_device *dev,
struct net *net) struct net *net)
{ {
enum skb_drop_reason reason;
const struct ipv6hdr *hdr; const struct ipv6hdr *hdr;
u32 pkt_len; u32 pkt_len;
struct inet6_dev *idev; struct inet6_dev *idev;
if (skb->pkt_type == PACKET_OTHERHOST) { if (skb->pkt_type == PACKET_OTHERHOST) {
dev_core_stats_rx_otherhost_dropped_inc(skb->dev); dev_core_stats_rx_otherhost_dropped_inc(skb->dev);
kfree_skb(skb); kfree_skb_reason(skb, SKB_DROP_REASON_OTHERHOST);
return NULL; return NULL;
} }
...@@ -161,9 +162,12 @@ static struct sk_buff *ip6_rcv_core(struct sk_buff *skb, struct net_device *dev, ...@@ -161,9 +162,12 @@ static struct sk_buff *ip6_rcv_core(struct sk_buff *skb, struct net_device *dev,
__IP6_UPD_PO_STATS(net, idev, IPSTATS_MIB_IN, skb->len); __IP6_UPD_PO_STATS(net, idev, IPSTATS_MIB_IN, skb->len);
SKB_DR_SET(reason, NOT_SPECIFIED);
if ((skb = skb_share_check(skb, GFP_ATOMIC)) == NULL || if ((skb = skb_share_check(skb, GFP_ATOMIC)) == NULL ||
!idev || unlikely(idev->cnf.disable_ipv6)) { !idev || unlikely(idev->cnf.disable_ipv6)) {
__IP6_INC_STATS(net, idev, IPSTATS_MIB_INDISCARDS); __IP6_INC_STATS(net, idev, IPSTATS_MIB_INDISCARDS);
if (unlikely(idev->cnf.disable_ipv6))
SKB_DR_SET(reason, IPV6DISABLED);
goto drop; goto drop;
} }
...@@ -187,8 +191,10 @@ static struct sk_buff *ip6_rcv_core(struct sk_buff *skb, struct net_device *dev, ...@@ -187,8 +191,10 @@ static struct sk_buff *ip6_rcv_core(struct sk_buff *skb, struct net_device *dev,
hdr = ipv6_hdr(skb); hdr = ipv6_hdr(skb);
if (hdr->version != 6) if (hdr->version != 6) {
SKB_DR_SET(reason, UNHANDLED_PROTO);
goto err; goto err;
}
__IP6_ADD_STATS(net, idev, __IP6_ADD_STATS(net, idev,
IPSTATS_MIB_NOECTPKTS + IPSTATS_MIB_NOECTPKTS +
...@@ -226,8 +232,10 @@ static struct sk_buff *ip6_rcv_core(struct sk_buff *skb, struct net_device *dev, ...@@ -226,8 +232,10 @@ static struct sk_buff *ip6_rcv_core(struct sk_buff *skb, struct net_device *dev,
if (!ipv6_addr_is_multicast(&hdr->daddr) && if (!ipv6_addr_is_multicast(&hdr->daddr) &&
(skb->pkt_type == PACKET_BROADCAST || (skb->pkt_type == PACKET_BROADCAST ||
skb->pkt_type == PACKET_MULTICAST) && skb->pkt_type == PACKET_MULTICAST) &&
idev->cnf.drop_unicast_in_l2_multicast) idev->cnf.drop_unicast_in_l2_multicast) {
SKB_DR_SET(reason, UNICAST_IN_L2_MULTICAST);
goto err; goto err;
}
/* RFC4291 2.7 /* RFC4291 2.7
* Nodes must not originate a packet to a multicast address whose scope * Nodes must not originate a packet to a multicast address whose scope
...@@ -256,12 +264,11 @@ static struct sk_buff *ip6_rcv_core(struct sk_buff *skb, struct net_device *dev, ...@@ -256,12 +264,11 @@ static struct sk_buff *ip6_rcv_core(struct sk_buff *skb, struct net_device *dev,
if (pkt_len + sizeof(struct ipv6hdr) > skb->len) { if (pkt_len + sizeof(struct ipv6hdr) > skb->len) {
__IP6_INC_STATS(net, __IP6_INC_STATS(net,
idev, IPSTATS_MIB_INTRUNCATEDPKTS); idev, IPSTATS_MIB_INTRUNCATEDPKTS);
SKB_DR_SET(reason, PKT_TOO_SMALL);
goto drop; goto drop;
} }
if (pskb_trim_rcsum(skb, pkt_len + sizeof(struct ipv6hdr))) { if (pskb_trim_rcsum(skb, pkt_len + sizeof(struct ipv6hdr)))
__IP6_INC_STATS(net, idev, IPSTATS_MIB_INHDRERRORS); goto err;
goto drop;
}
hdr = ipv6_hdr(skb); hdr = ipv6_hdr(skb);
} }
...@@ -282,9 +289,10 @@ static struct sk_buff *ip6_rcv_core(struct sk_buff *skb, struct net_device *dev, ...@@ -282,9 +289,10 @@ static struct sk_buff *ip6_rcv_core(struct sk_buff *skb, struct net_device *dev,
return skb; return skb;
err: err:
__IP6_INC_STATS(net, idev, IPSTATS_MIB_INHDRERRORS); __IP6_INC_STATS(net, idev, IPSTATS_MIB_INHDRERRORS);
SKB_DR_OR(reason, IP_INHDR);
drop: drop:
rcu_read_unlock(); rcu_read_unlock();
kfree_skb(skb); kfree_skb_reason(skb, reason);
return NULL; return NULL;
} }
...@@ -354,6 +362,7 @@ void ip6_protocol_deliver_rcu(struct net *net, struct sk_buff *skb, int nexthdr, ...@@ -354,6 +362,7 @@ void ip6_protocol_deliver_rcu(struct net *net, struct sk_buff *skb, int nexthdr,
const struct inet6_protocol *ipprot; const struct inet6_protocol *ipprot;
struct inet6_dev *idev; struct inet6_dev *idev;
unsigned int nhoff; unsigned int nhoff;
SKB_DR(reason);
bool raw; bool raw;
/* /*
...@@ -413,12 +422,16 @@ void ip6_protocol_deliver_rcu(struct net *net, struct sk_buff *skb, int nexthdr, ...@@ -413,12 +422,16 @@ void ip6_protocol_deliver_rcu(struct net *net, struct sk_buff *skb, int nexthdr,
if (ipv6_addr_is_multicast(&hdr->daddr) && if (ipv6_addr_is_multicast(&hdr->daddr) &&
!ipv6_chk_mcast_addr(dev, &hdr->daddr, !ipv6_chk_mcast_addr(dev, &hdr->daddr,
&hdr->saddr) && &hdr->saddr) &&
!ipv6_is_mld(skb, nexthdr, skb_network_header_len(skb))) !ipv6_is_mld(skb, nexthdr, skb_network_header_len(skb))) {
SKB_DR_SET(reason, IP_INADDRERRORS);
goto discard; goto discard;
}
} }
if (!(ipprot->flags & INET6_PROTO_NOPOLICY) && if (!(ipprot->flags & INET6_PROTO_NOPOLICY) &&
!xfrm6_policy_check(NULL, XFRM_POLICY_IN, skb)) !xfrm6_policy_check(NULL, XFRM_POLICY_IN, skb)) {
SKB_DR_SET(reason, XFRM_POLICY);
goto discard; goto discard;
}
ret = INDIRECT_CALL_2(ipprot->handler, tcp_v6_rcv, udpv6_rcv, ret = INDIRECT_CALL_2(ipprot->handler, tcp_v6_rcv, udpv6_rcv,
skb); skb);
...@@ -444,8 +457,11 @@ void ip6_protocol_deliver_rcu(struct net *net, struct sk_buff *skb, int nexthdr, ...@@ -444,8 +457,11 @@ void ip6_protocol_deliver_rcu(struct net *net, struct sk_buff *skb, int nexthdr,
IPSTATS_MIB_INUNKNOWNPROTOS); IPSTATS_MIB_INUNKNOWNPROTOS);
icmpv6_send(skb, ICMPV6_PARAMPROB, icmpv6_send(skb, ICMPV6_PARAMPROB,
ICMPV6_UNK_NEXTHDR, nhoff); ICMPV6_UNK_NEXTHDR, nhoff);
SKB_DR_SET(reason, IP_NOPROTO);
} else {
SKB_DR_SET(reason, XFRM_POLICY);
} }
kfree_skb(skb); kfree_skb_reason(skb, reason);
} else { } else {
__IP6_INC_STATS(net, idev, IPSTATS_MIB_INDELIVERS); __IP6_INC_STATS(net, idev, IPSTATS_MIB_INDELIVERS);
consume_skb(skb); consume_skb(skb);
...@@ -455,7 +471,7 @@ void ip6_protocol_deliver_rcu(struct net *net, struct sk_buff *skb, int nexthdr, ...@@ -455,7 +471,7 @@ void ip6_protocol_deliver_rcu(struct net *net, struct sk_buff *skb, int nexthdr,
discard: discard:
__IP6_INC_STATS(net, idev, IPSTATS_MIB_INDISCARDS); __IP6_INC_STATS(net, idev, IPSTATS_MIB_INDISCARDS);
kfree_skb(skb); kfree_skb_reason(skb, reason);
} }
static int ip6_input_finish(struct net *net, struct sock *sk, struct sk_buff *skb) static int ip6_input_finish(struct net *net, struct sock *sk, struct sk_buff *skb)
......
...@@ -469,6 +469,7 @@ int ip6_forward(struct sk_buff *skb) ...@@ -469,6 +469,7 @@ int ip6_forward(struct sk_buff *skb)
struct inet6_skb_parm *opt = IP6CB(skb); struct inet6_skb_parm *opt = IP6CB(skb);
struct net *net = dev_net(dst->dev); struct net *net = dev_net(dst->dev);
struct inet6_dev *idev; struct inet6_dev *idev;
SKB_DR(reason);
u32 mtu; u32 mtu;
idev = __in6_dev_get_safely(dev_get_by_index_rcu(net, IP6CB(skb)->iif)); idev = __in6_dev_get_safely(dev_get_by_index_rcu(net, IP6CB(skb)->iif));
...@@ -518,7 +519,7 @@ int ip6_forward(struct sk_buff *skb) ...@@ -518,7 +519,7 @@ int ip6_forward(struct sk_buff *skb)
icmpv6_send(skb, ICMPV6_TIME_EXCEED, ICMPV6_EXC_HOPLIMIT, 0); icmpv6_send(skb, ICMPV6_TIME_EXCEED, ICMPV6_EXC_HOPLIMIT, 0);
__IP6_INC_STATS(net, idev, IPSTATS_MIB_INHDRERRORS); __IP6_INC_STATS(net, idev, IPSTATS_MIB_INHDRERRORS);
kfree_skb(skb); kfree_skb_reason(skb, SKB_DROP_REASON_IP_INHDR);
return -ETIMEDOUT; return -ETIMEDOUT;
} }
...@@ -537,6 +538,7 @@ int ip6_forward(struct sk_buff *skb) ...@@ -537,6 +538,7 @@ int ip6_forward(struct sk_buff *skb)
if (!xfrm6_route_forward(skb)) { if (!xfrm6_route_forward(skb)) {
__IP6_INC_STATS(net, idev, IPSTATS_MIB_INDISCARDS); __IP6_INC_STATS(net, idev, IPSTATS_MIB_INDISCARDS);
SKB_DR_SET(reason, XFRM_POLICY);
goto drop; goto drop;
} }
dst = skb_dst(skb); dst = skb_dst(skb);
...@@ -596,7 +598,7 @@ int ip6_forward(struct sk_buff *skb) ...@@ -596,7 +598,7 @@ int ip6_forward(struct sk_buff *skb)
__IP6_INC_STATS(net, idev, IPSTATS_MIB_INTOOBIGERRORS); __IP6_INC_STATS(net, idev, IPSTATS_MIB_INTOOBIGERRORS);
__IP6_INC_STATS(net, ip6_dst_idev(dst), __IP6_INC_STATS(net, ip6_dst_idev(dst),
IPSTATS_MIB_FRAGFAILS); IPSTATS_MIB_FRAGFAILS);
kfree_skb(skb); kfree_skb_reason(skb, SKB_DROP_REASON_PKT_TOO_BIG);
return -EMSGSIZE; return -EMSGSIZE;
} }
...@@ -618,8 +620,9 @@ int ip6_forward(struct sk_buff *skb) ...@@ -618,8 +620,9 @@ int ip6_forward(struct sk_buff *skb)
error: error:
__IP6_INC_STATS(net, idev, IPSTATS_MIB_INADDRERRORS); __IP6_INC_STATS(net, idev, IPSTATS_MIB_INADDRERRORS);
SKB_DR_SET(reason, IP_INADDRERRORS);
drop: drop:
kfree_skb(skb); kfree_skb_reason(skb, reason);
return -EINVAL; return -EINVAL;
} }
......
...@@ -4482,6 +4482,7 @@ static int ip6_pkt_drop(struct sk_buff *skb, u8 code, int ipstats_mib_noroutes) ...@@ -4482,6 +4482,7 @@ static int ip6_pkt_drop(struct sk_buff *skb, u8 code, int ipstats_mib_noroutes)
struct dst_entry *dst = skb_dst(skb); struct dst_entry *dst = skb_dst(skb);
struct net *net = dev_net(dst->dev); struct net *net = dev_net(dst->dev);
struct inet6_dev *idev; struct inet6_dev *idev;
SKB_DR(reason);
int type; int type;
if (netif_is_l3_master(skb->dev) || if (netif_is_l3_master(skb->dev) ||
...@@ -4494,11 +4495,14 @@ static int ip6_pkt_drop(struct sk_buff *skb, u8 code, int ipstats_mib_noroutes) ...@@ -4494,11 +4495,14 @@ static int ip6_pkt_drop(struct sk_buff *skb, u8 code, int ipstats_mib_noroutes)
case IPSTATS_MIB_INNOROUTES: case IPSTATS_MIB_INNOROUTES:
type = ipv6_addr_type(&ipv6_hdr(skb)->daddr); type = ipv6_addr_type(&ipv6_hdr(skb)->daddr);
if (type == IPV6_ADDR_ANY) { if (type == IPV6_ADDR_ANY) {
SKB_DR_SET(reason, IP_INADDRERRORS);
IP6_INC_STATS(net, idev, IPSTATS_MIB_INADDRERRORS); IP6_INC_STATS(net, idev, IPSTATS_MIB_INADDRERRORS);
break; break;
} }
SKB_DR_SET(reason, IP_INNOROUTES);
fallthrough; fallthrough;
case IPSTATS_MIB_OUTNOROUTES: case IPSTATS_MIB_OUTNOROUTES:
SKB_DR_OR(reason, IP_OUTNOROUTES);
IP6_INC_STATS(net, idev, ipstats_mib_noroutes); IP6_INC_STATS(net, idev, ipstats_mib_noroutes);
break; break;
} }
...@@ -4508,7 +4512,7 @@ static int ip6_pkt_drop(struct sk_buff *skb, u8 code, int ipstats_mib_noroutes) ...@@ -4508,7 +4512,7 @@ static int ip6_pkt_drop(struct sk_buff *skb, u8 code, int ipstats_mib_noroutes)
skb_dst_drop(skb); skb_dst_drop(skb);
icmpv6_send(skb, ICMPV6_DEST_UNREACH, code, 0); icmpv6_send(skb, ICMPV6_DEST_UNREACH, code, 0);
kfree_skb(skb); kfree_skb_reason(skb, reason);
return 0; return 0;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment