Netdev List
 help / color / mirror / Atom feed
From: Ricardo Branco <rbranco@suse.de>
To: netdev@vger.kernel.org, rbranco@suse.de
Cc: davem@davemloft.net, edumazet@kernel.org, horms@kernel.org,
	dsahern@kernel.org, idosch@nvidia.com, linux-doc@vger.kernel.org
Subject: [PATCH net-next] ipv4: add ip_process_options sysctl
Date: Mon,  2 Nov 2026 16:59:58 +0100	[thread overview]
Message-ID: <20261102155958.60436-1-rbranco@suse.de> (raw)

Add net.ipv4.ip_process_options to control handling of IP options in
received packets, modelled after FreeBSD's net.inet.ip.process_options:

  0 - ignore options; the packet is handled as if it had none, and
      forwarded packets keep their options unmodified
  1 - process options (default, current behaviour)
  2 - drop packets carrying options and send an ICMP "packet filtered"
      error, as FreeBSD does

Most IP options are obsolete or rarely needed, and are commonly filtered
(RFC 7126), yet the only existing knob, accept_source_route, covers just
the source routing options; record route, timestamp and the rest are
always processed. Netfilter can match and drop such packets, but unlike
TCP options it has no way to strip or neutralise IPv4 options while
still accepting the packet. A sysctl gives a simple switch with the same
semantics as the FreeBSD one, which also eases porting existing
configurations.

The check is done first in ip_rcv_options(), so it takes precedence over
accept_source_route and skips the skb_cow() and option parsing.
Ignoring options is safe because IPCB(skb) is zeroed in ip_rcv_core().

Note that mode 2 also drops packets whose only option is Router Alert,
e.g. IGMP, which can break multicast on routers.

Tested with veth pairs in network namespaces using "ping -R", both for
local delivery and for forwarding through a router: mode 1 records the
route, mode 0 returns no list locally and forwards options without the
router recording its address, mode 2 drops the request and ping reports
"Packet filtered".

Signed-off-by: Ricardo Branco <rbranco@suse.de>
---
 Documentation/networking/ip-sysctl.rst | 15 +++++++++++++++
 include/net/netns/ipv4.h               |  1 +
 net/ipv4/af_inet.c                     |  1 +
 net/ipv4/ip_input.c                    | 14 ++++++++++++++
 net/ipv4/sysctl_net_ipv4.c             |  9 +++++++++
 5 files changed, 40 insertions(+)

diff --git a/Documentation/networking/ip-sysctl.rst b/Documentation/networking/ip-sysctl.rst
index d3523c9064a2..fdbd34384453 100644
--- a/Documentation/networking/ip-sysctl.rst
+++ b/Documentation/networking/ip-sysctl.rst
@@ -172,6 +172,21 @@ ip_forward_update_priority - INTEGER
 	- 0 - Do not update priority.
 	- 1 - Update priority.
 
+ip_process_options - INTEGER
+	Control processing of IPv4 options (source routing, record route,
+	timestamp, etc.) in received packets.
+
+	Possible values:
+
+	- 0 - Ignore options. Packets are accepted or forwarded as if they
+	  carried no options; forwarded packets keep their options unchanged.
+	- 1 - Process options.
+	- 2 - Drop packets carrying options and send an ICMP "packet
+	  filtered" error. Note that this includes packets carrying only
+	  the Router Alert option (e.g. IGMP), which can break multicast.
+
+	Default: 1
+
 route/max_size - INTEGER
 	Maximum number of routes allowed in the kernel.  Increase
 	this when using large numbers of interfaces and/or routes.
