ip6_icmp.c 2.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384
  1. // SPDX-License-Identifier: GPL-2.0
  2. #include <linux/export.h>
  3. #include <linux/icmpv6.h>
  4. #include <linux/mutex.h>
  5. #include <linux/netdevice.h>
  6. #include <linux/spinlock.h>
  7. #include <net/ipv6.h>
  8. #if IS_ENABLED(CONFIG_IPV6)
  9. #if !IS_BUILTIN(CONFIG_IPV6)
  10. static ip6_icmp_send_t __rcu *ip6_icmp_send;
  11. int inet6_register_icmp_sender(ip6_icmp_send_t *fn)
  12. {
  13. return (cmpxchg((ip6_icmp_send_t **)&ip6_icmp_send, NULL, fn) == NULL) ?
  14. 0 : -EBUSY;
  15. }
  16. EXPORT_SYMBOL(inet6_register_icmp_sender);
  17. int inet6_unregister_icmp_sender(ip6_icmp_send_t *fn)
  18. {
  19. int ret;
  20. ret = (cmpxchg((ip6_icmp_send_t **)&ip6_icmp_send, fn, NULL) == fn) ?
  21. 0 : -EINVAL;
  22. synchronize_net();
  23. return ret;
  24. }
  25. EXPORT_SYMBOL(inet6_unregister_icmp_sender);
  26. void __icmpv6_send(struct sk_buff *skb, u8 type, u8 code, __u32 info,
  27. const struct inet6_skb_parm *parm)
  28. {
  29. ip6_icmp_send_t *send;
  30. rcu_read_lock();
  31. send = rcu_dereference(ip6_icmp_send);
  32. if (send)
  33. send(skb, type, code, info, NULL, parm);
  34. rcu_read_unlock();
  35. }
  36. EXPORT_SYMBOL(__icmpv6_send);
  37. #endif
  38. #if IS_ENABLED(CONFIG_NF_NAT)
  39. #include <net/netfilter/nf_conntrack.h>
  40. void icmpv6_ndo_send(struct sk_buff *skb_in, u8 type, u8 code, __u32 info)
  41. {
  42. struct inet6_skb_parm parm = { 0 };
  43. struct sk_buff *cloned_skb = NULL;
  44. enum ip_conntrack_info ctinfo;
  45. struct in6_addr orig_ip;
  46. struct nf_conn *ct;
  47. ct = nf_ct_get(skb_in, &ctinfo);
  48. if (!ct || !(ct->status & IPS_SRC_NAT)) {
  49. __icmpv6_send(skb_in, type, code, info, &parm);
  50. return;
  51. }
  52. if (skb_shared(skb_in))
  53. skb_in = cloned_skb = skb_clone(skb_in, GFP_ATOMIC);
  54. if (unlikely(!skb_in || skb_network_header(skb_in) < skb_in->head ||
  55. (skb_network_header(skb_in) + sizeof(struct ipv6hdr)) >
  56. skb_tail_pointer(skb_in) || skb_ensure_writable(skb_in,
  57. skb_network_offset(skb_in) + sizeof(struct ipv6hdr))))
  58. goto out;
  59. orig_ip = ipv6_hdr(skb_in)->saddr;
  60. ipv6_hdr(skb_in)->saddr = ct->tuplehash[0].tuple.src.u3.in6;
  61. __icmpv6_send(skb_in, type, code, info, &parm);
  62. ipv6_hdr(skb_in)->saddr = orig_ip;
  63. out:
  64. consume_skb(cloned_skb);
  65. }
  66. EXPORT_SYMBOL(icmpv6_ndo_send);
  67. #endif
  68. #endif