aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorWillem de Bruijn <willemb@google.com>2014-11-30 22:22:35 -0500
committerDavid S. Miller <davem@davemloft.net>2014-12-08 20:20:48 -0500
commitcbd3aad5ce66f5a266a185aa37e0eb9be9ba4154 (patch)
treeb0adfab040099b369f38ebe9a19df9632f85ded8
parent829ae9d611651467fe6cd7be834bd33ca6b28dfe (diff)
net-timestamp: expand documentation and test
Documentation: expand explanation of timestamp counter Test: new: flag -I requests and prints PKTINFO new: flag -x prints payload (possibly truncated) fix: remove pretty print that breaks common flag '-l 1' Signed-off-by: Willem de Bruijn <willemb@google.com> Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r--Documentation/networking/timestamping.txt23
-rw-r--r--Documentation/networking/timestamping/txtimestamp.c90
2 files changed, 93 insertions, 20 deletions
diff --git a/Documentation/networking/timestamping.txt b/Documentation/networking/timestamping.txt
index b08e27261ff9..a5c784c89312 100644
--- a/Documentation/networking/timestamping.txt
+++ b/Documentation/networking/timestamping.txt
@@ -130,19 +130,26 @@ 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).
143
144 In datagram sockets, the counter increments on each send call. In
145 stream sockets, it increments with every byte.
146 153
147 154
148SOF_TIMESTAMPING_OPT_CMSG: 155SOF_TIMESTAMPING_OPT_CMSG:
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]);