From: Greg Kroah-Hartman Date: Fri, 14 Dec 2018 09:50:49 +0000 (+0100) Subject: 4.4-stable patches X-Git-Tag: v4.19.10~8 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=2ab409dd3f520968bf3d6188a6a7e24f81c44db5;p=thirdparty%2Fkernel%2Fstable-queue.git 4.4-stable patches added patches: rocker-fix-rocker_tlv_put_-functions-for-kasan.patch selftests-move-networking-timestamping-from-documentation.patch --- diff --git a/queue-4.4/rocker-fix-rocker_tlv_put_-functions-for-kasan.patch b/queue-4.4/rocker-fix-rocker_tlv_put_-functions-for-kasan.patch new file mode 100644 index 00000000000..0876865fb11 --- /dev/null +++ b/queue-4.4/rocker-fix-rocker_tlv_put_-functions-for-kasan.patch @@ -0,0 +1,88 @@ +From 6098d7ddd62f532f80ee2a4b01aca500a8e4e9e4 Mon Sep 17 00:00:00 2001 +From: Arnd Bergmann +Date: Fri, 22 Sep 2017 23:29:18 +0200 +Subject: rocker: fix rocker_tlv_put_* functions for KASAN + +From: Arnd Bergmann + +commit 6098d7ddd62f532f80ee2a4b01aca500a8e4e9e4 upstream. + +Inlining these functions creates lots of stack variables that each take +64 bytes when KASAN is enabled, leading to this warning about potential +stack overflow: + +drivers/net/ethernet/rocker/rocker_ofdpa.c: In function 'ofdpa_cmd_flow_tbl_add': +drivers/net/ethernet/rocker/rocker_ofdpa.c:621:1: error: the frame size of 2752 bytes is larger than 1536 bytes [-Werror=frame-larger-than=] + +gcc-8 can now consolidate the stack slots itself, but on older versions +we get the same behavior by using a temporary variable that holds a +copy of the inline function argument. + +Cc: stable@vger.kernel.org +Link: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=81715 +Signed-off-by: Arnd Bergmann +Signed-off-by: David S. Miller +Signed-off-by: Greg Kroah-Hartman + +--- + drivers/net/ethernet/rocker/rocker.c | 24 ++++++++++++++++++------ + 1 file changed, 18 insertions(+), 6 deletions(-) + +--- a/drivers/net/ethernet/rocker/rocker.c ++++ b/drivers/net/ethernet/rocker/rocker.c +@@ -821,37 +821,49 @@ static int rocker_tlv_put(struct rocker_ + static int rocker_tlv_put_u8(struct rocker_desc_info *desc_info, + int attrtype, u8 value) + { +- return rocker_tlv_put(desc_info, attrtype, sizeof(u8), &value); ++ u8 tmp = value; /* work around GCC PR81715 */ ++ ++ return rocker_tlv_put(desc_info, attrtype, sizeof(u8), &tmp); + } + + static int rocker_tlv_put_u16(struct rocker_desc_info *desc_info, + int attrtype, u16 value) + { +- return rocker_tlv_put(desc_info, attrtype, sizeof(u16), &value); ++ u16 tmp = value; ++ ++ return rocker_tlv_put(desc_info, attrtype, sizeof(u16), &tmp); + } + + static int rocker_tlv_put_be16(struct rocker_desc_info *desc_info, + int attrtype, __be16 value) + { +- return rocker_tlv_put(desc_info, attrtype, sizeof(__be16), &value); ++ __be16 tmp = value; ++ ++ return rocker_tlv_put(desc_info, attrtype, sizeof(__be16), &tmp); + } + + static int rocker_tlv_put_u32(struct rocker_desc_info *desc_info, + int attrtype, u32 value) + { +- return rocker_tlv_put(desc_info, attrtype, sizeof(u32), &value); ++ u32 tmp = value; ++ ++ return rocker_tlv_put(desc_info, attrtype, sizeof(u32), &tmp); + } + + static int rocker_tlv_put_be32(struct rocker_desc_info *desc_info, + int attrtype, __be32 value) + { +- return rocker_tlv_put(desc_info, attrtype, sizeof(__be32), &value); ++ __be32 tmp = value; ++ ++ return rocker_tlv_put(desc_info, attrtype, sizeof(__be32), &tmp); + } + + static int rocker_tlv_put_u64(struct rocker_desc_info *desc_info, + int attrtype, u64 value) + { +- return rocker_tlv_put(desc_info, attrtype, sizeof(u64), &value); ++ u64 tmp = value; ++ ++ return rocker_tlv_put(desc_info, attrtype, sizeof(u64), &tmp); + } + + static struct rocker_tlv * diff --git a/queue-4.4/selftests-move-networking-timestamping-from-documentation.patch b/queue-4.4/selftests-move-networking-timestamping-from-documentation.patch new file mode 100644 index 00000000000..8c5a516b341 --- /dev/null +++ b/queue-4.4/selftests-move-networking-timestamping-from-documentation.patch @@ -0,0 +1,2529 @@ +From 3d2c86e3057995270e08693231039d9d942871f0 Mon Sep 17 00:00:00 2001 +From: Shuah Khan +Date: Thu, 15 Sep 2016 08:36:07 -0600 +Subject: selftests: Move networking/timestamping from Documentation + +From: Shuah Khan + +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 +Signed-off-by: Shuah Khan +[ added to 4.4.y stable to remove a build warning - gregkh] +Signed-off-by: Greg Kroah-Hartman + +--- + 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 | 549 ---------- + 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 | 549 ++++++++++ + 12 files changed, 1223 insertions(+), 1231 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 +-#include +-#include +-#include +- +-#include +-#include +- +-#include +-#include +-#include +- +-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 +- * +- * 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 +-#include +-#include +-#include +- +-#include +-#include +-#include +-#include +-#include +-#include +- +-#include +-#include +-#include +- +-#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,549 +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. +- */ +- +-#define _GNU_SOURCE +- +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +-#include +- +-/* 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 bool cfg_show_payload; +-static bool cfg_do_pktinfo; +-static bool cfg_loop_nodata; +-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, " (%+" PRId64 " 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); +-} +- +-/* TODO: convert to check_and_print payload once API is stable */ +-static void print_payload(char *data, int len) +-{ +- int i; +- +- if (!len) +- return; +- +- if (len > 70) +- len = 70; +- +- fprintf(stderr, "payload: "); +- for (i = 0; i < len; i++) +- fprintf(stderr, "%02hhx ", data[i]); +- fprintf(stderr, "\n"); +-} +- +-static void print_pktinfo(int family, int ifindex, void *saddr, void *daddr) +-{ +- char sa[INET6_ADDRSTRLEN], da[INET6_ADDRSTRLEN]; +- +- fprintf(stderr, " pktinfo: ifindex=%u src=%s dst=%s\n", +- ifindex, +- saddr ? inet_ntop(family, saddr, sa, sizeof(sa)) : "unknown", +- daddr ? inet_ntop(family, daddr, da, sizeof(da)) : "unknown"); +-} +- +-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; +- int batch = 0; +- +- 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 if (cm->cmsg_level == SOL_IP && +- cm->cmsg_type == IP_PKTINFO) { +- struct in_pktinfo *info = (void *) CMSG_DATA(cm); +- print_pktinfo(AF_INET, info->ipi_ifindex, +- &info->ipi_spec_dst, &info->ipi_addr); +- } else if (cm->cmsg_level == SOL_IPV6 && +- cm->cmsg_type == IPV6_PKTINFO) { +- struct in6_pktinfo *info6 = (void *) CMSG_DATA(cm); +- print_pktinfo(AF_INET6, info6->ipi6_ifindex, +- NULL, &info6->ipi6_addr); +- } 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); +- serr = NULL; +- tss = NULL; +- batch++; +- } +- } +- +- if (batch > 1) +- fprintf(stderr, "batched %d timestamps\n", batch); +-} +- +-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"); +- +- if (ret >= 0) { +- __recv_errmsg_cmsg(&msg, ret); +- if (cfg_show_payload) +- print_payload(data, cfg_payload_len); +- } +- +- free(data); +- return ret == -1; +-} +- +-static void do_test(int family, unsigned int opt) +-{ +- char *buf; +- int fd, i, val = 1, total_len; +- +- if (family == AF_INET6 && 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) { +- 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"); +- } +- } +- +- if (cfg_do_pktinfo) { +- if (family == AF_INET6) { +- if (setsockopt(fd, SOL_IPV6, IPV6_RECVPKTINFO, +- &val, sizeof(val))) +- error(1, errno, "setsockopt pktinfo ipv6"); +- } else { +- if (setsockopt(fd, SOL_IP, IP_PKTINFO, +- &val, sizeof(val))) +- error(1, errno, "setsockopt pktinfo ipv4"); +- } +- } +- +- opt |= SOF_TIMESTAMPING_SOFTWARE | +- SOF_TIMESTAMPING_OPT_CMSG | +- SOF_TIMESTAMPING_OPT_ID; +- if (cfg_loop_nodata) +- opt |= SOF_TIMESTAMPING_OPT_TSONLY; +- +- 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); +- +- 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" +- " -I: request PKTINFO\n" +- " -l N: send N bytes at a time\n" +- " -n: set no-payload option\n" +- " -r: use raw\n" +- " -R: use raw (IP_HDRINCL)\n" +- " -p N: connect to port N\n" +- " -u: use udp\n" +- " -x: show payload (up to 70 bytes)\n", +- filepath); +- exit(1); +-} +- +-static void parse_opt(int argc, char **argv) +-{ +- int proto_count = 0; +- char c; +- +- while ((c = getopt(argc, argv, "46hIl:np:rRux")) != -1) { +- switch (c) { +- case '4': +- do_ipv6 = 0; +- break; +- case '6': +- do_ipv4 = 0; +- break; +- case 'I': +- cfg_do_pktinfo = true; +- break; +- case 'n': +- cfg_loop_nodata = true; +- 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 'x': +- cfg_show_payload = true; +- 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 ++#include ++#include ++#include ++ ++#include ++#include ++ ++#include ++#include ++#include ++ ++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 ++ * ++ * 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 ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++ ++#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,549 @@ ++/* ++ * 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. ++ */ ++ ++#define _GNU_SOURCE ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++/* 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 bool cfg_show_payload; ++static bool cfg_do_pktinfo; ++static bool cfg_loop_nodata; ++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, " (%+" PRId64 " 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); ++} ++ ++/* TODO: convert to check_and_print payload once API is stable */ ++static void print_payload(char *data, int len) ++{ ++ int i; ++ ++ if (!len) ++ return; ++ ++ if (len > 70) ++ len = 70; ++ ++ fprintf(stderr, "payload: "); ++ for (i = 0; i < len; i++) ++ fprintf(stderr, "%02hhx ", data[i]); ++ fprintf(stderr, "\n"); ++} ++ ++static void print_pktinfo(int family, int ifindex, void *saddr, void *daddr) ++{ ++ char sa[INET6_ADDRSTRLEN], da[INET6_ADDRSTRLEN]; ++ ++ fprintf(stderr, " pktinfo: ifindex=%u src=%s dst=%s\n", ++ ifindex, ++ saddr ? inet_ntop(family, saddr, sa, sizeof(sa)) : "unknown", ++ daddr ? inet_ntop(family, daddr, da, sizeof(da)) : "unknown"); ++} ++ ++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; ++ int batch = 0; ++ ++ 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 if (cm->cmsg_level == SOL_IP && ++ cm->cmsg_type == IP_PKTINFO) { ++ struct in_pktinfo *info = (void *) CMSG_DATA(cm); ++ print_pktinfo(AF_INET, info->ipi_ifindex, ++ &info->ipi_spec_dst, &info->ipi_addr); ++ } else if (cm->cmsg_level == SOL_IPV6 && ++ cm->cmsg_type == IPV6_PKTINFO) { ++ struct in6_pktinfo *info6 = (void *) CMSG_DATA(cm); ++ print_pktinfo(AF_INET6, info6->ipi6_ifindex, ++ NULL, &info6->ipi6_addr); ++ } 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); ++ serr = NULL; ++ tss = NULL; ++ batch++; ++ } ++ } ++ ++ if (batch > 1) ++ fprintf(stderr, "batched %d timestamps\n", batch); ++} ++ ++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"); ++ ++ if (ret >= 0) { ++ __recv_errmsg_cmsg(&msg, ret); ++ if (cfg_show_payload) ++ print_payload(data, cfg_payload_len); ++ } ++ ++ free(data); ++ return ret == -1; ++} ++ ++static void do_test(int family, unsigned int opt) ++{ ++ char *buf; ++ int fd, i, val = 1, total_len; ++ ++ if (family == AF_INET6 && 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) { ++ 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"); ++ } ++ } ++ ++ if (cfg_do_pktinfo) { ++ if (family == AF_INET6) { ++ if (setsockopt(fd, SOL_IPV6, IPV6_RECVPKTINFO, ++ &val, sizeof(val))) ++ error(1, errno, "setsockopt pktinfo ipv6"); ++ } else { ++ if (setsockopt(fd, SOL_IP, IP_PKTINFO, ++ &val, sizeof(val))) ++ error(1, errno, "setsockopt pktinfo ipv4"); ++ } ++ } ++ ++ opt |= SOF_TIMESTAMPING_SOFTWARE | ++ SOF_TIMESTAMPING_OPT_CMSG | ++ SOF_TIMESTAMPING_OPT_ID; ++ if (cfg_loop_nodata) ++ opt |= SOF_TIMESTAMPING_OPT_TSONLY; ++ ++ 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); ++ ++ 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" ++ " -I: request PKTINFO\n" ++ " -l N: send N bytes at a time\n" ++ " -n: set no-payload option\n" ++ " -r: use raw\n" ++ " -R: use raw (IP_HDRINCL)\n" ++ " -p N: connect to port N\n" ++ " -u: use udp\n" ++ " -x: show payload (up to 70 bytes)\n", ++ filepath); ++ exit(1); ++} ++ ++static void parse_opt(int argc, char **argv) ++{ ++ int proto_count = 0; ++ char c; ++ ++ while ((c = getopt(argc, argv, "46hIl:np:rRux")) != -1) { ++ switch (c) { ++ case '4': ++ do_ipv6 = 0; ++ break; ++ case '6': ++ do_ipv4 = 0; ++ break; ++ case 'I': ++ cfg_do_pktinfo = true; ++ break; ++ case 'n': ++ cfg_loop_nodata = true; ++ 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 'x': ++ cfg_show_payload = true; ++ 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; ++} diff --git a/queue-4.4/series b/queue-4.4/series index d0beb496b8a..b82557a6ab1 100644 --- a/queue-4.4/series +++ b/queue-4.4/series @@ -84,3 +84,5 @@ proc-do-not-access-cmdline-nor-environ-from-file-backed-areas.patch media-dvb-frontends-fix-i2c-access-helpers-for-kasan.patch matroxfb-fix-size-of-memcpy.patch staging-speakup-replace-strncpy-with-memcpy.patch +rocker-fix-rocker_tlv_put_-functions-for-kasan.patch +selftests-move-networking-timestamping-from-documentation.patch