--- /dev/null
+From e0fce6f945a26d4e953a147fe7ca11410322c9fe Mon Sep 17 00:00:00 2001
+From: "Jason A. Donenfeld" <Jason@zx2c4.com>
+Date: Tue, 11 Feb 2020 20:47:06 +0100
+Subject: gtp: use icmp_ndo_send helper
+
+From: Jason A. Donenfeld <Jason@zx2c4.com>
+
+commit e0fce6f945a26d4e953a147fe7ca11410322c9fe upstream.
+
+Because gtp is calling icmp from network device context, it should use
+the ndo helper so that the rate limiting applies correctly.
+
+Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
+Cc: Harald Welte <laforge@gnumonks.org>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/net/gtp.c | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+--- a/drivers/net/gtp.c
++++ b/drivers/net/gtp.c
+@@ -550,8 +550,8 @@ static int gtp_build_skb_ip4(struct sk_b
+ mtu < ntohs(iph->tot_len)) {
+ netdev_dbg(dev, "packet too big, fragmentation needed\n");
+ memset(IPCB(skb), 0, sizeof(*IPCB(skb)));
+- icmp_send(skb, ICMP_DEST_UNREACH, ICMP_FRAG_NEEDED,
+- htonl(mtu));
++ icmp_ndo_send(skb, ICMP_DEST_UNREACH, ICMP_FRAG_NEEDED,
++ htonl(mtu));
+ goto err_rt;
+ }
+
--- /dev/null
+From 0b41713b606694257b90d61ba7e2712d8457648b Mon Sep 17 00:00:00 2001
+From: "Jason A. Donenfeld" <Jason@zx2c4.com>
+Date: Tue, 11 Feb 2020 20:47:05 +0100
+Subject: icmp: introduce helper for nat'd source address in network device context
+
+From: Jason A. Donenfeld <Jason@zx2c4.com>
+
+commit 0b41713b606694257b90d61ba7e2712d8457648b upstream.
+
+This introduces a helper function to be called only by network drivers
+that wraps calls to icmp[v6]_send in a conntrack transformation, in case
+NAT has been used. We don't want to pollute the non-driver path, though,
+so we introduce this as a helper to be called by places that actually
+make use of this, as suggested by Florian.
+
+Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
+Cc: Florian Westphal <fw@strlen.de>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ include/linux/icmpv6.h | 6 ++++++
+ include/net/icmp.h | 6 ++++++
+ net/ipv4/icmp.c | 33 +++++++++++++++++++++++++++++++++
+ net/ipv6/ip6_icmp.c | 34 ++++++++++++++++++++++++++++++++++
+ 4 files changed, 79 insertions(+)
+
+--- a/include/linux/icmpv6.h
++++ b/include/linux/icmpv6.h
+@@ -31,6 +31,12 @@ static inline void icmpv6_send(struct sk
+ }
+ #endif
+
++#if IS_ENABLED(CONFIG_NF_NAT)
++void icmpv6_ndo_send(struct sk_buff *skb_in, u8 type, u8 code, __u32 info);
++#else
++#define icmpv6_ndo_send icmpv6_send
++#endif
++
+ extern int icmpv6_init(void);
+ extern int icmpv6_err_convert(u8 type, u8 code,
+ int *err);
+--- a/include/net/icmp.h
++++ b/include/net/icmp.h
+@@ -47,6 +47,12 @@ static inline void icmp_send(struct sk_b
+ __icmp_send(skb_in, type, code, info, &IPCB(skb_in)->opt);
+ }
+
++#if IS_ENABLED(CONFIG_NF_NAT)
++void icmp_ndo_send(struct sk_buff *skb_in, int type, int code, __be32 info);
++#else
++#define icmp_ndo_send icmp_send
++#endif
++
+ int icmp_rcv(struct sk_buff *skb);
+ void icmp_err(struct sk_buff *skb, u32 info);
+ int icmp_init(void);
+--- a/net/ipv4/icmp.c
++++ b/net/ipv4/icmp.c
+@@ -755,6 +755,39 @@ out:;
+ }
+ EXPORT_SYMBOL(__icmp_send);
+
++#if IS_ENABLED(CONFIG_NF_NAT)
++#include <net/netfilter/nf_conntrack.h>
++void icmp_ndo_send(struct sk_buff *skb_in, int type, int code, __be32 info)
++{
++ struct sk_buff *cloned_skb = NULL;
++ enum ip_conntrack_info ctinfo;
++ struct nf_conn *ct;
++ __be32 orig_ip;
++
++ ct = nf_ct_get(skb_in, &ctinfo);
++ if (!ct || !(ct->status & IPS_SRC_NAT)) {
++ icmp_send(skb_in, type, code, info);
++ return;
++ }
++
++ if (skb_shared(skb_in))
++ skb_in = cloned_skb = skb_clone(skb_in, GFP_ATOMIC);
++
++ if (unlikely(!skb_in || skb_network_header(skb_in) < skb_in->head ||
++ (skb_network_header(skb_in) + sizeof(struct iphdr)) >
++ skb_tail_pointer(skb_in) || skb_ensure_writable(skb_in,
++ skb_network_offset(skb_in) + sizeof(struct iphdr))))
++ goto out;
++
++ orig_ip = ip_hdr(skb_in)->saddr;
++ ip_hdr(skb_in)->saddr = ct->tuplehash[0].tuple.src.u3.ip;
++ icmp_send(skb_in, type, code, info);
++ ip_hdr(skb_in)->saddr = orig_ip;
++out:
++ consume_skb(cloned_skb);
++}
++EXPORT_SYMBOL(icmp_ndo_send);
++#endif
+
+ static void icmp_socket_deliver(struct sk_buff *skb, u32 info)
+ {
+--- a/net/ipv6/ip6_icmp.c
++++ b/net/ipv6/ip6_icmp.c
+@@ -45,4 +45,38 @@ out:
+ rcu_read_unlock();
+ }
+ EXPORT_SYMBOL(icmpv6_send);
++
++#if IS_ENABLED(CONFIG_NF_NAT)
++#include <net/netfilter/nf_conntrack.h>
++void icmpv6_ndo_send(struct sk_buff *skb_in, u8 type, u8 code, __u32 info)
++{
++ struct sk_buff *cloned_skb = NULL;
++ enum ip_conntrack_info ctinfo;
++ struct in6_addr orig_ip;
++ struct nf_conn *ct;
++
++ ct = nf_ct_get(skb_in, &ctinfo);
++ if (!ct || !(ct->status & IPS_SRC_NAT)) {
++ icmpv6_send(skb_in, type, code, info);
++ return;
++ }
++
++ if (skb_shared(skb_in))
++ skb_in = cloned_skb = skb_clone(skb_in, GFP_ATOMIC);
++
++ if (unlikely(!skb_in || skb_network_header(skb_in) < skb_in->head ||
++ (skb_network_header(skb_in) + sizeof(struct ipv6hdr)) >
++ skb_tail_pointer(skb_in) || skb_ensure_writable(skb_in,
++ skb_network_offset(skb_in) + sizeof(struct ipv6hdr))))
++ goto out;
++
++ orig_ip = ipv6_hdr(skb_in)->saddr;
++ ipv6_hdr(skb_in)->saddr = ct->tuplehash[0].tuple.src.u3.in6;
++ icmpv6_send(skb_in, type, code, info);
++ ipv6_hdr(skb_in)->saddr = orig_ip;
++out:
++ consume_skb(cloned_skb);
++}
++EXPORT_SYMBOL(icmpv6_ndo_send);
++#endif
+ #endif
--- /dev/null
+From cc7a21b6fbd945f8d8f61422ccd27203c1fafeb7 Mon Sep 17 00:00:00 2001
+From: Eric Dumazet <edumazet@google.com>
+Date: Fri, 19 Jun 2020 12:02:59 -0700
+Subject: ipv6: icmp6: avoid indirect call for icmpv6_send()
+
+From: Eric Dumazet <edumazet@google.com>
+
+commit cc7a21b6fbd945f8d8f61422ccd27203c1fafeb7 upstream.
+
+If IPv6 is builtin, we do not need an expensive indirect call
+to reach icmp6_send().
+
+v2: put inline keyword before the type to avoid sparse warnings.
+
+Signed-off-by: Eric Dumazet <edumazet@google.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ include/linux/icmpv6.h | 22 +++++++++++++++++++++-
+ net/ipv6/icmp.c | 5 +++--
+ net/ipv6/ip6_icmp.c | 10 +++++-----
+ 3 files changed, 29 insertions(+), 8 deletions(-)
+
+--- a/include/linux/icmpv6.h
++++ b/include/linux/icmpv6.h
+@@ -13,12 +13,32 @@ static inline struct icmp6hdr *icmp6_hdr
+ #include <linux/netdevice.h>
+
+ #if IS_ENABLED(CONFIG_IPV6)
+-extern void icmpv6_send(struct sk_buff *skb, u8 type, u8 code, __u32 info);
+
+ typedef void ip6_icmp_send_t(struct sk_buff *skb, u8 type, u8 code, __u32 info,
+ const struct in6_addr *force_saddr);
++#if IS_BUILTIN(CONFIG_IPV6)
++void icmp6_send(struct sk_buff *skb, u8 type, u8 code, __u32 info,
++ const struct in6_addr *force_saddr);
++static inline void icmpv6_send(struct sk_buff *skb, u8 type, u8 code, __u32 info)
++{
++ icmp6_send(skb, type, code, info, NULL);
++}
++static inline int inet6_register_icmp_sender(ip6_icmp_send_t *fn)
++{
++ BUILD_BUG_ON(fn != icmp6_send);
++ return 0;
++}
++static inline int inet6_unregister_icmp_sender(ip6_icmp_send_t *fn)
++{
++ BUILD_BUG_ON(fn != icmp6_send);
++ return 0;
++}
++#else
++extern void icmpv6_send(struct sk_buff *skb, u8 type, u8 code, __u32 info);
+ extern int inet6_register_icmp_sender(ip6_icmp_send_t *fn);
+ extern int inet6_unregister_icmp_sender(ip6_icmp_send_t *fn);
++#endif
++
+ int ip6_err_gen_icmpv6_unreach(struct sk_buff *skb, int nhs, int type,
+ unsigned int data_len);
+
+--- a/net/ipv6/icmp.c
++++ b/net/ipv6/icmp.c
+@@ -418,8 +418,8 @@ static int icmp6_iif(const struct sk_buf
+ /*
+ * Send an ICMP message in response to a packet in error
+ */
+-static void icmp6_send(struct sk_buff *skb, u8 type, u8 code, __u32 info,
+- const struct in6_addr *force_saddr)
++void icmp6_send(struct sk_buff *skb, u8 type, u8 code, __u32 info,
++ const struct in6_addr *force_saddr)
+ {
+ struct inet6_dev *idev = NULL;
+ struct ipv6hdr *hdr = ipv6_hdr(skb);
+@@ -592,6 +592,7 @@ out:
+ out_bh_enable:
+ local_bh_enable();
+ }
++EXPORT_SYMBOL(icmp6_send);
+
+ /* Slightly more convenient version of icmp6_send.
+ */
+--- a/net/ipv6/ip6_icmp.c
++++ b/net/ipv6/ip6_icmp.c
+@@ -9,6 +9,8 @@
+
+ #if IS_ENABLED(CONFIG_IPV6)
+
++#if !IS_BUILTIN(CONFIG_IPV6)
++
+ static ip6_icmp_send_t __rcu *ip6_icmp_send;
+
+ int inet6_register_icmp_sender(ip6_icmp_send_t *fn)
+@@ -37,14 +39,12 @@ void icmpv6_send(struct sk_buff *skb, u8
+
+ rcu_read_lock();
+ send = rcu_dereference(ip6_icmp_send);
+-
+- if (!send)
+- goto out;
+- send(skb, type, code, info, NULL);
+-out:
++ if (send)
++ send(skb, type, code, info, NULL);
+ rcu_read_unlock();
+ }
+ EXPORT_SYMBOL(icmpv6_send);
++#endif
+
+ #if IS_ENABLED(CONFIG_NF_NAT)
+ #include <net/netfilter/nf_conntrack.h>
--- /dev/null
+From 1faba27f11c8da244e793546a1b35a9b1da8208e Mon Sep 17 00:00:00 2001
+From: Leon Romanovsky <leonro@nvidia.com>
+Date: Wed, 3 Feb 2021 15:51:09 +0200
+Subject: ipv6: silence compilation warning for non-IPV6 builds
+
+From: Leon Romanovsky <leonro@nvidia.com>
+
+commit 1faba27f11c8da244e793546a1b35a9b1da8208e upstream.
+
+The W=1 compilation of allmodconfig generates the following warning:
+
+net/ipv6/icmp.c:448:6: warning: no previous prototype for 'icmp6_send' [-Wmissing-prototypes]
+ 448 | void icmp6_send(struct sk_buff *skb, u8 type, u8 code, __u32 info,
+ | ^~~~~~~~~~
+
+Fix it by providing function declaration for builds with ipv6 as a module.
+
+Signed-off-by: Leon Romanovsky <leonro@nvidia.com>
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ include/linux/icmpv6.h | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/include/linux/icmpv6.h
++++ b/include/linux/icmpv6.h
+@@ -16,9 +16,9 @@ static inline struct icmp6hdr *icmp6_hdr
+
+ typedef void ip6_icmp_send_t(struct sk_buff *skb, u8 type, u8 code, __u32 info,
+ const struct in6_addr *force_saddr);
+-#if IS_BUILTIN(CONFIG_IPV6)
+ void icmp6_send(struct sk_buff *skb, u8 type, u8 code, __u32 info,
+ const struct in6_addr *force_saddr);
++#if IS_BUILTIN(CONFIG_IPV6)
+ static inline void icmpv6_send(struct sk_buff *skb, u8 type, u8 code, __u32 info)
+ {
+ icmp6_send(skb, type, code, info, NULL);
--- /dev/null
+From ee576c47db60432c37e54b1e2b43a8ca6d3a8dca Mon Sep 17 00:00:00 2001
+From: "Jason A. Donenfeld" <Jason@zx2c4.com>
+Date: Tue, 23 Feb 2021 14:18:58 +0100
+Subject: net: icmp: pass zeroed opts from icmp{,v6}_ndo_send before sending
+
+From: Jason A. Donenfeld <Jason@zx2c4.com>
+
+commit ee576c47db60432c37e54b1e2b43a8ca6d3a8dca upstream.
+
+The icmp{,v6}_send functions make all sorts of use of skb->cb, casting
+it with IPCB or IP6CB, assuming the skb to have come directly from the
+inet layer. But when the packet comes from the ndo layer, especially
+when forwarded, there's no telling what might be in skb->cb at that
+point. As a result, the icmp sending code risks reading bogus memory
+contents, which can result in nasty stack overflows such as this one
+reported by a user:
+
+ panic+0x108/0x2ea
+ __stack_chk_fail+0x14/0x20
+ __icmp_send+0x5bd/0x5c0
+ icmp_ndo_send+0x148/0x160
+
+In icmp_send, skb->cb is cast with IPCB and an ip_options struct is read
+from it. The optlen parameter there is of particular note, as it can
+induce writes beyond bounds. There are quite a few ways that can happen
+in __ip_options_echo. For example:
+
+ // sptr/skb are attacker-controlled skb bytes
+ sptr = skb_network_header(skb);
+ // dptr/dopt points to stack memory allocated by __icmp_send
+ dptr = dopt->__data;
+ // sopt is the corrupt skb->cb in question
+ if (sopt->rr) {
+ optlen = sptr[sopt->rr+1]; // corrupt skb->cb + skb->data
+ soffset = sptr[sopt->rr+2]; // corrupt skb->cb + skb->data
+ // this now writes potentially attacker-controlled data, over
+ // flowing the stack:
+ memcpy(dptr, sptr+sopt->rr, optlen);
+ }
+
+In the icmpv6_send case, the story is similar, but not as dire, as only
+IP6CB(skb)->iif and IP6CB(skb)->dsthao are used. The dsthao case is
+worse than the iif case, but it is passed to ipv6_find_tlv, which does
+a bit of bounds checking on the value.
+
+This is easy to simulate by doing a `memset(skb->cb, 0x41,
+sizeof(skb->cb));` before calling icmp{,v6}_ndo_send, and it's only by
+good fortune and the rarity of icmp sending from that context that we've
+avoided reports like this until now. For example, in KASAN:
+
+ BUG: KASAN: stack-out-of-bounds in __ip_options_echo+0xa0e/0x12b0
+ Write of size 38 at addr ffff888006f1f80e by task ping/89
+ CPU: 2 PID: 89 Comm: ping Not tainted 5.10.0-rc7-debug+ #5
+ Call Trace:
+ dump_stack+0x9a/0xcc
+ print_address_description.constprop.0+0x1a/0x160
+ __kasan_report.cold+0x20/0x38
+ kasan_report+0x32/0x40
+ check_memory_region+0x145/0x1a0
+ memcpy+0x39/0x60
+ __ip_options_echo+0xa0e/0x12b0
+ __icmp_send+0x744/0x1700
+
+Actually, out of the 4 drivers that do this, only gtp zeroed the cb for
+the v4 case, while the rest did not. So this commit actually removes the
+gtp-specific zeroing, while putting the code where it belongs in the
+shared infrastructure of icmp{,v6}_ndo_send.
+
+This commit fixes the issue by passing an empty IPCB or IP6CB along to
+the functions that actually do the work. For the icmp_send, this was
+already trivial, thanks to __icmp_send providing the plumbing function.
+For icmpv6_send, this required a tiny bit of refactoring to make it
+behave like the v4 case, after which it was straight forward.
+
+Fixes: a2b78e9b2cac ("sunvnet: generate ICMP PTMUD messages for smaller port MTUs")
+Reported-by: SinYu <liuxyon@gmail.com>
+Reviewed-by: Willem de Bruijn <willemb@google.com>
+Link: https://lore.kernel.org/netdev/CAF=yD-LOF116aHub6RMe8vB8ZpnrrnoTdqhobEx+bvoA8AsP0w@mail.gmail.com/T/
+Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
+Link: https://lore.kernel.org/r/20210223131858.72082-1-Jason@zx2c4.com
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/net/gtp.c | 1 -
+ include/linux/icmpv6.h | 26 ++++++++++++++++++++------
+ include/linux/ipv6.h | 2 +-
+ include/net/icmp.h | 6 +++++-
+ net/ipv4/icmp.c | 5 +++--
+ net/ipv6/icmp.c | 16 ++++++++--------
+ net/ipv6/ip6_icmp.c | 12 +++++++-----
+ 7 files changed, 44 insertions(+), 24 deletions(-)
+
+--- a/drivers/net/gtp.c
++++ b/drivers/net/gtp.c
+@@ -549,7 +549,6 @@ static int gtp_build_skb_ip4(struct sk_b
+ if (!skb_is_gso(skb) && (iph->frag_off & htons(IP_DF)) &&
+ mtu < ntohs(iph->tot_len)) {
+ netdev_dbg(dev, "packet too big, fragmentation needed\n");
+- memset(IPCB(skb), 0, sizeof(*IPCB(skb)));
+ icmp_ndo_send(skb, ICMP_DEST_UNREACH, ICMP_FRAG_NEEDED,
+ htonl(mtu));
+ goto err_rt;
+--- a/include/linux/icmpv6.h
++++ b/include/linux/icmpv6.h
+@@ -3,6 +3,7 @@
+ #define _LINUX_ICMPV6_H
+
+ #include <linux/skbuff.h>
++#include <linux/ipv6.h>
+ #include <uapi/linux/icmpv6.h>
+
+ static inline struct icmp6hdr *icmp6_hdr(const struct sk_buff *skb)
+@@ -15,13 +16,16 @@ static inline struct icmp6hdr *icmp6_hdr
+ #if IS_ENABLED(CONFIG_IPV6)
+
+ typedef void ip6_icmp_send_t(struct sk_buff *skb, u8 type, u8 code, __u32 info,
+- const struct in6_addr *force_saddr);
++ const struct in6_addr *force_saddr,
++ const struct inet6_skb_parm *parm);
+ void icmp6_send(struct sk_buff *skb, u8 type, u8 code, __u32 info,
+- const struct in6_addr *force_saddr);
++ const struct in6_addr *force_saddr,
++ const struct inet6_skb_parm *parm);
+ #if IS_BUILTIN(CONFIG_IPV6)
+-static inline void icmpv6_send(struct sk_buff *skb, u8 type, u8 code, __u32 info)
++static inline void __icmpv6_send(struct sk_buff *skb, u8 type, u8 code, __u32 info,
++ const struct inet6_skb_parm *parm)
+ {
+- icmp6_send(skb, type, code, info, NULL);
++ icmp6_send(skb, type, code, info, NULL, parm);
+ }
+ static inline int inet6_register_icmp_sender(ip6_icmp_send_t *fn)
+ {
+@@ -34,11 +38,17 @@ static inline int inet6_unregister_icmp_
+ return 0;
+ }
+ #else
+-extern void icmpv6_send(struct sk_buff *skb, u8 type, u8 code, __u32 info);
++extern void __icmpv6_send(struct sk_buff *skb, u8 type, u8 code, __u32 info,
++ const struct inet6_skb_parm *parm);
+ extern int inet6_register_icmp_sender(ip6_icmp_send_t *fn);
+ extern int inet6_unregister_icmp_sender(ip6_icmp_send_t *fn);
+ #endif
+
++static inline void icmpv6_send(struct sk_buff *skb, u8 type, u8 code, __u32 info)
++{
++ __icmpv6_send(skb, type, code, info, IP6CB(skb));
++}
++
+ int ip6_err_gen_icmpv6_unreach(struct sk_buff *skb, int nhs, int type,
+ unsigned int data_len);
+
+@@ -54,7 +64,11 @@ static inline void icmpv6_send(struct sk
+ #if IS_ENABLED(CONFIG_NF_NAT)
+ void icmpv6_ndo_send(struct sk_buff *skb_in, u8 type, u8 code, __u32 info);
+ #else
+-#define icmpv6_ndo_send icmpv6_send
++static inline void icmpv6_ndo_send(struct sk_buff *skb_in, u8 type, u8 code, __u32 info)
++{
++ struct inet6_skb_parm parm = { 0 };
++ __icmpv6_send(skb_in, type, code, info, &parm);
++}
+ #endif
+
+ extern int icmpv6_init(void);
+--- a/include/linux/ipv6.h
++++ b/include/linux/ipv6.h
+@@ -3,6 +3,7 @@
+ #define _IPV6_H
+
+ #include <uapi/linux/ipv6.h>
++#include <uapi/linux/icmpv6.h>
+
+ #define ipv6_optlen(p) (((p)->hdrlen+1) << 3)
+ #define ipv6_authlen(p) (((p)->hdrlen+2) << 2)
+@@ -83,7 +84,6 @@ struct ipv6_params {
+ __s32 autoconf;
+ };
+ extern struct ipv6_params ipv6_defaults;
+-#include <linux/icmpv6.h>
+ #include <linux/tcp.h>
+ #include <linux/udp.h>
+
+--- a/include/net/icmp.h
++++ b/include/net/icmp.h
+@@ -50,7 +50,11 @@ static inline void icmp_send(struct sk_b
+ #if IS_ENABLED(CONFIG_NF_NAT)
+ void icmp_ndo_send(struct sk_buff *skb_in, int type, int code, __be32 info);
+ #else
+-#define icmp_ndo_send icmp_send
++static inline void icmp_ndo_send(struct sk_buff *skb_in, int type, int code, __be32 info)
++{
++ struct ip_options opts = { 0 };
++ __icmp_send(skb_in, type, code, info, &opts);
++}
+ #endif
+
+ int icmp_rcv(struct sk_buff *skb);
+--- a/net/ipv4/icmp.c
++++ b/net/ipv4/icmp.c
+@@ -760,13 +760,14 @@ EXPORT_SYMBOL(__icmp_send);
+ void icmp_ndo_send(struct sk_buff *skb_in, int type, int code, __be32 info)
+ {
+ struct sk_buff *cloned_skb = NULL;
++ struct ip_options opts = { 0 };
+ enum ip_conntrack_info ctinfo;
+ struct nf_conn *ct;
+ __be32 orig_ip;
+
+ ct = nf_ct_get(skb_in, &ctinfo);
+ if (!ct || !(ct->status & IPS_SRC_NAT)) {
+- icmp_send(skb_in, type, code, info);
++ __icmp_send(skb_in, type, code, info, &opts);
+ return;
+ }
+
+@@ -781,7 +782,7 @@ void icmp_ndo_send(struct sk_buff *skb_i
+
+ orig_ip = ip_hdr(skb_in)->saddr;
+ ip_hdr(skb_in)->saddr = ct->tuplehash[0].tuple.src.u3.ip;
+- icmp_send(skb_in, type, code, info);
++ __icmp_send(skb_in, type, code, info, &opts);
+ ip_hdr(skb_in)->saddr = orig_ip;
+ out:
+ consume_skb(cloned_skb);
+--- a/net/ipv6/icmp.c
++++ b/net/ipv6/icmp.c
+@@ -309,10 +309,9 @@ static int icmpv6_getfrag(void *from, ch
+ }
+
+ #if IS_ENABLED(CONFIG_IPV6_MIP6)
+-static void mip6_addr_swap(struct sk_buff *skb)
++static void mip6_addr_swap(struct sk_buff *skb, const struct inet6_skb_parm *opt)
+ {
+ struct ipv6hdr *iph = ipv6_hdr(skb);
+- struct inet6_skb_parm *opt = IP6CB(skb);
+ struct ipv6_destopt_hao *hao;
+ struct in6_addr tmp;
+ int off;
+@@ -329,7 +328,7 @@ static void mip6_addr_swap(struct sk_buf
+ }
+ }
+ #else
+-static inline void mip6_addr_swap(struct sk_buff *skb) {}
++static inline void mip6_addr_swap(struct sk_buff *skb, const struct inet6_skb_parm *opt) {}
+ #endif
+
+ static struct dst_entry *icmpv6_route_lookup(struct net *net,
+@@ -419,7 +418,8 @@ static int icmp6_iif(const struct sk_buf
+ * Send an ICMP message in response to a packet in error
+ */
+ void icmp6_send(struct sk_buff *skb, u8 type, u8 code, __u32 info,
+- const struct in6_addr *force_saddr)
++ const struct in6_addr *force_saddr,
++ const struct inet6_skb_parm *parm)
+ {
+ struct inet6_dev *idev = NULL;
+ struct ipv6hdr *hdr = ipv6_hdr(skb);
+@@ -512,7 +512,7 @@ void icmp6_send(struct sk_buff *skb, u8
+ if (!(skb->dev->flags&IFF_LOOPBACK) && !icmpv6_global_allow(type))
+ goto out_bh_enable;
+
+- mip6_addr_swap(skb);
++ mip6_addr_swap(skb, parm);
+
+ memset(&fl6, 0, sizeof(fl6));
+ fl6.flowi6_proto = IPPROTO_ICMPV6;
+@@ -598,7 +598,7 @@ EXPORT_SYMBOL(icmp6_send);
+ */
+ void icmpv6_param_prob(struct sk_buff *skb, u8 code, int pos)
+ {
+- icmp6_send(skb, ICMPV6_PARAMPROB, code, pos, NULL);
++ icmp6_send(skb, ICMPV6_PARAMPROB, code, pos, NULL, IP6CB(skb));
+ kfree_skb(skb);
+ }
+
+@@ -655,10 +655,10 @@ int ip6_err_gen_icmpv6_unreach(struct sk
+ }
+ if (type == ICMP_TIME_EXCEEDED)
+ icmp6_send(skb2, ICMPV6_TIME_EXCEED, ICMPV6_EXC_HOPLIMIT,
+- info, &temp_saddr);
++ info, &temp_saddr, IP6CB(skb2));
+ else
+ icmp6_send(skb2, ICMPV6_DEST_UNREACH, ICMPV6_ADDR_UNREACH,
+- info, &temp_saddr);
++ info, &temp_saddr, IP6CB(skb2));
+ if (rt)
+ ip6_rt_put(rt);
+
+--- a/net/ipv6/ip6_icmp.c
++++ b/net/ipv6/ip6_icmp.c
+@@ -33,23 +33,25 @@ int inet6_unregister_icmp_sender(ip6_icm
+ }
+ EXPORT_SYMBOL(inet6_unregister_icmp_sender);
+
+-void icmpv6_send(struct sk_buff *skb, u8 type, u8 code, __u32 info)
++void __icmpv6_send(struct sk_buff *skb, u8 type, u8 code, __u32 info,
++ const struct inet6_skb_parm *parm)
+ {
+ ip6_icmp_send_t *send;
+
+ rcu_read_lock();
+ send = rcu_dereference(ip6_icmp_send);
+ if (send)
+- send(skb, type, code, info, NULL);
++ send(skb, type, code, info, NULL, parm);
+ rcu_read_unlock();
+ }
+-EXPORT_SYMBOL(icmpv6_send);
++EXPORT_SYMBOL(__icmpv6_send);
+ #endif
+
+ #if IS_ENABLED(CONFIG_NF_NAT)
+ #include <net/netfilter/nf_conntrack.h>
+ void icmpv6_ndo_send(struct sk_buff *skb_in, u8 type, u8 code, __u32 info)
+ {
++ struct inet6_skb_parm parm = { 0 };
+ struct sk_buff *cloned_skb = NULL;
+ enum ip_conntrack_info ctinfo;
+ struct in6_addr orig_ip;
+@@ -57,7 +59,7 @@ void icmpv6_ndo_send(struct sk_buff *skb
+
+ ct = nf_ct_get(skb_in, &ctinfo);
+ if (!ct || !(ct->status & IPS_SRC_NAT)) {
+- icmpv6_send(skb_in, type, code, info);
++ __icmpv6_send(skb_in, type, code, info, &parm);
+ return;
+ }
+
+@@ -72,7 +74,7 @@ void icmpv6_ndo_send(struct sk_buff *skb
+
+ orig_ip = ipv6_hdr(skb_in)->saddr;
+ ipv6_hdr(skb_in)->saddr = ct->tuplehash[0].tuple.src.u3.in6;
+- icmpv6_send(skb_in, type, code, info);
++ __icmpv6_send(skb_in, type, code, info, &parm);
+ ipv6_hdr(skb_in)->saddr = orig_ip;
+ out:
+ consume_skb(cloned_skb);
dm-era-use-correct-value-size-in-equality-function-of-writeset-tree.patch
dm-era-reinitialize-bitset-cache-before-digesting-a-new-writeset.patch
dm-era-only-resize-metadata-in-preresume.patch
+icmp-introduce-helper-for-nat-d-source-address-in-network-device-context.patch
+gtp-use-icmp_ndo_send-helper.patch
+sunvnet-use-icmp_ndo_send-helper.patch
+xfrm-interface-use-icmp_ndo_send-helper.patch
+ipv6-icmp6-avoid-indirect-call-for-icmpv6_send.patch
+ipv6-silence-compilation-warning-for-non-ipv6-builds.patch
+net-icmp-pass-zeroed-opts-from-icmp-v6-_ndo_send-before-sending.patch
--- /dev/null
+From 67c9a7e1e3ac491b5df018803639addc36f154ba Mon Sep 17 00:00:00 2001
+From: "Jason A. Donenfeld" <Jason@zx2c4.com>
+Date: Tue, 11 Feb 2020 20:47:07 +0100
+Subject: sunvnet: use icmp_ndo_send helper
+
+From: Jason A. Donenfeld <Jason@zx2c4.com>
+
+commit 67c9a7e1e3ac491b5df018803639addc36f154ba upstream.
+
+Because sunvnet is calling icmp from network device context, it should use
+the ndo helper so that the rate limiting applies correctly. While we're
+at it, doing the additional route lookup before calling icmp_ndo_send is
+superfluous, since this is the job of the icmp code in the first place.
+
+Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
+Cc: Shannon Nelson <shannon.nelson@oracle.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/net/ethernet/sun/sunvnet_common.c | 23 ++++-------------------
+ 1 file changed, 4 insertions(+), 19 deletions(-)
+
+--- a/drivers/net/ethernet/sun/sunvnet_common.c
++++ b/drivers/net/ethernet/sun/sunvnet_common.c
+@@ -1353,27 +1353,12 @@ sunvnet_start_xmit_common(struct sk_buff
+ if (vio_version_after_eq(&port->vio, 1, 3))
+ localmtu -= VLAN_HLEN;
+
+- if (skb->protocol == htons(ETH_P_IP)) {
+- struct flowi4 fl4;
+- struct rtable *rt = NULL;
+-
+- memset(&fl4, 0, sizeof(fl4));
+- fl4.flowi4_oif = dev->ifindex;
+- fl4.flowi4_tos = RT_TOS(ip_hdr(skb)->tos);
+- fl4.daddr = ip_hdr(skb)->daddr;
+- fl4.saddr = ip_hdr(skb)->saddr;
+-
+- rt = ip_route_output_key(dev_net(dev), &fl4);
+- if (!IS_ERR(rt)) {
+- skb_dst_set(skb, &rt->dst);
+- icmp_send(skb, ICMP_DEST_UNREACH,
+- ICMP_FRAG_NEEDED,
+- htonl(localmtu));
+- }
+- }
++ if (skb->protocol == htons(ETH_P_IP))
++ icmp_ndo_send(skb, ICMP_DEST_UNREACH, ICMP_FRAG_NEEDED,
++ htonl(localmtu));
+ #if IS_ENABLED(CONFIG_IPV6)
+ else if (skb->protocol == htons(ETH_P_IPV6))
+- icmpv6_send(skb, ICMPV6_PKT_TOOBIG, 0, localmtu);
++ icmpv6_ndo_send(skb, ICMPV6_PKT_TOOBIG, 0, localmtu);
+ #endif
+ goto out_dropped;
+ }
--- /dev/null
+From 45942ba890e6f35232727a5fa33d732681f4eb9f Mon Sep 17 00:00:00 2001
+From: "Jason A. Donenfeld" <Jason@zx2c4.com>
+Date: Tue, 11 Feb 2020 20:47:09 +0100
+Subject: xfrm: interface: use icmp_ndo_send helper
+
+From: Jason A. Donenfeld <Jason@zx2c4.com>
+
+commit 45942ba890e6f35232727a5fa33d732681f4eb9f upstream.
+
+Because xfrmi is calling icmp from network device context, it should use
+the ndo helper so that the rate limiting applies correctly.
+
+Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
+Cc: Nicolas Dichtel <nicolas.dichtel@6wind.com>
+Cc: Steffen Klassert <steffen.klassert@secunet.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ net/xfrm/xfrm_interface.c | 6 +++---
+ 1 file changed, 3 insertions(+), 3 deletions(-)
+
+--- a/net/xfrm/xfrm_interface.c
++++ b/net/xfrm/xfrm_interface.c
+@@ -300,10 +300,10 @@ xfrmi_xmit2(struct sk_buff *skb, struct
+ if (mtu < IPV6_MIN_MTU)
+ mtu = IPV6_MIN_MTU;
+
+- icmpv6_send(skb, ICMPV6_PKT_TOOBIG, 0, mtu);
++ icmpv6_ndo_send(skb, ICMPV6_PKT_TOOBIG, 0, mtu);
+ } else {
+- icmp_send(skb, ICMP_DEST_UNREACH, ICMP_FRAG_NEEDED,
+- htonl(mtu));
++ icmp_ndo_send(skb, ICMP_DEST_UNREACH, ICMP_FRAG_NEEDED,
++ htonl(mtu));
+ }
+
+ dst_release(dst);