libnftnl  1.1.8
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 
24 static const char *const tcp_state_to_name[] = {
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 
38 static uint32_t tcp_dflt_timeout[] = {
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 
52 static const char *const udp_state_to_name[] = {
53  [NFTNL_CTTIMEOUT_UDP_UNREPLIED] = "UNREPLIED",
54  [NFTNL_CTTIMEOUT_UDP_REPLIED] = "REPLIED",
55 };
56 
57 static uint32_t udp_dflt_timeout[] = {
58  [NFTNL_CTTIMEOUT_UDP_UNREPLIED] = 30,
59  [NFTNL_CTTIMEOUT_UDP_REPLIED] = 180,
60 };
61 
62 static 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 
84 static int
85 nftnl_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 
101 static int
102 parse_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 
119 static int
120 timeout_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 
146 static 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, sizeof(timeout->l3proto));
154  break;
155  case NFTNL_OBJ_CT_TIMEOUT_L4PROTO:
156  memcpy(&timeout->l4proto, data, sizeof(timeout->l4proto));
157  break;
158  case NFTNL_OBJ_CT_TIMEOUT_ARRAY:
159  memcpy(timeout->timeout, data,
160  sizeof(uint32_t) * NFTNL_CTTIMEOUT_ARRAY_MAX);
161  break;
162  default:
163  return -1;
164  }
165  return 0;
166 }
167 
168 static const void *nftnl_obj_ct_timeout_get(const struct nftnl_obj *e,
169  uint16_t type, uint32_t *data_len)
170 {
171  struct nftnl_obj_ct_timeout *timeout = nftnl_obj_data(e);
172 
173  switch (type) {
174  case NFTNL_OBJ_CT_TIMEOUT_L3PROTO:
175  *data_len = sizeof(timeout->l3proto);
176  return &timeout->l3proto;
177  case NFTNL_OBJ_CT_TIMEOUT_L4PROTO:
178  *data_len = sizeof(timeout->l4proto);
179  return &timeout->l4proto;
180  case NFTNL_OBJ_CT_TIMEOUT_ARRAY:
181  *data_len = sizeof(uint32_t) * NFTNL_CTTIMEOUT_ARRAY_MAX;
182  return timeout->timeout;
183  }
184  return NULL;
185 }
186 
187 static int nftnl_obj_ct_timeout_cb(const struct nlattr *attr, void *data)
188 {
189  int type = mnl_attr_get_type(attr);
190  const struct nlattr **tb = data;
191 
192  if (mnl_attr_type_valid(attr, NFTA_CT_TIMEOUT_MAX) < 0)
193  return MNL_CB_OK;
194 
195  switch (type) {
196  case NFTA_CT_TIMEOUT_L3PROTO:
197  if (mnl_attr_validate(attr, MNL_TYPE_U16) < 0)
198  abi_breakage();
199  break;
200  case NFTA_CT_TIMEOUT_L4PROTO:
201  if (mnl_attr_validate(attr, MNL_TYPE_U8) < 0)
202  abi_breakage();
203  break;
204  case NFTA_CT_TIMEOUT_DATA:
205  if (mnl_attr_validate(attr, MNL_TYPE_NESTED) < 0)
206  abi_breakage();
207  break;
208  }
209 
210  tb[type] = attr;
211  return MNL_CB_OK;
212 }
213 
214 static void
215 nftnl_obj_ct_timeout_build(struct nlmsghdr *nlh, const struct nftnl_obj *e)
216 {
217  struct nftnl_obj_ct_timeout *timeout = nftnl_obj_data(e);
218  struct nlattr *nest;
219 
220  if (e->flags & (1 << NFTNL_OBJ_CT_TIMEOUT_L3PROTO))
221  mnl_attr_put_u16(nlh, NFTA_CT_TIMEOUT_L3PROTO, htons(timeout->l3proto));
222  if (e->flags & (1 << NFTNL_OBJ_CT_TIMEOUT_L4PROTO))
223  mnl_attr_put_u8(nlh, NFTA_CT_TIMEOUT_L4PROTO, timeout->l4proto);
224  if (e->flags & (1 << NFTNL_OBJ_CT_TIMEOUT_ARRAY)) {
225  int i;
226 
227  nest = mnl_attr_nest_start(nlh, NFTA_CT_TIMEOUT_DATA);
228  for (i = 0; i < timeout_protocol[timeout->l4proto].attr_max; i++)
229  mnl_attr_put_u32(nlh, i+1, htonl(timeout->timeout[i]));
230 
231  mnl_attr_nest_end(nlh, nest);
232  }
233 }
234 
235 static int
236 nftnl_obj_ct_timeout_parse(struct nftnl_obj *e, struct nlattr *attr)
237 {
238  struct nftnl_obj_ct_timeout *timeout = nftnl_obj_data(e);
239  struct nlattr *tb[NFTA_CT_TIMEOUT_MAX + 1] = {};
240 
241  if (mnl_attr_parse_nested(attr, nftnl_obj_ct_timeout_cb, tb) < 0)
242  return -1;
243 
244  if (tb[NFTA_CT_TIMEOUT_L3PROTO]) {
245  timeout->l3proto = ntohs(mnl_attr_get_u16(tb[NFTA_CT_TIMEOUT_L3PROTO]));
246  e->flags |= (1 << NFTNL_OBJ_CT_TIMEOUT_L3PROTO);
247  }
248  if (tb[NFTA_CT_TIMEOUT_L4PROTO]) {
249  timeout->l4proto = mnl_attr_get_u8(tb[NFTA_CT_TIMEOUT_L4PROTO]);
250  e->flags |= (1 << NFTNL_OBJ_CT_TIMEOUT_L4PROTO);
251  }
252  if (tb[NFTA_CT_TIMEOUT_DATA]) {
253  if (timeout_parse_attr_data(e, tb[NFTA_CT_TIMEOUT_DATA]) < 0)
254  return -1;
255  e->flags |= (1 << NFTNL_OBJ_CT_TIMEOUT_ARRAY);
256  }
257  return 0;
258 }
259 
260 static int nftnl_obj_ct_timeout_snprintf_default(char *buf, size_t len,
261  const struct nftnl_obj *e)
262 {
263  int ret = 0;
264  int offset = 0, remain = len;
265 
266  struct nftnl_obj_ct_timeout *timeout = nftnl_obj_data(e);
267 
268  if (e->flags & (1 << NFTNL_OBJ_CT_TIMEOUT_L3PROTO)) {
269  ret = snprintf(buf + offset, len, "family %d ",
270  timeout->l3proto);
271  SNPRINTF_BUFFER_SIZE(ret, remain, offset);
272  }
273  if (e->flags & (1 << NFTNL_OBJ_CT_TIMEOUT_L4PROTO)) {
274  ret = snprintf(buf + offset, len, "protocol %d ",
275  timeout->l4proto);
276  SNPRINTF_BUFFER_SIZE(ret, remain, offset);
277  }
278  if (e->flags & (1 << NFTNL_OBJ_CT_TIMEOUT_ARRAY)) {
279  uint8_t l4num = timeout->l4proto;
280  int i;
281 
282  /* default to generic protocol tracker. */
283  if (timeout_protocol[timeout->l4proto].attr_max == 0)
284  l4num = IPPROTO_RAW;
285 
286  ret = snprintf(buf + offset, len, "policy = {");
287  SNPRINTF_BUFFER_SIZE(ret, remain, offset);
288 
289  for (i = 0; i < timeout_protocol[l4num].attr_max; i++) {
290  const char *state_name =
291  timeout_protocol[l4num].state_to_name[i][0] ?
292  timeout_protocol[l4num].state_to_name[i] :
293  "UNKNOWN";
294 
295  if (timeout->timeout[i] != timeout_protocol[l4num].dflt_timeout[i]) {
296  ret = snprintf(buf + offset, len,
297  "%s = %u,", state_name, timeout->timeout[i]);
298  SNPRINTF_BUFFER_SIZE(ret, remain, offset);
299  }
300  }
301 
302  ret = snprintf(buf + offset, len, "}");
303  SNPRINTF_BUFFER_SIZE(ret, remain, offset);
304  }
305  buf[offset] = '\0';
306 
307  return offset;
308 }
309 
310 static int nftnl_obj_ct_timeout_snprintf(char *buf, size_t len, uint32_t type,
311  uint32_t flags,
312  const struct nftnl_obj *e)
313 {
314  if (len)
315  buf[0] = '\0';
316 
317  switch (type) {
318  case NFTNL_OUTPUT_DEFAULT:
319  return nftnl_obj_ct_timeout_snprintf_default(buf, len, e);
320  case NFTNL_OUTPUT_JSON:
321  default:
322  break;
323  }
324  return -1;
325 }
326 
327 struct obj_ops obj_ops_ct_timeout = {
328  .name = "ct_timeout",
329  .type = NFT_OBJECT_CT_TIMEOUT,
330  .alloc_len = sizeof(struct nftnl_obj_ct_timeout),
331  .max_attr = NFTA_CT_TIMEOUT_MAX,
332  .set = nftnl_obj_ct_timeout_set,
333  .get = nftnl_obj_ct_timeout_get,
334  .parse = nftnl_obj_ct_timeout_parse,
335  .build = nftnl_obj_ct_timeout_build,
336  .snprintf = nftnl_obj_ct_timeout_snprintf,
337 };
_container_policy_cb
Definition: ct_timeout.c:79