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

-- 
2.52.0


Reply via email to