libnftnl  1.2.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[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 
38 static 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 
52 static 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 
57 static uint32_t udp_dflt_timeout[NFTNL_CTTIMEOUT_UDP_MAX] = {
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, 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 
169 static 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 
188 static 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 
215 static void
216 nftnl_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 
236 static int
237 nftnl_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 
261 static 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 
311 static struct attr_policy
312 obj_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 
317 struct 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 };