libnftnl  1.0.2
exthdr.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 "internal.h"
13 
14 #include <stdio.h>
15 #include <string.h>
16 #include <stdint.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 
29 #ifndef IPPROTO_MH
30 #define IPPROTO_MH 135
31 #endif
32 
34  enum nft_registers dreg;
35  uint32_t offset;
36  uint32_t len;
37  uint8_t type;
38 };
39 
40 static int
41 nft_rule_expr_exthdr_set(struct nft_rule_expr *e, uint16_t type,
42  const void *data, uint32_t data_len)
43 {
44  struct nft_expr_exthdr *exthdr = nft_expr_data(e);
45 
46  switch(type) {
47  case NFT_EXPR_EXTHDR_DREG:
48  exthdr->dreg = *((uint32_t *)data);
49  break;
50  case NFT_EXPR_EXTHDR_TYPE:
51  exthdr->type = *((uint8_t *)data);
52  break;
53  case NFT_EXPR_EXTHDR_OFFSET:
54  exthdr->offset = *((uint32_t *)data);
55  break;
56  case NFT_EXPR_EXTHDR_LEN:
57  exthdr->len = *((uint32_t *)data);
58  break;
59  default:
60  return -1;
61  }
62  return 0;
63 }
64 
65 static const void *
66 nft_rule_expr_exthdr_get(const struct nft_rule_expr *e, uint16_t type,
67  uint32_t *data_len)
68 {
69  struct nft_expr_exthdr *exthdr = nft_expr_data(e);
70 
71  switch(type) {
72  case NFT_EXPR_EXTHDR_DREG:
73  *data_len = sizeof(exthdr->dreg);
74  return &exthdr->dreg;
75  case NFT_EXPR_EXTHDR_TYPE:
76  *data_len = sizeof(exthdr->type);
77  return &exthdr->type;
78  case NFT_EXPR_EXTHDR_OFFSET:
79  *data_len = sizeof(exthdr->offset);
80  return &exthdr->offset;
81  case NFT_EXPR_EXTHDR_LEN:
82  *data_len = sizeof(exthdr->len);
83  return &exthdr->len;
84  }
85  return NULL;
86 }
87 
88 static int nft_rule_expr_exthdr_cb(const struct nlattr *attr, void *data)
89 {
90  const struct nlattr **tb = data;
91  int type = mnl_attr_get_type(attr);
92 
93  if (mnl_attr_type_valid(attr, NFTA_EXTHDR_MAX) < 0)
94  return MNL_CB_OK;
95 
96  switch(type) {
97  case NFTA_EXTHDR_TYPE:
98  if (mnl_attr_validate(attr, MNL_TYPE_U8) < 0) {
99  perror("mnl_attr_validate");
100  return MNL_CB_ERROR;
101  }
102  break;
103  case NFTA_EXTHDR_DREG:
104  case NFTA_EXTHDR_OFFSET:
105  case NFTA_EXTHDR_LEN:
106  if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0) {
107  perror("mnl_attr_validate");
108  return MNL_CB_ERROR;
109  }
110  break;
111  }
112 
113  tb[type] = attr;
114  return MNL_CB_OK;
115 }
116 
117 static void
118 nft_rule_expr_exthdr_build(struct nlmsghdr *nlh, struct nft_rule_expr *e)
119 {
120  struct nft_expr_exthdr *exthdr = nft_expr_data(e);
121 
122  if (e->flags & (1 << NFT_EXPR_EXTHDR_DREG))
123  mnl_attr_put_u32(nlh, NFTA_EXTHDR_DREG, htonl(exthdr->dreg));
124  if (e->flags & (1 << NFT_EXPR_EXTHDR_TYPE))
125  mnl_attr_put_u8(nlh, NFTA_EXTHDR_TYPE, exthdr->type);
126  if (e->flags & (1 << NFT_EXPR_EXTHDR_OFFSET))
127  mnl_attr_put_u32(nlh, NFTA_EXTHDR_OFFSET, htonl(exthdr->offset));
128  if (e->flags & (1 << NFT_EXPR_EXTHDR_LEN))
129  mnl_attr_put_u32(nlh, NFTA_EXTHDR_LEN, htonl(exthdr->len));
130 }
131 
132 static int
133 nft_rule_expr_exthdr_parse(struct nft_rule_expr *e, struct nlattr *attr)
134 {
135  struct nft_expr_exthdr *exthdr = nft_expr_data(e);
136  struct nlattr *tb[NFTA_EXTHDR_MAX+1] = {};
137 
138  if (mnl_attr_parse_nested(attr, nft_rule_expr_exthdr_cb, tb) < 0)
139  return -1;
140 
141  if (tb[NFTA_EXTHDR_DREG]) {
142  exthdr->dreg = ntohl(mnl_attr_get_u32(tb[NFTA_EXTHDR_DREG]));
143  e->flags |= (1 << NFT_EXPR_EXTHDR_DREG);
144  }
145  if (tb[NFTA_EXTHDR_TYPE]) {
146  exthdr->type = mnl_attr_get_u8(tb[NFTA_EXTHDR_TYPE]);
147  e->flags |= (1 << NFT_EXPR_EXTHDR_TYPE);
148  }
149  if (tb[NFTA_EXTHDR_OFFSET]) {
150  exthdr->offset = ntohl(mnl_attr_get_u32(tb[NFTA_EXTHDR_OFFSET]));
151  e->flags |= (1 << NFT_EXPR_EXTHDR_OFFSET);
152  }
153  if (tb[NFTA_EXTHDR_LEN]) {
154  exthdr->len = ntohl(mnl_attr_get_u32(tb[NFTA_EXTHDR_LEN]));
155  e->flags |= (1 << NFT_EXPR_EXTHDR_LEN);
156  }
157 
158  return 0;
159 }
160 
161 static const char *exthdr_type2str(uint32_t type)
162 {
163  switch (type) {
164  case IPPROTO_HOPOPTS:
165  return "hopopts";
166  case IPPROTO_ROUTING:
167  return "routing";
168  case IPPROTO_FRAGMENT:
169  return "fragment";
170  case IPPROTO_DSTOPTS:
171  return "dstopts";
172  case IPPROTO_MH:
173  return "mh";
174  default:
175  return "unknown";
176  }
177 }
178 
179 static inline int str2exthdr_type(const char *str)
180 {
181  if (strcmp(str, "hopopts") == 0)
182  return IPPROTO_HOPOPTS;
183  else if (strcmp(str, "routing") == 0)
184  return IPPROTO_ROUTING;
185  else if (strcmp(str, "fragment") == 0)
186  return IPPROTO_FRAGMENT;
187  else if (strcmp(str, "dstopts") == 0)
188  return IPPROTO_DSTOPTS;
189  else if (strcmp(str, "mh") == 0)
190  return IPPROTO_MH;
191 
192  return -1;
193 }
194 
195 static int
196 nft_rule_expr_exthdr_json_parse(struct nft_rule_expr *e, json_t *root,
197  struct nft_parse_err *err)
198 {
199 #ifdef JSON_PARSING
200  const char *exthdr_type;
201  uint32_t uval32;
202  int type;
203 
204  if (nft_jansson_parse_reg(root, "dreg", NFT_TYPE_U32, &uval32,
205  err) == 0)
206  nft_rule_expr_set_u32(e, NFT_EXPR_EXTHDR_DREG, uval32);
207 
208  exthdr_type = nft_jansson_parse_str(root, "exthdr_type", err);
209  if (exthdr_type != NULL) {
210  type = str2exthdr_type(exthdr_type);
211  if (type < 0)
212  return -1;
213  nft_rule_expr_set_u32(e, NFT_EXPR_EXTHDR_TYPE, type);
214  }
215 
216  if (nft_jansson_parse_val(root, "offset", NFT_TYPE_U32, &uval32,
217  err) == 0)
218  nft_rule_expr_set_u32(e, NFT_EXPR_EXTHDR_OFFSET, uval32);
219 
220  if (nft_jansson_parse_val(root, "len", NFT_TYPE_U32, &uval32, err) == 0)
221  nft_rule_expr_set_u32(e, NFT_EXPR_EXTHDR_LEN, uval32);
222 
223  return 0;
224 #else
225  errno = EOPNOTSUPP;
226  return -1;
227 #endif
228 }
229 
230 static int
231 nft_rule_expr_exthdr_xml_parse(struct nft_rule_expr *e, mxml_node_t *tree,
232  struct nft_parse_err *err)
233 {
234 #ifdef XML_PARSING
235  const char *exthdr_type;
236  int type;
237  uint32_t dreg, len, offset;
238 
239  if (nft_mxml_reg_parse(tree, "dreg", &dreg, MXML_DESCEND_FIRST,
240  NFT_XML_MAND, err) == 0)
241  nft_rule_expr_set_u32(e, NFT_EXPR_EXTHDR_DREG, dreg);
242 
243  exthdr_type = nft_mxml_str_parse(tree, "exthdr_type",
244  MXML_DESCEND_FIRST, NFT_XML_MAND, err);
245  if (exthdr_type != NULL) {
246  type = str2exthdr_type(exthdr_type);
247  if (type < 0)
248  return -1;
249  nft_rule_expr_set_u8(e, NFT_EXPR_EXTHDR_TYPE, type);
250  }
251 
252  /* Get and set <offset> */
253  if (nft_mxml_num_parse(tree, "offset", MXML_DESCEND_FIRST, BASE_DEC,
254  &offset, NFT_TYPE_U32, NFT_XML_MAND, err) == 0)
255  nft_rule_expr_set_u32(e, NFT_EXPR_EXTHDR_OFFSET, offset);
256 
257  /* Get and set <len> */
258  if (nft_mxml_num_parse(tree, "len", MXML_DESCEND_FIRST, BASE_DEC,
259  &len, NFT_TYPE_U32, NFT_XML_MAND, err) == 0)
260  nft_rule_expr_set_u32(e, NFT_EXPR_EXTHDR_LEN, len);
261 
262  return 0;
263 #else
264  errno = EOPNOTSUPP;
265  return -1;
266 #endif
267 }
268 
269 static int nft_rule_expr_exthdr_snprintf_json(char *buf, size_t len,
270  struct nft_rule_expr *e)
271 {
272  struct nft_expr_exthdr *exthdr = nft_expr_data(e);
273  int ret, size = len, offset = 0;
274 
275  if (e->flags & (1 << NFT_EXPR_EXTHDR_DREG)) {
276  ret = snprintf(buf, len, "\"dreg\":%u,", exthdr->dreg);
277  SNPRINTF_BUFFER_SIZE(ret, size, len, offset);
278  }
279  if (e->flags & (1 << NFT_EXPR_EXTHDR_TYPE)) {
280  ret = snprintf(buf + offset, len, "\"exthdr_type\":\"%s\",",
281  exthdr_type2str(exthdr->type));
282  SNPRINTF_BUFFER_SIZE(ret, size, len, offset);
283  }
284  if (e->flags & (1 << NFT_EXPR_EXTHDR_OFFSET)) {
285  ret = snprintf(buf + offset, len, "\"offset\":%u,",
286  exthdr->offset);
287  SNPRINTF_BUFFER_SIZE(ret, size, len, offset);
288  }
289  if (e->flags & (1 << NFT_EXPR_EXTHDR_LEN)) {
290  ret = snprintf(buf + offset, len, "\"len\":%u,",
291  exthdr->len);
292  SNPRINTF_BUFFER_SIZE(ret, size, len, offset);
293  }
294  /* Remove the last comma characther */
295  if (offset > 0)
296  offset--;
297 
298  return offset;
299 }
300 
301 static int nft_rule_expr_exthdr_snprintf_xml(char *buf, size_t len,
302  struct nft_rule_expr *e)
303 {
304  struct nft_expr_exthdr *exthdr = nft_expr_data(e);
305  int ret, size = len, offset = 0;
306 
307  if (e->flags & (1 << NFT_EXPR_EXTHDR_DREG)) {
308  ret = snprintf(buf, len, "<dreg>%u</dreg>", exthdr->dreg);
309  SNPRINTF_BUFFER_SIZE(ret, size, len, offset);
310  }
311  if (e->flags & (1 << NFT_EXPR_EXTHDR_TYPE)) {
312  ret = snprintf(buf + offset, len,
313  "<exthdr_type>%s</exthdr_type>",
314  exthdr_type2str(exthdr->type));
315  SNPRINTF_BUFFER_SIZE(ret, size, len, offset);
316  }
317  if (e->flags & (1 << NFT_EXPR_EXTHDR_OFFSET)) {
318  ret = snprintf(buf + offset, len, "<offset>%u</offset>",
319  exthdr->offset);
320  SNPRINTF_BUFFER_SIZE(ret, size, len, offset);
321  }
322  if (e->flags & (1 << NFT_EXPR_EXTHDR_LEN)) {
323  ret = snprintf(buf + offset, len, "<len>%u</len>", exthdr->len);
324  SNPRINTF_BUFFER_SIZE(ret, size, len, offset);
325  }
326 
327  return offset;
328 }
329 
330 static int nft_rule_expr_exthdr_snprintf_default(char *buf, size_t len,
331  struct nft_rule_expr *e)
332 {
333  struct nft_expr_exthdr *exthdr = nft_expr_data(e);
334 
335  return snprintf(buf, len, "load %ub @ %u + %u => reg %u ",
336  exthdr->len, exthdr->type, exthdr->offset,
337  exthdr->dreg);
338 }
339 
340 static int
341 nft_rule_expr_exthdr_snprintf(char *buf, size_t len, uint32_t type,
342  uint32_t flags, struct nft_rule_expr *e)
343 {
344  switch(type) {
345  case NFT_OUTPUT_DEFAULT:
346  return nft_rule_expr_exthdr_snprintf_default(buf, len, e);
347  case NFT_OUTPUT_XML:
348  return nft_rule_expr_exthdr_snprintf_xml(buf, len, e);
349  case NFT_OUTPUT_JSON:
350  return nft_rule_expr_exthdr_snprintf_json(buf, len, e);
351  default:
352  break;
353  }
354  return -1;
355 }
356 
357 struct expr_ops expr_ops_exthdr = {
358  .name = "exthdr",
359  .alloc_len = sizeof(struct nft_expr_exthdr),
360  .max_attr = NFTA_EXTHDR_MAX,
361  .set = nft_rule_expr_exthdr_set,
362  .get = nft_rule_expr_exthdr_get,
363  .parse = nft_rule_expr_exthdr_parse,
364  .build = nft_rule_expr_exthdr_build,
365  .snprintf = nft_rule_expr_exthdr_snprintf,
366  .xml_parse = nft_rule_expr_exthdr_xml_parse,
367  .json_parse = nft_rule_expr_exthdr_json_parse,
368 };
369 
370 static void __init expr_exthdr_init(void)
371 {
372  nft_expr_ops_register(&expr_ops_exthdr);
373 }