blob: e363ef074579cffb8627aeb441fe78c655781ab4 [file] [log] [blame]
/** @file timestamp.c
*
* @brief Functions for timestamping feature
*
* Copyright (C) 2011-2017, Marvell International Ltd.
*
* This software file (the "File") is distributed by Marvell International
* Ltd. under the terms of the GNU General Public License Version 2, June 1991
* (the "License"). You may use, redistribute and/or modify this File in
* accordance with the terms and conditions of the License, a copy of which
* is available by writing to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the
* worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt.
*
* THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE
* IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE
* ARE EXPRESSLY DISCLAIMED. The License provides additional details about
* this warranty disclaimer.
*
*/
#include "timestamp.h"
#include "time.h"
/* GLobal Declarations */
struct timespec send_time;
struct interface_data inter;
/**
*@brief Receive Timestamps
*
*@param argc Number of arguments
*@param argv Pointer to the arguments array
*
* @return MLAN_STATUS_SUCCESS/MLAN_STATUS_FAILURE
**/
void
receive_timestamp(int argc, char *argv[])
{
int sockfd;
int sockopt;
char ifName[IFNAMSIZ];
struct ifreq if_ip; /* get ip addr */
int so_timestamping_flags = 0;
int siocgstamp = 0;
int siocgstampns = 0;
struct timeval now;
int res;
struct ifreq if_idx;
struct ifreq if_mac;
fd_set readfs, errorfs;
/* Get interface name */
if (argc > 2)
strcpy(ifName, argv[1]);
else {
fprintf(stderr,
"invalid no. of arguments to receive_timestamp \n");
exit(1);
}
/* Header structures */
so_timestamping_flags |= SOF_TIMESTAMPING_SOFTWARE;
so_timestamping_flags |= SOF_TIMESTAMPING_RAW_HARDWARE;
memset(&if_ip, 0, sizeof(struct ifreq));
/* Open PF_PACKET socket, listening for EtherType ETHER_TYPE */
if ((sockfd = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_802_EX1))) == -1) {
perror("listener: socket");
}
/* Get the index of the interface to receive on */
memset(&if_idx, 0, sizeof(struct ifreq));
strncpy(if_idx.ifr_name, ifName, IFNAMSIZ - 1);
if (ioctl(sockfd, SIOCGIFINDEX, &if_idx) < 0)
perror("SIOCGIFINDEX");
/* Get the MAC address of the interface to receive on */
memset(&if_mac, 0, sizeof(struct ifreq));
strncpy(if_mac.ifr_name, ifName, IFNAMSIZ - 1);
if (ioctl(sockfd, SIOCGIFHWADDR, &if_mac) < 0)
perror("SIOCGIFHWADDR");
/* Allow the socket to be reused - incase connection is closed prematurely */
if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &sockopt,
sizeof sockopt) == -1) {
perror("setsockopt");
close(sockfd);
exit(1);
}
/* Bind to device */
if (setsockopt(sockfd, SOL_SOCKET, SO_BINDTODEVICE,
ifName, IFNAMSIZ - 1) == -1) {
perror("SO_BINDTODEVICE");
close(sockfd);
exit(1);
}
if (so_timestamping_flags &&
setsockopt(sockfd, SOL_SOCKET, SO_TIMESTAMPING,
&so_timestamping_flags,
sizeof(so_timestamping_flags)) < 0)
perror("setsockopt SO_TIMESTAMPING");
while (1) {
FD_ZERO(&readfs);
FD_ZERO(&errorfs);
FD_SET(sockfd, &readfs);
FD_SET(sockfd, &errorfs);
gettimeofday(&now, NULL);
res = select(sockfd + 1, &readfs, 0, &errorfs, NULL);
if (res > 0) {
recvpacket(sockfd, 0, siocgstamp, siocgstampns);
}
}
}
/**
*@brief Send Timestamps
*
*@param argc Number of arguments
*@param argv Pointer to the arguments array
*
*@return MLAN_STATUS_SUCCESS/MLAN_STATUS_FAILURE
**/
int
send_timestamp(int argc, char *argv[])
{
int sockfd;
struct ifreq if_idx;
struct ifreq if_mac;
int tx_len = 0, i;
char sendbuf[BUF_SIZ];
char buff[BUF_SIZ];
struct ether_header *eh = (struct ether_header *)sendbuf;
struct sockaddr_ll socket_address;
char ifName[IFNAMSIZ], ip[50];
struct timeval delta;
fd_set readfs, errorfs;
int res, siocgstamp = 1, siocgstampns = 1;
int so_timestamping_flags = SOF_TIMESTAMPING_TX_HARDWARE;
struct ifreq hwtstamp;
struct hwtstamp_config hwconfig;
so_timestamping_flags |= SOF_TIMESTAMPING_RAW_HARDWARE;
so_timestamping_flags |= SOF_TIMESTAMPING_TX_HARDWARE;
so_timestamping_flags |= SOF_TIMESTAMPING_SYS_HARDWARE;
/* Get interface name */
if (argc > 4) {
strcpy(ifName, argv[1]);
strcpy(ip, argv[4]);
} else {
fprintf(stderr, "invalid no. of args for send_timestamp\n");
exit(1);
}
/* Open RAW socket to send on */
if ((sockfd = socket(PF_PACKET, SOCK_RAW, ETH_P_802_EX1)) == -1) {
perror("socket");
}
memset(&hwtstamp, 0, sizeof(hwtstamp));
strncpy(hwtstamp.ifr_name, ifName, 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;
/* Get the index of the interface to send on */
memset(&if_idx, 0, sizeof(struct ifreq));
strncpy(if_idx.ifr_name, ifName, IFNAMSIZ - 1);
if (ioctl(sockfd, SIOCGIFINDEX, &if_idx) < 0)
perror("SIOCGIFINDEX");
/* Get the MAC address of the interface to send on */
memset(&if_mac, 0, sizeof(struct ifreq));
strncpy(if_mac.ifr_name, ifName, IFNAMSIZ - 1);
if (ioctl(sockfd, SIOCGIFHWADDR, &if_mac) < 0)
perror("SIOCGIFHWADDR");
if (so_timestamping_flags &&
setsockopt(sockfd, SOL_SOCKET, SO_TIMESTAMPING,
&so_timestamping_flags,
sizeof(so_timestamping_flags)) < 0)
perror("setsockopt SO_TIMESTAMPING");
if (setsockopt(sockfd, SOL_SOCKET, SO_BINDTODEVICE,
ifName, IFNAMSIZ - 1) == -1) {
perror("bind");
exit(1);
}
/* Construct the Ethernet header */
memset(sendbuf, 0, BUF_SIZ);
/* Ethernet header */
memcpy(eh->ether_shost, (u_int8_t *) & if_mac.ifr_hwaddr.sa_data,
MLAN_MAC_ADDR_LENGTH);
eh->ether_type = htons(ETH_P_802_EX1);
tx_len += sizeof(struct ether_header);
get_mac(ifName, ip);
for (i = 0; i < MLAN_MAC_ADDR_LENGTH; i++) {
eh->ether_dhost[i] = (uint8_t) inter.mac[i];
}
/* Index of the network device */
socket_address.sll_ifindex = if_idx.ifr_ifindex;
/* Address length */
socket_address.sll_halen = ETH_ALEN;
memcpy(&socket_address.sll_addr, (uint8_t *) & inter.mac,
MLAN_MAC_ADDR_LENGTH);
clock_gettime(CLOCK_REALTIME, &send_time);
sprintf(buff, "%lld.%lld", (long long)send_time.tv_sec,
(long long)send_time.tv_nsec);
strcpy((sendbuf + tx_len), buff);
/* Send packet */
res = sendto(sockfd, sendbuf, tx_len + strlen(buff), 0,
(struct sockaddr *)&socket_address,
sizeof(struct sockaddr_ll));
if (res < 0)
perror("Send ");
fprintf(stdout, "Application time : %lld.%09lld (sent)\n",
(long long)send_time.tv_sec, (long long)send_time.tv_nsec);
delta.tv_sec = 5;
delta.tv_usec = 0;
FD_ZERO(&readfs);
FD_ZERO(&errorfs);
FD_SET(sockfd, &readfs);
FD_SET(sockfd, &errorfs);
res = select(sockfd + 1, &readfs, 0, &errorfs, &delta);
if (res > 0) {
recvpacket(sockfd, MSG_ERRQUEUE, siocgstamp, siocgstampns);
}
return MLAN_STATUS_SUCCESS;
}
/**
*@brief get destination mac address
*
*@param ifc interface from which packet has to be sent
*@param ip IP Address of destination
*
*@return N/A
**/
void
get_mac(char *ifc, char *ip)
{
char ipAddr[20];
char hwAddr[20];
char device[10], temp[3], in[50];
int i = 0, j = 0, k = 0, res = 0, retry = 0;
FILE *arpCache = fopen("/proc/net/arp", "r");
if (!arpCache) {
fprintf(stderr,
"Arp Cache: Failed to open file \"/proc/net/arp\"");
}
/* Ignore the first line, which contains the header */
char header[ARP_FILE_BUFFER_LEN];
retry_again:
if (!fgets(header, sizeof(header), arpCache))
fprintf(stderr, "error getting mac from proc files");
while (3 == fscanf(arpCache, ARP_FORMAT, ipAddr, hwAddr, device)) {
if ((!strcmp(ipAddr, ip)) && (!strcmp(ifc, device))) {
printf("Sending Packet to Peer : %s\n", hwAddr);
strcpy(inter.ip, ipAddr);
strcpy(inter.interface, device);
while (hwAddr[i] != '\0') {
if (hwAddr[i] == ':') {
inter.mac[j++] = strtol(temp, NULL, 16);
i++;
k = 0;
} else
temp[k++] = hwAddr[i++];
}
inter.mac[j] = strtol(temp, NULL, 16);
res = 1;
}
}
if (res != 1 && retry == 0) {
sprintf(in, "ping -c 2 %s > /dev/null", ip);
system(in);
retry = 1;
rewind(arpCache);
goto retry_again;
} else if (res != 1 && retry == 1) {
printf("cannot find mac address for the specified ip\n");
fclose(arpCache);
exit(1);
}
fclose(arpCache);
}
/*
*@brief Receive Sync Packets
*
*@param sock socket from which packet must be recieved
*@param recvmsg_flags flags for recvmsg
*@param siocgstamp timestamp flag
*@param siocgstampns timestamp flag for nano secs
*
*@return N/A
**/
void
recvpacket(int sock, int recvmsg_flags, int siocgstamp, int siocgstampns)
{
unsigned char data[256];
struct msghdr msg;
struct iovec entry;
struct sockaddr_in from_addr;
struct {
struct cmsghdr cm;
char control[512];
} control;
int res, i;
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) {
fprintf(stderr, "%s %s: %s\n",
"recvmsg",
(recvmsg_flags & MSG_ERRQUEUE) ? "error" : "regular",
strerror(errno));
} else {
if (!(recvmsg_flags & MSG_ERRQUEUE)) {
printf("Received Packet from Peer : ");
for (i = 6; i < 12; i++)
printf("%02x:", data[i]);
printf("\n");
}
printpacket(&msg, res, sock, recvmsg_flags, siocgstamp,
siocgstampns);
}
}
/**
* @brief Prints Sent/Received Sync packets
*
* @param msg msghdr structure variable
* @param res result of recvmsg call
* @param sock socket variable
* @param recvmsg_flags flags for receive message
* @param siocgstamp timestamp flag
* @param siocgstampns timestamp flag for nano secs
*
* @return N/A
**/
void
printpacket(struct msghdr *msg, int res,
int sock, int recvmsg_flags, int siocgstamp, int siocgstampns)
{
struct cmsghdr *cmsg;
struct timespec now;
struct timespec *stamp;
clock_gettime(CLOCK_REALTIME, &now);
if (!(recvmsg_flags & MSG_ERRQUEUE)) {
printf("Application time : %ld.%09ld (received)\n",
(long)now.tv_sec, (long)now.tv_nsec);
}
for (cmsg = CMSG_FIRSTHDR(msg); cmsg; cmsg = CMSG_NXTHDR(msg, cmsg)) {
if (cmsg->cmsg_level == SOL_SOCKET &&
cmsg->cmsg_type == SO_TIMESTAMPING) {
stamp = (struct timespec *)CMSG_DATA(cmsg);
stamp++;
/* skip deprecated HW transformed */
stamp++;
fprintf(stdout, "HW time : %ld.%09ld\n",
(long)stamp->tv_sec, (long)stamp->tv_nsec);
if (!(recvmsg_flags & MSG_ERRQUEUE))
fprintf(stdout, "Delta in nsecs= %lld\n",
((long long)(now.tv_sec -
stamp->tv_sec) *
1000000000L + now.tv_nsec) -
(stamp->tv_nsec));
else
fprintf(stdout, "Delta in nsecs= %lld",
((long long)(stamp->tv_sec -
send_time.tv_sec) *
1000000000L + (stamp->tv_nsec) -
send_time.tv_nsec));
}
}
fprintf(stdout, "\n");
}