Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
at v3.13-rc6 258 lines 6.1 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 22struct nft_ct { 23 enum nft_ct_keys key:8; 24 enum ip_conntrack_dir dir:8; 25 enum nft_registers dreg:8; 26 uint8_t family; 27}; 28 29static void nft_ct_eval(const struct nft_expr *expr, 30 struct nft_data data[NFT_REG_MAX + 1], 31 const struct nft_pktinfo *pkt) 32{ 33 const struct nft_ct *priv = nft_expr_priv(expr); 34 struct nft_data *dest = &data[priv->dreg]; 35 enum ip_conntrack_info ctinfo; 36 const struct nf_conn *ct; 37 const struct nf_conn_help *help; 38 const struct nf_conntrack_tuple *tuple; 39 const struct nf_conntrack_helper *helper; 40 long diff; 41 unsigned int state; 42 43 ct = nf_ct_get(pkt->skb, &ctinfo); 44 45 switch (priv->key) { 46 case NFT_CT_STATE: 47 if (ct == NULL) 48 state = NF_CT_STATE_INVALID_BIT; 49 else if (nf_ct_is_untracked(ct)) 50 state = NF_CT_STATE_UNTRACKED_BIT; 51 else 52 state = NF_CT_STATE_BIT(ctinfo); 53 dest->data[0] = state; 54 return; 55 } 56 57 if (ct == NULL) 58 goto err; 59 60 switch (priv->key) { 61 case NFT_CT_DIRECTION: 62 dest->data[0] = CTINFO2DIR(ctinfo); 63 return; 64 case NFT_CT_STATUS: 65 dest->data[0] = ct->status; 66 return; 67#ifdef CONFIG_NF_CONNTRACK_MARK 68 case NFT_CT_MARK: 69 dest->data[0] = ct->mark; 70 return; 71#endif 72#ifdef CONFIG_NF_CONNTRACK_SECMARK 73 case NFT_CT_SECMARK: 74 dest->data[0] = ct->secmark; 75 return; 76#endif 77 case NFT_CT_EXPIRATION: 78 diff = (long)jiffies - (long)ct->timeout.expires; 79 if (diff < 0) 80 diff = 0; 81 dest->data[0] = jiffies_to_msecs(diff); 82 return; 83 case NFT_CT_HELPER: 84 if (ct->master == NULL) 85 goto err; 86 help = nfct_help(ct->master); 87 if (help == NULL) 88 goto err; 89 helper = rcu_dereference(help->helper); 90 if (helper == NULL) 91 goto err; 92 if (strlen(helper->name) >= sizeof(dest->data)) 93 goto err; 94 strncpy((char *)dest->data, helper->name, sizeof(dest->data)); 95 return; 96 } 97 98 tuple = &ct->tuplehash[priv->dir].tuple; 99 switch (priv->key) { 100 case NFT_CT_L3PROTOCOL: 101 dest->data[0] = nf_ct_l3num(ct); 102 return; 103 case NFT_CT_SRC: 104 memcpy(dest->data, tuple->src.u3.all, 105 nf_ct_l3num(ct) == NFPROTO_IPV4 ? 4 : 16); 106 return; 107 case NFT_CT_DST: 108 memcpy(dest->data, tuple->dst.u3.all, 109 nf_ct_l3num(ct) == NFPROTO_IPV4 ? 4 : 16); 110 return; 111 case NFT_CT_PROTOCOL: 112 dest->data[0] = nf_ct_protonum(ct); 113 return; 114 case NFT_CT_PROTO_SRC: 115 dest->data[0] = (__force __u16)tuple->src.u.all; 116 return; 117 case NFT_CT_PROTO_DST: 118 dest->data[0] = (__force __u16)tuple->dst.u.all; 119 return; 120 } 121 return; 122err: 123 data[NFT_REG_VERDICT].verdict = NFT_BREAK; 124} 125 126static const struct nla_policy nft_ct_policy[NFTA_CT_MAX + 1] = { 127 [NFTA_CT_DREG] = { .type = NLA_U32 }, 128 [NFTA_CT_KEY] = { .type = NLA_U32 }, 129 [NFTA_CT_DIRECTION] = { .type = NLA_U8 }, 130}; 131 132static int nft_ct_init(const struct nft_ctx *ctx, 133 const struct nft_expr *expr, 134 const struct nlattr * const tb[]) 135{ 136 struct nft_ct *priv = nft_expr_priv(expr); 137 int err; 138 139 if (tb[NFTA_CT_DREG] == NULL || 140 tb[NFTA_CT_KEY] == NULL) 141 return -EINVAL; 142 143 priv->key = ntohl(nla_get_be32(tb[NFTA_CT_KEY])); 144 if (tb[NFTA_CT_DIRECTION] != NULL) { 145 priv->dir = nla_get_u8(tb[NFTA_CT_DIRECTION]); 146 switch (priv->dir) { 147 case IP_CT_DIR_ORIGINAL: 148 case IP_CT_DIR_REPLY: 149 break; 150 default: 151 return -EINVAL; 152 } 153 } 154 155 switch (priv->key) { 156 case NFT_CT_STATE: 157 case NFT_CT_DIRECTION: 158 case NFT_CT_STATUS: 159#ifdef CONFIG_NF_CONNTRACK_MARK 160 case NFT_CT_MARK: 161#endif 162#ifdef CONFIG_NF_CONNTRACK_SECMARK 163 case NFT_CT_SECMARK: 164#endif 165 case NFT_CT_EXPIRATION: 166 case NFT_CT_HELPER: 167 if (tb[NFTA_CT_DIRECTION] != NULL) 168 return -EINVAL; 169 break; 170 case NFT_CT_PROTOCOL: 171 case NFT_CT_SRC: 172 case NFT_CT_DST: 173 case NFT_CT_PROTO_SRC: 174 case NFT_CT_PROTO_DST: 175 if (tb[NFTA_CT_DIRECTION] == NULL) 176 return -EINVAL; 177 break; 178 default: 179 return -EOPNOTSUPP; 180 } 181 182 err = nf_ct_l3proto_try_module_get(ctx->afi->family); 183 if (err < 0) 184 return err; 185 priv->family = ctx->afi->family; 186 187 priv->dreg = ntohl(nla_get_be32(tb[NFTA_CT_DREG])); 188 err = nft_validate_output_register(priv->dreg); 189 if (err < 0) 190 goto err1; 191 192 err = nft_validate_data_load(ctx, priv->dreg, NULL, NFT_DATA_VALUE); 193 if (err < 0) 194 goto err1; 195 return 0; 196 197err1: 198 nf_ct_l3proto_module_put(ctx->afi->family); 199 return err; 200} 201 202static void nft_ct_destroy(const struct nft_expr *expr) 203{ 204 struct nft_ct *priv = nft_expr_priv(expr); 205 206 nf_ct_l3proto_module_put(priv->family); 207} 208 209static int nft_ct_dump(struct sk_buff *skb, const struct nft_expr *expr) 210{ 211 const struct nft_ct *priv = nft_expr_priv(expr); 212 213 if (nla_put_be32(skb, NFTA_CT_DREG, htonl(priv->dreg))) 214 goto nla_put_failure; 215 if (nla_put_be32(skb, NFTA_CT_KEY, htonl(priv->key))) 216 goto nla_put_failure; 217 if (nla_put_u8(skb, NFTA_CT_DIRECTION, priv->dir)) 218 goto nla_put_failure; 219 return 0; 220 221nla_put_failure: 222 return -1; 223} 224 225static struct nft_expr_type nft_ct_type; 226static const struct nft_expr_ops nft_ct_ops = { 227 .type = &nft_ct_type, 228 .size = NFT_EXPR_SIZE(sizeof(struct nft_ct)), 229 .eval = nft_ct_eval, 230 .init = nft_ct_init, 231 .destroy = nft_ct_destroy, 232 .dump = nft_ct_dump, 233}; 234 235static struct nft_expr_type nft_ct_type __read_mostly = { 236 .name = "ct", 237 .ops = &nft_ct_ops, 238 .policy = nft_ct_policy, 239 .maxattr = NFTA_CT_MAX, 240 .owner = THIS_MODULE, 241}; 242 243static int __init nft_ct_module_init(void) 244{ 245 return nft_register_expr(&nft_ct_type); 246} 247 248static void __exit nft_ct_module_exit(void) 249{ 250 nft_unregister_expr(&nft_ct_type); 251} 252 253module_init(nft_ct_module_init); 254module_exit(nft_ct_module_exit); 255 256MODULE_LICENSE("GPL"); 257MODULE_AUTHOR("Patrick McHardy <kaber@trash.net>"); 258MODULE_ALIAS_NFT_EXPR("ct");