libnftnl  1.1.7
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 
25  enum nft_registers sreg_key;
26  enum nft_registers sreg_data;
27  enum nft_dynset_ops op;
28  uint64_t timeout;
29  struct nftnl_expr *expr;
30  char *set_name;
31  uint32_t set_id;
32 };
33 
34 static int
35 nftnl_expr_dynset_set(struct nftnl_expr *e, uint16_t type,
36  const void *data, uint32_t data_len)
37 {
38  struct nftnl_expr_dynset *dynset = nftnl_expr_data(e);
39 
40  switch (type) {
41  case NFTNL_EXPR_DYNSET_SREG_KEY:
42  memcpy(&dynset->sreg_key, data, sizeof(dynset->sreg_key));
43  break;
44  case NFTNL_EXPR_DYNSET_SREG_DATA:
45  memcpy(&dynset->sreg_data, data, sizeof(dynset->sreg_data));
46  break;
47  case NFTNL_EXPR_DYNSET_OP:
48  memcpy(&dynset->op, data, sizeof(dynset->op));
49  break;
50  case NFTNL_EXPR_DYNSET_TIMEOUT:
51  memcpy(&dynset->timeout, data, sizeof(dynset->timeout));
52  break;
53  case NFTNL_EXPR_DYNSET_SET_NAME:
54  dynset->set_name = strdup((const char *)data);
55  if (!dynset->set_name)
56  return -1;
57  break;
58  case NFTNL_EXPR_DYNSET_SET_ID:
59  memcpy(&dynset->set_id, data, sizeof(dynset->set_id));
60  break;
61  case NFTNL_EXPR_DYNSET_EXPR:
62  dynset->expr = (void *)data;
63  break;
64  default:
65  return -1;
66  }
67  return 0;
68 }
69 
70 static const void *
71 nftnl_expr_dynset_get(const struct nftnl_expr *e, uint16_t type,
72  uint32_t *data_len)
73 {
74  struct nftnl_expr_dynset *dynset = nftnl_expr_data(e);
75 
76  switch (type) {
77  case NFTNL_EXPR_DYNSET_SREG_KEY:
78  *data_len = sizeof(dynset->sreg_key);
79  return &dynset->sreg_key;
80  case NFTNL_EXPR_DYNSET_SREG_DATA:
81  *data_len = sizeof(dynset->sreg_data);
82  return &dynset->sreg_data;
83  case NFTNL_EXPR_DYNSET_OP:
84  *data_len = sizeof(dynset->op);
85  return &dynset->op;
86  case NFTNL_EXPR_DYNSET_TIMEOUT:
87  *data_len = sizeof(dynset->timeout);
88  return &dynset->timeout;
89  case NFTNL_EXPR_DYNSET_SET_NAME:
90  *data_len = strlen(dynset->set_name) + 1;
91  return dynset->set_name;
92  case NFTNL_EXPR_DYNSET_SET_ID:
93  *data_len = sizeof(dynset->set_id);
94  return &dynset->set_id;
95  case NFTNL_EXPR_DYNSET_EXPR:
96  return dynset->expr;
97  }
98  return NULL;
99 }
100 
101 static int nftnl_expr_dynset_cb(const struct nlattr *attr, void *data)
102 {
103  const struct nlattr **tb = data;
104  int type = mnl_attr_get_type(attr);
105 
106  if (mnl_attr_type_valid(attr, NFTA_SET_MAX) < 0)
107  return MNL_CB_OK;
108 
109  switch (type) {
110  case NFTA_DYNSET_SREG_KEY:
111  case NFTA_DYNSET_SREG_DATA:
112  case NFTA_DYNSET_SET_ID:
113  case NFTA_DYNSET_OP:
114  if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0)
115  abi_breakage();
116  break;
117  case NFTA_DYNSET_TIMEOUT:
118  if (mnl_attr_validate(attr, MNL_TYPE_U64) < 0)
119  abi_breakage();
120  break;
121  case NFTA_DYNSET_SET_NAME:
122  if (mnl_attr_validate(attr, MNL_TYPE_STRING) < 0)
123  abi_breakage();
124  break;
125  case NFTA_DYNSET_EXPR:
126  if (mnl_attr_validate(attr, MNL_TYPE_NESTED) < 0)
127  abi_breakage();
128  break;
129  }
130 
131  tb[type] = attr;
132  return MNL_CB_OK;
133 }
134 
135 static void
136 nftnl_expr_dynset_build(struct nlmsghdr *nlh, const struct nftnl_expr *e)
137 {
138  struct nftnl_expr_dynset *dynset = nftnl_expr_data(e);
139  struct nlattr *nest;
140 
141  if (e->flags & (1 << NFTNL_EXPR_DYNSET_SREG_KEY))
142  mnl_attr_put_u32(nlh, NFTA_DYNSET_SREG_KEY, htonl(dynset->sreg_key));
143  if (e->flags & (1 << NFTNL_EXPR_DYNSET_SREG_DATA))
144  mnl_attr_put_u32(nlh, NFTA_DYNSET_SREG_DATA, htonl(dynset->sreg_data));
145  if (e->flags & (1 << NFTNL_EXPR_DYNSET_OP))
146  mnl_attr_put_u32(nlh, NFTA_DYNSET_OP, htonl(dynset->op));
147  if (e->flags & (1 << NFTNL_EXPR_DYNSET_TIMEOUT))
148  mnl_attr_put_u64(nlh, NFTA_DYNSET_TIMEOUT, htobe64(dynset->timeout));
149  if (e->flags & (1 << NFTNL_EXPR_DYNSET_SET_NAME))
150  mnl_attr_put_strz(nlh, NFTA_DYNSET_SET_NAME, dynset->set_name);
151  if (e->flags & (1 << NFTNL_EXPR_DYNSET_SET_ID))
152  mnl_attr_put_u32(nlh, NFTA_DYNSET_SET_ID, htonl(dynset->set_id));
153  if (e->flags & (1 << NFTNL_EXPR_DYNSET_EXPR)) {
154  nest = mnl_attr_nest_start(nlh, NFTA_DYNSET_EXPR);
155  nftnl_expr_build_payload(nlh, dynset->expr);
156  mnl_attr_nest_end(nlh, nest);
157  }
158 }
159 
160 static int
161 nftnl_expr_dynset_parse(struct nftnl_expr *e, struct nlattr *attr)
162 {
163  struct nftnl_expr_dynset *dynset = nftnl_expr_data(e);
164  struct nlattr *tb[NFTA_SET_MAX+1] = {};
165  int ret = 0;
166 
167  if (mnl_attr_parse_nested(attr, nftnl_expr_dynset_cb, tb) < 0)
168  return -1;
169 
170  if (tb[NFTA_DYNSET_SREG_KEY]) {
171  dynset->sreg_key = ntohl(mnl_attr_get_u32(tb[NFTA_DYNSET_SREG_KEY]));
172  e->flags |= (1 << NFTNL_EXPR_DYNSET_SREG_KEY);
173  }
174  if (tb[NFTA_DYNSET_SREG_DATA]) {
175  dynset->sreg_data = ntohl(mnl_attr_get_u32(tb[NFTA_DYNSET_SREG_DATA]));
176  e->flags |= (1 << NFTNL_EXPR_DYNSET_SREG_DATA);
177  }
178  if (tb[NFTA_DYNSET_OP]) {
179  dynset->op = ntohl(mnl_attr_get_u32(tb[NFTA_DYNSET_OP]));
180  e->flags |= (1 << NFTNL_EXPR_DYNSET_OP);
181  }
182  if (tb[NFTA_DYNSET_TIMEOUT]) {
183  dynset->timeout = be64toh(mnl_attr_get_u64(tb[NFTA_DYNSET_TIMEOUT]));
184  e->flags |= (1 << NFTNL_EXPR_DYNSET_TIMEOUT);
185  }
186  if (tb[NFTA_DYNSET_SET_NAME]) {
187  dynset->set_name =
188  strdup(mnl_attr_get_str(tb[NFTA_DYNSET_SET_NAME]));
189  if (!dynset->set_name)
190  return -1;
191  e->flags |= (1 << NFTNL_EXPR_DYNSET_SET_NAME);
192  }
193  if (tb[NFTA_DYNSET_SET_ID]) {
194  dynset->set_id = ntohl(mnl_attr_get_u32(tb[NFTA_DYNSET_SET_ID]));
195  e->flags |= (1 << NFTNL_EXPR_DYNSET_SET_ID);
196  }
197  if (tb[NFTA_DYNSET_EXPR]) {
198  e->flags |= (1 << NFTNL_EXPR_DYNSET_EXPR);
199  dynset->expr = nftnl_expr_parse(tb[NFTA_DYNSET_EXPR]);
200  if (dynset->expr == NULL)
201  return -1;
202  }
203 
204  return ret;
205 }
206 
207 static const char *op2str_array[] = {
208  [NFT_DYNSET_OP_ADD] = "add",
209  [NFT_DYNSET_OP_UPDATE] = "update",
210  [NFT_DYNSET_OP_DELETE] = "delete",
211 };
212 
213 static const char *op2str(enum nft_dynset_ops op)
214 {
215  if (op > NFT_DYNSET_OP_DELETE)
216  return "unknown";
217  return op2str_array[op];
218 }
219 
220 static int
221 nftnl_expr_dynset_snprintf_default(char *buf, size_t size,
222  const struct nftnl_expr *e)
223 {
224  struct nftnl_expr_dynset *dynset = nftnl_expr_data(e);
225  struct nftnl_expr *expr;
226  int remain = size, offset = 0, ret;
227 
228  ret = snprintf(buf, remain, "%s reg_key %u set %s ",
229  op2str(dynset->op), dynset->sreg_key, dynset->set_name);
230  SNPRINTF_BUFFER_SIZE(ret, remain, offset);
231 
232  if (e->flags & (1 << NFTNL_EXPR_DYNSET_SREG_DATA)) {
233  ret = snprintf(buf + offset, remain, "sreg_data %u ",
234  dynset->sreg_data);
235  SNPRINTF_BUFFER_SIZE(ret, remain, offset);
236  }
237  if (e->flags & (1 << NFTNL_EXPR_DYNSET_TIMEOUT)) {
238  ret = snprintf(buf + offset, remain, "timeout %"PRIu64"ms ",
239  dynset->timeout);
240  SNPRINTF_BUFFER_SIZE(ret, remain, offset);
241  }
242  if (e->flags & (1 << NFTNL_EXPR_DYNSET_EXPR)) {
243  expr = dynset->expr;
244  ret = snprintf(buf + offset, remain, "expr [ %s ",
245  expr->ops->name);
246  SNPRINTF_BUFFER_SIZE(ret, remain, offset);
247 
248  ret = nftnl_expr_snprintf(buf + offset, remain, expr,
249  NFTNL_OUTPUT_DEFAULT,
250  NFTNL_OF_EVENT_ANY);
251  SNPRINTF_BUFFER_SIZE(ret, remain, offset);
252 
253  ret = snprintf(buf + offset, remain, "] ");
254  SNPRINTF_BUFFER_SIZE(ret, remain, offset);
255  }
256 
257  return offset;
258 }
259 
260 static int
261 nftnl_expr_dynset_snprintf(char *buf, size_t size, uint32_t type,
262  uint32_t flags, const struct nftnl_expr *e)
263 {
264  switch (type) {
265  case NFTNL_OUTPUT_DEFAULT:
266  return nftnl_expr_dynset_snprintf_default(buf, size, e);
267  case NFTNL_OUTPUT_XML:
268  case NFTNL_OUTPUT_JSON:
269  default:
270  break;
271  }
272  return -1;
273 }
274 
275 static void nftnl_expr_dynset_free(const struct nftnl_expr *e)
276 {
277  struct nftnl_expr_dynset *dynset = nftnl_expr_data(e);
278 
279  xfree(dynset->set_name);
280  if (dynset->expr)
281  nftnl_expr_free(dynset->expr);
282 }
283 
284 struct expr_ops expr_ops_dynset = {
285  .name = "dynset",
286  .alloc_len = sizeof(struct nftnl_expr_dynset),
287  .max_attr = NFTA_DYNSET_MAX,
288  .free = nftnl_expr_dynset_free,
289  .set = nftnl_expr_dynset_set,
290  .get = nftnl_expr_dynset_get,
291  .parse = nftnl_expr_dynset_parse,
292  .build = nftnl_expr_dynset_build,
293  .snprintf = nftnl_expr_dynset_snprintf,
294 };