libnftnl  1.1.2
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 <= data_cb->nlattr_max) {
112  if (mnl_attr_validate(attr, MNL_TYPE_U32) < 0)
113  abi_breakage();
114  tb[type] = attr;
115  }
116  return MNL_CB_OK;
117 }
118 
119 static void
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  mnl_attr_parse_nested(nest, parse_timeout_attr_policy_cb, &cnt);
135 
136  for (i = 1; i <= attr_max; i++) {
137  if (tb[i]) {
138  nftnl_timeout_policy_attr_set_u32(e, i-1,
139  ntohl(mnl_attr_get_u32(tb[i])));
140  }
141  }
142 }
143 
144 static int nftnl_obj_ct_timeout_set(struct nftnl_obj *e, uint16_t type,
145  const void *data, uint32_t data_len)
146 {
147  struct nftnl_obj_ct_timeout *timeout = nftnl_obj_data(e);
148 
149  switch (type) {
150  case NFTNL_OBJ_CT_TIMEOUT_L3PROTO:
151  memcpy(&timeout->l3proto, data, sizeof(timeout->l3proto));
152  break;
153  case NFTNL_OBJ_CT_TIMEOUT_L4PROTO:
154  memcpy(&timeout->l4proto, data, sizeof(timeout->l4proto));
155  break;
156  case NFTNL_OBJ_CT_TIMEOUT_ARRAY:
157  memcpy(timeout->timeout, data,
158  sizeof(uint32_t) * NFTNL_CTTIMEOUT_ARRAY_MAX);
159  break;
160  default:
161  return -1;
162  }
163  return 0;
164 }
165 
166 static const void *nftnl_obj_ct_timeout_get(const struct nftnl_obj *e,
167  uint16_t type, uint32_t *data_len)
168 {
169  struct nftnl_obj_ct_timeout *timeout = nftnl_obj_data(e);
170 
171  switch (type) {
172  case NFTNL_OBJ_CT_TIMEOUT_L3PROTO:
173  *data_len = sizeof(timeout->l3proto);
174  return &timeout->l3proto;
175  case NFTNL_OBJ_CT_TIMEOUT_L4PROTO:
176  *data_len = sizeof(timeout->l4proto);
177  return &timeout->l4proto;
178  case NFTNL_OBJ_CT_TIMEOUT_ARRAY:
179  *data_len = sizeof(uint32_t) * NFTNL_CTTIMEOUT_ARRAY_MAX;
180  return timeout->timeout;
181  }
182  return NULL;
183 }
184 
185 static int nftnl_obj_ct_timeout_cb(const struct nlattr *attr, void *data)
186 {
187  int type = mnl_attr_get_type(attr);
188  const struct nlattr **tb = data;
189 
190  if (mnl_attr_type_valid(attr, NFTA_CT_TIMEOUT_MAX) < 0)
191  return MNL_CB_OK;
192 
193  switch (type) {
194  case NFTA_CT_TIMEOUT_L3PROTO:
195  if (mnl_attr_validate(attr, MNL_TYPE_U16) < 0)
196  abi_breakage();
197  break;
198  case NFTA_CT_TIMEOUT_L4PROTO:
199  if (mnl_attr_validate(attr, MNL_TYPE_U8) < 0)
200  abi_breakage();
201  break;
202  case NFTA_CT_TIMEOUT_DATA:
203  if (mnl_attr_validate(attr, MNL_TYPE_NESTED) < 0)
204  abi_breakage();
205  break;
206  }
207 
208  tb[type] = attr;
209  return MNL_CB_OK;
210 }
211 
212 static void
213 nftnl_obj_ct_timeout_build(struct nlmsghdr *nlh, const struct nftnl_obj *e)
214 {
215  struct nftnl_obj_ct_timeout *timeout = nftnl_obj_data(e);
216  struct nlattr *nest;
217 
218  if (e->flags & (1 << NFTNL_OBJ_CT_TIMEOUT_L3PROTO))
219  mnl_attr_put_u16(nlh, NFTA_CT_TIMEOUT_L3PROTO, htons(timeout->l3proto));
220  if (e->flags & (1 << NFTNL_OBJ_CT_TIMEOUT_L4PROTO))
221  mnl_attr_put_u8(nlh, NFTA_CT_TIMEOUT_L4PROTO, timeout->l4proto);
222  if (e->flags & (1 << NFTNL_OBJ_CT_TIMEOUT_ARRAY)) {
223  int i;
224 
225  nest = mnl_attr_nest_start(nlh, NFTA_CT_TIMEOUT_DATA);
226  for (i = 0; i < timeout_protocol[timeout->l4proto].attr_max; i++)
227  mnl_attr_put_u32(nlh, i+1, htonl(timeout->timeout[i]));
228 
229  mnl_attr_nest_end(nlh, nest);
230  }
231 }
232 
233 static int
234 nftnl_obj_ct_timeout_parse(struct nftnl_obj *e, struct nlattr *attr)
235 {
236  struct nftnl_obj_ct_timeout *timeout = nftnl_obj_data(e);
237  struct nlattr *tb[NFTA_CT_TIMEOUT_MAX + 1] = {};
238 
239  if (mnl_attr_parse_nested(attr, nftnl_obj_ct_timeout_cb, tb) < 0)
240  return -1;
241 
242  if (tb[NFTA_CT_TIMEOUT_L3PROTO]) {
243  timeout->l3proto = ntohs(mnl_attr_get_u16(tb[NFTA_CT_TIMEOUT_L3PROTO]));
244  e->flags |= (1 << NFTNL_OBJ_CT_TIMEOUT_L3PROTO);
245  }
246  if (tb[NFTA_CT_TIMEOUT_L4PROTO]) {
247  timeout->l4proto = mnl_attr_get_u8(tb[NFTA_CT_TIMEOUT_L4PROTO]);
248  e->flags |= (1 << NFTNL_OBJ_CT_TIMEOUT_L4PROTO);
249  }
250  if (tb[NFTA_CT_TIMEOUT_DATA]) {
251  timeout_parse_attr_data(e, tb[NFTA_CT_TIMEOUT_DATA]);
252  e->flags |= (1 << NFTNL_OBJ_CT_TIMEOUT_ARRAY);
253  }
254  return 0;
255 }
256 
257 static int nftnl_obj_ct_timeout_snprintf_default(char *buf, size_t len,
258  const struct nftnl_obj *e)
259 {
260  int ret = 0;
261  int offset = 0, remain = len;
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, len, "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, len, "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, len, "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, len,
294  "%s = %u,", state_name, timeout->timeout[i]);
295  SNPRINTF_BUFFER_SIZE(ret, remain, offset);
296  }
297  }
298 
299  ret = snprintf(buf + offset, len, "}");
300  SNPRINTF_BUFFER_SIZE(ret, remain, offset);
301  }
302  buf[offset] = '\0';
303 
304  return offset;
305 }
306 
307 static int nftnl_obj_ct_timeout_snprintf(char *buf, size_t len, uint32_t type,
308  uint32_t flags,
309  const struct nftnl_obj *e)
310 {
311  if (len)
312  buf[0] = '\0';
313 
314  switch (type) {
315  case NFTNL_OUTPUT_DEFAULT:
316  return nftnl_obj_ct_timeout_snprintf_default(buf, len, e);
317  case NFTNL_OUTPUT_JSON:
318  default:
319  break;
320  }
321  return -1;
322 }
323 
324 struct obj_ops obj_ops_ct_timeout = {
325  .name = "ct_timeout",
326  .type = NFT_OBJECT_CT_TIMEOUT,
327  .alloc_len = sizeof(struct nftnl_obj_ct_timeout),
328  .max_attr = NFTA_CT_TIMEOUT_MAX,
329  .set = nftnl_obj_ct_timeout_set,
330  .get = nftnl_obj_ct_timeout_get,
331  .parse = nftnl_obj_ct_timeout_parse,
332  .build = nftnl_obj_ct_timeout_build,
333  .snprintf = nftnl_obj_ct_timeout_snprintf,
334 };