1 // SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB
3 * m_ct.c Connection tracking action
5 * Authors: Paul Blakey <paulb@mellanox.com>
6 * Yossi Kuperman <yossiku@mellanox.com>
7 * Marcelo Ricardo Leitner <marcelo.leitner@gmail.com>
16 #include <linux/tc_act/tc_ct.h>
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"
31 static int ct_parse_nat_addr_range(const char *str
, struct nlmsghdr
*n
)
33 inet_prefix addr
= { .family
= AF_UNSPEC
, };
34 char *addr1
, *addr2
= 0;
39 strncpy(buffer
, str
, sizeof(buffer
) - 1);
42 addr2
= strchr(addr1
, '-');
48 ret
= get_addr(&addr
, addr1
, AF_UNSPEC
);
51 attr
= addr
.family
== AF_INET
? TCA_CT_NAT_IPV4_MIN
:
53 addattr_l(n
, MAX_MSG
, attr
, addr
.data
, addr
.bytelen
);
56 ret
= get_addr(&addr
, addr2
, addr
.family
);
60 attr
= addr
.family
== AF_INET
? TCA_CT_NAT_IPV4_MAX
:
62 addattr_l(n
, MAX_MSG
, attr
, addr
.data
, addr
.bytelen
);
67 static int ct_parse_nat_port_range(const char *str
, struct nlmsghdr
*n
)
69 char *port1
, *port2
= 0;
74 strncpy(buffer
, str
, sizeof(buffer
) - 1);
77 port2
= strchr(port1
, '-');
83 ret
= get_be16(&port
, port1
, 10);
86 addattr16(n
, MAX_MSG
, TCA_CT_NAT_PORT_MIN
, port
);
89 ret
= get_be16(&port
, port2
, 10);
93 addattr16(n
, MAX_MSG
, TCA_CT_NAT_PORT_MAX
, port
);
99 static int ct_parse_u16(char *str
, int value_type
, int mask_type
,
105 if (mask_type
!= TCA_CT_UNSPEC
) {
106 slash
= strchr(str
, '/');
111 if (get_u16(&value
, str
, 0))
115 if (get_u16(&mask
, slash
+ 1, 0))
121 addattr16(n
, MAX_MSG
, value_type
, value
);
122 if (mask_type
!= TCA_CT_UNSPEC
)
123 addattr16(n
, MAX_MSG
, mask_type
, mask
);
128 static int ct_parse_u32(char *str
, int value_type
, int mask_type
,
134 slash
= strchr(str
, '/');
138 if (get_u32(&value
, str
, 0))
142 if (get_u32(&mask
, slash
+ 1, 0))
148 addattr32(n
, MAX_MSG
, value_type
, value
);
149 addattr32(n
, MAX_MSG
, mask_type
, mask
);
154 static int ct_parse_mark(char *str
, struct nlmsghdr
*n
)
156 return ct_parse_u32(str
, TCA_CT_MARK
, TCA_CT_MARK_MASK
, n
);
159 static int ct_parse_labels(char *str
, struct nlmsghdr
*n
)
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;
166 slash
= index(str
, '/');
170 slen_mask
= strlen(mask
);
174 if (slen
> LABELS_SIZE
*2 || slen_mask
> LABELS_SIZE
*2) {
177 snprintf(errmsg
, sizeof(errmsg
),
178 "%zd Max allowed size %d",
179 slen
, LABELS_SIZE
*2);
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);
188 if (hex2mem(mask
, lmask
, slen_mask
/2) < 0)
189 invarg("ct: labels mask must be a hex string\n", mask
);
191 memset(lmask
, 0xff, sizeof(lmask
));
192 slen_mask
= sizeof(lmask
)*2;
194 addattr_l(n
, MAX_MSG
, TCA_CT_LABELS_MASK
, lmask
, slen_mask
/2);
200 parse_ct(struct action_util
*a
, int *argc_p
, char ***argv_p
, int tca_id
,
203 struct tc_ct sel
= {};
204 char **argv
= *argv_p
;
210 tail
= addattr_nest(n
, MAX_MSG
, tca_id
);
212 if (argc
&& matches(*argv
, "ct") == 0)
216 if (matches(*argv
, "zone") == 0) {
219 if (ct_parse_u16(*argv
,
220 TCA_CT_ZONE
, TCA_CT_UNSPEC
, n
)) {
221 fprintf(stderr
, "ct: Illegal \"zone\"\n");
224 } else if (matches(*argv
, "nat") == 0) {
225 ct_action
|= TCA_CT_ACT_NAT
;
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
;
236 if (matches(*argv
, "addr") != 0)
240 ret
= ct_parse_nat_addr_range(*argv
, n
);
242 fprintf(stderr
, "ct: Illegal nat address range\n");
247 if (matches(*argv
, "port") != 0)
251 ret
= ct_parse_nat_port_range(*argv
, n
);
253 fprintf(stderr
, "ct: Illegal nat port range\n");
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) {
264 if (get_u32(&sel
.index
, *argv
, 10)) {
265 fprintf(stderr
, "ct: Illegal \"index\"\n");
268 } else if (matches(*argv
, "mark") == 0) {
271 ret
= ct_parse_mark(*argv
, n
);
273 fprintf(stderr
, "ct: Illegal \"mark\"\n");
276 } else if (matches(*argv
, "label") == 0) {
279 ret
= ct_parse_labels(*argv
, n
);
281 fprintf(stderr
, "ct: Illegal \"label\"\n");
284 } else if (matches(*argv
, "help") == 0) {
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");
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");
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");
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");
317 parse_action_control_dflt(&argc
, &argv
, &sel
.action
, false,
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
);
329 static int ct_sprint_port(char *buf
, const char *prefix
, struct rtattr
*attr
)
334 return sprintf(buf
, "%s%d", prefix
, rta_getattr_be16(attr
));
337 static int ct_sprint_ip_addr(char *buf
, const char *prefix
,
346 len
= RTA_PAYLOAD(attr
);
355 return sprintf(buf
, "%s%s", prefix
, rt_addr_n2a_rta(family
, attr
));
358 static void ct_print_nat(int ct_action
, struct rtattr
**tb
)
364 if (!(ct_action
& TCA_CT_ACT_NAT
))
367 if (ct_action
& TCA_CT_ACT_NAT_SRC
) {
369 done
+= sprintf(out
+ done
, "src");
370 } else if (ct_action
& TCA_CT_ACT_NAT_DST
) {
372 done
+= sprintf(out
+ done
, "dst");
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
]);
400 print_string(PRINT_ANY
, "nat", " nat %s", out
);
402 print_string(PRINT_ANY
, "nat", " nat", "");
405 static void ct_print_labels(struct rtattr
*attr
,
406 struct rtattr
*mask_attr
)
408 const unsigned char *str
;
409 bool print_mask
= false;
416 data_len
= RTA_PAYLOAD(attr
);
417 hexstring_n2a(RTA_DATA(attr
), data_len
, out
, sizeof(out
));
418 p
= out
+ data_len
*2;
420 data_len
= RTA_PAYLOAD(attr
);
421 str
= RTA_DATA(mask_attr
);
424 for (i
= 0; !print_mask
&& i
< data_len
; i
++) {
430 hexstring_n2a(RTA_DATA(mask_attr
), data_len
, p
,
431 sizeof(out
)-(p
-out
));
436 print_string(PRINT_ANY
, "label", " label %s", out
);
439 static int print_ct(struct action_util
*au
, FILE *f
, struct rtattr
*arg
)
441 struct rtattr
*tb
[TCA_CT_MAX
+ 1];
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]");
455 p
= RTA_DATA(tb
[TCA_CT_PARMS
]);
457 print_string(PRINT_ANY
, "kind", "%s", "ct");
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");
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
);
474 print_action_control(f
, " ", p
->action
, "");
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
);
482 struct tcf_t
*tm
= RTA_DATA(tb
[TCA_CT_TM
]);
487 print_string(PRINT_FP
, NULL
, "%s", "\n ");
492 struct action_util ct_action_util
= {
494 .parse_aopt
= parse_ct
,
495 .print_aopt
= print_ct
,