libnftnl  1.0.2
ct.c
1 /*
2  * (C) 2012-2013 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 <string.h>
14 #include <stdint.h>
15 #include <arpa/inet.h>
16 #include <errno.h>
17 #include <linux/netfilter/nf_tables.h>
18 
19 #include "internal.h"
20 #include <libmnl/libmnl.h>
21 #include <libnftnl/expr.h>
22 #include <libnftnl/rule.h>
23 #include "expr_ops.h"
24 
25 struct nft_expr_ct {
26  enum nft_ct_keys key;
27  enum nft_registers dreg;
28  enum nft_registers sreg;
29  uint8_t dir;
30 };
31 
32 #define IP_CT_DIR_ORIGINAL 0
33 #define IP_CT_DIR_REPLY 1
34 
35 #ifndef NFT_CT_MAX
36 #define NFT_CT_MAX (NFT_CT_LABELS + 1)
37 #endif
38 
39 static int
40 nft_rule_expr_ct_set(struct nft_rule_expr *e, uint16_t type,
41  const void *data, uint32_t data_len)
42 {
43  struct nft_expr_ct *ct = nft_expr_data(e);
44 
45  switch(type) {
46  case NFT_EXPR_CT_KEY:
47  ct->key = *((uint32_t *)data);
48  break;
49  case NFT_EXPR_CT_DIR:
50  ct->dir = *((uint8_t *)data);
51  break;
52  case NFT_EXPR_CT_DREG:
53  ct->dreg = *((uint32_t *)data);
54  break;
55  case NFT_EXPR_CT_SREG:
56  ct->sreg = *((uint32_t *)data);
57  break;
58  default:
59  return -1;
60  }
61  return 0;
62 }
63 
64 static const void *
65 nft_rule_expr_ct_get(const struct nft_rule_expr *e, uint16_t type,
66  uint32_t *data_len)
67 {
68  struct nft_expr_ct *ct = nft_expr_data(e);
69 
70  switch(type) {
71  case NFT_EXPR_CT_KEY:
72  *data_len = sizeof(ct->key);
73  return &ct->key;
74  case NFT_EXPR_CT_DIR:
75  *data_len = sizeof(ct->dir);
76  return &ct->dir;
77  case NFT_EXPR_CT_DREG:
78  *data_len = sizeof(ct->dreg);
79  return &ct->dreg;
80  case NFT_EXPR_CT_SREG:
81  *data_len = sizeof(ct->sreg);
82  return &ct->sreg;
83  }
84  return NULL;
85 }
86 
87 static int nft_rule_expr_ct_cb(const struct nlattr *attr, void *data)
88 {
89  const struct nlattr **tb = data;
90  int type = mnl_attr_get_type(attr);
91 
92  if (mnl_attr_type_valid(attr, NFTA_CT_MAX) < 0)
93  return MNL_CB_OK;
94 
95  switch(type) {
96  case NFTA_CT_KEY:
97  case NFTA_CT_DREG:
98  case NFTA_CT_SREG:
99  if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0) {
100  perror("mnl_attr_validate");
101  return MNL_CB_ERROR;
102  }
103  break;
104  case NFTA_CT_DIRECTION:
105  if (mnl_attr_validate(attr, MNL_TYPE_U8) < 0) {
106  perror("mnl_attr_validate");
107  return MNL_CB_ERROR;
108  }
109  break;
110  }
111 
112  tb[type] = attr;
113  return MNL_CB_OK;
114 }
115 
116 static void
117 nft_rule_expr_ct_build(struct nlmsghdr *nlh, struct nft_rule_expr *e)
118 {
119  struct nft_expr_ct *ct = nft_expr_data(e);
120 
121  if (e->flags & (1 << NFT_EXPR_CT_KEY))
122  mnl_attr_put_u32(nlh, NFTA_CT_KEY, htonl(ct->key));
123  if (e->flags & (1 << NFT_EXPR_CT_DREG))
124  mnl_attr_put_u32(nlh, NFTA_CT_DREG, htonl(ct->dreg));
125  if (e->flags & (1 << NFT_EXPR_CT_DIR))
126  mnl_attr_put_u8(nlh, NFTA_CT_DIRECTION, ct->dir);
127  if (e->flags & (1 << NFT_EXPR_CT_SREG))
128  mnl_attr_put_u32(nlh, NFTA_CT_SREG, htonl(ct->sreg));
129 }
130 
131 static int
132 nft_rule_expr_ct_parse(struct nft_rule_expr *e, struct nlattr *attr)
133 {
134  struct nft_expr_ct *ct = nft_expr_data(e);
135  struct nlattr *tb[NFTA_CT_MAX+1] = {};
136 
137  if (mnl_attr_parse_nested(attr, nft_rule_expr_ct_cb, tb) < 0)
138  return -1;
139 
140  if (tb[NFTA_CT_KEY]) {
141  ct->key = ntohl(mnl_attr_get_u32(tb[NFTA_CT_KEY]));
142  e->flags |= (1 << NFT_EXPR_CT_KEY);
143  }
144  if (tb[NFTA_CT_DREG]) {
145  ct->dreg = ntohl(mnl_attr_get_u32(tb[NFTA_CT_DREG]));
146  e->flags |= (1 << NFT_EXPR_CT_DREG);
147  }
148  if (tb[NFTA_CT_SREG]) {
149  ct->sreg = ntohl(mnl_attr_get_u32(tb[NFTA_CT_SREG]));
150  e->flags |= (1 << NFT_EXPR_CT_SREG);
151  }
152  if (tb[NFTA_CT_DIRECTION]) {
153  ct->dir = mnl_attr_get_u8(tb[NFTA_CT_DIRECTION]);
154  e->flags |= (1 << NFT_EXPR_CT_DIR);
155  }
156 
157  return 0;
158 }
159 
160 const char *ctkey2str_array[NFT_CT_MAX] = {
161  [NFT_CT_STATE] = "state",
162  [NFT_CT_DIRECTION] = "direction",
163  [NFT_CT_STATUS] = "status",
164  [NFT_CT_MARK] = "mark",
165  [NFT_CT_SECMARK] = "secmark",
166  [NFT_CT_EXPIRATION] = "expiration",
167  [NFT_CT_HELPER] = "helper",
168  [NFT_CT_L3PROTOCOL] = "l3protocol",
169  [NFT_CT_PROTOCOL] = "protocol",
170  [NFT_CT_SRC] = "src",
171  [NFT_CT_DST] = "dst",
172  [NFT_CT_PROTO_SRC] = "proto_src",
173  [NFT_CT_PROTO_DST] = "proto_dst",
174  [NFT_CT_LABELS] = "label",
175 };
176 
177 static const char *ctkey2str(uint32_t ctkey)
178 {
179  if (ctkey > NFT_CT_MAX)
180  return "unknown";
181 
182  return ctkey2str_array[ctkey];
183 }
184 
185 static inline int str2ctkey(const char *ctkey)
186 {
187  int i;
188 
189  for (i = 0; i < NFT_CT_MAX; i++) {
190  if (strcmp(ctkey2str_array[i], ctkey) == 0)
191  return i;
192  }
193 
194  return -1;
195 }
196 
197 static const char *ctdir2str(uint8_t ctdir)
198 {
199  switch (ctdir) {
200  case IP_CT_DIR_ORIGINAL:
201  return "original";
202  case IP_CT_DIR_REPLY:
203  return "reply";
204  default:
205  return "unknow";
206  }
207 }
208 
209 static inline int str2ctdir(const char *str, uint8_t *ctdir)
210 {
211  if (strcmp(str, "original") == 0) {
212  *ctdir = IP_CT_DIR_ORIGINAL;
213  return 0;
214  }
215 
216  if (strcmp(str, "reply") == 0) {
217  *ctdir = IP_CT_DIR_REPLY;
218  return 0;
219  }
220 
221  return -1;
222 }
223 
224 static int nft_rule_expr_ct_json_parse(struct nft_rule_expr *e, json_t *root,
225  struct nft_parse_err *err)
226 {
227 #ifdef JSON_PARSING
228  const char *key_str, *dir_str;
229  uint32_t reg;
230  uint8_t dir;
231  int key;
232 
233  if (nft_jansson_parse_reg(root, "dreg", NFT_TYPE_U32, &reg, err) == 0)
234  nft_rule_expr_set_u32(e, NFT_EXPR_CT_DREG, reg);
235 
236  if (nft_jansson_parse_reg(root, "sreg", NFT_TYPE_U32, &reg, err) == 0)
237  nft_rule_expr_set_u32(e, NFT_EXPR_CT_SREG, reg);
238 
239  key_str = nft_jansson_parse_str(root, "key", err);
240  if (key_str != NULL) {
241  key = str2ctkey(key_str);
242  if (key < 0)
243  return -1;
244 
245  nft_rule_expr_set_u32(e, NFT_EXPR_CT_KEY, key);
246  }
247 
248  dir_str = nft_jansson_parse_str(root, "dir", err);
249  if (dir_str != NULL) {
250  if (str2ctdir(dir_str, &dir) != 0) {
251  err->node_name = "dir";
252  err->error = NFT_PARSE_EBADTYPE;
253  goto err;
254  }
255  nft_rule_expr_set_u8(e, NFT_EXPR_CT_DIR, dir);
256  }
257 
258  return 0;
259 err:
260  errno = EINVAL;
261  return -1;
262 #else
263  errno = EOPNOTSUPP;
264  return -1;
265 #endif
266 }
267 
268 
269 static int nft_rule_expr_ct_xml_parse(struct nft_rule_expr *e, mxml_node_t *tree,
270  struct nft_parse_err *err)
271 {
272 #ifdef XML_PARSING
273  const char *key_str, *dir_str;
274  int key;
275  uint8_t dir;
276  uint32_t dreg, sreg;
277 
278  if (nft_mxml_reg_parse(tree, "dreg", &dreg, MXML_DESCEND_FIRST,
279  NFT_XML_OPT, err) == 0)
280  nft_rule_expr_set_u32(e, NFT_EXPR_CT_DREG, dreg);
281 
282  if (nft_mxml_reg_parse(tree, "sreg", &sreg, MXML_DESCEND_FIRST,
283  NFT_XML_OPT, err) == 0)
284  nft_rule_expr_set_u32(e, NFT_EXPR_CT_SREG, sreg);
285 
286  key_str = nft_mxml_str_parse(tree, "key", MXML_DESCEND_FIRST,
287  NFT_XML_MAND, err);
288  if (key_str != NULL) {
289  key = str2ctkey(key_str);
290  if (key < 0)
291  return -1;
292 
293  nft_rule_expr_set_u32(e, NFT_EXPR_CT_KEY, key);
294  }
295  dir_str = nft_mxml_str_parse(tree, "dir", MXML_DESCEND_FIRST,
296  NFT_XML_OPT, err);
297  if (dir_str != NULL) {
298  if (str2ctdir(dir_str, &dir) != 0) {
299  err->node_name = "dir";
300  err->error = NFT_PARSE_EBADTYPE;
301  goto err;
302  }
303  nft_rule_expr_set_u8(e, NFT_EXPR_CT_DIR, dir);
304  }
305 
306  return 0;
307 err:
308  errno = EINVAL;
309  return -1;
310 #else
311  errno = EOPNOTSUPP;
312  return -1;
313 #endif
314 }
315 
316 static int
317 nft_expr_ct_snprintf_json(char *buf, size_t size, struct nft_rule_expr *e)
318 {
319  int ret, len = size, offset = 0;
320  struct nft_expr_ct *ct = nft_expr_data(e);
321 
322  if (e->flags & (1 << NFT_EXPR_CT_DREG)) {
323  ret = snprintf(buf+offset, len, "\"dreg\":%u,", ct->dreg);
324  SNPRINTF_BUFFER_SIZE(ret, size, len, offset);
325  }
326 
327  if (e->flags & (1 << NFT_EXPR_CT_SREG)) {
328  ret = snprintf(buf+offset, len, "\"sreg:\":%u,", ct->sreg);
329  SNPRINTF_BUFFER_SIZE(ret, size, len, offset);
330  }
331 
332  if (e->flags & (1 << NFT_EXPR_CT_KEY)) {
333  ret = snprintf(buf+offset, len, "\"key\":\"%s\",",
334  ctkey2str(ct->key));
335  SNPRINTF_BUFFER_SIZE(ret, size, len, offset);
336  }
337 
338  if (nft_rule_expr_is_set(e, NFT_EXPR_CT_DIR)) {
339  ret = snprintf(buf+offset, len, "\"dir\":\"%s\",",
340  ctdir2str(ct->dir));
341  SNPRINTF_BUFFER_SIZE(ret, size, len, offset);
342  }
343 
344  /* Remove the last separator characther */
345  if (offset > 0)
346  offset--;
347 
348  return offset;
349 }
350 
351 static int
352 nft_expr_ct_snprintf_xml(char *buf, size_t size, struct nft_rule_expr *e)
353 {
354  int ret, len = size, offset = 0;
355  struct nft_expr_ct *ct = nft_expr_data(e);
356 
357  if (e->flags & (1 << NFT_EXPR_CT_DREG)) {
358  ret = snprintf(buf + offset, len, "<dreg>%u</dreg>", ct->dreg);
359  SNPRINTF_BUFFER_SIZE(ret, size, len, offset);
360  }
361  if (e->flags & (1 << NFT_EXPR_CT_SREG)) {
362  ret = snprintf(buf + offset, len, "<sreg>%u</sreg>", ct->sreg);
363  SNPRINTF_BUFFER_SIZE(ret, size, len, offset);
364  }
365  if (e->flags & (1 << NFT_EXPR_CT_KEY)) {
366  ret = snprintf(buf + offset, len, "<key>%s</key>",
367  ctkey2str(ct->key));
368  SNPRINTF_BUFFER_SIZE(ret, size, len, offset);
369  }
370  if (nft_rule_expr_is_set(e, NFT_EXPR_CT_DIR)) {
371  ret = snprintf(buf + offset, len, "<dir>%s</dir>",
372  ctdir2str(ct->dir));
373  SNPRINTF_BUFFER_SIZE(ret, size, len, offset);
374  }
375 
376  return offset;
377 }
378 
379 static int
380 nft_expr_ct_snprintf_default(char *buf, size_t size, struct nft_rule_expr *e)
381 {
382  int ret, len = size, offset = 0;
383  struct nft_expr_ct *ct = nft_expr_data(e);
384 
385  if (e->flags & (1 << NFT_EXPR_CT_SREG)) {
386  ret = snprintf(buf, size, "set %s with reg %u ",
387  ctkey2str(ct->key), ct->sreg);
388  SNPRINTF_BUFFER_SIZE(ret, size, len, offset);
389  }
390 
391  if (e->flags & (1 << NFT_EXPR_CT_DREG)) {
392  ret = snprintf(buf, len, "load %s => reg %u ",
393  ctkey2str(ct->key), ct->dreg);
394  SNPRINTF_BUFFER_SIZE(ret, size, len, offset);
395  }
396 
397  if (nft_rule_expr_is_set(e, NFT_EXPR_CT_DIR)) {
398  ret = snprintf(buf+offset, len, ", dir %s ",
399  ctdir2str(ct->dir));
400  SNPRINTF_BUFFER_SIZE(ret, size, len, offset);
401  }
402 
403  return offset;
404 }
405 
406 static int
407 nft_rule_expr_ct_snprintf(char *buf, size_t len, uint32_t type,
408  uint32_t flags, struct nft_rule_expr *e)
409 {
410  switch(type) {
411  case NFT_OUTPUT_DEFAULT:
412  return nft_expr_ct_snprintf_default(buf, len, e);
413  case NFT_OUTPUT_XML:
414  return nft_expr_ct_snprintf_xml(buf, len, e);
415  case NFT_OUTPUT_JSON:
416  return nft_expr_ct_snprintf_json(buf, len, e);
417  default:
418  break;
419  }
420  return -1;
421 }
422 
423 struct expr_ops expr_ops_ct = {
424  .name = "ct",
425  .alloc_len = sizeof(struct nft_expr_ct),
426  .max_attr = NFTA_CT_MAX,
427  .set = nft_rule_expr_ct_set,
428  .get = nft_rule_expr_ct_get,
429  .parse = nft_rule_expr_ct_parse,
430  .build = nft_rule_expr_ct_build,
431  .snprintf = nft_rule_expr_ct_snprintf,
432  .xml_parse = nft_rule_expr_ct_xml_parse,
433  .json_parse = nft_rule_expr_ct_json_parse,
434 };
435 
436 static void __init expr_ct_init(void)
437 {
438  nft_expr_ops_register(&expr_ops_ct);
439 }
Definition: ct.c:25