[RFC v1 0/4] introduce PTP protocol library and software relay example

Stephen Hemminger stephen at networkplumber.org
Tue Apr 28 01:42:30 CEST 2026


On Tue, 28 Apr 2026 06:31:02 +0530
Rajesh Kumar <rajesh3.kumar at intel.com> wrote:

> This series introduces a new DPDK library (lib/ptp) for IEEE 1588-2019
> PTP protocol packet processing and a companion example application
> (ptp_tap_relay_sw) that demonstrates its usage.
> 
> Motivation
> ----------
> Several DPDK applications need to classify and manipulate PTP packets
> (e.g. ptpclient, ptp_tap_relay, custom Transparent Clocks). Today each
> application re-implements its own PTP header parsing and correctionField
> handling. A shared library avoids duplication and provides a tested,
> standards-compliant foundation.
> 
> Library: lib/ptp
> ----------------
> The library provides:
>   - PTP header structures (IEEE 1588-2019 common header, timestamp,
>     port identity)
>   - Packet classification: rte_ptp_classify() detects PTP over L2
>     (EtherType 0x88F7), VLAN-tagged L2, and UDP/IPv4 (ports 319/320)
>   - Header access: rte_ptp_hdr_get() returns a pointer to the PTP
>     header inside an mbuf
>   - Inline helpers: correctionField manipulation (48.16 fixed-point),
>     message type extraction, two-step flag check, timestamp conversion
>   - Debug: rte_ptp_msg_type_str() for human-readable message names

The things I noticed were:
Do not use older PTP terminology master/slave; we fixed this in DPDK.
This would cause a NAK for me, and checkpatch would flag it.

AI had lots more to say...

Subject: Re: [RFC v1 0/4] introduce PTP protocol library and software relay example

Some review comments on this RFC.  Overall direction (shared PTP
header+classification library) is good, but there are a couple of
correctness bugs in patch 1/4 that need to be fixed before this can
move out of RFC.

Patch 1/4: ptp: introduce PTP protocol library
==============================================

Error: flag bit positions wrong for the host-order representation
-----------------------------------------------------------------

  +#define RTE_PTP_FLAG_TWO_STEP    (1 << 1)  /**< Two-step flag (bit 1) */
  +#define RTE_PTP_FLAG_UNICAST     (1 << 2)  /**< Unicast flag */
  +#define RTE_PTP_FLAG_LI_61       (1 << 0)  /**< Leap indicator 61 */
  +#define RTE_PTP_FLAG_LI_59       (1 << 1)  /**< Leap indicator 59 (byte 1) */

  +static inline bool
  +rte_ptp_is_two_step(const struct rte_ptp_hdr *hdr)
  +{
  +     return (rte_be_to_cpu_16(hdr->flags) & RTE_PTP_FLAG_TWO_STEP) != 0;
  +}

twoStepFlag and unicastFlag live in byte 0 of the wire flag field
(IEEE 1588-2019 Table 37). leap61/leap59 live in byte 1. After
rte_be_to_cpu_16(), byte 0 of the wire ends up in the upper 8 bits of
the host value, so:

   TWO_STEP needs to be (1 << 9), not (1 << 1)
   UNICAST  needs to be (1 << 10), not (1 << 2)
   LI_61    stays at    (1 << 0)
   LI_59    stays at    (1 << 1)

As written, rte_ptp_is_two_step() always returns false for any real
two-step Sync from the wire, which silently breaks any Transparent
Clock or Boundary Clock built on this library.  TWO_STEP and LI_59
also collide on (1 << 1), which by itself shows the macro layout
isn't right.

Worth adding a unit test that constructs a packet with twoStepFlag
set and checks that the helper returns true.

Error: QinQ classification path is unreachable for standard QinQ
----------------------------------------------------------------

