Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

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