aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRicardo Branco <rbranco@suse.de>2026-04-12 11:01:47 +0000
committerPouria Mousavizadeh Tehrani <pouria@FreeBSD.org>2026-04-17 07:22:36 +0000
commit64e2715f5d8f03ab7e763a49a0e102d755673810 (patch)
treee21b191c7520f8e0e027c5bec8cc038f6c9e4fb4
parent3a54aa3b0911bef15e014b8a8185e116efb0a918 (diff)
linux: Support ICMP6_FILTER socket option translation
Handle Linux IPPROTO_ICMPV6 socket options in the Linuxulator and map ICMP6_FILTER for both getsockopt(2) and setsockopt(2). Linux and FreeBSD use inverted bit semantics for struct icmp6_filter, so invert the filter contents before/after calling setsockopt/getsockopt. Signed-off-by: Ricardo Branco <rbranco@suse.de> PR: 294434 Reviewed by: pouria Pull-Request: https://github.com/freebsd/freebsd-src/pull/2138
-rw-r--r--sys/compat/linux/linux_socket.c67
-rw-r--r--sys/compat/linux/linux_socket.h2
2 files changed, 69 insertions, 0 deletions
diff --git a/sys/compat/linux/linux_socket.c b/sys/compat/linux/linux_socket.c
index 023be1e6b885..d971b2a7fbe7 100644
--- a/sys/compat/linux/linux_socket.c
+++ b/sys/compat/linux/linux_socket.c
@@ -52,6 +52,7 @@
#include <netinet/ip.h>
#include <netinet/tcp.h>
#ifdef INET6
+#include <netinet/icmp6.h>
#include <netinet/ip6.h>
#include <netinet6/ip6_var.h>
#endif
@@ -622,6 +623,19 @@ bsd_to_linux_tcp_user_timeout(u_int bsd_timeout)
return (bsd_timeout * 1000U);
}
+#ifdef INET6
+static int
+linux_to_bsd_icmp6_sockopt(int opt)
+{
+
+ switch (opt) {
+ case LINUX_ICMP6_FILTER:
+ return (ICMP6_FILTER);
+ }
+ return (-1);
+}
+#endif
+
static int
linux_to_bsd_msg_flags(int flags)
{
@@ -2175,6 +2189,29 @@ linux_setsockopt(struct thread *td, struct linux_setsockopt_args *args)
break;
}
break;
+#ifdef INET6
+ case IPPROTO_ICMPV6: {
+ struct icmp6_filter f;
+ int i;
+
+ name = linux_to_bsd_icmp6_sockopt(args->optname);
+ if (name != ICMP6_FILTER)
+ break;
+
+ if (args->optlen != sizeof(f))
+ return (EINVAL);
+
+ error = copyin(PTRIN(args->optval), &f, sizeof(f));
+ if (error)
+ return (error);
+
+ /* Linux uses opposite values for pass/block in ICMPv6 */
+ for (i = 0; i < nitems(f.icmp6_filt); i++)
+ f.icmp6_filt[i] = ~f.icmp6_filt[i];
+ return (kern_setsockopt(td, args->s, IPPROTO_ICMPV6,
+ ICMP6_FILTER, &f, UIO_SYSSPACE, sizeof(f)));
+ }
+#endif
case SOL_NETLINK:
name = args->optname;
break;
@@ -2435,6 +2472,36 @@ linux_getsockopt(struct thread *td, struct linux_getsockopt_args *args)
break;
}
break;
+#ifdef INET6
+ case IPPROTO_ICMPV6: {
+ struct icmp6_filter f;
+ int i;
+
+ name = linux_to_bsd_icmp6_sockopt(args->optname);
+ if (name != ICMP6_FILTER)
+ break;
+
+ error = copyin(PTRIN(args->optlen), &len, sizeof(len));
+ if (error)
+ return (error);
+ if (len != sizeof(f))
+ return (EINVAL);
+
+ error = kern_getsockopt(td, args->s, IPPROTO_ICMPV6,
+ ICMP6_FILTER, &f, UIO_SYSSPACE, &len);
+ if (error)
+ return (error);
+
+ /* Linux uses opposite values for pass/block in ICMPv6 */
+ for (i = 0; i < nitems(f.icmp6_filt); i++)
+ f.icmp6_filt[i] = ~f.icmp6_filt[i];
+ error = copyout(&f, PTRIN(args->optval), len);
+ if (error)
+ return (error);
+
+ return (copyout(&len, PTRIN(args->optlen), sizeof(socklen_t)));
+ }
+#endif
default:
name = -1;
break;
diff --git a/sys/compat/linux/linux_socket.h b/sys/compat/linux/linux_socket.h
index f2a96b3e7dcb..d30d68409496 100644
--- a/sys/compat/linux/linux_socket.h
+++ b/sys/compat/linux/linux_socket.h
@@ -324,6 +324,8 @@ int linux_accept(struct thread *td, struct linux_accept_args *args);
#define LINUX_TCP_MD5SIG 14
#define LINUX_TCP_USER_TIMEOUT 18
+#define LINUX_ICMP6_FILTER 1
+
struct l_ifmap {
l_ulong mem_start;
l_ulong mem_end;