Blame tc/m_ctinfo.c

Packit Service 3880ab
/* SPDX-License-Identifier: GPL-2.0 */
Packit Service 3880ab
/*
Packit Service 3880ab
 * m_ctinfo.c		netfilter ctinfo mark action
Packit Service 3880ab
 *
Packit Service 3880ab
 * Copyright (c) 2019 Kevin Darbyshire-Bryant <ldir@darbyshire-bryant.me.uk>
Packit Service 3880ab
 */
Packit Service 3880ab
Packit Service 3880ab
#include <stdio.h>
Packit Service 3880ab
#include <stdlib.h>
Packit Service 3880ab
#include <unistd.h>
Packit Service 3880ab
#include <string.h>
Packit Service 3880ab
#include "utils.h"
Packit Service 3880ab
#include "tc_util.h"
Packit Service 3880ab
#include <linux/tc_act/tc_ctinfo.h>
Packit Service 3880ab
Packit Service 3880ab
static void
Packit Service 3880ab
explain(void)
Packit Service 3880ab
{
Packit Service 3880ab
	fprintf(stderr,
Packit Service 3880ab
		"Usage: ... ctinfo [dscp mask [statemask]] [cpmark [mask]] [zone ZONE] [CONTROL] [index <INDEX>]\n"
Packit Service 3880ab
		"where :\n"
Packit Service 3880ab
		"\tdscp   MASK bitmask location of stored DSCP\n"
Packit Service 3880ab
		"\t       STATEMASK bitmask to determine conditional restoring\n"
Packit Service 3880ab
		"\tcpmark MASK mask applied to mark on restoration\n"
Packit Service 3880ab
		"\tZONE is the conntrack zone\n"
Packit Service 3880ab
		"\tCONTROL := reclassify | pipe | drop | continue | ok |\n"
Packit Service 3880ab
		"\t           goto chain <CHAIN_INDEX>\n");
Packit Service 3880ab
}
Packit Service 3880ab
Packit Service 3880ab
static void
Packit Service 3880ab
usage(void)
Packit Service 3880ab
{
Packit Service 3880ab
	explain();
Packit Service 3880ab
	exit(-1);
Packit Service 3880ab
}
Packit Service 3880ab
Packit Service 3880ab
static int
Packit Service 3880ab
parse_ctinfo(struct action_util *a, int *argc_p, char ***argv_p, int tca_id,
Packit Service 3880ab
	     struct nlmsghdr *n)
