libnftnl  1.0.8
dynset.c
1 /*
2  * Copyright (c) 2014, 2015 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 as published
6  * by the Free Software Foundation; either version 2 of the License, or
7  * (at your option) any later version.
8  */
9 
10 #include "internal.h"
11 
12 #include <stdio.h>
13 #include <stdint.h>
14 #include <inttypes.h>
15 #include <errno.h>
16 #include <arpa/inet.h>
17 #include <libmnl/libmnl.h>
18 #include <linux/netfilter/nf_tables.h>
19 #include <libnftnl/rule.h>
20 #include <libnftnl/expr.h>
21 #include "data_reg.h"
22 #include "expr_ops.h"
23 #include <buffer.h>
24 
26  enum nft_registers sreg_key;
27  enum nft_registers sreg_data;
28  enum nft_dynset_ops op;
29  uint64_t timeout;
30  struct nftnl_expr *expr;
31  char *set_name;
32  uint32_t set_id;
33 };
34 
35 static int
36 nftnl_expr_dynset_set(struct nftnl_expr *e, uint16_t type,
37  const void *data, uint32_t data_len)
38 {
39  struct nftnl_expr_dynset *dynset = nftnl_expr_data(e);
40 
41  switch (type) {
42  case NFTNL_EXPR_DYNSET_SREG_KEY:
43  dynset->sreg_key = *((uint32_t *)data);
44  break;
45  case NFTNL_EXPR_DYNSET_SREG_DATA:
46  dynset->sreg_data = *((uint32_t *)data);
47  break;
48  case NFTNL_EXPR_DYNSET_OP:
49  dynset->op = *((uint32_t *)data);
50  break;
51  case NFTNL_EXPR_DYNSET_TIMEOUT:
52  dynset->timeout = *((uint64_t *)data);
53  break;
54  case NFTNL_EXPR_DYNSET_SET_NAME:
55  dynset->set_name = strdup((const char *)data);
56  if (!dynset->set_name)
57  return -1;
58  break;
59  case NFTNL_EXPR_DYNSET_SET_ID:
60  dynset->set_id = *((uint32_t *)data);
61  break;
62  case NFTNL_EXPR_DYNSET_EXPR:
63  dynset->expr = (void *)data;
64  break;
65  default:
66  return -1;
67  }
68  return 0;
69 }
70 
71 static const void *
72 nftnl_expr_dynset_get(const struct nftnl_expr *e, uint16_t type,
73  uint32_t *data_len)
74 {
75  struct nftnl_expr_dynset *dynset = nftnl_expr_data(e);
76 
77  switch (type) {
78  case NFTNL_EXPR_DYNSET_SREG_KEY:
79  *data_len = sizeof(dynset->sreg_key);
80  return &dynset->sreg_key;
81  case NFTNL_EXPR_DYNSET_SREG_DATA:
82  *data_len = sizeof(dynset->sreg_data);
83  return &dynset->sreg_data;
84  case NFTNL_EXPR_DYNSET_OP:
85  *data_len = sizeof(dynset->op);
86  return &dynset->op;
87  case NFTNL_EXPR_DYNSET_TIMEOUT:
88  *data_len = sizeof(dynset->timeout);
89  return &dynset->timeout;
90  case NFTNL_EXPR_DYNSET_SET_NAME:
91  *data_len = strlen(dynset->set_name) + 1;
92  return dynset->set_name;
93  case NFTNL_EXPR_DYNSET_SET_ID:
94  *data_len = sizeof(dynset->set_id);
95  return &dynset->set_id;
96  case NFTNL_EXPR_DYNSET_EXPR:
97  return dynset->expr;
98  }
99  return NULL;
100 }
101 
102 static int nftnl_expr_dynset_cb(const struct nlattr *attr, void *data)
103 {
104  const struct nlattr **tb = data;
105  int type = mnl_attr_get_type(attr);
106 
107  if (mnl_attr_type_valid(attr, NFTA_SET_MAX) < 0)
108  return MNL_CB_OK;
109 
110  switch (type) {
111  case NFTA_DYNSET_SREG_KEY:
112  case NFTA_DYNSET_SREG_DATA:
113  case NFTA_DYNSET_SET_ID:
114  case NFTA_DYNSET_OP:
115  if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0)
116  abi_breakage();
117  break;
118  case NFTA_DYNSET_TIMEOUT:
119  if (mnl_attr_validate(attr, MNL_TYPE_U64) < 0)
120  abi_breakage();
121  break;
122  case NFTA_DYNSET_SET_NAME:
123  if (mnl_attr_validate(attr, MNL_TYPE_STRING) < 0)
124  abi_breakage();
125  break;
126  case NFTA_DYNSET_EXPR:
127  if (mnl_attr_validate(attr, MNL_TYPE_NESTED) < 0)
128  abi_breakage();
129  break;
130  }
131 
132  tb[type] = attr;
133  return MNL_CB_OK;
134 }
135 
136 static void
137 nftnl_expr_dynset_build(struct nlmsghdr *nlh, const struct nftnl_expr *e)
138 {
139  struct nftnl_expr_dynset *dynset = nftnl_expr_data(e);
140  struct nlattr *nest;
141 
142  if (e->flags & (1 << NFTNL_EXPR_DYNSET_SREG_KEY))
143  mnl_attr_put_u32(nlh, NFTA_DYNSET_SREG_KEY, htonl(dynset->sreg_key));
144  if (e->flags & (1 << NFTNL_EXPR_DYNSET_SREG_DATA))
145  mnl_attr_put_u32(nlh, NFTA_DYNSET_SREG_DATA, htonl(dynset->sreg_data));
146  if (e->flags & (1 << NFTNL_EXPR_DYNSET_OP))
147  mnl_attr_put_u32(nlh, NFTA_DYNSET_OP, htonl(dynset->op));
148  if (e->flags & (1 << NFTNL_EXPR_DYNSET_TIMEOUT))
149  mnl_attr_put_u64(nlh, NFTA_DYNSET_TIMEOUT, htobe64(dynset->timeout));
150  if (e->flags & (1 << NFTNL_EXPR_DYNSET_SET_NAME))
151  mnl_attr_put_strz(nlh, NFTA_DYNSET_SET_NAME, dynset->set_name);
152  if (e->flags & (1 << NFTNL_EXPR_DYNSET_SET_ID))
153  mnl_attr_put_u32(nlh, NFTA_DYNSET_SET_ID, htonl(dynset->set_id));
154  if (e->flags & (1 << NFTNL_EXPR_DYNSET_EXPR)) {
155  nest = mnl_attr_nest_start(nlh, NFTA_DYNSET_EXPR);
156  nftnl_expr_build_payload(nlh, dynset->expr);
157  mnl_attr_nest_end(nlh, nest);
158  }
159 }
160 
161 static int
162 nftnl_expr_dynset_parse(struct nftnl_expr *e, struct nlattr *attr)
163 {
164  struct nftnl_expr_dynset *dynset = nftnl_expr_data(e);
165  struct nlattr *tb[NFTA_SET_MAX+1] = {};
166  int ret = 0;
167 
168  if (mnl_attr_parse_nested(attr, nftnl_expr_dynset_cb, tb) < 0)
169  return -1;
170 
171  if (tb[NFTA_DYNSET_SREG_KEY]) {
172  dynset->sreg_key = ntohl(mnl_attr_get_u32(tb[NFTA_DYNSET_SREG_KEY]));
173  e->flags |= (1 << NFTNL_EXPR_DYNSET_SREG_KEY);
174  }
175  if (tb[NFTA_DYNSET_SREG_DATA]) {
176  dynset->sreg_data = ntohl(mnl_attr_get_u32(tb[NFTA_DYNSET_SREG_DATA]));
177  e->flags |= (1 << NFTNL_EXPR_DYNSET_SREG_DATA);
178  }
179  if (tb[NFTA_DYNSET_OP]) {
180  dynset->op = ntohl(mnl_attr_get_u32(tb[NFTA_DYNSET_OP]));
181  e->flags |= (1 << NFTNL_EXPR_DYNSET_OP);
182  }
183  if (tb[NFTA_DYNSET_TIMEOUT]) {
184  dynset->timeout = be64toh(mnl_attr_get_u64(tb[NFTA_DYNSET_TIMEOUT]));
185  e->flags |= (1 << NFTNL_EXPR_DYNSET_TIMEOUT);
186  }
187  if (tb[NFTA_DYNSET_SET_NAME]) {
188  dynset->set_name =
189  strdup(mnl_attr_get_str(tb[NFTA_DYNSET_SET_NAME]));
190  if (!dynset->set_name)
191  return -1;
192  e->flags |= (1 << NFTNL_EXPR_DYNSET_SET_NAME);
193  }
194  if (tb[NFTA_DYNSET_SET_ID]) {
195  dynset->set_id = ntohl(mnl_attr_get_u32(tb[NFTA_DYNSET_SET_ID]));
196  e->flags |= (1 << NFTNL_EXPR_DYNSET_SET_ID);
197  }
198  if (tb[NFTA_DYNSET_EXPR]) {
199  e->flags |= (1 << NFTNL_EXPR_DYNSET_EXPR);
200  dynset->expr = nftnl_expr_parse(tb[NFTA_DYNSET_EXPR]);
201  if (dynset->expr == NULL)
202  return -1;
203  }
204 
205  return ret;
206 }
207 
208 static int
209 nftnl_expr_dynset_json_parse(struct nftnl_expr *e, json_t *root,
210  struct nftnl_parse_err *err)
211 {
212 #ifdef JSON_PARSING
213  const char *set_name;
214  uint32_t uval32;
215  uint64_t uval64;
216 
217  set_name = nftnl_jansson_parse_str(root, "set", err);
218  if (set_name != NULL)
219  nftnl_expr_set_str(e, NFTNL_EXPR_DYNSET_SET_NAME, set_name);
220 
221  if (nftnl_jansson_parse_reg(root, "sreg_key",
222  NFTNL_TYPE_U32, &uval32, err) == 0)
223  nftnl_expr_set_u32(e, NFTNL_EXPR_DYNSET_SREG_KEY, uval32);
224 
225  if (nftnl_jansson_parse_reg(root, "sreg_data",
226  NFTNL_TYPE_U32, &uval32, err) == 0)
227  nftnl_expr_set_u32(e, NFTNL_EXPR_DYNSET_SREG_DATA, uval32);
228 
229  if (nftnl_jansson_parse_val(root, "op", NFTNL_TYPE_U32, &uval32,
230  err) == 0)
231  nftnl_expr_set_u32(e, NFTNL_EXPR_DYNSET_OP, uval32);
232 
233  if (nftnl_jansson_parse_val(root, "timeout", NFTNL_TYPE_U64, &uval64,
234  err) == 0)
235  nftnl_expr_set_u64(e, NFTNL_EXPR_DYNSET_TIMEOUT, uval64);
236 
237  return 0;
238 #else
239  errno = EOPNOTSUPP;
240  return -1;
241 #endif
242 }
243 
244 static int
245 nftnl_expr_dynset_export(char *buf, size_t size,
246  const struct nftnl_expr *e, int type)
247 {
248  struct nftnl_expr_dynset *dynset = nftnl_expr_data(e);
249  NFTNL_BUF_INIT(b, buf, size);
250 
251  if (e->flags & (1 << NFTNL_EXPR_DYNSET_SET_NAME))
252  nftnl_buf_str(&b, type, dynset->set_name, SET_NAME);
253  if (e->flags & (1 << NFTNL_EXPR_DYNSET_SREG_KEY))
254  nftnl_buf_u32(&b, type, dynset->sreg_key, SREG_KEY);
255  if (e->flags & (1 << NFTNL_EXPR_DYNSET_SREG_DATA))
256  nftnl_buf_u32(&b, type, dynset->sreg_data, SREG_DATA);
257 
258  return nftnl_buf_done(&b);
259 }
260 
261 static const char *op2str_array[] = {
262  [NFT_DYNSET_OP_ADD] = "add",
263  [NFT_DYNSET_OP_UPDATE] = "update",
264 };
265 
266 static const char *op2str(enum nft_dynset_ops op)
267 {
268  if (op > NFT_DYNSET_OP_UPDATE)
269  return "unknown";
270  return op2str_array[op];
271 }
272 
273 static int
274 nftnl_expr_dynset_snprintf_default(char *buf, size_t size,
275  const struct nftnl_expr *e)
276 {
277  struct nftnl_expr_dynset *dynset = nftnl_expr_data(e);
278  struct nftnl_expr *expr;
279  int remain = size, offset = 0, ret;
280 
281  ret = snprintf(buf, remain, "%s reg_key %u set %s ",
282  op2str(dynset->op), dynset->sreg_key, dynset->set_name);
283  SNPRINTF_BUFFER_SIZE(ret, remain, offset);
284 
285  if (e->flags & (1 << NFTNL_EXPR_DYNSET_SREG_DATA)) {
286  ret = snprintf(buf + offset, remain, "sreg_data %u ",
287  dynset->sreg_data);
288  SNPRINTF_BUFFER_SIZE(ret, remain, offset);
289  }
290  if (e->flags & (1 << NFTNL_EXPR_DYNSET_TIMEOUT)) {
291  ret = snprintf(buf + offset, remain, "timeout %"PRIu64"ms ",
292  dynset->timeout);
293  SNPRINTF_BUFFER_SIZE(ret, remain, offset);
294  }
295  if (e->flags & (1 << NFTNL_EXPR_DYNSET_EXPR)) {
296  expr = dynset->expr;
297  ret = snprintf(buf + offset, remain, "expr [ %s ",
298  expr->ops->name);
299  SNPRINTF_BUFFER_SIZE(ret, remain, offset);
300 
301  ret = nftnl_expr_snprintf(buf + offset, remain, expr,
302  NFTNL_OUTPUT_DEFAULT,
303  NFTNL_OF_EVENT_ANY);
304  SNPRINTF_BUFFER_SIZE(ret, remain, offset);
305 
306  ret = snprintf(buf + offset, remain, "] ");
307  SNPRINTF_BUFFER_SIZE(ret, remain, offset);
308  }
309 
310  return offset;
311 }
312 
313 static int
314 nftnl_expr_dynset_snprintf(char *buf, size_t size, uint32_t type,
315  uint32_t flags, const struct nftnl_expr *e)
316 {
317  switch (type) {
318  case NFTNL_OUTPUT_DEFAULT:
319  return nftnl_expr_dynset_snprintf_default(buf, size, e);
320  case NFTNL_OUTPUT_XML:
321  case NFTNL_OUTPUT_JSON:
322  return nftnl_expr_dynset_export(buf, size, e, type);
323  default:
324  break;
325  }
326  return -1;
327 }
328 
329 static void nftnl_expr_dynset_free(const struct nftnl_expr *e)
330 {
331  struct nftnl_expr_dynset *dynset = nftnl_expr_data(e);
332 
333  xfree(dynset->set_name);
334 }
335 
336 static bool nftnl_expr_dynset_cmp(const struct nftnl_expr *e1,
337  const struct nftnl_expr *e2)
338 {
339  struct nftnl_expr_dynset *d1 = nftnl_expr_data(e1);
340  struct nftnl_expr_dynset *d2 = nftnl_expr_data(e2);
341  bool eq = true;
342 
343  if (e1->flags & (1 << NFTNL_EXPR_DYNSET_SREG_KEY))
344  eq &= (d1->sreg_key == d2->sreg_key);
345  if (e1->flags & (1 << NFTNL_EXPR_DYNSET_SREG_DATA))
346  eq &= (d1->sreg_data == d2->sreg_data);
347  if (e1->flags & (1 << NFTNL_EXPR_DYNSET_OP))
348  eq &= (d1->op == d2->op);
349  if (e1->flags & (1 << NFTNL_EXPR_DYNSET_TIMEOUT))
350  eq &= (d1->timeout == d2->timeout);
351  if (e1->flags & (1 << NFTNL_EXPR_DYNSET_EXPR))
352  eq &= nftnl_expr_cmp(d1->expr, d2->expr);
353  if (e1->flags & (1 << NFTNL_EXPR_DYNSET_SET_NAME))
354  eq &= !strcmp(d1->set_name, d2->set_name);
355  if (e1->flags & (1 << NFTNL_EXPR_DYNSET_SET_ID))
356  eq &= (d1->set_id == d2->set_id);
357 
358  return eq;
359 }
360 
361 struct expr_ops expr_ops_dynset = {
362  .name = "dynset",
363  .alloc_len = sizeof(struct nftnl_expr_dynset),
364  .max_attr = NFTA_DYNSET_MAX,
365  .free = nftnl_expr_dynset_free,
366  .cmp = nftnl_expr_dynset_cmp,
367  .set = nftnl_expr_dynset_set,
368  .get = nftnl_expr_dynset_get,
369  .parse = nftnl_expr_dynset_parse,
370  .build = nftnl_expr_dynset_build,
371  .snprintf = nftnl_expr_dynset_snprintf,
372  .json_parse = nftnl_expr_dynset_json_parse,
373 };