From: Fernando Fernandez Mancera <fmancera@suse.de>
To: netdev@vger.kernel.org
Cc: davem@davemloft.net, edumazet@google.com, kuba@kernel.org,
pabeni@redhat.com, dsahern@kernel.org, horms@kernel.org,
idosch@nvidia.com, Fernando Fernandez Mancera <fmancera@suse.de>,
Alexei Starovoitov <ast@kernel.org>,
Daniel Borkmann <daniel@iogearbox.net>,
Andrii Nakryiko <andrii@kernel.org>,
Eduard Zingerman <eddyz87@gmail.com>,
Kumar Kartikeya Dwivedi <memxor@gmail.com>,
Martin KaFai Lau <martin.lau@linux.dev>,
Song Liu <song@kernel.org>,
Yonghong Song <yonghong.song@linux.dev>,
Jiri Olsa <jolsa@kernel.org>,
Emil Tsalapatis <emil@etsalapatis.com>,
John Fastabend <john.fastabend@gmail.com>,
Stanislav Fomichev <sdf@fomichev.me>,
Neal Cardwell <ncardwell@google.com>,
Kuniyuki Iwashima <kuniyu@google.com>,
Kory Maincent <kory.maincent@bootlin.com>,
Vadim Fedorenko <vadim.fedorenko@linux.dev>,
Carolina Jubran <cjubran@nvidia.com>,
Jiaming Zhang <r772577952@gmail.com>,
Petr Machata <petrm@nvidia.com>,
linux-kernel@vger.kernel.org, bpf@vger.kernel.org
Subject: [PATCH 02/13 RFC net-next] net: core: add IPv4 fallback stubs and guards for CONFIG_IPV4=n
Date: Sun, 12 Jul 2026 03:39:00 +0200 [thread overview]
Message-ID: <20260712013941.4570-3-fmancera@suse.de> (raw)
In-Reply-To: <20260712013941.4570-1-fmancera@suse.de>
To enable compiling the core network stack without IPv4, generic
networking code must avoid referencing missing IPv4 symbols.
This patch provides static inline stubs in include/net/ip.h and
include/net/route.h that return a proper error code when IPv4 is
disabled. This behaves as IPv6 code does. In addition, it introduces
guards around IPv4 specific code in generic implementations to prevent
undefined reference errors while linking.
Signed-off-by: Fernando Fernandez Mancera <fmancera@suse.de>
---
include/linux/indirect_call_wrapper.h | 8 +-
include/net/ip.h | 104 ++++++++++++++++++++++----
include/net/route.h | 8 ++
net/core/dev_ioctl.c | 4 +
net/core/filter.c | 18 ++---
net/core/neighbour.c | 4 +
net/ipv4/inet_hashtables.c | 4 +
7 files changed, 122 insertions(+), 28 deletions(-)
diff --git a/include/linux/indirect_call_wrapper.h b/include/linux/indirect_call_wrapper.h
index 0e4340ecd857..583efbc03446 100644
--- a/include/linux/indirect_call_wrapper.h
+++ b/include/linux/indirect_call_wrapper.h
@@ -57,16 +57,18 @@
* builtin, this macro simplify dealing with indirect calls with only ipv4/ipv6
* alternatives
*/
-#if IS_ENABLED(CONFIG_IPV6)
+#if IS_ENABLED(CONFIG_IPV4) && IS_ENABLED(CONFIG_IPV6)
#define INDIRECT_CALL_INET(f, f2, f1, ...) \
INDIRECT_CALL_2(f, f2, f1, __VA_ARGS__)
-#elif IS_ENABLED(CONFIG_INET)
+#elif IS_ENABLED(CONFIG_IPV4)
#define INDIRECT_CALL_INET(f, f2, f1, ...) INDIRECT_CALL_1(f, f1, __VA_ARGS__)
+#elif IS_ENABLED(CONFIG_IPV6)
+#define INDIRECT_CALL_INET(f, f2, f1, ...) INDIRECT_CALL_1(f, f2, __VA_ARGS__)
#else
#define INDIRECT_CALL_INET(f, f2, f1, ...) f(__VA_ARGS__)
#endif
-#if IS_ENABLED(CONFIG_INET)
+#if IS_ENABLED(CONFIG_IPV4)
#define INDIRECT_CALL_INET_1(f, f1, ...) INDIRECT_CALL_1(f, f1, __VA_ARGS__)
#else
#define INDIRECT_CALL_INET_1(f, f1, ...) f(__VA_ARGS__)
diff --git a/include/net/ip.h b/include/net/ip.h
index 7f2fe1a8401b..aeac40d11c72 100644
--- a/include/net/ip.h
+++ b/include/net/ip.h
@@ -171,8 +171,17 @@ int ip_mr_input(struct sk_buff *skb);
int ip_mr_output(struct net *net, struct sock *sk, struct sk_buff *skb);
int ip_output(struct net *net, struct sock *sk, struct sk_buff *skb);
int ip_mc_output(struct net *net, struct sock *sk, struct sk_buff *skb);
+#if IS_ENABLED(CONFIG_IPV4)
int ip_do_fragment(struct net *net, struct sock *sk, struct sk_buff *skb,
int (*output)(struct net *, struct sock *, struct sk_buff *));
+#else
+static inline int ip_do_fragment(struct net *net, struct sock *sk, struct sk_buff *skb,
+ int (*output)(struct net *, struct sock *, struct sk_buff *))
+{
+ kfree_skb(skb);
+ return -EAFNOSUPPORT;
+}
+#endif
struct ip_fraglist_iter {
struct sk_buff *frag;
@@ -218,6 +227,7 @@ int ip_local_out(struct net *net, struct sock *sk, struct sk_buff *skb);
int __ip_queue_xmit(struct sock *sk, struct sk_buff *skb, struct flowi *fl,
__u8 tos);
void ip_init(void);
+#if IS_ENABLED(CONFIG_IPV4)
int ip_append_data(struct sock *sk, struct flowi4 *fl4,
int getfrag(void *from, char *to, int offset, int len,
int odd, struct sk_buff *skb),
@@ -225,27 +235,58 @@ int ip_append_data(struct sock *sk, struct flowi4 *fl4,
struct ipcm_cookie *ipc,
struct rtable **rt,
unsigned int flags);
-int ip_generic_getfrag(void *from, char *to, int offset, int len, int odd,
- struct sk_buff *skb);
-struct sk_buff *__ip_make_skb(struct sock *sk, struct flowi4 *fl4,
- struct sk_buff_head *queue,
- struct inet_cork *cork);
-int ip_send_skb(struct net *net, struct sk_buff *skb);
-int ip_push_pending_frames(struct sock *sk, struct flowi4 *fl4);
-void ip_flush_pending_frames(struct sock *sk);
struct sk_buff *ip_make_skb(struct sock *sk, struct flowi4 *fl4,
int getfrag(void *from, char *to, int offset,
int len, int odd, struct sk_buff *skb),
void *from, int length, int transhdrlen,
struct ipcm_cookie *ipc, struct rtable **rtp,
struct inet_cork *cork, unsigned int flags);
-
-int ip_queue_xmit(struct sock *sk, struct sk_buff *skb, struct flowi *fl);
+struct sk_buff *__ip_make_skb(struct sock *sk, struct flowi4 *fl4,
+ struct sk_buff_head *queue,
+ struct inet_cork *cork);
+void ip_flush_pending_frames(struct sock *sk);
static inline struct sk_buff *ip_finish_skb(struct sock *sk, struct flowi4 *fl4)
{
return __ip_make_skb(sk, fl4, &sk->sk_write_queue, &inet_sk(sk)->cork.base);
}
+#else
+static inline int ip_append_data(struct sock *sk, struct flowi4 *fl4,
+ int getfrag(void *from, char *to, int offset, int len,
+ int odd, struct sk_buff *skb),
+ void *from, int len, int protolen,
+ struct ipcm_cookie *ipc,
+ struct rtable **rt,
+ unsigned int flags)
+{
+ return -EAFNOSUPPORT;
+}
+
+static inline struct sk_buff *ip_make_skb(struct sock *sk, struct flowi4 *fl4,
+ int getfrag(void *from, char *to, int offset,
+ int len, int odd, struct sk_buff *skb),
+ void *from, int length, int transhdrlen,
+ struct ipcm_cookie *ipc, struct rtable **rtp,
+ struct inet_cork *cork, unsigned int flags)
+{
+ return ERR_PTR(-EAFNOSUPPORT);
+}
+
+static inline struct sk_buff *ip_finish_skb(struct sock *sk, struct flowi4 *fl4)
+{
+ return ERR_PTR(-EAFNOSUPPORT);
+}
+
+static inline void ip_flush_pending_frames(struct sock *sk)
+{
+}
+#endif
+
+int ip_generic_getfrag(void *from, char *to, int offset, int len, int odd,
+ struct sk_buff *skb);
+int ip_send_skb(struct net *net, struct sk_buff *skb);
+int ip_push_pending_frames(struct sock *sk, struct flowi4 *fl4);
+int ip_queue_xmit(struct sock *sk, struct sk_buff *skb, struct flowi *fl);
/* Get the route scope that should be used when sending a packet. */
static inline u8 ip_sendmsg_scope(const struct inet_sock *inet,
@@ -787,24 +828,55 @@ int ip_options_rcv_srr(struct sk_buff *skb, struct net_device *dev);
* Functions provided by ip_sockglue.c
*/
-void ipv4_pktinfo_prepare(const struct sock *sk, struct sk_buff *skb, bool drop_dst);
void ip_cmsg_recv_offset(struct msghdr *msg, struct sock *sk,
struct sk_buff *skb, int tlen, int offset);
+#if IS_ENABLED(CONFIG_IPV4)
+void ipv4_pktinfo_prepare(const struct sock *sk, struct sk_buff *skb, bool drop_dst);
int ip_cmsg_send(struct sock *sk, struct msghdr *msg,
struct ipcm_cookie *ipc, bool allow_ipv6);
+int ip_recv_error(struct sock *sk, struct msghdr *msg, int len);
+int ip_setsockopt(struct sock *sk, int level, int optname, sockptr_t optval,
+ unsigned int optlen);
+int ip_getsockopt(struct sock *sk, int level, int optname, char __user *optval,
+ int __user *optlen);
+#else
+static inline void ipv4_pktinfo_prepare(const struct sock *sk,
+ struct sk_buff *skb,
+ bool drop_dst)
+{
+}
+
+static inline int ip_cmsg_send(struct sock *sk, struct msghdr *msg,
+ struct ipcm_cookie *ipc, bool allow_ipv6)
+{
+ return 0;
+}
+
+static inline int ip_recv_error(struct sock *sk, struct msghdr *msg, int len)
+{
+ return -EAFNOSUPPORT;
+}
+
+static inline int ip_setsockopt(struct sock *sk, int level, int optname,
+ sockptr_t optval, unsigned int optlen)
+{
+ return -EAFNOSUPPORT;
+}
+
+static inline int ip_getsockopt(struct sock *sk, int level, int optname,
+ char __user *optval, int __user *optlen)
+{
+ return -EAFNOSUPPORT;
+}
+#endif
DECLARE_STATIC_KEY_FALSE(ip4_min_ttl);
int do_ip_setsockopt(struct sock *sk, int level, int optname, sockptr_t optval,
unsigned int optlen);
-int ip_setsockopt(struct sock *sk, int level, int optname, sockptr_t optval,
- unsigned int optlen);
int do_ip_getsockopt(struct sock *sk, int level, int optname,
sockptr_t optval, sockptr_t optlen);
-int ip_getsockopt(struct sock *sk, int level, int optname, char __user *optval,
- int __user *optlen);
int ip_ra_control(struct sock *sk, unsigned char on,
void (*destructor)(struct sock *));
-int ip_recv_error(struct sock *sk, struct msghdr *msg, int len);
void ip_icmp_error(struct sock *sk, struct sk_buff *skb, int err, __be16 port,
u32 info, u8 *payload);
void ip_local_error(struct sock *sk, int err, __be32 daddr, __be16 dport,
diff --git a/include/net/route.h b/include/net/route.h
index f90106f383c5..08adb14224a9 100644
--- a/include/net/route.h
+++ b/include/net/route.h
@@ -169,8 +169,16 @@ static inline struct rtable *__ip_route_output_key(struct net *net,
return ip_route_output_key_hash(net, flp, NULL);
}
+#if IS_ENABLED(CONFIG_IPV4)
struct rtable *ip_route_output_flow(struct net *, struct flowi4 *flp,
const struct sock *sk);
+#else
+static inline struct rtable *ip_route_output_flow(struct net *, struct flowi4 *flp,
+ const struct sock *sk)
+{
+ return ERR_PTR(-EAFNOSUPPORT);
+}
+#endif
struct dst_entry *ipv4_blackhole_route(struct net *net,
struct dst_entry *dst_orig);
diff --git a/net/core/dev_ioctl.c b/net/core/dev_ioctl.c
index a320e264eaaf..f7ca3701b6bd 100644
--- a/net/core/dev_ioctl.c
+++ b/net/core/dev_ioctl.c
@@ -39,6 +39,7 @@ static int dev_ifname(struct net *net, struct ifreq *ifr)
*/
int dev_ifconf(struct net *net, struct ifconf __user *uifc)
{
+#if IS_ENABLED(CONFIG_IPV4)
struct net_device *dev;
void __user *pos;
size_t size;
@@ -82,6 +83,9 @@ int dev_ifconf(struct net *net, struct ifconf __user *uifc)
rtnl_net_unlock(net);
return put_user(total, &uifc->ifc_len);
+#else
+ return -EAFNOSUPPORT;
+#endif
}
static int dev_getifmap(struct net_device *dev, struct ifreq *ifr)
diff --git a/net/core/filter.c b/net/core/filter.c
index b446aa8be5c3..55c2466c32ae 100644
--- a/net/core/filter.c
+++ b/net/core/filter.c
@@ -2314,7 +2314,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_IPV4)
static int bpf_out_neigh_v4(struct net *net, struct sk_buff *skb,
struct net_device *dev, struct bpf_nh_params *nh)
{
@@ -2423,7 +2423,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_IPV4 */
static int __bpf_redirect_neigh(struct sk_buff *skb, struct net_device *dev,
struct bpf_nh_params *nh)
@@ -5685,7 +5685,7 @@ static int __bpf_setsockopt(struct sock *sk, int level, int optname,
if (level == SOL_SOCKET)
return sol_socket_sockopt(sk, optname, optval, &optlen, false);
- else if (IS_ENABLED(CONFIG_INET) && level == SOL_IP)
+ else if (IS_ENABLED(CONFIG_IPV4) && level == SOL_IP)
return sol_ip_sockopt(sk, optname, optval, &optlen, false);
else if (IS_ENABLED(CONFIG_IPV6) && level == SOL_IPV6)
return sol_ipv6_sockopt(sk, optname, optval, &optlen, false);
@@ -5722,7 +5722,7 @@ static int __bpf_getsockopt(struct sock *sk, int level, int optname,
err = sol_socket_sockopt(sk, optname, optval, &optlen, true);
else if (IS_ENABLED(CONFIG_INET) && level == SOL_TCP)
err = sol_tcp_sockopt(sk, optname, optval, &optlen, true);
- else if (IS_ENABLED(CONFIG_INET) && level == SOL_IP)
+ else if (IS_ENABLED(CONFIG_IPV4) && level == SOL_IP)
err = sol_ip_sockopt(sk, optname, optval, &optlen, true);
else if (IS_ENABLED(CONFIG_IPV6) && level == SOL_IPV6)
err = sol_ipv6_sockopt(sk, optname, optval, &optlen, true);
@@ -6212,7 +6212,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_IPV4)
static int bpf_ipv4_fib_lookup(struct net *net, struct bpf_fib_lookup *params,
u32 flags, bool check_mtu)
{
@@ -6504,7 +6504,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_IPV4)
case AF_INET:
return bpf_ipv4_fib_lookup(dev_net(ctx->rxq->dev), params,
flags, true);
@@ -6545,7 +6545,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_IPV4)
case AF_INET:
rc = bpf_ipv4_fib_lookup(net, params, flags, check_mtu);
break;
@@ -12076,7 +12076,7 @@ BPF_CALL_1(bpf_skc_to_tcp_timewait_sock, struct sock *, sk)
BTF_TYPE_EMIT(struct inet_timewait_sock);
BTF_TYPE_EMIT(struct tcp_timewait_sock);
-#ifdef CONFIG_INET
+#ifdef CONFIG_IPV4
if (sk && sk->sk_prot == &tcp_prot && sk->sk_state == TCP_TIME_WAIT)
return (unsigned long)sk;
#endif
@@ -12099,7 +12099,7 @@ const struct bpf_func_proto bpf_skc_to_tcp_timewait_sock_proto = {
BPF_CALL_1(bpf_skc_to_tcp_request_sock, struct sock *, sk)
{
-#ifdef CONFIG_INET
+#ifdef CONFIG_IPV4
if (sk && sk->sk_prot == &tcp_prot && sk->sk_state == TCP_NEW_SYN_RECV)
return (unsigned long)sk;
#endif
diff --git a/net/core/neighbour.c b/net/core/neighbour.c
index 1349c0eedb64..1a40a0ab041b 100644
--- a/net/core/neighbour.c
+++ b/net/core/neighbour.c
@@ -3208,9 +3208,13 @@ int neigh_xmit(int index, struct net_device *dev,
goto out_kfree_skb;
}
if (index == NEIGH_ARP_TABLE) {
+#if IS_ENABLED(CONFIG_IPV4)
u32 key = *((u32 *)addr);
neigh = __ipv4_neigh_lookup_noref(dev, key);
+#else
+ goto out_kfree_skb;
+#endif
} else {
neigh = __neigh_lookup_noref(tbl, addr, dev);
}
diff --git a/net/ipv4/inet_hashtables.c b/net/ipv4/inet_hashtables.c
index ba0faa9ae2bb..47228ff6fa00 100644
--- a/net/ipv4/inet_hashtables.c
+++ b/net/ipv4/inet_hashtables.c
@@ -581,6 +581,7 @@ static int __inet_check_established(struct inet_timewait_death_row *death_row,
bool rcu_lookup,
u32 hash)
{
+#if IS_ENABLED(CONFIG_IPV4) //TODO: should this be moved out of here?
struct inet_hashinfo *hinfo = death_row->hashinfo;
struct inet_sock *inet = inet_sk(sk);
__be32 daddr = inet->inet_rcv_saddr;
@@ -651,6 +652,9 @@ static int __inet_check_established(struct inet_timewait_death_row *death_row,
not_unique:
spin_unlock(lock);
return -EADDRNOTAVAIL;
+#else
+ return -EAFNOSUPPORT;
+#endif
}
static u64 inet_sk_port_offset(const struct sock *sk)
--
2.54.0
next prev parent reply other threads:[~2026-07-12 1:40 UTC|newest]
Thread overview: 15+ messages / expand[flat|nested] mbox.gz Atom feed top
[not found] <20260712013941.4570-1-fmancera@suse.de>
2026-07-12 1:38 ` [PATCH 01/13 RFC net-next] net: ipv4: introduce CONFIG_IPV4 to decouple the IPv4 stack Fernando Fernandez Mancera
2026-07-12 11:01 ` Arnd Bergmann
2026-07-12 1:39 ` Fernando Fernandez Mancera [this message]
2026-07-12 1:39 ` [PATCH 03/13 RFC net-next] net: inet: relocate ip_generic_getfrag and guard IPv4 socket logic Fernando Fernandez Mancera
2026-07-12 1:39 ` [PATCH 04/13 RFC net-next] net: tcp: move protocol agnostic TCP functions out of tcp_ipv4.c Fernando Fernandez Mancera
2026-07-12 1:39 ` [PATCH 05/13 RFC net-next] net: raw: split IPv4 specific logic into raw_ipv4.c Fernando Fernandez Mancera
2026-07-12 1:39 ` [PATCH 06/13 RFC net-next] net: udp: split IPv4 specific logic into udp_ipv4.c Fernando Fernandez Mancera
2026-07-12 1:39 ` [PATCH 07/13 RFC net-next] net: icmp: split IPv4 specific logic into icmp_ipv4.c Fernando Fernandez Mancera
2026-07-12 1:39 ` [PATCH 08/13 RFC net-next] net: ping: split IPv4 specific logic into ping_ipv4.c Fernando Fernandez Mancera
2026-07-12 1:39 ` [PATCH 09/13 RFC net-next] net: fib: split common nexthop logic to fib_core.c Fernando Fernandez Mancera
2026-07-12 1:39 ` [PATCH 10/13 RFC net-next] net: tunnel: guard IPv4 tunnel functions with CONFIG_IPV4 Fernando Fernandez Mancera
2026-07-12 1:39 ` [PATCH 11/13 RFC net-next] net: cipso: guard IPv4 packet manipulation functions Fernando Fernandez Mancera
2026-07-12 16:22 ` Paul Moore
2026-07-12 1:39 ` [PATCH 12/13 RFC net-next] netfilter: ipv4: guard ip_route_me_harder() with CONFIG_IPV4 Fernando Fernandez Mancera
2026-07-12 1:39 ` [PATCH 13/13 RFC net-next] net: ipv4: make CONFIG_IPV4 boolean Fernando Fernandez Mancera
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=20260712013941.4570-3-fmancera@suse.de \
--to=fmancera@suse.de \
--cc=andrii@kernel.org \
--cc=ast@kernel.org \
--cc=bpf@vger.kernel.org \
--cc=cjubran@nvidia.com \
--cc=daniel@iogearbox.net \
--cc=davem@davemloft.net \
--cc=dsahern@kernel.org \
--cc=eddyz87@gmail.com \
--cc=edumazet@google.com \
--cc=emil@etsalapatis.com \
--cc=horms@kernel.org \
--cc=idosch@nvidia.com \
--cc=john.fastabend@gmail.com \
--cc=jolsa@kernel.org \
--cc=kory.maincent@bootlin.com \
--cc=kuba@kernel.org \
--cc=kuniyu@google.com \
--cc=linux-kernel@vger.kernel.org \
--cc=martin.lau@linux.dev \
--cc=memxor@gmail.com \
--cc=ncardwell@google.com \
--cc=netdev@vger.kernel.org \
--cc=pabeni@redhat.com \
--cc=petrm@nvidia.com \
--cc=r772577952@gmail.com \
--cc=sdf@fomichev.me \
--cc=song@kernel.org \
--cc=vadim.fedorenko@linux.dev \
--cc=yonghong.song@linux.dev \
/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