Packit Service 3880ab
{
Packit Service 3880ab
	unsigned int cpmarkmask = 0, dscpmask = 0, dscpstatemask = 0;
Packit Service 3880ab
	struct tc_ctinfo sel = {};
Packit Service 3880ab
	unsigned short zone = 0;
Packit Service 3880ab
	char **argv = *argv_p;
Packit Service 3880ab
	struct rtattr *tail;
Packit Service 3880ab
	int argc = *argc_p;
Packit Service 3880ab
	int ok = 0;
Packit Service 3880ab
	__u8 i;
Packit Service 3880ab
Packit Service 3880ab
	while (argc > 0) {
Packit Service 3880ab
		if (matches(*argv, "ctinfo") == 0) {
Packit Service 3880ab
			ok = 1;
Packit Service 3880ab
			NEXT_ARG_FWD();
Packit Service 3880ab
		} else if (matches(*argv, "help") == 0) {
Packit Service 3880ab
			usage();
Packit Service 3880ab
		} else {
Packit Service 3880ab
			break;
Packit Service 3880ab
		}
Packit Service 3880ab
Packit Service 3880ab
	}
Packit Service 3880ab
Packit Service 3880ab
	if (!ok) {
Packit Service 3880ab
		explain();
Packit Service 3880ab
		return -1;
Packit Service 3880ab
	}
Packit Service 3880ab
Packit Service 3880ab
	if (argc) {
Packit Service 3880ab
		if (matches(*argv, "dscp") == 0) {
Packit Service 3880ab
			NEXT_ARG();
Packit Service 3880ab
			if (get_u32(&dscpmask, *argv, 0)) {
Packit Service 3880ab
				fprintf(stderr,
Packit Service 3880ab
					"ctinfo: Illegal dscp \"mask\"\n");
Packit Service 3880ab
				return -1;
Packit Service 3880ab
			}
Packit Service 3880ab
			if (NEXT_ARG_OK()) {
Packit Service 3880ab
				NEXT_ARG_FWD();
Packit Service 3880ab
				if (!get_u32(&dscpstatemask, *argv, 0))
Packit Service 3880ab
					NEXT_ARG_FWD(); /* was a statemask */
Packit Service 3880ab
			} else {
Packit Service 3880ab
				NEXT_ARG_FWD();
Packit Service 3880ab
			}
Packit Service 3880ab
		}
Packit Service 3880ab
	}
Packit Service 3880ab
Packit Service 3880ab
	/* cpmark has optional mask parameter, so the next arg might not  */
Packit Service 3880ab
	/* exist, or it might be the next option, or it may actually be a */
Packit Service 3880ab
	/* 32bit mask */
Packit Service 3880ab
	if (argc) {
Packit Service 3880ab
		if (matches(*argv, "cpmark") == 0) {
Packit Service 3880ab
			cpmarkmask = ~0;
Packit Service 3880ab
			if (NEXT_ARG_OK()) {
Packit Service 3880ab
				NEXT_ARG_FWD();
Packit Service 3880ab
				if (!get_u32(&cpmarkmask, *argv, 0))
Packit Service 3880ab
					NEXT_ARG_FWD(); /* was a mask */
Packit Service 3880ab
			} else {
Packit Service 3880ab
				NEXT_ARG_FWD();
Packit Service 3880ab
			}
Packit Service 3880ab
		}
Packit Service 3880ab
	}
Packit Service 3880ab
Packit Service 3880ab
	if (argc) {
Packit Service 3880ab
		if (matches(*argv, "zone") == 0) {
Packit Service 3880ab
			NEXT_ARG();
Packit Service 3880ab
			if (get_u16(&zone, *argv, 10)) {
Packit Service 3880ab
				fprintf(stderr, "ctinfo: Illegal \"zone\"\n");
Packit Service 3880ab
				return -1;
Packit Service 3880ab
			}
Packit Service 3880ab
			NEXT_ARG_FWD();
Packit Service 3880ab
		}
Packit Service 3880ab
	}
Packit Service 3880ab
Packit Service 3880ab
	parse_action_control_dflt(&argc, &argv, &sel.action,
Packit Service 3880ab
				  false, TC_ACT_PIPE);
Packit Service 3880ab
Packit Service 3880ab
	if (argc) {
Packit Service 3880ab
		if (matches(*argv, "index") == 0) {
Packit Service 3880ab
			NEXT_ARG();
Packit Service 3880ab
			if (get_u32(&sel.index, *argv, 10)) {
Packit Service 3880ab
				fprintf(stderr, "ctinfo: Illegal \"index\"\n");
Packit Service 3880ab
				return -1;
Packit Service 3880ab
			}
Packit Service 3880ab
			NEXT_ARG_FWD();
Packit Service 3880ab
		}
Packit Service 3880ab
	}
Packit Service 3880ab
Packit Service 3880ab
	if (dscpmask & dscpstatemask) {
Packit Service 3880ab
		fprintf(stderr,
Packit Service 3880ab
			"ctinfo: dscp mask & statemask must NOT overlap\n");
Packit Service 3880ab
		return -1;
Packit Service 3880ab
	}
Packit Service 3880ab
Packit Service 3880ab
	i = ffs(dscpmask);
Packit Service 3880ab
	if (i && ((~0 & (dscpmask >> (i - 1))) != 0x3f)) {
Packit Service 3880ab
		fprintf(stderr,
Packit Service 3880ab
			"ctinfo: dscp mask must be 6 contiguous bits long\n");
Packit Service 3880ab
		return -1;
Packit Service 3880ab
	}
Packit Service 3880ab
Packit Service 3880ab
	tail = addattr_nest(n, MAX_MSG, tca_id);
Packit Service 3880ab
	addattr_l(n, MAX_MSG, TCA_CTINFO_ACT, &sel, sizeof(sel));
Packit Service 3880ab
	addattr16(n, MAX_MSG, TCA_CTINFO_ZONE, zone);
Packit Service 3880ab
Packit Service 3880ab
	if (dscpmask)
Packit Service 3880ab
		addattr32(n, MAX_MSG,
Packit Service 3880ab
			  TCA_CTINFO_PARMS_DSCP_MASK, dscpmask);
Packit Service 3880ab
Packit Service 3880ab
	if (dscpstatemask)
Packit Service 3880ab
		addattr32(n, MAX_MSG,
Packit Service 3880ab
			  TCA_CTINFO_PARMS_DSCP_STATEMASK, dscpstatemask);
Packit Service 3880ab
Packit Service 3880ab
	if (cpmarkmask)
Packit Service 3880ab
		addattr32(n, MAX_MSG,
Packit Service 3880ab
			  TCA_CTINFO_PARMS_CPMARK_MASK, cpmarkmask);
Packit Service 3880ab
Packit Service 3880ab
	addattr_nest_end(n, tail);
Packit Service 3880ab
Packit Service 3880ab
	*argc_p = argc;
Packit Service 3880ab
	*argv_p = argv;
Packit Service 3880ab
	return 0;
Packit Service 3880ab
}
Packit Service 3880ab
Packit Service 3880ab
static void print_ctinfo_stats(FILE *f, struct rtattr *tb[TCA_CTINFO_MAX + 1])
Packit Service 3880ab
{
Packit Service 3880ab
	struct tcf_t *tm;
Packit Service 3880ab
Packit Service 3880ab
	if (tb[TCA_CTINFO_TM]) {
Packit Service 3880ab
		tm = RTA_DATA(tb[TCA_CTINFO_TM]);
Packit Service 3880ab
Packit Service 3880ab
		print_tm(f, tm);
Packit Service 3880ab
	}
Packit Service 3880ab
Packit Service 3880ab
	if (tb[TCA_CTINFO_STATS_DSCP_SET])
Packit Service 3880ab
		print_lluint(PRINT_ANY, "dscpset", " DSCP set %llu",
Packit Service 3880ab
			     rta_getattr_u64(tb[TCA_CTINFO_STATS_DSCP_SET]));
Packit Service 3880ab
	if (tb[TCA_CTINFO_STATS_DSCP_ERROR])
Packit Service 3880ab
		print_lluint(PRINT_ANY, "dscperror", " error %llu",
Packit Service 3880ab
			     rta_getattr_u64(tb[TCA_CTINFO_STATS_DSCP_ERROR]));
Packit Service 3880ab
Packit Service 3880ab
	if (tb[TCA_CTINFO_STATS_CPMARK_SET])
Packit Service 3880ab
		print_lluint(PRINT_ANY, "cpmarkset", " CPMARK set %llu",
Packit Service 3880ab
			     rta_getattr_u64(tb[TCA_CTINFO_STATS_CPMARK_SET]));
Packit Service 3880ab
}
Packit Service 3880ab
Packit Service 3880ab
static int print_ctinfo(struct action_util *au, FILE *f, struct rtattr *arg)
Packit Service 3880ab
{
Packit Service 3880ab
	unsigned int cpmarkmask = ~0, dscpmask = 0, dscpstatemask = 0;
Packit Service 3880ab
	struct rtattr *tb[TCA_CTINFO_MAX + 1];
Packit Service 3880ab
	unsigned short zone = 0;
Packit Service 3880ab
	struct tc_ctinfo *ci;
Packit Service 3880ab
Packit Service 3880ab
	if (arg == NULL)
Packit Service 3880ab
		return -1;
Packit Service 3880ab
Packit Service 3880ab
	parse_rtattr_nested(tb, TCA_CTINFO_MAX, arg);
Packit Service 3880ab
	if (!tb[TCA_CTINFO_ACT]) {
Packit Service 3880ab
		print_string(PRINT_FP, NULL, "%s",
Packit Service 3880ab
			     "[NULL ctinfo action parameters]");
Packit Service 3880ab
		return -1;
Packit Service 3880ab
	}
Packit Service 3880ab
Packit Service 3880ab
	ci = RTA_DATA(tb[TCA_CTINFO_ACT]);
Packit Service 3880ab
Packit Service 3880ab
	if (tb[TCA_CTINFO_PARMS_DSCP_MASK]) {
Packit Service 3880ab
		if (RTA_PAYLOAD(tb[TCA_CTINFO_PARMS_DSCP_MASK]) >=
Packit Service 3880ab
		    sizeof(__u32))
Packit Service 3880ab
			dscpmask = rta_getattr_u32(
Packit Service 3880ab
					tb[TCA_CTINFO_PARMS_DSCP_MASK]);
Packit Service 3880ab
		else
Packit Service 3880ab
			print_string(PRINT_FP, NULL, "%s",
Packit Service 3880ab
				     "[invalid dscp mask parameter]");
Packit Service 3880ab
	}
Packit Service 3880ab
Packit Service 3880ab
	if (tb[TCA_CTINFO_PARMS_DSCP_STATEMASK]) {
Packit Service 3880ab
		if (RTA_PAYLOAD(tb[TCA_CTINFO_PARMS_DSCP_STATEMASK]) >=
Packit Service 3880ab
		    sizeof(__u32))
Packit Service 3880ab
			dscpstatemask = rta_getattr_u32(
Packit Service 3880ab
					tb[TCA_CTINFO_PARMS_DSCP_STATEMASK]);
Packit Service 3880ab
		else
Packit Service 3880ab
			print_string(PRINT_FP, NULL, "%s",
Packit Service 3880ab
				     "[invalid dscp statemask parameter]");
Packit Service 3880ab
	}
Packit Service 3880ab
Packit Service 3880ab
	if (tb[TCA_CTINFO_PARMS_CPMARK_MASK]) {
Packit Service 3880ab
		if (RTA_PAYLOAD(tb[TCA_CTINFO_PARMS_CPMARK_MASK]) >=
Packit Service 3880ab
		    sizeof(__u32))
Packit Service 3880ab
			cpmarkmask = rta_getattr_u32(
Packit Service 3880ab
					tb[TCA_CTINFO_PARMS_CPMARK_MASK]);
Packit Service 3880ab
		else
Packit Service 3880ab
			print_string(PRINT_FP, NULL, "%s",
Packit Service 3880ab
				     "[invalid cpmark mask parameter]");
Packit Service 3880ab
	}
Packit Service 3880ab
Packit Service 3880ab
	if (tb[TCA_CTINFO_ZONE] && RTA_PAYLOAD(tb[TCA_CTINFO_ZONE]) >=
Packit Service 3880ab
	    sizeof(__u16))
Packit Service 3880ab
		zone = rta_getattr_u16(tb[TCA_CTINFO_ZONE]);
Packit Service 3880ab
Packit Service 3880ab
	print_string(PRINT_ANY, "kind", "%s ", "ctinfo");
Packit Service 3880ab
	print_hu(PRINT_ANY, "zone", "zone %u", zone);
Packit Service 3880ab
	print_action_control(f, " ", ci->action, "");
Packit Service 3880ab
Packit Service 3880ab
	print_nl();
Packit Service 3880ab
	print_uint(PRINT_ANY, "index", "\t index %u", ci->index);
Packit Service 3880ab
	print_int(PRINT_ANY, "ref", " ref %d", ci->refcnt);
Packit Service 3880ab
	print_int(PRINT_ANY, "bind", " bind %d", ci->bindcnt);
Packit Service 3880ab
Packit Service 3880ab
	if (tb[TCA_CTINFO_PARMS_DSCP_MASK]) {
Packit Service 3880ab
		print_0xhex(PRINT_ANY, "dscpmask", " dscp %#010llx", dscpmask);
Packit Service 3880ab
		print_0xhex(PRINT_ANY, "dscpstatemask", " %#010llx",
Packit Service 3880ab
			    dscpstatemask);
Packit Service 3880ab
	}
Packit Service 3880ab
Packit Service 3880ab
	if (tb[TCA_CTINFO_PARMS_CPMARK_MASK])
Packit Service 3880ab
		print_0xhex(PRINT_ANY, "cpmark", " cpmark %#010llx",
Packit Service 3880ab
			    cpmarkmask);
Packit Service 3880ab
Packit Service 3880ab
	if (show_stats)
Packit Service 3880ab
		print_ctinfo_stats(f, tb);
Packit Service 3880ab
Packit Service 3880ab
	print_nl();
Packit Service 3880ab
Packit Service 3880ab
	return 0;
Packit Service 3880ab
}
Packit Service 3880ab
Packit Service 3880ab
struct action_util ctinfo_action_util = {
Packit Service 3880ab
	.id = "ctinfo",
Packit Service 3880ab
	.parse_aopt = parse_ctinfo,
Packit Service 3880ab
	.print_aopt = print_ctinfo,
Packit Service 3880ab
};