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
41static int
42nftnl_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
73static const void *
74nftnl_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
105static 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
133static void
134nftnl_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
154static int
155nftnl_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
195static 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
209static 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
220static 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
236static int
237nftnl_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
260static 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
270struct 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};