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
37static int
38nftnl_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
69static const void *
70nftnl_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
101static 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
129static void
130nftnl_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
150static int
151nftnl_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
191static 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
205static int
206nftnl_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
229static 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
239struct 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};