#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <stdbool.h>
#include <signal.h>
#include <getopt.h>
#include <time.h>
#include "ptp_parse.h"
#define RX_RING_SIZE 1024
#define TX_RING_SIZE 1024
#define NUM_MBUFS 8191
#define MBUF_CACHE 250
#define BURST_SIZE 32
#define NSEC_PER_SEC 1000000000ULL
#define LOG_INFO(fmt, ...) \
fprintf(stdout, "[PTP-SW] " fmt "\n", ##__VA_ARGS__)
#define LOG_ERR(fmt, ...) \
fprintf(stderr, "[PTP-SW ERROR] " fmt "\n", ##__VA_ARGS__)
static volatile bool force_quit;
static uint16_t phy_port;
static uint16_t tap_port = 1;
static unsigned int stats_interval = 10;
static struct {
uint64_t phy_rx;
uint64_t phy_rx_ptp;
uint64_t tap_tx;
uint64_t tap_rx;
uint64_t tap_rx_ptp;
uint64_t phy_tx;
uint64_t corrections;
} stats;
static void
signal_handler(int signum)
{
if (signum == SIGINT || signum == SIGTERM) {
LOG_INFO("Signal %d received, shutting down...", signum);
force_quit = true;
}
}
static inline uint64_t
sw_timestamp_ns(void)
{
struct timespec ts;
clock_gettime(CLOCK_MONOTONIC, &ts);
return (uint64_t)ts.tv_sec * NSEC_PER_SEC + (uint64_t)ts.tv_nsec;
}
static int
{
uint16_t nb_rxd = RX_RING_SIZE;
uint16_t nb_txd = TX_RING_SIZE;
int ret;
memset(&port_conf, 0, sizeof(port_conf));
if (ret != 0) {
LOG_ERR("rte_eth_dev_info_get(port %u) failed: %d", port, ret);
return ret;
}
port_conf.txmode.offloads |=
if (ret != 0)
return ret;
if (ret != 0)
return ret;
if (ret < 0)
return ret;
if (ret < 0)
return ret;
if (ret < 0)
return ret;
if (ret != 0) {
LOG_ERR("Failed to enable promiscuous on port %u: %s",
return ret;
}
return 0;
}
static void
relay_burst(uint16_t src_port, uint16_t dst_port,
uint64_t *rx_cnt, uint64_t *rx_ptp_cnt,
uint64_t *tx_cnt, uint64_t *corr_cnt)
{
uint64_t rx_ts;
uint16_t nb_rx, nb_tx, i;
if (nb_rx == 0)
return;
rx_ts = sw_timestamp_ns();
*rx_cnt += nb_rx;
memset(ptp_hdrs, 0, sizeof(ptp_hdrs[0]) * nb_rx);
for (i = 0; i < nb_rx; i++) {
if (hdr == NULL)
continue;
(*rx_ptp_cnt)++;
continue;
ptp_hdrs[i] = hdr;
}
uint64_t tx_ts = sw_timestamp_ns();
int64_t residence_ns = (int64_t)(tx_ts - rx_ts);
for (i = 0; i < nb_rx; i++) {
if (ptp_hdrs[i] == NULL)
continue;
(*corr_cnt)++;
}
*tx_cnt += nb_tx;
for (i = nb_tx; i < nb_rx; i++)
}
static void
print_stats(void)
{
LOG_INFO("=== Statistics ===");
LOG_INFO(" PHY RX total: %"PRIu64, stats.phy_rx);
LOG_INFO(" PHY RX PTP: %"PRIu64, stats.phy_rx_ptp);
LOG_INFO(" TAP TX: %"PRIu64, stats.tap_tx);
LOG_INFO(" TAP RX total: %"PRIu64, stats.tap_rx);
LOG_INFO(" TAP RX PTP: %"PRIu64, stats.tap_rx_ptp);
LOG_INFO(" PHY TX: %"PRIu64, stats.phy_tx);
LOG_INFO(" Corrections: %"PRIu64, stats.corrections);
}
static int
{
uint64_t last_stats = rte_rdtsc();
LOG_INFO(" PHY port %u <--> TAP port %u", phy_port, tap_port);
LOG_INFO(" Correction field updates: enabled for event messages");
while (!force_quit) {
relay_burst(phy_port, tap_port,
&stats.phy_rx, &stats.phy_rx_ptp,
&stats.tap_tx, &stats.corrections);
relay_burst(tap_port, phy_port,
&stats.tap_rx, &stats.tap_rx_ptp,
&stats.phy_tx, &stats.corrections);
if (rte_rdtsc() - last_stats > stats_tsc) {
print_stats();
last_stats = rte_rdtsc();
}
}
print_stats();
return 0;
}
static void
usage(const char *prog)
{
fprintf(stderr,
"Usage: %s [EAL options] -- [options]\n"
" -p PORT Physical NIC port ID (default: 0)\n"
" -t PORT TAP port ID (default: 1)\n"
" -T SECS Stats interval in seconds (default: 10)\n"
"\n"
"Example:\n"
" %s -l 0-1 --vdev=net_tap0,iface=dtap0 -- -p 0 -t 1\n"
"\n"
"Then run ptp4l with software timestamps:\n"
" ptp4l -i dtap0 -m -s -S\n",
prog, prog);
}
static int
parse_args(int argc, char **argv)
{
int opt;
while ((opt = getopt(argc, argv, "p:t:T:h")) != -1) {
switch (opt) {
case 'p':
phy_port = (uint16_t)atoi(optarg);
break;
case 't':
tap_port = (uint16_t)atoi(optarg);
break;
case 'T':
stats_interval = (unsigned int)atoi(optarg);
break;
case 'h':
default:
usage(argv[0]);
return -1;
}
}
return 0;
}
int
main(int argc, char **argv)
{
uint16_t nb_ports;
int ret;
if (ret < 0)
rte_exit(EXIT_FAILURE,
"EAL init failed\n");
argc -= ret;
argv += ret;
ret = parse_args(argc, argv);
if (ret < 0)
rte_exit(EXIT_FAILURE,
"Invalid arguments\n");
signal(SIGINT, signal_handler);
signal(SIGTERM, signal_handler);
if (nb_ports < 2)
"Need at least 2 ports (PHY + TAP).\n"
"Use --vdev=net_tap0,iface=dtap0\n");
rte_exit(EXIT_FAILURE,
"Invalid PHY port %u\n", phy_port);
rte_exit(EXIT_FAILURE,
"Invalid TAP port %u\n", tap_port);
MBUF_CACHE, 0,
RTE_MBUF_DEFAULT_BUF_SIZE,
if (mp == NULL)
rte_exit(EXIT_FAILURE,
"Cannot create mbuf pool\n");
LOG_INFO("Initializing PHY port %u...", phy_port);
ret = port_init(phy_port, mp);
if (ret != 0)
rte_exit(EXIT_FAILURE,
"Cannot init PHY port %u (%d)\n",
phy_port, ret);
LOG_INFO("Initializing TAP port %u...", tap_port);
ret = port_init(tap_port, mp);
if (ret != 0)
rte_exit(EXIT_FAILURE,
"Cannot init TAP port %u (%d)\n",
tap_port, ret);
LOG_INFO("PTP Software Relay ready");
LOG_INFO(" PHY port: %u", phy_port);
LOG_INFO(" TAP port: %u", tap_port);
LOG_INFO(" Stats every: %u seconds", stats_interval);
LOG_INFO(" Correction: Transparent Clock (SW timestamps)");
LOG_INFO("");
LOG_INFO("Run ptp4l: ptp4l -i dtap0 -m -s -S");
relay_loop(NULL);
LOG_INFO("Stopping ports...");
return 0;
}
__rte_noreturn void rte_exit(int exit_code, const char *format,...) __rte_format_printf(2
uint64_t rte_get_tsc_hz(void)
int rte_eal_init(int argc, char **argv)
int rte_eal_cleanup(void)
const char * rte_strerror(int errnum)
int rte_eth_dev_configure(uint16_t port_id, uint16_t nb_rx_queue, uint16_t nb_tx_queue, const struct rte_eth_conf *eth_conf)
int rte_eth_dev_is_valid_port(uint16_t port_id)
int rte_eth_rx_queue_setup(uint16_t port_id, uint16_t rx_queue_id, uint16_t nb_rx_desc, unsigned int socket_id, const struct rte_eth_rxconf *rx_conf, struct rte_mempool *mb_pool)
static uint16_t rte_eth_rx_burst(uint16_t port_id, uint16_t queue_id, struct rte_mbuf **rx_pkts, const uint16_t nb_pkts)
#define RTE_ETH_TX_OFFLOAD_MBUF_FAST_FREE
int rte_eth_dev_info_get(uint16_t port_id, struct rte_eth_dev_info *dev_info) __rte_warn_unused_result
int rte_eth_promiscuous_enable(uint16_t port_id)
int rte_eth_dev_stop(uint16_t port_id)
int rte_eth_tx_queue_setup(uint16_t port_id, uint16_t tx_queue_id, uint16_t nb_tx_desc, unsigned int socket_id, const struct rte_eth_txconf *tx_conf)
static uint16_t rte_eth_tx_burst(uint16_t port_id, uint16_t queue_id, struct rte_mbuf **tx_pkts, uint16_t nb_pkts)
uint16_t rte_eth_dev_count_avail(void)
int rte_eth_dev_close(uint16_t port_id)
int rte_eth_dev_socket_id(uint16_t port_id)
int rte_eth_dev_adjust_nb_rx_tx_desc(uint16_t port_id, uint16_t *nb_rx_desc, uint16_t *nb_tx_desc)
int rte_eth_dev_start(uint16_t port_id)
unsigned int rte_socket_id(void)
static unsigned rte_lcore_id(void)
static void rte_pktmbuf_free(struct rte_mbuf *m)
struct rte_mempool * rte_pktmbuf_pool_create(const char *name, unsigned n, unsigned cache_size, uint16_t priv_size, uint16_t data_room_size, int socket_id)
static void rte_ptp_add_correction(struct rte_ptp_hdr *hdr, uint64_t residence_ns)
static bool rte_ptp_is_event(const struct rte_ptp_hdr *hdr)