| From 3d2c86e3057995270e08693231039d9d942871f0 Mon Sep 17 00:00:00 2001 |
| From: Shuah Khan <shuahkh@osg.samsung.com> |
| Date: Thu, 15 Sep 2016 08:36:07 -0600 |
| Subject: selftests: Move networking/timestamping from Documentation |
| |
| From: Shuah Khan <shuahkh@osg.samsung.com> |
| |
| commit 3d2c86e3057995270e08693231039d9d942871f0 upstream. |
| |
| Remove networking from Documentation Makefile to move the test to |
| selftests. Update networking/timestamping Makefile to work under |
| selftests. These tests will not be run as part of selftests suite |
| and will not be included in install targets. They can be built and |
| run separately for now. |
| |
| This is part of the effort to move runnable code from Documentation. |
| |
| Acked-by: Jonathan Corbet <corbet@lwn.net> |
| Signed-off-by: Shuah Khan <shuahkh@osg.samsung.com> |
| [ added to 3.18.y stable to remove a build warning - gregkh] |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| |
| --- |
| Documentation/Makefile | 3 |
| Documentation/networking/Makefile | 1 |
| Documentation/networking/timestamping/.gitignore | 3 |
| Documentation/networking/timestamping/Makefile | 14 |
| Documentation/networking/timestamping/hwtstamp_config.c | 134 -- |
| Documentation/networking/timestamping/timestamping.c | 528 ---------- |
| Documentation/networking/timestamping/txtimestamp.c | 469 -------- |
| tools/testing/selftests/networking/timestamping/.gitignore | 3 |
| tools/testing/selftests/networking/timestamping/Makefile | 8 |
| tools/testing/selftests/networking/timestamping/hwtstamp_config.c | 134 ++ |
| tools/testing/selftests/networking/timestamping/timestamping.c | 528 ++++++++++ |
| tools/testing/selftests/networking/timestamping/txtimestamp.c | 469 ++++++++ |
| 12 files changed, 1143 insertions(+), 1151 deletions(-) |
| |
| --- a/Documentation/Makefile |
| +++ b/Documentation/Makefile |
| @@ -1,4 +1,3 @@ |
| subdir-y := accounting auxdisplay blackfin connector \ |
| filesystems filesystems ia64 laptops misc-devices \ |
| - networking pcmcia prctl ptp spi timers vDSO video4linux \ |
| - watchdog |
| + pcmcia prctl ptp spi timers vDSO video4linux watchdog |
| --- a/Documentation/networking/Makefile |
| +++ /dev/null |
| @@ -1 +0,0 @@ |
| -subdir-y := timestamping |
| --- a/Documentation/networking/timestamping/.gitignore |
| +++ /dev/null |
| @@ -1,3 +0,0 @@ |
| -timestamping |
| -txtimestamp |
| -hwtstamp_config |
| --- a/Documentation/networking/timestamping/Makefile |
| +++ /dev/null |
| @@ -1,14 +0,0 @@ |
| -# To compile, from the source root |
| -# |
| -# make headers_install |
| -# make M=documentation |
| - |
| -# List of programs to build |
| -hostprogs-y := hwtstamp_config timestamping txtimestamp |
| - |
| -# Tell kbuild to always build the programs |
| -always := $(hostprogs-y) |
| - |
| -HOSTCFLAGS_timestamping.o += -I$(objtree)/usr/include |
| -HOSTCFLAGS_txtimestamp.o += -I$(objtree)/usr/include |
| -HOSTCFLAGS_hwtstamp_config.o += -I$(objtree)/usr/include |
| --- a/Documentation/networking/timestamping/hwtstamp_config.c |
| +++ /dev/null |
| @@ -1,134 +0,0 @@ |
| -/* Test program for SIOC{G,S}HWTSTAMP |
| - * Copyright 2013 Solarflare Communications |
| - * Author: Ben Hutchings |
| - */ |
| - |
| -#include <errno.h> |
| -#include <stdio.h> |
| -#include <stdlib.h> |
| -#include <string.h> |
| - |
| -#include <sys/socket.h> |
| -#include <sys/ioctl.h> |
| - |
| -#include <linux/if.h> |
| -#include <linux/net_tstamp.h> |
| -#include <linux/sockios.h> |
| - |
| -static int |
| -lookup_value(const char **names, int size, const char *name) |
| -{ |
| - int value; |
| - |
| - for (value = 0; value < size; value++) |
| - if (names[value] && strcasecmp(names[value], name) == 0) |
| - return value; |
| - |
| - return -1; |
| -} |
| - |
| -static const char * |
| -lookup_name(const char **names, int size, int value) |
| -{ |
| - return (value >= 0 && value < size) ? names[value] : NULL; |
| -} |
| - |
| -static void list_names(FILE *f, const char **names, int size) |
| -{ |
| - int value; |
| - |
| - for (value = 0; value < size; value++) |
| - if (names[value]) |
| - fprintf(f, " %s\n", names[value]); |
| -} |
| - |
| -static const char *tx_types[] = { |
| -#define TX_TYPE(name) [HWTSTAMP_TX_ ## name] = #name |
| - TX_TYPE(OFF), |
| - TX_TYPE(ON), |
| - TX_TYPE(ONESTEP_SYNC) |
| -#undef TX_TYPE |
| -}; |
| -#define N_TX_TYPES ((int)(sizeof(tx_types) / sizeof(tx_types[0]))) |
| - |
| -static const char *rx_filters[] = { |
| -#define RX_FILTER(name) [HWTSTAMP_FILTER_ ## name] = #name |
| - RX_FILTER(NONE), |
| - RX_FILTER(ALL), |
| - RX_FILTER(SOME), |
| - RX_FILTER(PTP_V1_L4_EVENT), |
| - RX_FILTER(PTP_V1_L4_SYNC), |
| - RX_FILTER(PTP_V1_L4_DELAY_REQ), |
| - RX_FILTER(PTP_V2_L4_EVENT), |
| - RX_FILTER(PTP_V2_L4_SYNC), |
| - RX_FILTER(PTP_V2_L4_DELAY_REQ), |
| - RX_FILTER(PTP_V2_L2_EVENT), |
| - RX_FILTER(PTP_V2_L2_SYNC), |
| - RX_FILTER(PTP_V2_L2_DELAY_REQ), |
| - RX_FILTER(PTP_V2_EVENT), |
| - RX_FILTER(PTP_V2_SYNC), |
| - RX_FILTER(PTP_V2_DELAY_REQ), |
| -#undef RX_FILTER |
| -}; |
| -#define N_RX_FILTERS ((int)(sizeof(rx_filters) / sizeof(rx_filters[0]))) |
| - |
| -static void usage(void) |
| -{ |
| - fputs("Usage: hwtstamp_config if_name [tx_type rx_filter]\n" |
| - "tx_type is any of (case-insensitive):\n", |
| - stderr); |
| - list_names(stderr, tx_types, N_TX_TYPES); |
| - fputs("rx_filter is any of (case-insensitive):\n", stderr); |
| - list_names(stderr, rx_filters, N_RX_FILTERS); |
| -} |
| - |
| -int main(int argc, char **argv) |
| -{ |
| - struct ifreq ifr; |
| - struct hwtstamp_config config; |
| - const char *name; |
| - int sock; |
| - |
| - if ((argc != 2 && argc != 4) || (strlen(argv[1]) >= IFNAMSIZ)) { |
| - usage(); |
| - return 2; |
| - } |
| - |
| - if (argc == 4) { |
| - config.flags = 0; |
| - config.tx_type = lookup_value(tx_types, N_TX_TYPES, argv[2]); |
| - config.rx_filter = lookup_value(rx_filters, N_RX_FILTERS, argv[3]); |
| - if (config.tx_type < 0 || config.rx_filter < 0) { |
| - usage(); |
| - return 2; |
| - } |
| - } |
| - |
| - sock = socket(AF_INET, SOCK_DGRAM, 0); |
| - if (sock < 0) { |
| - perror("socket"); |
| - return 1; |
| - } |
| - |
| - strcpy(ifr.ifr_name, argv[1]); |
| - ifr.ifr_data = (caddr_t)&config; |
| - |
| - if (ioctl(sock, (argc == 2) ? SIOCGHWTSTAMP : SIOCSHWTSTAMP, &ifr)) { |
| - perror("ioctl"); |
| - return 1; |
| - } |
| - |
| - printf("flags = %#x\n", config.flags); |
| - name = lookup_name(tx_types, N_TX_TYPES, config.tx_type); |
| - if (name) |
| - printf("tx_type = %s\n", name); |
| - else |
| - printf("tx_type = %d\n", config.tx_type); |
| - name = lookup_name(rx_filters, N_RX_FILTERS, config.rx_filter); |
| - if (name) |
| - printf("rx_filter = %s\n", name); |
| - else |
| - printf("rx_filter = %d\n", config.rx_filter); |
| - |
| - return 0; |
| -} |
| --- a/Documentation/networking/timestamping/timestamping.c |
| +++ /dev/null |
| @@ -1,528 +0,0 @@ |
| -/* |
| - * This program demonstrates how the various time stamping features in |
| - * the Linux kernel work. It emulates the behavior of a PTP |
| - * implementation in stand-alone master mode by sending PTPv1 Sync |
| - * multicasts once every second. It looks for similar packets, but |
| - * beyond that doesn't actually implement PTP. |
| - * |
| - * Outgoing packets are time stamped with SO_TIMESTAMPING with or |
| - * without hardware support. |
| - * |
| - * Incoming packets are time stamped with SO_TIMESTAMPING with or |
| - * without hardware support, SIOCGSTAMP[NS] (per-socket time stamp) and |
| - * SO_TIMESTAMP[NS]. |
| - * |
| - * Copyright (C) 2009 Intel Corporation. |
| - * Author: Patrick Ohly <patrick.ohly@intel.com> |
| - * |
| - * This program is free software; you can redistribute it and/or modify it |
| - * under the terms and conditions of the GNU General Public License, |
| - * version 2, as published by the Free Software Foundation. |
| - * |
| - * This program is distributed in the hope it will be useful, but WITHOUT |
| - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
| - * FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for |
| - * more details. |
| - * |
| - * You should have received a copy of the GNU General Public License along with |
| - * this program; if not, write to the Free Software Foundation, Inc., |
| - * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. |
| - */ |
| - |
| -#include <stdio.h> |
| -#include <stdlib.h> |
| -#include <errno.h> |
| -#include <string.h> |
| - |
| -#include <sys/time.h> |
| -#include <sys/socket.h> |
| -#include <sys/select.h> |
| -#include <sys/ioctl.h> |
| -#include <arpa/inet.h> |
| -#include <net/if.h> |
| - |
| -#include <asm/types.h> |
| -#include <linux/net_tstamp.h> |
| -#include <linux/errqueue.h> |
| - |
| -#ifndef SO_TIMESTAMPING |
| -# define SO_TIMESTAMPING 37 |
| -# define SCM_TIMESTAMPING SO_TIMESTAMPING |
| -#endif |
| - |
| -#ifndef SO_TIMESTAMPNS |
| -# define SO_TIMESTAMPNS 35 |
| -#endif |
| - |
| -#ifndef SIOCGSTAMPNS |
| -# define SIOCGSTAMPNS 0x8907 |
| -#endif |
| - |
| -#ifndef SIOCSHWTSTAMP |
| -# define SIOCSHWTSTAMP 0x89b0 |
| -#endif |
| - |
| -static void usage(const char *error) |
| -{ |
| - if (error) |
| - printf("invalid option: %s\n", error); |
| - printf("timestamping interface option*\n\n" |
| - "Options:\n" |
| - " IP_MULTICAST_LOOP - looping outgoing multicasts\n" |
| - " SO_TIMESTAMP - normal software time stamping, ms resolution\n" |
| - " SO_TIMESTAMPNS - more accurate software time stamping\n" |
| - " SOF_TIMESTAMPING_TX_HARDWARE - hardware time stamping of outgoing packets\n" |
| - " SOF_TIMESTAMPING_TX_SOFTWARE - software fallback for outgoing packets\n" |
| - " SOF_TIMESTAMPING_RX_HARDWARE - hardware time stamping of incoming packets\n" |
| - " SOF_TIMESTAMPING_RX_SOFTWARE - software fallback for incoming packets\n" |
| - " SOF_TIMESTAMPING_SOFTWARE - request reporting of software time stamps\n" |
| - " SOF_TIMESTAMPING_RAW_HARDWARE - request reporting of raw HW time stamps\n" |
| - " SIOCGSTAMP - check last socket time stamp\n" |
| - " SIOCGSTAMPNS - more accurate socket time stamp\n"); |
| - exit(1); |
| -} |
| - |
| -static void bail(const char *error) |
| -{ |
| - printf("%s: %s\n", error, strerror(errno)); |
| - exit(1); |
| -} |
| - |
| -static const unsigned char sync[] = { |
| - 0x00, 0x01, 0x00, 0x01, |
| - 0x5f, 0x44, 0x46, 0x4c, |
| - 0x54, 0x00, 0x00, 0x00, |
| - 0x00, 0x00, 0x00, 0x00, |
| - 0x00, 0x00, 0x00, 0x00, |
| - 0x01, 0x01, |
| - |
| - /* fake uuid */ |
| - 0x00, 0x01, |
| - 0x02, 0x03, 0x04, 0x05, |
| - |
| - 0x00, 0x01, 0x00, 0x37, |
| - 0x00, 0x00, 0x00, 0x08, |
| - 0x00, 0x00, 0x00, 0x00, |
| - 0x49, 0x05, 0xcd, 0x01, |
| - 0x29, 0xb1, 0x8d, 0xb0, |
| - 0x00, 0x00, 0x00, 0x00, |
| - 0x00, 0x01, |
| - |
| - /* fake uuid */ |
| - 0x00, 0x01, |
| - 0x02, 0x03, 0x04, 0x05, |
| - |
| - 0x00, 0x00, 0x00, 0x37, |
| - 0x00, 0x00, 0x00, 0x04, |
| - 0x44, 0x46, 0x4c, 0x54, |
| - 0x00, 0x00, 0xf0, 0x60, |
| - 0x00, 0x01, 0x00, 0x00, |
| - 0x00, 0x00, 0x00, 0x01, |
| - 0x00, 0x00, 0xf0, 0x60, |
| - 0x00, 0x00, 0x00, 0x00, |
| - 0x00, 0x00, 0x00, 0x04, |
| - 0x44, 0x46, 0x4c, 0x54, |
| - 0x00, 0x01, |
| - |
| - /* fake uuid */ |
| - 0x00, 0x01, |
| - 0x02, 0x03, 0x04, 0x05, |
| - |
| - 0x00, 0x00, 0x00, 0x00, |
| - 0x00, 0x00, 0x00, 0x00, |
| - 0x00, 0x00, 0x00, 0x00, |
| - 0x00, 0x00, 0x00, 0x00 |
| -}; |
| - |
| -static void sendpacket(int sock, struct sockaddr *addr, socklen_t addr_len) |
| -{ |
| - struct timeval now; |
| - int res; |
| - |
| - res = sendto(sock, sync, sizeof(sync), 0, |
| - addr, addr_len); |
| - gettimeofday(&now, 0); |
| - if (res < 0) |
| - printf("%s: %s\n", "send", strerror(errno)); |
| - else |
| - printf("%ld.%06ld: sent %d bytes\n", |
| - (long)now.tv_sec, (long)now.tv_usec, |
| - res); |
| -} |
| - |
| -static void printpacket(struct msghdr *msg, int res, |
| - char *data, |
| - int sock, int recvmsg_flags, |
| - int siocgstamp, int siocgstampns) |
| -{ |
| - struct sockaddr_in *from_addr = (struct sockaddr_in *)msg->msg_name; |
| - struct cmsghdr *cmsg; |
| - struct timeval tv; |
| - struct timespec ts; |
| - struct timeval now; |
| - |
| - gettimeofday(&now, 0); |
| - |
| - printf("%ld.%06ld: received %s data, %d bytes from %s, %zu bytes control messages\n", |
| - (long)now.tv_sec, (long)now.tv_usec, |
| - (recvmsg_flags & MSG_ERRQUEUE) ? "error" : "regular", |
| - res, |
| - inet_ntoa(from_addr->sin_addr), |
| - msg->msg_controllen); |
| - for (cmsg = CMSG_FIRSTHDR(msg); |
| - cmsg; |
| - cmsg = CMSG_NXTHDR(msg, cmsg)) { |
| - printf(" cmsg len %zu: ", cmsg->cmsg_len); |
| - switch (cmsg->cmsg_level) { |
| - case SOL_SOCKET: |
| - printf("SOL_SOCKET "); |
| - switch (cmsg->cmsg_type) { |
| - case SO_TIMESTAMP: { |
| - struct timeval *stamp = |
| - (struct timeval *)CMSG_DATA(cmsg); |
| - printf("SO_TIMESTAMP %ld.%06ld", |
| - (long)stamp->tv_sec, |
| - (long)stamp->tv_usec); |
| - break; |
| - } |
| - case SO_TIMESTAMPNS: { |
| - struct timespec *stamp = |
| - (struct timespec *)CMSG_DATA(cmsg); |
| - printf("SO_TIMESTAMPNS %ld.%09ld", |
| - (long)stamp->tv_sec, |
| - (long)stamp->tv_nsec); |
| - break; |
| - } |
| - case SO_TIMESTAMPING: { |
| - struct timespec *stamp = |
| - (struct timespec *)CMSG_DATA(cmsg); |
| - printf("SO_TIMESTAMPING "); |
| - printf("SW %ld.%09ld ", |
| - (long)stamp->tv_sec, |
| - (long)stamp->tv_nsec); |
| - stamp++; |
| - /* skip deprecated HW transformed */ |
| - stamp++; |
| - printf("HW raw %ld.%09ld", |
| - (long)stamp->tv_sec, |
| - (long)stamp->tv_nsec); |
| - break; |
| - } |
| - default: |
| - printf("type %d", cmsg->cmsg_type); |
| - break; |
| - } |
| - break; |
| - case IPPROTO_IP: |
| - printf("IPPROTO_IP "); |
| - switch (cmsg->cmsg_type) { |
| - case IP_RECVERR: { |
| - struct sock_extended_err *err = |
| - (struct sock_extended_err *)CMSG_DATA(cmsg); |
| - printf("IP_RECVERR ee_errno '%s' ee_origin %d => %s", |
| - strerror(err->ee_errno), |
| - err->ee_origin, |
| -#ifdef SO_EE_ORIGIN_TIMESTAMPING |
| - err->ee_origin == SO_EE_ORIGIN_TIMESTAMPING ? |
| - "bounced packet" : "unexpected origin" |
| -#else |
| - "probably SO_EE_ORIGIN_TIMESTAMPING" |
| -#endif |
| - ); |
| - if (res < sizeof(sync)) |
| - printf(" => truncated data?!"); |
| - else if (!memcmp(sync, data + res - sizeof(sync), |
| - sizeof(sync))) |
| - printf(" => GOT OUR DATA BACK (HURRAY!)"); |
| - break; |
| - } |
| - case IP_PKTINFO: { |
| - struct in_pktinfo *pktinfo = |
| - (struct in_pktinfo *)CMSG_DATA(cmsg); |
| - printf("IP_PKTINFO interface index %u", |
| - pktinfo->ipi_ifindex); |
| - break; |
| - } |
| - default: |
| - printf("type %d", cmsg->cmsg_type); |
| - break; |
| - } |
| - break; |
| - default: |
| - printf("level %d type %d", |
| - cmsg->cmsg_level, |
| - cmsg->cmsg_type); |
| - break; |
| - } |
| - printf("\n"); |
| - } |
| - |
| - if (siocgstamp) { |
| - if (ioctl(sock, SIOCGSTAMP, &tv)) |
| - printf(" %s: %s\n", "SIOCGSTAMP", strerror(errno)); |
| - else |
| - printf("SIOCGSTAMP %ld.%06ld\n", |
| - (long)tv.tv_sec, |
| - (long)tv.tv_usec); |
| - } |
| - if (siocgstampns) { |
| - if (ioctl(sock, SIOCGSTAMPNS, &ts)) |
| - printf(" %s: %s\n", "SIOCGSTAMPNS", strerror(errno)); |
| - else |
| - printf("SIOCGSTAMPNS %ld.%09ld\n", |
| - (long)ts.tv_sec, |
| - (long)ts.tv_nsec); |
| - } |
| -} |
| - |
| -static void recvpacket(int sock, int recvmsg_flags, |
| - int siocgstamp, int siocgstampns) |
| -{ |
| - char data[256]; |
| - struct msghdr msg; |
| - struct iovec entry; |
| - struct sockaddr_in from_addr; |
| - struct { |
| - struct cmsghdr cm; |
| - char control[512]; |
| - } control; |
| - int res; |
| - |
| - memset(&msg, 0, sizeof(msg)); |
| - msg.msg_iov = &entry; |
| - msg.msg_iovlen = 1; |
| - entry.iov_base = data; |
| - entry.iov_len = sizeof(data); |
| - msg.msg_name = (caddr_t)&from_addr; |
| - msg.msg_namelen = sizeof(from_addr); |
| - msg.msg_control = &control; |
| - msg.msg_controllen = sizeof(control); |
| - |
| - res = recvmsg(sock, &msg, recvmsg_flags|MSG_DONTWAIT); |
| - if (res < 0) { |
| - printf("%s %s: %s\n", |
| - "recvmsg", |
| - (recvmsg_flags & MSG_ERRQUEUE) ? "error" : "regular", |
| - strerror(errno)); |
| - } else { |
| - printpacket(&msg, res, data, |
| - sock, recvmsg_flags, |
| - siocgstamp, siocgstampns); |
| - } |
| -} |
| - |
| -int main(int argc, char **argv) |
| -{ |
| - int so_timestamping_flags = 0; |
| - int so_timestamp = 0; |
| - int so_timestampns = 0; |
| - int siocgstamp = 0; |
| - int siocgstampns = 0; |
| - int ip_multicast_loop = 0; |
| - char *interface; |
| - int i; |
| - int enabled = 1; |
| - int sock; |
| - struct ifreq device; |
| - struct ifreq hwtstamp; |
| - struct hwtstamp_config hwconfig, hwconfig_requested; |
| - struct sockaddr_in addr; |
| - struct ip_mreq imr; |
| - struct in_addr iaddr; |
| - int val; |
| - socklen_t len; |
| - struct timeval next; |
| - |
| - if (argc < 2) |
| - usage(0); |
| - interface = argv[1]; |
| - |
| - for (i = 2; i < argc; i++) { |
| - if (!strcasecmp(argv[i], "SO_TIMESTAMP")) |
| - so_timestamp = 1; |
| - else if (!strcasecmp(argv[i], "SO_TIMESTAMPNS")) |
| - so_timestampns = 1; |
| - else if (!strcasecmp(argv[i], "SIOCGSTAMP")) |
| - siocgstamp = 1; |
| - else if (!strcasecmp(argv[i], "SIOCGSTAMPNS")) |
| - siocgstampns = 1; |
| - else if (!strcasecmp(argv[i], "IP_MULTICAST_LOOP")) |
| - ip_multicast_loop = 1; |
| - else if (!strcasecmp(argv[i], "SOF_TIMESTAMPING_TX_HARDWARE")) |
| - so_timestamping_flags |= SOF_TIMESTAMPING_TX_HARDWARE; |
| - else if (!strcasecmp(argv[i], "SOF_TIMESTAMPING_TX_SOFTWARE")) |
| - so_timestamping_flags |= SOF_TIMESTAMPING_TX_SOFTWARE; |
| - else if (!strcasecmp(argv[i], "SOF_TIMESTAMPING_RX_HARDWARE")) |
| - so_timestamping_flags |= SOF_TIMESTAMPING_RX_HARDWARE; |
| - else if (!strcasecmp(argv[i], "SOF_TIMESTAMPING_RX_SOFTWARE")) |
| - so_timestamping_flags |= SOF_TIMESTAMPING_RX_SOFTWARE; |
| - else if (!strcasecmp(argv[i], "SOF_TIMESTAMPING_SOFTWARE")) |
| - so_timestamping_flags |= SOF_TIMESTAMPING_SOFTWARE; |
| - else if (!strcasecmp(argv[i], "SOF_TIMESTAMPING_RAW_HARDWARE")) |
| - so_timestamping_flags |= SOF_TIMESTAMPING_RAW_HARDWARE; |
| - else |
| - usage(argv[i]); |
| - } |
| - |
| - sock = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP); |
| - if (sock < 0) |
| - bail("socket"); |
| - |
| - memset(&device, 0, sizeof(device)); |
| - strncpy(device.ifr_name, interface, sizeof(device.ifr_name)); |
| - if (ioctl(sock, SIOCGIFADDR, &device) < 0) |
| - bail("getting interface IP address"); |
| - |
| - memset(&hwtstamp, 0, sizeof(hwtstamp)); |
| - strncpy(hwtstamp.ifr_name, interface, sizeof(hwtstamp.ifr_name)); |
| - hwtstamp.ifr_data = (void *)&hwconfig; |
| - memset(&hwconfig, 0, sizeof(hwconfig)); |
| - hwconfig.tx_type = |
| - (so_timestamping_flags & SOF_TIMESTAMPING_TX_HARDWARE) ? |
| - HWTSTAMP_TX_ON : HWTSTAMP_TX_OFF; |
| - hwconfig.rx_filter = |
| - (so_timestamping_flags & SOF_TIMESTAMPING_RX_HARDWARE) ? |
| - HWTSTAMP_FILTER_PTP_V1_L4_SYNC : HWTSTAMP_FILTER_NONE; |
| - hwconfig_requested = hwconfig; |
| - if (ioctl(sock, SIOCSHWTSTAMP, &hwtstamp) < 0) { |
| - if ((errno == EINVAL || errno == ENOTSUP) && |
| - hwconfig_requested.tx_type == HWTSTAMP_TX_OFF && |
| - hwconfig_requested.rx_filter == HWTSTAMP_FILTER_NONE) |
| - printf("SIOCSHWTSTAMP: disabling hardware time stamping not possible\n"); |
| - else |
| - bail("SIOCSHWTSTAMP"); |
| - } |
| - printf("SIOCSHWTSTAMP: tx_type %d requested, got %d; rx_filter %d requested, got %d\n", |
| - hwconfig_requested.tx_type, hwconfig.tx_type, |
| - hwconfig_requested.rx_filter, hwconfig.rx_filter); |
| - |
| - /* bind to PTP port */ |
| - addr.sin_family = AF_INET; |
| - addr.sin_addr.s_addr = htonl(INADDR_ANY); |
| - addr.sin_port = htons(319 /* PTP event port */); |
| - if (bind(sock, |
| - (struct sockaddr *)&addr, |
| - sizeof(struct sockaddr_in)) < 0) |
| - bail("bind"); |
| - |
| - /* set multicast group for outgoing packets */ |
| - inet_aton("224.0.1.130", &iaddr); /* alternate PTP domain 1 */ |
| - addr.sin_addr = iaddr; |
| - imr.imr_multiaddr.s_addr = iaddr.s_addr; |
| - imr.imr_interface.s_addr = |
| - ((struct sockaddr_in *)&device.ifr_addr)->sin_addr.s_addr; |
| - if (setsockopt(sock, IPPROTO_IP, IP_MULTICAST_IF, |
| - &imr.imr_interface.s_addr, sizeof(struct in_addr)) < 0) |
| - bail("set multicast"); |
| - |
| - /* join multicast group, loop our own packet */ |
| - if (setsockopt(sock, IPPROTO_IP, IP_ADD_MEMBERSHIP, |
| - &imr, sizeof(struct ip_mreq)) < 0) |
| - bail("join multicast group"); |
| - |
| - if (setsockopt(sock, IPPROTO_IP, IP_MULTICAST_LOOP, |
| - &ip_multicast_loop, sizeof(enabled)) < 0) { |
| - bail("loop multicast"); |
| - } |
| - |
| - /* set socket options for time stamping */ |
| - if (so_timestamp && |
| - setsockopt(sock, SOL_SOCKET, SO_TIMESTAMP, |
| - &enabled, sizeof(enabled)) < 0) |
| - bail("setsockopt SO_TIMESTAMP"); |
| - |
| - if (so_timestampns && |
| - setsockopt(sock, SOL_SOCKET, SO_TIMESTAMPNS, |
| - &enabled, sizeof(enabled)) < 0) |
| - bail("setsockopt SO_TIMESTAMPNS"); |
| - |
| - if (so_timestamping_flags && |
| - setsockopt(sock, SOL_SOCKET, SO_TIMESTAMPING, |
| - &so_timestamping_flags, |
| - sizeof(so_timestamping_flags)) < 0) |
| - bail("setsockopt SO_TIMESTAMPING"); |
| - |
| - /* request IP_PKTINFO for debugging purposes */ |
| - if (setsockopt(sock, SOL_IP, IP_PKTINFO, |
| - &enabled, sizeof(enabled)) < 0) |
| - printf("%s: %s\n", "setsockopt IP_PKTINFO", strerror(errno)); |
| - |
| - /* verify socket options */ |
| - len = sizeof(val); |
| - if (getsockopt(sock, SOL_SOCKET, SO_TIMESTAMP, &val, &len) < 0) |
| - printf("%s: %s\n", "getsockopt SO_TIMESTAMP", strerror(errno)); |
| - else |
| - printf("SO_TIMESTAMP %d\n", val); |
| - |
| - if (getsockopt(sock, SOL_SOCKET, SO_TIMESTAMPNS, &val, &len) < 0) |
| - printf("%s: %s\n", "getsockopt SO_TIMESTAMPNS", |
| - strerror(errno)); |
| - else |
| - printf("SO_TIMESTAMPNS %d\n", val); |
| - |
| - if (getsockopt(sock, SOL_SOCKET, SO_TIMESTAMPING, &val, &len) < 0) { |
| - printf("%s: %s\n", "getsockopt SO_TIMESTAMPING", |
| - strerror(errno)); |
| - } else { |
| - printf("SO_TIMESTAMPING %d\n", val); |
| - if (val != so_timestamping_flags) |
| - printf(" not the expected value %d\n", |
| - so_timestamping_flags); |
| - } |
| - |
| - /* send packets forever every five seconds */ |
| - gettimeofday(&next, 0); |
| - next.tv_sec = (next.tv_sec + 1) / 5 * 5; |
| - next.tv_usec = 0; |
| - while (1) { |
| - struct timeval now; |
| - struct timeval delta; |
| - long delta_us; |
| - int res; |
| - fd_set readfs, errorfs; |
| - |
| - gettimeofday(&now, 0); |
| - delta_us = (long)(next.tv_sec - now.tv_sec) * 1000000 + |
| - (long)(next.tv_usec - now.tv_usec); |
| - if (delta_us > 0) { |
| - /* continue waiting for timeout or data */ |
| - delta.tv_sec = delta_us / 1000000; |
| - delta.tv_usec = delta_us % 1000000; |
| - |
| - FD_ZERO(&readfs); |
| - FD_ZERO(&errorfs); |
| - FD_SET(sock, &readfs); |
| - FD_SET(sock, &errorfs); |
| - printf("%ld.%06ld: select %ldus\n", |
| - (long)now.tv_sec, (long)now.tv_usec, |
| - delta_us); |
| - res = select(sock + 1, &readfs, 0, &errorfs, &delta); |
| - gettimeofday(&now, 0); |
| - printf("%ld.%06ld: select returned: %d, %s\n", |
| - (long)now.tv_sec, (long)now.tv_usec, |
| - res, |
| - res < 0 ? strerror(errno) : "success"); |
| - if (res > 0) { |
| - if (FD_ISSET(sock, &readfs)) |
| - printf("ready for reading\n"); |
| - if (FD_ISSET(sock, &errorfs)) |
| - printf("has error\n"); |
| - recvpacket(sock, 0, |
| - siocgstamp, |
| - siocgstampns); |
| - recvpacket(sock, MSG_ERRQUEUE, |
| - siocgstamp, |
| - siocgstampns); |
| - } |
| - } else { |
| - /* write one packet */ |
| - sendpacket(sock, |
| - (struct sockaddr *)&addr, |
| - sizeof(addr)); |
| - next.tv_sec += 5; |
| - continue; |
| - } |
| - } |
| - |
| - return 0; |
| -} |
| --- a/Documentation/networking/timestamping/txtimestamp.c |
| +++ /dev/null |
| @@ -1,469 +0,0 @@ |
| -/* |
| - * Copyright 2014 Google Inc. |
| - * Author: willemb@google.com (Willem de Bruijn) |
| - * |
| - * Test software tx timestamping, including |
| - * |
| - * - SCHED, SND and ACK timestamps |
| - * - RAW, UDP and TCP |
| - * - IPv4 and IPv6 |
| - * - various packet sizes (to test GSO and TSO) |
| - * |
| - * Consult the command line arguments for help on running |
| - * the various testcases. |
| - * |
| - * This test requires a dummy TCP server. |
| - * A simple `nc6 [-u] -l -p $DESTPORT` will do |
| - * |
| - * |
| - * This program is free software; you can redistribute it and/or modify it |
| - * under the terms and conditions of the GNU General Public License, |
| - * version 2, as published by the Free Software Foundation. |
| - * |
| - * This program is distributed in the hope it will be useful, but WITHOUT |
| - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
| - * FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for |
| - * more details. |
| - * |
| - * You should have received a copy of the GNU General Public License along with |
| - * this program; if not, write to the Free Software Foundation, Inc., |
| - * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. |
| - */ |
| - |
| -#include <arpa/inet.h> |
| -#include <asm/types.h> |
| -#include <error.h> |
| -#include <errno.h> |
| -#include <linux/errqueue.h> |
| -#include <linux/if_ether.h> |
| -#include <linux/net_tstamp.h> |
| -#include <netdb.h> |
| -#include <net/if.h> |
| -#include <netinet/in.h> |
| -#include <netinet/ip.h> |
| -#include <netinet/udp.h> |
| -#include <netinet/tcp.h> |
| -#include <netpacket/packet.h> |
| -#include <poll.h> |
| -#include <stdarg.h> |
| -#include <stdint.h> |
| -#include <stdio.h> |
| -#include <stdlib.h> |
| -#include <string.h> |
| -#include <sys/ioctl.h> |
| -#include <sys/select.h> |
| -#include <sys/socket.h> |
| -#include <sys/time.h> |
| -#include <sys/types.h> |
| -#include <time.h> |
| -#include <unistd.h> |
| - |
| -/* command line parameters */ |
| -static int cfg_proto = SOCK_STREAM; |
| -static int cfg_ipproto = IPPROTO_TCP; |
| -static int cfg_num_pkts = 4; |
| -static int do_ipv4 = 1; |
| -static int do_ipv6 = 1; |
| -static int cfg_payload_len = 10; |
| -static uint16_t dest_port = 9000; |
| - |
| -static struct sockaddr_in daddr; |
| -static struct sockaddr_in6 daddr6; |
| -static struct timespec ts_prev; |
| - |
| -static void __print_timestamp(const char *name, struct timespec *cur, |
| - uint32_t key, int payload_len) |
| -{ |
| - if (!(cur->tv_sec | cur->tv_nsec)) |
| - return; |
| - |
| - fprintf(stderr, " %s: %lu s %lu us (seq=%u, len=%u)", |
| - name, cur->tv_sec, cur->tv_nsec / 1000, |
| - key, payload_len); |
| - |
| - if ((ts_prev.tv_sec | ts_prev.tv_nsec)) { |
| - int64_t cur_ms, prev_ms; |
| - |
| - cur_ms = (long) cur->tv_sec * 1000 * 1000; |
| - cur_ms += cur->tv_nsec / 1000; |
| - |
| - prev_ms = (long) ts_prev.tv_sec * 1000 * 1000; |
| - prev_ms += ts_prev.tv_nsec / 1000; |
| - |
| - fprintf(stderr, " (%+ld us)", cur_ms - prev_ms); |
| - } |
| - |
| - ts_prev = *cur; |
| - fprintf(stderr, "\n"); |
| -} |
| - |
| -static void print_timestamp_usr(void) |
| -{ |
| - struct timespec ts; |
| - struct timeval tv; /* avoid dependency on -lrt */ |
| - |
| - gettimeofday(&tv, NULL); |
| - ts.tv_sec = tv.tv_sec; |
| - ts.tv_nsec = tv.tv_usec * 1000; |
| - |
| - __print_timestamp(" USR", &ts, 0, 0); |
| -} |
| - |
| -static void print_timestamp(struct scm_timestamping *tss, int tstype, |
| - int tskey, int payload_len) |
| -{ |
| - const char *tsname; |
| - |
| - switch (tstype) { |
| - case SCM_TSTAMP_SCHED: |
| - tsname = " ENQ"; |
| - break; |
| - case SCM_TSTAMP_SND: |
| - tsname = " SND"; |
| - break; |
| - case SCM_TSTAMP_ACK: |
| - tsname = " ACK"; |
| - break; |
| - default: |
| - error(1, 0, "unknown timestamp type: %u", |
| - tstype); |
| - } |
| - __print_timestamp(tsname, &tss->ts[0], tskey, payload_len); |
| -} |
| - |
| -static void __poll(int fd) |
| -{ |
| - struct pollfd pollfd; |
| - int ret; |
| - |
| - memset(&pollfd, 0, sizeof(pollfd)); |
| - pollfd.fd = fd; |
| - ret = poll(&pollfd, 1, 100); |
| - if (ret != 1) |
| - error(1, errno, "poll"); |
| -} |
| - |
| -static void __recv_errmsg_cmsg(struct msghdr *msg, int payload_len) |
| -{ |
| - struct sock_extended_err *serr = NULL; |
| - struct scm_timestamping *tss = NULL; |
| - struct cmsghdr *cm; |
| - |
| - for (cm = CMSG_FIRSTHDR(msg); |
| - cm && cm->cmsg_len; |
| - cm = CMSG_NXTHDR(msg, cm)) { |
| - if (cm->cmsg_level == SOL_SOCKET && |
| - cm->cmsg_type == SCM_TIMESTAMPING) { |
| - tss = (void *) CMSG_DATA(cm); |
| - } else if ((cm->cmsg_level == SOL_IP && |
| - cm->cmsg_type == IP_RECVERR) || |
| - (cm->cmsg_level == SOL_IPV6 && |
| - cm->cmsg_type == IPV6_RECVERR)) { |
| - |
| - serr = (void *) CMSG_DATA(cm); |
| - if (serr->ee_errno != ENOMSG || |
| - serr->ee_origin != SO_EE_ORIGIN_TIMESTAMPING) { |
| - fprintf(stderr, "unknown ip error %d %d\n", |
| - serr->ee_errno, |
| - serr->ee_origin); |
| - serr = NULL; |
| - } |
| - } else |
| - fprintf(stderr, "unknown cmsg %d,%d\n", |
| - cm->cmsg_level, cm->cmsg_type); |
| - } |
| - |
| - if (serr && tss) |
| - print_timestamp(tss, serr->ee_info, serr->ee_data, payload_len); |
| -} |
| - |
| -static int recv_errmsg(int fd) |
| -{ |
| - static char ctrl[1024 /* overprovision*/]; |
| - static struct msghdr msg; |
| - struct iovec entry; |
| - static char *data; |
| - int ret = 0; |
| - |
| - data = malloc(cfg_payload_len); |
| - if (!data) |
| - error(1, 0, "malloc"); |
| - |
| - memset(&msg, 0, sizeof(msg)); |
| - memset(&entry, 0, sizeof(entry)); |
| - memset(ctrl, 0, sizeof(ctrl)); |
| - |
| - entry.iov_base = data; |
| - entry.iov_len = cfg_payload_len; |
| - msg.msg_iov = &entry; |
| - msg.msg_iovlen = 1; |
| - msg.msg_name = NULL; |
| - msg.msg_namelen = 0; |
| - msg.msg_control = ctrl; |
| - msg.msg_controllen = sizeof(ctrl); |
| - |
| - ret = recvmsg(fd, &msg, MSG_ERRQUEUE); |
| - if (ret == -1 && errno != EAGAIN) |
| - error(1, errno, "recvmsg"); |
| - |
| - __recv_errmsg_cmsg(&msg, ret); |
| - |
| - free(data); |
| - return ret == -1; |
| -} |
| - |
| -static void do_test(int family, unsigned int opt) |
| -{ |
| - char *buf; |
| - int fd, i, val, total_len; |
| - |
| - if (family == IPPROTO_IPV6 && cfg_proto != SOCK_STREAM) { |
| - /* due to lack of checksum generation code */ |
| - fprintf(stderr, "test: skipping datagram over IPv6\n"); |
| - return; |
| - } |
| - |
| - total_len = cfg_payload_len; |
| - if (cfg_proto == SOCK_RAW) { |
| - total_len += sizeof(struct udphdr); |
| - if (cfg_ipproto == IPPROTO_RAW) |
| - total_len += sizeof(struct iphdr); |
| - } |
| - |
| - buf = malloc(total_len); |
| - if (!buf) |
| - error(1, 0, "malloc"); |
| - |
| - fd = socket(family, cfg_proto, cfg_ipproto); |
| - if (fd < 0) |
| - error(1, errno, "socket"); |
| - |
| - if (cfg_proto == SOCK_STREAM) { |
| - val = 1; |
| - if (setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, |
| - (char*) &val, sizeof(val))) |
| - error(1, 0, "setsockopt no nagle"); |
| - |
| - if (family == PF_INET) { |
| - if (connect(fd, (void *) &daddr, sizeof(daddr))) |
| - error(1, errno, "connect ipv4"); |
| - } else { |
| - if (connect(fd, (void *) &daddr6, sizeof(daddr6))) |
| - error(1, errno, "connect ipv6"); |
| - } |
| - } |
| - |
| - opt |= SOF_TIMESTAMPING_SOFTWARE | |
| - SOF_TIMESTAMPING_OPT_ID; |
| - if (setsockopt(fd, SOL_SOCKET, SO_TIMESTAMPING, |
| - (char *) &opt, sizeof(opt))) |
| - error(1, 0, "setsockopt timestamping"); |
| - |
| - for (i = 0; i < cfg_num_pkts; i++) { |
| - memset(&ts_prev, 0, sizeof(ts_prev)); |
| - memset(buf, 'a' + i, total_len); |
| - buf[total_len - 2] = '\n'; |
| - buf[total_len - 1] = '\0'; |
| - |
| - if (cfg_proto == SOCK_RAW) { |
| - struct udphdr *udph; |
| - int off = 0; |
| - |
| - if (cfg_ipproto == IPPROTO_RAW) { |
| - struct iphdr *iph = (void *) buf; |
| - |
| - memset(iph, 0, sizeof(*iph)); |
| - iph->ihl = 5; |
| - iph->version = 4; |
| - iph->ttl = 2; |
| - iph->daddr = daddr.sin_addr.s_addr; |
| - iph->protocol = IPPROTO_UDP; |
| - /* kernel writes saddr, csum, len */ |
| - |
| - off = sizeof(*iph); |
| - } |
| - |
| - udph = (void *) buf + off; |
| - udph->source = ntohs(9000); /* random spoof */ |
| - udph->dest = ntohs(dest_port); |
| - udph->len = ntohs(sizeof(*udph) + cfg_payload_len); |
| - udph->check = 0; /* not allowed for IPv6 */ |
| - } |
| - |
| - print_timestamp_usr(); |
| - if (cfg_proto != SOCK_STREAM) { |
| - if (family == PF_INET) |
| - val = sendto(fd, buf, total_len, 0, (void *) &daddr, sizeof(daddr)); |
| - else |
| - val = sendto(fd, buf, total_len, 0, (void *) &daddr6, sizeof(daddr6)); |
| - } else { |
| - val = send(fd, buf, cfg_payload_len, 0); |
| - } |
| - if (val != total_len) |
| - error(1, errno, "send"); |
| - |
| - /* wait for all errors to be queued, else ACKs arrive OOO */ |
| - usleep(50 * 1000); |
| - |
| - __poll(fd); |
| - |
| - while (!recv_errmsg(fd)) {} |
| - } |
| - |
| - if (close(fd)) |
| - error(1, errno, "close"); |
| - |
| - free(buf); |
| - usleep(400 * 1000); |
| -} |
| - |
| -static void __attribute__((noreturn)) usage(const char *filepath) |
| -{ |
| - fprintf(stderr, "\nUsage: %s [options] hostname\n" |
| - "\nwhere options are:\n" |
| - " -4: only IPv4\n" |
| - " -6: only IPv6\n" |
| - " -h: show this message\n" |
| - " -l N: send N bytes at a time\n" |
| - " -r: use raw\n" |
| - " -R: use raw (IP_HDRINCL)\n" |
| - " -p N: connect to port N\n" |
| - " -u: use udp\n", |
| - filepath); |
| - exit(1); |
| -} |
| - |
| -static void parse_opt(int argc, char **argv) |
| -{ |
| - int proto_count = 0; |
| - char c; |
| - |
| - while ((c = getopt(argc, argv, "46hl:p:rRu")) != -1) { |
| - switch (c) { |
| - case '4': |
| - do_ipv6 = 0; |
| - break; |
| - case '6': |
| - do_ipv4 = 0; |
| - break; |
| - case 'r': |
| - proto_count++; |
| - cfg_proto = SOCK_RAW; |
| - cfg_ipproto = IPPROTO_UDP; |
| - break; |
| - case 'R': |
| - proto_count++; |
| - cfg_proto = SOCK_RAW; |
| - cfg_ipproto = IPPROTO_RAW; |
| - break; |
| - case 'u': |
| - proto_count++; |
| - cfg_proto = SOCK_DGRAM; |
| - cfg_ipproto = IPPROTO_UDP; |
| - break; |
| - case 'l': |
| - cfg_payload_len = strtoul(optarg, NULL, 10); |
| - break; |
| - case 'p': |
| - dest_port = strtoul(optarg, NULL, 10); |
| - break; |
| - case 'h': |
| - default: |
| - usage(argv[0]); |
| - } |
| - } |
| - |
| - if (!cfg_payload_len) |
| - error(1, 0, "payload may not be nonzero"); |
| - if (cfg_proto != SOCK_STREAM && cfg_payload_len > 1472) |
| - error(1, 0, "udp packet might exceed expected MTU"); |
| - if (!do_ipv4 && !do_ipv6) |
| - error(1, 0, "pass -4 or -6, not both"); |
| - if (proto_count > 1) |
| - error(1, 0, "pass -r, -R or -u, not multiple"); |
| - |
| - if (optind != argc - 1) |
| - error(1, 0, "missing required hostname argument"); |
| -} |
| - |
| -static void resolve_hostname(const char *hostname) |
| -{ |
| - struct addrinfo *addrs, *cur; |
| - int have_ipv4 = 0, have_ipv6 = 0; |
| - |
| - if (getaddrinfo(hostname, NULL, NULL, &addrs)) |
| - error(1, errno, "getaddrinfo"); |
| - |
| - cur = addrs; |
| - while (cur && !have_ipv4 && !have_ipv6) { |
| - if (!have_ipv4 && cur->ai_family == AF_INET) { |
| - memcpy(&daddr, cur->ai_addr, sizeof(daddr)); |
| - daddr.sin_port = htons(dest_port); |
| - have_ipv4 = 1; |
| - } |
| - else if (!have_ipv6 && cur->ai_family == AF_INET6) { |
| - memcpy(&daddr6, cur->ai_addr, sizeof(daddr6)); |
| - daddr6.sin6_port = htons(dest_port); |
| - have_ipv6 = 1; |
| - } |
| - cur = cur->ai_next; |
| - } |
| - if (addrs) |
| - freeaddrinfo(addrs); |
| - |
| - do_ipv4 &= have_ipv4; |
| - do_ipv6 &= have_ipv6; |
| -} |
| - |
| -static void do_main(int family) |
| -{ |
| - fprintf(stderr, "family: %s\n", |
| - family == PF_INET ? "INET" : "INET6"); |
| - |
| - fprintf(stderr, "test SND\n"); |
| - do_test(family, SOF_TIMESTAMPING_TX_SOFTWARE); |
| - |
| - fprintf(stderr, "test ENQ\n"); |
| - do_test(family, SOF_TIMESTAMPING_TX_SCHED); |
| - |
| - fprintf(stderr, "test ENQ + SND\n"); |
| - do_test(family, SOF_TIMESTAMPING_TX_SCHED | |
| - SOF_TIMESTAMPING_TX_SOFTWARE); |
| - |
| - if (cfg_proto == SOCK_STREAM) { |
| - fprintf(stderr, "\ntest ACK\n"); |
| - do_test(family, SOF_TIMESTAMPING_TX_ACK); |
| - |
| - fprintf(stderr, "\ntest SND + ACK\n"); |
| - do_test(family, SOF_TIMESTAMPING_TX_SOFTWARE | |
| - SOF_TIMESTAMPING_TX_ACK); |
| - |
| - fprintf(stderr, "\ntest ENQ + SND + ACK\n"); |
| - do_test(family, SOF_TIMESTAMPING_TX_SCHED | |
| - SOF_TIMESTAMPING_TX_SOFTWARE | |
| - SOF_TIMESTAMPING_TX_ACK); |
| - } |
| -} |
| - |
| -const char *sock_names[] = { NULL, "TCP", "UDP", "RAW" }; |
| - |
| -int main(int argc, char **argv) |
| -{ |
| - if (argc == 1) |
| - usage(argv[0]); |
| - |
| - parse_opt(argc, argv); |
| - resolve_hostname(argv[argc - 1]); |
| - |
| - fprintf(stderr, "protocol: %s\n", sock_names[cfg_proto]); |
| - fprintf(stderr, "payload: %u\n", cfg_payload_len); |
| - fprintf(stderr, "server port: %u\n", dest_port); |
| - fprintf(stderr, "\n"); |
| - |
| - if (do_ipv4) |
| - do_main(PF_INET); |
| - if (do_ipv6) |
| - do_main(PF_INET6); |
| - |
| - return 0; |
| -} |
| --- /dev/null |
| +++ b/tools/testing/selftests/networking/timestamping/.gitignore |
| @@ -0,0 +1,3 @@ |
| +timestamping |
| +txtimestamp |
| +hwtstamp_config |
| --- /dev/null |
| +++ b/tools/testing/selftests/networking/timestamping/Makefile |
| @@ -0,0 +1,8 @@ |
| +TEST_PROGS := hwtstamp_config timestamping txtimestamp |
| + |
| +all: $(TEST_PROGS) |
| + |
| +include ../../lib.mk |
| + |
| +clean: |
| + rm -fr $(TEST_PROGS) |
| --- /dev/null |
| +++ b/tools/testing/selftests/networking/timestamping/hwtstamp_config.c |
| @@ -0,0 +1,134 @@ |
| +/* Test program for SIOC{G,S}HWTSTAMP |
| + * Copyright 2013 Solarflare Communications |
| + * Author: Ben Hutchings |
| + */ |
| + |
| +#include <errno.h> |
| +#include <stdio.h> |
| +#include <stdlib.h> |
| +#include <string.h> |
| + |
| +#include <sys/socket.h> |
| +#include <sys/ioctl.h> |
| + |
| +#include <linux/if.h> |
| +#include <linux/net_tstamp.h> |
| +#include <linux/sockios.h> |
| + |
| +static int |
| +lookup_value(const char **names, int size, const char *name) |
| +{ |
| + int value; |
| + |
| + for (value = 0; value < size; value++) |
| + if (names[value] && strcasecmp(names[value], name) == 0) |
| + return value; |
| + |
| + return -1; |
| +} |
| + |
| +static const char * |
| +lookup_name(const char **names, int size, int value) |
| +{ |
| + return (value >= 0 && value < size) ? names[value] : NULL; |
| +} |
| + |
| +static void list_names(FILE *f, const char **names, int size) |
| +{ |
| + int value; |
| + |
| + for (value = 0; value < size; value++) |
| + if (names[value]) |
| + fprintf(f, " %s\n", names[value]); |
| +} |
| + |
| +static const char *tx_types[] = { |
| +#define TX_TYPE(name) [HWTSTAMP_TX_ ## name] = #name |
| + TX_TYPE(OFF), |
| + TX_TYPE(ON), |
| + TX_TYPE(ONESTEP_SYNC) |
| +#undef TX_TYPE |
| +}; |
| +#define N_TX_TYPES ((int)(sizeof(tx_types) / sizeof(tx_types[0]))) |
| + |
| +static const char *rx_filters[] = { |
| +#define RX_FILTER(name) [HWTSTAMP_FILTER_ ## name] = #name |
| + RX_FILTER(NONE), |
| + RX_FILTER(ALL), |
| + RX_FILTER(SOME), |
| + RX_FILTER(PTP_V1_L4_EVENT), |
| + RX_FILTER(PTP_V1_L4_SYNC), |
| + RX_FILTER(PTP_V1_L4_DELAY_REQ), |
| + RX_FILTER(PTP_V2_L4_EVENT), |
| + RX_FILTER(PTP_V2_L4_SYNC), |
| + RX_FILTER(PTP_V2_L4_DELAY_REQ), |
| + RX_FILTER(PTP_V2_L2_EVENT), |
| + RX_FILTER(PTP_V2_L2_SYNC), |
| + RX_FILTER(PTP_V2_L2_DELAY_REQ), |
| + RX_FILTER(PTP_V2_EVENT), |
| + RX_FILTER(PTP_V2_SYNC), |
| + RX_FILTER(PTP_V2_DELAY_REQ), |
| +#undef RX_FILTER |
| +}; |
| +#define N_RX_FILTERS ((int)(sizeof(rx_filters) / sizeof(rx_filters[0]))) |
| + |
| +static void usage(void) |
| +{ |
| + fputs("Usage: hwtstamp_config if_name [tx_type rx_filter]\n" |
| + "tx_type is any of (case-insensitive):\n", |
| + stderr); |
| + list_names(stderr, tx_types, N_TX_TYPES); |
| + fputs("rx_filter is any of (case-insensitive):\n", stderr); |
| + list_names(stderr, rx_filters, N_RX_FILTERS); |
| +} |
| + |
| +int main(int argc, char **argv) |
| +{ |
| + struct ifreq ifr; |
| + struct hwtstamp_config config; |
| + const char *name; |
| + int sock; |
| + |
| + if ((argc != 2 && argc != 4) || (strlen(argv[1]) >= IFNAMSIZ)) { |
| + usage(); |
| + return 2; |
| + } |
| + |
| + if (argc == 4) { |
| + config.flags = 0; |
| + config.tx_type = lookup_value(tx_types, N_TX_TYPES, argv[2]); |
| + config.rx_filter = lookup_value(rx_filters, N_RX_FILTERS, argv[3]); |
| + if (config.tx_type < 0 || config.rx_filter < 0) { |
| + usage(); |
| + return 2; |
| + } |
| + } |
| + |
| + sock = socket(AF_INET, SOCK_DGRAM, 0); |
| + if (sock < 0) { |
| + perror("socket"); |
| + return 1; |
| + } |
| + |
| + strcpy(ifr.ifr_name, argv[1]); |
| + ifr.ifr_data = (caddr_t)&config; |
| + |
| + if (ioctl(sock, (argc == 2) ? SIOCGHWTSTAMP : SIOCSHWTSTAMP, &ifr)) { |
| + perror("ioctl"); |
| + return 1; |
| + } |
| + |
| + printf("flags = %#x\n", config.flags); |
| + name = lookup_name(tx_types, N_TX_TYPES, config.tx_type); |
| + if (name) |
| + printf("tx_type = %s\n", name); |
| + else |
| + printf("tx_type = %d\n", config.tx_type); |
| + name = lookup_name(rx_filters, N_RX_FILTERS, config.rx_filter); |
| + if (name) |
| + printf("rx_filter = %s\n", name); |
| + else |
| + printf("rx_filter = %d\n", config.rx_filter); |
| + |
| + return 0; |
| +} |
| --- /dev/null |
| +++ b/tools/testing/selftests/networking/timestamping/timestamping.c |
| @@ -0,0 +1,528 @@ |
| +/* |
| + * This program demonstrates how the various time stamping features in |
| + * the Linux kernel work. It emulates the behavior of a PTP |
| + * implementation in stand-alone master mode by sending PTPv1 Sync |
| + * multicasts once every second. It looks for similar packets, but |
| + * beyond that doesn't actually implement PTP. |
| + * |
| + * Outgoing packets are time stamped with SO_TIMESTAMPING with or |
| + * without hardware support. |
| + * |
| + * Incoming packets are time stamped with SO_TIMESTAMPING with or |
| + * without hardware support, SIOCGSTAMP[NS] (per-socket time stamp) and |
| + * SO_TIMESTAMP[NS]. |
| + * |
| + * Copyright (C) 2009 Intel Corporation. |
| + * Author: Patrick Ohly <patrick.ohly@intel.com> |
| + * |
| + * This program is free software; you can redistribute it and/or modify it |
| + * under the terms and conditions of the GNU General Public License, |
| + * version 2, as published by the Free Software Foundation. |
| + * |
| + * This program is distributed in the hope it will be useful, but WITHOUT |
| + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
| + * FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for |
| + * more details. |
| + * |
| + * You should have received a copy of the GNU General Public License along with |
| + * this program; if not, write to the Free Software Foundation, Inc., |
| + * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. |
| + */ |
| + |
| +#include <stdio.h> |
| +#include <stdlib.h> |
| +#include <errno.h> |
| +#include <string.h> |
| + |
| +#include <sys/time.h> |
| +#include <sys/socket.h> |
| +#include <sys/select.h> |
| +#include <sys/ioctl.h> |
| +#include <arpa/inet.h> |
| +#include <net/if.h> |
| + |
| +#include <asm/types.h> |
| +#include <linux/net_tstamp.h> |
| +#include <linux/errqueue.h> |
| + |
| +#ifndef SO_TIMESTAMPING |
| +# define SO_TIMESTAMPING 37 |
| +# define SCM_TIMESTAMPING SO_TIMESTAMPING |
| +#endif |
| + |
| +#ifndef SO_TIMESTAMPNS |
| +# define SO_TIMESTAMPNS 35 |
| +#endif |
| + |
| +#ifndef SIOCGSTAMPNS |
| +# define SIOCGSTAMPNS 0x8907 |
| +#endif |
| + |
| +#ifndef SIOCSHWTSTAMP |
| +# define SIOCSHWTSTAMP 0x89b0 |
| +#endif |
| + |
| +static void usage(const char *error) |
| +{ |
| + if (error) |
| + printf("invalid option: %s\n", error); |
| + printf("timestamping interface option*\n\n" |
| + "Options:\n" |
| + " IP_MULTICAST_LOOP - looping outgoing multicasts\n" |
| + " SO_TIMESTAMP - normal software time stamping, ms resolution\n" |
| + " SO_TIMESTAMPNS - more accurate software time stamping\n" |
| + " SOF_TIMESTAMPING_TX_HARDWARE - hardware time stamping of outgoing packets\n" |
| + " SOF_TIMESTAMPING_TX_SOFTWARE - software fallback for outgoing packets\n" |
| + " SOF_TIMESTAMPING_RX_HARDWARE - hardware time stamping of incoming packets\n" |
| + " SOF_TIMESTAMPING_RX_SOFTWARE - software fallback for incoming packets\n" |
| + " SOF_TIMESTAMPING_SOFTWARE - request reporting of software time stamps\n" |
| + " SOF_TIMESTAMPING_RAW_HARDWARE - request reporting of raw HW time stamps\n" |
| + " SIOCGSTAMP - check last socket time stamp\n" |
| + " SIOCGSTAMPNS - more accurate socket time stamp\n"); |
| + exit(1); |
| +} |
| + |
| +static void bail(const char *error) |
| +{ |
| + printf("%s: %s\n", error, strerror(errno)); |
| + exit(1); |
| +} |
| + |
| +static const unsigned char sync[] = { |
| + 0x00, 0x01, 0x00, 0x01, |
| + 0x5f, 0x44, 0x46, 0x4c, |
| + 0x54, 0x00, 0x00, 0x00, |
| + 0x00, 0x00, 0x00, 0x00, |
| + 0x00, 0x00, 0x00, 0x00, |
| + 0x01, 0x01, |
| + |
| + /* fake uuid */ |
| + 0x00, 0x01, |
| + 0x02, 0x03, 0x04, 0x05, |
| + |
| + 0x00, 0x01, 0x00, 0x37, |
| + 0x00, 0x00, 0x00, 0x08, |
| + 0x00, 0x00, 0x00, 0x00, |
| + 0x49, 0x05, 0xcd, 0x01, |
| + 0x29, 0xb1, 0x8d, 0xb0, |
| + 0x00, 0x00, 0x00, 0x00, |
| + 0x00, 0x01, |
| + |
| + /* fake uuid */ |
| + 0x00, 0x01, |
| + 0x02, 0x03, 0x04, 0x05, |
| + |
| + 0x00, 0x00, 0x00, 0x37, |
| + 0x00, 0x00, 0x00, 0x04, |
| + 0x44, 0x46, 0x4c, 0x54, |
| + 0x00, 0x00, 0xf0, 0x60, |
| + 0x00, 0x01, 0x00, 0x00, |
| + 0x00, 0x00, 0x00, 0x01, |
| + 0x00, 0x00, 0xf0, 0x60, |
| + 0x00, 0x00, 0x00, 0x00, |
| + 0x00, 0x00, 0x00, 0x04, |
| + 0x44, 0x46, 0x4c, 0x54, |
| + 0x00, 0x01, |
| + |
| + /* fake uuid */ |
| + 0x00, 0x01, |
| + 0x02, 0x03, 0x04, 0x05, |
| + |
| + 0x00, 0x00, 0x00, 0x00, |
| + 0x00, 0x00, 0x00, 0x00, |
| + 0x00, 0x00, 0x00, 0x00, |
| + 0x00, 0x00, 0x00, 0x00 |
| +}; |
| + |
| +static void sendpacket(int sock, struct sockaddr *addr, socklen_t addr_len) |
| +{ |
| + struct timeval now; |
| + int res; |
| + |
| + res = sendto(sock, sync, sizeof(sync), 0, |
| + addr, addr_len); |
| + gettimeofday(&now, 0); |
| + if (res < 0) |
| + printf("%s: %s\n", "send", strerror(errno)); |
| + else |
| + printf("%ld.%06ld: sent %d bytes\n", |
| + (long)now.tv_sec, (long)now.tv_usec, |
| + res); |
| +} |
| + |
| +static void printpacket(struct msghdr *msg, int res, |
| + char *data, |
| + int sock, int recvmsg_flags, |
| + int siocgstamp, int siocgstampns) |
| +{ |
| + struct sockaddr_in *from_addr = (struct sockaddr_in *)msg->msg_name; |
| + struct cmsghdr *cmsg; |
| + struct timeval tv; |
| + struct timespec ts; |
| + struct timeval now; |
| + |
| + gettimeofday(&now, 0); |
| + |
| + printf("%ld.%06ld: received %s data, %d bytes from %s, %zu bytes control messages\n", |
| + (long)now.tv_sec, (long)now.tv_usec, |
| + (recvmsg_flags & MSG_ERRQUEUE) ? "error" : "regular", |
| + res, |
| + inet_ntoa(from_addr->sin_addr), |
| + msg->msg_controllen); |
| + for (cmsg = CMSG_FIRSTHDR(msg); |
| + cmsg; |
| + cmsg = CMSG_NXTHDR(msg, cmsg)) { |
| + printf(" cmsg len %zu: ", cmsg->cmsg_len); |
| + switch (cmsg->cmsg_level) { |
| + case SOL_SOCKET: |
| + printf("SOL_SOCKET "); |
| + switch (cmsg->cmsg_type) { |
| + case SO_TIMESTAMP: { |
| + struct timeval *stamp = |
| + (struct timeval *)CMSG_DATA(cmsg); |
| + printf("SO_TIMESTAMP %ld.%06ld", |
| + (long)stamp->tv_sec, |
| + (long)stamp->tv_usec); |
| + break; |
| + } |
| + case SO_TIMESTAMPNS: { |
| + struct timespec *stamp = |
| + (struct timespec *)CMSG_DATA(cmsg); |
| + printf("SO_TIMESTAMPNS %ld.%09ld", |
| + (long)stamp->tv_sec, |
| + (long)stamp->tv_nsec); |
| + break; |
| + } |
| + case SO_TIMESTAMPING: { |
| + struct timespec *stamp = |
| + (struct timespec *)CMSG_DATA(cmsg); |
| + printf("SO_TIMESTAMPING "); |
| + printf("SW %ld.%09ld ", |
| + (long)stamp->tv_sec, |
| + (long)stamp->tv_nsec); |
| + stamp++; |
| + /* skip deprecated HW transformed */ |
| + stamp++; |
| + printf("HW raw %ld.%09ld", |
| + (long)stamp->tv_sec, |
| + (long)stamp->tv_nsec); |
| + break; |
| + } |
| + default: |
| + printf("type %d", cmsg->cmsg_type); |
| + break; |
| + } |
| + break; |
| + case IPPROTO_IP: |
| + printf("IPPROTO_IP "); |
| + switch (cmsg->cmsg_type) { |
| + case IP_RECVERR: { |
| + struct sock_extended_err *err = |
| + (struct sock_extended_err *)CMSG_DATA(cmsg); |
| + printf("IP_RECVERR ee_errno '%s' ee_origin %d => %s", |
| + strerror(err->ee_errno), |
| + err->ee_origin, |
| +#ifdef SO_EE_ORIGIN_TIMESTAMPING |
| + err->ee_origin == SO_EE_ORIGIN_TIMESTAMPING ? |
| + "bounced packet" : "unexpected origin" |
| +#else |
| + "probably SO_EE_ORIGIN_TIMESTAMPING" |
| +#endif |
| + ); |
| + if (res < sizeof(sync)) |
| + printf(" => truncated data?!"); |
| + else if (!memcmp(sync, data + res - sizeof(sync), |
| + sizeof(sync))) |
| + printf(" => GOT OUR DATA BACK (HURRAY!)"); |
| + break; |
| + } |
| + case IP_PKTINFO: { |
| + struct in_pktinfo *pktinfo = |
| + (struct in_pktinfo *)CMSG_DATA(cmsg); |
| + printf("IP_PKTINFO interface index %u", |
| + pktinfo->ipi_ifindex); |
| + break; |
| + } |
| + default: |
| + printf("type %d", cmsg->cmsg_type); |
| + break; |
| + } |
| + break; |
| + default: |
| + printf("level %d type %d", |
| + cmsg->cmsg_level, |
| + cmsg->cmsg_type); |
| + break; |
| + } |
| + printf("\n"); |
| + } |
| + |
| + if (siocgstamp) { |
| + if (ioctl(sock, SIOCGSTAMP, &tv)) |
| + printf(" %s: %s\n", "SIOCGSTAMP", strerror(errno)); |
| + else |
| + printf("SIOCGSTAMP %ld.%06ld\n", |
| + (long)tv.tv_sec, |
| + (long)tv.tv_usec); |
| + } |
| + if (siocgstampns) { |
| + if (ioctl(sock, SIOCGSTAMPNS, &ts)) |
| + printf(" %s: %s\n", "SIOCGSTAMPNS", strerror(errno)); |
| + else |
| + printf("SIOCGSTAMPNS %ld.%09ld\n", |
| + (long)ts.tv_sec, |
| + (long)ts.tv_nsec); |
| + } |
| +} |
| + |
| +static void recvpacket(int sock, int recvmsg_flags, |
| + int siocgstamp, int siocgstampns) |
| +{ |
| + char data[256]; |
| + struct msghdr msg; |
| + struct iovec entry; |
| + struct sockaddr_in from_addr; |
| + struct { |
| + struct cmsghdr cm; |
| + char control[512]; |
| + } control; |
| + int res; |
| + |
| + memset(&msg, 0, sizeof(msg)); |
| + msg.msg_iov = &entry; |
| + msg.msg_iovlen = 1; |
| + entry.iov_base = data; |
| + entry.iov_len = sizeof(data); |
| + msg.msg_name = (caddr_t)&from_addr; |
| + msg.msg_namelen = sizeof(from_addr); |
| + msg.msg_control = &control; |
| + msg.msg_controllen = sizeof(control); |
| + |
| + res = recvmsg(sock, &msg, recvmsg_flags|MSG_DONTWAIT); |
| + if (res < 0) { |
| + printf("%s %s: %s\n", |
| + "recvmsg", |
| + (recvmsg_flags & MSG_ERRQUEUE) ? "error" : "regular", |
| + strerror(errno)); |
| + } else { |
| + printpacket(&msg, res, data, |
| + sock, recvmsg_flags, |
| + siocgstamp, siocgstampns); |
| + } |
| +} |
| + |
| +int main(int argc, char **argv) |
| +{ |
| + int so_timestamping_flags = 0; |
| + int so_timestamp = 0; |
| + int so_timestampns = 0; |
| + int siocgstamp = 0; |
| + int siocgstampns = 0; |
| + int ip_multicast_loop = 0; |
| + char *interface; |
| + int i; |
| + int enabled = 1; |
| + int sock; |
| + struct ifreq device; |
| + struct ifreq hwtstamp; |
| + struct hwtstamp_config hwconfig, hwconfig_requested; |
| + struct sockaddr_in addr; |
| + struct ip_mreq imr; |
| + struct in_addr iaddr; |
| + int val; |
| + socklen_t len; |
| + struct timeval next; |
| + |
| + if (argc < 2) |
| + usage(0); |
| + interface = argv[1]; |
| + |
| + for (i = 2; i < argc; i++) { |
| + if (!strcasecmp(argv[i], "SO_TIMESTAMP")) |
| + so_timestamp = 1; |
| + else if (!strcasecmp(argv[i], "SO_TIMESTAMPNS")) |
| + so_timestampns = 1; |
| + else if (!strcasecmp(argv[i], "SIOCGSTAMP")) |
| + siocgstamp = 1; |
| + else if (!strcasecmp(argv[i], "SIOCGSTAMPNS")) |
| + siocgstampns = 1; |
| + else if (!strcasecmp(argv[i], "IP_MULTICAST_LOOP")) |
| + ip_multicast_loop = 1; |
| + else if (!strcasecmp(argv[i], "SOF_TIMESTAMPING_TX_HARDWARE")) |
| + so_timestamping_flags |= SOF_TIMESTAMPING_TX_HARDWARE; |
| + else if (!strcasecmp(argv[i], "SOF_TIMESTAMPING_TX_SOFTWARE")) |
| + so_timestamping_flags |= SOF_TIMESTAMPING_TX_SOFTWARE; |
| + else if (!strcasecmp(argv[i], "SOF_TIMESTAMPING_RX_HARDWARE")) |
| + so_timestamping_flags |= SOF_TIMESTAMPING_RX_HARDWARE; |
| + else if (!strcasecmp(argv[i], "SOF_TIMESTAMPING_RX_SOFTWARE")) |
| + so_timestamping_flags |= SOF_TIMESTAMPING_RX_SOFTWARE; |
| + else if (!strcasecmp(argv[i], "SOF_TIMESTAMPING_SOFTWARE")) |
| + so_timestamping_flags |= SOF_TIMESTAMPING_SOFTWARE; |
| + else if (!strcasecmp(argv[i], "SOF_TIMESTAMPING_RAW_HARDWARE")) |
| + so_timestamping_flags |= SOF_TIMESTAMPING_RAW_HARDWARE; |
| + else |
| + usage(argv[i]); |
| + } |
| + |
| + sock = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP); |
| + if (sock < 0) |
| + bail("socket"); |
| + |
| + memset(&device, 0, sizeof(device)); |
| + strncpy(device.ifr_name, interface, sizeof(device.ifr_name)); |
| + if (ioctl(sock, SIOCGIFADDR, &device) < 0) |
| + bail("getting interface IP address"); |
| + |
| + memset(&hwtstamp, 0, sizeof(hwtstamp)); |
| + strncpy(hwtstamp.ifr_name, interface, sizeof(hwtstamp.ifr_name)); |
| + hwtstamp.ifr_data = (void *)&hwconfig; |
| + memset(&hwconfig, 0, sizeof(hwconfig)); |
| + hwconfig.tx_type = |
| + (so_timestamping_flags & SOF_TIMESTAMPING_TX_HARDWARE) ? |
| + HWTSTAMP_TX_ON : HWTSTAMP_TX_OFF; |
| + hwconfig.rx_filter = |
| + (so_timestamping_flags & SOF_TIMESTAMPING_RX_HARDWARE) ? |
| + HWTSTAMP_FILTER_PTP_V1_L4_SYNC : HWTSTAMP_FILTER_NONE; |
| + hwconfig_requested = hwconfig; |
| + if (ioctl(sock, SIOCSHWTSTAMP, &hwtstamp) < 0) { |
| + if ((errno == EINVAL || errno == ENOTSUP) && |
| + hwconfig_requested.tx_type == HWTSTAMP_TX_OFF && |
| + hwconfig_requested.rx_filter == HWTSTAMP_FILTER_NONE) |
| + printf("SIOCSHWTSTAMP: disabling hardware time stamping not possible\n"); |
| + else |
| + bail("SIOCSHWTSTAMP"); |
| + } |
| + printf("SIOCSHWTSTAMP: tx_type %d requested, got %d; rx_filter %d requested, got %d\n", |
| + hwconfig_requested.tx_type, hwconfig.tx_type, |
| + hwconfig_requested.rx_filter, hwconfig.rx_filter); |
| + |
| + /* bind to PTP port */ |
| + addr.sin_family = AF_INET; |
| + addr.sin_addr.s_addr = htonl(INADDR_ANY); |
| + addr.sin_port = htons(319 /* PTP event port */); |
| + if (bind(sock, |
| + (struct sockaddr *)&addr, |
| + sizeof(struct sockaddr_in)) < 0) |
| + bail("bind"); |
| + |
| + /* set multicast group for outgoing packets */ |
| + inet_aton("224.0.1.130", &iaddr); /* alternate PTP domain 1 */ |
| + addr.sin_addr = iaddr; |
| + imr.imr_multiaddr.s_addr = iaddr.s_addr; |
| + imr.imr_interface.s_addr = |
| + ((struct sockaddr_in *)&device.ifr_addr)->sin_addr.s_addr; |
| + if (setsockopt(sock, IPPROTO_IP, IP_MULTICAST_IF, |
| + &imr.imr_interface.s_addr, sizeof(struct in_addr)) < 0) |
| + bail("set multicast"); |
| + |
| + /* join multicast group, loop our own packet */ |
| + if (setsockopt(sock, IPPROTO_IP, IP_ADD_MEMBERSHIP, |
| + &imr, sizeof(struct ip_mreq)) < 0) |
| + bail("join multicast group"); |
| + |
| + if (setsockopt(sock, IPPROTO_IP, IP_MULTICAST_LOOP, |
| + &ip_multicast_loop, sizeof(enabled)) < 0) { |
| + bail("loop multicast"); |
| + } |
| + |
| + /* set socket options for time stamping */ |
| + if (so_timestamp && |
| + setsockopt(sock, SOL_SOCKET, SO_TIMESTAMP, |
| + &enabled, sizeof(enabled)) < 0) |
| + bail("setsockopt SO_TIMESTAMP"); |
| + |
| + if (so_timestampns && |
| + setsockopt(sock, SOL_SOCKET, SO_TIMESTAMPNS, |
| + &enabled, sizeof(enabled)) < 0) |
| + bail("setsockopt SO_TIMESTAMPNS"); |
| + |
| + if (so_timestamping_flags && |
| + setsockopt(sock, SOL_SOCKET, SO_TIMESTAMPING, |
| + &so_timestamping_flags, |
| + sizeof(so_timestamping_flags)) < 0) |
| + bail("setsockopt SO_TIMESTAMPING"); |
| + |
| + /* request IP_PKTINFO for debugging purposes */ |
| + if (setsockopt(sock, SOL_IP, IP_PKTINFO, |
| + &enabled, sizeof(enabled)) < 0) |
| + printf("%s: %s\n", "setsockopt IP_PKTINFO", strerror(errno)); |
| + |
| + /* verify socket options */ |
| + len = sizeof(val); |
| + if (getsockopt(sock, SOL_SOCKET, SO_TIMESTAMP, &val, &len) < 0) |
| + printf("%s: %s\n", "getsockopt SO_TIMESTAMP", strerror(errno)); |
| + else |
| + printf("SO_TIMESTAMP %d\n", val); |
| + |
| + if (getsockopt(sock, SOL_SOCKET, SO_TIMESTAMPNS, &val, &len) < 0) |
| + printf("%s: %s\n", "getsockopt SO_TIMESTAMPNS", |
| + strerror(errno)); |
| + else |
| + printf("SO_TIMESTAMPNS %d\n", val); |
| + |
| + if (getsockopt(sock, SOL_SOCKET, SO_TIMESTAMPING, &val, &len) < 0) { |
| + printf("%s: %s\n", "getsockopt SO_TIMESTAMPING", |
| + strerror(errno)); |
| + } else { |
| + printf("SO_TIMESTAMPING %d\n", val); |
| + if (val != so_timestamping_flags) |
| + printf(" not the expected value %d\n", |
| + so_timestamping_flags); |
| + } |
| + |
| + /* send packets forever every five seconds */ |
| + gettimeofday(&next, 0); |
| + next.tv_sec = (next.tv_sec + 1) / 5 * 5; |
| + next.tv_usec = 0; |
| + while (1) { |
| + struct timeval now; |
| + struct timeval delta; |
| + long delta_us; |
| + int res; |
| + fd_set readfs, errorfs; |
| + |
| + gettimeofday(&now, 0); |
| + delta_us = (long)(next.tv_sec - now.tv_sec) * 1000000 + |
| + (long)(next.tv_usec - now.tv_usec); |
| + if (delta_us > 0) { |
| + /* continue waiting for timeout or data */ |
| + delta.tv_sec = delta_us / 1000000; |
| + delta.tv_usec = delta_us % 1000000; |
| + |
| + FD_ZERO(&readfs); |
| + FD_ZERO(&errorfs); |
| + FD_SET(sock, &readfs); |
| + FD_SET(sock, &errorfs); |
| + printf("%ld.%06ld: select %ldus\n", |
| + (long)now.tv_sec, (long)now.tv_usec, |
| + delta_us); |
| + res = select(sock + 1, &readfs, 0, &errorfs, &delta); |
| + gettimeofday(&now, 0); |
| + printf("%ld.%06ld: select returned: %d, %s\n", |
| + (long)now.tv_sec, (long)now.tv_usec, |
| + res, |
| + res < 0 ? strerror(errno) : "success"); |
| + if (res > 0) { |
| + if (FD_ISSET(sock, &readfs)) |
| + printf("ready for reading\n"); |
| + if (FD_ISSET(sock, &errorfs)) |
| + printf("has error\n"); |
| + recvpacket(sock, 0, |
| + siocgstamp, |
| + siocgstampns); |
| + recvpacket(sock, MSG_ERRQUEUE, |
| + siocgstamp, |
| + siocgstampns); |
| + } |
| + } else { |
| + /* write one packet */ |
| + sendpacket(sock, |
| + (struct sockaddr *)&addr, |
| + sizeof(addr)); |
| + next.tv_sec += 5; |
| + continue; |
| + } |
| + } |
| + |
| + return 0; |
| +} |
| --- /dev/null |
| +++ b/tools/testing/selftests/networking/timestamping/txtimestamp.c |
| @@ -0,0 +1,469 @@ |
| +/* |
| + * Copyright 2014 Google Inc. |
| + * Author: willemb@google.com (Willem de Bruijn) |
| + * |
| + * Test software tx timestamping, including |
| + * |
| + * - SCHED, SND and ACK timestamps |
| + * - RAW, UDP and TCP |
| + * - IPv4 and IPv6 |
| + * - various packet sizes (to test GSO and TSO) |
| + * |
| + * Consult the command line arguments for help on running |
| + * the various testcases. |
| + * |
| + * This test requires a dummy TCP server. |
| + * A simple `nc6 [-u] -l -p $DESTPORT` will do |
| + * |
| + * |
| + * This program is free software; you can redistribute it and/or modify it |
| + * under the terms and conditions of the GNU General Public License, |
| + * version 2, as published by the Free Software Foundation. |
| + * |
| + * This program is distributed in the hope it will be useful, but WITHOUT |
| + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
| + * FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for |
| + * more details. |
| + * |
| + * You should have received a copy of the GNU General Public License along with |
| + * this program; if not, write to the Free Software Foundation, Inc., |
| + * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. |
| + */ |
| + |
| +#include <arpa/inet.h> |
| +#include <asm/types.h> |
| +#include <error.h> |
| +#include <errno.h> |
| +#include <linux/errqueue.h> |
| +#include <linux/if_ether.h> |
| +#include <linux/net_tstamp.h> |
| +#include <netdb.h> |
| +#include <net/if.h> |
| +#include <netinet/in.h> |
| +#include <netinet/ip.h> |
| +#include <netinet/udp.h> |
| +#include <netinet/tcp.h> |
| +#include <netpacket/packet.h> |
| +#include <poll.h> |
| +#include <stdarg.h> |
| +#include <stdint.h> |
| +#include <stdio.h> |
| +#include <stdlib.h> |
| +#include <string.h> |
| +#include <sys/ioctl.h> |
| +#include <sys/select.h> |
| +#include <sys/socket.h> |
| +#include <sys/time.h> |
| +#include <sys/types.h> |
| +#include <time.h> |
| +#include <unistd.h> |
| + |
| +/* command line parameters */ |
| +static int cfg_proto = SOCK_STREAM; |
| +static int cfg_ipproto = IPPROTO_TCP; |
| +static int cfg_num_pkts = 4; |
| +static int do_ipv4 = 1; |
| +static int do_ipv6 = 1; |
| +static int cfg_payload_len = 10; |
| +static uint16_t dest_port = 9000; |
| + |
| +static struct sockaddr_in daddr; |
| +static struct sockaddr_in6 daddr6; |
| +static struct timespec ts_prev; |
| + |
| +static void __print_timestamp(const char *name, struct timespec *cur, |
| + uint32_t key, int payload_len) |
| +{ |
| + if (!(cur->tv_sec | cur->tv_nsec)) |
| + return; |
| + |
| + fprintf(stderr, " %s: %lu s %lu us (seq=%u, len=%u)", |
| + name, cur->tv_sec, cur->tv_nsec / 1000, |
| + key, payload_len); |
| + |
| + if ((ts_prev.tv_sec | ts_prev.tv_nsec)) { |
| + int64_t cur_ms, prev_ms; |
| + |
| + cur_ms = (long) cur->tv_sec * 1000 * 1000; |
| + cur_ms += cur->tv_nsec / 1000; |
| + |
| + prev_ms = (long) ts_prev.tv_sec * 1000 * 1000; |
| + prev_ms += ts_prev.tv_nsec / 1000; |
| + |
| + fprintf(stderr, " (%+ld us)", cur_ms - prev_ms); |
| + } |
| + |
| + ts_prev = *cur; |
| + fprintf(stderr, "\n"); |
| +} |
| + |
| +static void print_timestamp_usr(void) |
| +{ |
| + struct timespec ts; |
| + struct timeval tv; /* avoid dependency on -lrt */ |
| + |
| + gettimeofday(&tv, NULL); |
| + ts.tv_sec = tv.tv_sec; |
| + ts.tv_nsec = tv.tv_usec * 1000; |
| + |
| + __print_timestamp(" USR", &ts, 0, 0); |
| +} |
| + |
| +static void print_timestamp(struct scm_timestamping *tss, int tstype, |
| + int tskey, int payload_len) |
| +{ |
| + const char *tsname; |
| + |
| + switch (tstype) { |
| + case SCM_TSTAMP_SCHED: |
| + tsname = " ENQ"; |
| + break; |
| + case SCM_TSTAMP_SND: |
| + tsname = " SND"; |
| + break; |
| + case SCM_TSTAMP_ACK: |
| + tsname = " ACK"; |
| + break; |
| + default: |
| + error(1, 0, "unknown timestamp type: %u", |
| + tstype); |
| + } |
| + __print_timestamp(tsname, &tss->ts[0], tskey, payload_len); |
| +} |
| + |
| +static void __poll(int fd) |
| +{ |
| + struct pollfd pollfd; |
| + int ret; |
| + |
| + memset(&pollfd, 0, sizeof(pollfd)); |
| + pollfd.fd = fd; |
| + ret = poll(&pollfd, 1, 100); |
| + if (ret != 1) |
| + error(1, errno, "poll"); |
| +} |
| + |
| +static void __recv_errmsg_cmsg(struct msghdr *msg, int payload_len) |
| +{ |
| + struct sock_extended_err *serr = NULL; |
| + struct scm_timestamping *tss = NULL; |
| + struct cmsghdr *cm; |
| + |
| + for (cm = CMSG_FIRSTHDR(msg); |
| + cm && cm->cmsg_len; |
| + cm = CMSG_NXTHDR(msg, cm)) { |
| + if (cm->cmsg_level == SOL_SOCKET && |
| + cm->cmsg_type == SCM_TIMESTAMPING) { |
| + tss = (void *) CMSG_DATA(cm); |
| + } else if ((cm->cmsg_level == SOL_IP && |
| + cm->cmsg_type == IP_RECVERR) || |
| + (cm->cmsg_level == SOL_IPV6 && |
| + cm->cmsg_type == IPV6_RECVERR)) { |
| + |
| + serr = (void *) CMSG_DATA(cm); |
| + if (serr->ee_errno != ENOMSG || |
| + serr->ee_origin != SO_EE_ORIGIN_TIMESTAMPING) { |
| + fprintf(stderr, "unknown ip error %d %d\n", |
| + serr->ee_errno, |
| + serr->ee_origin); |
| + serr = NULL; |
| + } |
| + } else |
| + fprintf(stderr, "unknown cmsg %d,%d\n", |
| + cm->cmsg_level, cm->cmsg_type); |
| + } |
| + |
| + if (serr && tss) |
| + print_timestamp(tss, serr->ee_info, serr->ee_data, payload_len); |
| +} |
| + |
| +static int recv_errmsg(int fd) |
| +{ |
| + static char ctrl[1024 /* overprovision*/]; |
| + static struct msghdr msg; |
| + struct iovec entry; |
| + static char *data; |
| + int ret = 0; |
| + |
| + data = malloc(cfg_payload_len); |
| + if (!data) |
| + error(1, 0, "malloc"); |
| + |
| + memset(&msg, 0, sizeof(msg)); |
| + memset(&entry, 0, sizeof(entry)); |
| + memset(ctrl, 0, sizeof(ctrl)); |
| + |
| + entry.iov_base = data; |
| + entry.iov_len = cfg_payload_len; |
| + msg.msg_iov = &entry; |
| + msg.msg_iovlen = 1; |
| + msg.msg_name = NULL; |
| + msg.msg_namelen = 0; |
| + msg.msg_control = ctrl; |
| + msg.msg_controllen = sizeof(ctrl); |
| + |
| + ret = recvmsg(fd, &msg, MSG_ERRQUEUE); |
| + if (ret == -1 && errno != EAGAIN) |
| + error(1, errno, "recvmsg"); |
| + |
| + __recv_errmsg_cmsg(&msg, ret); |
| + |
| + free(data); |
| + return ret == -1; |
| +} |
| + |
| +static void do_test(int family, unsigned int opt) |
| +{ |
| + char *buf; |
| + int fd, i, val, total_len; |
| + |
| + if (family == IPPROTO_IPV6 && cfg_proto != SOCK_STREAM) { |
| + /* due to lack of checksum generation code */ |
| + fprintf(stderr, "test: skipping datagram over IPv6\n"); |
| + return; |
| + } |
| + |
| + total_len = cfg_payload_len; |
| + if (cfg_proto == SOCK_RAW) { |
| + total_len += sizeof(struct udphdr); |
| + if (cfg_ipproto == IPPROTO_RAW) |
| + total_len += sizeof(struct iphdr); |
| + } |
| + |
| + buf = malloc(total_len); |
| + if (!buf) |
| + error(1, 0, "malloc"); |
| + |
| + fd = socket(family, cfg_proto, cfg_ipproto); |
| + if (fd < 0) |
| + error(1, errno, "socket"); |
| + |
| + if (cfg_proto == SOCK_STREAM) { |
| + val = 1; |
| + if (setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, |
| + (char*) &val, sizeof(val))) |
| + error(1, 0, "setsockopt no nagle"); |
| + |
| + if (family == PF_INET) { |
| + if (connect(fd, (void *) &daddr, sizeof(daddr))) |
| + error(1, errno, "connect ipv4"); |
| + } else { |
| + if (connect(fd, (void *) &daddr6, sizeof(daddr6))) |
| + error(1, errno, "connect ipv6"); |
| + } |
| + } |
| + |
| + opt |= SOF_TIMESTAMPING_SOFTWARE | |
| + SOF_TIMESTAMPING_OPT_ID; |
| + if (setsockopt(fd, SOL_SOCKET, SO_TIMESTAMPING, |
| + (char *) &opt, sizeof(opt))) |
| + error(1, 0, "setsockopt timestamping"); |
| + |
| + for (i = 0; i < cfg_num_pkts; i++) { |
| + memset(&ts_prev, 0, sizeof(ts_prev)); |
| + memset(buf, 'a' + i, total_len); |
| + buf[total_len - 2] = '\n'; |
| + buf[total_len - 1] = '\0'; |
| + |
| + if (cfg_proto == SOCK_RAW) { |
| + struct udphdr *udph; |
| + int off = 0; |
| + |
| + if (cfg_ipproto == IPPROTO_RAW) { |
| + struct iphdr *iph = (void *) buf; |
| + |
| + memset(iph, 0, sizeof(*iph)); |
| + iph->ihl = 5; |
| + iph->version = 4; |
| + iph->ttl = 2; |
| + iph->daddr = daddr.sin_addr.s_addr; |
| + iph->protocol = IPPROTO_UDP; |
| + /* kernel writes saddr, csum, len */ |
| + |
| + off = sizeof(*iph); |
| + } |
| + |
| + udph = (void *) buf + off; |
| + udph->source = ntohs(9000); /* random spoof */ |
| + udph->dest = ntohs(dest_port); |
| + udph->len = ntohs(sizeof(*udph) + cfg_payload_len); |
| + udph->check = 0; /* not allowed for IPv6 */ |
| + } |
| + |
| + print_timestamp_usr(); |
| + if (cfg_proto != SOCK_STREAM) { |
| + if (family == PF_INET) |
| + val = sendto(fd, buf, total_len, 0, (void *) &daddr, sizeof(daddr)); |
| + else |
| + val = sendto(fd, buf, total_len, 0, (void *) &daddr6, sizeof(daddr6)); |
| + } else { |
| + val = send(fd, buf, cfg_payload_len, 0); |
| + } |
| + if (val != total_len) |
| + error(1, errno, "send"); |
| + |
| + /* wait for all errors to be queued, else ACKs arrive OOO */ |
| + usleep(50 * 1000); |
| + |
| + __poll(fd); |
| + |
| + while (!recv_errmsg(fd)) {} |
| + } |
| + |
| + if (close(fd)) |
| + error(1, errno, "close"); |
| + |
| + free(buf); |
| + usleep(400 * 1000); |
| +} |
| + |
| +static void __attribute__((noreturn)) usage(const char *filepath) |
| +{ |
| + fprintf(stderr, "\nUsage: %s [options] hostname\n" |
| + "\nwhere options are:\n" |
| + " -4: only IPv4\n" |
| + " -6: only IPv6\n" |
| + " -h: show this message\n" |
| + " -l N: send N bytes at a time\n" |
| + " -r: use raw\n" |
| + " -R: use raw (IP_HDRINCL)\n" |
| + " -p N: connect to port N\n" |
| + " -u: use udp\n", |
| + filepath); |
| + exit(1); |
| +} |
| + |
| +static void parse_opt(int argc, char **argv) |
| +{ |
| + int proto_count = 0; |
| + char c; |
| + |
| + while ((c = getopt(argc, argv, "46hl:p:rRu")) != -1) { |
| + switch (c) { |
| + case '4': |
| + do_ipv6 = 0; |
| + break; |
| + case '6': |
| + do_ipv4 = 0; |
| + break; |
| + case 'r': |
| + proto_count++; |
| + cfg_proto = SOCK_RAW; |
| + cfg_ipproto = IPPROTO_UDP; |
| + break; |
| + case 'R': |
| + proto_count++; |
| + cfg_proto = SOCK_RAW; |
| + cfg_ipproto = IPPROTO_RAW; |
| + break; |
| + case 'u': |
| + proto_count++; |
| + cfg_proto = SOCK_DGRAM; |
| + cfg_ipproto = IPPROTO_UDP; |
| + break; |
| + case 'l': |
| + cfg_payload_len = strtoul(optarg, NULL, 10); |
| + break; |
| + case 'p': |
| + dest_port = strtoul(optarg, NULL, 10); |
| + break; |
| + case 'h': |
| + default: |
| + usage(argv[0]); |
| + } |
| + } |
| + |
| + if (!cfg_payload_len) |
| + error(1, 0, "payload may not be nonzero"); |
| + if (cfg_proto != SOCK_STREAM && cfg_payload_len > 1472) |
| + error(1, 0, "udp packet might exceed expected MTU"); |
| + if (!do_ipv4 && !do_ipv6) |
| + error(1, 0, "pass -4 or -6, not both"); |
| + if (proto_count > 1) |
| + error(1, 0, "pass -r, -R or -u, not multiple"); |
| + |
| + if (optind != argc - 1) |
| + error(1, 0, "missing required hostname argument"); |
| +} |
| + |
| +static void resolve_hostname(const char *hostname) |
| +{ |
| + struct addrinfo *addrs, *cur; |
| + int have_ipv4 = 0, have_ipv6 = 0; |
| + |
| + if (getaddrinfo(hostname, NULL, NULL, &addrs)) |
| + error(1, errno, "getaddrinfo"); |
| + |
| + cur = addrs; |
| + while (cur && !have_ipv4 && !have_ipv6) { |
| + if (!have_ipv4 && cur->ai_family == AF_INET) { |
| + memcpy(&daddr, cur->ai_addr, sizeof(daddr)); |
| + daddr.sin_port = htons(dest_port); |
| + have_ipv4 = 1; |
| + } |
| + else if (!have_ipv6 && cur->ai_family == AF_INET6) { |
| + memcpy(&daddr6, cur->ai_addr, sizeof(daddr6)); |
| + daddr6.sin6_port = htons(dest_port); |
| + have_ipv6 = 1; |
| + } |
| + cur = cur->ai_next; |
| + } |
| + if (addrs) |
| + freeaddrinfo(addrs); |
| + |
| + do_ipv4 &= have_ipv4; |
| + do_ipv6 &= have_ipv6; |
| +} |
| + |
| +static void do_main(int family) |
| +{ |
| + fprintf(stderr, "family: %s\n", |
| + family == PF_INET ? "INET" : "INET6"); |
| + |
| + fprintf(stderr, "test SND\n"); |
| + do_test(family, SOF_TIMESTAMPING_TX_SOFTWARE); |
| + |
| + fprintf(stderr, "test ENQ\n"); |
| + do_test(family, SOF_TIMESTAMPING_TX_SCHED); |
| + |
| + fprintf(stderr, "test ENQ + SND\n"); |
| + do_test(family, SOF_TIMESTAMPING_TX_SCHED | |
| + SOF_TIMESTAMPING_TX_SOFTWARE); |
| + |
| + if (cfg_proto == SOCK_STREAM) { |
| + fprintf(stderr, "\ntest ACK\n"); |
| + do_test(family, SOF_TIMESTAMPING_TX_ACK); |
| + |
| + fprintf(stderr, "\ntest SND + ACK\n"); |
| + do_test(family, SOF_TIMESTAMPING_TX_SOFTWARE | |
| + SOF_TIMESTAMPING_TX_ACK); |
| + |
| + fprintf(stderr, "\ntest ENQ + SND + ACK\n"); |
| + do_test(family, SOF_TIMESTAMPING_TX_SCHED | |
| + SOF_TIMESTAMPING_TX_SOFTWARE | |
| + SOF_TIMESTAMPING_TX_ACK); |
| + } |
| +} |
| + |
| +const char *sock_names[] = { NULL, "TCP", "UDP", "RAW" }; |
| + |
| +int main(int argc, char **argv) |
| +{ |
| + if (argc == 1) |
| + usage(argv[0]); |
| + |
| + parse_opt(argc, argv); |
| + resolve_hostname(argv[argc - 1]); |
| + |
| + fprintf(stderr, "protocol: %s\n", sock_names[cfg_proto]); |
| + fprintf(stderr, "payload: %u\n", cfg_payload_len); |
| + fprintf(stderr, "server port: %u\n", dest_port); |
| + fprintf(stderr, "\n"); |
| + |
| + if (do_ipv4) |
| + do_main(PF_INET); |
| + if (do_ipv6) |
| + do_main(PF_INET6); |
| + |
| + return 0; |
| +} |