Merge branch 'add-option-to-provide-opt_id-value-via-cmsg'

Vadim Fedorenko says:

====================
Add option to provide OPT_ID value via cmsg

SOF_TIMESTAMPING_OPT_ID socket option flag gives a way to correlate TX
timestamps and packets sent via socket. Unfortunately, there is no way
to reliably predict socket timestamp ID value in case of error returned
by sendmsg. For UDP sockets it's impossible because of lockless
nature of UDP transmit, several threads may send packets in parallel. In
case of RAW sockets MSG_MORE option makes things complicated. More
details are in the conversation [1].
This patch adds new control message type to give user-space
software an opportunity to control the mapping between packets and
values by providing ID with each sendmsg.

The first patch in the series adds all needed definitions and implements
the function for UDP sockets. The explicit check of socket's type is not
added because subsequent patches in the series will add support for other
types of sockets. The documentation is also included into the first
patch.

Patch 2/4 adds support for TCP sockets. This part is simple and straight
forward.

Patch 3/4 adds support for RAW sockets. It's a bit tricky because
sock_tx_timestamp functions has to be refactored to receive full socket
cookie information to fill in ID. The commit b534dc46c8 ("net_tstamp:
add SOF_TIMESTAMPING_OPT_ID_TCP") did the conversion of sk_tsflags to
u32 but sock_tx_timestamp functions were not converted and still receive
16b flags. It wasn't a problem because SOF_TIMESTAMPING_OPT_ID_TCP was
not checked in these functions, that's why no backporting is needed.

Patch 4/4 adds selftests for new feature.

[1] https://lore.kernel.org/netdev/CALCETrU0jB+kg0mhV6A8mrHfTE1D1pr1SD_B9Eaa9aDPfgHdtA@mail.gmail.com/
====================

Link: https://patch.msgid.link/20241001125716.2832769-1-vadfed@meta.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
This commit is contained in:
Jakub Kicinski 2024-10-04 11:52:22 -07:00
commit a73f214e89
20 changed files with 149 additions and 48 deletions

View File

@ -194,6 +194,20 @@ SOF_TIMESTAMPING_OPT_ID:
among all possibly concurrently outstanding timestamp requests for among all possibly concurrently outstanding timestamp requests for
that socket. that socket.
The process can optionally override the default generated ID, by
passing a specific ID with control message SCM_TS_OPT_ID (not
supported for TCP sockets)::
struct msghdr *msg;
...
cmsg = CMSG_FIRSTHDR(msg);
cmsg->cmsg_level = SOL_SOCKET;
cmsg->cmsg_type = SCM_TS_OPT_ID;
cmsg->cmsg_len = CMSG_LEN(sizeof(__u32));
*((__u32 *) CMSG_DATA(cmsg)) = opt_id;
err = sendmsg(fd, msg, 0);
SOF_TIMESTAMPING_OPT_ID_TCP: SOF_TIMESTAMPING_OPT_ID_TCP:
Pass this modifier along with SOF_TIMESTAMPING_OPT_ID for new TCP Pass this modifier along with SOF_TIMESTAMPING_OPT_ID for new TCP
timestamping applications. SOF_TIMESTAMPING_OPT_ID defines how the timestamping applications. SOF_TIMESTAMPING_OPT_ID defines how the

View File

@ -146,6 +146,8 @@
#define SCM_DEVMEM_DMABUF SO_DEVMEM_DMABUF #define SCM_DEVMEM_DMABUF SO_DEVMEM_DMABUF
#define SO_DEVMEM_DONTNEED 80 #define SO_DEVMEM_DONTNEED 80
#define SCM_TS_OPT_ID 81
#if !defined(__KERNEL__) #if !defined(__KERNEL__)
#if __BITS_PER_LONG == 64 #if __BITS_PER_LONG == 64

View File

@ -157,6 +157,8 @@
#define SCM_DEVMEM_DMABUF SO_DEVMEM_DMABUF #define SCM_DEVMEM_DMABUF SO_DEVMEM_DMABUF
#define SO_DEVMEM_DONTNEED 80 #define SO_DEVMEM_DONTNEED 80
#define SCM_TS_OPT_ID 81
#if !defined(__KERNEL__) #if !defined(__KERNEL__)
#if __BITS_PER_LONG == 64 #if __BITS_PER_LONG == 64

View File

@ -138,6 +138,8 @@
#define SCM_DEVMEM_DMABUF SO_DEVMEM_DMABUF #define SCM_DEVMEM_DMABUF SO_DEVMEM_DMABUF
#define SO_DEVMEM_DONTNEED 80 #define SO_DEVMEM_DONTNEED 80
#define SCM_TS_OPT_ID 0x404C
#if !defined(__KERNEL__) #if !defined(__KERNEL__)
#if __BITS_PER_LONG == 64 #if __BITS_PER_LONG == 64

View File

@ -139,6 +139,8 @@
#define SCM_DEVMEM_DMABUF SO_DEVMEM_DMABUF #define SCM_DEVMEM_DMABUF SO_DEVMEM_DMABUF
#define SO_DEVMEM_DONTNEED 0x0059 #define SO_DEVMEM_DONTNEED 0x0059
#define SCM_TS_OPT_ID 0x005a
#if !defined(__KERNEL__) #if !defined(__KERNEL__)

View File

@ -174,6 +174,7 @@ struct inet_cork {
__s16 tos; __s16 tos;
char priority; char priority;
__u16 gso_size; __u16 gso_size;
u32 ts_opt_id;
u64 transmit_time; u64 transmit_time;
u32 mark; u32 mark;
}; };
@ -241,7 +242,8 @@ struct inet_sock {
struct inet_cork_full cork; struct inet_cork_full cork;
}; };
#define IPCORK_OPT 1 /* ip-options has been held in ipcork.opt */ #define IPCORK_OPT 1 /* ip-options has been held in ipcork.opt */
#define IPCORK_TS_OPT_ID 2 /* ts_opt_id field is valid, overriding sk_tskey */
enum { enum {
INET_FLAGS_PKTINFO = 0, INET_FLAGS_PKTINFO = 0,

View File

@ -954,6 +954,12 @@ enum sock_flags {
}; };
#define SK_FLAGS_TIMESTAMP ((1UL << SOCK_TIMESTAMP) | (1UL << SOCK_TIMESTAMPING_RX_SOFTWARE)) #define SK_FLAGS_TIMESTAMP ((1UL << SOCK_TIMESTAMP) | (1UL << SOCK_TIMESTAMPING_RX_SOFTWARE))
/*
* The highest bit of sk_tsflags is reserved for kernel-internal
* SOCKCM_FLAG_TS_OPT_ID. There is a check in core/sock.c to control that
* SOF_TIMESTAMPING* values do not reach this reserved area
*/
#define SOCKCM_FLAG_TS_OPT_ID BIT(31)
static inline void sock_copy_flags(struct sock *nsk, const struct sock *osk) static inline void sock_copy_flags(struct sock *nsk, const struct sock *osk)
{ {
@ -1796,6 +1802,7 @@ struct sockcm_cookie {
u64 transmit_time; u64 transmit_time;
u32 mark; u32 mark;
u32 tsflags; u32 tsflags;
u32 ts_opt_id;
}; };
static inline void sockcm_init(struct sockcm_cookie *sockc, static inline void sockcm_init(struct sockcm_cookie *sockc,
@ -2653,39 +2660,48 @@ static inline void sock_recv_cmsgs(struct msghdr *msg, struct sock *sk,
sock_write_timestamp(sk, 0); sock_write_timestamp(sk, 0);
} }
void __sock_tx_timestamp(__u16 tsflags, __u8 *tx_flags); void __sock_tx_timestamp(__u32 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 * @sockc: pointer to socket cmsg cookie to get timestamping info
* @tx_flags: completed with instructions for time stamping * @tx_flags: completed with instructions for time stamping
* @tskey: filled in with next sk_tskey (not for TCP, which uses seqno) * @tskey: filled in with next sk_tskey (not for TCP, which uses seqno)
* *
* 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(struct sock *sk, __u16 tsflags, static inline void _sock_tx_timestamp(struct sock *sk,
const struct sockcm_cookie *sockc,
__u8 *tx_flags, __u32 *tskey) __u8 *tx_flags, __u32 *tskey)
{ {
__u32 tsflags = sockc->tsflags;
if (unlikely(tsflags)) { if (unlikely(tsflags)) {
__sock_tx_timestamp(tsflags, tx_flags); __sock_tx_timestamp(tsflags, tx_flags);
if (tsflags & SOF_TIMESTAMPING_OPT_ID && tskey && if (tsflags & SOF_TIMESTAMPING_OPT_ID && tskey &&
tsflags & SOF_TIMESTAMPING_TX_RECORD_MASK) tsflags & SOF_TIMESTAMPING_TX_RECORD_MASK) {
*tskey = atomic_inc_return(&sk->sk_tskey) - 1; if (tsflags & SOCKCM_FLAG_TS_OPT_ID)
*tskey = sockc->ts_opt_id;
else
*tskey = atomic_inc_return(&sk->sk_tskey) - 1;
}
} }
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;
} }
static inline void sock_tx_timestamp(struct sock *sk, __u16 tsflags, static inline void sock_tx_timestamp(struct sock *sk,
const struct sockcm_cookie *sockc,
__u8 *tx_flags) __u8 *tx_flags)
{ {
_sock_tx_timestamp(sk, tsflags, tx_flags, NULL); _sock_tx_timestamp(sk, sockc, tx_flags, NULL);
} }
static inline void skb_setup_tx_timestamp(struct sk_buff *skb, __u16 tsflags) static inline void skb_setup_tx_timestamp(struct sk_buff *skb,
const struct sockcm_cookie *sockc)
{ {
_sock_tx_timestamp(skb->sk, tsflags, &skb_shinfo(skb)->tx_flags, _sock_tx_timestamp(skb->sk, sockc, &skb_shinfo(skb)->tx_flags,
&skb_shinfo(skb)->tskey); &skb_shinfo(skb)->tskey);
} }

View File

@ -141,6 +141,8 @@
#define SCM_DEVMEM_DMABUF SO_DEVMEM_DMABUF #define SCM_DEVMEM_DMABUF SO_DEVMEM_DMABUF
#define SO_DEVMEM_DONTNEED 80 #define SO_DEVMEM_DONTNEED 80
#define SCM_TS_OPT_ID 81
#if !defined(__KERNEL__) #if !defined(__KERNEL__)
#if __BITS_PER_LONG == 64 || (defined(__x86_64__) && defined(__ILP32__)) #if __BITS_PER_LONG == 64 || (defined(__x86_64__) && defined(__ILP32__))

View File

@ -966,7 +966,7 @@ static int raw_sendmsg(struct socket *sock, struct msghdr *msg, size_t size)
skb->mark = READ_ONCE(sk->sk_mark); skb->mark = READ_ONCE(sk->sk_mark);
skb->tstamp = sockc.transmit_time; skb->tstamp = sockc.transmit_time;
skb_setup_tx_timestamp(skb, sockc.tsflags); skb_setup_tx_timestamp(skb, &sockc);
err = can_send(skb, ro->loopback); err = can_send(skb, ro->loopback);

View File

@ -2899,6 +2899,8 @@ int __sock_cmsg_send(struct sock *sk, struct cmsghdr *cmsg,
{ {
u32 tsflags; u32 tsflags;
BUILD_BUG_ON(SOF_TIMESTAMPING_LAST == (1 << 31));
switch (cmsg->cmsg_type) { switch (cmsg->cmsg_type) {
case SO_MARK: case SO_MARK:
if (!ns_capable(sock_net(sk)->user_ns, CAP_NET_RAW) && if (!ns_capable(sock_net(sk)->user_ns, CAP_NET_RAW) &&
@ -2927,6 +2929,17 @@ int __sock_cmsg_send(struct sock *sk, struct cmsghdr *cmsg,
return -EINVAL; return -EINVAL;
sockc->transmit_time = get_unaligned((u64 *)CMSG_DATA(cmsg)); sockc->transmit_time = get_unaligned((u64 *)CMSG_DATA(cmsg));
break; break;
case SCM_TS_OPT_ID:
if (sk_is_tcp(sk))
return -EINVAL;
tsflags = READ_ONCE(sk->sk_tsflags);
if (!(tsflags & SOF_TIMESTAMPING_OPT_ID))
return -EINVAL;
if (cmsg->cmsg_len != CMSG_LEN(sizeof(u32)))
return -EINVAL;
sockc->ts_opt_id = *(u32 *)CMSG_DATA(cmsg);
sockc->tsflags |= SOCKCM_FLAG_TS_OPT_ID;
break;
/* SCM_RIGHTS and SCM_CREDENTIALS are semantically in SOL_UNIX. */ /* SCM_RIGHTS and SCM_CREDENTIALS are semantically in SOL_UNIX. */
case SCM_RIGHTS: case SCM_RIGHTS:
case SCM_CREDENTIALS: case SCM_CREDENTIALS:

View File

@ -973,7 +973,7 @@ static int __ip_append_data(struct sock *sk,
unsigned int maxfraglen, fragheaderlen, maxnonfragsize; unsigned int maxfraglen, fragheaderlen, maxnonfragsize;
int csummode = CHECKSUM_NONE; int csummode = CHECKSUM_NONE;
struct rtable *rt = dst_rtable(cork->dst); struct rtable *rt = dst_rtable(cork->dst);
bool paged, hold_tskey, extra_uref = false; bool paged, hold_tskey = false, extra_uref = false;
unsigned int wmem_alloc_delta = 0; unsigned int wmem_alloc_delta = 0;
u32 tskey = 0; u32 tskey = 0;
@ -1049,10 +1049,15 @@ static int __ip_append_data(struct sock *sk,
cork->length += length; cork->length += length;
hold_tskey = cork->tx_flags & SKBTX_ANY_TSTAMP && if (cork->tx_flags & SKBTX_ANY_TSTAMP &&
READ_ONCE(sk->sk_tsflags) & SOF_TIMESTAMPING_OPT_ID; READ_ONCE(sk->sk_tsflags) & SOF_TIMESTAMPING_OPT_ID) {
if (hold_tskey) if (cork->flags & IPCORK_TS_OPT_ID) {
tskey = atomic_inc_return(&sk->sk_tskey) - 1; tskey = cork->ts_opt_id;
} else {
tskey = atomic_inc_return(&sk->sk_tskey) - 1;
hold_tskey = true;
}
}
/* So, what's going on in the loop below? /* So, what's going on in the loop below?
* *
@ -1326,7 +1331,11 @@ static int ip_setup_cork(struct sock *sk, struct inet_cork *cork,
cork->priority = ipc->priority; cork->priority = ipc->priority;
cork->transmit_time = ipc->sockc.transmit_time; cork->transmit_time = ipc->sockc.transmit_time;
cork->tx_flags = 0; cork->tx_flags = 0;
sock_tx_timestamp(sk, ipc->sockc.tsflags, &cork->tx_flags); sock_tx_timestamp(sk, &ipc->sockc, &cork->tx_flags);
if (ipc->sockc.tsflags & SOCKCM_FLAG_TS_OPT_ID) {
cork->flags |= IPCORK_TS_OPT_ID;
cork->ts_opt_id = ipc->sockc.ts_opt_id;
}
return 0; return 0;
} }

View File

@ -370,7 +370,7 @@ static int raw_send_hdrinc(struct sock *sk, struct flowi4 *fl4,
skb->ip_summed = CHECKSUM_NONE; skb->ip_summed = CHECKSUM_NONE;
skb_setup_tx_timestamp(skb, sockc->tsflags); skb_setup_tx_timestamp(skb, sockc);
if (flags & MSG_CONFIRM) if (flags & MSG_CONFIRM)
skb_set_dst_pending_confirm(skb, 1); skb_set_dst_pending_confirm(skb, 1);

View File

@ -477,15 +477,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, u16 tsflags) static void tcp_tx_timestamp(struct sock *sk, struct sockcm_cookie *sockc)
{ {
struct sk_buff *skb = tcp_write_queue_tail(sk); struct sk_buff *skb = tcp_write_queue_tail(sk);
u32 tsflags = sockc->tsflags;
if (tsflags && skb) { if (tsflags && skb) {
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); struct tcp_skb_cb *tcb = TCP_SKB_CB(skb);
sock_tx_timestamp(sk, tsflags, &shinfo->tx_flags); sock_tx_timestamp(sk, sockc, &shinfo->tx_flags);
if (tsflags & SOF_TIMESTAMPING_TX_ACK) if (tsflags & SOF_TIMESTAMPING_TX_ACK)
tcb->txstamp_ack = 1; tcb->txstamp_ack = 1;
if (tsflags & SOF_TIMESTAMPING_TX_RECORD_MASK) if (tsflags & SOF_TIMESTAMPING_TX_RECORD_MASK)
@ -1321,7 +1322,7 @@ wait_for_space:
out: out:
if (copied) { if (copied) {
tcp_tx_timestamp(sk, sockc.tsflags); tcp_tx_timestamp(sk, &sockc);
tcp_push(sk, flags, mss_now, tp->nonagle, size_goal); tcp_push(sk, flags, mss_now, tp->nonagle, size_goal);
} }
out_nopush: out_nopush:

View File

@ -1401,8 +1401,11 @@ static int ip6_setup_cork(struct sock *sk, struct inet_cork_full *cork,
cork->base.gso_size = ipc6->gso_size; cork->base.gso_size = ipc6->gso_size;
cork->base.tx_flags = 0; cork->base.tx_flags = 0;
cork->base.mark = ipc6->sockc.mark; cork->base.mark = ipc6->sockc.mark;
sock_tx_timestamp(sk, ipc6->sockc.tsflags, &cork->base.tx_flags); sock_tx_timestamp(sk, &ipc6->sockc, &cork->base.tx_flags);
if (ipc6->sockc.tsflags & SOCKCM_FLAG_TS_OPT_ID) {
cork->base.flags |= IPCORK_TS_OPT_ID;
cork->base.ts_opt_id = ipc6->sockc.ts_opt_id;
}
cork->base.length = 0; cork->base.length = 0;
cork->base.transmit_time = ipc6->sockc.transmit_time; cork->base.transmit_time = ipc6->sockc.transmit_time;
@ -1433,7 +1436,7 @@ static int __ip6_append_data(struct sock *sk,
bool zc = false; bool zc = false;
u32 tskey = 0; u32 tskey = 0;
struct rt6_info *rt = dst_rt6_info(cork->dst); struct rt6_info *rt = dst_rt6_info(cork->dst);
bool paged, hold_tskey, extra_uref = false; bool paged, hold_tskey = false, extra_uref = false;
struct ipv6_txoptions *opt = v6_cork->opt; struct ipv6_txoptions *opt = v6_cork->opt;
int csummode = CHECKSUM_NONE; int csummode = CHECKSUM_NONE;
unsigned int maxnonfragsize, headersize; unsigned int maxnonfragsize, headersize;
@ -1543,10 +1546,15 @@ emsgsize:
flags &= ~MSG_SPLICE_PAGES; flags &= ~MSG_SPLICE_PAGES;
} }
hold_tskey = cork->tx_flags & SKBTX_ANY_TSTAMP && if (cork->tx_flags & SKBTX_ANY_TSTAMP &&
READ_ONCE(sk->sk_tsflags) & SOF_TIMESTAMPING_OPT_ID; READ_ONCE(sk->sk_tsflags) & SOF_TIMESTAMPING_OPT_ID) {
if (hold_tskey) if (cork->flags & IPCORK_TS_OPT_ID) {
tskey = atomic_inc_return(&sk->sk_tskey) - 1; tskey = cork->ts_opt_id;
} else {
tskey = atomic_inc_return(&sk->sk_tskey) - 1;
hold_tskey = true;
}
}
/* /*
* Let's try using as much space as possible. * Let's try using as much space as possible.

View File

@ -629,7 +629,7 @@ static int rawv6_send_hdrinc(struct sock *sk, struct msghdr *msg, int length,
skb->ip_summed = CHECKSUM_NONE; skb->ip_summed = CHECKSUM_NONE;
skb_setup_tx_timestamp(skb, sockc->tsflags); skb_setup_tx_timestamp(skb, sockc);
if (flags & MSG_CONFIRM) if (flags & MSG_CONFIRM)
skb_set_dst_pending_confirm(skb, 1); skb_set_dst_pending_confirm(skb, 1);

View File

@ -2118,7 +2118,7 @@ retry:
skb->priority = READ_ONCE(sk->sk_priority); skb->priority = READ_ONCE(sk->sk_priority);
skb->mark = READ_ONCE(sk->sk_mark); skb->mark = READ_ONCE(sk->sk_mark);
skb_set_delivery_type_by_clockid(skb, sockc.transmit_time, sk->sk_clockid); skb_set_delivery_type_by_clockid(skb, sockc.transmit_time, sk->sk_clockid);
skb_setup_tx_timestamp(skb, sockc.tsflags); skb_setup_tx_timestamp(skb, &sockc);
if (unlikely(extra_len == 4)) if (unlikely(extra_len == 4))
skb->no_fcs = 1; skb->no_fcs = 1;
@ -2650,7 +2650,7 @@ static int tpacket_fill_skb(struct packet_sock *po, struct sk_buff *skb,
skb->priority = READ_ONCE(po->sk.sk_priority); skb->priority = READ_ONCE(po->sk.sk_priority);
skb->mark = READ_ONCE(po->sk.sk_mark); skb->mark = READ_ONCE(po->sk.sk_mark);
skb_set_delivery_type_by_clockid(skb, sockc->transmit_time, po->sk.sk_clockid); skb_set_delivery_type_by_clockid(skb, sockc->transmit_time, po->sk.sk_clockid);
skb_setup_tx_timestamp(skb, sockc->tsflags); skb_setup_tx_timestamp(skb, sockc);
skb_zcopy_set_nouarg(skb, ph.raw); skb_zcopy_set_nouarg(skb, ph.raw);
skb_reserve(skb, hlen); skb_reserve(skb, hlen);
@ -3115,7 +3115,7 @@ static int packet_snd(struct socket *sock, struct msghdr *msg, size_t len)
goto out_free; goto out_free;
} }
skb_setup_tx_timestamp(skb, sockc.tsflags); skb_setup_tx_timestamp(skb, &sockc);
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)) {

View File

@ -687,7 +687,7 @@ void sock_release(struct socket *sock)
} }
EXPORT_SYMBOL(sock_release); EXPORT_SYMBOL(sock_release);
void __sock_tx_timestamp(__u16 tsflags, __u8 *tx_flags) void __sock_tx_timestamp(__u32 tsflags, __u8 *tx_flags)
{ {
u8 flags = *tx_flags; u8 flags = *tx_flags;

View File

@ -124,6 +124,8 @@
#define SO_PASSPIDFD 76 #define SO_PASSPIDFD 76
#define SO_PEERPIDFD 77 #define SO_PEERPIDFD 77
#define SCM_TS_OPT_ID 78
#if !defined(__KERNEL__) #if !defined(__KERNEL__)
#if __BITS_PER_LONG == 64 || (defined(__x86_64__) && defined(__ILP32__)) #if __BITS_PER_LONG == 64 || (defined(__x86_64__) && defined(__ILP32__))

View File

@ -77,6 +77,8 @@ static bool cfg_epollet;
static bool cfg_do_listen; static bool cfg_do_listen;
static uint16_t dest_port = 9000; static uint16_t dest_port = 9000;
static bool cfg_print_nsec; static bool cfg_print_nsec;
static uint32_t ts_opt_id;
static bool cfg_use_cmsg_opt_id;
static struct sockaddr_in daddr; static struct sockaddr_in daddr;
static struct sockaddr_in6 daddr6; static struct sockaddr_in6 daddr6;
@ -136,12 +138,13 @@ static void validate_key(int tskey, int tstype)
/* compare key for each subsequent request /* compare key for each subsequent request
* must only test for one type, the first one requested * must only test for one type, the first one requested
*/ */
if (saved_tskey == -1) if (saved_tskey == -1 || cfg_use_cmsg_opt_id)
saved_tskey_type = tstype; saved_tskey_type = tstype;
else if (saved_tskey_type != tstype) else if (saved_tskey_type != tstype)
return; return;
stepsize = cfg_proto == SOCK_STREAM ? cfg_payload_len : 1; stepsize = cfg_proto == SOCK_STREAM ? cfg_payload_len : 1;
stepsize = cfg_use_cmsg_opt_id ? 0 : stepsize;
if (tskey != saved_tskey + stepsize) { if (tskey != saved_tskey + stepsize) {
fprintf(stderr, "ERROR: key %d, expected %d\n", fprintf(stderr, "ERROR: key %d, expected %d\n",
tskey, saved_tskey + stepsize); tskey, saved_tskey + stepsize);
@ -484,7 +487,7 @@ static void fill_header_udp(void *p, bool is_ipv4)
static void do_test(int family, unsigned int report_opt) static void do_test(int family, unsigned int report_opt)
{ {
char control[CMSG_SPACE(sizeof(uint32_t))]; char control[2 * CMSG_SPACE(sizeof(uint32_t))];
struct sockaddr_ll laddr; struct sockaddr_ll laddr;
unsigned int sock_opt; unsigned int sock_opt;
struct cmsghdr *cmsg; struct cmsghdr *cmsg;
@ -624,18 +627,32 @@ static void do_test(int family, unsigned int report_opt)
msg.msg_iov = &iov; msg.msg_iov = &iov;
msg.msg_iovlen = 1; msg.msg_iovlen = 1;
if (cfg_use_cmsg) { if (cfg_use_cmsg || cfg_use_cmsg_opt_id) {
memset(control, 0, sizeof(control)); memset(control, 0, sizeof(control));
msg.msg_control = control; msg.msg_control = control;
msg.msg_controllen = sizeof(control); msg.msg_controllen = cfg_use_cmsg * CMSG_SPACE(sizeof(uint32_t));
msg.msg_controllen += cfg_use_cmsg_opt_id * CMSG_SPACE(sizeof(uint32_t));
cmsg = CMSG_FIRSTHDR(&msg); cmsg = NULL;
cmsg->cmsg_level = SOL_SOCKET; if (cfg_use_cmsg) {
cmsg->cmsg_type = SO_TIMESTAMPING; cmsg = CMSG_FIRSTHDR(&msg);
cmsg->cmsg_len = CMSG_LEN(sizeof(uint32_t)); cmsg->cmsg_level = SOL_SOCKET;
cmsg->cmsg_type = SO_TIMESTAMPING;
cmsg->cmsg_len = CMSG_LEN(sizeof(uint32_t));
*((uint32_t *)CMSG_DATA(cmsg)) = report_opt;
}
if (cfg_use_cmsg_opt_id) {
cmsg = cmsg ? CMSG_NXTHDR(&msg, cmsg) : CMSG_FIRSTHDR(&msg);
cmsg->cmsg_level = SOL_SOCKET;
cmsg->cmsg_type = SCM_TS_OPT_ID;
cmsg->cmsg_len = CMSG_LEN(sizeof(uint32_t));
*((uint32_t *)CMSG_DATA(cmsg)) = ts_opt_id;
saved_tskey = ts_opt_id;
}
*((uint32_t *) CMSG_DATA(cmsg)) = report_opt;
} }
val = sendmsg(fd, &msg, 0); val = sendmsg(fd, &msg, 0);
@ -685,6 +702,7 @@ static void __attribute__((noreturn)) usage(const char *filepath)
" -L listen on hostname and port\n" " -L listen on hostname and port\n"
" -n: set no-payload option\n" " -n: set no-payload option\n"
" -N: print timestamps and durations in nsec (instead of usec)\n" " -N: print timestamps and durations in nsec (instead of usec)\n"
" -o N: use SCM_TS_OPT_ID control message to provide N as tskey\n"
" -p N: connect to port N\n" " -p N: connect to port N\n"
" -P: use PF_PACKET\n" " -P: use PF_PACKET\n"
" -r: use raw\n" " -r: use raw\n"
@ -705,7 +723,7 @@ static void parse_opt(int argc, char **argv)
int c; int c;
while ((c = getopt(argc, argv, while ((c = getopt(argc, argv,
"46bc:CeEFhIl:LnNp:PrRS:t:uv:V:x")) != -1) { "46bc:CeEFhIl:LnNo:p:PrRS:t:uv:V:x")) != -1) {
switch (c) { switch (c) {
case '4': case '4':
do_ipv6 = 0; do_ipv6 = 0;
@ -746,6 +764,10 @@ static void parse_opt(int argc, char **argv)
case 'N': case 'N':
cfg_print_nsec = true; cfg_print_nsec = true;
break; break;
case 'o':
ts_opt_id = strtoul(optarg, NULL, 10);
cfg_use_cmsg_opt_id = true;
break;
case 'p': case 'p':
dest_port = strtoul(optarg, NULL, 10); dest_port = strtoul(optarg, NULL, 10);
break; break;
@ -803,6 +825,8 @@ static void parse_opt(int argc, char **argv)
error(1, 0, "cannot ask for pktinfo over pf_packet"); error(1, 0, "cannot ask for pktinfo over pf_packet");
if (cfg_busy_poll && cfg_use_epoll) if (cfg_busy_poll && cfg_use_epoll)
error(1, 0, "pass epoll or busy_poll, not both"); error(1, 0, "pass epoll or busy_poll, not both");
if (cfg_proto == SOCK_STREAM && cfg_use_cmsg_opt_id)
error(1, 0, "TCP sockets don't support SCM_TS_OPT_ID");
if (optind != argc - 1) if (optind != argc - 1)
error(1, 0, "missing required hostname argument"); error(1, 0, "missing required hostname argument");

View File

@ -37,11 +37,13 @@ run_test_v4v6() {
run_test_tcpudpraw() { run_test_tcpudpraw() {
local -r args=$@ local -r args=$@
run_test_v4v6 ${args} # tcp run_test_v4v6 ${args} # tcp
run_test_v4v6 ${args} -u # udp run_test_v4v6 ${args} -u # udp
run_test_v4v6 ${args} -r # raw run_test_v4v6 ${args} -u -o 42 # udp with fixed tskey
run_test_v4v6 ${args} -R # raw (IPPROTO_RAW) run_test_v4v6 ${args} -r # raw
run_test_v4v6 ${args} -P # pf_packet run_test_v4v6 ${args} -r -o 42 # raw
run_test_v4v6 ${args} -R # raw (IPPROTO_RAW)
run_test_v4v6 ${args} -P # pf_packet
} }
run_test_all() { run_test_all() {