From: Dietmar Maurer <dietmar@proxmox.com>
To: pve-devel@lists.proxmox.com
Subject: [RFC proxmox 11/22] firewall-api-types: add FirewallIcmpType
Date: Mon, 16 Feb 2026 11:43:49 +0100 [thread overview]
Message-ID: <20260216104401.3959270-12-dietmar@proxmox.com> (raw)
In-Reply-To: <20260216104401.3959270-1-dietmar@proxmox.com>
This adds the `FirewallIcmpType` enum, which can represent ICMP types
either as a named variant (using `FirewallIcmpTypeName`) or as a raw
numeric value (u8).
The `FirewallIcmpTypeName` enum covers standard ICMPv4 and ICMPv6 types,
including deprecated ones and those with codes (e.g., `destination-unreachable`).
It provides `ipv4()` and `ipv6()` helper methods to check if a specific
named type is valid for the respective protocol.
Serialization and deserialization are handled via `serde` and `serde_plain`,
allowing for easy conversion to/from strings.
Includes tests for serialization, deserialization, and protocol validity checks.
---
proxmox-firewall-api-types/src/icmp_type.rs | 555 ++++++++++++++++++++
proxmox-firewall-api-types/src/lib.rs | 3 +
2 files changed, 558 insertions(+)
create mode 100644 proxmox-firewall-api-types/src/icmp_type.rs
diff --git a/proxmox-firewall-api-types/src/icmp_type.rs b/proxmox-firewall-api-types/src/icmp_type.rs
new file mode 100644
index 00000000..b45c1505
--- /dev/null
+++ b/proxmox-firewall-api-types/src/icmp_type.rs
@@ -0,0 +1,555 @@
+use std::fmt;
+
+use anyhow::{bail, Error};
+use serde::{Deserialize, Serialize};
+
+#[cfg(feature = "enum-fallback")]
+use proxmox_fixed_string::FixedString;
+
+use proxmox_schema::{ApiStringFormat, ApiType, Schema, StringSchema};
+
+#[derive(Debug, Copy, Clone, PartialEq)]
+/// ICMP type, either named or numeric.
+pub enum FirewallIcmpType {
+ /// Named ICMP type, e.g. "echo-request"
+ Named(FirewallIcmpTypeName),
+ /// Numeric ICMP type, e.g. "8"
+ Numeric(u8),
+}
+
+impl ApiType for FirewallIcmpType {
+ const API_SCHEMA: Schema = StringSchema::new(
+ r#"ICMP type, either named or numeric.
+ Only valid if proto equals 'icmp' or 'icmpv6'/'ipv6-icmp'."#,
+ )
+ .format(&ApiStringFormat::VerifyFn(verify_firewall_icmp_type))
+ .schema();
+}
+
+fn verify_firewall_icmp_type(value: &str) -> Result<(), Error> {
+ value.parse::<FirewallIcmpType>().map(|_| ())
+}
+
+impl std::str::FromStr for FirewallIcmpType {
+ type Err = Error;
+
+ fn from_str(s: &str) -> Result<Self, Error> {
+ let s = s.trim();
+
+ if let Ok(ty) = s.parse::<u8>() {
+ return Ok(Self::Numeric(ty));
+ }
+
+ if let Ok(named) = serde_plain::from_str::<FirewallIcmpTypeName>(s) {
+ return Ok(Self::Named(named));
+ }
+
+ bail!("{s:?} is not a valid icmp type");
+ }
+}
+
+impl fmt::Display for FirewallIcmpType {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ match self {
+ FirewallIcmpType::Numeric(ty) => write!(f, "{ty}"),
+ FirewallIcmpType::Named(ty) => write!(f, "{ty}"),
+ }
+ }
+}
+
+serde_plain::derive_deserialize_from_fromstr!(FirewallIcmpType, "valid icmp type name or number");
+serde_plain::derive_serialize_from_display!(FirewallIcmpType);
+
+#[derive(Serialize, Deserialize, Debug, Copy, Clone, PartialEq)]
+#[serde(rename_all = "kebab-case")]
+/// Named ICMP type, e.g. "echo-request".
+pub enum FirewallIcmpTypeName {
+ Any,
+
+ // IPv4 specific
+ HostUnreachable, // 3:1 (ICMP-TYPE, ICMP-CODE)
+ ProtocolUnreachable, // 3:2
+ PortUnreachable, // 3:3
+ FragmentationNeeded, // 3:4
+ SourceRouteFailed, // 3:5
+ NetworkUnknown, // 3:6
+ HostUnknown, // 3:7
+ NetworkProhibited, // 3:9
+ HostProhibited, // 3:10
+ #[serde(rename = "TOS-network-unreachable")]
+ TOSNetworkUnreachable, // 3:11
+ #[serde(rename = "TOS-host-unreachable")]
+ TOSHostUnreachable, // 3:12
+ CommunicationProhibited, // 3:13
+ HostPrecedenceViolation, // 3:14
+ PrecedenceCutoff, // 3:15
+ SourceQuench, // 4
+ NetworkRedirect, // 5:0
+ HostRedirect, // 5:1
+ #[serde(rename = "TOS-network-redirect")]
+ TOSNetworkRedirect, // 5:2
+ #[serde(rename = "TOS-host-redirect")]
+ TOSHostRedirect, // 5:3
+ TimestampRequest, // 13
+ TimestampReply, // 14
+ AddressMaskRequest, // 17
+ AddressMaskReply, // 18
+ RouterAdvertisement, // 9
+ RouterSolicitation, // 10
+ IpHeaderBad, // 12:0
+ RequiredOptionMissing, // 12:1
+
+ // IPv6 specific
+ NoRoute, // 1:0
+ BeyondScope, // 1:2
+ AddressUnreachable, // 1:3
+ FailedPolicy, // 1:5
+ RejectRoute, // 1:6
+ PacketTooBig, // 2
+ BadHeader, // 4:0
+ UnknownHeaderType, // 4:1
+ UnknownOption, // 4:2
+ NeighborSolicitation, // 135
+ NeighbourSolicitation, // 135
+ NeighborAdvertisement, // 136
+ NeighbourAdvertisement, // 136
+
+ // Common
+ DestinationUnreachable, // 3:0 (v4), 1:0 (v6) ? v6 has no-route
+ NetworkUnreachable, // 3:0
+ EchoReply, // 0 (v4), 129 (v6)
+ EchoRequest, // 8 (v4), 128 (v6)
+ TimeExceeded, // 11 (v4), 3 (v6)
+ TtlZeroDuringTransit, // 11:0 (v4), 3:0 (v6)
+ TtlZeroDuringReassembly, // 11:1 (v4), 3:1 (v6)
+ ParameterProblem, // 12 (v4), 4 (v6)
+ Redirect, // 5 (v4), 137 (v6)
+
+ #[cfg(feature = "enum-fallback")]
+ #[serde(untagged)]
+ /// Unknwon
+ UnknownEnumValue(FixedString),
+}
+
+serde_plain::derive_display_from_serialize!(FirewallIcmpTypeName);
+serde_plain::derive_fromstr_from_deserialize!(FirewallIcmpTypeName);
+
+impl FirewallIcmpTypeName {
+ pub const fn ipv4(&self) -> bool {
+ match self {
+ FirewallIcmpTypeName::Any => true,
+ FirewallIcmpTypeName::EchoReply => true,
+ FirewallIcmpTypeName::DestinationUnreachable => true,
+ FirewallIcmpTypeName::NetworkUnreachable => true,
+ FirewallIcmpTypeName::HostUnreachable => true,
+ FirewallIcmpTypeName::ProtocolUnreachable => true,
+ FirewallIcmpTypeName::PortUnreachable => true,
+ FirewallIcmpTypeName::FragmentationNeeded => true,
+ FirewallIcmpTypeName::SourceRouteFailed => true,
+ FirewallIcmpTypeName::NetworkUnknown => true,
+ FirewallIcmpTypeName::HostUnknown => true,
+ FirewallIcmpTypeName::NetworkProhibited => true,
+ FirewallIcmpTypeName::HostProhibited => true,
+ FirewallIcmpTypeName::TOSNetworkUnreachable => true,
+ FirewallIcmpTypeName::TOSHostUnreachable => true,
+ FirewallIcmpTypeName::CommunicationProhibited => true,
+ FirewallIcmpTypeName::HostPrecedenceViolation => true,
+ FirewallIcmpTypeName::PrecedenceCutoff => true,
+ FirewallIcmpTypeName::SourceQuench => true,
+ FirewallIcmpTypeName::Redirect => true,
+ FirewallIcmpTypeName::NetworkRedirect => true,
+ FirewallIcmpTypeName::HostRedirect => true,
+ FirewallIcmpTypeName::TOSNetworkRedirect => true,
+ FirewallIcmpTypeName::TOSHostRedirect => true,
+ FirewallIcmpTypeName::EchoRequest => true,
+ FirewallIcmpTypeName::RouterAdvertisement => true,
+ FirewallIcmpTypeName::RouterSolicitation => true,
+ FirewallIcmpTypeName::TimeExceeded => true,
+ FirewallIcmpTypeName::TtlZeroDuringTransit => true,
+ FirewallIcmpTypeName::TtlZeroDuringReassembly => true,
+ FirewallIcmpTypeName::ParameterProblem => true,
+ FirewallIcmpTypeName::IpHeaderBad => true,
+ FirewallIcmpTypeName::RequiredOptionMissing => true,
+ FirewallIcmpTypeName::TimestampRequest => true,
+ FirewallIcmpTypeName::TimestampReply => true,
+ FirewallIcmpTypeName::AddressMaskRequest => true,
+ FirewallIcmpTypeName::AddressMaskReply => true,
+ _ => false,
+ }
+ }
+
+ pub const fn ipv6(&self) -> bool {
+ match self {
+ FirewallIcmpTypeName::Any => true,
+ FirewallIcmpTypeName::EchoReply => true,
+ FirewallIcmpTypeName::DestinationUnreachable => true,
+ FirewallIcmpTypeName::PacketTooBig => true,
+ FirewallIcmpTypeName::TimeExceeded => true,
+ FirewallIcmpTypeName::ParameterProblem => true,
+ FirewallIcmpTypeName::EchoRequest => true,
+ FirewallIcmpTypeName::RouterSolicitation => true,
+ FirewallIcmpTypeName::RouterAdvertisement => true,
+ FirewallIcmpTypeName::NeighborSolicitation => true,
+ FirewallIcmpTypeName::NeighbourSolicitation => true,
+ FirewallIcmpTypeName::NeighborAdvertisement => true,
+ FirewallIcmpTypeName::NeighbourAdvertisement => true,
+ FirewallIcmpTypeName::Redirect => true,
+ FirewallIcmpTypeName::NoRoute => true,
+ FirewallIcmpTypeName::CommunicationProhibited => true,
+ FirewallIcmpTypeName::BeyondScope => true,
+ FirewallIcmpTypeName::AddressUnreachable => true,
+ FirewallIcmpTypeName::PortUnreachable => true,
+ FirewallIcmpTypeName::FailedPolicy => true,
+ FirewallIcmpTypeName::RejectRoute => true,
+ FirewallIcmpTypeName::TtlZeroDuringTransit => true,
+ FirewallIcmpTypeName::TtlZeroDuringReassembly => true,
+ FirewallIcmpTypeName::BadHeader => true,
+ FirewallIcmpTypeName::UnknownHeaderType => true,
+ FirewallIcmpTypeName::UnknownOption => true,
+ _ => false,
+ }
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+
+ #[test]
+
+ fn test_icmp_types() {
+ // (name, variant, ipv4, ipv6)
+ let tests = [
+ ("any", FirewallIcmpTypeName::Any, true, true),
+ ("echo-reply", FirewallIcmpTypeName::EchoReply, true, true),
+ (
+ "destination-unreachable",
+ FirewallIcmpTypeName::DestinationUnreachable,
+ true,
+ true,
+ ),
+ (
+ "network-unreachable",
+ FirewallIcmpTypeName::NetworkUnreachable,
+ true,
+ false,
+ ),
+ (
+ "host-unreachable",
+ FirewallIcmpTypeName::HostUnreachable,
+ true,
+ false,
+ ),
+ (
+ "protocol-unreachable",
+ FirewallIcmpTypeName::ProtocolUnreachable,
+ true,
+ false,
+ ),
+ (
+ "port-unreachable",
+ FirewallIcmpTypeName::PortUnreachable,
+ true,
+ true,
+ ),
+ (
+ "fragmentation-needed",
+ FirewallIcmpTypeName::FragmentationNeeded,
+ true,
+ false,
+ ),
+ (
+ "source-route-failed",
+ FirewallIcmpTypeName::SourceRouteFailed,
+ true,
+ false,
+ ),
+ (
+ "network-unknown",
+ FirewallIcmpTypeName::NetworkUnknown,
+ true,
+ false,
+ ),
+ (
+ "host-unknown",
+ FirewallIcmpTypeName::HostUnknown,
+ true,
+ false,
+ ),
+ (
+ "network-prohibited",
+ FirewallIcmpTypeName::NetworkProhibited,
+ true,
+ false,
+ ),
+ (
+ "host-prohibited",
+ FirewallIcmpTypeName::HostProhibited,
+ true,
+ false,
+ ),
+ (
+ "TOS-network-unreachable",
+ FirewallIcmpTypeName::TOSNetworkUnreachable,
+ true,
+ false,
+ ),
+ (
+ "TOS-host-unreachable",
+ FirewallIcmpTypeName::TOSHostUnreachable,
+ true,
+ false,
+ ),
+ (
+ "communication-prohibited",
+ FirewallIcmpTypeName::CommunicationProhibited,
+ true,
+ true,
+ ),
+ (
+ "host-precedence-violation",
+ FirewallIcmpTypeName::HostPrecedenceViolation,
+ true,
+ false,
+ ),
+ (
+ "precedence-cutoff",
+ FirewallIcmpTypeName::PrecedenceCutoff,
+ true,
+ false,
+ ),
+ (
+ "source-quench",
+ FirewallIcmpTypeName::SourceQuench,
+ true,
+ false,
+ ),
+ ("redirect", FirewallIcmpTypeName::Redirect, true, true),
+ (
+ "network-redirect",
+ FirewallIcmpTypeName::NetworkRedirect,
+ true,
+ false,
+ ),
+ (
+ "host-redirect",
+ FirewallIcmpTypeName::HostRedirect,
+ true,
+ false,
+ ),
+ (
+ "TOS-network-redirect",
+ FirewallIcmpTypeName::TOSNetworkRedirect,
+ true,
+ false,
+ ),
+ (
+ "TOS-host-redirect",
+ FirewallIcmpTypeName::TOSHostRedirect,
+ true,
+ false,
+ ),
+ (
+ "echo-request",
+ FirewallIcmpTypeName::EchoRequest,
+ true,
+ true,
+ ),
+ (
+ "router-advertisement",
+ FirewallIcmpTypeName::RouterAdvertisement,
+ true,
+ true,
+ ),
+ (
+ "router-solicitation",
+ FirewallIcmpTypeName::RouterSolicitation,
+ true,
+ true,
+ ),
+ (
+ "time-exceeded",
+ FirewallIcmpTypeName::TimeExceeded,
+ true,
+ true,
+ ),
+ (
+ "ttl-zero-during-transit",
+ FirewallIcmpTypeName::TtlZeroDuringTransit,
+ true,
+ true,
+ ),
+ (
+ "ttl-zero-during-reassembly",
+ FirewallIcmpTypeName::TtlZeroDuringReassembly,
+ true,
+ true,
+ ),
+ (
+ "parameter-problem",
+ FirewallIcmpTypeName::ParameterProblem,
+ true,
+ true,
+ ),
+ (
+ "ip-header-bad",
+ FirewallIcmpTypeName::IpHeaderBad,
+ true,
+ false,
+ ),
+ (
+ "required-option-missing",
+ FirewallIcmpTypeName::RequiredOptionMissing,
+ true,
+ false,
+ ),
+ (
+ "timestamp-request",
+ FirewallIcmpTypeName::TimestampRequest,
+ true,
+ false,
+ ),
+ (
+ "timestamp-reply",
+ FirewallIcmpTypeName::TimestampReply,
+ true,
+ false,
+ ),
+ (
+ "address-mask-request",
+ FirewallIcmpTypeName::AddressMaskRequest,
+ true,
+ false,
+ ),
+ (
+ "address-mask-reply",
+ FirewallIcmpTypeName::AddressMaskReply,
+ true,
+ false,
+ ),
+ ("no-route", FirewallIcmpTypeName::NoRoute, false, true),
+ (
+ "beyond-scope",
+ FirewallIcmpTypeName::BeyondScope,
+ false,
+ true,
+ ),
+ (
+ "address-unreachable",
+ FirewallIcmpTypeName::AddressUnreachable,
+ false,
+ true,
+ ),
+ (
+ "failed-policy",
+ FirewallIcmpTypeName::FailedPolicy,
+ false,
+ true,
+ ),
+ (
+ "reject-route",
+ FirewallIcmpTypeName::RejectRoute,
+ false,
+ true,
+ ),
+ (
+ "packet-too-big",
+ FirewallIcmpTypeName::PacketTooBig,
+ false,
+ true,
+ ),
+ ("bad-header", FirewallIcmpTypeName::BadHeader, false, true),
+ (
+ "unknown-header-type",
+ FirewallIcmpTypeName::UnknownHeaderType,
+ false,
+ true,
+ ),
+ (
+ "unknown-option",
+ FirewallIcmpTypeName::UnknownOption,
+ false,
+ true,
+ ),
+ (
+ "neighbor-solicitation",
+ FirewallIcmpTypeName::NeighborSolicitation,
+ false,
+ true,
+ ),
+ (
+ "neighbour-solicitation",
+ FirewallIcmpTypeName::NeighbourSolicitation,
+ false,
+ true,
+ ),
+ (
+ "neighbor-advertisement",
+ FirewallIcmpTypeName::NeighborAdvertisement,
+ false,
+ true,
+ ),
+ (
+ "neighbour-advertisement",
+ FirewallIcmpTypeName::NeighbourAdvertisement,
+ false,
+ true,
+ ),
+ ];
+
+ for (input, expected, v4, v6) in tests {
+ let deserialized: FirewallIcmpTypeName =
+ serde_plain::from_str(input).expect("deserialize");
+ assert_eq!(deserialized, expected);
+ let serialized = serde_plain::to_string(&deserialized).expect("serialize");
+ assert_eq!(serialized, input);
+
+ assert_eq!(deserialized.ipv4(), v4, "ipv4 check failed for {}", input);
+ assert_eq!(deserialized.ipv6(), v6, "ipv6 check failed for {}", input);
+ }
+ }
+
+ #[test]
+ fn test_firewall_icmp_type_enum() {
+ // Numeric
+ for (input, output) in [
+ ("0", FirewallIcmpType::Numeric(0)),
+ ("10", FirewallIcmpType::Numeric(10)),
+ ("255", FirewallIcmpType::Numeric(255)),
+ ] {
+ let ty: FirewallIcmpType = input.parse().expect("valid numeric icmp type");
+ assert_eq!(ty, output);
+ assert_eq!(ty.to_string(), input);
+ }
+
+ // Named
+ for (input, output) in [
+ ("echo-request", FirewallIcmpTypeName::EchoRequest),
+ ("any", FirewallIcmpTypeName::Any),
+ ] {
+ let ty: FirewallIcmpType = input.parse().expect("valid named icmp type");
+ assert_eq!(ty, FirewallIcmpType::Named(output));
+ assert_eq!(ty.to_string(), input);
+ }
+
+ // Invalid
+ #[cfg(not(feature = "enum-fallback"))]
+ for input in ["echo-reques", "an", "256", "-1", "foo"] {
+ input
+ .parse::<FirewallIcmpType>()
+ .expect_err("invalid icmp type");
+ }
+
+ // Invalid, but with enum fallback enabled, should be parsed as unknown
+ #[cfg(feature = "enum-fallback")]
+ for input in ["echo-reques", "an", "256", "-1", "foo"] {
+ let ty = input.parse::<FirewallIcmpType>().expect("valid icmp type");
+ assert_eq!(
+ ty,
+ FirewallIcmpType::Named(FirewallIcmpTypeName::UnknownEnumValue(
+ FixedString::new(input).unwrap(),
+ )),
+ );
+ }
+ }
+}
diff --git a/proxmox-firewall-api-types/src/lib.rs b/proxmox-firewall-api-types/src/lib.rs
index b099be0c..610282bb 100644
--- a/proxmox-firewall-api-types/src/lib.rs
+++ b/proxmox-firewall-api-types/src/lib.rs
@@ -1,6 +1,9 @@
mod conntrack;
pub use conntrack::FirewallConntrackHelper;
+mod icmp_type;
+pub use icmp_type::{FirewallIcmpType, FirewallIcmpTypeName};
+
mod log;
pub use log::{
FirewallLogLevel, FirewallLogRateLimit, FirewallPacketRate, FirewallPacketRateTimescale,
--
2.47.3
next prev parent reply other threads:[~2026-02-16 10:46 UTC|newest]
Thread overview: 26+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-02-16 10:43 [RFC proxmox 00/22] New crate for firewall api types Dietmar Maurer
2026-02-16 10:43 ` [RFC proxmox 01/22] firewall-api-types: add new " Dietmar Maurer
2026-02-16 10:43 ` [RFC proxmox 02/22] firewall-api-types: add README.md Dietmar Maurer
2026-02-16 10:43 ` [RFC proxmox 03/22] firewall-api-types: add firewall policy types Dietmar Maurer
2026-02-16 10:43 ` [RFC proxmox 04/22] firewall-api-types: add logging types Dietmar Maurer
2026-02-16 10:43 ` [RFC proxmox 05/22] firewall-api-types: add FirewallClusterOptions Dietmar Maurer
2026-02-16 10:43 ` [RFC proxmox 06/22] firewall-api-types: add FirewallGuestOptions Dietmar Maurer
2026-02-16 10:43 ` [RFC proxmox 07/22] firewall-api-types: add FirewallConntrackHelper enum Dietmar Maurer
2026-02-16 10:43 ` [RFC proxmox 08/22] firewall-api-types: add FirewallNodeOptions struct Dietmar Maurer
2026-02-16 10:43 ` [RFC proxmox 09/22] firewall-api-types: add FirewallRef type Dietmar Maurer
2026-02-16 10:43 ` [RFC proxmox 10/22] firewall-api-types: add FirewallPortList types Dietmar Maurer
2026-02-16 10:43 ` Dietmar Maurer [this message]
2026-02-16 10:43 ` [RFC proxmox 12/22] firewall-api-types: add FirewallIpsetReference type Dietmar Maurer
2026-02-16 10:43 ` [RFC proxmox 13/22] firewall-api-types: add FirewallAliasReference type Dietmar Maurer
2026-02-16 10:43 ` [RFC proxmox 14/22] firewall-api-types: add firewall address types Dietmar Maurer
2026-02-16 10:43 ` [RFC proxmox 15/22] firewall-api-types: add FirewallRule type Dietmar Maurer
2026-02-16 10:43 ` [RFC proxmox 16/22] firewall-api-types: use ConfigDigest from proxmox-config-digest crate Dietmar Maurer
2026-02-16 10:43 ` [RFC proxmox 17/22] firewall-api-types: use COMMENT_SCHEMA from proxmox-schema crate Dietmar Maurer
2026-02-16 10:43 ` [RFC proxmox 18/22] firewall-api-types: add FirewallRuleUpdater type Dietmar Maurer
2026-02-16 10:43 ` [RFC proxmox 19/22] firewall-api-types: refactor FirewallRule and add FirewallRuleListEntry Dietmar Maurer
2026-02-16 10:43 ` [RFC proxmox 20/22] firewall-api-types: add DeletableFirewallRuleProperty enum Dietmar Maurer
2026-02-16 10:43 ` [RFC proxmox 21/22] firewall-api-types: add FirewallAliasEntry API type Dietmar Maurer
2026-02-16 10:44 ` [RFC proxmox 22/22] firewall-api-types: add FirewallIpsetListEntry and FirewallIpsetEntry api types Dietmar Maurer
2026-02-17 6:17 ` [RFC proxmox 00/22] New crate for firewall " Hannes Laimer
2026-02-17 6:39 ` Dietmar Maurer
2026-02-17 8:17 ` Hannes Laimer
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=20260216104401.3959270-12-dietmar@proxmox.com \
--to=dietmar@proxmox.com \
--cc=pve-devel@lists.proxmox.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