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 | 258 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 258 insertions(+) diff --git a/bsd-user/freebsd/os-socket.h b/bsd-user/freebsd/os-socket.h index 279157dd05..c565170d09 100644 --- a/bsd-user/freebsd/os-socket.h +++ b/bsd-user/freebsd/os-socket.h @@ -138,5 +138,263 @@ 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, 0); + 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); + target_to_host_ip_mreq(ip_mreq, optval_addr, optlen); + 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 */ -- 2.52.0
