All of lore.kernel.org
 help / color / mirror / Atom feed
From: Pablo Neira Ayuso <pablo@netfilter.org>
To: netdev@vger.kernel.org
Cc: davem@davemloft.net, thomas.lendacky@amd.com,
	f.fainelli@gmail.com, ariel.elior@cavium.com,
	michael.chan@broadcom.com, santosh@chelsio.com,
	madalin.bucur@nxp.com, yisen.zhuang@huawei.com,
	salil.mehta@huawei.com, jeffrey.t.kirsher@intel.com,
	tariqt@mellanox.com, saeedm@mellanox.com, jiri@mellanox.com,
	idosch@mellanox.com, jakub.kicinski@netronome.com,
	peppe.cavallaro@st.com, grygorii.strashko@ti.com, andrew@lunn.ch,
	vivien.didelot@savoirfairelinux.com, alexandre.torgue@st.com,
	joabreu@synopsys.com, linux-net-drivers@solarflare.com,
	ganeshgr@chelsio.com, ogerlitz@mellanox.com,
	Manish.Chopra@cavium.com, marcelo.leitner@gmail.com,
	mkubecek@suse.cz, venkatkumar.duvvuru@broadcom.com,
	julia.lawall@lip6.fr, john.fastabend@gmail.com,
	netfilter-devel@vger.kernel.org, cphealy@gmail.com
Subject: [PATCH 04/12 net-next,v7] cls_api: add translator to flow_action representation
Date: Sat,  2 Feb 2019 12:50:46 +0100	[thread overview]
Message-ID: <20190202115054.4880-5-pablo@netfilter.org> (raw)
In-Reply-To: <20190202115054.4880-1-pablo@netfilter.org>

This patch implements a new function to translate from native TC action
to the new flow_action representation. Moreover, this patch also updates
cls_flower to use this new function.

Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
Acked-by: Jiri Pirko <jiri@mellanox.com>
---
v7: rebase on top of net-next.

 include/net/pkt_cls.h  |  2 +
 net/sched/cls_api.c    | 99 ++++++++++++++++++++++++++++++++++++++++++++++++++
 net/sched/cls_flower.c | 14 +++++++
 3 files changed, 115 insertions(+)

diff --git a/include/net/pkt_cls.h b/include/net/pkt_cls.h
index 74a7582ad47c..c470c100b926 100644
--- a/include/net/pkt_cls.h
+++ b/include/net/pkt_cls.h
@@ -620,6 +620,8 @@ tcf_match_indev(struct sk_buff *skb, int ifindex)
 }
 #endif /* CONFIG_NET_CLS_IND */
 
+int tc_setup_flow_action(struct flow_action *flow_action,
+			 const struct tcf_exts *exts);
 int tc_setup_cb_call(struct tcf_block *block, enum tc_setup_type type,
 		     void *type_data, bool err_stop);
 unsigned int tcf_exts_num_actions(struct tcf_exts *exts);
diff --git a/net/sched/cls_api.c b/net/sched/cls_api.c
index 57713c63ac56..02cf6d2fa0e1 100644
--- a/net/sched/cls_api.c
+++ b/net/sched/cls_api.c
@@ -32,6 +32,13 @@
 #include <net/pkt_sched.h>
 #include <net/pkt_cls.h>
 #include <net/tc_act/tc_pedit.h>
+#include <net/tc_act/tc_mirred.h>
+#include <net/tc_act/tc_vlan.h>
+#include <net/tc_act/tc_tunnel_key.h>
+#include <net/tc_act/tc_csum.h>
+#include <net/tc_act/tc_gact.h>
+#include <net/tc_act/tc_skbedit.h>
+#include <net/tc_act/tc_mirred.h>
 
 extern const struct nla_policy rtm_tca_policy[TCA_MAX + 1];
 
@@ -2516,6 +2523,98 @@ int tc_setup_cb_call(struct tcf_block *block, enum tc_setup_type type,
 }
 EXPORT_SYMBOL(tc_setup_cb_call);
 
