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