ptp_classify.h 6.9 KB
Newer Older
1
/* SPDX-License-Identifier: GPL-2.0-or-later */
2 3 4 5 6 7 8 9 10 11 12
/*
 * PTP 1588 support
 *
 * This file implements a BPF that recognizes PTP event messages.
 *
 * Copyright (C) 2010 OMICRON electronics GmbH
 */

#ifndef _PTP_CLASSIFY_H_
#define _PTP_CLASSIFY_H_

13
#include <asm/unaligned.h>
14
#include <linux/ip.h>
15
#include <linux/ktime.h>
16
#include <linux/skbuff.h>
17 18
#include <linux/udp.h>
#include <net/checksum.h>
19 20 21 22 23 24 25

#define PTP_CLASS_NONE  0x00 /* not a PTP event message */
#define PTP_CLASS_V1    0x01 /* protocol version 1 */
#define PTP_CLASS_V2    0x02 /* protocol version 2 */
#define PTP_CLASS_VMASK 0x0f /* max protocol version is 15 */
#define PTP_CLASS_IPV4  0x10 /* event in an IPV4 UDP packet */
#define PTP_CLASS_IPV6  0x20 /* event in an IPV6 UDP packet */
26 27 28
#define PTP_CLASS_L2    0x40 /* event in a L2 packet */
#define PTP_CLASS_PMASK	0x70 /* mask for the packet type field */
#define PTP_CLASS_VLAN	0x80 /* event in a VLAN tagged packet */
29 30

#define PTP_CLASS_V1_IPV4 (PTP_CLASS_V1 | PTP_CLASS_IPV4)
31
#define PTP_CLASS_V1_IPV6 (PTP_CLASS_V1 | PTP_CLASS_IPV6) /* probably DNE */
32 33 34 35
#define PTP_CLASS_V2_IPV4 (PTP_CLASS_V2 | PTP_CLASS_IPV4)
#define PTP_CLASS_V2_IPV6 (PTP_CLASS_V2 | PTP_CLASS_IPV6)
#define PTP_CLASS_V2_L2   (PTP_CLASS_V2 | PTP_CLASS_L2)
#define PTP_CLASS_V2_VLAN (PTP_CLASS_V2 | PTP_CLASS_VLAN)
36
#define PTP_CLASS_L4      (PTP_CLASS_IPV4 | PTP_CLASS_IPV6)
37

38 39 40 41 42
#define PTP_MSGTYPE_SYNC        0x0
#define PTP_MSGTYPE_DELAY_REQ   0x1
#define PTP_MSGTYPE_PDELAY_REQ  0x2
#define PTP_MSGTYPE_PDELAY_RESP 0x3

43
#define PTP_EV_PORT 319
44
#define PTP_GEN_PORT 320
45
#define PTP_GEN_BIT 0x08 /* indicates general message, if set in message type */
46

47 48 49
#define OFF_PTP_SOURCE_UUID	22 /* PTPv1 only */
#define OFF_PTP_SEQUENCE_ID	30

50 51 52
/* PTP header flag fields */
#define PTP_FLAG_TWOSTEP	BIT(1)

53
/* Below defines should actually be removed at some point in time. */
54 55
#define IP6_HLEN	40
#define UDP_HLEN	8
56 57
#define OFF_IHL		14
#define IPV4_HLEN(data) (((struct iphdr *)(data + OFF_IHL))->ihl << 2)
58

59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82
struct clock_identity {
	u8 id[8];
} __packed;

struct port_identity {
	struct clock_identity	clock_identity;
	__be16			port_number;
} __packed;

struct ptp_header {
	u8			tsmt;  /* transportSpecific | messageType */
	u8			ver;   /* reserved          | versionPTP  */
	__be16			message_length;
	u8			domain_number;
	u8			reserved1;
	u8			flag_field[2];
	__be64			correction;
	__be32			reserved2;
	struct port_identity	source_port_identity;
	__be16			sequence_id;
	u8			control;
	u8			log_message_interval;
} __packed;

83 84 85 86 87 88 89 90 91 92 93
#if defined(CONFIG_NET_PTP_CLASSIFY)
/**
 * ptp_classify_raw - classify a PTP packet
 * @skb: buffer
 *
 * Runs a minimal BPF dissector to classify a network packet to
 * determine the PTP class. In case the skb does not contain any
 * PTP protocol data, PTP_CLASS_NONE will be returned, otherwise
 * PTP_CLASS_V1_IPV{4,6}, PTP_CLASS_V2_IPV{4,6} or
 * PTP_CLASS_V2_{L2,VLAN}, depending on the packet content.
 */
94 95
unsigned int ptp_classify_raw(const struct sk_buff *skb);

