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/x_tables.h> 20#include <linux/netfilter/xt_ipvs.h> 21#include <net/netfilter/nf_conntrack.h> 22 23#include <net/ip_vs.h> 24 25MODULE_AUTHOR("Hannes Eder <heder@google.com>"); 26MODULE_DESCRIPTION("Xtables: match IPVS connection properties"); 27MODULE_LICENSE("GPL"); 28MODULE_ALIAS("ipt_ipvs"); 29MODULE_ALIAS("ip6t_ipvs"); 30 31/* borrowed from xt_conntrack */ 32static bool ipvs_mt_addrcmp(const union nf_inet_addr *kaddr, 33 const union nf_inet_addr *uaddr, 34 const union nf_inet_addr *umask, 35 unsigned int l3proto) 36{ 37 if (l3proto == NFPROTO_IPV4) 38 return ((kaddr->ip ^ uaddr->ip) & umask->ip) == 0; 39#ifdef CONFIG_IP_VS_IPV6 40 else if (l3proto == NFPROTO_IPV6) 41 return ipv6_masked_addr_cmp(&kaddr->in6, &umask->in6, 42 &uaddr->in6) == 0; 43#endif 44 else 45 return false; 46} 47 48static bool 49ipvs_mt(const struct sk_buff *skb, struct xt_action_param *par) 50{ 51 const struct xt_ipvs_mtinfo *data = par->matchinfo; 52 /* ipvs_mt_check ensures that family is only NFPROTO_IPV[46]. */ 53 const u_int8_t family = par->family; 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_iphdr(family, skb_network_header(skb), &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(family, skb, pp, &iph, iph.len, 1 /* inverse */); 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 147out_put_cp: 148 __ip_vs_conn_put(cp); 149out: 150 pr_debug("match=%d\n", match); 151 return match; 152} 153 154static 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 168static 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 178static int __init ipvs_mt_init(void) 179{ 180 return xt_register_match(&xt_ipvs_mt_reg); 181} 182 183static void __exit ipvs_mt_exit(void) 184{ 185 xt_unregister_match(&xt_ipvs_mt_reg); 186} 187 188module_init(ipvs_mt_init); 189module_exit(ipvs_mt_exit); 190