linux/net/netfilter/xt_conntrack.c
<<
>>
Prefs
   1/*
   2 *      xt_conntrack - Netfilter module to match connection tracking
   3 *      information. (Superset of Rusty's minimalistic state match.)
   4 *
   5 *      (C) 2001  Marc Boucher (marc@mbsi.ca).
   6 *      (C) 2006-2012 Patrick McHardy <kaber@trash.net>
   7 *      Copyright © CC Computer Consultants GmbH, 2007 - 2008
   8 *
   9 *      This program is free software; you can redistribute it and/or modify
  10 *      it under the terms of the GNU General Public License version 2 as
  11 *      published by the Free Software Foundation.
  12 */
  13#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
  14#include <linux/module.h>
  15#include <linux/skbuff.h>
  16#include <net/ipv6.h>
  17#include <linux/netfilter/x_tables.h>
  18#include <linux/netfilter/xt_conntrack.h>
  19#include <net/netfilter/nf_conntrack.h>
  20
  21MODULE_LICENSE("GPL");
  22MODULE_AUTHOR("Marc Boucher <marc@mbsi.ca>");
  23MODULE_AUTHOR("Jan Engelhardt <jengelh@medozas.de>");
  24MODULE_DESCRIPTION("Xtables: connection tracking state match");
  25MODULE_ALIAS("ipt_conntrack");
  26MODULE_ALIAS("ip6t_conntrack");
  27
  28static bool
  29conntrack_addrcmp(const union nf_inet_addr *kaddr,
  30                  const union nf_inet_addr *uaddr,
  31                  const union nf_inet_addr *umask, unsigned int l3proto)
  32{
  33        if (l3proto == NFPROTO_IPV4)
  34                return ((kaddr->ip ^ uaddr->ip) & umask->ip) == 0;
  35        else if (l3proto == NFPROTO_IPV6)
  36                return ipv6_masked_addr_cmp(&kaddr->in6, &umask->in6,
  37                       &uaddr->in6) == 0;
  38        else
  39                return false;
  40}
  41
  42static inline bool
  43conntrack_mt_origsrc(const struct nf_conn *ct,
  44                     const struct xt_conntrack_mtinfo2 *info,
  45                     u_int8_t family)
  46{
  47        return conntrack_addrcmp(&ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.u3,
  48               &info->origsrc_addr, &info->origsrc_mask, family);
  49}
  50
  51static inline bool
  52conntrack_mt_origdst(const struct nf_conn *ct,
  53                     const struct xt_conntrack_mtinfo2 *info,
  54                     u_int8_t family)
  55{
  56        return conntrack_addrcmp(&ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.u3,
  57               &info->origdst_addr, &info->origdst_mask, family);
  58}
  59
  60static inline bool
  61conntrack_mt_replsrc(const struct nf_conn *ct,
  62                     const struct xt_conntrack_mtinfo2 *info,
  63                     u_int8_t family)
  64{
  65        return conntrack_addrcmp(&ct->tuplehash[IP_CT_DIR_REPLY].tuple.src.u3,
  66               &info->replsrc_addr, &info->replsrc_mask, family);
  67}
  68
  69static inline bool
  70conntrack_mt_repldst(const struct nf_conn *ct,
  71                     const struct xt_conntrack_mtinfo2 *info,
  72                     u_int8_t family)
  73{
  74        return conntrack_addrcmp(&ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.u3,
  75               &info->repldst_addr, &info->repldst_mask, family);
  76}
  77
  78static inline bool
  79ct_proto_port_check(const struct xt_conntrack_mtinfo2 *info,
  80                    const struct nf_conn *ct)
  81{
  82        const struct nf_conntrack_tuple *tuple;
  83
  84        tuple = &ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple;
  85        if ((info->match_flags & XT_CONNTRACK_PROTO) &&
  86            (nf_ct_protonum(ct) == info->l4proto) ^
  87            !(info->invert_flags & XT_CONNTRACK_PROTO))
  88                return false;
  89
  90        /* Shortcut to match all recognized protocols by using ->src.all. */
  91        if ((info->match_flags & XT_CONNTRACK_ORIGSRC_PORT) &&
  92            (tuple->src.u.all == info->origsrc_port) ^
  93            !(info->invert_flags & XT_CONNTRACK_ORIGSRC_PORT))
  94                return false;
  95
  96        if ((info->match_flags & XT_CONNTRACK_ORIGDST_PORT) &&
  97            (tuple->dst.u.all == info->origdst_port) ^
  98            !(info->invert_flags & XT_CONNTRACK_ORIGDST_PORT))
  99                return false;
 100
 101        tuple = &ct->tuplehash[IP_CT_DIR_REPLY].tuple;
 102
 103        if ((info->match_flags & XT_CONNTRACK_REPLSRC_PORT) &&
 104            (tuple->src.u.all == info->replsrc_port) ^
 105            !(info->invert_flags & XT_CONNTRACK_REPLSRC_PORT))
 106                return false;
 107
 108        if ((info->match_flags & XT_CONNTRACK_REPLDST_PORT) &&
 109            (tuple->dst.u.all == info->repldst_port) ^
 110            !(info->invert_flags & XT_CONNTRACK_REPLDST_PORT))
 111                return false;
 112
 113        return true;
 114}
 115
 116static inline bool
 117port_match(u16 min, u16 max, u16 port, bool invert)
 118{
 119        return (port >= min && port <= max) ^ invert;
 120}
 121
 122static inline bool
 123ct_proto_port_check_v3(const struct xt_conntrack_mtinfo3 *info,
 124                       const struct nf_conn *ct)
 125{
 126        const struct nf_conntrack_tuple *tuple;
 127
 128        tuple = &ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple;
 129        if ((info->match_flags & XT_CONNTRACK_PROTO) &&
 130            (nf_ct_protonum(ct) == info->l4proto) ^
 131            !(info->invert_flags & XT_CONNTRACK_PROTO))
 132                return false;
 133
 134        /* Shortcut to match all recognized protocols by using ->src.all. */
 135        if ((info->match_flags & XT_CONNTRACK_ORIGSRC_PORT) &&
 136            !port_match(info->origsrc_port, info->origsrc_port_high,
 137                        ntohs(tuple->src.u.all),
 138                        info->invert_flags & XT_CONNTRACK_ORIGSRC_PORT))
 139                return false;
 140
 141        if ((info->match_flags & XT_CONNTRACK_ORIGDST_PORT) &&
 142            !port_match(info->origdst_port, info->origdst_port_high,
 143                        ntohs(tuple->dst.u.all),
 144                        info->invert_flags & XT_CONNTRACK_ORIGDST_PORT))
 145                return false;
 146
 147        tuple = &ct->tuplehash[IP_CT_DIR_REPLY].tuple;
 148
 149        if ((info->match_flags & XT_CONNTRACK_REPLSRC_PORT) &&
 150            !port_match(info->replsrc_port, info->replsrc_port_high,
 151                        ntohs(tuple->src.u.all),
 152                        info->invert_flags & XT_CONNTRACK_REPLSRC_PORT))
 153                return false;
 154
 155        if ((info->match_flags & XT_CONNTRACK_REPLDST_PORT) &&
 156            !port_match(info->repldst_port, info->repldst_port_high,
 157                        ntohs(tuple->dst.u.all),
 158                        info->invert_flags & XT_CONNTRACK_REPLDST_PORT))
 159                return false;
 160
 161        return true;
 162}
 163
 164static bool
 165conntrack_mt(const struct sk_buff *skb, struct xt_action_param *par,
 166             u16 state_mask, u16 status_mask)
 167{
 168        const struct xt_conntrack_mtinfo2 *info = par->matchinfo;
 169        enum ip_conntrack_info ctinfo;
 170        const struct nf_conn *ct;
 171        unsigned int statebit;
 172
 173        ct = nf_ct_get(skb, &ctinfo);
 174
 175        if (ct)
 176                statebit = XT_CONNTRACK_STATE_BIT(ctinfo);
 177        else if (ctinfo == IP_CT_UNTRACKED)
 178                statebit = XT_CONNTRACK_STATE_UNTRACKED;
 179        else
 180                statebit = XT_CONNTRACK_STATE_INVALID;
 181
 182        if (info->match_flags & XT_CONNTRACK_STATE) {
 183                if (ct != NULL) {
 184                        if (test_bit(IPS_SRC_NAT_BIT, &ct->status))
 185                                statebit |= XT_CONNTRACK_STATE_SNAT;
 186                        if (test_bit(IPS_DST_NAT_BIT, &ct->status))
 187                                statebit |= XT_CONNTRACK_STATE_DNAT;
 188                }
 189                if (!!(state_mask & statebit) ^
 190                    !(info->invert_flags & XT_CONNTRACK_STATE))
 191                        return false;
 192        }
 193
 194        if (ct == NULL)
 195                return info->match_flags & XT_CONNTRACK_STATE;
 196        if ((info->match_flags & XT_CONNTRACK_DIRECTION) &&
 197            (CTINFO2DIR(ctinfo) == IP_CT_DIR_ORIGINAL) ^
 198            !(info->invert_flags & XT_CONNTRACK_DIRECTION))
 199                return false;
 200
 201        if (info->match_flags & XT_CONNTRACK_ORIGSRC)
 202                if (conntrack_mt_origsrc(ct, info, xt_family(par)) ^
 203                    !(info->invert_flags & XT_CONNTRACK_ORIGSRC))
 204                        return false;
 205
 206        if (info->match_flags & XT_CONNTRACK_ORIGDST)
 207                if (conntrack_mt_origdst(ct, info, xt_family(par)) ^
 208                    !(info->invert_flags & XT_CONNTRACK_ORIGDST))
 209                        return false;
 210
 211        if (info->match_flags & XT_CONNTRACK_REPLSRC)
 212                if (conntrack_mt_replsrc(ct, info, xt_family(par)) ^
 213                    !(info->invert_flags & XT_CONNTRACK_REPLSRC))
 214                        return false;
 215
 216        if (info->match_flags & XT_CONNTRACK_REPLDST)
 217                if (conntrack_mt_repldst(ct, info, xt_family(par)) ^
 218                    !(info->invert_flags & XT_CONNTRACK_REPLDST))
 219                        return false;
 220
 221        if (par->match->revision != 3) {
 222                if (!ct_proto_port_check(info, ct))
 223                        return false;
 224        } else {
 225                if (!ct_proto_port_check_v3(par->matchinfo, ct))
 226                        return false;
 227        }
 228
 229        if ((info->match_flags & XT_CONNTRACK_STATUS) &&
 230            (!!(status_mask & ct->status) ^
 231            !(info->invert_flags & XT_CONNTRACK_STATUS)))
 232                return false;
 233
 234        if (info->match_flags & XT_CONNTRACK_EXPIRES) {
 235                unsigned long expires = nf_ct_expires(ct) / HZ;
 236
 237                if ((expires >= info->expires_min &&
 238                    expires <= info->expires_max) ^
 239                    !(info->invert_flags & XT_CONNTRACK_EXPIRES))
 240                        return false;
 241        }
 242        return true;
 243}
 244
 245static bool
 246conntrack_mt_v1(const struct sk_buff *skb, struct xt_action_param *par)
 247{
 248        const struct xt_conntrack_mtinfo1 *info = par->matchinfo;
 249
 250        return conntrack_mt(skb, par, info->state_mask, info->status_mask);
 251}
 252
 253static bool
 254conntrack_mt_v2(const struct sk_buff *skb, struct xt_action_param *par)
 255{
 256        const struct xt_conntrack_mtinfo2 *info = par->matchinfo;
 257
 258        return conntrack_mt(skb, par, info->state_mask, info->status_mask);
 259}
 260
 261static bool
 262conntrack_mt_v3(const struct sk_buff *skb, struct xt_action_param *par)
 263{
 264        const struct xt_conntrack_mtinfo3 *info = par->matchinfo;
 265
 266        return conntrack_mt(skb, par, info->state_mask, info->status_mask);
 267}
 268
 269static int conntrack_mt_check(const struct xt_mtchk_param *par)
 270{
 271        int ret;
 272
 273        ret = nf_ct_netns_get(par->net, par->family);
 274        if (ret < 0)
 275                pr_info_ratelimited("cannot load conntrack support for proto=%u\n",
 276                                    par->family);
 277        return ret;
 278}
 279
 280static void conntrack_mt_destroy(const struct xt_mtdtor_param *par)
 281{
 282        nf_ct_netns_put(par->net, par->family);
 283}
 284
 285static struct xt_match conntrack_mt_reg[] __read_mostly = {
 286        {
 287                .name       = "conntrack",
 288                .revision   = 1,
 289                .family     = NFPROTO_UNSPEC,
 290                .matchsize  = sizeof(struct xt_conntrack_mtinfo1),
 291                .match      = conntrack_mt_v1,
 292                .checkentry = conntrack_mt_check,
 293                .destroy    = conntrack_mt_destroy,
 294                .me         = THIS_MODULE,
 295        },
 296        {
 297                .name       = "conntrack",
 298                .revision   = 2,
 299                .family     = NFPROTO_UNSPEC,
 300                .matchsize  = sizeof(struct xt_conntrack_mtinfo2),
 301                .match      = conntrack_mt_v2,
 302                .checkentry = conntrack_mt_check,
 303                .destroy    = conntrack_mt_destroy,
 304                .me         = THIS_MODULE,
 305        },
 306        {
 307                .name       = "conntrack",
 308                .revision   = 3,
 309                .family     = NFPROTO_UNSPEC,
 310                .matchsize  = sizeof(struct xt_conntrack_mtinfo3),
 311                .match      = conntrack_mt_v3,
 312                .checkentry = conntrack_mt_check,
 313                .destroy    = conntrack_mt_destroy,
 314                .me         = THIS_MODULE,
 315        },
 316};
 317
 318static int __init conntrack_mt_init(void)
 319{
 320        return xt_register_matches(conntrack_mt_reg,
 321               ARRAY_SIZE(conntrack_mt_reg));
 322}
 323
 324static void __exit conntrack_mt_exit(void)
 325{
 326        xt_unregister_matches(conntrack_mt_reg, ARRAY_SIZE(conntrack_mt_reg));
 327}
 328
 329module_init(conntrack_mt_init);
 330module_exit(conntrack_mt_exit);
 331