[OpenWrt-Devel] [PATCH] restoring dscp on ingress
Kevin 'ldir' Darbyshire-Bryant
ldir at darbyshire-bryant.me.uk
Mon May 27 14:07:53 EDT 2019
This was a test - please ignore. Already deleted from patchwork
> On 27 May 2019, at 19:02, Kevin 'ldir' Darbyshire-Bryant <ldir at darbyshire-bryant.me.uk> wrote:
>
> A combination of tc filter action act_ctinfo to do the restoration,
> a new savedscp option to iptables xt_connmark to store the DSCP
>
> Signed-off-by: Kevin Darbyshire-Bryant <ldir at darbyshire-bryant.me.uk>
> ---
> package/kernel/linux/modules/netsupport.mk | 10 +-
> ...-experimental-support-for-act_ctinfo.patch | 374 ++++++++++
> .../iptables/patches/0001-savedscp.patch | 213 ++++++
> ...et-sched-Introduce-act_ctinfo-action.patch | 636 +++++++++++++++++
> ...etfilter-connmark-introduce-savedscp.patch | 107 +++
> ...et-sched-Introduce-act_ctinfo-action.patch | 658 ++++++++++++++++++
> ...etfilter-connmark-introduce-savedscp.patch | 142 ++++
> 7 files changed, 2139 insertions(+), 1 deletion(-)
> create mode 100644 package/network/utils/iproute2/patches/0001-initial-experimental-support-for-act_ctinfo.patch
> create mode 100644 package/network/utils/iptables/patches/0001-savedscp.patch
> create mode 100644 target/linux/generic/hack-4.14/0001-net-sched-Introduce-act_ctinfo-action.patch
> create mode 100644 target/linux/generic/hack-4.14/0001-netfilter-connmark-introduce-savedscp.patch
> create mode 100644 target/linux/generic/hack-4.19/0001-net-sched-Introduce-act_ctinfo-action.patch
> create mode 100644 target/linux/generic/hack-4.19/0001-netfilter-connmark-introduce-savedscp.patch
>
> diff --git a/package/kernel/linux/modules/netsupport.mk b/package/kernel/linux/modules/netsupport.mk
> index d92992e13f..7ab6d3b1b3 100644
> --- a/package/kernel/linux/modules/netsupport.mk
> +++ b/package/kernel/linux/modules/netsupport.mk
> @@ -803,7 +803,6 @@ endef
>
> $(eval $(call KernelPackage,sched-mqprio))
>
> -
> define KernelPackage/sched-connmark
> SUBMENU:=$(NETWORK_SUPPORT_MENU)
> TITLE:=Traffic shaper conntrack mark support
> @@ -814,6 +813,15 @@ define KernelPackage/sched-connmark
> endef
> $(eval $(call KernelPackage,sched-connmark))
>
> +define KernelPackage/sched-ctinfo
> + SUBMENU:=$(NETWORK_SUPPORT_MENU)
> + TITLE:=Traffic shaper ctinfo support
> + DEPENDS:=+kmod-sched-core +kmod-ipt-core +kmod-ipt-conntrack-extra
> + KCONFIG:=CONFIG_NET_ACT_CTINFO
> + FILES:=$(LINUX_DIR)/net/sched/act_ctinfo.ko
> + AUTOLOAD:=$(call AutoLoad,71, act_ctinfo)
> +endef
> +$(eval $(call KernelPackage,sched-ctinfo))
>
> define KernelPackage/sched-ipset
> SUBMENU:=$(NETWORK_SUPPORT_MENU)
> diff --git a/package/network/utils/iproute2/patches/0001-initial-experimental-support-for-act_ctinfo.patch b/package/network/utils/iproute2/patches/0001-initial-experimental-support-for-act_ctinfo.patch
> new file mode 100644
> index 0000000000..6983e7faed
> --- /dev/null
> +++ b/package/network/utils/iproute2/patches/0001-initial-experimental-support-for-act_ctinfo.patch
> @@ -0,0 +1,374 @@
> +From 5dd8945255b679246041866285faab2e4348362c Mon Sep 17 00:00:00 2001
> +From: Kevin Darbyshire-Bryant <ldir at darbyshire-bryant.me.uk>
> +Date: Fri, 15 Mar 2019 09:35:37 +0000
> +Subject: [PATCH] initial experimental support for act_ctinfo
> +
> +switchtoctinfo
> +
> +Signed-off-by: Kevin Darbyshire-Bryant <ldir at darbyshire-bryant.me.uk>
> +---
> + include/uapi/linux/pkt_cls.h | 1 +
> + include/uapi/linux/tc_act/tc_ctinfo.h | 52 +++++
> + tc/Makefile | 1 +
> + tc/m_ctinfo.c | 266 ++++++++++++++++++++++++++
> + 4 files changed, 320 insertions(+)
> + create mode 100644 include/uapi/linux/tc_act/tc_ctinfo.h
> + create mode 100644 tc/m_ctinfo.c
> +
> +diff --git a/include/uapi/linux/pkt_cls.h b/include/uapi/linux/pkt_cls.h
> +index 95d0db2a..7df0e808 100644
> +--- a/include/uapi/linux/pkt_cls.h
> ++++ b/include/uapi/linux/pkt_cls.h
> +@@ -68,6 +68,7 @@ enum {
> + TCA_ID_UNSPEC=0,
> + TCA_ID_POLICE=1,
> + /* other actions go here */
> ++ TCA_ID_CTINFO=27,
> + __TCA_ID_MAX=255
> + };
> +
> +diff --git a/include/uapi/linux/tc_act/tc_ctinfo.h b/include/uapi/linux/tc_act/tc_ctinfo.h
> +new file mode 100644
> +index 00000000..48c40f65
> +--- /dev/null
> ++++ b/include/uapi/linux/tc_act/tc_ctinfo.h
> +@@ -0,0 +1,52 @@
> ++/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
> ++#ifndef __UAPI_TC_CTINFO_H
> ++#define __UAPI_TC_CTINFO_H
> ++
> ++#include <linux/types.h>
> ++#include <linux/pkt_cls.h>
> ++
> ++struct tc_ctinfo {
> ++ tc_gen;
> ++};
> ++
> ++struct tc_ctinfo_dscp {
> ++ __u32 mask;
> ++ __u32 statemask;
> ++};
> ++
> ++struct tc_ctinfo_cpmark {
> ++ __u32 mask;
> ++};
> ++
> ++struct tc_ctinfo_stats_dscp {
> ++ __u64 set;
> ++ __u64 error;
> ++};
> ++
> ++struct tc_ctinfo_stats_cpmark {
> ++ __u64 set;
> ++};
> ++
> ++enum {
> ++ TCA_CTINFO_UNSPEC,
> ++ TCA_CTINFO_PAD,
> ++ TCA_CTINFO_TM,
> ++ TCA_CTINFO_ACT,
> ++ TCA_CTINFO_ZONE,
> ++ TCA_CTINFO_PARMS_DSCP,
> ++ TCA_CTINFO_PARMS_CPMARK,
> ++ TCA_CTINFO_MODE_DSCP,
> ++ TCA_CTINFO_MODE_CPMARK,
> ++ TCA_CTINFO_STATS_DSCP,
> ++ TCA_CTINFO_STATS_CPMARK,
> ++ __TCA_CTINFO_MAX
> ++};
> ++
> ++#define TCA_CTINFO_MAX (__TCA_CTINFO_MAX - 1)
> ++
> ++enum {
> ++ CTINFO_MODE_DSCP = BIT(0),
> ++ CTINFO_MODE_CPMARK = BIT(1)
> ++};
> ++
> ++#endif
> +diff --git a/tc/Makefile b/tc/Makefile
> +index 2edaf2c8..ec93a9a1 100644
> +--- a/tc/Makefile
> ++++ b/tc/Makefile
> +@@ -48,6 +48,7 @@ TCMODULES += m_csum.o
> + TCMODULES += m_simple.o
> + TCMODULES += m_vlan.o
> + TCMODULES += m_connmark.o
> ++TCMODULES += m_ctinfo.o
> + TCMODULES += m_bpf.o
> + TCMODULES += m_tunnel_key.o
> + TCMODULES += m_sample.o
> +diff --git a/tc/m_ctinfo.c b/tc/m_ctinfo.c
> +new file mode 100644
> +index 00000000..1ee04452
> +--- /dev/null
> ++++ b/tc/m_ctinfo.c
> +@@ -0,0 +1,266 @@
> ++/*
> ++ * m_ctinfo.c netfilter ctinfo dscp<->ctinfo mark action
> ++ *
> ++ * Copyright (c) 2019 Kevin Darbyshire-Bryant <ldir at 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_ctinfo.h>
> ++
> ++static void
> ++explain(void)
> ++{
> ++ fprintf(stderr, "Usage: ... ctinfo [dscp mask[/statemask]] [mark [mask]] [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"
> ++ "\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_ctinfo(struct action_util *a, int *argc_p, char ***argv_p, int tca_id,
> ++ struct nlmsghdr *n)
> ++{
> ++ struct tc_ctinfo_cpmark cpmark = {
> ++ .mask = ~0
> ++ };
> ++ struct tc_ctinfo_dscp dscp = {};
> ++ struct tc_ctinfo sel = {};
> ++ char **argv = *argv_p;
> ++ int argc = *argc_p;
> ++ int ok = 0;
> ++ struct rtattr *tail;
> ++ unsigned short zone = 0;
> ++ int mark = 0;
> ++
> ++ while (argc > 0) {
> ++ if (matches(*argv, "ctinfo") == 0) {
> ++ ok = 1;
> ++ argc--;
> ++ argv++;
> ++ } else if (matches(*argv, "help") == 0) {
> ++ usage();
> ++ } else {
> ++ break;
> ++ }
> ++
> ++ }
> ++
> ++ if (!ok) {
> ++ explain();
> ++ return -1;
> ++ }
> ++
> ++ if (argc) {
> ++ if (matches(*argv, "dscp") == 0) {
> ++ NEXT_ARG();
> ++ char *slash;
> ++ if ((slash = strchr(*argv, '/')))
> ++ *slash = '\0';
> ++ if (get_u32(&dscp.mask, *argv, 0)) {
> ++ fprintf(stderr, "ctinfo: Illegal dscp \"mask\"\n");
> ++ return -1;
> ++ }
> ++ if (slash) {
> ++ if (get_u32(&dscp.statemask, slash + 1, 0)) {
> ++ fprintf(stderr, "ctinfo: Illegal dscp \"statemask\"\n");
> ++ return -1;
> ++ }
> ++ }
> ++ argc--;
> ++ argv++;
> ++ }
> ++ }
> ++
> ++ /* mark has optional mask parameter, so the next arg might not exist, */
> ++ /* or it might be the next option, or it may actually be a 32bit mask */
> ++ if (argc) {
> ++ if (matches(*argv, "mark") == 0) {
> ++ mark++;
> ++ if (NEXT_ARG_OK()) {
> ++ NEXT_ARG_FWD();
> ++ if (!get_u32(&cpmark.mask, *argv, 0)) {
> ++ NEXT_ARG_FWD(); /* was a mask */
> ++ }
> ++ } else {
> ++ NEXT_ARG_FWD();
> ++ }
> ++ }
> ++ }
> ++
> ++ if (argc) {
> ++ if (matches(*argv, "zone") == 0) {
> ++ NEXT_ARG();
> ++ if (get_u16(&zone, *argv, 10)) {
> ++ fprintf(stderr, "ctinfo: 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, "ctinfo: Illegal \"index\"\n");
> ++ return -1;
> ++ }
> ++ argc--;
> ++ argv++;
> ++ }
> ++ }
> ++
> ++ tail = addattr_nest(n, MAX_MSG, tca_id);
> ++ addattr_l(n, MAX_MSG, TCA_CTINFO_ACT, &sel, sizeof(sel));
> ++ if (zone)
> ++ addattr16(n, MAX_MSG, TCA_CTINFO_ZONE, zone);
> ++
> ++ if (dscp.mask) {
> ++ addattr(n, MAX_MSG, TCA_CTINFO_MODE_DSCP);
> ++ addattr_l(n, MAX_MSG, TCA_CTINFO_PARMS_DSCP, &dscp, sizeof(dscp));
> ++ }
> ++
> ++ if (mark) {
> ++ addattr(n, MAX_MSG, TCA_CTINFO_MODE_CPMARK);
> ++ addattr_l(n, MAX_MSG, TCA_CTINFO_PARMS_CPMARK, &cpmark, sizeof(cpmark));
> ++ }
> ++
> ++ addattr_nest_end(n, tail);
> ++
> ++ *argc_p = argc;
> ++ *argv_p = argv;
> ++ return 0;
> ++}
> ++
> ++static int print_ctinfo(struct action_util *au, FILE *f, struct rtattr *arg)
> ++{
> ++ struct tc_ctinfo_dscp *ci_cpmark = NULL;
> ++ struct tc_ctinfo_dscp *ci_dscp = NULL;
> ++ struct rtattr *tb[TCA_CTINFO_MAX + 1];
> ++ unsigned short zone = 0;
> ++ struct tc_ctinfo *ci;
> ++
> ++ if (arg == NULL)
> ++ return -1;
> ++
> ++ parse_rtattr_nested(tb, TCA_CTINFO_MAX, arg);
> ++ if (!tb[TCA_CTINFO_ACT]) {
> ++ print_string(PRINT_FP, NULL, "%s", "[NULL ctinfo parameters]");
> ++ return -1;
> ++ }
> ++
> ++ ci = RTA_DATA(tb[TCA_CTINFO_ACT]);
> ++
> ++ if (tb[TCA_CTINFO_MODE_DSCP]) {
> ++ if (!tb[TCA_CTINFO_PARMS_DSCP]) {
> ++ print_string(PRINT_FP, NULL, "%s", "[NULL dscp parameters]");
> ++ return -1;
> ++ } else {
> ++ if (RTA_PAYLOAD(tb[TCA_CTINFO_PARMS_DSCP]) >= sizeof(ci_dscp)) {
> ++ ci_dscp = RTA_DATA(tb[TCA_CTINFO_PARMS_DSCP]);
> ++ } else {
> ++ print_string(PRINT_FP, NULL, "%s", "[invalid dscp parameters]");
> ++ }
> ++ }
> ++ }
> ++
> ++ if (tb[TCA_CTINFO_MODE_CPMARK]) {
> ++ if (!tb[TCA_CTINFO_PARMS_CPMARK]) {
> ++ print_string(PRINT_FP, NULL, "%s", "[NULL cpmark parameters]");
> ++ return -1;
> ++ } else {
> ++ if (RTA_PAYLOAD(tb[TCA_CTINFO_PARMS_CPMARK]) >= sizeof(ci_cpmark)) {
> ++ ci_cpmark = RTA_DATA(tb[TCA_CTINFO_PARMS_CPMARK]);
> ++ } else {
> ++ print_string(PRINT_FP, NULL, "%s", "[invalid cpmark parameters]");
> ++ }
> ++ }
> ++ }
> ++
> ++ if (tb[TCA_CTINFO_ZONE] &&
> ++ RTA_PAYLOAD(tb[TCA_CTINFO_ZONE]) >= sizeof(__u16))
> ++ zone = rta_getattr_u16(tb[TCA_CTINFO_ZONE]);
> ++
> ++ print_string(PRINT_ANY, "kind", "%s ", "ctinfo");
> ++ print_hu(PRINT_ANY, "zone", "zone %u", 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);
> ++
> ++ if (tb[TCA_CTINFO_MODE_DSCP]) {
> ++ if (ci_dscp) {
> ++ print_0xhex(PRINT_ANY, "dscpmask", " dscp %#llx", ci_dscp->mask);
> ++ print_0xhex(PRINT_ANY, "dscpstatemask", "/%#llx", ci_dscp->statemask);
> ++ }
> ++ }
> ++
> ++ if (tb[TCA_CTINFO_MODE_CPMARK]) {
> ++ if (ci_cpmark) {
> ++ print_0xhex(PRINT_ANY, "mark", " mark %#llx", ci_cpmark->mask);
> ++ }
> ++ }
> ++
> ++ if (show_stats) {
> ++ if (tb[TCA_CTINFO_TM]) {
> ++ struct tcf_t *tm = RTA_DATA(tb[TCA_CTINFO_TM]);
> ++
> ++ print_tm(f, tm);
> ++ }
> ++ if (tb[TCA_CTINFO_STATS_DSCP]) {
> ++ struct tc_ctinfo_stats_dscp *dscpstats = RTA_DATA(tb[TCA_CTINFO_STATS_DSCP]);
> ++
> ++ if (dscpstats) {
> ++ print_lluint(PRINT_ANY, "dscpset", " DSCP set %llu", dscpstats->set);
> ++ print_lluint(PRINT_ANY, "dscperror", " error %llu", dscpstats->error);
> ++ }
> ++ }
> ++ if (tb[TCA_CTINFO_STATS_CPMARK]) {
> ++ struct tc_ctinfo_stats_cpmark *cpmarkstats = RTA_DATA(tb[TCA_CTINFO_STATS_CPMARK]);
> ++
> ++ if (cpmarkstats)
> ++ print_lluint(PRINT_ANY, "markset", " MARK set %llu", cpmarkstats->set);
> ++ }
> ++ }
> ++ print_string(PRINT_FP, NULL, "%s", _SL_);
> ++
> ++ return 0;
> ++}
> ++
> ++struct action_util ctinfo_action_util = {
> ++ .id = "ctinfo",
> ++ .parse_aopt = parse_ctinfo,
> ++ .print_aopt = print_ctinfo,
> ++};
> +--
> +2.20.1 (Apple Git-117)
> +
> diff --git a/package/network/utils/iptables/patches/0001-savedscp.patch b/package/network/utils/iptables/patches/0001-savedscp.patch
> new file mode 100644
> index 0000000000..396c8012ec
> --- /dev/null
> +++ b/package/network/utils/iptables/patches/0001-savedscp.patch
> @@ -0,0 +1,213 @@
> +From 69b427fb5e1211c1110ce63be62b70504d04b0ff Mon Sep 17 00:00:00 2001
> +From: Kevin Darbyshire-Bryant <ldir at darbyshire-bryant.me.uk>
> +Date: Sat, 23 Mar 2019 10:21:03 +0000
> +Subject: [PATCH] savedscp
> +
> +Signed-off-by: Kevin Darbyshire-Bryant <ldir at darbyshire-bryant.me.uk>
> +---
> + extensions/libxt_CONNMARK.c | 68 ++++++++++++++++++++++++++-
> + include/linux/netfilter/xt_connmark.h | 3 +-
> + 2 files changed, 69 insertions(+), 2 deletions(-)
> +
> +diff --git a/extensions/libxt_CONNMARK.c b/extensions/libxt_CONNMARK.c
> +index 21e10913..2dbd7a4a 100644
> +--- a/extensions/libxt_CONNMARK.c
> ++++ b/extensions/libxt_CONNMARK.c
> +@@ -49,6 +49,7 @@ enum {
> + O_CTMASK,
> + O_NFMASK,
> + O_MASK,
> ++ O_SAVEDSCP_MARK,
> + F_SET_MARK = 1 << O_SET_MARK,
> + F_SAVE_MARK = 1 << O_SAVE_MARK,
> + F_RESTORE_MARK = 1 << O_RESTORE_MARK,
> +@@ -61,8 +62,10 @@ enum {
> + F_CTMASK = 1 << O_CTMASK,
> + F_NFMASK = 1 << O_NFMASK,
> + F_MASK = 1 << O_MASK,
> ++ F_SAVEDSCP_MARK = 1 << O_SAVEDSCP_MARK,
> + F_OP_ANY = F_SET_MARK | F_SAVE_MARK | F_RESTORE_MARK |
> +- F_AND_MARK | F_OR_MARK | F_XOR_MARK | F_SET_XMARK,
> ++ F_AND_MARK | F_OR_MARK | F_XOR_MARK | F_SET_XMARK |
> ++ F_SAVEDSCP_MARK,
> + };
> +
> + static const char *const xt_connmark_shift_ops[] = {
> +@@ -75,6 +78,7 @@ static void CONNMARK_help(void)
> + printf(
> + "CONNMARK target options:\n"
> + " --set-mark value[/mask] Set conntrack mark value\n"
> ++" --savedscp-mark dscpmask/statemask Save DSCP to conntrack mark value\n"
> + " --save-mark [--mask mask] Save the packet nfmark in the connection\n"
> + " --restore-mark [--mask mask] Restore saved nfmark value\n");
> + }
> +@@ -83,6 +87,8 @@ static void CONNMARK_help(void)
> + static const struct xt_option_entry CONNMARK_opts[] = {
> + {.name = "set-mark", .id = O_SET_MARK, .type = XTTYPE_MARKMASK32,
> + .excl = F_OP_ANY},
> ++ {.name = "savedscp-mark", .id = O_SAVEDSCP_MARK, .type = XTTYPE_MARKMASK32,
> ++ .excl = F_OP_ANY},
> + {.name = "save-mark", .id = O_SAVE_MARK, .type = XTTYPE_NONE,
> + .excl = F_OP_ANY},
> + {.name = "restore-mark", .id = O_RESTORE_MARK, .type = XTTYPE_NONE,
> +@@ -98,6 +104,8 @@ static const struct xt_option_entry connmark_tg_opts[] = {
> + .excl = F_OP_ANY},
> + {.name = "set-mark", .id = O_SET_MARK, .type = XTTYPE_MARKMASK32,
> + .excl = F_OP_ANY},
> ++ {.name = "savedscp-mark", .id = O_SAVEDSCP_MARK, .type = XTTYPE_MARKMASK32,
> ++ .excl = F_OP_ANY},
> + {.name = "and-mark", .id = O_AND_MARK, .type = XTTYPE_UINT32,
> + .excl = F_OP_ANY},
> + {.name = "or-mark", .id = O_OR_MARK, .type = XTTYPE_UINT32,
> +@@ -124,6 +132,8 @@ static const struct xt_option_entry connmark_tg_opts_v2[] = {
> + .excl = F_OP_ANY},
> + {.name = "set-mark", .id = O_SET_MARK, .type = XTTYPE_MARKMASK32,
> + .excl = F_OP_ANY},
> ++ {.name = "savedscp-mark", .id = O_SAVEDSCP_MARK, .type = XTTYPE_MARKMASK32,
> ++ .excl = F_OP_ANY},
> + {.name = "and-mark", .id = O_AND_MARK, .type = XTTYPE_UINT32,
> + .excl = F_OP_ANY},
> + {.name = "or-mark", .id = O_OR_MARK, .type = XTTYPE_UINT32,
> +@@ -158,6 +168,7 @@ static void connmark_tg_help(void)
> + " --restore-mark [--ctmask mask] [--nfmask mask]\n"
> + " Copy nfmark to ctmark using masks\n"
> + " --set-mark value[/mask] Set conntrack mark value\n"
> ++" --savedscp-mark value/mask Save DSCP to conntrack mark value\n"
> + " --save-mark [--mask mask] Save the packet nfmark in the connection\n"
> + " --restore-mark [--mask mask] Restore saved nfmark value\n"
> + " --and-mark value Binary AND the ctmark with bits\n"
> +@@ -210,6 +221,11 @@ static void CONNMARK_parse(struct xt_option_call *cb)
> + markinfo->mark = cb->val.mark;
> + markinfo->mask = cb->val.mask;
> + break;
> ++ case O_SAVEDSCP_MARK:
> ++ markinfo->mode = XT_CONNMARK_SAVEDSCP;
> ++ markinfo->mark = cb->val.mark;
> ++ markinfo->mask = cb->val.mask;
> ++ break;
> + case O_SAVE_MARK:
> + markinfo->mode = XT_CONNMARK_SAVE;
> + break;
> +@@ -238,6 +254,19 @@ static void connmark_tg_parse(struct xt_option_call *cb)
> + info->ctmark = cb->val.mark;
> + info->ctmask = cb->val.mark | cb->val.mask;
> + break;
> ++ case O_SAVEDSCP_MARK:
> ++ info->mode = XT_CONNMARK_SAVEDSCP;
> ++ info->ctmark = cb->val.mark;
> ++ info->ctmask = cb->val.mask;
> ++ info->nfmask = info->ctmark ? ffs(info->ctmark) - 1 : 0;
> ++ /* need 6 contiguous bits */
> ++ if ((0x3f & (info->ctmark >> info->nfmask)) != 0x3f)
> ++ xtables_error(PARAMETER_PROBLEM,
> ++ "CONNMARK savedscp: insufficient contiguous dscpmask bits");
> ++ if (info->ctmark & info->ctmask)
> ++ xtables_error(PARAMETER_PROBLEM,
> ++ "CONNMARK savedscp: dscpmask/statemask bits overlap");
> ++ break;
> + case O_AND_MARK:
> + info->mode = XT_CONNMARK_SET;
> + info->ctmark = 0;
> +@@ -283,6 +312,19 @@ static void connmark_tg_parse_v2(struct xt_option_call *cb)
> + info->ctmark = cb->val.mark;
> + info->ctmask = cb->val.mark | cb->val.mask;
> + break;
> ++ case O_SAVEDSCP_MARK:
> ++ info->mode = XT_CONNMARK_SAVEDSCP;
> ++ info->ctmark = cb->val.mark;
> ++ info->ctmask = cb->val.mask;
> ++ info->shift_bits = info->ctmark ? ffs(info->ctmark) - 1 : 0;
> ++ /* need 6 contiguous bits */
> ++ if ((0x3f & (info->ctmark >> info->shift_bits)) != 0x3f)
> ++ xtables_error(PARAMETER_PROBLEM,
> ++ "CONNMARK savedscp: insufficient contiguous dscpmask bits");
> ++ if (info->ctmark & info->ctmask)
> ++ xtables_error(PARAMETER_PROBLEM,
> ++ "CONNMARK savedscp: dscpmask/statemask bits overlap");
> ++ break;
> + case O_AND_MARK:
> + info->mode = XT_CONNMARK_SET;
> + info->ctmark = 0;
> +@@ -351,6 +393,11 @@ static void CONNMARK_print(const void *ip,
> + print_mark(markinfo->mark);
> + print_mask("/", markinfo->mask);
> + break;
> ++ case XT_CONNMARK_SAVEDSCP:
> ++ printf(" CONNMARK savedscp ");
> ++ print_mark(markinfo->mark);
> ++ print_mask("/", markinfo->mask);
> ++ break;
> + case XT_CONNMARK_SAVE:
> + printf(" CONNMARK save ");
> + print_mask("mask ", markinfo->mask);
> +@@ -386,6 +433,10 @@ connmark_tg_print(const void *ip, const struct xt_entry_target *target,
> + printf(" CONNMARK xset 0x%x/0x%x",
> + info->ctmark, info->ctmask);
> + break;
> ++ case XT_CONNMARK_SAVEDSCP:
> ++ printf(" CONNMARK DSCP set 0x%x/0x%x",
> ++ info->ctmark, info->ctmask);
> ++ break;
> + case XT_CONNMARK_SAVE:
> + if (info->nfmask == UINT32_MAX && info->ctmask == UINT32_MAX)
> + printf(" CONNMARK save");
> +@@ -433,6 +484,10 @@ connmark_tg_print_v2(const void *ip, const struct xt_entry_target *target,
> + printf(" CONNMARK xset 0x%x/0x%x",
> + info->ctmark, info->ctmask);
> + break;
> ++ case XT_CONNMARK_SAVEDSCP:
> ++ printf(" CONNMARK DSCP xset 0x%x/0x%x",
> ++ info->ctmark, info->ctmask);
> ++ break;
> + case XT_CONNMARK_SAVE:
> + if (info->nfmask == UINT32_MAX && info->ctmask == UINT32_MAX)
> + printf(" CONNMARK save");
> +@@ -474,6 +529,11 @@ static void CONNMARK_save(const void *ip, const struct xt_entry_target *target)
> + print_mark(markinfo->mark);
> + print_mask("/", markinfo->mask);
> + break;
> ++ case XT_CONNMARK_SAVEDSCP:
> ++ printf(" --savedscp-mark ");
> ++ print_mark(markinfo->mark);
> ++ print_mask("/", markinfo->mask);
> ++ break;
> + case XT_CONNMARK_SAVE:
> + printf(" --save-mark ");
> + print_mask("--mask ", markinfo->mask);
> +@@ -505,6 +565,9 @@ connmark_tg_save(const void *ip, const struct xt_entry_target *target)
> + case XT_CONNMARK_SET:
> + printf(" --set-xmark 0x%x/0x%x", info->ctmark, info->ctmask);
> + break;
> ++ case XT_CONNMARK_SAVEDSCP:
> ++ printf(" --savedscp-mark 0x%x/0x%x", info->ctmark, info->ctmask);
> ++ break;
> + case XT_CONNMARK_SAVE:
> + printf(" --save-mark --nfmask 0x%x --ctmask 0x%x",
> + info->nfmask, info->ctmask);
> +@@ -529,6 +592,9 @@ connmark_tg_save_v2(const void *ip, const struct xt_entry_target *target)
> + case XT_CONNMARK_SET:
> + printf(" --set-xmark 0x%x/0x%x", info->ctmark, info->ctmask);
> + break;
> ++ case XT_CONNMARK_SAVEDSCP:
> ++ printf(" --savedscp-mark 0x%x/0x%x", info->ctmark, info->ctmask);
> ++ break;
> + case XT_CONNMARK_SAVE:
> + printf(" --save-mark --nfmask 0x%x --ctmask 0x%x",
> + info->nfmask, info->ctmask);
> +diff --git a/include/linux/netfilter/xt_connmark.h b/include/linux/netfilter/xt_connmark.h
> +index bbf2acc9..cf526101 100644
> +--- a/include/linux/netfilter/xt_connmark.h
> ++++ b/include/linux/netfilter/xt_connmark.h
> +@@ -15,7 +15,8 @@
> + enum {
> + XT_CONNMARK_SET = 0,
> + XT_CONNMARK_SAVE,
> +- XT_CONNMARK_RESTORE
> ++ XT_CONNMARK_RESTORE,
> ++ XT_CONNMARK_SAVEDSCP
> + };
> +
> + struct xt_connmark_tginfo1 {
> +--
> +2.20.1 (Apple Git-117)
> +
> diff --git a/target/linux/generic/hack-4.14/0001-net-sched-Introduce-act_ctinfo-action.patch b/target/linux/generic/hack-4.14/0001-net-sched-Introduce-act_ctinfo-action.patch
> new file mode 100644
> index 0000000000..7ff26df7d1
> --- /dev/null
> +++ b/target/linux/generic/hack-4.14/0001-net-sched-Introduce-act_ctinfo-action.patch
> @@ -0,0 +1,636 @@
> +From 82f7c4ec33323a371a97a5d68b4ccfd7bd62e087 Mon Sep 17 00:00:00 2001
> +From: Kevin Darbyshire-Bryant <ldir at darbyshire-bryant.me.uk>
> +Date: Wed, 13 Mar 2019 20:54:49 +0000
> +Subject: [PATCH] net: sched: Introduce act_ctinfo action
> +
> +ctinfo is a new tc filter action module. It is designed to restore DSCPs
> +stored in conntrack marks
> +
> +The feature is intended for use and has been found useful for restoring
> +ingress classifications based on egress classifications across links
> +that bleach or otherwise change DSCP, typically home ISP Internet links.
> +Restoring DSCP on ingress on the WAN link allows qdiscs such as CAKE to
> +shape inbound packets according to policies that are easier to implement
> +on egress.
> +
> +Ingress classification is traditionally a challenging task since
> +iptables rules haven't yet run and tc filter/eBPF programs are pre-NAT
> +lookups, hence are unable to see internal IPv4 addresses as used on the
> +typical home masquerading gateway.
> +
> +ctinfo understands the following parameters:
> +
> +dscp mask[/statemask]
> +
> +mask - a 32 bit mask of at least 6 contiguous bits where conndscp will
> +place the DSCP in conntrack mark. The DSCP is left-shifted by the
> +number of unset lower bits of the mask before storing into the mark
> +field.
> +
> +statemask - a 32 bit mask of (usually) 1 bit length, outside the area
> +specified by mask. This represents a conditional operation flag the
> +DSCP is only restored if the flag is set. This is useful to implement a
> +'one shot' iptables based classification where the 'complicated'
> +iptables rules are only run once to classify the connection on initial
> +(egress) packet and subsequent packets are all marked/restored with the
> +same DSCP. A mask of zero disables the conditional behaviour.
> +
> +optional parameters:
> +
> +zone - conntrack zone
> +
> +control - action related control (reclassify | pipe | drop | continue |
> +ok | goto chain <CHAIN_INDEX>
> +
> +Signed-off-by: Kevin Darbyshire-Bryant <ldir at darbyshire-bryant.me.uk>
> +
> +net:sched - v3 tweak license
> +
> +Signed-off-by: Kevin Darbyshire-Bryant <ldir at darbyshire-bryant.me.uk>
> +
> +drop inline & sort reverse christmas tree
> +
> +Signed-off-by: Kevin Darbyshire-Bryant <ldir at darbyshire-bryant.me.uk>
> +
> +v5 stuff
> +
> +Signed-off-by: Kevin Darbyshire-Bryant <ldir at darbyshire-bryant.me.uk>
> +---
> + include/net/tc_act/tc_ctinfo.h | 28 ++
> + include/uapi/linux/pkt_cls.h | 3 +-
> + include/uapi/linux/tc_act/tc_ctinfo.h | 52 ++++
> + net/sched/Kconfig | 13 +
> + net/sched/Makefile | 1 +
> + net/sched/act_ctinfo.c | 416 ++++++++++++++++++++++++++
> + 6 files changed, 512 insertions(+), 1 deletion(-)
> + create mode 100644 include/net/tc_act/tc_ctinfo.h
> + create mode 100644 include/uapi/linux/tc_act/tc_ctinfo.h
> + create mode 100644 net/sched/act_ctinfo.c
> +
> +diff --git a/include/net/tc_act/tc_ctinfo.h b/include/net/tc_act/tc_ctinfo.h
> +new file mode 100644
> +index 000000000000..d6a688571672
> +--- /dev/null
> ++++ b/include/net/tc_act/tc_ctinfo.h
> +@@ -0,0 +1,28 @@
> ++/* SPDX-License-Identifier: GPL-2.0 */
> ++#ifndef __NET_TC_CTINFO_H
> ++#define __NET_TC_CTINFO_H
> ++
> ++#include <net/act_api.h>
> ++
> ++struct tcf_ctinfo_params {
> ++ struct rcu_head rcu;
> ++ struct net *net;
> ++ u32 dscpmask;
> ++ u32 dscpstatemask;
> ++ u32 cpmarkmask;
> ++ u16 zone;
> ++ u8 mode;
> ++ u8 dscpmaskshift;
> ++};
> ++
> ++struct tcf_ctinfo {
> ++ struct tc_action common;
> ++ struct tcf_ctinfo_params __rcu *params;
> ++ u64 stats_dscp_set;
> ++ u64 stats_dscp_error;
> ++ u64 stats_cpmark_set;
> ++};
> ++
> ++#define to_ctinfo(a) ((struct tcf_ctinfo *)a)
> ++
> ++#endif /* __NET_TC_CTINFO_H */
> +diff --git a/include/uapi/linux/pkt_cls.h b/include/uapi/linux/pkt_cls.h
> +index 46c506615f4a..408b02fbb34a 100644
> +--- a/include/uapi/linux/pkt_cls.h
> ++++ b/include/uapi/linux/pkt_cls.h
> +@@ -66,7 +66,8 @@ enum {
> + TCA_ID_UNSPEC=0,
> + TCA_ID_POLICE=1,
> + /* other actions go here */
> +- __TCA_ID_MAX=255
> ++ TCA_ID_CTINFO=27,
> ++ __TCA_ID_MAX = 255
> + };
> +
> + #define TCA_ID_MAX __TCA_ID_MAX
> +diff --git a/include/uapi/linux/tc_act/tc_ctinfo.h b/include/uapi/linux/tc_act/tc_ctinfo.h
> +new file mode 100644
> +index 000000000000..48c40f657575
> +--- /dev/null
> ++++ b/include/uapi/linux/tc_act/tc_ctinfo.h
> +@@ -0,0 +1,52 @@
> ++/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
> ++#ifndef __UAPI_TC_CTINFO_H
> ++#define __UAPI_TC_CTINFO_H
> ++
> ++#include <linux/types.h>
> ++#include <linux/pkt_cls.h>
> ++
> ++struct tc_ctinfo {
> ++ tc_gen;
> ++};
> ++
> ++struct tc_ctinfo_dscp {
> ++ __u32 mask;
> ++ __u32 statemask;
> ++};
> ++
> ++struct tc_ctinfo_cpmark {
> ++ __u32 mask;
> ++};
> ++
> ++struct tc_ctinfo_stats_dscp {
> ++ __u64 set;
> ++ __u64 error;
> ++};
> ++
> ++struct tc_ctinfo_stats_cpmark {
> ++ __u64 set;
> ++};
> ++
> ++enum {
> ++ TCA_CTINFO_UNSPEC,
> ++ TCA_CTINFO_PAD,
> ++ TCA_CTINFO_TM,
> ++ TCA_CTINFO_ACT,
> ++ TCA_CTINFO_ZONE,
> ++ TCA_CTINFO_PARMS_DSCP,
> ++ TCA_CTINFO_PARMS_CPMARK,
> ++ TCA_CTINFO_MODE_DSCP,
> ++ TCA_CTINFO_MODE_CPMARK,
> ++ TCA_CTINFO_STATS_DSCP,
> ++ TCA_CTINFO_STATS_CPMARK,
> ++ __TCA_CTINFO_MAX
> ++};
> ++
> ++#define TCA_CTINFO_MAX (__TCA_CTINFO_MAX - 1)
> ++
> ++enum {
> ++ CTINFO_MODE_DSCP = BIT(0),
> ++ CTINFO_MODE_CPMARK = BIT(1)
> ++};
> ++
> ++#endif
> +diff --git a/net/sched/Kconfig b/net/sched/Kconfig
> +index e70ed26485a2..962d90f72f54 100644
> +--- a/net/sched/Kconfig
> ++++ b/net/sched/Kconfig
> +@@ -808,6 +808,19 @@ config NET_ACT_CONNMARK
> + To compile this code as a module, choose M here: the
> + module will be called act_connmark.
> +
> ++config NET_ACT_CTINFO
> ++ tristate "Netfilter Connmark to DSCP Retriever"
> ++ depends on NET_CLS_ACT && NETFILTER && IP_NF_IPTABLES
> ++ depends on NF_CONNTRACK && NF_CONNTRACK_MARK
> ++ help
> ++ Say Y here to allow transfer of a connmark stored DSCP into
> ++ ipv4/v6 diffserv
> ++
> ++ If unsure, say N.
> ++
> ++ To compile this code as a module, choose M here: the
> ++ module will be called act_ctinfo.
> ++
> + config NET_ACT_SKBMOD
> + tristate "skb data modification action"
> + depends on NET_CLS_ACT
> +diff --git a/net/sched/Makefile b/net/sched/Makefile
> +index 9e43a4721ef8..44ee5b87b895 100644
> +--- a/net/sched/Makefile
> ++++ b/net/sched/Makefile
> +@@ -21,6 +21,7 @@ obj-$(CONFIG_NET_ACT_CSUM) += act_csum.o
> + obj-$(CONFIG_NET_ACT_VLAN) += act_vlan.o
> + obj-$(CONFIG_NET_ACT_BPF) += act_bpf.o
> + obj-$(CONFIG_NET_ACT_CONNMARK) += act_connmark.o
> ++obj-$(CONFIG_NET_ACT_CTINFO) += act_ctinfo.o
> + obj-$(CONFIG_NET_ACT_SKBMOD) += act_skbmod.o
> + obj-$(CONFIG_NET_ACT_IFE) += act_ife.o
> + obj-$(CONFIG_NET_IFE_SKBMARK) += act_meta_mark.o
> +diff --git a/net/sched/act_ctinfo.c b/net/sched/act_ctinfo.c
> +new file mode 100644
> +index 000000000000..8150ba2e3867
> +--- /dev/null
> ++++ b/net/sched/act_ctinfo.c
> +@@ -0,0 +1,416 @@
> ++// SPDX-License-Identifier: GPL-2.0+
> ++/* net/sched/act_ctinfo.c netfilter ctinfo connmark actions
> ++ *
> ++ * Copyright (c) 2019 Kevin Darbyshire-Bryant <ldir at darbyshire-bryant.me.uk>
> ++ */
> ++
> ++#include <linux/module.h>
> ++#include <linux/init.h>
> ++#include <linux/kernel.h>
> ++#include <linux/skbuff.h>
> ++#include <linux/rtnetlink.h>
> ++#include <linux/pkt_cls.h>
> ++#include <linux/ip.h>
> ++#include <linux/ipv6.h>
> ++#include <net/netlink.h>
> ++#include <net/pkt_sched.h>
> ++#include <net/act_api.h>
> ++#include <net/pkt_cls.h>
> ++#include <uapi/linux/tc_act/tc_ctinfo.h>
> ++#include <net/tc_act/tc_ctinfo.h>
> ++
> ++#include <net/netfilter/nf_conntrack.h>
> ++#include <net/netfilter/nf_conntrack_core.h>
> ++#include <net/netfilter/nf_conntrack_ecache.h>
> ++#include <net/netfilter/nf_conntrack_zones.h>
> ++
> ++static struct tc_action_ops act_ctinfo_ops;
> ++static unsigned int ctinfo_net_id;
> ++
> ++static void tcf_ctinfo_dscp_set(struct nf_conn *ct, struct tcf_ctinfo *ca,
> ++ struct tcf_ctinfo_params *cp,
> ++ struct sk_buff *skb, int wlen, int proto)
> ++{
> ++ u8 dscp,newdscp;
> ++
> ++ newdscp = (((ct->mark & cp->dscpmask) >> cp->dscpmaskshift) << 2) &
> ++ ~INET_ECN_MASK;
> ++
> ++ /* mark contains DSCP so restore DSCP bits from ct->mark into diffserv */
> ++ /* using overlimits stats to count how many DSCP updates */
> ++ switch (proto) {
> ++ case NFPROTO_IPV4:
> ++ dscp = ipv4_get_dsfield(ip_hdr(skb)) & ~INET_ECN_MASK;
> ++ if (dscp != newdscp) {
> ++ if (likely(!skb_try_make_writable(skb, wlen))) {
> ++ ipv4_change_dsfield(ip_hdr(skb),
> ++ INET_ECN_MASK,
> ++ newdscp);
> ++ ca->stats_dscp_set++;
> ++ } else {
> ++ ca->stats_dscp_error++;
> ++ }
> ++ }
> ++ break;
> ++ case NFPROTO_IPV6:
> ++ dscp = ipv6_get_dsfield(ipv6_hdr(skb)) & ~INET_ECN_MASK;
> ++ if (dscp != newdscp) {
> ++ if (likely(!skb_try_make_writable(skb, wlen))) {
> ++ ipv6_change_dsfield(ipv6_hdr(skb),
> ++ INET_ECN_MASK,
> ++ newdscp);
> ++ ca->stats_dscp_set++;
> ++ } else {
> ++ ca->stats_dscp_error++;
> ++ }
> ++ }
> ++ break;
> ++ default:
> ++ break;
> ++ }
> ++}
> ++
> ++static void tcf_ctinfo_cpmark_set(struct nf_conn *ct, struct tcf_ctinfo *ca,
> ++ struct tcf_ctinfo_params *cp,
> ++ struct sk_buff *skb)
> ++{
> ++ ca->stats_cpmark_set++;
> ++ skb->mark = ct->mark & cp->cpmarkmask;
> ++}
> ++
> ++static int tcf_ctinfo_act(struct sk_buff *skb, const struct tc_action *a,
> ++ struct tcf_result *res)
> ++{
> ++ const struct nf_conntrack_tuple_hash *thash = NULL;
> ++ struct tcf_ctinfo *ca = to_ctinfo(a);
> ++ struct nf_conntrack_tuple tuple;
> ++ struct nf_conntrack_zone zone;
> ++ enum ip_conntrack_info ctinfo;
> ++ struct tcf_ctinfo_params *cp;
> ++ struct nf_conn *ct;
> ++ int proto, wlen;
> ++ int action;
> ++
> ++ cp = rcu_dereference_bh(ca->params);
> ++
> ++ tcf_lastuse_update(&ca->tcf_tm);
> ++ bstats_update(&ca->tcf_bstats, skb);
> ++ action = READ_ONCE(ca->tcf_action);
> ++
> ++ wlen = skb_network_offset(skb);
> ++ if (tc_skb_protocol(skb) == htons(ETH_P_IP)) {
> ++ wlen += sizeof(struct iphdr);
> ++ if (!pskb_may_pull(skb, wlen))
> ++ goto out;
> ++
> ++ proto = NFPROTO_IPV4;
> ++ } else if (tc_skb_protocol(skb) == htons(ETH_P_IPV6)) {
> ++ wlen += sizeof(struct ipv6hdr);
> ++ if (!pskb_may_pull(skb, wlen))
> ++ goto out;
> ++
> ++ proto = NFPROTO_IPV6;
> ++ } else {
> ++ goto out;
> ++ }
> ++
> ++ ct = nf_ct_get(skb, &ctinfo);
> ++ if (!ct) { /* look harder, usually ingress */
> ++ if (!nf_ct_get_tuplepr(skb, skb_network_offset(skb),
> ++ proto, cp->net, &tuple))
> ++ goto out;
> ++ zone.id = cp->zone;
> ++ zone.dir = NF_CT_DEFAULT_ZONE_DIR;
> ++
> ++ thash = nf_conntrack_find_get(cp->net, &zone, &tuple);
> ++ if (!thash)
> ++ goto out;
> ++
> ++ ct = nf_ct_tuplehash_to_ctrack(thash);
> ++ }
> ++
> ++ if (cp->mode & CTINFO_MODE_DSCP)
> ++ if (!cp->dscpstatemask || (ct->mark & cp->dscpstatemask))
> ++ tcf_ctinfo_dscp_set(ct, ca, cp, skb, wlen, proto);
> ++
> ++ if (cp->mode & CTINFO_MODE_CPMARK)
> ++ tcf_ctinfo_cpmark_set(ct, ca, cp, skb);
> ++
> ++ if (thash)
> ++ nf_ct_put(ct);
> ++out:
> ++ return action;
> ++}
> ++
> ++static const struct nla_policy ctinfo_policy[TCA_CTINFO_MAX + 1] = {
> ++ [TCA_CTINFO_ACT] = { .len = sizeof(struct tc_ctinfo) },
> ++ [TCA_CTINFO_ZONE] = { .type = NLA_U16 },
> ++ [TCA_CTINFO_PARMS_DSCP] = { .len = sizeof(struct tc_ctinfo_dscp) },
> ++ [TCA_CTINFO_PARMS_CPMARK] = { .len = sizeof(struct tc_ctinfo_cpmark) },
> ++ [TCA_CTINFO_MODE_DSCP] = { .type = NLA_FLAG },
> ++ [TCA_CTINFO_MODE_CPMARK] = { .type = NLA_FLAG },
> ++};
> ++
> ++static int tcf_ctinfo_init(struct net *net, struct nlattr *nla,
> ++ struct nlattr *est, struct tc_action **a,
> ++ int ovr, int bind)
> ++{
> ++ struct tc_action_net *tn = net_generic(net, ctinfo_net_id);
> ++ struct tc_ctinfo_cpmark *cpmarkparm = NULL;
> ++ struct nlattr *tb[TCA_CTINFO_MAX + 1];
> ++ struct tcf_ctinfo_params *cp_new;
> ++/* struct tcf_chain *goto_ch = NULL; */
> ++ struct tc_ctinfo_dscp *dscpparm;
> ++ struct tcf_ctinfo *ci;
> ++ struct tc_ctinfo *actparm;
> ++ int ret = 0, err, i;
> ++
> ++ if (!nla)
> ++ return -EINVAL;
> ++
> ++ err = nla_parse_nested(tb, TCA_CTINFO_MAX, nla, ctinfo_policy, NULL);
> ++ if (err < 0)
> ++ return err;
> ++
> ++ if (!tb[TCA_CTINFO_ACT])
> ++ return -EINVAL;
> ++
> ++ if (tb[TCA_CTINFO_MODE_DSCP] && !tb[TCA_CTINFO_PARMS_DSCP])
> ++ return -EINVAL;
> ++
> ++ if (tb[TCA_CTINFO_PARMS_CPMARK])
> ++ cpmarkparm = nla_data(tb[TCA_CTINFO_PARMS_CPMARK]);
> ++
> ++ dscpparm = nla_data(tb[TCA_CTINFO_PARMS_DSCP]);
> ++ actparm = nla_data(tb[TCA_CTINFO_ACT]);
> ++
> ++ if (dscpparm) {
> ++ /* need contiguous 6 bit mask */
> ++ i = dscpparm->mask ? __ffs(dscpparm->mask) : 0;
> ++ if ((~0 & (dscpparm->mask >> i)) != 0x3f)
> ++ return -EINVAL;
> ++ /* mask & statemask must not overlap */
> ++ if (dscpparm->mask & dscpparm->statemask)
> ++ return -EINVAL;
> ++ }
> ++ /* done the validation:now to the actual action allocation */
> ++ err = tcf_idr_check(tn, actparm->index, a, bind);
> ++ if (!err) {
> ++ ret = tcf_idr_create(tn, actparm->index, est, a,
> ++ &act_ctinfo_ops, bind, false);
> ++ if (ret) {
> ++ /* tcf_idr_cleanup(tn, actparm->index); */
> ++ return ret;
> ++ }
> ++ ret = ACT_P_CREATED;
> ++ } else if (err > 0) {
> ++ if (bind) /* don't override defaults */
> ++ return 0;
> ++ if (!ovr) {
> ++ tcf_idr_release(*a, bind);
> ++ return -EEXIST;
> ++ }
> ++ } else {
> ++ return err;
> ++ }
> ++
> ++/* err = tcf_action_check_ctrlact(actparm->action, tp, &goto_ch, extack);
> ++ if (err < 0)
> ++ goto release_idr;
> ++ */
> ++
> ++ ci = to_ctinfo(*a);
> ++
> ++ cp_new = kzalloc(sizeof(*cp_new), GFP_KERNEL);
> ++ if (unlikely(!cp_new)) {
> ++ err = -ENOMEM;
> ++ goto put_chain;
> ++ }
> ++
> ++ cp_new->net = net;
> ++ cp_new->zone = tb[TCA_CTINFO_ZONE] ? nla_get_u16(tb[TCA_CTINFO_ZONE]) : 0;
> ++ if (dscpparm) {
> ++ cp_new->dscpmask = dscpparm->mask;
> ++ cp_new->dscpmaskshift = cp_new->dscpmask ? __ffs(cp_new->dscpmask) : 0;
> ++ cp_new->dscpstatemask = dscpparm->statemask;
> ++ }
> ++
> ++ if (cpmarkparm)
> ++ cp_new->cpmarkmask = cpmarkparm->mask;
> ++ else
> ++ cp_new->cpmarkmask = ~0;
> ++
> ++ if (tb[TCA_CTINFO_MODE_DSCP])
> ++ cp_new->mode |= CTINFO_MODE_DSCP;
> ++ else
> ++ cp_new->mode &= ~CTINFO_MODE_DSCP;
> ++
> ++ if (tb[TCA_CTINFO_MODE_CPMARK])
> ++ cp_new->mode |= CTINFO_MODE_CPMARK;
> ++ else
> ++ cp_new->mode &= ~CTINFO_MODE_CPMARK;
> ++
> ++ spin_lock_bh(&ci->tcf_lock);
> ++/* goto_ch = tcf_action_set_ctrlact(*a, actparm->action, goto_ch); */
> ++ ci->tcf_action = actparm->action;
> ++ rcu_swap_protected(ci->params, cp_new,
> ++ lockdep_is_held(&ci->tcf_lock));
> ++ spin_unlock_bh(&ci->tcf_lock);
> ++
> ++/* if (goto_ch
> ++ tcf_chain_put_by_act(goto_ch); */
> ++ if (cp_new)
> ++ kfree_rcu(cp_new, rcu);
> ++
> ++ if (ret == ACT_P_CREATED)
> ++ tcf_idr_insert(tn, *a);
> ++
> ++ return ret;
> ++
> ++put_chain:
> ++/* if (goto_ch)
> ++ tcf_chain_put_by_act(goto_ch); */
> ++/*release_idr:*/
> ++ tcf_idr_release(*a, bind);
> ++ return err;
> ++}
> ++
> ++static int tcf_ctinfo_dump(struct sk_buff *skb, struct tc_action *a,
> ++ int bind, int ref)
> ++{
> ++ struct tcf_ctinfo *ci = to_ctinfo(a);
> ++ struct tc_ctinfo opt = {
> ++ .index = ci->tcf_index,
> ++ .refcnt = ci->tcf_refcnt - ref,
> ++ .bindcnt = ci->tcf_bindcnt - bind,
> ++ };
> ++ struct tc_ctinfo_stats_cpmark cpmarkstats;
> ++ unsigned char *b = skb_tail_pointer(skb);
> ++ struct tc_ctinfo_stats_dscp dscpstats;
> ++ struct tc_ctinfo_cpmark cpmarkparm;
> ++ struct tc_ctinfo_dscp dscpparm;
> ++ struct tcf_ctinfo_params *cp;
> ++ struct tcf_t t;
> ++
> ++ spin_lock_bh(&ci->tcf_lock);
> ++ cp = rcu_dereference_protected(ci->params,
> ++ lockdep_is_held(&ci->tcf_lock));
> ++ opt.action = ci->tcf_action;
> ++
> ++ if (nla_put(skb, TCA_CTINFO_ACT, sizeof(opt), &opt))
> ++ goto nla_put_failure;
> ++
> ++ if (cp->mode & CTINFO_MODE_DSCP) {
> ++ dscpparm.mask = cp->dscpmask;
> ++ dscpparm.statemask = cp->dscpstatemask;
> ++ if (nla_put(skb, TCA_CTINFO_PARMS_DSCP, sizeof(dscpparm),
> ++ &dscpparm))
> ++ goto nla_put_failure;
> ++
> ++ if (nla_put_flag(skb, TCA_CTINFO_MODE_DSCP))
> ++ goto nla_put_failure;
> ++
> ++ dscpstats.error = ci->stats_dscp_error;
> ++ dscpstats.set = ci->stats_dscp_set;
> ++ if (nla_put(skb, TCA_CTINFO_STATS_DSCP, sizeof(dscpstats),
> ++ &dscpstats))
> ++ goto nla_put_failure;
> ++ }
> ++
> ++ if (cp->mode & CTINFO_MODE_CPMARK) {
> ++ cpmarkparm.mask = cp->cpmarkmask;
> ++ if (nla_put(skb, TCA_CTINFO_PARMS_CPMARK, sizeof(cpmarkparm),
> ++ &cpmarkparm))
> ++ goto nla_put_failure;
> ++
> ++ if (nla_put_flag(skb, TCA_CTINFO_MODE_CPMARK))
> ++ goto nla_put_failure;
> ++
> ++ cpmarkstats.set = ci->stats_cpmark_set;
> ++ if (nla_put_u64_64bit(skb, TCA_CTINFO_STATS_CPMARK,
> ++ ci->stats_cpmark_set, TCA_CTINFO_PAD))
> ++ goto nla_put_failure;
> ++ }
> ++
> ++ if (cp->zone) {
> ++ if (nla_put_u16(skb, TCA_CTINFO_ZONE, cp->zone))
> ++ goto nla_put_failure;
> ++ }
> ++
> ++ tcf_tm_dump(&t, &ci->tcf_tm);
> ++ if (nla_put_64bit(skb, TCA_CTINFO_TM, sizeof(t), &t, TCA_CTINFO_PAD))
> ++ goto nla_put_failure;
> ++
> ++ spin_unlock_bh(&ci->tcf_lock);
> ++ return skb->len;
> ++
> ++nla_put_failure:
> ++ spin_unlock_bh(&ci->tcf_lock);
> ++ nlmsg_trim(skb, b);
> ++ return -1;
> ++}
> ++
> ++static int tcf_ctinfo_walker(struct net *net, struct sk_buff *skb,
> ++ struct netlink_callback *cb, int type,
> ++ const struct tc_action_ops *ops)
> ++{
> ++ struct tc_action_net *tn = net_generic(net, ctinfo_net_id);
> ++
> ++ return tcf_generic_walker(tn, skb, cb, type, ops);
> ++}
> ++
> ++static int tcf_ctinfo_search(struct net *net, struct tc_action **a, u32 index)
> ++{
> ++ struct tc_action_net *tn = net_generic(net, ctinfo_net_id);
> ++
> ++ return tcf_idr_search(tn, a, index);
> ++}
> ++
> ++static struct tc_action_ops act_ctinfo_ops = {
> ++ .kind = "ctinfo",
> ++ .type = TCA_ID_CTINFO,
> ++ .owner = THIS_MODULE,
> ++ .act = tcf_ctinfo_act,
> ++ .dump = tcf_ctinfo_dump,
> ++ .init = tcf_ctinfo_init,
> ++ .walk = tcf_ctinfo_walker,
> ++ .lookup = tcf_ctinfo_search,
> ++ .size = sizeof(struct tcf_ctinfo),
> ++};
> ++
> ++static __net_init int ctinfo_init_net(struct net *net)
> ++{
> ++ struct tc_action_net *tn = net_generic(net, ctinfo_net_id);
> ++
> ++ return tc_action_net_init(tn, &act_ctinfo_ops);
> ++}
> ++
> ++static void __net_exit ctinfo_exit_net(struct net *net)
> ++{
> ++ struct tc_action_net *tn = net_generic(net, ctinfo_net_id);
> ++
> ++ tc_action_net_exit(tn);
> ++}
> ++
> ++static struct pernet_operations ctinfo_net_ops = {
> ++ .init = ctinfo_init_net,
> ++ .exit = ctinfo_exit_net,
> ++ .id = &ctinfo_net_id,
> ++ .size = sizeof(struct tc_action_net),
> ++};
> ++
> ++static int __init ctinfo_init_module(void)
> ++{
> ++ return tcf_register_action(&act_ctinfo_ops, &ctinfo_net_ops);
> ++}
> ++
> ++static void __exit ctinfo_cleanup_module(void)
> ++{
> ++ tcf_unregister_action(&act_ctinfo_ops, &ctinfo_net_ops);
> ++}
> ++
> ++module_init(ctinfo_init_module);
> ++module_exit(ctinfo_cleanup_module);
> ++MODULE_AUTHOR("Kevin Darbyshire-Bryant <ldir at darbyshire-bryant.me.uk>");
> ++MODULE_DESCRIPTION("Conntrack mark to DSCP restoring");
> ++MODULE_LICENSE("GPL");
> +--
> +2.20.1 (Apple Git-117)
> +
> diff --git a/target/linux/generic/hack-4.14/0001-netfilter-connmark-introduce-savedscp.patch b/target/linux/generic/hack-4.14/0001-netfilter-connmark-introduce-savedscp.patch
> new file mode 100644
> index 0000000000..4124793bce
> --- /dev/null
> +++ b/target/linux/generic/hack-4.14/0001-netfilter-connmark-introduce-savedscp.patch
> @@ -0,0 +1,107 @@
> +From 5a4d7714faa28c03e85d696fba82716fbda5c432 Mon Sep 17 00:00:00 2001
> +From: Kevin Darbyshire-Bryant <ldir at darbyshire-bryant.me.uk>
> +Date: Sat, 23 Mar 2019 09:29:49 +0000
> +Subject: [PATCH] netfilter: connmark: introduce savedscp
> +
> +savedscp is a method of storing the DSCP of an ip packet into conntrack
> +mark. In combination with a suitable tc filter action (conndscp but may
> +end up being integrated into connmark) DSCP values are able to be stored
> +on egress and restored on ingress across links that otherwise alter or
> +bleach DSCP.
> +
> +This is useful for qdiscs such as CAKE which are able to shape according
> +to policies based on DSCP.
> +
> +Ingress classification is traditionally a challenging task since
> +iptables rules haven't yet run and tc filter/eBPF programs are pre-NAT
> +lookups, hence are unable to see internal IPv4 addresses as used on the
> +typical home masquerading gateway.
> +
> +The ingress problem is solved by the tc filter, but the tc people didn't
> +like the idea of tc setting conntrack mark values, though they are ok
> +with reading conntrack values and hence restoring DSCP from conntrack
> +marks.
> +
> +x_tables CONNMARK with the new savedscp action solves the problem of
> +storing the DSCP to the conntrack mark.
> +
> +It accepts 2 parameters. The mark is a 32bit value with usually one 1
> +bit set. This bit is set when savedscp saves the DSCP to the mark.
> +This is useful to implement a 'one shot'
> +iptables based classification where the 'complicated' iptables rules are
> +only run once to classify the connection on initial (egress) packet and
> +subsequent packets are all marked/restored with the same DSCP. A mark
> +of zero disables the setting of a status bit/s.
> +
> +The mask is a 32bit value of at least 6 contiguous bits and represents
> +the area where the DSCP will be stored.
> +
> +e.g.
> +
> +iptables -A QOS_MARK_eth0 -t mangle -j CONNMARK --savedscp-mark 0xfc000000/0x01000000
> +
> +Would store the DSCP in the top 6 bits of the 32bit mark field, and use
> +the LSB of the top byte as the 'DSCP has been stored' marker.
> +
> +Signed-off-by: Kevin Darbyshire-Bryant <ldir at darbyshire-bryant.me.uk>
> +---
> + include/uapi/linux/netfilter/xt_connmark.h | 3 ++-
> + net/netfilter/xt_connmark.c | 21 ++++++++++++++++++++-
> + 2 files changed, 22 insertions(+), 2 deletions(-)
> +
> +diff --git a/include/uapi/linux/netfilter/xt_connmark.h b/include/uapi/linux/netfilter/xt_connmark.h
> +index 408a9654f05c..e63ad3c89b92 100644
> +--- a/include/uapi/linux/netfilter/xt_connmark.h
> ++++ b/include/uapi/linux/netfilter/xt_connmark.h
> +@@ -16,7 +16,8 @@
> + enum {
> + XT_CONNMARK_SET = 0,
> + XT_CONNMARK_SAVE,
> +- XT_CONNMARK_RESTORE
> ++ XT_CONNMARK_RESTORE,
> ++ XT_CONNMARK_SAVEDSCP
> + };
> +
> + struct xt_connmark_tginfo1 {
> +diff --git a/net/netfilter/xt_connmark.c b/net/netfilter/xt_connmark.c
> +index ec377cc6a369..aa5bcea8f9ba 100644
> +--- a/net/netfilter/xt_connmark.c
> ++++ b/net/netfilter/xt_connmark.c
> +@@ -42,6 +42,7 @@ connmark_tg(struct sk_buff *skb, const struct xt_action_param *par)
> + enum ip_conntrack_info ctinfo;
> + struct nf_conn *ct;
> + u_int32_t newmark;
> ++ u8 dscp, maskshift;
> +
> + ct = nf_ct_get(skb, &ctinfo);
> + if (ct == NULL)
> +@@ -57,7 +58,25 @@ connmark_tg(struct sk_buff *skb, const struct xt_action_param *par)
> + break;
> + case XT_CONNMARK_SAVE:
> + newmark = (ct->mark & ~info->ctmask) ^
> +- (skb->mark & info->nfmask);
> ++ (skb->mark & info->nfmask);
> ++ if (ct->mark != newmark) {
> ++ ct->mark = newmark;
> ++ nf_conntrack_event_cache(IPCT_MARK, ct);
> ++ }
> ++ break;
> ++ case XT_CONNMARK_SAVEDSCP:
> ++ if (skb->protocol == htons(ETH_P_IP))
> ++ dscp = ipv4_get_dsfield(ip_hdr(skb)) >> 2;
> ++ else if (skb->protocol == htons(ETH_P_IPV6))
> ++ dscp = ipv6_get_dsfield(ipv6_hdr(skb)) >> 2;
> ++ else /* protocol doesn't have diffserv */
> ++ break;
> ++
> ++ /* nfmask contains the mask shift value */
> ++ maskshift = info->nfmask & 0x1f;
> ++ newmark = (ct->mark & ~info->ctmark) ^
> ++ (info->ctmask | (dscp << maskshift));
> ++
> + if (ct->mark != newmark) {
> + ct->mark = newmark;
> + nf_conntrack_event_cache(IPCT_MARK, ct);
> +--
> +2.20.1 (Apple Git-117)
> +
> diff --git a/target/linux/generic/hack-4.19/0001-net-sched-Introduce-act_ctinfo-action.patch b/target/linux/generic/hack-4.19/0001-net-sched-Introduce-act_ctinfo-action.patch
> new file mode 100644
> index 0000000000..4b4c75330d
> --- /dev/null
> +++ b/target/linux/generic/hack-4.19/0001-net-sched-Introduce-act_ctinfo-action.patch
> @@ -0,0 +1,658 @@
> +From e2448527ccb52ee173fb7fe3b35dc2f88281f4cc Mon Sep 17 00:00:00 2001
> +From: Kevin Darbyshire-Bryant <ldir at darbyshire-bryant.me.uk>
> +Date: Wed, 13 Mar 2019 20:54:49 +0000
> +Subject: [PATCH] net: sched: Introduce act_ctinfo action
> +
> +ctinfo is a new tc filter action module. It is designed to restore DSCPs
> +stored in conntrack marks into the ipv4/v6 diffserv field.
> +
> +The feature is intended for use and has been found useful for restoring
> +ingress classifications based on egress classifications across links
> +that bleach or otherwise change DSCP, typically home ISP Internet links.
> +Restoring DSCP on ingress on the WAN link allows qdiscs such as CAKE to
> +shape inbound packets according to policies that are easier to indicate
> +on egress.
> +
> +Ingress classification is traditionally a challenging task since
> +iptables rules haven't yet run and tc filter/eBPF programs are pre-NAT
> +lookups, hence are unable to see internal IPv4 addresses as used on the
> +typical home masquerading gateway.
> +
> +ctinfo understands the following parameters:
> +
> +dscp dscpmask[/statemask]
> +
> +dscpmask - a 32 bit mask of at least 6 contiguous bits and indicates
> +where ctinfo will find the DSCP bits stored in the conntrack mark.
> +
> +statemask - a 32 bit mask of (usually) 1 bit length, outside the area
> +specified by dscpmask. This represents a conditional operation flag
> +whereby the DSCP is only restored if the flag is set. This is useful to
> +implement a 'one shot' iptables based classification where the
> +'complicated' iptables rules are only run once to classify the
> +connection on initial (egress) packet and subsequent packets are all
> +marked/restored with the same DSCP. A mask of zero disables the
> +conditional behaviour ie. the conntrack mark DSCP bits are always
> +restored to the ip diffserv field (assuming the conntrack entry is found
> +& the skb is an ipv4/ipv6 type)
> +
> +optional parameters:
> +
> +zone - conntrack zone
> +
> +control - action related control (reclassify | pipe | drop | continue |
> +ok | goto chain <CHAIN_INDEX>)
> +
> +e.g. dscp 0xfc000000/0x01000000
> +
> +|----0xFC----conntrack mark----000000---|
> +| Bits 31-26 | bit 25 | bit24 |~~~ Bit 0|
> +| DSCP | unused | flag |unused |
> +|-----------------------0x01---000000---|
> + | |
> + | |
> + ---| Conditional flag
> + v only restore if set
> +|-ip diffserv-|
> +| 6 bits |
> +|-------------|
> +
> +Signed-off-by: Kevin Darbyshire-Bryant <ldir at darbyshire-bryant.me.uk>
> +---
> + include/net/tc_act/tc_ctinfo.h | 28 ++
> + include/uapi/linux/pkt_cls.h | 3 +-
> + include/uapi/linux/tc_act/tc_ctinfo.h | 52 +++
> + net/sched/Kconfig | 17 +
> + net/sched/Makefile | 1 +
> + net/sched/act_ctinfo.c | 418 ++++++++++++++++++++++
> + tools/testing/selftests/tc-testing/config | 1 +
> + 7 files changed, 519 insertions(+), 1 deletion(-)
> + create mode 100644 include/net/tc_act/tc_ctinfo.h
> + create mode 100644 include/uapi/linux/tc_act/tc_ctinfo.h
> + create mode 100644 net/sched/act_ctinfo.c
> +
> +diff --git a/include/net/tc_act/tc_ctinfo.h b/include/net/tc_act/tc_ctinfo.h
> +new file mode 100644
> +index 000000000000..d6a688571672
> +--- /dev/null
> ++++ b/include/net/tc_act/tc_ctinfo.h
> +@@ -0,0 +1,28 @@
> ++/* SPDX-License-Identifier: GPL-2.0 */
> ++#ifndef __NET_TC_CTINFO_H
> ++#define __NET_TC_CTINFO_H
> ++
> ++#include <net/act_api.h>
> ++
> ++struct tcf_ctinfo_params {
> ++ struct rcu_head rcu;
> ++ struct net *net;
> ++ u32 dscpmask;
> ++ u32 dscpstatemask;
> ++ u32 cpmarkmask;
> ++ u16 zone;
> ++ u8 mode;
> ++ u8 dscpmaskshift;
> ++};
> ++
> ++struct tcf_ctinfo {
> ++ struct tc_action common;
> ++ struct tcf_ctinfo_params __rcu *params;
> ++ u64 stats_dscp_set;
> ++ u64 stats_dscp_error;
> ++ u64 stats_cpmark_set;
> ++};
> ++
> ++#define to_ctinfo(a) ((struct tcf_ctinfo *)a)
> ++
> ++#endif /* __NET_TC_CTINFO_H */
> +diff --git a/include/uapi/linux/pkt_cls.h b/include/uapi/linux/pkt_cls.h
> +index be382fb0592d..71e10c5a96a0 100644
> +--- a/include/uapi/linux/pkt_cls.h
> ++++ b/include/uapi/linux/pkt_cls.h
> +@@ -68,7 +68,8 @@ enum {
> + TCA_ID_UNSPEC=0,
> + TCA_ID_POLICE=1,
> + /* other actions go here */
> +- __TCA_ID_MAX=255
> ++ TCA_ID_CTINFO,
> ++ __TCA_ID_MAX = 255
> + };
> +
> + #define TCA_ID_MAX __TCA_ID_MAX
> +diff --git a/include/uapi/linux/tc_act/tc_ctinfo.h b/include/uapi/linux/tc_act/tc_ctinfo.h
> +new file mode 100644
> +index 000000000000..48c40f657575
> +--- /dev/null
> ++++ b/include/uapi/linux/tc_act/tc_ctinfo.h
> +@@ -0,0 +1,52 @@
> ++/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
> ++#ifndef __UAPI_TC_CTINFO_H
> ++#define __UAPI_TC_CTINFO_H
> ++
> ++#include <linux/types.h>
> ++#include <linux/pkt_cls.h>
> ++
> ++struct tc_ctinfo {
> ++ tc_gen;
> ++};
> ++
> ++struct tc_ctinfo_dscp {
> ++ __u32 mask;
> ++ __u32 statemask;
> ++};
> ++
> ++struct tc_ctinfo_cpmark {
> ++ __u32 mask;
> ++};
> ++
> ++struct tc_ctinfo_stats_dscp {
> ++ __u64 set;
> ++ __u64 error;
> ++};
> ++
> ++struct tc_ctinfo_stats_cpmark {
> ++ __u64 set;
> ++};
> ++
> ++enum {
> ++ TCA_CTINFO_UNSPEC,
> ++ TCA_CTINFO_PAD,
> ++ TCA_CTINFO_TM,
> ++ TCA_CTINFO_ACT,
> ++ TCA_CTINFO_ZONE,
> ++ TCA_CTINFO_PARMS_DSCP,
> ++ TCA_CTINFO_PARMS_CPMARK,
> ++ TCA_CTINFO_MODE_DSCP,
> ++ TCA_CTINFO_MODE_CPMARK,
> ++ TCA_CTINFO_STATS_DSCP,
> ++ TCA_CTINFO_STATS_CPMARK,
> ++ __TCA_CTINFO_MAX
> ++};
> ++
> ++#define TCA_CTINFO_MAX (__TCA_CTINFO_MAX - 1)
> ++
> ++enum {
> ++ CTINFO_MODE_DSCP = BIT(0),
> ++ CTINFO_MODE_CPMARK = BIT(1)
> ++};
> ++
> ++#endif
> +diff --git a/net/sched/Kconfig b/net/sched/Kconfig
> +index e95741388311..1d79d5dba6e4 100644
> +--- a/net/sched/Kconfig
> ++++ b/net/sched/Kconfig
> +@@ -866,6 +866,23 @@ config NET_ACT_CONNMARK
> + To compile this code as a module, choose M here: the
> + module will be called act_connmark.
> +
> ++config NET_ACT_CTINFO
> ++ tristate "Netfilter Connection Mark Actions"
> ++ depends on NET_CLS_ACT && NETFILTER && IP_NF_IPTABLES
> ++ depends on NF_CONNTRACK && NF_CONNTRACK_MARK
> ++ help
> ++ Say Y here to allow transfer of a connmark stored information.
> ++ Current actions transfer connmark stored DSCP into
> ++ ipv4/v6 diffserv and/or to transfer connmark to packet
> ++ mark. Both are useful for restoring egress based marks
> ++ back onto ingress connections for qdisc priority mapping
> ++ purposes.
> ++
> ++ If unsure, say N.
> ++
> ++ To compile this code as a module, choose M here: the
> ++ module will be called act_ctinfo.
> ++
> + config NET_ACT_SKBMOD
> + tristate "skb data modification action"
> + depends on NET_CLS_ACT
> +diff --git a/net/sched/Makefile b/net/sched/Makefile
> +index f0403f49edcb..bb3c2bc44af7 100644
> +--- a/net/sched/Makefile
> ++++ b/net/sched/Makefile
> +@@ -21,6 +21,7 @@ obj-$(CONFIG_NET_ACT_CSUM) += act_csum.o
> + obj-$(CONFIG_NET_ACT_VLAN) += act_vlan.o
> + obj-$(CONFIG_NET_ACT_BPF) += act_bpf.o
> + obj-$(CONFIG_NET_ACT_CONNMARK) += act_connmark.o
> ++obj-$(CONFIG_NET_ACT_CTINFO) += act_ctinfo.o
> + obj-$(CONFIG_NET_ACT_SKBMOD) += act_skbmod.o
> + obj-$(CONFIG_NET_ACT_IFE) += act_ife.o
> + obj-$(CONFIG_NET_IFE_SKBMARK) += act_meta_mark.o
> +diff --git a/net/sched/act_ctinfo.c b/net/sched/act_ctinfo.c
> +new file mode 100644
> +index 000000000000..e0f180887447
> +--- /dev/null
> ++++ b/net/sched/act_ctinfo.c
> +@@ -0,0 +1,418 @@
> ++// SPDX-License-Identifier: GPL-2.0+
> ++/* net/sched/act_ctinfo.c netfilter ctinfo connmark actions
> ++ *
> ++ * Copyright (c) 2019 Kevin Darbyshire-Bryant <ldir at darbyshire-bryant.me.uk>
> ++ */
> ++
> ++#include <linux/module.h>
> ++#include <linux/init.h>
> ++#include <linux/kernel.h>
> ++#include <linux/skbuff.h>
> ++#include <linux/rtnetlink.h>
> ++#include <linux/pkt_cls.h>
> ++#include <linux/ip.h>
> ++#include <linux/ipv6.h>
> ++#include <net/netlink.h>
> ++#include <net/pkt_sched.h>
> ++#include <net/act_api.h>
> ++#include <net/pkt_cls.h>
> ++#include <uapi/linux/tc_act/tc_ctinfo.h>
> ++#include <net/tc_act/tc_ctinfo.h>
> ++
> ++#include <net/netfilter/nf_conntrack.h>
> ++#include <net/netfilter/nf_conntrack_core.h>
> ++#include <net/netfilter/nf_conntrack_ecache.h>
> ++#include <net/netfilter/nf_conntrack_zones.h>
> ++
> ++static struct tc_action_ops act_ctinfo_ops;
> ++static unsigned int ctinfo_net_id;
> ++
> ++static void tcf_ctinfo_dscp_set(struct nf_conn *ct, struct tcf_ctinfo *ca,
> ++ struct tcf_ctinfo_params *cp,
> ++ struct sk_buff *skb, int wlen, int proto)
> ++{
> ++ u8 dscp, newdscp;
> ++
> ++ newdscp = (((ct->mark & cp->dscpmask) >> cp->dscpmaskshift) << 2) &
> ++ ~INET_ECN_MASK;
> ++
> ++ switch (proto) {
> ++ case NFPROTO_IPV4:
> ++ dscp = ipv4_get_dsfield(ip_hdr(skb)) & ~INET_ECN_MASK;
> ++ if (dscp != newdscp) {
> ++ if (likely(!skb_try_make_writable(skb, wlen))) {
> ++ ipv4_change_dsfield(ip_hdr(skb),
> ++ INET_ECN_MASK,
> ++ newdscp);
> ++ ca->stats_dscp_set++;
> ++ } else {
> ++ ca->stats_dscp_error++;
> ++ }
> ++ }
> ++ break;
> ++ case NFPROTO_IPV6:
> ++ dscp = ipv6_get_dsfield(ipv6_hdr(skb)) & ~INET_ECN_MASK;
> ++ if (dscp != newdscp) {
> ++ if (likely(!skb_try_make_writable(skb, wlen))) {
> ++ ipv6_change_dsfield(ipv6_hdr(skb),
> ++ INET_ECN_MASK,
> ++ newdscp);
> ++ ca->stats_dscp_set++;
> ++ } else {
> ++ ca->stats_dscp_error++;
> ++ }
> ++ }
> ++ break;
> ++ default:
> ++ break;
> ++ }
> ++}
> ++
> ++static void tcf_ctinfo_cpmark_set(struct nf_conn *ct, struct tcf_ctinfo *ca,
> ++ struct tcf_ctinfo_params *cp,
> ++ struct sk_buff *skb)
> ++{
> ++ ca->stats_cpmark_set++;
> ++ skb->mark = ct->mark & cp->cpmarkmask;
> ++}
> ++
> ++static int tcf_ctinfo_act(struct sk_buff *skb, const struct tc_action *a,
> ++ struct tcf_result *res)
> ++{
> ++ const struct nf_conntrack_tuple_hash *thash = NULL;
> ++ struct tcf_ctinfo *ca = to_ctinfo(a);
> ++ struct nf_conntrack_tuple tuple;
> ++ struct nf_conntrack_zone zone;
> ++ enum ip_conntrack_info ctinfo;
> ++ struct tcf_ctinfo_params *cp;
> ++ struct nf_conn *ct;
> ++ int proto, wlen;
> ++ int action;
> ++
> ++ cp = rcu_dereference_bh(ca->params);
> ++
> ++ tcf_lastuse_update(&ca->tcf_tm);
> ++ bstats_update(&ca->tcf_bstats, skb);
> ++ action = READ_ONCE(ca->tcf_action);
> ++
> ++ wlen = skb_network_offset(skb);
> ++ if (tc_skb_protocol(skb) == htons(ETH_P_IP)) {
> ++ wlen += sizeof(struct iphdr);
> ++ if (!pskb_may_pull(skb, wlen))
> ++ goto out;
> ++
> ++ proto = NFPROTO_IPV4;
> ++ } else if (tc_skb_protocol(skb) == htons(ETH_P_IPV6)) {
> ++ wlen += sizeof(struct ipv6hdr);
> ++ if (!pskb_may_pull(skb, wlen))
> ++ goto out;
> ++
> ++ proto = NFPROTO_IPV6;
> ++ } else {
> ++ goto out;
> ++ }
> ++
> ++ ct = nf_ct_get(skb, &ctinfo);
> ++ if (!ct) { /* look harder, usually ingress */
> ++ if (!nf_ct_get_tuplepr(skb, skb_network_offset(skb),
> ++ proto, cp->net, &tuple))
> ++ goto out;
> ++ zone.id = cp->zone;
> ++ zone.dir = NF_CT_DEFAULT_ZONE_DIR;
> ++
> ++ thash = nf_conntrack_find_get(cp->net, &zone, &tuple);
> ++ if (!thash)
> ++ goto out;
> ++
> ++ ct = nf_ct_tuplehash_to_ctrack(thash);
> ++ }
> ++
> ++ if (cp->mode & CTINFO_MODE_DSCP)
> ++ if (!cp->dscpstatemask || (ct->mark & cp->dscpstatemask))
> ++ tcf_ctinfo_dscp_set(ct, ca, cp, skb, wlen, proto);
> ++
> ++ if (cp->mode & CTINFO_MODE_CPMARK)
> ++ tcf_ctinfo_cpmark_set(ct, ca, cp, skb);
> ++
> ++ if (thash)
> ++ nf_ct_put(ct);
> ++out:
> ++ return action;
> ++}
> ++
> ++static const struct nla_policy ctinfo_policy[TCA_CTINFO_MAX + 1] = {
> ++ [TCA_CTINFO_ACT] = { .len = sizeof(struct tc_ctinfo) },
> ++ [TCA_CTINFO_ZONE] = { .type = NLA_U16 },
> ++ [TCA_CTINFO_PARMS_DSCP] = { .len = sizeof(struct tc_ctinfo_dscp) },
> ++ [TCA_CTINFO_PARMS_CPMARK] = { .len = sizeof(struct tc_ctinfo_cpmark) },
> ++ [TCA_CTINFO_MODE_DSCP] = { .type = NLA_FLAG },
> ++ [TCA_CTINFO_MODE_CPMARK] = { .type = NLA_FLAG },
> ++};
> ++
> ++static int tcf_ctinfo_init(struct net *net, struct nlattr *nla,
> ++ struct nlattr *est, struct tc_action **a,
> ++ int ovr, int bind, bool rtnl_held,
> ++ struct netlink_ext_ack *extack)
> ++{
> ++ struct tc_action_net *tn = net_generic(net, ctinfo_net_id);
> ++ struct tc_ctinfo_cpmark *cpmarkparm = NULL;
> ++ struct nlattr *tb[TCA_CTINFO_MAX + 1];
> ++ struct tcf_ctinfo_params *cp_new;
> ++/* struct tcf_chain *goto_ch = NULL; */
> ++ struct tc_ctinfo_dscp *dscpparm;
> ++ struct tcf_ctinfo *ci;
> ++ struct tc_ctinfo *actparm;
> ++ int ret = 0, err, i;
> ++
> ++ if (!nla)
> ++ return -EINVAL;
> ++
> ++ err = nla_parse_nested(tb, TCA_CTINFO_MAX, nla, ctinfo_policy, NULL);
> ++ if (err < 0)
> ++ return err;
> ++
> ++ if (!tb[TCA_CTINFO_ACT])
> ++ return -EINVAL;
> ++
> ++ if (tb[TCA_CTINFO_MODE_DSCP] && !tb[TCA_CTINFO_PARMS_DSCP])
> ++ return -EINVAL;
> ++
> ++ if (tb[TCA_CTINFO_PARMS_CPMARK])
> ++ cpmarkparm = nla_data(tb[TCA_CTINFO_PARMS_CPMARK]);
> ++
> ++ dscpparm = nla_data(tb[TCA_CTINFO_PARMS_DSCP]);
> ++ actparm = nla_data(tb[TCA_CTINFO_ACT]);
> ++
> ++ if (dscpparm) {
> ++ /* need contiguous 6 bit mask */
> ++ i = dscpparm->mask ? __ffs(dscpparm->mask) : 0;
> ++ if ((~0 & (dscpparm->mask >> i)) != 0x3f)
> ++ return -EINVAL;
> ++ /* mask & statemask must not overlap */
> ++ if (dscpparm->mask & dscpparm->statemask)
> ++ return -EINVAL;
> ++ }
> ++
> ++ /* done the validation:now to the actual action allocation */
> ++ err = tcf_idr_check_alloc(tn, &actparm->index, a, bind);
> ++ if (!err) {
> ++ ret = tcf_idr_create(tn, actparm->index, est, a,
> ++ &act_ctinfo_ops, bind, false);
> ++ if (ret) {
> ++ tcf_idr_cleanup(tn, actparm->index);
> ++ return ret;
> ++ }
> ++ ret = ACT_P_CREATED;
> ++ } else if (err > 0) {
> ++ if (bind) /* don't override defaults */
> ++ return 0;
> ++ if (!ovr) {
> ++ tcf_idr_release(*a, bind);
> ++ return -EEXIST;
> ++ }
> ++ } else {
> ++ return err;
> ++ }
> ++
> ++/* err = tcf_action_check_ctrlact(actparm->action, tp, &goto_ch, extack);
> ++ if (err < 0)
> ++ goto release_idr;
> ++ */
> ++
> ++ ci = to_ctinfo(*a);
> ++
> ++ cp_new = kzalloc(sizeof(*cp_new), GFP_KERNEL);
> ++ if (unlikely(!cp_new)) {
> ++ err = -ENOMEM;
> ++ goto put_chain;
> ++ }
> ++
> ++ cp_new->net = net;
> ++ cp_new->zone = tb[TCA_CTINFO_ZONE] ?
> ++ nla_get_u16(tb[TCA_CTINFO_ZONE]) : 0;
> ++ if (dscpparm) {
> ++ cp_new->dscpmask = dscpparm->mask;
> ++ cp_new->dscpmaskshift = cp_new->dscpmask ?
> ++ __ffs(cp_new->dscpmask) : 0;
> ++ cp_new->dscpstatemask = dscpparm->statemask;
> ++ }
> ++
> ++ if (cpmarkparm)
> ++ cp_new->cpmarkmask = cpmarkparm->mask;
> ++ else
> ++ cp_new->cpmarkmask = ~0;
> ++
> ++ if (tb[TCA_CTINFO_MODE_DSCP])
> ++ cp_new->mode |= CTINFO_MODE_DSCP;
> ++ else
> ++ cp_new->mode &= ~CTINFO_MODE_DSCP;
> ++
> ++ if (tb[TCA_CTINFO_MODE_CPMARK])
> ++ cp_new->mode |= CTINFO_MODE_CPMARK;
> ++ else
> ++ cp_new->mode &= ~CTINFO_MODE_CPMARK;
> ++
> ++ spin_lock_bh(&ci->tcf_lock);
> ++/* goto_ch = tcf_action_set_ctrlact(*a, actparm->action, goto_ch); */
> ++ ci->tcf_action = actparm->action;
> ++ rcu_swap_protected(ci->params, cp_new,
> ++ lockdep_is_held(&ci->tcf_lock));
> ++ spin_unlock_bh(&ci->tcf_lock);
> ++
> ++/* if (goto_ch)
> ++ tcf_chain_put_by_act(goto_ch); */
> ++ if (cp_new)
> ++ kfree_rcu(cp_new, rcu);
> ++
> ++ if (ret == ACT_P_CREATED)
> ++ tcf_idr_insert(tn, *a);
> ++
> ++ return ret;
> ++
> ++put_chain:
> ++/* if (goto_ch)
> ++ tcf_chain_put_by_act(goto_ch);
> ++release_idr: */
> ++ tcf_idr_release(*a, bind);
> ++ return err;
> ++}
> ++
> ++static int tcf_ctinfo_dump(struct sk_buff *skb, struct tc_action *a,
> ++ int bind, int ref)
> ++{
> ++ struct tcf_ctinfo *ci = to_ctinfo(a);
> ++ struct tc_ctinfo opt = {
> ++ .index = ci->tcf_index,
> ++ .refcnt = refcount_read(&ci->tcf_refcnt) - ref,
> ++ .bindcnt = atomic_read(&ci->tcf_bindcnt) - bind,
> ++ };
> ++ struct tc_ctinfo_stats_cpmark cpmarkstats;
> ++ unsigned char *b = skb_tail_pointer(skb);
> ++ struct tc_ctinfo_stats_dscp dscpstats;
> ++ struct tc_ctinfo_cpmark cpmarkparm;
> ++ struct tc_ctinfo_dscp dscpparm;
> ++ struct tcf_ctinfo_params *cp;
> ++ struct tcf_t t;
> ++
> ++ spin_lock_bh(&ci->tcf_lock);
> ++ cp = rcu_dereference_protected(ci->params,
> ++ lockdep_is_held(&ci->tcf_lock));
> ++ opt.action = ci->tcf_action;
> ++
> ++ if (nla_put(skb, TCA_CTINFO_ACT, sizeof(opt), &opt))
> ++ goto nla_put_failure;
> ++
> ++ if (cp->mode & CTINFO_MODE_DSCP) {
> ++ dscpparm.mask = cp->dscpmask;
> ++ dscpparm.statemask = cp->dscpstatemask;
> ++ if (nla_put(skb, TCA_CTINFO_PARMS_DSCP, sizeof(dscpparm),
> ++ &dscpparm))
> ++ goto nla_put_failure;
> ++
> ++ if (nla_put_flag(skb, TCA_CTINFO_MODE_DSCP))
> ++ goto nla_put_failure;
> ++
> ++ dscpstats.error = ci->stats_dscp_error;
> ++ dscpstats.set = ci->stats_dscp_set;
> ++ if (nla_put(skb, TCA_CTINFO_STATS_DSCP, sizeof(dscpstats),
> ++ &dscpstats))
> ++ goto nla_put_failure;
> ++ }
> ++
> ++ if (cp->mode & CTINFO_MODE_CPMARK) {
> ++ cpmarkparm.mask = cp->cpmarkmask;
> ++ if (nla_put(skb, TCA_CTINFO_PARMS_CPMARK, sizeof(cpmarkparm),
> ++ &cpmarkparm))
> ++ goto nla_put_failure;
> ++
> ++ if (nla_put_flag(skb, TCA_CTINFO_MODE_CPMARK))
> ++ goto nla_put_failure;
> ++
> ++ cpmarkstats.set = ci->stats_cpmark_set;
> ++ if (nla_put_u64_64bit(skb, TCA_CTINFO_STATS_CPMARK,
> ++ ci->stats_cpmark_set, TCA_CTINFO_PAD))
> ++ goto nla_put_failure;
> ++ }
> ++
> ++ if (cp->zone) {
> ++ if (nla_put_u16(skb, TCA_CTINFO_ZONE, cp->zone))
> ++ goto nla_put_failure;
> ++ }
> ++
> ++ tcf_tm_dump(&t, &ci->tcf_tm);
> ++ if (nla_put_64bit(skb, TCA_CTINFO_TM, sizeof(t), &t, TCA_CTINFO_PAD))
> ++ goto nla_put_failure;
> ++
> ++ spin_unlock_bh(&ci->tcf_lock);
> ++ return skb->len;
> ++
> ++nla_put_failure:
> ++ spin_unlock_bh(&ci->tcf_lock);
> ++ nlmsg_trim(skb, b);
> ++ return -1;
> ++}
> ++
> ++static int tcf_ctinfo_walker(struct net *net, struct sk_buff *skb,
> ++ struct netlink_callback *cb, int type,
> ++ const struct tc_action_ops *ops,
> ++ struct netlink_ext_ack *extack)
> ++{
> ++ struct tc_action_net *tn = net_generic(net, ctinfo_net_id);
> ++
> ++ return tcf_generic_walker(tn, skb, cb, type, ops, extack);
> ++}
> ++
> ++static int tcf_ctinfo_search(struct net *net, struct tc_action **a, u32 index,
> ++ struct netlink_ext_ack *extack)
> ++{
> ++ struct tc_action_net *tn = net_generic(net, ctinfo_net_id);
> ++
> ++ return tcf_idr_search(tn, a, index);
> ++}
> ++
> ++static struct tc_action_ops act_ctinfo_ops = {
> ++ .kind = "ctinfo",
> ++ .type = TCA_ID_CTINFO,
> ++ .owner = THIS_MODULE,
> ++ .act = tcf_ctinfo_act,
> ++ .dump = tcf_ctinfo_dump,
> ++ .init = tcf_ctinfo_init,
> ++ .walk = tcf_ctinfo_walker,
> ++ .lookup = tcf_ctinfo_search,
> ++ .size = sizeof(struct tcf_ctinfo),
> ++};
> ++
> ++static __net_init int ctinfo_init_net(struct net *net)
> ++{
> ++ struct tc_action_net *tn = net_generic(net, ctinfo_net_id);
> ++
> ++ return tc_action_net_init(tn, &act_ctinfo_ops);
> ++}
> ++
> ++static void __net_exit ctinfo_exit_net(struct list_head *net_list)
> ++{
> ++ tc_action_net_exit(net_list, ctinfo_net_id);
> ++}
> ++
> ++static struct pernet_operations ctinfo_net_ops = {
> ++ .init = ctinfo_init_net,
> ++ .exit_batch = ctinfo_exit_net,
> ++ .id = &ctinfo_net_id,
> ++ .size = sizeof(struct tc_action_net),
> ++};
> ++
> ++static int __init ctinfo_init_module(void)
> ++{
> ++ return tcf_register_action(&act_ctinfo_ops, &ctinfo_net_ops);
> ++}
> ++
> ++static void __exit ctinfo_cleanup_module(void)
> ++{
> ++ tcf_unregister_action(&act_ctinfo_ops, &ctinfo_net_ops);
> ++}
> ++
> ++module_init(ctinfo_init_module);
> ++module_exit(ctinfo_cleanup_module);
> ++MODULE_AUTHOR("Kevin Darbyshire-Bryant <ldir at darbyshire-bryant.me.uk>");
> ++MODULE_DESCRIPTION("Connection tracking mark actions");
> ++MODULE_LICENSE("GPL");
> +diff --git a/tools/testing/selftests/tc-testing/config b/tools/testing/selftests/tc-testing/config
> +index 203302065458..9d1fddcfb887 100644
> +--- a/tools/testing/selftests/tc-testing/config
> ++++ b/tools/testing/selftests/tc-testing/config
> +@@ -37,6 +37,7 @@ CONFIG_NET_ACT_SKBEDIT=m
> + CONFIG_NET_ACT_CSUM=m
> + CONFIG_NET_ACT_VLAN=m
> + CONFIG_NET_ACT_BPF=m
> ++CONFIG_NET_ACT_CONNDSCP=m
> + CONFIG_NET_ACT_CONNMARK=m
> + CONFIG_NET_ACT_SKBMOD=m
> + CONFIG_NET_ACT_IFE=m
> +--
> +2.20.1 (Apple Git-117)
> +
> diff --git a/target/linux/generic/hack-4.19/0001-netfilter-connmark-introduce-savedscp.patch b/target/linux/generic/hack-4.19/0001-netfilter-connmark-introduce-savedscp.patch
> new file mode 100644
> index 0000000000..cef2dd80f5
> --- /dev/null
> +++ b/target/linux/generic/hack-4.19/0001-netfilter-connmark-introduce-savedscp.patch
> @@ -0,0 +1,142 @@
> +From f171924dcf1d0b31fb7bd1cff113d7a1f7f05ec2 Mon Sep 17 00:00:00 2001
> +From: Kevin Darbyshire-Bryant <ldir at darbyshire-bryant.me.uk>
> +Date: Sat, 23 Mar 2019 09:29:49 +0000
> +Subject: [PATCH] netfilter: connmark: introduce savedscp
> +
> +savedscp is a method of storing the DSCP of an ip packet into conntrack
> +mark. In combination with a suitable tc filter action (act_ctinfo) DSCP
> +values are able to be stored in the mark on egress and restored on
> +ingress across links that otherwise alter or bleach DSCP.
> +
> +This is useful for qdiscs such as CAKE which are able to shape according
> +to policies based on DSCP.
> +
> +Ingress classification is traditionally a challenging task since
> +iptables rules haven't yet run and tc filter/eBPF programs are pre-NAT
> +lookups, hence are unable to see internal IPv4 addresses as used on the
> +typical home masquerading gateway.
> +
> +x_tables CONNMARK savedscp action solves the problem of storing the DSCP
> +to the conntrack mark in a way suitable for the new act_ctinfo tc action
> +to restore.
> +
> +The savedsp option accepts 2 parameters, a 32bit 'dscpmask' and a 32bit
> +'statemask'. The dscp mask must be a minimum of 6 contiguous bits and
> +represents the area where the DSCP will be stored in the connmark. The
> +state mask is a minimum 1 bit length mask that must not overlap with the
> +dscpmask. It represents a flag which is set when the DSCP has been
> +stored in the conntrack mark. This is useful to implement a 'one shot'
> +iptables based classification where the 'complicated' iptables rules are
> +only run once to classify the connection on initial (egress) packet and
> +subsequent packets are all marked/restored with the same DSCP. A state
> +mask of zero disables the setting of a status bit/s.
> +
> +example syntax with a suitably modified iptables user space application:
> +
> +iptables -A QOS_MARK_eth0 -t mangle -j CONNMARK --savedscp-mark 0xfc000000/0x01000000
> +
> +Would store the DSCP in the top 6 bits of the 32bit mark field, and use
> +the LSB of the top byte as the 'DSCP has been stored' marker.
> +
> +|----0xFC----conntrack mark----000000---|
> +| Bits 31-26 | bit 25 | bit24 |~~~ Bit 0|
> +| DSCP | unused | flag |unused |
> +|-----------------------0x01---000000---|
> + ^ ^
> + | |
> + ---| Conditional flag
> + | set this when dscp
> +|-ip diffserv-| stored in mark
> +| 6 bits |
> +|-------------|
> +
> +an identically configured tc action to restore looks like:
> +
> +tc filter show dev eth0 ingress
> +filter parent ffff: protocol all pref 10 u32 chain 0
> +filter parent ffff: protocol all pref 10 u32 chain 0 fh 800: ht divisor 1
> +filter parent ffff: protocol all pref 10 u32 chain 0 fh 800::800 order 2048 key ht 800 bkt 0 flowid 1:1 not_in_hw
> + match 00000000/00000000 at 0
> + action order 1: ctinfo zone 0 pipe
> + index 2 ref 1 bind 1 dscp 0xfc000000/0x1000000
> +
> + action order 2: mirred (Egress Redirect to device ifb4eth0) stolen
> + index 1 ref 1 bind 1
> +
> +|----0xFC----conntrack mark----000000---|
> +| Bits 31-26 | bit 25 | bit24 |~~~ Bit 0|
> +| DSCP | unused | flag |unused |
> +|-----------------------0x01---000000---|
> + | |
> + | |
> + ---| Conditional flag
> + v only restore if set
> +|-ip diffserv-|
> +| 6 bits |
> +|-------------|
> +
> +Signed-off-by: Kevin Darbyshire-Bryant <ldir at darbyshire-bryant.me.uk>
> +---
> + include/uapi/linux/netfilter/xt_connmark.h | 3 ++-
> + net/netfilter/xt_connmark.c | 17 +++++++++++++++++
> + 2 files changed, 19 insertions(+), 1 deletion(-)
> +
> +diff --git a/include/uapi/linux/netfilter/xt_connmark.h b/include/uapi/linux/netfilter/xt_connmark.h
> +index 1aa5c955ee1e..24272cac2d37 100644
> +--- a/include/uapi/linux/netfilter/xt_connmark.h
> ++++ b/include/uapi/linux/netfilter/xt_connmark.h
> +@@ -16,7 +16,8 @@
> + enum {
> + XT_CONNMARK_SET = 0,
> + XT_CONNMARK_SAVE,
> +- XT_CONNMARK_RESTORE
> ++ XT_CONNMARK_RESTORE,
> ++ XT_CONNMARK_SAVEDSCP
> + };
> +
> + enum {
> +diff --git a/net/netfilter/xt_connmark.c b/net/netfilter/xt_connmark.c
> +index 29c38aa7f726..5ce2adde5595 100644
> +--- a/net/netfilter/xt_connmark.c
> ++++ b/net/netfilter/xt_connmark.c
> +@@ -42,6 +42,7 @@ connmark_tg_shift(struct sk_buff *skb, const struct xt_connmark_tginfo2 *info)
> + u_int32_t new_targetmark;
> + struct nf_conn *ct;
> + u_int32_t newmark;
> ++ u8 dscp;
> +
> + ct = nf_ct_get(skb, &ctinfo);
> + if (ct == NULL)
> +@@ -74,6 +75,21 @@ connmark_tg_shift(struct sk_buff *skb, const struct xt_connmark_tginfo2 *info)
> + nf_conntrack_event_cache(IPCT_MARK, ct);
> + }
> + break;
> ++ case XT_CONNMARK_SAVEDSCP:
> ++ if (skb->protocol == htons(ETH_P_IP))
> ++ dscp = ipv4_get_dsfield(ip_hdr(skb)) >> 2;
> ++ else if (skb->protocol == htons(ETH_P_IPV6))
> ++ dscp = ipv6_get_dsfield(ipv6_hdr(skb)) >> 2;
> ++ else /* protocol doesn't have diffserv */
> ++ break;
> ++
> ++ newmark = (ct->mark & ~info->ctmark) ^
> ++ (info->ctmask | (dscp << info->shift_bits));
> ++ if (ct->mark != newmark) {
> ++ ct->mark = newmark;
> ++ nf_conntrack_event_cache(IPCT_MARK, ct);
> ++ }
> ++ break;
> + case XT_CONNMARK_RESTORE:
> + new_targetmark = (ct->mark & info->ctmask);
> + if (info->shift_dir == D_SHIFT_RIGHT)
> +@@ -86,6 +102,7 @@ connmark_tg_shift(struct sk_buff *skb, const struct xt_connmark_tginfo2 *info)
> + skb->mark = newmark;
> + break;
> + }
> ++out:
> + return XT_CONTINUE;
> + }
> +
> +--
> +2.20.1 (Apple Git-117)
> +
> --
> 2.20.1 (Apple Git-117)
>
Cheers,
Kevin D-B
gpg: 012C ACB2 28C6 C53E 9775 9123 B3A2 389B 9DE2 334A
_______________________________________________
openwrt-devel mailing list
openwrt-devel at lists.openwrt.org
https://lists.openwrt.org/mailman/listinfo/openwrt-devel
More information about the openwrt-devel
mailing list