git: 05b9737f1064 - main - ipfw: make it possible to specify MTU for "unreach needfrag" action

From: Gleb Smirnoff <glebius_at_FreeBSD.org>
Date: Wed, 17 Aug 2022 15:24:52 UTC
The branch main has been updated by glebius:

URL: https://cgit.FreeBSD.org/src/commit/?id=05b9737f106447117fa38644221e19530b326911

commit 05b9737f106447117fa38644221e19530b326911
Author:     Gleb Smirnoff <glebius@FreeBSD.org>
AuthorDate: 2022-08-17 15:24:11 +0000
Commit:     Gleb Smirnoff <glebius@FreeBSD.org>
CommitDate: 2022-08-17 15:24:11 +0000

    ipfw: make it possible to specify MTU for "unreach needfrag" action
    
    Reviewed by:            ae, pauamma
    Differential revision:  https://reviews.freebsd.org/D36140
---
 sbin/ipfw/ipfw.8                 | 10 ++++++++--
 sbin/ipfw/ipfw2.c                | 15 +++++++++++++++
 sys/netpfil/ipfw/ip_fw2.c        | 15 ++++++++++++---
 sys/netpfil/ipfw/ip_fw_sockopt.c |  7 ++++++-
 4 files changed, 41 insertions(+), 6 deletions(-)

diff --git a/sbin/ipfw/ipfw.8 b/sbin/ipfw/ipfw.8
index db8a11525b4d..dbc551042859 100644
--- a/sbin/ipfw/ipfw.8
+++ b/sbin/ipfw/ipfw.8
@@ -1,7 +1,7 @@
 .\"
 .\" $FreeBSD$
 .\"
-.Dd June 4, 2022
+.Dd August 17, 2022
 .Dt IPFW 8
 .Os
 .Sh NAME
@@ -1080,7 +1080,7 @@ Send a copy of packets matching this rule to the
 socket bound to port
 .Ar port .
 The search continues with the next rule.
-.It Cm unreach Ar code
+.It Cm unreach Ar code Op mtu
 Discard packets that match this rule, and try to send an ICMP
 unreachable notice with code
 .Ar code ,
@@ -1093,6 +1093,12 @@ is a number from 0 to 255, or one of these aliases:
 .Cm toshost , filter-prohib , host-precedence
 or
 .Cm precedence-cutoff .
+The
+.Cm needfrag
+code may have an optional
+.Ar mtu
+parameter.
+If specified, the MTU value will be put into generated ICMP packet.
 The search terminates.
 .It Cm unreach6 Ar code
 Discard packets that match this rule, and try to send an ICMPv6
