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