Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
79 changes: 79 additions & 0 deletions README-FLUTE-V2.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
# FLUTE version 2 (RFC 6726) on this branch

This branch adds FLUTE version 2 to the library as a per-session choice, defaulting to version 1.
It is **not a complete RFC 6726 implementation**, and nothing here should be read as a claim that
it is. What is and is not covered is listed below.

## Why version 2 is a separate branch, and not an option on the main one

The two versions are different protocols, not a compatible upgrade. RFC 6726 clause 11.1:

> Therefore, an implementation that relies on [RFC3926] and RFC 3451 will not be backwards
> compatible with FLUTE as specified in this document.

3GPP does not use version 2. TS 26.346 clause L.4.1 references RFC 3926, and neither TS 26.346 nor
TS 26.517 references RFC 6726. The 3GPP MBMS Download Profile therefore lives entirely on the
version 1 branch, which carries none of the behaviour below: every difference here is reached only
by a caller that has selected version 2 explicitly.

## Selecting a version

`Transmitter::set_flute_version()` and `Receiver::set_flute_version()`. Both default to 1. The
selection reaches the ALC/LCT parser, the FDT, and the receiver's object handling.

## What this branch implements

- **Version signalling.** Transmit sets the EXT_FDT version field to the selected version, and
receive accepts only the version the session was configured for, rejecting the other in both
directions. RFC 6726 clause 3.4.1 requires the field to be 2 for a version 2 session.
- **The RFC 6726 LCT generation.** RFC 5651 removed the Sender Current Time and Expected Residual
Time header fields; RFC 6726 clause 11.1 states that under RFC 5651 the two bits that carried
them "MUST be set to zero and MUST be ignored by receivers". Under version 2 the parser gives
them no length and steps over nothing, so an extension placed after the TOI is reached. Under
version 1 the RFC 3451 reading is unchanged. The transmit side has always sent both bits zero.
- **The RFC 6726 FDT Instance ID sequence.** Version 2 wraps to the smallest expired identifier,
refuses to reuse one that is still live, and reports exhaustion; version 1 keeps RFC 3926's
wrap to zero.
- **Expires read in the correct NTP era.** RFC 6726 clause 3.3 has a receiver choose the epoch
"for which the expiration time is closest in time to the current time". Applied on parse for
version 2 only; RFC 3926 states no such rule.
- **Ordering of two TOIs sharing a Content-Location.** RFC 6726 clause 3.4.2 makes the declaration
from the greater FDT Instance ID the newer one. The receiver's replacement sweep follows that
under version 2 instead of keeping whichever completed first.
- **The RFC 6726 FDT namespace**, `urn:ietf:params:xml:ns:fdt`, from clause 3.4.2.

Covered by `tests/test_flute_v2.cpp`.

## Obligations checked and already met

These needed no code. They are recorded so nobody re-opens them.

- **EXT_TIME and EXT_AUTH.** RFC 5651 clause 5.2.1: senders and receivers "MUST recognize EXT_AUTH
and EXT_TIME, but are not required to be able to parse their content." The parser has explicit
cases for both and skips them by their declared length.
- **ALC, RFC 3450 to RFC 5775.** RFC 5775 clause 8's change list is almost entirely editorial. Its
two substantive items are the Source Packet Indication bit, which the LCT header struct carries,
and the definition of EXT_FTI, which is implemented. Read from the change list, not a full
re-read of the RFC.
- **The FDT schema body.** The attribute set RFC 6726 clause 3.4.2 defines matches what the library
emits; the 3GPP extension attributes and elements it also carries are admitted by that schema's
own `xs:any namespace="##other"` and `xs:anyAttribute`, and the 3GPP-only `schemaVersion` element
is not emitted for this namespace. Established by reading the schema, not by running an emitted
document through a validator.
- **IPsec/ESP.** RFC 6726 clause 7.5 makes it mandatory to implement, taking its service set from
RFC 5775: data origin authentication, content integrity and anti-replay SHALL be supported, and
confidentiality is RECOMMENDED. Encryption and HMAC-SHA256 authentication are configured, and
the association now sets a replay window.

## What is NOT implemented

**Congestion control.** RFC 5775: "Congestion control MUST be applied to all packets within a
session". The library has none, and the LCT Congestion Control Information field is sent as zero.
This is a building block, not a gap that can be patched, and until it exists **no RFC 6726
conformance claim can be made for this branch whatever else is in place**. Tracked as step 3 of
the issue this branch advances.

