DPDK patches and discussions
 help / color / mirror / Atom feed
From: Wenbo Cao <caowenbo@mucse.com>
To: thomas@monjalon.net, Wenbo Cao <caowenbo@mucse.com>
Cc: stephen@networkplumber.org, dev@dpdk.org, ferruh.yigit@amd.com,
	andrew.rybchenko@oktetlabs.ru, yaojun@mucse.com
Subject: [PATCH v7 07/28] net/rnp: add support mac promisc mode
Date: Sat,  8 Feb 2025 10:43:44 +0800	[thread overview]
Message-ID: <1738982645-34550-8-git-send-email-caowenbo@mucse.com> (raw)
In-Reply-To: <1738982645-34550-1-git-send-email-caowenbo@mucse.com>

add support two method of mac unicast promisc
mulcast promisc broadcast promisc mode

Signed-off-by: Wenbo Cao <caowenbo@mucse.com>
---
 doc/guides/nics/features/rnp.ini    |   2 +
 doc/guides/nics/rnp.rst             |   5 ++
 drivers/net/rnp/base/rnp_common.c   |   5 ++
 drivers/net/rnp/base/rnp_eth_regs.h |  15 +++++
 drivers/net/rnp/base/rnp_hw.h       |  12 +++-
 drivers/net/rnp/base/rnp_mac.c      | 114 +++++++++++++++++++++++++++++++++++-
 drivers/net/rnp/base/rnp_mac.h      |   2 +
 drivers/net/rnp/base/rnp_mac_regs.h |  39 ++++++++++++
 drivers/net/rnp/base/rnp_osdep.h    |   5 ++
 drivers/net/rnp/rnp_ethdev.c        |  43 ++++++++++++++
 10 files changed, 240 insertions(+), 2 deletions(-)
 create mode 100644 drivers/net/rnp/base/rnp_mac_regs.h

diff --git a/doc/guides/nics/features/rnp.ini b/doc/guides/nics/features/rnp.ini
index 6766130..65f1ed3 100644
--- a/doc/guides/nics/features/rnp.ini
+++ b/doc/guides/nics/features/rnp.ini
@@ -5,5 +5,7 @@
 ;
 [Features]
 Speed capabilities   = Y
+Promiscuous mode     = Y
+Allmulticast mode    = Y
 Linux                = Y
 x86-64               = Y
diff --git a/doc/guides/nics/rnp.rst b/doc/guides/nics/rnp.rst
index 618baa8..62585ac 100644
--- a/doc/guides/nics/rnp.rst
+++ b/doc/guides/nics/rnp.rst
@@ -7,6 +7,11 @@ RNP Poll Mode driver
 The RNP ETHDEV PMD (**librte_net_rnp**) provides poll mode ethdev
 driver support for the inbuilt network device found in the **Mucse RNP**
 
