ip6_icmp.c 2.1 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586
  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. enum ip_conntrack_dir dir;
  46. struct in6_addr orig_ip;
  47. struct nf_conn *ct;
  48. ct = nf_ct_get(skb_in, &ctinfo);
  49. if (!ct || !(READ_ONCE(ct->status) & IPS_NAT_MASK)) {
  50. __icmpv6_send(skb_in, type, code, info, &parm);
  51. return;
  52. }
  53. if (skb_shared(skb_in))
  54. skb_in = cloned_skb = skb_clone(skb_in, GFP_ATOMIC);
  55. if (unlikely(!skb_in || skb_network_header(skb_in) < skb_in->head ||
  56. (skb_network_header(skb_in) + sizeof(struct ipv6hdr)) >
  57. skb_tail_pointer(skb_in) || skb_ensure_writable(skb_in,
  58. skb_network_offset(skb_in) + sizeof(struct ipv6hdr))))
  59. goto out;
  60. orig_ip = ipv6_hdr(skb_in)->saddr;
  61. dir = CTINFO2DIR(ctinfo);
  62. ipv6_hdr(skb_in)->saddr = ct->tuplehash[dir].tuple.src.u3.in6;
  63. __icmpv6_send(skb_in, type, code, info, &parm);
  64. ipv6_hdr(skb_in)->saddr = orig_ip;
  65. out:
  66. consume_skb(cloned_skb);
  67. }
  68. EXPORT_SYMBOL(icmpv6_ndo_send);
  69. #endif
  70. #endif