From: Song Jiale <songx.jiale@intel.com>
To: dts@dpdk.org
Cc: Song Jiale <songx.jiale@intel.com>
Subject: [dts] [PATCH V1 4/4] tests/ice_iavf_dual_vlan: split test suite
Date: Wed, 7 Sep 2022 17:06:56 +0000 [thread overview]
Message-ID: <20220907170656.2551-4-songx.jiale@intel.com> (raw)
In-Reply-To: <20220907170656.2551-1-songx.jiale@intel.com>
split the test suite of ice_qinq into ice_dcf_dual_vlan and ice_iavf_dual_vlan.
Signed-off-by: Song Jiale <songx.jiale@intel.com>
---
tests/TestSuite_ice_iavf_dual_vlan.py | 496 ++++++++++++++++++++++++++
1 file changed, 496 insertions(+)
create mode 100644 tests/TestSuite_ice_iavf_dual_vlan.py
diff --git a/tests/TestSuite_ice_iavf_dual_vlan.py b/tests/TestSuite_ice_iavf_dual_vlan.py
new file mode 100644
index 00000000..ae3b8bef
--- /dev/null
+++ b/tests/TestSuite_ice_iavf_dual_vlan.py
@@ -0,0 +1,496 @@
+# SPDX-License-Identifier: BSD-3-Clause
+# Copyright(c) 2021 Intel Corporation
+#
+
+import re
+import time
+
+from framework.packet import Packet
+from framework.pmd_output import PmdOutput
+from framework.test_case import TestCase, check_supported_nic
+
+from .rte_flow_common import FdirProcessing
+
+
+class TestICEQinq(TestCase):
+ @check_supported_nic(
+ ["ICE_25G-E810C_SFP", "ICE_100G-E810C_QSFP", "ICE_25G-E810_XXV_SFP"]
+ )
+ def set_up_all(self):
+ """
+ Run at the start of each test suite.
+ prerequisites.
+ """
+ # Based on h/w type, choose how many ports to use
+ self.dut_ports = self.dut.get_ports(self.nic)
+ self.verify(len(self.dut_ports) >= 1, "Insufficient ports for testing")
+ # Verify that enough threads are available
+ cores = self.dut.get_core_list("1S/4C/1T")
+ self.verify(cores is not None, "Insufficient cores for speed testing")
+ self.ports_socket = self.dut.get_numa_id(self.dut_ports[0])
+ self.tester_port0 = self.tester.get_local_port(self.dut_ports[0])
+ self.tester_iface0 = self.tester.get_interface(self.tester_port0)
+
+ self.used_dut_port = self.dut_ports[0]
+ self.pf_interface = self.dut.ports_info[self.dut_ports[0]]["intf"]
+ port = self.dut.ports_info[0]["port"]
+ port.bind_driver()
+ # get priv-flags default stats
+ self.flag = "vf-vlan-pruning"
+ self.default_stats = self.dut.get_priv_flags_state(self.pf_interface, self.flag)
+
+ self.vf_flag = False
+ self.vf0_mac = "00:11:22:33:44:11"
+
+ self.path = self.dut.apps_name["test-pmd"]
+ self.pkt = Packet()
+ self.pmd_output = PmdOutput(self.dut)
+ self.rxq = 16
+ self.fdirprocess = FdirProcessing(
+ self, self.pmd_output, [self.tester_iface0], rxq=self.rxq
+ )
+
+ def set_up(self):
+ """
+ Run before each test case.
+ """
+ self.pci_list = []
+ if self.default_stats:
+ self.dut.send_expect(
+ "ethtool --set-priv-flags %s %s %s"
+ % (self.pf_interface, self.flag, self.default_stats),
+ "# ",
+ )
+
+ def setup_pf_vfs_env(self):
+ if self.vf_flag is False:
+ self.dut.generate_sriov_vfs_by_port(
+ self.used_dut_port, 1, driver=self.kdriver
+ )
+ self.sriov_vfs_port = self.dut.ports_info[self.used_dut_port]["vfs_port"]
+ self.vf_flag = True
+
+ self.dut.send_expect(
+ "ip link set %s vf 0 mac %s" % (self.pf_interface, self.vf0_mac),
+ "# ",
+ )
+
+ try:
+ for port in self.sriov_vfs_port:
+ port.bind_driver(self.drivername)
+ self.pci_list.append(port.pci)
+
+ self.vf0_prop = {"opt_host": self.sriov_vfs_port[0].pci}
+ self.dut.send_expect("ifconfig %s up" % self.pf_interface, "# ")
+ self.dut.send_expect(
+ "ip link set dev %s vf 0 spoofchk off" % self.pf_interface, "# "
+ )
+
+ except Exception as e:
+ self.destroy_iavf()
+ raise Exception(e)
+
+ def destroy_iavf(self):
+ if self.vf_flag is True:
+ self.dut.destroy_sriov_vfs_by_port(self.used_dut_port)
+ self.vf_flag = False
+
+ def launch_testpmd(self):
+ param = "--rxq={0} --txq={0}".format(self.rxq)
+ self.pmd_output.start_testpmd(
+ cores="1S/4C/1T",
+ param=param,
+ ports=self.pci_list,
+ socket=self.ports_socket,
+ )
+ self.confing_testpmd()
+
+ def confing_testpmd(self):
+ self.pmd_output.execute_cmd("set fwd mac")
+ self.pmd_output.execute_cmd("set verbose 1")
+ self.pmd_output.execute_cmd("start")
+
+ def check_packets(self, out, port_id, pkt_num=1, check_stats=True):
+ p = "port (\d+)/queue.*"
+ result_list = re.findall(p, out)
+ if check_stats:
+ self.verify(
+ len(result_list) == pkt_num, "received packets mismatch".format(port_id)
+ )
+ for res in result_list:
+ if check_stats:
+ self.verify(
+ int(res) == port_id,
+ "port {} did not received the packets".format(port_id),
+ )
+ else:
+ self.verify(
+ int(res) != port_id,
+ "port {} should not received a packets".format(port_id),
+ )
+
+ def send_packet_check_vlan_inter(
+ self, pkts, out_vlan, port_id=3, vlan_header=None, iner_vlan=None
+ ):
+ for pkt in pkts:
+ pkt_index = pkts.index(pkt)
+ out, tcpdump_out = self.tcpdump_send_packet_get_output(pkt)
+ self.check_packets(out, port_id)
+ p = "vlan (\d+)"
+ vlan_list = re.findall(p, tcpdump_out)
+ if vlan_header:
+ header = re.findall(vlan_header, tcpdump_out)
+ if pkt_index == 0:
+ if out_vlan == 1:
+ self.verify(
+ len(vlan_list) == 1,
+ "received packet outer vlan not is %s" % out_vlan,
+ )
+ elif out_vlan == 0:
+ self.verify(
+ len(vlan_list) == 0,
+ "received packet outer vlan not is %s" % out_vlan,
+ )
+ else:
+ self.verify(
+ int(vlan_list[0]) == out_vlan,
+ "received packet outer vlan not is %s" % out_vlan,
+ )
+ if iner_vlan:
+ self.verify(
+ int(vlan_list[1]) == iner_vlan,
+ "received packet outer vlan not is %s" % iner_vlan,
+ )
+ else:
+ if out_vlan == 1:
+ self.verify(
+ len(vlan_list) == 3 and int(vlan_list[1]) == out_vlan,
+ "received packet outer vlan not is %s" % out_vlan,
+ )
+ elif out_vlan == 0:
+ self.verify(
+ len(vlan_list) == 2,
+ "received packet outer vlan not is %s" % out_vlan,
+ )
+ else:
+ self.verify(
+ int(vlan_list[1]) == out_vlan,
+ "received packet outer vlan not is %s" % out_vlan,
+ )
+ if iner_vlan:
+ self.verify(
+ int(vlan_list[2]) == iner_vlan,
+ "received packet outer vlan not is %s" % iner_vlan,
+ )
+ if vlan_header == "0x8100":
+ self.verify(
+ vlan_header in tcpdump_out,
+ "vlan header not matched, expect: %s." % vlan_header,
+ )
+ elif vlan_header is None:
+ pass
+ else:
+ self.verify(
+ len(header) == 1,
+ "vlan header not matched, expect: %s." % vlan_header,
+ )
+
+ def check_vlan_offload(self, vlan_type, stats):
+ p = "VLAN offload.*\n.*?%s (\w+)" % vlan_type
+ out = self.pmd_output.execute_cmd("show port info 0")
+ vlan_stats = re.search(p, out).group(1)
+ self.verify(vlan_stats == stats, "VLAN stats mismatch")
+
+ def tcpdump_send_packet_get_output(self, pkt):
+ self.tester.send_expect("rm -rf getPackageByTcpdump.cap", "# ")
+ self.tester.send_expect(
+ "tcpdump -A -nn -e -vv -w getPackageByTcpdump.cap -i %s 2> /dev/null& "
+ % (self.tester_iface0),
+ "#",
+ )
+ time.sleep(2)
+ out1 = self.fdirprocess.send_pkt_get_out(pkt)
+ out2 = self.pmd_output.execute_cmd("show port stats 0")
+ out = out1 + out2
+ self.tester.send_expect("killall tcpdump", "# ")
+ tcpdump_out = self.tester.send_expect(
+ "tcpdump -A -nn -vv -er getPackageByTcpdump.cap", "# "
+ )
+
+ return (out, tcpdump_out)
+
+ def send_pkt_check_vlan_and_crc(
+ self, pkt, pkt_len=None, vlan_strip=False, crc_strip=False
+ ):
+ out, tcpdump_out = self.tcpdump_send_packet_get_output(pkt)
+ pkt_length = re.search("length=(\d+)", out).group(1)
+ rx_bytes = re.search("RX-bytes:\s+(\d+)", out).group(1)
+ if crc_strip:
+ self.verify(rx_bytes == pkt_length, "CRC strip on failed")
+ else:
+ self.verify(int(rx_bytes) == int(pkt_length) + 4, "CRC strip off failed")
+ if pkt_len:
+ vlan_list = re.findall("vlan\s+\d+", tcpdump_out)
+ if not vlan_strip:
+ self.verify(pkt_length == pkt_len, "vlan strip off failed")
+ self.verify(len(vlan_list) == 4, "Failed pass received vlan packet")
+ elif vlan_strip:
+ self.verify(
+ int(pkt_length) + 4 == int(pkt_len), "vlan strip off failed"
+ )
+ self.verify(
+ len(vlan_list) == 3 and vlan_list[0] != vlan_list[-1],
+ "Failed error received vlan packet",
+ )
+
+ def send_pkt_check_vlan_filter(self, pkt, expected_pkts, expected_vlan, rx_vlan=0):
+ out, tcpdump_out = self.tcpdump_send_packet_get_output(pkt)
+ received_pkt = re.findall("dst=%s" % self.vf0_mac, out)
+ vlan_captured = re.findall("vlan \d+", tcpdump_out)
+ if self.default_stats:
+ if rx_vlan:
+ self.verify(
+ len(received_pkt) == expected_pkts,
+ "Failed to get pkt num, expect %s, but got %s"
+ % (expected_pkts, len(received_pkt)),
+ )
+ self.verify(
+ len(vlan_captured) == expected_vlan,
+ "Failed to get pkt vlan num, expect %s, but got %s"
+ % (expected_vlan, len(vlan_captured)),
+ )
+ else:
+ self.verify(
+ len(received_pkt) == 0,
+ "Failed to get pkt num, expect %s, but got %s"
+ % (0, len(received_pkt)),
+ )
+ else:
+ self.verify(
+ len(received_pkt) == expected_pkts,
+ "Failed to get pkt num, expect %s, but got %s"
+ % (expected_pkts, len(received_pkt)),
+ )
+ self.verify(
+ len(vlan_captured) == expected_vlan,
+ "Failed to get pkt vlan num, expect %s, but got %s"
+ % (expected_vlan, len(vlan_captured)),
+ )
+
+ def test_enable_disable_iavf_vlan_filter(self):
+ """
+ Test case 1: Enable/Disable IAVF VLAN filtering
+ """
+ pkt_list1 = [
+ 'Ether(dst="%s",type=0x8100)/Dot1Q(vlan=1,type=0x8100)/Dot1Q(vlan=2,type=0x0800)/IP(src="196.222.232.221")/("X"*480)'
+ % self.vf0_mac,
+ 'Ether(dst="%s",type=0x8100)/Dot1Q(vlan=1,type=0x0800)/IP(src="196.222.232.221")/("X"*480)'
+ % self.vf0_mac,
+ ]
+ pkt_list2 = [
+ 'Ether(dst="%s",type=0x8100)/Dot1Q(vlan=11,type=0x8100)/Dot1Q(vlan=2,type=0x0800)/IP(src="196.222.232.221")/("X"*480)'
+ % self.vf0_mac,
+ 'Ether(dst="%s",type=0x8100)/Dot1Q(vlan=11,type=0x0800)/IP(src="196.222.232.221")/("X"*480)'
+ % self.vf0_mac,
+ ]
+ if self.default_stats:
+ self.dut.send_expect(
+ "ethtool --set-priv-flags %s %s on" % (self.pf_interface, self.flag),
+ "# ",
+ )
+ self.setup_pf_vfs_env()
+ self.launch_testpmd()
+ self.pmd_output.execute_cmd("vlan set filter on 0")
+ self.check_vlan_offload(vlan_type="filter", stats="on")
+ self.send_pkt_check_vlan_filter(
+ pkt_list1, expected_pkts=len(pkt_list1), expected_vlan=6, rx_vlan=False
+ )
+ self.pmd_output.execute_cmd("rx_vlan add 1 0")
+ self.send_pkt_check_vlan_filter(
+ pkt_list1, expected_pkts=len(pkt_list1), expected_vlan=6, rx_vlan=True
+ )
+
+ self.send_pkt_check_vlan_filter(
+ pkt_list2, expected_pkts=0, expected_vlan=3, rx_vlan=True
+ )
+
+ self.pmd_output.execute_cmd("rx_vlan rm 1 0")
+ self.send_pkt_check_vlan_filter(
+ pkt_list1, expected_pkts=len(pkt_list1), expected_vlan=6, rx_vlan=False
+ )
+
+ def test_enable_disable_iavf_vlan_strip(self):
+ """
+ Test case 2: Enable/Disable IAVF VLAN header stripping
+ """
+ pkt_list = [
+ 'Ether(dst="%s",type=0x8100)/Dot1Q(vlan=1,type=0x8100)/Dot1Q(vlan=2,type=0x0800)/IP(src="196.222.232.221")/("X"*480)'
+ % self.vf0_mac,
+ 'Ether(dst="%s",type=0x8100)/Dot1Q(vlan=1,type=0x0800)/IP(src="196.222.232.221")/("X"*480)'
+ % self.vf0_mac,
+ ]
+ if self.default_stats:
+ self.dut.send_expect(
+ "ethtool --set-priv-flags %s %s on" % (self.pf_interface, self.flag),
+ "# ",
+ )
+ self.setup_pf_vfs_env()
+ self.launch_testpmd()
+ self.pmd_output.execute_cmd("vlan set filter on 0")
+ self.pmd_output.execute_cmd("rx_vlan add 1 0")
+ self.check_vlan_offload(vlan_type="filter", stats="on")
+ self.pmd_output.execute_cmd("vlan set strip on 0")
+ self.check_vlan_offload(vlan_type="strip", stats="on")
+ self.send_pkt_check_vlan_filter(
+ pkt_list, expected_pkts=len(pkt_list), expected_vlan=4, rx_vlan=True
+ )
+
+ self.pmd_output.execute_cmd("vlan set strip off 0")
+ self.check_vlan_offload(vlan_type="strip", stats="off")
+ self.send_pkt_check_vlan_filter(
+ pkt_list, expected_pkts=len(pkt_list), expected_vlan=6, rx_vlan=True
+ )
+
+ def test_enable_disable_iavf_vlan_insert(self):
+ """
+ Test case 3: Enable/Disable IAVF VLAN header insertion
+ """
+ out_vlan = 1
+ pkt_list = [
+ 'Ether(dst="%s",type=0x0800)/IP(src="196.222.232.221")/("X"*480)'
+ % self.vf0_mac,
+ 'Ether(dst="%s",type=0x8100)/Dot1Q(vlan=11,type=0x0800)/IP(src="196.222.232.221")/("X"*480)'
+ % self.vf0_mac,
+ ]
+ if self.default_stats:
+ self.dut.send_expect(
+ "ethtool --set-priv-flags %s %s off" % (self.pf_interface, self.flag),
+ "# ",
+ )
+ self.setup_pf_vfs_env()
+ self.launch_testpmd()
+ self.pmd_output.execute_cmd("stop")
+ self.pmd_output.execute_cmd("port stop 0")
+ self.pmd_output.execute_cmd("tx_vlan set 0 1")
+ self.pmd_output.execute_cmd("port start 0")
+ self.pmd_output.execute_cmd("start")
+ self.send_packet_check_vlan_inter(pkt_list, out_vlan, port_id=0)
+
+ self.pmd_output.execute_cmd("stop")
+ self.pmd_output.execute_cmd("port stop 0")
+ self.pmd_output.execute_cmd("tx_vlan reset 0")
+ self.pmd_output.execute_cmd("port start 0")
+ self.pmd_output.execute_cmd("start")
+ self.send_packet_check_vlan_inter(pkt_list, out_vlan=0, port_id=0)
+
+ def test_enable_disable_iavf_CRC_strip(self):
+ """
+ Test case 4: Enable/disable AVF CRC stripping
+ """
+ param = "--rxq=16 --txq=16 --disable-crc-strip"
+ pkt = (
+ 'Ether(dst="%s",type=0x0800)/IP(src="196.222.232.221")/("X"*480)'
+ % self.vf0_mac
+ )
+ if self.default_stats:
+ self.dut.send_expect(
+ "ethtool --set-priv-flags %s %s on" % (self.pf_interface, self.flag),
+ "# ",
+ )
+ self.setup_pf_vfs_env()
+ self.pmd_output.start_testpmd(
+ cores="1S/4C/1T", param=param, ports=self.pci_list, socket=self.ports_socket
+ )
+ self.pmd_output.execute_cmd("set fwd mac")
+ self.pmd_output.execute_cmd("set verbose 1")
+ self.pmd_output.execute_cmd("start")
+ self.send_pkt_check_vlan_and_crc(pkt)
+
+ self.pmd_output.execute_cmd("stop")
+ self.pmd_output.execute_cmd("port stop 0")
+ self.pmd_output.execute_cmd("port config 0 rx_offload keep_crc off")
+ self.pmd_output.execute_cmd("port start 0")
+ self.pmd_output.execute_cmd("start")
+ self.send_pkt_check_vlan_and_crc(pkt, crc_strip=True)
+
+ self.pmd_output.execute_cmd("stop")
+ self.pmd_output.execute_cmd("port stop 0")
+ self.pmd_output.execute_cmd("port config 0 rx_offload keep_crc on")
+ self.pmd_output.execute_cmd("port start 0")
+ self.pmd_output.execute_cmd("start")
+ self.send_pkt_check_vlan_and_crc(pkt)
+
+ self.pmd_output.execute_cmd("quit", "#")
+ self.launch_testpmd()
+ self.send_pkt_check_vlan_and_crc(pkt, crc_strip=True)
+
+ def test_CRC_strip_iavf_vlan_strip_coexists(self):
+ """
+ Test case 5: AVF CRC strip and Vlan strip co-exists
+ """
+ pkt = (
+ 'Ether(dst="%s",type=0x8100)/Dot1Q(vlan=1,type=0x8100)/Dot1Q(vlan=2,type=0x0800)/IP(src="196.222.232.221")/("X"*480)'
+ % self.vf0_mac
+ )
+ if self.default_stats:
+ self.dut.send_expect(
+ "ethtool --set-priv-flags %s %s on" % (self.pf_interface, self.flag),
+ "# ",
+ )
+ self.setup_pf_vfs_env()
+ self.launch_testpmd()
+ self.check_vlan_offload(vlan_type="strip", stats="off")
+ self.pmd_output.execute_cmd("stop")
+ self.pmd_output.execute_cmd("vlan set strip off 0")
+ self.check_vlan_offload(vlan_type="strip", stats="off")
+ self.pmd_output.execute_cmd("vlan set filter on 0")
+ self.pmd_output.execute_cmd("rx_vlan add 1 0")
+ self.pmd_output.execute_cmd("start")
+
+ out, tcpdump_out = self.tcpdump_send_packet_get_output(pkt)
+ pkt_len = re.search("length=(\d+)", out).group(1)
+ vlan_list = re.findall("vlan\s+\d+", tcpdump_out)
+ self.verify(len(vlan_list) == 4, "vlan strip off failed")
+ rx_bytes = re.search("RX-bytes:\s+(\d+)", out).group(1)
+ tx_bytes = re.search("TX-bytes:\s+(\d+)", out).group(1)
+ self.verify(rx_bytes == tx_bytes == pkt_len, "CRC strip on failed")
+
+ self.pmd_output.execute_cmd("vlan set strip on 0")
+ self.check_vlan_offload(vlan_type="strip", stats="on")
+ self.send_pkt_check_vlan_and_crc(pkt=pkt, pkt_len=pkt_len, vlan_strip=True)
+
+ self.pmd_output.execute_cmd("vlan set strip off 0")
+ self.check_vlan_offload(vlan_type="strip", stats="off")
+ self.pmd_output.execute_cmd("stop")
+ self.pmd_output.execute_cmd("port stop 0")
+ self.pmd_output.execute_cmd("port config 0 rx_offload keep_crc on")
+ self.pmd_output.execute_cmd("port start 0")
+ self.pmd_output.execute_cmd("start")
+ self.send_pkt_check_vlan_and_crc(pkt=pkt, pkt_len=pkt_len)
+
+ out = self.pmd_output.execute_cmd("vlan set strip on 0")
+ p = "iavf_config_vlan_strip_v2(): fail to execute command VIRTCHNL_OP_ENABLE_VLAN_STRIPPING_V2"
+ self.verify(p in out, "set vlan strip on successfully")
+ self.send_pkt_check_vlan_and_crc(pkt=pkt, pkt_len=pkt_len)
+
+ self.pmd_output.execute_cmd("vlan set strip off 0")
+ self.check_vlan_offload(vlan_type="strip", stats="off")
+ self.pmd_output.execute_cmd("stop")
+ self.pmd_output.execute_cmd("port stop 0")
+ self.pmd_output.execute_cmd("port config 0 rx_offload keep_crc off")
+ self.pmd_output.execute_cmd("port start 0")
+ self.pmd_output.execute_cmd("start")
+ self.send_pkt_check_vlan_and_crc(pkt=pkt, pkt_len=pkt_len, crc_strip=True)
+
+ def tear_down(self):
+ self.pmd_output.quit()
+ self.dut.kill_all()
+ self.destroy_iavf()
+
+ def tear_down_all(self):
+ if self.default_stats:
+ self.dut.send_expect(
+ "ethtool --set-priv-flags %s %s %s"
+ % (self.pf_interface, self.flag, self.default_stats),
+ "# ",
+ )
--
2.17.1
next prev parent reply other threads:[~2022-09-07 8:52 UTC|newest]
Thread overview: 9+ messages / expand[flat|nested] mbox.gz Atom feed top
2022-09-07 17:06 [dts] [PATCH V1 1/4] test_plans/ice_dcf_dual_vlan: " Song Jiale
2022-09-07 17:06 ` [dts] [PATCH V1 2/4] test_plans/ice_iavf_dual_vlan: " Song Jiale
2022-09-13 8:56 ` Fu, Qi
2022-09-07 17:06 ` [dts] [PATCH V1 3/4] tests/ice_dcf_dual_vlan: " Song Jiale
2022-09-13 8:58 ` Fu, Qi
2022-09-07 17:06 ` Song Jiale [this message]
2022-09-13 8:33 ` [dts] [PATCH V1 4/4] tests/ice_iavf_dual_vlan: " Fu, Qi
2022-09-13 8:35 ` [dts] [PATCH V1 1/4] test_plans/ice_dcf_dual_vlan: " Fu, Qi
-- strict thread matches above, loose matches on Subject: below --
2022-09-07 13:54 Song Jiale
2022-09-07 13:54 ` [dts] [PATCH V1 4/4] tests/ice_iavf_dual_vlan: " Song Jiale
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=20220907170656.2551-4-songx.jiale@intel.com \
--to=songx.jiale@intel.com \
--cc=dts@dpdk.org \
/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).