diff options
author | Willem de Bruijn <willemb@google.com> | 2014-11-30 22:22:35 -0500 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2014-12-08 20:20:48 -0500 |
commit | cbd3aad5ce66f5a266a185aa37e0eb9be9ba4154 (patch) | |
tree | b0adfab040099b369f38ebe9a19df9632f85ded8 | |
parent | 829ae9d611651467fe6cd7be834bd33ca6b28dfe (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.txt | 23 | ||||
-rw-r--r-- | Documentation/networking/timestamping/txtimestamp.c | 90 |
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 | ||
148 | SOF_TIMESTAMPING_OPT_CMSG: | 155 | SOF_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 | ||
64 | struct in6_pktinfo { | ||
65 | struct in6_addr ipi6_addr; | ||
66 | int ipi6_ifindex; | ||
67 | }; | ||
68 | #endif | ||
69 | |||
61 | /* command line parameters */ | 70 | /* command line parameters */ |
62 | static int cfg_proto = SOCK_STREAM; | 71 | static int cfg_proto = SOCK_STREAM; |
63 | static int cfg_ipproto = IPPROTO_TCP; | 72 | static int cfg_ipproto = IPPROTO_TCP; |
@@ -65,6 +74,8 @@ static int cfg_num_pkts = 4; | |||
65 | static int do_ipv4 = 1; | 74 | static int do_ipv4 = 1; |
66 | static int do_ipv6 = 1; | 75 | static int do_ipv6 = 1; |
67 | static int cfg_payload_len = 10; | 76 | static int cfg_payload_len = 10; |
77 | static bool cfg_show_payload; | ||
78 | static bool cfg_do_pktinfo; | ||
68 | static uint16_t dest_port = 9000; | 79 | static uint16_t dest_port = 9000; |
69 | 80 | ||
70 | static struct sockaddr_in daddr; | 81 | static 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 */ | ||
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 | |||
134 | static void __poll(int fd) | 169 | static 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) | |||
215 | static void do_test(int family, unsigned int opt) | 263 | static 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]); |