WIP: Layer 2-Implementierung #6
|
@ -0,0 +1,182 @@
|
|||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "ham64.h"
|
||||
|
||||
static uint16_t char_to_ham64(char c)
|
||||
{
|
||||
if(c == 0) {
|
||||
return 0;
|
||||
} else if(c >= 'A' && c <= 'Z') {
|
||||
return 1 + c - 'A';
|
||||
} else if(c >= 'a' && c <= 'z') {
|
||||
return 1 + c - 'a';
|
||||
} else if(c >= '0' && c <= '9') {
|
||||
return 27 + c - '0';
|
||||
} else if(c == '/') {
|
||||
return 37;
|
||||
} else if(c == '-') {
|
||||
return 38;
|
||||
} else if(c == '^') {
|
||||
return 39;
|
||||
} else {
|
||||
return 0xFFFF; // undefined character
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static char ham64_to_char(uint16_t v) {
|
||||
if(v == 0) {
|
||||
return 0;
|
||||
} else if(v >= 1 && v <= 26) {
|
||||
return 'A' + v - 1;
|
||||
} else if(v >= 27 && v <= 36) {
|
||||
return '0' + v - 27;
|
||||
} else if(v == 37) {
|
||||
return '/';
|
||||
} else if(v == 38) {
|
||||
return '-';
|
||||
} else if(v == 39) {
|
||||
return '^';
|
||||
} else {
|
||||
return '?';
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
size_t ham64_encode(const char *call, ham64_t *ham64)
|
||||
{
|
||||
size_t chars_in_tmp = 0;
|
||||
size_t ham64_len = 0;
|
||||
|
||||
uint16_t *tmp = ham64->addr;
|
||||
|
||||
memset(ham64->addr, 0, sizeof(ham64->addr));
|
||||
|
||||
while(*call) {
|
||||
uint16_t encoded = char_to_ham64(*call);
|
||||
if(encoded == 0xFFFF) {
|
||||
// drop non-encodable characters
|
||||
continue;
|
||||
}
|
||||
|
||||
*tmp *= 40;
|
||||
*tmp += encoded;
|
||||
chars_in_tmp++;
|
||||
|
||||
if(chars_in_tmp == 3) {
|
||||
ham64_len++;
|
||||
tmp++;
|
||||
|
||||
chars_in_tmp = 0;
|
||||
}
|
||||
|
||||
call++;
|
||||
}
|
||||
|
||||
// finalize a partially filled word
|
||||
if(chars_in_tmp != 0) {
|
||||
while(chars_in_tmp < 3) {
|
||||
*tmp *= 40;
|
||||
chars_in_tmp++;
|
||||
}
|
||||
|
||||
ham64_len++;
|
||||
}
|
||||
|
||||
ham64->length = ham64_len;
|
||||
return ham64_len;
|
||||
}
|
||||
|
||||
|
||||
size_t ham64_decode_callsign(const ham64_t *ham64, char *call)
|
||||
{
|
||||
if(ham64->length > 4 || ham64->length == 0) {
|
||||
// unsupported length
|
||||
return 0;
|
||||
}
|
||||
|
||||
if(ham64->addr[0] < 0x0640 || ham64->addr[0] >= 0xFA00) {
|
||||
// special address, must not be decoded here
|
||||
return 0;
|
||||
}
|
||||
|
||||
size_t call_len = 0;
|
||||
|
||||
for(size_t i = 0; i < ham64->length; i++) {
|
||||
// big endian decoding
|
||||
uint16_t chunk = ham64->addr[i];
|
||||
|
||||
if(chunk < 0x0640 || chunk >= 0xFA00) {
|
||||
// invalid word
|
||||
return 0;
|
||||
}
|
||||
|
||||
for(uint16_t div = 1600; div != 0; div /= 40) {
|
||||
*call = ham64_to_char((chunk / div) % 40);
|
||||
if(!*call) {
|
||||
return call_len;
|
||||
}
|
||||
|
||||
call_len++;
|
||||
call++;
|
||||
}
|
||||
}
|
||||
|
||||
*call = '\0';
|
||||
return call_len;
|
||||
}
|
||||
|
||||
|
||||
const char *ham64_addr_type_to_string(ham64_addr_type_t addr_type)
|
||||
{
|
||||
static const char *type_strings[HAM64_NUM_ADDR_TYPES] = {
|
||||
"EMPTY",
|
||||
"TMP_SHORT",
|
||||
"CALLSIGN",
|
||||
"BROADCAST",
|
||||
"IPV6_MULTICAST",
|
||||
"IPV4_MULTICAST",
|
||||
"RESERVED",
|
||||
};
|
||||
|
||||
if(addr_type >= HAM64_NUM_ADDR_TYPES) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return type_strings[addr_type];
|
||||
}
|
||||
|
||||
|
||||
ham64_addr_type_t ham64_get_addr_type(const ham64_t *ham64)
|
||||
{
|
||||
uint16_t first_word = ham64->addr[0];
|
||||
uint8_t first_byte = first_word >> 8;
|
||||
|
||||
if(first_word >= 0x0640 && first_word < 0xFA00) {
|
||||
return HAM64_ADDR_TYPE_CALLSIGN;
|
||||
} else if(first_word == 0x0000) {
|
||||
return HAM64_ADDR_TYPE_EMPTY;
|
||||
} else if(first_word < 0x0640) {
|
||||
return HAM64_ADDR_TYPE_TMP_SHORT;
|
||||
} else if(first_word == 0xFFFF) {
|
||||
return HAM64_ADDR_TYPE_BROADCAST;
|
||||
} else if(first_byte == 0xFA) {
|
||||
return HAM64_ADDR_TYPE_IPV6_MULTICAST;
|
||||
} else if(first_byte == 0xFB) {
|
||||
return HAM64_ADDR_TYPE_IPV4_MULTICAST;
|
||||
} else {
|
||||
return HAM64_ADDR_TYPE_RESERVED;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void ham64_format(const ham64_t *ham64, char *out)
|
||||
{
|
||||
for(uint8_t i = 0; i < ham64->length; i++) {
|
||||
rudi_s marked this conversation as resolved
|
||||
const char *fmt = (i == 3) ? "%04X" : "%04X-";
|
||||
sprintf(out + 5 * i, fmt, ham64->addr[i]);
|
||||
}
|
||||
|
||||
out[5*ham64->length - 1] = '\0';
|
||||
}
|
|
@ -0,0 +1,59 @@
|
|||
#ifndef HAM64_H
|
||||
#define HAM64_H
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
// buffer size required for the string representation of a maximum-length HAM64
|
||||
// address, including terminating zero.
|
||||
#define HAM64_FMT_MAX_LEN (4*4 + 3 + 1)
|
||||
|
||||
typedef enum {
|
||||
HAM64_ADDR_TYPE_EMPTY,
|
||||
HAM64_ADDR_TYPE_TMP_SHORT,
|
||||
HAM64_ADDR_TYPE_CALLSIGN,
|
||||
HAM64_ADDR_TYPE_BROADCAST,
|
||||
HAM64_ADDR_TYPE_IPV6_MULTICAST,
|
||||
HAM64_ADDR_TYPE_IPV4_MULTICAST,
|
||||
HAM64_ADDR_TYPE_RESERVED,
|
||||
|
||||
HAM64_NUM_ADDR_TYPES
|
||||
} ham64_addr_type_t;
|
||||
|
||||
typedef struct ham64_s {
|
||||
uint16_t addr[4];
|
||||
uint8_t length;
|
||||
} ham64_t;
|
||||
|
||||
/*!\brief Encode a call sign as HAM64 address.
|
||||
* \param call The call sign as null-terminated string.
|
||||
* \param ham64 Pointer to the memory for the output address.
|
||||
* \returns The length in words of the encoded address. Zero if an error occurred.
|
||||
*/
|
||||
size_t ham64_encode(const char *call, ham64_t *ham64);
|
||||
|
||||
/*!\brief Decode a ham64 address to a readable call sign.
|
||||
* \param ham64 Pointer to the address to be decoded.
|
||||
* \param call Pointer to the output buffer (at least 13 characters in size).
|
||||
* \returns The length of the output string or zero in case of errors.
|
||||
*/
|
||||
size_t ham64_decode_callsign(const ham64_t *ham64, char *call);
|
||||
|
||||
/*!\brief Determine the type of a HAM64 address.
|
||||
* \param ham64 Pointer to the address to identify.
|
||||
* \returns The type of the address.
|
||||
*/
|
||||
ham64_addr_type_t ham64_get_addr_type(const ham64_t *ham64);
|
||||
|
||||
/*!\brief Get a string representation of the given address type.
|
||||
*/
|
||||
const char *ham64_addr_type_to_string(ham64_addr_type_t addr_type);
|
||||
|
||||
/*!\brief Format a ham64 address into a string for printing.
|
||||
* \param ham64 Pointer to the address to be formatted.
|
||||
* \param out Pointer to the buffer where the string representation will
|
||||
* be stored. Must be at least \ref HAM64_FMT_MAX_LEN bytes long.
|
||||
*/
|
||||
void ham64_format(const ham64_t *ham64, char *out);
|
||||
|
||||
#endif // HAM64_H
|
|
@ -0,0 +1,64 @@
|
|||
#include <assert.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "packet_structs.h"
|
||||
|
||||
|
||||
size_t layer2_encode_packet_header(const layer2_packet_header_t *header, uint8_t *encoded)
|
||||
{
|
||||
assert(header->src_addr.length <= 4 && header->src_addr.length != 0);
|
||||
assert(header->dst_addr.length <= 4 && header->dst_addr.length != 0);
|
||||
assert(header->tx_seq_nr < 16);
|
||||
assert(header->rx_seq_nr < 16);
|
||||
|
||||
size_t encoded_size = 2;
|
||||
|
||||
encoded[0] =
|
||||
((uint8_t)header->msg_type & 0x7) << 5
|
||||
| ((uint8_t)header->tx_request & 0x1) << 4
|
||||
| ((header->src_addr.length-1) & 0x3) << 2
|
||||
| ((header->dst_addr.length-1) & 0x3) << 0;
|
||||
|
||||
encoded[1] =
|
||||
(header->tx_seq_nr & 0xF) << 4
|
||||
| (header->rx_seq_nr & 0xF) << 0;
|
||||
|
||||
memcpy(encoded + encoded_size, header->src_addr.addr, 2 * header->src_addr.length);
|
||||
encoded_size += 2 * header->src_addr.length;
|
||||
|
||||
memcpy(encoded + encoded_size, header->dst_addr.addr, 2 * header->dst_addr.length);
|
||||
encoded_size += 2 * header->dst_addr.length;
|
||||
|
||||
return encoded_size;
|
||||
}
|
||||
|
||||
|
||||
bool layer2_decode_packet_header(const uint8_t *encoded, size_t encoded_len, layer2_packet_header_t *header)
|
||||
{
|
||||
// check if there are enough bytes for the minimum header size:
|
||||
// - 1 byte packet info
|
||||
// - 1 byte sequence numbers
|
||||
// - 2 bytes source address
|
||||
// - 2 bytes destination address
|
||||
if(encoded_len < 6) {
|
||||
return false;
|
||||
}
|
||||
|
||||
header->msg_type = (encoded[0] >> 5) & 0x7;
|
||||
header->tx_request = (encoded[0] & 0x10) != 0;
|
||||
header->src_addr.length = (encoded[0] >> 2) & 0x3 + 1;
|
||||
header->dst_addr.length = (encoded[0] >> 0) & 0x3 + 1;
|
||||
|
||||
// check for the actually needed size
|
||||
if(encoded_len < (2 + 2*header->src_addr.length + 2*header->dst_addr.length)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
header->tx_seq_nr = (encoded[1] >> 4) & 0xF;
|
||||
header->tx_seq_nr = (encoded[1] >> 0) & 0xF;
|
||||
|
||||
memcpy(header->src_addr.addr, encoded + 2, 2 * header->src_addr.length);
|
||||
memcpy(header->dst_addr.addr, encoded + 2 + 2 * header->src_addr.length, 2 * header->dst_addr.length);
|
||||
|
||||
return true;
|
||||
}
|
|
@ -0,0 +1,59 @@
|
|||
#ifndef PACKET_STRUCTS_H
|
||||
#define PACKET_STRUCTS_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#include "layer2/ham64.h"
|
||||
|
||||
/* Common Link-layer Header */
|
||||
|
||||
typedef enum {
|
||||
L2_MSG_TYPE_DATA = 0x0,
|
||||
L2_MSG_TYPE_CONN_MGMT = 0x1,
|
||||
L2_MSG_TYPE_CONNECTIONLESS = 0x4
|
||||
} layer2_message_type_t;
|
||||
|
||||
typedef struct layer2_packet_header_s {
|
||||
layer2_message_type_t msg_type; //!< message type
|
||||
bool tx_request; //!< transmission request (marks the end of the burst)
|
||||
|
||||
uint8_t tx_seq_nr; //!< sequence number (0 to 15) of this packet
|
||||
uint8_t rx_seq_nr; //!< sequence number (0 to 15) expected next from he other side
|
||||
|
||||
ham64_t src_addr; //!< source HAM-64 address
|
||||
ham64_t dst_addr; //!< destination HAM-64 address
|
||||
} layer2_packet_header_t;
|
||||
|
||||
/*!\brief Encode a layer2 packet header to the transmitted form.
|
||||
* \param header The header structure to encode.
|
||||
* \param encoded A byte array where the encoded header will be stored. Must have space for 18 bytes.
|
||||
* \returns The number of encoded bytes. Zero if an error occurred.
|
||||
*/
|
||||
size_t layer2_encode_packet_header(const layer2_packet_header_t *header, uint8_t *encoded);
|
||||
|
||||
/*!\brief Decode a layer2 packet header from the received form.
|
||||
* \param encoded A byte array containing the received header.
|
||||
* \param encoded_len Length (in bytes) of the given encoded data.
|
||||
* \param header The header structure where the decoded information is stored.
|
||||
* \returns Whether the header was decoded successfully.
|
||||
*/
|
||||
bool layer2_decode_packet_header(const uint8_t *encoded, size_t encoded_len, layer2_packet_header_t *header);
|
||||
|
||||
/* Data Packet Structs */
|
||||
|
||||
typedef enum {
|
||||
L2_PAYLOAD_TYPE_IPV4 = 0x00,
|
||||
L2_PAYLOAD_TYPE_IPV6 = 0x01
|
||||
} layer2_payload_type_t;
|
||||
|
||||
typedef struct layer2_data_header_s {
|
||||
layer2_payload_type_t payload_type; //!< Type of the contained layer 3 packet
|
||||
} layer2_data_header_t;
|
||||
|
||||
/* Connection Management Structs */
|
||||
|
||||
// TODO
|
||||
|
||||
#endif // PACKET_STRUCTS_H
|
|
@ -132,3 +132,16 @@ target_link_libraries(
|
|||
m
|
||||
liquid
|
||||
)
|
||||
|
||||
#------------------------------------
|
||||
|
||||
add_executable(
|
||||
test_ham64
|
||||
../src/layer2/ham64.c
|
||||
../src/layer2/ham64.h
|
||||
test_ham64.c
|
||||
)
|
||||
|
||||
target_link_libraries(
|
||||
test_ham64
|
||||
)
|
||||
|
|
|
@ -0,0 +1,52 @@
|
|||
#include <assert.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <layer2/ham64.h>
|
||||
|
||||
bool test_decode(const char *ref, const char *exp_fmt, const char *exp_type, const ham64_t *ham64)
|
||||
{
|
||||
char decoded[13] = {0};
|
||||
|
||||
char ham64_format_buf[HAM64_FMT_MAX_LEN];
|
||||
|
||||
size_t decoded_len = ham64_decode_callsign(ham64, decoded);
|
||||
|
||||
ham64_format(ham64, ham64_format_buf);
|
||||
const char *typestr = ham64_addr_type_to_string(ham64_get_addr_type(ham64));
|
||||
printf("»%s« → [%u] %s (%s) → [%zd] »%s«\n", ref, ham64->length, ham64_format_buf, typestr, decoded_len, decoded);
|
||||
rudi_s marked this conversation as resolved
rudi_s
commented
These printed results should be verified by the test as well. These printed results should be verified by the test as well.
|
||||
|
||||
return strcmp(ref, decoded) == 0
|
||||
&& strcmp(exp_fmt, ham64_format_buf) == 0
|
||||
&& strcmp(exp_type, typestr) == 0;
|
||||
}
|
||||
|
||||
bool test_call(const char *call, const char *exp_fmt)
|
||||
{
|
||||
ham64_t ham64;
|
||||
|
||||
ham64_encode(call, &ham64);
|
||||
return test_decode(call, exp_fmt, "CALLSIGN", &ham64);
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
char *call1 = "VI2BMARC50-X";
|
||||
char *call2 = "DL5TKL/T";
|
||||
char *call3 = "D9K";
|
||||
|
||||
ham64_t empty = {{0x0000, 0x0, 0x0, 0x0}, 1};
|
||||
ham64_t short_1 = {{0x0001, 0x0, 0x0, 0x0}, 1};
|
||||
ham64_t short_last = {{0x063F, 0x0, 0x0, 0x0}, 1};
|
||||
ham64_t broadcast = {{0xFFFF, 0x0, 0x0, 0x0}, 1};
|
||||
|
||||
assert(test_call(call1, "8B05-0E89-7118-AEC8"));
|
||||
assert(test_call(call2, "1B00-7EC4-EA60"));
|
||||
assert(test_call(call3, "1EAB"));
|
||||
|
||||
assert(test_decode("", "0000", "EMPTY", &empty));
|
||||
assert(test_decode("", "0001", "TMP_SHORT", &short_1));
|
||||
assert(test_decode("", "063F", "TMP_SHORT", &short_last));
|
||||
assert(test_decode("", "FFFF", "BROADCAST", &broadcast));
|
||||
}
|
Loading…
Reference in New Issue
Off-by-one overflow (found by fsanitize) if
ham64->length == 4
becausesprintf
NUL-terminates its output but there's not enough space for-
and NUL. Possible fix: