Commit 9d2355ba authored by David S. Miller's avatar David S. Miller

Merge branch 'cmsg_timestamp'

Soheil Hassas Yeganeh says:

====================
add TX timestamping via cmsg

This patch series aim at enabling TX timestamping via cmsg.

Currently, to occasionally sample TX timestamping on a socket,
applications need to call setsockopt twice: first for enabling
timestamps and then for disabling them. This is an unnecessary
overhead. With cmsg, in contrast, applications can sample TX
timestamps per sendmsg().

This patch series adds the code for processing SO_TIMESTAMPING
for cmsg's of the SOL_SOCKET level, and adds the glue code for
TCP, UDP, and RAW for both IPv4 and IPv6. This implementation
supports overriding timestamp generation flags (i.e.,
SOF_TIMESTAMPING_TX_*) but not timestamp reporting flags.
Applications must still enable timestamp reporting via
setsockopt to receive timestamps.

This series does not change existing timestamping behavior for
applications that are using socket options.

I will follow up with another patch to enable timestamping for
active TFO (client-side TCP Fast Open) and also setting packet
mark via cmsgs.

Thanks!

Changes in v2:
        - Replace u32 with __u32 in the documentation.

Changes in v3:
	- Fix the broken build for L2TP (due to changes
	  in IPv6).
====================
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents 833716e0 fd91e12f
...@@ -44,11 +44,17 @@ timeval of SO_TIMESTAMP (ms). ...@@ -44,11 +44,17 @@ timeval of SO_TIMESTAMP (ms).
Supports multiple types of timestamp requests. As a result, this Supports multiple types of timestamp requests. As a result, this
socket option takes a bitmap of flags, not a boolean. In socket option takes a bitmap of flags, not a boolean. In
err = setsockopt(fd, SOL_SOCKET, SO_TIMESTAMPING, (void *) val, &val); err = setsockopt(fd, SOL_SOCKET, SO_TIMESTAMPING, (void *) val,
sizeof(val));
val is an integer with any of the following bits set. Setting other val is an integer with any of the following bits set. Setting other
bit returns EINVAL and does not change the current state. bit returns EINVAL and does not change the current state.
The socket option configures timestamp generation for individual
sk_buffs (1.3.1), timestamp reporting to the socket's error
queue (1.3.2) and options (1.3.3). Timestamp generation can also
be enabled for individual sendmsg calls using cmsg (1.3.4).
1.3.1 Timestamp Generation 1.3.1 Timestamp Generation
...@@ -71,13 +77,16 @@ SOF_TIMESTAMPING_RX_SOFTWARE: ...@@ -71,13 +77,16 @@ SOF_TIMESTAMPING_RX_SOFTWARE:
kernel receive stack. kernel receive stack.
SOF_TIMESTAMPING_TX_HARDWARE: SOF_TIMESTAMPING_TX_HARDWARE:
Request tx timestamps generated by the network adapter. Request tx timestamps generated by the network adapter. This flag
can be enabled via both socket options and control messages.
SOF_TIMESTAMPING_TX_SOFTWARE: SOF_TIMESTAMPING_TX_SOFTWARE:
Request tx timestamps when data leaves the kernel. These timestamps Request tx timestamps when data leaves the kernel. These timestamps
are generated in the device driver as close as possible, but always are generated in the device driver as close as possible, but always
prior to, passing the packet to the network interface. Hence, they prior to, passing the packet to the network interface. Hence, they
require driver support and may not be available for all devices. require driver support and may not be available for all devices.
This flag can be enabled via both socket options and control messages.
SOF_TIMESTAMPING_TX_SCHED: SOF_TIMESTAMPING_TX_SCHED:
Request tx timestamps prior to entering the packet scheduler. Kernel Request tx timestamps prior to entering the packet scheduler. Kernel
...@@ -90,7 +99,8 @@ SOF_TIMESTAMPING_TX_SCHED: ...@@ -90,7 +99,8 @@ SOF_TIMESTAMPING_TX_SCHED:
machines with virtual devices where a transmitted packet travels machines with virtual devices where a transmitted packet travels
through multiple devices and, hence, multiple packet schedulers, through multiple devices and, hence, multiple packet schedulers,
a timestamp is generated at each layer. This allows for fine a timestamp is generated at each layer. This allows for fine
grained measurement of queuing delay. grained measurement of queuing delay. This flag can be enabled
via both socket options and control messages.
SOF_TIMESTAMPING_TX_ACK: SOF_TIMESTAMPING_TX_ACK:
Request tx timestamps when all data in the send buffer has been Request tx timestamps when all data in the send buffer has been
...@@ -99,6 +109,7 @@ SOF_TIMESTAMPING_TX_ACK: ...@@ -99,6 +109,7 @@ SOF_TIMESTAMPING_TX_ACK:
over-report measurement, because the timestamp is generated when all over-report measurement, because the timestamp is generated when all
data up to and including the buffer at send() was acknowledged: the data up to and including the buffer at send() was acknowledged: the
cumulative acknowledgment. The mechanism ignores SACK and FACK. cumulative acknowledgment. The mechanism ignores SACK and FACK.
This flag can be enabled via both socket options and control messages.
1.3.2 Timestamp Reporting 1.3.2 Timestamp Reporting
...@@ -183,6 +194,37 @@ having access to the contents of the original packet, so cannot be ...@@ -183,6 +194,37 @@ having access to the contents of the original packet, so cannot be
combined with SOF_TIMESTAMPING_OPT_TSONLY. combined with SOF_TIMESTAMPING_OPT_TSONLY.
1.3.4. Enabling timestamps via control messages
In addition to socket options, timestamp generation can be requested
per write via cmsg, only for SOF_TIMESTAMPING_TX_* (see Section 1.3.1).
Using this feature, applications can sample timestamps per sendmsg()
without paying the overhead of enabling and disabling timestamps via
setsockopt:
struct msghdr *msg;
...
cmsg = CMSG_FIRSTHDR(msg);
cmsg->cmsg_level = SOL_SOCKET;
cmsg->cmsg_type = SO_TIMESTAMPING;
cmsg->cmsg_len = CMSG_LEN(sizeof(__u32));
*((__u32 *) CMSG_DATA(cmsg)) = SOF_TIMESTAMPING_TX_SCHED |
SOF_TIMESTAMPING_TX_SOFTWARE |
SOF_TIMESTAMPING_TX_ACK;
err = sendmsg(fd, msg, 0);
The SOF_TIMESTAMPING_TX_* flags set via cmsg will override
the SOF_TIMESTAMPING_TX_* flags set via setsockopt.
Moreover, applications must still enable timestamp reporting via
setsockopt to receive timestamps:
__u32 val = SOF_TIMESTAMPING_SOFTWARE |
SOF_TIMESTAMPING_OPT_ID /* or any other flag */;
err = setsockopt(fd, SOL_SOCKET, SO_TIMESTAMPING, (void *) val,
sizeof(val));
1.4 Bytestream Timestamps 1.4 Bytestream Timestamps
The SO_TIMESTAMPING interface supports timestamping of bytes in a The SO_TIMESTAMPING interface supports timestamping of bytes in a
......
...@@ -861,7 +861,8 @@ static netdev_tx_t tun_net_xmit(struct sk_buff *skb, struct net_device *dev) ...@@ -861,7 +861,8 @@ static netdev_tx_t tun_net_xmit(struct sk_buff *skb, struct net_device *dev)
goto drop; goto drop;
if (skb->sk && sk_fullsock(skb->sk)) { if (skb->sk && sk_fullsock(skb->sk)) {
sock_tx_timestamp(skb->sk, &skb_shinfo(skb)->tx_flags); sock_tx_timestamp(skb->sk, skb->sk->sk_tsflags,
&skb_shinfo(skb)->tx_flags);
sw_tx_timestamp(skb); sw_tx_timestamp(skb);
} }
......
...@@ -56,6 +56,7 @@ static inline unsigned int ip_hdrlen(const struct sk_buff *skb) ...@@ -56,6 +56,7 @@ static inline unsigned int ip_hdrlen(const struct sk_buff *skb)
} }
struct ipcm_cookie { struct ipcm_cookie {
struct sockcm_cookie sockc;
__be32 addr; __be32 addr;
int oif; int oif;
struct ip_options_rcu *opt; struct ip_options_rcu *opt;
...@@ -550,7 +551,7 @@ int ip_options_rcv_srr(struct sk_buff *skb); ...@@ -550,7 +551,7 @@ int ip_options_rcv_srr(struct sk_buff *skb);
void ipv4_pktinfo_prepare(const struct sock *sk, struct sk_buff *skb); void ipv4_pktinfo_prepare(const struct sock *sk, struct sk_buff *skb);
void ip_cmsg_recv_offset(struct msghdr *msg, struct sk_buff *skb, int offset); void ip_cmsg_recv_offset(struct msghdr *msg, struct sk_buff *skb, int offset);
int ip_cmsg_send(struct net *net, struct msghdr *msg, int ip_cmsg_send(struct sock *sk, struct msghdr *msg,
struct ipcm_cookie *ipc, bool allow_ipv6); struct ipcm_cookie *ipc, bool allow_ipv6);
int ip_setsockopt(struct sock *sk, int level, int optname, char __user *optval, int ip_setsockopt(struct sock *sk, int level, int optname, char __user *optval,
unsigned int optlen); unsigned int optlen);
......
...@@ -867,7 +867,8 @@ int ip6_append_data(struct sock *sk, ...@@ -867,7 +867,8 @@ int ip6_append_data(struct sock *sk,
int odd, struct sk_buff *skb), int odd, struct sk_buff *skb),
void *from, int length, int transhdrlen, int hlimit, void *from, int length, int transhdrlen, int hlimit,
int tclass, struct ipv6_txoptions *opt, struct flowi6 *fl6, int tclass, struct ipv6_txoptions *opt, struct flowi6 *fl6,
struct rt6_info *rt, unsigned int flags, int dontfrag); struct rt6_info *rt, unsigned int flags, int dontfrag,
const struct sockcm_cookie *sockc);
int ip6_push_pending_frames(struct sock *sk); int ip6_push_pending_frames(struct sock *sk);
...@@ -884,7 +885,8 @@ struct sk_buff *ip6_make_skb(struct sock *sk, ...@@ -884,7 +885,8 @@ struct sk_buff *ip6_make_skb(struct sock *sk,
void *from, int length, int transhdrlen, void *from, int length, int transhdrlen,
int hlimit, int tclass, struct ipv6_txoptions *opt, int hlimit, int tclass, struct ipv6_txoptions *opt,
struct flowi6 *fl6, struct rt6_info *rt, struct flowi6 *fl6, struct rt6_info *rt,
unsigned int flags, int dontfrag); unsigned int flags, int dontfrag,
const struct sockcm_cookie *sockc);
static inline struct sk_buff *ip6_finish_skb(struct sock *sk) static inline struct sk_buff *ip6_finish_skb(struct sock *sk)
{ {
......
...@@ -1418,8 +1418,11 @@ void sk_send_sigurg(struct sock *sk); ...@@ -1418,8 +1418,11 @@ void sk_send_sigurg(struct sock *sk);
struct sockcm_cookie { struct sockcm_cookie {
u32 mark; u32 mark;
u16 tsflags;
}; };
int __sock_cmsg_send(struct sock *sk, struct msghdr *msg, struct cmsghdr *cmsg,
struct sockcm_cookie *sockc);
int sock_cmsg_send(struct sock *sk, struct msghdr *msg, int sock_cmsg_send(struct sock *sk, struct msghdr *msg,
struct sockcm_cookie *sockc); struct sockcm_cookie *sockc);
...@@ -2054,19 +2057,21 @@ static inline void sock_recv_ts_and_drops(struct msghdr *msg, struct sock *sk, ...@@ -2054,19 +2057,21 @@ static inline void sock_recv_ts_and_drops(struct msghdr *msg, struct sock *sk,
sk->sk_stamp = skb->tstamp; sk->sk_stamp = skb->tstamp;
} }
void __sock_tx_timestamp(const struct sock *sk, __u8 *tx_flags); void __sock_tx_timestamp(__u16 tsflags, __u8 *tx_flags);
/** /**
* sock_tx_timestamp - checks whether the outgoing packet is to be time stamped * sock_tx_timestamp - checks whether the outgoing packet is to be time stamped
* @sk: socket sending this packet * @sk: socket sending this packet
* @tsflags: timestamping flags to use
* @tx_flags: completed with instructions for time stamping * @tx_flags: completed with instructions for time stamping
* *
* Note : callers should take care of initial *tx_flags value (usually 0) * Note : callers should take care of initial *tx_flags value (usually 0)
*/ */
static inline void sock_tx_timestamp(const struct sock *sk, __u8 *tx_flags) static inline void sock_tx_timestamp(const struct sock *sk, __u16 tsflags,
__u8 *tx_flags)
{ {
if (unlikely(sk->sk_tsflags)) if (unlikely(tsflags))
__sock_tx_timestamp(sk, tx_flags); __sock_tx_timestamp(tsflags, tx_flags);
if (unlikely(sock_flag(sk, SOCK_WIFI_STATUS))) if (unlikely(sock_flag(sk, SOCK_WIFI_STATUS)))
*tx_flags |= SKBTX_WIFI_STATUS; *tx_flags |= SKBTX_WIFI_STATUS;
} }
......
...@@ -754,7 +754,8 @@ struct tcp_skb_cb { ...@@ -754,7 +754,8 @@ struct tcp_skb_cb {
TCPCB_REPAIRED) TCPCB_REPAIRED)
__u8 ip_dsfield; /* IPv4 tos or IPv6 dsfield */ __u8 ip_dsfield; /* IPv4 tos or IPv6 dsfield */
/* 1 byte hole */ __u8 txstamp_ack:1, /* Record TX timestamp for ack? */
unused:7;
__u32 ack_seq; /* Sequence number ACK'd */ __u32 ack_seq; /* Sequence number ACK'd */
union { union {
struct inet_skb_parm h4; struct inet_skb_parm h4;
......
...@@ -42,7 +42,8 @@ void ip6_datagram_recv_specific_ctl(struct sock *sk, struct msghdr *msg, ...@@ -42,7 +42,8 @@ void ip6_datagram_recv_specific_ctl(struct sock *sk, struct msghdr *msg,
int ip6_datagram_send_ctl(struct net *net, struct sock *sk, struct msghdr *msg, int ip6_datagram_send_ctl(struct net *net, struct sock *sk, struct msghdr *msg,
struct flowi6 *fl6, struct ipv6_txoptions *opt, struct flowi6 *fl6, struct ipv6_txoptions *opt,
int *hlimit, int *tclass, int *dontfrag); int *hlimit, int *tclass, int *dontfrag,
struct sockcm_cookie *sockc);
void ip6_dgram_sock_seq_show(struct seq_file *seq, struct sock *sp, void ip6_dgram_sock_seq_show(struct seq_file *seq, struct sock *sp,
__u16 srcp, __u16 destp, int bucket); __u16 srcp, __u16 destp, int bucket);
......
...@@ -31,6 +31,16 @@ enum { ...@@ -31,6 +31,16 @@ enum {
SOF_TIMESTAMPING_LAST SOF_TIMESTAMPING_LAST
}; };
/*
* SO_TIMESTAMPING flags are either for recording a packet timestamp or for
* reporting the timestamp to user space.
* Recording flags can be set both via socket options and control messages.
*/
#define SOF_TIMESTAMPING_TX_RECORD_MASK (SOF_TIMESTAMPING_TX_HARDWARE | \
SOF_TIMESTAMPING_TX_SOFTWARE | \
SOF_TIMESTAMPING_TX_SCHED | \
SOF_TIMESTAMPING_TX_ACK)
/** /**
* struct hwtstamp_config - %SIOCGHWTSTAMP and %SIOCSHWTSTAMP parameter * struct hwtstamp_config - %SIOCGHWTSTAMP and %SIOCSHWTSTAMP parameter
* *
......
...@@ -755,7 +755,7 @@ static int raw_sendmsg(struct socket *sock, struct msghdr *msg, size_t size) ...@@ -755,7 +755,7 @@ static int raw_sendmsg(struct socket *sock, struct msghdr *msg, size_t size)
if (err < 0) if (err < 0)
goto free_skb; goto free_skb;
sock_tx_timestamp(sk, &skb_shinfo(skb)->tx_flags); sock_tx_timestamp(sk, sk->sk_tsflags, &skb_shinfo(skb)->tx_flags);
skb->dev = dev; skb->dev = dev;
skb->sk = sk; skb->sk = sk;
......
...@@ -832,7 +832,8 @@ int sock_setsockopt(struct socket *sock, int level, int optname, ...@@ -832,7 +832,8 @@ int sock_setsockopt(struct socket *sock, int level, int optname,
!(sk->sk_tsflags & SOF_TIMESTAMPING_OPT_ID)) { !(sk->sk_tsflags & SOF_TIMESTAMPING_OPT_ID)) {
if (sk->sk_protocol == IPPROTO_TCP && if (sk->sk_protocol == IPPROTO_TCP &&
sk->sk_type == SOCK_STREAM) { sk->sk_type == SOCK_STREAM) {
if (sk->sk_state != TCP_ESTABLISHED) { if ((1 << sk->sk_state) &
(TCPF_CLOSE | TCPF_LISTEN)) {
ret = -EINVAL; ret = -EINVAL;
break; break;
} }
...@@ -1866,27 +1867,51 @@ struct sk_buff *sock_alloc_send_skb(struct sock *sk, unsigned long size, ...@@ -1866,27 +1867,51 @@ struct sk_buff *sock_alloc_send_skb(struct sock *sk, unsigned long size,
} }
EXPORT_SYMBOL(sock_alloc_send_skb); EXPORT_SYMBOL(sock_alloc_send_skb);
int __sock_cmsg_send(struct sock *sk, struct msghdr *msg, struct cmsghdr *cmsg,
struct sockcm_cookie *sockc)
{
u32 tsflags;
switch (cmsg->cmsg_type) {
case SO_MARK:
if (!ns_capable(sock_net(sk)->user_ns, CAP_NET_ADMIN))
return -EPERM;
if (cmsg->cmsg_len != CMSG_LEN(sizeof(u32)))
return -EINVAL;
sockc->mark = *(u32 *)CMSG_DATA(cmsg);
break;
case SO_TIMESTAMPING:
if (cmsg->cmsg_len != CMSG_LEN(sizeof(u32)))
return -EINVAL;
tsflags = *(u32 *)CMSG_DATA(cmsg);
if (tsflags & ~SOF_TIMESTAMPING_TX_RECORD_MASK)
return -EINVAL;
sockc->tsflags &= ~SOF_TIMESTAMPING_TX_RECORD_MASK;
sockc->tsflags |= tsflags;
break;
default:
return -EINVAL;
}
return 0;
}
EXPORT_SYMBOL(__sock_cmsg_send);
int sock_cmsg_send(struct sock *sk, struct msghdr *msg, int sock_cmsg_send(struct sock *sk, struct msghdr *msg,
struct sockcm_cookie *sockc) struct sockcm_cookie *sockc)
{ {
struct cmsghdr *cmsg; struct cmsghdr *cmsg;
int ret;
for_each_cmsghdr(cmsg, msg) { for_each_cmsghdr(cmsg, msg) {
if (!CMSG_OK(msg, cmsg)) if (!CMSG_OK(msg, cmsg))
return -EINVAL; return -EINVAL;
if (cmsg->cmsg_level != SOL_SOCKET) if (cmsg->cmsg_level != SOL_SOCKET)
continue; continue;
switch (cmsg->cmsg_type) { ret = __sock_cmsg_send(sk, msg, cmsg, sockc);
case SO_MARK: if (ret)
if (!ns_capable(sock_net(sk)->user_ns, CAP_NET_ADMIN)) return ret;
return -EPERM;
if (cmsg->cmsg_len != CMSG_LEN(sizeof(u32)))
return -EINVAL;
sockc->mark = *(u32 *)CMSG_DATA(cmsg);
break;
default:
return -EINVAL;
}
} }
return 0; return 0;
} }
......
...@@ -219,11 +219,12 @@ void ip_cmsg_recv_offset(struct msghdr *msg, struct sk_buff *skb, ...@@ -219,11 +219,12 @@ void ip_cmsg_recv_offset(struct msghdr *msg, struct sk_buff *skb,
} }
EXPORT_SYMBOL(ip_cmsg_recv_offset); EXPORT_SYMBOL(ip_cmsg_recv_offset);
int ip_cmsg_send(struct net *net, struct msghdr *msg, struct ipcm_cookie *ipc, int ip_cmsg_send(struct sock *sk, struct msghdr *msg, struct ipcm_cookie *ipc,
bool allow_ipv6) bool allow_ipv6)
{ {
int err, val; int err, val;
struct cmsghdr *cmsg; struct cmsghdr *cmsg;
struct net *net = sock_net(sk);
for_each_cmsghdr(cmsg, msg) { for_each_cmsghdr(cmsg, msg) {
if (!CMSG_OK(msg, cmsg)) if (!CMSG_OK(msg, cmsg))
...@@ -244,6 +245,12 @@ int ip_cmsg_send(struct net *net, struct msghdr *msg, struct ipcm_cookie *ipc, ...@@ -244,6 +245,12 @@ int ip_cmsg_send(struct net *net, struct msghdr *msg, struct ipcm_cookie *ipc,
continue; continue;
} }
#endif #endif
if (cmsg->cmsg_level == SOL_SOCKET) {
if (__sock_cmsg_send(sk, msg, cmsg, &ipc->sockc))
return -EINVAL;
continue;
}
if (cmsg->cmsg_level != SOL_IP) if (cmsg->cmsg_level != SOL_IP)
continue; continue;
switch (cmsg->cmsg_type) { switch (cmsg->cmsg_type) {
......
...@@ -737,6 +737,7 @@ static int ping_v4_sendmsg(struct sock *sk, struct msghdr *msg, size_t len) ...@@ -737,6 +737,7 @@ static int ping_v4_sendmsg(struct sock *sk, struct msghdr *msg, size_t len)
/* no remote port */ /* no remote port */
} }
ipc.sockc.tsflags = sk->sk_tsflags;
ipc.addr = inet->inet_saddr; ipc.addr = inet->inet_saddr;
ipc.opt = NULL; ipc.opt = NULL;
ipc.oif = sk->sk_bound_dev_if; ipc.oif = sk->sk_bound_dev_if;
...@@ -744,10 +745,8 @@ static int ping_v4_sendmsg(struct sock *sk, struct msghdr *msg, size_t len) ...@@ -744,10 +745,8 @@ static int ping_v4_sendmsg(struct sock *sk, struct msghdr *msg, size_t len)
ipc.ttl = 0; ipc.ttl = 0;
ipc.tos = -1; ipc.tos = -1;
sock_tx_timestamp(sk, &ipc.tx_flags);
if (msg->msg_controllen) { if (msg->msg_controllen) {
err = ip_cmsg_send(sock_net(sk), msg, &ipc, false); err = ip_cmsg_send(sk, msg, &ipc, false);
if (unlikely(err)) { if (unlikely(err)) {
kfree(ipc.opt); kfree(ipc.opt);
return err; return err;
...@@ -768,6 +767,8 @@ static int ping_v4_sendmsg(struct sock *sk, struct msghdr *msg, size_t len) ...@@ -768,6 +767,8 @@ static int ping_v4_sendmsg(struct sock *sk, struct msghdr *msg, size_t len)
rcu_read_unlock(); rcu_read_unlock();
} }
sock_tx_timestamp(sk, ipc.sockc.tsflags, &ipc.tx_flags);
saddr = ipc.addr; saddr = ipc.addr;
ipc.addr = faddr = daddr; ipc.addr = faddr = daddr;
......
...@@ -339,8 +339,8 @@ int raw_rcv(struct sock *sk, struct sk_buff *skb) ...@@ -339,8 +339,8 @@ int raw_rcv(struct sock *sk, struct sk_buff *skb)
static int raw_send_hdrinc(struct sock *sk, struct flowi4 *fl4, static int raw_send_hdrinc(struct sock *sk, struct flowi4 *fl4,
struct msghdr *msg, size_t length, struct msghdr *msg, size_t length,
struct rtable **rtp, struct rtable **rtp, unsigned int flags,
unsigned int flags) const struct sockcm_cookie *sockc)
{ {
struct inet_sock *inet = inet_sk(sk); struct inet_sock *inet = inet_sk(sk);
struct net *net = sock_net(sk); struct net *net = sock_net(sk);
...@@ -379,7 +379,7 @@ static int raw_send_hdrinc(struct sock *sk, struct flowi4 *fl4, ...@@ -379,7 +379,7 @@ static int raw_send_hdrinc(struct sock *sk, struct flowi4 *fl4,
skb->ip_summed = CHECKSUM_NONE; skb->ip_summed = CHECKSUM_NONE;
sock_tx_timestamp(sk, &skb_shinfo(skb)->tx_flags); sock_tx_timestamp(sk, sockc->tsflags, &skb_shinfo(skb)->tx_flags);
skb->transport_header = skb->network_header; skb->transport_header = skb->network_header;
err = -EFAULT; err = -EFAULT;
...@@ -540,6 +540,7 @@ static int raw_sendmsg(struct sock *sk, struct msghdr *msg, size_t len) ...@@ -540,6 +540,7 @@ static int raw_sendmsg(struct sock *sk, struct msghdr *msg, size_t len)
daddr = inet->inet_daddr; daddr = inet->inet_daddr;
} }
ipc.sockc.tsflags = sk->sk_tsflags;
ipc.addr = inet->inet_saddr; ipc.addr = inet->inet_saddr;
ipc.opt = NULL; ipc.opt = NULL;
ipc.tx_flags = 0; ipc.tx_flags = 0;
...@@ -548,7 +549,7 @@ static int raw_sendmsg(struct sock *sk, struct msghdr *msg, size_t len) ...@@ -548,7 +549,7 @@ static int raw_sendmsg(struct sock *sk, struct msghdr *msg, size_t len)
ipc.oif = sk->sk_bound_dev_if; ipc.oif = sk->sk_bound_dev_if;
if (msg->msg_controllen) { if (msg->msg_controllen) {
err = ip_cmsg_send(net, msg, &ipc, false); err = ip_cmsg_send(sk, msg, &ipc, false);
if (unlikely(err)) { if (unlikely(err)) {
kfree(ipc.opt); kfree(ipc.opt);
goto out; goto out;
...@@ -638,10 +639,10 @@ static int raw_sendmsg(struct sock *sk, struct msghdr *msg, size_t len) ...@@ -638,10 +639,10 @@ static int raw_sendmsg(struct sock *sk, struct msghdr *msg, size_t len)
if (inet->hdrincl) if (inet->hdrincl)
err = raw_send_hdrinc(sk, &fl4, msg, len, err = raw_send_hdrinc(sk, &fl4, msg, len,
&rt, msg->msg_flags); &rt, msg->msg_flags, &ipc.sockc);
else { else {
sock_tx_timestamp(sk, &ipc.tx_flags); sock_tx_timestamp(sk, ipc.sockc.tsflags, &ipc.tx_flags);
if (!ipc.addr) if (!ipc.addr)
ipc.addr = fl4.daddr; ipc.addr = fl4.daddr;
......
...@@ -428,14 +428,16 @@ void tcp_init_sock(struct sock *sk) ...@@ -428,14 +428,16 @@ void tcp_init_sock(struct sock *sk)
} }
EXPORT_SYMBOL(tcp_init_sock); EXPORT_SYMBOL(tcp_init_sock);
static void tcp_tx_timestamp(struct sock *sk, struct sk_buff *skb) static void tcp_tx_timestamp(struct sock *sk, u16 tsflags, struct sk_buff *skb)
{ {
if (sk->sk_tsflags) { if (sk->sk_tsflags || tsflags) {
struct skb_shared_info *shinfo = skb_shinfo(skb); struct skb_shared_info *shinfo = skb_shinfo(skb);
struct tcp_skb_cb *tcb = TCP_SKB_CB(skb);
sock_tx_timestamp(sk, &shinfo->tx_flags); sock_tx_timestamp(sk, tsflags, &shinfo->tx_flags);
if (shinfo->tx_flags & SKBTX_ANY_TSTAMP) if (shinfo->tx_flags & SKBTX_ANY_TSTAMP)
shinfo->tskey = TCP_SKB_CB(skb)->seq + skb->len - 1; shinfo->tskey = TCP_SKB_CB(skb)->seq + skb->len - 1;
tcb->txstamp_ack = !!(shinfo->tx_flags & SKBTX_ACK_TSTAMP);
} }
} }
...@@ -957,7 +959,7 @@ static ssize_t do_tcp_sendpages(struct sock *sk, struct page *page, int offset, ...@@ -957,7 +959,7 @@ static ssize_t do_tcp_sendpages(struct sock *sk, struct page *page, int offset,
offset += copy; offset += copy;
size -= copy; size -= copy;
if (!size) { if (!size) {
tcp_tx_timestamp(sk, skb); tcp_tx_timestamp(sk, sk->sk_tsflags, skb);
goto out; goto out;
} }
...@@ -1077,6 +1079,7 @@ int tcp_sendmsg(struct sock *sk, struct msghdr *msg, size_t size) ...@@ -1077,6 +1079,7 @@ int tcp_sendmsg(struct sock *sk, struct msghdr *msg, size_t size)
{ {
struct tcp_sock *tp = tcp_sk(sk); struct tcp_sock *tp = tcp_sk(sk);
struct sk_buff *skb; struct sk_buff *skb;
struct sockcm_cookie sockc;
int flags, err, copied = 0; int flags, err, copied = 0;
int mss_now = 0, size_goal, copied_syn = 0; int mss_now = 0, size_goal, copied_syn = 0;
bool sg; bool sg;
...@@ -1119,6 +1122,15 @@ int tcp_sendmsg(struct sock *sk, struct msghdr *msg, size_t size) ...@@ -1119,6 +1122,15 @@ int tcp_sendmsg(struct sock *sk, struct msghdr *msg, size_t size)
/* 'common' sending to sendq */ /* 'common' sending to sendq */
} }
sockc.tsflags = sk->sk_tsflags;
if (msg->msg_controllen) {
err = sock_cmsg_send(sk, msg, &sockc);
if (unlikely(err)) {
err = -EINVAL;
goto out_err;
}
}
/* This should be in poll */ /* This should be in poll */
sk_clear_bit(SOCKWQ_ASYNC_NOSPACE, sk); sk_clear_bit(SOCKWQ_ASYNC_NOSPACE, sk);
...@@ -1237,7 +1249,7 @@ int tcp_sendmsg(struct sock *sk, struct msghdr *msg, size_t size) ...@@ -1237,7 +1249,7 @@ int tcp_sendmsg(struct sock *sk, struct msghdr *msg, size_t size)
copied += copy; copied += copy;
if (!msg_data_left(msg)) { if (!msg_data_left(msg)) {
tcp_tx_timestamp(sk, skb); tcp_tx_timestamp(sk, sockc.tsflags, skb);
goto out; goto out;
} }
......
...@@ -3082,7 +3082,7 @@ static void tcp_ack_tstamp(struct sock *sk, struct sk_buff *skb, ...@@ -3082,7 +3082,7 @@ static void tcp_ack_tstamp(struct sock *sk, struct sk_buff *skb,
const struct skb_shared_info *shinfo; const struct skb_shared_info *shinfo;
/* Avoid cache line misses to get skb_shinfo() and shinfo->tx_flags */ /* Avoid cache line misses to get skb_shinfo() and shinfo->tx_flags */
if (likely(!(sk->sk_tsflags & SOF_TIMESTAMPING_TX_ACK))) if (likely(!TCP_SKB_CB(skb)->txstamp_ack))
return; return;
shinfo = skb_shinfo(skb); shinfo = skb_shinfo(skb);
......
...@@ -1027,15 +1027,13 @@ int udp_sendmsg(struct sock *sk, struct msghdr *msg, size_t len) ...@@ -1027,15 +1027,13 @@ int udp_sendmsg(struct sock *sk, struct msghdr *msg, size_t len)
*/ */
connected = 1; connected = 1;
} }
ipc.addr = inet->inet_saddr;
ipc.sockc.tsflags = sk->sk_tsflags;
ipc.addr = inet->inet_saddr;
ipc.oif = sk->sk_bound_dev_if; ipc.oif = sk->sk_bound_dev_if;
sock_tx_timestamp(sk, &ipc.tx_flags);
if (msg->msg_controllen) { if (msg->msg_controllen) {
err = ip_cmsg_send(sock_net(sk), msg, &ipc, err = ip_cmsg_send(sk, msg, &ipc, sk->sk_family == AF_INET6);
sk->sk_family == AF_INET6);
if (unlikely(err)) { if (unlikely(err)) {
kfree(ipc.opt); kfree(ipc.opt);
return err; return err;
...@@ -1060,6 +1058,8 @@ int udp_sendmsg(struct sock *sk, struct msghdr *msg, size_t len) ...@@ -1060,6 +1058,8 @@ int udp_sendmsg(struct sock *sk, struct msghdr *msg, size_t len)
saddr = ipc.addr; saddr = ipc.addr;
ipc.addr = faddr = daddr; ipc.addr = faddr = daddr;
sock_tx_timestamp(sk, ipc.sockc.tsflags, &ipc.tx_flags);
if (ipc.opt && ipc.opt->opt.srr) { if (ipc.opt && ipc.opt->opt.srr) {
if (!daddr) if (!daddr)
return -EINVAL; return -EINVAL;
......
...@@ -685,7 +685,8 @@ EXPORT_SYMBOL_GPL(ip6_datagram_recv_ctl); ...@@ -685,7 +685,8 @@ EXPORT_SYMBOL_GPL(ip6_datagram_recv_ctl);
int ip6_datagram_send_ctl(struct net *net, struct sock *sk, int ip6_datagram_send_ctl(struct net *net, struct sock *sk,
struct msghdr *msg, struct flowi6 *fl6, struct msghdr *msg, struct flowi6 *fl6,
struct ipv6_txoptions *opt, struct ipv6_txoptions *opt,
int *hlimit, int *tclass, int *dontfrag) int *hlimit, int *tclass, int *dontfrag,
struct sockcm_cookie *sockc)
{ {
struct in6_pktinfo *src_info; struct in6_pktinfo *src_info;
struct cmsghdr *cmsg; struct cmsghdr *cmsg;
...@@ -702,6 +703,12 @@ int ip6_datagram_send_ctl(struct net *net, struct sock *sk, ...@@ -702,6 +703,12 @@ int ip6_datagram_send_ctl(struct net *net, struct sock *sk,
goto exit_f; goto exit_f;
} }
if (cmsg->cmsg_level == SOL_SOCKET) {
if (__sock_cmsg_send(sk, msg, cmsg, sockc))
return -EINVAL;
continue;
}
if (cmsg->cmsg_level != SOL_IPV6) if (cmsg->cmsg_level != SOL_IPV6)
continue; continue;
......
...@@ -400,6 +400,7 @@ static void icmp6_send(struct sk_buff *skb, u8 type, u8 code, __u32 info) ...@@ -400,6 +400,7 @@ static void icmp6_send(struct sk_buff *skb, u8 type, u8 code, __u32 info)
struct icmp6hdr tmp_hdr; struct icmp6hdr tmp_hdr;
struct flowi6 fl6; struct flowi6 fl6;
struct icmpv6_msg msg; struct icmpv6_msg msg;
struct sockcm_cookie sockc_unused = {0};
int iif = 0; int iif = 0;
int addr_type = 0; int addr_type = 0;
int len; int len;
...@@ -527,7 +528,7 @@ static void icmp6_send(struct sk_buff *skb, u8 type, u8 code, __u32 info) ...@@ -527,7 +528,7 @@ static void icmp6_send(struct sk_buff *skb, u8 type, u8 code, __u32 info)
len + sizeof(struct icmp6hdr), len + sizeof(struct icmp6hdr),
sizeof(struct icmp6hdr), hlimit, sizeof(struct icmp6hdr), hlimit,
np->tclass, NULL, &fl6, (struct rt6_info *)dst, np->tclass, NULL, &fl6, (struct rt6_info *)dst,
MSG_DONTWAIT, np->dontfrag); MSG_DONTWAIT, np->dontfrag, &sockc_unused);
if (err) { if (err) {
ICMP6_INC_STATS(net, idev, ICMP6_MIB_OUTERRORS); ICMP6_INC_STATS(net, idev, ICMP6_MIB_OUTERRORS);
ip6_flush_pending_frames(sk); ip6_flush_pending_frames(sk);
...@@ -566,6 +567,7 @@ static void icmpv6_echo_reply(struct sk_buff *skb) ...@@ -566,6 +567,7 @@ static void icmpv6_echo_reply(struct sk_buff *skb)
int hlimit; int hlimit;
u8 tclass; u8 tclass;
u32 mark = IP6_REPLY_MARK(net, skb->mark); u32 mark = IP6_REPLY_MARK(net, skb->mark);
struct sockcm_cookie sockc_unused = {0};
saddr = &ipv6_hdr(skb)->daddr; saddr = &ipv6_hdr(skb)->daddr;
...@@ -617,7 +619,7 @@ static void icmpv6_echo_reply(struct sk_buff *skb) ...@@ -617,7 +619,7 @@ static void icmpv6_echo_reply(struct sk_buff *skb)
err = ip6_append_data(sk, icmpv6_getfrag, &msg, skb->len + sizeof(struct icmp6hdr), err = ip6_append_data(sk, icmpv6_getfrag, &msg, skb->len + sizeof(struct icmp6hdr),
sizeof(struct icmp6hdr), hlimit, tclass, NULL, &fl6, sizeof(struct icmp6hdr), hlimit, tclass, NULL, &fl6,
(struct rt6_info *)dst, MSG_DONTWAIT, (struct rt6_info *)dst, MSG_DONTWAIT,
np->dontfrag); np->dontfrag, &sockc_unused);
if (err) { if (err) {
ICMP6_INC_STATS_BH(net, idev, ICMP6_MIB_OUTERRORS); ICMP6_INC_STATS_BH(net, idev, ICMP6_MIB_OUTERRORS);
......
...@@ -372,6 +372,7 @@ fl_create(struct net *net, struct sock *sk, struct in6_flowlabel_req *freq, ...@@ -372,6 +372,7 @@ fl_create(struct net *net, struct sock *sk, struct in6_flowlabel_req *freq,
if (olen > 0) { if (olen > 0) {
struct msghdr msg; struct msghdr msg;
struct flowi6 flowi6; struct flowi6 flowi6;
struct sockcm_cookie sockc_junk;
int junk; int junk;
err = -ENOMEM; err = -ENOMEM;
...@@ -390,7 +391,7 @@ fl_create(struct net *net, struct sock *sk, struct in6_flowlabel_req *freq, ...@@ -390,7 +391,7 @@ fl_create(struct net *net, struct sock *sk, struct in6_flowlabel_req *freq,
memset(&flowi6, 0, sizeof(flowi6)); memset(&flowi6, 0, sizeof(flowi6));
err = ip6_datagram_send_ctl(net, sk, &msg, &flowi6, fl->opt, err = ip6_datagram_send_ctl(net, sk, &msg, &flowi6, fl->opt,
&junk, &junk, &junk); &junk, &junk, &junk, &sockc_junk);
if (err) if (err)
goto done; goto done;
err = -EINVAL; err = -EINVAL;
......
...@@ -1258,7 +1258,8 @@ static int __ip6_append_data(struct sock *sk, ...@@ -1258,7 +1258,8 @@ static int __ip6_append_data(struct sock *sk,
int getfrag(void *from, char *to, int offset, int getfrag(void *from, char *to, int offset,
int len, int odd, struct sk_buff *skb), int len, int odd, struct sk_buff *skb),
void *from, int length, int transhdrlen, void *from, int length, int transhdrlen,
unsigned int flags, int dontfrag) unsigned int flags, int dontfrag,
const struct sockcm_cookie *sockc)
{ {
struct sk_buff *skb, *skb_prev = NULL; struct sk_buff *skb, *skb_prev = NULL;
unsigned int maxfraglen, fragheaderlen, mtu, orig_mtu; unsigned int maxfraglen, fragheaderlen, mtu, orig_mtu;
...@@ -1329,7 +1330,7 @@ static int __ip6_append_data(struct sock *sk, ...@@ -1329,7 +1330,7 @@ static int __ip6_append_data(struct sock *sk,
csummode = CHECKSUM_PARTIAL; csummode = CHECKSUM_PARTIAL;
if (sk->sk_type == SOCK_DGRAM || sk->sk_type == SOCK_RAW) { if (sk->sk_type == SOCK_DGRAM || sk->sk_type == SOCK_RAW) {
sock_tx_timestamp(sk, &tx_flags); sock_tx_timestamp(sk, sockc->tsflags, &tx_flags);
if (tx_flags & SKBTX_ANY_SW_TSTAMP && if (tx_flags & SKBTX_ANY_SW_TSTAMP &&
sk->sk_tsflags & SOF_TIMESTAMPING_OPT_ID) sk->sk_tsflags & SOF_TIMESTAMPING_OPT_ID)
tskey = sk->sk_tskey++; tskey = sk->sk_tskey++;
...@@ -1565,7 +1566,8 @@ int ip6_append_data(struct sock *sk, ...@@ -1565,7 +1566,8 @@ int ip6_append_data(struct sock *sk,
int odd, struct sk_buff *skb), int odd, struct sk_buff *skb),
void *from, int length, int transhdrlen, int hlimit, void *from, int length, int transhdrlen, int hlimit,
int tclass, struct ipv6_txoptions *opt, struct flowi6 *fl6, int tclass, struct ipv6_txoptions *opt, struct flowi6 *fl6,
struct rt6_info *rt, unsigned int flags, int dontfrag) struct rt6_info *rt, unsigned int flags, int dontfrag,
const struct sockcm_cookie *sockc)
{ {
struct inet_sock *inet = inet_sk(sk); struct inet_sock *inet = inet_sk(sk);
struct ipv6_pinfo *np = inet6_sk(sk); struct ipv6_pinfo *np = inet6_sk(sk);
...@@ -1593,7 +1595,8 @@ int ip6_append_data(struct sock *sk, ...@@ -1593,7 +1595,8 @@ int ip6_append_data(struct sock *sk,
return __ip6_append_data(sk, fl6, &sk->sk_write_queue, &inet->cork.base, return __ip6_append_data(sk, fl6, &sk->sk_write_queue, &inet->cork.base,
&np->cork, sk_page_frag(sk), getfrag, &np->cork, sk_page_frag(sk), getfrag,
from, length, transhdrlen, flags, dontfrag); from, length, transhdrlen, flags, dontfrag,
sockc);
} }
EXPORT_SYMBOL_GPL(ip6_append_data); EXPORT_SYMBOL_GPL(ip6_append_data);
...@@ -1752,7 +1755,7 @@ struct sk_buff *ip6_make_skb(struct sock *sk, ...@@ -1752,7 +1755,7 @@ struct sk_buff *ip6_make_skb(struct sock *sk,
int hlimit, int tclass, int hlimit, int tclass,
struct ipv6_txoptions *opt, struct flowi6 *fl6, struct ipv6_txoptions *opt, struct flowi6 *fl6,
struct rt6_info *rt, unsigned int flags, struct rt6_info *rt, unsigned int flags,
int dontfrag) int dontfrag, const struct sockcm_cookie *sockc)
{ {
struct inet_cork_full cork; struct inet_cork_full cork;
struct inet6_cork v6_cork; struct inet6_cork v6_cork;
...@@ -1779,7 +1782,7 @@ struct sk_buff *ip6_make_skb(struct sock *sk, ...@@ -1779,7 +1782,7 @@ struct sk_buff *ip6_make_skb(struct sock *sk,
err = __ip6_append_data(sk, fl6, &queue, &cork.base, &v6_cork, err = __ip6_append_data(sk, fl6, &queue, &cork.base, &v6_cork,
&current->task_frag, getfrag, from, &current->task_frag, getfrag, from,
length + exthdrlen, transhdrlen + exthdrlen, length + exthdrlen, transhdrlen + exthdrlen,
flags, dontfrag); flags, dontfrag, sockc);
if (err) { if (err) {
__ip6_flush_pending_frames(sk, &queue, &cork, &v6_cork); __ip6_flush_pending_frames(sk, &queue, &cork, &v6_cork);
return ERR_PTR(err); return ERR_PTR(err);
......
...@@ -471,6 +471,7 @@ static int do_ipv6_setsockopt(struct sock *sk, int level, int optname, ...@@ -471,6 +471,7 @@ static int do_ipv6_setsockopt(struct sock *sk, int level, int optname,
struct ipv6_txoptions *opt = NULL; struct ipv6_txoptions *opt = NULL;
struct msghdr msg; struct msghdr msg;
struct flowi6 fl6; struct flowi6 fl6;
struct sockcm_cookie sockc_junk;
int junk; int junk;
memset(&fl6, 0, sizeof(fl6)); memset(&fl6, 0, sizeof(fl6));
...@@ -503,7 +504,7 @@ static int do_ipv6_setsockopt(struct sock *sk, int level, int optname, ...@@ -503,7 +504,7 @@ static int do_ipv6_setsockopt(struct sock *sk, int level, int optname,
msg.msg_control = (void *)(opt+1); msg.msg_control = (void *)(opt+1);
retv = ip6_datagram_send_ctl(net, sk, &msg, &fl6, opt, &junk, retv = ip6_datagram_send_ctl(net, sk, &msg, &fl6, opt, &junk,
&junk, &junk); &junk, &junk, &sockc_junk);
if (retv) if (retv)
goto done; goto done;
update: update:
......
...@@ -62,6 +62,7 @@ static int ping_v6_sendmsg(struct sock *sk, struct msghdr *msg, size_t len) ...@@ -62,6 +62,7 @@ static int ping_v6_sendmsg(struct sock *sk, struct msghdr *msg, size_t len)
struct dst_entry *dst; struct dst_entry *dst;
struct rt6_info *rt; struct rt6_info *rt;
struct pingfakehdr pfh; struct pingfakehdr pfh;
struct sockcm_cookie junk = {0};
pr_debug("ping_v6_sendmsg(sk=%p,sk->num=%u)\n", inet, inet->inet_num); pr_debug("ping_v6_sendmsg(sk=%p,sk->num=%u)\n", inet, inet->inet_num);
...@@ -144,7 +145,7 @@ static int ping_v6_sendmsg(struct sock *sk, struct msghdr *msg, size_t len) ...@@ -144,7 +145,7 @@ static int ping_v6_sendmsg(struct sock *sk, struct msghdr *msg, size_t len)
err = ip6_append_data(sk, ping_getfrag, &pfh, len, err = ip6_append_data(sk, ping_getfrag, &pfh, len,
0, hlimit, 0, hlimit,
np->tclass, NULL, &fl6, rt, np->tclass, NULL, &fl6, rt,
MSG_DONTWAIT, np->dontfrag); MSG_DONTWAIT, np->dontfrag, &junk);
if (err) { if (err) {
ICMP6_INC_STATS(sock_net(sk), rt->rt6i_idev, ICMP6_INC_STATS(sock_net(sk), rt->rt6i_idev,
......
...@@ -745,6 +745,7 @@ static int rawv6_sendmsg(struct sock *sk, struct msghdr *msg, size_t len) ...@@ -745,6 +745,7 @@ static int rawv6_sendmsg(struct sock *sk, struct msghdr *msg, size_t len)
struct dst_entry *dst = NULL; struct dst_entry *dst = NULL;
struct raw6_frag_vec rfv; struct raw6_frag_vec rfv;
struct flowi6 fl6; struct flowi6 fl6;
struct sockcm_cookie sockc;
int addr_len = msg->msg_namelen; int addr_len = msg->msg_namelen;
int hlimit = -1; int hlimit = -1;
int tclass = -1; int tclass = -1;
...@@ -821,13 +822,15 @@ static int rawv6_sendmsg(struct sock *sk, struct msghdr *msg, size_t len) ...@@ -821,13 +822,15 @@ static int rawv6_sendmsg(struct sock *sk, struct msghdr *msg, size_t len)
if (fl6.flowi6_oif == 0) if (fl6.flowi6_oif == 0)
fl6.flowi6_oif = sk->sk_bound_dev_if; fl6.flowi6_oif = sk->sk_bound_dev_if;
sockc.tsflags = sk->sk_tsflags;
if (msg->msg_controllen) { if (msg->msg_controllen) {
opt = &opt_space; opt = &opt_space;
memset(opt, 0, sizeof(struct ipv6_txoptions)); memset(opt, 0, sizeof(struct ipv6_txoptions));
opt->tot_len = sizeof(struct ipv6_txoptions); opt->tot_len = sizeof(struct ipv6_txoptions);
err = ip6_datagram_send_ctl(sock_net(sk), sk, msg, &fl6, opt, err = ip6_datagram_send_ctl(sock_net(sk), sk, msg, &fl6, opt,
&hlimit, &tclass, &dontfrag); &hlimit, &tclass, &dontfrag,
&sockc);
if (err < 0) { if (err < 0) {
fl6_sock_release(flowlabel); fl6_sock_release(flowlabel);
return err; return err;
...@@ -897,7 +900,7 @@ static int rawv6_sendmsg(struct sock *sk, struct msghdr *msg, size_t len) ...@@ -897,7 +900,7 @@ static int rawv6_sendmsg(struct sock *sk, struct msghdr *msg, size_t len)
lock_sock(sk); lock_sock(sk);
err = ip6_append_data(sk, raw6_getfrag, &rfv, err = ip6_append_data(sk, raw6_getfrag, &rfv,
len, 0, hlimit, tclass, opt, &fl6, (struct rt6_info *)dst, len, 0, hlimit, tclass, opt, &fl6, (struct rt6_info *)dst,
msg->msg_flags, dontfrag); msg->msg_flags, dontfrag, &sockc);
if (err) if (err)
ip6_flush_pending_frames(sk); ip6_flush_pending_frames(sk);
......
...@@ -1128,6 +1128,7 @@ int udpv6_sendmsg(struct sock *sk, struct msghdr *msg, size_t len) ...@@ -1128,6 +1128,7 @@ int udpv6_sendmsg(struct sock *sk, struct msghdr *msg, size_t len)
int connected = 0; int connected = 0;
int is_udplite = IS_UDPLITE(sk); int is_udplite = IS_UDPLITE(sk);
int (*getfrag)(void *, char *, int, int, int, struct sk_buff *); int (*getfrag)(void *, char *, int, int, int, struct sk_buff *);
struct sockcm_cookie sockc;
/* destination address check */ /* destination address check */
if (sin6) { if (sin6) {
...@@ -1247,6 +1248,7 @@ int udpv6_sendmsg(struct sock *sk, struct msghdr *msg, size_t len) ...@@ -1247,6 +1248,7 @@ int udpv6_sendmsg(struct sock *sk, struct msghdr *msg, size_t len)
fl6.flowi6_oif = np->sticky_pktinfo.ipi6_ifindex; fl6.flowi6_oif = np->sticky_pktinfo.ipi6_ifindex;
fl6.flowi6_mark = sk->sk_mark; fl6.flowi6_mark = sk->sk_mark;
sockc.tsflags = sk->sk_tsflags;
if (msg->msg_controllen) { if (msg->msg_controllen) {
opt = &opt_space; opt = &opt_space;
...@@ -1254,7 +1256,8 @@ int udpv6_sendmsg(struct sock *sk, struct msghdr *msg, size_t len) ...@@ -1254,7 +1256,8 @@ int udpv6_sendmsg(struct sock *sk, struct msghdr *msg, size_t len)
opt->tot_len = sizeof(*opt); opt->tot_len = sizeof(*opt);
err = ip6_datagram_send_ctl(sock_net(sk), sk, msg, &fl6, opt, err = ip6_datagram_send_ctl(sock_net(sk), sk, msg, &fl6, opt,
&hlimit, &tclass, &dontfrag); &hlimit, &tclass, &dontfrag,
&sockc);
if (err < 0) { if (err < 0) {
fl6_sock_release(flowlabel); fl6_sock_release(flowlabel);
return err; return err;
...@@ -1321,7 +1324,7 @@ int udpv6_sendmsg(struct sock *sk, struct msghdr *msg, size_t len) ...@@ -1321,7 +1324,7 @@ int udpv6_sendmsg(struct sock *sk, struct msghdr *msg, size_t len)
skb = ip6_make_skb(sk, getfrag, msg, ulen, skb = ip6_make_skb(sk, getfrag, msg, ulen,
sizeof(struct udphdr), hlimit, tclass, opt, sizeof(struct udphdr), hlimit, tclass, opt,
&fl6, (struct rt6_info *)dst, &fl6, (struct rt6_info *)dst,
msg->msg_flags, dontfrag); msg->msg_flags, dontfrag, &sockc);
err = PTR_ERR(skb); err = PTR_ERR(skb);
if (!IS_ERR_OR_NULL(skb)) if (!IS_ERR_OR_NULL(skb))
err = udp_v6_send_skb(skb, &fl6); err = udp_v6_send_skb(skb, &fl6);
...@@ -1348,7 +1351,8 @@ int udpv6_sendmsg(struct sock *sk, struct msghdr *msg, size_t len) ...@@ -1348,7 +1351,8 @@ int udpv6_sendmsg(struct sock *sk, struct msghdr *msg, size_t len)
err = ip6_append_data(sk, getfrag, msg, ulen, err = ip6_append_data(sk, getfrag, msg, ulen,
sizeof(struct udphdr), hlimit, tclass, opt, &fl6, sizeof(struct udphdr), hlimit, tclass, opt, &fl6,
(struct rt6_info *)dst, (struct rt6_info *)dst,
corkreq ? msg->msg_flags|MSG_MORE : msg->msg_flags, dontfrag); corkreq ? msg->msg_flags|MSG_MORE : msg->msg_flags, dontfrag,
&sockc);
if (err) if (err)
udp_v6_flush_pending_frames(sk); udp_v6_flush_pending_frames(sk);
else if (!corkreq) else if (!corkreq)
......
...@@ -492,6 +492,7 @@ static int l2tp_ip6_sendmsg(struct sock *sk, struct msghdr *msg, size_t len) ...@@ -492,6 +492,7 @@ static int l2tp_ip6_sendmsg(struct sock *sk, struct msghdr *msg, size_t len)
struct ip6_flowlabel *flowlabel = NULL; struct ip6_flowlabel *flowlabel = NULL;
struct dst_entry *dst = NULL; struct dst_entry *dst = NULL;
struct flowi6 fl6; struct flowi6 fl6;
struct sockcm_cookie sockc_unused = {0};
int addr_len = msg->msg_namelen; int addr_len = msg->msg_namelen;
int hlimit = -1; int hlimit = -1;
int tclass = -1; int tclass = -1;
...@@ -562,9 +563,10 @@ static int l2tp_ip6_sendmsg(struct sock *sk, struct msghdr *msg, size_t len) ...@@ -562,9 +563,10 @@ static int l2tp_ip6_sendmsg(struct sock *sk, struct msghdr *msg, size_t len)
memset(opt, 0, sizeof(struct ipv6_txoptions)); memset(opt, 0, sizeof(struct ipv6_txoptions));
opt->tot_len = sizeof(struct ipv6_txoptions); opt->tot_len = sizeof(struct ipv6_txoptions);
err = ip6_datagram_send_ctl(sock_net(sk), sk, msg, &fl6, opt, err = ip6_datagram_send_ctl(sock_net(sk), sk, msg, &fl6, opt,
&hlimit, &tclass, &dontfrag); &hlimit, &tclass, &dontfrag,
if (err < 0) { &sockc_unused);
if (err < 0) {
fl6_sock_release(flowlabel); fl6_sock_release(flowlabel);
return err; return err;
} }
...@@ -625,7 +627,7 @@ static int l2tp_ip6_sendmsg(struct sock *sk, struct msghdr *msg, size_t len) ...@@ -625,7 +627,7 @@ static int l2tp_ip6_sendmsg(struct sock *sk, struct msghdr *msg, size_t len)
err = ip6_append_data(sk, ip_generic_getfrag, msg, err = ip6_append_data(sk, ip_generic_getfrag, msg,
ulen, transhdrlen, hlimit, tclass, opt, ulen, transhdrlen, hlimit, tclass, opt,
&fl6, (struct rt6_info *)dst, &fl6, (struct rt6_info *)dst,
msg->msg_flags, dontfrag); msg->msg_flags, dontfrag, &sockc_unused);
if (err) if (err)
ip6_flush_pending_frames(sk); ip6_flush_pending_frames(sk);
else if (!(msg->msg_flags & MSG_MORE)) else if (!(msg->msg_flags & MSG_MORE))
......
...@@ -1837,6 +1837,7 @@ static int packet_sendmsg_spkt(struct socket *sock, struct msghdr *msg, ...@@ -1837,6 +1837,7 @@ static int packet_sendmsg_spkt(struct socket *sock, struct msghdr *msg,
DECLARE_SOCKADDR(struct sockaddr_pkt *, saddr, msg->msg_name); DECLARE_SOCKADDR(struct sockaddr_pkt *, saddr, msg->msg_name);
struct sk_buff *skb = NULL; struct sk_buff *skb = NULL;
struct net_device *dev; struct net_device *dev;
struct sockcm_cookie sockc;
__be16 proto = 0; __be16 proto = 0;
int err; int err;
int extra_len = 0; int extra_len = 0;
...@@ -1925,12 +1926,21 @@ static int packet_sendmsg_spkt(struct socket *sock, struct msghdr *msg, ...@@ -1925,12 +1926,21 @@ static int packet_sendmsg_spkt(struct socket *sock, struct msghdr *msg,
goto out_unlock; goto out_unlock;
} }
sockc.tsflags = 0;
if (msg->msg_controllen) {
err = sock_cmsg_send(sk, msg, &sockc);
if (unlikely(err)) {
err = -EINVAL;
goto out_unlock;
}
}
skb->protocol = proto; skb->protocol = proto;
skb->dev = dev; skb->dev = dev;
skb->priority = sk->sk_priority; skb->priority = sk->sk_priority;
skb->mark = sk->sk_mark; skb->mark = sk->sk_mark;
sock_tx_timestamp(sk, &skb_shinfo(skb)->tx_flags); sock_tx_timestamp(sk, sockc.tsflags, &skb_shinfo(skb)->tx_flags);
if (unlikely(extra_len == 4)) if (unlikely(extra_len == 4))
skb->no_fcs = 1; skb->no_fcs = 1;
...@@ -2486,7 +2496,8 @@ static int packet_snd_vnet_gso(struct sk_buff *skb, ...@@ -2486,7 +2496,8 @@ static int packet_snd_vnet_gso(struct sk_buff *skb,
static int tpacket_fill_skb(struct packet_sock *po, struct sk_buff *skb, static int tpacket_fill_skb(struct packet_sock *po, struct sk_buff *skb,
void *frame, struct net_device *dev, void *data, int tp_len, void *frame, struct net_device *dev, void *data, int tp_len,
__be16 proto, unsigned char *addr, int hlen, int copylen) __be16 proto, unsigned char *addr, int hlen, int copylen,
const struct sockcm_cookie *sockc)
{ {
union tpacket_uhdr ph; union tpacket_uhdr ph;
int to_write, offset, len, nr_frags, len_max; int to_write, offset, len, nr_frags, len_max;
...@@ -2500,7 +2511,7 @@ static int tpacket_fill_skb(struct packet_sock *po, struct sk_buff *skb, ...@@ -2500,7 +2511,7 @@ static int tpacket_fill_skb(struct packet_sock *po, struct sk_buff *skb,
skb->dev = dev; skb->dev = dev;
skb->priority = po->sk.sk_priority; skb->priority = po->sk.sk_priority;
skb->mark = po->sk.sk_mark; skb->mark = po->sk.sk_mark;
sock_tx_timestamp(&po->sk, &skb_shinfo(skb)->tx_flags); sock_tx_timestamp(&po->sk, sockc->tsflags, &skb_shinfo(skb)->tx_flags);
skb_shinfo(skb)->destructor_arg = ph.raw; skb_shinfo(skb)->destructor_arg = ph.raw;
skb_reserve(skb, hlen); skb_reserve(skb, hlen);
...@@ -2624,6 +2635,7 @@ static int tpacket_snd(struct packet_sock *po, struct msghdr *msg) ...@@ -2624,6 +2635,7 @@ static int tpacket_snd(struct packet_sock *po, struct msghdr *msg)
struct sk_buff *skb; struct sk_buff *skb;
struct net_device *dev; struct net_device *dev;
struct virtio_net_hdr *vnet_hdr = NULL; struct virtio_net_hdr *vnet_hdr = NULL;
struct sockcm_cookie sockc;
__be16 proto; __be16 proto;
int err, reserve = 0; int err, reserve = 0;
void *ph; void *ph;
...@@ -2655,6 +2667,13 @@ static int tpacket_snd(struct packet_sock *po, struct msghdr *msg) ...@@ -2655,6 +2667,13 @@ static int tpacket_snd(struct packet_sock *po, struct msghdr *msg)
dev = dev_get_by_index(sock_net(&po->sk), saddr->sll_ifindex); dev = dev_get_by_index(sock_net(&po->sk), saddr->sll_ifindex);
} }
sockc.tsflags = 0;
if (msg->msg_controllen) {
err = sock_cmsg_send(&po->sk, msg, &sockc);
if (unlikely(err))
goto out;
}
err = -ENXIO; err = -ENXIO;
if (unlikely(dev == NULL)) if (unlikely(dev == NULL))
goto out; goto out;
...@@ -2712,7 +2731,7 @@ static int tpacket_snd(struct packet_sock *po, struct msghdr *msg) ...@@ -2712,7 +2731,7 @@ static int tpacket_snd(struct packet_sock *po, struct msghdr *msg)
goto out_status; goto out_status;
} }
tp_len = tpacket_fill_skb(po, skb, ph, dev, data, tp_len, proto, tp_len = tpacket_fill_skb(po, skb, ph, dev, data, tp_len, proto,
addr, hlen, copylen); addr, hlen, copylen, &sockc);
if (likely(tp_len >= 0) && if (likely(tp_len >= 0) &&
tp_len > dev->mtu + reserve && tp_len > dev->mtu + reserve &&
!po->has_vnet_hdr && !po->has_vnet_hdr &&
...@@ -2851,6 +2870,7 @@ static int packet_snd(struct socket *sock, struct msghdr *msg, size_t len) ...@@ -2851,6 +2870,7 @@ static int packet_snd(struct socket *sock, struct msghdr *msg, size_t len)
if (unlikely(!(dev->flags & IFF_UP))) if (unlikely(!(dev->flags & IFF_UP)))
goto out_unlock; goto out_unlock;
sockc.tsflags = 0;
sockc.mark = sk->sk_mark; sockc.mark = sk->sk_mark;
if (msg->msg_controllen) { if (msg->msg_controllen) {
err = sock_cmsg_send(sk, msg, &sockc); err = sock_cmsg_send(sk, msg, &sockc);
...@@ -2908,7 +2928,7 @@ static int packet_snd(struct socket *sock, struct msghdr *msg, size_t len) ...@@ -2908,7 +2928,7 @@ static int packet_snd(struct socket *sock, struct msghdr *msg, size_t len)
goto out_free; goto out_free;
} }
sock_tx_timestamp(sk, &skb_shinfo(skb)->tx_flags); sock_tx_timestamp(sk, sockc.tsflags, &skb_shinfo(skb)->tx_flags);
if (!vnet_hdr.gso_type && (len > dev->mtu + reserve + extra_len) && if (!vnet_hdr.gso_type && (len > dev->mtu + reserve + extra_len) &&
!packet_extra_vlan_len_allowed(dev, skb)) { !packet_extra_vlan_len_allowed(dev, skb)) {
......
...@@ -587,20 +587,20 @@ void sock_release(struct socket *sock) ...@@ -587,20 +587,20 @@ void sock_release(struct socket *sock)
} }
EXPORT_SYMBOL(sock_release); EXPORT_SYMBOL(sock_release);
void __sock_tx_timestamp(const struct sock *sk, __u8 *tx_flags) void __sock_tx_timestamp(__u16 tsflags, __u8 *tx_flags)
{ {
u8 flags = *tx_flags; u8 flags = *tx_flags;
if (sk->sk_tsflags & SOF_TIMESTAMPING_TX_HARDWARE) if (tsflags & SOF_TIMESTAMPING_TX_HARDWARE)
flags |= SKBTX_HW_TSTAMP; flags |= SKBTX_HW_TSTAMP;
if (sk->sk_tsflags & SOF_TIMESTAMPING_TX_SOFTWARE) if (tsflags & SOF_TIMESTAMPING_TX_SOFTWARE)
flags |= SKBTX_SW_TSTAMP; flags |= SKBTX_SW_TSTAMP;
if (sk->sk_tsflags & SOF_TIMESTAMPING_TX_SCHED) if (tsflags & SOF_TIMESTAMPING_TX_SCHED)
flags |= SKBTX_SCHED_TSTAMP; flags |= SKBTX_SCHED_TSTAMP;
if (sk->sk_tsflags & SOF_TIMESTAMPING_TX_ACK) if (tsflags & SOF_TIMESTAMPING_TX_ACK)
flags |= SKBTX_ACK_TSTAMP; flags |= SKBTX_ACK_TSTAMP;
*tx_flags = flags; *tx_flags = flags;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment