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_acct.h>
  20#include <net/netfilter/nf_conntrack_tuple.h>
  21#include <net/netfilter/nf_conntrack_helper.h>
  22#include <net/netfilter/nf_conntrack_ecache.h>
  23#include <net/netfilter/nf_conntrack_labels.h>
  24
  25struct nft_ct {
  26        enum nft_ct_keys        key:8;
  27        enum ip_conntrack_dir   dir:8;
  28        union {
  29                enum nft_registers      dreg:8;
  30                enum nft_registers      sreg:8;
  31        };
  32};
  33
  34static u64 nft_ct_get_eval_counter(const struct nf_conn_counter *c,
  35                                   enum nft_ct_keys k,
  36                                   enum ip_conntrack_dir d)
  37{
  38        if (d < IP_CT_DIR_MAX)
  39                return k == NFT_CT_BYTES ? atomic64_read(&c[d].bytes) :
  40                                           atomic64_read(&c[d].packets);
  41
  42        return nft_ct_get_eval_counter(c, k, IP_CT_DIR_ORIGINAL) +
  43               nft_ct_get_eval_counter(c, k, IP_CT_DIR_REPLY);
  44}
  45
  46static void nft_ct_get_eval(const struct nft_expr *expr,
  47                            struct nft_regs *regs,
  48                            const struct nft_pktinfo *pkt)
  49{
  50        const struct nft_ct *priv = nft_expr_priv(expr);
  51        u32 *dest = &regs->data[priv->dreg];
  52        enum ip_conntrack_info ctinfo;
  53        const struct nf_conn *ct;
  54        const struct nf_conn_help *help;
  55        const struct nf_conntrack_tuple *tuple;
  56        const struct nf_conntrack_helper *helper;
  57        unsigned int state;
  58
  59        ct = nf_ct_get(pkt->skb, &ctinfo);
  60
  61        switch (priv->key) {
  62        case NFT_CT_STATE:
  63                if (ct == NULL)
  64                        state = NF_CT_STATE_INVALID_BIT;
  65                else if (nf_ct_is_untracked(ct))
  66                        state = NF_CT_STATE_UNTRACKED_BIT;
  67                else
  68                        state = NF_CT_STATE_BIT(ctinfo);
  69                *dest = state;
  70                return;
  71        default:
  72                break;
  73        }
  74
  75        if (ct == NULL)
  76                goto err;
  77
  78        switch (priv->key) {
  79        case NFT_CT_DIRECTION:
  80                *dest = CTINFO2DIR(ctinfo);
  81                return;
  82        case NFT_CT_STATUS:
  83                *dest = ct->status;
  84                return;
  85#ifdef CONFIG_NF_CONNTRACK_MARK
  86        case NFT_CT_MARK:
  87                *dest = ct->mark;
  88                return;
  89#endif
  90#ifdef CONFIG_NF_CONNTRACK_SECMARK
  91        case NFT_CT_SECMARK:
  92                *dest = ct->secmark;
  93                return;
  94#endif
  95        case NFT_CT_EXPIRATION:
  96                *dest = jiffies_to_msecs(nf_ct_expires(ct));
  97                return;
  98        case NFT_CT_HELPER:
  99                if (ct->master == NULL)
 100                        goto err;
 101                help = nfct_help(ct->master);
 102                if (help == NULL)
 103                        goto err;
 104                helper = rcu_dereference(help->helper);
 105                if (helper == NULL)
 106                        goto err;
 107                strncpy((char *)dest, helper->name, NF_CT_HELPER_NAME_LEN);
 108                return;
 109#ifdef CONFIG_NF_CONNTRACK_LABELS
 110        case NFT_CT_LABELS: {
 111                struct nf_conn_labels *labels = nf_ct_labels_find(ct);
 112
 113                if (labels)
 114                        memcpy(dest, labels->bits, NF_CT_LABELS_MAX_SIZE);
 115                else
 116                        memset(dest, 0, NF_CT_LABELS_MAX_SIZE);
 117                return;
 118        }
 119#endif
 120        case NFT_CT_BYTES: /* fallthrough */
 121        case NFT_CT_PKTS: {
 122                const struct nf_conn_acct *acct = nf_conn_acct_find(ct);
 123                u64 count = 0;
 124
 125                if (acct)
 126                        count = nft_ct_get_eval_counter(acct->counter,
 127                                                        priv->key, priv->dir);
 128                memcpy(dest, &count, sizeof(count));
 129                return;
 130        }
 131        default:
 132                break;
 133        }
 134
 135        tuple = &ct->tuplehash[priv->dir].tuple;
 136        switch (priv->key) {
 137        case NFT_CT_L3PROTOCOL:
 138                *dest = nf_ct_l3num(ct);
 139                return;
 140        case NFT_CT_SRC:
 141                memcpy(dest, tuple->src.u3.all,
 142                       nf_ct_l3num(ct) == NFPROTO_IPV4 ? 4 : 16);
 143                return;
 144        case NFT_CT_DST:
 145                memcpy(dest, tuple->dst.u3.all,
 146                       nf_ct_l3num(ct) == NFPROTO_IPV4 ? 4 : 16);
 147                return;
 148        case NFT_CT_PROTOCOL:
 149                *dest = nf_ct_protonum(ct);
 150                return;
 151        case NFT_CT_PROTO_SRC:
 152                *dest = (__force __u16)tuple->src.u.all;
 153                return;
 154        case NFT_CT_PROTO_DST:
 155                *dest = (__force __u16)tuple->dst.u.all;
 156                return;
 157        default:
 158                break;
 159        }
 160        return;
 161err:
 162        regs->verdict.code = NFT_BREAK;
 163}
 164
 165static void nft_ct_set_eval(const struct nft_expr *expr,
 166                            struct nft_regs *regs,
 167                            const struct nft_pktinfo *pkt)
 168{
 169        const struct nft_ct *priv = nft_expr_priv(expr);
 170        struct sk_buff *skb = pkt->skb;
 171#ifdef CONFIG_NF_CONNTRACK_MARK
 172        u32 value = regs->data[priv->sreg];
 173#endif
 174        enum ip_conntrack_info ctinfo;
 175        struct nf_conn *ct;
 176
 177        ct = nf_ct_get(skb, &ctinfo);
 178        if (ct == NULL)
 179                return;
 180
 181        switch (priv->key) {
 182#ifdef CONFIG_NF_CONNTRACK_MARK
 183        case NFT_CT_MARK:
 184                if (ct->mark != value) {
 185                        ct->mark = value;
 186                        nf_conntrack_event_cache(IPCT_MARK, ct);
 187                }
 188                break;
 189#endif
 190#ifdef CONFIG_NF_CONNTRACK_LABELS
 191        case NFT_CT_LABELS:
 192                nf_connlabels_replace(ct,
 193                                      &regs->data[priv->sreg],
 194                                      &regs->data[priv->sreg],
 195                                      NF_CT_LABELS_MAX_SIZE / sizeof(u32));
 196                break;
 197#endif
 198        default:
 199                break;
 200        }
 201}
 202
 203static const struct nla_policy nft_ct_policy[NFTA_CT_MAX + 1] = {
 204        [NFTA_CT_DREG]          = { .type = NLA_U32 },
 205        [NFTA_CT_KEY]           = { .type = NLA_U32 },
 206        [NFTA_CT_DIRECTION]     = { .type = NLA_U8 },
 207        [NFTA_CT_SREG]          = { .type = NLA_U32 },
 208};
 209
 210static int nft_ct_l3proto_try_module_get(uint8_t family)
 211{
 212        int err;
 213
 214        if (family == NFPROTO_INET) {
 215                err = nf_ct_l3proto_try_module_get(NFPROTO_IPV4);
 216                if (err < 0)
 217                        goto err1;
 218                err = nf_ct_l3proto_try_module_get(NFPROTO_IPV6);
 219                if (err < 0)
 220                        goto err2;
 221        } else {
 222                err = nf_ct_l3proto_try_module_get(family);
 223                if (err < 0)
 224                        goto err1;
 225        }
 226        return 0;
 227
 228err2:
 229        nf_ct_l3proto_module_put(NFPROTO_IPV4);
 230err1:
 231        return err;
 232}
 233
 234static void nft_ct_l3proto_module_put(uint8_t family)
 235{
 236        if (family == NFPROTO_INET) {
 237                nf_ct_l3proto_module_put(NFPROTO_IPV4);
 238                nf_ct_l3proto_module_put(NFPROTO_IPV6);
 239        } else
 240                nf_ct_l3proto_module_put(family);
 241}
 242
 243static int nft_ct_get_init(const struct nft_ctx *ctx,
 244                           const struct nft_expr *expr,
 245                           const struct nlattr * const tb[])
 246{
 247        struct nft_ct *priv = nft_expr_priv(expr);
 248        unsigned int len;
 249        int err;
 250
 251        priv->key = ntohl(nla_get_be32(tb[NFTA_CT_KEY]));
 252        switch (priv->key) {
 253        case NFT_CT_DIRECTION:
 254                if (tb[NFTA_CT_DIRECTION] != NULL)
 255                        return -EINVAL;
 256                len = sizeof(u8);
 257                break;
 258        case NFT_CT_STATE:
 259        case NFT_CT_STATUS:
 260#ifdef CONFIG_NF_CONNTRACK_MARK
 261        case NFT_CT_MARK:
 262#endif
 263#ifdef CONFIG_NF_CONNTRACK_SECMARK
 264        case NFT_CT_SECMARK:
 265#endif
 266        case NFT_CT_EXPIRATION:
 267                if (tb[NFTA_CT_DIRECTION] != NULL)
 268                        return -EINVAL;
 269                len = sizeof(u32);
 270                break;
 271#ifdef CONFIG_NF_CONNTRACK_LABELS
 272        case NFT_CT_LABELS:
 273                if (tb[NFTA_CT_DIRECTION] != NULL)
 274                        return -EINVAL;
 275                len = NF_CT_LABELS_MAX_SIZE;
 276                break;
 277#endif
 278        case NFT_CT_HELPER:
 279                if (tb[NFTA_CT_DIRECTION] != NULL)
 280                        return -EINVAL;
 281                len = NF_CT_HELPER_NAME_LEN;
 282                break;
 283
 284        case NFT_CT_L3PROTOCOL:
 285        case NFT_CT_PROTOCOL:
 286                if (tb[NFTA_CT_DIRECTION] == NULL)
 287                        return -EINVAL;
 288                len = sizeof(u8);
 289                break;
 290        case NFT_CT_SRC:
 291        case NFT_CT_DST:
 292                if (tb[NFTA_CT_DIRECTION] == NULL)
 293                        return -EINVAL;
 294
 295                switch (ctx->afi->family) {
 296                case NFPROTO_IPV4:
 297                        len = FIELD_SIZEOF(struct nf_conntrack_tuple,
 298                                           src.u3.ip);
 299                        break;
 300                case NFPROTO_IPV6:
 301                case NFPROTO_INET:
 302                        len = FIELD_SIZEOF(struct nf_conntrack_tuple,
 303                                           src.u3.ip6);
 304                        break;
 305                default:
 306                        return -EAFNOSUPPORT;
 307                }
 308                break;
 309        case NFT_CT_PROTO_SRC:
 310        case NFT_CT_PROTO_DST:
 311                if (tb[NFTA_CT_DIRECTION] == NULL)
 312                        return -EINVAL;
 313                len = FIELD_SIZEOF(struct nf_conntrack_tuple, src.u.all);
 314                break;
 315        case NFT_CT_BYTES:
 316        case NFT_CT_PKTS:
 317                /* no direction? return sum of original + reply */
 318                if (tb[NFTA_CT_DIRECTION] == NULL)
 319                        priv->dir = IP_CT_DIR_MAX;
 320                len = sizeof(u64);
 321                break;
 322        default:
 323                return -EOPNOTSUPP;
 324        }
 325
 326        if (tb[NFTA_CT_DIRECTION] != NULL) {
 327                priv->dir = nla_get_u8(tb[NFTA_CT_DIRECTION]);
 328                switch (priv->dir) {
 329                case IP_CT_DIR_ORIGINAL:
 330                case IP_CT_DIR_REPLY:
 331                        break;
 332                default:
 333                        return -EINVAL;
 334                }
 335        }
 336
 337        priv->dreg = nft_parse_register(tb[NFTA_CT_DREG]);
 338        err = nft_validate_register_store(ctx, priv->dreg, NULL,
 339                                          NFT_DATA_VALUE, len);
 340        if (err < 0)
 341                return err;
 342
 343        err = nft_ct_l3proto_try_module_get(ctx->afi->family);
 344        if (err < 0)
 345                return err;
 346
 347        if (priv->key == NFT_CT_BYTES || priv->key == NFT_CT_PKTS)
 348                nf_ct_set_acct(ctx->net, true);
 349
 350        return 0;
 351}
 352
 353static int nft_ct_set_init(const struct nft_ctx *ctx,
 354                           const struct nft_expr *expr,
 355                           const struct nlattr * const tb[])
 356{
 357        struct nft_ct *priv = nft_expr_priv(expr);
 358        bool label_got = false;
 359        unsigned int len;
 360        int err;
 361
 362        priv->key = ntohl(nla_get_be32(tb[NFTA_CT_KEY]));
 363        switch (priv->key) {
 364#ifdef CONFIG_NF_CONNTRACK_MARK
 365        case NFT_CT_MARK:
 366                len = FIELD_SIZEOF(struct nf_conn, mark);
 367                break;
 368#endif
 369#ifdef CONFIG_NF_CONNTRACK_LABELS
 370        case NFT_CT_LABELS:
 371                if (tb[NFTA_CT_DIRECTION])
 372                        return -EINVAL;
 373                len = NF_CT_LABELS_MAX_SIZE;
 374                err = nf_connlabels_get(ctx->net, (len * BITS_PER_BYTE) - 1);
 375                if (err)
 376                        return err;
 377                label_got = true;
 378                break;
 379#endif
 380        default:
 381                return -EOPNOTSUPP;
 382        }
 383
 384        priv->sreg = nft_parse_register(tb[NFTA_CT_SREG]);
 385        err = nft_validate_register_load(priv->sreg, len);
 386        if (err < 0)
 387                goto err1;
 388
 389        err = nft_ct_l3proto_try_module_get(ctx->afi->family);
 390        if (err < 0)
 391                goto err1;
 392
 393        return 0;
 394
 395err1:
 396        if (label_got)
 397                nf_connlabels_put(ctx->net);
 398        return err;
 399}
 400
 401static void nft_ct_get_destroy(const struct nft_ctx *ctx,
 402                               const struct nft_expr *expr)
 403{
 404        nft_ct_l3proto_module_put(ctx->afi->family);
 405}
 406
 407static void nft_ct_set_destroy(const struct nft_ctx *ctx,
 408                               const struct nft_expr *expr)
 409{
 410        struct nft_ct *priv = nft_expr_priv(expr);
 411
 412        switch (priv->key) {
 413#ifdef CONFIG_NF_CONNTRACK_LABELS
 414        case NFT_CT_LABELS:
 415                nf_connlabels_put(ctx->net);
 416                break;
 417#endif
 418        default:
 419                break;
 420        }
 421
 422        nft_ct_l3proto_module_put(ctx->afi->family);
 423}
 424
 425static int nft_ct_get_dump(struct sk_buff *skb, const struct nft_expr *expr)
 426{
 427        const struct nft_ct *priv = nft_expr_priv(expr);
 428
 429        if (nft_dump_register(skb, NFTA_CT_DREG, priv->dreg))
 430                goto nla_put_failure;
 431        if (nla_put_be32(skb, NFTA_CT_KEY, htonl(priv->key)))
 432                goto nla_put_failure;
 433
 434        switch (priv->key) {
 435        case NFT_CT_L3PROTOCOL:
 436        case NFT_CT_PROTOCOL:
 437        case NFT_CT_SRC:
 438        case NFT_CT_DST:
 439        case NFT_CT_PROTO_SRC:
 440        case NFT_CT_PROTO_DST:
 441                if (nla_put_u8(skb, NFTA_CT_DIRECTION, priv->dir))
 442                        goto nla_put_failure;
 443                break;
 444        case NFT_CT_BYTES:
 445        case NFT_CT_PKTS:
 446                if (priv->dir < IP_CT_DIR_MAX &&
 447                    nla_put_u8(skb, NFTA_CT_DIRECTION, priv->dir))
 448                        goto nla_put_failure;
 449                break;
 450        default:
 451                break;
 452        }
 453
 454        return 0;
 455
 456nla_put_failure:
 457        return -1;
 458}
 459
 460static int nft_ct_set_dump(struct sk_buff *skb, const struct nft_expr *expr)
 461{
 462        const struct nft_ct *priv = nft_expr_priv(expr);
 463
 464        if (nft_dump_register(skb, NFTA_CT_SREG, priv->sreg))
 465                goto nla_put_failure;
 466        if (nla_put_be32(skb, NFTA_CT_KEY, htonl(priv->key)))
 467                goto nla_put_failure;
 468        return 0;
 469
 470nla_put_failure:
 471        return -1;
 472}
 473
 474static struct nft_expr_type nft_ct_type;
 475static const struct nft_expr_ops nft_ct_get_ops = {
 476        .type           = &nft_ct_type,
 477        .size           = NFT_EXPR_SIZE(sizeof(struct nft_ct)),
 478        .eval           = nft_ct_get_eval,
 479        .init           = nft_ct_get_init,
 480        .destroy        = nft_ct_get_destroy,
 481        .dump           = nft_ct_get_dump,
 482};
 483
 484static const struct nft_expr_ops nft_ct_set_ops = {
 485        .type           = &nft_ct_type,
 486        .size           = NFT_EXPR_SIZE(sizeof(struct nft_ct)),
 487        .eval           = nft_ct_set_eval,
 488        .init           = nft_ct_set_init,
 489        .destroy        = nft_ct_set_destroy,
 490        .dump           = nft_ct_set_dump,
 491};
 492
 493static const struct nft_expr_ops *
 494nft_ct_select_ops(const struct nft_ctx *ctx,
 495                    const struct nlattr * const tb[])
 496{
 497        if (tb[NFTA_CT_KEY] == NULL)
 498                return ERR_PTR(-EINVAL);
 499
 500        if (tb[NFTA_CT_DREG] && tb[NFTA_CT_SREG])
 501                return ERR_PTR(-EINVAL);
 502
 503        if (tb[NFTA_CT_DREG])
 504                return &nft_ct_get_ops;
 505
 506        if (tb[NFTA_CT_SREG])
 507                return &nft_ct_set_ops;
 508
 509        return ERR_PTR(-EINVAL);
 510}
 511
 512static struct nft_expr_type nft_ct_type __read_mostly = {
 513        .name           = "ct",
 514        .select_ops     = &nft_ct_select_ops,
 515        .policy         = nft_ct_policy,
 516        .maxattr        = NFTA_CT_MAX,
 517        .owner          = THIS_MODULE,
 518};
 519
 520static int __init nft_ct_module_init(void)
 521{
 522        BUILD_BUG_ON(NF_CT_LABELS_MAX_SIZE > NFT_REG_SIZE);
 523
 524        return nft_register_expr(&nft_ct_type);
 525}
 526
 527static void __exit nft_ct_module_exit(void)
 528{
 529        nft_unregister_expr(&nft_ct_type);
 530}
 531
 532module_init(nft_ct_module_init);
 533module_exit(nft_ct_module_exit);
 534
 535MODULE_LICENSE("GPL");
 536MODULE_AUTHOR("Patrick McHardy <kaber@trash.net>");
 537MODULE_ALIAS_NFT_EXPR("ct");
 538