aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Documentation/networking/timestamping.txt33
-rw-r--r--Documentation/networking/timestamping/txtimestamp.c90
-rw-r--r--include/uapi/linux/net_tstamp.h3
-rw-r--r--net/ipv4/ip_sockglue.c24
-rw-r--r--net/ipv6/datagram.c21
5 files changed, 146 insertions, 25 deletions
diff --git a/Documentation/networking/timestamping.txt b/Documentation/networking/timestamping.txt
index 1d6d02d6ba52..a5c784c89312 100644
--- a/Documentation/networking/timestamping.txt
+++ b/Documentation/networking/timestamping.txt
@@ -122,7 +122,7 @@ SOF_TIMESTAMPING_RAW_HARDWARE:
122 122
1231.3.3 Timestamp Options 1231.3.3 Timestamp Options
124 124
125The interface supports one option 125The interface supports the options
126 126
127SOF_TIMESTAMPING_OPT_ID: 127SOF_TIMESTAMPING_OPT_ID:
128 128
@@ -130,19 +130,36 @@ SOF_TIMESTAMPING_OPT_ID:
130 have multiple concurrent timestamping requests outstanding. Packets 130 have multiple concurrent timestamping requests outstanding. Packets
131 can be reordered in the transmit path, for instance in the packet 131 can be reordered in the transmit path, for instance in the packet
132 scheduler. In that case timestamps will be queued onto the error 132 scheduler. In that case timestamps will be queued onto the error
133 queue out of order from the original send() calls. This option 133 queue out of order from the original send() calls. It is not always
134 embeds a counter that is incremented at send() time, to order 134 possible to uniquely match timestamps to the original send() calls
135 timestamps within a flow. 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.
136 147
137 This option is implemented only for transmit timestamps. There, the 148 This option is implemented only for transmit timestamps. There, the
138 timestamp is always looped along with a struct sock_extended_err. 149 timestamp is always looped along with a struct sock_extended_err.
139 The option modifies field ee_data to pass an id that is unique 150 The option modifies field ee_data to pass an id that is unique
140 among all possibly concurrently outstanding timestamp requests for 151 among all possibly concurrently outstanding timestamp requests for
141 that socket. In practice, it is a monotonically increasing u32 152 that socket.
142 (that wraps). 153
154
155SOF_TIMESTAMPING_OPT_CMSG:
143 156
144 In datagram sockets, the counter increments on each send call. In 157 Support recv() cmsg for all timestamped packets. Control messages
145 stream sockets, it increments with every byte. 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.
146 163
147 164
1481.4 Bytestream Timestamps 1651.4 Bytestream Timestamps
diff --git a/Documentation/networking/timestamping/txtimestamp.c b/Documentation/networking/timestamping/txtimestamp.c
index b32fc2a07734..876f71c5625a 100644
--- a/Documentation/networking/timestamping/txtimestamp.c
+++ b/Documentation/networking/timestamping/txtimestamp.c
@@ -46,6 +46,7 @@
46#include <netpacket/packet.h> 46#include <netpacket/packet.h>
47#include <poll.h> 47#include <poll.h>
48#include <stdarg.h> 48#include <stdarg.h>
49#include <stdbool.h>
49#include <stdint.h> 50#include <stdint.h>
50#include <stdio.h> 51#include <stdio.h>
51#include <stdlib.h> 52#include <stdlib.h>
@@ -58,6 +59,14 @@
58#include <time.h> 59#include <time.h>
59#include <unistd.h> 60#include <unistd.h>
60 61
62/* ugly hack to work around netinet/in.h and linux/ipv6.h conflicts */
63#ifndef in6_pktinfo
64struct in6_pktinfo {
65 struct in6_addr ipi6_addr;
66 int ipi6_ifindex;
67};
68#endif
69
61/* command line parameters */ 70/* command line parameters */
62static int cfg_proto = SOCK_STREAM; 71static int cfg_proto = SOCK_STREAM;
63static int cfg_ipproto = IPPROTO_TCP; 72static int cfg_ipproto = IPPROTO_TCP;
@@ -65,6 +74,8 @@ static int cfg_num_pkts = 4;
65static int do_ipv4 = 1; 74static int do_ipv4 = 1;
66static int do_ipv6 = 1; 75static int do_ipv6 = 1;
67static int cfg_payload_len = 10; 76static int cfg_payload_len = 10;
77static bool cfg_show_payload;
78static bool cfg_do_pktinfo;
68static uint16_t dest_port = 9000; 79static uint16_t dest_port = 9000;
69 80
70static struct sockaddr_in daddr; 81static struct sockaddr_in daddr;
@@ -131,6 +142,30 @@ static void print_timestamp(struct scm_timestamping *tss, int tstype,
131 __print_timestamp(tsname, &tss->ts[0], tskey, payload_len); 142 __print_timestamp(tsname, &tss->ts[0], tskey, payload_len);
132} 143}
133 144
145/* TODO: convert to check_and_print payload once API is stable */
146static 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
159static 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
134static void __poll(int fd) 169static void __poll(int fd)
135{ 170{
136 struct pollfd pollfd; 171 struct pollfd pollfd;
@@ -156,10 +191,9 @@ static void __recv_errmsg_cmsg(struct msghdr *msg, int payload_len)
156 cm->cmsg_type == SCM_TIMESTAMPING) { 191 cm->cmsg_type == SCM_TIMESTAMPING) {
157 tss = (void *) CMSG_DATA(cm); 192 tss = (void *) CMSG_DATA(cm);
158 } else if ((cm->cmsg_level == SOL_IP && 193 } else if ((cm->cmsg_level == SOL_IP &&
159 cm->cmsg_type == IP_RECVERR) || 194 cm->cmsg_type == IP_RECVERR) ||
160 (cm->cmsg_level == SOL_IPV6 && 195 (cm->cmsg_level == SOL_IPV6 &&
161 cm->cmsg_type == IPV6_RECVERR)) { 196 cm->cmsg_type == IPV6_RECVERR)) {
162
163 serr = (void *) CMSG_DATA(cm); 197 serr = (void *) CMSG_DATA(cm);
164 if (serr->ee_errno != ENOMSG || 198 if (serr->ee_errno != ENOMSG ||
165 serr->ee_origin != SO_EE_ORIGIN_TIMESTAMPING) { 199 serr->ee_origin != SO_EE_ORIGIN_TIMESTAMPING) {
@@ -168,6 +202,16 @@ static void __recv_errmsg_cmsg(struct msghdr *msg, int payload_len)
168 serr->ee_origin); 202 serr->ee_origin);
169 serr = NULL; 203 serr = NULL;
170 } 204 }
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);
171 } else 215 } else
172 fprintf(stderr, "unknown cmsg %d,%d\n", 216 fprintf(stderr, "unknown cmsg %d,%d\n",
173 cm->cmsg_level, cm->cmsg_type); 217 cm->cmsg_level, cm->cmsg_type);
@@ -206,7 +250,11 @@ static int recv_errmsg(int fd)
206 if (ret == -1 && errno != EAGAIN) 250 if (ret == -1 && errno != EAGAIN)
207 error(1, errno, "recvmsg"); 251 error(1, errno, "recvmsg");
208 252
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 }
210 258
211 free(data); 259 free(data);
212 return ret == -1; 260 return ret == -1;
@@ -215,9 +263,9 @@ static int recv_errmsg(int fd)
215static void do_test(int family, unsigned int opt) 263static void do_test(int family, unsigned int opt)
216{ 264{
217 char *buf; 265 char *buf;
218 int fd, i, val, total_len; 266 int fd, i, val = 1, total_len;
219 267
220 if (family == IPPROTO_IPV6 && cfg_proto != SOCK_STREAM) { 268 if (family == AF_INET6 && cfg_proto != SOCK_STREAM) {
221 /* due to lack of checksum generation code */ 269 /* due to lack of checksum generation code */
222 fprintf(stderr, "test: skipping datagram over IPv6\n"); 270 fprintf(stderr, "test: skipping datagram over IPv6\n");
223 return; 271 return;
@@ -239,7 +287,6 @@ static void do_test(int family, unsigned int opt)
239 error(1, errno, "socket"); 287 error(1, errno, "socket");
240 288
241 if (cfg_proto == SOCK_STREAM) { 289 if (cfg_proto == SOCK_STREAM) {
242 val = 1;
243 if (setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, 290 if (setsockopt(fd, IPPROTO_TCP, TCP_NODELAY,
244 (char*) &val, sizeof(val))) 291 (char*) &val, sizeof(val)))
245 error(1, 0, "setsockopt no nagle"); 292 error(1, 0, "setsockopt no nagle");
@@ -253,7 +300,20 @@ static void do_test(int family, unsigned int opt)
253 } 300 }
254 } 301 }
255 302
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
256 opt |= SOF_TIMESTAMPING_SOFTWARE | 315 opt |= SOF_TIMESTAMPING_SOFTWARE |
316 SOF_TIMESTAMPING_OPT_CMSG |
257 SOF_TIMESTAMPING_OPT_ID; 317 SOF_TIMESTAMPING_OPT_ID;
258 if (setsockopt(fd, SOL_SOCKET, SO_TIMESTAMPING, 318 if (setsockopt(fd, SOL_SOCKET, SO_TIMESTAMPING,
259 (char *) &opt, sizeof(opt))) 319 (char *) &opt, sizeof(opt)))
@@ -262,8 +322,6 @@ static void do_test(int family, unsigned int opt)
262 for (i = 0; i < cfg_num_pkts; i++) { 322 for (i = 0; i < cfg_num_pkts; i++) {
263 memset(&ts_prev, 0, sizeof(ts_prev)); 323 memset(&ts_prev, 0, sizeof(ts_prev));
264 memset(buf, 'a' + i, total_len); 324 memset(buf, 'a' + i, total_len);
265 buf[total_len - 2] = '\n';
266 buf[total_len - 1] = '\0';
267 325
268 if (cfg_proto == SOCK_RAW) { 326 if (cfg_proto == SOCK_RAW) {
269 struct udphdr *udph; 327 struct udphdr *udph;
@@ -324,11 +382,13 @@ static void __attribute__((noreturn)) usage(const char *filepath)
324 " -4: only IPv4\n" 382 " -4: only IPv4\n"
325 " -6: only IPv6\n" 383 " -6: only IPv6\n"
326 " -h: show this message\n" 384 " -h: show this message\n"
385 " -I: request PKTINFO\n"
327 " -l N: send N bytes at a time\n" 386 " -l N: send N bytes at a time\n"
328 " -r: use raw\n" 387 " -r: use raw\n"
329 " -R: use raw (IP_HDRINCL)\n" 388 " -R: use raw (IP_HDRINCL)\n"
330 " -p N: connect to port N\n" 389 " -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",
332 filepath); 392 filepath);
333 exit(1); 393 exit(1);
334} 394}
@@ -338,7 +398,7 @@ static void parse_opt(int argc, char **argv)
338 int proto_count = 0; 398 int proto_count = 0;
339 char c; 399 char c;
340 400
341 while ((c = getopt(argc, argv, "46hl:p:rRu")) != -1) { 401 while ((c = getopt(argc, argv, "46hIl:p:rRux")) != -1) {
342 switch (c) { 402 switch (c) {
343 case '4': 403 case '4':
344 do_ipv6 = 0; 404 do_ipv6 = 0;
@@ -346,6 +406,9 @@ static void parse_opt(int argc, char **argv)
346 case '6': 406 case '6':
347 do_ipv4 = 0; 407 do_ipv4 = 0;
348 break; 408 break;
409 case 'I':
410 cfg_do_pktinfo = true;
411 break;
349 case 'r': 412 case 'r':
350 proto_count++; 413 proto_count++;
351 cfg_proto = SOCK_RAW; 414 cfg_proto = SOCK_RAW;
@@ -367,6 +430,9 @@ static void parse_opt(int argc, char **argv)
367 case 'p': 430 case 'p':
368 dest_port = strtoul(optarg, NULL, 10); 431 dest_port = strtoul(optarg, NULL, 10);
369 break; 432 break;
433 case 'x':
434 cfg_show_payload = true;
435 break;
370 case 'h': 436 case 'h':
371 default: 437 default:
372 usage(argv[0]); 438 usage(argv[0]);
diff --git a/include/uapi/linux/net_tstamp.h b/include/uapi/linux/net_tstamp.h
index ff354021bb69..edbc888ceb51 100644
--- a/include/uapi/linux/net_tstamp.h
+++ b/include/uapi/linux/net_tstamp.h
@@ -23,8 +23,9 @@ enum {
23 SOF_TIMESTAMPING_OPT_ID = (1<<7), 23 SOF_TIMESTAMPING_OPT_ID = (1<<7),
24 SOF_TIMESTAMPING_TX_SCHED = (1<<8), 24 SOF_TIMESTAMPING_TX_SCHED = (1<<8),
25 SOF_TIMESTAMPING_TX_ACK = (1<<9), 25 SOF_TIMESTAMPING_TX_ACK = (1<<9),
26 SOF_TIMESTAMPING_OPT_CMSG = (1<<10),
26 27
27 SOF_TIMESTAMPING_LAST = SOF_TIMESTAMPING_TX_ACK, 28 SOF_TIMESTAMPING_LAST = SOF_TIMESTAMPING_OPT_CMSG,
28 SOF_TIMESTAMPING_MASK = (SOF_TIMESTAMPING_LAST - 1) | 29 SOF_TIMESTAMPING_MASK = (SOF_TIMESTAMPING_LAST - 1) |
29 SOF_TIMESTAMPING_LAST 30 SOF_TIMESTAMPING_LAST
30}; 31};
diff --git a/net/ipv4/ip_sockglue.c b/net/ipv4/ip_sockglue.c
index b7826575d215..640f26c6a9fe 100644
--- a/net/ipv4/ip_sockglue.c
+++ b/net/ipv4/ip_sockglue.c
@@ -399,6 +399,22 @@ void ip_local_error(struct sock *sk, int err, __be32 daddr, __be16 port, u32 inf
399 kfree_skb(skb); 399 kfree_skb(skb);
400} 400}
401 401
402static 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
402/* 418/*
403 * Handle MSG_ERRQUEUE 419 * Handle MSG_ERRQUEUE
404 */ 420 */
@@ -414,6 +430,8 @@ int ip_recv_error(struct sock *sk, struct msghdr *msg, int len, int *addr_len)
414 int err; 430 int err;
415 int copied; 431 int copied;
416 432
433 WARN_ON_ONCE(sk->sk_family == AF_INET6);
434
417 err = -EAGAIN; 435 err = -EAGAIN;
418 skb = sock_dequeue_err_skb(sk); 436 skb = sock_dequeue_err_skb(sk);
419 if (skb == NULL) 437 if (skb == NULL)
@@ -444,7 +462,9 @@ int ip_recv_error(struct sock *sk, struct msghdr *msg, int len, int *addr_len)
444 memcpy(&errhdr.ee, &serr->ee, sizeof(struct sock_extended_err)); 462 memcpy(&errhdr.ee, &serr->ee, sizeof(struct sock_extended_err));
445 sin = &errhdr.offender; 463 sin = &errhdr.offender;
446 sin->sin_family = AF_UNSPEC; 464 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)) {
448 struct inet_sock *inet = inet_sk(sk); 468 struct inet_sock *inet = inet_sk(sk);
449 469
450 sin->sin_family = AF_INET; 470 sin->sin_family = AF_INET;
@@ -1049,7 +1069,7 @@ e_inval:
1049} 1069}
1050 1070
1051/** 1071/**
1052 * ipv4_pktinfo_prepare - transfert some info from rtable to skb 1072 * ipv4_pktinfo_prepare - transfer some info from rtable to skb
1053 * @sk: socket 1073 * @sk: socket
1054 * @skb: buffer 1074 * @skb: buffer
1055 * 1075 *
diff --git a/net/ipv6/datagram.c b/net/ipv6/datagram.c
index cc1139687fd7..2464a00e36ab 100644
--- a/net/ipv6/datagram.c
+++ b/net/ipv6/datagram.c
@@ -325,6 +325,16 @@ void ipv6_local_rxpmtu(struct sock *sk, struct flowi6 *fl6, u32 mtu)
325 kfree_skb(skb); 325 kfree_skb(skb);
326} 326}
327 327
328static 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
328/* 338/*
329 * Handle MSG_ERRQUEUE 339 * Handle MSG_ERRQUEUE
330 */ 340 */
@@ -388,8 +398,12 @@ int ipv6_recv_error(struct sock *sk, struct msghdr *msg, int len, int *addr_len)
388 sin->sin6_family = AF_INET6; 398 sin->sin6_family = AF_INET6;
389 sin->sin6_flowinfo = 0; 399 sin->sin6_flowinfo = 0;
390 sin->sin6_port = 0; 400 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);
392 ip6_datagram_recv_common_ctl(sk, msg, skb); 405 ip6_datagram_recv_common_ctl(sk, msg, skb);
406 }
393 if (skb->protocol == htons(ETH_P_IPV6)) { 407 if (skb->protocol == htons(ETH_P_IPV6)) {
394 sin->sin6_addr = ipv6_hdr(skb)->saddr; 408 sin->sin6_addr = ipv6_hdr(skb)->saddr;
395 if (np->rxopt.all) 409 if (np->rxopt.all)
@@ -491,7 +505,10 @@ void ip6_datagram_recv_common_ctl(struct sock *sk, struct msghdr *msg,
491 ipv6_addr_set_v4mapped(ip_hdr(skb)->daddr, 505 ipv6_addr_set_v4mapped(ip_hdr(skb)->daddr,
492 &src_info.ipi6_addr); 506 &src_info.ipi6_addr);
493 } 507 }
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);
495 } 512 }
496} 513}
497 514