+int tc_setup_flow_action(struct flow_action *flow_action,
+			 const struct tcf_exts *exts)
+{
+	const struct tc_action *act;
+	int i, j, k;
+
+	if (!exts)
+		return 0;
+
+	j = 0;
+	tcf_exts_for_each_action(i, act, exts) {
+		struct flow_action_entry *entry;
+
+		entry = &flow_action->entries[j];
+		if (is_tcf_gact_ok(act)) {
+			entry->id = FLOW_ACTION_ACCEPT;
+		} else if (is_tcf_gact_shot(act)) {
+			entry->id = FLOW_ACTION_DROP;
+		} else if (is_tcf_gact_trap(act)) {
+			entry->id = FLOW_ACTION_TRAP;
+		} else if (is_tcf_gact_goto_chain(act)) {
+			entry->id = FLOW_ACTION_GOTO;
+			entry->chain_index = tcf_gact_goto_chain_index(act);
+		} else if (is_tcf_mirred_egress_redirect(act)) {
+			entry->id = FLOW_ACTION_REDIRECT;
+			entry->dev = tcf_mirred_dev(act);
+		} else if (is_tcf_mirred_egress_mirror(act)) {
+			entry->id = FLOW_ACTION_MIRRED;
+			entry->dev = tcf_mirred_dev(act);
+		} else if (is_tcf_vlan(act)) {
+			switch (tcf_vlan_action(act)) {
+			case TCA_VLAN_ACT_PUSH:
+				entry->id = FLOW_ACTION_VLAN_PUSH;
+				entry->vlan.vid = tcf_vlan_push_vid(act);
+				entry->vlan.proto = tcf_vlan_push_proto(act);
+				entry->vlan.prio = tcf_vlan_push_prio(act);
+				break;
+			case TCA_VLAN_ACT_POP:
+				entry->id = FLOW_ACTION_VLAN_POP;
+				break;
+			case TCA_VLAN_ACT_MODIFY:
+				entry->id = FLOW_ACTION_VLAN_MANGLE;
+				entry->vlan.vid = tcf_vlan_push_vid(act);
+				entry->vlan.proto = tcf_vlan_push_proto(act);
+				entry->vlan.prio = tcf_vlan_push_prio(act);
+				break;
+			default:
+				goto err_out;
+			}
+		} else if (is_tcf_tunnel_set(act)) {
+			entry->id = FLOW_ACTION_TUNNEL_ENCAP;
+			entry->tunnel = tcf_tunnel_info(act);
+		} else if (is_tcf_tunnel_release(act)) {
+			entry->id = FLOW_ACTION_TUNNEL_DECAP;
+			entry->tunnel = tcf_tunnel_info(act);
+		} else if (is_tcf_pedit(act)) {
+			for (k = 0; k < tcf_pedit_nkeys(act); k++) {
+				switch (tcf_pedit_cmd(act, k)) {
+				case TCA_PEDIT_KEY_EX_CMD_SET:
+					entry->id = FLOW_ACTION_MANGLE;
+					break;
+				case TCA_PEDIT_KEY_EX_CMD_ADD:
+					entry->id = FLOW_ACTION_ADD;
+					break;
+				default:
+					goto err_out;
+				}
+				entry->mangle.htype = tcf_pedit_htype(act, k);
+				entry->mangle.mask = tcf_pedit_mask(act, k);
+				entry->mangle.val = tcf_pedit_val(act, k);
+				entry->mangle.offset = tcf_pedit_offset(act, k);
+				entry = &flow_action->entries[++j];
+			}
+		} else if (is_tcf_csum(act)) {
+			entry->id = FLOW_ACTION_CSUM;
+			entry->csum_flags = tcf_csum_update_flags(act);
+		} else if (is_tcf_skbedit_mark(act)) {
+			entry->id = FLOW_ACTION_MARK;
+			entry->mark = tcf_skbedit_mark(act);
+		} else {
+			goto err_out;
+		}
+
+		if (!is_tcf_pedit(act))
+			j++;
+	}
+	return 0;
+err_out:
+	return -EOPNOTSUPP;
+}
+EXPORT_SYMBOL(tc_setup_flow_action);
+
 unsigned int tcf_exts_num_actions(struct tcf_exts *exts)
 {
 	unsigned int num_acts = 0;
diff --git a/net/sched/cls_flower.c b/net/sched/cls_flower.c
index 0062c9133a22..48c54ef52a98 100644
--- a/net/sched/cls_flower.c
+++ b/net/sched/cls_flower.c
@@ -394,6 +394,12 @@ static int fl_hw_replace_filter(struct tcf_proto *tp,
 	cls_flower.exts = &f->exts;
 	cls_flower.classid = f->res.classid;
 
+	err = tc_setup_flow_action(&cls_flower.rule->action, &f->exts);
+	if (err) {
+		kfree(cls_flower.rule);
+		return err;
+	}
+
 	err = tc_setup_cb_call(block, TC_SETUP_CLSFLOWER, &cls_flower, skip_sw);
 	kfree(cls_flower.rule);
 
@@ -1483,6 +1489,14 @@ static int fl_reoffload(struct tcf_proto *tp, bool add, tc_setup_cb_t *cb,
 			cls_flower.rule->match.mask = &mask->key;
 			cls_flower.rule->match.key = &f->mkey;
 			cls_flower.exts = &f->exts;
+
+			err = tc_setup_flow_action(&cls_flower.rule->action,
+						   &f->exts);
+			if (err) {
+				kfree(cls_flower.rule);
+				return err;
+			}
+
 			cls_flower.classid = f->res.classid;
 
 			err = cb(TC_SETUP_CLSFLOWER, &cls_flower, cb_priv);
-- 
2.11.0


WARNING: multiple messages have this Message-ID (diff)
From: Pablo Neira Ayuso <pablo@netfilter.org>
To: netdev@vger.kernel.org
Cc: davem@davemloft.net, thomas.lendacky@amd.com,
	f.fainelli@gmail.com, ariel.elior@cavium.com,
	michael.chan@broadcom.com, santosh@chelsio.com,
	madalin.bucur@nxp.com, yisen.zhuang@huawei.com,
	salil.mehta@huawei.com, jeffrey.t.kirsher@intel.com,
	tariqt@mellanox.com, saeedm@mellanox.com, jiri@mellanox.com,
	idosch@mellanox.com, jakub.kicinski@netronome.com,
	peppe.cavallaro@st.com, grygorii.strashko@ti.com, andrew@lunn.ch,
	vivien.didelot@savoirfairelinux.com, alexandre.torgue@st.com,
	joabreu@synopsys.com, linux-net-drivers@solarflare.com,
	ganeshgr@chelsio.com, ogerlitz@mellanox.com,
	Manish.Chopra@cavium.com, marcelo.leitner@gmail.com,
	mkubecek@suse.cz, venkatkumar.duvvuru@broadcom.com,
	julia.lawall@lip6.fr, john.fastabend@gmail.com,
	netfilter-devel@vger.kernel.org, cphealy@gmail.co
Subject: [PATCH 04/12 net-next,v7] cls_api: add translator to flow_action representation
Date: Sat,  2 Feb 2019 12:50:46 +0100	[thread overview]
Message-ID: <20190202115054.4880-5-pablo@netfilter.org> (raw)
In-Reply-To: <20190202115054.4880-1-pablo@netfilter.org>

This patch implements a new function to translate from native TC action
to the new flow_action representation. Moreover, this patch also updates
cls_flower to use this new function.

Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org>
Acked-by: Jiri Pirko <jiri@mellanox.com>
---
v7: rebase on top of net-next.

 include/net/pkt_cls.h  |  2 +
 net/sched/cls_api.c    | 99 ++++++++++++++++++++++++++++++++++++++++++++++++++
 net/sched/cls_flower.c | 14 +++++++
 3 files changed, 115 insertions(+)

diff --git a/include/net/pkt_cls.h b/include/net/pkt_cls.h
index 74a7582ad47c..c470c100b926 100644
--- a/include/net/pkt_cls.h
+++ b/include/net/pkt_cls.h
@@ -620,6 +620,8 @@ tcf_match_indev(struct sk_buff *skb, int ifindex)
 }
 #endif /* CONFIG_NET_CLS_IND */
 
+int tc_setup_flow_action(struct flow_action *flow_action,
+			 const struct tcf_exts *exts);
 int tc_setup_cb_call(struct tcf_block *block, enum tc_setup_type type,
 		     void *type_data, bool err_stop);
 unsigned int tcf_exts_num_actions(struct tcf_exts *exts);
diff --git a/net/sched/cls_api.c b/net/sched/cls_api.c
index 57713c63ac56..02cf6d2fa0e1 100644
--- a/net/sched/cls_api.c
+++ b/net/sched/cls_api.c
@@ -32,6 +32,13 @@
 #include <net/pkt_sched.h>
 #include <net/pkt_cls.h>
 #include <net/tc_act/tc_pedit.h>
+#include <net/tc_act/tc_mirred.h>
+#include <net/tc_act/tc_vlan.h>
+#include <net/tc_act/tc_tunnel_key.h>
+#include <net/tc_act/tc_csum.h>
+#include <net/tc_act/tc_gact.h>
+#include <net/tc_act/tc_skbedit.h>
+#include <net/tc_act/tc_mirred.h>
 
 extern const struct nla_policy rtm_tca_policy[TCA_MAX + 1];
 
@@ -2516,6 +2523,98 @@ int tc_setup_cb_call(struct tcf_block *block, enum tc_setup_type type,
 }
 EXPORT_SYMBOL(tc_setup_cb_call);
 
+int tc_setup_flow_action(struct flow_action *flow_action,
+			 const struct tcf_exts *exts)
+{
+	const struct tc_action *act;
+	int i, j, k;
+
+	if (!exts)
+		return 0;
+
+	j = 0;
+	tcf_exts_for_each_action(i, act, exts) {
+		struct flow_action_entry *entry;
+
+		entry = &flow_action->entries[j];
+		if (is_tcf_gact_ok(act)) {
+			entry->id = FLOW_ACTION_ACCEPT;
+		} else if (is_tcf_gact_shot(act)) {
+			entry->id = FLOW_ACTION_DROP;
+		} else if (is_tcf_gact_trap(act)) {
+			entry->id = FLOW_ACTION_TRAP;
+		} else if (is_tcf_gact_goto_chain(act)) {
+			entry->id = FLOW_ACTION_GOTO;
+			entry->chain_index = tcf_gact_goto_chain_index(act);
+		} else if (is_tcf_mirred_egress_redirect(act)) {
+			entry->id = FLOW_ACTION_REDIRECT;
+			entry->dev = tcf_mirred_dev(act);
+		} else if (is_tcf_mirred_egress_mirror(act)) {
+			entry->id = FLOW_ACTION_MIRRED;
+			entry->dev = tcf_mirred_dev(act);
+		} else if (is_tcf_vlan(act)) {
+			switch (tcf_vlan_action(act)) {
+			case TCA_VLAN_ACT_PUSH:
+				entry->id = FLOW_ACTION_VLAN_PUSH;
+				entry->vlan.vid = tcf_vlan_push_vid(act);
+				entry->vlan.proto = tcf_vlan_push_proto(act);
+				entry->vlan.prio = tcf_vlan_push_prio(act);
+				break;
+			case TCA_VLAN_ACT_POP:
+				entry->id = FLOW_ACTION_VLAN_POP;
+				break;
+			case TCA_VLAN_ACT_MODIFY:
+				entry->id = FLOW_ACTION_VLAN_MANGLE;
+				entry->vlan.vid = tcf_vlan_push_vid(act);
+				entry->vlan.proto = tcf_vlan_push_proto(act);
+				entry->vlan.prio = tcf_vlan_push_prio(act);
+				break;
+			default:
+				goto err_out;
+			}
+		} else if (is_tcf_tunnel_set(act)) {
+			entry->id = FLOW_ACTION_TUNNEL_ENCAP;
+			entry->tunnel = tcf_tunnel_info(act);
+		} else if (is_tcf_tunnel_release(act)) {
+			entry->id = FLOW_ACTION_TUNNEL_DECAP;
+			entry->tunnel = tcf_tunnel_info(act);
+		} else if (is_tcf_pedit(act)) {
+			for (k = 0; k < tcf_pedit_nkeys(act); k++) {
+				switch (tcf_pedit_cmd(act, k)) {
+				case TCA_PEDIT_KEY_EX_CMD_SET:
+					entry->id = FLOW_ACTION_MANGLE;
+					break;
+				case TCA_PEDIT_KEY_EX_CMD_ADD:
+					entry->id = FLOW_ACTION_ADD;
+					break;
+				default:
+					goto err_out;
+				}
+				entry->mangle.htype = tcf_pedit_htype(act, k);
+				entry->mangle.mask = tcf_pedit_mask(act, k);
+				entry->mangle.val = tcf_pedit_val(act, k);
+				entry->mangle.offset = tcf_pedit_offset(act, k);
+				entry = &flow_action->entries[++j];
+			}
+		} else if (is_tcf_csum(act)) {
+			entry->id = FLOW_ACTION_CSUM;
+			entry->csum_flags = tcf_csum_update_flags(act);
+		} else if (is_tcf_skbedit_mark(act)) {
+			entry->id = FLOW_ACTION_MARK;
+			entry->mark = tcf_skbedit_mark(act);
+		} else {
+			goto err_out;
+		}
+
+		if (!is_tcf_pedit(act))
+			j++;
+	}
+	return 0;
+err_out:
+	return -EOPNOTSUPP;
+}
+EXPORT_SYMBOL(tc_setup_flow_action);
+
 unsigned int tcf_exts_num_actions(struct tcf_exts *exts)
 {
 	unsigned int num_acts = 0;
diff --git a/net/sched/cls_flower.c b/net/sched/cls_flower.c
index 0062c9133a22..48c54ef52a98 100644
--- a/net/sched/cls_flower.c
+++ b/net/sched/cls_flower.c
@@ -394,6 +394,12 @@ static int fl_hw_replace_filter(struct tcf_proto *tp,
 	cls_flower.exts = &f->exts;
 	cls_flower.classid = f->res.classid;
 
+	err = tc_setup_flow_action(&cls_flower.rule->action, &f->exts);
+	if (err) {
+		kfree(cls_flower.rule);
+		return err;
+	}
+
 	err = tc_setup_cb_call(block, TC_SETUP_CLSFLOWER, &cls_flower, skip_sw);
 	kfree(cls_flower.rule);
 
@@ -1483,6 +1489,14 @@ static int fl_reoffload(struct tcf_proto *tp, bool add, tc_setup_cb_t *cb,
 			cls_flower.rule->match.mask = &mask->key;
 			cls_flower.rule->match.key = &f->mkey;
 			cls_flower.exts = &f->exts;
+
+			err = tc_setup_flow_action(&cls_flower.rule->action,
+						   &f->exts);
+			if (err) {
+				kfree(cls_flower.rule);
+				return err;
+			}
+
 			cls_flower.classid = f->res.classid;
 
 			err = cb(TC_SETUP_CLSFLOWER, &cls_flower, cb_priv);
-- 
2.11.0

  parent reply	other threads:[~2019-02-02 11:51 UTC|newest]

Thread overview: 39+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2019-02-02 11:50 [PATCH 00/12 net-next,v7] add flow_rule infrastructure Pablo Neira Ayuso
2019-02-02 11:50 ` Pablo Neira Ayuso
2019-02-02 11:50 ` [PATCH 01/12 net-next,v7] flow_offload: add flow_rule and flow_match structures and use them Pablo Neira Ayuso
2019-02-02 11:50   ` Pablo Neira Ayuso
2019-02-02 15:19   ` Jiri Pirko
2019-02-02 15:19     ` Jiri Pirko
2019-02-02 11:50 ` [PATCH 02/12 net-next,v7] net/mlx5e: support for two independent packet edit actions Pablo Neira Ayuso
2019-02-02 11:50   ` Pablo Neira Ayuso
2019-02-02 15:09   ` Tonghao Zhang
2019-02-02 15:09     ` Tonghao Zhang
2019-02-02 21:40     ` Or Gerlitz
2019-02-02 21:40       ` Or Gerlitz
2019-02-02 21:43     ` Or Gerlitz
2019-02-02 21:43       ` Or Gerlitz
2019-02-02 11:50 ` [PATCH 03/12 net-next,v7] flow_offload: add flow action infrastructure Pablo Neira Ayuso
2019-02-02 11:50   ` Pablo Neira Ayuso
2019-02-02 11:50 ` Pablo Neira Ayuso [this message]
2019-02-02 11:50   ` [PATCH 04/12 net-next,v7] cls_api: add translator to flow_action representation Pablo Neira Ayuso
2019-02-02 11:50 ` [PATCH 05/12 net-next,v7] flow_offload: add statistics retrieval infrastructure and use it Pablo Neira Ayuso
2019-02-02 11:50   ` Pablo Neira Ayuso
2019-02-02 11:50 ` [PATCH 06/12 net-next,v7] drivers: net: use flow action infrastructure Pablo Neira Ayuso
2019-02-02 11:50   ` Pablo Neira Ayuso
2019-02-02 15:44   ` Jiri Pirko
2019-02-02 15:44     ` Jiri Pirko
2019-02-02 11:50 ` [PATCH 07/12 net-next,v7] cls_flower: don't expose TC actions to drivers anymore Pablo Neira Ayuso
2019-02-02 11:50   ` Pablo Neira Ayuso
2019-02-02 11:50 ` [PATCH 08/12 net-next,v7] flow_offload: add wake-up-on-lan and queue to flow_action Pablo Neira Ayuso
2019-02-02 11:50   ` Pablo Neira Ayuso
2019-02-02 11:50 ` [PATCH 09/12 net-next,v7] ethtool: add ethtool_rx_flow_spec to flow_rule structure translator Pablo Neira Ayuso
2019-02-02 11:50   ` Pablo Neira Ayuso
2019-02-02 11:50 ` [PATCH 10/12 net-next,v7] dsa: bcm_sf2: use flow_rule infrastructure Pablo Neira Ayuso
2019-02-02 11:50   ` Pablo Neira Ayuso
2019-02-02 11:50 ` [PATCH 11/12 net-next,v7] qede: place ethtool_rx_flow_spec after code after TC flower codebase Pablo Neira Ayuso
2019-02-02 11:50   ` Pablo Neira Ayuso
2019-02-02 11:50 ` [PATCH 12/12 net-next,v7] qede: use ethtool_rx_flow_rule() to remove duplicated parser code Pablo Neira Ayuso
2019-02-02 11:50   ` Pablo Neira Ayuso
2019-02-06 18:39 ` [PATCH 00/12 net-next,v7] add flow_rule infrastructure David Miller
2019-02-06 18:39   ` David Miller
2019-02-06 20:44   ` Florian Fainelli

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20190202115054.4880-5-pablo@netfilter.org \
    --to=pablo@netfilter.org \
    --cc=Manish.Chopra@cavium.com \
    --cc=alexandre.torgue@st.com \
    --cc=andrew@lunn.ch \
    --cc=ariel.elior@cavium.com \
    --cc=cphealy@gmail.com \
    --cc=davem@davemloft.net \
    --cc=f.fainelli@gmail.com \
    --cc=ganeshgr@chelsio.com \
    --cc=grygorii.strashko@ti.com \
    --cc=idosch@mellanox.com \
    --cc=jakub.kicinski@netronome.com \
    --cc=jeffrey.t.kirsher@intel.com \
    --cc=jiri@mellanox.com \
    --cc=joabreu@synopsys.com \
    --cc=john.fastabend@gmail.com \
    --cc=julia.lawall@lip6.fr \
    --cc=linux-net-drivers@solarflare.com \
    --cc=madalin.bucur@nxp.com \
    --cc=marcelo.leitner@gmail.com \
    --cc=michael.chan@broadcom.com \
    --cc=mkubecek@suse.cz \
    --cc=netdev@vger.kernel.org \
    --cc=netfilter-devel@vger.kernel.org \
    --cc=ogerlitz@mellanox.com \
    --cc=peppe.cavallaro@st.com \
    --cc=saeedm@mellanox.com \
    --cc=salil.mehta@huawei.com \
    --cc=santosh@chelsio.com \
    --cc=tariqt@mellanox.com \
    --cc=thomas.lendacky@amd.com \
    --cc=venkatkumar.duvvuru@broadcom.com \
    --cc=vivien.didelot@savoirfairelinux.com \
    --cc=yisen.zhuang@huawei.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.