## If you are picking this up

Congestion control is the only remaining item, and it is the largest. Everything else listed above
is either implemented and tested, or checked against the specification and found already met.
11 changes: 9 additions & 2 deletions include/AlcPacket.h
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ namespace LibFlute {
* @param data Received data to be parsed
* @param len Length of the buffer
*/
AlcPacket(char* data, size_t len);
AlcPacket(char* data, size_t len, uint8_t expected_flute_version = 1);

/**
* Create an ALC packet from encoding symbols
Expand All @@ -45,6 +45,11 @@ namespace LibFlute {
* @param fdt_instance_id FDT instance ID (only relevant for FDT with TOI=0)
* @param close_session_flag Set the LCT Close Session flag (RFC 3451 clause 5.1, 'A' bit) on this packet
* @param close_object_flag Set the LCT Close Object flag (RFC 3451 clause 5.1, 'B' bit) on this packet
* @param flute_version FLUTE version to signal in the EXT_FDT version field. RFC 3926
* clause 3.4.1 requires 1 for a version 1 session and RFC 6726
* clause 3.4.1 requires 2 for a version 2 session; the two are
* different protocols, see RFC 6726 clause 11.1. Defaults to 1, which
* is what TS 26.346 selects, so a 3GPP session needs no change.
*/
/**
* Tag selecting the data-less Close Session packet constructor below.
Expand All @@ -68,7 +73,8 @@ namespace LibFlute {
AlcPacket(uint64_t tsi, CloseSession);

AlcPacket(uint64_t tsi, uint16_t toi, FecOti fec_oti, const std::vector<EncodingSymbol>& symbols, size_t max_size, uint32_t fdt_instance_id,
bool close_session_flag = false, bool close_object_flag = false);
bool close_session_flag = false, bool close_object_flag = false,
uint8_t flute_version = 1);

/**
* Default destructor.
Expand Down Expand Up @@ -160,6 +166,7 @@ namespace LibFlute {

char* _buffer = nullptr;
size_t _len;
uint8_t _flute_version = 1;

// RFC 3451 clause 5.1 - LCT Header Format
struct __attribute__((packed)) lct_header_t {
Expand Down
28 changes: 24 additions & 4 deletions include/FileDeliveryTable.h
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ namespace LibFlute {
FDT_NS_NONE = 0,
FDT_NS_RFC3926,
FDT_NS_DRAFT_2005,
// FDT_NS_RFC6726, // FLUTE v2 - will need other things implementing to use this correctly
FDT_NS_RFC6726, //< RFC 6726 FLUTE v2 namespace. Not for 3GPP MBMS use.
FDT_NS_3GPP_CONSOLIDATED_V2
};

Expand All @@ -48,7 +48,7 @@ namespace LibFlute {
* @param fdt_namespace The XML namespace to use for FDT
*/
FileDeliveryTable(uint32_t instance_id, FecOti fec_oti, FdtNamespace fdt_namespace = FDT_NS_NONE,
Profile profile = Profile::Ts26517);
Profile profile = Profile::Ts26517, uint8_t flute_version = 1);

/**
* Parse an XML string and create a FDT class from it
Expand All @@ -57,7 +57,7 @@ namespace LibFlute {
* @param buffer String containing the FDT XML
* @param len Length of the buffer
*/
FileDeliveryTable(uint32_t instance_id, char* buffer, size_t len);
FileDeliveryTable(uint32_t instance_id, char* buffer, size_t len, uint8_t flute_version = 1);

/**
* Default destructor.
Expand All @@ -82,6 +82,14 @@ namespace LibFlute {
/** The FDT-Instance Expires attribute, in NTP-epoch seconds. */
uint64_t expires() const { return _expires; }

/**
* Select the FLUTE version whose rules this table follows. Version 1 (RFC 3926) is the
* default; version 2 (RFC 6726) changes the FDT Instance ID sequence and how the Expires
* field is read. Set before the first instance is sent.
*/
void set_flute_version(uint8_t version);
uint8_t flute_version() const { return _flute_version; };

/** 20-bit field width (RFC 3926 clause 3.4.1, "FDT Instance ID, 20 bits"). */
static constexpr uint32_t kMaxFdtInstanceId = 0xFFFFF;

