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