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. /* ipvs_mt_check ensures that family is only NFPROTO_IPV[46]. */
  46. const u_int8_t family = par->family;
  47. struct ip_vs_iphdr iph;
  48. struct ip_vs_protocol *pp;
  49. struct ip_vs_conn *cp;
  50. bool match = true;
  51. if (data->bitmask == XT_IPVS_IPVS_PROPERTY) {
  52. match = skb->ipvs_property ^
  53. !!(data->invert & XT_IPVS_IPVS_PROPERTY);
  54. goto out;
  55. }
  56. /* other flags than XT_IPVS_IPVS_PROPERTY are set */
  57. if (!skb->ipvs_property) {
  58. match = false;
  59. goto out;
  60. }
  61. ip_vs_fill_iphdr(family, skb_network_header(skb), &iph);
  62. if (data->bitmask & XT_IPVS_PROTO)
  63. if ((iph.protocol == data->l4proto) ^
  64. !(data->invert & XT_IPVS_PROTO)) {
  65. match = false;
  66. goto out;
  67. }
  68. pp = ip_vs_proto_get(iph.protocol);
  69. if (unlikely(!pp)) {
  70. match = false;
  71. goto out;
  72. }
  73. /*
  74. * Check if the packet belongs to an existing entry
  75. */
  76. cp = pp->conn_out_get(family, skb, &iph, iph.len, 1 /* inverse */);
  77. if (unlikely(cp == NULL)) {
  78. match = false;
  79. goto out;
  80. }
  81. /*
  82. * We found a connection, i.e. ct != 0, make sure to call
  83. * __ip_vs_conn_put before returning. In our case jump to out_put_con.
  84. */
  85. if (data->bitmask & XT_IPVS_VPORT)
  86. if ((cp->vport == data->vport) ^
  87. !(data->invert & XT_IPVS_VPORT)) {
  88. match = false;
  89. goto out_put_cp;
  90. }
  91. if (data->bitmask & XT_IPVS_VPORTCTL)
  92. if ((cp->control != NULL &&
  93. cp->control->vport == data->vportctl) ^
  94. !(data->invert & XT_IPVS_VPORTCTL)) {
  95. match = false;
  96. goto out_put_cp;
  97. }
  98. if (data->bitmask & XT_IPVS_DIR) {
  99. enum ip_conntrack_info ctinfo;
  100. struct nf_conn *ct = nf_ct_get(skb, &ctinfo);
  101. if (ct == NULL || nf_ct_is_untracked(ct)) {
  102. match = false;
  103. goto out_put_cp;
  104. }
  105. if ((ctinfo >= IP_CT_IS_REPLY) ^
  106. !!(data->invert & XT_IPVS_DIR)) {
  107. match = false;
  108. goto out_put_cp;
  109. }
  110. }
  111. if (data->bitmask & XT_IPVS_METHOD)
  112. if (((cp->flags & IP_VS_CONN_F_FWD_MASK) == data->fwd_method) ^
  113. !(data->invert & XT_IPVS_METHOD)) {
  114. match = false;
  115. goto out_put_cp;
  116. }
  117. if (data->bitmask & XT_IPVS_VADDR) {
  118. if (ipvs_mt_addrcmp(&cp->vaddr, &data->vaddr,
  119. &data->vmask, family) ^
  120. !(data->invert & XT_IPVS_VADDR)) {
  121. match = false;
  122. goto out_put_cp;
  123. }
  124. }
  125. out_put_cp:
  126. __ip_vs_conn_put(cp);
  127. out:
  128. pr_debug("match=%d\n", match);
  129. return match;
  130. }
  131. static int ipvs_mt_check(const struct xt_mtchk_param *par)
  132. {
  133. if (par->family != NFPROTO_IPV4
  134. #ifdef CONFIG_IP_VS_IPV6
  135. && par->family != NFPROTO_IPV6
  136. #endif
  137. ) {
  138. pr_info("protocol family %u not supported\n", par->family);
  139. return -EINVAL;
  140. }
  141. return 0;
  142. }
  143. static struct xt_match xt_ipvs_mt_reg __read_mostly = {
  144. .name = "ipvs",
  145. .revision = 0,
  146. .family = NFPROTO_UNSPEC,
  147. .match = ipvs_mt,
  148. .checkentry = ipvs_mt_check,
  149. .matchsize = XT_ALIGN(sizeof(struct xt_ipvs_mtinfo)),
  150. .me = THIS_MODULE,
  151. };
  152. static int __init ipvs_mt_init(void)
  153. {
  154. return xt_register_match(&xt_ipvs_mt_reg);
  155. }
  156. static void __exit ipvs_mt_exit(void)
  157. {
  158. xt_unregister_match(&xt_ipvs_mt_reg);
  159. }
  160. module_init(ipvs_mt_init);
  161. module_exit(ipvs_mt_exit);