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                if (nf_ct_is_untracked(ct))
 177                        statebit = XT_CONNTRACK_STATE_UNTRACKED;
 178                else
 179                        statebit = XT_CONNTRACK_STATE_BIT(ctinfo);
 180        } else
 181                statebit = XT_CONNTRACK_STATE_INVALID;
 182
 183        if (info->match_flags & XT_CONNTRACK_STATE) {
 184                if (ct != NULL) {
 185                        if (test_bit(IPS_SRC_NAT_BIT, &ct->status))
 186                                statebit |= XT_CONNTRACK_STATE_SNAT;
 187                        if (test_bit(IPS_DST_NAT_BIT, &ct->status))
 188                                statebit |= XT_CONNTRACK_STATE_DNAT;
 189                }
 190                if (!!(state_mask & statebit) ^
 191                    !(info->invert_flags & XT_CONNTRACK_STATE))
 192                        return false;
 193        }
 194
 195        if (ct == NULL)
 196                return info->match_flags & XT_CONNTRACK_STATE;
 197        if ((info->match_flags & XT_CONNTRACK_DIRECTION) &&
 198            (CTINFO2DIR(ctinfo) == IP_CT_DIR_ORIGINAL) ^
 199            !(info->invert_flags & XT_CONNTRACK_DIRECTION))
 200                return false;
 201
 202        if (info->match_flags & XT_CONNTRACK_ORIGSRC)
 203                if (conntrack_mt_origsrc(ct, info, par->family) ^
 204                    !(info->invert_flags & XT_CONNTRACK_ORIGSRC))
 205                        return false;
 206
 207        if (info->match_flags & XT_CONNTRACK_ORIGDST)
 208                if (conntrack_mt_origdst(ct, info, par->family) ^
 209                    !(info->invert_flags & XT_CONNTRACK_ORIGDST))
 210                        return false;
 211
 212        if (info->match_flags & XT_CONNTRACK_REPLSRC)
 213                if (conntrack_mt_replsrc(ct, info, par->family) ^
 214                    !(info->invert_flags & XT_CONNTRACK_REPLSRC))
 215                        return false;
 216
 217        if (info->match_flags & XT_CONNTRACK_REPLDST)
 218                if (conntrack_mt_repldst(ct, info, par->family) ^
 219                    !(info->invert_flags & XT_CONNTRACK_REPLDST))
 220                        return false;
 221
 222        if (par->match->revision != 3) {
 223                if (!ct_proto_port_check(info, ct))
 224                        return false;
 225        } else {
 226                if (!ct_proto_port_check_v3(par->matchinfo, ct))
 227                        return false;
 228        }
 229
 230        if ((info->match_flags & XT_CONNTRACK_STATUS) &&
 231            (!!(status_mask & ct->status) ^
 232            !(info->invert_flags & XT_CONNTRACK_STATUS)))
 233                return false;
 234
 235        if (info->match_flags & XT_CONNTRACK_EXPIRES) {
 236                unsigned long expires = 0;
 237
 238                if (timer_pending(&ct->timeout))
 239                        expires = (ct->timeout.expires - jiffies) / HZ;
 240                if ((expires >= info->expires_min &&
 241                    expires <= info->expires_max) ^
 242                    !(info->invert_flags & XT_CONNTRACK_EXPIRES))
 243                        return false;
 244        }
 245        return true;
 246}
 247
 248static bool
 249conntrack_mt_v1(const struct sk_buff *skb, struct xt_action_param *par)
 250{
 251        const struct xt_conntrack_mtinfo1 *info = par->matchinfo;
 252
 253        return conntrack_mt(skb, par, info->state_mask, info->status_mask);
 254}
 255
 256static bool
 257conntrack_mt_v2(const struct sk_buff *skb, struct xt_action_param *par)
 258{
 259        const struct xt_conntrack_mtinfo2 *info = par->matchinfo;
 260
 261        return conntrack_mt(skb, par, info->state_mask, info->status_mask);
 262}
 263
 264static bool
 265conntrack_mt_v3(const struct sk_buff *skb, struct xt_action_param *par)
 266{
 267        const struct xt_conntrack_mtinfo3 *info = par->matchinfo;
 268
 269        return conntrack_mt(skb, par, info->state_mask, info->status_mask);
 270}
 271
 272static int conntrack_mt_check(const struct xt_mtchk_param *par)
 273{
 274        int ret;
 275
 276        ret = nf_ct_l3proto_try_module_get(par->family);
 277        if (ret < 0)
 278                pr_info("cannot load conntrack support for proto=%u\n",
 279                        par->family);
 280        return ret;
 281}
 282
 283static void conntrack_mt_destroy(const struct xt_mtdtor_param *par)
 284{
 285        nf_ct_l3proto_module_put(par->family);
 286}
 287
 288static struct xt_match conntrack_mt_reg[] __read_mostly = {
 289        {
 290                .name       = "conntrack",
 291                .revision   = 1,
 292                .family     = NFPROTO_UNSPEC,
 293                .matchsize  = sizeof(struct xt_conntrack_mtinfo1),
 294                .match      = conntrack_mt_v1,
 295                .checkentry = conntrack_mt_check,
 296                .destroy    = conntrack_mt_destroy,
 297                .me         = THIS_MODULE,
 298        },
 299        {
 300                .name       = "conntrack",
 301                .revision   = 2,
 302                .family     = NFPROTO_UNSPEC,
 303                .matchsize  = sizeof(struct xt_conntrack_mtinfo2),
 304                .match      = conntrack_mt_v2,
 305                .checkentry = conntrack_mt_check,
 306                .destroy    = conntrack_mt_destroy,
 307                .me         = THIS_MODULE,
 308        },
 309        {
 310                .name       = "conntrack",
 311                .revision   = 3,
 312                .family     = NFPROTO_UNSPEC,
 313                .matchsize  = sizeof(struct xt_conntrack_mtinfo3),
 314                .match      = conntrack_mt_v3,
 315                .checkentry = conntrack_mt_check,
 316                .destroy    = conntrack_mt_destroy,
 317                .me         = THIS_MODULE,
 318        },
 319};
 320
 321static int __init conntrack_mt_init(void)
 322{
 323        return xt_register_matches(conntrack_mt_reg,
 324               ARRAY_SIZE(conntrack_mt_reg));
 325}
 326
 327static void __exit conntrack_mt_exit(void)
 328{
 329        xt_unregister_matches(conntrack_mt_reg, ARRAY_SIZE(conntrack_mt_reg));
 330}
 331
 332module_init(conntrack_mt_init);
 333module_exit(conntrack_mt_exit);
 334