libnftnl  1.0.2
meta.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 <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 #ifndef NFT_META_MAX
26 #define NFT_META_MAX (NFT_META_BRI_OIFNAME + 1)
27 #endif
28 
29 struct nft_expr_meta {
30  enum nft_meta_keys key;
31  enum nft_registers dreg;
32  enum nft_registers sreg;
33 };
34 
35 static int
36 nft_rule_expr_meta_set(struct nft_rule_expr *e, uint16_t type,
37  const void *data, uint32_t data_len)
38 {
39  struct nft_expr_meta *meta = nft_expr_data(e);
40 
41  switch(type) {
42  case NFT_EXPR_META_KEY:
43  meta->key = *((uint32_t *)data);
44  break;
45  case NFT_EXPR_META_DREG:
46  meta->dreg = *((uint32_t *)data);
47  break;
48  case NFT_EXPR_META_SREG:
49  meta->sreg = *((uint32_t *)data);
50  break;
51  default:
52  return -1;
53  }
54  return 0;
55 }
56 
57 static const void *
58 nft_rule_expr_meta_get(const struct nft_rule_expr *e, uint16_t type,
59  uint32_t *data_len)
60 {
61  struct nft_expr_meta *meta = nft_expr_data(e);
62 
63  switch(type) {
64  case NFT_EXPR_META_KEY:
65  *data_len = sizeof(meta->key);
66  return &meta->key;
67  case NFT_EXPR_META_DREG:
68  *data_len = sizeof(meta->dreg);
69  return &meta->dreg;
70  case NFT_EXPR_META_SREG:
71  *data_len = sizeof(meta->sreg);
72  return &meta->sreg;
73  }
74  return NULL;
75 }
76 
77 static int nft_rule_expr_meta_cb(const struct nlattr *attr, void *data)
78 {
79  const struct nlattr **tb = data;
80  int type = mnl_attr_get_type(attr);
81 
82  if (mnl_attr_type_valid(attr, NFTA_META_MAX) < 0)
83  return MNL_CB_OK;
84 
85  switch(type) {
86  case NFTA_META_KEY:
87  case NFTA_META_DREG:
88  case NFTA_META_SREG:
89  if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0) {
90  perror("mnl_attr_validate");
91  return MNL_CB_ERROR;
92  }
93  break;
94  }
95 
96  tb[type] = attr;
97  return MNL_CB_OK;
98 }
99 
100 static void
101 nft_rule_expr_meta_build(struct nlmsghdr *nlh, struct nft_rule_expr *e)
102 {
103  struct nft_expr_meta *meta = nft_expr_data(e);
104 
105  if (e->flags & (1 << NFT_EXPR_META_KEY))
106  mnl_attr_put_u32(nlh, NFTA_META_KEY, htonl(meta->key));
107  if (e->flags & (1 << NFT_EXPR_META_DREG))
108  mnl_attr_put_u32(nlh, NFTA_META_DREG, htonl(meta->dreg));
109  if (e->flags & (1 << NFT_EXPR_META_SREG))
110  mnl_attr_put_u32(nlh, NFTA_META_SREG, htonl(meta->sreg));
111 }
112 
113 static int
114 nft_rule_expr_meta_parse(struct nft_rule_expr *e, struct nlattr *attr)
115 {
116  struct nft_expr_meta *meta = nft_expr_data(e);
117  struct nlattr *tb[NFTA_META_MAX+1] = {};
118 
119  if (mnl_attr_parse_nested(attr, nft_rule_expr_meta_cb, tb) < 0)
120  return -1;
121 
122  if (tb[NFTA_META_KEY]) {
123  meta->key = ntohl(mnl_attr_get_u32(tb[NFTA_META_KEY]));
124  e->flags |= (1 << NFT_EXPR_META_KEY);
125  }
126  if (tb[NFTA_META_DREG]) {
127  meta->dreg = ntohl(mnl_attr_get_u32(tb[NFTA_META_DREG]));
128  e->flags |= (1 << NFT_EXPR_META_DREG);
129  }
130  if (tb[NFTA_META_SREG]) {
131  meta->sreg = ntohl(mnl_attr_get_u32(tb[NFTA_META_SREG]));
132  e->flags |= (1 << NFT_EXPR_META_SREG);
133  }
134 
135  return 0;
136 }
137 
138 static const char *meta_key2str_array[NFT_META_MAX] = {
139  [NFT_META_LEN] = "len",
140  [NFT_META_PROTOCOL] = "protocol",
141  [NFT_META_NFPROTO] = "nfproto",
142  [NFT_META_L4PROTO] = "l4proto",
143  [NFT_META_PRIORITY] = "priority",
144  [NFT_META_MARK] = "mark",
145  [NFT_META_IIF] = "iif",
146  [NFT_META_OIF] = "oif",
147  [NFT_META_IIFNAME] = "iifname",
148  [NFT_META_OIFNAME] = "oifname",
149  [NFT_META_IIFTYPE] = "iiftype",
150  [NFT_META_OIFTYPE] = "oiftype",
151  [NFT_META_SKUID] = "skuid",
152  [NFT_META_SKGID] = "skgid",
153  [NFT_META_NFTRACE] = "nftrace",
154  [NFT_META_RTCLASSID] = "rtclassid",
155  [NFT_META_SECMARK] = "secmark",
156  [NFT_META_BRI_IIFNAME] = "bri_iifname",
157  [NFT_META_BRI_OIFNAME] = "bri_oifname",
158 };
159 
160 static const char *meta_key2str(uint8_t key)
161 {
162  if (key < NFT_META_MAX)
163  return meta_key2str_array[key];
164 
165  return "unknown";
166 }
167 
168 static inline int str2meta_key(const char *str)
169 {
170  int i;
171 
172  for (i = 0; i < NFT_META_MAX; i++) {
173  if (strcmp(str, meta_key2str_array[i]) == 0)
174  return i;
175  }
176 
177  errno = EINVAL;
178  return -1;
179 }
180 
181 static int nft_rule_expr_meta_json_parse(struct nft_rule_expr *e, json_t *root,
182  struct nft_parse_err *err)
183 {
184 #ifdef JSON_PARSING
185  const char *key_str;
186  uint32_t reg;
187  int key;
188 
189  key_str = nft_jansson_parse_str(root, "key", err);
190  if (key_str != NULL) {
191  key = str2meta_key(key_str);
192  if (key >= 0)
193  nft_rule_expr_set_u32(e, NFT_EXPR_META_KEY, key);
194  }
195 
196  if (nft_jansson_node_exist(root, "dreg")) {
197  if (nft_jansson_parse_reg(root, "dreg", NFT_TYPE_U32, &reg,
198  err) == 0)
199  nft_rule_expr_set_u32(e, NFT_EXPR_META_DREG, reg);
200  }
201 
202  if (nft_jansson_node_exist(root, "sreg")) {
203  if (nft_jansson_parse_reg(root, "sreg", NFT_TYPE_U32, &reg,
204  err) == 0)
205  nft_rule_expr_set_u32(e, NFT_EXPR_META_SREG, reg);
206  }
207 
208  return 0;
209 #else
210  errno = EOPNOTSUPP;
211  return -1;
212 #endif
213 }
214 
215 
216 static int nft_rule_expr_meta_xml_parse(struct nft_rule_expr *e, mxml_node_t *tree,
217  struct nft_parse_err *err)
218 {
219 #ifdef XML_PARSING
220  const char *key_str;
221  int key;
222  uint32_t dreg, sreg;
223 
224  key_str = nft_mxml_str_parse(tree, "key", MXML_DESCEND_FIRST,
225  NFT_XML_MAND, err);
226  if (key_str != NULL) {
227  key = str2meta_key(key_str);
228  if (key >= 0)
229  nft_rule_expr_set_u32(e, NFT_EXPR_META_KEY, key);
230  }
231 
232  if (nft_mxml_reg_parse(tree, "dreg", &dreg, MXML_DESCEND_FIRST,
233  NFT_XML_OPT, err) == 0)
234  nft_rule_expr_set_u32(e, NFT_EXPR_META_DREG, dreg);
235 
236  if (nft_mxml_reg_parse(tree, "sreg", &sreg, MXML_DESCEND_FIRST,
237  NFT_XML_OPT, err) == 0)
238  nft_rule_expr_set_u32(e, NFT_EXPR_META_SREG, sreg);
239 
240  return 0;
241 #else
242  errno = EOPNOTSUPP;
243  return -1;
244 #endif
245 }
246 
247 static int
248 nft_rule_expr_meta_snprintf_default(char *buf, size_t len,
249  struct nft_rule_expr *e)
250 {
251  struct nft_expr_meta *meta = nft_expr_data(e);
252 
253  if (e->flags & (1 << NFT_EXPR_META_SREG)) {
254  return snprintf(buf, len, "set %s with reg %u ",
255  meta_key2str(meta->key), meta->sreg);
256  }
257  if (e->flags & (1 << NFT_EXPR_META_DREG)) {
258  return snprintf(buf, len, "load %s => reg %u ",
259  meta_key2str(meta->key), meta->dreg);
260  }
261  return 0;
262 }
263 
264 static int
265 nft_rule_expr_meta_snprintf_xml(char *buf, size_t size,
266  struct nft_rule_expr *e)
267 {
268  int ret, len = size, offset = 0;
269  struct nft_expr_meta *meta = nft_expr_data(e);
270 
271  if (e->flags & (1 << NFT_EXPR_META_DREG)) {
272  ret = snprintf(buf, len, "<dreg>%u</dreg>", meta->dreg);
273  SNPRINTF_BUFFER_SIZE(ret, size, len, offset);
274  }
275  if (e->flags & (1 << NFT_EXPR_META_KEY)) {
276  ret = snprintf(buf + offset, len, "<key>%s</key>",
277  meta_key2str(meta->key));
278  SNPRINTF_BUFFER_SIZE(ret, size, len, offset);
279  }
280  if (e->flags & (1 << NFT_EXPR_META_SREG)) {
281  ret = snprintf(buf + offset, len, "<sreg>%u</sreg>",
282  meta->sreg);
283  SNPRINTF_BUFFER_SIZE(ret, size, len, offset);
284  }
285 
286  return offset;
287 }
288 
289 static int
290 nft_rule_expr_meta_snprintf_json(char *buf, size_t size,
291  struct nft_rule_expr *e)
292 {
293  int ret, len = size, offset = 0;
294  struct nft_expr_meta *meta = nft_expr_data(e);
295 
296  if (e->flags & (1 << NFT_EXPR_META_DREG)) {
297  ret = snprintf(buf + offset, len, "\"dreg\":%u,",
298  meta->dreg);
299  SNPRINTF_BUFFER_SIZE(ret, size, len, offset);
300  }
301  if (e->flags & (1 << NFT_EXPR_META_KEY)) {
302  ret = snprintf(buf + offset, len, "\"key\":\"%s\",",
303  meta_key2str(meta->key));
304  SNPRINTF_BUFFER_SIZE(ret, size, len, offset);
305  }
306  if (e->flags & (1 << NFT_EXPR_META_SREG)) {
307  ret = snprintf(buf + offset, len, "\"sreg\":%u,",
308  meta->sreg);
309  SNPRINTF_BUFFER_SIZE(ret, size, len, offset);
310  }
311  /* Remove the last comma separator */
312  if (offset > 0)
313  offset--;
314 
315  return offset;
316 }
317 
318 static int
319 nft_rule_expr_meta_snprintf(char *buf, size_t len, uint32_t type,
320  uint32_t flags, struct nft_rule_expr *e)
321 {
322  switch(type) {
323  case NFT_OUTPUT_DEFAULT:
324  return nft_rule_expr_meta_snprintf_default(buf, len, e);
325  case NFT_OUTPUT_XML:
326  return nft_rule_expr_meta_snprintf_xml(buf, len, e);
327  case NFT_OUTPUT_JSON:
328  return nft_rule_expr_meta_snprintf_json(buf, len, e);
329  default:
330  break;
331  }
332  return -1;
333 }
334 
335 struct expr_ops expr_ops_meta = {
336  .name = "meta",
337  .alloc_len = sizeof(struct nft_expr_meta),
338  .max_attr = NFTA_META_MAX,
339  .set = nft_rule_expr_meta_set,
340  .get = nft_rule_expr_meta_get,
341  .parse = nft_rule_expr_meta_parse,
342  .build = nft_rule_expr_meta_build,
343  .snprintf = nft_rule_expr_meta_snprintf,
344  .xml_parse = nft_rule_expr_meta_xml_parse,
345  .json_parse = nft_rule_expr_meta_json_parse,
346 };
347 
348 static void __init expr_meta_init(void)
349 {
350  nft_expr_ops_register(&expr_ops_meta);
351 }