libnftnl  1.0.5
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 #ifndef IPPROTO_MH
28 #define IPPROTO_MH 135
29 #endif
30 
32  enum nft_registers dreg;
33  uint32_t offset;
34  uint32_t len;
35  uint8_t type;
36 };
37 
38 static int
39 nftnl_expr_exthdr_set(struct nftnl_expr *e, uint16_t type,
40  const void *data, uint32_t data_len)
41 {
42  struct nftnl_expr_exthdr *exthdr = nftnl_expr_data(e);
43 
44  switch(type) {
45  case NFTNL_EXPR_EXTHDR_DREG:
46  exthdr->dreg = *((uint32_t *)data);
47  break;
48  case NFTNL_EXPR_EXTHDR_TYPE:
49  exthdr->type = *((uint8_t *)data);
50  break;
51  case NFTNL_EXPR_EXTHDR_OFFSET:
52  exthdr->offset = *((uint32_t *)data);
53  break;
54  case NFTNL_EXPR_EXTHDR_LEN:
55  exthdr->len = *((uint32_t *)data);
56  break;
57  default:
58  return -1;
59  }
60  return 0;
61 }
62 
63 static const void *
64 nftnl_expr_exthdr_get(const struct nftnl_expr *e, uint16_t type,
65  uint32_t *data_len)
66 {
67  struct nftnl_expr_exthdr *exthdr = nftnl_expr_data(e);
68 
69  switch(type) {
70  case NFTNL_EXPR_EXTHDR_DREG:
71  *data_len = sizeof(exthdr->dreg);
72  return &exthdr->dreg;
73  case NFTNL_EXPR_EXTHDR_TYPE:
74  *data_len = sizeof(exthdr->type);
75  return &exthdr->type;
76  case NFTNL_EXPR_EXTHDR_OFFSET:
77  *data_len = sizeof(exthdr->offset);
78  return &exthdr->offset;
79  case NFTNL_EXPR_EXTHDR_LEN:
80  *data_len = sizeof(exthdr->len);
81  return &exthdr->len;
82  }
83  return NULL;
84 }
85 
86 static int nftnl_expr_exthdr_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_EXTHDR_MAX) < 0)
92  return MNL_CB_OK;
93 
94  switch(type) {
95  case NFTA_EXTHDR_TYPE:
96  if (mnl_attr_validate(attr, MNL_TYPE_U8) < 0)
97  abi_breakage();
98  break;
99  case NFTA_EXTHDR_DREG:
100  case NFTA_EXTHDR_OFFSET:
101  case NFTA_EXTHDR_LEN:
102  if (mnl_attr_validate(attr, MNL_TYPE_U32) < 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_exthdr_build(struct nlmsghdr *nlh, struct nftnl_expr *e)
113 {
114  struct nftnl_expr_exthdr *exthdr = nftnl_expr_data(e);
115 
116  if (e->flags & (1 << NFTNL_EXPR_EXTHDR_DREG))
117  mnl_attr_put_u32(nlh, NFTA_EXTHDR_DREG, htonl(exthdr->dreg));
118  if (e->flags & (1 << NFTNL_EXPR_EXTHDR_TYPE))
119  mnl_attr_put_u8(nlh, NFTA_EXTHDR_TYPE, exthdr->type);
120  if (e->flags & (1 << NFTNL_EXPR_EXTHDR_OFFSET))
121  mnl_attr_put_u32(nlh, NFTA_EXTHDR_OFFSET, htonl(exthdr->offset));
122  if (e->flags & (1 << NFTNL_EXPR_EXTHDR_LEN))
123  mnl_attr_put_u32(nlh, NFTA_EXTHDR_LEN, htonl(exthdr->len));
124 }
125 
126 static int
127 nftnl_expr_exthdr_parse(struct nftnl_expr *e, struct nlattr *attr)
128 {
129  struct nftnl_expr_exthdr *exthdr = nftnl_expr_data(e);
130  struct nlattr *tb[NFTA_EXTHDR_MAX+1] = {};
131 
132  if (mnl_attr_parse_nested(attr, nftnl_expr_exthdr_cb, tb) < 0)
133  return -1;
134 
135  if (tb[NFTA_EXTHDR_DREG]) {
136  exthdr->dreg = ntohl(mnl_attr_get_u32(tb[NFTA_EXTHDR_DREG]));
137  e->flags |= (1 << NFTNL_EXPR_EXTHDR_DREG);
138  }
139  if (tb[NFTA_EXTHDR_TYPE]) {
140  exthdr->type = mnl_attr_get_u8(tb[NFTA_EXTHDR_TYPE]);
141  e->flags |= (1 << NFTNL_EXPR_EXTHDR_TYPE);
142  }
143  if (tb[NFTA_EXTHDR_OFFSET]) {
144  exthdr->offset = ntohl(mnl_attr_get_u32(tb[NFTA_EXTHDR_OFFSET]));
145  e->flags |= (1 << NFTNL_EXPR_EXTHDR_OFFSET);
146  }
147  if (tb[NFTA_EXTHDR_LEN]) {
148  exthdr->len = ntohl(mnl_attr_get_u32(tb[NFTA_EXTHDR_LEN]));
149  e->flags |= (1 << NFTNL_EXPR_EXTHDR_LEN);
150  }
151 
152  return 0;
153 }
154 
155 static const char *type2str(uint32_t type)
156 {
157  switch (type) {
158  case IPPROTO_HOPOPTS:
159  return "hopopts";
160  case IPPROTO_ROUTING:
161  return "routing";
162  case IPPROTO_FRAGMENT:
163  return "fragment";
164  case IPPROTO_DSTOPTS:
165  return "dstopts";
166  case IPPROTO_MH:
167  return "mh";
168  default:
169  return "unknown";
170  }
171 }
172 
173 static inline int str2exthdr_type(const char *str)
174 {
175  if (strcmp(str, "hopopts") == 0)
176  return IPPROTO_HOPOPTS;
177  else if (strcmp(str, "routing") == 0)
178  return IPPROTO_ROUTING;
179  else if (strcmp(str, "fragment") == 0)
180  return IPPROTO_FRAGMENT;
181  else if (strcmp(str, "dstopts") == 0)
182  return IPPROTO_DSTOPTS;
183  else if (strcmp(str, "mh") == 0)
184  return IPPROTO_MH;
185 
186  return -1;
187 }
188 
189 static int
190 nftnl_expr_exthdr_json_parse(struct nftnl_expr *e, json_t *root,
191  struct nftnl_parse_err *err)
192 {
193 #ifdef JSON_PARSING
194  const char *exthdr_type;
195  uint32_t uval32;
196  int type;
197 
198  if (nftnl_jansson_parse_reg(root, "dreg", NFTNL_TYPE_U32, &uval32,
199  err) == 0)
200  nftnl_expr_set_u32(e, NFTNL_EXPR_EXTHDR_DREG, uval32);
201 
202  exthdr_type = nftnl_jansson_parse_str(root, "exthdr_type", err);
203  if (exthdr_type != NULL) {
204  type = str2exthdr_type(exthdr_type);
205  if (type < 0)
206  return -1;
207  nftnl_expr_set_u32(e, NFTNL_EXPR_EXTHDR_TYPE, type);
208  }
209 
210  if (nftnl_jansson_parse_val(root, "offset", NFTNL_TYPE_U32, &uval32,
211  err) == 0)
212  nftnl_expr_set_u32(e, NFTNL_EXPR_EXTHDR_OFFSET, uval32);
213 
214  if (nftnl_jansson_parse_val(root, "len", NFTNL_TYPE_U32, &uval32, err) == 0)
215  nftnl_expr_set_u32(e, NFTNL_EXPR_EXTHDR_LEN, uval32);
216 
217  return 0;
218 #else
219  errno = EOPNOTSUPP;
220  return -1;
221 #endif
222 }
223 
224 static int
225 nftnl_expr_exthdr_xml_parse(struct nftnl_expr *e, mxml_node_t *tree,
226  struct nftnl_parse_err *err)
227 {
228 #ifdef XML_PARSING
229  const char *exthdr_type;
230  int type;
231  uint32_t dreg, len, offset;
232 
233  if (nftnl_mxml_reg_parse(tree, "dreg", &dreg, MXML_DESCEND_FIRST,
234  NFTNL_XML_MAND, err) == 0)
235  nftnl_expr_set_u32(e, NFTNL_EXPR_EXTHDR_DREG, dreg);
236 
237  exthdr_type = nftnl_mxml_str_parse(tree, "exthdr_type",
238  MXML_DESCEND_FIRST, NFTNL_XML_MAND, err);
239  if (exthdr_type != NULL) {
240  type = str2exthdr_type(exthdr_type);
241  if (type < 0)
242  return -1;
243  nftnl_expr_set_u8(e, NFTNL_EXPR_EXTHDR_TYPE, type);
244  }
245 
246  /* Get and set <offset> */
247  if (nftnl_mxml_num_parse(tree, "offset", MXML_DESCEND_FIRST, BASE_DEC,
248  &offset, NFTNL_TYPE_U32, NFTNL_XML_MAND, err) == 0)
249  nftnl_expr_set_u32(e, NFTNL_EXPR_EXTHDR_OFFSET, offset);
250 
251  /* Get and set <len> */
252  if (nftnl_mxml_num_parse(tree, "len", MXML_DESCEND_FIRST, BASE_DEC,
253  &len, NFTNL_TYPE_U32, NFTNL_XML_MAND, err) == 0)
254  nftnl_expr_set_u32(e, NFTNL_EXPR_EXTHDR_LEN, len);
255 
256  return 0;
257 #else
258  errno = EOPNOTSUPP;
259  return -1;
260 #endif
261 }
262 
263 static int nftnl_expr_exthdr_export(char *buf, size_t len,
264  struct nftnl_expr *e, int type)
265 {
266  struct nftnl_expr_exthdr *exthdr = nftnl_expr_data(e);
267  NFTNL_BUF_INIT(b, buf, len);
268 
269  if (e->flags & (1 << NFTNL_EXPR_EXTHDR_DREG))
270  nftnl_buf_u32(&b, type, exthdr->dreg, DREG);
271  if (e->flags & (1 << NFTNL_EXPR_EXTHDR_TYPE))
272  nftnl_buf_str(&b, type, type2str(exthdr->type), EXTHDR_TYPE);
273  if (e->flags & (1 << NFTNL_EXPR_EXTHDR_OFFSET))
274  nftnl_buf_u32(&b, type, exthdr->offset, OFFSET);
275  if (e->flags & (1 << NFTNL_EXPR_EXTHDR_LEN))
276  nftnl_buf_u32(&b, type, exthdr->len, LEN);
277 
278  return nftnl_buf_done(&b);
279 }
280 
281 static int nftnl_expr_exthdr_snprintf_default(char *buf, size_t len,
282  struct nftnl_expr *e)
283 {
284  struct nftnl_expr_exthdr *exthdr = nftnl_expr_data(e);
285 
286  return snprintf(buf, len, "load %ub @ %u + %u => reg %u ",
287  exthdr->len, exthdr->type, exthdr->offset,
288  exthdr->dreg);
289 }
290 
291 static int
292 nftnl_expr_exthdr_snprintf(char *buf, size_t len, uint32_t type,
293  uint32_t flags, struct nftnl_expr *e)
294 {
295  switch (type) {
296  case NFTNL_OUTPUT_DEFAULT:
297  return nftnl_expr_exthdr_snprintf_default(buf, len, e);
298  case NFTNL_OUTPUT_XML:
299  case NFTNL_OUTPUT_JSON:
300  return nftnl_expr_exthdr_export(buf, len, e, type);
301  default:
302  break;
303  }
304  return -1;
305 }
306 
307 struct expr_ops expr_ops_exthdr = {
308  .name = "exthdr",
309  .alloc_len = sizeof(struct nftnl_expr_exthdr),
310  .max_attr = NFTA_EXTHDR_MAX,
311  .set = nftnl_expr_exthdr_set,
312  .get = nftnl_expr_exthdr_get,
313  .parse = nftnl_expr_exthdr_parse,
314  .build = nftnl_expr_exthdr_build,
315  .snprintf = nftnl_expr_exthdr_snprintf,
316  .xml_parse = nftnl_expr_exthdr_xml_parse,
317  .json_parse = nftnl_expr_exthdr_json_parse,
318 };