Skip to contentSkip to conversation

Deprecate Legacy IP

PATCH Applied David Woodhouse 10 participants 25 messages Apr 1 – Apr 2

Subsystems (13)
lore
Share & tools
lore
#0 lore raw

cover Deprecate Legacy IP

RFC1883, the IPv6 standard, was published in the final decade of the 1900s. That's closer in time to the Apollo 11 moon landing than it was to today.

Even our esteemed Maddog has worked with computers for longer in the IPv6 era, than he ever did before it.

Yet Linux still can't even be *built* with only IPv6 support and without support for Legacy IP. This long overdue patch series fixes that, and immediately marks Legacy IP for deprecation.

It also cleans up a few tautological "INET && IPV6" and "INET || IPV6" checks, since IPV6 (and now LEGACY_IP) cannot be selected without the overall CONFIG_INET option.

For now, we only add a warning when a process *listens* on a Legacy IP socket (since you can listen on IPv6 and still accept connections which have come through a timewarp from the 20th century. Adding warnings for making outbound connections or *accepting* on Legacy IP can come later.

'I would be happy if "Legacy IP" ceased to be the "industry standard"
and IPv6 be the default, even if I had to beat IPv6 into the head of every single network administrator's head with a shovel.' said Jon
'maddog' Hall, ancient supporter of Free and Open Source Software.

David Woodhouse (6):
net: Simplify tautological CONFIG_INET/CONFIG_IPV6 guards
net: Add CONFIG_LEGACY_IP option
net: Guard Legacy IP entry points with CONFIG_LEGACY_IP
net: Make IPv4-only Kconfig options depend on LEGACY_IP
net: Change CONFIG_INET to CONFIG_LEGACY_IP for IPv4-only code
net: Warn when processes listen on AF_INET sockets

 .../net/ethernet/mellanox/mlx5/core/en/tc_tun.c    |  6 ++--
 .../net/ethernet/mellanox/mlx5/core/en/tc_tun.h    |  2 +-
 .../ethernet/mellanox/mlx5/core/en/tc_tun_encap.c  |  2 +-
 .../ethernet/netronome/nfp/flower/tunnel_conf.c    |  2 +-
 include/linux/indirect_call_wrapper.h              |  4 ++-
 net/bridge/br_arp_nd_proxy.c                       |  2 +-
 net/bridge/br_private.h                            |  8 +++++
 net/core/filter.c                                  | 12 +++----
 net/core/secure_seq.c                              |  2 +-
 net/core/sock.c                                    |  2 +-
 net/ipv4/Kconfig                                   | 37 ++++++++++++++++++++++
 net/ipv4/af_inet.c                                 | 23 +++++++++++---
 net/ipv4/devinet.c                                 |  2 ++
 net/ipv4/route.c                                   |  1 -
 net/ipv4/tcp_ipv4.c                                | 30 ++++++++++--------
 net/mac80211/main.c                                | 10 +++---
 net/netfilter/nfnetlink_queue.c                    |  2 +-
 17 files changed, 105 insertions(+), 42 deletions(-)
#1 lore raw

PATCH 1/6 net: Simplify tautological CONFIG_INET/CONFIG_IPV6 guards

From: David Woodhouse <[email protected]>

CONFIG_IPV6 depends on CONFIG_INET, so:
- 'IS_ENABLED(CONFIG_INET) && IS_ENABLED(CONFIG_IPV6)' simplifies to just 'IS_ENABLED(CONFIG_IPV6)'
- 'IS_ENABLED(CONFIG_INET) || IS_ENABLED(CONFIG_IPV6)' simplifies to just 'IS_ENABLED(CONFIG_INET)'

No functional change.

---
 drivers/net/ethernet/mellanox/mlx5/core/en/tc_tun.c       | 6 +++---
 drivers/net/ethernet/mellanox/mlx5/core/en/tc_tun.h       | 2 +-
 drivers/net/ethernet/mellanox/mlx5/core/en/tc_tun_encap.c | 2 +-
 drivers/net/ethernet/netronome/nfp/flower/tunnel_conf.c   | 2 +-
 net/core/filter.c                                         | 2 +-
 net/core/secure_seq.c                                     | 2 +-
 6 files changed, 8 insertions(+), 8 deletions(-)
drivers/net/ethernet/mellanox/mlx5/core/en/tc_tun.c +3 -3
index a14f216048cd..889dc1785772 100644
@@ -439,7 +439,7 @@ int mlx5e_tc_tun_update_header_ipv4(struct mlx5e_priv *priv,
 	return err;
 }
 
-#if IS_ENABLED(CONFIG_INET) && IS_ENABLED(CONFIG_IPV6)
+#if IS_ENABLED(CONFIG_IPV6)
 static int mlx5e_route_lookup_ipv6_get(struct mlx5e_priv *priv,
 				       struct net_device *dev,
 				       struct mlx5e_tc_tun_route_attr *attr)
@@ -727,7 +727,7 @@ int mlx5e_tc_tun_route_lookup(struct mlx5e_priv *priv,
 		attr.fl.fl4.daddr = esw_attr->rx_tun_attr->src_ip.v4;
 		err = mlx5e_route_lookup_ipv4_get(priv, filter_dev, &attr);
 	}
-#if IS_ENABLED(CONFIG_INET) && IS_ENABLED(CONFIG_IPV6)
+#if IS_ENABLED(CONFIG_IPV6)
 	else if (flow_attr->tun_ip_version == 6) {
 		/* Addresses are swapped for decap */
 		attr.fl.fl6.saddr = esw_attr->rx_tun_attr->dst_ip.v6;
@@ -762,7 +762,7 @@ int mlx5e_tc_tun_route_lookup(struct mlx5e_priv *priv,
 out:
 	if (flow_attr->tun_ip_version == 4)
 		mlx5e_route_lookup_ipv4_put(&attr);
-#if IS_ENABLED(CONFIG_INET) && IS_ENABLED(CONFIG_IPV6)
+#if IS_ENABLED(CONFIG_IPV6)
 	else if (flow_attr->tun_ip_version == 6)
 		mlx5e_route_lookup_ipv6_put(&attr);
 #endif
drivers/net/ethernet/mellanox/mlx5/core/en/tc_tun.h +1 -1
index 6873c1201803..f3c0e2d0f388 100644
@@ -73,7 +73,7 @@ int mlx5e_tc_tun_update_header_ipv4(struct mlx5e_priv *priv,
 				    struct net_device *mirred_dev,
 				    struct mlx5e_encap_entry *e);
 
-#if IS_ENABLED(CONFIG_INET) && IS_ENABLED(CONFIG_IPV6)
+#if IS_ENABLED(CONFIG_IPV6)
 int mlx5e_tc_tun_create_header_ipv6(struct mlx5e_priv *priv,
 				    struct net_device *mirred_dev,
 				    struct mlx5e_encap_entry *e);
drivers/net/ethernet/mellanox/mlx5/core/en/tc_tun_encap.c +1 -1
index bfd401bee9e8..b2973e8a7df8 100644
@@ -104,7 +104,7 @@ int mlx5e_tc_set_attr_rx_tun(struct mlx5e_tc_flow *flow,
 		if (!tun_attr->dst_ip.v4 || !tun_attr->src_ip.v4)
 			return 0;
 	}
-#if IS_ENABLED(CONFIG_INET) && IS_ENABLED(CONFIG_IPV6)
+#if IS_ENABLED(CONFIG_IPV6)
 	else if (ip_version == 6) {
 		int ipv6_size = MLX5_FLD_SZ_BYTES(ipv6_layout, ipv6);
 
drivers/net/ethernet/netronome/nfp/flower/tunnel_conf.c +1 -1
index 0cef0e2b85d0..5eb47e1a8d5e 100644
@@ -814,7 +814,7 @@ void nfp_tunnel_request_route_v6(struct nfp_app *app, struct sk_buff *skb)
 	flow.daddr = payload->ipv6_addr;
 	flow.flowi6_proto = IPPROTO_UDP;
 
-#if IS_ENABLED(CONFIG_INET) && IS_ENABLED(CONFIG_IPV6)
+#if IS_ENABLED(CONFIG_IPV6)
 	dst = ipv6_stub->ipv6_dst_lookup_flow(dev_net(netdev), NULL, &flow,
 					      NULL);
 	if (IS_ERR(dst))
net/core/filter.c +1 -1
index 78b548158fb0..ad71ceefcb5e 100644
@@ -6083,7 +6083,7 @@ static const struct bpf_func_proto bpf_skb_get_xfrm_state_proto = {
 };
 #endif
 
-#if IS_ENABLED(CONFIG_INET) || IS_ENABLED(CONFIG_IPV6)
+#if IS_ENABLED(CONFIG_INET)
 static int bpf_fib_set_fwd_params(struct bpf_fib_lookup *params, u32 mtu)
 {
 	params->h_vlan_TCI = 0;
net/core/secure_seq.c +1 -1
index 6a6f2cda5aae..4de049635db0 100644
@@ -15,7 +15,7 @@
 #include <linux/siphash.h>
 #include <net/secure_seq.h>
 
-#if IS_ENABLED(CONFIG_IPV6) || IS_ENABLED(CONFIG_INET)
+#if IS_ENABLED(CONFIG_INET)
 #include <linux/in6.h>
 #include <net/tcp.h>
 
Signature · 1 line
2.51.0
#2 lore raw

PATCH 2/6 net: Add CONFIG_LEGACY_IP option

In reply to David Woodhouse

From: David Woodhouse <[email protected]>

Add a new CONFIG_LEGACY_IP boolean option under CONFIG_INET that will gate Legacy IP functionality. When disabled, the kernel will not register the AF_INET socket family, IPv4 packet handler, ARP, or IPv4 routing, while the shared TCP/UDP/INET socket infrastructure remains available for IPv6.

This is the first step toward making Legacy IP optional. The option defaults to y and currently has no effect — subsequent patches will use it to guard IPv4 entry points.

---
 net/ipv4/Kconfig | 11 +++++++++++
 1 file changed, 11 insertions(+)
net/ipv4/Kconfig +11 -0
index df922f9f5289..aef2c5349e62 100644
@@ -2,6 +2,17 @@
 #
 # IP configuration
 #
+config LEGACY_IP
+	bool "The IPv4 protocol (Legacy IP)"
+	help
+	  Support for IP version 4 (IPv4).
+
+	  Legacy IP is the protocol used by the early ARPANET, before IPv6
+	  was standardised in the final decade of the 1900s. It should only
+	  be necessary these days to interoperate with legacy networks.
+
+	  If unsure, say N.
+
 config IP_MULTICAST
 	bool "IP: multicasting"
 	help
Signature · 1 line
2.51.0
#3 lore raw

Re: [PATCH 2/6] net: Add CONFIG_LEGACY_IP option

The commit message says "The option defaults to y", but the Kconfig entry has no "default y" line. A bool without an explicit default is n in Kconfig, so LEGACY_IP will be disabled on any fresh…
Quoted 3 lines

This is the first step toward making Legacy IP optional. The option defaults to y and currently has no effect — subsequent patches will use it to guard IPv4 entry points.

The commit message says "The option defaults to y", but the Kconfig entry has no "default y" line. A bool without an explicit default is n in Kconfig, so LEGACY_IP will be disabled on any fresh config built with make defconfig or make olddefconfig.

Later patches in this series (be2f051150f9, f04a0717c27d) add "depends on LEGACY_IP" to all IPv4-specific Kconfig symbols and guard IPv4 entry points in C code. With LEGACY_IP defaulting to n, those patches will silently disable IPv4 networking for any newly generated kernel configuration.

Should the Kconfig entry include "default y" to match the stated intent?

Quoted 8 lines
net/ipv4/Kconfig +0 -0
index df922f9f5289..aef2c5349e62 100644
@@ -2,6 +2,17 @@
 #
 # IP configuration
 #

net/ipv4/Kconfig line 14

Quoted 10 lines
net/ipv4/Kconfig +10 -0
+config LEGACY_IP
+	bool "The IPv4 protocol (Legacy IP)"
+	help
+	  Support for IP version 4 (IPv4).
+
+	  Legacy IP is the protocol used by the early ARPANET, before IPv6
+	  was standardised in the final decade of the 1900s. It should only
+	  be necessary these days to interoperate with legacy networks.
+
+	  If unsure, say N.

The help text says "If unsure, say N." Combined with the missing default, this guides users to disable IPv4. If the intent is a backward-compatible introduction where IPv4 stays enabled unless explicitly turned off, this should probably be "If unsure, say Y."

---
AI reviewed your patch. Please fix the bug or email reply why it's not a bug.
See: https://github.com/kernel-patches/vmtest/blob/master/ci/claude/README.md

CI run summary: https://github.com/kernel-patches/bpf/actions/runs/23838411859

#4 lore raw

PATCH 3/6 net: Guard Legacy IP entry points with CONFIG_LEGACY_IP

In reply to David Woodhouse

From: David Woodhouse <[email protected]>

Wrap the IPv4-specific registrations in inet_init() with CONFIG_LEGACY_IP guards. When LEGACY_IP is disabled, the kernel will not:
- Register the AF_INET socket family
- Register the ETH_P_IP packet handler (ip_rcv)
- Initialize ARP, ICMP, IGMP, or IPv4 routing
- Register IPv4 protocol handlers (TCP/UDP/ICMP over IPv4)
- Initialize IPv4 multicast routing, proc entries, or fragmentation

The shared INET infrastructure (tcp_prot, udp_prot, tcp_init, etc.) remains initialized for use by IPv6.

Also update INDIRECT_CALL_INET to not use ip_rcv/ip_list_rcv as direct call targets when LEGACY_IP is disabled, avoiding a link-time reference to functions that will eventually be compiled out.

---
 include/linux/indirect_call_wrapper.h |  4 +++-
 net/ipv4/af_inet.c                    | 20 +++++++++++++-----
 net/ipv4/devinet.c                    |  2 ++
 net/ipv4/route.c                      |  1 -
 net/ipv4/tcp_ipv4.c                   | 30 ++++++++++++++-------------
 5 files changed, 36 insertions(+), 21 deletions(-)
include/linux/indirect_call_wrapper.h +3 -1
index dc272b514a01..25a3873da462 100644
@@ -57,9 +57,11 @@
  * builtin, this macro simplify dealing with indirect calls with only ipv4/ipv6
  * alternatives
  */
-#if IS_BUILTIN(CONFIG_IPV6)
+#if IS_BUILTIN(CONFIG_IPV6) && IS_ENABLED(CONFIG_LEGACY_IP)
 #define INDIRECT_CALL_INET(f, f2, f1, ...) \
 	INDIRECT_CALL_2(f, f2, f1, __VA_ARGS__)
+#elif IS_BUILTIN(CONFIG_IPV6)
+#define INDIRECT_CALL_INET(f, f2, f1, ...) INDIRECT_CALL_1(f, f2, __VA_ARGS__)
 #elif IS_ENABLED(CONFIG_INET)
 #define INDIRECT_CALL_INET(f, f2, f1, ...) INDIRECT_CALL_1(f, f1, __VA_ARGS__)
 #else
net/ipv4/af_inet.c +15 -5
index c7731e300a44..dc358faa1647 100644
@@ -1922,7 +1922,15 @@ static int __init inet_init(void)
 	/*
 	 *	Tell SOCKET that we are alive...
 	 */
+	/* Initialize the socket-side protocol switch tables. */
+	for (r = &inetsw[0]; r < &inetsw[SOCK_MAX]; ++r)
+		INIT_LIST_HEAD(r);
+
+#ifdef CONFIG_XFRM
+	xfrm_init();
+#endif
 
+#ifdef CONFIG_LEGACY_IP
 	(void)sock_register(&inet_family_ops);
 
 #ifdef CONFIG_SYSCTL
@@ -1957,10 +1965,6 @@ static int __init inet_init(void)
 		pr_crit("%s: Cannot add IGMP protocol\n", __func__);
 #endif
 
-	/* Register the socket-side information for inet_create. */
-	for (r = &inetsw[0]; r < &inetsw[SOCK_MAX]; ++r)
-		INIT_LIST_HEAD(r);
-
 	for (q = inetsw_array; q < &inetsw_array[INETSW_ARRAY_LEN]; ++q)
 		inet_register_protosw(q);
 
@@ -1975,6 +1979,7 @@ static int __init inet_init(void)
 	 */
 
 	ip_init();
+#endif /* CONFIG_LEGACY_IP */
 
 	/* Initialise per-cpu ipv4 mibs */
 	if (init_ipv4_mibs())
@@ -1987,7 +1992,8 @@ static int __init inet_init(void)
 	udp_init();
 
 	/* Add UDP-Lite (RFC 3828) */
-	udplite4_register();
+	if (IS_ENABLED(CONFIG_LEGACY_IP))
+		udplite4_register();
 
 	raw_init();
 
@@ -1997,6 +2003,7 @@ static int __init inet_init(void)
 	 *	Set the ICMP layer up
 	 */
 
+#ifdef CONFIG_LEGACY_IP
 	if (icmp_init() < 0)
 		panic("Failed to create the ICMP control socket.\n");
 
@@ -2007,10 +2014,12 @@ static int __init inet_init(void)
 	if (ip_mr_init())
 		pr_crit("%s: Cannot init ipv4 mroute\n", __func__);
 #endif
+#endif /* CONFIG_LEGACY_IP */
 
 	if (init_inet_pernet_ops())
 		pr_crit("%s: Cannot init ipv4 inet pernet ops\n", __func__);
 
+#ifdef CONFIG_LEGACY_IP
 	ipv4_proc_init();
 
 	ipfrag_init();
@@ -2018,6 +2027,7 @@ static int __init inet_init(void)
 	dev_add_pack(&ip_packet_type);
 
 	ip_tunnel_core_init();
+#endif /* CONFIG_LEGACY_IP */
 
 	rc = 0;
 out:
net/ipv4/devinet.c +2 -0
index 537bb6c315d2..9b9db10e5db2 100644
@@ -348,7 +348,9 @@ static int __init inet_blackhole_dev_init(void)
 
 	return PTR_ERR_OR_ZERO(in_dev);
 }
+#ifdef CONFIG_LEGACY_IP
 late_initcall(inet_blackhole_dev_init);
+#endif
 
 int inet_addr_onlink(struct in_device *in_dev, __be32 a, __be32 b)
 {
net/ipv4/route.c +0 -1
index 463236e0dc2d..125614f552c7 100644
@@ -3773,7 +3773,6 @@ int __init ip_rt_init(void)
 	if (ip_rt_proc_init())
 		pr_err("Unable to create route proc files\n");
 #ifdef CONFIG_XFRM
-	xfrm_init();
 	xfrm4_init();
 #endif
 	rtnl_register_many(ip_rt_rtnl_msg_handlers);
net/ipv4/tcp_ipv4.c +16 -14
index c7b2463c2e25..7660bd45aac7 100644
@@ -3717,25 +3717,27 @@ static void __init bpf_iter_register(void)
 
 void __init tcp_v4_init(void)
 {
-	int cpu, res;
+	if (IS_ENABLED(CONFIG_LEGACY_IP)) {
+		int cpu, res;
 
-	for_each_possible_cpu(cpu) {
-		struct sock *sk;
+		for_each_possible_cpu(cpu) {
+			struct sock *sk;
 
-		res = inet_ctl_sock_create(&sk, PF_INET, SOCK_RAW,
-					   IPPROTO_TCP, &init_net);
-		if (res)
-			panic("Failed to create the TCP control socket.\n");
-		sock_set_flag(sk, SOCK_USE_WRITE_QUEUE);
+			res = inet_ctl_sock_create(&sk, PF_INET, SOCK_RAW,
+						   IPPROTO_TCP, &init_net);
+			if (res)
+				panic("Failed to create the TCP control socket.\n");
+			sock_set_flag(sk, SOCK_USE_WRITE_QUEUE);
 
-		/* Please enforce IP_DF and IPID==0 for RST and
-		 * ACK sent in SYN-RECV and TIME-WAIT state.
-		 */
-		inet_sk(sk)->pmtudisc = IP_PMTUDISC_DO;
+			/* Please enforce IP_DF and IPID==0 for RST and
+			 * ACK sent in SYN-RECV and TIME-WAIT state.
+			 */
+			inet_sk(sk)->pmtudisc = IP_PMTUDISC_DO;
 
-		sk->sk_clockid = CLOCK_MONOTONIC;
+			sk->sk_clockid = CLOCK_MONOTONIC;
 
-		per_cpu(ipv4_tcp_sk.sock, cpu) = sk;
+			per_cpu(ipv4_tcp_sk.sock, cpu) = sk;
+		}
 	}
 	if (register_pernet_subsys(&tcp_sk_ops))
 		panic("Failed to create the TCP control socket.\n");
Signature · 1 line
2.51.0
#5 lore raw

Re: [PATCH 3/6] net: Guard Legacy IP entry points with CONFIG_LEGACY_IP

Quoted 15 lines · David Woodhouse

From: David Woodhouse <[email protected]>

Wrap the IPv4-specific registrations in inet_init() with CONFIG_LEGACY_IP guards. When LEGACY_IP is disabled, the kernel will not:
- Register the AF_INET socket family
- Register the ETH_P_IP packet handler (ip_rcv)
- Initialize ARP, ICMP, IGMP, or IPv4 routing
- Register IPv4 protocol handlers (TCP/UDP/ICMP over IPv4)
- Initialize IPv4 multicast routing, proc entries, or fragmentation

The shared INET infrastructure (tcp_prot, udp_prot, tcp_init, etc.) remains initialized for use by IPv6.

...

net/ipv4/af_inet.c static int __init inet_init(void) · line 1996

Quoted 5 lines
        /* Add UDP-Lite (RFC 3828) */
net/ipv4/af_inet.c +2 -1
-       udplite4_register();
+       if (IS_ENABLED(CONFIG_LEGACY_IP))
+               udplite4_register();

udplite has been removed in net-next.

I would think your patch series is net-next material ?

#6 lore raw

Re: [PATCH 3/6] net: Guard Legacy IP entry points with CONFIG_LEGACY_IP

Quoted 10 lines · Eric Dumazet
Quoted 6 lines

         /* Add UDP-Lite (RFC 3828) */
-       udplite4_register();
+       if (IS_ENABLED(CONFIG_LEGACY_IP))
+               udplite4_register();

udplite has been removed in net-next.

I would think your patch series is net-next material ?

A more conservative variant of the patch series on another day of the year, sure. It also probably wants to land after
https://lore.kernel.org/lkml/[email protected]/
turns CONFIG_IPV6 into a boolean.

I'll need to take a closer look at CONFIG_INET too; it ends up being possible to configure with INET && !LEGACY_IP && !IPV6 which isn't a combination that makes sense (and I obviously didn't test).

As discussed, some of this series *is* realistic for another day, and I'll happily work on whatever direction we think makes sense.

#7 lore raw

Re: [PATCH 3/6] net: Guard Legacy IP entry points with CONFIG_LEGACY_IP

In reply to David Woodhouse
Moving xfrm_init() here places it before sock_register(), but xfrm_init() calls xfrm_nat_keepalive_init(AF_INET) which creates per-cpu PF_INET sockets via this chain:
Quoted 7 lines
net/ipv4/af_inet.c +0 -0
@@ -1922,7 +1922,15 @@ static int __init inet_init(void)
 	/*
 	 *	Tell SOCKET that we are alive...
 	 */

net/ipv4/af_inet.c static int __init inet_init(void) · line 1934

Quoted 10 lines
net/ipv4/af_inet.c +8 -0
+	/* Initialize the socket-side protocol switch tables. */
+	for (r = &inetsw[0]; r < &inetsw[SOCK_MAX]; ++r)
+		INIT_LIST_HEAD(r);
+
+#ifdef CONFIG_XFRM
+	xfrm_init();
+#endif
 
+#ifdef CONFIG_LEGACY_IP
 	(void)sock_register(&inet_family_ops);

Moving xfrm_init() here places it before sock_register(), but xfrm_init() calls xfrm_nat_keepalive_init(AF_INET) which creates per-cpu PF_INET sockets via this chain:

    xfrm_init()
      xfrm_nat_keepalive_init(AF_INET)
        nat_keepalive_sk_init(&nat_keepalive_sk_ipv4, PF_INET)
          inet_ctl_sock_create(&sk, PF_INET, ...)
            sock_create_kern()
              __sock_create()
                pf = rcu_dereference(net_families[PF_INET]);
                if (!pf) -> returns -EAFNOSUPPORT

Since sock_register(&inet_family_ops) has not been called yet, net_families[PF_INET] is still NULL at this point. This causes xfrm_nat_keepalive_init(AF_INET) to silently fail (the return value is not checked by xfrm_init()), leaving the IPv4 XFRM NAT keepalive sockets uninitialized.

This affects the default CONFIG_LEGACY_IP=y configuration as well, not just IPv6-only builds. Would it be better to leave xfrm_init() after sock_register(), or move the xfrm_nat_keepalive_init(AF_INET) call into xfrm4_init() where it would run after IPv4 socket registration is complete?

Previously, xfrm_init() was called from ip_rt_init() which ran well after sock_register():

    inet_init()
      sock_register(&inet_family_ops)   <-- AF_INET available
      ...
      ip_init()
        ip_rt_init()
          xfrm_init()                   <-- worked fine

---
AI reviewed your patch. Please fix the bug or email reply why it's not a bug.
See: https://github.com/kernel-patches/vmtest/blob/master/ci/claude/README.md

CI run summary: https://github.com/kernel-patches/bpf/actions/runs/23838411859

#8 lore raw

PATCH 4/6 net: Make IPv4-only Kconfig options depend on LEGACY_IP

In reply to David Woodhouse

From: David Woodhouse <[email protected]>

Add 'depends on LEGACY_IP' to Kconfig options that are purely IPv4-specific, so they are automatically disabled when LEGACY_IP=n.

IPv4-only options gated:
- IP_MULTICAST, IP_ADVANCED_ROUTER, IP_FIB_TRIE_STATS, IP_MULTIPLE_TABLES, IP_ROUTE_MULTIPATH, IP_ROUTE_VERBOSE, IP_ROUTE_CLASSID — IPv4 routing features
- IP_PNP (and children DHCP/BOOTP/RARP) — IPv4 autoconfiguration
- NET_IPIP, NET_IPGRE_DEMUX, NET_IPGRE, NET_IPGRE_BROADCAST — IPv4 tunnels
- IP_MROUTE_COMMON, IP_MROUTE, IP_MROUTE_MULTIPLE_TABLES, IP_PIMSM_V1, IP_PIMSM_V2 — IPv4 multicast routing
- NET_IPVTI, NET_FOU_IP_TUNNELS — IPv4 VTI and FOU tunnels
- INET_AH, INET_ESP, INET_ESP_OFFLOAD, INET_ESPINTCP, INET_IPCOMP — IPv4 IPsec (IPv6 has separate INET6_* options)
- INET_XFRM_TUNNEL, INET_TUNNEL — IPv4 tunnel infrastructure

Options intentionally left ungated (shared with IPv6):
- SYN_COOKIES, NET_IP_TUNNEL, NET_UDP_TUNNEL, NET_FOU
- INET_TABLE_PERTURB_ORDER, INET_DIAG and children
- TCP_CONG_*, DEFAULT_TCP_CONG, TCP_SIGPOOL, TCP_AO, TCP_MD5SIG

---
 net/ipv4/Kconfig | 26 ++++++++++++++++++++++++++
 1 file changed, 26 insertions(+)
net/ipv4/Kconfig +26 -0
index aef2c5349e62..03b5ba75c3cf 100644
@@ -15,6 +15,7 @@ config LEGACY_IP
 
 config IP_MULTICAST
 	bool "IP: multicasting"
+	depends on LEGACY_IP
 	help
 	  This is code for addressing several networked computers at once,
 	  enlarging your kernel by about 2 KB. You need multicasting if you
@@ -25,6 +26,7 @@ config IP_MULTICAST
 
 config IP_ADVANCED_ROUTER
 	bool "IP: advanced router"
+	depends on LEGACY_IP
 	help
 	  If you intend to run your Linux box mostly as a router, i.e. as a
 	  computer that forwards and redistributes network packets, say Y; you
@@ -66,6 +68,7 @@ config IP_ADVANCED_ROUTER
 
 config IP_FIB_TRIE_STATS
 	bool "FIB TRIE statistics"
+	depends on LEGACY_IP
 	depends on IP_ADVANCED_ROUTER
 	help
 	  Keep track of statistics on structure of FIB TRIE table.
@@ -73,6 +76,7 @@ config IP_FIB_TRIE_STATS
 
 config IP_MULTIPLE_TABLES
 	bool "IP: policy routing"
+	depends on LEGACY_IP
 	depends on IP_ADVANCED_ROUTER
 	select FIB_RULES
 	help
@@ -90,6 +94,7 @@ config IP_MULTIPLE_TABLES
 
 config IP_ROUTE_MULTIPATH
 	bool "IP: equal cost multipath"
+	depends on LEGACY_IP
 	depends on IP_ADVANCED_ROUTER
 	help
 	  Normally, the routing tables specify a single action to be taken in
@@ -102,6 +107,7 @@ config IP_ROUTE_MULTIPATH
 
 config IP_ROUTE_VERBOSE
 	bool "IP: verbose route monitoring"
+	depends on LEGACY_IP
 	depends on IP_ADVANCED_ROUTER
 	help
 	  If you say Y here, which is recommended, then the kernel will print
@@ -113,9 +119,11 @@ config IP_ROUTE_VERBOSE
 
 config IP_ROUTE_CLASSID
 	bool
+	depends on LEGACY_IP
 
 config IP_PNP
 	bool "IP: kernel level autoconfiguration"
+	depends on LEGACY_IP
 	help
 	  This enables automatic configuration of IP addresses of devices and
 	  of the routing table during kernel boot, based on either information
@@ -172,6 +180,7 @@ config IP_PNP_RARP
 
 config NET_IPIP
 	tristate "IP: tunneling"
+	depends on LEGACY_IP
 	select INET_TUNNEL
 	select NET_IP_TUNNEL
 	help
@@ -190,6 +199,7 @@ config NET_IPIP
 
 config NET_IPGRE_DEMUX
 	tristate "IP: GRE demultiplexer"
+	depends on LEGACY_IP
 	help
 	  This is helper module to demultiplex GRE packets on GRE version field criteria.
 	  Required by ip_gre and pptp modules.
@@ -202,6 +212,7 @@ config NET_IP_TUNNEL
 
 config NET_IPGRE
 	tristate "IP: GRE tunnels over IP"
+	depends on LEGACY_IP
 	depends on (IPV6 || IPV6=n) && NET_IPGRE_DEMUX
 	select NET_IP_TUNNEL
 	help
@@ -217,6 +228,7 @@ config NET_IPGRE
 
 config NET_IPGRE_BROADCAST
 	bool "IP: broadcast GRE over IP"
+	depends on LEGACY_IP
 	depends on IP_MULTICAST && NET_IPGRE
 	help
 	  One application of GRE/IP is to construct a broadcast WAN (Wide Area
@@ -226,10 +238,12 @@ config NET_IPGRE_BROADCAST
 
 config IP_MROUTE_COMMON
 	bool
+	depends on LEGACY_IP
 	depends on IP_MROUTE || IPV6_MROUTE
 
 config IP_MROUTE
 	bool "IP: multicast routing"
+	depends on LEGACY_IP
 	depends on IP_MULTICAST
 	select IP_MROUTE_COMMON
 	help
@@ -242,6 +256,7 @@ config IP_MROUTE
 
 config IP_MROUTE_MULTIPLE_TABLES
 	bool "IP: multicast policy routing"
+	depends on LEGACY_IP
 	depends on IP_MROUTE && IP_ADVANCED_ROUTER
 	select FIB_RULES
 	help
@@ -256,6 +271,7 @@ config IP_MROUTE_MULTIPLE_TABLES
 
 config IP_PIMSM_V1
 	bool "IP: PIM-SM version 1 support"
+	depends on LEGACY_IP
 	depends on IP_MROUTE
 	help
 	  Kernel side support for Sparse Mode PIM (Protocol Independent
@@ -269,6 +285,7 @@ config IP_PIMSM_V1
 
 config IP_PIMSM_V2
 	bool "IP: PIM-SM version 2 support"
+	depends on LEGACY_IP
 	depends on IP_MROUTE
 	help
 	  Kernel side support for Sparse Mode PIM version 2. In order to use
@@ -314,6 +331,7 @@ config SYN_COOKIES
 
 config NET_IPVTI
 	tristate "Virtual (secure) IP: tunneling"
+	depends on LEGACY_IP
 	depends on IPV6 || IPV6=n
 	select INET_TUNNEL
 	select NET_IP_TUNNEL
@@ -341,6 +359,7 @@ config NET_FOU
 
 config NET_FOU_IP_TUNNELS
 	bool "IP: FOU encapsulation of IP tunnels"
+	depends on LEGACY_IP
 	depends on NET_IPIP || NET_IPGRE || IPV6_SIT
 	select NET_FOU
 	help
@@ -350,6 +369,7 @@ config NET_FOU_IP_TUNNELS
 
 config INET_AH
 	tristate "IP: AH transformation"
+	depends on LEGACY_IP
 	select XFRM_AH
 	help
 	  Support for IPsec AH (Authentication Header).
@@ -365,6 +385,7 @@ config INET_AH
 
 config INET_ESP
 	tristate "IP: ESP transformation"
+	depends on LEGACY_IP
 	select XFRM_ESP
 	help
 	  Support for IPsec ESP (Encapsulating Security Payload).
@@ -380,6 +401,7 @@ config INET_ESP
 
 config INET_ESP_OFFLOAD
 	tristate "IP: ESP transformation offload"
+	depends on LEGACY_IP
 	depends on INET_ESP
 	select XFRM_OFFLOAD
 	default n
@@ -393,6 +415,7 @@ config INET_ESP_OFFLOAD
 
 config INET_ESPINTCP
 	bool "IP: ESP in TCP encapsulation (RFC 8229)"
+	depends on LEGACY_IP
 	depends on XFRM && INET_ESP
 	select STREAM_PARSER
 	select NET_SOCK_MSG
@@ -405,6 +428,7 @@ config INET_ESPINTCP
 
 config INET_IPCOMP
 	tristate "IP: IPComp transformation"
+	depends on LEGACY_IP
 	select INET_XFRM_TUNNEL
 	select XFRM_IPCOMP
 	help
@@ -425,11 +449,13 @@ config INET_TABLE_PERTURB_ORDER
 
 config INET_XFRM_TUNNEL
 	tristate
+	depends on LEGACY_IP
 	select INET_TUNNEL
 	default n
 
 config INET_TUNNEL
 	tristate
+	depends on LEGACY_IP
 	default n
 
 config INET_DIAG
Signature · 1 line
2.51.0
#9 lore raw

Re: [PATCH 4/6] net: Make IPv4-only Kconfig options depend on LEGACY_IP

IP_ROUTE_CLASSID is selected by NETFILTER_XT_MATCH_REALM in net/netfilter/Kconfig and by NET_CLS_ROUTE4 in net/sched/Kconfig, neither of which depends on LEGACY_IP.
Quoted 4 lines
net/ipv4/Kconfig +0 -0
index aef2c5349e62..03b5ba75c3cf 100644

[ ... ]

net/ipv4/Kconfig config IP_ROUTE_VERBOSE · line 122

Quoted 5 lines
net/ipv4/Kconfig +1 -0
@@ -113,9 +119,11 @@ config IP_ROUTE_VERBOSE
 
 config IP_ROUTE_CLASSID
 	bool
+	depends on LEGACY_IP

IP_ROUTE_CLASSID is selected by NETFILTER_XT_MATCH_REALM in net/netfilter/Kconfig and by NET_CLS_ROUTE4 in net/sched/Kconfig, neither of which depends on LEGACY_IP.

Would this produce unmet direct dependency warnings when either of those options is enabled with LEGACY_IP=n?

net/ipv4/Kconfig config NET_IPIP · line 205

Quoted 8 lines
net/ipv4/Kconfig +1 -0
@@ -190,6 +199,7 @@ config NET_IPIP
 
 config NET_IPGRE_DEMUX
 	tristate "IP: GRE demultiplexer"
+	depends on LEGACY_IP
 	help
 	  This is helper module to demultiplex GRE packets on GRE version field criteria.
 	  Required by ip_gre and pptp modules.

IPV6_GRE in net/ipv6/Kconfig has 'depends on NET_IPGRE_DEMUX'. ip6_gre.c uses gre_parse_header() from the GRE demux module.

Gating NET_IPGRE_DEMUX on LEGACY_IP silently makes IPv6 GRE tunneling unavailable on IPv6-only systems. Should this option be left ungated, similar to NET_IP_TUNNEL?

[ ... ]

net/ipv4/Kconfig config NET_IPGRE_BROADCAST · line 242

Quoted 6 lines
net/ipv4/Kconfig +1 -0
@@ -226,10 +238,12 @@ config NET_IPGRE_BROADCAST
 
 config IP_MROUTE_COMMON
 	bool
+	depends on LEGACY_IP
 	depends on IP_MROUTE || IPV6_MROUTE

IP_MROUTE_COMMON is shared infrastructure between IPv4 and IPv6 multicast routing. ipmr_base.c describes itself as "Common logic shared by IPv4 [ipmr] and IPv6 [ip6mr] implementation".

IPV6_MROUTE in net/ipv6/Kconfig does 'select IP_MROUTE_COMMON' without depending on LEGACY_IP. When LEGACY_IP=n and IPV6_MROUTE=y, this will produce an unmet dependency warning.

Should IP_MROUTE_COMMON be left ungated, like NET_IP_TUNNEL and the other shared options listed in the commit message?

[ ... ]

net/ipv4/Kconfig config INET_TABLE_PERTURB_ORDER · line 459

Quoted 12 lines
net/ipv4/Kconfig +2 -0
@@ -425,11 +449,13 @@ config INET_TABLE_PERTURB_ORDER
 
 config INET_XFRM_TUNNEL
 	tristate
+	depends on LEGACY_IP
 	select INET_TUNNEL
 	default n
 
 config INET_TUNNEL
 	tristate
+	depends on LEGACY_IP
 	default n

IPV6_SIT in net/ipv6/Kconfig does 'select INET_TUNNEL' without depending on LEGACY_IP. When LEGACY_IP=n and IPV6_SIT is enabled, this will produce an unmet dependency warning for INET_TUNNEL.

SIT is inherently IPv4-dependent (IPv6-in-IPv4 tunneling), so perhaps IPV6_SIT also needs 'depends on LEGACY_IP'?

---
AI reviewed your patch. Please fix the bug or email reply why it's not a bug.
See: https://github.com/kernel-patches/vmtest/blob/master/ci/claude/README.md

CI run summary: https://github.com/kernel-patches/bpf/actions/runs/23838411859

#10 lore raw

PATCH 5/6 net: Change CONFIG_INET to CONFIG_LEGACY_IP for IPv4-only code

In reply to David Woodhouse

From: David Woodhouse <[email protected]>

Several functions guarded by CONFIG_INET are actually IPv4-specific and should be gated by CONFIG_LEGACY_IP instead:

- bpf_out_neigh_v4(): BPF IPv4 neighbour output helper
- bpf_ipv4_fib_lookup(): BPF IPv4 FIB lookup
- case AF_INET in bpf_xdp_fib_lookup/bpf_skb_fib_lookup switch
- br_arp_send(): bridge ARP proxy (ARP is IPv4-only)

This allows the compiler to eliminate these functions when LEGACY_IP=n.

---
 net/bridge/br_arp_nd_proxy.c    |  2 +-
 net/bridge/br_private.h         |  8 ++++++++
 net/core/filter.c               | 10 +++++-----
 net/core/sock.c                 |  2 +-
 net/mac80211/main.c             | 10 +++++-----
 net/netfilter/nfnetlink_queue.c |  2 +-
 6 files changed, 21 insertions(+), 13 deletions(-)
net/bridge/br_arp_nd_proxy.c +1 -1
index 1e2b51769eec..e056fa0cd1fe 100644
@@ -39,7 +39,7 @@ void br_recalculate_neigh_suppress_enabled(struct net_bridge *br)
 	br_opt_toggle(br, BROPT_NEIGH_SUPPRESS_ENABLED, neigh_suppress);
 }
 
-#if IS_ENABLED(CONFIG_INET)
+#if IS_ENABLED(CONFIG_LEGACY_IP)
 static void br_arp_send(struct net_bridge *br, struct net_bridge_port *p,
 			struct net_device *dev, __be32 dest_ip, __be32 src_ip,
 			const unsigned char *dest_hw,
net/bridge/br_private.h +8 -0
index 9b55d38ea9ed..28131fa0a7c5 100644
@@ -2347,8 +2347,16 @@ static inline void br_switchdev_init(struct net_bridge *br)
 
 /* br_arp_nd_proxy.c */
 void br_recalculate_neigh_suppress_enabled(struct net_bridge *br);
+#if IS_ENABLED(CONFIG_LEGACY_IP)
 void br_do_proxy_suppress_arp(struct sk_buff *skb, struct net_bridge *br,
 			      u16 vid, struct net_bridge_port *p);
+#else
+static inline void br_do_proxy_suppress_arp(struct sk_buff *skb,
+					    struct net_bridge *br,
+					    u16 vid, struct net_bridge_port *p)
+{
+}
+#endif
 void br_do_suppress_nd(struct sk_buff *skb, struct net_bridge *br,
 		       u16 vid, struct net_bridge_port *p, struct nd_msg *msg);
 struct nd_msg *br_is_nd_neigh_msg(const struct sk_buff *skb, struct nd_msg *m);
net/core/filter.c +5 -5
index ad71ceefcb5e..ef99bd9fddd6 100644
@@ -2310,7 +2310,7 @@ static int __bpf_redirect_neigh_v6(struct sk_buff *skb, struct net_device *dev,
 }
 #endif /* CONFIG_IPV6 */
 
-#if IS_ENABLED(CONFIG_INET)
+#if IS_ENABLED(CONFIG_LEGACY_IP)
 static int bpf_out_neigh_v4(struct net *net, struct sk_buff *skb,
 			    struct net_device *dev, struct bpf_nh_params *nh)
 {
@@ -2419,7 +2419,7 @@ static int __bpf_redirect_neigh_v4(struct sk_buff *skb, struct net_device *dev,
 	kfree_skb(skb);
 	return NET_XMIT_DROP;
 }
-#endif /* CONFIG_INET */
+#endif /* CONFIG_LEGACY_IP */
 
 static int __bpf_redirect_neigh(struct sk_buff *skb, struct net_device *dev,
 				struct bpf_nh_params *nh)
@@ -6095,7 +6095,7 @@ static int bpf_fib_set_fwd_params(struct bpf_fib_lookup *params, u32 mtu)
 }
 #endif
 
-#if IS_ENABLED(CONFIG_INET)
+#if IS_ENABLED(CONFIG_LEGACY_IP)
 static int bpf_ipv4_fib_lookup(struct net *net, struct bpf_fib_lookup *params,
 			       u32 flags, bool check_mtu)
 {
@@ -6390,7 +6390,7 @@ BPF_CALL_4(bpf_xdp_fib_lookup, struct xdp_buff *, ctx,
 		return -EINVAL;
 
 	switch (params->family) {
-#if IS_ENABLED(CONFIG_INET)
+#if IS_ENABLED(CONFIG_LEGACY_IP)
 	case AF_INET:
 		return bpf_ipv4_fib_lookup(dev_net(ctx->rxq->dev), params,
 					   flags, true);
@@ -6431,7 +6431,7 @@ BPF_CALL_4(bpf_skb_fib_lookup, struct sk_buff *, skb,
 		check_mtu = true;
 
 	switch (params->family) {
-#if IS_ENABLED(CONFIG_INET)
+#if IS_ENABLED(CONFIG_LEGACY_IP)
 	case AF_INET:
 		rc = bpf_ipv4_fib_lookup(net, params, flags, check_mtu);
 		break;
net/core/sock.c +1 -1
index 5976100a9d55..6b2914702a38 100644
@@ -4267,7 +4267,7 @@ int sock_load_diag_module(int family, int protocol)
 				      NETLINK_SOCK_DIAG, family);
 	}
 
-#ifdef CONFIG_INET
+#ifdef CONFIG_LEGACY_IP
 	if (family == AF_INET &&
 	    protocol != IPPROTO_RAW &&
 	    protocol < MAX_INET_PROTOS &&
net/mac80211/main.c +5 -5
index 616f86b1a7e4..7c1bbbb2c5c7 100644
@@ -558,7 +558,7 @@ void ieee80211_restart_hw(struct ieee80211_hw *hw)
 }
 EXPORT_SYMBOL(ieee80211_restart_hw);
 
-#ifdef CONFIG_INET
+#ifdef CONFIG_LEGACY_IP
 static int ieee80211_ifa_changed(struct notifier_block *nb,
 				 unsigned long data, void *arg)
 {
@@ -1624,7 +1624,7 @@ int ieee80211_register_hw(struct ieee80211_hw *hw)
 	wiphy_unlock(hw->wiphy);
 	rtnl_unlock();
 
-#ifdef CONFIG_INET
+#ifdef CONFIG_LEGACY_IP
 	local->ifa_notifier.notifier_call = ieee80211_ifa_changed;
 	result = register_inetaddr_notifier(&local->ifa_notifier);
 	if (result)
@@ -1642,11 +1642,11 @@ int ieee80211_register_hw(struct ieee80211_hw *hw)
 
 #if IS_ENABLED(CONFIG_IPV6)
  fail_ifa6:
-#ifdef CONFIG_INET
+#ifdef CONFIG_LEGACY_IP
 	unregister_inetaddr_notifier(&local->ifa_notifier);
 #endif
 #endif
-#if defined(CONFIG_INET) || defined(CONFIG_IPV6)
+#if defined(CONFIG_LEGACY_IP) || defined(CONFIG_IPV6)
  fail_ifa:
 #endif
 	wiphy_unregister(local->hw.wiphy);
@@ -1673,7 +1673,7 @@ void ieee80211_unregister_hw(struct ieee80211_hw *hw)
 	tasklet_kill(&local->tx_pending_tasklet);
 	tasklet_kill(&local->tasklet);
 
-#ifdef CONFIG_INET
+#ifdef CONFIG_LEGACY_IP
 	unregister_inetaddr_notifier(&local->ifa_notifier);
 #endif
 #if IS_ENABLED(CONFIG_IPV6)
net/netfilter/nfnetlink_queue.c +1 -1
index 47f7f62906e2..e453fdb2254c 100644
@@ -339,7 +339,7 @@ static struct nf_hook_entries *nf_hook_entries_head(const struct net *net, u8 pf
 
 static int nf_ip_reroute(struct sk_buff *skb, const struct nf_queue_entry *entry)
 {
-#ifdef CONFIG_INET
+#ifdef CONFIG_LEGACY_IP
 	const struct ip_rt_info *rt_info = nf_queue_entry_reroute(entry);
 
 	if (entry->state.hook == NF_INET_LOCAL_OUT) {
Signature · 1 line
2.51.0
#11 lore raw

Re: [PATCH 5/6] net: Change CONFIG_INET to CONFIG_LEGACY_IP for IPv4-only code

bpf_fib_set_fwd_params() above this hunk is still guarded by CONFIG_INET, and its only two callers are bpf_ipv4_fib_lookup() (now CONFIG_LEGACY_IP) and bpf_ipv6_fib_lookup() (CONFIG_IPV6).
Quoted 4 lines
net/core/filter.c +0 -0
index ad71ceefcb5e..ef99bd9fddd6 100644

[ ... ]

net/core/filter.c static int bpf_fib_set_fwd_params(struct bpf_fib_lookup *params, u32 mtu) · line 6100

Quoted 8 lines
net/core/filter.c +1 -1
@@ -6095,7 +6095,7 @@ static int bpf_fib_set_fwd_params(struct bpf_fib_lookup *params, u32 mtu)
 }
 #endif
 
-#if IS_ENABLED(CONFIG_INET)
+#if IS_ENABLED(CONFIG_LEGACY_IP)
 static int bpf_ipv4_fib_lookup(struct net *net, struct bpf_fib_lookup *params,
 			       u32 flags, bool check_mtu)

bpf_fib_set_fwd_params() above this hunk is still guarded by CONFIG_INET, and its only two callers are bpf_ipv4_fib_lookup() (now CONFIG_LEGACY_IP) and bpf_ipv6_fib_lookup() (CONFIG_IPV6).

When CONFIG_INET=y with both CONFIG_LEGACY_IP=n and CONFIG_IPV6=n, bpf_fib_set_fwd_params() becomes an unused static function, which will produce a -Wunused-function warning and break CONFIG_WERROR builds.

Should the guard on bpf_fib_set_fwd_params() be changed to something like:

  #if IS_ENABLED(CONFIG_LEGACY_IP) || IS_ENABLED(CONFIG_IPV6)

to match its actual callers?

---
AI reviewed your patch. Please fix the bug or email reply why it's not a bug.
See: https://github.com/kernel-patches/vmtest/blob/master/ci/claude/README.md

CI run summary: https://github.com/kernel-patches/bpf/actions/runs/23838411859

#12 lore raw

PATCH 6/6 net: Warn when processes listen on AF_INET sockets

In reply to David Woodhouse

From: David Woodhouse <[email protected]>

There is no need to listen on AF_INET sockets; a modern application can listen on IPv6 (without IPV6_V6ONLY) and will accept connections from the 20th century via IPv4-mapped addresses (::ffff:x.x.x.x) on the IPv6 socket.

---
 net/ipv4/af_inet.c | 3 +++
 1 file changed, 3 insertions(+)
net/ipv4/af_inet.c +3 -0
index dc358faa1647..3838782a8437 100644
@@ -240,6 +240,9 @@ int inet_listen(struct socket *sock, int backlog)
 	struct sock *sk = sock->sk;
 	int err = -EINVAL;
 
+	pr_warn_once("process '%s' (pid %d) is listening on an AF_INET socket. Consider using AF_INET6 with IPV6_V6ONLY=0 instead.\n",
+		     current->comm, task_pid_nr(current));
+
 	lock_sock(sk);
 
 	if (sock->state != SS_UNCONNECTED || sock->type != SOCK_STREAM)
Signature · 1 line
2.51.0
#13 lore raw

Re: [PATCH 6/6] net: Warn when processes listen on AF_INET sockets

Quoted 18 lines · David Woodhouse

From: David Woodhouse <[email protected]>

There is no need to listen on AF_INET sockets; a modern application can listen on IPv6 (without IPV6_V6ONLY) and will accept connections from the 20th century via IPv4-mapped addresses (::ffff:x.x.x.x) on the IPv6 socket.

---
 net/ipv4/af_inet.c | 3 +++
 1 file changed, 3 insertions(+)
net/ipv4/af_inet.c +0 -0
index dc358faa1647..3838782a8437 100644

net/ipv4/af_inet.c int inet_listen(struct socket *sock, int backlog) · line 245

Quoted 6 lines
net/ipv4/af_inet.c +3 -0
@@ -240,6 +240,9 @@ int inet_listen(struct socket *sock, int backlog)
        struct sock *sk = sock->sk;
        int err = -EINVAL;
 
+       pr_warn_once("process '%s' (pid %d) is listening on an AF_INET socket. Consider using AF_INET6 with IPV6_V6ONLY=0 instead.\n",
+                    current->comm, task_pid_nr(current));
+

Some kernels are built without CONFIG_IPV6, so this warning would be quite misleading.

#14 lore raw

Re: [PATCH 6/6] net: Warn when processes listen on AF_INET sockets

Quoted 22 lines · Eric Dumazet
Quoted 21 lines · David Woodhouse

From: David Woodhouse <[email protected]>

There is no need to listen on AF_INET sockets; a modern application can listen on IPv6 (without IPV6_V6ONLY) and will accept connections from the 20th century via IPv4-mapped addresses (::ffff:x.x.x.x) on the IPv6 socket.

---
 net/ipv4/af_inet.c | 3 +++

 1 file changed, 3 insertions(+)
net/ipv4/af_inet.c +0 -0
index dc358faa1647..3838782a8437 100644
@@ -240,6 +240,9 @@ int inet_listen(struct socket *sock, int backlog)
         struct sock *sk = sock->sk;
         int err = -EINVAL;
 
Quoted 6 lines

net/ipv4/af_inet.c int inet_listen(struct socket *sock, int backlog) · line 245

Quoted 3 lines
net/ipv4/af_inet.c +3 -0
+       pr_warn_once("process '%s' (pid %d) is listening on an AF_INET socket. Consider using AF_INET6 with IPV6_V6ONLY=0 instead.\n",
+                    current->comm, task_pid_nr(current));
+

Some kernels are built without CONFIG_IPV6, so this warning would be quite misleading.

Maybe on this date next year, we could make it not possible to build the kernel *without* IPv6... ?

#15 lore raw

Re: [PATCH 6/6] net: Warn when processes listen on AF_INET sockets

Quoted 5 lines · On Wed, 01 Apr 2026 10:28:23 +0100 David Woodhouse <[email protected]> wrote:
Quoted 2 lines

Some kernels are built without CONFIG_IPV6, so this warning would be quite misleading.

Maybe on this date next year, we could make it not possible to build the kernel *without* IPv6... ?

There are some government agencies that used to require that IPV6 was disabled for security reasons. Yes they had broken old firewalls

#16 lore raw

Re: [PATCH 6/6] net: Warn when processes listen on AF_INET sockets

Quoted 9 lines · Stephen Hemminger
Quoted 3 lines · On Wed, 01 Apr 2026 10:28:23 +0100 David Woodhouse <[email protected]> wrote:

Maybe on this date next year, we could make it not possible to build the kernel *without* IPv6... ?

There are some government agencies that used to require that IPV6 was disabled for security reasons. Yes they had broken old firewalls

I think you missed the big clue here. "This date".

Sigh. It's going to be a long long day.

Linus

maintainer
#17 lore raw

Re: [PATCH 6/6] net: Warn when processes listen on AF_INET sockets

In reply to Stephen Hemminger
Quoted 11 lines · On Wed, 1 Apr 2026 08:06:57 -0700 Stephen Hemminger wrote:
Quoted 5 lines · On Wed, 01 Apr 2026 10:28:23 +0100 David Woodhouse <[email protected]> wrote:
Quoted 2 lines

Some kernels are built without CONFIG_IPV6, so this warning would be quite misleading.

Maybe on this date next year, we could make it not possible to build the kernel *without* IPv6... ?

There are some government agencies that used to require that IPV6 was disabled for security reasons. Yes they had broken old firewalls

Which is why we sadly have to keep the ipv6_mod_enabled() sillilitude around. But that's a runtime thing.

#18 lore raw

Re: [PATCH 6/6] net: Warn when processes listen on AF_INET sockets

In reply to David Woodhouse
inet_listen() is the .listen handler for both inet_stream_ops (AF_INET) and inet6_stream_ops (AF_INET6):

net/ipv4/af_inet.c int inet_listen(struct socket *sock, int backlog) · line 244

Quoted 10 lines
net/ipv4/af_inet.c +2 -0
index dc358faa1647..3838782a8437 100644
@@ -240,6 +240,9 @@ int inet_listen(struct socket *sock, int backlog)
 	struct sock *sk = sock->sk;
 	int err = -EINVAL;
 
+	pr_warn_once("process '%s' (pid %d) is listening on an AF_INET socket. Consider using AF_INET6 with IPV6_V6ONLY=0 instead.\n",
+		     current->comm, task_pid_nr(current));

inet_listen() is the .listen handler for both inet_stream_ops (AF_INET) and inet6_stream_ops (AF_INET6):

net/ipv4/af_inet.c:
.listen = inet_listen,

net/ipv6/af_inet6.c:
.listen = inet_listen, /* ok */

Since pr_warn_once fires on the first listen() call regardless of address family, won't this warn about "listening on an AF_INET socket" when an AF_INET6 socket calls listen() first? Many services (e.g. systemd socket activation) create AF_INET6 sockets before AF_INET ones. Should there be a check for sk->sk_family == AF_INET before printing?

Separately, the warning suggests using AF_INET6 with IPV6_V6ONLY=0, but when CONFIG_IPV6 is disabled, AF_INET6 sockets are not available. Should the warning be conditional on IS_ENABLED(CONFIG_IPV6)?

---
AI reviewed your patch. Please fix the bug or email reply why it's not a bug.
See: https://github.com/kernel-patches/vmtest/blob/master/ci/claude/README.md

CI run summary: https://github.com/kernel-patches/bpf/actions/runs/23838411859

#19 lore raw

Re: [PATCH 6/6] net: Warn when processes listen on AF_INET sockets

In reply to David Woodhouse
Quoted 29 lines · David Woodhouse

From: David Woodhouse <[email protected]>

There is no need to listen on AF_INET sockets; a modern application can listen on IPv6 (without IPV6_V6ONLY) and will accept connections from the 20th century via IPv4-mapped addresses (::ffff:x.x.x.x) on the IPv6 socket.

---
 net/ipv4/af_inet.c | 3 +++
 1 file changed, 3 insertions(+)
net/ipv4/af_inet.c +3 -0
index dc358faa1647..3838782a8437 100644
@@ -240,6 +240,9 @@ int inet_listen(struct socket *sock, int backlog)
 	struct sock *sk = sock->sk;
 	int err = -EINVAL;
 
+	pr_warn_once("process '%s' (pid %d) is listening on an AF_INET socket. Consider using AF_INET6 with IPV6_V6ONLY=0 instead.\n",
+		     current->comm, task_pid_nr(current));
+
 	lock_sock(sk);
 
 	if (sock->state != SS_UNCONNECTED || sock->type != SOCK_STREAM)
Signature · 1 line
2.51.0

Does this also need to look at the proto? inet6_stream_ops seem to be using inet_listen as well.

const struct proto_ops inet6_stream_ops = {

        .family            = PF_INET6,
        .owner             = THIS_MODULE,
        .release           = inet6_release,
        .bind              = inet6_bind,
        .connect           = inet_stream_connect,       /* ok           */
        .socketpair        = sock_no_socketpair,        /* a do nothing */
        .accept            = inet_accept,               /* ok           */
        .getname           = inet6_getname,
        .poll              = tcp_poll,                  /* ok           */
        .ioctl             = inet6_ioctl,               /* must change  */
        .gettstamp         = sock_gettstamp,
        .listen            = inet_listen,               /* ok           */
#20 lore raw
In reply to David Woodhouse
Quoted 19 lines · David Woodhouse

RFC1883, the IPv6 standard, was published in the final decade of the 1900s. That's closer in time to the Apollo 11 moon landing than it was to today.

Even our esteemed Maddog has worked with computers for longer in the IPv6 era, than he ever did before it.

Yet Linux still can't even be *built* with only IPv6 support and without support for Legacy IP. This long overdue patch series fixes that, and immediately marks Legacy IP for deprecation.

It also cleans up a few tautological "INET && IPV6" and "INET || IPV6" checks, since IPV6 (and now LEGACY_IP) cannot be selected without the overall CONFIG_INET option.

For now, we only add a warning when a process *listens* on a Legacy IP socket (since you can listen on IPv6 and still accept connections which have come through a timewarp from the 20th century. Adding warnings for making outbound connections or *accepting* on Legacy IP can come later.

Quoted 5 lines

'I would be happy if "Legacy IP" ceased to be the "industry standard"
and IPv6 be the default, even if I had to beat IPv6 into the head of every single network administrator's head with a shovel.' said Jon
'maddog' Hall, ancient supporter of Free and Open Source Software.

Dammit, you've beaten me to it! This was my next step for 7.2.

#21 lore raw
Quoted 5 lines · Fernando Fernandez Mancera

Dammit, you've beaten me to it! This was my next step for 7.2.

Yeah. The date notwithstanding, I do actually think we should do most of this for real.

Maybe we don't get away with the actual deprecation and the warnings on use *just* yet, and *maybe* we won't even get away with calling the config option CONFIG_LEGACY_IP, although I would genuinely like to see us moving consistently towards saying "Legacy IP" instead of "IPv4" everywhere.

But we *should* clean up the separation of CONFIG_INET and CONFIG_IPV[64] and make it possible to build with either protocol alone.

#22 lore raw
Quoted 8 lines · On Wed, 01 Apr 2026 09:25:08 +0100 David Woodhouse <[email protected]> wrote:
Quoted 5 lines · Fernando Fernandez Mancera

Dammit, you've beaten me to it! This was my next step for 7.2.

Yeah. The date notwithstanding,

You tricked me on this April fools day...

Very funny!

Quoted 8 lines

I do actually think we should do most
of this for real.

Maybe we don't get away with the actual deprecation and the warnings on use *just* yet, and *maybe* we won't even get away with calling the config option CONFIG_LEGACY_IP, although I would genuinely like to see us moving consistently towards saying "Legacy IP" instead of "IPv4" everywhere.

IPv4 is not legacy yet... Lots of configurations, service providers and corporations that requires the usage of VPN are IPv4 only still today. For instance, my paid VPN provider only grants IPv4 addresses (at least for those not using their proprietary software).

Quoted 4 lines

But we *should* clean up the separation of CONFIG_INET and CONFIG_IPV[64] and make it possible to build with either protocol alone.

That makes sense on my eyes.

Thanks,
Mauro

#23 lore raw
In reply to David Woodhouse

On 4/1/26 07:44, David Woodhouse wrote:

Hi David,

(fun fishing this out from nntp.lore.kernel.org needing NAT64)

Quoted 9 lines

RFC1883, the IPv6 standard, was published in the final decade of the 1900s. That's closer in time to the Apollo 11 moon landing than it was to today.

Even our esteemed Maddog has worked with computers for longer in the IPv6 era, than he ever did before it.

Yet Linux still can't even be *built* with only IPv6 support and without support for Legacy IP. This long overdue patch series fixes that, and
...

This is very interesting; I'll be happy to read the more serious discussions for 6/6 this year then :)

That said, I've been there 15 years ago and done that for real, just not for Linux:
https://freebsdfoundation.org/blog/freebsd-foundation-and-ixsystems-announce-ipv6-only-testing-versions-of-freebsd-and-pc-bsd/

A lot of parts (e.g., PC-BSD,the IPv6-only snapshots we published back then, websites) are long gone, but FreeBSD today still has NO-INET (as well as NO-INET6 and NO-IP) kernel configs which are regularly tested
as part of a universe build to make sure the status-quo stayed, along with
options to build (large parts) of userspace without IPv4 support.

I have since run real IPv6-only machines :]] EAFNOSUPPORT and EPROTONOSUPPORT are (were) a good friend of mine.

It helped a lot back then to find applications which had real trouble working without IPv4.
It was fun sitting in a UKNOF presentation years later to hear about all these applications just working on IPv6-only and knowing why, whereas the presenter was unaware, and still had a 127.1 on his loopback *sigh*

IPv6-only is something a lot of people will not understand and someone just has to do it! It is a worthwhile goal, even if late, as you say. My reminder to people these days is: DNSsec is even older than IPv6.

I have moved on (though would love to go back to more IPv6); please feel free to get in touch in case you want me to go and swap in some more memories from that time to share experience and help.

To the global deployment of IPv6!
/bz

maintainer Applied
#24 lore raw
In reply to David Woodhouse

Hello:

This series was applied to netdev/net-next.git (main) by Jakub Kicinski <[email protected]>:

Quoted 6 lines · On Wed, 1 Apr 2026 08:44:14 +0100 David Woodhouse wrote:

RFC1883, the IPv6 standard, was published in the final decade of the 1900s. That's closer in time to the Apollo 11 moon landing than it was to today.

Even our esteemed Maddog has worked with computers for longer in the IPv6 era, than he ever did before it.
[...]

Here is the summary with links:
- [net-next,v4,1/6] net: Simplify tautological CONFIG_INET/CONFIG_IPV6 guards

    https://git.kernel.org/netdev/net-next/c/8888bf4fb980
  - [net-next,v4,2/6] net: Add CONFIG_LEGACY_IP option
    https://git.kernel.org/netdev/net-next/c/9b29afa11660
  - [net-next,v4,3/6] net: Guard Legacy IP entry points with CONFIG_LEGACY_IP
    https://git.kernel.org/netdev/net-next/c/f26d43acf12f
  - [net-next,v4,4/6] net: Make IPv4-only Kconfig options depend on LEGACY_IP
    https://git.kernel.org/netdev/net-next/c/ba5d4128fca8
  - [net-next,v4,5/6] net: Change CONFIG_INET to CONFIG_LEGACY_IP for IPv4-only code
    https://git.kernel.org/netdev/net-next/c/ff1cb3ad2abc
  - [net-next,v4,6/6] net: Warn when processes listen on AF_INET sockets
    https://git.kernel.org/netdev/net-next/c/7dae8ffb0987

You are awesome, thank you!

Signature · 2 lines
Deet-doot-dot, I am a bot.
https://korg.docs.kernel.org/patchwork/pwbot.html