nf_dup_ipv6.c 2.3 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283
  1. /*
  2. * (C) 2007 by Sebastian Claßen <sebastian.classen@freenet.ag>
  3. * (C) 2007-2010 by Jan Engelhardt <jengelh@medozas.de>
  4. *
  5. * Extracted from xt_TEE.c
  6. *
  7. * This program is free software; you can redistribute it and/or modify it
  8. * under the terms of the GNU General Public License version 2 or later, as
  9. * published by the Free Software Foundation.
  10. */
  11. #include <linux/module.h>
  12. #include <linux/percpu.h>
  13. #include <linux/skbuff.h>
  14. #include <linux/netfilter.h>
  15. #include <net/ipv6.h>
  16. #include <net/ip6_route.h>
  17. #include <net/netfilter/ipv6/nf_dup_ipv6.h>
  18. #if IS_ENABLED(CONFIG_NF_CONNTRACK)
  19. #include <net/netfilter/nf_conntrack.h>
  20. #endif
  21. static bool nf_dup_ipv6_route(struct net *net, struct sk_buff *skb,
  22. const struct in6_addr *gw, int oif)
  23. {
  24. const struct ipv6hdr *iph = ipv6_hdr(skb);
  25. struct dst_entry *dst;
  26. struct flowi6 fl6;
  27. memset(&fl6, 0, sizeof(fl6));
  28. if (oif != -1)
  29. fl6.flowi6_oif = oif;
  30. fl6.daddr = *gw;
  31. fl6.flowlabel = (__force __be32)(((iph->flow_lbl[0] & 0xF) << 16) |
  32. (iph->flow_lbl[1] << 8) | iph->flow_lbl[2]);
  33. fl6.flowi6_flags = FLOWI_FLAG_KNOWN_NH;
  34. dst = ip6_route_output(net, NULL, &fl6);
  35. if (dst->error) {
  36. dst_release(dst);
  37. return false;
  38. }
  39. skb_dst_drop(skb);
  40. skb_dst_set(skb, dst);
  41. skb->dev = dst->dev;
  42. skb->protocol = htons(ETH_P_IPV6);
  43. return true;
  44. }
  45. void nf_dup_ipv6(struct net *net, struct sk_buff *skb, unsigned int hooknum,
  46. const struct in6_addr *gw, int oif)
  47. {
  48. if (this_cpu_read(nf_skb_duplicated))
  49. return;
  50. skb = pskb_copy(skb, GFP_ATOMIC);
  51. if (skb == NULL)
  52. return;
  53. #if IS_ENABLED(CONFIG_NF_CONNTRACK)
  54. nf_conntrack_put(skb->nfct);
  55. skb->nfct = &nf_ct_untracked_get()->ct_general;
  56. skb->nfctinfo = IP_CT_NEW;
  57. nf_conntrack_get(skb->nfct);
  58. #endif
  59. if (hooknum == NF_INET_PRE_ROUTING ||
  60. hooknum == NF_INET_LOCAL_IN) {
  61. struct ipv6hdr *iph = ipv6_hdr(skb);
  62. --iph->hop_limit;
  63. }
  64. if (nf_dup_ipv6_route(net, skb, gw, oif)) {
  65. __this_cpu_write(nf_skb_duplicated, true);
  66. ip6_local_out(net, skb->sk, skb);
  67. __this_cpu_write(nf_skb_duplicated, false);
  68. } else {
  69. kfree_skb(skb);
  70. }
  71. }
  72. EXPORT_SYMBOL_GPL(nf_dup_ipv6);
  73. MODULE_AUTHOR("Sebastian Claßen <sebastian.classen@freenet.ag>");
  74. MODULE_AUTHOR("Jan Engelhardt <jengelh@medozas.de>");
  75. MODULE_DESCRIPTION("nf_dup_ipv6: IPv6 packet duplication");
  76. MODULE_LICENSE("GPL");