Skip to content

Commit aae68bc

Browse files
committed
Merge branch 'tstamp-next'
Willem de Bruijn says: ==================== timestamping updates The main goal for this patchset is to allow correlating timestamps with the egress interface. Also introduce a warning, as discussed previously, and update the tests to verify the new feature. ==================== Signed-off-by: David S. Miller <davem@davemloft.net>
2 parents 8d0c469 + cbd3aad commit aae68bc

File tree

5 files changed

+146
-25
lines changed

5 files changed

+146
-25
lines changed

Documentation/networking/timestamping.txt

Lines changed: 25 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -122,27 +122,44 @@ SOF_TIMESTAMPING_RAW_HARDWARE:
122122

123123
1.3.3 Timestamp Options
124124

125-
The interface supports one option
125+
The interface supports the options
126126

127127
SOF_TIMESTAMPING_OPT_ID:
128128

129129
Generate a unique identifier along with each packet. A process can
130130
have multiple concurrent timestamping requests outstanding. Packets
131131
can be reordered in the transmit path, for instance in the packet
132132
scheduler. In that case timestamps will be queued onto the error
133-
queue out of order from the original send() calls. This option
134-
embeds a counter that is incremented at send() time, to order
135-
timestamps within a flow.
133+
queue out of order from the original send() calls. It is not always
134+
possible to uniquely match timestamps to the original send() calls
135+
based on timestamp order or payload inspection alone, then.
136+
137+
This option associates each packet at send() with a unique
138+
identifier and returns that along with the timestamp. The identifier
139+
is derived from a per-socket u32 counter (that wraps). For datagram
140+
sockets, the counter increments with each sent packet. For stream
141+
sockets, it increments with every byte.
142+
143+
The counter starts at zero. It is initialized the first time that
144+
the socket option is enabled. It is reset each time the option is
145+
enabled after having been disabled. Resetting the counter does not
146+
change the identifiers of existing packets in the system.
136147

137148
This option is implemented only for transmit timestamps. There, the
138149
timestamp is always looped along with a struct sock_extended_err.
139150
The option modifies field ee_data to pass an id that is unique
140151
among all possibly concurrently outstanding timestamp requests for
141-
that socket. In practice, it is a monotonically increasing u32
142-
(that wraps).
152+
that socket.
153+
154+
155+
SOF_TIMESTAMPING_OPT_CMSG:
143156

144-
In datagram sockets, the counter increments on each send call. In
145-
stream sockets, it increments with every byte.
157+
Support recv() cmsg for all timestamped packets. Control messages
158+
are already supported unconditionally on all packets with receive
159+
timestamps and on IPv6 packets with transmit timestamp. This option
160+
extends them to IPv4 packets with transmit timestamp. One use case
161+
is to correlate packets with their egress device, by enabling socket
162+
option IP_PKTINFO simultaneously.
146163

147164

148165
1.4 Bytestream Timestamps

Documentation/networking/timestamping/txtimestamp.c

Lines changed: 78 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@
4646
#include <netpacket/packet.h>
4747
#include <poll.h>
4848
#include <stdarg.h>
49+
#include <stdbool.h>
4950
#include <stdint.h>
5051
#include <stdio.h>
5152
#include <stdlib.h>
@@ -58,13 +59,23 @@
5859
#include <time.h>
5960
#include <unistd.h>
6061

62+
/* ugly hack to work around netinet/in.h and linux/ipv6.h conflicts */
63+
#ifndef in6_pktinfo
64+
struct in6_pktinfo {
65+
struct in6_addr ipi6_addr;
66+
int ipi6_ifindex;
67+
};
68+
#endif
69+
6170
/* command line parameters */
6271
static int cfg_proto = SOCK_STREAM;
6372
static int cfg_ipproto = IPPROTO_TCP;
6473
static int cfg_num_pkts = 4;
6574
static int do_ipv4 = 1;
6675
static int do_ipv6 = 1;
6776
static int cfg_payload_len = 10;
77+
static bool cfg_show_payload;
78+
static bool cfg_do_pktinfo;
6879
static uint16_t dest_port = 9000;
6980

7081
static struct sockaddr_in daddr;
@@ -131,6 +142,30 @@ static void print_timestamp(struct scm_timestamping *tss, int tstype,
131142
__print_timestamp(tsname, &tss->ts[0], tskey, payload_len);
132143
}
133144