In ptp_hdr_find(), the VLAN block is only entered when the outer
EtherType is RTE_ETHER_TYPE_VLAN (0x8100):

  +     if (ether_type == RTE_ETHER_TYPE_VLAN) {
  ...
  +             /* Double-tagged VLAN (QinQ) */
  +             if (ether_type == RTE_ETHER_TYPE_QINQ ||
  +                 ether_type == RTE_ETHER_TYPE_VLAN) {

Standard 802.1ad QinQ uses outer S-tag 0x88A8 (RTE_ETHER_TYPE_QINQ)
followed by a C-tag 0x8100, then the payload EtherType.  Packets with
the typical S-tag outer never enter the VLAN branch at all, so the
"QinQ supported" claim in the cover letter and the prog_guide does
not match what the code does.

Either accept 0x88A8 as an entry point to the VLAN parser, or drop
the QinQ claim from the documentation.

Warning: VLAN-tagged PTP-over-UDP not handled
---------------------------------------------

The classifier handles plain L2, VLAN-L2, IPv4-UDP, and IPv6-UDP, but
there is no path for VLAN -> IPv4/IPv6 -> UDP -> PTP.  This is a
common deployment pattern.  Not fatal, but please mention it in the
limitations list if it's intentional.

Warning: signed left shift in rte_ptp_add_correction()
------------------------------------------------------

  +     int64_t cf = (int64_t)rte_be_to_cpu_64(hdr->correction);
  +     cf += (residence_ns << 16);

residence_ns is int64_t.  Left-shift of a signed value where the
shifted value is not representable in the result type is undefined
behaviour (C11 6.5.7p4).  In the relay example residence_ns is
computed from (uint64_t)tx_ts - (uint64_t)rx_ts cast to int64_t, so
clock noise or a tx_ts < rx_ts edge case turns this negative.  GCC
and clang happen to produce arithmetic shift, but please make it
well-defined:

   cf += (int64_t)((uint64_t)residence_ns << 16);

or just multiply by (1LL << 16).

Same issue, lower severity, in rte_ptp_correction_ns():

  +     return (int64_t)rte_be_to_cpu_64(hdr->correction) >> 16;

Right-shift of a negative signed value is implementation-defined
(not UB), but a divide expresses intent more clearly.

Info: include order
-------------------

  +#include "rte_ptp.h"
  +
  +#include <eal_export.h>

eal_export.h is conventionally included with the other system /
EAL headers, before the local rte_ptp.h.

Info: overlap with examples/ptpclient
-------------------------------------

ptpclient.c defines its own struct ptp_header / ptp_message.  Since
this series adds a shared library, it would be worth converting
ptpclient as part of the series (or in a follow-up) to avoid
duplicating the same definitions in-tree.


Patch 2/4: doc: add PTP library programmer's guide
==================================================

Just the QinQ wording carryover - prog_guide claims "Double VLAN
(QinQ) + EtherType 0x88F7" but the code as posted only handles
0x8100/0x8100 nesting.  Update either the code or the doc to match.


Patch 3/4: examples/ptp_tap_relay_sw
====================================

Warning: promiscuous enable failure on PHY port is not fatal
------------------------------------------------------------

  +     ret = rte_eth_promiscuous_enable(port);
  +     if (ret != 0)
  +             LOG_ERR("Failed to enable promiscuous on port %u", port);
  +
  +     return 0;

For the PHY port this is what lets the relay see the PTP multicast
group (01:1B:19:00:00:00 / 01:80:C2:00:00:0E).  If it fails, the
relay starts up looking healthy but never receives anything.  At
least exit on failure for the PHY port, or enable allmulticast
explicitly so the failure mode is visible.

Info: per-burst single timestamp
--------------------------------

A single rx_ts and tx_ts is taken for the whole burst.  At
logSyncInterval=-4 (16 packets/sec) bursts are essentially always 1
packet so this doesn't matter, but at higher rates the early
packets in a burst get under-corrected and the late ones
over-corrected by up to a burst's worth of poll latency.  Worth
calling out in the comment block; the test results already imply
single-packet bursts.

Info: missing release notes for new library
-------------------------------------------

Targeted at 26.07 per RTE_EXPORT_EXPERIMENTAL_SYMBOL.  A new library
should add an entry under "New Features" in
doc/guides/rel_notes/release_26_07.rst.


More information about the dev mailing list