libnftnl  1.2.9
exthdr.c
1 /* SPDX-License-Identifier: GPL-2.0-or-later */
2 /*
3  * (C) 2012-2013 by Pablo Neira Ayuso <pablo@netfilter.org>
4  *
5  * This code has been sponsored by Sophos Astaro <http://www.sophos.com>
6  */
7 
8 #include "internal.h"
9 
10 #include <stdio.h>
11 #include <string.h>
12 #include <stdint.h>
13 #include <limits.h>
14 #include <arpa/inet.h>
15 #include <errno.h>
16 #include <libmnl/libmnl.h>
17 
18 #include <linux/netfilter/nf_tables.h>
19 
20 #include <libnftnl/expr.h>
21 #include <libnftnl/rule.h>
22 
23 #ifndef IPPROTO_MH
24 #define IPPROTO_MH 135
25 #endif
26 
28  enum nft_registers dreg;
29  enum nft_registers sreg;
30  uint32_t offset;
31  uint32_t len;
32  uint8_t type;
33  uint32_t op;
34  uint32_t flags;
35 };
36 
37 static int
38 nftnl_expr_exthdr_set(struct nftnl_expr *e, uint16_t type,
39  const void *data, uint32_t data_len)
40 {
41  struct nftnl_expr_exthdr *exthdr = nftnl_expr_data(e);
42 
43  switch(type) {
44  case NFTNL_EXPR_EXTHDR_DREG:
45  memcpy(&exthdr->dreg, data, data_len);
46  break;
47  case NFTNL_EXPR_EXTHDR_TYPE:
48  memcpy(&exthdr->type, data, data_len);
49  break;
50  case NFTNL_EXPR_EXTHDR_OFFSET:
51  memcpy(&exthdr->offset, data, data_len);
52  break;
53  case NFTNL_EXPR_EXTHDR_LEN:
54  memcpy(&exthdr->len, data, data_len);
55  break;
56  case NFTNL_EXPR_EXTHDR_OP:
57  memcpy(&exthdr->op, data, data_len);
58  break;
59  case NFTNL_EXPR_EXTHDR_FLAGS:
60  memcpy(&exthdr->flags, data, data_len);
61  break;
62  case NFTNL_EXPR_EXTHDR_SREG:
63  memcpy(&exthdr->sreg, data, data_len);
64  break;
65  }
66  return 0;
67 }
68 
69 static const void *
70 nftnl_expr_exthdr_get(const struct nftnl_expr *e, uint16_t type,
71  uint32_t *data_len)
72 {
73  struct nftnl_expr_exthdr *exthdr = nftnl_expr_data(e);
74 
75  switch(type) {
76  case NFTNL_EXPR_EXTHDR_DREG:
77  *data_len = sizeof(exthdr->dreg);
78  return &exthdr->dreg;
79  case NFTNL_EXPR_EXTHDR_TYPE:
80  *data_len = sizeof(exthdr->type);
81  return &exthdr->type;
82  case NFTNL_EXPR_EXTHDR_OFFSET:
83  *data_len = sizeof(exthdr->offset);
84  return &exthdr->offset;
85  case NFTNL_EXPR_EXTHDR_LEN:
86  *data_len = sizeof(exthdr->len);
87  return &exthdr->len;
88  case NFTNL_EXPR_EXTHDR_OP:
89  *data_len = sizeof(exthdr->op);
90  return &exthdr->op;
91  case NFTNL_EXPR_EXTHDR_FLAGS:
92  *data_len = sizeof(exthdr->flags);
93  return &exthdr->flags;
94  case NFTNL_EXPR_EXTHDR_SREG:
95  *data_len = sizeof(exthdr->sreg);
96  return &exthdr->sreg;
97  }
98  return NULL;
99 }
100 
101 static int nftnl_expr_exthdr_cb(const struct nlattr *attr, void *data)
102 {
103  const struct nlattr **tb = data;
104  int type = mnl_attr_get_type(attr);
105 
106  if (mnl_attr_type_valid(attr, NFTA_EXTHDR_MAX) < 0)
107  return MNL_CB_OK;
108 
109  switch(type) {
110  case NFTA_EXTHDR_TYPE:
111  if (mnl_attr_validate(attr, MNL_TYPE_U8) < 0)
112  abi_breakage();
113  break;
114  case NFTA_EXTHDR_DREG:
115  case NFTA_EXTHDR_SREG:
116  case NFTA_EXTHDR_OFFSET:
117  case NFTA_EXTHDR_LEN:
118  case NFTA_EXTHDR_OP:
119  case NFTA_EXTHDR_FLAGS:
120  if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0)
121  abi_breakage();
122  break;
123  }
124 
125  tb[type] = attr;
126  return MNL_CB_OK;
127 }
128 
129 static void
130 nftnl_expr_exthdr_build(struct nlmsghdr *nlh, const struct nftnl_expr *e)
131 {
132  struct nftnl_expr_exthdr *exthdr = nftnl_expr_data(e);
133 
134  if (e->flags & (1 << NFTNL_EXPR_EXTHDR_DREG))
135  mnl_attr_put_u32(nlh, NFTA_EXTHDR_DREG, htonl(exthdr->dreg));
136  if (e->flags & (1 << NFTNL_EXPR_EXTHDR_SREG))
137  mnl_attr_put_u32(nlh, NFTA_EXTHDR_SREG, htonl(exthdr->sreg));
138  if (e->flags & (1 << NFTNL_EXPR_EXTHDR_TYPE))
139  mnl_attr_put_u8(nlh, NFTA_EXTHDR_TYPE, exthdr->type);
140  if (e->flags & (1 << NFTNL_EXPR_EXTHDR_OFFSET))
141  mnl_attr_put_u32(nlh, NFTA_EXTHDR_OFFSET, htonl(exthdr->offset));
142  if (e->flags & (1 << NFTNL_EXPR_EXTHDR_LEN))
143  mnl_attr_put_u32(nlh, NFTA_EXTHDR_LEN, htonl(exthdr->len));
144  if (e->flags & (1 << NFTNL_EXPR_EXTHDR_OP))
145  mnl_attr_put_u32(nlh, NFTA_EXTHDR_OP, htonl(exthdr->op));
146  if (e->flags & (1 << NFTNL_EXPR_EXTHDR_FLAGS))
147  mnl_attr_put_u32(nlh, NFTA_EXTHDR_FLAGS, htonl(exthdr->flags));
148 }
149 
150 static int
151 nftnl_expr_exthdr_parse(struct nftnl_expr *e, struct nlattr *attr)
152 {
153  struct nftnl_expr_exthdr *exthdr = nftnl_expr_data(e);
154  struct nlattr *tb[NFTA_EXTHDR_MAX+1] = {};
155 
156  if (mnl_attr_parse_nested(attr, nftnl_expr_exthdr_cb, tb) < 0)
157  return -1;
158 
159  if (tb[NFTA_EXTHDR_DREG]) {
160  exthdr->dreg = ntohl(mnl_attr_get_u32(tb[NFTA_EXTHDR_DREG]));
161  e->flags |= (1 << NFTNL_EXPR_EXTHDR_DREG);
162  }
163  if (tb[NFTA_EXTHDR_SREG]) {
164  exthdr->sreg = ntohl(mnl_attr_get_u32(tb[NFTA_EXTHDR_SREG]));
165  e->flags |= (1 << NFTNL_EXPR_EXTHDR_SREG);
166  }
167  if (tb[NFTA_EXTHDR_TYPE]) {
168  exthdr->type = mnl_attr_get_u8(tb[NFTA_EXTHDR_TYPE]);
169  e->flags |= (1 << NFTNL_EXPR_EXTHDR_TYPE);
170  }
171  if (tb[NFTA_EXTHDR_OFFSET]) {
172  exthdr->offset = ntohl(mnl_attr_get_u32(tb[NFTA_EXTHDR_OFFSET]));
173  e->flags |= (1 << NFTNL_EXPR_EXTHDR_OFFSET);
174  }
175  if (tb[NFTA_EXTHDR_LEN]) {
176  exthdr->len = ntohl(mnl_attr_get_u32(tb[NFTA_EXTHDR_LEN]));
177  e->flags |= (1 << NFTNL_EXPR_EXTHDR_LEN);
178  }
179  if (tb[NFTA_EXTHDR_OP]) {
180  exthdr->op = ntohl(mnl_attr_get_u32(tb[NFTA_EXTHDR_OP]));
181  e->flags |= (1 << NFTNL_EXPR_EXTHDR_OP);
182  }
183  if (tb[NFTA_EXTHDR_FLAGS]) {
184  exthdr->flags = ntohl(mnl_attr_get_u32(tb[NFTA_EXTHDR_FLAGS]));
185  e->flags |= (1 << NFTNL_EXPR_EXTHDR_FLAGS);
186  }
187 
188  return 0;
189 }
190 
191 static const char *op2str(uint8_t op)
192 {
193  switch (op) {
194  case NFT_EXTHDR_OP_TCPOPT:
195  return " tcpopt";
196  case NFT_EXTHDR_OP_IPV6:
197  return " ipv6";
198  case NFT_EXTHDR_OP_IPV4:
199  return " ipv4";
200  default:
201  return "";
202  }
203 }
204 
205 static int
206 nftnl_expr_exthdr_snprintf(char *buf, size_t len,
207  uint32_t flags, const struct nftnl_expr *e)
208 {
209  struct nftnl_expr_exthdr *exthdr = nftnl_expr_data(e);
210 
211  if (e->flags & (1 << NFTNL_EXPR_EXTHDR_DREG))
212  return snprintf(buf, len, "load%s %ub @ %u + %u%s => reg %u ",
213  op2str(exthdr->op), exthdr->len, exthdr->type,
214  exthdr->offset,
215  exthdr->flags & NFT_EXTHDR_F_PRESENT ? " present" : "",
216  exthdr->dreg);
217  else if (e->flags & (1 << NFTNL_EXPR_EXTHDR_SREG))
218  return snprintf(buf, len, "write%s reg %u => %ub @ %u + %u ",
219  op2str(exthdr->op), exthdr->sreg, exthdr->len, exthdr->type,
220  exthdr->offset);
221  else if (exthdr->op == NFT_EXTHDR_OP_TCPOPT && exthdr->len == 0)
222  return snprintf(buf, len, "reset tcpopt %u ", exthdr->type);
223  else
224  return snprintf(buf, len, "op %u len %u type %u offset %u ",
225  exthdr->op, exthdr->len, exthdr->type, exthdr->offset);
226 
227 }
228 
229 static struct attr_policy exthdr_attr_policy[__NFTNL_EXPR_EXTHDR_MAX] = {
230  [NFTNL_EXPR_EXTHDR_DREG] = { .maxlen = sizeof(uint32_t) },
231  [NFTNL_EXPR_EXTHDR_TYPE] = { .maxlen = sizeof(uint8_t) },
232  [NFTNL_EXPR_EXTHDR_OFFSET] = { .maxlen = sizeof(uint32_t) },
233  [NFTNL_EXPR_EXTHDR_LEN] = { .maxlen = sizeof(uint32_t) },
234  [NFTNL_EXPR_EXTHDR_FLAGS] = { .maxlen = sizeof(uint32_t) },
235  [NFTNL_EXPR_EXTHDR_OP] = { .maxlen = sizeof(uint32_t) },
236  [NFTNL_EXPR_EXTHDR_SREG] = { .maxlen = sizeof(uint32_t) },
237 };
238 
239 struct expr_ops expr_ops_exthdr = {
240  .name = "exthdr",
241  .alloc_len = sizeof(struct nftnl_expr_exthdr),
242  .nftnl_max_attr = __NFTNL_EXPR_EXTHDR_MAX - 1,
243  .attr_policy = exthdr_attr_policy,
244  .set = nftnl_expr_exthdr_set,
245  .get = nftnl_expr_exthdr_get,
246  .parse = nftnl_expr_exthdr_parse,
247  .build = nftnl_expr_exthdr_build,
248  .output = nftnl_expr_exthdr_snprintf,
249 };