Commit 7255f628 authored by Alessandro Rubini's avatar Alessandro Rubini

tools/mtp: new tool-set, still to be cleaned up and documented

This is the verbatim copy of what I wrote as example code for an
article about time stamping. It's called "mini time protocol".  Some
of the code, in turn, comes from my previous experiments with White
Rabbit.

I'd better have the programs here as I use them often. As time permits
I'll add some more features and documentation in the ppsi manual.
Signed-off-by: Alessandro Rubini's avatarAlessandro Rubini <rubini@gnudd.com>
parent c7cc61d8
mtp_udp
mtp_packet
mtp_stamp
onestamp
CFLAGS = -Wall -O2 -ggdb
OBJ = mtp_udp.o mtp_packet.o onestamp.o mtp_stamp.o
PRG = $(OBJ:.o=)
LIB = libmtp.a
LOBJ = stamp-funcs.o report.o
LDFLAGS = -L. -lmtp
all: $(OBJ) $(PRG)
$(LIB): $(LOBJ)
ar r $@ $^
%: %.o $(LIB)
$(CC) $*.o $(LDFLAGS) -o $@
clean:
rm -f $(PRG) $(LIB) *.o *~
\ No newline at end of file
/* Common stuff for these misc tools */
#include <sys/types.h>
#ifndef SO_TIMESTAMPING
# define SO_TIMESTAMPING 37
# define SCM_TIMESTAMPING SO_TIMESTAMPING
#endif
#ifndef SIOCSHWTSTAMP
# define SIOCSHWTSTAMP 0x89b0
#endif
#ifndef ETH_P_1588
# define ETH_P_1588 0x88F7
#endif
extern int make_stamping_socket(FILE *errchan, char *argv0, char *ifname,
int tx_type, int rx_filter, int bits,
unsigned char *macaddr, int proto);
extern ssize_t send_and_stamp(int sock, void *buf, size_t len, int flags);
extern ssize_t recv_and_stamp(int sock, void *buf, size_t len, int flags);
extern int print_stamp(FILE *out, char *prefix, FILE *err /* may be NULL */);
extern int get_stamp(struct timespec ts[4]);
/* Functions to make the difference between timevals and timespecs */
static inline int tvdiff(struct timeval *tv2, struct timeval *tv1)
{
return (tv2->tv_sec - tv1->tv_sec) * 1000 * 1000
+ tv2->tv_usec - tv1->tv_usec;
}
static inline int tsdiff(struct timespec *tv2, struct timespec *tv1)
{
return (tv2->tv_sec - tv1->tv_sec) * 1000 * 1000 * 1000
+ tv2->tv_nsec - tv1->tv_nsec;
}
#include <stdint.h>
enum mtp_ptype {
MTP_FORWARD = 0xb8,
MTP_BACKWARD,
MTP_BACKSTAMP
};
struct mtp_packet {
uint32_t tragic;
uint32_t ptype;
struct timespec t[4][3];
};
static inline void tv_to_ts(struct timeval *tv, struct timespec *ts)
{
ts->tv_sec = tv->tv_sec;
ts->tv_nsec = tv->tv_usec * 1000;
}
extern void mtp_result(struct mtp_packet *pkt);
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <time.h>
#include <string.h>
#include <errno.h>
#include <netdb.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netpacket/packet.h>
#include <net/ethernet.h>
#include <net/if.h>
#include "mtp.h"
#define MTP_PROTO 0x6d74 /* 'm' 't' */
struct eth_packet {
struct ethhdr hdr;
struct mtp_packet mtp;
};
static void run_passive_host(int argc, char **argv, int sock,
unsigned char *ourmac)
{
int i;
socklen_t slen;
struct sockaddr_ll addr;
struct timeval tv1, tv2;
struct eth_packet pkt;
/* get forward packet and stamp it */
slen = sizeof(addr);
i = recvfrom(sock, &pkt, sizeof(pkt), MSG_TRUNC,
(struct sockaddr *)&addr, &slen);
gettimeofday(&tv1, NULL);
if (i < 0) {
fprintf(stderr, "%s: recvfrom(): %s\n", argv[0],
strerror(errno));
exit(1);
}
if (i < sizeof(pkt)) {
fprintf(stderr, "%s: short packet 1\n", argv[0]);
exit(1);
}
if (pkt.mtp.ptype != MTP_FORWARD) {
fprintf(stderr, "%s: wrong packet 1\n", argv[0]);
exit(1);
}
/* send backward packet -- swap macaddress */
memcpy(pkt.hdr.h_dest, pkt.hdr.h_source, ETH_ALEN);
memcpy(pkt.hdr.h_source, ourmac, ETH_ALEN);
pkt.mtp.ptype = MTP_BACKWARD;
gettimeofday(&tv2, NULL);
send(sock, &pkt, sizeof(pkt), 0);
/* send stamps */
tv_to_ts(&tv1, pkt.mtp.t[1]);
tv_to_ts(&tv2, pkt.mtp.t[2]);
pkt.mtp.ptype = MTP_BACKSTAMP;
send(sock, &pkt, sizeof(pkt), 0);
}
static int run_active_host(int argc, char **argv, int sock,
struct sockaddr_ll *addr, unsigned char *ourmac)
{
int i, tragic;
socklen_t slen;
struct timeval tv0, tv3;
struct eth_packet pkt;
char othermac[ETH_ALEN];
/* retrieve the remote mac*/
if (sscanf(argv[2], "%hhx:%hhx:%hhx:%hhx:%hhx:%hhx",
othermac+0, othermac+1, othermac+2,
othermac+3, othermac+4, othermac+5) != ETH_ALEN) {
fprintf(stderr, "%s: %s: can't parse macaddress\n",
argv[0], argv[2]);
exit(1);
}
/* stamp and send the first packet */
memset(&pkt, 0, sizeof(pkt));
memcpy(pkt.hdr.h_dest, othermac, ETH_ALEN);
memcpy(pkt.hdr.h_source, ourmac, ETH_ALEN);
pkt.hdr.h_proto = addr->sll_protocol;
pkt.mtp.ptype = MTP_FORWARD;
srand(time(NULL));
pkt.mtp.tragic = tragic = rand();
gettimeofday(&tv0, NULL);
send(sock, &pkt, sizeof(pkt), 0);
/* get the second packet -- and discard it */
slen = sizeof(*addr);
i = recvfrom(sock, &pkt, sizeof(pkt), MSG_TRUNC,
(struct sockaddr *)addr, &slen);
gettimeofday(&tv3, NULL);
if (i < 0) {
fprintf(stderr, "%s: recvfrom(): %s\n", argv[0],
strerror(errno));
exit(1);
}
if (i < sizeof(pkt)) {
fprintf(stderr, "%s: short packet 1\n", argv[0]);
exit(1);
}
if (pkt.mtp.ptype != MTP_BACKWARD || pkt.mtp.tragic != tragic) {
fprintf(stderr, "%s: wrong packet 1\n", argv[0]);
exit(1);
}
/* get the final packet */
slen = sizeof(*addr);
i = recvfrom(sock, &pkt, sizeof(pkt), MSG_TRUNC,
(struct sockaddr *)addr, &slen);
if (i < 0) {
fprintf(stderr, "%s: recvfrom(): %s\n", argv[0],
strerror(errno));
exit(1);
}
if (i < sizeof(pkt)) {
fprintf(stderr, "%s: short packet 2\n", argv[0]);
exit(1);
}
if (pkt.mtp.ptype != MTP_BACKSTAMP || pkt.mtp.tragic != tragic) {
fprintf(stderr, "%s: wrong packet 2\n", argv[0]);
exit(1);
}
/* add our stamp, we are using only the first value */
tv_to_ts(&tv0, pkt.mtp.t[0]);
tv_to_ts(&tv3, pkt.mtp.t[3]);
mtp_result(&pkt.mtp);
return 0;
}
int main(int argc, char **argv)
{
int sock, iindex, mtp_listen = 0;
struct sockaddr_ll addr;
struct ifreq ifr;
unsigned char ourmac[ETH_ALEN];
if (argc != 3) {
fprintf(stderr, "%s: Use: \"%s <eth> -l\" or "
"\"%s <eth> <macaddress>\"\n",
argv[0], argv[0], argv[0]);
exit(1);
}
if (!strcmp(argv[2], "-l")) {
mtp_listen = 1;
}
/* open file descriptor */
sock = socket(AF_PACKET, SOCK_RAW, ETH_P_ALL); //htons(MTP_PROTO));
if (sock < 0) {
fprintf(stderr, "%s: socket(): %s\n", argv[0],
strerror(errno));
exit(1);
}
/* get interface index */
memset (&ifr, 0, sizeof(ifr));
strcpy(ifr.ifr_name, argv[1]);
if (ioctl(sock, SIOCGIFINDEX, &ifr) < 0) {
fprintf(stderr, "%s: SIOCGIFINDEX(%s): %s\n", argv[0],
argv[1], strerror(errno));
exit(1);
}
iindex = ifr.ifr_ifindex;
/* get our mac address */
if (ioctl(sock, SIOCGIFHWADDR, &ifr) < 0) {
fprintf(stderr, "%s: SIOCGIFHWADDR(%s): %s\n", argv[0],
argv[1], strerror(errno));
exit(1);
}
memcpy(ourmac, ifr.ifr_hwaddr.sa_data, ETH_ALEN);
/* bind to a port, so we can get data */
memset(&addr, 0, sizeof(addr));
addr.sll_family = AF_PACKET;
addr.sll_protocol = htons(MTP_PROTO);
addr.sll_ifindex = iindex;
if (bind(sock, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
fprintf(stderr, "%s: bind(%s): %s\n", argv[0], argv[1],
strerror(errno));
exit(1);
}
if (!mtp_listen)
return run_active_host(argc, argv, sock, &addr, ourmac);
while (1)
run_passive_host(argc, argv, sock, ourmac);
}
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <time.h>
#include <string.h>
#include <errno.h>
#include <netdb.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netpacket/packet.h>
#include <net/ethernet.h>
#include <net/if.h>
#include "mtp.h"
#include "net_tstamp.h" /* copied from Linux headers */
#include "misc-common.h"
#define MTP_PROTO 0x7878 //0x6d74 /* 'm' 't' */
struct eth_packet {
struct ethhdr hdr;
struct mtp_packet mtp;
};
static void run_passive_host(int argc, char **argv, int sock,
unsigned char *ourmac)
{
int i;
socklen_t slen;
struct sockaddr_ll addr;
struct timespec ts1[4], ts2[4];
struct eth_packet pkt;
/* get forward packet and stamp it */
slen = sizeof(addr);
i = recv_and_stamp(sock, &pkt, sizeof(pkt), MSG_TRUNC);
if (i < 0) {
fprintf(stderr, "%s: recvfrom(): %s\n", argv[0],
strerror(errno));
exit(1);
}
if (i < sizeof(pkt)) {
fprintf(stderr, "%s: short packet 1\n", argv[0]);
exit(1);
}
if (pkt.mtp.ptype != MTP_FORWARD) {
fprintf(stderr, "%s: wrong packet 1\n", argv[0]);
exit(1);
}
get_stamp(ts1); /* the 4 stamps collected by recv_and_stamp() */
/* Wait a while, so our peer can get the tx-done interrupt */
usleep(100);
/* send backward packet -- swap macaddress */
memcpy(pkt.hdr.h_dest, pkt.hdr.h_source, ETH_ALEN);
memcpy(pkt.hdr.h_source, ourmac, ETH_ALEN);
pkt.mtp.ptype = MTP_BACKWARD;
send_and_stamp(sock, &pkt, sizeof(pkt), 0);
get_stamp(ts2); /* the 4 stamps collected by recv_and_stamp() */
/*
* fill the packet: the first ts is the same as the second
* (see onestamp output), so only copy the next three
*/
memcpy(pkt.mtp.t[1], ts1+1, sizeof(pkt.mtp.t[1]));
memcpy(pkt.mtp.t[2], ts2+1, sizeof(pkt.mtp.t[2]));
pkt.mtp.ptype = MTP_BACKSTAMP;
/* se don't use the stamp but the message queue must be emptied */
send_and_stamp(sock, &pkt, sizeof(pkt), 0);
}
static int run_active_host(int argc, char **argv, int sock,
unsigned char *ourmac)
{
int i, tragic;
struct timespec ts0[4], ts3[4];
struct eth_packet pkt;
char othermac[ETH_ALEN];
/* retrieve the remote mac*/
if (sscanf(argv[2], "%hhx:%hhx:%hhx:%hhx:%hhx:%hhx",
othermac+0, othermac+1, othermac+2,
othermac+3, othermac+4, othermac+5) != ETH_ALEN) {
fprintf(stderr, "%s: %s: can't parse macaddress\n",
argv[0], argv[2]);
exit(1);
}
/* stamp and send the first packet */
memset(&pkt, 0, sizeof(pkt));
memcpy(pkt.hdr.h_dest, othermac, ETH_ALEN);
memcpy(pkt.hdr.h_source, ourmac, ETH_ALEN);
pkt.hdr.h_proto = ntohs(MTP_PROTO);
pkt.mtp.ptype = MTP_FORWARD;
srand(time(NULL));
pkt.mtp.tragic = tragic = rand();
send_and_stamp(sock, &pkt, sizeof(pkt), 0);
get_stamp(ts0);
/* get the second packet -- and discard it */
i = recv_and_stamp(sock, &pkt, sizeof(pkt), MSG_TRUNC);
if (i < 0) {
fprintf(stderr, "%s: recvfrom(): %s\n", argv[0],
strerror(errno));
exit(1);
}
if (i < sizeof(pkt)) {
fprintf(stderr, "%s: short packet 1\n", argv[0]);
exit(1);
}
if (pkt.mtp.ptype != MTP_BACKWARD || pkt.mtp.tragic != tragic) {
fprintf(stderr, "%s: wrong packet 1\n", argv[0]);
exit(1);
}
get_stamp(ts3);
/* get the final packet */
i = recv(sock, &pkt, sizeof(pkt), MSG_TRUNC);
if (i < 0) {
fprintf(stderr, "%s: recvfrom(): %s\n", argv[0],
strerror(errno));
exit(1);
}
if (i < sizeof(pkt)) {
fprintf(stderr, "%s: short packet 2\n", argv[0]);
exit(1);
}
if (pkt.mtp.ptype != MTP_BACKSTAMP || pkt.mtp.tragic != tragic) {
fprintf(stderr, "%s: wrong packet 2\n", argv[0]);
exit(1);
}
/* add our stamp, we are using only the first value */
memcpy(pkt.mtp.t[0], ts0+1, sizeof(pkt.mtp.t[0]));
memcpy(pkt.mtp.t[3], ts3+1, sizeof(pkt.mtp.t[3]));
mtp_result(&pkt.mtp);
return 0;
}
int main(int argc, char **argv)
{
int sock, mtp_listen = 0;
unsigned char ourmac[ETH_ALEN];
if (argc != 3) {
fprintf(stderr, "%s: Use: \"%s <eth> -l\" or "
"\"%s <eth> <macaddress>\"\n",
argv[0], argv[0], argv[0]);
exit(1);
}
if (!strcmp(argv[2], "-l")) {
mtp_listen = 1;
}
/* open file descriptor */
sock = make_stamping_socket(stderr, argv[0], argv[1],
HWTSTAMP_TX_ON, HWTSTAMP_FILTER_ALL,
127 /* all bits for stamping */,
ourmac, MTP_PROTO);
if (sock < 0) /* message already printed */
exit(1);
if (!mtp_listen)
return run_active_host(argc, argv, sock, ourmac);
while (1)
run_passive_host(argc, argv, sock, ourmac);
}
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <time.h>
#include <string.h>
#include <errno.h>
#include <netdb.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include "mtp.h"
#define MTP_PORT 0x6d74 /* 'm' 't' */
static void run_passive_host(int argc, char **argv, int sock)
{
int i;
socklen_t slen;
struct sockaddr_in addr;
struct timeval tv1, tv2;
struct mtp_packet pkt;
/* get forward packet and stamp it */
slen = sizeof(addr);
i = recvfrom(sock, &pkt, sizeof(pkt), MSG_TRUNC,
(struct sockaddr *)&addr, &slen);
gettimeofday(&tv1, NULL);
if (i < 0) {
fprintf(stderr, "%s: recvfrom(): %s\n", argv[0],
strerror(errno));
exit(1);
}
if (i < sizeof(pkt)) {
fprintf(stderr, "%s: short packet 1\n", argv[0]);
exit(1);
}
if (pkt.ptype != MTP_FORWARD) {
fprintf(stderr, "%s: wrong packet 1\n", argv[0]);
exit(1);
}
/* send backward packet */
pkt.ptype = MTP_BACKWARD;
gettimeofday(&tv2, NULL);
sendto(sock, &pkt, sizeof(pkt), 0,
(struct sockaddr *)&addr, sizeof(addr));
/* send stamps */
tv_to_ts(&tv1, pkt.t[1]);
tv_to_ts(&tv2, pkt.t[2]);
pkt.ptype = MTP_BACKSTAMP;
sendto(sock, &pkt, sizeof(pkt), 0,
(struct sockaddr *)&addr, sizeof(addr));
}
static int run_active_host(int argc, char **argv, int sock,
struct sockaddr_in *addr)
{
int i, tragic;
socklen_t slen;
struct hostent *h;
struct timeval tv0, tv3;
struct mtp_packet pkt;
/* retrieve the remote host */
h = gethostbyname(argv[1]);
if (!h) {
fprintf(stderr, "%s: %s: can't resolve hostname\n",
argv[0], argv[1]);
exit(1);
}
addr->sin_addr.s_addr = *(uint32_t *)h->h_addr;
/* stamp and send the first packet */
memset(&pkt, 0, sizeof(pkt));
pkt.ptype = MTP_FORWARD;
srand(time(NULL));
pkt.tragic = tragic = rand();
gettimeofday(&tv0, NULL);
sendto(sock, &pkt, sizeof(pkt), 0,
(struct sockaddr *)addr, sizeof(*addr));
/* get the second packet -- stamp and discard it */
slen = sizeof(*addr);
i = recvfrom(sock, &pkt, sizeof(pkt), MSG_TRUNC,
(struct sockaddr *)addr, &slen);
gettimeofday(&tv3, NULL);
if (i < 0) {
fprintf(stderr, "%s: recvfrom(): %s\n", argv[0],
strerror(errno));
exit(1);
}
if (i < sizeof(pkt)) {
fprintf(stderr, "%s: short packet 1\n", argv[0]);
exit(1);
}
if (pkt.ptype != MTP_BACKWARD || pkt.tragic != tragic) {
fprintf(stderr, "%s: wrong packet 1\n", argv[0]);
exit(1);
}
/* get the final packet */
slen = sizeof(*addr);
i = recvfrom(sock, &pkt, sizeof(pkt), MSG_TRUNC,
(struct sockaddr *)addr, &slen);
if (i < 0) {
fprintf(stderr, "%s: recvfrom(): %s\n", argv[0],
strerror(errno));
exit(1);
}
if (i < sizeof(pkt)) {
fprintf(stderr, "%s: short packet 2\n", argv[0]);
exit(1);
}
if (pkt.ptype != MTP_BACKSTAMP || pkt.tragic != tragic) {
fprintf(stderr, "%s: wrong packet 2\n", argv[0]);
exit(1);
}
/* add our stamp, we are using only the first value */
tv_to_ts(&tv0, pkt.t[0]);
tv_to_ts(&tv3, pkt.t[3]);
mtp_result(&pkt);
return 0;
}
int main(int argc, char **argv)
{
int sock, mtp_listen = 0;
struct sockaddr_in addr;
if (argc != 2) {
fprintf(stderr, "%s: Use: \"%s -l\" or \"%s <host>\"\n",
argv[0], argv[0], argv[0]);
exit(1);
}
if (!strcmp(argv[1], "-l")) {
mtp_listen = 1;
}
/* open file descriptor */
sock = socket(AF_INET, SOCK_DGRAM, 0);
if (sock < 0) {
fprintf(stderr, "%s: socket(): %s\n", argv[0],
strerror(errno));
exit(1);
}
/* bind to a port, so we can get data */
memset(&addr, 0, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = INADDR_ANY;
addr.sin_port = htons(MTP_PORT);
if (bind(sock, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
fprintf(stderr, "%s: bind(): %s\n", argv[0],
strerror(errno));
exit(1);
}
if (!mtp_listen)
return run_active_host(argc, argv, sock, &addr);
while (1)
run_passive_host(argc, argv, sock);
}
/*
* Userspace API for hardware time stamping of network packets
*
* Copyright (C) 2008,2009 Intel Corporation
* Author: Patrick Ohly <patrick.ohly@intel.com>
*
*/
#ifndef _NET_TIMESTAMPING_H
#define _NET_TIMESTAMPING_H
#include <linux/socket.h> /* for SO_TIMESTAMPING */
/* SO_TIMESTAMPING gets an integer bit field comprised of these values */
enum {
SOF_TIMESTAMPING_TX_HARDWARE = (1<<0),
SOF_TIMESTAMPING_TX_SOFTWARE = (1<<1),
SOF_TIMESTAMPING_RX_HARDWARE = (1<<2),
SOF_TIMESTAMPING_RX_SOFTWARE = (1<<3),
SOF_TIMESTAMPING_SOFTWARE = (1<<4),
SOF_TIMESTAMPING_SYS_HARDWARE = (1<<5),
SOF_TIMESTAMPING_RAW_HARDWARE = (1<<6),
SOF_TIMESTAMPING_MASK =
(SOF_TIMESTAMPING_RAW_HARDWARE - 1) |
SOF_TIMESTAMPING_RAW_HARDWARE
};
/**
* struct hwtstamp_config - %SIOCSHWTSTAMP parameter
*
* @flags: no flags defined right now, must be zero
* @tx_type: one of HWTSTAMP_TX_*
* @rx_type: one of one of HWTSTAMP_FILTER_*
*
* %SIOCSHWTSTAMP expects a &struct ifreq with a ifr_data pointer to
* this structure. dev_ifsioc() in the kernel takes care of the
* translation between 32 bit userspace and 64 bit kernel. The
* structure is intentionally chosen so that it has the same layout on
* 32 and 64 bit systems, don't break this!
*/
struct hwtstamp_config {
int flags;
int tx_type;
int rx_filter;
};
/* possible values for hwtstamp_config->tx_type */
enum {
/*
* No outgoing packet will need hardware time stamping;
* should a packet arrive which asks for it, no hardware
* time stamping will be done.
*/
HWTSTAMP_TX_OFF,
/*
* Enables hardware time stamping for outgoing packets;
* the sender of the packet decides which are to be
* time stamped by setting %SOF_TIMESTAMPING_TX_SOFTWARE
* before sending the packet.
*/
HWTSTAMP_TX_ON,
};
/* possible values for hwtstamp_config->rx_filter */
enum {
/* time stamp no incoming packet at all */
HWTSTAMP_FILTER_NONE,
/* time stamp any incoming packet */
HWTSTAMP_FILTER_ALL,
/* return value: time stamp all packets requested plus some others */
HWTSTAMP_FILTER_SOME,
/* PTP v1, UDP, any kind of event packet */
HWTSTAMP_FILTER_PTP_V1_L4_EVENT,
/* PTP v1, UDP, Sync packet */
HWTSTAMP_FILTER_PTP_V1_L4_SYNC,
/* PTP v1, UDP, Delay_req packet */
HWTSTAMP_FILTER_PTP_V1_L4_DELAY_REQ,
/* PTP v2, UDP, any kind of event packet */
HWTSTAMP_FILTER_PTP_V2_L4_EVENT,
/* PTP v2, UDP, Sync packet */
HWTSTAMP_FILTER_PTP_V2_L4_SYNC,
/* PTP v2, UDP, Delay_req packet */
HWTSTAMP_FILTER_PTP_V2_L4_DELAY_REQ,
/* 802.AS1, Ethernet, any kind of event packet */
HWTSTAMP_FILTER_PTP_V2_L2_EVENT,
/* 802.AS1, Ethernet, Sync packet */
HWTSTAMP_FILTER_PTP_V2_L2_SYNC,
/* 802.AS1, Ethernet, Delay_req packet */
HWTSTAMP_FILTER_PTP_V2_L2_DELAY_REQ,
/* PTP v2/802.AS1, any layer, any kind of event packet */
HWTSTAMP_FILTER_PTP_V2_EVENT,
/* PTP v2/802.AS1, any layer, Sync packet */
HWTSTAMP_FILTER_PTP_V2_SYNC,
/* PTP v2/802.AS1, any layer, Delay_req packet */
HWTSTAMP_FILTER_PTP_V2_DELAY_REQ,
};
#endif /* _NET_TIMESTAMPING_H */
/*
* A simple tool to timestamp one tx and one rx packet
* Alessandro Rubini 2011, GPL2 or later
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <net/ethernet.h>
#include "net_tstamp.h"
#include "misc-common.h"
#define BSIZE 2048
char pkt[BSIZE];
int main(int argc, char **argv)
{
int sock;
unsigned char macaddr[6];
char *rest;
int howto;
if (argc != 3) {
fprintf(stderr, "%s: Use \"%s <ifname> <bitmask>\n", argv[0],
argv[0]);
exit(1);
}
howto = strtol(argv[2], &rest, 0);
if (rest && *rest) {
fprintf(stderr, "%s: \"%s\" is not a number\n", argv[0],
argv[2]);
exit(1);
}
/* From ./net_tstamp.h, these are the "howto" values
*
* SOF_TIMESTAMPING_TX_HARDWARE = 1,
* SOF_TIMESTAMPING_TX_SOFTWARE = 2
* SOF_TIMESTAMPING_RX_HARDWARE = 4,
* SOF_TIMESTAMPING_RX_SOFTWARE = 8,
* SOF_TIMESTAMPING_SOFTWARE = 16,
* SOF_TIMESTAMPING_SYS_HARDWARE = 32,
* SOF_TIMESTAMPING_RAW_HARDWARE = 64,
*/
printf("%s: Using interface %s, with %i as SO_TIMESTAMPING\n",
argv[0], argv[1], howto);
/* Create a socket to use for stamping, use the library code */
sock = make_stamping_socket(stderr, argv[0], argv[1],
HWTSTAMP_TX_ON, HWTSTAMP_FILTER_ALL,
howto, macaddr, ETH_P_ALL);
if (sock < 0) /* message already printed */
exit(1);
/* build a packet */
memcpy(pkt + 0, "\xff\xff\xff\xff\xff\xff", 6);
memcpy(pkt + 6, macaddr, 6);
memcpy(pkt + 12, "xx", 2);
if (send_and_stamp(sock, pkt, 64, 0) < 0) {
fprintf(stderr, "%s: send_and_stamp: %s\n", argv[0],
strerror(errno));
} else {
print_stamp(stdout, "tx", stderr);
}
if (recv_and_stamp(sock, pkt, sizeof(pkt), 0) < 0) {
fprintf(stderr, "%s: recv_and_stamp: %s\n", argv[0],
strerror(errno));
} else {
print_stamp(stdout, "rx", stderr);
}
exit(0);
}
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <time.h>
#include "mtp.h"
#define NSEC_PER_SEC (1000*1000*1000)
static signed long long tsdiff(struct timespec *new, struct timespec *old)
{
return (new->tv_sec - old->tv_sec) * (long long)NSEC_PER_SEC
+ new->tv_nsec - old->tv_nsec;
}
void mtp_result(struct mtp_packet *pkt)
{
int i;
long total, remote, rtt;
signed long long delta; /* may be more than 32 bits (4 seconds) */
/* Report information about the values that are not 0 */
for (i = 0; i < 3; i++) {
if (pkt->t[0][i].tv_sec == 0)
continue;
if (0) {
int j;
printf("Times at slot %i:\n", i);
for (j = 0; j < 4; j++) {
printf(" t%i: %li.%09li\n", i,
pkt->t[j][i].tv_sec,
pkt->t[j][i].tv_nsec);
}
}
total = tsdiff(pkt->t[3] + i, pkt->t[0] + i);
remote = tsdiff(pkt->t[2] + i, pkt->t[1] + i);
rtt = total - remote;
delta = tsdiff(pkt->t[1] + i, pkt->t[0] + i);
if (0) {
printf("total %li\n", total);
printf("remote %li\n", remote);
printf("rtt %li\n", rtt);
printf("bigdelta %lli\n", delta);
}
delta -= rtt / 2;
printf("%i: rtt %12.9lf delta %13.9lf\n",
i, (double)rtt / NSEC_PER_SEC,
(double)delta / NSEC_PER_SEC);
}
}
/*
* This file is a "library" file which offers functions with the same
* Interface as send() and recv() but that handle stamping as well.
* The timestamp informazion is saved to global variables, so no
* concurrency is allowed and so on. In short: it's as crappy as
* possible, but not crappier.
*
* Alessandro Rubini 2011, GPL2 or later
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <netpacket/packet.h>
#include <net/if.h>
#include <net/ethernet.h>
#include <arpa/inet.h>
#include "net_tstamp.h"
#include "misc-common.h"
/* This functions opens a socket and configures it for stamping */
int make_stamping_socket(FILE *errchan, char *argv0, char *ifname,
int tx_type, int rx_filter, int bits,
unsigned char *macaddr, int proto)
{
struct ifreq ifr;
struct sockaddr_ll addr;
struct hwtstamp_config hwconfig;
int sock, iindex, enable = 1;
sock = socket(PF_PACKET, SOCK_RAW, proto);
if (sock < 0 && errchan)
fprintf(errchan, "%s: socket(): %s\n", argv0,
strerror(errno));
if (sock < 0)
return sock;
memset (&ifr, 0, sizeof(ifr));
strcpy(ifr.ifr_name, ifname);
/* hw interface information */
if (ioctl(sock, SIOCGIFINDEX, &ifr) < 0) {
if (errchan)
fprintf(errchan, "%s: SIOCGIFINDEX(%s): %s\n", argv0,
ifname, strerror(errno));
close(sock);
return -1;
}
iindex = ifr.ifr_ifindex;
if (ioctl(sock, SIOCGIFHWADDR, &ifr) < 0) {
if (errchan)
fprintf(errchan, "%s: SIOCGIFHWADDR(%s): %s\n", argv0,
ifname, strerror(errno));
close(sock);
return -1;
}
memcpy(macaddr, ifr.ifr_hwaddr.sa_data, 6);
/* Also, enable stamping for the hw interface */
memset(&hwconfig, 0, sizeof(hwconfig));
hwconfig.tx_type = tx_type;
hwconfig.rx_filter = rx_filter;
ifr.ifr_data = (void *)&hwconfig;
if (ioctl(sock, SIOCSHWTSTAMP, &ifr) < 0) {
if (errchan)
fprintf(errchan, "%s: SIOCSHWSTAMP(%s): %s\n", argv0,
ifname, strerror(errno));
/* continue anyway, so any ether device can soft-stamp */
}
/* bind and setsockopt */
memset(&addr, 0, sizeof(addr));
addr.sll_family = AF_PACKET;
addr.sll_protocol = htons(proto);
addr.sll_ifindex = iindex;
if (bind(sock, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
if (errchan)
fprintf(errchan, "%s: SIOCSHWSTAMP(%s): %s\n", argv0,
ifname, strerror(errno));
close(sock);
return -1;
}
if (setsockopt(sock, SOL_SOCKET, SO_TIMESTAMPNS,
&enable, sizeof(enable)) < 0) {
if (errchan)
fprintf(errchan, "%s: setsockopt(TIMESTAMPNS): %s\n",
argv0, strerror(errno));
close(sock);
return -1;
}
if (setsockopt(sock, SOL_SOCKET, SO_TIMESTAMPING,
&bits, sizeof(bits)) < 0) {
if (errchan)
fprintf(errchan, "%s: setsockopt(TIMESTAMPING): %s\n",
argv0, strerror(errno));
close(sock);
return -1;
}
return sock;
}
/* This static data is used to collect stamping information */
static struct timespec __stamp_ns;
static struct timespec __stamp_hw_info[3];
static int __stamp_errno;
/* We can print such stamp info */
int print_stamp(FILE *out, char *prefix, FILE *err)
{
int i;
if (__stamp_errno) {
if (err)
fprintf(err, "get_timestamp: %s\n", strerror(errno));
errno = __stamp_errno;
return -1;
}
fprintf(out, "%s ns: %10li.%09li\n", prefix, __stamp_ns.tv_sec,
__stamp_ns.tv_nsec);
for (i = 0; i < 3; i++)
fprintf(out, "%s ns%i: %10li.%09li\n", prefix, i,
__stamp_hw_info[i].tv_sec,
__stamp_hw_info[i].tv_nsec);
fprintf(out, "\n");
return 0;
}
/* Or we can return it to the caller -- FIXME */
int get_stamp(struct timespec ts[4])
{
if (__stamp_errno) {
errno = __stamp_errno;
memset(ts, 0, 4 * sizeof(ts[0]));
return -1;
}
ts[0] = __stamp_ns;
memcpy(ts + 1, __stamp_hw_info, sizeof(__stamp_hw_info));
return 0;
}
/* This collecting of data is in common between send and recv */
static void __collect_data(struct msghdr *msgp)
{
struct cmsghdr *cmsg;
int i;
__stamp_errno = 0;
memset(&__stamp_ns, 0, sizeof(__stamp_ns));
memset(__stamp_hw_info, 0, sizeof(__stamp_hw_info));
/* Ok, got the tx stamp. Now collect stamp data */
for (cmsg = CMSG_FIRSTHDR(msgp); cmsg;
cmsg = CMSG_NXTHDR(msgp, cmsg)) {
struct timespec *stamp_ptr;
if (0) {
unsigned char *data;
printf("level %i, type %i, len %i\n", cmsg->cmsg_level,
cmsg->cmsg_type, cmsg->cmsg_len);
data = CMSG_DATA(cmsg);
for (i = 0; i < cmsg->cmsg_len; i++)
printf (" %02x", data[i]);
putchar('\n');
}
if (cmsg->cmsg_level != SOL_SOCKET)
continue;
stamp_ptr = (struct timespec *)CMSG_DATA(cmsg);
if (cmsg->cmsg_type == SO_TIMESTAMPNS)
__stamp_ns = *stamp_ptr;
if (cmsg->cmsg_type != SO_TIMESTAMPING)
continue;
for (i = 0; i < 3; i++, stamp_ptr++)
__stamp_hw_info[i] = *stamp_ptr;
}
}
/*
* These functions are like send/recv but handle stamping too.
*/
ssize_t send_and_stamp(int sock, void *buf, size_t len, int flags)
{
struct msghdr msg; /* this line and more from timestamping.c */
struct iovec entry;
struct sockaddr_ll from_addr;
struct {
struct cmsghdr cm;
char control[512];
} control;
char data[3*1024];
int i, j, ret;
ret = send(sock, buf, len, flags);
if (ret < 0)
return ret;
/* Then, get back from the error queue */
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);
j = 10; /* number of trials */
while ( (i = recvmsg(sock, &msg, MSG_ERRQUEUE | MSG_DONTWAIT)) < 0
&& j--)
usleep(1000); /* retry... */
if (i < 0) {
__stamp_errno = ETIMEDOUT;
memset(&__stamp_ns, 0, sizeof(__stamp_ns));
memset(__stamp_hw_info, 0, sizeof(__stamp_hw_info));
return ret;
}
if (getenv("STAMP_VERBOSE")) {
int b;
printf("send %i =", i);
for (b = 0; b < i && b < 20; b++)
printf(" %02x", data[b] & 0xff);
putchar('\n');
}
if (0) { /* print the data buffer */
int j;
printf("sent: here is the data back (%i bytes):\n", i);
for (j = 0; j < i; j++)
printf(" %02x", data[j] & 0xff);
putchar('\n');
}
__collect_data(&msg);
return ret;
}
ssize_t recv_and_stamp(int sock, void *buf, size_t len, int flags)
{
int ret;
struct msghdr msg;
struct iovec entry;
struct sockaddr_ll from_addr;
struct {
struct cmsghdr cm;
char control[512];
} control;
if (0) {
/* we can't really call recv, do it with cmsg alone */
ret = recv(sock, buf, len, flags);
} else {
memset(&msg, 0, sizeof(msg));
msg.msg_iov = &entry;
msg.msg_iovlen = 1;
entry.iov_base = buf;
entry.iov_len = len;
msg.msg_name = (caddr_t)&from_addr;
msg.msg_namelen = sizeof(from_addr);
msg.msg_control = &control;
msg.msg_controllen = sizeof(control);
ret = recvmsg(sock, &msg, 0);
if (ret < 0)
return ret;
if (getenv("STAMP_VERBOSE")) {
int b;
printf("recv %i =", ret);
for (b = 0; b < ret && b < 20; b++)
printf(" %02x", ((char *)buf)[b] & 0xff);
putchar('\n');
}
__collect_data(&msg);
}
return ret;
}
/*
* A simple tool to test packet transmission, with or without hw stamping
* Alessandro Rubini 2011, GPL2 or later
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <time.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <netpacket/packet.h>
#include <net/if.h>
#include <net/ethernet.h>
#include <arpa/inet.h>
#include "net_tstamp.h"
#include "misc-common.h"
#define BSIZE 2048 /* Packet buffer */
static int howto = 1;
static char pkt[2][BSIZE];
int main(int argc, char **argv)
{
int sock[2];
unsigned char macaddr[2][6];
int i, j, k, nloop=1000;
if (argc != 3) {
fprintf(stderr, "%s: Use: \"%s <ethname> <ethname>\"\n",
argv[0], argv[0]);
exit(1);
}
/* Extra arguments from environment */
if (getenv("STAMP_NLOOP"))
nloop = atoi(getenv("STAMP_NLOOP"));
if (getenv("STAMP_HOWTO"))
howto = atoi(getenv("STAMP_HOWTO"));
fprintf(stderr, "%s: working on %s and %s, %i loops, %02x stamp\n",
argv[0], argv[1], argv[2], nloop, howto);
/* Create two sockets */
sock[0] = make_stamping_socket(stderr, argv[0], argv[1],
HWTSTAMP_TX_ON, HWTSTAMP_FILTER_ALL,
howto, macaddr[0]);
if (sock[0] < 0)
exit(1);
sock[1] = make_stamping_socket(stderr, argv[0], argv[2],
HWTSTAMP_TX_ON, HWTSTAMP_FILTER_ALL,
howto, macaddr[1]);
if (sock[1] < 0)
exit(1);
/* Build the packet */
memcpy(pkt[0]+0, macaddr[1], 6);
memcpy(pkt[0]+6, macaddr[0], 6);
pkt[0][12] = ETH_P_1588 >> 8;
pkt[0][13] = ETH_P_1588 & 0xff;
for (i = 64; i < 1500; i += 64) {
/* there are 4 timespecs to consider */
unsigned long min[4], avg[4], max[4];
unsigned long long tot[4];
for (k = 0; k < 4; k++) {
max[k] = 0; avg[k] = 0; min[k] = ~0L;
}
for (j = 0; j < nloop; j++) {
long diff;
struct timespec ts0[4], ts1[4];
if (send_and_stamp(sock[0], pkt, i, 0) < 0) {
fprintf(stderr, "%s: send: %s\n", argv[0],
strerror(errno));
exit(1);
}
if (get_stamp(ts0) < 0) {
fprintf(stderr, "%s: getstamp(send): %s\n",
argv[0], strerror(errno));
continue;
}
if (recv_and_stamp(sock[1], pkt, sizeof(pkt), 0) < 0) {
fprintf(stderr, "%s: recv: %s\n", argv[0],
strerror(errno));
exit(1);
}
get_stamp(ts1);
for (k = 0; 0 && k < 4; k++) {
printf("%i: %10li.%09li %10li.%09li\n", k,
ts0[k].tv_sec, ts0[k].tv_nsec,
ts1[k].tv_sec, ts1[k].tv_nsec);
}
/* collect differences for all 4 values*/
for (k = 0; k < 4; k++) {
diff = tsdiff(ts1 + k, ts0 + k);
tot[k] += diff;
if (diff < min[k]) min[k] = diff;
if (diff > max[k]) max[k] = diff;
if (0)
printf("%li%c", diff,
k == 3 ? '\n' : ' ');
}
}
for (k = 0; k < 4; k++) {
avg[k] = tot[k] / nloop;
printf("size %4i ts %i: %7li %7li %7li\n", i, k,
min[k], avg[k], max[k]);
}
}
exit(0);
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment