/* Highbits contain state. Allows to distinguish sockopt_seq * of listener and established: * s0 = new_listener() * sockopt(s0) - seq is 1 * s1 = accept(s0) - s1 inherits seq 1 if listener sk (s0) * sockopt(s0) - seq increments to 2 on s0 * sockopt(s1) // seq increments to 2 on s1 (different option) * new ssk completes join, inherits options from s0 // seq 2 * Needs sync from mptcp join logic, but ssk->seq == msk->seq * * Set High order bits to sk_state so ssk->seq == msk->seq test * will fail.
*/
staticint mptcp_setsockopt_sol_socket_int(struct mptcp_sock *msk, int optname,
sockptr_t optval, unsignedint optlen)
{ int val, ret;
ret = mptcp_get_int_option(msk, optval, optlen, &val); if (ret) return ret;
switch (optname) { case SO_KEEPALIVE: case SO_DEBUG: case SO_MARK: case SO_PRIORITY: case SO_SNDBUF: case SO_SNDBUFFORCE: case SO_RCVBUF: case SO_RCVBUFFORCE: return mptcp_sol_socket_intval(msk, optname, val); case SO_INCOMING_CPU:
mptcp_so_incoming_cpu(msk, val); return 0; case SO_TIMESTAMP_OLD: case SO_TIMESTAMP_NEW: case SO_TIMESTAMPNS_OLD: case SO_TIMESTAMPNS_NEW: return mptcp_setsockopt_sol_socket_tstamp(msk, optname, val);
}
switch (optname) { case SO_REUSEPORT: case SO_REUSEADDR: case SO_BINDTODEVICE: case SO_BINDTOIFINDEX:
lock_sock(sk);
ssk = __mptcp_nmpc_sk(msk); if (IS_ERR(ssk)) {
release_sock(sk); return PTR_ERR(ssk);
}
ret = sk_setsockopt(ssk, SOL_SOCKET, optname, optval, optlen); if (ret == 0) { if (optname == SO_REUSEPORT)
sk->sk_reuseport = ssk->sk_reuseport; elseif (optname == SO_REUSEADDR)
sk->sk_reuse = ssk->sk_reuse; elseif (optname == SO_BINDTODEVICE)
sk->sk_bound_dev_if = ssk->sk_bound_dev_if; elseif (optname == SO_BINDTOIFINDEX)
sk->sk_bound_dev_if = ssk->sk_bound_dev_if;
}
release_sock(sk); return ret; case SO_KEEPALIVE: case SO_PRIORITY: case SO_SNDBUF: case SO_SNDBUFFORCE: case SO_RCVBUF: case SO_RCVBUFFORCE: case SO_MARK: case SO_INCOMING_CPU: case SO_DEBUG: case SO_TIMESTAMP_OLD: case SO_TIMESTAMP_NEW: case SO_TIMESTAMPNS_OLD: case SO_TIMESTAMPNS_NEW: return mptcp_setsockopt_sol_socket_int(msk, optname, optval,
optlen); case SO_TIMESTAMPING_OLD: case SO_TIMESTAMPING_NEW: return mptcp_setsockopt_sol_socket_timestamping(msk, optname,
optval, optlen); case SO_LINGER: return mptcp_setsockopt_sol_socket_linger(msk, optval, optlen); case SO_RCVLOWAT: case SO_RCVTIMEO_OLD: case SO_RCVTIMEO_NEW: case SO_SNDTIMEO_OLD: case SO_SNDTIMEO_NEW: case SO_BUSY_POLL: case SO_PREFER_BUSY_POLL: case SO_BUSY_POLL_BUDGET: /* No need to copy: only relevant for msk */ return sock_setsockopt(sk->sk_socket, SOL_SOCKET, optname, optval, optlen); case SO_NO_CHECK: case SO_DONTROUTE: case SO_BROADCAST: case SO_BSDCOMPAT: case SO_PASSCRED: case SO_PASSPIDFD: case SO_PASSSEC: case SO_RXQ_OVFL: case SO_WIFI_STATUS: case SO_NOFCS: case SO_SELECT_ERR_QUEUE: return 0;
}
/* SO_OOBINLINE is not supported, let's avoid the related mess * SO_ATTACH_FILTER, SO_ATTACH_BPF, SO_ATTACH_REUSEPORT_CBPF, * SO_DETACH_REUSEPORT_BPF, SO_DETACH_FILTER, SO_LOCK_FILTER, * we must be careful with subflows * * SO_ATTACH_REUSEPORT_EBPF is not supported, at it checks * explicitly the sk_protocol field * * SO_PEEK_OFF is unsupported, as it is for plain TCP * SO_MAX_PACING_RATE is unsupported, we must be careful with subflows * SO_CNX_ADVICE is currently unsupported, could possibly be relevant, * but likely needs careful design * * SO_ZEROCOPY is currently unsupported, TODO in sndmsg * SO_TXTIME is currently unsupported
*/
return -EOPNOTSUPP;
}
staticint mptcp_setsockopt_v6(struct mptcp_sock *msk, int optname,
sockptr_t optval, unsignedint optlen)
{ struct sock *sk = (struct sock *)msk; int ret = -EOPNOTSUPP; struct sock *ssk;
switch (optname) { case IPV6_V6ONLY: case IPV6_TRANSPARENT: case IPV6_FREEBIND:
lock_sock(sk);
ssk = __mptcp_nmpc_sk(msk); if (IS_ERR(ssk)) {
release_sock(sk); return PTR_ERR(ssk);
}
ret = tcp_setsockopt(ssk, SOL_IPV6, optname, optval, optlen); if (ret != 0) {
release_sock(sk); return ret;
}
sockopt_seq_inc(msk);
switch (optname) { case IPV6_V6ONLY:
sk->sk_ipv6only = ssk->sk_ipv6only; break; case IPV6_TRANSPARENT:
inet_assign_bit(TRANSPARENT, sk,
inet_test_bit(TRANSPARENT, ssk)); break; case IPV6_FREEBIND:
inet_assign_bit(FREEBIND, sk,
inet_test_bit(FREEBIND, ssk)); break;
}
release_sock(sk); break;
}
return ret;
}
staticbool mptcp_supported_sockopt(int level, int optname)
{ if (level == SOL_IP) { switch (optname) { /* should work fine */ case IP_FREEBIND: case IP_TRANSPARENT: case IP_BIND_ADDRESS_NO_PORT: case IP_LOCAL_PORT_RANGE:
/* the following are control cmsg related */ case IP_PKTINFO: case IP_RECVTTL: case IP_RECVTOS: case IP_RECVOPTS: case IP_RETOPTS: case IP_PASSSEC: case IP_RECVORIGDSTADDR: case IP_CHECKSUM: case IP_RECVFRAGSIZE:
/* common stuff that need some love */ case IP_TOS: case IP_TTL: case IP_MTU_DISCOVER: case IP_RECVERR:
/* possibly less common may deserve some love */ case IP_MINTTL:
/* the following is apparently a no-op for plain TCP */ case IP_RECVERR_RFC4884: returntrue;
}
/* IP_OPTIONS is not supported, needs subflow care */ /* IP_HDRINCL, IP_NODEFRAG are not supported, RAW specific */ /* IP_MULTICAST_TTL, IP_MULTICAST_LOOP, IP_UNICAST_IF, * IP_ADD_MEMBERSHIP, IP_ADD_SOURCE_MEMBERSHIP, IP_DROP_MEMBERSHIP, * IP_DROP_SOURCE_MEMBERSHIP, IP_BLOCK_SOURCE, IP_UNBLOCK_SOURCE, * MCAST_JOIN_GROUP, MCAST_LEAVE_GROUP MCAST_JOIN_SOURCE_GROUP, * MCAST_LEAVE_SOURCE_GROUP, MCAST_BLOCK_SOURCE, MCAST_UNBLOCK_SOURCE, * MCAST_MSFILTER, IP_MULTICAST_ALL are not supported, better not deal * with mcast stuff
*/ /* IP_IPSEC_POLICY, IP_XFRM_POLICY are nut supported, unrelated here */ returnfalse;
} if (level == SOL_IPV6) { switch (optname) { case IPV6_V6ONLY:
/* the following are control cmsg related */ case IPV6_RECVPKTINFO: case IPV6_2292PKTINFO: case IPV6_RECVHOPLIMIT: case IPV6_2292HOPLIMIT: case IPV6_RECVRTHDR: case IPV6_2292RTHDR: case IPV6_RECVHOPOPTS: case IPV6_2292HOPOPTS: case IPV6_RECVDSTOPTS: case IPV6_2292DSTOPTS: case IPV6_RECVTCLASS: case IPV6_FLOWINFO: case IPV6_RECVPATHMTU: case IPV6_RECVORIGDSTADDR: case IPV6_RECVFRAGSIZE:
/* the following ones need some love but are quite common */ case IPV6_TCLASS: case IPV6_TRANSPARENT: case IPV6_FREEBIND: case IPV6_PKTINFO: case IPV6_2292PKTOPTIONS: case IPV6_UNICAST_HOPS: case IPV6_MTU_DISCOVER: case IPV6_MTU: case IPV6_RECVERR: case IPV6_FLOWINFO_SEND: case IPV6_FLOWLABEL_MGR: case IPV6_MINHOPCOUNT: case IPV6_DONTFRAG: case IPV6_AUTOFLOWLABEL:
/* the following one is a no-op for plain TCP */ case IPV6_RECVERR_RFC4884: returntrue;
}
/* IPV6_HOPOPTS, IPV6_RTHDRDSTOPTS, IPV6_RTHDR, IPV6_DSTOPTS are * not supported
*/ /* IPV6_MULTICAST_HOPS, IPV6_MULTICAST_LOOP, IPV6_UNICAST_IF, * IPV6_MULTICAST_IF, IPV6_ADDRFORM, * IPV6_ADD_MEMBERSHIP, IPV6_DROP_MEMBERSHIP, IPV6_JOIN_ANYCAST, * IPV6_LEAVE_ANYCAST, IPV6_MULTICAST_ALL, MCAST_JOIN_GROUP, MCAST_LEAVE_GROUP, * MCAST_JOIN_SOURCE_GROUP, MCAST_LEAVE_SOURCE_GROUP, * MCAST_BLOCK_SOURCE, MCAST_UNBLOCK_SOURCE, MCAST_MSFILTER * are not supported better not deal with mcast
*/ /* IPV6_ROUTER_ALERT, IPV6_ROUTER_ALERT_ISOLATE are not supported, since are evil */
/* IPV6_IPSEC_POLICY, IPV6_XFRM_POLICY are not supported */ /* IPV6_ADDR_PREFERENCES is not supported, we must be careful with subflows */ returnfalse;
} if (level == SOL_TCP) { switch (optname) { /* the following are no-op or should work just fine */ case TCP_THIN_DUPACK: case TCP_DEFER_ACCEPT:
/* the following need some love */ case TCP_MAXSEG: case TCP_NODELAY: case TCP_THIN_LINEAR_TIMEOUTS: case TCP_CONGESTION: case TCP_CORK: case TCP_KEEPIDLE: case TCP_KEEPINTVL: case TCP_KEEPCNT: case TCP_SYNCNT: case TCP_SAVE_SYN: case TCP_LINGER2: case TCP_WINDOW_CLAMP: case TCP_QUICKACK: case TCP_USER_TIMEOUT: case TCP_TIMESTAMP: case TCP_NOTSENT_LOWAT: case TCP_TX_DELAY: case TCP_INQ: case TCP_FASTOPEN: case TCP_FASTOPEN_CONNECT: case TCP_FASTOPEN_KEY: case TCP_FASTOPEN_NO_COOKIE: returntrue;
}
/* TCP_MD5SIG, TCP_MD5SIG_EXT are not supported, MD5 is not compatible with MPTCP */
/* TCP_REPAIR, TCP_REPAIR_QUEUE, TCP_QUEUE_SEQ, TCP_REPAIR_OPTIONS, * TCP_REPAIR_WINDOW are not supported, better avoid this mess
*/
} returnfalse;
}
ret = 0;
lock_sock(sk);
sockopt_seq_inc(msk);
mptcp_for_each_subflow(msk, subflow) { struct sock *ssk = mptcp_subflow_tcp_sock(subflow); int err;
lock_sock(ssk);
err = tcp_set_congestion_control(ssk, name, true, cap_net_admin); if (err < 0 && ret == 0)
ret = err;
subflow->setsockopt_seq = msk->setsockopt_seq;
release_sock(ssk);
}
if (ret == 0)
strscpy(msk->ca_name, name, sizeof(msk->ca_name));
release_sock(sk); return ret;
}
staticint __mptcp_setsockopt_set_val(struct mptcp_sock *msk, int max, int (*set_val)(struct sock *, int), int *msk_val, int val)
{ struct mptcp_subflow_context *subflow; int err = 0;
mptcp_for_each_subflow(msk, subflow) { struct sock *ssk = mptcp_subflow_tcp_sock(subflow); int ret;
staticint mptcp_setsockopt_v4(struct mptcp_sock *msk, int optname,
sockptr_t optval, unsignedint optlen)
{ switch (optname) { case IP_FREEBIND: case IP_TRANSPARENT: case IP_BIND_ADDRESS_NO_PORT: case IP_LOCAL_PORT_RANGE: return mptcp_setsockopt_sol_ip_set(msk, optname, optval, optlen); case IP_TOS: return mptcp_setsockopt_v4_set_tos(msk, optname, optval, optlen);
}
return -EOPNOTSUPP;
}
staticint mptcp_setsockopt_first_sf_only(struct mptcp_sock *msk, int level, int optname,
sockptr_t optval, unsignedint optlen)
{ struct sock *sk = (struct sock *)msk; struct sock *ssk; int ret;
/* Limit to first subflow, before the connection establishment */
lock_sock(sk);
ssk = __mptcp_nmpc_sk(msk); if (IS_ERR(ssk)) {
ret = PTR_ERR(ssk); goto unlock;
}
ret = tcp_setsockopt(ssk, level, optname, optval, optlen);
unlock:
release_sock(sk); return ret;
}
staticint mptcp_setsockopt_all_sf(struct mptcp_sock *msk, int level, int optname, sockptr_t optval, unsignedint optlen)
{ struct mptcp_subflow_context *subflow; int ret = 0;
ret = tcp_setsockopt(ssk, level, optname, optval, optlen); if (ret) break;
} return ret;
}
staticint mptcp_setsockopt_sol_tcp(struct mptcp_sock *msk, int optname,
sockptr_t optval, unsignedint optlen)
{ struct sock *sk = (void *)msk; int ret, val;
switch (optname) { case TCP_ULP: return -EOPNOTSUPP; case TCP_CONGESTION: return mptcp_setsockopt_sol_tcp_congestion(msk, optval, optlen); case TCP_DEFER_ACCEPT: /* See tcp.c: TCP_DEFER_ACCEPT does not fail */
mptcp_setsockopt_first_sf_only(msk, SOL_TCP, optname, optval, optlen); return 0; case TCP_FASTOPEN: case TCP_FASTOPEN_CONNECT: case TCP_FASTOPEN_KEY: case TCP_FASTOPEN_NO_COOKIE: return mptcp_setsockopt_first_sf_only(msk, SOL_TCP, optname,
optval, optlen);
}
ret = mptcp_get_int_option(msk, optval, optlen, &val); if (ret) return ret;
lock_sock(sk); switch (optname) { case TCP_INQ: if (val < 0 || val > 1)
ret = -EINVAL; else
msk->recvmsg_inq = !!val; break; case TCP_NOTSENT_LOWAT:
WRITE_ONCE(msk->notsent_lowat, val);
mptcp_write_space(sk); break; case TCP_CORK:
ret = __mptcp_setsockopt_sol_tcp_cork(msk, val); break; case TCP_NODELAY:
ret = __mptcp_setsockopt_sol_tcp_nodelay(msk, val); break; case TCP_KEEPIDLE:
ret = __mptcp_setsockopt_set_val(msk, MAX_TCP_KEEPIDLE,
&tcp_sock_set_keepidle_locked,
&msk->keepalive_idle, val); break; case TCP_KEEPINTVL:
ret = __mptcp_setsockopt_set_val(msk, MAX_TCP_KEEPINTVL,
&tcp_sock_set_keepintvl,
&msk->keepalive_intvl, val); break; case TCP_KEEPCNT:
ret = __mptcp_setsockopt_set_val(msk, MAX_TCP_KEEPCNT,
&tcp_sock_set_keepcnt,
&msk->keepalive_cnt,
val); break; case TCP_MAXSEG:
msk->maxseg = val;
ret = mptcp_setsockopt_all_sf(msk, SOL_TCP, optname, optval,
optlen); break; default:
ret = -ENOPROTOOPT;
}
release_sock(sk); return ret;
}
int mptcp_setsockopt(struct sock *sk, int level, int optname,
sockptr_t optval, unsignedint optlen)
{ struct mptcp_sock *msk = mptcp_sk(sk); struct sock *ssk;
pr_debug("msk=%p\n", msk);
if (level == SOL_SOCKET) return mptcp_setsockopt_sol_socket(msk, optname, optval, optlen);
if (!mptcp_supported_sockopt(level, optname)) return -ENOPROTOOPT;
/* @@ the meaning of setsockopt() when the socket is connected and * there are multiple subflows is not yet defined. It is up to the * MPTCP-level socket to configure the subflows until the subflow * is in TCP fallback, when TCP socket options are passed through * to the one remaining subflow.
*/
lock_sock(sk);
ssk = __mptcp_tcp_fallback(msk);
release_sock(sk); if (ssk) return tcp_setsockopt(ssk, level, optname, optval, optlen);
if (level == SOL_IP) return mptcp_setsockopt_v4(msk, optname, optval, optlen);
if (level == SOL_IPV6) return mptcp_setsockopt_v6(msk, optname, optval, optlen);
if (level == SOL_TCP) return mptcp_setsockopt_sol_tcp(msk, optname, optval, optlen);
return -EOPNOTSUPP;
}
staticint mptcp_getsockopt_first_sf_only(struct mptcp_sock *msk, int level, int optname, char __user *optval, int __user *optlen)
{ struct sock *sk = (struct sock *)msk; struct sock *ssk; int ret;
lock_sock(sk);
ssk = msk->first; if (ssk) goto get;
ssk = __mptcp_nmpc_sk(msk); if (IS_ERR(ssk)) {
ret = PTR_ERR(ssk); goto out;
}
get:
ret = tcp_getsockopt(ssk, level, optname, optval, optlen);
/* The following limits only make sense for the in-kernel PM */ if (mptcp_pm_is_kernel(msk)) {
info->mptcpi_subflows_max =
mptcp_pm_get_subflows_max(msk);
info->mptcpi_add_addr_signal_max =
mptcp_pm_get_add_addr_signal_max(msk);
info->mptcpi_add_addr_accepted_max =
mptcp_pm_get_add_addr_accept_max(msk);
info->mptcpi_local_addr_max =
mptcp_pm_get_local_addr_max(msk);
}
if (__mptcp_check_fallback(msk))
flags |= MPTCP_INFO_FLAG_FALLBACK; if (READ_ONCE(msk->can_ack))
flags |= MPTCP_INFO_FLAG_REMOTE_KEY_RECEIVED;
info->mptcpi_flags = flags;
if (copied)
copied += sfd->size_subflow_data; else
copied = copylen;
if (put_user(copied, optlen)) return -EFAULT;
if (copy_to_user(optval, sfd, copylen)) return -EFAULT;
return 0;
}
staticint mptcp_get_subflow_data(struct mptcp_subflow_data *sfd, char __user *optval, int __user *optlen)
{ int len, copylen;
if (get_user(len, optlen)) return -EFAULT;
/* if mptcp_subflow_data size is changed, need to adjust * this function to deal with programs using old version.
*/
BUILD_BUG_ON(sizeof(*sfd) != MIN_INFO_OPTLEN_SIZE);
len = mptcp_get_full_info(&mfi, optval, optlen); if (len < 0) return len;
/* don't bother filling the mptcp info if there is not enough * user-space-provided storage
*/ if (len > 0) {
mptcp_diag_fill_info(msk, &mfi.mptcp_info);
copylen += min_t(unsignedint, len, sizeof(struct mptcp_info));
}
/* fetch addr/tcp_info only if the user space buffers * are wide enough
*/
memset(&sfinfo, 0, sizeof(sfinfo));
sfinfo.id = subflow->subflow_id; if (mfi.size_sfinfo_user >
offsetof(struct mptcp_subflow_info, addrs))
mptcp_get_sub_addrs(ssk, &sfinfo.addrs); if (copy_to_user(sfinfoptr, &sfinfo, mfi.size_sfinfo_user)) goto fail_release;
if (mfi.size_tcpinfo_user) {
tcp_get_info(ssk, &tcp_info); if (copy_to_user(tcpinfoptr, &tcp_info,
mfi.size_tcpinfo_user)) goto fail_release;
}
mfi.num_subflows = sfcount; if (mptcp_put_full_info(&mfi, optval, copylen, optlen)) return -EFAULT;
return 0;
fail_release:
release_sock(sk); return -EFAULT;
}
staticint mptcp_put_int_option(struct mptcp_sock *msk, char __user *optval, int __user *optlen, int val)
{ int len;
if (get_user(len, optlen)) return -EFAULT; if (len < 0) return -EINVAL;
if (len < sizeof(int) && len > 0 && val >= 0 && val <= 255) { unsignedchar ucval = (unsignedchar)val;
len = 1; if (put_user(len, optlen)) return -EFAULT; if (copy_to_user(optval, &ucval, 1)) return -EFAULT;
} else {
len = min_t(unsignedint, len, sizeof(int)); if (put_user(len, optlen)) return -EFAULT; if (copy_to_user(optval, &val, len)) return -EFAULT;
}
return 0;
}
staticint mptcp_getsockopt_sol_tcp(struct mptcp_sock *msk, int optname, char __user *optval, int __user *optlen)
{ struct sock *sk = (void *)msk;
switch (optname) { case TCP_ULP: case TCP_CONGESTION: case TCP_INFO: case TCP_CC_INFO: case TCP_DEFER_ACCEPT: case TCP_FASTOPEN: case TCP_FASTOPEN_CONNECT: case TCP_FASTOPEN_KEY: case TCP_FASTOPEN_NO_COOKIE: return mptcp_getsockopt_first_sf_only(msk, SOL_TCP, optname,
optval, optlen); case TCP_INQ: return mptcp_put_int_option(msk, optval, optlen, msk->recvmsg_inq); case TCP_CORK: return mptcp_put_int_option(msk, optval, optlen, msk->cork); case TCP_NODELAY: return mptcp_put_int_option(msk, optval, optlen, msk->nodelay); case TCP_KEEPIDLE: return mptcp_put_int_option(msk, optval, optlen,
msk->keepalive_idle ? :
READ_ONCE(sock_net(sk)->ipv4.sysctl_tcp_keepalive_time) / HZ); case TCP_KEEPINTVL: return mptcp_put_int_option(msk, optval, optlen,
msk->keepalive_intvl ? :
READ_ONCE(sock_net(sk)->ipv4.sysctl_tcp_keepalive_intvl) / HZ); case TCP_KEEPCNT: return mptcp_put_int_option(msk, optval, optlen,
msk->keepalive_cnt ? :
READ_ONCE(sock_net(sk)->ipv4.sysctl_tcp_keepalive_probes)); case TCP_NOTSENT_LOWAT: return mptcp_put_int_option(msk, optval, optlen, msk->notsent_lowat); case TCP_IS_MPTCP: return mptcp_put_int_option(msk, optval, optlen, 1); case TCP_MAXSEG: return mptcp_getsockopt_first_sf_only(msk, SOL_TCP, optname,
optval, optlen);
} return -EOPNOTSUPP;
}
staticint mptcp_getsockopt_v4(struct mptcp_sock *msk, int optname, char __user *optval, int __user *optlen)
{ struct sock *sk = (void *)msk;
switch (optname) { case IP_TOS: return mptcp_put_int_option(msk, optval, optlen, READ_ONCE(inet_sk(sk)->tos)); case IP_FREEBIND: return mptcp_put_int_option(msk, optval, optlen,
inet_test_bit(FREEBIND, sk)); case IP_TRANSPARENT: return mptcp_put_int_option(msk, optval, optlen,
inet_test_bit(TRANSPARENT, sk)); case IP_BIND_ADDRESS_NO_PORT: return mptcp_put_int_option(msk, optval, optlen,
inet_test_bit(BIND_ADDRESS_NO_PORT, sk)); case IP_LOCAL_PORT_RANGE: return mptcp_put_int_option(msk, optval, optlen,
READ_ONCE(inet_sk(sk)->local_port_range));
}
return -EOPNOTSUPP;
}
staticint mptcp_getsockopt_v6(struct mptcp_sock *msk, int optname, char __user *optval, int __user *optlen)
{ struct sock *sk = (void *)msk;
switch (optname) { case IPV6_V6ONLY: return mptcp_put_int_option(msk, optval, optlen,
sk->sk_ipv6only); case IPV6_TRANSPARENT: return mptcp_put_int_option(msk, optval, optlen,
inet_test_bit(TRANSPARENT, sk)); case IPV6_FREEBIND: return mptcp_put_int_option(msk, optval, optlen,
inet_test_bit(FREEBIND, sk));
}
return -EOPNOTSUPP;
}
staticint mptcp_getsockopt_sol_mptcp(struct mptcp_sock *msk, int optname, char __user *optval, int __user *optlen)
{ switch (optname) { case MPTCP_INFO: return mptcp_getsockopt_info(msk, optval, optlen); case MPTCP_FULL_INFO: return mptcp_getsockopt_full_info(msk, optval, optlen); case MPTCP_TCPINFO: return mptcp_getsockopt_tcpinfo(msk, optval, optlen); case MPTCP_SUBFLOW_ADDRS: return mptcp_getsockopt_subflow_addrs(msk, optval, optlen);
}
return -EOPNOTSUPP;
}
int mptcp_getsockopt(struct sock *sk, int level, int optname, char __user *optval, int __user *option)
{ struct mptcp_sock *msk = mptcp_sk(sk); struct sock *ssk;
pr_debug("msk=%p\n", msk);
/* @@ the meaning of setsockopt() when the socket is connected and * there are multiple subflows is not yet defined. It is up to the * MPTCP-level socket to configure the subflows until the subflow * is in TCP fallback, when socket options are passed through * to the one remaining subflow.
*/
lock_sock(sk);
ssk = __mptcp_tcp_fallback(msk);
release_sock(sk); if (ssk) return tcp_getsockopt(ssk, level, optname, optval, option);
if (level == SOL_IP) return mptcp_getsockopt_v4(msk, optname, optval, option); if (level == SOL_IPV6) return mptcp_getsockopt_v6(msk, optname, optval, option); if (level == SOL_TCP) return mptcp_getsockopt_sol_tcp(msk, optname, optval, option); if (level == SOL_MPTCP) return mptcp_getsockopt_sol_mptcp(msk, optname, optval, option); return -EOPNOTSUPP;
}
/* subflows must ignore any latency-related settings: will not affect * the user-space - only the msk is relevant - but will foul the * mptcp scheduler
*/
tcp_sk(ssk)->notsent_lowat = UINT_MAX;
if (READ_ONCE(subflow->setsockopt_seq) != msk->setsockopt_seq) {
sync_socket_options(msk, ssk);
/* unfortunately this is different enough from the tcp version so * that we can't factor it out
*/ int mptcp_set_rcvlowat(struct sock *sk, int val)
{ struct mptcp_subflow_context *subflow; int space, cap;
/* bpf can land here with a wrong sk type */ if (sk->sk_protocol == IPPROTO_TCP) return -EINVAL;
if (sk->sk_userlocks & SOCK_RCVBUF_LOCK)
cap = sk->sk_rcvbuf >> 1; else
cap = READ_ONCE(sock_net(sk)->ipv4.sysctl_tcp_rmem[2]) >> 1;
val = min(val, cap);
WRITE_ONCE(sk->sk_rcvlowat, val ? : 1);
/* Check if we need to signal EPOLLIN right now */ if (mptcp_epollin_ready(sk))
sk->sk_data_ready(sk);
if (sk->sk_userlocks & SOCK_RCVBUF_LOCK) return 0;
space = mptcp_space_from_win(sk, val); if (space <= sk->sk_rcvbuf) return 0;
/* propagate the rcvbuf changes to all the subflows */
WRITE_ONCE(sk->sk_rcvbuf, space);
mptcp_for_each_subflow(mptcp_sk(sk), subflow) { struct sock *ssk = mptcp_subflow_tcp_sock(subflow); bool slow;
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.