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 */
> >
>
>

Reply via email to