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 
20 static 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 
34 static 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 
48 static 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 
53 static uint32_t udp_dflt_timeout[NFTNL_CTTIMEOUT_UDP_MAX] = {
54  [NFTNL_CTTIMEOUT_UDP_UNREPLIED] = 30,
55  [NFTNL_CTTIMEOUT_UDP_REPLIED] = 180,
56 };
57 
58 static 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 
80 static int
81 nftnl_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 
97 static int
98 parse_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 
115 static int
116 timeout_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 
142 static 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 
165 static 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 
184 static 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 
211 static void
212 nftnl_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 
232 static int
233 nftnl_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 
257 static 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 
307 static struct attr_policy
308 obj_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 
313 struct 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 };