libnftnl  1.0.2
cmp.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 "internal.h"
13 
14 #include <stdio.h>
15 #include <stdint.h>
16 #include <string.h>
17 #include <arpa/inet.h>
18 #include <errno.h>
19 
20 #include <libmnl/libmnl.h>
21 #include <linux/netfilter/nf_tables.h>
22 #include <libnftnl/expr.h>
23 #include <libnftnl/rule.h>
24 #include "expr_ops.h"
25 #include "data_reg.h"
26 
27 struct nft_expr_cmp {
28  union nft_data_reg data;
29  enum nft_registers sreg;
30  enum nft_cmp_ops op;
31 };
32 
33 static int
34 nft_rule_expr_cmp_set(struct nft_rule_expr *e, uint16_t type,
35  const void *data, uint32_t data_len)
36 {
37  struct nft_expr_cmp *cmp = nft_expr_data(e);
38 
39  switch(type) {
40  case NFT_EXPR_CMP_SREG:
41  cmp->sreg = *((uint32_t *)data);
42  break;
43  case NFT_EXPR_CMP_OP:
44  cmp->op = *((uint32_t *)data);
45  break;
46  case NFT_EXPR_CMP_DATA:
47  memcpy(&cmp->data.val, data, data_len);
48  cmp->data.len = data_len;
49  break;
50  default:
51  return -1;
52  }
53  return 0;
54 }
55 
56 static const void *
57 nft_rule_expr_cmp_get(const struct nft_rule_expr *e, uint16_t type,
58  uint32_t *data_len)
59 {
60  struct nft_expr_cmp *cmp = nft_expr_data(e);
61 
62  switch(type) {
63  case NFT_EXPR_CMP_SREG:
64  *data_len = sizeof(cmp->sreg);
65  return &cmp->sreg;
66  case NFT_EXPR_CMP_OP:
67  *data_len = sizeof(cmp->op);
68  return &cmp->op;
69  case NFT_EXPR_CMP_DATA:
70  *data_len = cmp->data.len;
71  return &cmp->data.val;
72  }
73  return NULL;
74 }
75 
76 static int nft_rule_expr_cmp_cb(const struct nlattr *attr, void *data)
77 {
78  const struct nlattr **tb = data;
79  int type = mnl_attr_get_type(attr);
80 
81  if (mnl_attr_type_valid(attr, NFTA_CMP_MAX) < 0)
82  return MNL_CB_OK;
83 
84  switch(type) {
85  case NFTA_CMP_SREG:
86  case NFTA_CMP_OP:
87  if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0) {
88  perror("mnl_attr_validate");
89  return MNL_CB_ERROR;
90  }
91  break;
92  case NFTA_CMP_DATA:
93  if (mnl_attr_validate(attr, MNL_TYPE_BINARY) < 0) {
94  perror("mnl_attr_validate");
95  return MNL_CB_ERROR;
96  }
97  break;
98  }
99 
100  tb[type] = attr;
101  return MNL_CB_OK;
102 }
103 
104 static void
105 nft_rule_expr_cmp_build(struct nlmsghdr *nlh, struct nft_rule_expr *e)
106 {
107  struct nft_expr_cmp *cmp = nft_expr_data(e);
108 
109  if (e->flags & (1 << NFT_EXPR_CMP_SREG))
110  mnl_attr_put_u32(nlh, NFTA_CMP_SREG, htonl(cmp->sreg));
111  if (e->flags & (1 << NFT_EXPR_CMP_OP))
112  mnl_attr_put_u32(nlh, NFTA_CMP_OP, htonl(cmp->op));
113  if (e->flags & (1 << NFT_EXPR_CMP_DATA)) {
114  struct nlattr *nest;
115 
116  nest = mnl_attr_nest_start(nlh, NFTA_CMP_DATA);
117  mnl_attr_put(nlh, NFTA_DATA_VALUE, cmp->data.len, cmp->data.val);
118  mnl_attr_nest_end(nlh, nest);
119  }
120 }
121 
122 static int
123 nft_rule_expr_cmp_parse(struct nft_rule_expr *e, struct nlattr *attr)
124 {
125  struct nft_expr_cmp *cmp = nft_expr_data(e);
126  struct nlattr *tb[NFTA_CMP_MAX+1] = {};
127  int ret = 0;
128 
129  if (mnl_attr_parse_nested(attr, nft_rule_expr_cmp_cb, tb) < 0)
130  return -1;
131 
132  if (tb[NFTA_CMP_SREG]) {
133  cmp->sreg = ntohl(mnl_attr_get_u32(tb[NFTA_CMP_SREG]));
134  e->flags |= (1 << NFTA_CMP_SREG);
135  }
136  if (tb[NFTA_CMP_OP]) {
137  cmp->op = ntohl(mnl_attr_get_u32(tb[NFTA_CMP_OP]));
138  e->flags |= (1 << NFTA_CMP_OP);
139  }
140  if (tb[NFTA_CMP_DATA]) {
141  ret = nft_parse_data(&cmp->data, tb[NFTA_CMP_DATA], NULL);
142  e->flags |= (1 << NFTA_CMP_DATA);
143  }
144 
145  return ret;
146 }
147 
148 static char *expr_cmp_str[] = {
149  [NFT_CMP_EQ] = "eq",
150  [NFT_CMP_NEQ] = "neq",
151  [NFT_CMP_LT] = "lt",
152  [NFT_CMP_LTE] = "lte",
153  [NFT_CMP_GT] = "gt",
154  [NFT_CMP_GTE] = "gte",
155 };
156 
157 static inline int nft_str2cmp(const char *op)
158 {
159  if (strcmp(op, "eq") == 0)
160  return NFT_CMP_EQ;
161  else if (strcmp(op, "neq") == 0)
162  return NFT_CMP_NEQ;
163  else if (strcmp(op, "lt") == 0)
164  return NFT_CMP_LT;
165  else if (strcmp(op, "lte") == 0)
166  return NFT_CMP_LTE;
167  else if (strcmp(op, "gt") == 0)
168  return NFT_CMP_GT;
169  else if (strcmp(op, "gte") == 0)
170  return NFT_CMP_GTE;
171  else {
172  errno = EINVAL;
173  return -1;
174  }
175 }
176 
177 static int nft_rule_expr_cmp_json_parse(struct nft_rule_expr *e, json_t *root,
178  struct nft_parse_err *err)
179 {
180 #ifdef JSON_PARSING
181  struct nft_expr_cmp *cmp = nft_expr_data(e);
182  const char *op;
183  uint32_t uval32;
184  int base;
185 
186  if (nft_jansson_parse_val(root, "sreg", NFT_TYPE_U32, &uval32,
187  err) == 0)
188  nft_rule_expr_set_u32(e, NFT_EXPR_CMP_SREG, uval32);
189 
190  op = nft_jansson_parse_str(root, "op", err);
191  if (op != NULL) {
192  base = nft_str2cmp(op);
193  if (base < 0)
194  return -1;
195 
196  nft_rule_expr_set_u32(e, NFT_EXPR_CMP_OP, base);
197  }
198 
199  if (nft_jansson_data_reg_parse(root, "cmpdata",
200  &cmp->data, err) == DATA_VALUE)
201  e->flags |= (1 << NFT_EXPR_CMP_DATA);
202 
203  return 0;
204 #else
205  errno = EOPNOTSUPP;
206  return -1;
207 #endif
208 }
209 
210 static int nft_rule_expr_cmp_xml_parse(struct nft_rule_expr *e, mxml_node_t *tree,
211  struct nft_parse_err *err)
212 {
213 #ifdef XML_PARSING
214  struct nft_expr_cmp *cmp = nft_expr_data(e);
215  const char *op;
216  int32_t op_value;
217  uint32_t sreg;
218 
219  if (nft_mxml_reg_parse(tree, "sreg", &sreg, MXML_DESCEND_FIRST,
220  NFT_XML_MAND, err) == 0)
221  nft_rule_expr_set_u32(e, NFT_EXPR_CMP_SREG, sreg);
222 
223  op = nft_mxml_str_parse(tree, "op", MXML_DESCEND_FIRST, NFT_XML_MAND,
224  err);
225  if (op != NULL) {
226  op_value = nft_str2cmp(op);
227  if (op_value < 0)
228  return -1;
229 
230  nft_rule_expr_set_u32(e, NFT_EXPR_CMP_OP, op_value);
231  }
232 
233  if (nft_mxml_data_reg_parse(tree, "cmpdata",
234  &cmp->data, NFT_XML_MAND,
235  err) == DATA_VALUE)
236  e->flags |= (1 << NFT_EXPR_CMP_DATA);
237 
238  return 0;
239 #else
240  errno = EOPNOTSUPP;
241  return -1;
242 #endif
243 }
244 
245 static int nft_rule_expr_cmp_snprintf_json(char *buf, size_t size,
246  struct nft_rule_expr *e)
247 {
248  struct nft_expr_cmp *cmp = nft_expr_data(e);
249  int len = size, offset = 0, ret;
250 
251  if (e->flags & (1 << NFT_EXPR_CMP_SREG)) {
252  ret = snprintf(buf + offset, len, "\"sreg\":%u,", cmp->sreg);
253  SNPRINTF_BUFFER_SIZE(ret, size, len, offset);
254  }
255  if (e->flags & (1 << NFT_EXPR_CMP_OP)) {
256  ret = snprintf(buf + offset, len, "\"op\":\"%s\",",
257  expr_cmp_str[cmp->op]);
258  SNPRINTF_BUFFER_SIZE(ret, size, len, offset);
259  }
260  ret = nft_data_reg_snprintf(buf + offset, len, &cmp->data,
261  NFT_OUTPUT_JSON, 0, DATA_VALUE);
262  SNPRINTF_BUFFER_SIZE(ret, size, len, offset);
263 
264  return offset;
265 }
266 
267 static int nft_rule_expr_cmp_snprintf_xml(char *buf, size_t size,
268  struct nft_rule_expr *e)
269 {
270  struct nft_expr_cmp *cmp = nft_expr_data(e);
271  int len = size, offset = 0, ret;
272 
273  if (e->flags & (1 << NFT_EXPR_CMP_SREG)) {
274  ret = snprintf(buf, len, "<sreg>%u</sreg>",
275  cmp->sreg);
276  SNPRINTF_BUFFER_SIZE(ret, size, len, offset);
277  }
278 
279  if (e->flags & (1 << NFT_EXPR_CMP_SREG)) {
280  ret = snprintf(buf + offset, len, "<op>%s</op>",
281  expr_cmp_str[cmp->op]);
282  SNPRINTF_BUFFER_SIZE(ret, size, len, offset);
283  }
284 
285  ret = nft_data_reg_snprintf(buf + offset, len, &cmp->data,
286  NFT_OUTPUT_XML, 0, DATA_VALUE);
287  SNPRINTF_BUFFER_SIZE(ret, size, len, offset);
288 
289  return offset;
290 }
291 
292 static int nft_rule_expr_cmp_snprintf_default(char *buf, size_t size,
293  struct nft_rule_expr *e)
294 {
295  struct nft_expr_cmp *cmp = nft_expr_data(e);
296  int len = size, offset = 0, ret;
297 
298  ret = snprintf(buf, len, "%s reg %u ",
299  expr_cmp_str[cmp->op], cmp->sreg);
300  SNPRINTF_BUFFER_SIZE(ret, size, len, offset);
301 
302  ret = nft_data_reg_snprintf(buf+offset, len, &cmp->data,
303  NFT_OUTPUT_DEFAULT, 0, DATA_VALUE);
304  SNPRINTF_BUFFER_SIZE(ret, size, len, offset);
305 
306  return offset;
307 }
308 
309 static int
310 nft_rule_expr_cmp_snprintf(char *buf, size_t size, uint32_t type,
311  uint32_t flags, struct nft_rule_expr *e)
312 {
313  switch(type) {
314  case NFT_OUTPUT_DEFAULT:
315  return nft_rule_expr_cmp_snprintf_default(buf, size, e);
316  case NFT_OUTPUT_XML:
317  return nft_rule_expr_cmp_snprintf_xml(buf, size, e);
318  case NFT_OUTPUT_JSON:
319  return nft_rule_expr_cmp_snprintf_json(buf, size, e);
320  default:
321  break;
322  }
323  return -1;
324 }
325 
326 struct expr_ops expr_ops_cmp = {
327  .name = "cmp",
328  .alloc_len = sizeof(struct nft_expr_cmp),
329  .max_attr = NFTA_CMP_MAX,
330  .set = nft_rule_expr_cmp_set,
331  .get = nft_rule_expr_cmp_get,
332  .parse = nft_rule_expr_cmp_parse,
333  .build = nft_rule_expr_cmp_build,
334  .snprintf = nft_rule_expr_cmp_snprintf,
335  .xml_parse = nft_rule_expr_cmp_xml_parse,
336  .json_parse = nft_rule_expr_cmp_json_parse,
337 };
338 static void __init expr_cmp_init(void)
339 {
340  nft_expr_ops_register(&expr_ops_cmp);
341 }