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