xt_ipvs.c 4.2 KB

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