| Message ID | 20260518-misc-2026q2-v2-12-6c16fe448301@bsdimp.com |
|---|---|
| State | New |
| Headers | show |
| Series | bsd-user: Upstream most of the remaining system calls | expand |
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 <sson@FreeBSD.org> > Signed-off-by: Mikaël Urankar <mikael.urankar@gmail.com> > Signed-off-by: Jessica Clarke <jrtc27@jrtc27.com> > Signed-off-by: Kyle Evans <kevans@FreeBSD.org> > Signed-off-by: Warner Losh <imp@bsdimp.com> > 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.
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 */