git: 2407636ff1cc - main - ipfw: document logdst opcode

From: Andrey V. Elsukov <ae_at_FreeBSD.org>
Date: Fri, 18 Apr 2025 12:34:19 UTC
The branch main has been updated by ae:

URL: https://cgit.FreeBSD.org/src/commit/?id=2407636ff1cc30e30355a6adc28d63e35780a7f0

commit 2407636ff1cc30e30355a6adc28d63e35780a7f0
Author:     Andrey V. Elsukov <ae@FreeBSD.org>
AuthorDate: 2025-04-18 12:00:07 +0000
Commit:     Andrey V. Elsukov <ae@FreeBSD.org>
CommitDate: 2025-04-18 12:00:07 +0000

    ipfw: document logdst opcode
    
    Also fix some problems reported by mandoc -T lint.
    Add a comment describing the format of rtsock messages.
    
    Submitted by:   Boris Lytochkin <lytboris at gmail com>
    Obtained from:  Yandex LLC
    Sponsored by:   Yandex LLC
---
 sbin/ipfw/ipfw.8             | 98 ++++++++++++++++++++++++++++++++------------
 sys/netpfil/ipfw/ip_fw_log.c | 53 ++++++++++++++++++++----
 2 files changed, 115 insertions(+), 36 deletions(-)

diff --git a/sbin/ipfw/ipfw.8 b/sbin/ipfw/ipfw.8
index 348e9a58f2ce..719f92e96e68 100644
--- a/sbin/ipfw/ipfw.8
+++ b/sbin/ipfw/ipfw.8
@@ -1,11 +1,11 @@
 .\"
-.Dd March 3, 2025
+.Dd April 18, 2025
 .Dt IPFW 8
 .Os
 .Sh NAME
 .Nm ipfw , dnctl
 .Nd User interface for firewall, traffic shaper, packet scheduler,
-in-kernel NAT.
+in-kernel NAT.\&
 .Sh SYNOPSIS
 .Ss FIREWALL CONFIGURATION
 .Nm
@@ -331,8 +331,8 @@ When listing, show dynamic rules in addition to static ones.
 When listing, show only dynamic states.
 When deleting, delete only dynamic states.
 .It Fl f
-Run without prompting for confirmation for commands that can cause problems if misused,
-i.e.,
+Run without prompting for confirmation for commands that can cause problems
+if misused, i.e.,
 .Cm flush .
 If there is no tty associated with the process, this is implied.
 The
@@ -577,7 +577,7 @@ The format of firewall rules is the following:
 .Op Cm set Ar set_number
 .Op Cm prob Ar match_probability
 .Ar action
-.Op Cm log Op Cm logamount Ar number
+.Op Cm log Op log_opts
 .Op Cm altq Ar queue
 .Oo
 .Bro Cm tag | untag
@@ -694,8 +694,10 @@ side effects.
 .It Cm log Op Cm logamount Ar number
 Packets matching a rule with the
 .Cm log
-keyword will be made available for logging in two ways:
-if the sysctl variable
+keyword will be made available for logging.
+Unless per-rule log destination is specified by
+.Cm logdst Ar logdst_spec
+option (see below), packets are logged in two ways: if the sysctl variable
 .Va net.inet.ip.fw.verbose
 is set to 0 (default), one can use
 .Xr bpf 4
@@ -743,6 +745,47 @@ command.
 Note: logging is done after all other packet matching conditions
 have been successfully verified, and before performing the final
 action (accept, deny, etc.) on the packet.
