// SPDX-License-Identifier: GPL-2.0
/* Multipath TCP
*
* Copyright (c) 2021, Red Hat.
*/
#define pr_fmt(fmt) "MPTCP: " fmt
#include <linux/kernel.h>
#include <linux/module.h>
#include <net/sock.h>
#include <net/protocol.h>
#include <net/tcp.h>
#include <net/mptcp.h>
#include "protocol.h"
#define MIN_INFO_OPTLEN_SIZE 16
static struct sock *__mptcp_tcp_fallback(struct mptcp_sock *msk)
{
msk_owned_by_me(msk);
if (likely(!__mptcp_check_fallback(msk)))
return NULL;
return msk->first;
}
static u32 sockopt_seq_reset(const struct sock *sk)
{
sock_owned_by_me(sk);
/* 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.
*/
return (u32)sk->sk_state << 24u;
}
static void sockopt_seq_inc(struct mptcp_sock *msk)
{
u32 seq = (msk->setsockopt_seq + 1) & 0x00ffffff;
msk->setsockopt_seq = sockopt_seq_reset((struct sock *)msk) + seq;
}
static int mptcp_get_int_option(struct mptcp_sock *msk, sockptr_t optval,
unsigned int optlen, int *val)
{
if (optlen < sizeof(int))
return -EINVAL;
if (copy_from_sockptr(val, optval, sizeof(*val)))
return -EFAULT;
return 0;
}
static void mptcp_sol_socket_sync_intval(struct mptcp_sock *msk, int optname, int val)
{
struct mptcp_subflow_context *subflow;
struct sock *sk = (struct sock *)msk;
lock_sock(sk);
sockopt_seq_inc(msk);
mptcp_for_each_subflow(msk, subflow) {
struct sock *ssk = mptcp_subflow_tcp_sock(subflow);
bool slow = lock_sock_fast(ssk);
switch (optname) {
case SO_DEBUG:
sock_valbool_flag(ssk, SOCK_DBG, !!val);
break;
case SO_KEEPALIVE:
if (ssk->sk_prot->keepalive)
ssk->sk_prot->keepalive(ssk, !!val);
sock_valbool_flag(ssk, SOCK_KEEPOPEN, !!val);
break;
case SO_PRIORITY:
ssk->sk_priority = val;
break;
case SO_SNDBUF:
case SO_SNDBUFFORCE:
ssk->sk_userlocks |= SOCK_SNDBUF_LOCK;
WRITE_ONCE(ssk->sk_sndbuf, sk->