libnftnl 1.2.9
ct_timeout.c
1/* SPDX-License-Identifier: GPL-2.0-or-later */
2/*
3 * (C) 2018 by Harsha Sharma <harshasharmaiitr@gmail.com>
4 */
5
6#include <stdio.h>
7#include <stdint.h>
8#include <arpa/inet.h>
9#include <errno.h>
10#include <inttypes.h>
11
12#include <linux/netfilter/nf_tables.h>
13
14#include "internal.h"
15#include <libmnl/libmnl.h>
16#include <libnftnl/object.h>
17
18#include "obj.h"
19
20static const char *const tcp_state_to_name[NFTNL_CTTIMEOUT_TCP_MAX] = {
21 [NFTNL_CTTIMEOUT_TCP_SYN_SENT] = "SYN_SENT",
22 [NFTNL_CTTIMEOUT_TCP_SYN_RECV] = "SYN_RECV",
23 [NFTNL_CTTIMEOUT_TCP_ESTABLISHED] = "ESTABLISHED",
24 [NFTNL_CTTIMEOUT_TCP_FIN_WAIT] = "FIN_WAIT",
25 [NFTNL_CTTIMEOUT_TCP_CLOSE_WAIT] = "CLOSE_WAIT",
26 [NFTNL_CTTIMEOUT_TCP_LAST_ACK] = "LAST_ACK",
27 [NFTNL_CTTIMEOUT_TCP_TIME_WAIT] = "TIME_WAIT",
28 [NFTNL_CTTIMEOUT_TCP_CLOSE] = "CLOSE",
29 [NFTNL_CTTIMEOUT_TCP_SYN_SENT2] = "SYN_SENT2",
30 [NFTNL_CTTIMEOUT_TCP_RETRANS] = "RETRANS",
31 [NFTNL_CTTIMEOUT_TCP_UNACK] = "UNACKNOWLEDGED",
32};
33
34static uint32_t tcp_dflt_timeout[NFTNL_CTTIMEOUT_TCP_MAX] = {
35 [NFTNL_CTTIMEOUT_TCP_SYN_SENT] = 120,
36 [NFTNL_CTTIMEOUT_TCP_SYN_RECV] = 60,
37 [NFTNL_CTTIMEOUT_TCP_ESTABLISHED] = 432000,
38 [NFTNL_CTTIMEOUT_TCP_FIN_WAIT] = 120,
39 [NFTNL_CTTIMEOUT_TCP_CLOSE_WAIT] = 60,
40 [NFTNL_CTTIMEOUT_TCP_LAST_ACK] = 30,
41 [NFTNL_CTTIMEOUT_TCP_TIME_WAIT] = 120,
42 [NFTNL_CTTIMEOUT_TCP_CLOSE] = 10,
43 [NFTNL_CTTIMEOUT_TCP_SYN_SENT2] = 120,
44 [NFTNL_CTTIMEOUT_TCP_RETRANS] = 300,
45 [NFTNL_CTTIMEOUT_TCP_UNACK] = 300,
46};
47
48static const char *const udp_state_to_name[NFTNL_CTTIMEOUT_UDP_MAX] = {
49 [NFTNL_CTTIMEOUT_UDP_UNREPLIED] = "UNREPLIED",
50 [NFTNL_CTTIMEOUT_UDP_REPLIED] = "REPLIED",
51};
52
53static uint32_t udp_dflt_timeout[NFTNL_CTTIMEOUT_UDP_MAX] = {
54 [NFTNL_CTTIMEOUT_UDP_UNREPLIED] = 30,
55 [NFTNL_CTTIMEOUT_UDP_REPLIED] = 180,
56};
57
58static struct {
59 uint32_t attr_max;
60 const char *const *state_to_name;
61 uint32_t *dflt_timeout;
62} timeout_protocol[IPPROTO_MAX] = {
63 [IPPROTO_TCP] = {
64 .attr_max = NFTNL_CTTIMEOUT_TCP_MAX,
65 .state_to_name = tcp_state_to_name,
66 .dflt_timeout = tcp_dflt_timeout,
67 },
68 [IPPROTO_UDP] = {
69 .attr_max = NFTNL_CTTIMEOUT_UDP_MAX,
70 .state_to_name = udp_state_to_name,
71 .dflt_timeout = udp_dflt_timeout,
72 },
73};
74
76 unsigned int nlattr_max;
77 void *tb;
78};
79
80static int
81nftnl_timeout_policy_attr_set_u32(struct nftnl_obj *e,
82 uint32_t type, uint32_t data)
83{
84 struct nftnl_obj_ct_timeout *t = nftnl_obj_data(e);
85
86 if (type >= NFTNL_CTTIMEOUT_ARRAY_MAX)
87 return -1;
88
89 t->timeout[type] = data;
90
91 if (!(e->flags & (1 << NFTNL_OBJ_CT_TIMEOUT_ARRAY)))
92 e->flags |= (1 << NFTNL_OBJ_CT_TIMEOUT_ARRAY);
93
94 return 0;
95}
96
97static int
98parse_timeout_attr_policy_cb(const struct nlattr *attr, void *data)
99{
100 struct _container_policy_cb *data_cb = data;
101 const struct nlattr **tb = data_cb->tb;
102 uint16_t type = mnl_attr_get_type(attr);
103
104 if (mnl_attr_type_valid(attr, data_cb->nlattr_max) < 0)
105 return MNL_CB_OK;
106
107 if (type > 0 && type <= data_cb->nlattr_max) {
108 if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0)
109 abi_breakage();
110 tb[type - 1] = attr;
111 }
112 return MNL_CB_OK;
113}
114
115static int
116timeout_parse_attr_data(struct nftnl_obj *e,
117 const struct nlattr *nest)
118{
119 struct nftnl_obj_ct_timeout *t = nftnl_obj_data(e);
120 unsigned int attr_max = timeout_protocol[t->l4proto].attr_max;
121 struct nlattr *tb[attr_max];
122 struct _container_policy_cb cnt = {
123 .nlattr_max = attr_max,
124 .tb = tb,
125 };
126 unsigned int i;
127
128 memset(tb, 0, sizeof(struct nlattr *) * attr_max);
129
130 if (mnl_attr_parse_nested(nest, parse_timeout_attr_policy_cb, &cnt) < 0)
131 return -1;
132
133 for (i = 0; i < array_size(tb); i++) {
134 if (tb[i]) {
135 nftnl_timeout_policy_attr_set_u32(e, i,
136 ntohl(mnl_attr_get_u32(tb[i])));
137 }
138 }
139 return 0;
140}
141
142static int nftnl_obj_ct_timeout_set(struct nftnl_obj *e, uint16_t type,
143 const void *data, uint32_t data_len)
144{
145 struct nftnl_obj_ct_timeout *timeout = nftnl_obj_data(e);
146
147 switch (type) {
148 case NFTNL_OBJ_CT_TIMEOUT_L3PROTO:
149 memcpy(&timeout->l3proto, data, data_len);
150 break;
151 case NFTNL_OBJ_CT_TIMEOUT_L4PROTO:
152 memcpy(&timeout->l4proto, data, data_len);
153 break;
154 case NFTNL_OBJ_CT_TIMEOUT_ARRAY:
155 if (data_len < sizeof(uint32_t) * NFTNL_CTTIMEOUT_ARRAY_MAX)
156 return -1;
157
158 memcpy(timeout->timeout, data,
159 sizeof(uint32_t) * NFTNL_CTTIMEOUT_ARRAY_MAX);
160 break;
161 }
162 return 0;
163}
164
165static const void *nftnl_obj_ct_timeout_get(const struct nftnl_obj *e,
166 uint16_t type, uint32_t *data_len)
167{
168 struct nftnl_obj_ct_timeout *timeout = nftnl_obj_data(e);
169
170 switch (type) {
171 case NFTNL_OBJ_CT_TIMEOUT_L3PROTO:
172 *data_len = sizeof(timeout->l3proto);
173 return &timeout->l3proto;
174 case NFTNL_OBJ_CT_TIMEOUT_L4PROTO:
175 *data_len = sizeof(timeout->l4proto);
176 return &timeout->l4proto;
177 case NFTNL_OBJ_CT_TIMEOUT_ARRAY:
178 *data_len = sizeof(uint32_t) * NFTNL_CTTIMEOUT_ARRAY_MAX;
179 return timeout->timeout;
180 }
181 return NULL;
182}
183
184static int nftnl_obj_ct_timeout_cb(const struct nlattr *attr, void *data)
185{
186 int type = mnl_attr_get_type(attr);
187 const struct nlattr **tb = data;
188
189 if (mnl_attr_type_valid(attr, NFTA_CT_TIMEOUT_MAX) < 0)
190 return MNL_CB_OK;
191
192 switch (type) {
193 case NFTA_CT_TIMEOUT_L3PROTO:
194 if (mnl_attr_validate(attr, MNL_TYPE_U16) < 0)
195 abi_breakage();
196 break;
197 case NFTA_CT_TIMEOUT_L4PROTO:
198 if (mnl_attr_validate(attr, MNL_TYPE_U8) < 0)
199 abi_breakage();
200 break;
201 case NFTA_CT_TIMEOUT_DATA:
202 if (mnl_attr_validate(attr, MNL_TYPE_NESTED) < 0)
203 abi_breakage();
204 break;
205 }
206
207 tb[type] = attr;
208 return MNL_CB_OK;
209}
210
211static void
212nftnl_obj_ct_timeout_build(struct nlmsghdr *nlh, const struct nftnl_obj *e)
213{
214 struct nftnl_obj_ct_timeout *timeout = nftnl_obj_data(e);
215 struct nlattr *nest;
216
217 if (e->flags & (1 << NFTNL_OBJ_CT_TIMEOUT_L3PROTO))
218 mnl_attr_put_u16(nlh, NFTA_CT_TIMEOUT_L3PROTO, htons(timeout->l3proto));
219 if (e->flags & (1 << NFTNL_OBJ_CT_TIMEOUT_L4PROTO))
220 mnl_attr_put_u8(nlh, NFTA_CT_TIMEOUT_L4PROTO, timeout->l4proto);
221 if (e->flags & (1 << NFTNL_OBJ_CT_TIMEOUT_ARRAY)) {
222 int i;
223
224 nest = mnl_attr_nest_start(nlh, NFTA_CT_TIMEOUT_DATA);
225 for (i = 0; i < timeout_protocol[timeout->l4proto].attr_max; i++)
226 mnl_attr_put_u32(nlh, i+1, htonl(timeout->timeout[i]));
227
228 mnl_attr_nest_end(nlh, nest);
229 }
230}
231
232static int
233nftnl_obj_ct_timeout_parse(struct nftnl_obj *e, struct nlattr *attr)
234{
235 struct nftnl_obj_ct_timeout *timeout = nftnl_obj_data(e);
236 struct nlattr *tb[NFTA_CT_TIMEOUT_MAX + 1] = {};
237
238 if (mnl_attr_parse_nested(attr, nftnl_obj_ct_timeout_cb, tb) < 0)
239 return -1;
240
241 if (tb[NFTA_CT_TIMEOUT_L3PROTO]) {
242 timeout->l3proto = ntohs(mnl_attr_get_u16(tb[NFTA_CT_TIMEOUT_L3PROTO]));
243 e->flags |= (1 << NFTNL_OBJ_CT_TIMEOUT_L3PROTO);
244 }
245 if (tb[NFTA_CT_TIMEOUT_L4PROTO]) {
246 timeout->l4proto = mnl_attr_get_u8(tb[NFTA_CT_TIMEOUT_L4PROTO]);
247 e->flags |= (1 << NFTNL_OBJ_CT_TIMEOUT_L4PROTO);
248 }
249 if (tb[NFTA_CT_TIMEOUT_DATA]) {
250 if (timeout_parse_attr_data(e, tb[NFTA_CT_TIMEOUT_DATA]) < 0)
251 return -1;
252 e->flags |= (1 << NFTNL_OBJ_CT_TIMEOUT_ARRAY);
253 }
254 return 0;
255}
256
257static int nftnl_obj_ct_timeout_snprintf(char *buf, size_t remain,
258 uint32_t flags,
259 const struct nftnl_obj *e)
260{
261 int ret = 0, offset = 0;
262
263 struct nftnl_obj_ct_timeout *timeout = nftnl_obj_data(e);
264
265 if (e->flags & (1 << NFTNL_OBJ_CT_TIMEOUT_L3PROTO)) {
266 ret = snprintf(buf + offset, remain, "family %d ",
267 timeout->l3proto);
268 SNPRINTF_BUFFER_SIZE(ret, remain, offset);
269 }
270 if (e->flags & (1 << NFTNL_OBJ_CT_TIMEOUT_L4PROTO)) {
271 ret = snprintf(buf + offset, remain, "protocol %d ",
272 timeout->l4proto);
273 SNPRINTF_BUFFER_SIZE(ret, remain, offset);
274 }
275 if (e->flags & (1 << NFTNL_OBJ_CT_TIMEOUT_ARRAY)) {
276 uint8_t l4num = timeout->l4proto;
277 int i;
278
279 /* default to generic protocol tracker. */
280 if (timeout_protocol[timeout->l4proto].attr_max == 0)
281 l4num = IPPROTO_RAW;
282
283 ret = snprintf(buf + offset, remain, "policy = {");
284 SNPRINTF_BUFFER_SIZE(ret, remain, offset);
285
286 for (i = 0; i < timeout_protocol[l4num].attr_max; i++) {
287 const char *state_name =
288 timeout_protocol[l4num].state_to_name[i][0] ?
289 timeout_protocol[l4num].state_to_name[i] :
290 "UNKNOWN";
291
292 if (timeout->timeout[i] != timeout_protocol[l4num].dflt_timeout[i]) {
293 ret = snprintf(buf + offset, remain,
294 "%s = %u,", state_name, timeout->timeout[i]);
295 SNPRINTF_BUFFER_SIZE(ret, remain, offset);
296 }
297 }
298
299 ret = snprintf(buf + offset, remain, "}");
300 SNPRINTF_BUFFER_SIZE(ret, remain, offset);
301 }
302 buf[offset] = '\0';
303
304 return offset;
305}
306
307static struct attr_policy
308obj_ct_timeout_attr_policy[__NFTNL_OBJ_CT_TIMEOUT_MAX] = {
309 [NFTNL_OBJ_CT_TIMEOUT_L3PROTO] = { .maxlen = sizeof(uint16_t) },
310 [NFTNL_OBJ_CT_TIMEOUT_L4PROTO] = { .maxlen = sizeof(uint8_t) },
311};
312
313struct obj_ops obj_ops_ct_timeout = {
314 .name = "ct_timeout",
315 .type = NFT_OBJECT_CT_TIMEOUT,
316 .alloc_len = sizeof(struct nftnl_obj_ct_timeout),
317 .nftnl_max_attr = __NFTNL_OBJ_CT_TIMEOUT_MAX - 1,
318 .attr_policy = obj_ct_timeout_attr_policy,
319 .set = nftnl_obj_ct_timeout_set,
320 .get = nftnl_obj_ct_timeout_get,
321 .parse = nftnl_obj_ct_timeout_parse,
322 .build = nftnl_obj_ct_timeout_build,
323 .output = nftnl_obj_ct_timeout_snprintf,
324};