libnftnl  1.0.2
payload.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 <limits.h>
18 #include <arpa/inet.h>
19 #include <errno.h>
20 #include <libmnl/libmnl.h>
21 
22 #include <linux/netfilter/nf_tables.h>
23 
24 #include <libnftnl/expr.h>
25 #include <libnftnl/rule.h>
26 
27 #include "expr_ops.h"
28 
30  enum nft_registers dreg;
31  enum nft_payload_bases base;
32  uint32_t offset;
33  uint32_t len;
34 };
35 
36 static int
37 nft_rule_expr_payload_set(struct nft_rule_expr *e, uint16_t type,
38  const void *data, uint32_t data_len)
39 {
40  struct nft_expr_payload *payload = nft_expr_data(e);
41 
42  switch(type) {
43  case NFT_EXPR_PAYLOAD_DREG:
44  payload->dreg = *((uint32_t *)data);
45  break;
46  case NFT_EXPR_PAYLOAD_BASE:
47  payload->base = *((uint32_t *)data);
48  break;
49  case NFT_EXPR_PAYLOAD_OFFSET:
50  payload->offset = *((unsigned int *)data);
51  break;
52  case NFT_EXPR_PAYLOAD_LEN:
53  payload->len = *((unsigned int *)data);
54  break;
55  default:
56  return -1;
57  }
58  return 0;
59 }
60 
61 static const void *
62 nft_rule_expr_payload_get(const struct nft_rule_expr *e, uint16_t type,
63  uint32_t *data_len)
64 {
65  struct nft_expr_payload *payload = nft_expr_data(e);
66 
67  switch(type) {
68  case NFT_EXPR_PAYLOAD_DREG:
69  *data_len = sizeof(payload->dreg);
70  return &payload->dreg;
71  case NFT_EXPR_PAYLOAD_BASE:
72  *data_len = sizeof(payload->base);
73  return &payload->base;
74  case NFT_EXPR_PAYLOAD_OFFSET:
75  *data_len = sizeof(payload->offset);
76  return &payload->offset;
77  case NFT_EXPR_PAYLOAD_LEN:
78  *data_len = sizeof(payload->len);
79  return &payload->len;
80  }
81  return NULL;
82 }
83 
84 static int nft_rule_expr_payload_cb(const struct nlattr *attr, void *data)
85 {
86  const struct nlattr **tb = data;
87  int type = mnl_attr_get_type(attr);
88 
89  if (mnl_attr_type_valid(attr, NFTA_PAYLOAD_MAX) < 0)
90  return MNL_CB_OK;
91 
92  switch(type) {
93  case NFTA_PAYLOAD_DREG:
94  case NFTA_PAYLOAD_BASE:
95  case NFTA_PAYLOAD_OFFSET:
96  case NFTA_PAYLOAD_LEN:
97  if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0) {
98  perror("mnl_attr_validate");
99  return MNL_CB_ERROR;
100  }
101  break;
102  }
103 
104  tb[type] = attr;
105  return MNL_CB_OK;
106 }
107 
108 static void
109 nft_rule_expr_payload_build(struct nlmsghdr *nlh, struct nft_rule_expr *e)
110 {
111  struct nft_expr_payload *payload = nft_expr_data(e);
112 
113  if (e->flags & (1 << NFT_EXPR_PAYLOAD_DREG))
114  mnl_attr_put_u32(nlh, NFTA_PAYLOAD_DREG, htonl(payload->dreg));
115  if (e->flags & (1 << NFT_EXPR_PAYLOAD_BASE))
116  mnl_attr_put_u32(nlh, NFTA_PAYLOAD_BASE, htonl(payload->base));
117  if (e->flags & (1 << NFT_EXPR_PAYLOAD_OFFSET))
118  mnl_attr_put_u32(nlh, NFTA_PAYLOAD_OFFSET, htonl(payload->offset));
119  if (e->flags & (1 << NFT_EXPR_PAYLOAD_LEN))
120  mnl_attr_put_u32(nlh, NFTA_PAYLOAD_LEN, htonl(payload->len));
121 }
122 
123 static int
124 nft_rule_expr_payload_parse(struct nft_rule_expr *e, struct nlattr *attr)
125 {
126  struct nft_expr_payload *payload = nft_expr_data(e);
127  struct nlattr *tb[NFTA_PAYLOAD_MAX+1] = {};
128 
129  if (mnl_attr_parse_nested(attr, nft_rule_expr_payload_cb, tb) < 0)
130  return -1;
131 
132  if (tb[NFTA_PAYLOAD_DREG]) {
133  payload->dreg = ntohl(mnl_attr_get_u32(tb[NFTA_PAYLOAD_DREG]));
134  e->flags |= (1 << NFT_EXPR_PAYLOAD_DREG);
135  }
136  if (tb[NFTA_PAYLOAD_BASE]) {
137  payload->base = ntohl(mnl_attr_get_u32(tb[NFTA_PAYLOAD_BASE]));
138  e->flags |= (1 << NFT_EXPR_PAYLOAD_BASE);
139  }
140  if (tb[NFTA_PAYLOAD_OFFSET]) {
141  payload->offset = ntohl(mnl_attr_get_u32(tb[NFTA_PAYLOAD_OFFSET]));
142  e->flags |= (1 << NFT_EXPR_PAYLOAD_OFFSET);
143  }
144  if (tb[NFTA_PAYLOAD_LEN]) {
145  payload->len = ntohl(mnl_attr_get_u32(tb[NFTA_PAYLOAD_LEN]));
146  e->flags |= (1 << NFT_EXPR_PAYLOAD_LEN);
147  }
148 
149  return 0;
150 }
151 
152 static char *base2str_array[NFT_PAYLOAD_TRANSPORT_HEADER+1] = {
153  [NFT_PAYLOAD_LL_HEADER] = "link",
154  [NFT_PAYLOAD_NETWORK_HEADER] = "network",
155  [NFT_PAYLOAD_TRANSPORT_HEADER] = "transport",
156 };
157 
158 static const char *base2str(enum nft_payload_bases base)
159 {
160  if (base > NFT_PAYLOAD_TRANSPORT_HEADER)
161  return "unknown";
162 
163  return base2str_array[base];
164 }
165 
166 static int
167 nft_rule_expr_payload_snprintf_json(char *buf, size_t len, uint32_t flags,
168  struct nft_rule_expr *e)
169 {
170  struct nft_expr_payload *payload = nft_expr_data(e);
171  int size = len, offset = 0, ret;
172 
173  if (e->flags & (1 << NFT_EXPR_PAYLOAD_DREG)) {
174  ret = snprintf(buf, len, "\"dreg\":%u,", payload->dreg);
175  SNPRINTF_BUFFER_SIZE(ret, size, len, offset);
176  }
177  if (e->flags & (1 << NFT_EXPR_PAYLOAD_OFFSET)) {
178  ret = snprintf(buf + offset, len, "\"offset\":%u,",
179  payload->offset);
180  SNPRINTF_BUFFER_SIZE(ret, size, len, offset);
181  }
182  if (e->flags & (1 << NFT_EXPR_PAYLOAD_LEN)) {
183  ret = snprintf(buf + offset, len, "\"len\":%u,", payload->len);
184  SNPRINTF_BUFFER_SIZE(ret, size, len, offset);
185  }
186  if (e->flags & (1 << NFT_EXPR_PAYLOAD_BASE)) {
187  ret = snprintf(buf + offset, len, "\"base\":\"%s\"",
188  base2str(payload->base));
189  SNPRINTF_BUFFER_SIZE(ret, size, len, offset);
190  }
191  return offset;
192 }
193 
194 static inline int nft_str2base(const char *base)
195 {
196  if (strcmp(base, "link") == 0)
197  return NFT_PAYLOAD_LL_HEADER;
198  else if (strcmp(base, "network") == 0)
199  return NFT_PAYLOAD_NETWORK_HEADER;
200  else if (strcmp(base, "transport") == 0)
201  return NFT_PAYLOAD_TRANSPORT_HEADER;
202  else {
203  errno = EINVAL;
204  return -1;
205  }
206 }
207 
208 static int
209 nft_rule_expr_payload_json_parse(struct nft_rule_expr *e, json_t *root,
210  struct nft_parse_err *err)
211 {
212 #ifdef JSON_PARSING
213  const char *base_str;
214  uint32_t reg, uval32;
215  int base;
216 
217  if (nft_jansson_parse_reg(root, "dreg", NFT_TYPE_U32, &reg, err) == 0)
218  nft_rule_expr_set_u32(e, NFT_EXPR_PAYLOAD_DREG, reg);
219 
220  base_str = nft_jansson_parse_str(root, "base", err);
221  if (base_str != NULL) {
222  base = nft_str2base(base_str);
223  if (base < 0)
224  return -1;
225 
226  nft_rule_expr_set_u32(e, NFT_EXPR_PAYLOAD_BASE, base);
227  }
228 
229  if (nft_jansson_parse_val(root, "offset", NFT_TYPE_U32, &uval32,
230  err) == 0)
231  nft_rule_expr_set_u32(e, NFT_EXPR_PAYLOAD_OFFSET, uval32);
232 
233  if (nft_jansson_parse_val(root, "len", NFT_TYPE_U32, &uval32, err) == 0)
234  nft_rule_expr_set_u32(e, NFT_EXPR_PAYLOAD_LEN, uval32);
235 
236  return 0;
237 #else
238  errno = EOPNOTSUPP;
239  return -1;
240 #endif
241 }
242 
243 static int
244 nft_rule_expr_payload_xml_parse(struct nft_rule_expr *e, mxml_node_t *tree,
245  struct nft_parse_err *err)
246 {
247 #ifdef XML_PARSING
248  const char *base_str;
249  int32_t base;
250  uint32_t dreg, offset, len;
251 
252  if (nft_mxml_reg_parse(tree, "dreg", &dreg, MXML_DESCEND_FIRST,
253  NFT_XML_MAND, err) == 0)
254  nft_rule_expr_set_u32(e, NFT_EXPR_PAYLOAD_DREG, dreg);
255 
256  base_str = nft_mxml_str_parse(tree, "base", MXML_DESCEND_FIRST,
257  NFT_XML_MAND, err);
258  if (base_str != NULL) {
259  base = nft_str2base(base_str);
260  if (base < 0)
261  return -1;
262 
263  nft_rule_expr_set_u32(e, NFT_EXPR_PAYLOAD_BASE, base);
264  }
265 
266  if (nft_mxml_num_parse(tree, "offset", MXML_DESCEND_FIRST, BASE_DEC,
267  &offset, NFT_TYPE_U32, NFT_XML_MAND, err) == 0)
268  nft_rule_expr_set_u32(e, NFT_EXPR_PAYLOAD_OFFSET, offset);
269 
270 
271  if (nft_mxml_num_parse(tree, "len", MXML_DESCEND_FIRST, BASE_DEC,
272  &len, NFT_TYPE_U32, NFT_XML_MAND, err) == 0)
273  nft_rule_expr_set_u32(e, NFT_EXPR_PAYLOAD_LEN, len);
274 
275  return 0;
276 #else
277  errno = EOPNOTSUPP;
278  return -1;
279 #endif
280 }
281 
282 static int
283 nft_rule_expr_payload_snprintf_xml(char *buf, size_t len, uint32_t flags,
284  struct nft_rule_expr *e)
285 {
286  struct nft_expr_payload *payload = nft_expr_data(e);
287  int size = len, offset = 0, ret;
288 
289  if (e->flags & (1 << NFT_EXPR_PAYLOAD_DREG)) {
290  ret = snprintf(buf, len, "<dreg>%u</dreg>", payload->dreg);
291  SNPRINTF_BUFFER_SIZE(ret, size, len, offset);
292  }
293  if (e->flags & (1 << NFT_EXPR_PAYLOAD_OFFSET)) {
294  ret = snprintf(buf + offset, len, "<offset>%u</offset>",
295  payload->offset);
296  SNPRINTF_BUFFER_SIZE(ret, size, len, offset);
297  }
298  if (e->flags & (1 << NFT_EXPR_PAYLOAD_LEN)) {
299  ret = snprintf(buf + offset, len, "<len>%u</len>", payload->len);
300  SNPRINTF_BUFFER_SIZE(ret, size, len, offset);
301  }
302  if (e->flags & (1 << NFT_EXPR_PAYLOAD_BASE)) {
303  ret = snprintf(buf + offset, len, "<base>%s</base>",
304  base2str(payload->base));
305  SNPRINTF_BUFFER_SIZE(ret, size, len, offset);
306  }
307 
308  return offset;
309 }
310 
311 static int
312 nft_rule_expr_payload_snprintf(char *buf, size_t len, uint32_t type,
313  uint32_t flags, struct nft_rule_expr *e)
314 {
315  struct nft_expr_payload *payload = nft_expr_data(e);
316 
317  switch(type) {
318  case NFT_OUTPUT_DEFAULT:
319  return snprintf(buf, len, "load %ub @ %s header + %u => reg %u ",
320  payload->len, base2str(payload->base),
321  payload->offset, payload->dreg);
322  case NFT_OUTPUT_XML:
323  return nft_rule_expr_payload_snprintf_xml(buf, len, flags, e);
324  case NFT_OUTPUT_JSON:
325  return nft_rule_expr_payload_snprintf_json(buf, len, flags, e);
326  default:
327  break;
328  }
329  return -1;
330 }
331 
332 struct expr_ops expr_ops_payload = {
333  .name = "payload",
334  .alloc_len = sizeof(struct nft_expr_payload),
335  .max_attr = NFTA_PAYLOAD_MAX,
336  .set = nft_rule_expr_payload_set,
337  .get = nft_rule_expr_payload_get,
338  .parse = nft_rule_expr_payload_parse,
339  .build = nft_rule_expr_payload_build,
340  .snprintf = nft_rule_expr_payload_snprintf,
341  .xml_parse = nft_rule_expr_payload_xml_parse,
342  .json_parse = nft_rule_expr_payload_json_parse,
343 };
344 
345 static void __init expr_payload_init(void)
346 {
347  nft_expr_ops_register(&expr_ops_payload);
348 }