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 24 MODULE_AUTHOR("Hannes Eder <heder@google.com>"); 25 MODULE_DESCRIPTION("Xtables: match IPVS connection properties"); 26 MODULE_LICENSE("GPL"); 27 MODULE_ALIAS("ipt_ipvs"); 28 MODULE_ALIAS("ip6t_ipvs"); 29 30 /* borrowed from xt_conntrack */ 31 static 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 47 static bool 48 ipvs_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 146 out_put_cp: 147 __ip_vs_conn_put(cp); 148 out: 149 pr_debug("match=%d\n", match); 150 return match; 151 } 152 153 static 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 167 static 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 177 static int __init ipvs_mt_init(void) 178 { 179 return xt_register_match(&xt_ipvs_mt_reg); 180 } 181 182 static void __exit ipvs_mt_exit(void) 183 { 184 xt_unregister_match(&xt_ipvs_mt_reg); 185 } 186 187 module_init(ipvs_mt_init); 188 module_exit(ipvs_mt_exit); 189