nft_chain_route.c 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169
  1. // SPDX-License-Identifier: GPL-2.0
  2. #include <linux/skbuff.h>
  3. #include <linux/netfilter.h>
  4. #include <linux/netfilter_ipv4.h>
  5. #include <linux/netfilter_ipv6.h>
  6. #include <linux/netfilter/nfnetlink.h>
  7. #include <linux/netfilter/nf_tables.h>
  8. #include <net/netfilter/nf_tables.h>
  9. #include <net/netfilter/nf_tables_ipv4.h>
  10. #include <net/netfilter/nf_tables_ipv6.h>
  11. #include <net/route.h>
  12. #include <net/ip.h>
  13. #ifdef CONFIG_NF_TABLES_IPV4
  14. static unsigned int nf_route_table_hook4(void *priv,
  15. struct sk_buff *skb,
  16. const struct nf_hook_state *state)
  17. {
  18. const struct iphdr *iph;
  19. struct nft_pktinfo pkt;
  20. __be32 saddr, daddr;
  21. unsigned int ret;
  22. u32 mark;
  23. int err;
  24. u8 tos;
  25. nft_set_pktinfo(&pkt, skb, state);
  26. nft_set_pktinfo_ipv4(&pkt, skb);
  27. mark = skb->mark;
  28. iph = ip_hdr(skb);
  29. saddr = iph->saddr;
  30. daddr = iph->daddr;
  31. tos = iph->tos;
  32. ret = nft_do_chain(&pkt, priv);
  33. if (ret == NF_ACCEPT) {
  34. iph = ip_hdr(skb);
  35. if (iph->saddr != saddr ||
  36. iph->daddr != daddr ||
  37. skb->mark != mark ||
  38. iph->tos != tos) {
  39. err = ip_route_me_harder(state->net, state->sk, skb, RTN_UNSPEC);
  40. if (err < 0)
  41. ret = NF_DROP_ERR(err);
  42. }
  43. }
  44. return ret;
  45. }
  46. static const struct nft_chain_type nft_chain_route_ipv4 = {
  47. .name = "route",
  48. .type = NFT_CHAIN_T_ROUTE,
  49. .family = NFPROTO_IPV4,
  50. .hook_mask = (1 << NF_INET_LOCAL_OUT),
  51. .hooks = {
  52. [NF_INET_LOCAL_OUT] = nf_route_table_hook4,
  53. },
  54. };
  55. #endif
  56. #ifdef CONFIG_NF_TABLES_IPV6
  57. static unsigned int nf_route_table_hook6(void *priv,
  58. struct sk_buff *skb,
  59. const struct nf_hook_state *state)
  60. {
  61. struct in6_addr saddr, daddr;
  62. struct nft_pktinfo pkt;
  63. u32 mark, flowlabel;
  64. unsigned int ret;
  65. u8 hop_limit;
  66. int err;
  67. nft_set_pktinfo(&pkt, skb, state);
  68. nft_set_pktinfo_ipv6(&pkt, skb);
  69. /* save source/dest address, mark, hoplimit, flowlabel, priority */
  70. memcpy(&saddr, &ipv6_hdr(skb)->saddr, sizeof(saddr));
  71. memcpy(&daddr, &ipv6_hdr(skb)->daddr, sizeof(daddr));
  72. mark = skb->mark;
  73. hop_limit = ipv6_hdr(skb)->hop_limit;
  74. /* flowlabel and prio (includes version, which shouldn't change either)*/
  75. flowlabel = *((u32 *)ipv6_hdr(skb));
  76. ret = nft_do_chain(&pkt, priv);
  77. if (ret == NF_ACCEPT &&
  78. (memcmp(&ipv6_hdr(skb)->saddr, &saddr, sizeof(saddr)) ||
  79. memcmp(&ipv6_hdr(skb)->daddr, &daddr, sizeof(daddr)) ||
  80. skb->mark != mark ||
  81. ipv6_hdr(skb)->hop_limit != hop_limit ||
  82. flowlabel != *((u32 *)ipv6_hdr(skb)))) {
  83. err = nf_ip6_route_me_harder(state->net, state->sk, skb);
  84. if (err < 0)
  85. ret = NF_DROP_ERR(err);
  86. }
  87. return ret;
  88. }
  89. static const struct nft_chain_type nft_chain_route_ipv6 = {
  90. .name = "route",
  91. .type = NFT_CHAIN_T_ROUTE,
  92. .family = NFPROTO_IPV6,
  93. .hook_mask = (1 << NF_INET_LOCAL_OUT),
  94. .hooks = {
  95. [NF_INET_LOCAL_OUT] = nf_route_table_hook6,
  96. },
  97. };
  98. #endif
  99. #ifdef CONFIG_NF_TABLES_INET
  100. static unsigned int nf_route_table_inet(void *priv,
  101. struct sk_buff *skb,
  102. const struct nf_hook_state *state)
  103. {
  104. struct nft_pktinfo pkt;
  105. switch (state->pf) {
  106. case NFPROTO_IPV4:
  107. return nf_route_table_hook4(priv, skb, state);
  108. case NFPROTO_IPV6:
  109. return nf_route_table_hook6(priv, skb, state);
  110. default:
  111. nft_set_pktinfo(&pkt, skb, state);
  112. break;
  113. }
  114. return nft_do_chain(&pkt, priv);
  115. }
  116. static const struct nft_chain_type nft_chain_route_inet = {
  117. .name = "route",
  118. .type = NFT_CHAIN_T_ROUTE,
  119. .family = NFPROTO_INET,
  120. .hook_mask = (1 << NF_INET_LOCAL_OUT),
  121. .hooks = {
  122. [NF_INET_LOCAL_OUT] = nf_route_table_inet,
  123. },
  124. };
  125. #endif
  126. void __init nft_chain_route_init(void)
  127. {
  128. #ifdef CONFIG_NF_TABLES_IPV6
  129. nft_register_chain_type(&nft_chain_route_ipv6);
  130. #endif
  131. #ifdef CONFIG_NF_TABLES_IPV4
  132. nft_register_chain_type(&nft_chain_route_ipv4);
  133. #endif
  134. #ifdef CONFIG_NF_TABLES_INET
  135. nft_register_chain_type(&nft_chain_route_inet);
  136. #endif
  137. }
  138. void __exit nft_chain_route_fini(void)
  139. {
  140. #ifdef CONFIG_NF_TABLES_IPV6
  141. nft_unregister_chain_type(&nft_chain_route_ipv6);
  142. #endif
  143. #ifdef CONFIG_NF_TABLES_IPV4
  144. nft_unregister_chain_type(&nft_chain_route_ipv4);
  145. #endif
  146. #ifdef CONFIG_NF_TABLES_INET
  147. nft_unregister_chain_type(&nft_chain_route_inet);
  148. #endif
  149. }