linux/net/netfilter/nft_ct.c
<<
>>
Prefs
   1/*
   2 * Copyright (c) 2008-2009 Patrick McHardy <kaber@trash.net>
   3 *
   4 * This program is free software; you can redistribute it and/or modify
   5 * it under the terms of the GNU General Public License version 2 as
   6 * published by the Free Software Foundation.
   7 *
   8 * Development of this code funded by Astaro AG (http://www.astaro.com/)
   9 */
  10
  11#include <linux/kernel.h>
  12#include <linux/init.h>
  13#include <linux/module.h>
  14#include <linux/netlink.h>
  15#include <linux/netfilter.h>
  16#include <linux/netfilter/nf_tables.h>
  17#include <net/netfilter/nf_tables.h>
  18#include <net/netfilter/nf_conntrack.h>
  19#include <net/netfilter/nf_conntrack_tuple.h>
  20#include <net/netfilter/nf_conntrack_helper.h>
  21#include <net/netfilter/nf_conntrack_ecache.h>
  22#include <net/netfilter/nf_conntrack_labels.h>
  23
  24struct nft_ct {
  25        enum nft_ct_keys        key:8;
  26        enum ip_conntrack_dir   dir:8;
  27        union {
  28                enum nft_registers      dreg:8;
  29                enum nft_registers      sreg:8;
  30        };
  31};
  32
  33static void nft_ct_get_eval(const struct nft_expr *expr,
  34                            struct nft_data data[NFT_REG_MAX + 1],
  35                            const struct nft_pktinfo *pkt)
  36{
  37        const struct nft_ct *priv = nft_expr_priv(expr);
  38        struct nft_data *dest = &data[priv->dreg];
  39        enum ip_conntrack_info ctinfo;
  40        const struct nf_conn *ct;
  41        const struct nf_conn_help *help;
  42        const struct nf_conntrack_tuple *tuple;
  43        const struct nf_conntrack_helper *helper;
  44        long diff;
  45        unsigned int state;
  46
  47        ct = nf_ct_get(pkt->skb, &ctinfo);
  48
  49        switch (priv->key) {
  50        case NFT_CT_STATE:
  51                if (ct == NULL)
  52                        state = NF_CT_STATE_INVALID_BIT;
  53                else if (nf_ct_is_untracked(ct))
  54                        state = NF_CT_STATE_UNTRACKED_BIT;
  55                else
  56                        state = NF_CT_STATE_BIT(ctinfo);
  57                dest->data[0] = state;
  58                return;
  59        }
  60
  61        if (ct == NULL)
  62                goto err;
  63
  64        switch (priv->key) {
  65        case NFT_CT_DIRECTION:
  66                dest->data[0] = CTINFO2DIR(ctinfo);
  67                return;
  68        case NFT_CT_STATUS:
  69                dest->data[0] = ct->status;
  70                return;
  71#ifdef CONFIG_NF_CONNTRACK_MARK
  72        case NFT_CT_MARK:
  73                dest->data[0] = ct->mark;
  74                return;
  75#endif
  76#ifdef CONFIG_NF_CONNTRACK_SECMARK
  77        case NFT_CT_SECMARK:
  78                dest->data[0] = ct->secmark;
  79                return;
  80#endif
  81        case NFT_CT_EXPIRATION:
  82                diff = (long)jiffies - (long)ct->timeout.expires;
  83                if (diff < 0)
  84                        diff = 0;
  85                dest->data[0] = jiffies_to_msecs(diff);
  86                return;
  87        case NFT_CT_HELPER:
  88                if (ct->master == NULL)
  89                        goto err;
  90                help = nfct_help(ct->master);
  91                if (help == NULL)
  92                        goto err;
  93                helper = rcu_dereference(help->helper);
  94                if (helper == NULL)
  95                        goto err;
  96                if (strlen(helper->name) >= sizeof(dest->data))
  97                        goto err;
  98                strncpy((char *)dest->data, helper->name, sizeof(dest->data));
  99                return;
 100#ifdef CONFIG_NF_CONNTRACK_LABELS
 101        case NFT_CT_LABELS: {
 102                struct nf_conn_labels *labels = nf_ct_labels_find(ct);
 103                unsigned int size;
 104
 105                if (!labels) {
 106                        memset(dest->data, 0, sizeof(dest->data));
 107                        return;
 108                }
 109
 110                BUILD_BUG_ON(NF_CT_LABELS_MAX_SIZE > sizeof(dest->data));
 111                size = labels->words * sizeof(long);
 112
 113                memcpy(dest->data, labels->bits, size);
 114                if (size < sizeof(dest->data))
 115                        memset(((char *) dest->data) + size, 0,
 116                               sizeof(dest->data) - size);
 117                return;
 118        }
 119#endif
 120        }
 121
 122        tuple = &ct->tuplehash[priv->dir].tuple;
 123        switch (priv->key) {
 124        case NFT_CT_L3PROTOCOL:
 125                dest->data[0] = nf_ct_l3num(ct);
 126                return;
 127        case NFT_CT_SRC:
 128                memcpy(dest->data, tuple->src.u3.all,
 129                       nf_ct_l3num(ct) == NFPROTO_IPV4 ? 4 : 16);
 130                return;
 131        case NFT_CT_DST:
 132                memcpy(dest->data, tuple->dst.u3.all,
 133                       nf_ct_l3num(ct) == NFPROTO_IPV4 ? 4 : 16);
 134                return;
 135        case NFT_CT_PROTOCOL:
 136                dest->data[0] = nf_ct_protonum(ct);
 137                return;
 138        case NFT_CT_PROTO_SRC:
 139                dest->data[0] = (__force __u16)tuple->src.u.all;
 140                return;
 141        case NFT_CT_PROTO_DST:
 142                dest->data[0] = (__force __u16)tuple->dst.u.all;
 143                return;
 144        }
 145        return;
 146err:
 147        data[NFT_REG_VERDICT].verdict = NFT_BREAK;
 148}
 149
 150static void nft_ct_set_eval(const struct nft_expr *expr,
 151                            struct nft_data data[NFT_REG_MAX + 1],
 152                            const struct nft_pktinfo *pkt)
 153{
 154        const struct nft_ct *priv = nft_expr_priv(expr);
 155        struct sk_buff *skb = pkt->skb;
 156#ifdef CONFIG_NF_CONNTRACK_MARK
 157        u32 value = data[priv->sreg].data[0];
 158#endif
 159        enum ip_conntrack_info ctinfo;
 160        struct nf_conn *ct;
 161
 162        ct = nf_ct_get(skb, &ctinfo);
 163        if (ct == NULL)
 164                return;
 165
 166        switch (priv->key) {
 167#ifdef CONFIG_NF_CONNTRACK_MARK
 168        case NFT_CT_MARK:
 169                if (ct->mark != value) {
 170                        ct->mark = value;
 171                        nf_conntrack_event_cache(IPCT_MARK, ct);
 172                }
 173                break;
 174#endif
 175        }
 176}
 177
 178static const struct nla_policy nft_ct_policy[NFTA_CT_MAX + 1] = {
 179        [NFTA_CT_DREG]          = { .type = NLA_U32 },
 180        [NFTA_CT_KEY]           = { .type = NLA_U32 },
 181        [NFTA_CT_DIRECTION]     = { .type = NLA_U8 },
 182        [NFTA_CT_SREG]          = { .type = NLA_U32 },
 183};
 184
 185static int nft_ct_l3proto_try_module_get(uint8_t family)
 186{
 187        int err;
 188
 189        if (family == NFPROTO_INET) {
 190                err = nf_ct_l3proto_try_module_get(NFPROTO_IPV4);
 191                if (err < 0)
 192                        goto err1;
 193                err = nf_ct_l3proto_try_module_get(NFPROTO_IPV6);
 194                if (err < 0)
 195                        goto err2;
 196        } else {
 197                err = nf_ct_l3proto_try_module_get(family);
 198                if (err < 0)
 199                        goto err1;
 200        }
 201        return 0;
 202
 203err2:
 204        nf_ct_l3proto_module_put(NFPROTO_IPV4);
 205err1:
 206        return err;
 207}
 208
 209static void nft_ct_l3proto_module_put(uint8_t family)
 210{
 211        if (family == NFPROTO_INET) {
 212                nf_ct_l3proto_module_put(NFPROTO_IPV4);
 213                nf_ct_l3proto_module_put(NFPROTO_IPV6);
 214        } else
 215                nf_ct_l3proto_module_put(family);
 216}
 217
 218static int nft_ct_get_init(const struct nft_ctx *ctx,
 219                           const struct nft_expr *expr,
 220                           const struct nlattr * const tb[])
 221{
 222        struct nft_ct *priv = nft_expr_priv(expr);
 223        int err;
 224
 225        priv->key = ntohl(nla_get_be32(tb[NFTA_CT_KEY]));
 226        switch (priv->key) {
 227        case NFT_CT_STATE:
 228        case NFT_CT_DIRECTION:
 229        case NFT_CT_STATUS:
 230#ifdef CONFIG_NF_CONNTRACK_MARK
 231        case NFT_CT_MARK:
 232#endif
 233#ifdef CONFIG_NF_CONNTRACK_SECMARK
 234        case NFT_CT_SECMARK:
 235#endif
 236#ifdef CONFIG_NF_CONNTRACK_LABELS
 237        case NFT_CT_LABELS:
 238#endif
 239        case NFT_CT_EXPIRATION:
 240        case NFT_CT_HELPER:
 241                if (tb[NFTA_CT_DIRECTION] != NULL)
 242                        return -EINVAL;
 243                break;
 244        case NFT_CT_L3PROTOCOL:
 245        case NFT_CT_PROTOCOL:
 246        case NFT_CT_SRC:
 247        case NFT_CT_DST:
 248        case NFT_CT_PROTO_SRC:
 249        case NFT_CT_PROTO_DST:
 250                if (tb[NFTA_CT_DIRECTION] == NULL)
 251                        return -EINVAL;
 252                break;
 253        default:
 254                return -EOPNOTSUPP;
 255        }
 256
 257        if (tb[NFTA_CT_DIRECTION] != NULL) {
 258                priv->dir = nla_get_u8(tb[NFTA_CT_DIRECTION]);
 259                switch (priv->dir) {
 260                case IP_CT_DIR_ORIGINAL:
 261                case IP_CT_DIR_REPLY:
 262                        break;
 263                default:
 264                        return -EINVAL;
 265                }
 266        }
 267
 268        priv->dreg = ntohl(nla_get_be32(tb[NFTA_CT_DREG]));
 269        err = nft_validate_output_register(priv->dreg);
 270        if (err < 0)
 271                return err;
 272
 273        err = nft_validate_data_load(ctx, priv->dreg, NULL, NFT_DATA_VALUE);
 274        if (err < 0)
 275                return err;
 276
 277        err = nft_ct_l3proto_try_module_get(ctx->afi->family);
 278        if (err < 0)
 279                return err;
 280
 281        return 0;
 282}
 283
 284static int nft_ct_set_init(const struct nft_ctx *ctx,
 285                           const struct nft_expr *expr,
 286                           const struct nlattr * const tb[])
 287{
 288        struct nft_ct *priv = nft_expr_priv(expr);
 289        int err;
 290
 291        priv->key = ntohl(nla_get_be32(tb[NFTA_CT_KEY]));
 292        switch (priv->key) {
 293#ifdef CONFIG_NF_CONNTRACK_MARK
 294        case NFT_CT_MARK:
 295                break;
 296#endif
 297        default:
 298                return -EOPNOTSUPP;
 299        }
 300
 301        priv->sreg = ntohl(nla_get_be32(tb[NFTA_CT_SREG]));
 302        err = nft_validate_input_register(priv->sreg);
 303        if (err < 0)
 304                return err;
 305
 306        err = nft_ct_l3proto_try_module_get(ctx->afi->family);
 307        if (err < 0)
 308                return err;
 309
 310        return 0;
 311}
 312
 313static void nft_ct_destroy(const struct nft_ctx *ctx,
 314                           const struct nft_expr *expr)
 315{
 316        nft_ct_l3proto_module_put(ctx->afi->family);
 317}
 318
 319static int nft_ct_get_dump(struct sk_buff *skb, const struct nft_expr *expr)
 320{
 321        const struct nft_ct *priv = nft_expr_priv(expr);
 322
 323        if (nla_put_be32(skb, NFTA_CT_DREG, htonl(priv->dreg)))
 324                goto nla_put_failure;
 325        if (nla_put_be32(skb, NFTA_CT_KEY, htonl(priv->key)))
 326                goto nla_put_failure;
 327
 328        switch (priv->key) {
 329        case NFT_CT_PROTOCOL:
 330        case NFT_CT_SRC:
 331        case NFT_CT_DST:
 332        case NFT_CT_PROTO_SRC:
 333        case NFT_CT_PROTO_DST:
 334                if (nla_put_u8(skb, NFTA_CT_DIRECTION, priv->dir))
 335                        goto nla_put_failure;
 336        default:
 337                break;
 338        }
 339
 340        return 0;
 341
 342nla_put_failure:
 343        return -1;
 344}
 345
 346static int nft_ct_set_dump(struct sk_buff *skb, const struct nft_expr *expr)
 347{
 348        const struct nft_ct *priv = nft_expr_priv(expr);
 349
 350        if (nla_put_be32(skb, NFTA_CT_SREG, htonl(priv->sreg)))
 351                goto nla_put_failure;
 352        if (nla_put_be32(skb, NFTA_CT_KEY, htonl(priv->key)))
 353                goto nla_put_failure;
 354        return 0;
 355
 356nla_put_failure:
 357        return -1;
 358}
 359
 360static struct nft_expr_type nft_ct_type;
 361static const struct nft_expr_ops nft_ct_get_ops = {
 362        .type           = &nft_ct_type,
 363        .size           = NFT_EXPR_SIZE(sizeof(struct nft_ct)),
 364        .eval           = nft_ct_get_eval,
 365        .init           = nft_ct_get_init,
 366        .destroy        = nft_ct_destroy,
 367        .dump           = nft_ct_get_dump,
 368};
 369
 370static const struct nft_expr_ops nft_ct_set_ops = {
 371        .type           = &nft_ct_type,
 372        .size           = NFT_EXPR_SIZE(sizeof(struct nft_ct)),
 373        .eval           = nft_ct_set_eval,
 374        .init           = nft_ct_set_init,
 375        .destroy        = nft_ct_destroy,
 376        .dump           = nft_ct_set_dump,
 377};
 378
 379static const struct nft_expr_ops *
 380nft_ct_select_ops(const struct nft_ctx *ctx,
 381                    const struct nlattr * const tb[])
 382{
 383        if (tb[NFTA_CT_KEY] == NULL)
 384                return ERR_PTR(-EINVAL);
 385
 386        if (tb[NFTA_CT_DREG] && tb[NFTA_CT_SREG])
 387                return ERR_PTR(-EINVAL);
 388
 389        if (tb[NFTA_CT_DREG])
 390                return &nft_ct_get_ops;
 391
 392        if (tb[NFTA_CT_SREG])
 393                return &nft_ct_set_ops;
 394
 395        return ERR_PTR(-EINVAL);
 396}
 397
 398static struct nft_expr_type nft_ct_type __read_mostly = {
 399        .name           = "ct",
 400        .select_ops     = &nft_ct_select_ops,
 401        .policy         = nft_ct_policy,
 402        .maxattr        = NFTA_CT_MAX,
 403        .owner          = THIS_MODULE,
 404};
 405
 406static int __init nft_ct_module_init(void)
 407{
 408        return nft_register_expr(&nft_ct_type);
 409}
 410
 411static void __exit nft_ct_module_exit(void)
 412{
 413        nft_unregister_expr(&nft_ct_type);
 414}
 415
 416module_init(nft_ct_module_init);
 417module_exit(nft_ct_module_exit);
 418
 419MODULE_LICENSE("GPL");
 420MODULE_AUTHOR("Patrick McHardy <kaber@trash.net>");
 421MODULE_ALIAS_NFT_EXPR("ct");
 422