+.It Cm log Oo
+.Cm logamount Ar number
+.Oc Cm logdst Ar logdst_spec
+.Ar logdst_spec
+is a comma-separated list of log destinations for logging
+packets matching the rule.
+Destinations supported are:
+.Bl -tag -width indent
+.It Ar syslog
+Logs a packet to
+.Xr syslogd 8
+with a
+.Dv LOG_SECURITY
+facility.
+.It Ar ipfw0
+Logs a packet to the
+.Li ipfw0
+pseudo interface.
+.It Ar rtsock
+Logs a packet to the
+.Xr route 4
+socket.
+See the comments of
+.Fn ipfw_log_rtsock
+in ipfw source code for more
+information on the message's structure.
+.El
+.Pp
+Note:
+.Cm logamount
+limits a number of logging events rather than packets being logged.
+I.e. A packet matching a rule with
+.Bd -ragged -offset indent
+ ...
+.Cm log logamount
+100
+.Cm logdst
+syslog,ipfw0 ...
+.Ed
+.Pp
+will log upto 50 packets.
 .It Cm tag Ar number
 When a packet matches a rule with the
 .Cm tag
@@ -951,7 +994,7 @@ Pass packet to a
 nat instance
 (for network address translation, address redirect, etc.):
 see the
-.Sx NETWORK ADDRESS TRANSLATION (NAT)
+.Sx NETWORK ADDRESS TRANSLATION (NAT)\&
 Section for further information.
 .It Cm nat64lsn Ar name
 Pass packet to a stateful NAT64 instance (for IPv6/IPv4 network address and
@@ -964,14 +1007,14 @@ protocol translation): see the
 .Sx IPv6/IPv4 NETWORK ADDRESS AND PROTOCOL TRANSLATION
 Section for further information.
 .It Cm nat64clat Ar name
-Pass packet to a CLAT NAT64 instance (for client-side IPv6/IPv4 network address and
-protocol translation): see the
+Pass packet to a CLAT NAT64 instance (for client-side IPv6/IPv4 network address
+and protocol translation): see the
 .Sx IPv6/IPv4 NETWORK ADDRESS AND PROTOCOL TRANSLATION
 Section for further information.
 .It Cm nptv6 Ar name
 Pass packet to a NPTv6 instance (for IPv6-to-IPv6 network prefix translation):
 see the
-.Sx IPv6-to-IPv6 NETWORK PREFIX TRANSLATION (NPTv6)
+.Sx IPv6-to-IPv6 NETWORK PREFIX TRANSLATION (NPTv6)\&
 Section for further information.
 .It Cm pipe Ar pipe_nr
 Pass packet to a
@@ -1286,7 +1329,7 @@ protocol options, incoming or outgoing interfaces, etc.)
 that the packet must match in order to be recognised.
 In general, the patterns are connected by (implicit)
 .Cm and
