Blame tc/m_ctinfo.c

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