libnftnl  1.0.5
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 
24 struct nftnl_expr_ct {
25  enum nft_ct_keys key;
26  enum nft_registers dreg;
27  enum nft_registers sreg;
28  uint8_t dir;
29 };
30 
31 #define IP_CT_DIR_ORIGINAL 0
32 #define IP_CT_DIR_REPLY 1
33 
34 #ifndef NFT_CT_MAX
35 #define NFT_CT_MAX (NFT_CT_LABELS + 1)
36 #endif
37 
38 static int
39 nftnl_expr_ct_set(struct nftnl_expr *e, uint16_t type,
40  const void *data, uint32_t data_len)
41 {
42  struct nftnl_expr_ct *ct = nftnl_expr_data(e);
43 
44  switch(type) {
45  case NFTNL_EXPR_CT_KEY:
46  ct->key = *((uint32_t *)data);
47  break;
48  case NFTNL_EXPR_CT_DIR:
49  ct->dir = *((uint8_t *)data);
50  break;
51  case NFTNL_EXPR_CT_DREG:
52  ct->dreg = *((uint32_t *)data);
53  break;
54  case NFTNL_EXPR_CT_SREG:
55  ct->sreg = *((uint32_t *)data);
56  break;
57  default:
58  return -1;
59  }
60  return 0;
61 }
62 
63 static const void *
64 nftnl_expr_ct_get(const struct nftnl_expr *e, uint16_t type,
65  uint32_t *data_len)
66 {
67  struct nftnl_expr_ct *ct = nftnl_expr_data(e);
68 
69  switch(type) {
70  case NFTNL_EXPR_CT_KEY:
71  *data_len = sizeof(ct->key);
72  return &ct->key;
73  case NFTNL_EXPR_CT_DIR:
74  *data_len = sizeof(ct->dir);
75  return &ct->dir;
76  case NFTNL_EXPR_CT_DREG:
77  *data_len = sizeof(ct->dreg);
78  return &ct->dreg;
79  case NFTNL_EXPR_CT_SREG:
80  *data_len = sizeof(ct->sreg);
81  return &ct->sreg;
82  }
83  return NULL;
84 }
85 
86 static int nftnl_expr_ct_cb(const struct nlattr *attr, void *data)
87 {
88  const struct nlattr **tb = data;
89  int type = mnl_attr_get_type(attr);
90 
91  if (mnl_attr_type_valid(attr, NFTA_CT_MAX) < 0)
92  return MNL_CB_OK;
93 
94  switch(type) {
95  case NFTA_CT_KEY:
96  case NFTA_CT_DREG:
97  case NFTA_CT_SREG:
98  if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0)
99  abi_breakage();
100  break;
101  case NFTA_CT_DIRECTION:
102  if (mnl_attr_validate(attr, MNL_TYPE_U8) < 0)
103  abi_breakage();
104  break;
105  }
106 
107  tb[type] = attr;
108  return MNL_CB_OK;
109 }
110 
111 static void
112 nftnl_expr_ct_build(struct nlmsghdr *nlh, struct nftnl_expr *e)
113 {
114  struct nftnl_expr_ct *ct = nftnl_expr_data(e);
115 
116  if (e->flags & (1 << NFTNL_EXPR_CT_KEY))
117  mnl_attr_put_u32(nlh, NFTA_CT_KEY, htonl(ct->key));
118  if (e->flags & (1 << NFTNL_EXPR_CT_DREG))
119  mnl_attr_put_u32(nlh, NFTA_CT_DREG, htonl(ct->dreg));
120  if (e->flags & (1 << NFTNL_EXPR_CT_DIR))
121  mnl_attr_put_u8(nlh, NFTA_CT_DIRECTION, ct->dir);
122  if (e->flags & (1 << NFTNL_EXPR_CT_SREG))
123  mnl_attr_put_u32(nlh, NFTA_CT_SREG, htonl(ct->sreg));
124 }
125 
126 static int
127 nftnl_expr_ct_parse(struct nftnl_expr *e, struct nlattr *attr)
128 {
129  struct nftnl_expr_ct *ct = nftnl_expr_data(e);
130  struct nlattr *tb[NFTA_CT_MAX+1] = {};
131 
132  if (mnl_attr_parse_nested(attr, nftnl_expr_ct_cb, tb) < 0)
133  return -1;
134 
135  if (tb[NFTA_CT_KEY]) {
136  ct->key = ntohl(mnl_attr_get_u32(tb[NFTA_CT_KEY]));
137  e->flags |= (1 << NFTNL_EXPR_CT_KEY);
138  }
139  if (tb[NFTA_CT_DREG]) {
140  ct->dreg = ntohl(mnl_attr_get_u32(tb[NFTA_CT_DREG]));
141  e->flags |= (1 << NFTNL_EXPR_CT_DREG);
142  }
143  if (tb[NFTA_CT_SREG]) {
144  ct->sreg = ntohl(mnl_attr_get_u32(tb[NFTA_CT_SREG]));
145  e->flags |= (1 << NFTNL_EXPR_CT_SREG);
146  }
147  if (tb[NFTA_CT_DIRECTION]) {
148  ct->dir = mnl_attr_get_u8(tb[NFTA_CT_DIRECTION]);
149  e->flags |= (1 << NFTNL_EXPR_CT_DIR);
150  }
151 
152  return 0;
153 }
154 
155 const char *ctkey2str_array[NFT_CT_MAX] = {
156  [NFT_CT_STATE] = "state",
157  [NFT_CT_DIRECTION] = "direction",
158  [NFT_CT_STATUS] = "status",
159  [NFT_CT_MARK] = "mark",
160  [NFT_CT_SECMARK] = "secmark",
161  [NFT_CT_EXPIRATION] = "expiration",
162  [NFT_CT_HELPER] = "helper",
163  [NFT_CT_L3PROTOCOL] = "l3protocol",
164  [NFT_CT_PROTOCOL] = "protocol",
165  [NFT_CT_SRC] = "src",
166  [NFT_CT_DST] = "dst",
167  [NFT_CT_PROTO_SRC] = "proto_src",
168  [NFT_CT_PROTO_DST] = "proto_dst",
169  [NFT_CT_LABELS] = "label",
170 };
171 
172 static const char *ctkey2str(uint32_t ctkey)
173 {
174  if (ctkey > NFT_CT_MAX)
175  return "unknown";
176 
177  return ctkey2str_array[ctkey];
178 }
179 
180 static inline int str2ctkey(const char *ctkey)
181 {
182  int i;
183 
184  for (i = 0; i < NFT_CT_MAX; i++) {
185  if (strcmp(ctkey2str_array[i], ctkey) == 0)
186  return i;
187  }
188 
189  return -1;
190 }
191 
192 static const char *ctdir2str(uint8_t ctdir)
193 {
194  switch (ctdir) {
195  case IP_CT_DIR_ORIGINAL:
196  return "original";
197  case IP_CT_DIR_REPLY:
198  return "reply";
199  default:
200  return "unknow";
201  }
202 }
203 
204 static inline int str2ctdir(const char *str, uint8_t *ctdir)
205 {
206  if (strcmp(str, "original") == 0) {
207  *ctdir = IP_CT_DIR_ORIGINAL;
208  return 0;
209  }
210 
211  if (strcmp(str, "reply") == 0) {
212  *ctdir = IP_CT_DIR_REPLY;
213  return 0;
214  }
215 
216  return -1;
217 }
218 
219 static int nftnl_expr_ct_json_parse(struct nftnl_expr *e, json_t *root,
220  struct nftnl_parse_err *err)
221 {
222 #ifdef JSON_PARSING
223  const char *key_str, *dir_str;
224  uint32_t reg;
225  uint8_t dir;
226  int key;
227 
228  if (nftnl_jansson_parse_reg(root, "dreg", NFTNL_TYPE_U32, &reg, err) == 0)
229  nftnl_expr_set_u32(e, NFTNL_EXPR_CT_DREG, reg);
230 
231  if (nftnl_jansson_parse_reg(root, "sreg", NFTNL_TYPE_U32, &reg, err) == 0)
232  nftnl_expr_set_u32(e, NFTNL_EXPR_CT_SREG, reg);
233 
234  key_str = nftnl_jansson_parse_str(root, "key", err);
235  if (key_str != NULL) {
236  key = str2ctkey(key_str);
237  if (key < 0)
238  return -1;
239 
240  nftnl_expr_set_u32(e, NFTNL_EXPR_CT_KEY, key);
241  }
242 
243  dir_str = nftnl_jansson_parse_str(root, "dir", err);
244  if (dir_str != NULL) {
245  if (str2ctdir(dir_str, &dir) != 0) {
246  err->node_name = "dir";
247  err->error = NFTNL_PARSE_EBADTYPE;
248  goto err;
249  }
250  nftnl_expr_set_u8(e, NFTNL_EXPR_CT_DIR, dir);
251  }
252 
253  return 0;
254 err:
255  errno = EINVAL;
256  return -1;
257 #else
258  errno = EOPNOTSUPP;
259  return -1;
260 #endif
261 }
262 
263 
264 static int nftnl_expr_ct_xml_parse(struct nftnl_expr *e, mxml_node_t *tree,
265  struct nftnl_parse_err *err)
266 {
267 #ifdef XML_PARSING
268  const char *key_str, *dir_str;
269  int key;
270  uint8_t dir;
271  uint32_t dreg, sreg;
272 
273  if (nftnl_mxml_reg_parse(tree, "dreg", &dreg, MXML_DESCEND_FIRST,
274  NFTNL_XML_OPT, err) == 0)
275  nftnl_expr_set_u32(e, NFTNL_EXPR_CT_DREG, dreg);
276 
277  if (nftnl_mxml_reg_parse(tree, "sreg", &sreg, MXML_DESCEND_FIRST,
278  NFTNL_XML_OPT, err) == 0)
279  nftnl_expr_set_u32(e, NFTNL_EXPR_CT_SREG, sreg);
280 
281  key_str = nftnl_mxml_str_parse(tree, "key", MXML_DESCEND_FIRST,
282  NFTNL_XML_MAND, err);
283  if (key_str != NULL) {
284  key = str2ctkey(key_str);
285  if (key < 0)
286  return -1;
287 
288  nftnl_expr_set_u32(e, NFTNL_EXPR_CT_KEY, key);
289  }
290  dir_str = nftnl_mxml_str_parse(tree, "dir", MXML_DESCEND_FIRST,
291  NFTNL_XML_OPT, err);
292  if (dir_str != NULL) {
293  if (str2ctdir(dir_str, &dir) != 0) {
294  err->node_name = "dir";
295  err->error = NFTNL_PARSE_EBADTYPE;
296  goto err;
297  }
298  nftnl_expr_set_u8(e, NFTNL_EXPR_CT_DIR, dir);
299  }
300 
301  return 0;
302 err:
303  errno = EINVAL;
304  return -1;
305 #else
306  errno = EOPNOTSUPP;
307  return -1;
308 #endif
309 }
310 
311 static int
312 nftnl_expr_ct_export(char *buf, size_t size, struct nftnl_expr *e, int type)
313 {
314  struct nftnl_expr_ct *ct = nftnl_expr_data(e);
315  NFTNL_BUF_INIT(b, buf, size);
316 
317  if (e->flags & (1 << NFTNL_EXPR_CT_SREG))
318  nftnl_buf_u32(&b, type, ct->sreg, SREG);
319  if (e->flags & (1 << NFTNL_EXPR_CT_DREG))
320  nftnl_buf_u32(&b, type, ct->dreg, DREG);
321  if (e->flags & (1 << NFTNL_EXPR_CT_KEY))
322  nftnl_buf_str(&b, type, ctkey2str(ct->key), KEY);
323  if (e->flags & (1 << NFTNL_EXPR_CT_DIR))
324  nftnl_buf_str(&b, type, ctdir2str(ct->dir), DIR);
325 
326  return nftnl_buf_done(&b);
327 }
328 
329 static int
330 nftnl_expr_ct_snprintf_default(char *buf, size_t size, struct nftnl_expr *e)
331 {
332  int ret, len = size, offset = 0;
333  struct nftnl_expr_ct *ct = nftnl_expr_data(e);
334 
335  if (e->flags & (1 << NFTNL_EXPR_CT_SREG)) {
336  ret = snprintf(buf, size, "set %s with reg %u ",
337  ctkey2str(ct->key), ct->sreg);
338  SNPRINTF_BUFFER_SIZE(ret, size, len, offset);
339  }
340 
341  if (e->flags & (1 << NFTNL_EXPR_CT_DREG)) {
342  ret = snprintf(buf, len, "load %s => reg %u ",
343  ctkey2str(ct->key), ct->dreg);
344  SNPRINTF_BUFFER_SIZE(ret, size, len, offset);
345  }
346 
347  if (nftnl_expr_is_set(e, NFTNL_EXPR_CT_DIR)) {
348  ret = snprintf(buf+offset, len, ", dir %s ",
349  ctdir2str(ct->dir));
350  SNPRINTF_BUFFER_SIZE(ret, size, len, offset);
351  }
352 
353  return offset;
354 }
355 
356 static int
357 nftnl_expr_ct_snprintf(char *buf, size_t len, uint32_t type,
358  uint32_t flags, struct nftnl_expr *e)
359 {
360  switch (type) {
361  case NFTNL_OUTPUT_DEFAULT:
362  return nftnl_expr_ct_snprintf_default(buf, len, e);
363  case NFTNL_OUTPUT_XML:
364  case NFTNL_OUTPUT_JSON:
365  return nftnl_expr_ct_export(buf, len, e, type);
366  default:
367  break;
368  }
369  return -1;
370 }
371 
372 struct expr_ops expr_ops_ct = {
373  .name = "ct",
374  .alloc_len = sizeof(struct nftnl_expr_ct),
375  .max_attr = NFTA_CT_MAX,
376  .set = nftnl_expr_ct_set,
377  .get = nftnl_expr_ct_get,
378  .parse = nftnl_expr_ct_parse,
379  .build = nftnl_expr_ct_build,
380  .snprintf = nftnl_expr_ct_snprintf,
381  .xml_parse = nftnl_expr_ct_xml_parse,
382  .json_parse = nftnl_expr_ct_json_parse,
383 };