Expand All @@ -90,7 +98,18 @@ namespace LibFlute {
* function so the wraparound is testable without a live session.
*/
static uint32_t next_instance_id(uint32_t current, uint64_t current_expires, uint64_t now,
std::map<uint32_t, uint64_t>& expired_instance_ids);
std::map<uint32_t, uint64_t>& expired_instance_ids,
uint8_t flute_version = 1);

/**
* Interpret a 32-bit NTP expiry against the era it belongs to.
*
* RFC 6726 clause 3.3 describes how a version 2 peer reads the field: "both a sender and a
* receiver easily determine to which (136-year) epoch the FDT Instance expiration time value
* pertains by choosing the epoch for which the expiration time is closest in time to the
* current time." Version 1 states no such rule, so this is applied only for version 2.
*/
static uint64_t expiry_in_nearest_era(uint64_t wire_value, uint64_t now);

/**
* Which obligation set this FDT is emitted under. See Profile.
Expand Down Expand Up @@ -216,6 +235,7 @@ namespace LibFlute {
FecOti _global_fec_oti;

uint64_t _expires;
uint8_t _flute_version = 1;
bool _complete = false;

FdtNamespace _fdt_namespace;
Expand Down
19 changes: 19 additions & 0 deletions include/Receiver.h
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,24 @@ namespace LibFlute {
*/
class Receiver {
public:
/**
* Select the FLUTE protocol version for this session.
*
* RFC 3926 clause 3.4.1 requires the EXT_FDT version field to be 1 in a version 1 session,
* and RFC 6726 clause 3.4.1 requires 2 in a version 2 session. They are separate protocols:
* RFC 6726 clause 11.1 records that version 1 uses RFC 3451 and version 2 uses RFC 5651,
* "Therefore, an implementation that relies on [RFC3926] and RFC 3451 will not be backwards
* compatible with FLUTE as specified in this document."
*
* The default is 1. TS 26.346 V18.2.0 clause L.2 selects RFC 3926, so a 3GPP MBMS download
* session must leave this at 1.
*
* Version 2 is incompletely implemented here: see the note in this branch's README on the
* RFC 6726 obligations that are not met.
*/
void set_flute_version(uint8_t version) { _flute_version = version; };
uint8_t flute_version() const { return _flute_version; };

/**
* Definition of a file reception completion callback function that can be
* registered through ::register_completion_callback.
Expand Down Expand Up @@ -137,6 +155,7 @@ namespace LibFlute {
enum { max_length = 65536 };
char _data[max_length];
uint64_t _tsi;
uint8_t _flute_version = 1;
std::unique_ptr<LibFlute::FileDeliveryTable> _fdt;
// FDT instance currently being reassembled at TOI 0 (0xFFFFFFFF = none).
// Used to discard a partial FDT object when a newer instance starts
Expand Down
20 changes: 20 additions & 0 deletions include/Transmitter.h
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,25 @@ namespace LibFlute {
*/
using FdtNamespace = FileDeliveryTable::FdtNamespace;

/**
* Select the FLUTE protocol version for this session.
*
* RFC 3926 clause 3.4.1 requires the EXT_FDT version field to be 1 in a version 1 session,
* and RFC 6726 clause 3.4.1 requires 2 in a version 2 session. They are separate protocols:
* RFC 6726 clause 11.1 records that version 1 uses RFC 3451 and version 2 uses RFC 5651,
* "Therefore, an implementation that relies on [RFC3926] and RFC 3451 will not be backwards
* compatible with FLUTE as specified in this document."
*
* The default is 1. TS 26.346 V18.2.0 clause L.2 selects RFC 3926, so a 3GPP MBMS download
* session must leave this at 1.
*
* Version 2 is incompletely implemented here: see the note in this branch's README on the
* RFC 6726 obligations that are not met.
*/
void set_flute_version(uint8_t version);
uint8_t flute_version() const { return _flute_version; };



/**
* File Description object
Expand Down Expand Up @@ -725,6 +744,7 @@ namespace LibFlute {
boost::asio::steady_timer _fdt_timer;

uint64_t _tsi;
uint8_t _flute_version = 1;
uint16_t _mtu;

std::unique_ptr<FileDeliveryTable> _fdt;
Expand Down
Loading