> From: Rajesh Kumar [mailto:[email protected]] > Sent: Thursday, 7 May 2026 15.45 > To: [email protected] > > Add PTP (Precision Time Protocol) header structures and inline helper > functions to lib/net following DPDK conventions for protocol libraries > (similar to rte_tcp.h, rte_ip.h). > > Provides wire-format structures with endian-annotated types: > - rte_ptp_port_id: 10-byte port identity with EUI-64 clock ID > - rte_ptp_hdr: 34-byte common message header with correctionField > - rte_ptp_timestamp: 10-byte nanosecond-precision timestamp > > PTP message type constants for all IEEE 1588-2019 message types > (Sync, Delay_Req, Announce, Management, etc.) and flag field bits > (two-step, unicast, leap indicator). > > Inline accessor and utility functions for performance: > - rte_ptp_msg_type(), rte_ptp_version(), rte_ptp_domain() > - rte_ptp_seq_id(), rte_ptp_is_event(), rte_ptp_is_two_step() > - rte_ptp_correction_ns(), rte_ptp_add_correction() > - rte_ptp_timestamp_to_ns() for timestamp conversion > > Supports all PTP encapsulations: L2 (EtherType 0x88F7), VLAN/QinQ, > UDP/IPv4, and UDP/IPv6 (ports 319/320). > > All inline functions — zero function call overhead, suitable for > real-time packet processing. > > Signed-off-by: Rajesh Kumar <[email protected]>
Great progress. My next wave of comments inline below. :-) > --- > MAINTAINERS | 6 + > lib/net/meson.build | 1 + > lib/net/rte_ptp.h | 264 ++++++++++++++++++++++++++++++++++++++++++++ > 3 files changed, 271 insertions(+) > create mode 100644 lib/net/rte_ptp.h > > diff --git a/MAINTAINERS b/MAINTAINERS > index 0f5539f851..da31ada871 100644 > --- a/MAINTAINERS > +++ b/MAINTAINERS > @@ -1665,6 +1665,12 @@ F: doc/guides/prog_guide/ipsec_lib.rst > M: Vladimir Medvedkin <[email protected]> > F: app/test-sad/ > > +PTP - lib/net Please remove lib/net from this headline. Intead, suggest: PTP (IEEE 1588 Precision Time Protocol) > +M: Rajesh Kumar <[email protected]> > +F: lib/net/rte_ptp.h > +F: examples/ptp_tap_relay_sw/ > +F: doc/guides/sample_app_ug/ptp_tap_relay_sw.rst > + > PDCP - EXPERIMENTAL > M: Anoob Joseph <[email protected]> > M: Volodymyr Fialko <[email protected]> > diff --git a/lib/net/meson.build b/lib/net/meson.build > index 3fad5edc5b..63d13719f3 100644 > --- a/lib/net/meson.build > +++ b/lib/net/meson.build > @@ -28,6 +28,7 @@ headers = files( > 'rte_geneve.h', > 'rte_l2tpv2.h', > 'rte_ppp.h', > + 'rte_ptp.h', > 'rte_ib.h', > ) > > diff --git a/lib/net/rte_ptp.h b/lib/net/rte_ptp.h > new file mode 100644 > index 0000000000..649b944d29 > --- /dev/null > +++ b/lib/net/rte_ptp.h > @@ -0,0 +1,264 @@ > +/* SPDX-License-Identifier: BSD-3-Clause > + * Copyright(c) 2026 Intel Corporation > + */ > + > +#ifndef _RTE_PTP_H_ > +#define _RTE_PTP_H_ > + > +/** > + * @file > + * > + * PTP (IEEE 1588) protocol definitions > + */ > + > +#include <stdint.h> > +#include <stdbool.h> > + > +#include <rte_byteorder.h> > +#include <rte_common.h> > + > +#ifdef __cplusplus > +extern "C" { > +#endif > + > +/* > + * PTP Constants > + */ > + > +/** PTP over UDP event port (Sync, Delay_Req, PDelay_Req, > PDelay_Resp). */ > +#define RTE_PTP_EVENT_PORT 319 > + > +/** PTP over UDP general port (Follow_Up, Delay_Resp, Announce, etc.). > */ > +#define RTE_PTP_GENERAL_PORT 320 The libc header <netinet/in.h> defines IPPORT_xxx. We should use something similar: #define RTE_IPPORT_PTP_EVENT and RTE_IPPORT_PTP_GENERAL > + > +/** PTP multicast MAC address: 01:1B:19:00:00:00. */ > +#define RTE_PTP_MULTICAST_MAC { 0x01, 0x1B, 0x19, 0x00, 0x00, 0x00 > } > + > +/** PTP peer delay multicast MAC: 01:80:C2:00:00:0E. */ > +#define RTE_PTP_PDELAY_MULTICAST_MAC { 0x01, 0x80, 0xC2, 0x00, 0x00, > 0x0E } Similarly here; we should establish a convention for MAC addresses, like RTE_ETHER_TYPE_xxx in DPDK <lib/net/rte_ether.h>. Suggest: RTE_ETHER_ADDR_PTP_MULTICAST and RTE_ETHER_ADDR_PTP_MULTICAST_PDELAY This also follows the general naming convention of having the broadest scope is first and the narrowest scope last. > + > +/* > + * PTP Message Types (IEEE 1588-2019 Table 36) > + */ > + > +#define RTE_PTP_MSGTYPE_SYNC 0x0 /**< Sync (event). */ > +#define RTE_PTP_MSGTYPE_DELAY_REQ 0x1 /**< Delay_Req (event). > */ > +#define RTE_PTP_MSGTYPE_PDELAY_REQ 0x2 /**< Peer_Delay_Req > (event). */ > +#define RTE_PTP_MSGTYPE_PDELAY_RESP 0x3 /**< Peer_Delay_Resp > (event). */ > +#define RTE_PTP_MSGTYPE_FOLLOW_UP 0x8 /**< Follow_Up (general). > */ > +#define RTE_PTP_MSGTYPE_DELAY_RESP 0x9 /**< Delay_Resp > (general). */ > +#define RTE_PTP_MSGTYPE_PDELAY_RESP_FU 0xA /**< > Peer_Delay_Resp_Follow_Up. */ Missing in the description: (general) For consistency, consider renaming RTE_PTP_MSGTYPE_FOLLOW_UP to RTE_PTP_MSGTYPE_FU. > +#define RTE_PTP_MSGTYPE_ANNOUNCE 0xB /**< Announce (general). > */ > +#define RTE_PTP_MSGTYPE_SIGNALING 0xC /**< Signaling (general). > */ > +#define RTE_PTP_MSGTYPE_MANAGEMENT 0xD /**< Management > (general). */ > + > +/* > + * PTP Flag Field Bits (IEEE 1588-2019 Table 37) > + * > + * These constants are for use after rte_be_to_cpu_16(hdr->flags). > + * flagField[0] (octet 6) maps to host bits 8-15. > + * flagField[1] (octet 7) maps to host bits 0-7. > + */ > + > +#define RTE_PTP_FLAG_TWO_STEP (1 << 9) /**< Two-step flag. */ > +#define RTE_PTP_FLAG_UNICAST (1 << 10) /**< Unicast flag. */ > +#define RTE_PTP_FLAG_LI_61 (1 << 0) /**< Leap indicator 61. */ > +#define RTE_PTP_FLAG_LI_59 (1 << 1) /**< Leap indicator 59. */ We don't have a RTE_BIT16() macro like the RTE_BIT32/64() macros, so maybe use: #define RTE_PTP_FLAG_TWO_STEP (UINT16_C(1) << 9) /**< Two-step flag. */ #define RTE_PTP_FLAG_UNICAST (UINT16_C(1) << 10) /**< Unicast flag. */ #define RTE_PTP_FLAG_LI_61 (UINT16_C(1) << 0) /**< Leap indicator 61. */ #define RTE_PTP_FLAG_LI_59 (UINT16_C(1) << 1) /**< Leap indicator 59. */ > + > +/* > + * PTP Header Structures (IEEE 1588-2019) > + */ > + > +/** > + * PTP Port Identity (10 bytes). > + */ > +struct __rte_packed_begin rte_ptp_port_id { > + uint8_t clock_id[8]; /**< clockIdentity (EUI-64). */ > + rte_be16_t port_number; /**< portNumber. */ > +} __rte_packed_end; > + > +/** > + * PTP Common Message Header (34 bytes). > + */ > +struct __rte_packed_begin rte_ptp_hdr { > + uint8_t msg_type; /**< transportSpecific (4) | > messageType (4). */ > + uint8_t version; /**< minorVersionPTP (4) | versionPTP > (4). */ The two fields above should be split into their nibbles, for direct access, like the version_ihl field in the IPv4 header: https://elixir.bootlin.com/dpdk/v26.03/source/lib/net/rte_ip4.h#L43 E.g.: __extension__ union { uint8_t ts_msgtype; /**< Message type */ struct { #if RTE_BYTE_ORDER == RTE_LITTLE_ENDIAN uint8_t msg_type:4; /**< messageType */ uint8_t ts:4; /**< transportSpecific */ #elif RTE_BYTE_ORDER == RTE_BIG_ENDIAN uint8_t ts:4; /**< transportSpecific */ uint8_t msg_type:4; /**< messageType */ #endif }; }; Warning: I'm not sure I got the nibble order right. Make sure you do! :-) > + rte_be16_t msg_length; /**< Total message length in bytes. */ > + uint8_t domain_number; /**< PTP domain (0-255). */ > + uint8_t minor_sdo_id; /**< minorSdoId (IEEE 1588-2019). */ > + rte_be16_t flags; /**< Flag field (see RTE_PTP_FLAG_*). > */ > + rte_be64_t correction; /**< correctionField (scaled ns, 48.16 > fixed). */ > + rte_be32_t msg_type_specific; /**< messageTypeSpecific. */ > + struct rte_ptp_port_id source_port_id; /**< sourcePortIdentity. > */ > + rte_be16_t sequence_id; /**< sequenceId. */ > + uint8_t control; /**< controlField (deprecated in 1588- > 2019). */ > + int8_t log_msg_interval; /**< logMessageInterval. */ > +} __rte_packed_end; > + > +/** > + * PTP Timestamp (10 bytes, used in Sync/Delay_Req/Follow_Up bodies). The PTP timestamp origo should be mentioned here. E.g. the UNIX time_t origo is Jan 1st 00:00:00 1970. > + */ > +struct __rte_packed_begin rte_ptp_timestamp { > + rte_be16_t seconds_hi; /**< Upper 16 bits of seconds. */ > + rte_be32_t seconds_lo; /**< Lower 32 bits of seconds. */ > + rte_be32_t nanoseconds; /**< Nanoseconds (0-999999999). */ > +} __rte_packed_end; > + > +/* > + * Inline Helpers > + */ > + > +/** > + * Extract PTP message type from header. > + * > + * @param hdr > + * Pointer to PTP header. > + * @return > + * Message type (0x0-0xF). > + */ > +static inline uint8_t > +rte_ptp_msg_type(const struct rte_ptp_hdr *hdr) > +{ > + return hdr->msg_type & 0x0F; > +} > + > +/** > + * Extract transport-specific field from header. > + * > + * @param hdr > + * Pointer to PTP header. > + * @return > + * Transport-specific value (upper nibble, 0x0-0xF). > + */ > +static inline uint8_t > +rte_ptp_transport_specific(const struct rte_ptp_hdr *hdr) > +{ > + return (hdr->msg_type >> 4) & 0x0F; > +} > + > +/** > + * Extract PTP version from header. > + * > + * @param hdr > + * Pointer to PTP header. > + * @return > + * PTP version number (typically 2). > + */ > +static inline uint8_t > +rte_ptp_version(const struct rte_ptp_hdr *hdr) > +{ > + return hdr->version & 0x0F; > +} > + > +/** > + * Get sequence ID from PTP header (host byte order). > + * > + * @param hdr > + * Pointer to PTP header. > + * @return > + * Sequence ID in host byte order. > + */ > +static inline uint16_t > +rte_ptp_seq_id(const struct rte_ptp_hdr *hdr) > +{ > + return rte_be_to_cpu_16(hdr->sequence_id); > +} > + > +/** > + * Get PTP domain number. > + * > + * @param hdr > + * Pointer to PTP header. > + * @return > + * Domain number (0-255). > + */ > +static inline uint8_t > +rte_ptp_domain(const struct rte_ptp_hdr *hdr) > +{ > + return hdr->domain_number; > +} The above "get" accessor functions are superfluous. Please remove: rte_ptp_msg_type() rte_ptp_transport_specific() rte_ptp_version() rte_ptp_seq_id() rte_ptp_domain() > + > +/** > + * Check if PTP message type is an event message. > + * Event messages (msg_type 0x0-0x3) require timestamps. > + * > + * @param msg_type > + * PTP message type value (0x0-0xF). > + * @return > + * true if event message, false otherwise. > + */ > +static inline bool > +rte_ptp_is_event(int msg_type) > +{ > + return msg_type >= 0 && msg_type <= RTE_PTP_MSGTYPE_PDELAY_RESP; > +} Suggest passing the parameter as const struct rte_ptp_hdr *hdr, like in rte_ptp_is_two_step() below. > + > +/** > + * Check if the two-step flag is set in a PTP header. > + * > + * @param hdr > + * Pointer to PTP header. > + * @return > + * true if two-step flag is set. > + */ > +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; Faster way: return (hdr->flags & RTE_BE16(RTE_PTP_FLAG_TWO_STEP)) != 0; > +} > + > +/** > + * Get correctionField value in nanoseconds (from 48.16 fixed-point). > + * > + * @param hdr > + * Pointer to PTP header. > + * @return > + * Correction value in nanoseconds. > + */ > +static inline int64_t > +rte_ptp_correction_ns(const struct rte_ptp_hdr *hdr) > +{ > + return (int64_t)rte_be_to_cpu_64(hdr->correction) >> 16; > +} This "get" accessor function is superfluous. Please remove: rte_ptp_correction_ns() The "correction" field in the PTP header structure should sufficiently describe the field's fixed-point encoding. > + > +/** > + * Add a residence time (in nanoseconds) to the correctionField. > + * Used by Transparent Clocks to account for relay transit delay. > + * The correctionField uses IEEE 1588 scaled nanoseconds (48.16 fixed- > point). > + * > + * @param hdr > + * Pointer to PTP header (will be modified in-place). > + * @param residence_ns > + * Residence time in nanoseconds to add. > + */ > +static inline void > +rte_ptp_add_correction(struct rte_ptp_hdr *hdr, int64_t residence_ns) > +{ > + int64_t cf = (int64_t)rte_be_to_cpu_64(hdr->correction); > + > + cf += (int64_t)((uint64_t)residence_ns << 16); > + hdr->correction = rte_cpu_to_be_64(cf); > +} I don't think negative time can be added, so please use uint64_t instead of int64_t for the parameter and inside the function. Note to reviewers: "residence time" is the term in the standard. "sojourn time" is not used. > + > +/** > + * Convert a PTP timestamp structure to nanoseconds since epoch. > + * > + * @param ts > + * Pointer to PTP timestamp. > + * @return > + * Time in nanoseconds since epoch. > + */ > +static inline uint64_t > +rte_ptp_timestamp_to_ns(const struct rte_ptp_timestamp *ts) > +{ > + uint64_t sec = ((uint64_t)rte_be_to_cpu_16(ts->seconds_hi) << 32) > | > + rte_be_to_cpu_32(ts->seconds_lo); > + > + return sec * 1000000000ULL + rte_be_to_cpu_32(ts->nanoseconds); > +} 1000000000ULL -> UINT64_C(1000000000) > + > +#ifdef __cplusplus > +} > +#endif > + > +#endif /* _RTE_PTP_H_ */ > -- > 2.53.0

