]> git.ipfire.org Git - thirdparty/iproute2.git/blame - tc/m_ct.c
tc: make action_util arg const
[thirdparty/iproute2.git] / tc / m_ct.c
CommitLineData
c8a49431
PB
1// SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB
2/* -
3 * m_ct.c Connection tracking action
4 *
5 * Authors: Paul Blakey <paulb@mellanox.com>
6 * Yossi Kuperman <yossiku@mellanox.com>
7 * Marcelo Ricardo Leitner <marcelo.leitner@gmail.com>
8 */
9
10#include <stdio.h>
11#include <stdlib.h>
12#include <unistd.h>
13#include <string.h>
14#include "utils.h"
15#include "tc_util.h"
4cdce041 16#include "rt_names.h"
c8a49431
PB
17#include <linux/tc_act/tc_ct.h>
18
19static void
20usage(void)
21{
22 fprintf(stderr,
23 "Usage: ct clear\n"
4cdce041 24 " ct commit [force] [zone ZONE] [mark MASKED_MARK] [label MASKED_LABEL] [nat NAT_SPEC] [helper HELPER]\n"
c8a49431
PB
25 " ct [nat] [zone ZONE]\n"
26 "Where: ZONE is the conntrack zone table number\n"
27 " NAT_SPEC is {src|dst} addr addr1[-addr2] [port port1[-port2]]\n"
4cdce041 28 " HELPER is family-proto-name such as ipv4-tcp-ftp\n"
c8a49431
PB
29 "\n");
30 exit(-1);
31}
32
33static int ct_parse_nat_addr_range(const char *str, struct nlmsghdr *n)
34{
35 inet_prefix addr = { .family = AF_UNSPEC, };
36 char *addr1, *addr2 = 0;
37 SPRINT_BUF(buffer);
38 int attr;
39 int ret;
40
41 strncpy(buffer, str, sizeof(buffer) - 1);
42
43 addr1 = buffer;
44 addr2 = strchr(addr1, '-');
45 if (addr2) {
46 *addr2 = '\0';
47 addr2++;
48 }
49
50 ret = get_addr(&addr, addr1, AF_UNSPEC);
51 if (ret)
52 return ret;
53 attr = addr.family == AF_INET ? TCA_CT_NAT_IPV4_MIN :
54 TCA_CT_NAT_IPV6_MIN;
55 addattr_l(n, MAX_MSG, attr, addr.data, addr.bytelen);
56
57 if (addr2) {
58 ret = get_addr(&addr, addr2, addr.family);
59 if (ret)
60 return ret;
61 }
62 attr = addr.family == AF_INET ? TCA_CT_NAT_IPV4_MAX :
63 TCA_CT_NAT_IPV6_MAX;
64 addattr_l(n, MAX_MSG, attr, addr.data, addr.bytelen);
65
66 return 0;
67}
68
69static int ct_parse_nat_port_range(const char *str, struct nlmsghdr *n)
70{
71 char *port1, *port2 = 0;
72 SPRINT_BUF(buffer);
73 __be16 port;
74 int ret;
75
76 strncpy(buffer, str, sizeof(buffer) - 1);
77
78 port1 = buffer;
79 port2 = strchr(port1, '-');
80 if (port2) {
81 *port2 = '\0';
82 port2++;
83 }
84
85 ret = get_be16(&port, port1, 10);
86 if (ret)
87 return -1;
88 addattr16(n, MAX_MSG, TCA_CT_NAT_PORT_MIN, port);
89
90 if (port2) {
91 ret = get_be16(&port, port2, 10);
92 if (ret)
93 return -1;
94 }
95 addattr16(n, MAX_MSG, TCA_CT_NAT_PORT_MAX, port);
96
97 return 0;
98}
99
100
101static int ct_parse_u16(char *str, int value_type, int mask_type,
102 struct nlmsghdr *n)
103{
104 __u16 value, mask;
105 char *slash = 0;
106
107 if (mask_type != TCA_CT_UNSPEC) {
108 slash = strchr(str, '/');
109 if (slash)
110 *slash = '\0';
111 }
112
113 if (get_u16(&value, str, 0))
114 return -1;
115
116 if (slash) {
117 if (get_u16(&mask, slash + 1, 0))
118 return -1;
119 } else {
120 mask = UINT16_MAX;
121 }
122
123 addattr16(n, MAX_MSG, value_type, value);
124 if (mask_type != TCA_CT_UNSPEC)
125 addattr16(n, MAX_MSG, mask_type, mask);
126
127 return 0;
128}
129
130static int ct_parse_u32(char *str, int value_type, int mask_type,
131 struct nlmsghdr *n)
132{
133 __u32 value, mask;
134 char *slash;
135
136 slash = strchr(str, '/');
137 if (slash)
138 *slash = '\0';
139
140 if (get_u32(&value, str, 0))
141 return -1;
142
143 if (slash) {
144 if (get_u32(&mask, slash + 1, 0))
145 return -1;
146 } else {
147 mask = UINT32_MAX;
148 }
149
150 addattr32(n, MAX_MSG, value_type, value);
151 addattr32(n, MAX_MSG, mask_type, mask);
152
153 return 0;
154}
155
156static int ct_parse_mark(char *str, struct nlmsghdr *n)
157{
158 return ct_parse_u32(str, TCA_CT_MARK, TCA_CT_MARK_MASK, n);
159}
160
4cdce041
XL
161static int ct_parse_helper(char *str, struct nlmsghdr *n)
162{
163 char f[32], p[32], name[32];
ac6d95a8
SH
164 __u8 family;
165 int proto;
4cdce041
XL
166
167 if (strlen(str) >= 32 ||
168 sscanf(str, "%[^-]-%[^-]-%[^-]", f, p, name) != 3)
169 return -1;
170 if (!strcmp(f, "ipv4"))
171 family = AF_INET;
172 else if (!strcmp(f, "ipv6"))
173 family = AF_INET6;
174 else
175 return -1;
ac6d95a8 176
4cdce041
XL
177 proto = inet_proto_a2n(p);
178 if (proto < 0)
179 return -1;
180
181 addattr8(n, MAX_MSG, TCA_CT_HELPER_FAMILY, family);
182 addattr8(n, MAX_MSG, TCA_CT_HELPER_PROTO, proto);
183 addattrstrz(n, MAX_MSG, TCA_CT_HELPER_NAME, name);
184 return 0;
185}
186
c8a49431
PB
187static int ct_parse_labels(char *str, struct nlmsghdr *n)
188{
189#define LABELS_SIZE 16
190 uint8_t labels[LABELS_SIZE], lmask[LABELS_SIZE];
191 char *slash, *mask = NULL;
192 size_t slen, slen_mask = 0;
193
194 slash = index(str, '/');
195 if (slash) {
196 *slash = 0;
197 mask = slash+1;
198 slen_mask = strlen(mask);
199 }
200
201 slen = strlen(str);
202 if (slen > LABELS_SIZE*2 || slen_mask > LABELS_SIZE*2) {
203 char errmsg[128];
204
205 snprintf(errmsg, sizeof(errmsg),
206 "%zd Max allowed size %d",
207 slen, LABELS_SIZE*2);
208 invarg(errmsg, str);
209 }
210
211 if (hex2mem(str, labels, slen/2) < 0)
212 invarg("ct: labels must be a hex string\n", str);
213 addattr_l(n, MAX_MSG, TCA_CT_LABELS, labels, slen/2);
214
215 if (mask) {
216 if (hex2mem(mask, lmask, slen_mask/2) < 0)
217 invarg("ct: labels mask must be a hex string\n", mask);
218 } else {
219 memset(lmask, 0xff, sizeof(lmask));
220 slen_mask = sizeof(lmask)*2;
221 }
222 addattr_l(n, MAX_MSG, TCA_CT_LABELS_MASK, lmask, slen_mask/2);
223
224 return 0;
225}
226
227static int
38b0e6c1 228parse_ct(const struct action_util *a, int *argc_p, char ***argv_p, int tca_id,
c8a49431
PB
229 struct nlmsghdr *n)
230{
231 struct tc_ct sel = {};
232 char **argv = *argv_p;
233 struct rtattr *tail;
234 int argc = *argc_p;
235 int ct_action = 0;
236 int ret;
237
238 tail = addattr_nest(n, MAX_MSG, tca_id);
239
240 if (argc && matches(*argv, "ct") == 0)
241 NEXT_ARG_FWD();
242
243 while (argc > 0) {
244 if (matches(*argv, "zone") == 0) {
245 NEXT_ARG();
246
247 if (ct_parse_u16(*argv,
248 TCA_CT_ZONE, TCA_CT_UNSPEC, n)) {
249 fprintf(stderr, "ct: Illegal \"zone\"\n");
250 return -1;
251 }
252 } else if (matches(*argv, "nat") == 0) {
253 ct_action |= TCA_CT_ACT_NAT;
254
255 NEXT_ARG();
256 if (matches(*argv, "src") == 0)
257 ct_action |= TCA_CT_ACT_NAT_SRC;
258 else if (matches(*argv, "dst") == 0)
259 ct_action |= TCA_CT_ACT_NAT_DST;
260 else
261 continue;
262
263 NEXT_ARG();
264 if (matches(*argv, "addr") != 0)
265 usage();
266
267 NEXT_ARG();
268 ret = ct_parse_nat_addr_range(*argv, n);
269 if (ret) {
270 fprintf(stderr, "ct: Illegal nat address range\n");
271 return -1;
272 }
273
4de59102 274 NEXT_ARG();
c8a49431
PB
275 if (matches(*argv, "port") != 0)
276 continue;
277
278 NEXT_ARG();
279 ret = ct_parse_nat_port_range(*argv, n);
280 if (ret) {
281 fprintf(stderr, "ct: Illegal nat port range\n");
282 return -1;
283 }
284 } else if (matches(*argv, "clear") == 0) {
285 ct_action |= TCA_CT_ACT_CLEAR;
286 } else if (matches(*argv, "commit") == 0) {
287 ct_action |= TCA_CT_ACT_COMMIT;
288 } else if (matches(*argv, "force") == 0) {
289 ct_action |= TCA_CT_ACT_FORCE;
290 } else if (matches(*argv, "index") == 0) {
291 NEXT_ARG();
292 if (get_u32(&sel.index, *argv, 10)) {
293 fprintf(stderr, "ct: Illegal \"index\"\n");
294 return -1;
295 }
296 } else if (matches(*argv, "mark") == 0) {
297 NEXT_ARG();
298
299 ret = ct_parse_mark(*argv, n);
300 if (ret) {
301 fprintf(stderr, "ct: Illegal \"mark\"\n");
302 return -1;
303 }
304 } else if (matches(*argv, "label") == 0) {
305 NEXT_ARG();
306
307 ret = ct_parse_labels(*argv, n);
308 if (ret) {
309 fprintf(stderr, "ct: Illegal \"label\"\n");
310 return -1;
311 }
312 } else if (matches(*argv, "help") == 0) {
313 usage();
4cdce041
XL
314 } else if (matches(*argv, "helper") == 0) {
315 NEXT_ARG();
316
317 ret = ct_parse_helper(*argv, n);
318 if (ret) {
319 fprintf(stderr, "ct: Illegal \"helper\"\n");
320 return -1;
321 }
c8a49431
PB
322 } else {
323 break;
324 }
325 NEXT_ARG_FWD();
326 }
327
328 if (ct_action & TCA_CT_ACT_CLEAR &&
329 ct_action & ~TCA_CT_ACT_CLEAR) {
330 fprintf(stderr, "ct: clear can only be used alone\n");
331 return -1;
332 }
333
334 if (ct_action & TCA_CT_ACT_NAT_SRC &&
335 ct_action & TCA_CT_ACT_NAT_DST) {
336 fprintf(stderr, "ct: src and dst nat can't be used together\n");
337 return -1;
338 }
339
340 if ((ct_action & TCA_CT_ACT_COMMIT) &&
341 (ct_action & TCA_CT_ACT_NAT) &&
342 !(ct_action & (TCA_CT_ACT_NAT_SRC | TCA_CT_ACT_NAT_DST))) {
343 fprintf(stderr, "ct: commit and nat must set src or dst\n");
344 return -1;
345 }
346
347 if (!(ct_action & TCA_CT_ACT_COMMIT) &&
348 (ct_action & (TCA_CT_ACT_NAT_SRC | TCA_CT_ACT_NAT_DST))) {
349 fprintf(stderr, "ct: src or dst is only valid if commit is set\n");
350 return -1;
351 }
352
353 parse_action_control_dflt(&argc, &argv, &sel.action, false,
354 TC_ACT_PIPE);
c8a49431
PB
355
356 addattr16(n, MAX_MSG, TCA_CT_ACTION, ct_action);
357 addattr_l(n, MAX_MSG, TCA_CT_PARMS, &sel, sizeof(sel));
358 addattr_nest_end(n, tail);
359
360 *argc_p = argc;
361 *argv_p = argv;
362 return 0;
363}
364
365static int ct_sprint_port(char *buf, const char *prefix, struct rtattr *attr)
366{
367 if (!attr)
368 return 0;
369
370 return sprintf(buf, "%s%d", prefix, rta_getattr_be16(attr));
371}
372
373static int ct_sprint_ip_addr(char *buf, const char *prefix,
374 struct rtattr *attr)
375{
376 int family;
377 size_t len;
378
379 if (!attr)
380 return 0;
381
382 len = RTA_PAYLOAD(attr);
383
384 if (len == 4)
385 family = AF_INET;
386 else if (len == 16)
387 family = AF_INET6;
388 else
389 return 0;
390
391 return sprintf(buf, "%s%s", prefix, rt_addr_n2a_rta(family, attr));
392}
393
394static void ct_print_nat(int ct_action, struct rtattr **tb)
395{
396 size_t done = 0;
397 char out[256] = "";
cad1b0bc 398 bool nat = false;
c8a49431
PB
399
400 if (!(ct_action & TCA_CT_ACT_NAT))
401 return;
402
403 if (ct_action & TCA_CT_ACT_NAT_SRC) {
404 nat = true;
405 done += sprintf(out + done, "src");
406 } else if (ct_action & TCA_CT_ACT_NAT_DST) {
407 nat = true;
408 done += sprintf(out + done, "dst");
409 }
410
411 if (nat) {
412 done += ct_sprint_ip_addr(out + done, " addr ",
413 tb[TCA_CT_NAT_IPV4_MIN]);
414 done += ct_sprint_ip_addr(out + done, " addr ",
415 tb[TCA_CT_NAT_IPV6_MIN]);
416 if (tb[TCA_CT_NAT_IPV4_MAX] &&
417 memcmp(RTA_DATA(tb[TCA_CT_NAT_IPV4_MIN]),
418 RTA_DATA(tb[TCA_CT_NAT_IPV4_MAX]), 4))
419 done += ct_sprint_ip_addr(out + done, "-",
420 tb[TCA_CT_NAT_IPV4_MAX]);
421 else if (tb[TCA_CT_NAT_IPV6_MAX] &&
422 memcmp(RTA_DATA(tb[TCA_CT_NAT_IPV6_MIN]),
423 RTA_DATA(tb[TCA_CT_NAT_IPV6_MAX]), 16))
424 done += ct_sprint_ip_addr(out + done, "-",
425 tb[TCA_CT_NAT_IPV6_MAX]);
426 done += ct_sprint_port(out + done, " port ",
427 tb[TCA_CT_NAT_PORT_MIN]);
428 if (tb[TCA_CT_NAT_PORT_MAX] &&
429 memcmp(RTA_DATA(tb[TCA_CT_NAT_PORT_MIN]),
430 RTA_DATA(tb[TCA_CT_NAT_PORT_MAX]), 2))
431 done += ct_sprint_port(out + done, "-",
432 tb[TCA_CT_NAT_PORT_MAX]);
433 }
434
435 if (done)
436 print_string(PRINT_ANY, "nat", " nat %s", out);
437 else
438 print_string(PRINT_ANY, "nat", " nat", "");
439}
440
441static void ct_print_labels(struct rtattr *attr,
442 struct rtattr *mask_attr)
443{
444 const unsigned char *str;
445 bool print_mask = false;
446 char out[256], *p;
447 int data_len, i;
448
449 if (!attr)
450 return;
451
452 data_len = RTA_PAYLOAD(attr);
453 hexstring_n2a(RTA_DATA(attr), data_len, out, sizeof(out));
454 p = out + data_len*2;
455
456 data_len = RTA_PAYLOAD(attr);
457 str = RTA_DATA(mask_attr);
458 if (data_len != 16)
459 print_mask = true;
460 for (i = 0; !print_mask && i < data_len; i++) {
461 if (str[i] != 0xff)
462 print_mask = true;
463 }
464 if (print_mask) {
465 *p++ = '/';
466 hexstring_n2a(RTA_DATA(mask_attr), data_len, p,
467 sizeof(out)-(p-out));
468 p += data_len*2;
469 }
470 *p = '\0';
471
472 print_string(PRINT_ANY, "label", " label %s", out);
473}
474
4cdce041
XL
475static void ct_print_helper(struct rtattr *family, struct rtattr *proto, struct rtattr *name)
476{
477 char helper[32], buf[32], *n;
478 int *f, *p;
479
480 if (!family || !proto || !name)
481 return;
482
483 f = RTA_DATA(family);
484 p = RTA_DATA(proto);
485 n = RTA_DATA(name);
486 snprintf(helper, sizeof(helper), "%s-%s-%s", (*f == AF_INET) ? "ipv4" : "ipv6",
487 inet_proto_n2a(*p, buf, sizeof(buf)), n);
488 print_string(PRINT_ANY, "helper", " helper %s", helper);
489}
490
38b0e6c1 491static int print_ct(const struct action_util *au, FILE *f, struct rtattr *arg)
c8a49431
PB
492{
493 struct rtattr *tb[TCA_CT_MAX + 1];
494 const char *commit;
495 struct tc_ct *p;
496 int ct_action = 0;
497
a99ebeee 498 print_string(PRINT_ANY, "kind", "%s", "ct");
c8a49431 499 if (arg == NULL)
a99ebeee 500 return 0;
c8a49431
PB
501
502 parse_rtattr_nested(tb, TCA_CT_MAX, arg);
503 if (tb[TCA_CT_PARMS] == NULL) {
504 print_string(PRINT_FP, NULL, "%s", "[NULL ct parameters]");
505 return -1;
506 }
507
508 p = RTA_DATA(tb[TCA_CT_PARMS]);
509
c8a49431
PB
510 if (tb[TCA_CT_ACTION])
511 ct_action = rta_getattr_u16(tb[TCA_CT_ACTION]);
512 if (ct_action & TCA_CT_ACT_COMMIT) {
513 commit = ct_action & TCA_CT_ACT_FORCE ?
514 "commit force" : "commit";
515 print_string(PRINT_ANY, "action", " %s", commit);
516 } else if (ct_action & TCA_CT_ACT_CLEAR) {
517 print_string(PRINT_ANY, "action", " %s", "clear");
518 }
519
746e6c0f
EB
520 print_masked_u32("mark", tb[TCA_CT_MARK], tb[TCA_CT_MARK_MASK], false);
521 print_masked_u16("zone", tb[TCA_CT_ZONE], NULL, false);
c8a49431 522 ct_print_labels(tb[TCA_CT_LABELS], tb[TCA_CT_LABELS_MASK]);
4cdce041 523 ct_print_helper(tb[TCA_CT_HELPER_FAMILY], tb[TCA_CT_HELPER_PROTO], tb[TCA_CT_HELPER_NAME]);
c8a49431
PB
524 ct_print_nat(ct_action, tb);
525
526 print_action_control(f, " ", p->action, "");
527
7b0d424a
SH
528 print_nl();
529 print_uint(PRINT_ANY, "index", "\t index %u", p->index);
c8a49431
PB
530 print_int(PRINT_ANY, "ref", " ref %d", p->refcnt);
531 print_int(PRINT_ANY, "bind", " bind %d", p->bindcnt);
532
533 if (show_stats) {
534 if (tb[TCA_CT_TM]) {
535 struct tcf_t *tm = RTA_DATA(tb[TCA_CT_TM]);
536
537 print_tm(f, tm);
538 }
539 }
7b0d424a 540 print_nl();
c8a49431
PB
541
542 return 0;
543}
544
545struct action_util ct_action_util = {
546 .id = "ct",
547 .parse_aopt = parse_ct,
548 .print_aopt = print_ct,
549};