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