2 * Copyright (c) 2015 Patrick McHardy <kaber@trash.net>
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.
10 #include <linux/kernel.h>
11 #include <linux/module.h>
12 #include <linux/init.h>
13 #include <linux/netlink.h>
14 #include <linux/netfilter.h>
15 #include <linux/netfilter/nf_tables.h>
16 #include <net/netfilter/nf_tables.h>
17 #include <net/netfilter/nf_tables_core.h>
21 struct nft_set_ext_tmpl tmpl
;
22 enum nft_dynset_ops op
:8;
23 enum nft_registers sreg_key
:8;
24 enum nft_registers sreg_data
:8;
27 struct nft_expr
*expr
;
28 struct nft_set_binding binding
;
31 static void *nft_dynset_new(struct nft_set
*set
, const struct nft_expr
*expr
,
32 struct nft_regs
*regs
)
34 const struct nft_dynset
*priv
= nft_expr_priv(expr
);
35 struct nft_set_ext
*ext
;
39 if (set
->size
&& !atomic_add_unless(&set
->nelems
, 1, set
->size
))
42 timeout
= priv
->timeout
? : set
->timeout
;
43 elem
= nft_set_elem_init(set
, &priv
->tmpl
,
44 ®s
->data
[priv
->sreg_key
],
45 ®s
->data
[priv
->sreg_data
],
50 ext
= nft_set_elem_ext(set
, elem
);
51 if (priv
->expr
!= NULL
&&
52 nft_expr_clone(nft_set_ext_expr(ext
), priv
->expr
) < 0)
58 nft_set_elem_destroy(set
, elem
, false);
61 atomic_dec(&set
->nelems
);
65 static void nft_dynset_eval(const struct nft_expr
*expr
,
66 struct nft_regs
*regs
,
67 const struct nft_pktinfo
*pkt
)
69 const struct nft_dynset
*priv
= nft_expr_priv(expr
);
70 struct nft_set
*set
= priv
->set
;
71 const struct nft_set_ext
*ext
;
72 const struct nft_expr
*sexpr
;
75 if (set
->ops
->update(set
, ®s
->data
[priv
->sreg_key
], nft_dynset_new
,
78 if (nft_set_ext_exists(ext
, NFT_SET_EXT_EXPR
))
79 sexpr
= nft_set_ext_expr(ext
);
81 if (priv
->op
== NFT_DYNSET_OP_UPDATE
&&
82 nft_set_ext_exists(ext
, NFT_SET_EXT_EXPIRATION
)) {
83 timeout
= priv
->timeout
? : set
->timeout
;
84 *nft_set_ext_expiration(ext
) = jiffies
+ timeout
;
85 } else if (sexpr
== NULL
)
89 sexpr
->ops
->eval(sexpr
, regs
, pkt
);
92 regs
->verdict
.code
= NFT_BREAK
;
97 regs
->verdict
.code
= NFT_BREAK
;
100 static const struct nla_policy nft_dynset_policy
[NFTA_DYNSET_MAX
+ 1] = {
101 [NFTA_DYNSET_SET_NAME
] = { .type
= NLA_STRING
,
102 .len
= NFT_SET_MAXNAMELEN
- 1 },
103 [NFTA_DYNSET_SET_ID
] = { .type
= NLA_U32
},
104 [NFTA_DYNSET_OP
] = { .type
= NLA_U32
},
105 [NFTA_DYNSET_SREG_KEY
] = { .type
= NLA_U32
},
106 [NFTA_DYNSET_SREG_DATA
] = { .type
= NLA_U32
},
107 [NFTA_DYNSET_TIMEOUT
] = { .type
= NLA_U64
},
108 [NFTA_DYNSET_EXPR
] = { .type
= NLA_NESTED
},
109 [NFTA_DYNSET_FLAGS
] = { .type
= NLA_U32
},
112 static int nft_dynset_init(const struct nft_ctx
*ctx
,
113 const struct nft_expr
*expr
,
114 const struct nlattr
* const tb
[])
116 struct nft_dynset
*priv
= nft_expr_priv(expr
);
117 u8 genmask
= nft_genmask_next(ctx
->net
);
122 if (tb
[NFTA_DYNSET_SET_NAME
] == NULL
||
123 tb
[NFTA_DYNSET_OP
] == NULL
||
124 tb
[NFTA_DYNSET_SREG_KEY
] == NULL
)
127 if (tb
[NFTA_DYNSET_FLAGS
]) {
128 u32 flags
= ntohl(nla_get_be32(tb
[NFTA_DYNSET_FLAGS
]));
130 if (flags
& ~NFT_DYNSET_F_INV
)
132 if (flags
& NFT_DYNSET_F_INV
)
136 set
= nf_tables_set_lookup(ctx
->table
, tb
[NFTA_DYNSET_SET_NAME
],
139 if (tb
[NFTA_DYNSET_SET_ID
])
140 set
= nf_tables_set_lookup_byid(ctx
->net
,
141 tb
[NFTA_DYNSET_SET_ID
],
147 if (set
->ops
->update
== NULL
)
150 if (set
->flags
& NFT_SET_CONSTANT
)
153 priv
->op
= ntohl(nla_get_be32(tb
[NFTA_DYNSET_OP
]));
155 case NFT_DYNSET_OP_ADD
:
157 case NFT_DYNSET_OP_UPDATE
:
158 if (!(set
->flags
& NFT_SET_TIMEOUT
))
166 if (tb
[NFTA_DYNSET_TIMEOUT
] != NULL
) {
167 if (!(set
->flags
& NFT_SET_TIMEOUT
))
169 timeout
= msecs_to_jiffies(be64_to_cpu(nla_get_be64(
170 tb
[NFTA_DYNSET_TIMEOUT
])));
173 priv
->sreg_key
= nft_parse_register(tb
[NFTA_DYNSET_SREG_KEY
]);
174 err
= nft_validate_register_load(priv
->sreg_key
, set
->klen
);;
178 if (tb
[NFTA_DYNSET_SREG_DATA
] != NULL
) {
179 if (!(set
->flags
& NFT_SET_MAP
))
181 if (set
->dtype
== NFT_DATA_VERDICT
)
184 priv
->sreg_data
= nft_parse_register(tb
[NFTA_DYNSET_SREG_DATA
]);
185 err
= nft_validate_register_load(priv
->sreg_data
, set
->dlen
);
188 } else if (set
->flags
& NFT_SET_MAP
)
191 if (tb
[NFTA_DYNSET_EXPR
] != NULL
) {
192 if (!(set
->flags
& NFT_SET_EVAL
))
194 if (!(set
->flags
& NFT_SET_ANONYMOUS
))
197 priv
->expr
= nft_expr_init(ctx
, tb
[NFTA_DYNSET_EXPR
]);
198 if (IS_ERR(priv
->expr
))
199 return PTR_ERR(priv
->expr
);
202 if (!(priv
->expr
->ops
->type
->flags
& NFT_EXPR_STATEFUL
))
204 } else if (set
->flags
& NFT_SET_EVAL
)
207 nft_set_ext_prepare(&priv
->tmpl
);
208 nft_set_ext_add_length(&priv
->tmpl
, NFT_SET_EXT_KEY
, set
->klen
);
209 if (set
->flags
& NFT_SET_MAP
)
210 nft_set_ext_add_length(&priv
->tmpl
, NFT_SET_EXT_DATA
, set
->dlen
);
211 if (priv
->expr
!= NULL
)
212 nft_set_ext_add_length(&priv
->tmpl
, NFT_SET_EXT_EXPR
,
213 priv
->expr
->ops
->size
);
214 if (set
->flags
& NFT_SET_TIMEOUT
) {
215 if (timeout
|| set
->timeout
)
216 nft_set_ext_add(&priv
->tmpl
, NFT_SET_EXT_EXPIRATION
);
219 priv
->timeout
= timeout
;
221 err
= nf_tables_bind_set(ctx
, set
, &priv
->binding
);
229 if (priv
->expr
!= NULL
)
230 nft_expr_destroy(ctx
, priv
->expr
);
234 static void nft_dynset_destroy(const struct nft_ctx
*ctx
,
235 const struct nft_expr
*expr
)
237 struct nft_dynset
*priv
= nft_expr_priv(expr
);
239 nf_tables_unbind_set(ctx
, priv
->set
, &priv
->binding
);
240 if (priv
->expr
!= NULL
)
241 nft_expr_destroy(ctx
, priv
->expr
);
244 static int nft_dynset_dump(struct sk_buff
*skb
, const struct nft_expr
*expr
)
246 const struct nft_dynset
*priv
= nft_expr_priv(expr
);
247 u32 flags
= priv
->invert
? NFT_DYNSET_F_INV
: 0;
249 if (nft_dump_register(skb
, NFTA_DYNSET_SREG_KEY
, priv
->sreg_key
))
250 goto nla_put_failure
;
251 if (priv
->set
->flags
& NFT_SET_MAP
&&
252 nft_dump_register(skb
, NFTA_DYNSET_SREG_DATA
, priv
->sreg_data
))
253 goto nla_put_failure
;
254 if (nla_put_be32(skb
, NFTA_DYNSET_OP
, htonl(priv
->op
)))
255 goto nla_put_failure
;
256 if (nla_put_string(skb
, NFTA_DYNSET_SET_NAME
, priv
->set
->name
))
257 goto nla_put_failure
;
258 if (nla_put_be64(skb
, NFTA_DYNSET_TIMEOUT
,
259 cpu_to_be64(jiffies_to_msecs(priv
->timeout
)),
261 goto nla_put_failure
;
262 if (priv
->expr
&& nft_expr_dump(skb
, NFTA_DYNSET_EXPR
, priv
->expr
))
263 goto nla_put_failure
;
264 if (nla_put_be32(skb
, NFTA_DYNSET_FLAGS
, htonl(flags
)))
265 goto nla_put_failure
;
272 static const struct nft_expr_ops nft_dynset_ops
= {
273 .type
= &nft_dynset_type
,
274 .size
= NFT_EXPR_SIZE(sizeof(struct nft_dynset
)),
275 .eval
= nft_dynset_eval
,
276 .init
= nft_dynset_init
,
277 .destroy
= nft_dynset_destroy
,
278 .dump
= nft_dynset_dump
,
281 struct nft_expr_type nft_dynset_type __read_mostly
= {
283 .ops
= &nft_dynset_ops
,
284 .policy
= nft_dynset_policy
,
285 .maxattr
= NFTA_DYNSET_MAX
,
286 .owner
= THIS_MODULE
,