+Features
+--------
+
+- Promiscuous mode
+
 Prerequisites
 -------------
 More information can be found at `Mucse, Official Website
diff --git a/drivers/net/rnp/base/rnp_common.c b/drivers/net/rnp/base/rnp_common.c
index 47a979b..3fa2a49 100644
--- a/drivers/net/rnp/base/rnp_common.c
+++ b/drivers/net/rnp/base/rnp_common.c
@@ -4,6 +4,7 @@
 
 #include "rnp_osdep.h"
 #include "rnp_hw.h"
+#include "rnp_mac_regs.h"
 #include "rnp_eth_regs.h"
 #include "rnp_dma_regs.h"
 #include "rnp_common.h"
@@ -28,6 +29,7 @@ int rnp_init_hw(struct rnp_hw *hw)
 	struct rnp_eth_port *port = RNP_DEV_TO_PORT(hw->back->eth_dev);
 	u32 version = 0;
 	int ret = -1;
+	u32 idx = 0;
 	u32 state;
 
 	PMD_INIT_FUNC_TRACE();
@@ -60,6 +62,9 @@ int rnp_init_hw(struct rnp_hw *hw)
 	if (hw->nic_mode == RNP_DUAL_10G && hw->max_port_num == 2)
 		RNP_E_REG_WR(hw, RNP_TC_PORT_OFFSET(RNP_TARGET_TC_PORT),
 				RNP_PORT_OFF_QUEUE_NUM);
+	/* setup mac resiger ctrl base */
+	for (idx = 0; idx < hw->max_port_num; idx++)
+		hw->mac_base[idx] = (u8 *)hw->e_ctrl + RNP_MAC_BASE_OFFSET(idx);
 
 	return 0;
 }
diff --git a/drivers/net/rnp/base/rnp_eth_regs.h b/drivers/net/rnp/base/rnp_eth_regs.h
index 6957866..c4519ba 100644
--- a/drivers/net/rnp/base/rnp_eth_regs.h
+++ b/drivers/net/rnp/base/rnp_eth_regs.h
@@ -10,6 +10,21 @@
 #define RNP_E_FILTER_EN		_ETH_(0x801c)
 #define RNP_E_REDIR_EN		_ETH_(0x8030)
 
+/* Mac Host Filter  */
+#define RNP_MAC_FCTRL		_ETH_(0x9110)
+#define RNP_MAC_FCTRL_MPE	RTE_BIT32(8)  /* Multicast Promiscuous En */
+#define RNP_MAC_FCTRL_UPE	RTE_BIT32(9)  /* Unicast Promiscuous En */
+#define RNP_MAC_FCTRL_BAM	RTE_BIT32(10) /* Broadcast Accept Mode */
+#define RNP_MAC_FCTRL_BYPASS	(\
+		RNP_MAC_FCTRL_MPE | \
+		RNP_MAC_FCTRL_UPE | \
+		RNP_MAC_FCTRL_BAM)
+/* Mucast unicast mac hash filter ctrl */
+#define RNP_MAC_MCSTCTRL		_ETH_(0x9114)
+#define RNP_MAC_HASH_MASK		RTE_GENMASK32(11, 0)
+#define RNP_MAC_MULTICASE_TBL_EN	RTE_BIT32(2)
+#define RNP_MAC_UNICASE_TBL_EN		RTE_BIT32(3)
+
 #define RNP_TC_PORT_OFFSET(lane)	_ETH_(0xe840 + 0x04 * (lane))
 
 #endif /* _RNP_ETH_REGS_H */
diff --git a/drivers/net/rnp/base/rnp_hw.h b/drivers/net/rnp/base/rnp_hw.h
index e150543..1b31362 100644
--- a/drivers/net/rnp/base/rnp_hw.h
+++ b/drivers/net/rnp/base/rnp_hw.h
@@ -59,9 +59,18 @@ struct rnp_mbx_info {
 
 struct rnp_eth_port;
 /* mac operations */
+enum rnp_mpf_modes {
+	RNP_MPF_MODE_NONE = 0,
+	RNP_MPF_MODE_MULTI,    /* Multitle Filter */
+	RNP_MPF_MODE_ALLMULTI, /* Multitle Promisc */
+	RNP_MPF_MODE_PROMISC,  /* Unicast Promisc */
+};
+
 struct rnp_mac_ops {
-	/* update mac packet filter mode */
+	/* get default mac address */
 	int (*get_macaddr)(struct rnp_eth_port *port, u8 *mac);
+	/* update mac packet filter mode */
+	int (*update_mpfm)(struct rnp_eth_port *port, u32 mode, bool en);
 };
 
 struct rnp_eth_adapter;
@@ -91,6 +100,7 @@ struct rnp_hw {
 	struct rnp_eth_adapter *back;	/* backup to the adapter handle */
 	void __iomem *e_ctrl;           /* ethernet control bar */
 	void __iomem *c_ctrl;           /* crypto control bar */
+	void __iomem *mac_base[RNP_MAX_PORT_OF_PF]; /* mac ctrl register base */
 	u32 c_blen;                     /* crypto bar size */
 
 	/* pci device info */
diff --git a/drivers/net/rnp/base/rnp_mac.c b/drivers/net/rnp/base/rnp_mac.c
index b063f4c..2c9499f 100644
--- a/drivers/net/rnp/base/rnp_mac.c
+++ b/drivers/net/rnp/base/rnp_mac.c
@@ -6,10 +6,110 @@
 
 #include "rnp_mbx_fw.h"
 #include "rnp_mac.h"
+#include "rnp_eth_regs.h"
+#include "rnp_mac_regs.h"
 #include "../rnp.h"
 
+static int
+rnp_update_mpfm_indep(struct rnp_eth_port *port, u32 mode, bool en)
+{
+	u32 nr_lane = port->attr.nr_lane;
+	struct rnp_hw *hw = port->hw;
+	u32 disable = 0, enable = 0;
+	u32 reg;
+
+	reg = RNP_MAC_REG_RD(hw, nr_lane, RNP_MAC_PKT_FLT_CTRL);
+	/* make sure not all receive modes are available */
+	reg &= ~RNP_MAC_RA;
+	switch (mode) {
+	case RNP_MPF_MODE_NONE:
+		break;
+	case RNP_MPF_MODE_MULTI:
+		disable = RNP_MAC_PM | RNP_MAC_PROMISC_EN;
+		enable = RNP_MAC_HPF;
+		break;
+	case RNP_MPF_MODE_ALLMULTI:
+		enable = RNP_MAC_PM;
+		disable = 0;
+		break;
+	case RNP_MPF_MODE_PROMISC:
+		enable = RNP_MAC_PROMISC_EN;
+		disable = 0;
+		break;
+	default:
+		RNP_PMD_LOG(ERR, "update_mpfm argument is invalid");
+		return -EINVAL;
+	}
+	if (en) {
+		reg &= ~disable;
+		reg |= enable;
+	} else {
+		reg &= ~enable;
+		reg |= disable;
+	}
+	/* disable common filter when indep mode */
+	reg |= RNP_MAC_HPF;
+	RNP_MAC_REG_WR(hw, nr_lane, RNP_MAC_PKT_FLT_CTRL, reg);
+	RNP_MAC_REG_WR(hw, nr_lane, RNP_MAC_FCTRL, RNP_MAC_FCTRL_BYPASS);
+
+	return 0;
+}
+
+static int
+rnp_update_mpfm_pf(struct rnp_eth_port *port, u32 mode, bool en)
+{
+	u32 nr_lane = port->attr.nr_lane;
+	struct rnp_hw *hw = port->hw;
+	u32 mac_filter_ctrl;
+	u32 filter_ctrl;
+	u32 bypass_ctrl;
+	u32 bypass = 0;
+
+	bypass_ctrl = RNP_E_REG_RD(hw, RNP_MAC_FCTRL);
+	bypass_ctrl |= RNP_MAC_FCTRL_BAM;
+
+	filter_ctrl = RNP_MAC_MULTICASE_TBL_EN | RNP_MAC_UNICASE_TBL_EN;
+	RNP_E_REG_WR(hw, RNP_MAC_MCSTCTRL, filter_ctrl);
+
+	switch (mode) {
+	case RNP_MPF_MODE_NONE:
+		bypass = 0;
+		break;
+	case RNP_MPF_MODE_MULTI:
+		bypass = RNP_MAC_FCTRL_MPE;
+		break;
+	case RNP_MPF_MODE_ALLMULTI:
+		bypass = RNP_MAC_FCTRL_MPE;
+		break;
+	case RNP_MPF_MODE_PROMISC:
+		bypass = RNP_MAC_FCTRL_UPE | RNP_MAC_FCTRL_MPE;
+		break;
+	default:
+		RNP_PMD_LOG(ERR, "update_mpfm argument is invalid");
+		return -EINVAL;
+	}
+	if (en)
+		bypass_ctrl |= bypass;
+	else
+		bypass_ctrl &= ~bypass;
+
+	RNP_E_REG_WR(hw, RNP_MAC_FCTRL, bypass_ctrl);
+	mac_filter_ctrl = RNP_MAC_REG_RD(hw, nr_lane, RNP_MAC_PKT_FLT_CTRL);
+	mac_filter_ctrl |= RNP_MAC_PM | RNP_MAC_PROMISC_EN;
+	mac_filter_ctrl &= ~RNP_MAC_RA;
+	RNP_MAC_REG_WR(hw, nr_lane, RNP_MAC_PKT_FLT_CTRL, mac_filter_ctrl);
+
+	return 0;
+}
+
 const struct rnp_mac_ops rnp_mac_ops_pf = {
 	.get_macaddr = rnp_mbx_fw_get_macaddr,
+	.update_mpfm = rnp_update_mpfm_pf,
+};
+
+const struct rnp_mac_ops rnp_mac_ops_indep = {
+	.get_macaddr = rnp_mbx_fw_get_macaddr,
+	.update_mpfm = rnp_update_mpfm_indep,
 };
 
 int rnp_get_mac_addr(struct rnp_eth_port *port, u8 *mac)
@@ -20,9 +120,21 @@ int rnp_get_mac_addr(struct rnp_eth_port *port, u8 *mac)
 	return rnp_call_hwif_impl(port, mac_ops->get_macaddr, mac);
 }
 
+int rnp_update_mpfm(struct rnp_eth_port *port,
+		    u32 mode, bool en)
+{
+	const struct rnp_mac_ops *mac_ops =
+		RNP_DEV_PP_TO_MAC_OPS(port->eth_dev);
+
+	return rnp_call_hwif_impl(port, mac_ops->update_mpfm, mode, en);
+}
+
 void rnp_mac_ops_init(struct rnp_hw *hw)
 {
 	struct rnp_proc_priv *proc_priv = RNP_DEV_TO_PROC_PRIV(hw->back->eth_dev);
 
-	proc_priv->mac_ops = &rnp_mac_ops_pf;
+	if (rnp_pf_is_multiple_ports(hw->device_id))
+		proc_priv->mac_ops = &rnp_mac_ops_indep;
+	else
+		proc_priv->mac_ops = &rnp_mac_ops_pf;
 }
diff --git a/drivers/net/rnp/base/rnp_mac.h b/drivers/net/rnp/base/rnp_mac.h
index 8a12aa4..57cbd9e 100644
--- a/drivers/net/rnp/base/rnp_mac.h
+++ b/drivers/net/rnp/base/rnp_mac.h
@@ -10,5 +10,7 @@
 
 void rnp_mac_ops_init(struct rnp_hw *hw);
 int rnp_get_mac_addr(struct rnp_eth_port *port, u8 *mac);
+int rnp_update_mpfm(struct rnp_eth_port *port,
+		    u32 mode, bool en);
 
 #endif /* _RNP_MAC_H_ */
diff --git a/drivers/net/rnp/base/rnp_mac_regs.h b/drivers/net/rnp/base/rnp_mac_regs.h
new file mode 100644
index 0000000..1dc0668
--- /dev/null
+++ b/drivers/net/rnp/base/rnp_mac_regs.h
@@ -0,0 +1,39 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(C) 2023 Mucse IC Design Ltd.
+ */
+
+#ifndef _RNP_MAC_REGS_H_
+#define _RNP_MAC_REGS_H_
+
+#define RNP_MAC_BASE_OFFSET(n)  (_MAC_(0) + ((0x10000) * (n)))
+
+#define RNP_MAC_PKT_FLT_CTRL	(0x8)
+/* Receive All */
+#define RNP_MAC_RA		RTE_BIT32(31)
+/* Pass Control Packets */
+#define RNP_MAC_PCF		RTE_GENMASK32(7, 6)
+#define RNP_MAC_PCF_S		(6)
+/* Mac Filter ALL Ctrl Frame */
+#define RNP_MAC_PCF_FAC		(0)
+/* Mac Forward ALL Ctrl Frame Except Pause */
+#define RNP_MAC_PCF_NO_PAUSE	(1)
+/* Mac Forward All Ctrl Pkt */
+#define RNP_MAC_PCF_PA		(2)
+/* Mac Forward Ctrl Frame Match Unicast */
+#define RNP_MAC_PCF_PUN		(3)
+/* Promiscuous Mode */
+#define RNP_MAC_PROMISC_EN	RTE_BIT32(0)
+/* Hash Unicast */
+#define RNP_MAC_HUC		RTE_BIT32(1)
+/* Hash Multicast */
+#define RNP_MAC_HMC		RTE_BIT32(2)
+/*  Pass All Multicast */
+#define RNP_MAC_PM		RTE_BIT32(4)
+/* Disable Broadcast Packets */
+#define RNP_MAC_DBF		RTE_BIT32(5)
+/* Hash or Perfect Filter */
+#define RNP_MAC_HPF		RTE_BIT32(10)
+#define RNP_MAC_VTFE		RTE_BIT32(16)
+
+
+#endif /* _RNP_MAC_REGS_H_ */
diff --git a/drivers/net/rnp/base/rnp_osdep.h b/drivers/net/rnp/base/rnp_osdep.h
index 3f31f9b..03f6c51 100644
--- a/drivers/net/rnp/base/rnp_osdep.h
+++ b/drivers/net/rnp/base/rnp_osdep.h
@@ -44,6 +44,7 @@
 
 #define _ETH_(off)	((off) + (0x10000))
 #define _NIC_(off)	((off) + (0x30000))
+#define _MAC_(off)	((off) + (0x60000))
 #define _MSI_(off)	((off) + (0xA0000))
 
 #ifndef _PACKED_ALIGN4
@@ -139,5 +140,9 @@ struct rnp_dma_mem {
 #define RNP_REG_WR(base, offset, val)	rnp_reg_write32(base, offset, val)
 #define RNP_E_REG_WR(hw, off, value)	rnp_reg_write32((hw)->e_ctrl, (off), (value))
 #define RNP_E_REG_RD(hw, off)		rnp_reg_read32((hw)->e_ctrl, (off))
+#define RNP_MAC_REG_WR(hw, lane, off, value) \
+	rnp_reg_write32((hw)->mac_base[lane], (off), (value))
+#define RNP_MAC_REG_RD(hw, lane, off) \
+	rnp_reg_read32((hw)->mac_base[lane], (off))
 
 #endif /* _RNP_OSDEP_H_ */
diff --git a/drivers/net/rnp/rnp_ethdev.c b/drivers/net/rnp/rnp_ethdev.c
index a7404ee..13d949a 100644
--- a/drivers/net/rnp/rnp_ethdev.c
+++ b/drivers/net/rnp/rnp_ethdev.c
@@ -189,11 +189,54 @@ static int rnp_dev_infos_get(struct rte_eth_dev *eth_dev,
 	return 0;
 }
 
+static int rnp_promiscuous_enable(struct rte_eth_dev *eth_dev)
+{
+	struct rnp_eth_port *port = RNP_DEV_TO_PORT(eth_dev);
+
+	PMD_INIT_FUNC_TRACE();
+
+	return rnp_update_mpfm(port, RNP_MPF_MODE_PROMISC, 1);
+}
+
+static int rnp_promiscuous_disable(struct rte_eth_dev *eth_dev)
+{
+	struct rnp_eth_port *port = RNP_DEV_TO_PORT(eth_dev);
+
+	PMD_INIT_FUNC_TRACE();
+
+	return rnp_update_mpfm(port, RNP_MPF_MODE_PROMISC, 0);
+}
+
+static int rnp_allmulticast_enable(struct rte_eth_dev *eth_dev)
+{
+	struct rnp_eth_port *port = RNP_DEV_TO_PORT(eth_dev);
+
+	PMD_INIT_FUNC_TRACE();
+
+	return rnp_update_mpfm(port, RNP_MPF_MODE_ALLMULTI, 1);
+}
+
+static int rnp_allmulticast_disable(struct rte_eth_dev *eth_dev)
+{
+	struct rnp_eth_port *port = RNP_DEV_TO_PORT(eth_dev);
+
+	PMD_INIT_FUNC_TRACE();
+	if (eth_dev->data->promiscuous == 1)
+		return 0;
+	return rnp_update_mpfm(port, RNP_MPF_MODE_ALLMULTI, 0);
+}
+
 /* Features supported by this driver */
 static const struct eth_dev_ops rnp_eth_dev_ops = {
 	.dev_close                    = rnp_dev_close,
 	.dev_stop                     = rnp_dev_stop,
 	.dev_infos_get                = rnp_dev_infos_get,
+
+	/* PROMISC */
+	.promiscuous_enable           = rnp_promiscuous_enable,
+	.promiscuous_disable          = rnp_promiscuous_disable,
+	.allmulticast_enable          = rnp_allmulticast_enable,
+	.allmulticast_disable         = rnp_allmulticast_disable,
 };
 
 static void
-- 
1.8.3.1


  parent reply	other threads:[~2025-02-08  2:45 UTC|newest]

Thread overview: 29+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2025-02-08  2:43 [PATCH v7 00/28] [v6]drivers/net Add Support mucse N10 Pmd Driver Wenbo Cao
2025-02-08  2:43 ` [PATCH v7 01/28] net/rnp: add skeleton Wenbo Cao
2025-02-08  2:43 ` [PATCH v7 02/28] net/rnp: add ethdev probe and remove Wenbo Cao
2025-02-08  2:43 ` [PATCH v7 03/28] net/rnp: add log Wenbo Cao
2025-02-08  2:43 ` [PATCH v7 04/28] net/rnp: support mailbox basic operate Wenbo Cao
2025-02-08  2:43 ` [PATCH v7 05/28] net/rnp: add device init and uninit Wenbo Cao
2025-02-08  2:43 ` [PATCH v7 06/28] net/rnp: add get device information operation Wenbo Cao
2025-02-08  2:43 ` Wenbo Cao [this message]
2025-02-08  2:43 ` [PATCH v7 08/28] net/rnp: add queue setup and release operations Wenbo Cao
2025-02-08  2:43 ` [PATCH v7 09/28] net/rnp: add queue stop and start operations Wenbo Cao
2025-02-08  2:43 ` [PATCH v7 10/28] net/rnp: add support device start stop operations Wenbo Cao
2025-02-08  2:43 ` [PATCH v7 11/28] net/rnp: add RSS support operations Wenbo Cao
2025-02-08  2:43 ` [PATCH v7 12/28] net/rnp: add support link update operations Wenbo Cao
2025-02-08  2:43 ` [PATCH v7 13/28] net/rnp: add support link setup operations Wenbo Cao
2025-02-08  2:43 ` [PATCH v7 14/28] net/rnp: add Rx burst simple support Wenbo Cao
2025-02-08  2:43 ` [PATCH v7 15/28] net/rnp: add Tx " Wenbo Cao
2025-02-08  2:43 ` [PATCH v7 16/28] net/rnp: add MTU set operation Wenbo Cao
2025-02-08  2:43 ` [PATCH v7 17/28] net/rnp: add Rx scatter segment version Wenbo Cao
2025-02-08  2:43 ` [PATCH v7 18/28] net/rnp: add Tx multiple " Wenbo Cao
2025-02-08  2:43 ` [PATCH v7 19/28] net/rnp: add support basic stats operation Wenbo Cao
2025-02-08  2:43 ` [PATCH v7 20/28] net/rnp: add support xstats operation Wenbo Cao
2025-02-08  2:43 ` [PATCH v7 21/28] net/rnp: add unicast MAC filter operation Wenbo Cao
2025-02-08  2:43 ` [PATCH v7 22/28] net/rnp: add supported packet types Wenbo Cao
2025-02-08  2:44 ` [PATCH v7 23/28] net/rnp: add support Rx checksum offload Wenbo Cao
2025-02-08  2:44 ` [PATCH v7 24/28] net/rnp: add support Tx TSO offload Wenbo Cao
2025-02-08  2:44 ` [PATCH v7 25/28] net/rnp: support VLAN offloads Wenbo Cao
2025-02-08  2:44 ` [PATCH v7 26/28] net/rnp: add support VLAN filters operations Wenbo Cao
2025-02-08  2:44 ` [PATCH v7 27/28] net/rnp: add queue info operation Wenbo Cao
2025-02-08  2:44 ` [PATCH v7 28/28] net/rnp: support Rx/Tx burst mode info Wenbo Cao

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=1738982645-34550-8-git-send-email-caowenbo@mucse.com \
    --to=caowenbo@mucse.com \
    --cc=andrew.rybchenko@oktetlabs.ru \
    --cc=dev@dpdk.org \
    --cc=ferruh.yigit@amd.com \
    --cc=stephen@networkplumber.org \
    --cc=thomas@monjalon.net \
    --cc=yaojun@mucse.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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).