libnftnl  1.0.8
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_EVENTMASK + 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, const 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 static 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  [NFT_CT_PKTS] = "packets",
171  [NFT_CT_BYTES] = "bytes",
172  [NFT_CT_AVGPKT] = "avgpkt",
173  [NFT_CT_ZONE] = "zone",
174  [NFT_CT_EVENTMASK] = "event",
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 "unknown";
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 nftnl_expr_ct_json_parse(struct nftnl_expr *e, json_t *root,
225  struct nftnl_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 (nftnl_jansson_parse_reg(root, "dreg", NFTNL_TYPE_U32, &reg, err) == 0)
234  nftnl_expr_set_u32(e, NFTNL_EXPR_CT_DREG, reg);
235 
236  if (nftnl_jansson_parse_reg(root, "sreg", NFTNL_TYPE_U32, &reg, err) == 0)
237  nftnl_expr_set_u32(e, NFTNL_EXPR_CT_SREG, reg);
238 
239  key_str = nftnl_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  nftnl_expr_set_u32(e, NFTNL_EXPR_CT_KEY, key);
246  }
247 
248  dir_str = nftnl_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 = NFTNL_PARSE_EBADTYPE;
253  goto err;
254  }
255  nftnl_expr_set_u8(e, NFTNL_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
270 nftnl_expr_ct_export(char *buf, size_t size, const struct nftnl_expr *e,
271  int type)
272 {
273  struct nftnl_expr_ct *ct = nftnl_expr_data(e);
274  NFTNL_BUF_INIT(b, buf, size);
275 
276  if (e->flags & (1 << NFTNL_EXPR_CT_SREG))
277  nftnl_buf_u32(&b, type, ct->sreg, SREG);
278  if (e->flags & (1 << NFTNL_EXPR_CT_DREG))
279  nftnl_buf_u32(&b, type, ct->dreg, DREG);
280  if (e->flags & (1 << NFTNL_EXPR_CT_KEY))
281  nftnl_buf_str(&b, type, ctkey2str(ct->key), KEY);
282  if (e->flags & (1 << NFTNL_EXPR_CT_DIR))
283  nftnl_buf_str(&b, type, ctdir2str(ct->dir), DIR);
284 
285  return nftnl_buf_done(&b);
286 }
287 
288 static int
289 nftnl_expr_ct_snprintf_default(char *buf, size_t size,
290  const struct nftnl_expr *e)
291 {
292  int ret, remain = size, offset = 0;
293  struct nftnl_expr_ct *ct = nftnl_expr_data(e);
294 
295  if (e->flags & (1 << NFTNL_EXPR_CT_SREG)) {
296  ret = snprintf(buf, size, "set %s with reg %u ",
297  ctkey2str(ct->key), ct->sreg);
298  SNPRINTF_BUFFER_SIZE(ret, remain, offset);
299  }
300 
301  if (e->flags & (1 << NFTNL_EXPR_CT_DREG)) {
302  ret = snprintf(buf, remain, "load %s => reg %u ",
303  ctkey2str(ct->key), ct->dreg);
304  SNPRINTF_BUFFER_SIZE(ret, remain, offset);
305  }
306 
307  if (nftnl_expr_is_set(e, NFTNL_EXPR_CT_DIR)) {
308  ret = snprintf(buf + offset, remain, ", dir %s ",
309  ctdir2str(ct->dir));
310  SNPRINTF_BUFFER_SIZE(ret, remain, offset);
311  }
312 
313  return offset;
314 }
315 
316 static int
317 nftnl_expr_ct_snprintf(char *buf, size_t len, uint32_t type,
318  uint32_t flags, const struct nftnl_expr *e)
319 {
320  switch (type) {
321  case NFTNL_OUTPUT_DEFAULT:
322  return nftnl_expr_ct_snprintf_default(buf, len, e);
323  case NFTNL_OUTPUT_XML:
324  case NFTNL_OUTPUT_JSON:
325  return nftnl_expr_ct_export(buf, len, e, type);
326  default:
327  break;
328  }
329  return -1;
330 }
331 
332 static bool nftnl_expr_ct_cmp(const struct nftnl_expr *e1,
333  const struct nftnl_expr *e2)
334 {
335  struct nftnl_expr_ct *c1 = nftnl_expr_data(e1);
336  struct nftnl_expr_ct *c2 = nftnl_expr_data(e2);
337  bool eq = true;
338 
339  if (e1->flags & (1 << NFTNL_EXPR_CT_KEY))
340  eq &= (c1->key == c2->key);
341  if (e1->flags & (1 << NFTNL_EXPR_CT_DREG))
342  eq &= (c1->dreg == c2->dreg);
343  if (e1->flags & (1 << NFTNL_EXPR_CT_SREG))
344  eq &= (c1->sreg == c2->sreg);
345  if (e1->flags & (1 << NFTNL_EXPR_CT_DIR))
346  eq &= (c1->dir == c2->dir);
347 
348  return eq;
349 }
350 
351 struct expr_ops expr_ops_ct = {
352  .name = "ct",
353  .alloc_len = sizeof(struct nftnl_expr_ct),
354  .max_attr = NFTA_CT_MAX,
355  .cmp = nftnl_expr_ct_cmp,
356  .set = nftnl_expr_ct_set,
357  .get = nftnl_expr_ct_get,
358  .parse = nftnl_expr_ct_parse,
359  .build = nftnl_expr_ct_build,
360  .snprintf = nftnl_expr_ct_snprintf,
361  .json_parse = nftnl_expr_ct_json_parse,
362 };