96 97 98 99 100 101 102 103 104 105 106 107 108 109 110
/**
 * ptp_parse_header - Get pointer to the PTP v2 header
 * @skb: packet buffer
 * @type: type of the packet (see ptp_classify_raw())
 *
 * This function takes care of the VLAN, UDP, IPv4 and IPv6 headers. The length
 * is checked.
 *
 * Note, internally skb_mac_header() is used. Make sure that the @skb is
 * initialized accordingly.
 *
 * Return: Pointer to the ptp v2 header or NULL if not found
 */
struct ptp_header *ptp_parse_header(struct sk_buff *skb, unsigned int type);

111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135
/**
 * ptp_get_msgtype - Extract ptp message type from given header
 * @hdr: ptp header
 * @type: type of the packet (see ptp_classify_raw())
 *
 * This function returns the message type for a given ptp header. It takes care
 * of the different ptp header versions (v1 or v2).
 *
 * Return: The message type
 */
static inline u8 ptp_get_msgtype(const struct ptp_header *hdr,
				 unsigned int type)
{
	u8 msgtype;

	if (unlikely(type & PTP_CLASS_V1)) {
		/* msg type is located at the control field for ptp v1 */
		msgtype = hdr->control;
	} else {
		msgtype = hdr->tsmt & 0x0f;
	}

	return msgtype;
}

136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198
/**
 * ptp_check_diff8 - Computes new checksum (when altering a 64-bit field)
 * @old: old field value
 * @new: new field value
 * @oldsum: previous checksum
 *
 * This function can be used to calculate a new checksum when only a single
 * field is changed. Similar as ip_vs_check_diff*() in ip_vs.h.
 *
 * Return: Updated checksum
 */
static inline __wsum ptp_check_diff8(__be64 old, __be64 new, __wsum oldsum)
{
	__be64 diff[2] = { ~old, new };

	return csum_partial(diff, sizeof(diff), oldsum);
}

/**
 * ptp_header_update_correction - Update PTP header's correction field
 * @skb: packet buffer
 * @type: type of the packet (see ptp_classify_raw())
 * @hdr: ptp header
 * @correction: new correction value
 *
 * This updates the correction field of a PTP header and updates the UDP
 * checksum (if UDP is used as transport). It is needed for hardware capable of
 * one-step P2P that does not already modify the correction field of Pdelay_Req
 * event messages on ingress.
 */
static inline
void ptp_header_update_correction(struct sk_buff *skb, unsigned int type,
				  struct ptp_header *hdr, s64 correction)
{
	__be64 correction_old;
	struct udphdr *uhdr;

	/* previous correction value is required for checksum update. */
	memcpy(&correction_old,  &hdr->correction, sizeof(correction_old));

	/* write new correction value */
	put_unaligned_be64((u64)correction, &hdr->correction);

	switch (type & PTP_CLASS_PMASK) {
	case PTP_CLASS_IPV4:
	case PTP_CLASS_IPV6:
		/* locate udp header */
		uhdr = (struct udphdr *)((char *)hdr - sizeof(struct udphdr));
		break;
	default:
		return;
	}

	/* update checksum */
	uhdr->check = csum_fold(ptp_check_diff8(correction_old,
						hdr->correction,
						~csum_unfold(uhdr->check)));
	if (!uhdr->check)
		uhdr->check = CSUM_MANGLED_0;

	skb->ip_summed = CHECKSUM_NONE;
}

199 200 201 202 203 204 205 206 207 208 209
/**
 * ptp_msg_is_sync - Evaluates whether the given skb is a PTP Sync message
 * @skb: packet buffer
 * @type: type of the packet (see ptp_classify_raw())
 *
 * This function evaluates whether the given skb is a PTP Sync message.
 *
 * Return: true if sync message, false otherwise
 */
bool ptp_msg_is_sync(struct sk_buff *skb, unsigned int type);

210 211 212 213 214
void __init ptp_classifier_init(void);
#else
static inline void ptp_classifier_init(void)
{
}
215 216 217 218
static inline unsigned int ptp_classify_raw(struct sk_buff *skb)
{
	return PTP_CLASS_NONE;
}
219 220 221 222 223
static inline struct ptp_header *ptp_parse_header(struct sk_buff *skb,
						  unsigned int type)
{
	return NULL;
}
224 225 226 227 228 229
static inline u8 ptp_get_msgtype(const struct ptp_header *hdr,
				 unsigned int type)
{
	/* The return is meaningless. The stub function would not be
	 * executed since no available header from ptp_parse_header.
	 */
230
	return PTP_MSGTYPE_SYNC;
231
}
232 233 234 235
static inline bool ptp_msg_is_sync(struct sk_buff *skb, unsigned int type)
{
	return false;
}
236 237 238 239 240 241

static inline
void ptp_header_update_correction(struct sk_buff *skb, unsigned int type,
				  struct ptp_header *hdr, s64 correction)
{
}
242
#endif
243
#endif /* _PTP_CLASSIFY_H_ */