145+
/* TODO: convert to check_and_print payload once API is stable */
146+
static void print_payload(char *data, int len)
147+
{
148+
int i;
149+
150+
if (len > 70)
151+
len = 70;
152+
153+
fprintf(stderr, "payload: ");
154+
for (i = 0; i < len; i++)
155+
fprintf(stderr, "%02hhx ", data[i]);
156+
fprintf(stderr, "\n");
157+
}
158+
159+
static void print_pktinfo(int family, int ifindex, void *saddr, void *daddr)
160+
{
161+
char sa[INET6_ADDRSTRLEN], da[INET6_ADDRSTRLEN];
162+
163+
fprintf(stderr, " pktinfo: ifindex=%u src=%s dst=%s\n",
164+
ifindex,
165+
saddr ? inet_ntop(family, saddr, sa, sizeof(sa)) : "unknown",
166+
daddr ? inet_ntop(family, daddr, da, sizeof(da)) : "unknown");
167+
}
168+
134169
static void __poll(int fd)
135170
{
136171
struct pollfd pollfd;
@@ -156,10 +191,9 @@ static void __recv_errmsg_cmsg(struct msghdr *msg, int payload_len)
156191
cm->cmsg_type == SCM_TIMESTAMPING) {
157192
tss = (void *) CMSG_DATA(cm);
158193
} else if ((cm->cmsg_level == SOL_IP &&
159-
cm->cmsg_type == IP_RECVERR) ||
160-
(cm->cmsg_level == SOL_IPV6 &&
161-
cm->cmsg_type == IPV6_RECVERR)) {
162-
194+
cm->cmsg_type == IP_RECVERR) ||
195+
(cm->cmsg_level == SOL_IPV6 &&
196+
cm->cmsg_type == IPV6_RECVERR)) {
163197
serr = (void *) CMSG_DATA(cm);
164198
if (serr->ee_errno != ENOMSG ||
165199
serr->ee_origin != SO_EE_ORIGIN_TIMESTAMPING) {
@@ -168,6 +202,16 @@ static void __recv_errmsg_cmsg(struct msghdr *msg, int payload_len)
168202
serr->ee_origin);
169203
serr = NULL;
170204
}
205+
} else if (cm->cmsg_level == SOL_IP &&
206+
cm->cmsg_type == IP_PKTINFO) {
207+
struct in_pktinfo *info = (void *) CMSG_DATA(cm);
208+
print_pktinfo(AF_INET, info->ipi_ifindex,
209+
&info->ipi_spec_dst, &info->ipi_addr);
210+
} else if (cm->cmsg_level == SOL_IPV6 &&
211+
cm->cmsg_type == IPV6_PKTINFO) {
212+
struct in6_pktinfo *info6 = (void *) CMSG_DATA(cm);
213+
print_pktinfo(AF_INET6, info6->ipi6_ifindex,
214+
NULL, &info6->ipi6_addr);
171215
} else
172216
fprintf(stderr, "unknown cmsg %d,%d\n",
173217
cm->cmsg_level, cm->cmsg_type);
@@ -206,7 +250,11 @@ static int recv_errmsg(int fd)
206250
if (ret == -1 && errno != EAGAIN)
207251
error(1, errno, "recvmsg");
208252

209-
__recv_errmsg_cmsg(&msg, ret);
253+
if (ret > 0) {
254+
__recv_errmsg_cmsg(&msg, ret);
255+
if (cfg_show_payload)
256+
print_payload(data, cfg_payload_len);
257+
}
210258