-operators -- i.e., all must match in order for the
+operators \(em i.e., all must match in order for the
 rule to match.
 Individual patterns can be prefixed by the
 .Cm not
@@ -1409,7 +1452,7 @@ See the
 .Sx LOOKUP TABLES
 section below for more information on lookup tables.
 .El
-.It Ar addr-list : ip-addr Ns Op Ns , Ns Ar addr-list
+.It Ar addr-list : ip-addr Ns Op , Ns Ar addr-list
 .It Ar ip-addr :
 A host or subnet address specified in one of the following ways:
 .Bl -tag -width indent
@@ -1425,7 +1468,7 @@ and mask width of
 bits.
 As an example, 1.2.3.4/25 or 1.2.3.0/25 will match
 all IP numbers from 1.2.3.0 to 1.2.3.127 .
-.It Ar addr Ns : Ns Ar mask
+.It Ar addr : Ns Ar mask
 Matches all addresses with base
 .Ar addr
 (specified as an IP address, a network number, or a hostname)
@@ -1442,7 +1485,7 @@ format for contiguous masks, which is more compact and less
 error-prone.
 .El
 .It Ar addr-set : addr Ns Oo Ns / Ns Ar masklen Oc Ns Cm { Ns Ar list Ns Cm }
-.It Ar list : Bro Ar num | num-num Brc Ns Op Ns , Ns Ar list
+.It Ar list : Bro Ar num | num-num Brc Ns Op , Ns Ar list
 Matches all addresses with base address
 .Ar addr
 (specified as an IP address, a network number, or a hostname)
@@ -1469,7 +1512,7 @@ or 1.2.3.0/24{128,35-55,89}
 will match the following IP addresses:
 .br
 1.2.3.128, 1.2.3.35 to 1.2.3.55, 1.2.3.89 .
-.It Ar addr6-list : ip6-addr Ns Op Ns , Ns Ar addr6-list
+.It Ar addr6-list : ip6-addr Ns Op , Ns Ar addr6-list
 .It Ar ip6-addr :
 A host or subnet specified one of the following ways:
 .Bl -tag -width indent
@@ -3075,10 +3118,10 @@ This has the associated probabilities
 .Po Ar K
 and
 .Ar H Pc
-for the loss probability. This is different from the literature,
-where this model is described with probabilities of successful
-transmission k and h. However, converting from literature is
-easy:
+for the loss probability.
+This is different from the literature, where this model is described with
+probabilities of successful transmission k and h.
+However, converting from literature is easy:
 .Pp
 K = 1 - k ; H = 1 - h
 .Pp
@@ -3204,8 +3247,8 @@ delay low.
 At regular time intervals of
 .Cm tupdate
 .Ar time
-(15ms by default) a background process (re)calculates the probability based on queue delay
-deviations from
+(15ms by default) a background process (re)calculates the probability based on
+queue delay deviations from
 .Cm target
 .Ar time
 (15ms by default) and queue delay trends.
@@ -3224,8 +3267,8 @@ delay deviations that is used in drop probability calculation.
 0.125 is the default.
 .It Cm beta Ar n
 .Ar n
-is a floating point number between 0 and 7 which specifies is the weight of queue
-delay trend that is used in drop probability calculation.
+is a floating point number between 0 and 7 which specifies is the weight of
+queue delay trend that is used in drop probability calculation.
 1.25 is the default.
 .It Cm max_burst Ar time
 The maximum period of time that PIE does not drop/mark packets.
@@ -3584,7 +3627,8 @@ The NAT64 instance will determine a destination IPv4 address from prefix
 .It Cm states_chunks Ar number
 The number of states chunks in single ports group.
 Each ports group by default can keep 64 state entries in single chunk.
-The above value affects the maximum number of states that can be associated with single IPv4 alias address and port.
+The above value affects the maximum number of states that can be associated with
+a single IPv4 alias address and port.
 The value must be power of 2, and up to 128.
 .It Cm host_del_age Ar seconds
 The number of seconds until the host entry for a IPv6 client will be deleted
@@ -4460,7 +4504,7 @@ and
 .Cm defer-action
 can be used to precisely control creation and checking of dynamic rules.
 Example of usage of these options are provided in
-.Sx NETWORK ADDRESS TRANSLATION (NAT)
+.Sx NETWORK ADDRESS TRANSLATION (NAT)\&
 Section.
 .Pp
 To limit the number of connections a user can open
@@ -4949,7 +4993,7 @@ The syntax has grown over the years and sometimes it might be confusing.
 Unfortunately, backward compatibility prevents cleaning up mistakes
 made in the definition of the syntax.
 .Pp
-.Em !!! WARNING !!!
+.Em !!! WARNING !!!\&
 .Pp
 Misconfiguring the firewall can put your computer in an unusable state,
 possibly shutting down network services and requiring console access to
diff --git a/sys/netpfil/ipfw/ip_fw_log.c b/sys/netpfil/ipfw/ip_fw_log.c
index 98b7a758c612..b87aa3da9413 100644
--- a/sys/netpfil/ipfw/ip_fw_log.c
+++ b/sys/netpfil/ipfw/ip_fw_log.c
@@ -605,10 +605,42 @@ ipfw_copy_rule_comment(struct ip_fw *f, char *dst)
 	return (rcomment_len);
 }
 
