libnftnl  1.0.5
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 
28  enum nft_registers dreg;
29  enum nft_payload_bases base;
30  uint32_t offset;
31  uint32_t len;
32 };
33 
34 static int
35 nftnl_expr_payload_set(struct nftnl_expr *e, uint16_t type,
36  const void *data, uint32_t data_len)
37 {
38  struct nftnl_expr_payload *payload = nftnl_expr_data(e);
39 
40  switch(type) {
41  case NFTNL_EXPR_PAYLOAD_DREG:
42  payload->dreg = *((uint32_t *)data);
43  break;
44  case NFTNL_EXPR_PAYLOAD_BASE:
45  payload->base = *((uint32_t *)data);
46  break;
47  case NFTNL_EXPR_PAYLOAD_OFFSET:
48  payload->offset = *((unsigned int *)data);
49  break;
50  case NFTNL_EXPR_PAYLOAD_LEN:
51  payload->len = *((unsigned int *)data);
52  break;
53  default:
54  return -1;
55  }
56  return 0;
57 }
58 
59 static const void *
60 nftnl_expr_payload_get(const struct nftnl_expr *e, uint16_t type,
61  uint32_t *data_len)
62 {
63  struct nftnl_expr_payload *payload = nftnl_expr_data(e);
64 
65  switch(type) {
66  case NFTNL_EXPR_PAYLOAD_DREG:
67  *data_len = sizeof(payload->dreg);
68  return &payload->dreg;
69  case NFTNL_EXPR_PAYLOAD_BASE:
70  *data_len = sizeof(payload->base);
71  return &payload->base;
72  case NFTNL_EXPR_PAYLOAD_OFFSET:
73  *data_len = sizeof(payload->offset);
74  return &payload->offset;
75  case NFTNL_EXPR_PAYLOAD_LEN:
76  *data_len = sizeof(payload->len);
77  return &payload->len;
78  }
79  return NULL;
80 }
81 
82 static int nftnl_expr_payload_cb(const struct nlattr *attr, void *data)
83 {
84  const struct nlattr **tb = data;
85  int type = mnl_attr_get_type(attr);
86 
87  if (mnl_attr_type_valid(attr, NFTA_PAYLOAD_MAX) < 0)
88  return MNL_CB_OK;
89 
90  switch(type) {
91  case NFTA_PAYLOAD_DREG:
92  case NFTA_PAYLOAD_BASE:
93  case NFTA_PAYLOAD_OFFSET:
94  case NFTA_PAYLOAD_LEN:
95  if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0)
96  abi_breakage();
97  break;
98  }
99 
100  tb[type] = attr;
101  return MNL_CB_OK;
102 }
103 
104 static void
105 nftnl_expr_payload_build(struct nlmsghdr *nlh, struct nftnl_expr *e)
106 {
107  struct nftnl_expr_payload *payload = nftnl_expr_data(e);
108 
109  if (e->flags & (1 << NFTNL_EXPR_PAYLOAD_DREG))
110  mnl_attr_put_u32(nlh, NFTA_PAYLOAD_DREG, htonl(payload->dreg));
111  if (e->flags & (1 << NFTNL_EXPR_PAYLOAD_BASE))
112  mnl_attr_put_u32(nlh, NFTA_PAYLOAD_BASE, htonl(payload->base));
113  if (e->flags & (1 << NFTNL_EXPR_PAYLOAD_OFFSET))
114  mnl_attr_put_u32(nlh, NFTA_PAYLOAD_OFFSET, htonl(payload->offset));
115  if (e->flags & (1 << NFTNL_EXPR_PAYLOAD_LEN))
116  mnl_attr_put_u32(nlh, NFTA_PAYLOAD_LEN, htonl(payload->len));
117 }
118 
119 static int
120 nftnl_expr_payload_parse(struct nftnl_expr *e, struct nlattr *attr)
121 {
122  struct nftnl_expr_payload *payload = nftnl_expr_data(e);
123  struct nlattr *tb[NFTA_PAYLOAD_MAX+1] = {};
124 
125  if (mnl_attr_parse_nested(attr, nftnl_expr_payload_cb, tb) < 0)
126  return -1;
127 
128  if (tb[NFTA_PAYLOAD_DREG]) {
129  payload->dreg = ntohl(mnl_attr_get_u32(tb[NFTA_PAYLOAD_DREG]));
130  e->flags |= (1 << NFTNL_EXPR_PAYLOAD_DREG);
131  }
132  if (tb[NFTA_PAYLOAD_BASE]) {
133  payload->base = ntohl(mnl_attr_get_u32(tb[NFTA_PAYLOAD_BASE]));
134  e->flags |= (1 << NFTNL_EXPR_PAYLOAD_BASE);
135  }
136  if (tb[NFTA_PAYLOAD_OFFSET]) {
137  payload->offset = ntohl(mnl_attr_get_u32(tb[NFTA_PAYLOAD_OFFSET]));
138  e->flags |= (1 << NFTNL_EXPR_PAYLOAD_OFFSET);
139  }
140  if (tb[NFTA_PAYLOAD_LEN]) {
141  payload->len = ntohl(mnl_attr_get_u32(tb[NFTA_PAYLOAD_LEN]));
142  e->flags |= (1 << NFTNL_EXPR_PAYLOAD_LEN);
143  }
144 
145  return 0;
146 }
147 
148 static char *base2str_array[NFT_PAYLOAD_TRANSPORT_HEADER+1] = {
149  [NFT_PAYLOAD_LL_HEADER] = "link",
150  [NFT_PAYLOAD_NETWORK_HEADER] = "network",
151  [NFT_PAYLOAD_TRANSPORT_HEADER] = "transport",
152 };
153 
154 static const char *base2str(enum nft_payload_bases base)
155 {
156  if (base > NFT_PAYLOAD_TRANSPORT_HEADER)
157  return "unknown";
158 
159  return base2str_array[base];
160 }
161 
162 static inline int nftnl_str2base(const char *base)
163 {
164  if (strcmp(base, "link") == 0)
165  return NFT_PAYLOAD_LL_HEADER;
166  else if (strcmp(base, "network") == 0)
167  return NFT_PAYLOAD_NETWORK_HEADER;
168  else if (strcmp(base, "transport") == 0)
169  return NFT_PAYLOAD_TRANSPORT_HEADER;
170  else {
171  errno = EINVAL;
172  return -1;
173  }
174 }
175 
176 static int
177 nftnl_expr_payload_json_parse(struct nftnl_expr *e, json_t *root,
178  struct nftnl_parse_err *err)
179 {
180 #ifdef JSON_PARSING
181  const char *base_str;
182  uint32_t reg, uval32;
183  int base;
184 
185  if (nftnl_jansson_parse_reg(root, "dreg", NFTNL_TYPE_U32, &reg, err) == 0)
186  nftnl_expr_set_u32(e, NFTNL_EXPR_PAYLOAD_DREG, reg);
187 
188  base_str = nftnl_jansson_parse_str(root, "base", err);
189  if (base_str != NULL) {
190  base = nftnl_str2base(base_str);
191  if (base < 0)
192  return -1;
193 
194  nftnl_expr_set_u32(e, NFTNL_EXPR_PAYLOAD_BASE, base);
195  }
196 
197  if (nftnl_jansson_parse_val(root, "offset", NFTNL_TYPE_U32, &uval32,
198  err) == 0)
199  nftnl_expr_set_u32(e, NFTNL_EXPR_PAYLOAD_OFFSET, uval32);
200 
201  if (nftnl_jansson_parse_val(root, "len", NFTNL_TYPE_U32, &uval32, err) == 0)
202  nftnl_expr_set_u32(e, NFTNL_EXPR_PAYLOAD_LEN, uval32);
203 
204  return 0;
205 #else
206  errno = EOPNOTSUPP;
207  return -1;
208 #endif
209 }
210 
211 static int
212 nftnl_expr_payload_xml_parse(struct nftnl_expr *e, mxml_node_t *tree,
213  struct nftnl_parse_err *err)
214 {
215 #ifdef XML_PARSING
216  const char *base_str;
217  int32_t base;
218  uint32_t dreg, offset, len;
219 
220  if (nftnl_mxml_reg_parse(tree, "dreg", &dreg, MXML_DESCEND_FIRST,
221  NFTNL_XML_MAND, err) == 0)
222  nftnl_expr_set_u32(e, NFTNL_EXPR_PAYLOAD_DREG, dreg);
223 
224  base_str = nftnl_mxml_str_parse(tree, "base", MXML_DESCEND_FIRST,
225  NFTNL_XML_MAND, err);
226  if (base_str != NULL) {
227  base = nftnl_str2base(base_str);
228  if (base < 0)
229  return -1;
230 
231  nftnl_expr_set_u32(e, NFTNL_EXPR_PAYLOAD_BASE, base);
232  }
233 
234  if (nftnl_mxml_num_parse(tree, "offset", MXML_DESCEND_FIRST, BASE_DEC,
235  &offset, NFTNL_TYPE_U32, NFTNL_XML_MAND, err) == 0)
236  nftnl_expr_set_u32(e, NFTNL_EXPR_PAYLOAD_OFFSET, offset);
237 
238 
239  if (nftnl_mxml_num_parse(tree, "len", MXML_DESCEND_FIRST, BASE_DEC,
240  &len, NFTNL_TYPE_U32, NFTNL_XML_MAND, err) == 0)
241  nftnl_expr_set_u32(e, NFTNL_EXPR_PAYLOAD_LEN, len);
242 
243  return 0;
244 #else
245  errno = EOPNOTSUPP;
246  return -1;
247 #endif
248 }
249 
250 static int nftnl_expr_payload_export(char *buf, size_t size, uint32_t flags,
251  struct nftnl_expr *e, int type)
252 {
253  struct nftnl_expr_payload *payload = nftnl_expr_data(e);
254  NFTNL_BUF_INIT(b, buf, size);
255 
256  if (e->flags & (1 << NFTNL_EXPR_PAYLOAD_DREG))
257  nftnl_buf_u32(&b, type, payload->dreg, DREG);
258  if (e->flags & (1 << NFTNL_EXPR_PAYLOAD_OFFSET))
259  nftnl_buf_u32(&b, type, payload->offset, OFFSET);
260  if (e->flags & (1 << NFTNL_EXPR_PAYLOAD_LEN))
261  nftnl_buf_u32(&b, type, payload->len, LEN);
262  if (e->flags & (1 << NFTNL_EXPR_PAYLOAD_BASE))
263  nftnl_buf_str(&b, type, base2str(payload->base), BASE);
264 
265  return nftnl_buf_done(&b);
266 }
267 
268 static int
269 nftnl_expr_payload_snprintf(char *buf, size_t len, uint32_t type,
270  uint32_t flags, struct nftnl_expr *e)
271 {
272  struct nftnl_expr_payload *payload = nftnl_expr_data(e);
273 
274  switch (type) {
275  case NFTNL_OUTPUT_DEFAULT:
276  return snprintf(buf, len, "load %ub @ %s header + %u => reg %u ",
277  payload->len, base2str(payload->base),
278  payload->offset, payload->dreg);
279  case NFTNL_OUTPUT_XML:
280  case NFTNL_OUTPUT_JSON:
281  return nftnl_expr_payload_export(buf, len, flags, e, type);
282  default:
283  break;
284  }
285  return -1;
286 }
287 
288 struct expr_ops expr_ops_payload = {
289  .name = "payload",
290  .alloc_len = sizeof(struct nftnl_expr_payload),
291  .max_attr = NFTA_PAYLOAD_MAX,
292  .set = nftnl_expr_payload_set,
293  .get = nftnl_expr_payload_get,
294  .parse = nftnl_expr_payload_parse,
295  .build = nftnl_expr_payload_build,
296  .snprintf = nftnl_expr_payload_snprintf,
297  .xml_parse = nftnl_expr_payload_xml_parse,
298  .json_parse = nftnl_expr_payload_json_parse,
299 };