On Fri, May 22, 2026 at 7:28 PM Pierrick Bouvier < [email protected]> wrote:
> On 5/18/2026 2:27 PM, Warner Losh wrote: > > Add the getsockopt(2) implementation handling SOL_SOCKET, IPPROTO_TCP, > > IPPROTO_IP, and IPPROTO_IPV6 levels with proper target-to-host option > > name translation. > > > > Signed-off-by: Stacey Son <[email protected]> > > Signed-off-by: Mikaël Urankar <[email protected]> > > Signed-off-by: Kyle Evans <[email protected]> > > Signed-off-by: Warner Losh <[email protected]> > > Assisted-by: Claude Opus 4.6 (1M context) > > --- > > bsd-user/freebsd/os-socket.h | 284 > +++++++++++++++++++++++++++++++++++++++++++ > > 1 file changed, 284 insertions(+) > > > > diff --git a/bsd-user/freebsd/os-socket.h b/bsd-user/freebsd/os-socket.h > > index 897a1f4d2f..f8521780c1 100644 > > --- a/bsd-user/freebsd/os-socket.h > > +++ b/bsd-user/freebsd/os-socket.h > > @@ -399,5 +399,289 @@ unimplemented: > > return ret; > > } > > > > +/* getsockopt(2) */ > > +static inline abi_long do_bsd_getsockopt(int sockfd, int level, int > optname, > > + abi_ulong optval_addr, abi_ulong optlen) > > +{ > > + abi_long ret; > > + int len, val; > > + socklen_t lv; > > + void *p; > > + > > + switch (level) { > > + case TARGET_SOL_SOCKET: > > + level = SOL_SOCKET; > > + switch (optname) { > > + > > + /* These don't just return a single integer */ > > + case TARGET_SO_LINGER: > > + case TARGET_SO_RCVTIMEO: > > + case TARGET_SO_SNDTIMEO: > > + case TARGET_SO_ACCEPTFILTER: > > + goto unimplemented; > > + > > + /* Options with 'int' argument. */ > > + case TARGET_SO_DEBUG: > > + optname = SO_DEBUG; > > + goto int_case; > > + > > + case TARGET_SO_REUSEADDR: > > + optname = SO_REUSEADDR; > > + goto int_case; > > + > > + case TARGET_SO_REUSEPORT: > > + optname = SO_REUSEPORT; > > + goto int_case; > > + > > + case TARGET_SO_TYPE: > > + optname = SO_TYPE; > > + goto int_case; > > + > > + case TARGET_SO_ERROR: > > + optname = SO_ERROR; > > + goto int_case; > > + > > + case TARGET_SO_DONTROUTE: > > + optname = SO_DONTROUTE; > > + goto int_case; > > + > > + case TARGET_SO_BROADCAST: > > + optname = SO_BROADCAST; > > + goto int_case; > > + > > + case TARGET_SO_SNDBUF: > > + optname = SO_SNDBUF; > > + goto int_case; > > + > > + case TARGET_SO_RCVBUF: > > + optname = SO_RCVBUF; > > + goto int_case; > > + > > + case TARGET_SO_KEEPALIVE: > > + optname = SO_KEEPALIVE; > > + goto int_case; > > + > > + case TARGET_SO_OOBINLINE: > > + optname = SO_OOBINLINE; > > + goto int_case; > > + > > + case TARGET_SO_TIMESTAMP: > > + optname = SO_TIMESTAMP; > > + goto int_case; > > + > > + case TARGET_SO_RCVLOWAT: > > + optname = SO_RCVLOWAT; > > + goto int_case; > > + > > + case TARGET_SO_LISTENINCQLEN: > > + optname = SO_LISTENINCQLEN; > > + goto int_case; > > + > > This duplicates what there is in previous patch. > We should add a target_to_host_sockopt that takes care of this. > I've actually reimplemented this more fundamentally. I fixed the annotations that the FreeBSD source code so that I can make a table of the options so I can automatically do all this. There may be a few regresions in what I've done, but they should be easy to fix later. Warner > > + default: > > +int_case: > > + if (get_user_u32(len, optlen)) { > > + return -TARGET_EFAULT; > > + } > > + if (len < 0) { > > + return -TARGET_EINVAL; > > + } > > + lv = sizeof(lv); > > + ret = get_errno(getsockopt(sockfd, level, optname, &val, > &lv)); > > + if (ret < 0) { > > + return ret; > > + } > > + if (len > lv) { > > + len = lv; > > + } > > + if (len == 4) { > > + if (put_user_u32(val, optval_addr)) { > > + return -TARGET_EFAULT; > > + } > > + } else { > > + if (put_user_u8(val, optval_addr)) { > > + return -TARGET_EFAULT; > > + } > > + } > > + if (put_user_u32(len, optlen)) { > > + return -TARGET_EFAULT; > > + } > > + break; > > + > > + } > > + break; > > + > > + case IPPROTO_TCP: > > + /* TCP options all take an 'int' value. */ > > + goto int_case; > > + > > + case IPPROTO_IP: > > + switch (optname) { > > + case IP_OPTIONS: > > + if (get_user_u32(len, optlen)) { > > + return -TARGET_EFAULT; > > + } > > + lv = (socklen_t)len; > > + p = lock_user(VERIFY_WRITE, optval_addr, len, 0); > > + if (p == NULL) { > > + return -TARGET_EFAULT; > > + } > > + ret = get_errno(getsockopt(sockfd, level, optname, p, &lv)); > > + unlock_user(p, optval_addr, len); > > + if (put_user_u32(lv, optlen)) { > > + return -TARGET_EFAULT; > > + } > > + break; > > + case IP_HDRINCL: > > + case IP_TOS: > > + case IP_TTL: > > + case IP_RECVOPTS: > > + case IP_RECVRETOPTS: > > + case IP_RECVDSTADDR: > > + > > + case IP_RETOPTS: > > +#if defined(IP_RECVTOS) > > + case IP_RECVTOS: > > +#endif > > + case IP_MULTICAST_TTL: > > + case IP_MULTICAST_LOOP: > > + case IP_PORTRANGE: > > + case IP_IPSEC_POLICY: > > + case IP_ONESBCAST: > > + case IP_BINDANY: > > + if (get_user_u32(len, optlen)) { > > + return -TARGET_EFAULT; > > + } > > + if (len < 0) { > > + return -TARGET_EINVAL; > > + } > > + lv = sizeof(lv); > > + ret = get_errno(getsockopt(sockfd, level, optname, > > + &val, &lv)); > > + if (ret < 0) { > > + return ret; > > + } > > + if (len < sizeof(int) && len > 0 && val >= 0 && > > + val < 255) { > > + len = 1; > > + if (put_user_u32(len, optlen) || > > + put_user_u8(val, optval_addr)) { > > + return -TARGET_EFAULT; > > + } > > + } else { > > + if (len > sizeof(int)) { > > + len = sizeof(int); > > + } > > + if (put_user_u32(len, optlen) || > > + put_user_u32(val, optval_addr)) { > > + return -TARGET_EFAULT; > > + } > > + } > > + break; > > + > > + default: > > + goto unimplemented; > > + } > > + break; > > + > > + case IPPROTO_IPV6: > > + switch (optname) { > > + case IPV6_UNICAST_HOPS: /* int; IP6 hops */ > > + case IPV6_MULTICAST_IF: /* u_int; set/get IP6 multicast > i/f */ > > + case IPV6_MULTICAST_HOPS: /* int; set/get IP6 multicast hops > */ > > + case IPV6_MULTICAST_LOOP: /* u_int; set/get IP6 multicast > loopback */ > > + case IPV6_PORTRANGE: /* int; range to choose for unspec > port */ > > + case IPV6_CHECKSUM: /* int; checksum offset for raw > socket */ > > + case IPV6_V6ONLY: /* bool; make AF_INET6 sockets v6 > only */ > > + case IPV6_RECVPKTINFO: /* bool; recv if, dst addr */ > > + case IPV6_RECVHOPLIMIT: /* bool; recv hop limit */ > > + case IPV6_RECVRTHDR: /* bool; recv routing header */ > > + case IPV6_RECVHOPOPTS: /* bool; recv hop-by-hop option */ > > + case IPV6_RECVDSTOPTS: /* bool; recv dst option after > rthdr */ > > + case IPV6_USE_MIN_MTU: /* bool; send packets at the > minimum MTU */ > > + case IPV6_RECVPATHMTU: /* bool; notify an according MTU */ > > + case IPV6_HOPLIMIT: /* int; send hop limit */ > > + case IPV6_RECVTCLASS: /* bool; recv traffic class values > */ > > + case IPV6_AUTOFLOWLABEL: /* bool; attach flowlabel > automagically */ > > + case IPV6_TCLASS: /* int; send traffic class value */ > > + case IPV6_DONTFRAG: /* bool; disable IPv6 fragmentation > */ > > + case IPV6_PREFER_TEMPADDR: /* int; prefer temporary addresses > */ > > + case IPV6_BINDANY: /* bool: allow bind to any address > */ > > +#ifdef IPV6_BINDMULTI > > + case IPV6_BINDMULTI: /* bool; allow multibind to same > addr/port*/ > > +#endif /* IPV6_BINDMULTI */ > > +#ifdef IPV6_RSS_LISTEN_BUCKET > > + case IPV6_RSS_LISTEN_BUCKET: /* int; set RSS listen bucket */ > > +#endif /* IPV6_RSS_LISTEN_BUCKET */ > > +#ifdef IPV6_FLOWID > > + case IPV6_FLOWID: /* int; flowid of given socket */ > > +#endif /* IPV6_FLOWID */ > > +#ifdef IPV6_FLOWTYPE > > + case IPV6_FLOWTYPE: /* int; flowtype of given socket */ > > +#endif /* IPV6_FLOWTYPE */ > > +#ifdef IPV6_RSSBUCKETID > > + case IPV6_RSSBUCKETID: /* int; RSS bucket ID of given > socket */ > > +#endif /* IPV6_RSSBUCKETID */ > > + if (get_user_u32(len, optlen)) { > > + return -TARGET_EFAULT; > > + } > > + if (len < 0) { > > + return -TARGET_EINVAL; > > + } > > + lv = sizeof(lv); > > + ret = get_errno(getsockopt(sockfd, level, optname, > > + &val, &lv)); > > + if (ret < 0) { > > + return ret; > > + } > > + if (len < sizeof(int) && len > 0 && val >= 0 && > > + val < 255) { > > + len = 1; > > + if (put_user_u32(len, optlen) || > > + put_user_u8(val, optval_addr)) { > > + return -TARGET_EFAULT; > > + } > > + } else { > > + if (len > sizeof(int)) { > > + len = sizeof(int); > > + } > > + if (put_user_u32(len, optlen) || > > + put_user_u32(val, optval_addr)) { > > + return -TARGET_EFAULT; > > + } > > + } > > + break; > > + > > + case IPV6_JOIN_GROUP: /* ipv6_mreq; join a group membership */ > > + case IPV6_LEAVE_GROUP: /* ipv6_mreq; leave a group membership > */ > > + case ICMP6_FILTER: /* icmp6_filter; icmp6 filter */ > > + case IPV6_IPSEC_POLICY: /* struct; get/set security policy */ > > + case IPV6_FW_ADD: /* add a firewall rule to chain */ > > + case IPV6_FW_DEL: /* delete a firewall rule from chain */ > > + case IPV6_FW_FLUSH: /* flush firewall rule chain */ > > + case IPV6_FW_ZERO: /* clear single/all firewall counter(s) > */ > > + case IPV6_FW_GET: /* get entire firewall rule chain */ > > + case IPV6_RTHDRDSTOPTS: /* ip6_dest; send dst option before > rthdr */ > > + case IPV6_PATHMTU: /* mtuinfo; get the current path MTU */ > > + case IPV6_PKTINFO: /* in6_pktinfo; send if, src addr */ > > + case IPV6_NEXTHOP: /* sockaddr; next hop addr */ > > + case IPV6_HOPOPTS: /* ip6_hbh; send hop-by-hop option */ > > + case IPV6_DSTOPTS: /* ip6_dest; send dst option befor > rthdr */ > > + case IPV6_RTHDR: /* ip6_rthdr; send routing header */ > > + case IPV6_MSFILTER: /* struct __msfilterreq; */ > > + default: > > + goto unimplemented; > > + } > > + break; > > + > > + default: > > +unimplemented: > > + gemu_log("getsockopt level=%d optname=%d not yet supported\n", > > + level, optname); > > + ret = -TARGET_EOPNOTSUPP; > > + break; > > + } > > + return ret; > > +} > > + > > > > #endif /* BSD_USER_FREEBSD_OS_SOCKET_H */ > > > >