diff --git a/include/net/netns/ipv4.h b/include/net/netns/ipv4.h
index cb7f8bf15671..3bbd15be505d 100644
--- a/include/net/netns/ipv4.h
+++ b/include/net/netns/ipv4.h
@@ -162,6 +162,7 @@ struct netns_ipv4 {
 
 	u8 sysctl_ip_default_ttl;
 	u8 sysctl_ip_no_pmtu_disc;
+	u8 sysctl_ip_process_options;
 	u8 sysctl_ip_fwd_update_priority;
 	u8 sysctl_ip_nonlocal_bind;
 	u8 sysctl_ip_autobind_reuse;
diff --git a/net/ipv4/af_inet.c b/net/ipv4/af_inet.c
index 7677b0343abd..2f22e69fa3cd 100644
--- a/net/ipv4/af_inet.c
+++ b/net/ipv4/af_inet.c
@@ -1798,6 +1798,7 @@ static __net_init int inet_init_net(struct net *net)
 	 */
 	net->ipv4.sysctl_ip_default_ttl = IPDEFTTL;
 	net->ipv4.sysctl_ip_fwd_update_priority = 1;
+	net->ipv4.sysctl_ip_process_options = 1;
 	net->ipv4.sysctl_ip_dynaddr = 0;
 	net->ipv4.sysctl_ip_early_demux = 1;
 	net->ipv4.sysctl_udp_early_demux = 1;
diff --git a/net/ipv4/ip_input.c b/net/ipv4/ip_input.c
index 9860178752b8..d1616ecddfa8 100644
--- a/net/ipv4/ip_input.c
+++ b/net/ipv4/ip_input.c
@@ -271,6 +271,20 @@ ip_rcv_options(struct sk_buff *skb, struct net_device *dev)
 	const struct iphdr *iph;
 	struct ip_options *opt;
 
+	switch (READ_ONCE(dev_net(dev)->ipv4.sysctl_ip_process_options)) {
+	case 0:
+		/* Ignore options: IPCB(skb)->opt was zeroed in ip_rcv_core(),
+		 * so the packet is handled as if it carried no options.
+		 */
+		return SKB_NOT_DROPPED_YET;
+	case 2:
+		__IP_INC_STATS(dev_net(dev), IPSTATS_MIB_INHDRERRORS);
+		icmp_send(skb, ICMP_DEST_UNREACH, ICMP_PKT_FILTERED, 0);
+		return SKB_DROP_REASON_IP_INHDR;
+	default:	/* 1: process options */
+		break;
+	}
+
 	/* It looks as overkill, because not all
 	   IP options require packet mangling.
 	   But it is the easiest for now, especially taking
diff --git a/net/ipv4/sysctl_net_ipv4.c b/net/ipv4/sysctl_net_ipv4.c
index bf193e7ec516..9bdf5ad23614 100644
--- a/net/ipv4/sysctl_net_ipv4.c
+++ b/net/ipv4/sysctl_net_ipv4.c
@@ -847,6 +847,15 @@ static const struct ctl_table ipv4_net_table[] = {
 		.mode		= 0644,
 		.proc_handler	= proc_dou8vec_minmax,
 	},
+	{
+		.procname	= "ip_process_options",
+		.data		= &init_net.ipv4.sysctl_ip_process_options,
+		.maxlen		= sizeof(u8),
+		.mode		= 0644,
+		.proc_handler	= proc_dou8vec_minmax,
+		.extra1		= SYSCTL_ZERO,
+		.extra2		= SYSCTL_TWO,
+	},
 	{
 		.procname	= "ip_forward_use_pmtu",
 		.data		= &init_net.ipv4.sysctl_ip_fwd_use_pmtu,
-- 
2.56.0


             reply	other threads:[~2026-11-02 16:00 UTC|newest]

Thread overview: 2+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-11-02 15:59 Ricardo Branco [this message]
2026-11-03 12:19 ` [PATCH net-next] ipv4: add ip_process_options sysctl Ido Schimmel

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=20261102155958.60436-1-rbranco@suse.de \
    --to=rbranco@suse.de \
    --cc=davem@davemloft.net \
    --cc=dsahern@kernel.org \
    --cc=edumazet@kernel.org \
    --cc=horms@kernel.org \
    --cc=idosch@nvidia.com \
    --cc=linux-doc@vger.kernel.org \
    --cc=netdev@vger.kernel.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