libnftnl 1.3.2
data_reg.c
1/* SPDX-License-Identifier: GPL-2.0-or-later */
2/*
3 * (C) 2012 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 <stdio.h>
9#include <stdint.h>
10#include <string.h>
11#include <limits.h>
12#include <arpa/inet.h>
13#include <errno.h>
14#include <netinet/in.h>
15
16#include <libmnl/libmnl.h>
17#include <linux/netfilter.h>
18#include <linux/netfilter/nf_tables.h>
19#include <libnftnl/expr.h>
20#include <libnftnl/rule.h>
21#include "internal.h"
22
23static bool big_endian_host(void)
24{
25 uint16_t v = 1;
26
27 return v == htons(v);
28}
29
30static int __reg_value_snprintf(char *buf, size_t remain,
31 uint8_t *data, size_t datalen,
32 bool reverse, const char *pfx)
33{
34 int offset = 0, ret, i, idx;
35 const char *sep = "";
36
37 for (i = 0; i < datalen; i++) {
38 if ((i % 4) == 0) {
39 ret = snprintf(buf + offset, remain, "%s%s", sep, pfx);
40 SNPRINTF_BUFFER_SIZE(ret, remain, offset);
41 sep = " ";
42 }
43 if (reverse)
44 idx = datalen - i - 1;
45 else
46 idx = i;
47
48 ret = snprintf(buf + offset, remain, "%.2x", data[idx]);
49 SNPRINTF_BUFFER_SIZE(ret, remain, offset);
50 }
51
52 return offset;
53}
54
55static int
56nftnl_data_reg_value_snprintf_default(char *buf, size_t remain,
57 const union nftnl_data_reg *reg,
58 uint32_t flags)
59{
60 uint32_t byteorder = big_endian_host() ? 0 : reg->byteorder;
61 const char *pfx = flags & DATA_F_NOPFX ? "" : "0x";
62 int offset = 0, ret, i, pos = 0;
63
64 for (i = 0; i < array_size(reg->sizes); i++) {
65 int curlen = reg->sizes[i] ?: reg->len;
66 bool reverse = byteorder & (1 << i);
67
68 if (i > 0) {
69 ret = snprintf(buf + offset, remain, " . ");
70 SNPRINTF_BUFFER_SIZE(ret, remain, offset);
71 }
72
73 ret = __reg_value_snprintf(buf + offset, remain,
74 (void *)&reg->val[pos],
75 curlen, reverse, pfx);
76 SNPRINTF_BUFFER_SIZE(ret, remain, offset);
77
78 pos += div_round_up(curlen, sizeof(uint32_t));
79 if (pos >= reg->len / sizeof(uint32_t))
80 break;
81 }
82
83 return offset;
84}
85
86static int
87nftnl_data_reg_verdict_snprintf_def(char *buf, size_t size,
88 const union nftnl_data_reg *reg,
89 uint32_t flags)
90{
91 int remain = size, offset = 0, ret = 0;
92
93 ret = snprintf(buf, size, "%s", nftnl_verdict2str(reg->verdict));
94 SNPRINTF_BUFFER_SIZE(ret, remain, offset);
95
96 if (reg->chain != NULL) {
97 ret = snprintf(buf + offset, remain, " -> %s", reg->chain);
98 SNPRINTF_BUFFER_SIZE(ret, remain, offset);
99 }
100
101 return offset;
102}
103
104int nftnl_data_reg_snprintf(char *buf, size_t size,
105 const union nftnl_data_reg *reg,
106 uint32_t flags, int reg_type)
107{
108 switch(reg_type) {
109 case DATA_VALUE:
110 return nftnl_data_reg_value_snprintf_default(buf, size,
111 reg, flags);
112 case DATA_VERDICT:
113 case DATA_CHAIN:
114 return nftnl_data_reg_verdict_snprintf_def(buf, size,
115 reg, flags);
116 default:
117 return -1;
118 }
119}
120
121static int nftnl_data_parse_cb(const struct nlattr *attr, void *data)
122{
123 const struct nlattr **tb = data;
124 int type = mnl_attr_get_type(attr);
125
126 if (mnl_attr_type_valid(attr, NFTA_DATA_MAX) < 0)
127 return MNL_CB_OK;
128
129 switch(type) {
130 case NFTA_DATA_VALUE:
131 if (mnl_attr_validate(attr, MNL_TYPE_BINARY) < 0)
132 abi_breakage();
133 break;
134 case NFTA_DATA_VERDICT:
135 if (mnl_attr_validate(attr, MNL_TYPE_NESTED) < 0)
136 abi_breakage();
137 break;
138 }
139 tb[type] = attr;
140 return MNL_CB_OK;
141}
142
143static int nftnl_verdict_parse_cb(const struct nlattr *attr, void *data)
144{
145 const struct nlattr **tb = data;
146 int type = mnl_attr_get_type(attr);
147
148 if (mnl_attr_type_valid(attr, NFTA_VERDICT_MAX) < 0)
149 return MNL_CB_OK;
150
151 switch(type) {
152 case NFTA_VERDICT_CODE:
153 case NFTA_VERDICT_CHAIN_ID:
154 if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0)
155 abi_breakage();
156 break;
157 case NFTA_VERDICT_CHAIN:
158 if (mnl_attr_validate(attr, MNL_TYPE_STRING) < 0)
159 abi_breakage();
160 break;
161 }
162 tb[type] = attr;
163 return MNL_CB_OK;
164}
165
166static int
167nftnl_parse_verdict(union nftnl_data_reg *data, const struct nlattr *attr, int *type)
168{
169 struct nlattr *tb[NFTA_VERDICT_MAX+1];
170
171 if (mnl_attr_parse_nested(attr, nftnl_verdict_parse_cb, tb) < 0)
172 return -1;
173
174 if (!tb[NFTA_VERDICT_CODE])
175 return -1;
176
177 data->verdict = ntohl(mnl_attr_get_u32(tb[NFTA_VERDICT_CODE]));
178
179 switch(data->verdict) {
180 case NF_ACCEPT:
181 case NF_DROP:
182 case NF_QUEUE:
183 case NFT_CONTINUE:
184 case NFT_BREAK:
185 case NFT_RETURN:
186 if (type)
187 *type = DATA_VERDICT;
188 data->len = sizeof(data->verdict);
189 break;
190 case NFT_JUMP:
191 case NFT_GOTO:
192 if (!tb[NFTA_VERDICT_CHAIN])
193 return -1;
194
195 data->chain = strdup(mnl_attr_get_str(tb[NFTA_VERDICT_CHAIN]));
196 if (!data->chain)
197 return -1;
198
199 if (type)
200 *type = DATA_CHAIN;
201 break;
202 default:
203 return -1;
204 }
205
206 return 0;
207}
208
209static int
210__nftnl_parse_data(union nftnl_data_reg *data, const struct nlattr *attr)
211{
212 void *orig = mnl_attr_get_payload(attr);
213 uint32_t data_len = mnl_attr_get_payload_len(attr);
214
215 if (data_len == 0)
216 return -1;
217
218 if (data_len > sizeof(data->val))
219 return -1;
220
221 memcpy(data->val, orig, data_len);
222 data->len = data_len;
223
224 return 0;
225}
226
227int nftnl_parse_data(union nftnl_data_reg *data, struct nlattr *attr, int *type)
228{
229 struct nlattr *tb[NFTA_DATA_MAX+1] = {};
230 int ret = 0;
231
232 if (mnl_attr_parse_nested(attr, nftnl_data_parse_cb, tb) < 0)
233 return -1;
234
235 if (tb[NFTA_DATA_VALUE]) {
236 if (type)
237 *type = DATA_VALUE;
238
239 ret = __nftnl_parse_data(data, tb[NFTA_DATA_VALUE]);
240 if (ret < 0)
241 return ret;
242 }
243 if (tb[NFTA_DATA_VERDICT])
244 ret = nftnl_parse_verdict(data, tb[NFTA_DATA_VERDICT], type);
245
246 return ret;
247}
248
249int nftnl_data_cpy(union nftnl_data_reg *dreg, const void *src,
250 uint32_t len, uint32_t byteorder, uint8_t *sizes)
251{
252 int ret = 0;
253
254 if (len > sizeof(dreg->val)) {
255 len = sizeof(dreg->val);
256 ret = -1;
257 }
258
259 memcpy(dreg->val, src, len);
260 dreg->len = len;
261 dreg->byteorder = byteorder;
262 if (sizes)
263 memcpy(dreg->sizes, sizes, sizeof(dreg->sizes));
264 else
265 memset(dreg->sizes, 0, sizeof(dreg->sizes));
266 return ret;
267}