Commit 750d019d authored by David S. Miller's avatar David S. Miller

Merge branch 'icmp-skb-reason'

Menglong Dong says:

====================
net: icmp: add skb drop reasons to icmp
In the commit c504e5c2 ("net: skb: introduce kfree_skb_reason()"),
we added the support of reporting the reasons of skb drops to kfree_skb
tracepoint. And in this series patches, reasons for skb drops are added
to ICMP protocol.

In order to report the reasons of skb drops in 'sock_queue_rcv_skb()',
the function 'sock_queue_rcv_skb_reason()' is introduced in the 1th
patch, which is used in the 3th patch.

As David Ahern suggested, the reasons for skb drops should be more
general and not be code based. Therefore, in the 2th patch,
SKB_DROP_REASON_PTYPE_ABSENT is renamed to
SKB_DROP_REASON_UNHANDLED_PROTO, which is used for the cases of no
L3 protocol handler, no L4 protocol handler, version extensions, etc.

In the 3th patch, we introduce the new function __ping_queue_rcv_skb()
to report drop reasons by its return value and keep the return value of
ping_queue_rcv_skb() still.

In the 4th patch, we make ICMP message handler functions return drop
reasons, which means we change the return type of 'handler()' in
'struct icmp_control' from 'bool' to 'enum skb_drop_reason'. This
changed its original intention, as 'false' means failure, but
'SKB_NOT_DROPPED_YET', which is 0, means success now. Therefore, we
have to change all usages of these handler. Following "handler"
functions are involved:

icmp_unreach()
icmp_redirect()
icmp_echo()
icmp_timestamp()
icmp_discard()

And following drop reasons are added(what they mean can be see
in the document for them):

SKB_DROP_REASON_ICMP_CSUM
SKB_DROP_REASON_INVALID_PROTO

The reason 'INVALID_PROTO' is introduced for the case that the packet
doesn't follow rfc 1122 and is dropped. I think this reason is different
from the 'UNHANDLED_PROTO', as the 'UNHANDLED_PROTO' means the packet is
fine, and it is just not supported. This is not a common case, and I
believe we can locate the problem from the data in the packet. For now,
this 'INVALID_PROTO' is used for the icmp broadcasts with wrong types.

Maybe there should be a document file for these reasons. For example,
list all the case that causes the 'INVALID_PROTO' drop reason. Therefore,
users can locate their problems according to the document.

Changes since v4:
- rename SKB_DROP_REASON_RFC_1122 to SKB_DROP_REASON_INVALID_PROTO

Changes since v3:
- rename SKB_DROP_REASON_PTYPE_ABSENT to SKB_DROP_REASON_UNHANDLED_PROTO
  in the 2th patch
- fix the return value problem of ping_queue_rcv_skb() in the 3th patch
- remove SKB_DROP_REASON_ICMP_TYPE and SKB_DROP_REASON_ICMP_BROADCAST
  and introduce the SKB_DROP_REASON_RFC_1122 in the 4th patch

Changes since v2:
- fix aliegnment problem in the 2th patch

Changes since v1:
- introduce __ping_queue_rcv_skb() instead of change the return value
  of ping_queue_rcv_skb() in the 2th patch, as Paolo suggested
