libnftnl  1.2.8
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  enum nft_registers sreg;
34  uint32_t offset;
35  uint32_t len;
36  uint8_t type;
37  uint32_t op;
38  uint32_t flags;
39 };
40 
41 static int
42 nftnl_expr_exthdr_set(struct nftnl_expr *e, uint16_t type,
43  const void *data, uint32_t data_len)
44 {
45  struct nftnl_expr_exthdr *exthdr = nftnl_expr_data(e);
46 
47  switch(type) {
48  case NFTNL_EXPR_EXTHDR_DREG:
49  memcpy(&exthdr->dreg, data, data_len);
50  break;
51  case NFTNL_EXPR_EXTHDR_TYPE:
52  memcpy(&exthdr->type, data, data_len);
53  break;
54  case NFTNL_EXPR_EXTHDR_OFFSET:
55  memcpy(&exthdr->offset, data, data_len);
56  break;
57  case NFTNL_EXPR_EXTHDR_LEN:
58  memcpy(&exthdr->len, data, data_len);
59  break;
60  case NFTNL_EXPR_EXTHDR_OP:
61  memcpy(&exthdr->op, data, data_len);
62  break;
63  case NFTNL_EXPR_EXTHDR_FLAGS:
64  memcpy(&exthdr->flags, data, data_len);
65  break;
66  case NFTNL_EXPR_EXTHDR_SREG:
67  memcpy(&exthdr->sreg, data, data_len);
68  break;
69  }
70  return 0;
71 }
72 
73 static const void *
74 nftnl_expr_exthdr_get(const struct nftnl_expr *e, uint16_t type,
75  uint32_t *data_len)
76 {
77  struct nftnl_expr_exthdr *exthdr = nftnl_expr_data(e);
78 
79  switch(type) {
80  case NFTNL_EXPR_EXTHDR_DREG:
81  *data_len = sizeof(exthdr->dreg);
82  return &exthdr->dreg;
83  case NFTNL_EXPR_EXTHDR_TYPE:
84  *data_len = sizeof(exthdr->type);
85  return &exthdr->type;
86  case NFTNL_EXPR_EXTHDR_OFFSET:
87  *data_len = sizeof(exthdr->offset);
88  return &exthdr->offset;
89  case NFTNL_EXPR_EXTHDR_LEN:
90  *data_len = sizeof(exthdr->len);
91  return &exthdr->len;
92  case NFTNL_EXPR_EXTHDR_OP:
93  *data_len = sizeof(exthdr->op);
94  return &exthdr->op;
95  case NFTNL_EXPR_EXTHDR_FLAGS:
96  *data_len = sizeof(exthdr->flags);
97  return &exthdr->flags;
98  case NFTNL_EXPR_EXTHDR_SREG:
99  *data_len = sizeof(exthdr->sreg);
100  return &exthdr->sreg;
101  }
102  return NULL;
103 }
104 
105 static int nftnl_expr_exthdr_cb(const struct nlattr *attr, void *data)
106 {
107  const struct nlattr **tb = data;
108  int type = mnl_attr_get_type(attr);
109 
110  if (mnl_attr_type_valid(attr, NFTA_EXTHDR_MAX) < 0)
111  return MNL_CB_OK;
112 
113  switch(type) {
114  case NFTA_EXTHDR_TYPE:
115  if (mnl_attr_validate(attr, MNL_TYPE_U8) < 0)
116  abi_breakage();
117  break;
118  case NFTA_EXTHDR_DREG:
119  case NFTA_EXTHDR_SREG:
120  case NFTA_EXTHDR_OFFSET:
121  case NFTA_EXTHDR_LEN:
122  case NFTA_EXTHDR_OP:
123  case NFTA_EXTHDR_FLAGS:
124  if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0)
125  abi_breakage();
126  break;
127  }
128 
129  tb[type] = attr;
130  return MNL_CB_OK;
131 }
132 
133 static void
134 nftnl_expr_exthdr_build(struct nlmsghdr *nlh, const struct nftnl_expr *e)
135 {
136  struct nftnl_expr_exthdr *exthdr = nftnl_expr_data(e);
137 
138  if (e->flags & (1 << NFTNL_EXPR_EXTHDR_DREG))
139  mnl_attr_put_u32(nlh, NFTA_EXTHDR_DREG, htonl(exthdr->dreg));
140  if (e->flags & (1 << NFTNL_EXPR_EXTHDR_SREG))
141  mnl_attr_put_u32(nlh, NFTA_EXTHDR_SREG, htonl(exthdr->sreg));
142  if (e->flags & (1 << NFTNL_EXPR_EXTHDR_TYPE))
143  mnl_attr_put_u8(nlh, NFTA_EXTHDR_TYPE, exthdr->type);
144  if (e->flags & (1 << NFTNL_EXPR_EXTHDR_OFFSET))
145  mnl_attr_put_u32(nlh, NFTA_EXTHDR_OFFSET, htonl(exthdr->offset));
146  if (e->flags & (1 << NFTNL_EXPR_EXTHDR_LEN))
147  mnl_attr_put_u32(nlh, NFTA_EXTHDR_LEN, htonl(exthdr->len));
148  if (e->flags & (1 << NFTNL_EXPR_EXTHDR_OP))
149  mnl_attr_put_u32(nlh, NFTA_EXTHDR_OP, htonl(exthdr->op));
150  if (e->flags & (1 << NFTNL_EXPR_EXTHDR_FLAGS))
151  mnl_attr_put_u32(nlh, NFTA_EXTHDR_FLAGS, htonl(exthdr->flags));
152 }
153 
154 static int
155 nftnl_expr_exthdr_parse(struct nftnl_expr *e, struct nlattr *attr)
156 {
157  struct nftnl_expr_exthdr *exthdr = nftnl_expr_data(e);
158  struct nlattr *tb[NFTA_EXTHDR_MAX+1] = {};
159 
160  if (mnl_attr_parse_nested(attr, nftnl_expr_exthdr_cb, tb) < 0)
161  return -1;
162 
163  if (tb[NFTA_EXTHDR_DREG]) {
164  exthdr->dreg = ntohl(mnl_attr_get_u32(tb[NFTA_EXTHDR_DREG]));
165  e->flags |= (1 << NFTNL_EXPR_EXTHDR_DREG);
166  }
167  if (tb[NFTA_EXTHDR_SREG]) {
168  exthdr->sreg = ntohl(mnl_attr_get_u32(tb[NFTA_EXTHDR_SREG]));
169  e->flags |= (1 << NFTNL_EXPR_EXTHDR_SREG);
170  }
171  if (tb[NFTA_EXTHDR_TYPE]) {
172  exthdr->type = mnl_attr_get_u8(tb[NFTA_EXTHDR_TYPE]);
173  e->flags |= (1 << NFTNL_EXPR_EXTHDR_TYPE);
174  }
175  if (tb[NFTA_EXTHDR_OFFSET]) {
176  exthdr->offset = ntohl(mnl_attr_get_u32(tb[NFTA_EXTHDR_OFFSET]));
177  e->flags |= (1 << NFTNL_EXPR_EXTHDR_OFFSET);
178  }
179  if (tb[NFTA_EXTHDR_LEN]) {
180  exthdr->len = ntohl(mnl_attr_get_u32(tb[NFTA_EXTHDR_LEN]));
181  e->flags |= (1 << NFTNL_EXPR_EXTHDR_LEN);
182  }
183  if (tb[NFTA_EXTHDR_OP]) {
184  exthdr->op = ntohl(mnl_attr_get_u32(tb[NFTA_EXTHDR_OP]));
185  e->flags |= (1 << NFTNL_EXPR_EXTHDR_OP);
186  }
187  if (tb[NFTA_EXTHDR_FLAGS]) {
188  exthdr->flags = ntohl(mnl_attr_get_u32(tb[NFTA_EXTHDR_FLAGS]));
189  e->flags |= (1 << NFTNL_EXPR_EXTHDR_FLAGS);
190  }
191 
192  return 0;
193 }
194 
195 static const char *op2str(uint8_t op)
196 {
197  switch (op) {
198  case NFT_EXTHDR_OP_TCPOPT:
199  return " tcpopt";
200  case NFT_EXTHDR_OP_IPV6:
201  return " ipv6";
202  case NFT_EXTHDR_OP_IPV4:
203  return " ipv4";
204  default:
205  return "";
206  }
207 }
208 
209 static inline int str2exthdr_op(const char* str)
210 {
211  if (!strcmp(str, "tcpopt"))
212  return NFT_EXTHDR_OP_TCPOPT;
213  if (!strcmp(str, "ipv4"))
214  return NFT_EXTHDR_OP_IPV4;
215 
216  /* if str == "ipv6" or anything else */
217  return NFT_EXTHDR_OP_IPV6;
218 }
219 
220 static inline int str2exthdr_type(const char *str)
221 {
222  if (strcmp(str, "hopopts") == 0)
223  return IPPROTO_HOPOPTS;
224  else if (strcmp(str, "routing") == 0)
225  return IPPROTO_ROUTING;
226  else if (strcmp(str, "fragment") == 0)
227  return IPPROTO_FRAGMENT;
228  else if (strcmp(str, "dstopts") == 0)
229  return IPPROTO_DSTOPTS;
230  else if (strcmp(str, "mh") == 0)
231  return IPPROTO_MH;
232 
233  return -1;
234 }
235 
236 static int
237 nftnl_expr_exthdr_snprintf(char *buf, size_t len,
238  uint32_t flags, const struct nftnl_expr *e)
239 {
240  struct nftnl_expr_exthdr *exthdr = nftnl_expr_data(e);
241 
242  if (e->flags & (1 << NFTNL_EXPR_EXTHDR_DREG))
243  return snprintf(buf, len, "load%s %ub @ %u + %u%s => reg %u ",
244  op2str(exthdr->op), exthdr->len, exthdr->type,
245  exthdr->offset,
246  exthdr->flags & NFT_EXTHDR_F_PRESENT ? " present" : "",
247  exthdr->dreg);
248  else if (e->flags & (1 << NFTNL_EXPR_EXTHDR_SREG))
249  return snprintf(buf, len, "write%s reg %u => %ub @ %u + %u ",
250  op2str(exthdr->op), exthdr->sreg, exthdr->len, exthdr->type,
251  exthdr->offset);
252  else if (exthdr->op == NFT_EXTHDR_OP_TCPOPT && exthdr->len == 0)
253  return snprintf(buf, len, "reset tcpopt %u ", exthdr->type);
254  else
255  return snprintf(buf, len, "op %u len %u type %u offset %u ",
256  exthdr->op, exthdr->len, exthdr->type, exthdr->offset);
257 
258 }
259 
260 static struct attr_policy exthdr_attr_policy[__NFTNL_EXPR_EXTHDR_MAX] = {
261  [NFTNL_EXPR_EXTHDR_DREG] = { .maxlen = sizeof(uint32_t) },
262  [NFTNL_EXPR_EXTHDR_TYPE] = { .maxlen = sizeof(uint8_t) },
263  [NFTNL_EXPR_EXTHDR_OFFSET] = { .maxlen = sizeof(uint32_t) },
264  [NFTNL_EXPR_EXTHDR_LEN] = { .maxlen = sizeof(uint32_t) },
265  [NFTNL_EXPR_EXTHDR_FLAGS] = { .maxlen = sizeof(uint32_t) },
266  [NFTNL_EXPR_EXTHDR_OP] = { .maxlen = sizeof(uint32_t) },
267  [NFTNL_EXPR_EXTHDR_SREG] = { .maxlen = sizeof(uint32_t) },
268 };
269 
270 struct expr_ops expr_ops_exthdr = {
271  .name = "exthdr",
272  .alloc_len = sizeof(struct nftnl_expr_exthdr),
273  .nftnl_max_attr = __NFTNL_EXPR_EXTHDR_MAX - 1,
274  .attr_policy = exthdr_attr_policy,
275  .set = nftnl_expr_exthdr_set,
276  .get = nftnl_expr_exthdr_get,
277  .parse = nftnl_expr_exthdr_parse,
278  .build = nftnl_expr_exthdr_build,
279  .output = nftnl_expr_exthdr_snprintf,
280 };