mirror of
https://agent.ghink.cloud/wb2osz/direwolf
synced 2025-04-19 21:09:31 +00:00
679 lines
22 KiB
C
679 lines
22 KiB
C
//
|
|
// This file is part of Dire Wolf, an amateur radio packet TNC.
|
|
//
|
|
// Copyright (C) 2021 John Langner, WB2OSZ
|
|
//
|
|
// This program is free software: you can redistribute it and/or modify
|
|
// it under the terms of the GNU General Public License as published by
|
|
// the Free Software Foundation, either version 2 of the License, or
|
|
// (at your option) any later version.
|
|
//
|
|
// This program is distributed in the hope that it will be useful,
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
// GNU General Public License for more details.
|
|
//
|
|
// You should have received a copy of the GNU General Public License
|
|
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
//
|
|
|
|
#include "direwolf.h"
|
|
|
|
#include <assert.h>
|
|
#include <string.h>
|
|
#include <stdio.h>
|
|
#include <ctype.h>
|
|
|
|
#include "textcolor.h"
|
|
#include "ax25_pad.h"
|
|
#include "ax25_pad2.h"
|
|
#include "il2p.h"
|
|
|
|
|
|
|
|
/*--------------------------------------------------------------------------------
|
|
*
|
|
* File: il2p_header.c
|
|
*
|
|
* Purpose: Functions to deal with the IL2P header.
|
|
*
|
|
* Reference: http://tarpn.net/t/il2p/il2p-specification0-4.pdf
|
|
*
|
|
*--------------------------------------------------------------------------------*/
|
|
|
|
|
|
|
|
// Convert ASCII to/from DEC SIXBIT as defined here:
|
|
// https://en.wikipedia.org/wiki/Six-bit_character_code#DEC_six-bit_code
|
|
|
|
static inline int ascii_to_sixbit (int a)
|
|
{
|
|
if (a >= ' ' && a <= '_') return (a - ' ');
|
|
return (31); // '?' for any invalid.
|
|
}
|
|
|
|
static inline int sixbit_to_ascii (int s)
|
|
{
|
|
return (s + ' ');
|
|
}
|
|
|
|
// Functions for setting the various header fields.
|
|
// It is assumed that it was zeroed first so only the '1' bits are set.
|
|
|
|
static void set_field (unsigned char *hdr, int bit_num, int lsb_index, int width, int value)
|
|
{
|
|
while (width > 0 && value != 0) {
|
|
assert (lsb_index >= 0 && lsb_index <= 11);
|
|
if (value & 1) {
|
|
hdr[lsb_index] |= 1 << bit_num;
|
|
}
|
|
value >>= 1;
|
|
lsb_index--;
|
|
width--;
|
|
}
|
|
assert (value == 0);
|
|
}
|
|
|
|
#define SET_UI(hdr,val) set_field(hdr, 6, 0, 1, val)
|
|
|
|
#define SET_PID(hdr,val) set_field(hdr, 6, 4, 4, val)
|
|
|
|
#define SET_CONTROL(hdr,val) set_field(hdr, 6, 11, 7, val)
|
|
|
|
|
|
#define SET_FEC_LEVEL(hdr,val) set_field(hdr, 7, 0, 1, val)
|
|
|
|
#define SET_HDR_TYPE(hdr,val) set_field(hdr, 7, 1, 1, val)
|
|
|
|
#define SET_PAYLOAD_BYTE_COUNT(hdr,val) set_field(hdr, 7, 11, 10, val)
|
|
|
|
|
|
// Extracting the fields.
|
|
|
|
static int get_field (unsigned char *hdr, int bit_num, int lsb_index, int width)
|
|
{
|
|
int result = 0;
|
|
lsb_index -= width - 1;
|
|
while (width > 0) {
|
|
result <<= 1;
|
|
assert (lsb_index >= 0 && lsb_index <= 11);
|
|
if (hdr[lsb_index] & (1 << bit_num)) {
|
|
result |= 1;
|
|
}
|
|
lsb_index++;
|
|
width--;
|
|
}
|
|
return (result);
|
|
}
|
|
|
|
#define GET_UI(hdr) get_field(hdr, 6, 0, 1)
|
|
|
|
#define GET_PID(hdr) get_field(hdr, 6, 4, 4)
|
|
|
|
#define GET_CONTROL(hdr) get_field(hdr, 6, 11, 7)
|
|
|
|
|
|
#define GET_FEC_LEVEL(hdr) get_field(hdr, 7, 0, 1)
|
|
|
|
#define GET_HDR_TYPE(hdr) get_field(hdr, 7, 1, 1)
|
|
|
|
#define GET_PAYLOAD_BYTE_COUNT(hdr) get_field(hdr, 7, 11, 10)
|
|
|
|
|
|
|
|
// AX.25 'I' and 'UI' frames have a protocol ID which determines how the
|
|
// information part should be interpreted.
|
|
// Here we squeeze the most common cases down to 4 bits.
|
|
// Return -1 if translation is not possible. Fall back to type 0 header in this case.
|
|
|
|
static int encode_pid (packet_t pp)
|
|
{
|
|
int pid = ax25_get_pid(pp);
|
|
|
|
if ((pid & 0x30) == 0x20) return (0x2); // AX.25 Layer 3
|
|
if ((pid & 0x30) == 0x10) return (0x2); // AX.25 Layer 3
|
|
if (pid == 0x01) return (0x3); // ISO 8208 / CCIT X.25 PLP
|
|
if (pid == 0x06) return (0x4); // Compressed TCP/IP
|
|
if (pid == 0x07) return (0x5); // Uncompressed TCP/IP
|
|
if (pid == 0x08) return (0x6); // Segmentation fragmen
|
|
if (pid == 0xcc) return (0xb); // ARPA Internet Protocol
|
|
if (pid == 0xcd) return (0xc); // ARPA Address Resolution
|
|
if (pid == 0xce) return (0xd); // FlexNet
|
|
if (pid == 0xcf) return (0xe); // TheNET
|
|
if (pid == 0xf0) return (0xf); // No L3
|
|
return (-1);
|
|
}
|
|
|
|
// Convert IL2P 4 bit PID to AX.25 8 bit PID.
|
|
|
|
|
|
static int decode_pid (int pid)
|
|
{
|
|
static const unsigned char axpid[16] = {
|
|
0xf0, // Should not happen. 0 is for 'S' frames.
|
|
0xf0, // Should not happen. 1 is for 'U' frames (but not UI).
|
|
0x20, // AX.25 Layer 3
|
|
0x01, // ISO 8208 / CCIT X.25 PLP
|
|
0x06, // Compressed TCP/IP
|
|
0x07, // Uncompressed TCP/IP
|
|
0x08, // Segmentation fragment
|
|
0xf0, // Future
|
|
0xf0, // Future
|
|
0xf0, // Future
|
|
0xf0, // Future
|
|
0xcc, // ARPA Internet Protocol
|
|
0xcd, // ARPA Address Resolution
|
|
0xce, // FlexNet
|
|
0xcf, // TheNET
|
|
0xf0 }; // No L3
|
|
|
|
assert (pid >= 0 && pid <= 15);
|
|
return (axpid[pid]);
|
|
}
|
|
|
|
|
|
|
|
/*--------------------------------------------------------------------------------
|
|
*
|
|
* Function: il2p_type_1_header
|
|
*
|
|
* Purpose: Attempt to create type 1 header from packet object.
|
|
*
|
|
* Inputs: pp - Packet object.
|
|
*
|
|
* max_fec - 1 to use maximum FEC symbols , 0 for automatic.
|
|
*
|
|
* Outputs: hdr - IL2P header with no scrambling or parity symbols.
|
|
* Must be large enough to hold IL2P_HEADER_SIZE unsigned bytes.
|
|
*
|
|
* Returns: Number of bytes for information part or -1 for failure.
|
|
* In case of failure, fall back to type 0 transparent encapsulation.
|
|
*
|
|
* Description: Type 1 Headers do not support AX.25 repeater callsign addressing,
|
|
* Modulo-128 extended mode window sequence numbers, nor any callsign
|
|
* characters that cannot translate to DEC SIXBIT.
|
|
* If these cases are encountered during IL2P packet encoding,
|
|
* the encoder switches to Type 0 Transparent Encapsulation.
|
|
* SABME can't be handled by type 1.
|
|
*
|
|
*--------------------------------------------------------------------------------*/
|
|
|
|
int il2p_type_1_header (packet_t pp, int max_fec, unsigned char *hdr)
|
|
{
|
|
memset (hdr, 0, IL2P_HEADER_SIZE);
|
|
|
|
if (ax25_get_num_addr(pp) != 2) {
|
|
// Only two addresses are allowed for type 1 header.
|
|
return (-1);
|
|
}
|
|
|
|
// Check does not apply for 'U' frames but put in one place rather than two.
|
|
|
|
if (ax25_get_modulo(pp) == 128) return(-1);
|
|
|
|
// Destination and source addresses go into low bits 0-5 for bytes 0-11.
|
|
|
|
char dst_addr[AX25_MAX_ADDR_LEN];
|
|
char src_addr[AX25_MAX_ADDR_LEN];
|
|
|
|
ax25_get_addr_no_ssid (pp, AX25_DESTINATION, dst_addr);
|
|
int dst_ssid = ax25_get_ssid (pp, AX25_DESTINATION);
|
|
|
|
ax25_get_addr_no_ssid (pp, AX25_SOURCE, src_addr);
|
|
int src_ssid = ax25_get_ssid (pp, AX25_SOURCE);
|
|
|
|
unsigned char *a = (unsigned char *)dst_addr;
|
|
for (int i = 0; *a != '\0'; i++, a++) {
|
|
if (*a < ' ' || *a > '_') {
|
|
// Shouldn't happen but follow the rule.
|
|
return (-1);
|
|
}
|
|
hdr[i] = ascii_to_sixbit(*a);
|
|
}
|
|
|
|
a = (unsigned char *)src_addr;
|
|
for (int i = 6; *a != '\0'; i++, a++) {
|
|
if (*a < ' ' || *a > '_') {
|
|
// Shouldn't happen but follow the rule.
|
|
return (-1);
|
|
}
|
|
hdr[i] = ascii_to_sixbit(*a);
|
|
}
|
|
|
|
// Byte 12 has DEST SSID in upper nybble and SRC SSID in lower nybble and
|
|
hdr[12] = (dst_ssid << 4) | src_ssid;
|
|
|
|
ax25_frame_type_t frame_type;
|
|
cmdres_t cr; // command or response.
|
|
char description[64];
|
|
int pf; // Poll/Final.
|
|
int nr, ns; // Sequence numbers.
|
|
|
|
frame_type = ax25_frame_type (pp, &cr, description, &pf, &nr, &ns);
|
|
|
|
//dw_printf ("%s(): %s-%d>%s-%d: %s\n", __func__, src_addr, src_ssid, dst_addr, dst_ssid, description);
|
|
|
|
switch (frame_type) {
|
|
|
|
case frame_type_S_RR: // Receive Ready - System Ready To Receive
|
|
case frame_type_S_RNR: // Receive Not Ready - TNC Buffer Full
|
|
case frame_type_S_REJ: // Reject Frame - Out of Sequence or Duplicate
|
|
case frame_type_S_SREJ: // Selective Reject - Request single frame repeat
|
|
|
|
// S frames (RR, RNR, REJ, SREJ), mod 8, have control N(R) P/F S S 0 1
|
|
// These are mapped into P/F N(R) C S S
|
|
// Bit 6 is not mentioned in documentation but it is used for P/F for the other frame types.
|
|
// C is copied from the C bit in the destination addr.
|
|
// C from source is not used here. Reception assumes it is the opposite.
|
|
// PID is set to 0, meaning none, for S frames.
|
|
|
|
SET_UI(hdr, 0);
|
|
SET_PID(hdr, 0);
|
|
SET_CONTROL(hdr, (pf<<6) | (nr<<3) | (((cr == cr_cmd) | (cr == cr_11))<<2));
|
|
|
|
// This gets OR'ed into the above.
|
|
switch (frame_type) {
|
|
case frame_type_S_RR: SET_CONTROL(hdr, 0); break;
|
|
case frame_type_S_RNR: SET_CONTROL(hdr, 1); break;
|
|
case frame_type_S_REJ: SET_CONTROL(hdr, 2); break;
|
|
case frame_type_S_SREJ: SET_CONTROL(hdr, 3); break;
|
|
default: break;
|
|
}
|
|
|
|
break;
|
|
|
|
case frame_type_U_SABM: // Set Async Balanced Mode
|
|
case frame_type_U_DISC: // Disconnect
|
|
case frame_type_U_DM: // Disconnect Mode
|
|
case frame_type_U_UA: // Unnumbered Acknowledge
|
|
case frame_type_U_FRMR: // Frame Reject
|
|
case frame_type_U_UI: // Unnumbered Information
|
|
case frame_type_U_XID: // Exchange Identification
|
|
case frame_type_U_TEST: // Test
|
|
|
|
// The encoding allows only 3 bits for frame type and SABME got left out.
|
|
// Control format: P/F opcode[3] C n/a n/a
|
|
// The grayed out n/a bits are observed as 00 in the example.
|
|
// The header UI field must also be set for UI frames.
|
|
// PID is set to 1 for all U frames other than UI.
|
|
|
|
if (frame_type == frame_type_U_UI) {
|
|
SET_UI(hdr, 1); // I guess this is how we distinguish 'I' and 'UI'
|
|
// on the receiving end.
|
|
int pid = encode_pid(pp);
|
|
if (pid < 0) return (-1);
|
|
SET_PID(hdr, pid);
|
|
}
|
|
else {
|
|
SET_PID(hdr, 1); // 1 for 'U' other than 'UI'.
|
|
}
|
|
|
|
// Each of the destination and source addresses has a "C" bit.
|
|
// They should normally have the opposite setting.
|
|
// IL2P has only a single bit to represent 4 possbilities.
|
|
//
|
|
// dst src il2p meaning
|
|
// --- --- ---- -------
|
|
// 0 0 0 Not valid (earlier protocol version)
|
|
// 1 0 1 Command (v2)
|
|
// 0 1 0 Response (v2)
|
|
// 1 1 1 Not valid (earlier protocol version)
|
|
//
|
|
// APRS does not mention how to set these bits and all 4 combinations
|
|
// are seen in the wild. Apparently these are ignored on receive and no
|
|
// one cares. Here we copy from the C bit in the destination address.
|
|
// It should be noted that the case of both C bits being the same can't
|
|
// be represented so the il2p encode/decode bit not produce exactly the
|
|
// same bits. We see this in the second example in the protocol spec.
|
|
// The original UI frame has both C bits of 0 so it is received as a response.
|
|
|
|
SET_CONTROL(hdr, (pf<<6) | (((cr == cr_cmd) | (cr == cr_11))<<2));
|
|
|
|
// This gets OR'ed into the above.
|
|
switch (frame_type) {
|
|
case frame_type_U_SABM: SET_CONTROL(hdr, 0<<3); break;
|
|
case frame_type_U_DISC: SET_CONTROL(hdr, 1<<3); break;
|
|
case frame_type_U_DM: SET_CONTROL(hdr, 2<<3); break;
|
|
case frame_type_U_UA: SET_CONTROL(hdr, 3<<3); break;
|
|
case frame_type_U_FRMR: SET_CONTROL(hdr, 4<<3); break;
|
|
case frame_type_U_UI: SET_CONTROL(hdr, 5<<3); break;
|
|
case frame_type_U_XID: SET_CONTROL(hdr, 6<<3); break;
|
|
case frame_type_U_TEST: SET_CONTROL(hdr, 7<<3); break;
|
|
default: break;
|
|
}
|
|
break;
|
|
|
|
case frame_type_I: // Information
|
|
|
|
// I frames (mod 8 only)
|
|
// encoded control: P/F N(R) N(S)
|
|
|
|
SET_UI(hdr, 0);
|
|
|
|
int pid2 = encode_pid(pp);
|
|
if (pid2 < 0) return (-1);
|
|
SET_PID(hdr, pid2);
|
|
|
|
SET_CONTROL(hdr, (pf<<6) | (nr<<3) | ns);
|
|
break;
|
|
|
|
case frame_type_U_SABME: // Set Async Balanced Mode, Extended
|
|
case frame_type_U: // other Unnumbered, not used by AX.25.
|
|
case frame_not_AX25: // Could not get control byte from frame.
|
|
default:
|
|
|
|
// Fall back to the header type 0 for these.
|
|
return (-1);
|
|
}
|
|
|
|
// Common for all header type 1.
|
|
|
|
// Bit 7 has [FEC Level:1], [HDR Type:1], [Payload byte Count:10]
|
|
|
|
SET_FEC_LEVEL(hdr, max_fec);
|
|
SET_HDR_TYPE(hdr, 1);
|
|
|
|
unsigned char *pinfo;
|
|
int info_len;
|
|
|
|
info_len = ax25_get_info (pp, &pinfo);
|
|
if (info_len < 0 || info_len > IL2P_MAX_PAYLOAD_SIZE) {
|
|
return (-2);
|
|
}
|
|
|
|
SET_PAYLOAD_BYTE_COUNT(hdr, info_len);
|
|
return (info_len);
|
|
}
|
|
|
|
|
|
// This should create a packet from the IL2P header.
|
|
// The information part will not be filled in.
|
|
|
|
static void trim (char *stuff)
|
|
{
|
|
char *p = stuff + strlen(stuff) - 1;
|
|
while (strlen(stuff) > 0 && (*p == ' ')) {
|
|
*p = '\0';
|
|
p--;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/*--------------------------------------------------------------------------------
|
|
*
|
|
* Function: il2p_decode_header_type_1
|
|
*
|
|
* Purpose: Attempt to convert type 1 header to a packet object.
|
|
*
|
|
* Inputs: hdr - IL2P header with no scrambling or parity symbols.
|
|
*
|
|
* num_sym_changed - Number of symbols changed by FEC in the header.
|
|
* Should be 0 or 1.
|
|
*
|
|
* Returns: Packet Object or NULL for failure.
|
|
*
|
|
* Description: A later step will process the payload for the information part.
|
|
*
|
|
*--------------------------------------------------------------------------------*/
|
|
|
|
packet_t il2p_decode_header_type_1 (unsigned char *hdr, int num_sym_changed)
|
|
{
|
|
|
|
if (GET_HDR_TYPE(hdr) != 1 ) {
|
|
text_color_set(DW_COLOR_ERROR);
|
|
dw_printf ("IL2P Internal error. Should not be here: %s, when header type is 0.\n", __func__);
|
|
return (NULL);
|
|
}
|
|
|
|
// First get the addresses including SSID.
|
|
|
|
char addrs[AX25_MAX_ADDRS][AX25_MAX_ADDR_LEN];
|
|
int num_addr = 2;
|
|
memset (addrs, 0, 2*AX25_MAX_ADDR_LEN);
|
|
|
|
// The IL2P header uses 2 parity symbols which means a single corrupted symbol (byte)
|
|
// can always be corrected.
|
|
// However, I have seen cases, where the error rate is very high, where the RS decoder
|
|
// thinks it found a valid code block by changing one symbol but it was the wrong one.
|
|
// The result is trash. This shows up as address fields like 'R&G4"A' and 'TEW\ !'.
|
|
// I added a sanity check here to catch characters other than upper case letters and digits.
|
|
// The frame should be rejected in this case. The question is whether to discard it
|
|
// silently or print a message so the user can see that something strange is happening?
|
|
// My current thinking is that it should be silently ignored if the header has been
|
|
// modified (correctee or more likely, made worse in this cases).
|
|
// If no changes were made, something weird is happening. We should mention it for
|
|
// troubleshooting rather than sweeping it under the rug.
|
|
|
|
// The same thing has been observed with the payload, under very high error conditions,
|
|
// and max_fec==0. Here I don't see a good solution. AX.25 information can contain
|
|
// "binary" data so I'm not sure what sort of sanity check could be added.
|
|
// This was not observed with max_fec==1. If we make that the default, same as Nino TNC,
|
|
// it would be extremely extremely unlikely unless someone explicitly selects weaker FEC.
|
|
|
|
// TODO: We could do something similar for header type 0.
|
|
// The address fields should be all binary zero values.
|
|
// Someone overly ambitious might check the addresses found in the first payload block.
|
|
|
|
for (int i = 0; i <= 5; i++) {
|
|
addrs[AX25_DESTINATION][i] = sixbit_to_ascii(hdr[i] & 0x3f);
|
|
}
|
|
trim (addrs[AX25_DESTINATION]);
|
|
for (int i = 0; i < strlen(addrs[AX25_DESTINATION]); i++) {
|
|
if (! isupper(addrs[AX25_DESTINATION][i]) && ! isdigit(addrs[AX25_DESTINATION][i])) {
|
|
if (num_sym_changed == 0) {
|
|
// This can pop up sporadically when receiving random noise.
|
|
// Would be better to show only when debug is enabled but variable not available here.
|
|
// TODO: For now we will just suppress it.
|
|
//text_color_set(DW_COLOR_ERROR);
|
|
//dw_printf ("IL2P: Invalid character '%c' in destination address '%s'\n", addrs[AX25_DESTINATION][i], addrs[AX25_DESTINATION]);
|
|
}
|
|
return (NULL);
|
|
}
|
|
}
|
|
snprintf (addrs[AX25_DESTINATION]+strlen(addrs[AX25_DESTINATION]), 4, "-%d", (hdr[12] >> 4) &0xf);
|
|
|
|
for (int i = 0; i <= 5; i++) {
|
|
addrs[AX25_SOURCE][i] = sixbit_to_ascii(hdr[i+6] & 0x3f);
|
|
}
|
|
trim (addrs[AX25_SOURCE]);
|
|
for (int i = 0; i < strlen(addrs[AX25_SOURCE]); i++) {
|
|
if (! isupper(addrs[AX25_SOURCE][i]) && ! isdigit(addrs[AX25_SOURCE][i])) {
|
|
if (num_sym_changed == 0) {
|
|
// This can pop up sporadically when receiving random noise.
|
|
// Would be better to show only when debug is enabled but variable not available here.
|
|
// TODO: For now we will just suppress it.
|
|
//text_color_set(DW_COLOR_ERROR);
|
|
//dw_printf ("IL2P: Invalid character '%c' in source address '%s'\n", addrs[AX25_SOURCE][i], addrs[AX25_SOURCE]);
|
|
}
|
|
return (NULL);
|
|
}
|
|
}
|
|
snprintf (addrs[AX25_SOURCE]+strlen(addrs[AX25_SOURCE]), 4, "-%d", hdr[12] &0xf);
|
|
|
|
// The PID field gives us the general type.
|
|
// 0 = 'S' frame.
|
|
// 1 = 'U' frame other than UI.
|
|
// others are either 'UI' or 'I' depending on the UI field.
|
|
|
|
int pid = GET_PID(hdr);
|
|
int ui = GET_UI(hdr);
|
|
|
|
if (pid == 0) {
|
|
|
|
// 'S' frame.
|
|
// The control field contains: P/F N(R) C S S
|
|
|
|
int control = GET_CONTROL(hdr);
|
|
cmdres_t cr = (control & 0x04) ? cr_cmd : cr_res;
|
|
ax25_frame_type_t ftype;
|
|
switch (control & 0x03) {
|
|
case 0: ftype = frame_type_S_RR; break;
|
|
case 1: ftype = frame_type_S_RNR; break;
|
|
case 2: ftype = frame_type_S_REJ; break;
|
|
default: ftype = frame_type_S_SREJ; break;
|
|
}
|
|
int modulo = 8;
|
|
int nr = (control >> 3) & 0x07;
|
|
int pf = (control >> 6) & 0x01;
|
|
unsigned char *pinfo = NULL; // Any info for SREJ will be added later.
|
|
int info_len = 0;
|
|
return (ax25_s_frame (addrs, num_addr, cr, ftype, modulo, nr, pf, pinfo, info_len));
|
|
}
|
|
else if (pid == 1) {
|
|
|
|
// 'U' frame other than 'UI'.
|
|
// The control field contains: P/F OPCODE{3) C x x
|
|
|
|
int control = GET_CONTROL(hdr);
|
|
cmdres_t cr = (control & 0x04) ? cr_cmd : cr_res;
|
|
int axpid = 0; // unused for U other than UI.
|
|
ax25_frame_type_t ftype;
|
|
switch ((control >> 3) & 0x7) {
|
|
case 0: ftype = frame_type_U_SABM; break;
|
|
case 1: ftype = frame_type_U_DISC; break;
|
|
case 2: ftype = frame_type_U_DM; break;
|
|
case 3: ftype = frame_type_U_UA; break;
|
|
case 4: ftype = frame_type_U_FRMR; break;
|
|
case 5: ftype = frame_type_U_UI; axpid = 0xf0; break; // Should not happen with IL2P pid == 1.
|
|
case 6: ftype = frame_type_U_XID; break;
|
|
default: ftype = frame_type_U_TEST; break;
|
|
}
|
|
int pf = (control >> 6) & 0x01;
|
|
unsigned char *pinfo = NULL; // Any info for UI, XID, TEST will be added later.
|
|
int info_len = 0;
|
|
return (ax25_u_frame (addrs, num_addr, cr, ftype, pf, axpid, pinfo, info_len));
|
|
}
|
|
else if (ui) {
|
|
|
|
// 'UI' frame.
|
|
// The control field contains: P/F OPCODE{3) C x x
|
|
|
|
int control = GET_CONTROL(hdr);
|
|
cmdres_t cr = (control & 0x04) ? cr_cmd : cr_res;
|
|
ax25_frame_type_t ftype = frame_type_U_UI;
|
|
int pf = (control >> 6) & 0x01;
|
|
int axpid = decode_pid(GET_PID(hdr));
|
|
unsigned char *pinfo = NULL; // Any info for UI, XID, TEST will be added later.
|
|
int info_len = 0;
|
|
return (ax25_u_frame (addrs, num_addr, cr, ftype, pf, axpid, pinfo, info_len));
|
|
}
|
|
else {
|
|
|
|
// 'I' frame.
|
|
// The control field contains: P/F N(R) N(S)
|
|
|
|
int control = GET_CONTROL(hdr);
|
|
cmdres_t cr = cr_cmd; // Always command.
|
|
int pf = (control >> 6) & 0x01;
|
|
int nr = (control >> 3) & 0x7;
|
|
int ns = control & 0x7;
|
|
int modulo = 8;
|
|
int axpid = decode_pid(GET_PID(hdr));
|
|
unsigned char *pinfo = NULL; // Any info for UI, XID, TEST will be added later.
|
|
int info_len = 0;
|
|
return (ax25_i_frame (addrs, num_addr, cr, modulo, nr, ns, pf, axpid, pinfo, info_len));
|
|
}
|
|
return (NULL); // unreachable but avoid warning.
|
|
|
|
} // end
|
|
|
|
|
|
/*--------------------------------------------------------------------------------
|
|
*
|
|
* Function: il2p_type_0_header
|
|
*
|
|
* Purpose: Attempt to create type 0 header from packet object.
|
|
*
|
|
* Inputs: pp - Packet object.
|
|
*
|
|
* max_fec - 1 to use maximum FEC symbols, 0 for automatic.
|
|
*
|
|
* Outputs: hdr - IL2P header with no scrambling or parity symbols.
|
|
* Must be large enough to hold IL2P_HEADER_SIZE unsigned bytes.
|
|
*
|
|
* Returns: Number of bytes for information part or -1 for failure.
|
|
* In case of failure, fall back to type 0 transparent encapsulation.
|
|
*
|
|
* Description: The type 0 header is used when it is not one of the restricted cases
|
|
* covered by the type 1 header.
|
|
* The AX.25 frame is put in the payload.
|
|
* This will cover: more than one address, mod 128 sequences, etc.
|
|
*
|
|
*--------------------------------------------------------------------------------*/
|
|
|
|
int il2p_type_0_header (packet_t pp, int max_fec, unsigned char *hdr)
|
|
{
|
|
memset (hdr, 0, IL2P_HEADER_SIZE);
|
|
|
|
// Bit 7 has [FEC Level:1], [HDR Type:1], [Payload byte Count:10]
|
|
|
|
SET_FEC_LEVEL(hdr, max_fec);
|
|
SET_HDR_TYPE(hdr, 0);
|
|
|
|
int frame_len = ax25_get_frame_len (pp);
|
|
|
|
if (frame_len < 14 || frame_len > IL2P_MAX_PAYLOAD_SIZE) {
|
|
return (-2);
|
|
}
|
|
|
|
SET_PAYLOAD_BYTE_COUNT(hdr, frame_len);
|
|
return (frame_len);
|
|
}
|
|
|
|
|
|
/***********************************************************************************
|
|
*
|
|
* Name: il2p_get_header_attributes
|
|
*
|
|
* Purpose: Extract a few attributes from an IL2p header.
|
|
*
|
|
* Inputs: hdr - IL2P header structure.
|
|
*
|
|
* Outputs: hdr_type - 0 or 1.
|
|
*
|
|
* max_fec - 0 for automatic or 1 for fixed maximum size.
|
|
*
|
|
* Returns: Payload byte count. (actual payload size, not the larger encoded format)
|
|
*
|
|
***********************************************************************************/
|
|
|
|
|
|
int il2p_get_header_attributes (unsigned char *hdr, int *hdr_type, int *max_fec)
|
|
{
|
|
*hdr_type = GET_HDR_TYPE(hdr);
|
|
*max_fec = GET_FEC_LEVEL(hdr);
|
|
return(GET_PAYLOAD_BYTE_COUNT(hdr));
|
|
}
|
|
|
|
|
|
/***********************************************************************************
|
|
*
|
|
* Name: il2p_clarify_header
|
|
*
|
|
* Purpose: Convert received header to usable form.
|
|
* This involves RS FEC then descrambling.
|
|
*
|
|
* Inputs: rec_hdr - Header as received over the radio.
|
|
*
|
|
* Outputs: corrected_descrambled_hdr - After RS FEC and unscrambling.
|
|
*
|
|
* Returns: Number of symbols that were corrected:
|
|
* 0 = No errors
|
|
* 1 = Single symbol corrected.
|
|
* <0 = Unable to obtain good header.
|
|
*
|
|
***********************************************************************************/
|
|
|
|
int il2p_clarify_header(unsigned char *rec_hdr, unsigned char *corrected_descrambled_hdr)
|
|
{
|
|
unsigned char corrected[IL2P_HEADER_SIZE+IL2P_HEADER_PARITY];
|
|
|
|
int e = il2p_decode_rs (rec_hdr, IL2P_HEADER_SIZE, IL2P_HEADER_PARITY, corrected);
|
|
|
|
il2p_descramble_block (corrected, corrected_descrambled_hdr, IL2P_HEADER_SIZE);
|
|
|
|
return (e);
|
|
}
|
|
|
|
// end il2p_header.c
|