211259
free(data);
212260
return ret == -1;
@@ -215,9 +263,9 @@ static int recv_errmsg(int fd)
215263
static void do_test(int family, unsigned int opt)
216264
{
217265
char *buf;
218-
int fd, i, val, total_len;
266+
int fd, i, val = 1, total_len;
219267

220-
if (family == IPPROTO_IPV6 && cfg_proto != SOCK_STREAM) {
268+
if (family == AF_INET6 && cfg_proto != SOCK_STREAM) {
221269
/* due to lack of checksum generation code */
222270
fprintf(stderr, "test: skipping datagram over IPv6\n");
223271
return;
@@ -239,7 +287,6 @@ static void do_test(int family, unsigned int opt)
239287
error(1, errno, "socket");
240288

241289
if (cfg_proto == SOCK_STREAM) {
242-
val = 1;
243290
if (setsockopt(fd, IPPROTO_TCP, TCP_NODELAY,
244291
(char*) &val, sizeof(val)))
245292
error(1, 0, "setsockopt no nagle");
@@ -253,7 +300,20 @@ static void do_test(int family, unsigned int opt)
253300
}
254301
}
255302

303+
if (cfg_do_pktinfo) {
304+
if (family == AF_INET6) {
305+
if (setsockopt(fd, SOL_IPV6, IPV6_RECVPKTINFO,
306+
&val, sizeof(val)))
307+
error(1, errno, "setsockopt pktinfo ipv6");
308+
} else {
309+
if (setsockopt(fd, SOL_IP, IP_PKTINFO,
310+
&val, sizeof(val)))
311+
error(1, errno, "setsockopt pktinfo ipv4");
312+
}
313+
}
314+
256315
opt |= SOF_TIMESTAMPING_SOFTWARE |
316+
SOF_TIMESTAMPING_OPT_CMSG |
257317
SOF_TIMESTAMPING_OPT_ID;
258318
if (setsockopt(fd, SOL_SOCKET, SO_TIMESTAMPING,
259319
(char *) &opt, sizeof(opt)))
@@ -262,8 +322,6 @@ static void do_test(int family, unsigned int opt)
262322
for (i = 0; i < cfg_num_pkts; i++) {
263323
memset(&ts_prev, 0, sizeof(ts_prev));
264324
memset(buf, 'a' + i, total_len);
265-
buf[total_len - 2] = '\n';
266-
buf[total_len - 1] = '\0';
267325

268326
if (cfg_proto == SOCK_RAW) {
269327
struct udphdr *udph;
@@ -324,11 +382,13 @@ static void __attribute__((noreturn)) usage(const char *filepath)
324382
" -4: only IPv4\n"
325383
" -6: only IPv6\n"
326384
" -h: show this message\n"
385+
" -I: request PKTINFO\n"
327386
" -l N: send N bytes at a time\n"
328387
" -r: use raw\n"
329388
" -R: use raw (IP_HDRINCL)\n"
330389
" -p N: connect to port N\n"
331-
" -u: use udp\n",
390+
" -u: use udp\n"
391+
" -x: show payload (up to 70 bytes)\n",
332392
filepath);
333393
exit(1);
334394
}
@@ -338,14 +398,17 @@ static void parse_opt(int argc, char **argv)
338398
int proto_count = 0;
339399
char c;
340400

341-
while ((c = getopt(argc, argv, "46hl:p:rRu")) != -1) {
401+
while ((c = getopt(argc, argv, "46hIl:p:rRux")) != -1) {
342402
switch (c) {
343403
case '4':
344404
do_ipv6 = 0;
345405
break;
346406
case '6':
347407
do_ipv4 = 0;
348408
break;
409+
case 'I':
410+
cfg_do_pktinfo = true;
411+
break;
349412
case 'r':
350413
proto_count++;
351414
cfg_proto = SOCK_RAW;
@@ -367,6 +430,9 @@ static void parse_opt(int argc, char **argv)
367430
case 'p':
368431
dest_port = strtoul(optarg, NULL, 10);
369432
break;
433+
case 'x':
434+
cfg_show_payload = true;
435+
break;
370436
case 'h':
371437
default:
372438
usage(argv[0]);

include/uapi/linux/net_tstamp.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,9 @@ enum {
2323
SOF_TIMESTAMPING_OPT_ID = (1<<7),
2424
SOF_TIMESTAMPING_TX_SCHED = (1<<8),
2525
SOF_TIMESTAMPING_TX_ACK = (1<<9),
26+
SOF_TIMESTAMPING_OPT_CMSG = (1<<10),
2627

27-
SOF_TIMESTAMPING_LAST = SOF_TIMESTAMPING_TX_ACK,
28+
SOF_TIMESTAMPING_LAST = SOF_TIMESTAMPING_OPT_CMSG,
2829
SOF_TIMESTAMPING_MASK = (SOF_TIMESTAMPING_LAST - 1) |
2930
SOF_TIMESTAMPING_LAST
3031
};

net/ipv4/ip_sockglue.c

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -399,6 +399,22 @@ void ip_local_error(struct sock *sk, int err, __be32 daddr, __be16 port, u32 inf
399399
kfree_skb(skb);
400400
}
401401

402+
static bool ipv4_pktinfo_prepare_errqueue(const struct sock *sk,
403+
const struct sk_buff *skb,
404+
int ee_origin)
405+
{
406+
struct in_pktinfo *info = PKTINFO_SKB_CB(skb);
407+
408+
if ((ee_origin != SO_EE_ORIGIN_TIMESTAMPING) ||
409+
(!(sk->sk_tsflags & SOF_TIMESTAMPING_OPT_CMSG)) ||
410+
(!skb->dev))
411+
return false;
412+
413+
info->ipi_spec_dst.s_addr = ip_hdr(skb)->saddr;
414+
info->ipi_ifindex = skb->dev->ifindex;
415+
return true;
416+
}
417+
402418
/*
403419
* Handle MSG_ERRQUEUE
404420
*/
@@ -414,6 +430,8 @@ int ip_recv_error(struct sock *sk, struct msghdr *msg, int len, int *addr_len)
414430
int err;
415431
int copied;
416432

433+
WARN_ON_ONCE(sk->sk_family == AF_INET6);
434+
417435
err = -EAGAIN;
418436
skb = sock_dequeue_err_skb(sk);
419437
if (skb == NULL)
@@ -444,7 +462,9 @@ int ip_recv_error(struct sock *sk, struct msghdr *msg, int len, int *addr_len)
444462
memcpy(&errhdr.ee, &serr->ee, sizeof(struct sock_extended_err));
445463
sin = &errhdr.offender;
446464
sin->sin_family = AF_UNSPEC;
447-
if (serr->ee.ee_origin == SO_EE_ORIGIN_ICMP) {
465+
466+
if (serr->ee.ee_origin == SO_EE_ORIGIN_ICMP ||
467+
ipv4_pktinfo_prepare_errqueue(sk, skb, serr->ee.ee_origin)) {
448468
struct inet_sock *inet = inet_sk(sk);
449469

450470
sin->sin_family = AF_INET;
@@ -1049,7 +1069,7 @@ static int do_ip_setsockopt(struct sock *sk, int level,
10491069
}
10501070

10511071
/**
1052-
* ipv4_pktinfo_prepare - transfert some info from rtable to skb
1072+
* ipv4_pktinfo_prepare - transfer some info from rtable to skb
10531073
* @sk: socket
10541074
* @skb: buffer
10551075
*

net/ipv6/datagram.c

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -325,6 +325,16 @@ void ipv6_local_rxpmtu(struct sock *sk, struct flowi6 *fl6, u32 mtu)
325325
kfree_skb(skb);
326326
}
327327

328+
static void ip6_datagram_prepare_pktinfo_errqueue(struct sk_buff *skb)
329+
{
330+
int ifindex = skb->dev ? skb->dev->ifindex : -1;
331+
332+
if (skb->protocol == htons(ETH_P_IPV6))
333+
IP6CB(skb)->iif = ifindex;
334+
else
335+
PKTINFO_SKB_CB(skb)->ipi_ifindex = ifindex;
336+
}
337+
328338
/*
329339
* Handle MSG_ERRQUEUE
330340
*/
@@ -388,8 +398,12 @@ int ipv6_recv_error(struct sock *sk, struct msghdr *msg, int len, int *addr_len)
388398
sin->sin6_family = AF_INET6;
389399
sin->sin6_flowinfo = 0;
390400
sin->sin6_port = 0;
391-
if (np->rxopt.all)
401+
if (np->rxopt.all) {
402+
if (serr->ee.ee_origin != SO_EE_ORIGIN_ICMP &&
403+
serr->ee.ee_origin != SO_EE_ORIGIN_ICMP6)
404+
ip6_datagram_prepare_pktinfo_errqueue(skb);
392405
ip6_datagram_recv_common_ctl(sk, msg, skb);
406+
}
393407
if (skb->protocol == htons(ETH_P_IPV6)) {
394408
sin->sin6_addr = ipv6_hdr(skb)->saddr;
395409
if (np->rxopt.all)
@@ -491,7 +505,10 @@ void ip6_datagram_recv_common_ctl(struct sock *sk, struct msghdr *msg,
491505
ipv6_addr_set_v4mapped(ip_hdr(skb)->daddr,
492506
&src_info.ipi6_addr);
493507
}
494-
put_cmsg(msg, SOL_IPV6, IPV6_PKTINFO, sizeof(src_info), &src_info);
508+
509+
if (src_info.ipi6_ifindex >= 0)
510+
put_cmsg(msg, SOL_IPV6, IPV6_PKTINFO,
511+
sizeof(src_info), &src_info);
495512
}
496513
}
497514

0 commit comments

Comments
 (0)