--- /dev/null
+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;
++}