Commit 64816aa7 authored by Jakub Kicinski's avatar Jakub Kicinski

Merge branch 'ipv6-more-drop-reason'

Eric Dumazet says:

====================
ipv6: more drop reason

Add more drop reasons to IPv6:

 - IPV6_BAD_EXTHDR
 - IPV6_NDISC_FRAG
 - IPV6_NDISC_HOP_LIMIT
 - IPV6_NDISC_BAD_CODE
====================

Link: https://lore.kernel.org/r/20230210184708.2172562-1-edumazet@google.comSigned-off-by: default avatarJakub Kicinski <kuba@kernel.org>
parents 1a940b00 545dbcd1
...@@ -2631,13 +2631,24 @@ void *skb_pull_data(struct sk_buff *skb, size_t len); ...@@ -2631,13 +2631,24 @@ void *skb_pull_data(struct sk_buff *skb, size_t len);
void *__pskb_pull_tail(struct sk_buff *skb, int delta); void *__pskb_pull_tail(struct sk_buff *skb, int delta);
static inline bool pskb_may_pull(struct sk_buff *skb, unsigned int len) static inline enum skb_drop_reason
pskb_may_pull_reason(struct sk_buff *skb, unsigned int len)
{ {
if (likely(len <= skb_headlen(skb))) if (likely(len <= skb_headlen(skb)))
return true; return SKB_NOT_DROPPED_YET;
if (unlikely(len > skb->len)) if (unlikely(len > skb->len))
return false; return SKB_DROP_REASON_PKT_TOO_SMALL;
return __pskb_pull_tail(skb, len - skb_headlen(skb)) != NULL;
if (unlikely(!__pskb_pull_tail(skb, len - skb_headlen(skb))))
return SKB_DROP_REASON_NOMEM;
return SKB_NOT_DROPPED_YET;
}
static inline bool pskb_may_pull(struct sk_buff *skb, unsigned int len)
{
return pskb_may_pull_reason(skb, len) == SKB_NOT_DROPPED_YET;
} }
static inline void *pskb_pull(struct sk_buff *skb, unsigned int len) static inline void *pskb_pull(struct sk_buff *skb, unsigned int len)
......
...@@ -72,6 +72,10 @@ ...@@ -72,6 +72,10 @@
FN(FRAG_REASM_TIMEOUT) \ FN(FRAG_REASM_TIMEOUT) \
FN(FRAG_TOO_FAR) \ FN(FRAG_TOO_FAR) \
FN(TCP_MINTTL) \ FN(TCP_MINTTL) \
FN(IPV6_BAD_EXTHDR) \
FN(IPV6_NDISC_FRAG) \
FN(IPV6_NDISC_HOP_LIMIT) \
FN(IPV6_NDISC_BAD_CODE) \
FNe(MAX) FNe(MAX)
/** /**
...@@ -318,6 +322,14 @@ enum skb_drop_reason { ...@@ -318,6 +322,14 @@ enum skb_drop_reason {
* the threshold (IP_MINTTL or IPV6_MINHOPCOUNT). * the threshold (IP_MINTTL or IPV6_MINHOPCOUNT).
*/ */
SKB_DROP_REASON_TCP_MINTTL, SKB_DROP_REASON_TCP_MINTTL,
/** @SKB_DROP_REASON_IPV6_BAD_EXTHDR: Bad IPv6 extension header. */
SKB_DROP_REASON_IPV6_BAD_EXTHDR,
/** @SKB_DROP_REASON_IPV6_NDISC_FRAG: invalid frag (suppress_frag_ndisc). */
SKB_DROP_REASON_IPV6_NDISC_FRAG,
/** @SKB_DROP_REASON_IPV6_NDISC_HOP_LIMIT: invalid hop limit. */
SKB_DROP_REASON_IPV6_NDISC_HOP_LIMIT,
/** @SKB_DROP_REASON_IPV6_NDISC_BAD_CODE: invalid NDISC icmp6 code. */
SKB_DROP_REASON_IPV6_NDISC_BAD_CODE,
/** /**
* @SKB_DROP_REASON_MAX: the maximum of drop reason, which shouldn't be * @SKB_DROP_REASON_MAX: the maximum of drop reason, which shouldn't be
* used as a real 'reason' * used as a real 'reason'
......
...@@ -436,7 +436,8 @@ static inline void fl6_sock_release(struct ip6_flowlabel *fl) ...@@ -436,7 +436,8 @@ static inline void fl6_sock_release(struct ip6_flowlabel *fl)
atomic_dec(&fl->users); atomic_dec(&fl->users);
} }
void icmpv6_notify(struct sk_buff *skb, u8 type, u8 code, __be32 info); enum skb_drop_reason icmpv6_notify(struct sk_buff *skb, u8 type,
u8 code, __be32 info);
void icmpv6_push_pending_frames(struct sock *sk, struct flowi6 *fl6, void icmpv6_push_pending_frames(struct sock *sk, struct flowi6 *fl6,
struct icmp6hdr *thdr, int len); struct icmp6hdr *thdr, int len);
......
...@@ -445,7 +445,7 @@ int ndisc_late_init(void); ...@@ -445,7 +445,7 @@ int ndisc_late_init(void);
void ndisc_late_cleanup(void); void ndisc_late_cleanup(void);
void ndisc_cleanup(void); void ndisc_cleanup(void);
int ndisc_rcv(struct sk_buff *skb); enum skb_drop_reason ndisc_rcv(struct sk_buff *skb);
struct sk_buff *ndisc_ns_create(struct net_device *dev, const struct in6_addr *solicit, struct sk_buff *ndisc_ns_create(struct net_device *dev, const struct in6_addr *solicit,
const struct in6_addr *saddr, u64 nonce); const struct in6_addr *saddr, u64 nonce);
......
...@@ -813,16 +813,19 @@ static void icmpv6_echo_reply(struct sk_buff *skb) ...@@ -813,16 +813,19 @@ static void icmpv6_echo_reply(struct sk_buff *skb)
local_bh_enable(); local_bh_enable();
} }
void icmpv6_notify(struct sk_buff *skb, u8 type, u8 code, __be32 info) enum skb_drop_reason icmpv6_notify(struct sk_buff *skb, u8 type,
u8 code, __be32 info)
{ {
struct inet6_skb_parm *opt = IP6CB(skb); struct inet6_skb_parm *opt = IP6CB(skb);
struct net *net = dev_net(skb->dev);
const struct inet6_protocol *ipprot; const struct inet6_protocol *ipprot;
enum skb_drop_reason reason;
int inner_offset; int inner_offset;
__be16 frag_off; __be16 frag_off;
u8 nexthdr; u8 nexthdr;
struct net *net = dev_net(skb->dev);
if (!pskb_may_pull(skb, sizeof(struct ipv6hdr))) reason = pskb_may_pull_reason(skb, sizeof(struct ipv6hdr));
if (reason != SKB_NOT_DROPPED_YET)
goto out; goto out;
seg6_icmp_srh(skb, opt); seg6_icmp_srh(skb, opt);
...@@ -832,14 +835,17 @@ void icmpv6_notify(struct sk_buff *skb, u8 type, u8 code, __be32 info) ...@@ -832,14 +835,17 @@ void icmpv6_notify(struct sk_buff *skb, u8 type, u8 code, __be32 info)
/* now skip over extension headers */ /* now skip over extension headers */
inner_offset = ipv6_skip_exthdr(skb, sizeof(struct ipv6hdr), inner_offset = ipv6_skip_exthdr(skb, sizeof(struct ipv6hdr),
&nexthdr, &frag_off); &nexthdr, &frag_off);
if (inner_offset < 0) if (inner_offset < 0) {
SKB_DR_SET(reason, IPV6_BAD_EXTHDR);
goto out; goto out;
}
} else { } else {
inner_offset = sizeof(struct ipv6hdr); inner_offset = sizeof(struct ipv6hdr);
} }
/* Checkin header including 8 bytes of inner protocol header. */ /* Checkin header including 8 bytes of inner protocol header. */
if (!pskb_may_pull(skb, inner_offset+8)) reason = pskb_may_pull_reason(skb, inner_offset + 8);
if (reason != SKB_NOT_DROPPED_YET)
goto out; goto out;
/* BUGGG_FUTURE: we should try to parse exthdrs in this packet. /* BUGGG_FUTURE: we should try to parse exthdrs in this packet.
...@@ -854,10 +860,11 @@ void icmpv6_notify(struct sk_buff *skb, u8 type, u8 code, __be32 info) ...@@ -854,10 +860,11 @@ void icmpv6_notify(struct sk_buff *skb, u8 type, u8 code, __be32 info)
ipprot->err_handler(skb, opt, type, code, inner_offset, info); ipprot->err_handler(skb, opt, type, code, inner_offset, info);
raw6_icmp_error(skb, nexthdr, type, code, inner_offset, info); raw6_icmp_error(skb, nexthdr, type, code, inner_offset, info);
return; return SKB_CONSUMED;
out: out:
__ICMP6_INC_STATS(net, __in6_dev_get(skb->dev), ICMP6_MIB_INERRORS); __ICMP6_INC_STATS(net, __in6_dev_get(skb->dev), ICMP6_MIB_INERRORS);
return reason;
} }
/* /*
...@@ -953,7 +960,8 @@ static int icmpv6_rcv(struct sk_buff *skb) ...@@ -953,7 +960,8 @@ static int icmpv6_rcv(struct sk_buff *skb)
case ICMPV6_DEST_UNREACH: case ICMPV6_DEST_UNREACH:
case ICMPV6_TIME_EXCEED: case ICMPV6_TIME_EXCEED:
case ICMPV6_PARAMPROB: case ICMPV6_PARAMPROB:
icmpv6_notify(skb, type, hdr->icmp6_code, hdr->icmp6_mtu); reason = icmpv6_notify(skb, type, hdr->icmp6_code,
hdr->icmp6_mtu);
break; break;
case NDISC_ROUTER_SOLICITATION: case NDISC_ROUTER_SOLICITATION:
...@@ -961,7 +969,7 @@ static int icmpv6_rcv(struct sk_buff *skb) ...@@ -961,7 +969,7 @@ static int icmpv6_rcv(struct sk_buff *skb)
case NDISC_NEIGHBOUR_SOLICITATION: case NDISC_NEIGHBOUR_SOLICITATION:
case NDISC_NEIGHBOUR_ADVERTISEMENT: case NDISC_NEIGHBOUR_ADVERTISEMENT:
case NDISC_REDIRECT: case NDISC_REDIRECT:
ndisc_rcv(skb); reason = ndisc_rcv(skb);
break; break;
case ICMPV6_MGM_QUERY: case ICMPV6_MGM_QUERY:
...@@ -995,7 +1003,8 @@ static int icmpv6_rcv(struct sk_buff *skb) ...@@ -995,7 +1003,8 @@ static int icmpv6_rcv(struct sk_buff *skb)
* must pass to upper level * must pass to upper level
*/ */
icmpv6_notify(skb, type, hdr->icmp6_code, hdr->icmp6_mtu); reason = icmpv6_notify(skb, type, hdr->icmp6_code,
hdr->icmp6_mtu);
} }
/* until the v6 path can be better sorted assume failure and /* until the v6 path can be better sorted assume failure and
......
...@@ -1804,15 +1804,16 @@ static bool ndisc_suppress_frag_ndisc(struct sk_buff *skb) ...@@ -1804,15 +1804,16 @@ static bool ndisc_suppress_frag_ndisc(struct sk_buff *skb)
return false; return false;
} }
int ndisc_rcv(struct sk_buff *skb) enum skb_drop_reason ndisc_rcv(struct sk_buff *skb)
{ {
struct nd_msg *msg; struct nd_msg *msg;
SKB_DR(reason);
if (ndisc_suppress_frag_ndisc(skb)) if (ndisc_suppress_frag_ndisc(skb))
return 0; return SKB_DROP_REASON_IPV6_NDISC_FRAG;
if (skb_linearize(skb)) if (skb_linearize(skb))
return 0; return SKB_DROP_REASON_NOMEM;
msg = (struct nd_msg *)skb_transport_header(skb); msg = (struct nd_msg *)skb_transport_header(skb);
...@@ -1821,13 +1822,13 @@ int ndisc_rcv(struct sk_buff *skb) ...@@ -1821,13 +1822,13 @@ int ndisc_rcv(struct sk_buff *skb)
if (ipv6_hdr(skb)->hop_limit != 255) { if (ipv6_hdr(skb)->hop_limit != 255) {
ND_PRINTK(2, warn, "NDISC: invalid hop-limit: %d\n", ND_PRINTK(2, warn, "NDISC: invalid hop-limit: %d\n",
ipv6_hdr(skb)->hop_limit); ipv6_hdr(skb)->hop_limit);
return 0; return SKB_DROP_REASON_IPV6_NDISC_HOP_LIMIT;
} }
if (msg->icmph.icmp6_code != 0) { if (msg->icmph.icmp6_code != 0) {
ND_PRINTK(2, warn, "NDISC: invalid ICMPv6 code: %d\n", ND_PRINTK(2, warn, "NDISC: invalid ICMPv6 code: %d\n",
msg->icmph.icmp6_code); msg->icmph.icmp6_code);
return 0; return SKB_DROP_REASON_IPV6_NDISC_BAD_CODE;
} }
switch (msg->icmph.icmp6_type) { switch (msg->icmph.icmp6_type) {
...@@ -1853,7 +1854,7 @@ int ndisc_rcv(struct sk_buff *skb) ...@@ -1853,7 +1854,7 @@ int ndisc_rcv(struct sk_buff *skb)
break; break;
} }
return 0; return reason;
} }
static int ndisc_netdev_event(struct notifier_block *this, unsigned long event, void *ptr) static int ndisc_netdev_event(struct notifier_block *this, unsigned long event, void *ptr)
......
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