| Message ID | 20260518-misc-2026q2-v2-13-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 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 <sson@FreeBSD.org> > Signed-off-by: Mikaël Urankar <mikael.urankar@gmail.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 | 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. > + 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 */ >
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; + + 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 */