mirror of
https://mirrors.bfsu.edu.cn/git/linux.git
synced 2024-11-24 04:34:08 +08:00
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:
commit
a73f214e89
@ -194,6 +194,20 @@ SOF_TIMESTAMPING_OPT_ID:
|
||||
among all possibly concurrently outstanding timestamp requests for
|
||||
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:
|
||||
Pass this modifier along with SOF_TIMESTAMPING_OPT_ID for new TCP
|
||||
timestamping applications. SOF_TIMESTAMPING_OPT_ID defines how the
|
||||
|
@ -146,6 +146,8 @@
|
||||
#define SCM_DEVMEM_DMABUF SO_DEVMEM_DMABUF
|
||||
#define SO_DEVMEM_DONTNEED 80
|
||||
|
||||
#define SCM_TS_OPT_ID 81
|
||||
|
||||
#if !defined(__KERNEL__)
|
||||
|
||||
#if __BITS_PER_LONG == 64
|
||||
|
@ -157,6 +157,8 @@
|
||||
#define SCM_DEVMEM_DMABUF SO_DEVMEM_DMABUF
|
||||
#define SO_DEVMEM_DONTNEED 80
|
||||
|
||||
#define SCM_TS_OPT_ID 81
|
||||
|
||||
#if !defined(__KERNEL__)
|
||||
|
||||
#if __BITS_PER_LONG == 64
|
||||
|
@ -138,6 +138,8 @@
|
||||
#define SCM_DEVMEM_DMABUF SO_DEVMEM_DMABUF
|
||||
#define SO_DEVMEM_DONTNEED 80
|
||||
|
||||
#define SCM_TS_OPT_ID 0x404C
|
||||
|
||||
#if !defined(__KERNEL__)
|
||||
|
||||
#if __BITS_PER_LONG == 64
|
||||
|
@ -139,6 +139,8 @@
|
||||
#define SCM_DEVMEM_DMABUF SO_DEVMEM_DMABUF
|
||||
#define SO_DEVMEM_DONTNEED 0x0059
|
||||
|
||||
#define SCM_TS_OPT_ID 0x005a
|
||||
|
||||
#if !defined(__KERNEL__)
|
||||
|
||||
|
||||
|
@ -174,6 +174,7 @@ struct inet_cork {
|
||||
__s16 tos;
|
||||
char priority;
|
||||
__u16 gso_size;
|
||||
u32 ts_opt_id;
|
||||
u64 transmit_time;
|
||||
u32 mark;
|
||||
};
|
||||
@ -241,7 +242,8 @@ struct inet_sock {
|
||||
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 {
|
||||
INET_FLAGS_PKTINFO = 0,
|
||||
|
@ -954,6 +954,12 @@ enum sock_flags {
|
||||
};
|
||||
|
||||
#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)
|
||||
{
|
||||
@ -1796,6 +1802,7 @@ struct sockcm_cookie {
|
||||
u64 transmit_time;
|
||||
u32 mark;
|
||||
u32 tsflags;
|
||||
u32 ts_opt_id;
|
||||
};
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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
|
||||
* @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
|
||||
* @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)
|
||||
*/
|
||||
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)
|
||||
{
|
||||
__u32 tsflags = sockc->tsflags;
|
||||
|
||||
if (unlikely(tsflags)) {
|
||||
__sock_tx_timestamp(tsflags, tx_flags);
|
||||
if (tsflags & SOF_TIMESTAMPING_OPT_ID && tskey &&
|
||||
tsflags & SOF_TIMESTAMPING_TX_RECORD_MASK)
|
||||
*tskey = atomic_inc_return(&sk->sk_tskey) - 1;
|
||||
tsflags & SOF_TIMESTAMPING_TX_RECORD_MASK) {
|
||||
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)))
|
||||
*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)
|
||||
{
|
||||
_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);
|
||||
}
|
||||
|
||||
|
@ -141,6 +141,8 @@
|
||||
#define SCM_DEVMEM_DMABUF SO_DEVMEM_DMABUF
|
||||
#define SO_DEVMEM_DONTNEED 80
|
||||
|
||||
#define SCM_TS_OPT_ID 81
|
||||
|
||||
#if !defined(__KERNEL__)
|
||||
|
||||
#if __BITS_PER_LONG == 64 || (defined(__x86_64__) && defined(__ILP32__))
|
||||
|
@ -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->tstamp = sockc.transmit_time;
|
||||
|
||||
skb_setup_tx_timestamp(skb, sockc.tsflags);
|
||||
skb_setup_tx_timestamp(skb, &sockc);
|
||||
|
||||
err = can_send(skb, ro->loopback);
|
||||
|
||||
|
@ -2899,6 +2899,8 @@ int __sock_cmsg_send(struct sock *sk, struct cmsghdr *cmsg,
|
||||
{
|
||||
u32 tsflags;
|
||||
|
||||
BUILD_BUG_ON(SOF_TIMESTAMPING_LAST == (1 << 31));
|
||||
|
||||
switch (cmsg->cmsg_type) {
|
||||
case SO_MARK:
|
||||
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;
|
||||
sockc->transmit_time = get_unaligned((u64 *)CMSG_DATA(cmsg));
|
||||
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. */
|
||||
case SCM_RIGHTS:
|
||||
case SCM_CREDENTIALS:
|
||||
|
@ -973,7 +973,7 @@ static int __ip_append_data(struct sock *sk,
|
||||
unsigned int maxfraglen, fragheaderlen, maxnonfragsize;
|
||||
int csummode = CHECKSUM_NONE;
|
||||
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;
|
||||
u32 tskey = 0;
|
||||
|
||||
@ -1049,10 +1049,15 @@ static int __ip_append_data(struct sock *sk,
|
||||
|
||||
cork->length += length;
|
||||
|
||||
hold_tskey = cork->tx_flags & SKBTX_ANY_TSTAMP &&
|
||||
READ_ONCE(sk->sk_tsflags) & SOF_TIMESTAMPING_OPT_ID;
|
||||
if (hold_tskey)
|
||||
tskey = atomic_inc_return(&sk->sk_tskey) - 1;
|
||||
if (cork->tx_flags & SKBTX_ANY_TSTAMP &&
|
||||
READ_ONCE(sk->sk_tsflags) & SOF_TIMESTAMPING_OPT_ID) {
|
||||
if (cork->flags & IPCORK_TS_OPT_ID) {
|
||||
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?
|
||||
*
|
||||
@ -1326,7 +1331,11 @@ static int ip_setup_cork(struct sock *sk, struct inet_cork *cork,
|
||||
cork->priority = ipc->priority;
|
||||
cork->transmit_time = ipc->sockc.transmit_time;
|
||||
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;
|
||||
}
|
||||
|
@ -370,7 +370,7 @@ static int raw_send_hdrinc(struct sock *sk, struct flowi4 *fl4,
|
||||
|
||||
skb->ip_summed = CHECKSUM_NONE;
|
||||
|
||||
skb_setup_tx_timestamp(skb, sockc->tsflags);
|
||||
skb_setup_tx_timestamp(skb, sockc);
|
||||
|
||||
if (flags & MSG_CONFIRM)
|
||||
skb_set_dst_pending_confirm(skb, 1);
|
||||
|
@ -477,15 +477,16 @@ void tcp_init_sock(struct sock *sk)
|
||||
}
|
||||
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);
|
||||
u32 tsflags = sockc->tsflags;
|
||||
|
||||
if (tsflags && skb) {
|
||||
struct skb_shared_info *shinfo = skb_shinfo(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)
|
||||
tcb->txstamp_ack = 1;
|
||||
if (tsflags & SOF_TIMESTAMPING_TX_RECORD_MASK)
|
||||
@ -1321,7 +1322,7 @@ wait_for_space:
|
||||
|
||||
out:
|
||||
if (copied) {
|
||||
tcp_tx_timestamp(sk, sockc.tsflags);
|
||||
tcp_tx_timestamp(sk, &sockc);
|
||||
tcp_push(sk, flags, mss_now, tp->nonagle, size_goal);
|
||||
}
|
||||
out_nopush:
|
||||
|
@ -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.tx_flags = 0;
|
||||
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.transmit_time = ipc6->sockc.transmit_time;
|
||||
|
||||
@ -1433,7 +1436,7 @@ static int __ip6_append_data(struct sock *sk,
|
||||
bool zc = false;
|
||||
u32 tskey = 0;
|
||||
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;
|
||||
int csummode = CHECKSUM_NONE;
|
||||
unsigned int maxnonfragsize, headersize;
|
||||
@ -1543,10 +1546,15 @@ emsgsize:
|
||||
flags &= ~MSG_SPLICE_PAGES;
|
||||
}
|
||||
|
||||
hold_tskey = cork->tx_flags & SKBTX_ANY_TSTAMP &&
|
||||
READ_ONCE(sk->sk_tsflags) & SOF_TIMESTAMPING_OPT_ID;
|
||||
if (hold_tskey)
|
||||
tskey = atomic_inc_return(&sk->sk_tskey) - 1;
|
||||
if (cork->tx_flags & SKBTX_ANY_TSTAMP &&
|
||||
READ_ONCE(sk->sk_tsflags) & SOF_TIMESTAMPING_OPT_ID) {
|
||||
if (cork->flags & IPCORK_TS_OPT_ID) {
|
||||
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.
|
||||
|
@ -629,7 +629,7 @@ static int rawv6_send_hdrinc(struct sock *sk, struct msghdr *msg, int length,
|
||||
|
||||
skb->ip_summed = CHECKSUM_NONE;
|
||||
|
||||
skb_setup_tx_timestamp(skb, sockc->tsflags);
|
||||
skb_setup_tx_timestamp(skb, sockc);
|
||||
|
||||
if (flags & MSG_CONFIRM)
|
||||
skb_set_dst_pending_confirm(skb, 1);
|
||||
|
@ -2118,7 +2118,7 @@ retry:
|
||||
skb->priority = READ_ONCE(sk->sk_priority);
|
||||
skb->mark = READ_ONCE(sk->sk_mark);
|
||||
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))
|
||||
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->mark = READ_ONCE(po->sk.sk_mark);
|
||||
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_reserve(skb, hlen);
|
||||
@ -3115,7 +3115,7 @@ static int packet_snd(struct socket *sock, struct msghdr *msg, size_t len)
|
||||
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) &&
|
||||
!packet_extra_vlan_len_allowed(dev, skb)) {
|
||||
|
@ -687,7 +687,7 @@ void sock_release(struct socket *sock)
|
||||
}
|
||||
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;
|
||||
|
||||
|
@ -124,6 +124,8 @@
|
||||
#define SO_PASSPIDFD 76
|
||||
#define SO_PEERPIDFD 77
|
||||
|
||||
#define SCM_TS_OPT_ID 78
|
||||
|
||||
#if !defined(__KERNEL__)
|
||||
|
||||
#if __BITS_PER_LONG == 64 || (defined(__x86_64__) && defined(__ILP32__))
|
||||
|
@ -77,6 +77,8 @@ static bool cfg_epollet;
|
||||
static bool cfg_do_listen;
|
||||
static uint16_t dest_port = 9000;
|
||||
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_in6 daddr6;
|
||||
@ -136,12 +138,13 @@ static void validate_key(int tskey, int tstype)
|
||||
/* compare key for each subsequent request
|
||||
* 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;
|
||||
else if (saved_tskey_type != tstype)
|
||||
return;
|
||||
|
||||
stepsize = cfg_proto == SOCK_STREAM ? cfg_payload_len : 1;
|
||||
stepsize = cfg_use_cmsg_opt_id ? 0 : stepsize;
|
||||
if (tskey != saved_tskey + stepsize) {
|
||||
fprintf(stderr, "ERROR: key %d, expected %d\n",
|
||||
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)
|
||||
{
|
||||
char control[CMSG_SPACE(sizeof(uint32_t))];
|
||||
char control[2 * CMSG_SPACE(sizeof(uint32_t))];
|
||||
struct sockaddr_ll laddr;
|
||||
unsigned int sock_opt;
|
||||
struct cmsghdr *cmsg;
|
||||
@ -624,18 +627,32 @@ static void do_test(int family, unsigned int report_opt)
|
||||
msg.msg_iov = &iov;
|
||||
msg.msg_iovlen = 1;
|
||||
|
||||
if (cfg_use_cmsg) {
|
||||
if (cfg_use_cmsg || cfg_use_cmsg_opt_id) {
|
||||
memset(control, 0, sizeof(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->cmsg_level = SOL_SOCKET;
|
||||
cmsg->cmsg_type = SO_TIMESTAMPING;
|
||||
cmsg->cmsg_len = CMSG_LEN(sizeof(uint32_t));
|
||||
cmsg = NULL;
|
||||
if (cfg_use_cmsg) {
|
||||
cmsg = CMSG_FIRSTHDR(&msg);
|
||||
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);
|
||||
@ -685,6 +702,7 @@ static void __attribute__((noreturn)) usage(const char *filepath)
|
||||
" -L listen on hostname and port\n"
|
||||
" -n: set no-payload option\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: use PF_PACKET\n"
|
||||
" -r: use raw\n"
|
||||
@ -705,7 +723,7 @@ static void parse_opt(int argc, char **argv)
|
||||
int c;
|
||||
|
||||
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) {
|
||||
case '4':
|
||||
do_ipv6 = 0;
|
||||
@ -746,6 +764,10 @@ static void parse_opt(int argc, char **argv)
|
||||
case 'N':
|
||||
cfg_print_nsec = true;
|
||||
break;
|
||||
case 'o':
|
||||
ts_opt_id = strtoul(optarg, NULL, 10);
|
||||
cfg_use_cmsg_opt_id = true;
|
||||
break;
|
||||
case 'p':
|
||||
dest_port = strtoul(optarg, NULL, 10);
|
||||
break;
|
||||
@ -803,6 +825,8 @@ static void parse_opt(int argc, char **argv)
|
||||
error(1, 0, "cannot ask for pktinfo over pf_packet");
|
||||
if (cfg_busy_poll && cfg_use_epoll)
|
||||
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)
|
||||
error(1, 0, "missing required hostname argument");
|
||||
|
@ -37,11 +37,13 @@ run_test_v4v6() {
|
||||
run_test_tcpudpraw() {
|
||||
local -r args=$@
|
||||
|
||||
run_test_v4v6 ${args} # tcp
|
||||
run_test_v4v6 ${args} -u # udp
|
||||
run_test_v4v6 ${args} -r # raw
|
||||
run_test_v4v6 ${args} -R # raw (IPPROTO_RAW)
|
||||
run_test_v4v6 ${args} -P # pf_packet
|
||||
run_test_v4v6 ${args} # tcp
|
||||
run_test_v4v6 ${args} -u # udp
|
||||
run_test_v4v6 ${args} -u -o 42 # udp with fixed tskey
|
||||
run_test_v4v6 ${args} -r # raw
|
||||
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() {
|
||||
|
Loading…
Reference in New Issue
Block a user