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 04480094de7242d08bb62088e713fd7fe00443b4 397 lines 9.0 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_PROTOCOL: 230 case NFT_CT_SRC: 231 case NFT_CT_DST: 232 case NFT_CT_PROTO_SRC: 233 case NFT_CT_PROTO_DST: 234 if (tb[NFTA_CT_DIRECTION] == NULL) 235 return -EINVAL; 236 break; 237 default: 238 return -EOPNOTSUPP; 239 } 240 241 return 0; 242} 243 244static int nft_ct_init_validate_set(uint32_t key) 245{ 246 switch (key) { 247 case NFT_CT_MARK: 248 break; 249 default: 250 return -EOPNOTSUPP; 251 } 252 253 return 0; 254} 255 256static int nft_ct_init(const struct nft_ctx *ctx, 257 const struct nft_expr *expr, 258 const struct nlattr * const tb[]) 259{ 260 struct nft_ct *priv = nft_expr_priv(expr); 261 int err; 262 263 priv->key = ntohl(nla_get_be32(tb[NFTA_CT_KEY])); 264 265 if (tb[NFTA_CT_DREG]) { 266 err = nft_ct_init_validate_get(expr, tb); 267 if (err < 0) 268 return err; 269 270 priv->dreg = ntohl(nla_get_be32(tb[NFTA_CT_DREG])); 271 err = nft_validate_output_register(priv->dreg); 272 if (err < 0) 273 return err; 274 275 err = nft_validate_data_load(ctx, priv->dreg, NULL, 276 NFT_DATA_VALUE); 277 if (err < 0) 278 return err; 279 } else { 280 err = nft_ct_init_validate_set(priv->key); 281 if (err < 0) 282 return err; 283 284 priv->sreg = ntohl(nla_get_be32(tb[NFTA_CT_SREG])); 285 err = nft_validate_input_register(priv->sreg); 286 if (err < 0) 287 return err; 288 } 289 290 err = nft_ct_l3proto_try_module_get(ctx->afi->family); 291 if (err < 0) 292 return err; 293 294 priv->family = ctx->afi->family; 295 296 return 0; 297} 298 299static void nft_ct_destroy(const struct nft_expr *expr) 300{ 301 struct nft_ct *priv = nft_expr_priv(expr); 302 303 nft_ct_l3proto_module_put(priv->family); 304} 305 306static int nft_ct_get_dump(struct sk_buff *skb, const struct nft_expr *expr) 307{ 308 const struct nft_ct *priv = nft_expr_priv(expr); 309 310 if (nla_put_be32(skb, NFTA_CT_DREG, htonl(priv->dreg))) 311 goto nla_put_failure; 312 if (nla_put_be32(skb, NFTA_CT_KEY, htonl(priv->key))) 313 goto nla_put_failure; 314 if (nla_put_u8(skb, NFTA_CT_DIRECTION, priv->dir)) 315 goto nla_put_failure; 316 return 0; 317 318nla_put_failure: 319 return -1; 320} 321 322static int nft_ct_set_dump(struct sk_buff *skb, const struct nft_expr *expr) 323{ 324 const struct nft_ct *priv = nft_expr_priv(expr); 325 326 if (nla_put_be32(skb, NFTA_CT_SREG, htonl(priv->sreg))) 327 goto nla_put_failure; 328 if (nla_put_be32(skb, NFTA_CT_KEY, htonl(priv->key))) 329 goto nla_put_failure; 330 return 0; 331 332nla_put_failure: 333 return -1; 334} 335 336static struct nft_expr_type nft_ct_type; 337static const struct nft_expr_ops nft_ct_get_ops = { 338 .type = &nft_ct_type, 339 .size = NFT_EXPR_SIZE(sizeof(struct nft_ct)), 340 .eval = nft_ct_get_eval, 341 .init = nft_ct_init, 342 .destroy = nft_ct_destroy, 343 .dump = nft_ct_get_dump, 344}; 345 346static const struct nft_expr_ops nft_ct_set_ops = { 347 .type = &nft_ct_type, 348 .size = NFT_EXPR_SIZE(sizeof(struct nft_ct)), 349 .eval = nft_ct_set_eval, 350 .init = nft_ct_init, 351 .destroy = nft_ct_destroy, 352 .dump = nft_ct_set_dump, 353}; 354 355static const struct nft_expr_ops * 356nft_ct_select_ops(const struct nft_ctx *ctx, 357 const struct nlattr * const tb[]) 358{ 359 if (tb[NFTA_CT_KEY] == NULL) 360 return ERR_PTR(-EINVAL); 361 362 if (tb[NFTA_CT_DREG] && tb[NFTA_CT_SREG]) 363 return ERR_PTR(-EINVAL); 364 365 if (tb[NFTA_CT_DREG]) 366 return &nft_ct_get_ops; 367 368 if (tb[NFTA_CT_SREG]) 369 return &nft_ct_set_ops; 370 371 return ERR_PTR(-EINVAL); 372} 373 374static struct nft_expr_type nft_ct_type __read_mostly = { 375 .name = "ct", 376 .select_ops = &nft_ct_select_ops, 377 .policy = nft_ct_policy, 378 .maxattr = NFTA_CT_MAX, 379 .owner = THIS_MODULE, 380}; 381 382static int __init nft_ct_module_init(void) 383{ 384 return nft_register_expr(&nft_ct_type); 385} 386 387static void __exit nft_ct_module_exit(void) 388{ 389 nft_unregister_expr(&nft_ct_type); 390} 391 392module_init(nft_ct_module_init); 393module_exit(nft_ct_module_exit); 394 395MODULE_LICENSE("GPL"); 396MODULE_AUTHOR("Patrick McHardy <kaber@trash.net>"); 397MODULE_ALIAS_NFT_EXPR("ct");