linux/net/netfilter/xt_ipvs.c
<<
>>
Prefs
   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