Add ability to add optional attributes to session packet without breaking protocol format

This commit is contained in:
Vasily Evseenko 2024-08-01 19:11:21 +03:00
parent 9c46b4230a
commit d2ef1edfea
6 changed files with 154 additions and 51 deletions

View File

@ -1,6 +1,6 @@
% WFB-NG Data Transport Standard [Draft]
% Vasily Evseenko <<svpcom@p2ptech.org>>
% Sep 13, 2022
% Aug 1, 2024
## Introduction
@ -15,7 +15,7 @@ using ordinary wifi adapters that support the transmission of "raw" packets. At
## Areas of use:
- Communication between robots and ground station
- Communication of amateur satellites (CUBESAT) with the earth
- Communication of amateur satellites (CUBESAT) with the Earth
- Digital radio communication on the ground
- ...
@ -76,6 +76,7 @@ There are two packet types
2. Session packet (`packet_type = 2`, has encrypted and authenticated session parameters and session key, see note below)
Currently only supported FEC type is Reed-Solomon on Vandermonde matrix, but new FEC algorithms can be added in future.
Session packet can have any amount of optional tags. Receiver should ignore all unknown or unused tags.
``` .c
// FEC types
@ -105,7 +106,13 @@ Currently only supported FEC type is Reed-Solomon on Vandermonde matrix, but new
+-- encrypted and authenticated by session key
2. Session packet:
wsession_hdr_t { packet_type = 2, nonce = random() }
wsession_data_t { epoch, channel_id, fec_type, fec_k, fec_n, session_key } # -- encrypted and authenticated using crypto_box_easy(rx_publickey, tx_secretkey)
wsession_data_t { epoch, channel_id, #
fec_type, fec_k, fec_n, #
session_key, #
optional TLV list } # -- encrypted and authenticated using crypto_box_easy(rx_publickey, tx_secretkey)
Where TLV list is a list of optional tags with the following format:
[{tag_id : tag_size : <tag_size bytes of value>}, ... ]
data nonce: 56bit block_idx + 8bit fragment_idx
session nonce: crypto_box_NONCEBYTES of random bytes
@ -129,8 +136,16 @@ Currently only supported FEC type is Reed-Solomon on Vandermonde matrix, but new
uint8_t k; // FEC k
uint8_t n; // FEC n
uint8_t session_key[crypto_aead_chacha20poly1305_KEYBYTES];
uint8_t tags[]; // Optional TLV attributes
} __attribute__ ((packed)) wsession_data_t;
// TLV item header
typedef struct {
uint8_t id;
uint16_t len;
uint8_t value[];
} __attribute__ ((packed)) tlv_hdr_t;
// Data packet. Embed FEC-encoded data
typedef struct {

View File

@ -508,12 +508,35 @@ void Aggregator::log_rssi(const sockaddr_in *sockaddr, uint8_t wlan_idx, const u
}
}
int Aggregator::get_tag(const void *buf, size_t size, uint8_t tag_id, void *value, size_t value_size)
{
tlv_hdr_t *p = (tlv_hdr_t*)buf;
void *end = (uint8_t*)buf + size;
while((void*)(p + 1) <= end)
{
if(p->id != tag_id)
{
p = (tlv_hdr_t*)((uint8_t*)(p + 1) + p->len);
continue;
}
if(p->len > value_size) return -1;
if(p->value + p->len > end) return -1;
memcpy(value, p->value, p->len);
return p->len;
}
return -1;
}
void Aggregator::process_packet(const uint8_t *buf, size_t size, uint8_t wlan_idx, const uint8_t *antenna,
const int8_t *rssi, const int8_t *noise, uint16_t freq, uint8_t mcs_index,
uint8_t bandwidth, sockaddr_in *sockaddr)
{
wsession_data_t new_session_data;
uint8_t session_tmp[MAX_SESSION_PACKET_SIZE - crypto_box_MACBYTES - sizeof(wsession_hdr_t)];
wsession_data_t* new_session_data = NULL;
//size_t new_session_tags_size = 0;
count_p_all += 1;
count_b_all += size;
@ -537,17 +560,20 @@ void Aggregator::process_packet(const uint8_t *buf, size_t size, uint8_t wlan_id
}
break;
case WFB_PACKET_KEY:
if(size != sizeof(wsession_hdr_t) + sizeof(wsession_data_t) + crypto_box_MACBYTES)
case WFB_PACKET_SESSION:
new_session_data = (wsession_data_t*)session_tmp;
if(size < sizeof(wsession_hdr_t) + sizeof(wsession_data_t) + crypto_box_MACBYTES || \
size > MAX_SESSION_PACKET_SIZE)
{
fprintf(stderr, "Invalid session key packet\n");
count_p_bad += 1;
return;
}
if(crypto_box_open_easy((uint8_t*)&new_session_data,
if(crypto_box_open_easy((uint8_t*)session_tmp,
buf + sizeof(wsession_hdr_t),
sizeof(wsession_data_t) + crypto_box_MACBYTES,
size - sizeof(wsession_hdr_t),
((wsession_hdr_t*)buf)->session_nonce,
tx_publickey, rx_secretkey) != 0)
{
@ -556,37 +582,39 @@ void Aggregator::process_packet(const uint8_t *buf, size_t size, uint8_t wlan_id
return;
}
if (be64toh(new_session_data.epoch) < epoch)
//new_session_tags_size = size - (sizeof(wsession_hdr_t) + sizeof(wsession_data_t) + crypto_box_MACBYTES);
if (be64toh(new_session_data->epoch) < epoch)
{
fprintf(stderr, "Session epoch doesn't match: %" PRIu64 " < %" PRIu64 "\n", be64toh(new_session_data.epoch), epoch);
fprintf(stderr, "Session epoch doesn't match: %" PRIu64 " < %" PRIu64 "\n", be64toh(new_session_data->epoch), epoch);
count_p_dec_err += 1;
return;
}
if (be32toh(new_session_data.channel_id) != channel_id)
if (be32toh(new_session_data->channel_id) != channel_id)
{
fprintf(stderr, "Session channel_id doesn't match: %u != %u\n", be32toh(new_session_data.channel_id), channel_id);
fprintf(stderr, "Session channel_id doesn't match: %u != %u\n", be32toh(new_session_data->channel_id), channel_id);
count_p_dec_err += 1;
return;
}
if (new_session_data.fec_type != WFB_FEC_VDM_RS)
if (new_session_data->fec_type != WFB_FEC_VDM_RS)
{
fprintf(stderr, "Unsupported FEC codec type: %d\n", new_session_data.fec_type);
fprintf(stderr, "Unsupported FEC codec type: %d\n", new_session_data->fec_type);
count_p_dec_err += 1;
return;
}
if (new_session_data.n < 1)
if (new_session_data->n < 1)
{
fprintf(stderr, "Invalid FEC N: %d\n", new_session_data.n);
fprintf(stderr, "Invalid FEC N: %d\n", new_session_data->n);
count_p_dec_err += 1;
return;
}
if (new_session_data.k < 1 || new_session_data.k > new_session_data.n)
if (new_session_data->k < 1 || new_session_data->k > new_session_data->n)
{
fprintf(stderr, "Invalid FEC K: %d\n", new_session_data.k);
fprintf(stderr, "Invalid FEC K: %d\n", new_session_data->k);
count_p_dec_err += 1;
return;
}
@ -594,22 +622,23 @@ void Aggregator::process_packet(const uint8_t *buf, size_t size, uint8_t wlan_id
count_p_dec_ok += 1;
log_rssi(sockaddr, wlan_idx, antenna, rssi, noise, freq, mcs_index, bandwidth);
if (memcmp(session_key, new_session_data.session_key, sizeof(session_key)) != 0)
if (memcmp(session_key, new_session_data->session_key, sizeof(session_key)) != 0)
{
epoch = be64toh(new_session_data.epoch);
memcpy(session_key, new_session_data.session_key, sizeof(session_key));
epoch = be64toh(new_session_data->epoch);
memcpy(session_key, new_session_data->session_key, sizeof(session_key));
if (fec_p != NULL)
{
deinit_fec();
}
init_fec(new_session_data.k, new_session_data.n);
init_fec(new_session_data->k, new_session_data->n);
fprintf(stdout, "%" PRIu64 "\tSESSION\t%" PRIu64 ":%u:%d:%d\n", get_time_ms(), epoch, WFB_FEC_VDM_RS, fec_k, fec_n);
fflush(stdout);
}
return;
default:

View File

@ -217,6 +217,8 @@ private:
const int8_t *noise, uint16_t freq, uint8_t mcs_index, uint8_t bandwidth);
int get_block_ring_idx(uint64_t block_idx);
int rx_ring_push(void);
int get_tag(const void *buf, size_t size, uint8_t tag_id, void *value, size_t value_size);
fec_t* fec_p;
int fec_k; // RS number of primary fragments in block
int fec_n; // RS total number of fragments in block

View File

@ -49,13 +49,19 @@ using namespace std;
#include "tx.hpp"
Transmitter::Transmitter(int k, int n, const string &keypair, uint64_t epoch, uint32_t channel_id, uint32_t fec_delay) : \
Transmitter::Transmitter(int k, int n, const string &keypair, uint64_t epoch, uint32_t channel_id, uint32_t fec_delay, std::vector<tags_item_t> &tags) : \
fec_k(k), fec_n(n), block_idx(0),
fragment_idx(0),
max_packet_size(0),
epoch(epoch),
channel_id(channel_id),
fec_delay(fec_delay)
fec_delay(fec_delay),
tx_secretkey{},
rx_publickey{},
session_key{},
session_packet{},
session_packet_size(0),
tags(tags)
{
fec_p = fec_new(fec_k, fec_n);
@ -97,33 +103,59 @@ Transmitter::~Transmitter()
}
void Transmitter::make_session_key(void)
void Transmitter::make_session_key()
{
// init session key
randombytes_buf(session_key, sizeof(session_key));
// fill packet header
wsession_hdr_t *session_hdr = (wsession_hdr_t *)session_key_packet;
session_hdr->packet_type = WFB_PACKET_KEY;
wsession_hdr_t *session_hdr = (wsession_hdr_t *)session_packet;
session_hdr->packet_type = WFB_PACKET_SESSION;
randombytes_buf(session_hdr->session_nonce, sizeof(session_hdr->session_nonce));
// fill packet contents
wsession_data_t session_data = { .epoch = htobe64(epoch),
.channel_id = htobe32(channel_id),
.fec_type = WFB_FEC_VDM_RS,
.k = (uint8_t)fec_k,
.n = (uint8_t)fec_n,
};
memcpy(session_data.session_key, session_key, sizeof(session_key));
uint8_t tmp[MAX_SESSION_PACKET_SIZE - crypto_box_MACBYTES - sizeof(wsession_hdr_t)];
if (crypto_box_easy(session_key_packet + sizeof(wsession_hdr_t),
(uint8_t*)&session_data, sizeof(session_data),
// Fill fixed headers
{
wsession_data_t* session_data = (wsession_data_t*)tmp;
assert(sizeof(*session_data) <= sizeof(tmp));
session_data->epoch = htobe64(epoch);
session_data->channel_id = htobe32(channel_id);
session_data->fec_type = WFB_FEC_VDM_RS;
session_data->k = (uint8_t)fec_k;
session_data->n = (uint8_t)fec_n;
assert(sizeof(session_data->session_key) == sizeof(session_key));
memcpy(session_data->session_key, session_key, sizeof(session_key));
}
// Fill optional Tags
uint32_t session_data_size = sizeof(wsession_data_t);
for(auto it=tags.begin(); it != tags.end(); it++)
{
tlv_hdr_t* tlv = (tlv_hdr_t*)((uint8_t*)tmp + session_data_size);
session_data_size += sizeof(tlv_hdr_t) + it->len;
assert(session_data_size <= sizeof(tmp));
tlv->id = it->id;
tlv->len = it->len;
memcpy(tlv->value, it->value, it->len);
}
if (crypto_box_easy(session_packet + sizeof(wsession_hdr_t),
(uint8_t*)tmp, session_data_size,
session_hdr->session_nonce, rx_publickey, tx_secretkey) != 0)
{
throw runtime_error("Unable to make session key!");
}
session_packet_size = sizeof(wsession_hdr_t) + session_data_size + crypto_box_MACBYTES;
assert(session_packet_size <= MAX_SESSION_PACKET_SIZE);
}
void RawSocketTransmitter::set_mark(uint32_t idx)
@ -144,9 +176,9 @@ void RawSocketTransmitter::set_mark(uint32_t idx)
RawSocketTransmitter::RawSocketTransmitter(int k, int n, const string &keypair, uint64_t epoch, uint32_t channel_id, uint32_t fec_delay,
const vector<string> &wlans, shared_ptr<uint8_t[]> radiotap_header, size_t radiotap_header_len,
std::vector<tags_item_t> &tags, const vector<string> &wlans, shared_ptr<uint8_t[]> radiotap_header, size_t radiotap_header_len,
uint8_t frame_type, bool use_qdisc, uint32_t fwmark) : \
Transmitter(k, n, keypair, epoch, channel_id, fec_delay),
Transmitter(k, n, keypair, epoch, channel_id, fec_delay, tags),
channel_id(channel_id),
current_output(0),
ieee80211_seq(0),
@ -335,7 +367,7 @@ void Transmitter::send_block_fragment(size_t packet_size)
void Transmitter::send_session_key(void)
{
//fprintf(stderr, "Announce session key\n");
inject_packet((uint8_t*)session_key_packet, sizeof(session_key_packet));
inject_packet((uint8_t*)session_packet, session_packet_size);
}
bool Transmitter::send_packet(const uint8_t *buf, size_t size, uint8_t flags)
@ -890,6 +922,7 @@ int main(int argc, char * const *argv)
fflush(stdout);
}
std::vector<tags_item_t> tags;
shared_ptr<Transmitter> t;
uint32_t channel_id = (link_id << 8) + radio_port;
@ -898,9 +931,9 @@ int main(int argc, char * const *argv)
{
fprintf(stderr, "Using %zu ports from %d for wlan emulation\n", wlans.size(), debug_port);
t = shared_ptr<UdpTransmitter>(new UdpTransmitter(k, n, keypair, "127.0.0.1", debug_port, epoch, channel_id,
fec_delay, use_qdisc, fwmark));
fec_delay, tags, use_qdisc, fwmark));
} else {
t = shared_ptr<RawSocketTransmitter>(new RawSocketTransmitter(k, n, keypair, epoch, channel_id, fec_delay,
t = shared_ptr<RawSocketTransmitter>(new RawSocketTransmitter(k, n, keypair, epoch, channel_id, fec_delay, tags,
wlans, radiotap_header, radiotap_header_len,
frame_type, use_qdisc, fwmark));
}

View File

@ -33,7 +33,7 @@
class Transmitter
{
public:
Transmitter(int k, int m, const std::string &keypair, uint64_t epoch, uint32_t channel_id, uint32_t fec_delay);
Transmitter(int k, int m, const std::string &keypair, uint64_t epoch, uint32_t channel_id, uint32_t fec_delay, std::vector<tags_item_t> &tags);
virtual ~Transmitter();
bool send_packet(const uint8_t *buf, size_t size, uint8_t flags);
void send_session_key(void);
@ -62,7 +62,9 @@ private:
uint8_t tx_secretkey[crypto_box_SECRETKEYBYTES];
uint8_t rx_publickey[crypto_box_PUBLICKEYBYTES];
uint8_t session_key[crypto_aead_chacha20poly1305_KEYBYTES];
uint8_t session_key_packet[sizeof(wsession_hdr_t) + sizeof(wsession_data_t) + crypto_box_MACBYTES];
uint8_t session_packet[MAX_SESSION_PACKET_SIZE];
uint16_t session_packet_size;
std::vector<tags_item_t> &tags;
};
class txAntennaItem
@ -108,8 +110,9 @@ typedef std::unordered_map<uint64_t, txAntennaItem> tx_antenna_stat_t;
class RawSocketTransmitter : public Transmitter
{
public:
RawSocketTransmitter(int k, int m, const std::string &keypair, uint64_t epoch, uint32_t channel_id, uint32_t fec_delay, const std::vector<std::string> &wlans,
shared_ptr<uint8_t[]> radiotap_header, size_t radiotap_header_len, uint8_t frame_type, bool use_qdisc, uint32_t fwmark);
RawSocketTransmitter(int k, int m, const std::string &keypair, uint64_t epoch, uint32_t channel_id, uint32_t fec_delay, std::vector<tags_item_t> &tags,
const std::vector<std::string> &wlans, shared_ptr<uint8_t[]> radiotap_header, size_t radiotap_header_len,
uint8_t frame_type, bool use_qdisc, uint32_t fwmark);
virtual ~RawSocketTransmitter();
virtual void select_output(int idx)
{
@ -143,8 +146,8 @@ class UdpTransmitter : public Transmitter
{
public:
UdpTransmitter(int k, int m, const std::string &keypair, const std::string &client_addr, int base_port, uint64_t epoch, uint32_t channel_id,
uint32_t fec_delay, bool use_qdisc, uint32_t fwmark): \
Transmitter(k, m, keypair, epoch, channel_id, fec_delay), base_port(base_port), use_qdisc(use_qdisc), fwmark(fwmark)
uint32_t fec_delay, std::vector<tags_item_t> &tags, bool use_qdisc, uint32_t fwmark): \
Transmitter(k, m, keypair, epoch, channel_id, fec_delay, tags), base_port(base_port), use_qdisc(use_qdisc), fwmark(fwmark)
{
sockfd = socket(AF_INET, SOCK_DGRAM, 0);
if (sockfd < 0) throw std::runtime_error(string_format("Error opening socket: %s", strerror(errno)));

View File

@ -170,7 +170,10 @@ static const uint8_t ieee80211_header[] __attribute__((unused)) = {
+-- encrypted and authenticated by session key
2. Session packet:
wsession_hdr_t { packet_type = 2, nonce = random() }
wsession_data_t { epoch, channel_id, fec_type, fec_k, fec_n, session_key } # -- encrypted and signed using rx and tx keys
wsession_data_t { epoch, channel_id, #
fec_type, fec_k, fec_n, #
session_key, #
optional TLV list } # -- encrypted and signed using rx and tx keys
*/
// data nonce: 56bit block_idx + 8bit fragment_idx
@ -180,8 +183,8 @@ static const uint8_t ieee80211_header[] __attribute__((unused)) = {
#define MAX_BLOCK_IDX ((1LLU << 55) - 1)
// packet types
#define WFB_PACKET_DATA 0x1
#define WFB_PACKET_KEY 0x2
#define WFB_PACKET_DATA 0x1
#define WFB_PACKET_SESSION 0x2
// FEC types
#define WFB_FEC_VDM_RS 0x1 //Reed-Solomon on Vandermonde matrix
@ -213,15 +216,32 @@ typedef struct {
uint8_t session_nonce[crypto_box_NONCEBYTES]; // random data
} __attribute__ ((packed)) wsession_hdr_t;
typedef struct{
typedef struct {
uint64_t epoch; // Drop session packets from old epoch
uint32_t channel_id; // (link_id << 8) + port_number
uint8_t fec_type; // Now only supported type is WFB_FEC_VDM_RS
uint8_t k; // FEC k
uint8_t n; // FEC n
uint8_t session_key[crypto_aead_chacha20poly1305_KEYBYTES];
uint8_t tags[]; // Optional TLV attributes
} __attribute__ ((packed)) wsession_data_t;
// TLV attr header (in packet)
typedef struct {
uint8_t id;
uint16_t len;
uint8_t value[];
} __attribute__ ((packed)) tlv_hdr_t;
// TLV item
typedef struct {
uint8_t id;
uint16_t len;
void* value;
} tags_item_t;
// Data packet. Embed FEC-encoded data
typedef struct {
@ -239,6 +259,7 @@ typedef struct {
#define MAX_PAYLOAD_SIZE (WIFI_MTU - sizeof(ieee80211_header) - sizeof(wblock_hdr_t) - crypto_aead_chacha20poly1305_ABYTES - sizeof(wpacket_hdr_t))
#define MAX_FEC_PAYLOAD (WIFI_MTU - sizeof(ieee80211_header) - sizeof(wblock_hdr_t) - crypto_aead_chacha20poly1305_ABYTES)
#define MAX_FORWARDER_PACKET_SIZE (WIFI_MTU - sizeof(ieee80211_header))
#define MAX_SESSION_PACKET_SIZE (WIFI_MTU - sizeof(ieee80211_header))
int open_udp_socket_for_rx(int port, int rcv_buf_size);
uint64_t get_time_ms(void);