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