+/*
+ * Logs a packet matched by a rule as a route(4) socket message.
+ *
+ * While ipfw0 pseudo interface provides a way to observe full packet body,
+ * no metadata (rule number, action, mark, etc) is available.
+ * pflog(4) is not an option either as it's header is hardcoded and does not
+ * provide sufficient space for ipfw meta information.
+ *
+ * To be able to get a machine-readable event with all meta information needed
+ * for user-space daemons we construct a route(4) message and pack as much meta
+ * information as we can into it.
+ *
+ * RTAX_DST(0): (struct sockaddr_dl) carrying ipfwlog_rtsock_hdr_v2 in sdl_data
+ *		with general rule information (rule number, set, action, mark,
+ *		cmd, comment) and source/destination MAC addresses in case we're
+ *		logging in layer2 pass.
+ *
+ * RTAX_GATEWAY(1): (struct sockaddr) IP source address
+ *
+ * RTAX_NETMASK(2): (struct sockaddr) IP destination address
+ *
+ * RTAX_GENMASK(3): (struct sockaddr) IP address and port used in fwd action
+ *
+ * One SHOULD set an explicit logamount for any rule using rtsock as flooding
+ * route socket with such events could lead to various system-wide side effects.
+ * RTF_PROTO1 flag in (struct rt_addrinfo).rti_flags is set in all messages
+ * once half of logamount limit is crossed. This could be used by the software
+ * processing these logs to issue `ipfw resetlog` command to keep the event
+ * flow.
+ *
+ * TODO: convert ipfwlog_rtsock_hdr_v2 data into TLV to ease expansion.
+*/
+
 static void
 ipfw_log_rtsock(struct ip_fw_chain *chain, struct ip_fw *f, u_int hlen,
-    struct ip_fw_args *args, u_short offset, uint32_t tablearg,
-    void *_eh)
+    struct ip_fw_args *args, u_short offset, uint32_t tablearg, void *_eh)
 {
 	struct sockaddr_dl *sdl_ipfwcmd;
 	struct ether_header *eh = _eh;
@@ -628,6 +660,9 @@ ipfw_log_rtsock(struct ip_fw_chain *chain, struct ip_fw *f, u_int hlen,
 	if (l->max_log != 0 && l->log_left == 0)
 		return;
 
+	if (hlen == 0) /* non-ip */
+		return;
+
 	l->log_left--;
 	if (V_fw_verbose != 0 && l->log_left == 0) {
 		log(LOG_SECURITY | LOG_NOTICE,
@@ -688,6 +723,9 @@ ipfw_log_rtsock(struct ip_fw_chain *chain, struct ip_fw *f, u_int hlen,
 	    &info->rti_info[RTAX_GATEWAY],
 	    &info->rti_info[RTAX_NETMASK]);
 
+	KASSERT(buf <= (orig_buf + buflen),
+	    ("ipfw: buffer for logdst rtsock is not big enough"));
+
 	info->rti_ifp = args->ifp;
 	rtsock_routemsg_info(RTM_IPFWLOG, info, RT_ALL_FIBS);
 
@@ -704,13 +742,10 @@ ipfw_log(struct ip_fw_chain *chain, struct ip_fw *f, u_int hlen,
 {
 	ipfw_insn *cmd;
 
-	if (f == NULL || hlen == 0)
-		return;
-
-	/* O_LOG is the first action */
-	cmd = ACTION_PTR(f);
-
-	if (cmd->arg1 == IPFW_LOG_DEFAULT) {
+	/* Fallback to default logging if we're missing rule pointer */
+	if (f == NULL ||
+	    /* O_LOG is the first action */
+	    ((cmd = ACTION_PTR(f)) && cmd->arg1 == IPFW_LOG_DEFAULT)) {
 		if (V_fw_verbose == 0) {
 			ipfw_log_ipfw0(args, ip);
 			return;