From dd6785c1068131b0ba636713147228d1be53ffd9 Mon Sep 17 00:00:00 2001
From: Kevin Darbyshire-Bryant <ldir@darbyshire-bryant.me.uk>
Date: Fri, 15 Mar 2019 09:35:37 +0000
Subject: [PATCH] initial experimental support for act_conndscp

Signed-off-by: Kevin Darbyshire-Bryant <ldir@darbyshire-bryant.me.uk>
---
 include/uapi/linux/tc_act/tc_conndscp.h |  33 ++++
 tc/Makefile                             |   1 +
 tc/m_conndscp.c                         | 203 ++++++++++++++++++++++++
 3 files changed, 237 insertions(+)
 create mode 100644 include/uapi/linux/tc_act/tc_conndscp.h
 create mode 100644 tc/m_conndscp.c

diff --git a/include/uapi/linux/tc_act/tc_conndscp.h b/include/uapi/linux/tc_act/tc_conndscp.h
new file mode 100644
index 00000000..e857833b
--- /dev/null
+++ b/include/uapi/linux/tc_act/tc_conndscp.h
@@ -0,0 +1,33 @@
+/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
+#ifndef __UAPI_TC_CONNDSCP_H
+#define __UAPI_TC_CONNDSCP_H
+
+#include <linux/types.h>
+#include <linux/pkt_cls.h>
+
+#define TCA_ACT_CONNDSCP 99
+
+struct tc_conndscp {
+	tc_gen;
+	__u16 zone;
+	__u32 mask;
+	__u32 statemask;
+	__u8 mode;
+	__u8 maskshift;
+};
+
+enum {
+	TCA_CONNDSCP_UNSPEC,
+	TCA_CONNDSCP_PARMS,
+	TCA_CONNDSCP_TM,
+	TCA_CONNDSCP_PAD,
+	__TCA_CONNDSCP_MAX
+};
+#define TCA_CONNDSCP_MAX (__TCA_CONNDSCP_MAX - 1)
+
+enum {
+	CONNDSCP_FLAG_GETDSCP	= BIT(0),
+	CONNDSCP_FLAG_SETDSCP	= BIT(1)
+};
+
+#endif
diff --git a/tc/Makefile b/tc/Makefile
index 2edaf2c8..6ab64f0f 100644
--- a/tc/Makefile
+++ b/tc/Makefile
@@ -47,6 +47,7 @@ TCMODULES += m_skbmod.o
 TCMODULES += m_csum.o
 TCMODULES += m_simple.o
 TCMODULES += m_vlan.o
+TCMODULES += m_conndscp.o
 TCMODULES += m_connmark.o
 TCMODULES += m_bpf.o
 TCMODULES += m_tunnel_key.o
