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