#include <linux/netdevice.h>
 
-extern void                            icmpv6_send(struct sk_buff *skb,
-                                                   u8 type, u8 code,
-                                                   __u32 info);
+#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);
+extern int inet6_register_icmp_sender(ip6_icmp_send_t *fn);
+extern int inet6_unregister_icmp_sender(ip6_icmp_send_t *fn);
+
+#else
+
+static inline void icmpv6_send(struct sk_buff *skb,
+                              u8 type, u8 code, __u32 info)
+{
+
+}
+#endif
 
 extern int                             icmpv6_init(void);
 extern int                             icmpv6_err_convert(u8 type, u8 code,
 
        spin_unlock_bh(&sk->sk_lock.slock);
 }
 
-/*
- * Slightly more convenient version of icmpv6_send.
- */
-void icmpv6_param_prob(struct sk_buff *skb, u8 code, int pos)
-{
-       icmpv6_send(skb, ICMPV6_PARAMPROB, code, pos);
-       kfree_skb(skb);
-}
-
 /*
  * Figure out, may we reply to this packet with icmp error.
  *
         * anycast.
         */
        if (((struct rt6_info *)dst)->rt6i_flags & RTF_ANYCAST) {
-               LIMIT_NETDEBUG(KERN_DEBUG "icmpv6_send: acast source\n");
+               LIMIT_NETDEBUG(KERN_DEBUG "icmp6_send: acast source\n");
                dst_release(dst);
                return ERR_PTR(-EINVAL);
        }
 /*
  *     Send an ICMP message in response to a packet in error
  */
-void icmpv6_send(struct sk_buff *skb, u8 type, u8 code, __u32 info)
+static void icmp6_send(struct sk_buff *skb, u8 type, u8 code, __u32 info)
 {
        struct net *net = dev_net(skb->dev);
        struct inet6_dev *idev = NULL;
        /*
         *      Make sure we respect the rules
         *      i.e. RFC 1885 2.4(e)
-        *      Rule (e.1) is enforced by not using icmpv6_send
+        *      Rule (e.1) is enforced by not using icmp6_send
         *      in any code that processes icmp errors.
         */
        addr_type = ipv6_addr_type(&hdr->daddr);
         *      and anycast addresses will be checked later.
         */
        if ((addr_type == IPV6_ADDR_ANY) || (addr_type & IPV6_ADDR_MULTICAST)) {
-               LIMIT_NETDEBUG(KERN_DEBUG "icmpv6_send: addr_any/mcast source\n");
+               LIMIT_NETDEBUG(KERN_DEBUG "icmp6_send: addr_any/mcast source\n");
                return;
        }
 
         *      Never answer to a ICMP packet.
         */
        if (is_ineligible(skb)) {
-               LIMIT_NETDEBUG(KERN_DEBUG "icmpv6_send: no reply to icmp error\n");
+               LIMIT_NETDEBUG(KERN_DEBUG "icmp6_send: no reply to icmp error\n");
                return;
        }
 
 out:
        icmpv6_xmit_unlock(sk);
 }
-EXPORT_SYMBOL(icmpv6_send);
+
+/* Slightly more convenient version of icmp6_send.
+ */
+void icmpv6_param_prob(struct sk_buff *skb, u8 code, int pos)
+{
+       icmp6_send(skb, ICMPV6_PARAMPROB, code, pos);
+       kfree_skb(skb);
+}
 
 static void icmpv6_echo_reply(struct sk_buff *skb)
 {
        err = -EAGAIN;
        if (inet6_add_protocol(&icmpv6_protocol, IPPROTO_ICMPV6) < 0)
                goto fail;
+
+       err = inet6_register_icmp_sender(icmp6_send);
+       if (err)
+               goto sender_reg_err;
        return 0;
 
+sender_reg_err:
+       inet6_del_protocol(&icmpv6_protocol, IPPROTO_ICMPV6);
 fail:
        pr_err("Failed to register ICMP6 protocol\n");
        unregister_pernet_subsys(&icmpv6_sk_ops);
 
 void icmpv6_cleanup(void)
 {
+       inet6_unregister_icmp_sender(icmp6_send);
        unregister_pernet_subsys(&icmpv6_sk_ops);
        inet6_del_protocol(&icmpv6_protocol, IPPROTO_ICMPV6);
 }
 
--- /dev/null
+#include <linux/export.h>
+#include <linux/icmpv6.h>
+#include <linux/mutex.h>
+#include <linux/netdevice.h>
+#include <linux/spinlock.h>
+
+#include <net/ipv6.h>
+
+#if IS_ENABLED(CONFIG_IPV6)
+
+static ip6_icmp_send_t __rcu *ip6_icmp_send;
+
+int inet6_register_icmp_sender(ip6_icmp_send_t *fn)
+{
+       return (cmpxchg((ip6_icmp_send_t **)&ip6_icmp_send, NULL, fn) == NULL) ?
+               0 : -EBUSY;
+}
+EXPORT_SYMBOL(inet6_register_icmp_sender);
+
+int inet6_unregister_icmp_sender(ip6_icmp_send_t *fn)
+{
+       int ret;
+
+       ret = (cmpxchg((ip6_icmp_send_t **)&ip6_icmp_send, fn, NULL) == fn) ?
+             0 : -EINVAL;
+
+       synchronize_net();
+
+       return ret;
+}
+EXPORT_SYMBOL(inet6_unregister_icmp_sender);
+
+void icmpv6_send(struct sk_buff *skb, u8 type, u8 code, __u32 info)
+{
+       ip6_icmp_send_t *send;
+
+       rcu_read_lock();
+       send = rcu_dereference(ip6_icmp_send);
+
+       if (!send)
+               goto out;
+       send(skb, type, code, info);
+out:
+       rcu_read_unlock();
+}
+EXPORT_SYMBOL(icmpv6_send);
+#endif