Merge branch 'mptcp-timestamps'
Mat Martineau says: ==================== mptcp: Add timestamp support Enable the SO_TIMESTAMP and SO_TIMESTAMPING socket options for MPTCP sockets and add receive path cmsg support for timestamps. Patches 1, 2, and 5 expose existing sock and tcp helpers for timestamps (no new EXPORT_SYMBOLS()s). Patch 3 propagates timestamp options to subflows. Patch 4 cleans up MPTCP handling of SOL_SOCKET options. Patch 6 adds timestamp csmg data when receiving on sockets that have been configured for timestamps. Patch 7 adds self test coverage for timestamps. ==================== Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
@@ -2743,6 +2743,9 @@ static inline bool sk_dev_equal_l3scope(struct sock *sk, int dif)
|
|||||||
void sock_def_readable(struct sock *sk);
|
void sock_def_readable(struct sock *sk);
|
||||||
|
|
||||||
int sock_bindtoindex(struct sock *sk, int ifindex, bool lock_sk);
|
int sock_bindtoindex(struct sock *sk, int ifindex, bool lock_sk);
|
||||||
|
void sock_set_timestamp(struct sock *sk, int optname, bool valbool);
|
||||||
|
int sock_set_timestamping(struct sock *sk, int optname, int val);
|
||||||
|
|
||||||
void sock_enable_timestamps(struct sock *sk);
|
void sock_enable_timestamps(struct sock *sk);
|
||||||
void sock_no_linger(struct sock *sk);
|
void sock_no_linger(struct sock *sk);
|
||||||
void sock_set_keepalive(struct sock *sk);
|
void sock_set_keepalive(struct sock *sk);
|
||||||
|
|||||||
@@ -412,6 +412,10 @@ int tcp_recvmsg(struct sock *sk, struct msghdr *msg, size_t len, int nonblock,
|
|||||||
int flags, int *addr_len);
|
int flags, int *addr_len);
|
||||||
int tcp_set_rcvlowat(struct sock *sk, int val);
|
int tcp_set_rcvlowat(struct sock *sk, int val);
|
||||||
int tcp_set_window_clamp(struct sock *sk, int val);
|
int tcp_set_window_clamp(struct sock *sk, int val);
|
||||||
|
void tcp_update_recv_tstamps(struct sk_buff *skb,
|
||||||
|
struct scm_timestamping_internal *tss);
|
||||||
|
void tcp_recv_timestamp(struct msghdr *msg, const struct sock *sk,
|
||||||
|
struct scm_timestamping_internal *tss);
|
||||||
void tcp_data_ready(struct sock *sk);
|
void tcp_data_ready(struct sock *sk);
|
||||||
#ifdef CONFIG_MMU
|
#ifdef CONFIG_MMU
|
||||||
int tcp_mmap(struct file *file, struct socket *sock,
|
int tcp_mmap(struct file *file, struct socket *sock,
|
||||||
|
|||||||
@@ -776,6 +776,58 @@ void sock_enable_timestamps(struct sock *sk)
|
|||||||
}
|
}
|
||||||
EXPORT_SYMBOL(sock_enable_timestamps);
|
EXPORT_SYMBOL(sock_enable_timestamps);
|
||||||
|
|
||||||
|
void sock_set_timestamp(struct sock *sk, int optname, bool valbool)
|
||||||
|
{
|
||||||
|
switch (optname) {
|
||||||
|
case SO_TIMESTAMP_OLD:
|
||||||
|
__sock_set_timestamps(sk, valbool, false, false);
|
||||||
|
break;
|
||||||
|
case SO_TIMESTAMP_NEW:
|
||||||
|
__sock_set_timestamps(sk, valbool, true, false);
|
||||||
|
break;
|
||||||
|
case SO_TIMESTAMPNS_OLD:
|
||||||
|
__sock_set_timestamps(sk, valbool, false, true);
|
||||||
|
break;
|
||||||
|
case SO_TIMESTAMPNS_NEW:
|
||||||
|
__sock_set_timestamps(sk, valbool, true, true);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int sock_set_timestamping(struct sock *sk, int optname, int val)
|
||||||
|
{
|
||||||
|
if (val & ~SOF_TIMESTAMPING_MASK)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
if (val & SOF_TIMESTAMPING_OPT_ID &&
|
||||||
|
!(sk->sk_tsflags & SOF_TIMESTAMPING_OPT_ID)) {
|
||||||
|
if (sk->sk_protocol == IPPROTO_TCP &&
|
||||||
|
sk->sk_type == SOCK_STREAM) {
|
||||||
|
if ((1 << sk->sk_state) &
|
||||||
|
(TCPF_CLOSE | TCPF_LISTEN))
|
||||||
|
return -EINVAL;
|
||||||
|
sk->sk_tskey = tcp_sk(sk)->snd_una;
|
||||||
|
} else {
|
||||||
|
sk->sk_tskey = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (val & SOF_TIMESTAMPING_OPT_STATS &&
|
||||||
|
!(val & SOF_TIMESTAMPING_OPT_TSONLY))
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
sk->sk_tsflags = val;
|
||||||
|
sock_valbool_flag(sk, SOCK_TSTAMP_NEW, optname == SO_TIMESTAMPING_NEW);
|
||||||
|
|
||||||
|
if (val & SOF_TIMESTAMPING_RX_SOFTWARE)
|
||||||
|
sock_enable_timestamp(sk,
|
||||||
|
SOCK_TIMESTAMPING_RX_SOFTWARE);
|
||||||
|
else
|
||||||
|
sock_disable_timestamp(sk,
|
||||||
|
(1UL << SOCK_TIMESTAMPING_RX_SOFTWARE));
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
void sock_set_keepalive(struct sock *sk)
|
void sock_set_keepalive(struct sock *sk)
|
||||||
{
|
{
|
||||||
lock_sock(sk);
|
lock_sock(sk);
|
||||||
@@ -989,54 +1041,15 @@ set_sndbuf:
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case SO_TIMESTAMP_OLD:
|
case SO_TIMESTAMP_OLD:
|
||||||
__sock_set_timestamps(sk, valbool, false, false);
|
|
||||||
break;
|
|
||||||
case SO_TIMESTAMP_NEW:
|
case SO_TIMESTAMP_NEW:
|
||||||
__sock_set_timestamps(sk, valbool, true, false);
|
|
||||||
break;
|
|
||||||
case SO_TIMESTAMPNS_OLD:
|
case SO_TIMESTAMPNS_OLD:
|
||||||
__sock_set_timestamps(sk, valbool, false, true);
|
|
||||||
break;
|
|
||||||
case SO_TIMESTAMPNS_NEW:
|
case SO_TIMESTAMPNS_NEW:
|
||||||
__sock_set_timestamps(sk, valbool, true, true);
|
sock_set_timestamp(sk, valbool, optname);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case SO_TIMESTAMPING_NEW:
|
case SO_TIMESTAMPING_NEW:
|
||||||
case SO_TIMESTAMPING_OLD:
|
case SO_TIMESTAMPING_OLD:
|
||||||
if (val & ~SOF_TIMESTAMPING_MASK) {
|
ret = sock_set_timestamping(sk, optname, val);
|
||||||
ret = -EINVAL;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (val & SOF_TIMESTAMPING_OPT_ID &&
|
|
||||||
!(sk->sk_tsflags & SOF_TIMESTAMPING_OPT_ID)) {
|
|
||||||
if (sk->sk_protocol == IPPROTO_TCP &&
|
|
||||||
sk->sk_type == SOCK_STREAM) {
|
|
||||||
if ((1 << sk->sk_state) &
|
|
||||||
(TCPF_CLOSE | TCPF_LISTEN)) {
|
|
||||||
ret = -EINVAL;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
sk->sk_tskey = tcp_sk(sk)->snd_una;
|
|
||||||
} else {
|
|
||||||
sk->sk_tskey = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (val & SOF_TIMESTAMPING_OPT_STATS &&
|
|
||||||
!(val & SOF_TIMESTAMPING_OPT_TSONLY)) {
|
|
||||||
ret = -EINVAL;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
sk->sk_tsflags = val;
|
|
||||||
sock_valbool_flag(sk, SOCK_TSTAMP_NEW, optname == SO_TIMESTAMPING_NEW);
|
|
||||||
|
|
||||||
if (val & SOF_TIMESTAMPING_RX_SOFTWARE)
|
|
||||||
sock_enable_timestamp(sk,
|
|
||||||
SOCK_TIMESTAMPING_RX_SOFTWARE);
|
|
||||||
else
|
|
||||||
sock_disable_timestamp(sk,
|
|
||||||
(1UL << SOCK_TIMESTAMPING_RX_SOFTWARE));
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case SO_RCVLOWAT:
|
case SO_RCVLOWAT:
|
||||||
|
|||||||
@@ -1738,7 +1738,7 @@ int tcp_set_rcvlowat(struct sock *sk, int val)
|
|||||||
}
|
}
|
||||||
EXPORT_SYMBOL(tcp_set_rcvlowat);
|
EXPORT_SYMBOL(tcp_set_rcvlowat);
|
||||||
|
|
||||||
static void tcp_update_recv_tstamps(struct sk_buff *skb,
|
void tcp_update_recv_tstamps(struct sk_buff *skb,
|
||||||
struct scm_timestamping_internal *tss)
|
struct scm_timestamping_internal *tss)
|
||||||
{
|
{
|
||||||
if (skb->tstamp)
|
if (skb->tstamp)
|
||||||
@@ -2024,8 +2024,6 @@ static int tcp_zerocopy_vm_insert_batch(struct vm_area_struct *vma,
|
|||||||
}
|
}
|
||||||
|
|
||||||
#define TCP_VALID_ZC_MSG_FLAGS (TCP_CMSG_TS)
|
#define TCP_VALID_ZC_MSG_FLAGS (TCP_CMSG_TS)
|
||||||
static void tcp_recv_timestamp(struct msghdr *msg, const struct sock *sk,
|
|
||||||
struct scm_timestamping_internal *tss);
|
|
||||||
static void tcp_zc_finalize_rx_tstamp(struct sock *sk,
|
static void tcp_zc_finalize_rx_tstamp(struct sock *sk,
|
||||||
struct tcp_zerocopy_receive *zc,
|
struct tcp_zerocopy_receive *zc,
|
||||||
struct scm_timestamping_internal *tss)
|
struct scm_timestamping_internal *tss)
|
||||||
@@ -2197,7 +2195,7 @@ out:
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* Similar to __sock_recv_timestamp, but does not require an skb */
|
/* Similar to __sock_recv_timestamp, but does not require an skb */
|
||||||
static void tcp_recv_timestamp(struct msghdr *msg, const struct sock *sk,
|
void tcp_recv_timestamp(struct msghdr *msg, const struct sock *sk,
|
||||||
struct scm_timestamping_internal *tss)
|
struct scm_timestamping_internal *tss)
|
||||||
{
|
{
|
||||||
int new_tstamp = sock_flag(sk, SOCK_TSTAMP_NEW);
|
int new_tstamp = sock_flag(sk, SOCK_TSTAMP_NEW);
|
||||||
|
|||||||
@@ -39,10 +39,15 @@ struct mptcp_skb_cb {
|
|||||||
u64 map_seq;
|
u64 map_seq;
|
||||||
u64 end_seq;
|
u64 end_seq;
|
||||||
u32 offset;
|
u32 offset;
|
||||||
|
u8 has_rxtstamp:1;
|
||||||
};
|
};
|
||||||
|
|
||||||
#define MPTCP_SKB_CB(__skb) ((struct mptcp_skb_cb *)&((__skb)->cb[0]))
|
#define MPTCP_SKB_CB(__skb) ((struct mptcp_skb_cb *)&((__skb)->cb[0]))
|
||||||
|
|
||||||
|
enum {
|
||||||
|
MPTCP_CMSG_TS = BIT(0),
|
||||||
|
};
|
||||||
|
|
||||||
static struct percpu_counter mptcp_sockets_allocated;
|
static struct percpu_counter mptcp_sockets_allocated;
|
||||||
|
|
||||||
static void __mptcp_destroy_sock(struct sock *sk);
|
static void __mptcp_destroy_sock(struct sock *sk);
|
||||||
@@ -272,6 +277,7 @@ static bool __mptcp_move_skb(struct mptcp_sock *msk, struct sock *ssk,
|
|||||||
struct mptcp_subflow_context *subflow = mptcp_subflow_ctx(ssk);
|
struct mptcp_subflow_context *subflow = mptcp_subflow_ctx(ssk);
|
||||||
struct sock *sk = (struct sock *)msk;
|
struct sock *sk = (struct sock *)msk;
|
||||||
struct sk_buff *tail;
|
struct sk_buff *tail;
|
||||||
|
bool has_rxtstamp;
|
||||||
|
|
||||||
__skb_unlink(skb, &ssk->sk_receive_queue);
|
__skb_unlink(skb, &ssk->sk_receive_queue);
|
||||||
|
|
||||||
@@ -287,6 +293,8 @@ static bool __mptcp_move_skb(struct mptcp_sock *msk, struct sock *ssk,
|
|||||||
goto drop;
|
goto drop;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
has_rxtstamp = TCP_SKB_CB(skb)->has_rxtstamp;
|
||||||
|
|
||||||
/* the skb map_seq accounts for the skb offset:
|
/* the skb map_seq accounts for the skb offset:
|
||||||
* mptcp_subflow_get_mapped_dsn() is based on the current tp->copied_seq
|
* mptcp_subflow_get_mapped_dsn() is based on the current tp->copied_seq
|
||||||
* value
|
* value
|
||||||
@@ -294,6 +302,7 @@ static bool __mptcp_move_skb(struct mptcp_sock *msk, struct sock *ssk,
|
|||||||
MPTCP_SKB_CB(skb)->map_seq = mptcp_subflow_get_mapped_dsn(subflow);
|
MPTCP_SKB_CB(skb)->map_seq = mptcp_subflow_get_mapped_dsn(subflow);
|
||||||
MPTCP_SKB_CB(skb)->end_seq = MPTCP_SKB_CB(skb)->map_seq + copy_len;
|
MPTCP_SKB_CB(skb)->end_seq = MPTCP_SKB_CB(skb)->map_seq + copy_len;
|
||||||
MPTCP_SKB_CB(skb)->offset = offset;
|
MPTCP_SKB_CB(skb)->offset = offset;
|
||||||
|
MPTCP_SKB_CB(skb)->has_rxtstamp = has_rxtstamp;
|
||||||
|
|
||||||
if (MPTCP_SKB_CB(skb)->map_seq == msk->ack_seq) {
|
if (MPTCP_SKB_CB(skb)->map_seq == msk->ack_seq) {
|
||||||
/* in sequence */
|
/* in sequence */
|
||||||
@@ -1757,7 +1766,9 @@ static void mptcp_wait_data(struct sock *sk, long *timeo)
|
|||||||
|
|
||||||
static int __mptcp_recvmsg_mskq(struct mptcp_sock *msk,
|
static int __mptcp_recvmsg_mskq(struct mptcp_sock *msk,
|
||||||
struct msghdr *msg,
|
struct msghdr *msg,
|
||||||
size_t len, int flags)
|
size_t len, int flags,
|
||||||
|
struct scm_timestamping_internal *tss,
|
||||||
|
int *cmsg_flags)
|
||||||
{
|
{
|
||||||
struct sk_buff *skb, *tmp;
|
struct sk_buff *skb, *tmp;
|
||||||
int copied = 0;
|
int copied = 0;
|
||||||
@@ -1777,6 +1788,11 @@ static int __mptcp_recvmsg_mskq(struct mptcp_sock *msk,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (MPTCP_SKB_CB(skb)->has_rxtstamp) {
|
||||||
|
tcp_update_recv_tstamps(skb, tss);
|
||||||
|
*cmsg_flags |= MPTCP_CMSG_TS;
|
||||||
|
}
|
||||||
|
|
||||||
copied += count;
|
copied += count;
|
||||||
|
|
||||||
if (count < data_len) {
|
if (count < data_len) {
|
||||||
@@ -1964,7 +1980,8 @@ static int mptcp_recvmsg(struct sock *sk, struct msghdr *msg, size_t len,
|
|||||||
int nonblock, int flags, int *addr_len)
|
int nonblock, int flags, int *addr_len)
|
||||||
{
|
{
|
||||||
struct mptcp_sock *msk = mptcp_sk(sk);
|
struct mptcp_sock *msk = mptcp_sk(sk);
|
||||||
int copied = 0;
|
struct scm_timestamping_internal tss;
|
||||||
|
int copied = 0, cmsg_flags = 0;
|
||||||
int target;
|
int target;
|
||||||
long timeo;
|
long timeo;
|
||||||
|
|
||||||
@@ -1986,7 +2003,7 @@ static int mptcp_recvmsg(struct sock *sk, struct msghdr *msg, size_t len,
|
|||||||
while (copied < len) {
|
while (copied < len) {
|
||||||
int bytes_read;
|
int bytes_read;
|
||||||
|
|
||||||
bytes_read = __mptcp_recvmsg_mskq(msk, msg, len - copied, flags);
|
bytes_read = __mptcp_recvmsg_mskq(msk, msg, len - copied, flags, &tss, &cmsg_flags);
|
||||||
if (unlikely(bytes_read < 0)) {
|
if (unlikely(bytes_read < 0)) {
|
||||||
if (!copied)
|
if (!copied)
|
||||||
copied = bytes_read;
|
copied = bytes_read;
|
||||||
@@ -2067,6 +2084,11 @@ static int mptcp_recvmsg(struct sock *sk, struct msghdr *msg, size_t len,
|
|||||||
set_bit(MPTCP_DATA_READY, &msk->flags);
|
set_bit(MPTCP_DATA_READY, &msk->flags);
|
||||||
}
|
}
|
||||||
out_err:
|
out_err:
|
||||||
|
if (cmsg_flags && copied >= 0) {
|
||||||
|
if (cmsg_flags & MPTCP_CMSG_TS)
|
||||||
|
tcp_recv_timestamp(msg, sk, &tss);
|
||||||
|
}
|
||||||
|
|
||||||
pr_debug("msk=%p data_ready=%d rx queue empty=%d copied=%d",
|
pr_debug("msk=%p data_ready=%d rx queue empty=%d copied=%d",
|
||||||
msk, test_bit(MPTCP_DATA_READY, &msk->flags),
|
msk, test_bit(MPTCP_DATA_READY, &msk->flags),
|
||||||
skb_queue_empty_lockless(&sk->sk_receive_queue), copied);
|
skb_queue_empty_lockless(&sk->sk_receive_queue), copied);
|
||||||
|
|||||||
@@ -140,6 +140,43 @@ static void mptcp_so_incoming_cpu(struct mptcp_sock *msk, int val)
|
|||||||
mptcp_sol_socket_sync_intval(msk, SO_INCOMING_CPU, val);
|
mptcp_sol_socket_sync_intval(msk, SO_INCOMING_CPU, val);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int mptcp_setsockopt_sol_socket_tstamp(struct mptcp_sock *msk, int optname, int val)
|
||||||
|
{
|
||||||
|
sockptr_t optval = KERNEL_SOCKPTR(&val);
|
||||||
|
struct mptcp_subflow_context *subflow;
|
||||||
|
struct sock *sk = (struct sock *)msk;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
ret = sock_setsockopt(sk->sk_socket, SOL_SOCKET, optname,
|
||||||
|
optval, sizeof(val));
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
lock_sock(sk);
|
||||||
|
mptcp_for_each_subflow(msk, subflow) {
|
||||||
|
struct sock *ssk = mptcp_subflow_tcp_sock(subflow);
|
||||||
|
bool slow = lock_sock_fast(ssk);
|
||||||
|
|
||||||
|
switch (optname) {
|
||||||
|
case SO_TIMESTAMP_OLD:
|
||||||
|
case SO_TIMESTAMP_NEW:
|
||||||
|
case SO_TIMESTAMPNS_OLD:
|
||||||
|
case SO_TIMESTAMPNS_NEW:
|
||||||
|
sock_set_timestamp(sk, optname, !!val);
|
||||||
|
break;
|
||||||
|
case SO_TIMESTAMPING_NEW:
|
||||||
|
case SO_TIMESTAMPING_OLD:
|
||||||
|
sock_set_timestamping(sk, optname, val);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
unlock_sock_fast(ssk, slow);
|
||||||
|
}
|
||||||
|
|
||||||
|
release_sock(sk);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static int mptcp_setsockopt_sol_socket_int(struct mptcp_sock *msk, int optname,
|
static int mptcp_setsockopt_sol_socket_int(struct mptcp_sock *msk, int optname,
|
||||||
sockptr_t optval, unsigned int optlen)
|
sockptr_t optval, unsigned int optlen)
|
||||||
{
|
{
|
||||||
@@ -164,6 +201,13 @@ static int mptcp_setsockopt_sol_socket_int(struct mptcp_sock *msk, int optname,
|
|||||||
case SO_INCOMING_CPU:
|
case SO_INCOMING_CPU:
|
||||||
mptcp_so_incoming_cpu(msk, val);
|
mptcp_so_incoming_cpu(msk, val);
|
||||||
return 0;
|
return 0;
|
||||||
|
case SO_TIMESTAMP_OLD:
|
||||||
|
case SO_TIMESTAMP_NEW:
|
||||||
|
case SO_TIMESTAMPNS_OLD:
|
||||||
|
case SO_TIMESTAMPNS_NEW:
|
||||||
|
case SO_TIMESTAMPING_OLD:
|
||||||
|
case SO_TIMESTAMPING_NEW:
|
||||||
|
return mptcp_setsockopt_sol_socket_tstamp(msk, optname, val);
|
||||||
}
|
}
|
||||||
|
|
||||||
return -ENOPROTOOPT;
|
return -ENOPROTOOPT;
|
||||||
@@ -251,9 +295,23 @@ static int mptcp_setsockopt_sol_socket(struct mptcp_sock *msk, int optname,
|
|||||||
case SO_MARK:
|
case SO_MARK:
|
||||||
case SO_INCOMING_CPU:
|
case SO_INCOMING_CPU:
|
||||||
case SO_DEBUG:
|
case SO_DEBUG:
|
||||||
|
case SO_TIMESTAMP_OLD:
|
||||||
|
case SO_TIMESTAMP_NEW:
|
||||||
|
case SO_TIMESTAMPNS_OLD:
|
||||||
|
case SO_TIMESTAMPNS_NEW:
|
||||||
|
case SO_TIMESTAMPING_OLD:
|
||||||
|
case SO_TIMESTAMPING_NEW:
|
||||||
return mptcp_setsockopt_sol_socket_int(msk, optname, optval, optlen);
|
return mptcp_setsockopt_sol_socket_int(msk, optname, optval, optlen);
|
||||||
case SO_LINGER:
|
case SO_LINGER:
|
||||||
return mptcp_setsockopt_sol_socket_linger(msk, optval, optlen);
|
return mptcp_setsockopt_sol_socket_linger(msk, optval, optlen);
|
||||||
|
case SO_RCVLOWAT:
|
||||||
|
case SO_RCVTIMEO_OLD:
|
||||||
|
case SO_RCVTIMEO_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_NO_CHECK:
|
||||||
case SO_DONTROUTE:
|
case SO_DONTROUTE:
|
||||||
case SO_BROADCAST:
|
case SO_BROADCAST:
|
||||||
@@ -267,7 +325,24 @@ static int mptcp_setsockopt_sol_socket(struct mptcp_sock *msk, int optname,
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
return sock_setsockopt(sk->sk_socket, SOL_SOCKET, optname, optval, optlen);
|
/* 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;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int mptcp_setsockopt_v6(struct mptcp_sock *msk, int optname,
|
static int mptcp_setsockopt_v6(struct mptcp_sock *msk, int optname,
|
||||||
@@ -299,72 +374,6 @@ static int mptcp_setsockopt_v6(struct mptcp_sock *msk, int optname,
|
|||||||
|
|
||||||
static bool mptcp_supported_sockopt(int level, int optname)
|
static bool mptcp_supported_sockopt(int level, int optname)
|
||||||
{
|
{
|
||||||
if (level == SOL_SOCKET) {
|
|
||||||
switch (optname) {
|
|
||||||
case SO_DEBUG:
|
|
||||||
case SO_REUSEPORT:
|
|
||||||
case SO_REUSEADDR:
|
|
||||||
|
|
||||||
/* the following ones need a better implementation,
|
|
||||||
* but are quite common we want to preserve them
|
|
||||||
*/
|
|
||||||
case SO_BINDTODEVICE:
|
|
||||||
case SO_SNDBUF:
|
|
||||||
case SO_SNDBUFFORCE:
|
|
||||||
case SO_RCVBUF:
|
|
||||||
case SO_RCVBUFFORCE:
|
|
||||||
case SO_KEEPALIVE:
|
|
||||||
case SO_PRIORITY:
|
|
||||||
case SO_LINGER:
|
|
||||||
case SO_TIMESTAMP_OLD:
|
|
||||||
case SO_TIMESTAMP_NEW:
|
|
||||||
case SO_TIMESTAMPNS_OLD:
|
|
||||||
case SO_TIMESTAMPNS_NEW:
|
|
||||||
case SO_TIMESTAMPING_OLD:
|
|
||||||
case SO_TIMESTAMPING_NEW:
|
|
||||||
case SO_RCVLOWAT:
|
|
||||||
case SO_RCVTIMEO_OLD:
|
|
||||||
case SO_RCVTIMEO_NEW:
|
|
||||||
case SO_SNDTIMEO_OLD:
|
|
||||||
case SO_SNDTIMEO_NEW:
|
|
||||||
case SO_MARK:
|
|
||||||
case SO_INCOMING_CPU:
|
|
||||||
case SO_BINDTOIFINDEX:
|
|
||||||
case SO_BUSY_POLL:
|
|
||||||
case SO_PREFER_BUSY_POLL:
|
|
||||||
case SO_BUSY_POLL_BUDGET:
|
|
||||||
|
|
||||||
/* next ones are no-op for plain TCP */
|
|
||||||
case SO_NO_CHECK:
|
|
||||||
case SO_DONTROUTE:
|
|
||||||
case SO_BROADCAST:
|
|
||||||
case SO_BSDCOMPAT:
|
|
||||||
case SO_PASSCRED:
|
|
||||||
case SO_PASSSEC:
|
|
||||||
case SO_RXQ_OVFL:
|
|
||||||
case SO_WIFI_STATUS:
|
|
||||||
case SO_NOFCS:
|
|
||||||
case SO_SELECT_ERR_QUEUE:
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 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 false;
|
|
||||||
}
|
|
||||||
if (level == SOL_IP) {
|
if (level == SOL_IP) {
|
||||||
switch (optname) {
|
switch (optname) {
|
||||||
/* should work fine */
|
/* should work fine */
|
||||||
@@ -574,12 +583,12 @@ int mptcp_setsockopt(struct sock *sk, int level, int optname,
|
|||||||
|
|
||||||
pr_debug("msk=%p", msk);
|
pr_debug("msk=%p", msk);
|
||||||
|
|
||||||
if (!mptcp_supported_sockopt(level, optname))
|
|
||||||
return -ENOPROTOOPT;
|
|
||||||
|
|
||||||
if (level == SOL_SOCKET)
|
if (level == SOL_SOCKET)
|
||||||
return mptcp_setsockopt_sol_socket(msk, optname, optval, optlen);
|
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
|
/* @@ the meaning of setsockopt() when the socket is connected and
|
||||||
* there are multiple subflows is not yet defined. It is up to the
|
* there are multiple subflows is not yet defined. It is up to the
|
||||||
* MPTCP-level socket to configure the subflows until the subflow
|
* MPTCP-level socket to configure the subflows until the subflow
|
||||||
|
|||||||
@@ -6,6 +6,7 @@
|
|||||||
#include <limits.h>
|
#include <limits.h>
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
#include <stdarg.h>
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
@@ -25,6 +26,7 @@
|
|||||||
#include <netinet/in.h>
|
#include <netinet/in.h>
|
||||||
|
|
||||||
#include <linux/tcp.h>
|
#include <linux/tcp.h>
|
||||||
|
#include <linux/time_types.h>
|
||||||
|
|
||||||
extern int optind;
|
extern int optind;
|
||||||
|
|
||||||
@@ -66,6 +68,13 @@ static unsigned int cfg_do_w;
|
|||||||
static int cfg_wait;
|
static int cfg_wait;
|
||||||
static uint32_t cfg_mark;
|
static uint32_t cfg_mark;
|
||||||
|
|
||||||
|
struct cfg_cmsg_types {
|
||||||
|
unsigned int cmsg_enabled:1;
|
||||||
|
unsigned int timestampns:1;
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct cfg_cmsg_types cfg_cmsg_types;
|
||||||
|
|
||||||
static void die_usage(void)
|
static void die_usage(void)
|
||||||
{
|
{
|
||||||
fprintf(stderr, "Usage: mptcp_connect [-6] [-u] [-s MPTCP|TCP] [-p port] [-m mode]"
|
fprintf(stderr, "Usage: mptcp_connect [-6] [-u] [-s MPTCP|TCP] [-p port] [-m mode]"
|
||||||
@@ -80,11 +89,22 @@ static void die_usage(void)
|
|||||||
fprintf(stderr, "\t-M mark -- set socket packet mark\n");
|
fprintf(stderr, "\t-M mark -- set socket packet mark\n");
|
||||||
fprintf(stderr, "\t-u -- check mptcp ulp\n");
|
fprintf(stderr, "\t-u -- check mptcp ulp\n");
|
||||||
fprintf(stderr, "\t-w num -- wait num sec before closing the socket\n");
|
fprintf(stderr, "\t-w num -- wait num sec before closing the socket\n");
|
||||||
|
fprintf(stderr, "\t-c cmsg -- test cmsg type <cmsg>\n");
|
||||||
fprintf(stderr,
|
fprintf(stderr,
|
||||||
"\t-P [saveWithPeek|saveAfterPeek] -- save data with/after MSG_PEEK form tcp socket\n");
|
"\t-P [saveWithPeek|saveAfterPeek] -- save data with/after MSG_PEEK form tcp socket\n");
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void xerror(const char *fmt, ...)
|
||||||
|
{
|
||||||
|
va_list ap;
|
||||||
|
|
||||||
|
va_start(ap, fmt);
|
||||||
|
vfprintf(stderr, fmt, ap);
|
||||||
|
va_end(ap);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
static void handle_signal(int nr)
|
static void handle_signal(int nr)
|
||||||
{
|
{
|
||||||
quit = true;
|
quit = true;
|
||||||
@@ -338,6 +358,58 @@ static size_t do_write(const int fd, char *buf, const size_t len)
|
|||||||
return offset;
|
return offset;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void process_cmsg(struct msghdr *msgh)
|
||||||
|
{
|
||||||
|
struct __kernel_timespec ts;
|
||||||
|
bool ts_found = false;
|
||||||
|
struct cmsghdr *cmsg;
|
||||||
|
|
||||||
|
for (cmsg = CMSG_FIRSTHDR(msgh); cmsg ; cmsg = CMSG_NXTHDR(msgh, cmsg)) {
|
||||||
|
if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SO_TIMESTAMPNS_NEW) {
|
||||||
|
memcpy(&ts, CMSG_DATA(cmsg), sizeof(ts));
|
||||||
|
ts_found = true;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cfg_cmsg_types.timestampns) {
|
||||||
|
if (!ts_found)
|
||||||
|
xerror("TIMESTAMPNS not present\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static ssize_t do_recvmsg_cmsg(const int fd, char *buf, const size_t len)
|
||||||
|
{
|
||||||
|
char msg_buf[8192];
|
||||||
|
struct iovec iov = {
|
||||||
|
.iov_base = buf,
|
||||||
|
.iov_len = len,
|
||||||
|
};
|
||||||
|
struct msghdr msg = {
|
||||||
|
.msg_iov = &iov,
|
||||||
|
.msg_iovlen = 1,
|
||||||
|
.msg_control = msg_buf,
|
||||||
|
.msg_controllen = sizeof(msg_buf),
|
||||||
|
};
|
||||||
|
int flags = 0;
|
||||||
|
int ret = recvmsg(fd, &msg, flags);
|
||||||
|
|
||||||
|
if (ret <= 0)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
if (msg.msg_controllen && !cfg_cmsg_types.cmsg_enabled)
|
||||||
|
xerror("got %lu bytes of cmsg data, expected 0\n",
|
||||||
|
(unsigned long)msg.msg_controllen);
|
||||||
|
|
||||||
|
if (msg.msg_controllen == 0 && cfg_cmsg_types.cmsg_enabled)
|
||||||
|
xerror("%s\n", "got no cmsg data");
|
||||||
|
|
||||||
|
if (msg.msg_controllen)
|
||||||
|
process_cmsg(&msg);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
static ssize_t do_rnd_read(const int fd, char *buf, const size_t len)
|
static ssize_t do_rnd_read(const int fd, char *buf, const size_t len)
|
||||||
{
|
{
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
@@ -357,6 +429,8 @@ static ssize_t do_rnd_read(const int fd, char *buf, const size_t len)
|
|||||||
} else if (cfg_peek == CFG_AFTER_PEEK) {
|
} else if (cfg_peek == CFG_AFTER_PEEK) {
|
||||||
ret = recv(fd, buf, cap, MSG_PEEK);
|
ret = recv(fd, buf, cap, MSG_PEEK);
|
||||||
ret = (ret < 0) ? ret : read(fd, buf, cap);
|
ret = (ret < 0) ? ret : read(fd, buf, cap);
|
||||||
|
} else if (cfg_cmsg_types.cmsg_enabled) {
|
||||||
|
ret = do_recvmsg_cmsg(fd, buf, cap);
|
||||||
} else {
|
} else {
|
||||||
ret = read(fd, buf, cap);
|
ret = read(fd, buf, cap);
|
||||||
}
|
}
|
||||||
@@ -786,6 +860,48 @@ static void init_rng(void)
|
|||||||
srand(foo);
|
srand(foo);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void xsetsockopt(int fd, int level, int optname, const void *optval, socklen_t optlen)
|
||||||
|
{
|
||||||
|
int err;
|
||||||
|
|
||||||
|
err = setsockopt(fd, level, optname, optval, optlen);
|
||||||
|
if (err) {
|
||||||
|
perror("setsockopt");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void apply_cmsg_types(int fd, const struct cfg_cmsg_types *cmsg)
|
||||||
|
{
|
||||||
|
static const unsigned int on = 1;
|
||||||
|
|
||||||
|
if (cmsg->timestampns)
|
||||||
|
xsetsockopt(fd, SOL_SOCKET, SO_TIMESTAMPNS_NEW, &on, sizeof(on));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void parse_cmsg_types(const char *type)
|
||||||
|
{
|
||||||
|
char *next = strchr(type, ',');
|
||||||
|
unsigned int len = 0;
|
||||||
|
|
||||||
|
cfg_cmsg_types.cmsg_enabled = 1;
|
||||||
|
|
||||||
|
if (next) {
|
||||||
|
parse_cmsg_types(next + 1);
|
||||||
|
len = next - type;
|
||||||
|
} else {
|
||||||
|
len = strlen(type);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (strncmp(type, "TIMESTAMPNS", len) == 0) {
|
||||||
|
cfg_cmsg_types.timestampns = 1;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
fprintf(stderr, "Unrecognized cmsg option %s\n", type);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
int main_loop(void)
|
int main_loop(void)
|
||||||
{
|
{
|
||||||
int fd;
|
int fd;
|
||||||
@@ -801,6 +917,8 @@ int main_loop(void)
|
|||||||
set_rcvbuf(fd, cfg_rcvbuf);
|
set_rcvbuf(fd, cfg_rcvbuf);
|
||||||
if (cfg_sndbuf)
|
if (cfg_sndbuf)
|
||||||
set_sndbuf(fd, cfg_sndbuf);
|
set_sndbuf(fd, cfg_sndbuf);
|
||||||
|
if (cfg_cmsg_types.cmsg_enabled)
|
||||||
|
apply_cmsg_types(fd, &cfg_cmsg_types);
|
||||||
|
|
||||||
return copyfd_io(0, fd, 1);
|
return copyfd_io(0, fd, 1);
|
||||||
}
|
}
|
||||||
@@ -887,7 +1005,7 @@ static void parse_opts(int argc, char **argv)
|
|||||||
{
|
{
|
||||||
int c;
|
int c;
|
||||||
|
|
||||||
while ((c = getopt(argc, argv, "6jr:lp:s:hut:m:S:R:w:M:P:")) != -1) {
|
while ((c = getopt(argc, argv, "6jr:lp:s:hut:m:S:R:w:M:P:c:")) != -1) {
|
||||||
switch (c) {
|
switch (c) {
|
||||||
case 'j':
|
case 'j':
|
||||||
cfg_join = true;
|
cfg_join = true;
|
||||||
@@ -943,6 +1061,9 @@ static void parse_opts(int argc, char **argv)
|
|||||||
case 'P':
|
case 'P':
|
||||||
cfg_peek = parse_peek(optarg);
|
cfg_peek = parse_peek(optarg);
|
||||||
break;
|
break;
|
||||||
|
case 'c':
|
||||||
|
parse_cmsg_types(optarg);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -976,6 +1097,8 @@ int main(int argc, char *argv[])
|
|||||||
set_sndbuf(fd, cfg_sndbuf);
|
set_sndbuf(fd, cfg_sndbuf);
|
||||||
if (cfg_mark)
|
if (cfg_mark)
|
||||||
set_mark(fd, cfg_mark);
|
set_mark(fd, cfg_mark);
|
||||||
|
if (cfg_cmsg_types.cmsg_enabled)
|
||||||
|
apply_cmsg_types(fd, &cfg_cmsg_types);
|
||||||
|
|
||||||
return main_loop_s(fd);
|
return main_loop_s(fd);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -178,7 +178,7 @@ do_transfer()
|
|||||||
|
|
||||||
timeout ${timeout_test} \
|
timeout ${timeout_test} \
|
||||||
ip netns exec ${listener_ns} \
|
ip netns exec ${listener_ns} \
|
||||||
$mptcp_connect -t ${timeout_poll} -l -M 1 -p $port -s ${srv_proto} \
|
$mptcp_connect -t ${timeout_poll} -l -M 1 -p $port -s ${srv_proto} -c TIMESTAMPNS \
|
||||||
${local_addr} < "$sin" > "$sout" &
|
${local_addr} < "$sin" > "$sout" &
|
||||||
spid=$!
|
spid=$!
|
||||||
|
|
||||||
@@ -186,7 +186,7 @@ do_transfer()
|
|||||||
|
|
||||||
timeout ${timeout_test} \
|
timeout ${timeout_test} \
|
||||||
ip netns exec ${connector_ns} \
|
ip netns exec ${connector_ns} \
|
||||||
$mptcp_connect -t ${timeout_poll} -M 2 -p $port -s ${cl_proto} \
|
$mptcp_connect -t ${timeout_poll} -M 2 -p $port -s ${cl_proto} -c TIMESTAMPNS \
|
||||||
$connect_addr < "$cin" > "$cout" &
|
$connect_addr < "$cin" > "$cout" &
|
||||||
|
|
||||||
cpid=$!
|
cpid=$!
|
||||||
|
|||||||
Reference in New Issue
Block a user