diff --git a/sbin/ipfw/ipfw2.c b/sbin/ipfw/ipfw2.c
index 7d820698a25d..698e0d147669 100644
--- a/sbin/ipfw/ipfw2.c
+++ b/sbin/ipfw/ipfw2.c
@@ -1890,6 +1890,10 @@ print_action_instruction(struct buf_pr *bp, const struct format_opts *fo,
 			bprintf(bp, "abort");
 		else if (cmd->arg1 == ICMP_UNREACH_HOST)
 			bprintf(bp, "reject");
+		else if (cmd->arg1 == ICMP_UNREACH_NEEDFRAG &&
+		    cmd->len == F_INSN_SIZE(ipfw_insn_u16))
+			bprintf(bp, "needfrag %u",
+			    ((const ipfw_insn_u16 *)cmd)->ports[0]);
 		else
 			print_reject_code(bp, cmd->arg1);
 		break;
@@ -3992,6 +3996,17 @@ compile_rule(char *av[], uint32_t *rbuf, int *rbufsize, struct tidx *tstate)
 		NEED1("missing reject code");
 		fill_reject_code(&action->arg1, *av);
 		av++;
+		if (action->arg1 == ICMP_UNREACH_NEEDFRAG && isdigit(**av)) {
+			uint16_t mtu;
+
+			mtu = strtoul(*av, NULL, 10);
+			if (mtu < 68 || mtu >= IP_MAXPACKET)
+				errx(EX_DATAERR, "illegal argument for %s",
+				    *(av - 1));
+			action->len = F_INSN_SIZE(ipfw_insn_u16);
+			((ipfw_insn_u16 *)action)->ports[0] = mtu;
+			av++;
+		}
 		break;
 
 	case TOK_UNREACH6:
diff --git a/sys/netpfil/ipfw/ip_fw2.c b/sys/netpfil/ipfw/ip_fw2.c
index 99d3a9c58cb7..49f64a851e5f 100644
--- a/sys/netpfil/ipfw/ip_fw2.c
+++ b/sys/netpfil/ipfw/ip_fw2.c
@@ -993,8 +993,17 @@ send_reject6(struct ip_fw_args *args, int code, u_int hlen, struct ip6_hdr *ip6)
  * sends a reject message, consuming the mbuf passed as an argument.
  */
 static void
-send_reject(struct ip_fw_args *args, int code, int iplen, struct ip *ip)
+send_reject(struct ip_fw_args *args, const ipfw_insn *cmd, int iplen,
+    struct ip *ip)
 {
+	int code, mtu;
+
+	code = cmd->arg1;
+	if (code == ICMP_UNREACH_NEEDFRAG &&
+	    cmd->len == F_INSN_SIZE(ipfw_insn_u16))
+		mtu = ((const ipfw_insn_u16 *)cmd)->ports[0];
+	else
+		mtu = 0;
 
 #if 0
 	/* XXX When ip is not guaranteed to be at mtod() we will
@@ -1008,7 +1017,7 @@ send_reject(struct ip_fw_args *args, int code, int iplen, struct ip *ip)
 #endif
 	if (code != ICMP_REJECT_RST && code != ICMP_REJECT_ABORT) {
 		/* Send an ICMP unreach */
-		icmp_error(args->m, ICMP_UNREACH, code, 0L, 0);
+		icmp_error(args->m, ICMP_UNREACH, code, 0L, mtu);
 	} else if (code == ICMP_REJECT_RST && args->f_id.proto == IPPROTO_TCP) {
 		struct tcphdr *const tcp =
 		    L3HDR(struct tcphdr, mtod(args->m, struct ip *));
@@ -3042,7 +3051,7 @@ do {								\
 				     is_icmp_query(ICMP(ulp))) &&
 				    !(m->m_flags & (M_BCAST|M_MCAST)) &&
 				    !IN_MULTICAST(ntohl(dst_ip.s_addr))) {
-					send_reject(args, cmd->arg1, iplen, ip);
+					send_reject(args, cmd, iplen, ip);
 					m = args->m;
 				}
 				/* FALLTHROUGH */
diff --git a/sys/netpfil/ipfw/ip_fw_sockopt.c b/sys/netpfil/ipfw/ip_fw_sockopt.c
index e0d80a67827e..8348f8644347 100644
--- a/sys/netpfil/ipfw/ip_fw_sockopt.c
+++ b/sys/netpfil/ipfw/ip_fw_sockopt.c
@@ -2000,12 +2000,17 @@ check_ipfw_rule_body(ipfw_insn *cmd, int cmd_len, struct rule_check_info *ci)
  			goto check_action;
 		case O_CHECK_STATE:
 			ci->object_opcodes++;
+			goto check_size;
+		case O_REJECT:
+			/* "unreach needfrag" has variable len. */
+			if ((cmdlen == F_INSN_SIZE(ipfw_insn) ||
+			    cmdlen == F_INSN_SIZE(ipfw_insn_u16)))
+				goto check_action;
 			/* FALLTHROUGH */
 		case O_FORWARD_MAC: /* XXX not implemented yet */
 		case O_COUNT:
 		case O_ACCEPT:
 		case O_DENY:
-		case O_REJECT:
 		case O_SETDSCP:
 #ifdef INET6
 		case O_UNREACH6: