On 5/18/2026 2:27 PM, Warner Losh wrote: > Add the setsockopt(2) implementation handling IPPROTO_TCP, IPPROTO_IP, > IPPROTO_IPV6, and SOL_SOCKET levels. Also add the TARGET_SO_* socket > option definitions and TARGET_SOL_SOCKET to syscall_defs.h. > > Signed-off-by: Stacey Son <[email protected]> > Signed-off-by: Mikaƫl Urankar <[email protected]> > Signed-off-by: Jessica Clarke <[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 | 261 > +++++++++++++++++++++++++++++++++++++++++++ > 1 file changed, 261 insertions(+) > > diff --git a/bsd-user/freebsd/os-socket.h b/bsd-user/freebsd/os-socket.h > index 279157dd05..897a1f4d2f 100644 > --- a/bsd-user/freebsd/os-socket.h > +++ b/bsd-user/freebsd/os-socket.h > @@ -138,5 +138,266 @@ static abi_long do_sendrecvmsg(int fd, abi_ulong > target_msg, > return ret; > } > > +/* setsockopt(2) */ > +static inline abi_long do_bsd_setsockopt(int sockfd, int level, int optname, > + abi_ulong optval_addr, socklen_t optlen) > +{ > + abi_long ret; > + int val; > + struct ip_mreqn *ip_mreq; > + void *p; > + > + switch (level) { > + case IPPROTO_TCP: > + /* TCP options all take an 'int' value. */ > + if (optlen < sizeof(uint32_t)) { > + return -TARGET_EINVAL; > + } > + if (get_user_u32(val, optval_addr)) { > + return -TARGET_EFAULT; > + } > + ret = get_errno(setsockopt(sockfd, level, optname, &val, > sizeof(val))); > + break; > + > + case IPPROTO_IP: > + switch (optname) { > + case IP_OPTIONS: > + p = lock_user(VERIFY_READ, optval_addr, optlen, 1); > + if (p == NULL) { > + return -TARGET_EFAULT; > + } > + ret = get_errno(setsockopt(sockfd, level, optname, p, optlen)); > + unlock_user(p, optval_addr, 0); > + break; > + case IP_HDRINCL:/* int; header is included with data */ > + case IP_TOS: /* int; IP type of service and preced. */ > + case IP_TTL: /* int; IP time to live */ > + case IP_RECVOPTS: /* bool; receive all IP opts w/dgram */ > + case IP_RECVRETOPTS: /* bool; receive IP opts for response */ > + case IP_RECVDSTADDR: /* bool; receive IP dst addr w/dgram */ > + case IP_MULTICAST_IF:/* u_char; set/get IP multicast i/f */ > + case IP_MULTICAST_TTL:/* u_char; set/get IP multicast ttl */ > + case IP_MULTICAST_LOOP:/*u_char;set/get IP multicast loopback */ > + case IP_PORTRANGE: /* int; range to choose for unspec port */ > + case IP_RECVIF: /* bool; receive reception if w/dgram */ > + case IP_IPSEC_POLICY: /* int; set/get security policy */ > + case IP_RECVTTL: /* bool; receive reception TTL w/dgram */ > + val = 0; > + if (optlen >= sizeof(uint32_t)) { > + if (get_user_u32(val, optval_addr)) { > + return -TARGET_EFAULT; > + } > + } else if (optlen >= 1) { > + if (get_user_u8(val, optval_addr)) { > + return -TARGET_EFAULT; > + } > + } > + ret = get_errno(setsockopt(sockfd, level, optname, &val, > + sizeof(val))); > + break; > + > + case IP_ADD_MEMBERSHIP: /*ip_mreq; add an IP group membership */ > + case IP_DROP_MEMBERSHIP:/*ip_mreq; drop an IP group membership*/ > + if (optlen < sizeof(struct target_ip_mreq) || > + optlen > sizeof(struct target_ip_mreqn)) { > + return -TARGET_EINVAL; > + } > + ip_mreq = (struct ip_mreqn *) alloca(optlen); > + ret = target_to_host_ip_mreq(ip_mreq, optval_addr, optlen); > + if (is_error(ret)) { > + return -TARGET_EFAULT; > + } > + ret = get_errno(setsockopt(sockfd, level, optname, ip_mreq, > + optlen)); > + 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 */ > + val = 0; > + if (optlen >= sizeof(uint32_t)) { > + if (get_user_u32(val, optval_addr)) { > + return -TARGET_EFAULT; > + } > + } else if (optlen >= 1) { > + if (get_user_u8(val, optval_addr)) { > + return -TARGET_EFAULT; > + } > + } > + ret = get_errno(setsockopt(sockfd, level, optname, &val, > + sizeof(val))); > + 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; > + > + case TARGET_SOL_SOCKET: > + switch (optname) { > + /* Options with 'int' argument. */ > + case TARGET_SO_DEBUG: > + optname = SO_DEBUG; > + break; > + > + case TARGET_SO_REUSEADDR: > + optname = SO_REUSEADDR; > + break; > + > + case TARGET_SO_REUSEPORT: > + optname = SO_REUSEPORT; > + break; > + > + case TARGET_SO_KEEPALIVE: > + optname = SO_KEEPALIVE; > + break; > + > + case TARGET_SO_DONTROUTE: > + optname = SO_DONTROUTE; > + break; > + > + case TARGET_SO_LINGER: > + optname = SO_LINGER; > + break; > + > + case TARGET_SO_BROADCAST: > + optname = SO_BROADCAST; > + break; > + > + case TARGET_SO_OOBINLINE: > + optname = SO_OOBINLINE; > + break; > + > + case TARGET_SO_SNDBUF: > + optname = SO_SNDBUF; > + break; > + > + case TARGET_SO_RCVBUF: > + optname = SO_RCVBUF; > + break; > + > + case TARGET_SO_SNDLOWAT: > + optname = SO_RCVLOWAT; > + break; > + > + case TARGET_SO_RCVLOWAT: > + optname = SO_RCVLOWAT; > + break; > + > + case TARGET_SO_SNDTIMEO: > + optname = SO_SNDTIMEO; > + break; > + > + case TARGET_SO_RCVTIMEO: > + optname = SO_RCVTIMEO; > + break; > + > + case TARGET_SO_ACCEPTFILTER: > + goto unimplemented; > + > + case TARGET_SO_NOSIGPIPE: > + optname = SO_NOSIGPIPE; > + break; > + > + case TARGET_SO_TIMESTAMP: > + optname = SO_TIMESTAMP; > + break; > + > + case TARGET_SO_BINTIME: > + optname = SO_BINTIME; > + break; > + > + case TARGET_SO_ERROR: > + optname = SO_ERROR; > + break; > + > + case TARGET_SO_SETFIB: > + optname = SO_SETFIB; > + break; > + > +#ifdef SO_USER_COOKIE > + case TARGET_SO_USER_COOKIE: > + optname = SO_USER_COOKIE; > + break; > +#endif > + default: > + goto unimplemented; > + } > + if (optlen < sizeof(uint32_t)) { > + return -TARGET_EINVAL; > + } > + if (get_user_u32(val, optval_addr)) { > + return -TARGET_EFAULT; > + } > + ret = get_errno(setsockopt(sockfd, SOL_SOCKET, optname, &val, > + sizeof(val))); > + break; > + default: > +unimplemented: > + gemu_log("Unsupported setsockopt level=%d optname=%d\n", > + level, optname); > + ret = -TARGET_ENOPROTOOPT; > + } > + > + return ret; > +} > + > > #endif /* BSD_USER_FREEBSD_OS_SOCKET_H */ >
See comment on next patch to solve duplicate for translating sockopt.
