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