xt_ipvs.c 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191
  1. // SPDX-License-Identifier: GPL-2.0-only
  2. /*
  3. * xt_ipvs - kernel module to match IPVS connection properties
  4. *
  5. * Author: Hannes Eder <heder@google.com>
  6. */
  7. #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
  8. #include <linux/module.h>
  9. #include <linux/moduleparam.h>
  10. #include <linux/spinlock.h>
  11. #include <linux/skbuff.h>
  12. #ifdef CONFIG_IP_VS_IPV6
  13. #include <net/ipv6.h>
  14. #endif
  15. #include <linux/ip_vs.h>
  16. #include <linux/types.h>
  17. #include <linux/netfilter/x_tables.h>
  18. #include <linux/netfilter/xt_ipvs.h>
  19. #include <net/netfilter/nf_conntrack.h>
  20. #include <net/ip_vs.h>
  21. MODULE_AUTHOR("Hannes Eder <heder@google.com>");
  22. MODULE_DESCRIPTION("Xtables: match IPVS connection properties");
  23. MODULE_LICENSE("GPL");
  24. MODULE_ALIAS("ipt_ipvs");
  25. MODULE_ALIAS("ip6t_ipvs");
  26. /* borrowed from xt_conntrack */
  27. static bool ipvs_mt_addrcmp(const union nf_inet_addr *kaddr,
  28. const union nf_inet_addr *uaddr,
  29. const union nf_inet_addr *umask,
  30. unsigned int l3proto)
  31. {
  32. if (l3proto == NFPROTO_IPV4)
  33. return ((kaddr->ip ^ uaddr->ip) & umask->ip) == 0;
  34. #ifdef CONFIG_IP_VS_IPV6
  35. else if (l3proto == NFPROTO_IPV6)
  36. return ipv6_masked_addr_cmp(&kaddr->in6, &umask->in6,
  37. &uaddr->in6) == 0;
  38. #endif
  39. else
  40. return false;
  41. }
  42. static bool
  43. ipvs_mt(const struct sk_buff *skb, struct xt_action_param *par)
  44. {
  45. const struct xt_ipvs_mtinfo *data = par->matchinfo;
  46. struct netns_ipvs *ipvs = net_ipvs(xt_net(par));
  47. /* ipvs_mt_check ensures that family is only NFPROTO_IPV[46]. */
  48. const u_int8_t family = xt_family(par);
  49. struct ip_vs_iphdr iph;
  50. struct ip_vs_protocol *pp;
  51. struct ip_vs_conn *cp;
  52. bool match = true;
  53. if (data->bitmask == XT_IPVS_IPVS_PROPERTY) {
  54. match = skb->ipvs_property ^
  55. !!(data->invert & XT_IPVS_IPVS_PROPERTY);
  56. goto out;
  57. }
  58. /* other flags than XT_IPVS_IPVS_PROPERTY are set */
  59. if (!skb->ipvs_property) {
  60. match = false;
  61. goto out;
  62. }
  63. ip_vs_fill_iph_skb(family, skb, true, &iph);
  64. if (data->bitmask & XT_IPVS_PROTO)
  65. if ((iph.protocol == data->l4proto) ^
  66. !(data->invert & XT_IPVS_PROTO)) {
  67. match = false;
  68. goto out;
  69. }
  70. pp = ip_vs_proto_get(iph.protocol);
  71. if (unlikely(!pp)) {
  72. match = false;
  73. goto out;
  74. }
  75. /*
  76. * Check if the packet belongs to an existing entry
  77. */
  78. cp = pp->conn_out_get(ipvs, family, skb, &iph);
  79. if (unlikely(cp == NULL)) {
  80. match = false;
  81. goto out;
  82. }
  83. /*
  84. * We found a connection, i.e. ct != 0, make sure to call
  85. * __ip_vs_conn_put before returning. In our case jump to out_put_con.
  86. */
  87. if (data->bitmask & XT_IPVS_VPORT)
  88. if ((cp->vport == data->vport) ^
  89. !(data->invert & XT_IPVS_VPORT)) {
  90. match = false;
  91. goto out_put_cp;
  92. }
  93. if (data->bitmask & XT_IPVS_VPORTCTL)
  94. if ((cp->control != NULL &&
  95. cp->control->vport == data->vportctl) ^
  96. !(data->invert & XT_IPVS_VPORTCTL)) {
  97. match = false;
  98. goto out_put_cp;
  99. }
  100. if (data->bitmask & XT_IPVS_DIR) {
  101. enum ip_conntrack_info ctinfo;
  102. struct nf_conn *ct = nf_ct_get(skb, &ctinfo);
  103. if (ct == NULL) {
  104. match = false;
  105. goto out_put_cp;
  106. }
  107. if ((ctinfo >= IP_CT_IS_REPLY) ^
  108. !!(data->invert & XT_IPVS_DIR)) {
  109. match = false;
  110. goto out_put_cp;
  111. }
  112. }
  113. if (data->bitmask & XT_IPVS_METHOD)
  114. if (((cp->flags & IP_VS_CONN_F_FWD_MASK) == data->fwd_method) ^
  115. !(data->invert & XT_IPVS_METHOD)) {
  116. match = false;
  117. goto out_put_cp;
  118. }
  119. if (data->bitmask & XT_IPVS_VADDR) {
  120. if (ipvs_mt_addrcmp(&cp->vaddr, &data->vaddr,
  121. &data->vmask, family) ^
  122. !(data->invert & XT_IPVS_VADDR)) {
  123. match = false;
  124. goto out_put_cp;
  125. }
  126. }
  127. out_put_cp:
  128. __ip_vs_conn_put(cp);
  129. out:
  130. pr_debug("match=%d\n", match);
  131. return match;
  132. }
  133. static int ipvs_mt_check(const struct xt_mtchk_param *par)
  134. {
  135. if (par->family != NFPROTO_IPV4
  136. #ifdef CONFIG_IP_VS_IPV6
  137. && par->family != NFPROTO_IPV6
  138. #endif
  139. ) {
  140. pr_info_ratelimited("protocol family %u not supported\n",
  141. par->family);
  142. return -EINVAL;
  143. }
  144. return 0;
  145. }
  146. static struct xt_match xt_ipvs_mt_reg __read_mostly = {
  147. .name = "ipvs",
  148. .revision = 0,
  149. .family = NFPROTO_UNSPEC,
  150. .match = ipvs_mt,
  151. .checkentry = ipvs_mt_check,
  152. .matchsize = XT_ALIGN(sizeof(struct xt_ipvs_mtinfo)),
  153. .me = THIS_MODULE,
  154. };
  155. static int __init ipvs_mt_init(void)
  156. {
  157. return xt_register_match(&xt_ipvs_mt_reg);
  158. }
  159. static void __exit ipvs_mt_exit(void)
  160. {
  161. xt_unregister_match(&xt_ipvs_mt_reg);
  162. }
  163. module_init(ipvs_mt_init);
  164. module_exit(ipvs_mt_exit);