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 *      Copyright \xC2\xA9 CC Computer Consultants GmbH, 2007 - 2008
   7 *
   8 *      This program is free software; you can redistribute it and/or modify
   9 *      it under the terms of the GNU General Public License version 2 as
  10 *      published by the Free Software Foundation.
  11 */
  12
  13#include <linux/module.h>
  14#include <linux/skbuff.h>
  15#include <net/ipv6.h>
  16#include <linux/netfilter/x_tables.h>
  17#include <linux/netfilter/xt_conntrack.h>
  18#include <net/netfilter/nf_conntrack.h>
  19
  20MODULE_LICENSE("GPL");
  21MODULE_AUTHOR("Marc Boucher <marc@mbsi.ca>");
  22MODULE_AUTHOR("Jan Engelhardt <jengelh@computergmbh.de>");
  23MODULE_DESCRIPTION("Xtables: connection tracking state match");
  24MODULE_ALIAS("ipt_conntrack");
  25MODULE_ALIAS("ip6t_conntrack");
  26
  27static bool
  28conntrack_mt_v0(const struct sk_buff *skb, const struct xt_match_param *par)
  29{
  30        const struct xt_conntrack_info *sinfo = par->matchinfo;
  31        const struct nf_conn *ct;
  32        enum ip_conntrack_info ctinfo;
  33        unsigned int statebit;
  34
  35        ct = nf_ct_get(skb, &ctinfo);
  36
  37#define FWINV(bool, invflg) ((bool) ^ !!(sinfo->invflags & (invflg)))
  38
  39        if (ct == &nf_conntrack_untracked)
  40                statebit = XT_CONNTRACK_STATE_UNTRACKED;
  41        else if (ct)
  42                statebit = XT_CONNTRACK_STATE_BIT(ctinfo);
  43        else
  44                statebit = XT_CONNTRACK_STATE_INVALID;
  45
  46        if (sinfo->flags & XT_CONNTRACK_STATE) {
  47                if (ct) {
  48                        if (test_bit(IPS_SRC_NAT_BIT, &ct->status))
  49                                statebit |= XT_CONNTRACK_STATE_SNAT;
  50                        if (test_bit(IPS_DST_NAT_BIT, &ct->status))
  51                                statebit |= XT_CONNTRACK_STATE_DNAT;
  52                }
  53                if (FWINV((statebit & sinfo->statemask) == 0,
  54                          XT_CONNTRACK_STATE))
  55                        return false;
  56        }
  57
  58        if (ct == NULL) {
  59                if (sinfo->flags & ~XT_CONNTRACK_STATE)
  60                        return false;
  61                return true;
  62        }
  63
  64        if (sinfo->flags & XT_CONNTRACK_PROTO &&
  65            FWINV(nf_ct_protonum(ct) !=
  66                  sinfo->tuple[IP_CT_DIR_ORIGINAL].dst.protonum,
  67                  XT_CONNTRACK_PROTO))
  68                return false;
  69
  70        if (sinfo->flags & XT_CONNTRACK_ORIGSRC &&
  71            FWINV((ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.u3.ip &
  72                   sinfo->sipmsk[IP_CT_DIR_ORIGINAL].s_addr) !=
  73                  sinfo->tuple[IP_CT_DIR_ORIGINAL].src.ip,
  74                  XT_CONNTRACK_ORIGSRC))
  75                return false;
  76
  77        if (sinfo->flags & XT_CONNTRACK_ORIGDST &&
  78            FWINV((ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.u3.ip &
  79                   sinfo->dipmsk[IP_CT_DIR_ORIGINAL].s_addr) !=
  80                  sinfo->tuple[IP_CT_DIR_ORIGINAL].dst.ip,
  81                  XT_CONNTRACK_ORIGDST))
  82                return false;
  83
  84        if (sinfo->flags & XT_CONNTRACK_REPLSRC &&
  85            FWINV((ct->tuplehash[IP_CT_DIR_REPLY].tuple.src.u3.ip &
  86                   sinfo->sipmsk[IP_CT_DIR_REPLY].s_addr) !=
  87                  sinfo->tuple[IP_CT_DIR_REPLY].src.ip,
  88                  XT_CONNTRACK_REPLSRC))
  89                return false;
  90
  91        if (sinfo->flags & XT_CONNTRACK_REPLDST &&
  92            FWINV((ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.u3.ip &
  93                   sinfo->dipmsk[IP_CT_DIR_REPLY].s_addr) !=
  94                  sinfo->tuple[IP_CT_DIR_REPLY].dst.ip,
  95                  XT_CONNTRACK_REPLDST))
  96                return false;
  97
  98        if (sinfo->flags & XT_CONNTRACK_STATUS &&
  99            FWINV((ct->status & sinfo->statusmask) == 0,
 100                  XT_CONNTRACK_STATUS))
 101                return false;
 102
 103        if(sinfo->flags & XT_CONNTRACK_EXPIRES) {
 104                unsigned long expires = timer_pending(&ct->timeout) ?
 105                                        (ct->timeout.expires - jiffies)/HZ : 0;
 106
 107                if (FWINV(!(expires >= sinfo->expires_min &&
 108                            expires <= sinfo->expires_max),
 109                          XT_CONNTRACK_EXPIRES))
 110                        return false;
 111        }
 112        return true;
 113#undef FWINV
 114}
 115
 116static bool
 117conntrack_addrcmp(const union nf_inet_addr *kaddr,
 118                  const union nf_inet_addr *uaddr,
 119                  const union nf_inet_addr *umask, unsigned int l3proto)
 120{
 121        if (l3proto == NFPROTO_IPV4)
 122                return ((kaddr->ip ^ uaddr->ip) & umask->ip) == 0;
 123        else if (l3proto == NFPROTO_IPV6)
 124                return ipv6_masked_addr_cmp(&kaddr->in6, &umask->in6,
 125                       &uaddr->in6) == 0;
 126        else
 127                return false;
 128}
 129
 130static inline bool
 131conntrack_mt_origsrc(const struct nf_conn *ct,
 132                     const struct xt_conntrack_mtinfo1 *info,
 133                     u_int8_t family)
 134{
 135        return conntrack_addrcmp(&ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.src.u3,
 136               &info->origsrc_addr, &info->origsrc_mask, family);
 137}
 138
 139static inline bool
 140conntrack_mt_origdst(const struct nf_conn *ct,
 141                     const struct xt_conntrack_mtinfo1 *info,
 142                     u_int8_t family)
 143{
 144        return conntrack_addrcmp(&ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple.dst.u3,
 145               &info->origdst_addr, &info->origdst_mask, family);
 146}
 147
 148static inline bool
 149conntrack_mt_replsrc(const struct nf_conn *ct,
 150                     const struct xt_conntrack_mtinfo1 *info,
 151                     u_int8_t family)
 152{
 153        return conntrack_addrcmp(&ct->tuplehash[IP_CT_DIR_REPLY].tuple.src.u3,
 154               &info->replsrc_addr, &info->replsrc_mask, family);
 155}
 156
 157static inline bool
 158conntrack_mt_repldst(const struct nf_conn *ct,
 159                     const struct xt_conntrack_mtinfo1 *info,
 160                     u_int8_t family)
 161{
 162        return conntrack_addrcmp(&ct->tuplehash[IP_CT_DIR_REPLY].tuple.dst.u3,
 163               &info->repldst_addr, &info->repldst_mask, family);
 164}
 165
 166static inline bool
 167ct_proto_port_check(const struct xt_conntrack_mtinfo1 *info,
 168                    const struct nf_conn *ct)
 169{
 170        const struct nf_conntrack_tuple *tuple;
 171
 172        tuple = &ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple;
 173        if ((info->match_flags & XT_CONNTRACK_PROTO) &&
 174            (nf_ct_protonum(ct) == info->l4proto) ^
 175            !(info->invert_flags & XT_CONNTRACK_PROTO))
 176                return false;
 177
 178        /* Shortcut to match all recognized protocols by using ->src.all. */
 179        if ((info->match_flags & XT_CONNTRACK_ORIGSRC_PORT) &&
 180            (tuple->src.u.all == info->origsrc_port) ^
 181            !(info->invert_flags & XT_CONNTRACK_ORIGSRC_PORT))
 182                return false;
 183
 184        if ((info->match_flags & XT_CONNTRACK_ORIGDST_PORT) &&
 185            (tuple->dst.u.all == info->origdst_port) ^
 186            !(info->invert_flags & XT_CONNTRACK_ORIGDST_PORT))
 187                return false;
 188
 189        tuple = &ct->tuplehash[IP_CT_DIR_REPLY].tuple;
 190
 191        if ((info->match_flags & XT_CONNTRACK_REPLSRC_PORT) &&
 192            (tuple->src.u.all == info->replsrc_port) ^
 193            !(info->invert_flags & XT_CONNTRACK_REPLSRC_PORT))
 194                return false;
 195
 196        if ((info->match_flags & XT_CONNTRACK_REPLDST_PORT) &&
 197            (tuple->dst.u.all == info->repldst_port) ^
 198            !(info->invert_flags & XT_CONNTRACK_REPLDST_PORT))
 199                return false;
 200
 201        return true;
 202}
 203
 204static bool
 205conntrack_mt(const struct sk_buff *skb, const struct xt_match_param *par)
 206{
 207        const struct xt_conntrack_mtinfo1 *info = par->matchinfo;
 208        enum ip_conntrack_info ctinfo;
 209        const struct nf_conn *ct;
 210        unsigned int statebit;
 211
 212        ct = nf_ct_get(skb, &ctinfo);
 213
 214        if (ct == &nf_conntrack_untracked)
 215                statebit = XT_CONNTRACK_STATE_UNTRACKED;
 216        else if (ct != NULL)
 217                statebit = XT_CONNTRACK_STATE_BIT(ctinfo);
 218        else
 219                statebit = XT_CONNTRACK_STATE_INVALID;
 220
 221        if (info->match_flags & XT_CONNTRACK_STATE) {
 222                if (ct != NULL) {
 223                        if (test_bit(IPS_SRC_NAT_BIT, &ct->status))
 224                                statebit |= XT_CONNTRACK_STATE_SNAT;
 225                        if (test_bit(IPS_DST_NAT_BIT, &ct->status))
 226                                statebit |= XT_CONNTRACK_STATE_DNAT;
 227                }
 228                if (!!(info->state_mask & statebit) ^
 229                    !(info->invert_flags & XT_CONNTRACK_STATE))
 230                        return false;
 231        }
 232
 233        if (ct == NULL)
 234                return info->match_flags & XT_CONNTRACK_STATE;
 235        if ((info->match_flags & XT_CONNTRACK_DIRECTION) &&
 236            (CTINFO2DIR(ctinfo) == IP_CT_DIR_ORIGINAL) ^
 237            !!(info->invert_flags & XT_CONNTRACK_DIRECTION))
 238                return false;
 239
 240        if (info->match_flags & XT_CONNTRACK_ORIGSRC)
 241                if (conntrack_mt_origsrc(ct, info, par->family) ^
 242                    !(info->invert_flags & XT_CONNTRACK_ORIGSRC))
 243                        return false;
 244
 245        if (info->match_flags & XT_CONNTRACK_ORIGDST)
 246                if (conntrack_mt_origdst(ct, info, par->family) ^
 247                    !(info->invert_flags & XT_CONNTRACK_ORIGDST))
 248                        return false;
 249
 250        if (info->match_flags & XT_CONNTRACK_REPLSRC)
 251                if (conntrack_mt_replsrc(ct, info, par->family) ^
 252                    !(info->invert_flags & XT_CONNTRACK_REPLSRC))
 253                        return false;
 254
 255        if (info->match_flags & XT_CONNTRACK_REPLDST)
 256                if (conntrack_mt_repldst(ct, info, par->family) ^
 257                    !(info->invert_flags & XT_CONNTRACK_REPLDST))
 258                        return false;
 259
 260        if (!ct_proto_port_check(info, ct))
 261                return false;
 262
 263        if ((info->match_flags & XT_CONNTRACK_STATUS) &&
 264            (!!(info->status_mask & ct->status) ^
 265            !(info->invert_flags & XT_CONNTRACK_STATUS)))
 266                return false;
 267
 268        if (info->match_flags & XT_CONNTRACK_EXPIRES) {
 269                unsigned long expires = 0;
 270
 271                if (timer_pending(&ct->timeout))
 272                        expires = (ct->timeout.expires - jiffies) / HZ;
 273                if ((expires >= info->expires_min &&
 274                    expires <= info->expires_max) ^
 275                    !(info->invert_flags & XT_CONNTRACK_EXPIRES))
 276                        return false;
 277        }
 278        return true;
 279}
 280
 281static bool conntrack_mt_check(const struct xt_mtchk_param *par)
 282{
 283        if (nf_ct_l3proto_try_module_get(par->family) < 0) {
 284                printk(KERN_WARNING "can't load conntrack support for "
 285                                    "proto=%u\n", par->family);
 286                return false;
 287        }
 288        return true;
 289}
 290
 291static void conntrack_mt_destroy(const struct xt_mtdtor_param *par)
 292{
 293        nf_ct_l3proto_module_put(par->family);
 294}
 295
 296#ifdef CONFIG_COMPAT
 297struct compat_xt_conntrack_info
 298{
 299        compat_uint_t                   statemask;
 300        compat_uint_t                   statusmask;
 301        struct ip_conntrack_old_tuple   tuple[IP_CT_DIR_MAX];
 302        struct in_addr                  sipmsk[IP_CT_DIR_MAX];
 303        struct in_addr                  dipmsk[IP_CT_DIR_MAX];
 304        compat_ulong_t                  expires_min;
 305        compat_ulong_t                  expires_max;
 306        u_int8_t                        flags;
 307        u_int8_t                        invflags;
 308};
 309
 310static void conntrack_mt_compat_from_user_v0(void *dst, void *src)
 311{
 312        const struct compat_xt_conntrack_info *cm = src;
 313        struct xt_conntrack_info m = {
 314                .statemask      = cm->statemask,
 315                .statusmask     = cm->statusmask,
 316                .expires_min    = cm->expires_min,
 317                .expires_max    = cm->expires_max,
 318                .flags          = cm->flags,
 319                .invflags       = cm->invflags,
 320        };
 321        memcpy(m.tuple, cm->tuple, sizeof(m.tuple));
 322        memcpy(m.sipmsk, cm->sipmsk, sizeof(m.sipmsk));
 323        memcpy(m.dipmsk, cm->dipmsk, sizeof(m.dipmsk));
 324        memcpy(dst, &m, sizeof(m));
 325}
 326
 327static int conntrack_mt_compat_to_user_v0(void __user *dst, void *src)
 328{
 329        const struct xt_conntrack_info *m = src;
 330        struct compat_xt_conntrack_info cm = {
 331                .statemask      = m->statemask,
 332                .statusmask     = m->statusmask,
 333                .expires_min    = m->expires_min,
 334                .expires_max    = m->expires_max,
 335                .flags          = m->flags,
 336                .invflags       = m->invflags,
 337        };
 338        memcpy(cm.tuple, m->tuple, sizeof(cm.tuple));
 339        memcpy(cm.sipmsk, m->sipmsk, sizeof(cm.sipmsk));
 340        memcpy(cm.dipmsk, m->dipmsk, sizeof(cm.dipmsk));
 341        return copy_to_user(dst, &cm, sizeof(cm)) ? -EFAULT : 0;
 342}
 343#endif
 344
 345static struct xt_match conntrack_mt_reg[] __read_mostly = {
 346        {
 347                .name       = "conntrack",
 348                .revision   = 0,
 349                .family     = NFPROTO_IPV4,
 350                .match      = conntrack_mt_v0,
 351                .checkentry = conntrack_mt_check,
 352                .destroy    = conntrack_mt_destroy,
 353                .matchsize  = sizeof(struct xt_conntrack_info),
 354                .me         = THIS_MODULE,
 355#ifdef CONFIG_COMPAT
 356                .compatsize       = sizeof(struct compat_xt_conntrack_info),
 357                .compat_from_user = conntrack_mt_compat_from_user_v0,
 358                .compat_to_user   = conntrack_mt_compat_to_user_v0,
 359#endif
 360        },
 361        {
 362                .name       = "conntrack",
 363                .revision   = 1,
 364                .family     = NFPROTO_UNSPEC,
 365                .matchsize  = sizeof(struct xt_conntrack_mtinfo1),
 366                .match      = conntrack_mt,
 367                .checkentry = conntrack_mt_check,
 368                .destroy    = conntrack_mt_destroy,
 369                .me         = THIS_MODULE,
 370        },
 371};
 372
 373static int __init conntrack_mt_init(void)
 374{
 375        return xt_register_matches(conntrack_mt_reg,
 376               ARRAY_SIZE(conntrack_mt_reg));
 377}
 378
 379static void __exit conntrack_mt_exit(void)
 380{
 381        xt_unregister_matches(conntrack_mt_reg, ARRAY_SIZE(conntrack_mt_reg));
 382}
 383
 384module_init(conntrack_mt_init);
 385module_exit(conntrack_mt_exit);
 386