support TLVs with flexible size

These flexible TLVs don't need to be represented as a single packed
struct directly in the message buffer. Instead a separate struct
contains pointers to the individual parts of the TLV in the message
buffer. The flexible TLV can only be the last TLV in a message.

Signed-off-by: Geoff Salmon <gsalmon@se-instruments.com>
master
Geoff Salmon 2013-02-24 18:56:35 -05:00 committed by Richard Cochran
parent 09f8fe0e0b
commit f4e8f5be3f
4 changed files with 44 additions and 15 deletions

12
msg.c
View File

@ -144,7 +144,7 @@ static void port_id_pre_send(struct PortIdentity *pid)
pid->portNumber = htons(pid->portNumber); pid->portNumber = htons(pid->portNumber);
} }
static int suffix_post_recv(uint8_t *ptr, int len) static int suffix_post_recv(uint8_t *ptr, int len, struct tlv_extra *last)
{ {
int cnt; int cnt;
struct TLV *tlv; struct TLV *tlv;
@ -166,14 +166,14 @@ static int suffix_post_recv(uint8_t *ptr, int len)
} }
len -= tlv->length; len -= tlv->length;
ptr += tlv->length; ptr += tlv->length;
if (tlv_post_recv(tlv)) { if (tlv_post_recv(tlv, len ? NULL : last)) {
return -1; return -1;
} }
} }
return cnt; return cnt;
} }
static void suffix_pre_send(uint8_t *ptr, int cnt) static void suffix_pre_send(uint8_t *ptr, int cnt, struct tlv_extra *last)
{ {
int i; int i;
struct TLV *tlv; struct TLV *tlv;
@ -183,7 +183,7 @@ static void suffix_pre_send(uint8_t *ptr, int cnt)
for (i = 0; i < cnt; i++) { for (i = 0; i < cnt; i++) {
tlv = (struct TLV *) ptr; tlv = (struct TLV *) ptr;
tlv_pre_send(tlv); tlv_pre_send(tlv, i == cnt - 1 ? last : NULL);
ptr += sizeof(struct TLV) + tlv->length; ptr += sizeof(struct TLV) + tlv->length;
tlv->type = htons(tlv->type); tlv->type = htons(tlv->type);
tlv->length = htons(tlv->length); tlv->length = htons(tlv->length);
@ -344,7 +344,7 @@ int msg_post_recv(struct ptp_message *m, int cnt)
return -1; return -1;
} }
m->tlv_count = suffix_post_recv(suffix, cnt - pdulen); m->tlv_count = suffix_post_recv(suffix, cnt - pdulen, &m->last_tlv);
if (m->tlv_count == -1) { if (m->tlv_count == -1) {
return -1; return -1;
} }
@ -401,7 +401,7 @@ int msg_pre_send(struct ptp_message *m)
default: default:
return -1; return -1;
} }
suffix_pre_send(suffix, m->tlv_count); suffix_pre_send(suffix, m->tlv_count, &m->last_tlv);
return 0; return 0;
} }

7
msg.h
View File

