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)
  64                        state = NF_CT_STATE_BIT(ctinfo);
  65                else if (ctinfo == IP_CT_UNTRACKED)
  66                        state = NF_CT_STATE_UNTRACKED_BIT;
  67                else
  68                        state = NF_CT_STATE_INVALID_BIT;
  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                nft_reg_store8(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_counter *counter = nf_conn_acct_find(ct);
 123                u64 count = 0;
 124
 125                if (counter)
 126                        count = nft_ct_get_eval_counter(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                nft_reg_store8(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                nft_reg_store8(dest, nf_ct_protonum(ct));
 150                return;
 151        case NFT_CT_PROTO_SRC:
 152                nft_reg_store16(dest, (__force u16)tuple->src.u.all);
 153                return;
 154        case NFT_CT_PROTO_DST:
 155                nft_reg_store16(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        default:
 191                break;
 192        }
 193}
 194
 195static const struct nla_policy nft_ct_policy[NFTA_CT_MAX + 1] = {
 196        [NFTA_CT_DREG]          = { .type = NLA_U32 },
 197        [NFTA_CT_KEY]           = { .type = NLA_U32 },
 198        [NFTA_CT_DIRECTION]     = { .type = NLA_U8 },
 199        [NFTA_CT_SREG]          = { .type = NLA_U32 },
 200};
 201
 202static int nft_ct_l3proto_try_module_get(uint8_t family)
 203{
 204        int err;
 205
 206        if (family == NFPROTO_INET) {
 207                err = nf_ct_l3proto_try_module_get(NFPROTO_IPV4);
 208                if (err < 0)
 209                        goto err1;
 210                err = nf_ct_l3proto_try_module_get(NFPROTO_IPV6);
 211                if (err < 0)
 212                        goto err2;
 213        } else {
 214                err = nf_ct_l3proto_try_module_get(family);
 215                if (err < 0)
 216                        goto err1;
 217        }
 218        return 0;
 219
 220err2:
 221        nf_ct_l3proto_module_put(NFPROTO_IPV4);
 222err1:
 223        return err;
 224}
 225
 226static void nft_ct_l3proto_module_put(uint8_t family)
 227{
 228        if (family == NFPROTO_INET) {
 229                nf_ct_l3proto_module_put(NFPROTO_IPV4);
 230                nf_ct_l3proto_module_put(NFPROTO_IPV6);
 231        } else
 232                nf_ct_l3proto_module_put(family);
 233}
 234
 235static int nft_ct_get_init(const struct nft_ctx *ctx,
 236                           const struct nft_expr *expr,
 237                           const struct nlattr * const tb[])
 238{
 239        struct nft_ct *priv = nft_expr_priv(expr);
 240        unsigned int len;
 241        int err;
 242
 243        priv->key = ntohl(nla_get_be32(tb[NFTA_CT_KEY]));
 244        switch (priv->key) {
 245        case NFT_CT_DIRECTION:
 246                if (tb[NFTA_CT_DIRECTION] != NULL)
 247                        return -EINVAL;
 248                len = sizeof(u8);
 249                break;
 250        case NFT_CT_STATE:
 251        case NFT_CT_STATUS:
 252#ifdef CONFIG_NF_CONNTRACK_MARK
 253        case NFT_CT_MARK:
 254#endif
 255#ifdef CONFIG_NF_CONNTRACK_SECMARK
 256        case NFT_CT_SECMARK:
 257#endif
 258        case NFT_CT_EXPIRATION:
 259                if (tb[NFTA_CT_DIRECTION] != NULL)
 260                        return -EINVAL;
 261                len = sizeof(u32);
 262                break;
 263#ifdef CONFIG_NF_CONNTRACK_LABELS
 264        case NFT_CT_LABELS:
 265                if (tb[NFTA_CT_DIRECTION] != NULL)
 266                        return -EINVAL;
 267                len = NF_CT_LABELS_MAX_SIZE;
 268                break;
 269#endif
 270        case NFT_CT_HELPER:
 271                if (tb[NFTA_CT_DIRECTION] != NULL)
 272                        return -EINVAL;
 273                len = NF_CT_HELPER_NAME_LEN;
 274                break;
 275
 276        case NFT_CT_L3PROTOCOL:
 277        case NFT_CT_PROTOCOL:
 278                if (tb[NFTA_CT_DIRECTION] == NULL)
 279                        return -EINVAL;
 280                len = sizeof(u8);
 281                break;
 282        case NFT_CT_SRC:
 283        case NFT_CT_DST:
 284                if (tb[NFTA_CT_DIRECTION] == NULL)
 285                        return -EINVAL;
 286
 287                switch (ctx->afi->family) {
 288                case NFPROTO_IPV4:
 289                        len = FIELD_SIZEOF(struct nf_conntrack_tuple,
 290                                           src.u3.ip);
 291                        break;
 292                case NFPROTO_IPV6:
 293                case NFPROTO_INET:
 294                        len = FIELD_SIZEOF(struct nf_conntrack_tuple,
 295                                           src.u3.ip6);
 296                        break;
 297                default:
 298                        return -EAFNOSUPPORT;
 299                }
 300                break;
 301        case NFT_CT_PROTO_SRC:
 302        case NFT_CT_PROTO_DST:
 303                if (tb[NFTA_CT_DIRECTION] == NULL)
 304                        return -EINVAL;
 305                len = FIELD_SIZEOF(struct nf_conntrack_tuple, src.u.all);
 306                break;
 307        case NFT_CT_BYTES:
 308        case NFT_CT_PKTS:
 309                /* no direction? return sum of original + reply */
 310                if (tb[NFTA_CT_DIRECTION] == NULL)
 311                        priv->dir = IP_CT_DIR_MAX;
 312                len = sizeof(u64);
 313                break;
 314        default:
 315                return -EOPNOTSUPP;
 316        }
 317
 318        if (tb[NFTA_CT_DIRECTION] != NULL) {
 319                priv->dir = nla_get_u8(tb[NFTA_CT_DIRECTION]);
 320                switch (priv->dir) {
 321                case IP_CT_DIR_ORIGINAL:
 322                case IP_CT_DIR_REPLY:
 323                        break;
 324                default:
 325                        return -EINVAL;
 326                }
 327        }
 328
 329        priv->dreg = nft_parse_register(tb[NFTA_CT_DREG]);
 330        err = nft_validate_register_store(ctx, priv->dreg, NULL,
 331                                          NFT_DATA_VALUE, len);
 332        if (err < 0)
 333                return err;
 334
 335        err = nft_ct_l3proto_try_module_get(ctx->afi->family);
 336        if (err < 0)
 337                return err;
 338
 339        return 0;
 340}
 341
 342static int nft_ct_set_init(const struct nft_ctx *ctx,
 343                           const struct nft_expr *expr,
 344                           const struct nlattr * const tb[])
 345{
 346        struct nft_ct *priv = nft_expr_priv(expr);
 347        unsigned int len;
 348        int err;
 349
 350        priv->key = ntohl(nla_get_be32(tb[NFTA_CT_KEY]));
 351        switch (priv->key) {
 352#ifdef CONFIG_NF_CONNTRACK_MARK
 353        case NFT_CT_MARK:
 354                len = FIELD_SIZEOF(struct nf_conn, mark);
 355                break;
 356#endif
 357        default:
 358                return -EOPNOTSUPP;
 359        }
 360
 361        priv->sreg = nft_parse_register(tb[NFTA_CT_SREG]);
 362        err = nft_validate_register_load(priv->sreg, len);
 363        if (err < 0)
 364                return err;
 365
 366        err = nft_ct_l3proto_try_module_get(ctx->afi->family);
 367        if (err < 0)
 368                return err;
 369
 370        return 0;
 371}
 372
 373static void nft_ct_destroy(const struct nft_ctx *ctx,
 374                           const struct nft_expr *expr)
 375{
 376        nft_ct_l3proto_module_put(ctx->afi->family);
 377}
 378
 379static int nft_ct_get_dump(struct sk_buff *skb, const struct nft_expr *expr)
 380{
 381        const struct nft_ct *priv = nft_expr_priv(expr);
 382
 383        if (nft_dump_register(skb, NFTA_CT_DREG, priv->dreg))
 384                goto nla_put_failure;
 385        if (nla_put_be32(skb, NFTA_CT_KEY, htonl(priv->key)))
 386                goto nla_put_failure;
 387
 388        switch (priv->key) {
 389        case NFT_CT_L3PROTOCOL:
 390        case NFT_CT_PROTOCOL:
 391        case NFT_CT_SRC:
 392        case NFT_CT_DST:
 393        case NFT_CT_PROTO_SRC:
 394        case NFT_CT_PROTO_DST:
 395                if (nla_put_u8(skb, NFTA_CT_DIRECTION, priv->dir))
 396                        goto nla_put_failure;
 397                break;
 398        case NFT_CT_BYTES:
 399        case NFT_CT_PKTS:
 400                if (priv->dir < IP_CT_DIR_MAX &&
 401                    nla_put_u8(skb, NFTA_CT_DIRECTION, priv->dir))
 402                        goto nla_put_failure;
 403                break;
 404        default:
 405                break;
 406        }
 407
 408        return 0;
 409
 410nla_put_failure:
 411        return -1;
 412}
 413
 414static int nft_ct_set_dump(struct sk_buff *skb, const struct nft_expr *expr)
 415{
 416        const struct nft_ct *priv = nft_expr_priv(expr);
 417
 418        if (nft_dump_register(skb, NFTA_CT_SREG, priv->sreg))
 419                goto nla_put_failure;
 420        if (nla_put_be32(skb, NFTA_CT_KEY, htonl(priv->key)))
 421                goto nla_put_failure;
 422        return 0;
 423
 424nla_put_failure:
 425        return -1;
 426}
 427
 428static struct nft_expr_type nft_ct_type;
 429static const struct nft_expr_ops nft_ct_get_ops = {
 430        .type           = &nft_ct_type,
 431        .size           = NFT_EXPR_SIZE(sizeof(struct nft_ct)),
 432        .eval           = nft_ct_get_eval,
 433        .init           = nft_ct_get_init,
 434        .destroy        = nft_ct_destroy,
 435        .dump           = nft_ct_get_dump,
 436};
 437
 438static const struct nft_expr_ops nft_ct_set_ops = {
 439        .type           = &nft_ct_type,
 440        .size           = NFT_EXPR_SIZE(sizeof(struct nft_ct)),
 441        .eval           = nft_ct_set_eval,
 442        .init           = nft_ct_set_init,
 443        .destroy        = nft_ct_destroy,
 444        .dump           = nft_ct_set_dump,
 445};
 446
 447static const struct nft_expr_ops *
 448nft_ct_select_ops(const struct nft_ctx *ctx,
 449                    const struct nlattr * const tb[])
 450{
 451        if (tb[NFTA_CT_KEY] == NULL)
 452                return ERR_PTR(-EINVAL);
 453
 454        if (tb[NFTA_CT_DREG] && tb[NFTA_CT_SREG])
 455                return ERR_PTR(-EINVAL);
 456
 457        if (tb[NFTA_CT_DREG])
 458                return &nft_ct_get_ops;
 459
 460        if (tb[NFTA_CT_SREG])
 461                return &nft_ct_set_ops;
 462
 463        return ERR_PTR(-EINVAL);
 464}
 465
 466static struct nft_expr_type nft_ct_type __read_mostly = {
 467        .name           = "ct",
 468        .select_ops     = &nft_ct_select_ops,
 469        .policy         = nft_ct_policy,
 470        .maxattr        = NFTA_CT_MAX,
 471        .owner          = THIS_MODULE,
 472};
 473
 474static int __init nft_ct_module_init(void)
 475{
 476        BUILD_BUG_ON(NF_CT_LABELS_MAX_SIZE > NFT_REG_SIZE);
 477
 478        return nft_register_expr(&nft_ct_type);
 479}
 480
 481static void __exit nft_ct_module_exit(void)
 482{
 483        nft_unregister_expr(&nft_ct_type);
 484}
 485
 486module_init(nft_ct_module_init);
 487module_exit(nft_ct_module_exit);
 488
 489MODULE_LICENSE("GPL");
 490MODULE_AUTHOR("Patrick McHardy <kaber@trash.net>");
 491MODULE_ALIAS_NFT_EXPR("ct");
 492