diff --git a/tc/m_conndscp.c b/tc/m_conndscp.c
new file mode 100644
index 00000000..56c7a555
--- /dev/null
+++ b/tc/m_conndscp.c
@@ -0,0 +1,203 @@
+/*
+ * m_conndscp.c		netfilter conndscp dscp<->conntrack mark action
+ *
+ * Copyright (c) 2019 Kevin Darbyshire-Bryant <ldir@darbyshire-bryant.me.uk>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, see <http://www.gnu.org/licenses>.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include "utils.h"
+#include "tc_util.h"
+#include <linux/tc_act/tc_conndscp.h>
+
+static const char * conndscp_modes[] = {
+	"?invalid",
+	"get",
+	"set",
+	"both",
+};
+
+static void
+explain(void)
+{
+	fprintf(stderr, "Usage: ... conndscp mask MASK statemask STATEMASK mode set/get/both [zone ZONE] [CONTROL] [index <INDEX>]\n");
+	fprintf(stderr, "where :\n"
+		"\tMASK is the bitmask to store/restore DSCP\n"
+		"\tSTATEMASK is the bitmask to determine conditional storing/restoring\n"
+		"\tMODE get (typically ingress) set (typically egress)\n"
+		"\tZONE is the conntrack zone\n"
+		"\tCONTROL := reclassify | pipe | drop | continue | ok |\n"
+		"\t           goto chain <CHAIN_INDEX>\n");
+}
+
+static void
+usage(void)
+{
+	explain();
+	exit(-1);
+}
+
+static int
+parse_conndscp(struct action_util *a, int *argc_p, char ***argv_p, int tca_id,
+	      struct nlmsghdr *n)
+{
+	struct tc_conndscp sel = {};
+	char **argv = *argv_p;
+	int argc = *argc_p;
+	int ok = 0;
+	struct rtattr *tail;
+
+	while (argc > 0) {
+		if (matches(*argv, "conndscp") == 0) {
+			ok = 1;
+			argc--;
+			argv++;
+		} else if (matches(*argv, "help") == 0) {
+			usage();
+		} else {
+			break;
+		}
+
+	}
+
+	if (!ok) {
+		explain();
+		return -1;
+	}
+
+	if (argc) {
+		if (matches(*argv, "mask") == 0) {
+			NEXT_ARG();
+			if (get_u32(&sel.mask, *argv, 0)) {
+				fprintf(stderr, "conndscp: Illegal \"mask\"\n");
+				return -1;
+			}
+			argc--;
+			argv++;
+		}
+	}
+
+	if (argc) {
+		if (matches(*argv, "statemask") == 0) {
+			NEXT_ARG();
+			if (get_u32(&sel.statemask, *argv, 0)) {
+				fprintf(stderr, "conndscp: Illegal \"statemask\"\n");
+				return -1;
+			}
+			argc--;
+			argv++;
+		}
+	}
+
+	if (argc) {
+		if (matches(*argv, "mode") == 0) {
+			NEXT_ARG();
+			if (matches(*argv, "set") == 0)
+				sel.mode |= CONNDSCP_FLAG_SETDSCP;
+			else if (matches(*argv, "get") == 0)
+				sel.mode |= CONNDSCP_FLAG_GETDSCP;
+			else if (matches(*argv, "both") == 0)
+				sel.mode |= (CONNDSCP_FLAG_GETDSCP | CONNDSCP_FLAG_SETDSCP);
+			else {
+				fprintf(stderr, "conndscp: Illegal \"mode\"\n");
+				return -1;
+			}
+			argc--;
+			argv++;
+		}
+	}
+
+	if (argc) {
+		if (matches(*argv, "zone") == 0) {
+			NEXT_ARG();
+			if (get_u16(&sel.zone, *argv, 10)) {
+				fprintf(stderr, "conndscp: Illegal \"zone\"\n");
+				return -1;
+			}
+			argc--;
+			argv++;
+		}
+	}
+
+	parse_action_control_dflt(&argc, &argv, &sel.action, false, TC_ACT_PIPE);
+
+	if (argc) {
+		if (matches(*argv, "index") == 0) {
+			NEXT_ARG();
+			if (get_u32(&sel.index, *argv, 10)) {
+				fprintf(stderr, "conndscp: Illegal \"index\"\n");
+				return -1;
+			}
+			argc--;
+			argv++;
+		}
+	}
+
+	tail = addattr_nest(n, MAX_MSG, tca_id);
+	addattr_l(n, MAX_MSG, TCA_CONNDSCP_PARMS, &sel, sizeof(sel));
+	addattr_nest_end(n, tail);
+
+	*argc_p = argc;
+	*argv_p = argv;
+	return 0;
+}
+
+static int print_conndscp(struct action_util *au, FILE *f, struct rtattr *arg)
+{
+	struct rtattr *tb[TCA_CONNDSCP_MAX + 1];
+	struct tc_conndscp *ci;
+
+	if (arg == NULL)
+		return -1;
+
+	parse_rtattr_nested(tb, TCA_CONNDSCP_MAX, arg);
+	if (tb[TCA_CONNDSCP_PARMS] == NULL) {
+		print_string(PRINT_FP, NULL, "%s", "[NULL conndscp parameters]");
+		return -1;
+	}
+
+	ci = RTA_DATA(tb[TCA_CONNDSCP_PARMS]);
+
+	print_string(PRINT_ANY, "kind", "%s ", "conndscp");
+	print_uint(PRINT_ANY, "zone", "zone %u", ci->zone);
+	print_action_control(f, " ", ci->action, "");
+
+	print_string(PRINT_FP, NULL, "%s", _SL_);
+	print_uint(PRINT_ANY, "index", "\t index %u", ci->index);
+	print_int(PRINT_ANY, "ref", " ref %d", ci->refcnt);
+	print_int(PRINT_ANY, "bind", " bind %d", ci->bindcnt);
+	print_uint(PRINT_ANY, "mask", " mask 0x%08x", ci->mask);
+	print_uint(PRINT_ANY, "statemask", " statemask 0x%08x", ci->statemask);
+	print_string(PRINT_ANY, "mode", " mode %s", conndscp_modes[ci->mode & 0x3]);
+
+	if (show_stats) {
+		if (tb[TCA_CONNDSCP_TM]) {
+			struct tcf_t *tm = RTA_DATA(tb[TCA_CONNDSCP_TM]);
+
+			print_tm(f, tm);
+		}
+	}
+	print_string(PRINT_FP, NULL, "%s", _SL_);
+
+	return 0;
+}
+
+struct action_util conndscp_action_util = {
+	.id = "conndscp",
+	.parse_aopt = parse_conndscp,
+	.print_aopt = print_conndscp,
+};
-- 
2.17.2 (Apple Git-113)