====================
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents 516a2f1f b384c95a
...@@ -408,11 +408,9 @@ enum skb_drop_reason { ...@@ -408,11 +408,9 @@ enum skb_drop_reason {
*/ */
SKB_DROP_REASON_XDP, /* dropped by XDP in input path */ SKB_DROP_REASON_XDP, /* dropped by XDP in input path */
SKB_DROP_REASON_TC_INGRESS, /* dropped in TC ingress HOOK */ SKB_DROP_REASON_TC_INGRESS, /* dropped in TC ingress HOOK */
SKB_DROP_REASON_PTYPE_ABSENT, /* not packet_type found to handle SKB_DROP_REASON_UNHANDLED_PROTO, /* protocol not implemented
* the skb. For an etner packet, * or not supported
* this means that L3 protocol is */
* not supported
*/
SKB_DROP_REASON_SKB_CSUM, /* sk_buff checksum computation SKB_DROP_REASON_SKB_CSUM, /* sk_buff checksum computation
* error * error
*/ */
...@@ -444,6 +442,11 @@ enum skb_drop_reason { ...@@ -444,6 +442,11 @@ enum skb_drop_reason {
SKB_DROP_REASON_TAP_TXFILTER, /* dropped by tx filter implemented SKB_DROP_REASON_TAP_TXFILTER, /* dropped by tx filter implemented
* at tun/tap, e.g., check_filter() * at tun/tap, e.g., check_filter()
*/ */
SKB_DROP_REASON_ICMP_CSUM, /* ICMP checksum error */
SKB_DROP_REASON_INVALID_PROTO, /* the packet doesn't follow RFC
* 2211, such as a broadcasts
* ICMP_TIMESTAMP
*/
SKB_DROP_REASON_MAX, SKB_DROP_REASON_MAX,
}; };
......
...@@ -76,7 +76,7 @@ int ping_recvmsg(struct sock *sk, struct msghdr *msg, size_t len, int noblock, ...@@ -76,7 +76,7 @@ int ping_recvmsg(struct sock *sk, struct msghdr *msg, size_t len, int noblock,
int ping_common_sendmsg(int family, struct msghdr *msg, size_t len, int ping_common_sendmsg(int family, struct msghdr *msg, size_t len,
void *user_icmph, size_t icmph_len); void *user_icmph, size_t icmph_len);
int ping_queue_rcv_skb(struct sock *sk, struct sk_buff *skb); int ping_queue_rcv_skb(struct sock *sk, struct sk_buff *skb);
bool ping_rcv(struct sk_buff *skb); enum skb_drop_reason ping_rcv(struct sk_buff *skb);
#ifdef CONFIG_PROC_FS #ifdef CONFIG_PROC_FS
void *ping_seq_start(struct seq_file *seq, loff_t *pos, sa_family_t family); void *ping_seq_start(struct seq_file *seq, loff_t *pos, sa_family_t family);
......
...@@ -2392,7 +2392,14 @@ int __sk_queue_drop_skb(struct sock *sk, struct sk_buff_head *sk_queue, ...@@ -2392,7 +2392,14 @@ int __sk_queue_drop_skb(struct sock *sk, struct sk_buff_head *sk_queue,
void (*destructor)(struct sock *sk, void (*destructor)(struct sock *sk,
struct sk_buff *skb)); struct sk_buff *skb));
int __sock_queue_rcv_skb(struct sock *sk, struct sk_buff *skb); int __sock_queue_rcv_skb(struct sock *sk, struct sk_buff *skb);
int sock_queue_rcv_skb(struct sock *sk, struct sk_buff *skb);
int sock_queue_rcv_skb_reason(struct sock *sk, struct sk_buff *skb,
enum skb_drop_reason *reason);
static inline int sock_queue_rcv_skb(struct sock *sk, struct sk_buff *skb)
{
return sock_queue_rcv_skb_reason(sk, skb, NULL);
}
int sock_queue_err_skb(struct sock *sk, struct sk_buff *skb); int sock_queue_err_skb(struct sock *sk, struct sk_buff *skb);
struct sk_buff *sock_dequeue_err_skb(struct sock *sk); struct sk_buff *sock_dequeue_err_skb(struct sock *sk);
......
...@@ -50,7 +50,7 @@ ...@@ -50,7 +50,7 @@
EM(SKB_DROP_REASON_CPU_BACKLOG, CPU_BACKLOG) \ EM(SKB_DROP_REASON_CPU_BACKLOG, CPU_BACKLOG) \
EM(SKB_DROP_REASON_XDP, XDP) \ EM(SKB_DROP_REASON_XDP, XDP) \
EM(SKB_DROP_REASON_TC_INGRESS, TC_INGRESS) \ EM(SKB_DROP_REASON_TC_INGRESS, TC_INGRESS) \
EM(SKB_DROP_REASON_PTYPE_ABSENT, PTYPE_ABSENT) \ EM(SKB_DROP_REASON_UNHANDLED_PROTO, UNHANDLED_PROTO) \
EM(SKB_DROP_REASON_SKB_CSUM, SKB_CSUM) \ EM(SKB_DROP_REASON_SKB_CSUM, SKB_CSUM) \
EM(SKB_DROP_REASON_SKB_GSO_SEG, SKB_GSO_SEG) \ EM(SKB_DROP_REASON_SKB_GSO_SEG, SKB_GSO_SEG) \
EM(SKB_DROP_REASON_SKB_UCOPY_FAULT, SKB_UCOPY_FAULT) \ EM(SKB_DROP_REASON_SKB_UCOPY_FAULT, SKB_UCOPY_FAULT) \
...@@ -61,6 +61,8 @@ ...@@ -61,6 +61,8 @@
EM(SKB_DROP_REASON_HDR_TRUNC, HDR_TRUNC) \ EM(SKB_DROP_REASON_HDR_TRUNC, HDR_TRUNC) \
EM(SKB_DROP_REASON_TAP_FILTER, TAP_FILTER) \ EM(SKB_DROP_REASON_TAP_FILTER, TAP_FILTER) \
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_INVALID_PROTO, INVALID_PROTO) \
EMe(SKB_DROP_REASON_MAX, MAX) EMe(SKB_DROP_REASON_MAX, MAX)
#undef EM #undef EM
......
...@@ -5375,13 +5375,11 @@ static int __netif_receive_skb_core(struct sk_buff **pskb, bool pfmemalloc, ...@@ -5375,13 +5375,11 @@ static int __netif_receive_skb_core(struct sk_buff **pskb, bool pfmemalloc,
*ppt_prev = pt_prev; *ppt_prev = pt_prev;
} else { } else {
drop: drop:
if (!deliver_exact) { if (!deliver_exact)
dev_core_stats_rx_dropped_inc(skb->dev); dev_core_stats_rx_dropped_inc(skb->dev);
kfree_skb_reason(skb, SKB_DROP_REASON_PTYPE_ABSENT); else
} else {
dev_core_stats_rx_nohandler_inc(skb->dev); dev_core_stats_rx_nohandler_inc(skb->dev);
kfree_skb(skb); kfree_skb_reason(skb, SKB_DROP_REASON_UNHANDLED_PROTO);
}
/* Jamal, now you will not able to escape explaining /* Jamal, now you will not able to escape explaining
* me how you were going to use this. :-) * me how you were going to use this. :-)
*/ */
......
...@@ -505,17 +505,35 @@ int __sock_queue_rcv_skb(struct sock *sk, struct sk_buff *skb) ...@@ -505,17 +505,35 @@ int __sock_queue_rcv_skb(struct sock *sk, struct sk_buff *skb)
} }
EXPORT_SYMBOL(__sock_queue_rcv_skb); EXPORT_SYMBOL(__sock_queue_rcv_skb);
int sock_queue_rcv_skb(struct sock *sk, struct sk_buff *skb) int sock_queue_rcv_skb_reason(struct sock *sk, struct sk_buff *skb,
enum skb_drop_reason *reason)
{ {
enum skb_drop_reason drop_reason;
int err; int err;
err = sk_filter(sk, skb); err = sk_filter(sk, skb);
if (err) if (err) {
return err; drop_reason = SKB_DROP_REASON_SOCKET_FILTER;
goto out;
return __sock_queue_rcv_skb(sk, skb); }
err = __sock_queue_rcv_skb(sk, skb);
switch (err) {
case -ENOMEM:
drop_reason = SKB_DROP_REASON_SOCKET_RCVBUFF;
break;
case -ENOBUFS:
drop_reason = SKB_DROP_REASON_PROTO_MEM;
break;
default:
drop_reason = SKB_NOT_DROPPED_YET;
break;
}
out:
if (reason)
*reason = drop_reason;
return err;
} }
EXPORT_SYMBOL(sock_queue_rcv_skb); EXPORT_SYMBOL(sock_queue_rcv_skb_reason);
int __sk_receive_skb(struct sock *sk, struct sk_buff *skb, int __sk_receive_skb(struct sock *sk, struct sk_buff *skb,
const int nested, unsigned int trim_cap, bool refcounted) const int nested, unsigned int trim_cap, bool refcounted)
......
...@@ -186,7 +186,7 @@ EXPORT_SYMBOL(icmp_err_convert); ...@@ -186,7 +186,7 @@ EXPORT_SYMBOL(icmp_err_convert);
*/ */
struct icmp_control { struct icmp_control {
bool (*handler)(struct sk_buff *skb); enum skb_drop_reason (*handler)(struct sk_buff *skb);
short error; /* This ICMP is classed as an error message */ short error; /* This ICMP is classed as an error message */
}; };
...@@ -839,8 +839,9 @@ static bool icmp_tag_validation(int proto) ...@@ -839,8 +839,9 @@ static bool icmp_tag_validation(int proto)
* ICMP_PARAMETERPROB. * ICMP_PARAMETERPROB.
*/ */
static bool icmp_unreach(struct sk_buff *skb) static enum skb_drop_reason icmp_unreach(struct sk_buff *skb)
{ {
enum skb_drop_reason reason = SKB_NOT_DROPPED_YET;
const struct iphdr *iph; const struct iphdr *iph;
struct icmphdr *icmph; struct icmphdr *icmph;
struct net *net; struct net *net;
...@@ -860,8 +861,10 @@ static bool icmp_unreach(struct sk_buff *skb) ...@@ -860,8 +861,10 @@ static bool icmp_unreach(struct sk_buff *skb)
icmph = icmp_hdr(skb); icmph = icmp_hdr(skb);
iph = (const struct iphdr *)skb->data; iph = (const struct iphdr *)skb->data;
if (iph->ihl < 5) /* Mangled header, drop. */ if (iph->ihl < 5) { /* Mangled header, drop. */
reason = SKB_DROP_REASON_IP_INHDR;
goto out_err; goto out_err;
}
switch (icmph->type) { switch (icmph->type) {
case ICMP_DEST_UNREACH: case ICMP_DEST_UNREACH:
...@@ -941,10 +944,10 @@ static bool icmp_unreach(struct sk_buff *skb) ...@@ -941,10 +944,10 @@ static bool icmp_unreach(struct sk_buff *skb)
icmp_socket_deliver(skb, info); icmp_socket_deliver(skb, info);
out: out:
return true; return reason;
out_err: out_err:
__ICMP_INC_STATS(net, ICMP_MIB_INERRORS); __ICMP_INC_STATS(net, ICMP_MIB_INERRORS);
return false; return reason ?: SKB_DROP_REASON_NOT_SPECIFIED;
} }
...@@ -952,20 +955,20 @@ static bool icmp_unreach(struct sk_buff *skb) ...@@ -952,20 +955,20 @@ static bool icmp_unreach(struct sk_buff *skb)
* Handle ICMP_REDIRECT. * Handle ICMP_REDIRECT.
*/ */
static bool icmp_redirect(struct sk_buff *skb) static enum skb_drop_reason icmp_redirect(struct sk_buff *skb)
{ {
if (skb->len < sizeof(struct iphdr)) { if (skb->len < sizeof(struct iphdr)) {
__ICMP_INC_STATS(dev_net(skb->dev), ICMP_MIB_INERRORS); __ICMP_INC_STATS(dev_net(skb->dev), ICMP_MIB_INERRORS);
return false; return SKB_DROP_REASON_PKT_TOO_SMALL;
} }
if (!pskb_may_pull(skb, sizeof(struct iphdr))) { if (!pskb_may_pull(skb, sizeof(struct iphdr))) {
/* there aught to be a stat */ /* there aught to be a stat */
return false; return SKB_DROP_REASON_NOMEM;
} }
icmp_socket_deliver(skb, ntohl(icmp_hdr(skb)->un.gateway)); icmp_socket_deliver(skb, ntohl(icmp_hdr(skb)->un.gateway));
return true; return SKB_NOT_DROPPED_YET;
} }
/* /*
...@@ -982,7 +985,7 @@ static bool icmp_redirect(struct sk_buff *skb) ...@@ -982,7 +985,7 @@ static bool icmp_redirect(struct sk_buff *skb)
* See also WRT handling of options once they are done and working. * See also WRT handling of options once they are done and working.
*/ */
static bool icmp_echo(struct sk_buff *skb) static enum skb_drop_reason icmp_echo(struct sk_buff *skb)
{ {
struct icmp_bxm icmp_param; struct icmp_bxm icmp_param;
struct net *net; struct net *net;
...@@ -990,7 +993,7 @@ static bool icmp_echo(struct sk_buff *skb) ...@@ -990,7 +993,7 @@ static bool icmp_echo(struct sk_buff *skb)
net = dev_net(skb_dst(skb)->dev); net = dev_net(skb_dst(skb)->dev);
/* should there be an ICMP stat for ignored echos? */ /* should there be an ICMP stat for ignored echos? */
if (net->ipv4.sysctl_icmp_echo_ignore_all) if (net->ipv4.sysctl_icmp_echo_ignore_all)
return true; return SKB_NOT_DROPPED_YET;
icmp_param.data.icmph = *icmp_hdr(skb); icmp_param.data.icmph = *icmp_hdr(skb);
icmp_param.skb = skb; icmp_param.skb = skb;
...@@ -1001,10 +1004,10 @@ static bool icmp_echo(struct sk_buff *skb) ...@@ -1001,10 +1004,10 @@ static bool icmp_echo(struct sk_buff *skb)
if (icmp_param.data.icmph.type == ICMP_ECHO) if (icmp_param.data.icmph.type == ICMP_ECHO)
icmp_param.data.icmph.type = ICMP_ECHOREPLY; icmp_param.data.icmph.type = ICMP_ECHOREPLY;
else if (!icmp_build_probe(skb, &icmp_param.data.icmph)) else if (!icmp_build_probe(skb, &icmp_param.data.icmph))
return true; return SKB_NOT_DROPPED_YET;
icmp_reply(&icmp_param, skb); icmp_reply(&icmp_param, skb);
return true; return SKB_NOT_DROPPED_YET;
} }
/* Helper for icmp_echo and icmpv6_echo_reply. /* Helper for icmp_echo and icmpv6_echo_reply.
...@@ -1122,7 +1125,7 @@ EXPORT_SYMBOL_GPL(icmp_build_probe); ...@@ -1122,7 +1125,7 @@ EXPORT_SYMBOL_GPL(icmp_build_probe);
* MUST be accurate to a few minutes. * MUST be accurate to a few minutes.
* MUST be updated at least at 15Hz. * MUST be updated at least at 15Hz.
*/ */
static bool icmp_timestamp(struct sk_buff *skb) static enum skb_drop_reason icmp_timestamp(struct sk_buff *skb)
{ {
struct icmp_bxm icmp_param; struct icmp_bxm icmp_param;
/* /*
...@@ -1147,17 +1150,17 @@ static bool icmp_timestamp(struct sk_buff *skb) ...@@ -1147,17 +1150,17 @@ static bool icmp_timestamp(struct sk_buff *skb)
icmp_param.data_len = 0; icmp_param.data_len = 0;
icmp_param.head_len = sizeof(struct icmphdr) + 12; icmp_param.head_len = sizeof(struct icmphdr) + 12;
icmp_reply(&icmp_param, skb); icmp_reply(&icmp_param, skb);
return true; return SKB_NOT_DROPPED_YET;
out_err: out_err:
__ICMP_INC_STATS(dev_net(skb_dst(skb)->dev), ICMP_MIB_INERRORS); __ICMP_INC_STATS(dev_net(skb_dst(skb)->dev), ICMP_MIB_INERRORS);
return false; return SKB_DROP_REASON_PKT_TOO_SMALL;
} }
static bool icmp_discard(struct sk_buff *skb) static enum skb_drop_reason icmp_discard(struct sk_buff *skb)
{ {
/* pretend it was a success */ /* pretend it was a success */
return true; return SKB_NOT_DROPPED_YET;
} }
/* /*
...@@ -1165,18 +1168,20 @@ static bool icmp_discard(struct sk_buff *skb) ...@@ -1165,18 +1168,20 @@ static bool icmp_discard(struct sk_buff *skb)
*/ */
int icmp_rcv(struct sk_buff *skb) int icmp_rcv(struct sk_buff *skb)
{ {
struct icmphdr *icmph; enum skb_drop_reason reason = SKB_DROP_REASON_NOT_SPECIFIED;
struct rtable *rt = skb_rtable(skb); struct rtable *rt = skb_rtable(skb);
struct net *net = dev_net(rt->dst.dev); struct net *net = dev_net(rt->dst.dev);
bool success; struct icmphdr *icmph;
if (!xfrm4_policy_check(NULL, XFRM_POLICY_IN, skb)) { if (!xfrm4_policy_check(NULL, XFRM_POLICY_IN, skb)) {
struct sec_path *sp = skb_sec_path(skb); struct sec_path *sp = skb_sec_path(skb);
int nh; int nh;
if (!(sp && sp->xvec[sp->len - 1]->props.flags & if (!(sp && sp->xvec[sp->len - 1]->props.flags &
XFRM_STATE_ICMP)) XFRM_STATE_ICMP)) {
reason = SKB_DROP_REASON_XFRM_POLICY;
goto drop; goto drop;
}
if (!pskb_may_pull(skb, sizeof(*icmph) + sizeof(struct iphdr))) if (!pskb_may_pull(skb, sizeof(*icmph) + sizeof(struct iphdr)))
goto drop; goto drop;
...@@ -1184,8 +1189,11 @@ int icmp_rcv(struct sk_buff *skb) ...@@ -1184,8 +1189,11 @@ int icmp_rcv(struct sk_buff *skb)
nh = skb_network_offset(skb); nh = skb_network_offset(skb);
skb_set_network_header(skb, sizeof(*icmph)); skb_set_network_header(skb, sizeof(*icmph));
if (!xfrm4_policy_check_reverse(NULL, XFRM_POLICY_IN, skb)) if (!xfrm4_policy_check_reverse(NULL, XFRM_POLICY_IN,
skb)) {
reason = SKB_DROP_REASON_XFRM_POLICY;
goto drop; goto drop;
}
skb_set_network_header(skb, nh); skb_set_network_header(skb, nh);
} }
...@@ -1207,13 +1215,13 @@ int icmp_rcv(struct sk_buff *skb) ...@@ -1207,13 +1215,13 @@ int icmp_rcv(struct sk_buff *skb)
/* We can't use icmp_pointers[].handler() because it is an array of /* We can't use icmp_pointers[].handler() because it is an array of
* size NR_ICMP_TYPES + 1 (19 elements) and PROBE has code 42. * size NR_ICMP_TYPES + 1 (19 elements) and PROBE has code 42.
*/ */
success = icmp_echo(skb); reason = icmp_echo(skb);
goto success_check; goto reason_check;
} }
if (icmph->type == ICMP_EXT_ECHOREPLY) { if (icmph->type == ICMP_EXT_ECHOREPLY) {
success = ping_rcv(skb); reason = ping_rcv(skb);
goto success_check; goto reason_check;
} }
/* /*
...@@ -1222,8 +1230,10 @@ int icmp_rcv(struct sk_buff *skb) ...@@ -1222,8 +1230,10 @@ int icmp_rcv(struct sk_buff *skb)
* RFC 1122: 3.2.2 Unknown ICMP messages types MUST be silently * RFC 1122: 3.2.2 Unknown ICMP messages types MUST be silently
* discarded. * discarded.
*/ */
if (icmph->type > NR_ICMP_TYPES) if (icmph->type > NR_ICMP_TYPES) {
reason = SKB_DROP_REASON_UNHANDLED_PROTO;
goto error; goto error;
}
/* /*
* Parse the ICMP message * Parse the ICMP message
...@@ -1239,27 +1249,30 @@ int icmp_rcv(struct sk_buff *skb) ...@@ -1239,27 +1249,30 @@ int icmp_rcv(struct sk_buff *skb)
if ((icmph->type == ICMP_ECHO || if ((icmph->type == ICMP_ECHO ||
icmph->type == ICMP_TIMESTAMP) && icmph->type == ICMP_TIMESTAMP) &&
net->ipv4.sysctl_icmp_echo_ignore_broadcasts) { net->ipv4.sysctl_icmp_echo_ignore_broadcasts) {
reason = SKB_DROP_REASON_INVALID_PROTO;
goto error; goto error;
} }
if (icmph->type != ICMP_ECHO && if (icmph->type != ICMP_ECHO &&
icmph->type != ICMP_TIMESTAMP && icmph->type != ICMP_TIMESTAMP &&
icmph->type != ICMP_ADDRESS && icmph->type != ICMP_ADDRESS &&
icmph->type != ICMP_ADDRESSREPLY) { icmph->type != ICMP_ADDRESSREPLY) {
reason = SKB_DROP_REASON_INVALID_PROTO;
goto error; goto error;
} }
} }
success = icmp_pointers[icmph->type].handler(skb); reason = icmp_pointers[icmph->type].handler(skb);
success_check: reason_check:
if (success) { if (!reason) {
consume_skb(skb); consume_skb(skb);
return NET_RX_SUCCESS; return NET_RX_SUCCESS;
} }
drop: drop:
kfree_skb(skb); kfree_skb_reason(skb, reason);
return NET_RX_DROP; return NET_RX_DROP;
csum_error: csum_error:
reason = SKB_DROP_REASON_ICMP_CSUM;
__ICMP_INC_STATS(net, ICMP_MIB_CSUMERRORS); __ICMP_INC_STATS(net, ICMP_MIB_CSUMERRORS);
error: error:
__ICMP_INC_STATS(net, ICMP_MIB_INERRORS); __ICMP_INC_STATS(net, ICMP_MIB_INERRORS);
......
...@@ -935,16 +935,24 @@ int ping_recvmsg(struct sock *sk, struct msghdr *msg, size_t len, int noblock, ...@@ -935,16 +935,24 @@ int ping_recvmsg(struct sock *sk, struct msghdr *msg, size_t len, int noblock,
} }
EXPORT_SYMBOL_GPL(ping_recvmsg); EXPORT_SYMBOL_GPL(ping_recvmsg);
int ping_queue_rcv_skb(struct sock *sk, struct sk_buff *skb) static enum skb_drop_reason __ping_queue_rcv_skb(struct sock *sk,
struct sk_buff *skb)
{ {
enum skb_drop_reason reason;
pr_debug("ping_queue_rcv_skb(sk=%p,sk->num=%d,skb=%p)\n", pr_debug("ping_queue_rcv_skb(sk=%p,sk->num=%d,skb=%p)\n",
inet_sk(sk), inet_sk(sk)->inet_num, skb); inet_sk(sk), inet_sk(sk)->inet_num, skb);
if (sock_queue_rcv_skb(sk, skb) < 0) { if (sock_queue_rcv_skb_reason(sk, skb, &reason) < 0) {
kfree_skb(skb); kfree_skb_reason(skb, reason);
pr_debug("ping_queue_rcv_skb -> failed\n"); pr_debug("ping_queue_rcv_skb -> failed\n");
return -1; return reason;
} }
return 0; return SKB_NOT_DROPPED_YET;
}
int ping_queue_rcv_skb(struct sock *sk, struct sk_buff *skb)
{
return __ping_queue_rcv_skb(sk, skb) ? -1 : 0;
} }
EXPORT_SYMBOL_GPL(ping_queue_rcv_skb); EXPORT_SYMBOL_GPL(ping_queue_rcv_skb);
...@@ -953,12 +961,12 @@ EXPORT_SYMBOL_GPL(ping_queue_rcv_skb); ...@@ -953,12 +961,12 @@ EXPORT_SYMBOL_GPL(ping_queue_rcv_skb);
* All we need to do is get the socket. * All we need to do is get the socket.
*/ */
bool ping_rcv(struct sk_buff *skb) enum skb_drop_reason ping_rcv(struct sk_buff *skb)
{ {
enum skb_drop_reason reason = SKB_DROP_REASON_NO_SOCKET;
struct sock *sk; struct sock *sk;
struct net *net = dev_net(skb->dev); struct net *net = dev_net(skb->dev);
struct icmphdr *icmph = icmp_hdr(skb); struct icmphdr *icmph = icmp_hdr(skb);
bool rc = false;
/* We assume the packet has already been checked by icmp_rcv */ /* We assume the packet has already been checked by icmp_rcv */
...@@ -973,15 +981,17 @@ bool ping_rcv(struct sk_buff *skb) ...@@ -973,15 +981,17 @@ bool ping_rcv(struct sk_buff *skb)
struct sk_buff *skb2 = skb_clone(skb, GFP_ATOMIC); struct sk_buff *skb2 = skb_clone(skb, GFP_ATOMIC);
pr_debug("rcv on socket %p\n", sk); pr_debug("rcv on socket %p\n", sk);
if (skb2 && !ping_queue_rcv_skb(sk, skb2)) if (skb2)
rc = true; reason = __ping_queue_rcv_skb(sk, skb2);
else
reason = SKB_DROP_REASON_NOMEM;
sock_put(sk); sock_put(sk);
} }
if (!rc) if (reason)
pr_debug("no socket, dropping\n"); pr_debug("no socket, dropping\n");
return rc; return reason;
} }
EXPORT_SYMBOL_GPL(ping_rcv); EXPORT_SYMBOL_GPL(ping_rcv);
......
...@@ -864,21 +864,23 @@ void icmpv6_notify(struct sk_buff *skb, u8 type, u8 code, __be32 info) ...@@ -864,21 +864,23 @@ void icmpv6_notify(struct sk_buff *skb, u8 type, u8 code, __be32 info)
static int icmpv6_rcv(struct sk_buff *skb) static int icmpv6_rcv(struct sk_buff *skb)
{ {
enum skb_drop_reason reason = SKB_DROP_REASON_NOT_SPECIFIED;
struct net *net = dev_net(skb->dev); struct net *net = dev_net(skb->dev);
struct net_device *dev = icmp6_dev(skb); struct net_device *dev = icmp6_dev(skb);
struct inet6_dev *idev = __in6_dev_get(dev); struct inet6_dev *idev = __in6_dev_get(dev);
const struct in6_addr *saddr, *daddr; const struct in6_addr *saddr, *daddr;
struct icmp6hdr *hdr; struct icmp6hdr *hdr;
u8 type; u8 type;
bool success = false;
if (!xfrm6_policy_check(NULL, XFRM_POLICY_IN, skb)) { if (!xfrm6_policy_check(NULL, XFRM_POLICY_IN, skb)) {
struct sec_path *sp = skb_sec_path(skb); struct sec_path *sp = skb_sec_path(skb);
int nh; int nh;
if (!(sp && sp->xvec[sp->len - 1]->props.flags & if (!(sp && sp->xvec[sp->len - 1]->props.flags &
XFRM_STATE_ICMP)) XFRM_STATE_ICMP)) {
reason = SKB_DROP_REASON_XFRM_POLICY;
goto drop_no_count; goto drop_no_count;
}
if (!pskb_may_pull(skb, sizeof(*hdr) + sizeof(struct ipv6hdr))) if (!pskb_may_pull(skb, sizeof(*hdr) + sizeof(struct ipv6hdr)))
goto drop_no_count; goto drop_no_count;
...@@ -886,8 +888,11 @@ static int icmpv6_rcv(struct sk_buff *skb) ...@@ -886,8 +888,11 @@ static int icmpv6_rcv(struct sk_buff *skb)
nh = skb_network_offset(skb); nh = skb_network_offset(skb);
skb_set_network_header(skb, sizeof(*hdr)); skb_set_network_header(skb, sizeof(*hdr));
if (!xfrm6_policy_check_reverse(NULL, XFRM_POLICY_IN, skb)) if (!xfrm6_policy_check_reverse(NULL, XFRM_POLICY_IN,
skb)) {
reason = SKB_DROP_REASON_XFRM_POLICY;
goto drop_no_count; goto drop_no_count;
}
skb_set_network_header(skb, nh); skb_set_network_header(skb, nh);
} }
...@@ -924,11 +929,11 @@ static int icmpv6_rcv(struct sk_buff *skb) ...@@ -924,11 +929,11 @@ static int icmpv6_rcv(struct sk_buff *skb)
break; break;
case ICMPV6_ECHO_REPLY: case ICMPV6_ECHO_REPLY:
success = ping_rcv(skb); reason = ping_rcv(skb);
break; break;
case ICMPV6_EXT_ECHO_REPLY: case ICMPV6_EXT_ECHO_REPLY:
success = ping_rcv(skb); reason = ping_rcv(skb);
break; break;
case ICMPV6_PKT_TOOBIG: case ICMPV6_PKT_TOOBIG:
...@@ -994,19 +999,20 @@ static int icmpv6_rcv(struct sk_buff *skb) ...@@ -994,19 +999,20 @@ static int icmpv6_rcv(struct sk_buff *skb)
/* until the v6 path can be better sorted assume failure and /* until the v6 path can be better sorted assume failure and
* preserve the status quo behaviour for the rest of the paths to here * preserve the status quo behaviour for the rest of the paths to here
*/ */
if (success) if (reason)
consume_skb(skb); kfree_skb_reason(skb, reason);
else else
kfree_skb(skb); consume_skb(skb);
return 0; return 0;
csum_error: csum_error:
reason = SKB_DROP_REASON_ICMP_CSUM;
__ICMP6_INC_STATS(dev_net(dev), idev, ICMP6_MIB_CSUMERRORS); __ICMP6_INC_STATS(dev_net(dev), idev, ICMP6_MIB_CSUMERRORS);
discard_it: discard_it:
__ICMP6_INC_STATS(dev_net(dev), idev, ICMP6_MIB_INERRORS); __ICMP6_INC_STATS(dev_net(dev), idev, ICMP6_MIB_INERRORS);
drop_no_count: drop_no_count:
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