libnftnl  1.0.2
immediate.c
1 /*
2  * (C) 2012 by Pablo Neira Ayuso <pablo@netfilter.org>
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  * This code has been sponsored by Sophos Astaro <http://www.sophos.com>
10  */
11 
12 #include <stdio.h>
13 #include <stdint.h>
14 #include <string.h>
15 #include <arpa/inet.h>
16 #include <errno.h>
17 #include "internal.h"
18 #include <libmnl/libmnl.h>
19 #include <linux/netfilter/nf_tables.h>
20 #include <libnftnl/expr.h>
21 #include <libnftnl/rule.h>
22 #include "expr_ops.h"
23 #include "data_reg.h"
24 
26  union nft_data_reg data;
27  enum nft_registers dreg;
28 };
29 
30 static int
31 nft_rule_expr_immediate_set(struct nft_rule_expr *e, uint16_t type,
32  const void *data, uint32_t data_len)
33 {
34  struct nft_expr_immediate *imm = nft_expr_data(e);
35 
36  switch(type) {
37  case NFT_EXPR_IMM_DREG:
38  imm->dreg = *((uint32_t *)data);
39  break;
40  case NFT_EXPR_IMM_DATA:
41  memcpy(&imm->data.val, data, data_len);
42  imm->data.len = data_len;
43  break;
44  case NFT_EXPR_IMM_VERDICT:
45  imm->data.verdict = *((uint32_t *)data);
46  break;
47  case NFT_EXPR_IMM_CHAIN:
48  if (imm->data.chain)
49  xfree(imm->data.chain);
50 
51  imm->data.chain = strdup(data);
52  break;
53  default:
54  return -1;
55  }
56  return 0;
57 }
58 
59 static const void *
60 nft_rule_expr_immediate_get(const struct nft_rule_expr *e, uint16_t type,
61  uint32_t *data_len)
62 {
63  struct nft_expr_immediate *imm = nft_expr_data(e);
64 
65  switch(type) {
66  case NFT_EXPR_IMM_DREG:
67  *data_len = sizeof(imm->dreg);
68  return &imm->dreg;
69  case NFT_EXPR_IMM_DATA:
70  *data_len = imm->data.len;
71  return &imm->data.val;
72  case NFT_EXPR_IMM_VERDICT:
73  *data_len = sizeof(imm->data.verdict);
74  return &imm->data.verdict;
75  case NFT_EXPR_IMM_CHAIN:
76  *data_len = strlen(imm->data.chain)+1;
77  return imm->data.chain;
78  }
79  return NULL;
80 }
81 
82 static int nft_rule_expr_immediate_cb(const struct nlattr *attr, void *data)
83 {
84  const struct nlattr **tb = data;
85  int type = mnl_attr_get_type(attr);
86 
87  if (mnl_attr_type_valid(attr, NFTA_IMMEDIATE_MAX) < 0)
88  return MNL_CB_OK;
89 
90  switch(type) {
91  case NFTA_IMMEDIATE_DREG:
92  if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0) {
93  perror("mnl_attr_validate");
94  return MNL_CB_ERROR;
95  }
96  break;
97  case NFTA_IMMEDIATE_DATA:
98  if (mnl_attr_validate(attr, MNL_TYPE_BINARY) < 0) {
99  perror("mnl_attr_validate");
100  return MNL_CB_ERROR;
101  }
102  break;
103  }
104 
105  tb[type] = attr;
106  return MNL_CB_OK;
107 }
108 
109 static void
110 nft_rule_expr_immediate_build(struct nlmsghdr *nlh, struct nft_rule_expr *e)
111 {
112  struct nft_expr_immediate *imm = nft_expr_data(e);
113 
114  if (e->flags & (1 << NFT_EXPR_IMM_DREG))
115  mnl_attr_put_u32(nlh, NFTA_IMMEDIATE_DREG, htonl(imm->dreg));
116 
117  /* Sane configurations allows you to set ONLY one of these two below */
118  if (e->flags & (1 << NFT_EXPR_IMM_DATA)) {
119  struct nlattr *nest;
120 
121  nest = mnl_attr_nest_start(nlh, NFTA_IMMEDIATE_DATA);
122  mnl_attr_put(nlh, NFTA_DATA_VALUE, imm->data.len, imm->data.val);
123  mnl_attr_nest_end(nlh, nest);
124 
125  } else if (e->flags & (1 << NFT_EXPR_IMM_VERDICT)) {
126  struct nlattr *nest1, *nest2;
127 
128  nest1 = mnl_attr_nest_start(nlh, NFTA_IMMEDIATE_DATA);
129  nest2 = mnl_attr_nest_start(nlh, NFTA_DATA_VERDICT);
130  mnl_attr_put_u32(nlh, NFTA_VERDICT_CODE, htonl(imm->data.verdict));
131  if (e->flags & (1 << NFT_EXPR_IMM_CHAIN))
132  mnl_attr_put_strz(nlh, NFTA_VERDICT_CHAIN, imm->data.chain);
133 
134  mnl_attr_nest_end(nlh, nest1);
135  mnl_attr_nest_end(nlh, nest2);
136  }
137 }
138 
139 static int
140 nft_rule_expr_immediate_parse(struct nft_rule_expr *e, struct nlattr *attr)
141 {
142  struct nft_expr_immediate *imm = nft_expr_data(e);
143  struct nlattr *tb[NFTA_IMMEDIATE_MAX+1] = {};
144  int ret = 0;
145 
146  if (mnl_attr_parse_nested(attr, nft_rule_expr_immediate_cb, tb) < 0)
147  return -1;
148 
149  if (tb[NFTA_IMMEDIATE_DREG]) {
150  imm->dreg = ntohl(mnl_attr_get_u32(tb[NFTA_IMMEDIATE_DREG]));
151  e->flags |= (1 << NFT_EXPR_IMM_DREG);
152  }
153  if (tb[NFTA_IMMEDIATE_DATA]) {
154  int type;
155 
156  ret = nft_parse_data(&imm->data, tb[NFTA_IMMEDIATE_DATA], &type);
157  if (ret < 0)
158  return ret;
159 
160  switch(type) {
161  case DATA_VALUE:
162  /* real immediate data to be loaded to destination */
163  e->flags |= (1 << NFT_EXPR_IMM_DATA);
164  break;
165  case DATA_VERDICT:
166  /* NF_ACCEPT, NF_DROP, NF_QUEUE and NFT_RETURN case */
167  e->flags |= (1 << NFT_EXPR_IMM_VERDICT);
168  break;
169  case DATA_CHAIN:
170  /* NFT_GOTO and NFT_JUMP case */
171  e->flags |= (1 << NFT_EXPR_IMM_VERDICT) |
172  (1 << NFT_EXPR_IMM_CHAIN);
173  break;
174  }
175  }
176 
177  return ret;
178 }
179 
180 static int
181 nft_rule_expr_immediate_json_parse(struct nft_rule_expr *e, json_t *root,
182  struct nft_parse_err *err)
183 {
184 #ifdef JSON_PARSING
185  struct nft_expr_immediate *imm = nft_expr_data(e);
186  int datareg_type;
187  uint32_t reg;
188 
189  if (nft_jansson_parse_reg(root, "dreg", NFT_TYPE_U32, &reg, err) == 0)
190  nft_rule_expr_set_u32(e, NFT_EXPR_IMM_DREG, reg);
191 
192  datareg_type = nft_jansson_data_reg_parse(root, "immediatedata",
193  &imm->data, err);
194  if (datareg_type >= 0) {
195  switch (datareg_type) {
196  case DATA_VALUE:
197  e->flags |= (1 << NFT_EXPR_IMM_DATA);
198  break;
199  case DATA_VERDICT:
200  e->flags |= (1 << NFT_EXPR_IMM_VERDICT);
201  break;
202  case DATA_CHAIN:
203  e->flags |= (1 << NFT_EXPR_IMM_CHAIN);
204  break;
205  default:
206  return -1;
207  }
208  }
209  return 0;
210 #else
211  errno = EOPNOTSUPP;
212  return -1;
213 #endif
214 }
215 
216 static int
217 nft_rule_expr_immediate_xml_parse(struct nft_rule_expr *e, mxml_node_t *tree,
218  struct nft_parse_err *err)
219 {
220 #ifdef XML_PARSING
221  struct nft_expr_immediate *imm = nft_expr_data(e);
222  int datareg_type;
223  uint32_t reg;
224 
225  if (nft_mxml_reg_parse(tree, "dreg", &reg, MXML_DESCEND_FIRST,
226  NFT_XML_MAND, err) == 0)
227  nft_rule_expr_set_u32(e, NFT_EXPR_IMM_DREG, reg);
228 
229  datareg_type = nft_mxml_data_reg_parse(tree, "immediatedata",
230  &imm->data, NFT_XML_MAND, err);
231  if (datareg_type >= 0) {
232  switch (datareg_type) {
233  case DATA_VALUE:
234  e->flags |= (1 << NFT_EXPR_IMM_DATA);
235  break;
236  case DATA_VERDICT:
237  e->flags |= (1 << NFT_EXPR_IMM_VERDICT);
238  break;
239  case DATA_CHAIN:
240  e->flags |= (1 << NFT_EXPR_IMM_CHAIN);
241  break;
242  default:
243  return -1;
244  }
245  }
246 
247  return 0;
248 #else
249  errno = EOPNOTSUPP;
250  return -1;
251 #endif
252 }
253 
254 static int
255 nft_rule_expr_immediate_snprintf_json(char *buf, size_t len,
256  struct nft_rule_expr *e, uint32_t flags)
257 {
258  int size = len, offset = 0, ret;
259  struct nft_expr_immediate *imm = nft_expr_data(e);
260 
261  if (e->flags & (1 << NFT_EXPR_IMM_DREG)) {
262  ret = snprintf(buf, len, "\"dreg\":%u,", imm->dreg);
263  SNPRINTF_BUFFER_SIZE(ret, size, len, offset);
264  }
265  if (e->flags & (1 << NFT_EXPR_IMM_DATA)) {
266  ret = nft_data_reg_snprintf(buf + offset, len, &imm->data,
267  NFT_OUTPUT_JSON, flags, DATA_VALUE);
268  SNPRINTF_BUFFER_SIZE(ret, size, len, offset);
269 
270  } else if (e->flags & (1 << NFT_EXPR_IMM_VERDICT)) {
271  ret = nft_data_reg_snprintf(buf + offset, len, &imm->data,
272  NFT_OUTPUT_JSON, flags, DATA_VERDICT);
273  SNPRINTF_BUFFER_SIZE(ret, size, len, offset);
274 
275  } else if (e->flags & (1 << NFT_EXPR_IMM_CHAIN)) {
276  ret = nft_data_reg_snprintf(buf + offset, len, &imm->data,
277  NFT_OUTPUT_JSON, flags, DATA_CHAIN);
278  SNPRINTF_BUFFER_SIZE(ret, size, len, offset);
279  }
280 
281  return offset;
282 }
283 
284 static int
285 nft_rule_expr_immediate_snprintf_xml(char *buf, size_t len,
286  struct nft_rule_expr *e, uint32_t flags)
287 {
288  int size = len, offset = 0, ret;
289  struct nft_expr_immediate *imm = nft_expr_data(e);
290 
291  if (e->flags & (1 << NFT_EXPR_IMM_DREG)) {
292  ret = snprintf(buf, len, "<dreg>%u</dreg>", imm->dreg);
293  SNPRINTF_BUFFER_SIZE(ret, size, len, offset);
294  }
295  if (e->flags & (1 << NFT_EXPR_IMM_DATA)) {
296  ret = nft_data_reg_snprintf(buf + offset, len, &imm->data,
297  NFT_OUTPUT_XML, flags, DATA_VALUE);
298  SNPRINTF_BUFFER_SIZE(ret, size, len, offset);
299 
300  } else if (e->flags & (1 << NFT_EXPR_IMM_VERDICT)) {
301  ret = nft_data_reg_snprintf(buf + offset, len, &imm->data,
302  NFT_OUTPUT_XML, flags, DATA_VERDICT);
303  SNPRINTF_BUFFER_SIZE(ret, size, len, offset);
304 
305  } else if (e->flags & (1 << NFT_EXPR_IMM_CHAIN)) {
306  ret = nft_data_reg_snprintf(buf + offset, len, &imm->data,
307  NFT_OUTPUT_XML, flags, DATA_CHAIN);
308  SNPRINTF_BUFFER_SIZE(ret, size, len, offset);
309  }
310 
311  return offset;
312 }
313 
314 static int
315 nft_rule_expr_immediate_snprintf_default(char *buf, size_t len,
316  struct nft_rule_expr *e, uint32_t flags)
317 {
318  int size = len, offset = 0, ret;
319  struct nft_expr_immediate *imm = nft_expr_data(e);
320 
321  ret = snprintf(buf, len, "reg %u ", imm->dreg);
322  SNPRINTF_BUFFER_SIZE(ret, size, len, offset);
323 
324  if (e->flags & (1 << NFT_EXPR_IMM_DATA)) {
325  ret = nft_data_reg_snprintf(buf+offset, len, &imm->data,
326  NFT_OUTPUT_DEFAULT, flags, DATA_VALUE);
327  SNPRINTF_BUFFER_SIZE(ret, size, len, offset);
328 
329  } else if (e->flags & (1 << NFT_EXPR_IMM_VERDICT)) {
330  ret = nft_data_reg_snprintf(buf+offset, len, &imm->data,
331  NFT_OUTPUT_DEFAULT, flags, DATA_VERDICT);
332  SNPRINTF_BUFFER_SIZE(ret, size, len, offset);
333 
334  } else if (e->flags & (1 << NFT_EXPR_IMM_CHAIN)) {
335  ret = nft_data_reg_snprintf(buf+offset, len, &imm->data,
336  NFT_OUTPUT_DEFAULT, flags, DATA_CHAIN);
337  SNPRINTF_BUFFER_SIZE(ret, size, len, offset);
338  }
339 
340  return offset;
341 }
342 
343 static int
344 nft_rule_expr_immediate_snprintf(char *buf, size_t len, uint32_t type,
345  uint32_t flags, struct nft_rule_expr *e)
346 {
347  switch(type) {
348  case NFT_OUTPUT_DEFAULT:
349  return nft_rule_expr_immediate_snprintf_default(buf, len, e, flags);
350  case NFT_OUTPUT_XML:
351  return nft_rule_expr_immediate_snprintf_xml(buf, len, e, flags);
352  case NFT_OUTPUT_JSON:
353  return nft_rule_expr_immediate_snprintf_json(buf, len, e, flags);
354  default:
355  break;
356  }
357  return -1;
358 }
359 
360 struct expr_ops expr_ops_immediate = {
361  .name = "immediate",
362  .alloc_len = sizeof(struct nft_expr_immediate),
363  .max_attr = NFTA_IMMEDIATE_MAX,
364  .set = nft_rule_expr_immediate_set,
365  .get = nft_rule_expr_immediate_get,
366  .parse = nft_rule_expr_immediate_parse,
367  .build = nft_rule_expr_immediate_build,
368  .snprintf = nft_rule_expr_immediate_snprintf,
369  .xml_parse = nft_rule_expr_immediate_xml_parse,
370  .json_parse = nft_rule_expr_immediate_json_parse,
371 };
372 
373 static void __init expr_immediate_init(void)
374 {
375  nft_expr_ops_register(&expr_ops_immediate);
376 }