nf_tables_bridge.c 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188
  1. /*
  2. * Copyright (c) 2008 Patrick McHardy <kaber@trash.net>
  3. * Copyright (c) 2013 Pablo Neira Ayuso <pablo@netfilter.org>
  4. *
  5. * This program is free software; you can redistribute it and/or modify
  6. * it under the terms of the GNU General Public License version 2 as
  7. * published by the Free Software Foundation.
  8. *
  9. * Development of this code funded by Astaro AG (http://www.astaro.com/)
  10. */
  11. #include <linux/init.h>
  12. #include <linux/module.h>
  13. #include <linux/netfilter_bridge.h>
  14. #include <net/netfilter/nf_tables.h>
  15. #include <net/netfilter/nf_tables_bridge.h>
  16. #include <linux/ip.h>
  17. #include <linux/ipv6.h>
  18. #include <net/netfilter/nf_tables_ipv4.h>
  19. #include <net/netfilter/nf_tables_ipv6.h>
  20. int nft_bridge_iphdr_validate(struct sk_buff *skb)
  21. {
  22. struct iphdr *iph;
  23. u32 len;
  24. if (!pskb_may_pull(skb, sizeof(struct iphdr)))
  25. return 0;
  26. iph = ip_hdr(skb);
  27. if (iph->ihl < 5 || iph->version != 4)
  28. return 0;
  29. len = ntohs(iph->tot_len);
  30. if (skb->len < len)
  31. return 0;
  32. else if (len < (iph->ihl*4))
  33. return 0;
  34. if (!pskb_may_pull(skb, iph->ihl*4))
  35. return 0;
  36. return 1;
  37. }
  38. EXPORT_SYMBOL_GPL(nft_bridge_iphdr_validate);
  39. int nft_bridge_ip6hdr_validate(struct sk_buff *skb)
  40. {
  41. struct ipv6hdr *hdr;
  42. u32 pkt_len;
  43. if (!pskb_may_pull(skb, sizeof(struct ipv6hdr)))
  44. return 0;
  45. hdr = ipv6_hdr(skb);
  46. if (hdr->version != 6)
  47. return 0;
  48. pkt_len = ntohs(hdr->payload_len);
  49. if (pkt_len + sizeof(struct ipv6hdr) > skb->len)
  50. return 0;
  51. return 1;
  52. }
  53. EXPORT_SYMBOL_GPL(nft_bridge_ip6hdr_validate);
  54. static inline void nft_bridge_set_pktinfo_ipv4(struct nft_pktinfo *pkt,
  55. struct sk_buff *skb,
  56. const struct nf_hook_state *state)
  57. {
  58. if (nft_bridge_iphdr_validate(skb))
  59. nft_set_pktinfo_ipv4(pkt, skb, state);
  60. else
  61. nft_set_pktinfo(pkt, skb, state);
  62. }
  63. static inline void nft_bridge_set_pktinfo_ipv6(struct nft_pktinfo *pkt,
  64. struct sk_buff *skb,
  65. const struct nf_hook_state *state)
  66. {
  67. #if IS_ENABLED(CONFIG_IPV6)
  68. if (nft_bridge_ip6hdr_validate(skb) &&
  69. nft_set_pktinfo_ipv6(pkt, skb, state) == 0)
  70. return;
  71. #endif
  72. nft_set_pktinfo(pkt, skb, state);
  73. }
  74. static unsigned int
  75. nft_do_chain_bridge(void *priv,
  76. struct sk_buff *skb,
  77. const struct nf_hook_state *state)
  78. {
  79. struct nft_pktinfo pkt;
  80. switch (eth_hdr(skb)->h_proto) {
  81. case htons(ETH_P_IP):
  82. nft_bridge_set_pktinfo_ipv4(&pkt, skb, state);
  83. break;
  84. case htons(ETH_P_IPV6):
  85. nft_bridge_set_pktinfo_ipv6(&pkt, skb, state);
  86. break;
  87. default:
  88. nft_set_pktinfo(&pkt, skb, state);
  89. break;
  90. }
  91. return nft_do_chain(&pkt, priv);
  92. }
  93. static struct nft_af_info nft_af_bridge __read_mostly = {
  94. .family = NFPROTO_BRIDGE,
  95. .nhooks = NF_BR_NUMHOOKS,
  96. .owner = THIS_MODULE,
  97. .nops = 1,
  98. .hooks = {
  99. [NF_BR_PRE_ROUTING] = nft_do_chain_bridge,
  100. [NF_BR_LOCAL_IN] = nft_do_chain_bridge,
  101. [NF_BR_FORWARD] = nft_do_chain_bridge,
  102. [NF_BR_LOCAL_OUT] = nft_do_chain_bridge,
  103. [NF_BR_POST_ROUTING] = nft_do_chain_bridge,
  104. },
  105. };
  106. static int nf_tables_bridge_init_net(struct net *net)
  107. {
  108. net->nft.bridge = kmalloc(sizeof(struct nft_af_info), GFP_KERNEL);
  109. if (net->nft.bridge == NULL)
  110. return -ENOMEM;
  111. memcpy(net->nft.bridge, &nft_af_bridge, sizeof(nft_af_bridge));
  112. if (nft_register_afinfo(net, net->nft.bridge) < 0)
  113. goto err;
  114. return 0;
  115. err:
  116. kfree(net->nft.bridge);
  117. return -ENOMEM;
  118. }
  119. static void nf_tables_bridge_exit_net(struct net *net)
  120. {
  121. nft_unregister_afinfo(net->nft.bridge);
  122. kfree(net->nft.bridge);
  123. }
  124. static struct pernet_operations nf_tables_bridge_net_ops = {
  125. .init = nf_tables_bridge_init_net,
  126. .exit = nf_tables_bridge_exit_net,
  127. };
  128. static const struct nf_chain_type filter_bridge = {
  129. .name = "filter",
  130. .type = NFT_CHAIN_T_DEFAULT,
  131. .family = NFPROTO_BRIDGE,
  132. .owner = THIS_MODULE,
  133. .hook_mask = (1 << NF_BR_PRE_ROUTING) |
  134. (1 << NF_BR_LOCAL_IN) |
  135. (1 << NF_BR_FORWARD) |
  136. (1 << NF_BR_LOCAL_OUT) |
  137. (1 << NF_BR_POST_ROUTING),
  138. };
  139. static int __init nf_tables_bridge_init(void)
  140. {
  141. int ret;
  142. nft_register_chain_type(&filter_bridge);
  143. ret = register_pernet_subsys(&nf_tables_bridge_net_ops);
  144. if (ret < 0)
  145. nft_unregister_chain_type(&filter_bridge);
  146. return ret;
  147. }
  148. static void __exit nf_tables_bridge_exit(void)
  149. {
  150. unregister_pernet_subsys(&nf_tables_bridge_net_ops);
  151. nft_unregister_chain_type(&filter_bridge);
  152. }
  153. module_init(nf_tables_bridge_init);
  154. module_exit(nf_tables_bridge_exit);
  155. MODULE_LICENSE("GPL");
  156. MODULE_AUTHOR("Patrick McHardy <kaber@trash.net>");
  157. MODULE_ALIAS_NFT_FAMILY(AF_BRIDGE);