diff options
| author | Ricardo Branco <rbranco@suse.de> | 2026-04-12 11:01:47 +0000 |
|---|---|---|
| committer | Pouria Mousavizadeh Tehrani <pouria@FreeBSD.org> | 2026-04-17 07:22:36 +0000 |
| commit | 64e2715f5d8f03ab7e763a49a0e102d755673810 (patch) | |
| tree | e21b191c7520f8e0e027c5bec8cc038f6c9e4fb4 | |
| parent | 3a54aa3b0911bef15e014b8a8185e116efb0a918 (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.c | 67 | ||||
| -rw-r--r-- | sys/compat/linux/linux_socket.h | 2 |
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; |
