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;
26 struct nft_set_binding binding
;
29 static void *nft_dynset_new(struct nft_set
*set
, const struct nft_expr
*expr
,
30 struct nft_data data
[NFT_REG_MAX
+ 1])
32 const struct nft_dynset
*priv
= nft_expr_priv(expr
);
36 if (set
->size
&& !atomic_add_unless(&set
->nelems
, 1, set
->size
))
39 timeout
= priv
->timeout
? : set
->timeout
;
40 elem
= nft_set_elem_init(set
, &priv
->tmpl
,
41 &data
[priv
->sreg_key
], &data
[priv
->sreg_data
],
45 atomic_dec(&set
->nelems
);
50 static void nft_dynset_eval(const struct nft_expr
*expr
,
51 struct nft_data data
[NFT_REG_MAX
+ 1],
52 const struct nft_pktinfo
*pkt
)
54 const struct nft_dynset
*priv
= nft_expr_priv(expr
);
55 struct nft_set
*set
= priv
->set
;
56 const struct nft_set_ext
*ext
;
59 if (set
->ops
->update(set
, &data
[priv
->sreg_key
], nft_dynset_new
,
61 if (priv
->op
== NFT_DYNSET_OP_UPDATE
&&
62 nft_set_ext_exists(ext
, NFT_SET_EXT_EXPIRATION
)) {
63 timeout
= priv
->timeout
? : set
->timeout
;
64 *nft_set_ext_expiration(ext
) = jiffies
+ timeout
;
69 data
[NFT_REG_VERDICT
].verdict
= NFT_BREAK
;
72 static const struct nla_policy nft_dynset_policy
[NFTA_DYNSET_MAX
+ 1] = {
73 [NFTA_DYNSET_SET_NAME
] = { .type
= NLA_STRING
},
74 [NFTA_DYNSET_SET_ID
] = { .type
= NLA_U32
},
75 [NFTA_DYNSET_OP
] = { .type
= NLA_U32
},
76 [NFTA_DYNSET_SREG_KEY
] = { .type
= NLA_U32
},
77 [NFTA_DYNSET_SREG_DATA
] = { .type
= NLA_U32
},
78 [NFTA_DYNSET_TIMEOUT
] = { .type
= NLA_U64
},
81 static int nft_dynset_init(const struct nft_ctx
*ctx
,
82 const struct nft_expr
*expr
,
83 const struct nlattr
* const tb
[])
85 struct nft_dynset
*priv
= nft_expr_priv(expr
);
90 if (tb
[NFTA_DYNSET_SET_NAME
] == NULL
||
91 tb
[NFTA_DYNSET_OP
] == NULL
||
92 tb
[NFTA_DYNSET_SREG_KEY
] == NULL
)
95 set
= nf_tables_set_lookup(ctx
->table
, tb
[NFTA_DYNSET_SET_NAME
]);
97 if (tb
[NFTA_DYNSET_SET_ID
])
98 set
= nf_tables_set_lookup_byid(ctx
->net
,
99 tb
[NFTA_DYNSET_SET_ID
]);
104 if (set
->flags
& NFT_SET_CONSTANT
)
107 priv
->op
= ntohl(nla_get_be32(tb
[NFTA_DYNSET_OP
]));
109 case NFT_DYNSET_OP_ADD
:
111 case NFT_DYNSET_OP_UPDATE
:
112 if (!(set
->flags
& NFT_SET_TIMEOUT
))
120 if (tb
[NFTA_DYNSET_TIMEOUT
] != NULL
) {
121 if (!(set
->flags
& NFT_SET_TIMEOUT
))
123 timeout
= be64_to_cpu(nla_get_be64(tb
[NFTA_DYNSET_TIMEOUT
]));
126 priv
->sreg_key
= ntohl(nla_get_be32(tb
[NFTA_DYNSET_SREG_KEY
]));
127 err
= nft_validate_register_load(priv
->sreg_key
, set
->klen
);;
131 if (tb
[NFTA_DYNSET_SREG_DATA
] != NULL
) {
132 if (!(set
->flags
& NFT_SET_MAP
))
134 if (set
->dtype
== NFT_DATA_VERDICT
)
137 priv
->sreg_data
= ntohl(nla_get_be32(tb
[NFTA_DYNSET_SREG_DATA
]));
138 err
= nft_validate_register_load(priv
->sreg_data
, set
->dlen
);
141 } else if (set
->flags
& NFT_SET_MAP
)
144 nft_set_ext_prepare(&priv
->tmpl
);
145 nft_set_ext_add_length(&priv
->tmpl
, NFT_SET_EXT_KEY
, set
->klen
);
146 if (set
->flags
& NFT_SET_MAP
)
147 nft_set_ext_add_length(&priv
->tmpl
, NFT_SET_EXT_DATA
, set
->dlen
);
148 if (set
->flags
& NFT_SET_TIMEOUT
) {
149 if (timeout
|| set
->timeout
)
150 nft_set_ext_add(&priv
->tmpl
, NFT_SET_EXT_EXPIRATION
);
153 priv
->timeout
= timeout
;
155 err
= nf_tables_bind_set(ctx
, set
, &priv
->binding
);
163 static void nft_dynset_destroy(const struct nft_ctx
*ctx
,
164 const struct nft_expr
*expr
)
166 struct nft_dynset
*priv
= nft_expr_priv(expr
);
168 nf_tables_unbind_set(ctx
, priv
->set
, &priv
->binding
);
171 static int nft_dynset_dump(struct sk_buff
*skb
, const struct nft_expr
*expr
)
173 const struct nft_dynset
*priv
= nft_expr_priv(expr
);
175 if (nla_put_be32(skb
, NFTA_DYNSET_SREG_KEY
, htonl(priv
->sreg_key
)))
176 goto nla_put_failure
;
177 if (priv
->set
->flags
& NFT_SET_MAP
&&
178 nla_put_be32(skb
, NFTA_DYNSET_SREG_DATA
, htonl(priv
->sreg_data
)))
179 goto nla_put_failure
;
180 if (nla_put_be32(skb
, NFTA_DYNSET_OP
, htonl(priv
->op
)))
181 goto nla_put_failure
;
182 if (nla_put_string(skb
, NFTA_DYNSET_SET_NAME
, priv
->set
->name
))
183 goto nla_put_failure
;
184 if (nla_put_be64(skb
, NFTA_DYNSET_TIMEOUT
, cpu_to_be64(priv
->timeout
)))
185 goto nla_put_failure
;
192 static struct nft_expr_type nft_dynset_type
;
193 static const struct nft_expr_ops nft_dynset_ops
= {
194 .type
= &nft_dynset_type
,
195 .size
= NFT_EXPR_SIZE(sizeof(struct nft_dynset
)),
196 .eval
= nft_dynset_eval
,
197 .init
= nft_dynset_init
,
198 .destroy
= nft_dynset_destroy
,
199 .dump
= nft_dynset_dump
,
202 static struct nft_expr_type nft_dynset_type __read_mostly
= {
204 .ops
= &nft_dynset_ops
,
205 .policy
= nft_dynset_policy
,
206 .maxattr
= NFTA_DYNSET_MAX
,
207 .owner
= THIS_MODULE
,
210 int __init
nft_dynset_module_init(void)
212 return nft_register_expr(&nft_dynset_type
);
215 void nft_dynset_module_exit(void)
217 nft_unregister_expr(&nft_dynset_type
);