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 v3.14-rc4 409 lines 9.2 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_tuple.h> 20#include <net/netfilter/nf_conntrack_helper.h> 21#include <net/netfilter/nf_conntrack_ecache.h> 22 23struct nft_ct { 24 enum nft_ct_keys key:8; 25 enum ip_conntrack_dir dir:8; 26 union{ 27 enum nft_registers dreg:8; 28 enum nft_registers sreg:8; 29 }; 30 uint8_t family; 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 } 101 102 tuple = &ct->tuplehash[priv->dir].tuple; 103 switch (priv->key) { 104 case NFT_CT_L3PROTOCOL: 105 dest->data[0] = nf_ct_l3num(ct); 106 return; 107 case NFT_CT_SRC: 108 memcpy(dest->data, tuple->src.u3.all, 109 nf_ct_l3num(ct) == NFPROTO_IPV4 ? 4 : 16); 110 return; 111 case NFT_CT_DST: 112 memcpy(dest->data, tuple->dst.u3.all, 113 nf_ct_l3num(ct) == NFPROTO_IPV4 ? 4 : 16); 114 return; 115 case NFT_CT_PROTOCOL: 116 dest->data[0] = nf_ct_protonum(ct); 117 return; 118 case NFT_CT_PROTO_SRC: 119 dest->data[0] = (__force __u16)tuple->src.u.all; 120 return; 121 case NFT_CT_PROTO_DST: 122 dest->data[0] = (__force __u16)tuple->dst.u.all; 123 return; 124 } 125 return; 126err: 127 data[NFT_REG_VERDICT].verdict = NFT_BREAK; 128} 129 130static void nft_ct_set_eval(const struct nft_expr *expr, 131 struct nft_data data[NFT_REG_MAX + 1], 132 const struct nft_pktinfo *pkt) 133{ 134 const struct nft_ct *priv = nft_expr_priv(expr); 135 struct sk_buff *skb = pkt->skb; 136#ifdef CONFIG_NF_CONNTRACK_MARK 137 u32 value = data[priv->sreg].data[0]; 138#endif 139 enum ip_conntrack_info ctinfo; 140 struct nf_conn *ct; 141 142 ct = nf_ct_get(skb, &ctinfo); 143 if (ct == NULL) 144 return; 145 146 switch (priv->key) { 147#ifdef CONFIG_NF_CONNTRACK_MARK 148 case NFT_CT_MARK: 149 if (ct->mark != value) { 150 ct->mark = value; 151 nf_conntrack_event_cache(IPCT_MARK, ct); 152 } 153 break; 154#endif 155 } 156} 157 158static const struct nla_policy nft_ct_policy[NFTA_CT_MAX + 1] = { 159 [NFTA_CT_DREG] = { .type = NLA_U32 }, 160 [NFTA_CT_KEY] = { .type = NLA_U32 }, 161 [NFTA_CT_DIRECTION] = { .type = NLA_U8 }, 162 [NFTA_CT_SREG] = { .type = NLA_U32 }, 163}; 164 165static int nft_ct_l3proto_try_module_get(uint8_t family) 166{ 167 int err; 168 169 if (family == NFPROTO_INET) { 170 err = nf_ct_l3proto_try_module_get(NFPROTO_IPV4); 171 if (err < 0) 172 goto err1; 173 err = nf_ct_l3proto_try_module_get(NFPROTO_IPV6); 174 if (err < 0) 175 goto err2; 176 } else { 177 err = nf_ct_l3proto_try_module_get(family); 178 if (err < 0) 179 goto err1; 180 } 181 return 0; 182 183err2: 184 nf_ct_l3proto_module_put(NFPROTO_IPV4); 185err1: 186 return err; 187} 188 189static void nft_ct_l3proto_module_put(uint8_t family) 190{ 191 if (family == NFPROTO_INET) { 192 nf_ct_l3proto_module_put(NFPROTO_IPV4); 193 nf_ct_l3proto_module_put(NFPROTO_IPV6); 194 } else 195 nf_ct_l3proto_module_put(family); 196} 197 198static int nft_ct_init_validate_get(const struct nft_expr *expr, 199 const struct nlattr * const tb[]) 200{ 201 struct nft_ct *priv = nft_expr_priv(expr); 202 203 if (tb[NFTA_CT_DIRECTION] != NULL) { 204 priv->dir = nla_get_u8(tb[NFTA_CT_DIRECTION]); 205 switch (priv->dir) { 206 case IP_CT_DIR_ORIGINAL: 207 case IP_CT_DIR_REPLY: 208 break; 209 default: 210 return -EINVAL; 211 } 212 } 213 214 switch (priv->key) { 215 case NFT_CT_STATE: 216 case NFT_CT_DIRECTION: 217 case NFT_CT_STATUS: 218#ifdef CONFIG_NF_CONNTRACK_MARK 219 case NFT_CT_MARK: 220#endif 221#ifdef CONFIG_NF_CONNTRACK_SECMARK 222 case NFT_CT_SECMARK: 223#endif 224 case NFT_CT_EXPIRATION: 225 case NFT_CT_HELPER: 226 if (tb[NFTA_CT_DIRECTION] != NULL) 227 return -EINVAL; 228 break; 229 case NFT_CT_L3PROTOCOL: 230 case NFT_CT_PROTOCOL: 231 case NFT_CT_SRC: 232 case NFT_CT_DST: 233 case NFT_CT_PROTO_SRC: 234 case NFT_CT_PROTO_DST: 235 if (tb[NFTA_CT_DIRECTION] == NULL) 236 return -EINVAL; 237 break; 238 default: 239 return -EOPNOTSUPP; 240 } 241 242 return 0; 243} 244 245static int nft_ct_init_validate_set(uint32_t key) 246{ 247 switch (key) { 248 case NFT_CT_MARK: 249 break; 250 default: 251 return -EOPNOTSUPP; 252 } 253 254 return 0; 255} 256 257static int nft_ct_init(const struct nft_ctx *ctx, 258 const struct nft_expr *expr, 259 const struct nlattr * const tb[]) 260{ 261 struct nft_ct *priv = nft_expr_priv(expr); 262 int err; 263 264 priv->key = ntohl(nla_get_be32(tb[NFTA_CT_KEY])); 265 266 if (tb[NFTA_CT_DREG]) { 267 err = nft_ct_init_validate_get(expr, tb); 268 if (err < 0) 269 return err; 270 271 priv->dreg = ntohl(nla_get_be32(tb[NFTA_CT_DREG])); 272 err = nft_validate_output_register(priv->dreg); 273 if (err < 0) 274 return err; 275 276 err = nft_validate_data_load(ctx, priv->dreg, NULL, 277 NFT_DATA_VALUE); 278 if (err < 0) 279 return err; 280 } else { 281 err = nft_ct_init_validate_set(priv->key); 282 if (err < 0) 283 return err; 284 285 priv->sreg = ntohl(nla_get_be32(tb[NFTA_CT_SREG])); 286 err = nft_validate_input_register(priv->sreg); 287 if (err < 0) 288 return err; 289 } 290 291 err = nft_ct_l3proto_try_module_get(ctx->afi->family); 292 if (err < 0) 293 return err; 294 295 priv->family = ctx->afi->family; 296 297 return 0; 298} 299 300static void nft_ct_destroy(const struct nft_expr *expr) 301{ 302 struct nft_ct *priv = nft_expr_priv(expr); 303 304 nft_ct_l3proto_module_put(priv->family); 305} 306 307static int nft_ct_get_dump(struct sk_buff *skb, const struct nft_expr *expr) 308{ 309 const struct nft_ct *priv = nft_expr_priv(expr); 310 311 if (nla_put_be32(skb, NFTA_CT_DREG, htonl(priv->dreg))) 312 goto nla_put_failure; 313 if (nla_put_be32(skb, NFTA_CT_KEY, htonl(priv->key))) 314 goto nla_put_failure; 315 316 switch (priv->key) { 317 case NFT_CT_PROTOCOL: 318 case NFT_CT_SRC: 319 case NFT_CT_DST: 320 case NFT_CT_PROTO_SRC: 321 case NFT_CT_PROTO_DST: 322 if (nla_put_u8(skb, NFTA_CT_DIRECTION, priv->dir)) 323 goto nla_put_failure; 324 default: 325 break; 326 } 327 328 return 0; 329 330nla_put_failure: 331 return -1; 332} 333 334static int nft_ct_set_dump(struct sk_buff *skb, const struct nft_expr *expr) 335{ 336 const struct nft_ct *priv = nft_expr_priv(expr); 337 338 if (nla_put_be32(skb, NFTA_CT_SREG, htonl(priv->sreg))) 339 goto nla_put_failure; 340 if (nla_put_be32(skb, NFTA_CT_KEY, htonl(priv->key))) 341 goto nla_put_failure; 342 return 0; 343 344nla_put_failure: 345 return -1; 346} 347 348static struct nft_expr_type nft_ct_type; 349static const struct nft_expr_ops nft_ct_get_ops = { 350 .type = &nft_ct_type, 351 .size = NFT_EXPR_SIZE(sizeof(struct nft_ct)), 352 .eval = nft_ct_get_eval, 353 .init = nft_ct_init, 354 .destroy = nft_ct_destroy, 355 .dump = nft_ct_get_dump, 356}; 357 358static const struct nft_expr_ops nft_ct_set_ops = { 359 .type = &nft_ct_type, 360 .size = NFT_EXPR_SIZE(sizeof(struct nft_ct)), 361 .eval = nft_ct_set_eval, 362 .init = nft_ct_init, 363 .destroy = nft_ct_destroy, 364 .dump = nft_ct_set_dump, 365}; 366 367static const struct nft_expr_ops * 368nft_ct_select_ops(const struct nft_ctx *ctx, 369 const struct nlattr * const tb[]) 370{ 371 if (tb[NFTA_CT_KEY] == NULL) 372 return ERR_PTR(-EINVAL); 373 374 if (tb[NFTA_CT_DREG] && tb[NFTA_CT_SREG]) 375 return ERR_PTR(-EINVAL); 376 377 if (tb[NFTA_CT_DREG]) 378 return &nft_ct_get_ops; 379 380 if (tb[NFTA_CT_SREG]) 381 return &nft_ct_set_ops; 382 383 return ERR_PTR(-EINVAL); 384} 385 386static struct nft_expr_type nft_ct_type __read_mostly = { 387 .name = "ct", 388 .select_ops = &nft_ct_select_ops, 389 .policy = nft_ct_policy, 390 .maxattr = NFTA_CT_MAX, 391 .owner = THIS_MODULE, 392}; 393 394static int __init nft_ct_module_init(void) 395{ 396 return nft_register_expr(&nft_ct_type); 397} 398 399static void __exit nft_ct_module_exit(void) 400{ 401 nft_unregister_expr(&nft_ct_type); 402} 403 404module_init(nft_ct_module_init); 405module_exit(nft_ct_module_exit); 406 407MODULE_LICENSE("GPL"); 408MODULE_AUTHOR("Patrick McHardy <kaber@trash.net>"); 409MODULE_ALIAS_NFT_EXPR("ct");