@ -26,6 +26,7 @@
#include "ddt.h" #include "ddt.h"
#include "transport.h" #include "transport.h"
#include "tlv.h"
#define PTP_VERSION 2 #define PTP_VERSION 2
@ -204,6 +205,12 @@ struct ptp_message {
* Contains the number of TLVs in the suffix. * Contains the number of TLVs in the suffix.
*/ */
int tlv_count; int tlv_count;
/**
* Used to hold the data of the last TLV in the message when
* the layout of the TLV makes it difficult to access the data
* directly from the message's buffer.
*/
struct tlv_extra last_tlv;
}; };
/** /**

24
tlv.c
View File

@ -21,6 +21,7 @@
#include "port.h" #include "port.h"
#include "tlv.h" #include "tlv.h"
#include "msg.h"
#define TLV_LENGTH_INVALID(tlv, type) \ #define TLV_LENGTH_INVALID(tlv, type) \
(tlv->length < sizeof(struct type) - sizeof(struct TLV)) (tlv->length < sizeof(struct type) - sizeof(struct TLV))
@ -41,7 +42,8 @@ static void scaled_ns_h2n(ScaledNs *sns)
sns->fractional_nanoseconds = htons(sns->fractional_nanoseconds); sns->fractional_nanoseconds = htons(sns->fractional_nanoseconds);
} }
static int mgt_post_recv(struct management_tlv *m, uint16_t data_len) static int mgt_post_recv(struct management_tlv *m, uint16_t data_len,
struct tlv_extra *extra)
{ {
struct defaultDS *dds; struct defaultDS *dds;
struct currentDS *cds; struct currentDS *cds;
@ -49,6 +51,7 @@ static int mgt_post_recv(struct management_tlv *m, uint16_t data_len)
struct timePropertiesDS *tp; struct timePropertiesDS *tp;
struct portDS *p; struct portDS *p;
struct time_status_np *tsn; struct time_status_np *tsn;
int extra_len = 0;
switch (m->id) { switch (m->id) {
case DEFAULT_DATA_SET: case DEFAULT_DATA_SET:
if (data_len != sizeof(struct defaultDS)) if (data_len != sizeof(struct defaultDS))
@ -105,12 +108,18 @@ static int mgt_post_recv(struct management_tlv *m, uint16_t data_len)
tsn->gmPresent = ntohl(tsn->gmPresent); tsn->gmPresent = ntohl(tsn->gmPresent);
break; break;
} }
if (extra_len) {
if (extra_len % 2)
extra_len++;
if (extra_len + sizeof(m->id) != m->length)
goto bad_length;
}
return 0; return 0;
bad_length: bad_length:
return -1; return -1;
} }
static void mgt_pre_send(struct management_tlv *m) static void mgt_pre_send(struct management_tlv *m, struct tlv_extra *extra)
{ {
struct defaultDS *dds; struct defaultDS *dds;
struct currentDS *cds; struct currentDS *cds;
@ -209,12 +218,15 @@ static void org_pre_send(struct organization_tlv *org)
} }
} }
int tlv_post_recv(struct TLV *tlv) int tlv_post_recv(struct TLV *tlv, struct tlv_extra *extra)
{ {
int result = 0; int result = 0;
struct management_tlv *mgt; struct management_tlv *mgt;
struct management_error_status *mes; struct management_error_status *mes;
struct path_trace_tlv *ptt; struct path_trace_tlv *ptt;
struct tlv_extra dummy_extra;
if (!extra)
extra = &dummy_extra;
switch (tlv->type) { switch (tlv->type) {
case TLV_MANAGEMENT: case TLV_MANAGEMENT:
@ -223,7 +235,7 @@ int tlv_post_recv(struct TLV *tlv)
mgt = (struct management_tlv *) tlv; mgt = (struct management_tlv *) tlv;
mgt->id = ntohs(mgt->id); mgt->id = ntohs(mgt->id);
if (tlv->length > sizeof(mgt->id)) if (tlv->length > sizeof(mgt->id))
result = mgt_post_recv(mgt, tlv->length - sizeof(mgt->id)); result = mgt_post_recv(mgt, tlv->length - sizeof(mgt->id), extra);
break; break;
case TLV_MANAGEMENT_ERROR_STATUS: case TLV_MANAGEMENT_ERROR_STATUS:
if (TLV_LENGTH_INVALID(tlv, management_error_status)) if (TLV_LENGTH_INVALID(tlv, management_error_status))
@ -261,7 +273,7 @@ bad_length:
return -1; return -1;
} }
void tlv_pre_send(struct TLV *tlv) void tlv_pre_send(struct TLV *tlv, struct tlv_extra *extra)
{ {
struct management_tlv *mgt; struct management_tlv *mgt;
struct management_error_status *mes; struct management_error_status *mes;
@ -270,7 +282,7 @@ void tlv_pre_send(struct TLV *tlv)
case TLV_MANAGEMENT: case TLV_MANAGEMENT:
mgt = (struct management_tlv *) tlv; mgt = (struct management_tlv *) tlv;
if (tlv->length > sizeof(mgt->id)) if (tlv->length > sizeof(mgt->id))
mgt_pre_send(mgt); mgt_pre_send(mgt, extra);
mgt->id = htons(mgt->id); mgt->id = htons(mgt->id);
break; break;
case TLV_MANAGEMENT_ERROR_STATUS: case TLV_MANAGEMENT_ERROR_STATUS:

16
tlv.h
View File

@ -21,7 +21,6 @@
#define HAVE_TLV_H #define HAVE_TLV_H
#include "ddt.h" #include "ddt.h"
#include "msg.h"
/* TLV types */ /* TLV types */
#define TLV_MANAGEMENT 0x0001 #define TLV_MANAGEMENT 0x0001
@ -177,17 +176,28 @@ struct time_status_np {
struct ClockIdentity gmIdentity; struct ClockIdentity gmIdentity;
} PACKED; } PACKED;
struct tlv_extra {
union {
/* Empty for now, but will contain structs for the
* TLVs that use the tlv_extra support. */
};
};
/** /**
* Converts recognized value sub-fields into host byte order. * Converts recognized value sub-fields into host byte order.
* @param tlv Pointer to a Type Length Value field. * @param tlv Pointer to a Type Length Value field.
* @param extra Additional struct where data from tlv will be saved,
* can be NULL.
* @return Zero if successful, otherwise non-zero * @return Zero if successful, otherwise non-zero
*/ */
int tlv_post_recv(struct TLV *tlv); int tlv_post_recv(struct TLV *tlv, struct tlv_extra *extra);
/** /**
* Converts recognized value sub-fields into network byte order. * Converts recognized value sub-fields into network byte order.
* @param tlv Pointer to a Type Length Value field. * @param tlv Pointer to a Type Length Value field.
* @param extra Additional struct containing tlv data to send, can be
* NULL.
*/ */
void tlv_pre_send(struct TLV *tlv); void tlv_pre_send(struct TLV *tlv, struct tlv_extra *extra);
#endif #endif